Skip to content

Commit

Permalink
Fix bug #78192 PDO SQLite SegFault when reuse statement after schema …
Browse files Browse the repository at this point in the history
…has changed

Reset stmt->columns when column count changed on new execution of prepared statement
  • Loading branch information
vincent4vx authored and cmb69 committed Jun 28, 2019
1 parent 7d28a24 commit 05c00a8
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 2 deletions.
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
44 changes: 42 additions & 2 deletions ext/pdo_sqlite/sqlite_statement.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
46 changes: 46 additions & 0 deletions ext/pdo_sqlite/tests/bug78192.phpt
Original file line number Diff line number Diff line change
@@ -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
}
}

0 comments on commit 05c00a8

Please sign in to comment.