Skip to content

Commit 2f6dea4

Browse files
committed
New crud vtab: Only update $local bucket once
1 parent 5253af8 commit 2f6dea4

File tree

2 files changed

+79
-12
lines changed

2 files changed

+79
-12
lines changed

crates/core/src/crud_vtab.rs

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ enum CrudTransactionMode {
5555
Simple {
5656
stmt: ManagedStmt,
5757
set_updated_rows: ManagedStmt,
58-
update_local_bucket: ManagedStmt,
58+
had_writes: bool,
5959
},
6060
}
6161

@@ -73,13 +73,13 @@ impl VirtualTable {
7373
}
7474
}
7575

76-
fn handle_insert(&self, args: &[*mut sqlite::value]) -> Result<(), SQLiteError> {
76+
fn handle_insert(&mut self, args: &[*mut sqlite::value]) -> Result<(), SQLiteError> {
7777
let current_tx = self
7878
.current_tx
79-
.as_ref()
79+
.as_mut()
8080
.ok_or_else(|| SQLiteError(ResultCode::MISUSE, Some(String::from("No tx_id"))))?;
8181

82-
match &current_tx.mode {
82+
match &mut current_tx.mode {
8383
CrudTransactionMode::Manual { stmt } => {
8484
// Columns are (data TEXT, options INT HIDDEN)
8585
let data = args[0].text();
@@ -96,7 +96,7 @@ impl VirtualTable {
9696
CrudTransactionMode::Simple {
9797
stmt,
9898
set_updated_rows,
99-
update_local_bucket,
99+
had_writes,
100100
} => {
101101
// Columns are (op TEXT, id TEXT, type TEXT, data TEXT, old_values TEXT, metadata TEXT, options INT HIDDEN)
102102
let flags = match args[6].value_type() {
@@ -154,7 +154,7 @@ impl VirtualTable {
154154
set_updated_rows.bind_text(1, row_type, sqlite::Destructor::STATIC)?;
155155
set_updated_rows.bind_text(2, id, sqlite::Destructor::STATIC)?;
156156
set_updated_rows.exec()?;
157-
update_local_bucket.exec()?;
157+
*had_writes = true;
158158
}
159159
}
160160

@@ -184,7 +184,7 @@ impl VirtualTable {
184184
"INSERT OR IGNORE INTO ps_updated_rows(row_type, row_id) VALUES(?, ?)",
185185
0,
186186
)?,
187-
update_local_bucket: db.prepare_v3(formatcp!("INSERT OR REPLACE INTO ps_buckets(name, last_op, target_op) VALUES('$local', 0, {MAX_OP_ID})"), 0)?,
187+
had_writes: false,
188188
}
189189
} else {
190190
const SQL: &str = formatcp!(
@@ -206,7 +206,30 @@ SELECT * FROM insertion WHERE (NOT (?3 & {})) OR data->>'op' != 'PATCH' OR data-
206206
Ok(())
207207
}
208208

209-
fn end_transaction(&mut self) {
209+
fn end_transaction(&mut self) -> Result<(), SQLiteError> {
210+
let tx = self.current_tx.take();
211+
if let Some(tx) = tx {
212+
let needs_local_bucket_update = match tx.mode {
213+
CrudTransactionMode::Manual { .. } => {
214+
// In manual mode, users need to update the $local bucket themselves.
215+
false
216+
}
217+
CrudTransactionMode::Simple {
218+
had_writes,
219+
stmt: _,
220+
set_updated_rows: _,
221+
} => had_writes,
222+
};
223+
224+
if needs_local_bucket_update {
225+
self.db.exec_safe(formatcp!("INSERT OR REPLACE INTO ps_buckets(name, last_op, target_op) VALUES('$local', 0, {MAX_OP_ID})"))?;
226+
}
227+
}
228+
229+
Ok(())
230+
}
231+
232+
fn clear_transaction_state(&mut self) {
210233
self.current_tx = None;
211234
}
212235
}
@@ -269,13 +292,12 @@ extern "C" fn begin(vtab: *mut sqlite::vtab) -> c_int {
269292

270293
extern "C" fn commit(vtab: *mut sqlite::vtab) -> c_int {
271294
let tab = unsafe { &mut *(vtab.cast::<VirtualTable>()) };
272-
tab.end_transaction();
273-
ResultCode::OK as c_int
295+
vtab_result(vtab, tab.end_transaction())
274296
}
275297

276298
extern "C" fn rollback(vtab: *mut sqlite::vtab) -> c_int {
277299
let tab = unsafe { &mut *(vtab.cast::<VirtualTable>()) };
278-
tab.end_transaction();
300+
tab.clear_transaction_state();
279301
// ps_tx will be rolled back automatically
280302
ResultCode::OK as c_int
281303
}
@@ -295,7 +317,7 @@ extern "C" fn update(
295317
ResultCode::MISUSE as c_int
296318
} else if rowid.value_type() == sqlite::ColumnType::Null {
297319
// INSERT
298-
let tab = unsafe { &*(vtab.cast::<VirtualTable>()) };
320+
let tab = unsafe { &mut *(vtab.cast::<VirtualTable>()) };
299321
let result = tab.handle_insert(&args[2..]);
300322
vtab_result(vtab, result)
301323
} else {

dart/test/crud_test.dart

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,51 @@ void main() {
322322
'{"op":"PUT","id":"foo","type":"users","data":{"my":"value"},"old":{"previous":"value"}}',
323323
});
324324
});
325+
326+
test('resets state after commit', () {
327+
db.execute('BEGIN');
328+
db.execute(
329+
'INSERT INTO powersync_crud (op, id, type) VALUES (?, ?, ?)', [
330+
'DELETE',
331+
'foo',
332+
'users',
333+
]);
334+
db.execute('commit');
335+
336+
db.execute(
337+
'INSERT INTO powersync_crud (op, id, type) VALUES (?, ?, ?)', [
338+
'DELETE',
339+
'foo',
340+
'users',
341+
]);
342+
expect(db.select('SELECT * FROM ps_crud').map((r) => r['tx_id']),
343+
[1, 2]);
344+
});
345+
346+
test('resets state after rollback', () {
347+
db.execute('BEGIN');
348+
db.execute(
349+
'INSERT INTO powersync_crud (op, id, type) VALUES (?, ?, ?)', [
350+
'DELETE',
351+
'foo',
352+
'users',
353+
]);
354+
db.execute('rollback');
355+
356+
db.execute(
357+
'INSERT INTO powersync_crud (op, id, type) VALUES (?, ?, ?)', [
358+
'DELETE',
359+
'foo2',
360+
'users',
361+
]);
362+
expect(db.select('SELECT * FROM ps_crud'), [
363+
{
364+
'id': 1,
365+
'data': '{"op":"DELETE","id":"foo2","type":"users"}',
366+
'tx_id': 1,
367+
}
368+
]);
369+
});
325370
});
326371
});
327372

0 commit comments

Comments
 (0)