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