@@ -753,6 +753,10 @@ impl mz_sql::catalog::CatalogItem for CatalogCollectionEntry {
753753 self . entry . writable_table_details ( )
754754 }
755755
756+ fn replacement_target ( & self ) -> Option < CatalogItemId > {
757+ self . entry . replacement_target ( )
758+ }
759+
756760 fn type_details ( & self ) -> Option < & CatalogTypeDetails < IdReference > > {
757761 self . entry . type_details ( )
758762 }
@@ -1447,6 +1451,67 @@ impl MaterializedView {
14471451 self . desc
14481452 . at_version ( RelationVersionSelector :: Specific ( * version) )
14491453 }
1454+
1455+ /// Apply the given replacement materialized view to this [`MaterializedView`].
1456+ pub fn apply_replacement ( & mut self , replacement : Self ) {
1457+ let target_id = replacement
1458+ . replacement_target
1459+ . expect ( "replacement has target" ) ;
1460+
1461+ fn parse ( create_sql : & str ) -> mz_sql:: ast:: CreateMaterializedViewStatement < Raw > {
1462+ let res = mz_sql:: parse:: parse ( create_sql) . unwrap_or_else ( |e| {
1463+ panic ! ( "invalid create_sql persisted in catalog: {e}\n {create_sql}" ) ;
1464+ } ) ;
1465+ if let Statement :: CreateMaterializedView ( cmvs) = res. into_element ( ) . ast {
1466+ cmvs
1467+ } else {
1468+ panic ! ( "invalid MV create_sql persisted in catalog\n {create_sql}" ) ;
1469+ }
1470+ }
1471+
1472+ let old_stmt = parse ( & self . create_sql ) ;
1473+ let rpl_stmt = parse ( & replacement. create_sql ) ;
1474+ let new_stmt = mz_sql:: ast:: CreateMaterializedViewStatement {
1475+ if_exists : old_stmt. if_exists ,
1476+ name : old_stmt. name ,
1477+ columns : rpl_stmt. columns ,
1478+ replacing : None ,
1479+ in_cluster : rpl_stmt. in_cluster ,
1480+ query : rpl_stmt. query ,
1481+ as_of : rpl_stmt. as_of ,
1482+ with_options : rpl_stmt. with_options ,
1483+ } ;
1484+ let create_sql = new_stmt. to_ast_string_stable ( ) ;
1485+
1486+ let mut collections = std:: mem:: take ( & mut self . collections ) ;
1487+ // Note: We can't use `self.desc.latest_version` here because a replacement doesn't
1488+ // necessary evolve the relation schema, so that version might be lower than the actual
1489+ // latest version.
1490+ let latest_version = collections. keys ( ) . max ( ) . expect ( "at least one version" ) ;
1491+ let new_version = latest_version. bump ( ) ;
1492+ collections. insert ( new_version, replacement. global_id_writes ( ) ) ;
1493+
1494+ let mut resolved_ids = replacement. resolved_ids ;
1495+ resolved_ids. remove_item ( & target_id) ;
1496+ let mut dependencies = replacement. dependencies ;
1497+ dependencies. 0 . remove ( & target_id) ;
1498+
1499+ * self = Self {
1500+ create_sql,
1501+ collections,
1502+ raw_expr : replacement. raw_expr ,
1503+ optimized_expr : replacement. optimized_expr ,
1504+ desc : replacement. desc ,
1505+ resolved_ids,
1506+ dependencies,
1507+ replacement_target : None ,
1508+ cluster_id : replacement. cluster_id ,
1509+ non_null_assertions : replacement. non_null_assertions ,
1510+ custom_logical_compaction_window : replacement. custom_logical_compaction_window ,
1511+ refresh_schedule : replacement. refresh_schedule ,
1512+ initial_as_of : replacement. initial_as_of ,
1513+ } ;
1514+ }
14501515}
14511516
14521517#[ derive( Debug , Clone , Serialize ) ]
@@ -3401,6 +3466,14 @@ impl mz_sql::catalog::CatalogItem for CatalogEntry {
34013466 }
34023467 }
34033468
3469+ fn replacement_target ( & self ) -> Option < CatalogItemId > {
3470+ if let CatalogItem :: MaterializedView ( mv) = self . item ( ) {
3471+ mv. replacement_target
3472+ } else {
3473+ None
3474+ }
3475+ }
3476+
34043477 fn type_details ( & self ) -> Option < & CatalogTypeDetails < IdReference > > {
34053478 if let CatalogItem :: Type ( Type { details, .. } ) = self . item ( ) {
34063479 Some ( details)
0 commit comments