1- //! Errors that occur while fulfilling [`GraphRead`] suspensions .
1+ //! Error types for the orchestration layer .
22//!
3- //! These are internal runtime errors: failures in compiled query execution,
4- //! row decoding, or parameter encoding. The user wrote HashQL, not SQL; if
5- //! the bridge fails, it indicates a bug in the compiler or runtime.
3+ //! The orchestrator sits between the MIR interpreter and external data sources
4+ //! (PostgreSQL). Errors fall into two families:
5+ //!
6+ //! - **Interpreter errors**: failures in the MIR interpreter itself (type invariant violations,
7+ //! control flow errors, etc.). These are produced by the interpreter and forwarded through the
8+ //! orchestrator.
9+ //! - **Bridge errors**: failures while fulfilling [`GraphRead`] suspensions (query execution, row
10+ //! decoding, parameter encoding). The user wrote HashQL, not SQL; if the bridge fails, it
11+ //! indicates a bug in the compiler or runtime.
12+ //!
13+ //! [`OrchestratorDiagnosticCategory`] unifies both families under a single
14+ //! category hierarchy so that downstream consumers (the eval crate) see one
15+ //! coherent diagnostic type from the orchestration layer.
616//!
717//! [`GraphRead`]: hashql_mir::body::terminator::GraphRead
818
9- use alloc:: string:: String ;
19+ use alloc:: { borrow :: Cow , string:: String } ;
1020
1121use hashql_core:: {
1222 pretty:: { Formatter , RenderOptions } ,
@@ -15,15 +25,15 @@ use hashql_core::{
1525 r#type:: { TypeFormatter , TypeFormatterOptions , TypeId , environment:: Environment } ,
1626} ;
1727use hashql_diagnostics:: {
18- Diagnostic , Label , category:: TerminalDiagnosticCategory , diagnostic:: Message ,
19- severity:: Severity ,
28+ Diagnostic , Label ,
29+ category:: { DiagnosticCategory , TerminalDiagnosticCategory } ,
30+ diagnostic:: Message ,
31+ severity:: Critical ,
2032} ;
2133use hashql_mir:: {
2234 body:: { basic_block:: BasicBlockId , local:: Local } ,
2335 def:: DefId ,
24- interpret:: error:: {
25- InterpretDiagnostic , InterpretDiagnosticCategory , SuspensionDiagnosticCategory ,
26- } ,
36+ interpret:: error:: InterpretDiagnosticCategory ,
2737} ;
2838
2939use super :: { Indexed , codec:: JsonValueKind } ;
@@ -89,8 +99,64 @@ const VALUE_SERIALIZATION: TerminalDiagnosticCategory = TerminalDiagnosticCatego
8999 name : "Value Serialization" ,
90100} ;
91101
92- const fn category ( terminal : & ' static TerminalDiagnosticCategory ) -> InterpretDiagnosticCategory {
93- InterpretDiagnosticCategory :: Suspension ( SuspensionDiagnosticCategory ( terminal) )
102+ /// Type alias for orchestrator diagnostics.
103+ ///
104+ /// The default severity kind is [`Critical`].
105+ pub type OrchestratorDiagnostic < K = Critical > =
106+ Diagnostic < OrchestratorDiagnosticCategory , SpanId , K > ;
107+
108+ /// Diagnostic subcategory for errors that occur while fulfilling a suspension.
109+ ///
110+ /// Wraps a [`TerminalDiagnosticCategory`] that identifies the specific bridge
111+ /// failure (query execution, row hydration, parameter encoding, etc.).
112+ #[ derive( Debug , Copy , Clone , PartialEq , Eq , Hash ) ]
113+ pub struct BridgeDiagnosticCategory ( pub & ' static TerminalDiagnosticCategory ) ;
114+
115+ impl DiagnosticCategory for BridgeDiagnosticCategory {
116+ fn id ( & self ) -> Cow < ' _ , str > {
117+ Cow :: Borrowed ( "bridge" )
118+ }
119+
120+ fn name ( & self ) -> Cow < ' _ , str > {
121+ Cow :: Borrowed ( "Bridge" )
122+ }
123+
124+ fn subcategory ( & self ) -> Option < & dyn DiagnosticCategory > {
125+ Some ( self . 0 )
126+ }
127+ }
128+
129+ /// Top-level diagnostic category for the orchestration layer.
130+ ///
131+ /// Unifies interpreter errors (forwarded from the MIR interpreter) and bridge
132+ /// errors (failures while fulfilling suspensions) under a single hierarchy.
133+ #[ derive( Debug , Copy , Clone , PartialEq , Eq , Hash ) ]
134+ pub enum OrchestratorDiagnosticCategory {
135+ /// An error produced by the MIR interpreter itself.
136+ Interpret ( InterpretDiagnosticCategory ) ,
137+ /// An error from the bridge while fulfilling a suspension.
138+ Bridge ( BridgeDiagnosticCategory ) ,
139+ }
140+
141+ impl DiagnosticCategory for OrchestratorDiagnosticCategory {
142+ fn id ( & self ) -> Cow < ' _ , str > {
143+ Cow :: Borrowed ( "orchestrator" )
144+ }
145+
146+ fn name ( & self ) -> Cow < ' _ , str > {
147+ Cow :: Borrowed ( "Orchestrator" )
148+ }
149+
150+ fn subcategory ( & self ) -> Option < & dyn DiagnosticCategory > {
151+ match self {
152+ Self :: Interpret ( cat) => Some ( cat) ,
153+ Self :: Bridge ( cat) => Some ( cat) ,
154+ }
155+ }
156+ }
157+
158+ const fn category ( terminal : & ' static TerminalDiagnosticCategory ) -> OrchestratorDiagnosticCategory {
159+ OrchestratorDiagnosticCategory :: Bridge ( BridgeDiagnosticCategory ( terminal) )
94160}
95161
96162/// Errors that occur while decoding a JSON value into a typed [`Value`].
@@ -356,7 +422,7 @@ pub enum BridgeError<'heap> {
356422}
357423
358424impl < ' heap > BridgeError < ' heap > {
359- pub fn into_diagnostic ( self , span : SpanId , env : & Environment < ' heap > ) -> InterpretDiagnostic {
425+ pub fn into_diagnostic ( self , span : SpanId , env : & Environment < ' heap > ) -> OrchestratorDiagnostic {
360426 match self {
361427 Self :: QueryExecution { sql, source } => query_execution ( span, & sql, & source) ,
362428 Self :: RowHydration { column, source } => row_hydration ( span, column, & source) ,
@@ -388,8 +454,12 @@ impl<'heap> BridgeError<'heap> {
388454 }
389455}
390456
391- fn query_execution ( span : SpanId , sql : & str , error : & tokio_postgres:: Error ) -> InterpretDiagnostic {
392- let mut diagnostic = Diagnostic :: new ( category ( & QUERY_EXECUTION ) , Severity :: Bug ) . primary (
457+ fn query_execution (
458+ span : SpanId ,
459+ sql : & str ,
460+ error : & tokio_postgres:: Error ,
461+ ) -> OrchestratorDiagnostic {
462+ let mut diagnostic = Diagnostic :: new ( category ( & QUERY_EXECUTION ) , Critical :: BUG ) . primary (
393463 Label :: new ( span, "compiled query was rejected by the database" ) ,
394464 ) ;
395465
@@ -411,9 +481,9 @@ fn row_hydration(
411481 value : column,
412482 } : Indexed < ColumnDescriptor > ,
413483 source : & tokio_postgres:: Error ,
414- ) -> InterpretDiagnostic {
484+ ) -> OrchestratorDiagnostic {
415485 let mut diagnostic =
416- Diagnostic :: new ( category ( & ROW_HYDRATION ) , Severity :: Bug ) . primary ( Label :: new (
486+ Diagnostic :: new ( category ( & ROW_HYDRATION ) , Critical :: BUG ) . primary ( Label :: new (
417487 span,
418488 format ! ( "cannot decode result column {index} ({column})" ) ,
419489 ) ) ;
@@ -429,7 +499,7 @@ fn row_hydration(
429499
430500/// Adds notes describing a [`DecodeError`] to a diagnostic.
431501fn add_decode_error_notes (
432- diagnostic : & mut InterpretDiagnostic ,
502+ diagnostic : & mut OrchestratorDiagnostic ,
433503 source : & DecodeError < ' _ > ,
434504 env : & Environment < ' _ > ,
435505) {
@@ -543,9 +613,9 @@ fn value_deserialization(
543613 } : Indexed < ColumnDescriptor > ,
544614 source : & DecodeError < ' _ > ,
545615 env : & Environment < ' _ > ,
546- ) -> InterpretDiagnostic {
616+ ) -> OrchestratorDiagnostic {
547617 let mut diagnostic =
548- Diagnostic :: new ( category ( & VALUE_DESERIALIZATION ) , Severity :: Bug ) . primary ( Label :: new (
618+ Diagnostic :: new ( category ( & VALUE_DESERIALIZATION ) , Critical :: BUG ) . primary ( Label :: new (
549619 span,
550620 format ! ( "cannot deserialize result column {index} ({column})" ) ,
551621 ) ) ;
@@ -565,8 +635,8 @@ fn continuation_deserialization(
565635 local : Local ,
566636 source : & DecodeError < ' _ > ,
567637 env : & Environment < ' _ > ,
568- ) -> InterpretDiagnostic {
569- let mut diagnostic = Diagnostic :: new ( category ( & CONTINUATION_DESERIALIZATION ) , Severity :: Bug )
638+ ) -> OrchestratorDiagnostic {
639+ let mut diagnostic = Diagnostic :: new ( category ( & CONTINUATION_DESERIALIZATION ) , Critical :: BUG )
570640 . primary ( Label :: new (
571641 span,
572642 format ! ( "cannot deserialize continuation local {local} in definition {body}" ) ,
@@ -582,9 +652,13 @@ fn continuation_deserialization(
582652 diagnostic
583653}
584654
585- fn invalid_continuation_block_id ( span : SpanId , body : DefId , block_id : i32 ) -> InterpretDiagnostic {
655+ fn invalid_continuation_block_id (
656+ span : SpanId ,
657+ body : DefId ,
658+ block_id : i32 ,
659+ ) -> OrchestratorDiagnostic {
586660 let mut diagnostic =
587- Diagnostic :: new ( category ( & INVALID_CONTINUATION_BLOCK_ID ) , Severity :: Bug ) . primary (
661+ Diagnostic :: new ( category ( & INVALID_CONTINUATION_BLOCK_ID ) , Critical :: BUG ) . primary (
588662 Label :: new ( span, "continuation returned an invalid block ID" ) ,
589663 ) ;
590664
@@ -599,8 +673,8 @@ fn invalid_continuation_block_id(span: SpanId, body: DefId, block_id: i32) -> In
599673 diagnostic
600674}
601675
602- fn invalid_continuation_local ( span : SpanId , body : DefId , local : i32 ) -> InterpretDiagnostic {
603- let mut diagnostic = Diagnostic :: new ( category ( & INVALID_CONTINUATION_LOCAL ) , Severity :: Bug )
676+ fn invalid_continuation_local ( span : SpanId , body : DefId , local : i32 ) -> OrchestratorDiagnostic {
677+ let mut diagnostic = Diagnostic :: new ( category ( & INVALID_CONTINUATION_LOCAL ) , Critical :: BUG )
604678 . primary ( Label :: new ( span, "continuation returned an invalid local" ) ) ;
605679
606680 diagnostic. add_message ( Message :: note ( format ! (
@@ -618,9 +692,9 @@ fn parameter_encoding(
618692 span : SpanId ,
619693 parameter : usize ,
620694 error : & ( dyn core:: error:: Error + Send + Sync ) ,
621- ) -> InterpretDiagnostic {
695+ ) -> OrchestratorDiagnostic {
622696 let mut diagnostic =
623- Diagnostic :: new ( category ( & PARAMETER_ENCODING ) , Severity :: Bug ) . primary ( Label :: new (
697+ Diagnostic :: new ( category ( & PARAMETER_ENCODING ) , Critical :: BUG ) . primary ( Label :: new (
624698 span,
625699 format ! (
626700 "cannot encode parameter ${} for the database" ,
@@ -637,8 +711,8 @@ fn parameter_encoding(
637711 diagnostic
638712}
639713
640- fn query_lookup ( span : SpanId , body : DefId , block : BasicBlockId ) -> InterpretDiagnostic {
641- let mut diagnostic = Diagnostic :: new ( category ( & QUERY_LOOKUP ) , Severity :: Bug ) . primary (
714+ fn query_lookup ( span : SpanId , body : DefId , block : BasicBlockId ) -> OrchestratorDiagnostic {
715+ let mut diagnostic = Diagnostic :: new ( category ( & QUERY_LOOKUP ) , Critical :: BUG ) . primary (
642716 Label :: new ( span, "no compiled query found for this data access" ) ,
643717 ) ;
644718
@@ -653,8 +727,8 @@ fn query_lookup(span: SpanId, body: DefId, block: BasicBlockId) -> InterpretDiag
653727 diagnostic
654728}
655729
656- fn incomplete_continuation ( span : SpanId , body : DefId , field : & str ) -> InterpretDiagnostic {
657- let mut diagnostic = Diagnostic :: new ( category ( & INCOMPLETE_CONTINUATION ) , Severity :: Bug )
730+ fn incomplete_continuation ( span : SpanId , body : DefId , field : & str ) -> OrchestratorDiagnostic {
731+ let mut diagnostic = Diagnostic :: new ( category ( & INCOMPLETE_CONTINUATION ) , Critical :: BUG )
658732 . primary ( Label :: new (
659733 span,
660734 "continuation state is missing required columns" ,
@@ -671,8 +745,8 @@ fn incomplete_continuation(span: SpanId, body: DefId, field: &str) -> InterpretD
671745 diagnostic
672746}
673747
674- fn missing_execution_residual ( span : SpanId , body : DefId ) -> InterpretDiagnostic {
675- let mut diagnostic = Diagnostic :: new ( category ( & MISSING_EXECUTION_RESIDUAL ) , Severity :: Bug )
748+ fn missing_execution_residual ( span : SpanId , body : DefId ) -> OrchestratorDiagnostic {
749+ let mut diagnostic = Diagnostic :: new ( category ( & MISSING_EXECUTION_RESIDUAL ) , Critical :: BUG )
676750 . primary ( Label :: new (
677751 span,
678752 "no execution residual found for this definition" ,
@@ -689,8 +763,8 @@ fn missing_execution_residual(span: SpanId, body: DefId) -> InterpretDiagnostic
689763 diagnostic
690764}
691765
692- fn invalid_filter_return ( span : SpanId , body : DefId ) -> InterpretDiagnostic {
693- let mut diagnostic = Diagnostic :: new ( category ( & INVALID_FILTER_RETURN ) , Severity :: Bug )
766+ fn invalid_filter_return ( span : SpanId , body : DefId ) -> OrchestratorDiagnostic {
767+ let mut diagnostic = Diagnostic :: new ( category ( & INVALID_FILTER_RETURN ) , Critical :: BUG )
694768 . primary ( Label :: new ( span, "filter body returned a non-boolean value" ) ) ;
695769
696770 diagnostic. add_message ( Message :: note ( format ! (
@@ -704,8 +778,8 @@ fn invalid_filter_return(span: SpanId, body: DefId) -> InterpretDiagnostic {
704778 diagnostic
705779}
706780
707- fn value_serialization ( span : SpanId , error : & serde_json:: Error ) -> InterpretDiagnostic {
708- let mut diagnostic = Diagnostic :: new ( category ( & VALUE_SERIALIZATION ) , Severity :: Bug )
781+ fn value_serialization ( span : SpanId , error : & serde_json:: Error ) -> OrchestratorDiagnostic {
782+ let mut diagnostic = Diagnostic :: new ( category ( & VALUE_SERIALIZATION ) , Critical :: BUG )
709783 . primary ( Label :: new ( span, "cannot serialize runtime value to JSON" ) ) ;
710784
711785 diagnostic. add_message ( Message :: note ( format ! ( "serialization failed: {error}" ) ) ) ;
0 commit comments