Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bug #78192 PDO SQLite reset columns on schema changed #4313

Closed
wants to merge 1 commit into from
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 42 additions & 2 deletions ext/pdo_sqlite/sqlite_statement.c
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
@@ -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
}
}