11extern crate alloc;
22
3- use alloc:: format;
43use alloc:: string:: String ;
54use alloc:: vec:: Vec ;
5+ use alloc:: { format, vec} ;
66use core:: ffi:: c_int;
77
88use sqlite:: { Connection , ResultCode , Value } ;
@@ -14,6 +14,8 @@ use crate::ext::ExtendedDatabase;
1414use crate :: util:: { quote_identifier, quote_json_path} ;
1515use crate :: { create_auto_tx_function, create_sqlite_text_fn} ;
1616
17+ use super :: Schema ;
18+
1719fn update_tables ( db : * mut sqlite:: sqlite3 , schema : & str ) -> Result < ( ) , SQLiteError > {
1820 {
1921 // In a block so that the statement is finalized before dropping tables
@@ -138,87 +140,83 @@ SELECT name, internal_name, local_only FROM powersync_tables WHERE name NOT IN (
138140
139141fn update_indexes ( db : * mut sqlite:: sqlite3 , schema : & str ) -> Result < ( ) , SQLiteError > {
140142 let mut statements: Vec < String > = alloc:: vec![ ] ;
143+ let schema = serde_json:: from_str :: < Schema > ( schema) ?;
144+ let mut expected_index_names: Vec < String > = vec ! [ ] ;
141145
142146 {
143147 // In a block so that the statement is finalized before dropping indexes
144148 // language=SQLite
145- let statement = db. prepare_v2 ( "\
146- SELECT
147- powersync_internal_table_name(tables.value) as table_name,
148- (powersync_internal_table_name(tables.value) || '__' || json_extract(indexes.value, '$.name')) as index_name,
149- json_extract(indexes.value, '$.columns') as index_columns,
150- ifnull(sqlite_master.sql, '') as sql
151- FROM json_each(json_extract(?, '$.tables')) tables
152- CROSS JOIN json_each(json_extract(tables.value, '$.indexes')) indexes
153- LEFT JOIN sqlite_master ON sqlite_master.name = index_name AND sqlite_master.type = 'index'
154- " ) . into_db_result ( db) ?;
155- statement. bind_text ( 1 , schema, sqlite:: Destructor :: STATIC ) ?;
149+ let find_index =
150+ db. prepare_v2 ( "SELECT sql FROM sqlite_master WHERE name = ? AND type = 'index'" ) ?;
156151
157- while statement. step ( ) . into_db_result ( db) ? == ResultCode :: ROW {
158- let table_name = statement. column_text ( 0 ) ?;
159- let index_name = statement. column_text ( 1 ) ?;
160- let columns = statement. column_text ( 2 ) ?;
161- let existing_sql = statement. column_text ( 3 ) ?;
162-
163- // language=SQLite
164- let stmt2 = db. prepare_v2 ( "select json_extract(e.value, '$.name') as name, json_extract(e.value, '$.type') as type, json_extract(e.value, '$.ascending') as ascending from json_each(?) e" ) ?;
165- stmt2. bind_text ( 1 , columns, sqlite:: Destructor :: STATIC ) ?;
166-
167- let mut column_values: Vec < String > = alloc:: vec![ ] ;
168- while stmt2. step ( ) ? == ResultCode :: ROW {
169- let name = stmt2. column_text ( 0 ) ?;
170- let type_name = stmt2. column_text ( 1 ) ?;
171- let ascending = stmt2. column_int ( 2 ) != 0 ;
172-
173- if ascending {
174- let value = format ! (
152+ for table in & schema. tables {
153+ let table_name = table. internal_name ( ) ;
154+
155+ for index in & table. indexes {
156+ let index_name = format ! ( "{}__{}" , table_name, & index. name) ;
157+
158+ let existing_sql = {
159+ find_index. reset ( ) ?;
160+ find_index. bind_text ( 1 , & index_name, sqlite:: Destructor :: STATIC ) ?;
161+
162+ let result = if let ResultCode :: ROW = find_index. step ( ) ? {
163+ Some ( find_index. column_text ( 0 ) ?)
164+ } else {
165+ None
166+ } ;
167+
168+ result
169+ } ;
170+
171+ let mut column_values: Vec < String > = alloc:: vec![ ] ;
172+ for indexed_column in & index. columns {
173+ let mut value = format ! (
175174 "CAST(json_extract(data, {:}) as {:})" ,
176- quote_json_path( name) ,
177- type_name
178- ) ;
179- column_values. push ( value) ;
180- } else {
181- let value = format ! (
182- "CAST(json_extract(data, {:}) as {:}) DESC" ,
183- quote_json_path( name) ,
184- type_name
175+ quote_json_path( & indexed_column. name) ,
176+ & indexed_column. type_name
185177 ) ;
178+
179+ if !indexed_column. ascending {
180+ value += " DESC" ;
181+ }
182+
186183 column_values. push ( value) ;
187184 }
188- }
189185
190- let sql = format ! (
191- "CREATE INDEX {} ON {}({})" ,
192- quote_identifier( index_name) ,
193- quote_identifier( table_name) ,
194- column_values. join( ", " )
195- ) ;
196- if existing_sql == "" {
197- statements. push ( sql) ;
198- } else if existing_sql != sql {
199- statements. push ( format ! ( "DROP INDEX {}" , quote_identifier( index_name) ) ) ;
200- statements. push ( sql) ;
186+ let sql = format ! (
187+ "CREATE INDEX {} ON {}({})" ,
188+ quote_identifier( & index_name) ,
189+ quote_identifier( & table_name) ,
190+ column_values. join( ", " )
191+ ) ;
192+
193+ if existing_sql. is_none ( ) {
194+ statements. push ( sql) ;
195+ } else if existing_sql != Some ( & sql) {
196+ statements. push ( format ! ( "DROP INDEX {}" , quote_identifier( & index_name) ) ) ;
197+ statements. push ( sql) ;
198+ }
199+
200+ expected_index_names. push ( index_name) ;
201201 }
202202 }
203203
204204 // In a block so that the statement is finalized before dropping indexes
205205 // language=SQLite
206- let statement = db. prepare_v2 ( "\
207- WITH schema_indexes AS (
208- SELECT
209- powersync_internal_table_name(tables.value) as table_name,
210- (powersync_internal_table_name(tables.value) || '__' || json_extract(indexes.value, '$.name')) as index_name
211- FROM json_each(json_extract(?1, '$.tables')) tables
212- CROSS JOIN json_each(json_extract(tables.value, '$.indexes')) indexes
213- )
206+ let statement = db
207+ . prepare_v2 (
208+ "\
214209 SELECT
215210 sqlite_master.name as index_name
216211 FROM sqlite_master
217212 WHERE sqlite_master.type = 'index'
218213 AND sqlite_master.name GLOB 'ps_data_*'
219- AND sqlite_master.name NOT IN (SELECT index_name FROM schema_indexes)
220- " ) . into_db_result ( db) ?;
221- statement. bind_text ( 1 , schema, sqlite:: Destructor :: STATIC ) ?;
214+ AND sqlite_master.name NOT IN (SELECT value FROM json_each(?))
215+ " ,
216+ )
217+ . into_db_result ( db) ?;
218+ let json_names = serde_json:: to_string ( & expected_index_names) ?;
219+ statement. bind_text ( 1 , & json_names, sqlite:: Destructor :: STATIC ) ?;
222220
223221 while statement. step ( ) ? == ResultCode :: ROW {
224222 let name = statement. column_text ( 0 ) ?;
0 commit comments