mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Merge branch 'PHP-8.0'
* PHP-8.0: Handle column count change in PDO MySQL
This commit is contained in:
commit
0ac8518d76
5 changed files with 108 additions and 77 deletions
|
@ -173,6 +173,45 @@ int pdo_stmt_describe_columns(pdo_stmt_t *stmt) /* {{{ */
|
|||
}
|
||||
/* }}} */
|
||||
|
||||
static void pdo_stmt_reset_columns(pdo_stmt_t *stmt) {
|
||||
if (stmt->columns) {
|
||||
int i;
|
||||
struct pdo_column_data *cols = stmt->columns;
|
||||
|
||||
for (i = 0; i < stmt->column_count; i++) {
|
||||
if (cols[i].name) {
|
||||
zend_string_release_ex(cols[i].name, 0);
|
||||
}
|
||||
}
|
||||
efree(stmt->columns);
|
||||
}
|
||||
stmt->columns = NULL;
|
||||
stmt->column_count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the column count on the statement. If it differs from the previous one,
|
||||
* discard existing columns information.
|
||||
*/
|
||||
PDO_API void php_pdo_stmt_set_column_count(pdo_stmt_t *stmt, int new_count)
|
||||
{
|
||||
/* Columns not yet "described". */
|
||||
if (!stmt->columns) {
|
||||
stmt->column_count = new_count;
|
||||
return;
|
||||
}
|
||||
|
||||
/* The column count has not changed: No need to reload columns description.
|
||||
* Note: Do not handle attribute name change, without column count change. */
|
||||
if (new_count == stmt->column_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Free previous columns to force reload description. */
|
||||
pdo_stmt_reset_columns(stmt);
|
||||
stmt->column_count = new_count;
|
||||
}
|
||||
|
||||
static void get_lazy_object(pdo_stmt_t *stmt, zval *return_value) /* {{{ */
|
||||
{
|
||||
if (Z_ISUNDEF(stmt->lazy_object_ref)) {
|
||||
|
@ -1910,20 +1949,7 @@ PHP_METHOD(PDOStatement, setFetchMode)
|
|||
|
||||
static bool pdo_stmt_do_next_rowset(pdo_stmt_t *stmt)
|
||||
{
|
||||
/* un-describe */
|
||||
if (stmt->columns) {
|
||||
int i;
|
||||
struct pdo_column_data *cols = stmt->columns;
|
||||
|
||||
for (i = 0; i < stmt->column_count; i++) {
|
||||
if (cols[i].name) {
|
||||
zend_string_release_ex(cols[i].name, 0);
|
||||
}
|
||||
}
|
||||
efree(stmt->columns);
|
||||
stmt->columns = NULL;
|
||||
stmt->column_count = 0;
|
||||
}
|
||||
pdo_stmt_reset_columns(stmt);
|
||||
|
||||
if (!stmt->methods->next_rowset(stmt)) {
|
||||
/* Set the executed flag to 0 to reallocate columns on next execute */
|
||||
|
@ -2156,19 +2182,7 @@ PDO_API void php_pdo_free_statement(pdo_stmt_t *stmt)
|
|||
efree(stmt->query_string);
|
||||
}
|
||||
|
||||
if (stmt->columns) {
|
||||
int i;
|
||||
struct pdo_column_data *cols = stmt->columns;
|
||||
|
||||
for (i = 0; i < stmt->column_count; i++) {
|
||||
if (cols[i].name) {
|
||||
zend_string_release_ex(cols[i].name, 0);
|
||||
cols[i].name = NULL;
|
||||
}
|
||||
}
|
||||
efree(stmt->columns);
|
||||
stmt->columns = NULL;
|
||||
}
|
||||
pdo_stmt_reset_columns(stmt);
|
||||
|
||||
if (!Z_ISUNDEF(stmt->fetch.into) && stmt->default_fetch_type == PDO_FETCH_INTO) {
|
||||
zval_ptr_dtor(&stmt->fetch.into);
|
||||
|
|
|
@ -695,7 +695,7 @@ PDO_API void php_pdo_dbh_addref(pdo_dbh_t *dbh);
|
|||
PDO_API void php_pdo_dbh_delref(pdo_dbh_t *dbh);
|
||||
|
||||
PDO_API void php_pdo_free_statement(pdo_stmt_t *stmt);
|
||||
|
||||
PDO_API void php_pdo_stmt_set_column_count(pdo_stmt_t *stmt, int new_count);
|
||||
|
||||
PDO_API void pdo_throw_exception(unsigned int driver_errcode, char *driver_errmsg, pdo_error_type *pdo_error);
|
||||
#endif /* PHP_PDO_DRIVER_H */
|
||||
|
|
|
@ -144,7 +144,7 @@ static int pdo_mysql_fill_stmt_from_result(pdo_stmt_t *stmt) /* {{{ */
|
|||
}
|
||||
|
||||
stmt->row_count = (zend_long) mysql_num_rows(S->result);
|
||||
stmt->column_count = (int) mysql_num_fields(S->result);
|
||||
php_pdo_stmt_set_column_count(stmt, (int) mysql_num_fields(S->result));
|
||||
S->fields = mysql_fetch_fields(S->result);
|
||||
} else {
|
||||
/* this was a DML or DDL query (INSERT, UPDATE, DELETE, ... */
|
||||
|
@ -194,7 +194,7 @@ static int pdo_mysql_stmt_execute_prepared_libmysql(pdo_stmt_t *stmt) /* {{{ */
|
|||
efree(S->out_length);
|
||||
}
|
||||
|
||||
stmt->column_count = (int)mysql_num_fields(S->result);
|
||||
php_pdo_stmt_set_column_count(stmt, (int)mysql_num_fields(S->result));
|
||||
S->bound_result = ecalloc(stmt->column_count, sizeof(MYSQL_BIND));
|
||||
S->out_null = ecalloc(stmt->column_count, sizeof(my_bool));
|
||||
S->out_length = ecalloc(stmt->column_count, sizeof(zend_ulong));
|
||||
|
@ -290,7 +290,7 @@ static int pdo_mysql_stmt_execute_prepared_mysqlnd(pdo_stmt_t *stmt) /* {{{ */
|
|||
}
|
||||
|
||||
/* for SHOW/DESCRIBE and others the column/field count is not available before execute */
|
||||
stmt->column_count = mysql_stmt_field_count(S->stmt);
|
||||
php_pdo_stmt_set_column_count(stmt, mysql_stmt_field_count(S->stmt));
|
||||
for (i = 0; i < stmt->column_count; i++) {
|
||||
mysqlnd_stmt_bind_one_result(S->stmt, i);
|
||||
}
|
||||
|
@ -378,7 +378,7 @@ static int pdo_mysql_stmt_next_rowset(pdo_stmt_t *stmt) /* {{{ */
|
|||
/* for SHOW/DESCRIBE and others the column/field count is not available before execute */
|
||||
int i;
|
||||
|
||||
stmt->column_count = mysql_stmt_field_count(S->stmt);
|
||||
php_pdo_stmt_set_column_count(stmt, mysql_stmt_field_count(S->stmt));
|
||||
for (i = 0; i < stmt->column_count; i++) {
|
||||
mysqlnd_stmt_bind_one_result(S->stmt, i);
|
||||
}
|
||||
|
@ -407,9 +407,6 @@ static int pdo_mysql_stmt_next_rowset(pdo_stmt_t *stmt) /* {{{ */
|
|||
/* ensure that we free any previous unfetched results */
|
||||
#ifndef PDO_USE_MYSQLND
|
||||
if (S->stmt) {
|
||||
if (S->result) {
|
||||
stmt->column_count = (int)mysql_num_fields(S->result);
|
||||
}
|
||||
mysql_stmt_free_result(S->stmt);
|
||||
}
|
||||
#endif
|
||||
|
|
60
ext/pdo_mysql/tests/change_column_count.phpt
Normal file
60
ext/pdo_mysql/tests/change_column_count.phpt
Normal file
|
@ -0,0 +1,60 @@
|
|||
--TEST--
|
||||
Change column count after statement has been prepared
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) die('skip not loaded');
|
||||
require_once(__DIR__ . DIRECTORY_SEPARATOR . 'skipif.inc');
|
||||
require_once(__DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
|
||||
MySQLPDOTest::skip();
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once(__DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
|
||||
|
||||
$db = MySQLPDOTest::factory();
|
||||
$db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$db->exec('DROP TABLE IF EXISTS test');
|
||||
$db->exec('CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(255) NOT NULL)');
|
||||
|
||||
$stmt = $db->prepare('INSERT INTO test (id, name) VALUES(:id, :name)');
|
||||
$stmt->execute([
|
||||
'id' => 10,
|
||||
'name' => 'test',
|
||||
]);
|
||||
|
||||
$stmt = $db->prepare('SELECT * FROM test WHERE id = :id');
|
||||
$stmt->execute(['id' => 10]);
|
||||
var_dump($stmt->fetchAll(\PDO::FETCH_ASSOC));
|
||||
|
||||
$db->exec('ALTER TABLE test ADD new_col VARCHAR(255)');
|
||||
$stmt->execute(['id' => 10]);
|
||||
var_dump($stmt->fetchAll(\PDO::FETCH_ASSOC));
|
||||
|
||||
?>
|
||||
--CLEAN--
|
||||
<?php
|
||||
require __DIR__ . '/mysql_pdo_test.inc';
|
||||
MySQLPDOTest::dropTestTable();
|
||||
?>
|
||||
--EXPECT--
|
||||
array(1) {
|
||||
[0]=>
|
||||
array(2) {
|
||||
["id"]=>
|
||||
string(2) "10"
|
||||
["name"]=>
|
||||
string(4) "test"
|
||||
}
|
||||
}
|
||||
array(1) {
|
||||
[0]=>
|
||||
array(3) {
|
||||
["id"]=>
|
||||
string(2) "10"
|
||||
["name"]=>
|
||||
string(4) "test"
|
||||
["new_col"]=>
|
||||
NULL
|
||||
}
|
||||
}
|
|
@ -39,46 +39,6 @@ static int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the column count on the statement.
|
||||
*
|
||||
* Since PHP 7.2 sqlite3_prepare_v2 is used which auto recompile prepared statement on schema change.
|
||||
* Instead of raise an error on schema change, the result set will change, and the statement's columns must be updated.
|
||||
*
|
||||
* See bug #78192
|
||||
*/
|
||||
static void pdo_sqlite_stmt_set_column_count(pdo_stmt_t *stmt, int new_count)
|
||||
{
|
||||
/* Columns not yet "described" */
|
||||
if (!stmt->columns) {
|
||||
stmt->column_count = new_count;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The column count has not changed : no need to reload columns description
|
||||
* Note: Do not handle attribute name change, without column count change
|
||||
*/
|
||||
if (new_count == stmt->column_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Free previous columns to force reload description */
|
||||
int i;
|
||||
|
||||
for (i = 0; i < stmt->column_count; i++) {
|
||||
if (stmt->columns[i].name) {
|
||||
zend_string_release(stmt->columns[i].name);
|
||||
stmt->columns[i].name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
efree(stmt->columns);
|
||||
stmt->columns = NULL;
|
||||
stmt->column_count = new_count;
|
||||
}
|
||||
|
||||
static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt)
|
||||
{
|
||||
pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;
|
||||
|
@ -91,11 +51,11 @@ static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt)
|
|||
switch (sqlite3_step(S->stmt)) {
|
||||
case SQLITE_ROW:
|
||||
S->pre_fetched = 1;
|
||||
pdo_sqlite_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt));
|
||||
php_pdo_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt));
|
||||
return 1;
|
||||
|
||||
case SQLITE_DONE:
|
||||
pdo_sqlite_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt));
|
||||
php_pdo_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt));
|
||||
stmt->row_count = sqlite3_changes(S->H->db);
|
||||
sqlite3_reset(S->stmt);
|
||||
S->done = 1;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue