Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
}