Skip to content

Commit 4523105

Browse files
committed
sqlite3: Fix additional issues introduced by bug 64531 fix
Fixes an existing issue tha was surfaced in #5204 where re-using a variable to get the result of a statement on the second round of execution would cause a spurious reset call to be made to SQLite between execute() and fetchArray() being called. This was previously not changing the behaviour, as reset was already being called too many times, but it did mean the bug was not fixed for all potential scenarios.
1 parent 3553d71 commit 4523105

7 files changed

+172
-8
lines changed

ext/sqlite3/sqlite3.c

+11-6
Original file line numberDiff line numberDiff line change
@@ -596,8 +596,10 @@ PHP_METHOD(SQLite3, query)
596596
result->stmt_obj->last_step_result = return_code;
597597

598598
switch (return_code) {
599-
case SQLITE_ROW: /* Valid Row */
600599
case SQLITE_DONE: /* Valid but no results */
600+
sqlite3_reset(result->stmt_obj->stmt);
601+
ZEND_FALLTHROUGH;
602+
case SQLITE_ROW: /* Valid Row */
601603
{
602604
php_sqlite3_free_list *free_item;
603605
free_item = emalloc(sizeof(php_sqlite3_free_list));
@@ -1791,8 +1793,10 @@ PHP_METHOD(SQLite3Stmt, execute)
17911793
stmt_obj->last_step_result = return_code;
17921794

17931795
switch (return_code) {
1794-
case SQLITE_ROW: /* Valid Row */
17951796
case SQLITE_DONE: /* Valid but no results */
1797+
sqlite3_reset(stmt_obj->stmt);
1798+
ZEND_FALLTHROUGH;
1799+
case SQLITE_ROW: /* Valid Row */
17961800
{
17971801
object_init_ex(return_value, php_sqlite3_result_entry);
17981802
result = Z_SQLITE3_RESULT_P(return_value);
@@ -2284,12 +2288,13 @@ static void php_sqlite3_result_object_free_storage(zend_object *object) /* {{{ *
22842288
sqlite3result_clear_column_names_cache(intern);
22852289

22862290
if (!Z_ISNULL(intern->stmt_obj_zval)) {
2291+
zval_ptr_dtor(&intern->stmt_obj_zval);
22872292
if (intern->stmt_obj && intern->stmt_obj->initialised) {
2288-
sqlite3_reset(intern->stmt_obj->stmt);
2289-
intern->stmt_obj->has_stepped = 0;
2293+
if (GC_REFCOUNT(&intern->stmt_obj->zo) == 0) {
2294+
sqlite3_reset(intern->stmt_obj->stmt);
2295+
intern->stmt_obj->has_stepped = 0;
2296+
}
22902297
}
2291-
2292-
zval_ptr_dtor(&intern->stmt_obj_zval);
22932298
}
22942299

22952300
zend_object_std_dtor(&intern->zo);

ext/sqlite3/tests/bug64531_1.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
Bug #64531 (SQLite3Result::fetchArray runs the query again)
2+
Bug #64531 (SQLite3Result::fetchArray runs the query again) - SQLite3->query
33
--SKIPIF--
44
<?php
55
if (!extension_loaded('sqlite3')) die('skip sqlite3 extension not available');

ext/sqlite3/tests/bug64531_2.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
Bug #64531 (SQLite3Result::fetchArray runs the query again)
2+
Bug #64531 (SQLite3Result::fetchArray runs the query again) - SQLite3->query
33
--SKIPIF--
44
<?php
55
if (!extension_loaded('sqlite3')) die('skip sqlite3 extension not available');

ext/sqlite3/tests/bug64531_3.phpt

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
Bug #64531 (SQLite3Result::fetchArray runs the query again) - SQLite3Stmt->execute
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('sqlite3')) die('skip sqlite3 extension not available');
6+
?>
7+
--FILE--
8+
<?php
9+
$conn = new SQLite3(':memory:');
10+
$conn->exec("CREATE TABLE foo (id INT)");
11+
12+
$stmt = $conn->prepare("INSERT INTO foo VALUES (1)");
13+
$res = $stmt->execute();
14+
$res->fetchArray();
15+
$res->fetchArray();
16+
17+
$res = $conn->query("SELECT * FROM foo");
18+
while (($row = $res->fetchArray(SQLITE3_NUM))) {
19+
var_dump($row);
20+
}
21+
?>
22+
--EXPECT--
23+
array(1) {
24+
[0]=>
25+
int(1)
26+
}

ext/sqlite3/tests/bug64531_4.phpt

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
--TEST--
2+
Bug #64531 (SQLite3Result::fetchArray runs the query again) - SQLite3Stmt->execute (reused)
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('sqlite3')) die('skip sqlite3 extension not available');
6+
?>
7+
--FILE--
8+
<?php
9+
$conn = new SQLite3(':memory:');
10+
$conn->exec("CREATE TABLE foo (id INT)");
11+
12+
function testing($val) {
13+
echo $val;
14+
debug_print_backtrace();
15+
return $val;
16+
}
17+
18+
$conn->createFunction('testing', 'testing', 1);
19+
20+
$stmt = $conn->prepare("INSERT INTO foo VALUES (testing(?))");
21+
22+
$stmt->bindValue(1, 1);
23+
24+
$res = $stmt->execute(); // Should run
25+
$res->fetchArray(); // Should not run
26+
$res->fetchArray(); // Should not run
27+
$res->fetchArray(); // Should not run
28+
29+
$stmt->bindValue(1, 2);
30+
31+
$res = $stmt->execute(); // Should run
32+
$res->fetchArray(); // Should not run
33+
$res->fetchArray(); // Should not run
34+
$res->fetchArray(); // Should not run
35+
36+
$stmt->bindValue(1, 3);
37+
38+
$res2 = $stmt->execute(); // Should run
39+
$res2->fetchArray(); // Should not run
40+
$res2->fetchArray(); // Should not run
41+
$res2->fetchArray(); // Should not run
42+
43+
$res = $conn->query("SELECT * FROM foo");
44+
while (($row = $res->fetchArray(SQLITE3_NUM))) {
45+
var_dump($row);
46+
}
47+
?>
48+
--EXPECT--
49+
1#0 [internal function]: testing(1)
50+
#1 /home/mark/Code/m-php-src/ext/sqlite3/tests/bug64531_4.php(17): SQLite3Stmt->execute()
51+
2#0 [internal function]: testing(2)
52+
#1 /home/mark/Code/m-php-src/ext/sqlite3/tests/bug64531_4.php(24): SQLite3Stmt->execute()
53+
3#0 [internal function]: testing(3)
54+
#1 /home/mark/Code/m-php-src/ext/sqlite3/tests/bug64531_4.php(31): SQLite3Stmt->execute()
55+
array(1) {
56+
[0]=>
57+
int(1)
58+
}
59+
array(1) {
60+
[0]=>
61+
int(2)
62+
}
63+
array(1) {
64+
[0]=>
65+
int(3)
66+
}

ext/sqlite3/tests/bug64531_5.phpt

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
Bug #64531 (SQLite3Result::fetchArray runs the query again) - SQLite3Stmt->prepare
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('sqlite3')) die('skip sqlite3 extension not available');
6+
?>
7+
--FILE--
8+
<?php
9+
function testing($val) {
10+
echo "Testing with: $val\n";
11+
return true;
12+
}
13+
14+
$conn = new SQLite3(':memory:');
15+
$conn->exec("CREATE TABLE foo (id INT)");
16+
for ($i = 1; $i <= 3; $i++) {
17+
$conn->exec("INSERT INTO foo VALUES ($i)");
18+
}
19+
$conn->createFunction('testing', 'testing', 1);
20+
21+
$stmt = $conn->prepare("SELECT * FROM foo WHERE testing(id)");
22+
$res = $stmt->execute();
23+
$arr = $res->fetchArray();
24+
$arr = $res->fetchArray();
25+
$arr = $res->fetchArray();
26+
$arr = $res->fetchArray();
27+
?>
28+
--EXPECT--
29+
Testing with: 1
30+
Testing with: 2
31+
Testing with: 3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
SQLite3 extended error code Function
3+
--EXTENSIONS--
4+
sqlite3
5+
--FILE--
6+
<?php
7+
8+
require_once(__DIR__ . '/new_db.inc');
9+
10+
$db->query("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )");
11+
12+
echo "Inserting first time which should succeed" . PHP_EOL;
13+
$result = $db->prepare("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)")->execute();
14+
echo "First Error Code: " . $db->lastErrorCode() . PHP_EOL;
15+
16+
echo "Inserting second time which should fail" . PHP_EOL;
17+
$result = $db->prepare("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)")->execute();
18+
echo "Second Error Code: " . $db->lastErrorCode() . PHP_EOL;
19+
echo "Second Extended Error Code: " . $db->lastExtendedErrorCode() . PHP_EOL;
20+
21+
echo "Closing database\n";
22+
var_dump($db->close());
23+
echo "Done" . PHP_EOL;
24+
?>
25+
--EXPECTF--
26+
Inserting first time which should succeed
27+
First Error Code: 0
28+
Inserting second time which should fail
29+
30+
Warning: SQLite3Stmt::execute(): Unable to execute statement: UNIQUE constraint failed: dog.id in %s on line %d
31+
Second Error Code: 19
32+
Second Extended Error Code: 1555
33+
Closing database
34+
bool(true)
35+
Done
36+

0 commit comments

Comments
 (0)