@@ -48,17 +48,17 @@ mod triggers;
4848mod unpack_columns_vtab;
4949mod util;
5050
51- use alloc:: borrow:: Cow ;
5251use alloc:: format;
5352use alloc:: string:: ToString ;
53+ use alloc:: { borrow:: Cow , boxed:: Box , collections:: BTreeMap , vec:: Vec } ;
5454use core:: ffi:: c_char;
5555use core:: mem;
5656use core:: ptr:: null_mut;
5757extern crate alloc;
5858use alter:: crsql_compact_post_alter;
5959use automigrate:: * ;
6060use backfill:: * ;
61- use c:: { crsql_freeExtData, crsql_newExtData} ;
61+ use c:: { crsql_freeExtData, crsql_initSiteIdExt , crsql_newExtData} ;
6262use config:: { crsql_config_get, crsql_config_set} ;
6363use core:: ffi:: { c_int, c_void, CStr } ;
6464use create_crr:: create_crr;
@@ -232,6 +232,30 @@ pub extern "C" fn sqlite3_crsqlcore_init(
232232 return null_mut ( ) ;
233233 }
234234
235+ // allocate ext data earlier in the init process because we need its
236+ // pointer to be available for the crsql_update_site_id function.
237+ let ext_data = unsafe { crsql_newExtData ( db) } ;
238+ if ext_data. is_null ( ) {
239+ return null_mut ( ) ;
240+ }
241+
242+ let rc = db
243+ . create_function_v2 (
244+ "crsql_update_site_id" ,
245+ 2 ,
246+ sqlite:: UTF8 | sqlite:: INNOCUOUS | sqlite:: DETERMINISTIC ,
247+ Some ( ext_data as * mut c_void ) ,
248+ Some ( x_crsql_update_site_id) ,
249+ None ,
250+ None ,
251+ None ,
252+ )
253+ . unwrap_or ( ResultCode :: ERROR ) ;
254+ if rc != ResultCode :: OK {
255+ unsafe { crsql_freeExtData ( ext_data) } ;
256+ return null_mut ( ) ;
257+ }
258+
235259 // TODO: convert this function to a proper rust function
236260 // and have rust free:
237261 // 1. site_id_buffer
@@ -243,12 +267,18 @@ pub extern "C" fn sqlite3_crsqlcore_init(
243267 let rc = crate :: bootstrap:: crsql_init_site_id ( db, site_id_buffer) ;
244268 if rc != ResultCode :: OK as c_int {
245269 sqlite:: free ( site_id_buffer as * mut c_void ) ;
270+ unsafe { crsql_freeExtData ( ext_data) } ;
246271 return null_mut ( ) ;
247272 }
248273
249- let ext_data = unsafe { crsql_newExtData ( db, site_id_buffer as * mut c_char ) } ;
250- if ext_data. is_null ( ) {
251- // no need to free the site id buffer here, this is cleaned up already.
274+ let rc = unsafe { crsql_initSiteIdExt ( db, ext_data, site_id_buffer as * mut c_char ) } ;
275+ if rc != ResultCode :: OK as c_int {
276+ unsafe { crsql_freeExtData ( ext_data) } ;
277+ return null_mut ( ) ;
278+ }
279+
280+ if let Err ( _) = crate :: bootstrap:: create_site_id_triggers ( db) {
281+ sqlite:: free ( site_id_buffer as * mut c_void ) ;
252282 return null_mut ( ) ;
253283 }
254284
@@ -408,7 +438,7 @@ pub extern "C" fn sqlite3_crsqlcore_init(
408438 let rc = db
409439 . create_function_v2 (
410440 "crsql_set_ts" ,
411- - 1 ,
441+ 1 ,
412442 sqlite:: UTF8 | sqlite:: DETERMINISTIC ,
413443 Some ( ext_data as * mut c_void ) ,
414444 Some ( x_crsql_set_ts) ,
@@ -422,6 +452,24 @@ pub extern "C" fn sqlite3_crsqlcore_init(
422452 return null_mut ( ) ;
423453 }
424454
455+ #[ cfg( feature = "test" ) ]
456+ let rc = db
457+ . create_function_v2 (
458+ "crsql_cache_site_ordinal" ,
459+ 1 ,
460+ sqlite:: UTF8 | sqlite:: DETERMINISTIC ,
461+ Some ( ext_data as * mut c_void ) ,
462+ Some ( x_crsql_cache_site_ordinal) ,
463+ None ,
464+ None ,
465+ None ,
466+ )
467+ . unwrap_or ( ResultCode :: ERROR ) ;
468+ if rc != ResultCode :: OK {
469+ unsafe { crsql_freeExtData ( ext_data) } ;
470+ return null_mut ( ) ;
471+ }
472+
425473 let rc = db
426474 . create_function_v2 (
427475 "crsql_set_db_version" ,
@@ -627,6 +675,32 @@ unsafe extern "C" fn x_crsql_site_id(
627675 sqlite:: result_blob ( ctx, site_id, consts:: SITE_ID_LEN , Destructor :: STATIC ) ;
628676}
629677
678+ /**
679+ * update in-memory map of site ids to ordinals. Only valid within a transaction.
680+ *
681+ * `select crsql_update_site_id(site_id, ordinal)`
682+ */
683+ unsafe extern "C" fn x_crsql_update_site_id (
684+ ctx : * mut sqlite:: context ,
685+ argc : i32 ,
686+ argv : * mut * mut sqlite:: value ,
687+ ) {
688+ let ext_data = ctx. user_data ( ) as * mut c:: crsql_ExtData ;
689+ let args = sqlite:: args!( argc, argv) ;
690+ let site_id = args[ 0 ] . blob ( ) ;
691+ let ordinal = args[ 1 ] . int64 ( ) ;
692+ let mut ordinals: mem:: ManuallyDrop < Box < BTreeMap < Vec < u8 > , i64 > > > = mem:: ManuallyDrop :: new (
693+ Box :: from_raw ( ( * ext_data) . ordinalMap as * mut BTreeMap < Vec < u8 > , i64 > ) ,
694+ ) ;
695+
696+ if ordinal == -1 {
697+ ordinals. remove ( & site_id. to_vec ( ) ) ;
698+ } else {
699+ ordinals. insert ( site_id. to_vec ( ) , ordinal) ;
700+ }
701+ ctx. result_text_static ( "OK" ) ;
702+ }
703+
630704unsafe extern "C" fn x_crsql_finalize (
631705 ctx : * mut sqlite:: context ,
632706 _argc : i32 ,
@@ -854,10 +928,7 @@ unsafe extern "C" fn x_crsql_set_ts(
854928 argv : * mut * mut sqlite:: value ,
855929) {
856930 if argc == 0 {
857- ctx. result_error (
858- "Wrong number of args provided to crsql_begin_alter. Provide the
859- schema name and table name or just the table name." ,
860- ) ;
931+ ctx. result_error ( "Wrong number of args provided to x_crsql_set_ts. Provide the timestamp." ) ;
861932 return ;
862933 }
863934
@@ -876,6 +947,37 @@ unsafe extern "C" fn x_crsql_set_ts(
876947 ctx. result_text_static ( "OK" ) ;
877948}
878949
950+ /**
951+ * Get the site ordinal cached in the ext data for the current transaction.
952+ * only used for test to inspect the ordinal map.
953+ */
954+ #[ cfg( feature = "test" ) ]
955+ unsafe extern "C" fn x_crsql_cache_site_ordinal (
956+ ctx : * mut sqlite:: context ,
957+ argc : i32 ,
958+ argv : * mut * mut sqlite:: value ,
959+ ) {
960+ if argc == 0 {
961+ ctx. result_error (
962+ "Wrong number of args provided to crsql_cache_site_ordinal. Provide the site id." ,
963+ ) ;
964+ return ;
965+ }
966+
967+ let ext_data = ctx. user_data ( ) as * mut c:: crsql_ExtData ;
968+ let args = sqlite:: args!( argc, argv) ;
969+ let site_id = args[ 0 ] . blob ( ) ;
970+
971+ let ord_map = mem:: ManuallyDrop :: new ( Box :: from_raw (
972+ ( * ext_data) . ordinalMap as * mut BTreeMap < Vec < u8 > , i64 > ,
973+ ) ) ;
974+ let res = ord_map. get ( site_id) . cloned ( ) . unwrap_or ( -1 ) ;
975+ sqlite:: result_int64 ( ctx, res) ;
976+ }
977+
978+ /**
979+ * Return the timestamp for the current transaction.
980+ */
879981unsafe extern "C" fn x_crsql_get_ts (
880982 ctx : * mut sqlite:: context ,
881983 _argc : i32 ,
0 commit comments