Edit report at https://bugs.php.net/bug.php?id=64531&edit=1
ID: 64531
Patch added by: cmb@php.net
Reported by: phplists at stanvassilev dot com
Summary: SQLite3Result::fetchArray runs the query again.
Status: Verified
Type: Bug
Package: SQLite related
Operating System: Windows XP SP3 32bit
PHP Version: 5.4.13
Block user comment: N
Private report: N
New Comment:
The following pull request has been associated:
Patch Name: [POC] Fix #64531: SQLite3Result::fetchArray runs the query again
On GitHub: https://github.com/php/php-src/pull/5204
Patch: https://github.com/php/php-src/pull/5204.patch
Previous Comments:
------------------------------------------------------------------------
[2020-02-01 00:13:34] antoni at friki dot cat
The following patch has been added/updated:
Patch Name: fix64531-sqlite3-fetchArray-skipNoColumns
Revision: 1580516014
URL: https://bugs.php.net/patch-display.php?bug=64531&patch=fix64531-sqlite3-fetchArray-skipNoColumns&revision=1580516014
------------------------------------------------------------------------
[2020-01-31 23:23:44] antoni at friki dot cat
I think that small patch should fix most problems on fetchArray() usage.
It's not a breaking change as no one expects re-run insert/update/create statements using a
function to acquire a row of data. IMAO
--- php-7.4.2/ext/sqlite3/sqlite3.c 2020-01-21 12:35:21.000000000 +0100
+++ php-7.4.2-sqlite3fixed2/ext/sqlite3/sqlite3.c 2020-01-31 23:37:17.449123405 +0100
@@ -2007,6 +2007,10 @@ PHP_METHOD(sqlite3result, fetchArray)
return;
}
+ if (sqlite3_column_count(result_obj->stmt_obj->stmt) == 0) {
+ return;
+ }
+
ret = sqlite3_step(result_obj->stmt_obj->stmt);
switch (ret) {
case SQLITE_ROW:
------------------------------------------------------------------------
[2020-01-31 22:20:19] antoni at friki dot cat
Here an PHP example reproducing the weird issue.
Find some commented out alternatives to avoid problems with current php-sqlite3 implementation:
https://pastebin.com/kCvb65FH
The key point using current SQLite3 version is not to call sqlite3_step() on a statement if previous
sqlite3_step() over same statement returned !=SQLITE_ROW.
The key point using current PHP version is not to call fetchArray() on query/execute $result. Using
if numColumns()!==0 you can figure out if you want to run fetchArray() data after execute()/query().
Regards,
------------------------------------------------------------------------
[2020-01-31 21:20:35] antoni at friki dot cat
Hi,
today I've faced that problem.
Here my patch:
--- php-7.4.2/ext/sqlite3/sqlite3.c 2020-01-21 12:35:21.000000000 +0100
+++ php-7.4.2-sqlite3fixed/ext/sqlite3/sqlite3.c 2020-01-31 22:12:56.599653191 +0100
@@ -612,8 +612,11 @@
return_code = sqlite3_step(result->stmt_obj->stmt);
switch (return_code) {
- case SQLITE_ROW: /* Valid Row */
case SQLITE_DONE: /* Valid but no results */
+ {
+ result->complete = 1;
+ }
+ case SQLITE_ROW: /* Valid Row */
{
php_sqlite3_free_list *free_item;
free_item = emalloc(sizeof(php_sqlite3_free_list));
@@ -624,6 +627,7 @@
break;
}
default:
+ result->complete = 1;
if (!EG(exception)) {
php_sqlite3_error(db_obj, "Unable to execute statement: %s",
sqlite3_errmsg(db_obj->db));
}
@@ -713,6 +717,9 @@
return_code = sqlite3_step(stmt);
switch (return_code) {
+ php_sqlite3_result *result_obj;
+ zval *object = ZEND_THIS;
+ result_obj = Z_SQLITE3_RESULT_P(object);
case SQLITE_ROW: /* Valid Row */
{
if (!entire_row) {
@@ -730,6 +737,8 @@
}
case SQLITE_DONE: /* Valid but no results */
{
+ result_obj->complete = 1;
+
if (!entire_row) {
RETVAL_NULL();
} else {
@@ -738,10 +747,12 @@
break;
}
default:
- if (!EG(exception)) {
- php_sqlite3_error(db_obj, "Unable to execute statement: %s",
sqlite3_errmsg(db_obj->db));
- }
- RETVAL_FALSE;
+ result_obj->complete = 1;
+
+ if (!EG(exception)) {
+ php_sqlite3_error(db_obj, "Unable to execute statement: %s",
sqlite3_errmsg(db_obj->db));
+ }
+ RETVAL_FALSE;
}
sqlite3_finalize(stmt);
}
@@ -1844,14 +1855,16 @@
return_code = sqlite3_step(stmt_obj->stmt);
+ sqlite3_reset(stmt_obj->stmt);
+ object_init_ex(return_value, php_sqlite3_result_entry);
+ result = Z_SQLITE3_RESULT_P(return_value);
switch (return_code) {
- case SQLITE_ROW: /* Valid Row */
case SQLITE_DONE: /* Valid but no results */
+ {
+ result->complete = 1;
+ }
+ case SQLITE_ROW: /* Valid Row */
{
- sqlite3_reset(stmt_obj->stmt);
- object_init_ex(return_value, php_sqlite3_result_entry);
- result = Z_SQLITE3_RESULT_P(return_value);
-
result->is_prepared_statement = 1;
result->db_obj = stmt_obj->db_obj;
result->stmt_obj = stmt_obj;
@@ -1861,9 +1874,11 @@
break;
}
case SQLITE_ERROR:
+ result->complete = 1;
sqlite3_reset(stmt_obj->stmt);
default:
+ result->complete = 1;
if (!EG(exception)) {
php_sqlite3_error(stmt_obj->db_obj, "Unable to execute statement: %s",
sqlite3_errmsg(sqlite3_db_handle(stmt_obj->stmt)));
}
@@ -1939,6 +1954,10 @@
return;
}
+ if (result_obj->complete) {
+ RETURN_FALSE;
+ }
+
RETURN_LONG(sqlite3_column_count(result_obj->stmt_obj->stmt));
}
/* }}} */
@@ -1958,6 +1977,11 @@
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &column) == FAILURE) {
return;
}
+
+ if (result_obj->complete) {
+ RETURN_FALSE;
+ }
+
column_name = (char*) sqlite3_column_name(result_obj->stmt_obj->stmt, column);
if (column_name == NULL) {
@@ -2007,6 +2031,10 @@
return;
}
+ if (result_obj->complete==1) {
+ return;
+ }
+
ret = sqlite3_step(result_obj->stmt_obj->stmt);
switch (ret) {
case SQLITE_ROW:
@@ -2043,6 +2071,7 @@
break;
default:
+ result_obj->complete = 1;
php_sqlite3_error(result_obj->db_obj, "Unable to execute statement: %s",
sqlite3_errmsg(sqlite3_db_handle(result_obj->stmt_obj->stmt)));
}
}
------------------------------------------------------------------------
[2019-11-11 13:17:01] cmb@php.net
Related To: Bug #77977
------------------------------------------------------------------------
The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at
https://bugs.php.net/bug.php?id=64531
--
Edit this bug report at https://bugs.php.net/bug.php?id=64531&edit=1