@@ -14,9 +14,10 @@ use asap_aware_mapping::{
1414 SummaryMaintenanceLifecycleCapabilities , SummaryMaintenanceLifecycleCostInputs , WorkloadDemand ,
1515} ;
1616use planner_types:: post_asap:: {
17- CompositionOperator , EvaluationSchedule , OutputRepresentation , SketchQuery , SummaryExpr ,
18- SummaryFamilyType , SummaryMaintenanceLifecycle , SummaryMaintenanceLifecycleGuarantee ,
19- SummaryMaintenanceMode , SummaryNode , SummaryWindowFramework ,
17+ CompositionOperator , EvaluationSchedule , OutputRepresentation , SketchAlgorithm , SketchParams ,
18+ SketchQuery , SummaryExpr , SummaryFamilyType , SummaryMaintenanceLifecycle ,
19+ SummaryMaintenanceLifecycleGuarantee , SummaryMaintenanceMode , SummaryNode ,
20+ SummaryWindowFramework ,
2021} ;
2122use planner_types:: pre_asap:: QueryExpr ;
2223use planner_types:: workload:: {
@@ -236,14 +237,62 @@ pub enum StateEncoding {
236237 ExactAccumulatorV1 ,
237238}
238239
240+ /// Serializable physical state identity derived from Planner's canonical
241+ /// summary family. This is a wire DTO, not a second planning algebra.
242+ #[ derive( Debug , Clone , Serialize , Deserialize , PartialEq ) ]
243+ #[ serde( tag = "family" , rename_all = "snake_case" , deny_unknown_fields) ]
244+ pub enum StateFamilyContract {
245+ Exact {
246+ kind : ExactStateKind ,
247+ } ,
248+ Sketch {
249+ algorithm : SketchAlgorithm ,
250+ parameters : SketchParams ,
251+ } ,
252+ }
253+
254+ #[ derive( Debug , Clone , Copy , Serialize , Deserialize , PartialEq , Eq ) ]
255+ #[ serde( rename_all = "snake_case" ) ]
256+ pub enum ExactStateKind {
257+ Sum ,
258+ Count ,
259+ MinMax ,
260+ Increase ,
261+ Rate ,
262+ }
263+
264+ impl TryFrom < & SummaryFamilyType > for StateFamilyContract {
265+ type Error = ( ) ;
266+
267+ fn try_from ( family : & SummaryFamilyType ) -> Result < Self , Self :: Error > {
268+ use planner_types:: post_asap:: ExactKind ;
269+ Ok ( match family {
270+ SummaryFamilyType :: ExactAggregate ( kind, _) => Self :: Exact {
271+ kind : match kind {
272+ ExactKind :: Sum => ExactStateKind :: Sum ,
273+ ExactKind :: Count => ExactStateKind :: Count ,
274+ ExactKind :: MinMax => ExactStateKind :: MinMax ,
275+ ExactKind :: Increase => ExactStateKind :: Increase ,
276+ ExactKind :: Rate => ExactStateKind :: Rate ,
277+ } ,
278+ } ,
279+ SummaryFamilyType :: Sketch ( kind, _) => Self :: Sketch {
280+ algorithm : kind. algorithm ( ) . clone ( ) ,
281+ parameters : kind. params ( ) . clone ( ) ,
282+ } ,
283+ _ => return Err ( ( ) ) ,
284+ } )
285+ }
286+ }
287+
239288/// Decoder/schema contract for one content-addressed materialization.
240289#[ derive( Debug , Clone , Serialize , Deserialize , PartialEq ) ]
241290#[ serde( deny_unknown_fields) ]
242291pub struct StateSchemaContract {
243292 pub schema_id : String ,
244293 pub schema_version : u32 ,
245294 pub materialization : asap_types:: PolicyFingerprint ,
246- pub family : SummaryFamilyType ,
295+ pub family : StateFamilyContract ,
247296 pub source : Source ,
248297 pub value_column : planner_types:: pre_asap:: ColumnRef ,
249298 pub group_by : Vec < String > ,
@@ -282,6 +331,8 @@ pub enum PrecomputePlanError {
282331 SchemaSetMismatch ,
283332 #[ error( "schema {schema_id} has invalid version or no encoding" ) ]
284333 InvalidSchema { schema_id : String } ,
334+ #[ error( "materialization {0} uses a summary family unsupported by the runtime schema" ) ]
335+ UnsupportedFamily ( u64 ) ,
285336 #[ error( "producer {producer_id} references an unknown materialization or schema" ) ]
286337 InvalidProducer { producer_id : String } ,
287338 #[ error( "materialization {0} has no registered producer" ) ]
@@ -309,22 +360,26 @@ impl PrecomputePlan {
309360 let schemas = backend_plan
310361 . materializations
311362 . iter ( )
312- . map ( |( fingerprint, materialization) | StateSchemaContract {
313- schema_id : state_schema_id ( * fingerprint) ,
314- schema_version : 1 ,
315- materialization : * fingerprint,
316- family : materialization. family . clone ( ) ,
317- source : materialization. source . clone ( ) ,
318- value_column : materialization. col . clone ( ) ,
319- group_by : materialization. group_by . clone ( ) ,
320- window : StateWindowContract {
321- kind : materialization. window . kind ,
322- size_ms : materialization. window . size_ms ,
323- slide_ms : materialization. window . slide_ms ,
324- } ,
325- encodings : state_encodings ( & materialization. family ) ,
363+ . map ( |( fingerprint, materialization) | {
364+ let family = StateFamilyContract :: try_from ( & materialization. family )
365+ . map_err ( |_| PrecomputePlanError :: UnsupportedFamily ( fingerprint. 0 ) ) ?;
366+ Ok ( StateSchemaContract {
367+ schema_id : state_schema_id ( * fingerprint) ,
368+ schema_version : 1 ,
369+ materialization : * fingerprint,
370+ family,
371+ source : materialization. source . clone ( ) ,
372+ value_column : materialization. col . clone ( ) ,
373+ group_by : materialization. group_by . clone ( ) ,
374+ window : StateWindowContract {
375+ kind : materialization. window . kind ,
376+ size_ms : materialization. window . size_ms ,
377+ slide_ms : materialization. window . slide_ms ,
378+ } ,
379+ encodings : state_encodings ( & materialization. family ) ,
380+ } )
326381 } )
327- . collect :: < Vec < _ > > ( ) ;
382+ . collect :: < Result < Vec < _ > , PrecomputePlanError > > ( ) ? ;
328383 let producers = producer_ids
329384 . iter ( )
330385 . flat_map ( |producer_id| {
@@ -443,7 +498,9 @@ impl PrecomputePlan {
443498 else {
444499 return Err ( PrecomputePlanError :: SchemaSetMismatch ) ;
445500 } ;
446- if schema. family != materialization. family
501+ let expected_family = StateFamilyContract :: try_from ( & materialization. family )
502+ . map_err ( |_| PrecomputePlanError :: UnsupportedFamily ( schema. materialization . 0 ) ) ?;
503+ if schema. family != expected_family
447504 || schema. schema_id != state_schema_id ( schema. materialization )
448505 || schema. source != materialization. source
449506 || schema. value_column != materialization. col
0 commit comments