From f133f0024ec801dc9636ee5bf84a93de1300d4b2 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Fri, 21 Feb 2020 13:24:37 +0100 Subject: [PATCH] Fix #79294: ::columnType() may fail after SQLite3Stmt::reset() The fix for feature request #53466 did not properly handle resetting of the corresponding statement; the problem with this is that the statement does not know about its result sets. But even if we could fix this, the `complete` handling still appears to be brittle, since the `sqlite3_column_type()`docs[1] state: | If the SQL statement does not currently point to a valid row, or if | the column index is out of range, the result is undefined. Fortunately, we can use `sqlite3_data_count()` instead, since[2]: | If prepared statement P does not have results ready to return (via | calls to the sqlite3_column() family of interfaces) then | sqlite3_data_count(P) returns 0. Thus, we guard `SQLite3::columnType()` with `sqlite3_data_count()`, and completely drop updating the `php_sqlite3_result_object.complete` field, but keep it for ABI BC purposes. [1] [2] --- NEWS | 3 +++ ext/sqlite3/php_sqlite3_structs.h | 2 +- ext/sqlite3/sqlite3.c | 5 +---- ext/sqlite3/tests/bug79294.phpt | 34 +++++++++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 ext/sqlite3/tests/bug79294.phpt diff --git a/NEWS b/NEWS index 4d154b2be9..c5eaa64e77 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,9 @@ PHP NEWS - PDO_ODBC: . Fixed bug #79038 (PDOStatement::nextRowset() leaks column values). (cmb) +- SQLite3: + . Fixed bug #79294 (::columnType() may fail after SQLite3Stmt::reset()). (cmb) + - Standard: . Fixed bug #79254 (getenv() w/o arguments not showing changes). (cmb) diff --git a/ext/sqlite3/php_sqlite3_structs.h b/ext/sqlite3/php_sqlite3_structs.h index bd65063906..5a25af61e1 100644 --- a/ext/sqlite3/php_sqlite3_structs.h +++ b/ext/sqlite3/php_sqlite3_structs.h @@ -108,7 +108,7 @@ struct _php_sqlite3_result_object { zval stmt_obj_zval; int is_prepared_statement; - int complete; + int complete; // unused zend_object zo; }; diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index 6ae049a740..c2b4fa8bca 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -1786,7 +1786,7 @@ PHP_METHOD(sqlite3result, columnType) return; } - if (result_obj->complete) { + if (!sqlite3_data_count(result_obj->stmt_obj->stmt)) { RETURN_FALSE; } @@ -1841,7 +1841,6 @@ PHP_METHOD(sqlite3result, fetchArray) break; case SQLITE_DONE: - result_obj->complete = 1; RETURN_FALSE; break; @@ -1869,8 +1868,6 @@ PHP_METHOD(sqlite3result, reset) RETURN_FALSE; } - result_obj->complete = 0; - RETURN_TRUE; } /* }}} */ diff --git a/ext/sqlite3/tests/bug79294.phpt b/ext/sqlite3/tests/bug79294.phpt new file mode 100644 index 0000000000..5538e3886b --- /dev/null +++ b/ext/sqlite3/tests/bug79294.phpt @@ -0,0 +1,34 @@ +--TEST-- +Bug #79294 ()::columnType() may fail after SQLite3Stmt::reset()) +--SKIPIF-- + +--FILE-- +exec("CREATE TABLE foo (bar INT)"); +$db->exec("INSERT INTO foo VALUES (1)"); + +$stmt = $db->prepare("SELECT * FROM foo"); +$res = $stmt->execute(); +var_dump($res->fetchArray() !== false); +var_dump($res->columnType(0)); +var_dump($res->fetchArray() !== false); +var_dump($res->columnType(0)); +$stmt->reset(); +var_dump($res->fetchArray() !== false); +var_dump($res->columnType(0)); +$res->reset(); +var_dump($res->fetchArray() !== false); +var_dump($res->columnType(0)); +?> +--EXPECT-- +bool(true) +int(1) +bool(false) +bool(false) +bool(true) +int(1) +bool(true) +int(1) -- 2.49.0