summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/pdo/pdo_stmt.c68
-rw-r--r--ext/pdo/php_pdo_driver.h2
-rw-r--r--ext/pdo_mysql/mysql_statement.c11
-rw-r--r--ext/pdo_mysql/tests/change_column_count.phpt60
-rw-r--r--ext/pdo_sqlite/sqlite_statement.c44
5 files changed, 108 insertions, 77 deletions
diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c
index f8ff90ba9b..d4c6086690 100644
--- a/ext/pdo/pdo_stmt.c
+++ b/ext/pdo/pdo_stmt.c
@@ -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);
diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h
index 960ddec4ef..2ee8acdd56 100644
--- a/ext/pdo/php_pdo_driver.h
+++ b/ext/pdo/php_pdo_driver.h
@@ -690,7 +690,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 */
diff --git a/ext/pdo_mysql/mysql_statement.c b/ext/pdo_mysql/mysql_statement.c
index 0d1cd348c7..c8a6a218ab 100644
--- a/ext/pdo_mysql/mysql_statement.c
+++ b/ext/pdo_mysql/mysql_statement.c
@@ -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
diff --git a/ext/pdo_mysql/tests/change_column_count.phpt b/ext/pdo_mysql/tests/change_column_count.phpt
new file mode 100644
index 0000000000..5bb521d300
--- /dev/null
+++ b/ext/pdo_mysql/tests/change_column_count.phpt
@@ -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
+ }
+}
diff --git a/ext/pdo_sqlite/sqlite_statement.c b/ext/pdo_sqlite/sqlite_statement.c
index 64a90e0ede..3769b1e049 100644
--- a/ext/pdo_sqlite/sqlite_statement.c
+++ b/ext/pdo_sqlite/sqlite_statement.c
@@ -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;