From fb5b72859f42e852e24d487ca064407b677c4ff9 Mon Sep 17 00:00:00 2001 From: atman <57639310+HochMartinez@users.noreply.github.com> Date: Thu, 21 Apr 2022 13:24:10 +0200 Subject: [PATCH 1/4] Fixed DOUBLE EXECUTION OF QUERIES when executing first SQLite3Result->fetcharray() Steps to fix the bug: 1. Disabled sqlite3_reset(stmt_obj->stmt) in SQLite3::query() and SQLite3Stmt::execute(). sqlite3_reset(stmt_obj->stmt) forces a new execution of the query on the first execution of SQLite3Result::fetcharray(). 2. Added 'int last_error' to struct '_php_sqlite3_result_object'. last_error is used to store the returned value of the step command executed in SQLite3::query() and SQLite3Stmt::execute(), in order to pass this step return code to SQLite3Result::fetcharray(). 3. SQLite3Result::fetcharray() reads last_error, process it and only steps if it is equal to SQLITE_ROW. --- ext/sqlite3/sqlite3.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index f06d373c56f09..d628a55aad5fd 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -591,6 +591,10 @@ PHP_METHOD(SQLite3, query) ZVAL_OBJ(&result->stmt_obj_zval, Z_OBJ(stmt)); return_code = sqlite3_step(result->stmt_obj->stmt); + /* START */ + /* Copy step error code in result struct for SQLite3Result::fetcharray() */ + result->last_error = return_code; + /* END */ switch (return_code) { case SQLITE_ROW: /* Valid Row */ @@ -601,7 +605,11 @@ PHP_METHOD(SQLite3, query) free_item->stmt_obj = stmt_obj; free_item->stmt_obj_zval = stmt; zend_llist_add_element(&(db_obj->free_list), &free_item); - sqlite3_reset(result->stmt_obj->stmt); + /* START */ + /* Main problem. This resets the query and forces SQLite3Result::fetcharray() to execute it again */ + /* So we have to disable it to avoid a double execution of the query */ + /*sqlite3_reset(result->stmt_obj->stmt);*/ + /* END */ break; } default: @@ -1787,7 +1795,10 @@ PHP_METHOD(SQLite3Stmt, execute) case SQLITE_ROW: /* Valid Row */ case SQLITE_DONE: /* Valid but no results */ { - sqlite3_reset(stmt_obj->stmt); + /* START */ + /* Main problem. This resets the query and forces SQLite3Result::fetcharray() to execute it again */ + /*sqlite3_reset(stmt_obj->stmt);*/ + /* END */ object_init_ex(return_value, php_sqlite3_result_entry); result = Z_SQLITE3_RESULT_P(return_value); @@ -1796,6 +1807,10 @@ PHP_METHOD(SQLite3Stmt, execute) result->stmt_obj = stmt_obj; result->column_names = NULL; result->column_count = -1; + /* START */ + /* Do not reset the query just pass the step error code in result struct for SQLite3Result::fetcharray() */ + result->last_error = return_code; + /* END */ ZVAL_OBJ_COPY(&result->stmt_obj_zval, Z_OBJ_P(object)); break; @@ -1941,7 +1956,12 @@ PHP_METHOD(SQLite3Result, fetchArray) SQLITE3_CHECK_INITIALIZED(result_obj->db_obj, result_obj->stmt_obj->initialised, SQLite3Result) - ret = sqlite3_step(result_obj->stmt_obj->stmt); + /* START */ + /* Get result code stored in result struct, returned by Sqlite3::query(), SQLite3Stmt::execute() or previous SQLite3Result::fetchArray() */ + ret = result_obj->last_error; + /* Process error code first and then execute another step, after switch, ONLY if ret = SQLITE_ROW */ + /* ret = sqlite3_step(result_obj->stmt_obj->stmt); */ + /* END */ switch (ret) { case SQLITE_ROW: /* If there was no return value then just skip fetching */ @@ -1993,7 +2013,14 @@ PHP_METHOD(SQLite3Result, fetchArray) default: php_sqlite3_error(result_obj->db_obj, "Unable to execute statement: %s", sqlite3_errmsg(sqlite3_db_handle(result_obj->stmt_obj->stmt))); + /* START */ + RETURN_FALSE; + /* END */ } + /* START */ + /* Step result set ONLY if ret == SQLITE_ROW and store error code in result struct for next SQLite3Result::fetcharray() */ + result_obj->last_error = sqlite3_step(result_obj->stmt_obj->stmt); + /* END */ } /* }}} */ From 2c134e6d339e89d5c3ae00001a76dd95953a79fa Mon Sep 17 00:00:00 2001 From: atman <57639310+HochMartinez@users.noreply.github.com> Date: Thu, 21 Apr 2022 13:39:34 +0200 Subject: [PATCH 2/4] Add last step error code to _php_sqlite3_result_object struct to fix DOUBLE QUERY EXECUTION on first SQLite3Result->fetchArray() Store the last step error code from Sqlite3::query(), SQlite3Stmt::execute() in _php_sqlite3_result_object, to passe it to SQLite3Result::fetchArray(). Also stores last step error from SQLite3Result::fetchArray() to pass it to next SQLite3Result::fetchArray(). --- ext/sqlite3/php_sqlite3_structs.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ext/sqlite3/php_sqlite3_structs.h b/ext/sqlite3/php_sqlite3_structs.h index 15cb8a8cb303d..4033c8fbff98f 100644 --- a/ext/sqlite3/php_sqlite3_structs.h +++ b/ext/sqlite3/php_sqlite3_structs.h @@ -106,6 +106,11 @@ struct _php_sqlite3_result_object { php_sqlite3_db_object *db_obj; php_sqlite3_stmt *stmt_obj; zval stmt_obj_zval; + /* START */ + /* Store the last step error code from Sqlite3::query(), SQlite3Stmt::execute() to passe it to SQLite3Result::fetchArray() */ + /* Also stores last step error from last SQLite3Result::fetchArray() */ + int last_error; + /* END */ /* Cache of column names to speed up repeated fetchArray(SQLITE3_ASSOC) calls. * Cache is cleared on reset() and finalize() calls. */ From f5ef9b10cbe36526c2f9e646d0b8336c450c44e7 Mon Sep 17 00:00:00 2001 From: atman <57639310+HochMartinez@users.noreply.github.com> Date: Thu, 21 Apr 2022 19:59:18 +0200 Subject: [PATCH 3/4] Second fix to avoid DOUBLE EXECUTION OF A QUERY when calling SQLite3Result->fetchArray() This fix prevents avoids the double execution of a query when calling SQLite3Result->fetchArray(). This second fix does not alter or affect the error logic of SQLite3Result->fetchArray(). --- ext/sqlite3/sqlite3.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index d628a55aad5fd..91b1cb5c3f209 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -592,7 +592,7 @@ PHP_METHOD(SQLite3, query) return_code = sqlite3_step(result->stmt_obj->stmt); /* START */ - /* Copy step error code in result struct for SQLite3Result::fetcharray() */ + /* Copy step error code in result struct to pass it to SQLite3Result::fetcharray() */ result->last_error = return_code; /* END */ @@ -1808,7 +1808,7 @@ PHP_METHOD(SQLite3Stmt, execute) result->column_names = NULL; result->column_count = -1; /* START */ - /* Do not reset the query just pass the step error code in result struct for SQLite3Result::fetcharray() */ + /* Do not reset the query just pass the step error code in 'result' struct to SQLite3Result::fetcharray() */ result->last_error = return_code; /* END */ ZVAL_OBJ_COPY(&result->stmt_obj_zval, Z_OBJ_P(object)); @@ -1957,11 +1957,16 @@ PHP_METHOD(SQLite3Result, fetchArray) SQLITE3_CHECK_INITIALIZED(result_obj->db_obj, result_obj->stmt_obj->initialised, SQLite3Result) /* START */ - /* Get result code stored in result struct, returned by Sqlite3::query(), SQLite3Stmt::execute() or previous SQLite3Result::fetchArray() */ - ret = result_obj->last_error; - /* Process error code first and then execute another step, after switch, ONLY if ret = SQLITE_ROW */ - /* ret = sqlite3_step(result_obj->stmt_obj->stmt); */ + /* Get result code stored in 'result_obj' struct, initialized by Sqlite3::query(), SQLite3Stmt::execute() */ + /* The first time we call SQLite3Result->fetchArray(), skip step and process the step result of Sqlite3::query() or SQLite3Stmt::execute() */ + /* The first time we call SQLite3Result->fetchArray(), the step result stored in 'last_error' can only be SQLITE_ROW or SQLITE_DONE */ + /* From the second call to SQLite3Result->fetchArray() onwards, 'last_error' will be -1 and therefore step will be executed */ + ret = result_obj->last_error; + if (ret == -1) { + ret = sqlite3_step(result_obj->stmt_obj->stmt); + } /* END */ + switch (ret) { case SQLITE_ROW: /* If there was no return value then just skip fetching */ @@ -2005,6 +2010,12 @@ PHP_METHOD(SQLite3Result, fetchArray) zend_symtable_add_new(Z_ARR_P(return_value), result_obj->column_names[i], &data); } } + + /* START */ + /* Intialize to -1 so on the second call to SQLite3Result->fetchArray() onwards, step is executed */ + result_obj->last_error = -1; + /* END */ + break; case SQLITE_DONE: @@ -2013,14 +2024,7 @@ PHP_METHOD(SQLite3Result, fetchArray) default: php_sqlite3_error(result_obj->db_obj, "Unable to execute statement: %s", sqlite3_errmsg(sqlite3_db_handle(result_obj->stmt_obj->stmt))); - /* START */ - RETURN_FALSE; - /* END */ } - /* START */ - /* Step result set ONLY if ret == SQLITE_ROW and store error code in result struct for next SQLite3Result::fetcharray() */ - result_obj->last_error = sqlite3_step(result_obj->stmt_obj->stmt); - /* END */ } /* }}} */ From aa9ddb23dc5076ab8f152156d46cfd8cefcfc618 Mon Sep 17 00:00:00 2001 From: atman <57639310+HochMartinez@users.noreply.github.com> Date: Thu, 21 Apr 2022 20:02:54 +0200 Subject: [PATCH 4/4] Correction to a comment Correction to a comment. --- ext/sqlite3/php_sqlite3_structs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/sqlite3/php_sqlite3_structs.h b/ext/sqlite3/php_sqlite3_structs.h index 4033c8fbff98f..847228eddf32a 100644 --- a/ext/sqlite3/php_sqlite3_structs.h +++ b/ext/sqlite3/php_sqlite3_structs.h @@ -106,9 +106,9 @@ struct _php_sqlite3_result_object { php_sqlite3_db_object *db_obj; php_sqlite3_stmt *stmt_obj; zval stmt_obj_zval; + /* START */ /* Store the last step error code from Sqlite3::query(), SQlite3Stmt::execute() to passe it to SQLite3Result::fetchArray() */ - /* Also stores last step error from last SQLite3Result::fetchArray() */ int last_error; /* END */