Skip to content

Commit 2e6a43e

Browse files
authored
Merge pull request #21 from powersync-ja/client-id
powersync_client_id() / powersync_last_synced_at() / powersync_clear()
2 parents 1504864 + a22137f commit 2e6a43e

File tree

10 files changed

+372
-275
lines changed

10 files changed

+372
-275
lines changed

Diff for: crates/core/src/error.rs

+1-14
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
use alloc::string::{String, ToString};
22
use core::error::Error;
3-
use sqlite_nostd::{Connection, ResultCode, sqlite3};
4-
3+
use sqlite_nostd::{sqlite3, Connection, ResultCode};
54

65
#[derive(Debug)]
76
pub struct SQLiteError(pub ResultCode, pub Option<String>);
87

9-
108
impl core::fmt::Display for SQLiteError {
119
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1210
write!(f, "{:?}", self)
@@ -16,21 +14,10 @@ impl core::fmt::Display for SQLiteError {
1614
impl Error for SQLiteError {}
1715

1816
pub trait PSResult<T> {
19-
fn into_result(self) -> Result<T, SQLiteError>;
2017
fn into_db_result(self, db: *mut sqlite3) -> Result<T, SQLiteError>;
2118
}
2219

2320
impl<T> PSResult<T> for Result<T, ResultCode> {
24-
fn into_result(self) -> Result<T, SQLiteError> {
25-
if let Err(code) = self {
26-
Err(SQLiteError(code, None))
27-
} else if let Ok(r) = self {
28-
Ok(r)
29-
} else {
30-
Err(SQLiteError(ResultCode::ABORT, None))
31-
}
32-
}
33-
3421
fn into_db_result(self, db: *mut sqlite3) -> Result<T, SQLiteError> {
3522
if let Err(code) = self {
3623
let message = db.errmsg().unwrap_or(String::from("Conversion error"));

Diff for: crates/core/src/kv.rs

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
extern crate alloc;
2+
3+
use alloc::format;
4+
use alloc::string::{String, ToString};
5+
use core::ffi::c_int;
6+
use core::slice;
7+
8+
use sqlite::ResultCode;
9+
use sqlite_nostd as sqlite;
10+
use sqlite_nostd::{Connection, Context};
11+
12+
use crate::create_sqlite_optional_text_fn;
13+
use crate::create_sqlite_text_fn;
14+
use crate::error::SQLiteError;
15+
16+
fn powersync_client_id_impl(
17+
ctx: *mut sqlite::context,
18+
_args: &[*mut sqlite::value],
19+
) -> Result<String, SQLiteError> {
20+
let db = ctx.db_handle();
21+
22+
// language=SQLite
23+
let statement = db.prepare_v2("select value from ps_kv where key = 'client_id'")?;
24+
25+
if statement.step()? == ResultCode::ROW {
26+
let client_id = statement.column_text(0)?;
27+
return Ok(client_id.to_string());
28+
} else {
29+
return Err(SQLiteError(
30+
ResultCode::ABORT,
31+
Some(format!("No client_id found in ps_kv")),
32+
));
33+
}
34+
}
35+
36+
create_sqlite_text_fn!(
37+
powersync_client_id,
38+
powersync_client_id_impl,
39+
"powersync_last_synced_at"
40+
);
41+
42+
fn powersync_last_synced_at_impl(
43+
ctx: *mut sqlite::context,
44+
_args: &[*mut sqlite::value],
45+
) -> Result<Option<String>, SQLiteError> {
46+
let db = ctx.db_handle();
47+
48+
// language=SQLite
49+
let statement = db.prepare_v2("select value from ps_kv where key = 'last_synced_at'")?;
50+
51+
if statement.step()? == ResultCode::ROW {
52+
let client_id = statement.column_text(0)?;
53+
return Ok(Some(client_id.to_string()));
54+
} else {
55+
return Ok(None);
56+
}
57+
}
58+
59+
create_sqlite_optional_text_fn!(
60+
powersync_last_synced_at,
61+
powersync_last_synced_at_impl,
62+
"powersync_last_synced_at"
63+
);
64+
65+
pub fn register(db: *mut sqlite::sqlite3) -> Result<(), ResultCode> {
66+
db.create_function_v2(
67+
"powersync_client_id",
68+
0,
69+
sqlite::UTF8 | sqlite::DETERMINISTIC,
70+
None,
71+
Some(powersync_client_id),
72+
None,
73+
None,
74+
None,
75+
)?;
76+
db.create_function_v2(
77+
"powersync_last_synced_at",
78+
0,
79+
sqlite::UTF8 | sqlite::DETERMINISTIC,
80+
None,
81+
Some(powersync_last_synced_at),
82+
None,
83+
None,
84+
None,
85+
)?;
86+
87+
Ok(())
88+
}

Diff for: crates/core/src/lib.rs

+24-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![no_std]
22
#![feature(vec_into_raw_parts)]
3+
#![allow(internal_features)]
34
#![feature(core_intrinsics)]
45
#![feature(error_in_core)]
56
#![feature(assert_matches)]
@@ -11,23 +12,24 @@ use core::ffi::{c_char, c_int};
1112
use sqlite::ResultCode;
1213
use sqlite_nostd as sqlite;
1314

14-
mod util;
15-
mod uuid;
16-
mod views;
17-
mod view_admin;
18-
mod macros;
15+
mod checkpoint;
16+
mod crud_vtab;
1917
mod diff;
20-
mod schema_management;
21-
mod operations_vtab;
22-
mod operations;
23-
mod ext;
2418
mod error;
25-
mod crud_vtab;
26-
mod vtab_util;
19+
mod ext;
20+
mod kv;
21+
mod macros;
22+
mod operations;
23+
mod operations_vtab;
24+
mod schema_management;
2725
mod sync_local;
28-
mod checkpoint;
29-
mod version;
3026
mod sync_types;
27+
mod util;
28+
mod uuid;
29+
mod version;
30+
mod view_admin;
31+
mod views;
32+
mod vtab_util;
3133

3234
#[no_mangle]
3335
pub extern "C" fn sqlite3_powersync_init(
@@ -43,7 +45,7 @@ pub extern "C" fn sqlite3_powersync_init(
4345
code as c_int
4446
} else {
4547
ResultCode::OK as c_int
46-
}
48+
};
4749
}
4850

4951
fn init_extension(db: *mut sqlite::sqlite3) -> Result<(), ResultCode> {
@@ -53,6 +55,7 @@ fn init_extension(db: *mut sqlite::sqlite3) -> Result<(), ResultCode> {
5355
crate::diff::register(db)?;
5456
crate::view_admin::register(db)?;
5557
crate::checkpoint::register(db)?;
58+
crate::kv::register(db)?;
5659

5760
crate::schema_management::register(db)?;
5861
crate::operations_vtab::register(db)?;
@@ -61,12 +64,17 @@ fn init_extension(db: *mut sqlite::sqlite3) -> Result<(), ResultCode> {
6164
Ok(())
6265
}
6366

64-
6567
extern "C" {
6668
#[cfg(feature = "static")]
6769
#[allow(non_snake_case)]
6870
pub fn sqlite3_auto_extension(
69-
xEntryPoint: Option<extern "C" fn(*mut sqlite::sqlite3, *mut *mut c_char, *mut sqlite::api_routines) -> c_int>,
71+
xEntryPoint: Option<
72+
extern "C" fn(
73+
*mut sqlite::sqlite3,
74+
*mut *mut c_char,
75+
*mut sqlite::api_routines,
76+
) -> c_int,
77+
>,
7078
) -> ::core::ffi::c_int;
7179
}
7280

Diff for: crates/core/src/macros.rs

+35-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
#[macro_export]
32
macro_rules! create_sqlite_text_fn {
43
($fn_name:ident, $fn_impl_name:ident, $description:literal) => {
@@ -31,6 +30,41 @@ macro_rules! create_sqlite_text_fn {
3130
};
3231
}
3332

33+
#[macro_export]
34+
macro_rules! create_sqlite_optional_text_fn {
35+
($fn_name:ident, $fn_impl_name:ident, $description:literal) => {
36+
extern "C" fn $fn_name(
37+
ctx: *mut sqlite::context,
38+
argc: c_int,
39+
argv: *mut *mut sqlite::value,
40+
) {
41+
let args = sqlite::args!(argc, argv);
42+
43+
let result = $fn_impl_name(ctx, args);
44+
45+
if let Err(err) = result {
46+
let SQLiteError(code, message) = SQLiteError::from(err);
47+
if message.is_some() {
48+
ctx.result_error(&format!("{:} {:}", $description, message.unwrap()));
49+
} else {
50+
let error = ctx.db_handle().errmsg().unwrap();
51+
if error == "not an error" {
52+
ctx.result_error(&format!("{:}", $description));
53+
} else {
54+
ctx.result_error(&format!("{:} {:}", $description, error));
55+
}
56+
}
57+
ctx.result_error_code(code);
58+
} else if let Ok(r) = result {
59+
if let Some(s) = r {
60+
ctx.result_text_transient(&s);
61+
} else {
62+
ctx.result_null();
63+
}
64+
}
65+
}
66+
};
67+
}
3468

3569
// Wrap a function in an auto-transaction.
3670
// Gives the equivalent of SQLite's auto-commit behaviour, except that applies to all statements

Diff for: crates/core/src/operations.rs

+1-11
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
use alloc::format;
22
use alloc::string::{String, ToString};
3-
use alloc::vec::Vec;
4-
use serde::{Deserialize, Deserializer, Serialize};
5-
use serde_json as json;
63

74
use crate::error::{PSResult, SQLiteError};
85
use sqlite_nostd as sqlite;
96
use sqlite_nostd::{Connection, ResultCode};
107

118
use crate::ext::SafeManagedStmt;
12-
use crate::sync_types::{BucketChecksum, Checkpoint, StreamingSyncLine};
139
use crate::util::*;
1410

1511
// Run inside a transaction
@@ -142,7 +138,7 @@ INSERT INTO ps_oplog(bucket, op_id, op, key, row_type, row_id, data, hash, super
142138
supersede_statement.reset()?;
143139

144140
let should_skip_remove = !superseded && op == "REMOVE";
145-
if (should_skip_remove) {
141+
if should_skip_remove {
146142
// If a REMOVE statement did not replace (supersede) any previous
147143
// operations, we do not need to persist it.
148144
// The same applies if the bucket was not synced to the local db yet,
@@ -307,9 +303,3 @@ pub fn delete_bucket(db: *mut sqlite::sqlite3, name: &str) -> Result<(), SQLiteE
307303

308304
Ok(())
309305
}
310-
311-
pub fn stream_operation(db: *mut sqlite::sqlite3, data: &str) -> Result<(), SQLiteError> {
312-
let line: StreamingSyncLine = serde_json::from_str(data)?;
313-
314-
Ok(())
315-
}

Diff for: crates/core/src/operations_vtab.rs

+8-12
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,21 @@ use core::slice;
77
use sqlite::{Connection, ResultCode, Value};
88
use sqlite_nostd as sqlite;
99

10-
use crate::operations::{clear_remove_ops, delete_bucket, delete_pending_buckets, insert_operation, stream_operation};
10+
use crate::operations::{
11+
clear_remove_ops, delete_bucket, delete_pending_buckets, insert_operation,
12+
};
1113
use crate::sync_local::sync_local;
12-
use crate::sync_types::Checkpoint;
1314
use crate::vtab_util::*;
1415

1516
#[repr(C)]
1617
struct VirtualTable {
1718
base: sqlite::vtab,
1819
db: *mut sqlite::sqlite3,
1920

20-
target_checkpoint: Option<Checkpoint>,
2121
target_applied: bool,
22-
target_validated: bool
22+
target_validated: bool,
2323
}
2424

25-
2625
extern "C" fn connect(
2726
db: *mut sqlite::sqlite3,
2827
_aux: *mut c_void,
@@ -31,7 +30,8 @@ extern "C" fn connect(
3130
vtab: *mut *mut sqlite::vtab,
3231
_err: *mut *mut c_char,
3332
) -> c_int {
34-
if let Err(rc) = sqlite::declare_vtab(db, "CREATE TABLE powersync_operations(op TEXT, data TEXT);")
33+
if let Err(rc) =
34+
sqlite::declare_vtab(db, "CREATE TABLE powersync_operations(op TEXT, data TEXT);")
3535
{
3636
return rc as c_int;
3737
}
@@ -44,9 +44,8 @@ extern "C" fn connect(
4444
zErrMsg: core::ptr::null_mut(),
4545
},
4646
db,
47-
target_checkpoint: None,
4847
target_validated: false,
49-
target_applied: false
48+
target_applied: false,
5049
}));
5150
*vtab = tab.cast::<sqlite::vtab>();
5251
let _ = sqlite::vtab_config(db, 0);
@@ -102,10 +101,7 @@ extern "C" fn update(
102101
} else if op == "delete_bucket" {
103102
let result = delete_bucket(db, data);
104103
vtab_result(vtab, result)
105-
} else if op == "stream" {
106-
let result = stream_operation(db, data);
107-
vtab_result(vtab, result)
108-
} else {
104+
} else {
109105
ResultCode::MISUSE as c_int
110106
}
111107
} else {

0 commit comments

Comments
 (0)