summaryrefslogtreecommitdiff
path: root/ext/pdo_pgsql
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2020-12-17 12:05:37 +0100
committerNikita Popov <nikita.ppv@gmail.com>2020-12-22 15:56:34 +0100
commitcaa710037e663fd78f67533b29611183090068b2 (patch)
tree7acbcdf527eb148899d6cb90e9f2cb233a3ec2ed /ext/pdo_pgsql
parent57d69b51373bfb1aa5117022c63c93c612e707f6 (diff)
downloadphp-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.c160
-rw-r--r--ext/pdo_pgsql/php_pdo_pgsql_int.h2
-rw-r--r--ext/pdo_pgsql/tests/bug62498.phpt36
-rw-r--r--ext/pdo_pgsql/tests/debug_emulated_prepares.phpt6
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"