Skip to content

Commit 0423b4d

Browse files
committed
Disallow local writes during sync
1 parent 5239746 commit 0423b4d

File tree

4 files changed

+76
-8
lines changed

4 files changed

+76
-8
lines changed

crates/core/src/crud_vtab.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ extern crate alloc;
22

33
use alloc::boxed::Box;
44
use alloc::string::String;
5+
use alloc::sync::Arc;
56
use const_format::formatcp;
67
use core::ffi::{c_char, c_int, c_void};
8+
use core::sync::atomic::Ordering;
79

810
use sqlite::{Connection, ResultCode, Value};
911
use sqlite_nostd as sqlite;
@@ -13,6 +15,7 @@ use sqlite_nostd::ResultCode::NULL;
1315
use crate::error::SQLiteError;
1416
use crate::ext::SafeManagedStmt;
1517
use crate::schema::TableInfoFlags;
18+
use crate::state::DatabaseState;
1619
use crate::vtab_util::*;
1720

1821
// Structure:
@@ -31,11 +34,12 @@ struct VirtualTable {
3134
db: *mut sqlite::sqlite3,
3235
current_tx: Option<i64>,
3336
insert_statement: Option<ManagedStmt>,
37+
state: Arc<DatabaseState>,
3438
}
3539

3640
extern "C" fn connect(
3741
db: *mut sqlite::sqlite3,
38-
_aux: *mut c_void,
42+
aux: *mut c_void,
3943
_argc: c_int,
4044
_argv: *const *const c_char,
4145
vtab: *mut *mut sqlite::vtab,
@@ -58,6 +62,14 @@ extern "C" fn connect(
5862
db,
5963
current_tx: None,
6064
insert_statement: None,
65+
state: {
66+
// Increase refcount - we can't use from_raw alone because we don't own the aux
67+
// data (connect could be called multiple times).
68+
let state = Arc::from_raw(aux as *mut DatabaseState);
69+
let clone = state.clone();
70+
core::mem::forget(state);
71+
clone
72+
},
6173
}));
6274
*vtab = tab.cast::<sqlite::vtab>();
6375
let _ = sqlite::vtab_config(db, 0);
@@ -127,13 +139,20 @@ fn insert_operation(
127139
flags: TableInfoFlags,
128140
) -> Result<(), SQLiteError> {
129141
let tab = unsafe { &mut *(vtab.cast::<VirtualTable>()) };
130-
if tab.current_tx.is_none() {
142+
if tab.state.is_in_sync_local.load(Ordering::Relaxed) {
131143
return Err(SQLiteError(
132144
ResultCode::MISUSE,
133-
Some(String::from("No tx_id")),
145+
Some(String::from("Using ps_crud during sync operation")),
134146
));
135147
}
136-
let current_tx = tab.current_tx.unwrap();
148+
149+
let Some(current_tx) = tab.current_tx else {
150+
return Err(SQLiteError(
151+
ResultCode::MISUSE,
152+
Some(String::from("No tx_id")),
153+
));
154+
};
155+
137156
// language=SQLite
138157
let statement = tab
139158
.insert_statement
@@ -206,8 +225,13 @@ static MODULE: sqlite_nostd::module = sqlite_nostd::module {
206225
xIntegrity: None,
207226
};
208227

209-
pub fn register(db: *mut sqlite::sqlite3) -> Result<(), ResultCode> {
210-
db.create_module_v2("powersync_crud_", &MODULE, None, None)?;
228+
pub fn register(db: *mut sqlite::sqlite3, state: Arc<DatabaseState>) -> Result<(), ResultCode> {
229+
db.create_module_v2(
230+
"powersync_crud_",
231+
&MODULE,
232+
Some(Arc::into_raw(state) as *mut c_void),
233+
Some(DatabaseState::destroy_arc),
234+
)?;
211235

212236
Ok(())
213237
}

crates/core/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ fn init_extension(db: *mut sqlite::sqlite3) -> Result<(), ResultCode> {
7373

7474
crate::schema::register(db)?;
7575
crate::operations_vtab::register(db, state.clone())?;
76-
crate::crud_vtab::register(db)?;
76+
crate::crud_vtab::register(db, state)?;
7777

7878
Ok(())
7979
}

crates/core/src/sync/storage_adapter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use core::{assert_matches::debug_assert_matches, fmt::Display};
22

3-
use alloc::{string::ToString, sync::Arc, vec::Vec};
3+
use alloc::{string::ToString, vec::Vec};
44
use serde::Serialize;
55
use sqlite_nostd::{self as sqlite, Connection, ManagedStmt, ResultCode};
66
use streaming_iterator::StreamingIterator;

dart/test/sync_test.dart

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,50 @@ END;
780780

781781
expect(db.select('SELECT * FROM users'), isEmpty);
782782
});
783+
784+
test("can't use crud vtab during sync", () {
785+
db.execute(
786+
'CREATE TABLE users (id TEXT NOT NULL PRIMARY KEY, name TEXT NOT NULL) STRICT;');
787+
788+
invokeControl(
789+
'start',
790+
json.encode({
791+
'schema': {
792+
'raw_tables': [
793+
{
794+
'name': 'users',
795+
'put': {
796+
// Inserting into powersync_crud_ during a sync operation is
797+
// forbidden, that vtab should only collect local writes.
798+
'sql': "INSERT INTO powersync_crud_(data) VALUES (?);",
799+
'params': [
800+
{'Column': 'name'}
801+
],
802+
},
803+
'delete': {
804+
'sql': 'DELETE FROM users WHERE id = ?',
805+
'params': ['Id'],
806+
},
807+
}
808+
],
809+
'tables': [],
810+
},
811+
}),
812+
);
813+
814+
// Insert
815+
pushCheckpoint(buckets: [bucketDescription('a')]);
816+
pushSyncData(
817+
'a',
818+
'1',
819+
'my_user',
820+
'PUT',
821+
{'name': 'First user'},
822+
objectType: 'users',
823+
);
824+
825+
expect(pushCheckpointComplete, throwsA(isA<SqliteException>()));
826+
});
783827
});
784828
}
785829

0 commit comments

Comments
 (0)