@@ -16,17 +16,10 @@ fn miss(message: impl Into<String>) -> EngineError {
1616 EngineError :: capability_miss ( "exact_subquery" , message. into ( ) )
1717}
1818
19- /// Traverse only the installed graph.
20- #[ derive( Debug , Clone ) ]
21- enum ExactLeaf {
22- Legacy ( QueryTimeOperator ) ,
23- External ( ExternalExactRequest ) ,
24- }
25-
2619fn leaves (
2720 entry : & QueryPlanEntry ,
2821 times : & [ u64 ] ,
29- ) -> Result < BTreeMap < ( QueryNodeId , i64 ) , ExactLeaf > , EngineError > {
22+ ) -> Result < BTreeMap < ( QueryNodeId , i64 ) , ExternalExactRequest > , EngineError > {
3023 let mut pending = Vec :: new ( ) ;
3124 for at in times {
3225 pending. push ( (
@@ -54,7 +47,9 @@ fn leaves(
5447 }
5548 QueryTimeOperator :: ExactSubquery { .. }
5649 | QueryTimeOperator :: CandidateExactSubquery { .. } => {
57- result. insert ( ( id, at) , ExactLeaf :: Legacy ( operator. clone ( ) ) ) ;
50+ return Err ( miss (
51+ "legacy exact operators are not executable; recompile the plan" ,
52+ ) ) ;
5853 }
5954 _ => pending. extend ( inputs. iter ( ) . map ( |input| ( * input, at) ) ) ,
6055 } ,
@@ -82,7 +77,7 @@ fn leaves(
8277
8378 QueryPlanNode :: ExternalExact { request, inputs } => {
8479 pending. extend ( inputs. iter ( ) . map ( |input| ( * input, at) ) ) ;
85- result. insert ( ( id, at) , ExactLeaf :: External ( request. clone ( ) ) ) ;
80+ result. insert ( ( id, at) , request. clone ( ) ) ;
8681 }
8782 // Existing bound summary subtrees are read by the synchronous callback.
8883 _ => { }
@@ -96,24 +91,13 @@ pub(super) fn external_dependencies(
9691 times : & [ u64 ] ,
9792) -> Result < Vec < ( QueryNodeId , QueryNodeId , i64 ) > , EngineError > {
9893 let mut result = Vec :: new ( ) ;
99- for ( ( id, at) , leaf) in leaves ( entry, times) ? {
100- if matches ! ( leaf, ExactLeaf :: External ( _) ) {
101- result. extend (
102- entry. nodes [ & id]
103- . inputs ( )
104- . iter ( )
105- . map ( |input| ( id, * input, at) ) ,
106- ) ;
107- } else if matches ! (
108- leaf,
109- ExactLeaf :: Legacy ( QueryTimeOperator :: CandidateExactSubquery { .. } )
110- ) {
111- let input = * entry. nodes [ & id]
94+ for ( ( id, at) , _) in leaves ( entry, times) ? {
95+ result. extend (
96+ entry. nodes [ & id]
11297 . inputs ( )
113- . first ( )
114- . ok_or_else ( || miss ( "candidate exact subtree has no membership input" ) ) ?;
115- result. push ( ( id, input, at) ) ;
116- }
98+ . iter ( )
99+ . map ( |input| ( id, * input, at) ) ,
100+ ) ;
117101 }
118102 Ok ( result)
119103}
@@ -292,36 +276,23 @@ pub(super) async fn prepare_external(
292276 let mut remote_cache = HashMap :: < ( QueryLanguage , String , i64 ) , Value > :: new ( ) ;
293277 for ( ( id, at) , leaf) in leaves ( entry, times) ? {
294278 u64:: try_from ( at) . map_err ( |_| miss ( "subquery predates epoch" ) ) ?;
295- let ( language, query, candidate_input) = match & leaf {
296- ExactLeaf :: Legacy ( QueryTimeOperator :: ExactSubquery { query } ) => {
297- ( QueryLanguage :: PromQl , query. clone ( ) , None )
298- }
299- ExactLeaf :: Legacy ( QueryTimeOperator :: CandidateExactSubquery { query, item_label } ) => (
300- QueryLanguage :: PromQl ,
301- query. clone ( ) ,
302- Some ( ( entry. nodes [ & id] . inputs ( ) [ 0 ] , item_label. as_str ( ) ) ) ,
303- ) ,
304- ExactLeaf :: External ( request) => {
305- if !matches ! (
306- request. language,
307- QueryLanguage :: PromQl | QueryLanguage :: MetricsQl
308- ) {
309- return Err ( miss ( format ! (
310- "external exact language {:?} has no installed adapter" ,
311- request. language
312- ) ) ) ;
313- }
314- let candidate = match request. input_contracts . as_slice ( ) {
315- [ ] => None ,
316- [ ExternalExactInput :: CandidateMembership { item_label } ] => {
317- Some ( ( entry. nodes [ & id] . inputs ( ) [ 0 ] , item_label. as_str ( ) ) )
318- }
319- _ => return Err ( miss ( "unsupported external exact input contract" ) ) ,
320- } ;
321- ( request. language , request. expression . clone ( ) , candidate)
279+ if !matches ! (
280+ leaf. language,
281+ QueryLanguage :: PromQl | QueryLanguage :: MetricsQl
282+ ) {
283+ return Err ( miss ( format ! (
284+ "external exact language {:?} has no installed adapter" ,
285+ leaf. language
286+ ) ) ) ;
287+ }
288+ let candidate_input = match leaf. input_contracts . as_slice ( ) {
289+ [ ] => None ,
290+ [ ExternalExactInput :: CandidateMembership { item_label } ] => {
291+ Some ( ( entry. nodes [ & id] . inputs ( ) [ 0 ] , item_label. as_str ( ) ) )
322292 }
323- _ => return Err ( miss ( "prepared leaf is not an exact subtree " ) ) ,
293+ _ => return Err ( miss ( "unsupported external exact input contract " ) ) ,
324294 } ;
295+ let ( language, query) = ( leaf. language , leaf. expression . clone ( ) ) ;
325296 let ( query, candidate_filtered) = if let Some ( ( candidate_input, item_label) ) =
326297 candidate_input
327298 {
@@ -809,6 +780,21 @@ mod tests {
809780 ) ) ;
810781 server. abort ( ) ;
811782 }
783+ fn external_exact_leaf ( expression : & str ) -> QueryPlanNode {
784+ QueryPlanNode :: ExternalExact {
785+ request : ExternalExactRequest {
786+ language : QueryLanguage :: PromQl ,
787+ expression : expression. into ( ) ,
788+ output : asap_types:: query_plan:: ExternalExactOutput :: InstantVector ,
789+ parameters : BTreeMap :: new ( ) ,
790+ start_parameter : None ,
791+ end_parameter : None ,
792+ input_contracts : vec ! [ ] ,
793+ } ,
794+ inputs : vec ! [ ] ,
795+ }
796+ }
797+
812798 /// Planner's label-map division, the computation over a prepared exact leaf.
813799 fn planner_division ( ) -> Vec < u8 > {
814800 use planner_types:: {
@@ -863,13 +849,7 @@ mod tests {
863849 QueryNodeId ( 1 ) ,
864850 QueryPlanNode :: SummaryMerge { inputs : vec ! [ ] } ,
865851 ) ,
866- (
867- QueryNodeId ( 2 ) ,
868- QueryPlanNode :: Logical {
869- operator : QueryTimeOperator :: ExactSubquery { query : "b" . into ( ) } ,
870- inputs : vec ! [ ] ,
871- } ,
872- ) ,
852+ ( QueryNodeId ( 2 ) , external_exact_leaf ( "b" ) ) ,
873853 ] ) ) ;
874854 let leaves = prepare (
875855 & entry,
@@ -905,13 +885,9 @@ mod tests {
905885 assert_eq ! ( stats. summary_readout_evaluations, 1 ) ;
906886 assert_eq ! ( calls. load( Ordering :: SeqCst ) , 1 ) ;
907887 let mut repeated = entry. clone ( ) ;
908- repeated. nodes . insert (
909- QueryNodeId ( 1 ) ,
910- QueryPlanNode :: Logical {
911- operator : QueryTimeOperator :: ExactSubquery { query : "b" . into ( ) } ,
912- inputs : vec ! [ ] ,
913- } ,
914- ) ;
888+ repeated
889+ . nodes
890+ . insert ( QueryNodeId ( 1 ) , external_exact_leaf ( "b" ) ) ;
915891 let prepared = prepare (
916892 & repeated,
917893 & [ 1000 ] ,
@@ -992,15 +968,7 @@ mod tests {
992968 pruning : None ,
993969 } ,
994970 ) ,
995- (
996- QueryNodeId ( 1 ) ,
997- QueryPlanNode :: Logical {
998- operator : QueryTimeOperator :: ExactSubquery {
999- query : exact_query. into ( ) ,
1000- } ,
1001- inputs : vec ! [ ] ,
1002- } ,
1003- ) ,
971+ ( QueryNodeId ( 1 ) , external_exact_leaf ( exact_query) ) ,
1004972 (
1005973 QueryNodeId ( 2 ) ,
1006974 QueryPlanNode :: ExactReadout {
@@ -1069,6 +1037,27 @@ mod tests {
10691037 assert_eq ! ( calls. load( Ordering :: SeqCst ) , 0 ) ;
10701038 server. abort ( ) ;
10711039 }
1040+ // Legacy exact operators never reach the runtime or trigger a PromQL reparse.
1041+ #[ test]
1042+ fn legacy_exact_leaves_are_rejected_before_execution ( ) {
1043+ for operator in [
1044+ QueryTimeOperator :: ExactSubquery { query : "m" . into ( ) } ,
1045+ QueryTimeOperator :: CandidateExactSubquery {
1046+ query : "m" . into ( ) ,
1047+ item_label : "job" . into ( ) ,
1048+ } ,
1049+ ] {
1050+ let entry = entry ( BTreeMap :: from ( [ (
1051+ QueryNodeId ( 0 ) ,
1052+ QueryPlanNode :: Logical {
1053+ operator,
1054+ inputs : vec ! [ ] ,
1055+ } ,
1056+ ) ] ) ) ;
1057+ assert ! ( leaves( & entry, & [ 1000 ] ) . is_err( ) ) ;
1058+ }
1059+ }
1060+
10721061 #[ test]
10731062 fn deployed_raw_scan_is_rejected_before_execution ( ) {
10741063 let entry = entry ( BTreeMap :: from ( [ (
0 commit comments