diff --git a/lib/sqlite3db.cpp b/lib/sqlite3db.cpp index 3acae23c0..7ba83cc8c 100644 --- a/lib/sqlite3db.cpp +++ b/lib/sqlite3db.cpp @@ -17,6 +17,13 @@ #define USLEEP_SQLITE_LOCKED 100 +/** + * @brief Check if a SQL statement is a DDL query that doesn't affect rows + * + * @param query The SQL statement to check + * @return True if the statement is a DDL query that doesn't affect rows + */ +static bool is_ddl_query_without_row_changes(const char *query); /** * @brief Constructor for the SQLite3_column class. @@ -353,7 +360,14 @@ bool SQLite3DB::execute_statement(const char *str, char **error, int *cols, int } } while (rc==SQLITE_LOCKED || rc==SQLITE_BUSY); if (rc==SQLITE_DONE) { - *affected_rows=(*proxy_sqlite3_changes)(db); + int changes = (*proxy_sqlite3_changes)(db); + // For DDL queries that don't affect rows, reset affected_rows to 0 + // to prevent incorrect reporting of previous DML affected rows + if (is_ddl_query_without_row_changes(str)) { + *affected_rows = 0; + } else { + *affected_rows = changes; + } ret=true; } else { *error=strdup((*proxy_sqlite3_errmsg)(db)); @@ -370,9 +384,43 @@ __exit_execute_statement: return ret; } +/** + * @brief Check if a SQL statement is a DDL query that doesn't affect rows + * + * @param query The SQL statement to check + * @return True if the statement is a DDL query that doesn't affect rows + */ +static bool is_ddl_query_without_row_changes(const char *query) { + if (!query) return false; + + // Skip leading whitespace + while (isspace(*query)) { + query++; + } + + // Check for DDL statements that don't affect row counts + return ( + (strncasecmp(query, "CREATE", 6) == 0) || + (strncasecmp(query, "DROP", 4) == 0) || + (strncasecmp(query, "ALTER", 5) == 0) || + (strncasecmp(query, "TRUNCATE", 8) == 0) || + (strncasecmp(query, "VACUUM", 6) == 0) || + (strncasecmp(query, "REINDEX", 7) == 0) || + (strncasecmp(query, "ANALYZE", 7) == 0) || + (strncasecmp(query, "CHECKPOINT", 10) == 0) || + (strncasecmp(query, "PRAGMA", 6) == 0) || + (strncasecmp(query, "BEGIN", 5) == 0) || + (strncasecmp(query, "COMMIT", 6) == 0) || + (strncasecmp(query, "ROLLBACK", 8) == 0) || + (strncasecmp(query, "SAVEPOINT", 9) == 0) || + (strncasecmp(query, "RELEASE", 7) == 0) || + (strncasecmp(query, "EXPLAIN", 7) == 0) + ); +} + /** * @brief Executes a SQL statement and returns the result set without parsing it. - * + * * @param str The SQL statement to execute. * @param error Pointer to a variable to store the error message. * @param cols Pointer to a variable to store the number of columns. @@ -401,7 +449,14 @@ bool SQLite3DB::execute_statement_raw(const char *str, char **error, int *cols, } } while (rc==SQLITE_LOCKED || rc==SQLITE_BUSY); if (rc==SQLITE_DONE) { - *affected_rows=(*proxy_sqlite3_changes)(db); + int changes = (*proxy_sqlite3_changes)(db); + // For DDL queries that don't affect rows, reset affected_rows to 0 + // to prevent incorrect reporting of previous DML affected rows + if (is_ddl_query_without_row_changes(str)) { + *affected_rows = 0; + } else { + *affected_rows = changes; + } ret=true; } else { *error=strdup((*proxy_sqlite3_errmsg)(db));