diff --git a/core/src/triggers.c b/core/src/triggers.c index 95704a8b0..57d6c7949 100644 --- a/core/src/triggers.c +++ b/core/src/triggers.c @@ -176,11 +176,11 @@ int crsql_createUpdateTrigger(sqlite3 *db, crsql_TableInfo *tableInfo, __crsql_db_version,\ __crsql_seq,\ __crsql_site_id\ - ) SELECT %s, %Q, 1, crsql_nextdbversion(), crsql_increment_and_get_seq(), NULL WHERE crsql_internal_sync_bit() = 0 AND NEW.\"%w\" IS NOT OLD.\"%w\"\ + ) SELECT %s, %Q, 1, crsql_nextdbversion(), crsql_increment_and_get_seq() - 1, NULL WHERE crsql_internal_sync_bit() = 0 AND NEW.\"%w\" IS NOT OLD.\"%w\"\ ON CONFLICT DO UPDATE SET\ __crsql_col_version = __crsql_col_version + 1,\ __crsql_db_version = crsql_nextdbversion(),\ - __crsql_seq = crsql_get_seq(),\ + __crsql_seq = crsql_get_seq() - 1,\ __crsql_site_id = NULL;\n", tableInfo->tblName, pkList, pkNewList, tableInfo->nonPks[i].name, tableInfo->nonPks[i].name, tableInfo->nonPks[i].name); @@ -251,7 +251,7 @@ char *crsql_deleteTriggerQuery(crsql_TableInfo *tableInfo) { WHERE crsql_internal_sync_bit() = 0 ON CONFLICT DO UPDATE SET\ __crsql_col_version = __crsql_col_version + 1,\ __crsql_db_version = crsql_nextdbversion(),\ - __crsql_seq = crsql_increment_and_get_seq(),\ + __crsql_seq = crsql_get_seq(),\ __crsql_site_id = NULL;\ \ DELETE FROM \"%w__crsql_clock\" WHERE crsql_internal_sync_bit() = 0 AND %s AND __crsql_col_name != '__crsql_del';\ diff --git a/core/src/triggers.test.c b/core/src/triggers.test.c index 86e11e9f5..d41cecf78 100644 --- a/core/src/triggers.test.c +++ b/core/src/triggers.test.c @@ -67,7 +67,6 @@ static void testDeleteTriggerQuery() { rc += sqlite3_exec(db, "DROP TABLE foo", 0, 0, &errMsg); char *query = crsql_deleteTriggerQuery(tableInfo); - printf("Q: X%sX", query); assert( strcmp( "CREATE TRIGGER IF NOT EXISTS \"foo__crsql_dtrig\" AFTER DELETE " @@ -79,7 +78,7 @@ static void testDeleteTriggerQuery() { "NULL WHERE crsql_internal_sync_bit() = 0 ON CONFLICT DO UPDATE " "SET __crsql_col_version = __crsql_col_version + 1, " "__crsql_db_version = crsql_nextdbversion(), __crsql_seq = " - "crsql_increment_and_get_seq(), __crsql_site_id = NULL; " + "crsql_get_seq(), __crsql_site_id = NULL; " " DELETE FROM \"foo__crsql_clock\" WHERE crsql_internal_sync_bit() " "= 0 AND \"a\" = OLD.\"a\" AND __crsql_col_name != '__crsql_del'; " " END; ", @@ -118,7 +117,7 @@ static void testInsertTriggerQuery() { "NULL WHERE crsql_internal_sync_bit() = 0 ON CONFLICT DO UPDATE SET " " __crsql_col_version = __crsql_col_version + 1, " "__crsql_db_version = crsql_nextdbversion(), __crsql_seq = " - "crsql_increment_and_get_seq(), __crsql_site_id = " + "crsql_get_seq(), __crsql_site_id = " "NULL;\n"; assert(strcmp(expected, query) == 0); diff --git a/py/correctness/tests/test_seq.py b/py/correctness/tests/test_seq.py index cf31b3250..831c41d2f 100644 --- a/py/correctness/tests/test_seq.py +++ b/py/correctness/tests/test_seq.py @@ -71,6 +71,85 @@ def test_preserved_on_merge(): assert (c_rows == c2_rows) +def test_incr_by_one(): + # insert + # update + # delete + c = connect(":memory:") + c.execute("create table foo (a primary key, b, c, d)") + c.execute("select crsql_as_crr('foo')") + c.commit() + + c.execute("INSERT INTO foo VALUES (1, 2, 3, 4)") + c.execute("INSERT INTO foo VALUES (2, 2, 3, 4)") + c.execute("INSERT INTO foo VALUES (3, 2, 3, 4)") + c.commit() + + rows = c.execute( + "SELECT seq FROM crsql_changes WHERE db_version = 1").fetchall() + assert (rows == [(0,), (1,), (2,), (3,), (4,), (5,), (6,), (7,), (8,)]) + + c.execute("UPDATE foo SET c = 'c', d = 'd' WHERE a = 1") + c.execute("UPDATE foo SET c = 'c', d = 'd' WHERE a = 2") + c.execute("UPDATE foo SET c = 'c', d = 'd' WHERE a = 3") + c.commit() + + rows = c.execute( + "SELECT seq FROM crsql_changes WHERE db_version = 2").fetchall() + assert (rows == [(0,), (1,), (2,), (3,), (4,), (5,)]) + + c.execute("UPDATE foo SET b = 'b' WHERE a = 1") + c.execute("UPDATE foo SET b = 'b' WHERE a = 2") + c.execute("UPDATE foo SET b = 'b' WHERE a = 3") + c.commit() + rows = c.execute( + "SELECT seq FROM crsql_changes WHERE db_version = 3").fetchall() + assert (rows == [(0,), (1,), (2,)]) + + c.execute("DELETE FROM foo") + c.commit() + + rows = c.execute("SELECT seq FROM crsql_changes").fetchall() + assert (rows == [(0,), (1,), (2,)]) + + c.execute("create table bar (a primary key, b);") + c.execute("select crsql_as_crr('bar')") + c.commit() + + c.execute("INSERT INTO bar VALUES (1, 2)") + c.execute("INSERT INTO bar VALUES (3, 4)") + c.commit() + + c.execute("UPDATE bar SET b = 'b' WHERE a = 1") + c.execute("UPDATE bar SET b = 'b' WHERE a = 2") + c.commit() + + rows = c.execute("SELECT __crsql_seq FROM bar__crsql_clock").fetchall() + assert (rows == [(0,), (1,)]) + + c.execute("CREATE TABLE baz (a primary key)") + c.execute("SELECT crsql_as_crr('baz')") + c.commit() + c.execute("INSERT INTO baz VALUES (1)") + c.execute("INSERT INTO baz VALUES (2)") + c.commit() + + rows = c.execute("SELECT __crsql_seq FROM baz__crsql_clock").fetchall() + assert (rows == [(0,), (1,)]) + + c.execute("UPDATE baz SET a = 11 WHERE a = 1") + c.execute("UPDATE baz SET a = 22 WHERE a = 2") + c.commit() + rows = c.execute("SELECT __crsql_seq FROM baz__crsql_clock").fetchall() + # TODO: The seqs are right but what should have happened is a delete being recorded + # for the old primary key values. + assert (rows == [(0,), (1,), (0,), (1,)]) + + # c.execute("DELETE FROM baz") + + # pprint(c.execute( + # "SELECT *, seq FROM crsql_changes WHERE [table] = 'baz'").fetchall()) + # db is locked when doing concurrent writes so the following case cannot happen in SQLite # def test_preserved_conc_transactions(): # filename = "./test_preserved_conc_transactions.db"