Skip to content

Commit 54b2cb8

Browse files
committed
Remove helpers for legacy sync clients
1 parent 90358fc commit 54b2cb8

14 files changed

Lines changed: 158 additions & 667 deletions

crates/core/src/checkpoint.rs

Lines changed: 0 additions & 71 deletions
This file was deleted.

crates/core/src/lib.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use sqlite::ResultCode;
1010
use crate::{error::PowerSyncError, state::DatabaseState};
1111

1212
mod bson;
13-
mod checkpoint;
1413
mod constants;
1514
mod crud_vtab;
1615
mod diff;
@@ -21,8 +20,7 @@ mod json_util;
2120
mod kv;
2221
mod macros;
2322
mod migrations;
24-
mod operations;
25-
mod operations_vtab;
23+
mod pre_close_vtab;
2624
mod schema;
2725
mod state;
2826
mod sync;
@@ -73,14 +71,13 @@ fn init_extension(db: *mut sqlite::sqlite3) -> Result<(), PowerSyncError> {
7371
crate::fix_data::register(db)?;
7472
crate::json_util::register(db)?;
7573
crate::view_admin::register(db, state.clone())?;
76-
crate::checkpoint::register(db)?;
7774
crate::kv::register(db)?;
7875
crate::state::register(db, state.clone())?;
7976
sync::register(db, state.clone())?;
8077
update_hooks::register(db, state.clone())?;
8178

8279
crate::schema::register(db, state.clone())?;
83-
crate::operations_vtab::register(db, state.clone())?;
80+
crate::pre_close_vtab::register(db, state.clone())?;
8481
crate::crud_vtab::register(db, state)?;
8582

8683
Ok(())

crates/core/src/operations.rs

Lines changed: 0 additions & 69 deletions
This file was deleted.
Lines changed: 14 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,21 @@ use alloc::rc::Rc;
55
use core::ffi::{c_char, c_int, c_void};
66

77
use powersync_sqlite_nostd as sqlite;
8-
use sqlite::{Connection, ResultCode, Value};
8+
use sqlite::{Connection, ResultCode};
99

10-
use crate::operations::{
11-
clear_remove_ops, delete_bucket, delete_pending_buckets, insert_operation,
12-
};
1310
use crate::state::DatabaseState;
14-
use crate::sync_local::sync_local;
1511
use crate::vtab_util::*;
1612

13+
/// A virtual table hack to implement a "pre-close hook" for databases.
14+
///
15+
/// When `sqlite3_close` is called, SQLite invokes the disconnect callback of virtual tables
16+
/// attached to the connection. This gives us an opportunity to call
17+
/// [DatabaseState::release_resources], allowing us to close active sync clients and associated
18+
/// prepared statements. Without this, our internal statements might lease resources.
1719
#[repr(C)]
1820
struct VirtualTable {
1921
base: sqlite::vtab,
20-
db: *mut sqlite::sqlite3,
2122
state: Rc<DatabaseState>,
22-
23-
target_applied: bool,
24-
target_validated: bool,
2523
}
2624

2725
extern "C" fn connect(
@@ -32,9 +30,7 @@ extern "C" fn connect(
3230
vtab: *mut *mut sqlite::vtab,
3331
_err: *mut *mut c_char,
3432
) -> c_int {
35-
if let Err(rc) =
36-
sqlite::declare_vtab(db, "CREATE TABLE powersync_operations(op TEXT, data TEXT);")
37-
{
33+
if let Err(rc) = sqlite::declare_vtab(db, "CREATE TABLE powersync_internal_close(_ TEXT);") {
3834
return rc as c_int;
3935
}
4036

@@ -45,10 +41,7 @@ extern "C" fn connect(
4541
pModule: core::ptr::null(),
4642
zErrMsg: core::ptr::null_mut(),
4743
},
48-
db,
4944
state: DatabaseState::clone_from(aux),
50-
target_validated: false,
51-
target_applied: false,
5245
}));
5346
*vtab = tab.cast::<sqlite::vtab>();
5447
let _ = sqlite::vtab_config(db, 0);
@@ -69,57 +62,12 @@ extern "C" fn disconnect(vtab: *mut sqlite::vtab) -> c_int {
6962
}
7063

7164
extern "C" fn update(
72-
vtab: *mut sqlite::vtab,
73-
argc: c_int,
74-
argv: *mut *mut sqlite::value,
75-
p_row_id: *mut sqlite::int64,
65+
_vtab: *mut sqlite::vtab,
66+
_argc: c_int,
67+
_argv: *mut *mut sqlite::value,
68+
_p_row_id: *mut sqlite::int64,
7669
) -> c_int {
77-
let args = sqlite::args!(argc, argv);
78-
79-
let rowid = args[0];
80-
81-
return if args.len() == 1 {
82-
// DELETE
83-
ResultCode::MISUSE as c_int
84-
} else if rowid.value_type() == sqlite::ColumnType::Null {
85-
// INSERT
86-
let op = args[2].text();
87-
88-
let tab = unsafe { &mut *vtab.cast::<VirtualTable>() };
89-
let db = tab.db;
90-
91-
if op == "save" {
92-
let result = insert_operation(db, args[3].text());
93-
vtab_result(vtab, result)
94-
} else if op == "sync_local" {
95-
let result = sync_local(&tab.state, db, &args[3]);
96-
if let Ok(result_row) = result {
97-
unsafe {
98-
*p_row_id = result_row;
99-
}
100-
}
101-
vtab_result(vtab, result)
102-
} else if op == "clear_remove_ops" {
103-
let result = clear_remove_ops(db, args[3].text());
104-
vtab_result(vtab, result)
105-
} else if op == "delete_pending_buckets" {
106-
let result = delete_pending_buckets(db, args[3].text());
107-
vtab_result(vtab, result)
108-
} else if op == "delete_bucket" {
109-
let result = delete_bucket(db, args[3].text());
110-
vtab_result(vtab, result)
111-
} else if op == "noop" {
112-
// We call this to ensure the table is added to an active database, ensuring that
113-
// the disconnect callback runs before the database is closed (allowing us to free
114-
// resources from the client state).
115-
ResultCode::OK as c_int
116-
} else {
117-
ResultCode::MISUSE as c_int
118-
}
119-
} else {
120-
// UPDATE - not supported
121-
ResultCode::MISUSE as c_int
122-
} as c_int;
70+
0
12371
}
12472

12573
// Insert-only virtual table.
@@ -155,7 +103,7 @@ static MODULE: sqlite::module = sqlite::module {
155103

156104
pub fn register(db: *mut sqlite::sqlite3, state: Rc<DatabaseState>) -> Result<(), ResultCode> {
157105
db.create_module_v2(
158-
"powersync_operations",
106+
"powersync_internal_close",
159107
&MODULE,
160108
Some(Rc::into_raw(state) as *mut c_void),
161109
Some(DatabaseState::destroy_rc),

crates/core/src/sync/interface.rs

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -224,24 +224,14 @@ pub fn register(db: *mut sqlite::sqlite3, state: Rc<DatabaseState>) -> Result<()
224224

225225
let op = op.text();
226226
let event = match op {
227-
"start" => {
228-
// Ensure the operations vtab exists. It's not actually used by the sync client,
229-
// but we rely on that vtab being destroyed as a pre-close hook for the database
230-
// connection to free statements preserved across multiple powersync_control
231-
// invocations.
232-
db.exec_safe(
233-
"insert into powersync_operations (op, data) VALUES ('noop', null);",
234-
)?;
235-
236-
SyncControlRequest::StartSyncStream({
237-
if payload.value_type() == ColumnType::Text {
238-
serde_json::from_str(payload.text())
239-
.map_err(PowerSyncError::as_argument_error)?
240-
} else {
241-
StartSyncStream::default()
242-
}
243-
})
244-
}
227+
"start" => SyncControlRequest::StartSyncStream({
228+
if payload.value_type() == ColumnType::Text {
229+
serde_json::from_str(payload.text())
230+
.map_err(PowerSyncError::as_argument_error)?
231+
} else {
232+
StartSyncStream::default()
233+
}
234+
}),
245235
"stop" => SyncControlRequest::StopSyncStream,
246236
"line_text" => SyncControlRequest::SyncEvent(SyncEvent::TextLine {
247237
data: if payload.value_type() == ColumnType::Text {

crates/core/src/sync/storage_adapter.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use serde::Serialize;
77
use crate::{
88
error::{PSResult, PowerSyncError},
99
ext::SafeManagedStmt,
10-
operations::delete_bucket,
1110
schema::Schema,
1211
state::DatabaseState,
1312
sync::{
@@ -132,9 +131,48 @@ impl StorageAdapter {
132131
&self,
133132
buckets: impl IntoIterator<Item = &'a str>,
134133
) -> Result<(), ResultCode> {
134+
// Prepare statements lazily, this method may be called without any buckets to delete.
135+
let mut delete_bucket_returning_id = None::<ManagedStmt>;
136+
let mut mark_updated = None::<ManagedStmt>;
137+
let mut delete_oplog = None::<ManagedStmt>;
138+
135139
for bucket in buckets {
136-
// TODO: This is a neat opportunity to create the statements here and cache them
137-
delete_bucket(self.db, bucket)?;
140+
let delete_bucket_returning_id = match delete_bucket_returning_id {
141+
Some(ref stmt) => stmt,
142+
None => delete_bucket_returning_id.insert(
143+
self.db
144+
.prepare_v2("DELETE FROM ps_buckets WHERE name = ?1 RETURNING id")?,
145+
),
146+
};
147+
148+
delete_bucket_returning_id.bind_text(1, bucket, sqlite::Destructor::STATIC)?;
149+
if let ResultCode::ROW = delete_bucket_returning_id.step()? {
150+
let bucket_id = delete_bucket_returning_id.column_int64(0);
151+
152+
let mark_updated = match mark_updated {
153+
Some(ref stmt) => stmt,
154+
None => mark_updated.insert(self.db.prepare_v2(
155+
"\
156+
INSERT OR IGNORE INTO ps_updated_rows(row_type, row_id)
157+
SELECT row_type, row_id
158+
FROM ps_oplog
159+
WHERE bucket = ?1",
160+
)?),
161+
};
162+
let delete_oplog = match delete_oplog {
163+
Some(ref stmt) => stmt,
164+
None => delete_oplog
165+
.insert(self.db.prepare_v2("DELETE FROM ps_oplog WHERE bucket=?1")?),
166+
};
167+
168+
mark_updated.bind_int64(1, bucket_id)?;
169+
mark_updated.exec()?;
170+
171+
delete_oplog.bind_int64(1, bucket_id)?;
172+
delete_oplog.exec()?;
173+
}
174+
175+
delete_bucket_returning_id.reset()?;
138176
}
139177

140178
Ok(())

0 commit comments

Comments
 (0)