Skip to content

Commit 81e02ab

Browse files
marcobambiniclaude
andcommitted
fix: cloudsync_cleanup now drops the blocks table for block LWW tables
When a table has columns configured with algo=block, cloudsync_cleanup was leaving the {table}_cloudsync_blocks table behind. This fix drops it alongside the meta-table during cleanup. Adds a unit test to verify both companion tables are removed. Bumps version to 1.0.11. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent e19bbbe commit 81e02ab

4 files changed

Lines changed: 75 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ 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.0.11] - 2026-04-11
8+
9+
### Fixed
10+
11+
- **cloudsync_cleanup**: Now also drops the `{table}_cloudsync_blocks` table when the table has block LWW columns configured via `cloudsync_set_column(..., 'algo', 'block')`.
12+
13+
### Added
14+
15+
- Unit test `do_test_block_lww_cleanup` verifying that both `{table}_cloudsync` and `{table}_cloudsync_blocks` are removed after `cloudsync_cleanup`.
16+
717
## [1.0.10] - 2026-04-08
818

919
### Fixed

src/cloudsync.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3378,7 +3378,7 @@ int cloudsync_table_sanity_check (cloudsync_context *data, const char *name, CLO
33783378

33793379
int cloudsync_cleanup_internal (cloudsync_context *data, cloudsync_table_context *table) {
33803380
if (cloudsync_context_init(data) == NULL) return DBRES_MISUSE;
3381-
3381+
33823382
// drop meta-table
33833383
const char *table_name = table->name;
33843384
char *sql = cloudsync_memory_mprintf(SQL_DROP_CLOUDSYNC_TABLE, table->meta_ref);
@@ -3389,7 +3389,19 @@ int cloudsync_cleanup_internal (cloudsync_context *data, cloudsync_table_context
33893389
snprintf(buffer, sizeof(buffer), "Unable to drop cloudsync table %s_cloudsync in cloudsync_cleanup", table_name);
33903390
return cloudsync_set_error(data, buffer, rc);
33913391
}
3392-
3392+
3393+
// drop blocks table if this table has block LWW columns
3394+
if (table->blocks_ref) {
3395+
sql = cloudsync_memory_mprintf(SQL_DROP_CLOUDSYNC_TABLE, table->blocks_ref);
3396+
rc = database_exec(data, sql);
3397+
cloudsync_memory_free(sql);
3398+
if (rc != DBRES_OK) {
3399+
char buffer[1024];
3400+
snprintf(buffer, sizeof(buffer), "Unable to drop blocks table %s_cloudsync_blocks in cloudsync_cleanup", table_name);
3401+
return cloudsync_set_error(data, buffer, rc);
3402+
}
3403+
}
3404+
33933405
// drop original triggers
33943406
rc = database_delete_triggers(data, table_name);
33953407
if (rc != DBRES_OK) {

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.0.10"
21+
#define CLOUDSYNC_VERSION "1.0.11"
2222
#define CLOUDSYNC_MAX_TABLENAME_LEN 512
2323

2424
#define CLOUDSYNC_VALUE_NOTSET -1

test/unit.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11281,6 +11281,55 @@ bool do_test_block_lww_whitespace(int nclients, bool print_result, bool cleanup_
1128111281
return false;
1128211282
}
1128311283

11284+
bool do_test_block_lww_cleanup(bool print_result, bool cleanup_databases) {
11285+
// Test: cloudsync_cleanup removes both the meta-table and the blocks table
11286+
// when a table has block LWW columns configured.
11287+
sqlite3 *db = NULL;
11288+
time_t timestamp = time(NULL);
11289+
int rc;
11290+
bool result = false;
11291+
11292+
db = do_create_database_file(0, timestamp, test_counter++);
11293+
if (!db) return false;
11294+
11295+
rc = sqlite3_exec(db, "CREATE TABLE docs (id TEXT NOT NULL PRIMARY KEY, body TEXT);", NULL, NULL, NULL);
11296+
if (rc != SQLITE_OK) { printf("block_cleanup: CREATE TABLE failed: %s\n", sqlite3_errmsg(db)); goto fail; }
11297+
11298+
rc = sqlite3_exec(db, "SELECT cloudsync_init('docs');", NULL, NULL, NULL);
11299+
if (rc != SQLITE_OK) { printf("block_cleanup: cloudsync_init failed: %s\n", sqlite3_errmsg(db)); goto fail; }
11300+
11301+
rc = sqlite3_exec(db, "SELECT cloudsync_set_column('docs', 'body', 'algo', 'block');", NULL, NULL, NULL);
11302+
if (rc != SQLITE_OK) { printf("block_cleanup: set_column failed: %s\n", sqlite3_errmsg(db)); goto fail; }
11303+
11304+
// Insert a row to populate the blocks table
11305+
rc = sqlite3_exec(db, "INSERT INTO docs (id, body) VALUES ('doc1', 'Line 1\nLine 2\nLine 3');", NULL, NULL, NULL);
11306+
if (rc != SQLITE_OK) { printf("block_cleanup: INSERT failed: %s\n", sqlite3_errmsg(db)); goto fail; }
11307+
11308+
// Confirm docs_cloudsync and docs_cloudsync_blocks exist before cleanup
11309+
int64_t meta_exists = do_select_int(db, "SELECT COUNT(*) FROM sqlite_master WHERE name='docs_cloudsync';");
11310+
if (meta_exists != 1) { printf("block_cleanup: docs_cloudsync missing before cleanup\n"); goto fail; }
11311+
11312+
int64_t blocks_exists = do_select_int(db, "SELECT COUNT(*) FROM sqlite_master WHERE name='docs_cloudsync_blocks';");
11313+
if (blocks_exists != 1) { printf("block_cleanup: docs_cloudsync_blocks missing before cleanup\n"); goto fail; }
11314+
11315+
// Run cleanup
11316+
rc = sqlite3_exec(db, "SELECT cloudsync_cleanup('docs');", NULL, NULL, NULL);
11317+
if (rc != SQLITE_OK) { printf("block_cleanup: cloudsync_cleanup failed: %s\n", sqlite3_errmsg(db)); goto fail; }
11318+
11319+
// Both companion tables must be gone after cleanup
11320+
int64_t meta_after = do_select_int(db, "SELECT COUNT(*) FROM sqlite_master WHERE name='docs_cloudsync';");
11321+
if (meta_after != 0) { printf("block_cleanup: docs_cloudsync still exists after cleanup\n"); goto fail; }
11322+
11323+
int64_t blocks_after = do_select_int(db, "SELECT COUNT(*) FROM sqlite_master WHERE name='docs_cloudsync_blocks';");
11324+
if (blocks_after != 0) { printf("block_cleanup: docs_cloudsync_blocks still exists after cleanup\n"); goto fail; }
11325+
11326+
result = true;
11327+
11328+
fail:
11329+
if (db) close_db(db);
11330+
return result;
11331+
}
11332+
1128411333
// MARK: - New edge-case tests
1128511334

1128611335
bool do_test_unsupported_algorithms (sqlite3 *db) {
@@ -11997,6 +12046,7 @@ int main (int argc, const char * argv[]) {
1199712046
result += test_report("Test Block LWW NonOverlap:", do_test_block_lww_nonoverlap_add(2, print_result, cleanup_databases));
1199812047
result += test_report("Test Block LWW LongLine:", do_test_block_lww_long_line(2, print_result, cleanup_databases));
1199912048
result += test_report("Test Block LWW Whitespace:", do_test_block_lww_whitespace(2, print_result, cleanup_databases));
12049+
result += test_report("Test Block LWW Cleanup:", do_test_block_lww_cleanup(print_result, cleanup_databases));
1200012050

1200112051
// edge-case tests
1200212052
result += test_report("Corrupted Payload Test:", do_test_corrupted_payload(2, print_result, cleanup_databases));

0 commit comments

Comments
 (0)