diff options
-rw-r--r-- | NEWS | 4 | ||||
-rw-r--r-- | ext/pdo_sqlite/sqlite_statement.c | 44 | ||||
-rw-r--r-- | ext/pdo_sqlite/tests/bug78192.phpt | 46 |
3 files changed, 92 insertions, 2 deletions
@@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? 2019, PHP 7.2.21 +- PDO_Sqlite: + . Fixed #78192 (SegFault when reuse statement after schema has changed). + (Vincent Quatrevieux) + - XMLRPC: . Fixed #78173 (XML-RPC mutates immutable objects during encoding). (Asher Baker) diff --git a/ext/pdo_sqlite/sqlite_statement.c b/ext/pdo_sqlite/sqlite_statement.c index 4437bbbde0..58811aee58 100644 --- a/ext/pdo_sqlite/sqlite_statement.c +++ b/ext/pdo_sqlite/sqlite_statement.c @@ -43,6 +43,46 @@ 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; @@ -55,11 +95,11 @@ static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt) switch (sqlite3_step(S->stmt)) { case SQLITE_ROW: S->pre_fetched = 1; - stmt->column_count = sqlite3_data_count(S->stmt); + pdo_sqlite_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt)); return 1; case SQLITE_DONE: - stmt->column_count = sqlite3_column_count(S->stmt); + pdo_sqlite_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; diff --git a/ext/pdo_sqlite/tests/bug78192.phpt b/ext/pdo_sqlite/tests/bug78192.phpt new file mode 100644 index 0000000000..dcf4b749be --- /dev/null +++ b/ext/pdo_sqlite/tests/bug78192.phpt @@ -0,0 +1,46 @@ +--TEST-- +PDO SQLite Bug #78192 SegFault when reuse statement after schema change +--SKIPIF-- +<?php +if (!extension_loaded('pdo_sqlite')) print 'skip not loaded'; +?> +--FILE-- +<?php +$connection = new \PDO('sqlite::memory:'); +$connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); +$connection->query('CREATE TABLE user (id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(255) NOT NULL)'); + +$stmt = $connection->prepare('INSERT INTO user (id, name) VALUES(:id, :name)'); +$stmt->execute([ + 'id' => 10, + 'name' => 'test', +]); + +$stmt = $connection->prepare('SELECT * FROM user WHERE id = :id'); +$stmt->execute(['id' => 10]); +var_dump($stmt->fetchAll(\PDO::FETCH_ASSOC)); + +$connection->query('ALTER TABLE user ADD new_col VARCHAR(255)'); +$stmt->execute(['id' => 10]); +var_dump($stmt->fetchAll(\PDO::FETCH_ASSOC)); +--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 + } +} |