Skip to content

Commit 81c0aa0

Browse files
authored
Merge pull request #1 from somtochiama/fix-pk-update
Update columns in clock table when changing primary keys.
2 parents 0db37d3 + 97bb82c commit 81c0aa0

File tree

7 files changed

+292
-54
lines changed

7 files changed

+292
-54
lines changed

.github/workflows/py-tests.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
cd core
2929
make loadable
3030
31-
- uses: conda-incubator/setup-miniconda@v2
31+
- uses: conda-incubator/setup-miniconda@v3
3232
with:
3333
auto-update-conda: true
3434
auto-activate-base: true

core/Makefile

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ dbg_prefix=./dbg
7272
bundle=bundle_static
7373
valgrind: bundle = integration_check
7474
test: bundle = integration_check
75+
ubsan: bundle = integration_check
76+
asan: bundle = integration_check
7577

7678
TARGET_LOADABLE=$(prefix)/crsqlite.$(LOADABLE_EXTENSION)
7779
TARGET_DBG_LOADABLE=$(dbg_prefix)/crsqlite.$(LOADABLE_EXTENSION)
@@ -124,7 +126,10 @@ valgrind: $(TARGET_TEST)
124126
valgrind $(prefix)/test
125127
analyzer:
126128
scan-build $(MAKE) clean loadable
127-
ubsan: CC=clang
129+
asan: export RUSTFLAGS=-Zsanitizer=address
130+
asan: rs_build_flags=--target x86_64-unknown-linux-gnu -Zbuild-std
131+
asan: rs_lib_dbg_static=./rs/$(bundle)/target/x86_64-unknown-linux-gnu/debug/libcrsql_$(bundle).a
132+
asan: CC=clang
128133
ubsan: LDLIBS += -lubsan
129134
ubsan: clean $(TARGET_TEST)
130135
$(prefix)/test
@@ -255,7 +260,7 @@ $(TARGET_TEST): $(prefix) $(TARGET_SQLITE3_EXTRA_C) src/tests.c src/*.test.c $(e
255260
$(TARGET_SQLITE3_EXTRA_C) src/tests.c src/*.test.c $(ext_files) $(rs_lib_dbg_static_cpy) \
256261
$(LDLIBS) -o $@
257262

258-
$(TARGET_TEST_ASAN): $(prefix) $(TARGET_SQLITE3_EXTRA_C) src/tests.c src/*.test.c $(ext_files)
263+
$(TARGET_TEST_ASAN): $(prefix) $(TARGET_SQLITE3_EXTRA_C) src/tests.c src/*.test.c $(ext_files) $(rs_lib_dbg_static_cpy)
259264
$(CC) -fsanitize=address -g -fno-omit-frame-pointer -Wall \
260265
-DSQLITE_THREADSAFE=0 \
261266
-DSQLITE_OMIT_LOAD_EXTENSION=1 \

core/rs/core/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ unsafe extern "C" fn x_crsql_as_crr(
548548
) {
549549
if argc == 0 {
550550
ctx.result_error(
551-
"Wrong number of args provided to crsql_as_crr. Provide the schema
551+
"Wrong number of args provided to crsql_as_crr. Provide the schema
552552
name and table name or just the table name.",
553553
);
554554
return;
@@ -609,7 +609,7 @@ unsafe extern "C" fn x_crsql_begin_alter(
609609
) {
610610
if argc == 0 {
611611
ctx.result_error(
612-
"Wrong number of args provided to crsql_begin_alter. Provide the
612+
"Wrong number of args provided to crsql_begin_alter. Provide the
613613
schema name and table name or just the table name.",
614614
);
615615
return;
@@ -620,7 +620,7 @@ unsafe extern "C" fn x_crsql_begin_alter(
620620
let (_schema_name, table_name) = if argc == 2 {
621621
(args[0].text(), args[1].text())
622622
} else {
623-
("main", args[0].text())
623+
("main\0", args[0].text())
624624
};
625625

626626
let db = ctx.db_handle();
@@ -645,7 +645,7 @@ unsafe extern "C" fn x_crsql_commit_alter(
645645
) {
646646
if argc == 0 {
647647
ctx.result_error(
648-
"Wrong number of args provided to crsql_commit_alter. Provide the
648+
"Wrong number of args provided to crsql_commit_alter. Provide the
649649
schema name and table name or just the table name.",
650650
);
651651
return;
@@ -655,7 +655,7 @@ unsafe extern "C" fn x_crsql_commit_alter(
655655
let (schema_name, table_name) = if argc >= 2 {
656656
(args[0].text(), args[1].text())
657657
} else {
658-
("main", args[0].text())
658+
("main\0", args[0].text())
659659
};
660660

661661
let non_destructive = if argc >= 3 { args[2].int() == 1 } else { false };

core/rs/core/src/local_writes/after_update.rs

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ fn after_update(
7474
let next_db_version = crate::db_version::next_db_version(db, ext_data, None)?;
7575
let new_key = tbl_info
7676
.get_or_create_key_via_raw_values(db, pks_new)
77-
.or_else(|_| Err("failed geteting or creating lookaside key"))?;
77+
.or_else(|_| Err("failed getting or creating lookaside key"))?;
7878

7979
// Changing a primary key column to a new value is the same thing as deleting the row
8080
// previously identified by the primary key.
@@ -85,16 +85,21 @@ fn after_update(
8585
let next_seq = super::bump_seq(ext_data);
8686
// Record the delete of the row identified by the old primary keys
8787
after_update__mark_old_pk_row_deleted(db, tbl_info, old_key, next_db_version, next_seq)?;
88-
// TODO: each non sentinel needs a unique seq on the move?
89-
after_update__move_non_sentinels(db, tbl_info, new_key, old_key)?;
90-
// Record a create of the row identified by the new primary keys
91-
// if no rows were moved. This is related to the optimization to not save
92-
// sentinels unless required.
93-
// if db.changes64() == 0 { <-- an optimization if we can get to it. we'd need to know to increment causal length.
94-
// so we can get to this when CL is stored in the lookaside.
9588
let next_seq = super::bump_seq(ext_data);
89+
// todo: we don't need to this, if there's no existing row (cl is assumed to be 1).
9690
super::mark_new_pk_row_created(db, tbl_info, new_key, next_db_version, next_seq)?;
97-
// }
91+
for col in tbl_info.non_pks.iter() {
92+
let next_seq = super::bump_seq(ext_data);
93+
after_update__move_non_pk_col(
94+
db,
95+
tbl_info,
96+
new_key,
97+
old_key,
98+
&col.name,
99+
next_db_version,
100+
next_seq,
101+
)?;
102+
}
98103
}
99104

100105
// now for each non_pk_col we need to do an insert
@@ -146,6 +151,32 @@ fn after_update__mark_old_pk_row_deleted(
146151
super::step_trigger_stmt(mark_locally_deleted_stmt)
147152
}
148153

154+
#[allow(non_snake_case)]
155+
fn after_update__move_non_pk_col(
156+
db: *mut sqlite3,
157+
tbl_info: &TableInfo,
158+
new_key: sqlite::int64,
159+
old_key: sqlite::int64,
160+
col_name: &str,
161+
db_version: sqlite::int64,
162+
seq: i32,
163+
) -> Result<ResultCode, String> {
164+
let move_non_pk_col_stmt_ref = tbl_info
165+
.get_move_non_pk_col_stmt(db)
166+
.or_else(|_e| Err("failed to get move_non_pk_col_stmt"))?;
167+
let move_non_pk_col_stmt = move_non_pk_col_stmt_ref
168+
.as_ref()
169+
.ok_or("Failed to deref sentinel stmt")?;
170+
move_non_pk_col_stmt
171+
.bind_int64(1, new_key)
172+
.and_then(|_| move_non_pk_col_stmt.bind_int64(2, db_version))
173+
.and_then(|_| move_non_pk_col_stmt.bind_int(3, seq))
174+
.and_then(|_| move_non_pk_col_stmt.bind_int64(4, old_key))
175+
.and_then(|_| move_non_pk_col_stmt.bind_text(5, col_name, sqlite::Destructor::STATIC))
176+
.or_else(|_| Err("failed binding to move_non_pk_col_stmt"))?;
177+
super::step_trigger_stmt(move_non_pk_col_stmt)
178+
}
179+
149180
// TODO: in the future we can keep sentinel information in the lookaside
150181
#[allow(non_snake_case)]
151182
fn after_update__move_non_sentinels(

core/rs/core/src/tableinfo.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,32 @@ impl TableInfo {
468468
Ok(self.move_non_sentinels_stmt.try_borrow()?)
469469
}
470470

471+
pub fn get_move_non_pk_col_stmt(
472+
&self,
473+
db: *mut sqlite3,
474+
) -> Result<Ref<Option<ManagedStmt>>, ResultCode> {
475+
if self.move_non_sentinels_stmt.try_borrow()?.is_none() {
476+
// Incrementing col_version is especially important for the case where we
477+
// are updating to a currently existing pk, so that the columns
478+
// from the old pk can override the ones from the new at a node
479+
// following our changes.
480+
let sql = format!(
481+
"UPDATE OR REPLACE \"{table_name}__crsql_clock\" SET
482+
key = ?,
483+
db_version = ?,
484+
seq = ?,
485+
col_version = col_version + 1,
486+
site_id = 0
487+
WHERE
488+
key = ? AND col_name = ?",
489+
table_name = crate::util::escape_ident(&self.tbl_name),
490+
);
491+
let ret = db.prepare_v3(&sql, sqlite::PREPARE_PERSISTENT)?;
492+
*self.move_non_sentinels_stmt.try_borrow_mut()? = Some(ret);
493+
}
494+
Ok(self.move_non_sentinels_stmt.try_borrow()?)
495+
}
496+
471497
pub fn get_mark_locally_created_stmt(
472498
&self,
473499
db: *mut sqlite3,

0 commit comments

Comments
 (0)