11extern crate alloc;
22
3+ use alloc:: borrow:: ToOwned ;
4+ use alloc:: collections:: btree_map:: BTreeMap ;
35use alloc:: rc:: Rc ;
46use alloc:: string:: String ;
57use alloc:: vec:: Vec ;
@@ -12,8 +14,13 @@ use sqlite::{Connection, ResultCode, Value};
1214
1315use crate :: error:: { PSResult , PowerSyncError } ;
1416use crate :: ext:: ExtendedDatabase ;
17+ use crate :: schema:: inspection:: ExistingView ;
1518use crate :: state:: DatabaseState ;
1619use crate :: util:: { quote_identifier, quote_json_path} ;
20+ use crate :: views:: {
21+ powersync_trigger_delete_sql, powersync_trigger_insert_sql, powersync_trigger_update_sql,
22+ powersync_view_sql,
23+ } ;
1724use crate :: { create_auto_tx_function, create_sqlite_text_fn} ;
1825
1926use super :: Schema ;
@@ -236,55 +243,46 @@ SELECT
236243 Ok ( ( ) )
237244}
238245
239- fn update_views ( db : * mut sqlite:: sqlite3 , schema : & str ) -> Result < ( ) , PowerSyncError > {
240- // Update existing views if modified
241- // language=SQLite
242- db. exec_text ( "\
243- UPDATE powersync_views SET
244- sql = gen.sql,
245- delete_trigger_sql = gen.delete_trigger_sql,
246- insert_trigger_sql = gen.insert_trigger_sql,
247- update_trigger_sql = gen.update_trigger_sql
248- FROM (SELECT
249- ifnull(json_extract(json_each.value, '$.view_name'), json_extract(json_each.value, '$.name')) as name,
250- powersync_view_sql(json_each.value) as sql,
251- powersync_trigger_delete_sql(json_each.value) as delete_trigger_sql,
252- powersync_trigger_insert_sql(json_each.value) as insert_trigger_sql,
253- powersync_trigger_update_sql(json_each.value) as update_trigger_sql
254- FROM json_each(json_extract(?, '$.tables'))) as gen
255- WHERE powersync_views.name = gen.name AND
256- (powersync_views.sql IS NOT gen.sql OR
257- powersync_views.delete_trigger_sql IS NOT gen.delete_trigger_sql OR
258- powersync_views.insert_trigger_sql IS NOT gen.insert_trigger_sql OR
259- powersync_views.update_trigger_sql IS NOT gen.update_trigger_sql)
260- " , schema) . into_db_result ( db) ?;
261-
262- // Create new views
263- // language=SQLite
264- db. exec_text ( "\
265- INSERT INTO powersync_views(
266- name,
267- sql,
268- delete_trigger_sql,
269- insert_trigger_sql,
270- update_trigger_sql
271- )
272- SELECT
273- ifnull(json_extract(json_each.value, '$.view_name'), json_extract(json_each.value, '$.name')) as name,
274- powersync_view_sql(json_each.value) as sql,
275- powersync_trigger_delete_sql(json_each.value) as delete_trigger_sql,
276- powersync_trigger_insert_sql(json_each.value) as insert_trigger_sql,
277- powersync_trigger_update_sql(json_each.value) as update_trigger_sql
278- FROM json_each(json_extract(?, '$.tables'))
279- WHERE name NOT IN (SELECT name FROM powersync_views)" , schema) . into_db_result ( db) ?;
280-
281- // Delete old views
282- // language=SQLite
283- db. exec_text ( "\
284- DELETE FROM powersync_views WHERE name NOT IN (
285- SELECT ifnull(json_extract(json_each.value, '$.view_name'), json_extract(json_each.value, '$.name'))
286- FROM json_each(json_extract(?, '$.tables'))
287- )" , schema) . into_db_result ( db) ?;
246+ fn update_views ( db : * mut sqlite:: sqlite3 , schema : & Schema ) -> Result < ( ) , PowerSyncError > {
247+ // First, find all existing views and index them by name.
248+ let existing = ExistingView :: list ( db) ?;
249+ let mut existing = {
250+ let mut map = BTreeMap :: new ( ) ;
251+ for entry in & existing {
252+ map. insert ( & * entry. name , entry) ;
253+ }
254+ map
255+ } ;
256+
257+ for table in & schema. tables {
258+ let view_sql = powersync_view_sql ( table) ;
259+ let delete_trigger_sql = powersync_trigger_delete_sql ( table) ?;
260+ let insert_trigger_sql = powersync_trigger_insert_sql ( table) ?;
261+ let update_trigger_sql = powersync_trigger_update_sql ( table) ?;
262+
263+ let wanted_view = ExistingView {
264+ name : table. view_name ( ) . to_owned ( ) ,
265+ sql : view_sql,
266+ delete_trigger_sql,
267+ insert_trigger_sql,
268+ update_trigger_sql,
269+ } ;
270+
271+ if let Some ( actual_view) = existing. remove ( table. view_name ( ) ) {
272+ if * actual_view == wanted_view {
273+ // View exists with identical definition, don't re-create.
274+ continue ;
275+ }
276+ }
277+
278+ // View does not exist or has been defined differently, re-create.
279+ wanted_view. create ( db) ?;
280+ }
281+
282+ // Delete old views.
283+ for remaining in existing. values ( ) {
284+ ExistingView :: drop_by_name ( db, & remaining. name ) ?;
285+ }
288286
289287 Ok ( ( ) )
290288}
@@ -309,7 +307,7 @@ fn powersync_replace_schema_impl(
309307
310308 update_tables ( db, schema) ?;
311309 update_indexes ( db, & parsed_schema) ?;
312- update_views ( db, schema ) ?;
310+ update_views ( db, & parsed_schema ) ?;
313311
314312 state. set_schema ( parsed_schema) ;
315313 Ok ( String :: from ( "" ) )
0 commit comments