Skip to content

Commit 05c00a8

Browse files
vincent4vxcmb69
authored andcommitted
Fix bug #78192 PDO SQLite SegFault when reuse statement after schema has changed
Reset stmt->columns when column count changed on new execution of prepared statement
1 parent 7d28a24 commit 05c00a8

File tree

3 files changed

+92
-2
lines changed

3 files changed

+92
-2
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? 2019, PHP 7.2.21
44

5+
- PDO_Sqlite:
6+
. Fixed #78192 (SegFault when reuse statement after schema has changed).
7+
(Vincent Quatrevieux)
8+
59
- XMLRPC:
610
. Fixed #78173 (XML-RPC mutates immutable objects during encoding). (Asher
711
Baker)

ext/pdo_sqlite/sqlite_statement.c

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,46 @@ static int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt)
4343
return 1;
4444
}
4545

46+
/**
47+
* Change the column count on the statement.
48+
*
49+
* Since PHP 7.2 sqlite3_prepare_v2 is used which auto recompile prepared statement on schema change.
50+
* Instead of raise an error on schema change, the result set will change, and the statement's columns must be updated.
51+
*
52+
* See bug #78192
53+
*/
54+
static void pdo_sqlite_stmt_set_column_count(pdo_stmt_t *stmt, int new_count)
55+
{
56+
/* Columns not yet "described" */
57+
if (!stmt->columns) {
58+
stmt->column_count = new_count;
59+
60+
return;
61+
}
62+
63+
/*
64+
* The column count has not changed : no need to reload columns description
65+
* Note: Do not handle attribute name change, without column count change
66+
*/
67+
if (new_count == stmt->column_count) {
68+
return;
69+
}
70+
71+
/* Free previous columns to force reload description */
72+
int i;
73+
74+
for (i = 0; i < stmt->column_count; i++) {
75+
if (stmt->columns[i].name) {
76+
zend_string_release(stmt->columns[i].name);
77+
stmt->columns[i].name = NULL;
78+
}
79+
}
80+
81+
efree(stmt->columns);
82+
stmt->columns = NULL;
83+
stmt->column_count = new_count;
84+
}
85+
4686
static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt)
4787
{
4888
pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;
@@ -55,11 +95,11 @@ static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt)
5595
switch (sqlite3_step(S->stmt)) {
5696
case SQLITE_ROW:
5797
S->pre_fetched = 1;
58-
stmt->column_count = sqlite3_data_count(S->stmt);
98+
pdo_sqlite_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt));
5999
return 1;
60100

61101
case SQLITE_DONE:
62-
stmt->column_count = sqlite3_column_count(S->stmt);
102+
pdo_sqlite_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt));
63103
stmt->row_count = sqlite3_changes(S->H->db);
64104
sqlite3_reset(S->stmt);
65105
S->done = 1;

ext/pdo_sqlite/tests/bug78192.phpt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
PDO SQLite Bug #78192 SegFault when reuse statement after schema change
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('pdo_sqlite')) print 'skip not loaded';
6+
?>
7+
--FILE--
8+
<?php
9+
$connection = new \PDO('sqlite::memory:');
10+
$connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
11+
$connection->query('CREATE TABLE user (id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(255) NOT NULL)');
12+
13+
$stmt = $connection->prepare('INSERT INTO user (id, name) VALUES(:id, :name)');
14+
$stmt->execute([
15+
'id' => 10,
16+
'name' => 'test',
17+
]);
18+
19+
$stmt = $connection->prepare('SELECT * FROM user WHERE id = :id');
20+
$stmt->execute(['id' => 10]);
21+
var_dump($stmt->fetchAll(\PDO::FETCH_ASSOC));
22+
23+
$connection->query('ALTER TABLE user ADD new_col VARCHAR(255)');
24+
$stmt->execute(['id' => 10]);
25+
var_dump($stmt->fetchAll(\PDO::FETCH_ASSOC));
26+
--EXPECT--
27+
array(1) {
28+
[0]=>
29+
array(2) {
30+
["id"]=>
31+
string(2) "10"
32+
["name"]=>
33+
string(4) "test"
34+
}
35+
}
36+
array(1) {
37+
[0]=>
38+
array(3) {
39+
["id"]=>
40+
string(2) "10"
41+
["name"]=>
42+
string(4) "test"
43+
["new_col"]=>
44+
NULL
45+
}
46+
}

0 commit comments

Comments
 (0)