1
1
extern crate alloc;
2
2
3
- use alloc:: format;
4
3
use alloc:: string:: String ;
5
4
use alloc:: vec:: Vec ;
5
+ use alloc:: { format, vec} ;
6
6
use core:: ffi:: c_int;
7
7
8
8
use sqlite:: { Connection , ResultCode , Value } ;
@@ -14,6 +14,8 @@ use crate::ext::ExtendedDatabase;
14
14
use crate :: util:: { quote_identifier, quote_json_path} ;
15
15
use crate :: { create_auto_tx_function, create_sqlite_text_fn} ;
16
16
17
+ use super :: Schema ;
18
+
17
19
fn update_tables ( db : * mut sqlite:: sqlite3 , schema : & str ) -> Result < ( ) , SQLiteError > {
18
20
{
19
21
// 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 (
138
140
139
141
fn update_indexes ( db : * mut sqlite:: sqlite3 , schema : & str ) -> Result < ( ) , SQLiteError > {
140
142
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 ! [ ] ;
141
145
142
146
{
143
147
// In a block so that the statement is finalized before dropping indexes
144
148
// 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'" ) ?;
156
151
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 ! (
175
174
"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
185
177
) ;
178
+
179
+ if !indexed_column. ascending {
180
+ value += " DESC" ;
181
+ }
182
+
186
183
column_values. push ( value) ;
187
184
}
188
- }
189
185
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) ;
201
201
}
202
202
}
203
203
204
204
// In a block so that the statement is finalized before dropping indexes
205
205
// 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
+ "\
214
209
SELECT
215
210
sqlite_master.name as index_name
216
211
FROM sqlite_master
217
212
WHERE sqlite_master.type = 'index'
218
213
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 ) ?;
222
220
223
221
while statement. step ( ) ? == ResultCode :: ROW {
224
222
let name = statement. column_text ( 0 ) ?;
0 commit comments