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_mysql | |
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_mysql')
-rw-r--r-- | ext/pdo_mysql/mysql_statement.c | 42 | ||||
-rw-r--r-- | ext/pdo_mysql/tests/pdo_mysql_stmt_blobs.phpt | 10 | ||||
-rw-r--r-- | ext/pdo_mysql/tests/pdo_mysql_stmt_getcolumnmeta.phpt | 27 |
3 files changed, 37 insertions, 42 deletions
diff --git a/ext/pdo_mysql/mysql_statement.c b/ext/pdo_mysql/mysql_statement.c index f2ba5aa95a..e1f6c6ff95 100644 --- a/ext/pdo_mysql/mysql_statement.c +++ b/ext/pdo_mysql/mysql_statement.c @@ -658,18 +658,13 @@ static int pdo_mysql_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */ cols[i].precision = S->fields[i].decimals; cols[i].maxlen = S->fields[i].length; - -#ifdef PDO_USE_MYSQLND - cols[i].param_type = PDO_PARAM_ZVAL; -#else - cols[i].param_type = PDO_PARAM_STR; -#endif } PDO_DBG_RETURN(1); } /* }}} */ -static int pdo_mysql_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_t *len, int *caller_frees) /* {{{ */ +static int pdo_mysql_stmt_get_col( + pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) /* {{{ */ { pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; @@ -685,38 +680,33 @@ static int pdo_mysql_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_ } #ifdef PDO_USE_MYSQLND if (S->stmt) { - Z_TRY_ADDREF(S->stmt->data->result_bind[colno].zv); - *ptr = (char*)&S->stmt->data->result_bind[colno].zv; + ZVAL_COPY(result, &S->stmt->data->result_bind[colno].zv); } else { - Z_TRY_ADDREF(S->current_row[colno]); - *ptr = (char*)&S->current_row[colno]; + ZVAL_COPY(result, &S->current_row[colno]); } - *len = sizeof(zval); PDO_DBG_RETURN(1); #else if (S->stmt) { if (S->out_null[colno]) { - *ptr = NULL; - *len = 0; PDO_DBG_RETURN(1); } - *ptr = S->bound_result[colno].buffer; - if (S->out_length[colno] > S->bound_result[colno].buffer_length) { + + size_t length = S->out_length[colno]; + if (length > S->bound_result[colno].buffer_length) { /* mysql lied about the column width */ strcpy(stmt->error_code, "01004"); /* truncated */ - S->out_length[colno] = S->bound_result[colno].buffer_length; - *len = S->out_length[colno]; - PDO_DBG_RETURN(0); + length = S->out_length[colno] = S->bound_result[colno].buffer_length; } - *len = S->out_length[colno]; + ZVAL_STRINGL_FAST(result, S->bound_result[colno].buffer, length); PDO_DBG_RETURN(1); } if (S->current_data == NULL) { PDO_DBG_RETURN(0); } - *ptr = S->current_data[colno]; - *len = S->current_lengths[colno]; + if (S->current_data[colno]) { + ZVAL_STRINGL_FAST(result, S->current_data[colno], S->current_lengths[colno]); + } PDO_DBG_RETURN(1); #endif } /* }}} */ @@ -815,7 +805,7 @@ static int pdo_mysql_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *retu add_assoc_string(return_value, "native_type", str); } -#ifdef PDO_USE_MYSQLND + enum pdo_param_type param_type; switch (F->type) { case MYSQL_TYPE_BIT: case MYSQL_TYPE_YEAR: @@ -826,13 +816,13 @@ static int pdo_mysql_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *retu #if SIZEOF_ZEND_LONG==8 case MYSQL_TYPE_LONGLONG: #endif - add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT); + param_type = PDO_PARAM_INT; break; default: - add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + param_type = PDO_PARAM_STR; break; } -#endif + add_assoc_long(return_value, "pdo_type", param_type); add_assoc_zval(return_value, "flags", &flags); add_assoc_string(return_value, "table", (char *) (F->table?F->table : "")); diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_blobs.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_blobs.phpt index 9bdd03d7bd..7f45e56b6b 100644 --- a/ext/pdo_mysql/tests/pdo_mysql_stmt_blobs.phpt +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_blobs.phpt @@ -48,9 +48,15 @@ MySQLPDOTest::skip(); return false; } - if ($label !== $value) { + if (!is_resource($label)) { + printf("[%03d + 3] Returned value is not a stream resource\n", $offset); + return false; + } + + $contents = stream_get_contents($label); + if ($contents !== $value) { printf("[%03d + 3] Returned value seems to be wrong (%d vs. %d characters). Check manually\n", - $offset, strlen($label), strlen($value)); + $offset, strlen($contents), strlen($value)); return false; } diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_getcolumnmeta.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_getcolumnmeta.phpt index b5b0275f04..0e87b118ee 100644 --- a/ext/pdo_mysql/tests/pdo_mysql_stmt_getcolumnmeta.phpt +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_getcolumnmeta.phpt @@ -139,23 +139,22 @@ try { $real_as_float = (false === stristr($row['_mode'], "REAL_AS_FLOAT")) ? false : true; $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); - $is_mysqlnd = MySQLPDOTest::isPDOMySQLnd(); - test_meta($db, 20, 'BIT(8)', 1, 'BIT', ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); - test_meta($db, 30, 'TINYINT', -127, 'TINY', ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); - test_meta($db, 40, 'TINYINT UNSIGNED', 255, 'TINY', ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); - test_meta($db, 50, 'BOOLEAN', 1, NULL, ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); + test_meta($db, 20, 'BIT(8)', 1, 'BIT', PDO::PARAM_INT); + test_meta($db, 30, 'TINYINT', -127, 'TINY', PDO::PARAM_INT); + test_meta($db, 40, 'TINYINT UNSIGNED', 255, 'TINY', PDO::PARAM_INT); + test_meta($db, 50, 'BOOLEAN', 1, NULL, PDO::PARAM_INT); - test_meta($db, 60, 'SMALLINT', -32768, 'SHORT', ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); - test_meta($db, 70, 'SMALLINT UNSIGNED', 65535, 'SHORT', ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); + test_meta($db, 60, 'SMALLINT', -32768, 'SHORT', PDO::PARAM_INT); + test_meta($db, 70, 'SMALLINT UNSIGNED', 65535, 'SHORT', PDO::PARAM_INT); - test_meta($db, 80, 'MEDIUMINT', -8388608, 'INT24', ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); - test_meta($db, 90, 'MEDIUMINT UNSIGNED', 16777215, 'INT24', ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); + test_meta($db, 80, 'MEDIUMINT', -8388608, 'INT24', PDO::PARAM_INT); + test_meta($db, 90, 'MEDIUMINT UNSIGNED', 16777215, 'INT24', PDO::PARAM_INT); - test_meta($db, 100, 'INT', -2147483648, 'LONG', ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); - test_meta($db, 110, 'INT UNSIGNED', 4294967295, 'LONG', ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); + test_meta($db, 100, 'INT', -2147483648, 'LONG', PDO::PARAM_INT); + test_meta($db, 110, 'INT UNSIGNED', 4294967295, 'LONG', PDO::PARAM_INT); - test_meta($db, 120, 'BIGINT', '-9223372036854775808', 'LONGLONG', ($is_mysqlnd) ? ((PHP_INT_SIZE == 4) ? PDO::PARAM_STR : PDO::PARAM_INT) : PDO::PARAM_STR); - test_meta($db, 130, 'BIGINT UNSIGNED', '18446744073709551615', 'LONGLONG', ($is_mysqlnd) ? ((PHP_INT_SIZE == 4) ? PDO::PARAM_STR : PDO::PARAM_INT) : PDO::PARAM_STR); + test_meta($db, 120, 'BIGINT', '-9223372036854775808', 'LONGLONG', (PHP_INT_SIZE == 4) ? PDO::PARAM_STR : PDO::PARAM_INT); + test_meta($db, 130, 'BIGINT UNSIGNED', '18446744073709551615', 'LONGLONG', (PHP_INT_SIZE == 4) ? PDO::PARAM_STR : PDO::PARAM_INT); test_meta($db, 130, 'REAL', -1.01, ($real_as_float) ? 'FLOAT' : 'DOUBLE', PDO::PARAM_STR); test_meta($db, 140, 'REAL UNSIGNED', 1.01, ($real_as_float) ? 'FLOAT' : 'DOUBLE', PDO::PARAM_STR); @@ -186,7 +185,7 @@ try { test_meta($db, 340, 'TIME', '14:37:00', 'TIME', PDO::PARAM_STR); test_meta($db, 350, 'TIMESTAMP', '2008-03-23 14:38:00', 'TIMESTAMP', PDO::PARAM_STR); test_meta($db, 360, 'DATETIME', '2008-03-23 14:38:00', 'DATETIME', PDO::PARAM_STR); - test_meta($db, 370, 'YEAR', '2008', 'YEAR', ($is_mysqlnd) ? PDO::PARAM_INT : PDO::PARAM_STR); + test_meta($db, 370, 'YEAR', '2008', 'YEAR', PDO::PARAM_INT); test_meta($db, 380, 'CHAR(1)', 'a', 'STRING', PDO::PARAM_STR); test_meta($db, 390, 'CHAR(10)', '0123456789', 'STRING', PDO::PARAM_STR); |