@@ -41,6 +41,7 @@ use datafusion::{
4141 catalog:: TableProvider ,
4242 datasource:: listing:: { ListingTable , ListingTableUrl } ,
4343} ;
44+ use datafusion_common:: DataFusionError ;
4445use datafusion_expr:: LogicalPlan ;
4546use itertools:: Itertools ;
4647
@@ -110,6 +111,14 @@ pub trait Materialized: ListingTableLike {
110111 fn config ( & self ) -> MaterializedConfig {
111112 MaterializedConfig :: default ( )
112113 }
114+
115+ /// Which partition columns are 'static'.
116+ /// Static partition columns are those that are used in incremental view maintenance.
117+ /// These should be a prefix of the full set of partition columns returned by [`ListingTableLike::partition_columns`].
118+ /// The rest of the partition columns are 'dynamic' and their values will be generated at runtime during incremental refresh.
119+ fn static_partition_columns ( & self ) -> Vec < String > {
120+ <Self as ListingTableLike >:: partition_columns ( self )
121+ }
113122}
114123
115124/// Register a [`Materialized`] implementation in this registry.
@@ -122,13 +131,38 @@ pub fn register_materialized<T: Materialized>() {
122131}
123132
124133/// Attempt to cast the given TableProvider into a [`Materialized`].
125- /// If the table's type has not been registered using [`register_materialized`], will return `None`.
126- pub fn cast_to_materialized ( table : & dyn TableProvider ) -> Option < & dyn Materialized > {
127- TABLE_TYPE_REGISTRY . cast_to_materialized ( table) . or_else ( || {
128- TABLE_TYPE_REGISTRY
129- . cast_to_decorator ( table)
130- . and_then ( |decorator| cast_to_materialized ( decorator. base ( ) ) )
131- } )
134+ /// If the table's type has not been registered using [`register_materialized`], will return `Ok(None)`.
135+ ///
136+ /// Does a runtime check on some invariants of `Materialized` and returns an error if they are violated.
137+ /// In particular, checks that the static partition columns are a prefix of all partition columns.
138+ pub fn cast_to_materialized (
139+ table : & dyn TableProvider ,
140+ ) -> Result < Option < & dyn Materialized > , DataFusionError > {
141+ let materialized = match TABLE_TYPE_REGISTRY
142+ . cast_to_materialized ( table)
143+ . map ( Ok )
144+ . or_else ( || {
145+ TABLE_TYPE_REGISTRY
146+ . cast_to_decorator ( table)
147+ . and_then ( |decorator| cast_to_materialized ( decorator. base ( ) ) . transpose ( ) )
148+ } )
149+ . transpose ( ) ?
150+ {
151+ None => return Ok ( None ) ,
152+ Some ( m) => m,
153+ } ;
154+
155+ let static_partition_cols = materialized. static_partition_columns ( ) ;
156+ let all_partition_cols = materialized. partition_columns ( ) ;
157+
158+ if materialized. partition_columns ( ) [ ..static_partition_cols. len ( ) ] != static_partition_cols[ ..]
159+ {
160+ return Err ( DataFusionError :: Plan ( format ! (
161+ "Materialized view's static partition columns ({static_partition_cols:?}) must be a prefix of all partition columns ({all_partition_cols:?})"
162+ ) ) ) ;
163+ }
164+
165+ Ok ( Some ( materialized) )
132166}
133167
134168/// A `TableProvider` that decorates other `TableProvider`s.
0 commit comments