@@ -49,14 +49,20 @@ struct ActiveCrudTransaction {
4949}
5050
5151enum CrudTransactionMode {
52- Manual {
53- stmt : ManagedStmt ,
54- } ,
55- Simple {
56- stmt : ManagedStmt ,
57- set_updated_rows : ManagedStmt ,
58- had_writes : bool ,
59- } ,
52+ Manual ( ManualCrudTransactionMode ) ,
53+ Simple ( SimpleCrudTransactionMode ) ,
54+ }
55+
56+ #[ derive( Default ) ]
57+ struct ManualCrudTransactionMode {
58+ stmt : Option < ManagedStmt > ,
59+ }
60+
61+ #[ derive( Default ) ]
62+ struct SimpleCrudTransactionMode {
63+ stmt : Option < ManagedStmt > ,
64+ set_updated_rows : Option < ManagedStmt > ,
65+ had_writes : bool ,
6066}
6167
6268impl VirtualTable {
@@ -78,26 +84,24 @@ impl VirtualTable {
7884 . current_tx
7985 . as_mut ( )
8086 . ok_or_else ( || SQLiteError ( ResultCode :: MISUSE , Some ( String :: from ( "No tx_id" ) ) ) ) ?;
87+ let db = self . db ;
8188
8289 match & mut current_tx. mode {
83- CrudTransactionMode :: Manual { stmt } => {
90+ CrudTransactionMode :: Manual ( manual ) => {
8491 // Columns are (data TEXT, options INT HIDDEN)
8592 let data = args[ 0 ] . text ( ) ;
8693 let flags = match args[ 1 ] . value_type ( ) {
8794 sqlite_nostd:: ColumnType :: Null => TableInfoFlags :: default ( ) ,
8895 _ => TableInfoFlags ( args[ 1 ] . int ( ) as u32 ) ,
8996 } ;
9097
98+ let stmt = manual. raw_crud_statement ( db) ?;
9199 stmt. bind_int64 ( 1 , current_tx. tx_id ) ?;
92100 stmt. bind_text ( 2 , data, sqlite:: Destructor :: STATIC ) ?;
93101 stmt. bind_int ( 3 , flags. 0 as i32 ) ?;
94102 stmt. exec ( ) ?;
95103 }
96- CrudTransactionMode :: Simple {
97- stmt,
98- set_updated_rows,
99- had_writes,
100- } => {
104+ CrudTransactionMode :: Simple ( simple) => {
101105 // Columns are (op TEXT, id TEXT, type TEXT, data TEXT, old_values TEXT, metadata TEXT, options INT HIDDEN)
102106 let flags = match args[ 6 ] . value_type ( ) {
103107 sqlite_nostd:: ColumnType :: Null => TableInfoFlags :: default ( ) ,
@@ -133,6 +137,7 @@ impl VirtualTable {
133137
134138 // First, we insert into ps_crud like the manual vtab would too. We have to create
135139 // the JSON out of the individual components for that.
140+ let stmt = simple. raw_crud_statement ( db) ?;
136141 stmt. bind_int64 ( 1 , current_tx. tx_id ) ?;
137142
138143 let serialized = serde_json:: to_string ( & CrudEntry {
@@ -151,10 +156,11 @@ impl VirtualTable {
151156 stmt. exec ( ) ?;
152157
153158 // However, we also set ps_updated_rows and update the $local bucket
159+ let set_updated_rows = simple. set_updated_rows_statement ( db) ?;
154160 set_updated_rows. bind_text ( 1 , row_type, sqlite:: Destructor :: STATIC ) ?;
155161 set_updated_rows. bind_text ( 2 , id, sqlite:: Destructor :: STATIC ) ?;
156162 set_updated_rows. exec ( ) ?;
157- * had_writes = true ;
163+ simple . had_writes = true ;
158164 }
159165 }
160166
@@ -176,30 +182,9 @@ impl VirtualTable {
176182 self . current_tx = Some ( ActiveCrudTransaction {
177183 tx_id,
178184 mode : if self . is_simple {
179- CrudTransactionMode :: Simple {
180- // language=SQLite
181- stmt : db. prepare_v3 ( "INSERT INTO ps_crud(tx_id, data) VALUES (?, ?)" , 0 ) ?,
182- // language=SQLite
183- set_updated_rows : db. prepare_v3 (
184- "INSERT OR IGNORE INTO ps_updated_rows(row_type, row_id) VALUES(?, ?)" ,
185- 0 ,
186- ) ?,
187- had_writes : false ,
188- }
185+ CrudTransactionMode :: Simple ( Default :: default ( ) )
189186 } else {
190- const SQL : & str = formatcp ! (
191- "\
192- WITH insertion (tx_id, data) AS (VALUES (?1, ?2))
193- INSERT INTO ps_crud(tx_id, data)
194- SELECT * FROM insertion WHERE (NOT (?3 & {})) OR data->>'op' != 'PATCH' OR data->'data' != '{{}}';
195- " ,
196- TableInfoFlags :: IGNORE_EMPTY_UPDATE
197- ) ;
198-
199- let insert_statement = db. prepare_v3 ( SQL , 0 ) ?;
200- CrudTransactionMode :: Manual {
201- stmt : insert_statement,
202- }
187+ CrudTransactionMode :: Manual ( Default :: default ( ) )
203188 } ,
204189 } ) ;
205190
@@ -214,11 +199,7 @@ SELECT * FROM insertion WHERE (NOT (?3 & {})) OR data->>'op' != 'PATCH' OR data-
214199 // In manual mode, users need to update the $local bucket themselves.
215200 false
216201 }
217- CrudTransactionMode :: Simple {
218- had_writes,
219- stmt : _,
220- set_updated_rows : _,
221- } => had_writes,
202+ CrudTransactionMode :: Simple ( simple) => simple. had_writes ,
222203 } ;
223204
224205 if needs_local_bucket_update {
@@ -234,6 +215,57 @@ SELECT * FROM insertion WHERE (NOT (?3 & {})) OR data->>'op' != 'PATCH' OR data-
234215 }
235216}
236217
218+ impl ManualCrudTransactionMode {
219+ fn raw_crud_statement ( & mut self , db : * mut sqlite:: sqlite3 ) -> Result < & ManagedStmt , ResultCode > {
220+ prepare_lazy ( & mut self . stmt , || {
221+ const SQL : & str = formatcp ! (
222+ "\
223+ WITH insertion (tx_id, data) AS (VALUES (?1, ?2))
224+ INSERT INTO ps_crud(tx_id, data)
225+ SELECT * FROM insertion WHERE (NOT (?3 & {})) OR data->>'op' != 'PATCH' OR data->'data' != '{{}}';
226+ " ,
227+ TableInfoFlags :: IGNORE_EMPTY_UPDATE
228+ ) ;
229+
230+ db. prepare_v3 ( SQL , 0 )
231+ } )
232+ }
233+ }
234+
235+ impl SimpleCrudTransactionMode {
236+ fn raw_crud_statement ( & mut self , db : * mut sqlite:: sqlite3 ) -> Result < & ManagedStmt , ResultCode > {
237+ prepare_lazy ( & mut self . stmt , || {
238+ // language=SQLite
239+ db. prepare_v3 ( "INSERT INTO ps_crud(tx_id, data) VALUES (?, ?)" , 0 )
240+ } )
241+ }
242+
243+ fn set_updated_rows_statement (
244+ & mut self ,
245+ db : * mut sqlite:: sqlite3 ,
246+ ) -> Result < & ManagedStmt , ResultCode > {
247+ prepare_lazy ( & mut self . set_updated_rows , || {
248+ // language=SQLite
249+ db. prepare_v3 (
250+ "INSERT OR IGNORE INTO ps_updated_rows(row_type, row_id) VALUES(?, ?)" ,
251+ 0 ,
252+ )
253+ } )
254+ }
255+ }
256+
257+ /// A variant of `Option.get_or_insert` that handles insertions returning errors.
258+ fn prepare_lazy (
259+ stmt : & mut Option < ManagedStmt > ,
260+ prepare : impl FnOnce ( ) -> Result < ManagedStmt , ResultCode > ,
261+ ) -> Result < & ManagedStmt , ResultCode > {
262+ if let None = stmt {
263+ * stmt = Some ( prepare ( ) ?) ;
264+ }
265+
266+ return Ok ( unsafe { stmt. as_ref ( ) . unwrap_unchecked ( ) } ) ;
267+ }
268+
237269extern "C" fn connect (
238270 db : * mut sqlite:: sqlite3 ,
239271 _aux : * mut c_void ,
0 commit comments