Skip to content

Commit c600156

Browse files
authored
fix(changes): actionable error when settings exist but *_cloudsync tables are missing (#55)
Querying cloudsync_changes (directly or via cloudsync_payload_blob_checked) on a database whose cloudsync_table_settings rows survived while every *_cloudsync meta table was lost — e.g. a dump/restore that skipped them — returned SQLITE_NOMEM: the NULL from vtab_build_changes_sql fell through the config-exists guard added in #54. SQLite Cloud reported it on the /check path as "Not enough memory to execute query", pointing operators at memory instead of the real state. - xFilter now distinguishes this state (settings rows present, zero %_cloudsync tables in sqlite_master) and raises "cloudsync settings reference tables whose sync metadata is missing" with the re-init remediation; genuine OOM still returns SQLITE_NOMEM - unit test: Changes Vtab Missing Meta Tables (red before, green after) - bump CLOUDSYNC_VERSION to 1.1.2 and add the CHANGELOG entry (SQLite vtab only, no PG changes, extension version stays 1.1)
1 parent 92a83cd commit c600156

4 files changed

Lines changed: 58 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

7+
## [1.1.2] - 2026-07-13
8+
9+
### Fixed
10+
11+
- Querying the SQLite `cloudsync_changes` virtual table (directly or via `cloudsync_payload_blob_checked()`) on a database whose `cloudsync_table_settings` rows survived while all `*_cloudsync` meta tables were lost (e.g. a dump/restore that skipped them) now raises an actionable error ("cloudsync settings reference tables whose sync metadata is missing ...") instead of a misclassified `SQLITE_NOMEM`, which servers reported as "Not enough memory to execute query".
12+
713
## [1.1.1] - 2026-07-10
814

915
### Fixed

src/cloudsync.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
extern "C" {
1919
#endif
2020

21-
#define CLOUDSYNC_VERSION "1.1.1"
21+
#define CLOUDSYNC_VERSION "1.1.2"
2222
#define CLOUDSYNC_MAX_TABLENAME_LEN 512
2323

2424
#define CLOUDSYNC_VALUE_NOTSET -1

src/sqlite/cloudsync_changes_sqlite.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,21 @@ int cloudsync_changesvtab_filter (sqlite3_vtab_cursor *cursor, int idxn, const c
413413
"SELECT cloudsync_init('<table_name>') to enable sync on a "
414414
"table before querying cloudsync_changes.");
415415
}
416+
// settings rows exist but no *_cloudsync tables do (e.g. a dump/restore
417+
// that skipped the meta tables): still not an OOM
418+
sqlite3_stmt *vm = NULL;
419+
bool meta_missing = false;
420+
if (sqlite3_prepare_v2(db, "SELECT 1 FROM sqlite_master WHERE type='table' AND tbl_name LIKE '%_cloudsync' LIMIT 1;", -1, &vm, NULL) == SQLITE_OK) {
421+
meta_missing = (sqlite3_step(vm) != SQLITE_ROW);
422+
}
423+
if (vm) sqlite3_finalize(vm);
424+
if (meta_missing) {
425+
return vtab_set_error((sqlite3_vtab *)c->vtab,
426+
"cloudsync settings reference tables whose sync metadata is "
427+
"missing (no *_cloudsync tables found). Re-run "
428+
"SELECT cloudsync_init('<table_name>') for each configured "
429+
"table to rebuild it.");
430+
}
416431
return SQLITE_NOMEM;
417432
}
418433

test/unit.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12976,6 +12976,41 @@ bool do_test_payload_chunks_uninitialized (bool print_result, bool cleanup_datab
1297612976
return result;
1297712977
}
1297812978

12979+
// A database whose cloudsync_table_settings rows survived while the *_cloudsync
12980+
// meta tables were lost (e.g. a dump/restore that skipped them) must raise an
12981+
// actionable error from cloudsync_changes, not a misclassified SQLITE_NOMEM
12982+
// that servers report as "Not enough memory to execute query".
12983+
bool do_test_changes_vtab_missing_meta_tables (bool print_result) {
12984+
sqlite3 *db = NULL;
12985+
sqlite3_stmt *stmt = NULL;
12986+
bool result = false;
12987+
12988+
db = do_create_database();
12989+
if (!db) goto finalize;
12990+
12991+
if (sqlite3_exec(db, "CREATE TABLE barang (id TEXT PRIMARY KEY, name TEXT);", NULL, NULL, NULL) != SQLITE_OK) goto finalize;
12992+
if (sqlite3_exec(db, "SELECT cloudsync_init('barang');", NULL, NULL, NULL) != SQLITE_OK) goto finalize;
12993+
if (sqlite3_exec(db, "INSERT INTO barang VALUES ('1','test');", NULL, NULL, NULL) != SQLITE_OK) goto finalize;
12994+
12995+
// settings rows survive, meta table does not
12996+
if (sqlite3_exec(db, "DROP TABLE barang_cloudsync;", NULL, NULL, NULL) != SQLITE_OK) goto finalize;
12997+
12998+
if (sqlite3_prepare_v2(db, "SELECT COUNT(*) FROM cloudsync_changes;", -1, &stmt, NULL) != SQLITE_OK) goto finalize;
12999+
if (sqlite3_step(stmt) != SQLITE_ERROR) goto finalize;
13000+
if (!strstr(sqlite3_errmsg(db), "sync metadata")) goto finalize;
13001+
sqlite3_finalize(stmt); stmt = NULL;
13002+
13003+
result = true;
13004+
13005+
finalize:
13006+
if (!result && print_result) {
13007+
printf("do_test_changes_vtab_missing_meta_tables error: %s\n", db ? sqlite3_errmsg(db) : "no db");
13008+
}
13009+
if (stmt) sqlite3_finalize(stmt);
13010+
if (db) close_db(db);
13011+
return result;
13012+
}
13013+
1297913014
bool do_test_payload_idempotency (int nclients, bool print_result, bool cleanup_databases) {
1298013015
sqlite3 *db[2] = {NULL, NULL};
1298113016
bool result = false;
@@ -13429,6 +13464,7 @@ int main (int argc, const char * argv[]) {
1342913464
result += test_report("Payload Chunks Split db_version:", do_test_payload_chunks_split_dbversion(print_result, cleanup_databases));
1343013465
result += test_report("Payload Chunks Positional Resume:", do_test_payload_chunks_positional_resume(print_result, cleanup_databases));
1343113466
result += test_report("Payload Chunks Uninitialized:", do_test_payload_chunks_uninitialized(print_result, cleanup_databases));
13467+
result += test_report("Changes Vtab Missing Meta Tables:", do_test_changes_vtab_missing_meta_tables(print_result));
1343213468

1343313469
// close local database
1343413470
close_db(db);

0 commit comments

Comments
 (0)