diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2020-12-17 12:05:37 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2020-12-22 15:56:34 +0100 |
commit | caa710037e663fd78f67533b29611183090068b2 (patch) | |
tree | 7acbcdf527eb148899d6cb90e9f2cb233a3ec2ed /ext/pdo_pgsql | |
parent | 57d69b51373bfb1aa5117022c63c93c612e707f6 (diff) | |
download | php-git-caa710037e663fd78f67533b29611183090068b2.tar.gz |
Rewrite PDO result binding
Instead of requiring the type to be determined in advance by the
describer function and then requiring get_col to return a buffer
of appropriate type, allow get_col to return an arbitrary zval.
See UPGRADING.INTERNALS for a more detailed description of the
change.
This makes the result fetching simpler, more efficient and more
flexible. The general possibility already existed via the special
PDO_PARAM_ZVAL type, but the usage was very inconvenient and/or
inefficient. Now it's possible to easily implement behavior like
"return int if it fits, otherwise string" and to avoid any kind
of complex management of temporary buffers.
This also fixes bug #40913 (our second highest voted bug of all
time, for some reason). PARAM_LOB result bindings will now
consistently return a stream resource, independently of the used
database driver.
I've tried my best to update all PDO drivers for this change, but
some of the changes may be broken, as I cannot test or even build
some of these drivers (in particular PDO dblib and PDO oci).
Fixes are appreciated -- a working CI setup would be even more
appreciated ;)
Diffstat (limited to 'ext/pdo_pgsql')
-rw-r--r-- | ext/pdo_pgsql/pgsql_statement.c | 160 | ||||
-rw-r--r-- | ext/pdo_pgsql/php_pdo_pgsql_int.h | 2 | ||||
-rw-r--r-- | ext/pdo_pgsql/tests/bug62498.phpt | 36 | ||||
-rw-r--r-- | ext/pdo_pgsql/tests/debug_emulated_prepares.phpt | 6 |
4 files changed, 89 insertions, 115 deletions
diff --git a/ext/pdo_pgsql/pgsql_statement.c b/ext/pdo_pgsql/pgsql_statement.c index bf07af59a5..98ea8cb780 100644 --- a/ext/pdo_pgsql/pgsql_statement.c +++ b/ext/pdo_pgsql/pgsql_statement.c @@ -467,7 +467,6 @@ static int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno) { pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; struct pdo_column_data *cols = stmt->columns; - struct pdo_bound_param_data *param; char *str; if (!S->result) { @@ -480,122 +479,79 @@ static int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno) cols[colno].precision = PQfmod(S->result, colno); S->cols[colno].pgsql_type = PQftype(S->result, colno); - switch (S->cols[colno].pgsql_type) { - - case BOOLOID: - cols[colno].param_type = PDO_PARAM_BOOL; - break; - - case OIDOID: - /* did the user bind the column as a LOB ? */ - if (stmt->bound_columns && ( - (param = zend_hash_index_find_ptr(stmt->bound_columns, colno)) != NULL || - (param = zend_hash_find_ptr(stmt->bound_columns, cols[colno].name)) != NULL)) { - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { - cols[colno].param_type = PDO_PARAM_LOB; - break; - } - } - cols[colno].param_type = PDO_PARAM_INT; - break; - - case INT2OID: - case INT4OID: - cols[colno].param_type = PDO_PARAM_INT; - break; - - case INT8OID: - if (sizeof(zend_long)>=8) { - cols[colno].param_type = PDO_PARAM_INT; - } else { - cols[colno].param_type = PDO_PARAM_STR; - } - break; - - case BYTEAOID: - cols[colno].param_type = PDO_PARAM_LOB; - break; - - default: - cols[colno].param_type = PDO_PARAM_STR; - } - return 1; } -static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees ) +static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) { pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; - struct pdo_column_data *cols = stmt->columns; - size_t tmp_len; - if (!S->result) { return 0; } /* We have already increased count by 1 in pgsql_stmt_fetch() */ if (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */ - *ptr = NULL; - *len = 0; + ZVAL_NULL(result); } else { - *ptr = PQgetvalue(S->result, S->current_row - 1, colno); - *len = PQgetlength(S->result, S->current_row - 1, colno); - - switch (cols[colno].param_type) { + char *ptr = PQgetvalue(S->result, S->current_row - 1, colno); + size_t len = PQgetlength(S->result, S->current_row - 1, colno); - case PDO_PARAM_INT: - ZEND_ATOL(S->cols[colno].intval, *ptr); - *ptr = (char *) &(S->cols[colno].intval); - *len = sizeof(zend_long); + switch (S->cols[colno].pgsql_type) { + case BOOLOID: + ZVAL_BOOL(result, *ptr == 't'); break; - case PDO_PARAM_BOOL: - S->cols[colno].boolval = **ptr == 't'; - *ptr = (char *) &(S->cols[colno].boolval); - *len = sizeof(zend_bool); + case INT2OID: + case INT4OID: +#if SIZEOF_ZEND_LONG >= 8 + case INT8OID: +#endif + { + zend_long intval; + ZEND_ATOL(intval, ptr); + ZVAL_LONG(result, intval); break; + } - case PDO_PARAM_LOB: - if (S->cols[colno].pgsql_type == OIDOID) { - /* ooo, a real large object */ - char *end_ptr; - Oid oid = (Oid)strtoul(*ptr, &end_ptr, 10); + case OIDOID: { + char *end_ptr; + Oid oid = (Oid)strtoul(ptr, &end_ptr, 10); + if (type && *type == PDO_PARAM_LOB) { + /* If column was bound as LOB, return a stream. */ int loid = lo_open(S->H->server, oid, INV_READ); if (loid >= 0) { - *ptr = (char*)pdo_pgsql_create_lob_stream(&stmt->database_object_handle, loid, oid); - *len = 0; - return *ptr ? 1 : 0; + php_stream *stream = pdo_pgsql_create_lob_stream(&stmt->database_object_handle, loid, oid); + if (stream) { + php_stream_to_zval(stream, result); + return 1; + } } - *ptr = NULL; - *len = 0; return 0; } else { - char *tmp_ptr = (char *)PQunescapeBytea((unsigned char *)*ptr, &tmp_len); - if (!tmp_ptr) { - /* PQunescapeBytea returned an error */ - *len = 0; - return 0; - } - if (!tmp_len) { - /* Empty string, return as empty stream */ - *ptr = (char *)php_stream_memory_create(TEMP_STREAM_READONLY); - PQfreemem(tmp_ptr); - *len = 0; - } else { - *ptr = estrndup(tmp_ptr, tmp_len); - PQfreemem(tmp_ptr); - *len = tmp_len; - *caller_frees = 1; - } + /* Otherwise return OID as integer. */ + ZVAL_LONG(result, oid); } break; - case PDO_PARAM_NULL: - case PDO_PARAM_STR: - case PDO_PARAM_STMT: - case PDO_PARAM_INPUT_OUTPUT: - case PDO_PARAM_ZVAL: + } + + case BYTEAOID: { + size_t tmp_len; + char *tmp_ptr = (char *)PQunescapeBytea((unsigned char *) ptr, &tmp_len); + if (!tmp_ptr) { + /* PQunescapeBytea returned an error */ + return 0; + } + + zend_string *str = zend_string_init(tmp_ptr, tmp_len, 0); + php_stream *stream = php_stream_memory_open(TEMP_STREAM_READONLY, str); + php_stream_to_zval(stream, result); + zend_string_release(str); + PQfreemem(tmp_ptr); + break; + } + default: + ZVAL_STRINGL_FAST(result, ptr, len); break; } } @@ -698,6 +654,26 @@ static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *r } PQclear(res); } + + enum pdo_param_type param_type; + switch (S->cols[colno].pgsql_type) { + case BOOLOID: + param_type = PDO_PARAM_BOOL; + break; + case INT2OID: + case INT4OID: + case INT8OID: + param_type = PDO_PARAM_INT; + break; + case OIDOID: + case BYTEAOID: + param_type = PDO_PARAM_LOB; + break; + default: + param_type = PDO_PARAM_STR; + } + add_assoc_long(return_value, "pdo_type", param_type); + return 1; } diff --git a/ext/pdo_pgsql/php_pdo_pgsql_int.h b/ext/pdo_pgsql/php_pdo_pgsql_int.h index 1f5ea30063..b4bc6318e7 100644 --- a/ext/pdo_pgsql/php_pdo_pgsql_int.h +++ b/ext/pdo_pgsql/php_pdo_pgsql_int.h @@ -48,9 +48,7 @@ typedef struct { } pdo_pgsql_db_handle; typedef struct { - zend_long intval; Oid pgsql_type; - zend_bool boolval; } pdo_pgsql_column; typedef struct { diff --git a/ext/pdo_pgsql/tests/bug62498.phpt b/ext/pdo_pgsql/tests/bug62498.phpt index 1620e7e77a..2970278204 100644 --- a/ext/pdo_pgsql/tests/bug62498.phpt +++ b/ext/pdo_pgsql/tests/bug62498.phpt @@ -56,14 +56,14 @@ array(9) { string(13) "bugtest_62498" ["native_type"]=> string(4) "int2" + ["pdo_type"]=> + int(2) ["name"]=> string(7) "int2col" ["len"]=> int(2) ["precision"]=> int(-1) - ["pdo_type"]=> - int(1) } [1]=> array(8) { @@ -75,14 +75,14 @@ array(9) { string(13) "bugtest_62498" ["native_type"]=> string(4) "int4" + ["pdo_type"]=> + int(2) ["name"]=> string(7) "int4col" ["len"]=> int(4) ["precision"]=> int(-1) - ["pdo_type"]=> - int(1) } [2]=> array(8) { @@ -94,14 +94,14 @@ array(9) { string(13) "bugtest_62498" ["native_type"]=> string(4) "int8" + ["pdo_type"]=> + int(2) ["name"]=> string(7) "int8col" ["len"]=> int(8) ["precision"]=> int(-1) - ["pdo_type"]=> - int(1) } [3]=> array(8) { @@ -113,14 +113,14 @@ array(9) { string(13) "bugtest_62498" ["native_type"]=> string(7) "varchar" + ["pdo_type"]=> + int(3) ["name"]=> string(9) "stringcol" ["len"]=> int(-1) ["precision"]=> int(259) - ["pdo_type"]=> - int(2) } [4]=> array(8) { @@ -132,14 +132,14 @@ array(9) { string(13) "bugtest_62498" ["native_type"]=> string(4) "bool" + ["pdo_type"]=> + int(1) ["name"]=> string(7) "boolcol" ["len"]=> int(1) ["precision"]=> int(-1) - ["pdo_type"]=> - int(5) } [5]=> array(8) { @@ -151,14 +151,14 @@ array(9) { string(13) "bugtest_62498" ["native_type"]=> string(4) "date" + ["pdo_type"]=> + int(3) ["name"]=> string(7) "datecol" ["len"]=> int(4) ["precision"]=> int(-1) - ["pdo_type"]=> - int(2) } [6]=> array(8) { @@ -170,14 +170,14 @@ array(9) { string(13) "bugtest_62498" ["native_type"]=> string(4) "text" + ["pdo_type"]=> + int(3) ["name"]=> string(7) "textcol" ["len"]=> int(-1) ["precision"]=> int(-1) - ["pdo_type"]=> - int(2) } [7]=> array(8) { @@ -189,14 +189,14 @@ array(9) { string(13) "bugtest_62498" ["native_type"]=> string(9) "timestamp" + ["pdo_type"]=> + int(3) ["name"]=> string(5) "tscol" ["len"]=> int(8) ["precision"]=> int(-1) - ["pdo_type"]=> - int(2) } [8]=> array(8) { @@ -208,14 +208,14 @@ array(9) { string(13) "bugtest_62498" ["native_type"]=> string(5) "bytea" + ["pdo_type"]=> + int(4) ["name"]=> string(8) "byteacol" ["len"]=> int(-1) ["precision"]=> int(-1) - ["pdo_type"]=> - int(3) } } Done diff --git a/ext/pdo_pgsql/tests/debug_emulated_prepares.phpt b/ext/pdo_pgsql/tests/debug_emulated_prepares.phpt index dfbbcb4ad4..5c8f590351 100644 --- a/ext/pdo_pgsql/tests/debug_emulated_prepares.phpt +++ b/ext/pdo_pgsql/tests/debug_emulated_prepares.phpt @@ -31,17 +31,17 @@ Key: Name: [5] :bool paramno=-1 name=[5] ":bool" is_param=1 -param_type=2 +param_type=3 Key: Name: [4] :int paramno=-1 name=[4] ":int" is_param=1 -param_type=1 +param_type=2 Key: Name: [7] :string paramno=-1 name=[7] ":string" is_param=1 -param_type=2 +param_type=3 Key: Name: [5] :null paramno=-1 name=[5] ":null" |