Skip to main content

icydb_diagnostic_code/
lib.rs

1//! Module: lib
2//! Responsibility: compact diagnostic identity and public numeric error-code mapping.
3//! Does not own: rich diagnostic prose, Wire wire types, or runtime error construction.
4//! Boundary: maps rich internal diagnostic categories to stable compact public codes.
5//!
6//! This crate intentionally contains no rich diagnostic prose or Wire wire
7//! types. Production canister builds collapse diagnostics to numeric wire
8//! codes before they cross the public canister boundary. `Debug` output is
9//! numeric for the same reason: host tooling can recover labels from the code
10//! table without making every wasm canister retain those labels.
11
12use std::fmt;
13
14mod fact;
15mod query_field;
16
17pub use fact::{
18    DiagnosticAggregateKind, DiagnosticBacklogResource, DiagnosticComponentKind,
19    DiagnosticConstraintContext, DiagnosticConstraintKind, DiagnosticDecodeReason,
20    DiagnosticExecutionBudgetResource, DiagnosticExecutionBudgetScope, DiagnosticExecutionLane,
21    DiagnosticFactSchemaMismatch, DiagnosticFactTag, DiagnosticFunctionKind,
22    DiagnosticGroupBudgetResource, DiagnosticMutationOperation, DiagnosticOperatorKind,
23    DiagnosticTypeFamily, MAX_PUBLIC_DIAGNOSTIC_FACTS, pack_u32_pair, unpack_u32_pair,
24    validate_known_diagnostic_fact_schema, validate_raw_diagnostic_fact_schema,
25};
26pub use query_field::{
27    MAX_PUBLIC_QUERY_FIELD_BYTES, QueryFieldRole, QueryFieldSchemaMismatch,
28    validate_query_field_schema,
29};
30
31///
32/// DiagnosticCode
33///
34/// Stable machine-readable diagnostic reason.
35///
36
37#[remain::sorted]
38#[derive(Clone, Copy, Eq, Hash, PartialEq)]
39pub enum DiagnosticCode {
40    /// An admitted exact count has no available exact-cardinality metadata.
41    QueryExactCountMetadataUnavailable,
42    QueryIntent,
43    QueryInvalidContinuationCursor,
44    QueryNotFound,
45    QueryNotUnique,
46    QueryNumericNotRepresentable,
47    QueryNumericOverflow,
48    QueryPlan,
49    QueryReadAdmission,
50    QuerySqlSurfaceMismatch,
51    QuerySqlWriteBoundary,
52    QueryUnknownAggregateTargetField,
53    QueryUnorderedPagination,
54    QueryUnsupportedProjection,
55    QueryUnsupportedSqlFeature,
56    QueryValidate,
57    RuntimeConflict,
58    RuntimeCorruption,
59    RuntimeIncompatiblePersistedFormat,
60    RuntimeInternal,
61    RuntimeInvariantViolation,
62    RuntimeNotFound,
63    RuntimeUnsupported,
64    SchemaDdlAdmission,
65    StoreCorruption,
66    StoreInvariantViolation,
67    StoreNotFound,
68}
69
70impl DiagnosticCode {
71    /// Return the broad diagnostic class for this code.
72    #[must_use]
73    pub const fn class(self) -> ErrorClass {
74        match self {
75            Self::StoreCorruption | Self::RuntimeCorruption => ErrorClass::Corruption,
76            Self::RuntimeIncompatiblePersistedFormat => ErrorClass::IncompatiblePersistedFormat,
77            Self::QueryNotFound | Self::StoreNotFound | Self::RuntimeNotFound => {
78                ErrorClass::NotFound
79            }
80            Self::RuntimeConflict => ErrorClass::Conflict,
81            Self::QueryExactCountMetadataUnavailable
82            | Self::QueryUnsupportedSqlFeature
83            | Self::QueryUnknownAggregateTargetField
84            | Self::QueryUnsupportedProjection
85            | Self::QuerySqlSurfaceMismatch
86            | Self::QuerySqlWriteBoundary
87            | Self::RuntimeUnsupported => ErrorClass::Unsupported,
88            Self::StoreInvariantViolation | Self::RuntimeInvariantViolation => {
89                ErrorClass::InvariantViolation
90            }
91            Self::RuntimeInternal => ErrorClass::Internal,
92            Self::QueryValidate
93            | Self::QueryIntent
94            | Self::QueryPlan
95            | Self::QueryReadAdmission
96            | Self::QueryUnorderedPagination
97            | Self::QueryInvalidContinuationCursor
98            | Self::QueryNotUnique
99            | Self::QueryNumericOverflow
100            | Self::QueryNumericNotRepresentable
101            | Self::SchemaDdlAdmission => ErrorClass::Query,
102        }
103    }
104
105    /// Return the default diagnostic origin for this code.
106    #[must_use]
107    pub const fn origin(self) -> ErrorOrigin {
108        match self {
109            Self::StoreNotFound | Self::StoreCorruption | Self::StoreInvariantViolation => {
110                ErrorOrigin::Store
111            }
112            Self::RuntimeCorruption
113            | Self::RuntimeIncompatiblePersistedFormat
114            | Self::RuntimeInvariantViolation
115            | Self::RuntimeConflict
116            | Self::RuntimeNotFound
117            | Self::RuntimeUnsupported
118            | Self::RuntimeInternal => ErrorOrigin::Runtime,
119            Self::QueryExactCountMetadataUnavailable
120            | Self::QueryValidate
121            | Self::QueryIntent
122            | Self::QueryPlan
123            | Self::QueryReadAdmission
124            | Self::QueryUnorderedPagination
125            | Self::QueryInvalidContinuationCursor
126            | Self::QueryNotFound
127            | Self::QueryNotUnique
128            | Self::QueryNumericOverflow
129            | Self::QueryNumericNotRepresentable
130            | Self::QueryUnknownAggregateTargetField
131            | Self::QueryUnsupportedProjection
132            | Self::QueryUnsupportedSqlFeature
133            | Self::QuerySqlSurfaceMismatch
134            | Self::QuerySqlWriteBoundary
135            | Self::SchemaDdlAdmission => ErrorOrigin::Query,
136        }
137    }
138
139    /// Return the compact public wire code for this broad diagnostic reason.
140    #[must_use]
141    pub const fn error_code(self) -> ErrorCode {
142        match self {
143            Self::QueryExactCountMetadataUnavailable => {
144                ErrorCode::QUERY_EXACT_COUNT_METADATA_UNAVAILABLE
145            }
146            Self::QueryValidate => ErrorCode::QUERY_VALIDATE,
147            Self::QueryIntent => ErrorCode::QUERY_INTENT,
148            Self::QueryPlan => ErrorCode::QUERY_PLAN,
149            Self::QueryReadAdmission => ErrorCode::QUERY_READ_ADMISSION,
150            Self::QueryUnorderedPagination => ErrorCode::QUERY_UNORDERED_PAGINATION,
151            Self::QueryInvalidContinuationCursor => ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
152            Self::QueryNotFound => ErrorCode::QUERY_NOT_FOUND,
153            Self::QueryNotUnique => ErrorCode::QUERY_NOT_UNIQUE,
154            Self::QueryNumericOverflow => ErrorCode::QUERY_NUMERIC_OVERFLOW,
155            Self::QueryNumericNotRepresentable => ErrorCode::QUERY_NUMERIC_NOT_REPRESENTABLE,
156            Self::QueryUnknownAggregateTargetField => {
157                ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD
158            }
159            Self::QueryUnsupportedProjection => ErrorCode::QUERY_UNSUPPORTED_PROJECTION,
160            Self::QueryUnsupportedSqlFeature => ErrorCode::QUERY_UNSUPPORTED_SQL_FEATURE,
161            Self::QuerySqlSurfaceMismatch => ErrorCode::QUERY_SQL_SURFACE_MISMATCH,
162            Self::QuerySqlWriteBoundary => ErrorCode::QUERY_SQL_WRITE_BOUNDARY,
163            Self::SchemaDdlAdmission => ErrorCode::SCHEMA_DDL_ADMISSION,
164            Self::StoreNotFound => ErrorCode::STORE_NOT_FOUND,
165            Self::StoreCorruption => ErrorCode::STORE_CORRUPTION,
166            Self::StoreInvariantViolation => ErrorCode::STORE_INVARIANT_VIOLATION,
167            Self::RuntimeCorruption => ErrorCode::RUNTIME_CORRUPTION,
168            Self::RuntimeIncompatiblePersistedFormat => {
169                ErrorCode::RUNTIME_INCOMPATIBLE_PERSISTED_FORMAT
170            }
171            Self::RuntimeInvariantViolation => ErrorCode::RUNTIME_INVARIANT_VIOLATION,
172            Self::RuntimeConflict => ErrorCode::RUNTIME_CONFLICT,
173            Self::RuntimeNotFound => ErrorCode::RUNTIME_NOT_FOUND,
174            Self::RuntimeUnsupported => ErrorCode::RUNTIME_UNSUPPORTED,
175            Self::RuntimeInternal => ErrorCode::RUNTIME_INTERNAL,
176        }
177    }
178}
179
180impl fmt::Debug for DiagnosticCode {
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        fmt_compact_code(f, self.error_code().raw())
183    }
184}
185
186///
187/// ErrorCode
188///
189/// Stable numeric public error identity.
190///
191/// The public Wire `icydb::Error` stores this value as native `u16` so canister
192/// interfaces do not retain rich diagnostic enum labels. Rich diagnostics can
193/// still be reconstructed by host-side tooling from this leaf code. Before
194/// 1.0.0, the code space is hard-cut to a single compact sequential range.
195///
196
197#[derive(Clone, Copy, Eq, Hash, PartialEq)]
198pub struct ErrorCode(u16);
199
200mod registry;
201
202impl ErrorCode {
203    /// Build an error code from its raw public wire value.
204    #[must_use]
205    pub const fn from_raw(raw: u16) -> Self {
206        Self(raw)
207    }
208
209    /// Return the raw public wire value.
210    #[must_use]
211    pub const fn raw(self) -> u16 {
212        self.0
213    }
214}
215
216impl fmt::Debug for ErrorCode {
217    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218        fmt_compact_code(f, self.raw())
219    }
220}
221
222///
223/// ErrorClass
224///
225/// Broad diagnostic class used for recovery decisions.
226///
227
228#[remain::sorted]
229#[derive(Clone, Copy, Eq, Hash, PartialEq)]
230pub enum ErrorClass {
231    Conflict,
232    Corruption,
233    IncompatiblePersistedFormat,
234    Internal,
235    InvariantViolation,
236    NotFound,
237    Query,
238    Unsupported,
239}
240
241impl ErrorClass {
242    /// Return the compact public wire code for this diagnostic class.
243    #[must_use]
244    pub const fn wire_code(self) -> u8 {
245        match self {
246            Self::Query => 1,
247            Self::Corruption => 2,
248            Self::IncompatiblePersistedFormat => 3,
249            Self::NotFound => 4,
250            Self::Internal => 5,
251            Self::Conflict => 6,
252            Self::Unsupported => 7,
253            Self::InvariantViolation => 8,
254        }
255    }
256
257    /// Recover a diagnostic class from its compact public wire code.
258    #[must_use]
259    pub const fn from_wire_code(code: u8) -> Option<Self> {
260        match code {
261            1 => Some(Self::Query),
262            2 => Some(Self::Corruption),
263            3 => Some(Self::IncompatiblePersistedFormat),
264            4 => Some(Self::NotFound),
265            5 => Some(Self::Internal),
266            6 => Some(Self::Conflict),
267            7 => Some(Self::Unsupported),
268            8 => Some(Self::InvariantViolation),
269            _ => None,
270        }
271    }
272}
273
274impl fmt::Debug for ErrorClass {
275    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276        fmt_compact_code(f, u16::from(self.wire_code()))
277    }
278}
279
280///
281/// ErrorOrigin
282///
283/// Subsystem that owns the diagnostic.
284///
285
286#[remain::sorted]
287#[derive(Clone, Copy, Eq, Hash, PartialEq)]
288pub enum ErrorOrigin {
289    Cursor,
290    Executor,
291    Identity,
292    Index,
293    Interface,
294    Planner,
295    Query,
296    Recovery,
297    Response,
298    Runtime,
299    Serialize,
300    Store,
301}
302
303impl ErrorOrigin {
304    /// Return the compact public wire code for this diagnostic origin.
305    #[must_use]
306    pub const fn wire_code(self) -> u8 {
307        match self {
308            Self::Cursor => 1,
309            Self::Executor => 2,
310            Self::Identity => 3,
311            Self::Index => 4,
312            Self::Interface => 5,
313            Self::Planner => 6,
314            Self::Query => 7,
315            Self::Recovery => 8,
316            Self::Response => 9,
317            Self::Runtime => 10,
318            Self::Serialize => 11,
319            Self::Store => 12,
320        }
321    }
322
323    /// Recover a known diagnostic origin from its compact public wire code.
324    #[must_use]
325    pub const fn from_known_wire_code(code: u8) -> Option<Self> {
326        match code {
327            1 => Some(Self::Cursor),
328            2 => Some(Self::Executor),
329            3 => Some(Self::Identity),
330            4 => Some(Self::Index),
331            5 => Some(Self::Interface),
332            6 => Some(Self::Planner),
333            7 => Some(Self::Query),
334            8 => Some(Self::Recovery),
335            9 => Some(Self::Response),
336            10 => Some(Self::Runtime),
337            11 => Some(Self::Serialize),
338            12 => Some(Self::Store),
339            _ => None,
340        }
341    }
342
343    /// Recover a diagnostic origin from its compact public wire code.
344    ///
345    /// Unknown origin codes fail closed to `Runtime`, matching the public
346    /// boundary behavior used by the Wire facade.
347    #[must_use]
348    pub const fn from_wire_code(code: u8) -> Self {
349        match Self::from_known_wire_code(code) {
350            Some(origin) => origin,
351            None => Self::Runtime,
352        }
353    }
354}
355
356impl fmt::Debug for ErrorOrigin {
357    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358        fmt_compact_code(f, u16::from(self.wire_code()))
359    }
360}
361
362///
363/// QueryErrorKind
364///
365/// Public query error category.
366///
367
368#[repr(u16)]
369#[derive(Clone, Copy, Eq, Hash, PartialEq)]
370pub enum QueryErrorKind {
371    Validate,
372    Intent,
373    Plan,
374    UnorderedPagination,
375    InvalidContinuationCursor,
376    NotFound,
377    NotUnique,
378}
379
380impl fmt::Debug for QueryErrorKind {
381    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382        fmt_compact_code(f, *self as u16)
383    }
384}
385
386///
387/// QueryProjectionCode
388///
389/// Compact query projection admission/runtime identifier.
390/// Variant order is wire-order significant for public error-code offsets.
391///
392
393#[repr(u16)]
394#[derive(Clone, Copy, Eq, Hash, PartialEq)]
395pub enum QueryProjectionCode {
396    NumericLiteralRequired,
397    NumericScaleArguments,
398    NestedFieldPathPreview,
399    CaseConditionBooleanRequired,
400    NumericInputRequired,
401    TextOrBlobInputRequired,
402    TextInputRequired,
403    TextOrNullArgumentRequired,
404    IntegerOrNullArgumentRequired,
405    UnaryOperandIncompatible,
406    BinaryOperandsIncompatible,
407}
408
409impl fmt::Debug for QueryProjectionCode {
410    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
411        fmt_compact_code(f, *self as u16)
412    }
413}
414
415///
416/// QueryReadAdmissionCode
417///
418/// Compact read-admission rejection identifier.
419/// Variant order is wire-order significant for public error-code offsets.
420///
421
422#[repr(u16)]
423#[derive(Clone, Copy, Eq, Hash, PartialEq)]
424pub enum QueryReadAdmissionCode {
425    PublicQueryRequiresLimit,
426    PublicQueryRequiresIndex,
427    UnboundedFullScanRejected,
428    SortRequiresMaterialization,
429    GroupedQueryRequiresLimits,
430    GroupedQueryExceedsBudget,
431    DiagnosticLaneDoesNotExecute,
432    ReturnedRowBoundExceedsPolicy,
433    PrimaryKeyInputExceedsPolicy,
434    /// Authored query expressions or values exceed the shared depth ceiling.
435    InputDepthExceeded,
436    /// Authored query components exceed the shared node ceiling.
437    InputNodesExceeded,
438    /// Authored query payload exceeds the shared byte ceiling.
439    InputBytesExceeded,
440    /// Logical explain describes a request before any cursor boundary.
441    ExplainDoesNotAcceptCursor,
442}
443
444impl fmt::Debug for QueryReadAdmissionCode {
445    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
446        fmt_compact_code(f, *self as u16)
447    }
448}
449
450///
451/// RuntimeErrorKind
452///
453/// Public runtime error category.
454///
455
456#[repr(u16)]
457#[derive(Clone, Copy, Eq, Hash, PartialEq)]
458pub enum RuntimeErrorKind {
459    Corruption,
460    IncompatiblePersistedFormat,
461    InvariantViolation,
462    Conflict,
463    NotFound,
464    Unsupported,
465    Internal,
466}
467
468impl fmt::Debug for RuntimeErrorKind {
469    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470        fmt_compact_code(f, *self as u16)
471    }
472}
473
474///
475/// RuntimeBoundaryCode
476///
477/// Compact public-runtime boundary identifier.
478/// Variant order is wire-order significant for public error-code offsets.
479///
480
481#[repr(u16)]
482#[derive(Clone, Copy, Eq, Hash, PartialEq)]
483pub enum RuntimeBoundaryCode {
484    SqlSurfaceControllerRequired,
485    SchemaSurfaceControllerRequired,
486    SqlQueryNoConfiguredEntities,
487    SqlQueryEntityNotFound,
488    SqlDdlTargetRequired,
489    SqlDdlEntityNotConfigured,
490    SqlIntrospectionDisabled,
491    /// A complete accepted mutation omitted a required field.
492    MutationRequiredFieldMissing,
493    /// A logical write would move an accepted managed timestamp backward.
494    MutationManagedTimestampRegression,
495    /// A persisted row's stamp falls outside the accepted layout window.
496    PersistedRowLayoutOutsideAcceptedWindow,
497    /// A persisted row's physical slot count disagrees with its layout stamp.
498    PersistedRowSlotCountMismatch,
499    /// A generated field would collide with an accepted DDL-owned slot.
500    GeneratedFieldAfterDdlField,
501    /// A journaled mutation cannot reserve a representable post-commit revision.
502    JournalMutationRevisionExhausted,
503    /// A final canonical after-image violates one accepted row constraint or gate.
504    ConstraintViolation,
505    /// Accepted row-constraint metadata or its compiled program is inconsistent.
506    AcceptedRowConstraintProgramCorrupt,
507    /// A write conflicts with one incomplete accepted constraint activation.
508    ConstraintActivationWriteBlocked,
509    /// A live generated constraint activation no longer matches its proposal.
510    GeneratedConstraintActivationStale,
511    /// A caller explicitly authored a field owned by accepted database policy.
512    MutationDatabaseOwnedFieldExplicit,
513    /// A mixed structural mutation batch contained no operations.
514    MutationBatchEmpty,
515    /// A mixed structural mutation batch exceeded its operation-count bound.
516    MutationBatchTooManyItems,
517    /// A mixed structural mutation batch exceeded its staged-byte bound.
518    MutationBatchStagedBytesExceeded,
519    /// A mixed structural mutation result exceeded its encoded response bound.
520    MutationBatchResultBytesExceeded,
521    /// A mixed structural mutation batch crossed an accepted store boundary.
522    MutationBatchStoreMismatch,
523    /// A mixed structural mutation batch exceeded its distinct-entity bound.
524    MutationBatchTooManyEntities,
525    /// More than one mixed structural operation targeted the same accepted key.
526    MutationBatchDuplicateKey,
527    /// An operational report or reset endpoint requires a controller caller.
528    OperationalSurfaceControllerRequired,
529    /// An exact-key batch exceeded its admitted input item count.
530    ExactKeyBatchTooManyItems,
531    /// An exact-key batch exceeded its admitted encoded input-key bytes.
532    ExactKeyBatchInputBytesExceeded,
533    /// An exact-key batch exceeded its admitted distinct stored-row bytes.
534    ExactKeyBatchStoredBytesExceeded,
535    /// An exact-key batch exceeded its admitted logical result bytes.
536    ExactKeyBatchResultBytesExceeded,
537    /// One charged execution resource exceeded its absolute safety ceiling.
538    ExecutionBudgetExceeded,
539    /// One indivisible scalar-page unit cannot fit in an otherwise empty page envelope.
540    PageUnitTooLarge,
541    /// Database access was attempted without an active request-execution scope.
542    RequestExecutionScopeRequired,
543    /// An explicit request root conflicts with the root already active for this request.
544    RequestExecutionRootMismatch,
545    /// A successful generated SQL query reply exceeds the deployed IC query-response limit.
546    SqlQueryReplyBytesExceeded,
547    QueryExplainOutputExceeded,
548    /// A derived logical explain access tree exceeds the diagnostic depth limit.
549    QueryExplainDepthExceeded,
550    /// Startup recovery remains incomplete and ordinary database work must retry later.
551    DatabaseStartupRecoveryPending,
552    /// An application guard denied access to the generated SQL read surface.
553    SqlSurfacePolicyDenied,
554    /// An application guard denied access to the generated accepted-schema surface.
555    SchemaSurfacePolicyDenied,
556    /// A structural mutation batch exceeded its canonical prepared-commit work bound.
557    MutationBatchCommitWorkExceeded,
558    /// Retained journal debt leaves insufficient bounded convergence capacity.
559    ConvergenceBacklogPressure,
560    /// Requested manager bucket pages differ from the existing memory layout.
561    MemoryBucketSizeMismatch,
562    /// Host grants cannot resolve or authorize current logical allocations.
563    MemoryAllocationResolutionFailed,
564    /// Current declarations omitted a historical IcyDB database namespace.
565    MemoryNamespaceRemoved,
566    /// Current database controls or store allocations lack a complete role set.
567    MemoryAllocationRolesIncomplete,
568    /// Current memory declarations violate their bounded identity/role contract.
569    MemoryDeclarationInvalid,
570    /// Current declaration requirements differ from established memory authority.
571    MemoryDeclarationSnapshotMismatch,
572    /// A historical allocation required for journal recovery cannot be selected.
573    MemoryHistoricalJournalUnavailable,
574    /// A typed batch handle belongs to another result owner.
575    TypedAdapterBatchHandleMismatch,
576    /// A typed batch result row has already been consumed.
577    TypedAdapterBatchRowConsumed,
578    /// Generated source descriptors cannot bind to accepted schema authority.
579    TypedAdapterBindingUnavailable,
580    /// The output row belongs to another accepted entity.
581    TypedAdapterEntityMismatch,
582    /// The requested immutable field source is absent from the binding.
583    TypedAdapterFieldUnavailable,
584    /// A generated field contract disagrees with accepted schema authority.
585    TypedAdapterIncompatibleField,
586    /// A projected output row omits the field needed by its typed adapter.
587    TypedAdapterRowFieldUnavailable,
588    /// Output column and value cardinalities disagree.
589    TypedAdapterRowShapeMismatch,
590    /// A typed binding no longer represents current accepted authority.
591    TypedAdapterStaleBinding,
592    /// An output value cannot be represented by the generated Rust field type.
593    TypedAdapterValueShapeMismatch,
594}
595
596impl fmt::Debug for RuntimeBoundaryCode {
597    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
598        fmt_compact_code(f, *self as u16)
599    }
600}
601
602///
603/// SqlFeatureCode
604///
605/// Compact SQL feature identifier used by unsupported-feature diagnostics.
606/// Variant order is wire-order significant for public error-code offsets.
607///
608
609#[repr(u16)]
610#[derive(Clone, Copy, Eq, Hash, PartialEq)]
611pub enum SqlFeatureCode {
612    AggregateFilterClause,
613    AlterStatementBeyondAlterTable,
614    AlterTableAddColumnDuplicateDefault,
615    AlterTableAddColumnModifiers,
616    AlterTableAddStatementBeyondAddColumn,
617    AlterTableAlterColumnDropUnsupportedAction,
618    AlterTableAlterColumnModifiers,
619    AlterTableAlterColumnSetUnsupportedAction,
620    AlterTableAlterColumnUnsupportedAction,
621    AlterTableAlterStatementBeyondAlterColumn,
622    AlterTableDropColumnIfExistsSyntax,
623    AlterTableDropColumnModifiers,
624    AlterTableDropStatementBeyondDropColumn,
625    AlterTableRenameColumnMissingTo,
626    AlterTableRenameColumnModifiers,
627    AlterTableRenameStatementBeyondRenameColumn,
628    AlterTableUnsupportedOperation,
629    ColumnAlias,
630    CreateIndexIfNotExistsSyntax,
631    CreateIndexKeyOrderingModifiers,
632    CreateIndexModifiers,
633    CreateStatementBeyondCreateIndex,
634    DescribeModifier,
635    DdlSchemaVersionDuplicateExpectedClause,
636    DdlSchemaVersionDuplicateSetClause,
637    DropIndexModifiers,
638    DropIndexIfExistsSyntax,
639    DropStatementBeyondDropIndex,
640    ExpressionIndexUnsupportedFunction,
641    Having,
642    Insert,
643    Join,
644    LikePatternBeyondTrailingPrefix,
645    LowerFieldPredicateUnsupported,
646    MultiStatementSql,
647    NestedAggregateInput,
648    NestedProjectionFunctionInArithmetic,
649    OrderByUnsupportedForm,
650    Other,
651    PredicateStartsWithFirstArgument,
652    QuotedIdentifiers,
653    ReturningUnsupportedShape,
654    ScalarFunctionExpressionPosition,
655    ScaleTakingNumericFunctionExpressionPosition,
656    ShowColumnsModifiers,
657    ShowEntitiesModifiers,
658    ShowIndexesModifiers,
659    ShowMemoryModifiers,
660    ShowStoresModifiers,
661    ShowUnsupportedCommand,
662    SimpleCaseExpression,
663    StandaloneLiteralProjectionItem,
664    UnionIntersectExcept,
665    UnsupportedFunctionNamespace,
666    Update,
667    UpperFieldPredicateUnsupported,
668    WindowFunction,
669    With,
670    NumericScaleFunctionArguments,
671    OrderByFieldNotOrderable,
672    ShowConstraintsModifiers,
673    AlterTableAddConstraintBeyondCheck,
674    AlterTableAddConstraintModifiers,
675    AlterTableDropConstraintIfExistsSyntax,
676    AlterTableDropConstraintModifiers,
677    AlterTableValidateBeyondConstraint,
678    AlterTableValidateConstraintModifiers,
679    ShowRelationsModifiers,
680}
681
682impl fmt::Debug for SqlFeatureCode {
683    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
684        fmt_compact_code(f, *self as u16)
685    }
686}
687
688///
689/// SqlLoweringCode
690///
691/// Compact SQL lowering rejection identifier used after parsing succeeds but
692/// before a statement becomes canonical query intent.
693/// Variant order is wire-order significant for public error-code offsets.
694///
695
696#[repr(u16)]
697#[derive(Clone, Copy, Eq, Hash, PartialEq)]
698pub enum SqlLoweringCode {
699    EntityMismatch,
700    SelectProjectionShape,
701    SelectDistinct,
702    DistinctOrderByProjection,
703    GlobalAggregateProjection,
704    GlobalAggregateGroupBy,
705    SelectGroupByShape,
706    GroupedProjectionExplicitListRequired,
707    GroupedProjectionAggregateRequired,
708    GroupedProjectionNonGroupField,
709    GroupedProjectionScalarAfterAggregate,
710    HavingRequiresGroupBy,
711    SelectHavingShape,
712    AggregateInputExpressions,
713    WhereExpressionShape,
714    ParameterPlacement,
715    SqlDdlExecutionUnsupported,
716    BindingCount,
717    BindingFamily,
718    BindingLimit,
719}
720
721impl fmt::Debug for SqlLoweringCode {
722    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
723        fmt_compact_code(f, *self as u16)
724    }
725}
726
727///
728/// SqlSurfaceMismatchCode
729///
730/// Compact SQL endpoint surface mismatch identifier.
731/// Variant order is wire-order significant for public error-code offsets.
732///
733
734#[repr(u16)]
735#[derive(Clone, Copy, Eq, Hash, PartialEq)]
736pub enum SqlSurfaceMismatchCode {
737    QueryRejectsInsert,
738    QueryRejectsUpdate,
739    QueryRejectsDelete,
740    MutationRejectsSelect,
741    MutationRejectsExplain,
742    MutationRejectsDescribe,
743    MutationRejectsShowIndexes,
744    MutationRejectsShowColumns,
745    MutationRejectsShowEntities,
746    MutationRejectsShowStores,
747    MutationRejectsShowMemory,
748    MutationRequiresExplicitUpdateIntent,
749    MutationRejectsShowConstraints,
750    MutationRejectsShowRelations,
751}
752
753impl fmt::Debug for SqlSurfaceMismatchCode {
754    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
755        fmt_compact_code(f, *self as u16)
756    }
757}
758
759///
760/// SqlWriteBoundaryCode
761///
762/// Compact SQL write fail-closed boundary identifier.
763/// Variant order is wire-order significant for public error-code offsets.
764///
765
766#[repr(u16)]
767#[derive(Clone, Copy, Eq, Hash, PartialEq)]
768pub enum SqlWriteBoundaryCode {
769    PrimaryKeyLiteralIncompatible,
770    MissingPrimaryKey,
771    MissingRequiredFields,
772    ExplicitManagedField,
773    ExplicitGeneratedField,
774    InsertSelectRequiresScalar,
775    InsertSelectAggregateProjection,
776    InsertSelectWidthMismatch,
777    UpdatePrimaryKeyMutation,
778    InvalidFieldLiteral,
779    UnknownReturningField,
780    DuplicateReturningField,
781    UpdateMissingWherePredicate,
782    WriteOrderByUnsupportedShape,
783    ReturningResponseTooLarge,
784    ReturningRowsTooMany,
785    StagedRowsTooMany,
786    InsertDefaultRequiredField,
787    UpdateDefaultRequiredField,
788    UpdateDefaultDatabaseOwnedField,
789    ExactUpdateAssertionRequired,
790    ExactUpdateAssertionTooHigh,
791    ExactUpdateAffectedRowsExceeded,
792    ExactUpdateWindowUnsupported,
793    WriteScanBudgetExceeded,
794    ResumableUpdateWindowUnsupported,
795    ResumableUpdateReturningUnsupported,
796    ResumableUpdateExpressionUnsupported,
797    ResumableUpdateRequiresJournaledStore,
798    ResumableUpdateAssignedFieldHasGlobalConstraint,
799    ResumableUpdateScopeDependsOnAssignedField,
800    ResumableUpdateScopeDependencyUnknown,
801    ResumableUpdateContinuationMalformed,
802    ResumableUpdateContinuationTargetMismatch,
803    ResumableUpdateContinuationSchemaMismatch,
804    ResumableUpdateContinuationScopeMismatch,
805    ResumableUpdateContinuationPatchMismatch,
806    ResumableUpdateContinuationBatchPolicyMismatch,
807    ResumableUpdateManagedFieldHasGlobalConstraint,
808}
809
810impl fmt::Debug for SqlWriteBoundaryCode {
811    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
812        fmt_compact_code(f, *self as u16)
813    }
814}
815
816///
817/// SchemaDdlAdmissionCode
818///
819/// Compact SQL DDL admission rejection reason.
820/// Variant order is wire-order significant for public error-code offsets.
821///
822
823#[repr(u16)]
824#[derive(Clone, Copy, Eq, Hash, PartialEq)]
825pub enum SchemaDdlAdmissionCode {
826    MissingExpectedSchemaVersion,
827    MissingNextSchemaVersion,
828    StaleExpectedSchemaVersion,
829    InvalidExpectedSchemaVersion,
830    InvalidNextSchemaVersion,
831    AcceptedSchemaChangeWithoutVersionBump,
832    EmptyVersionBump,
833    VersionGap,
834    VersionRollback,
835    FingerprintMethodMismatch,
836    UnsupportedTransitionClass,
837    PhysicalRunnerMissing,
838    ValidationFailed,
839    PublicationRaceLost,
840    InvalidAddColumnDefault,
841    InvalidAlterColumnDefault,
842    GeneratedIndexDropRejected,
843    SchemaRewriteRequiresMigration,
844    SchemaTransitionBudgetExceeded,
845    GeneratedFieldDefaultChangeRejected,
846    GeneratedFieldNullabilityChangeRejected,
847    RowLayoutVersionExhausted,
848}
849
850impl fmt::Debug for SchemaDdlAdmissionCode {
851    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
852        fmt_compact_code(f, *self as u16)
853    }
854}
855
856/// Machine-readable source-migration rejection or lifecycle finding.
857#[repr(u16)]
858#[derive(Clone, Copy, Eq, Hash, PartialEq)]
859pub enum SchemaMigrationCode {
860    Unadopted,
861    MissingMigration,
862    VersionGap,
863    Downgrade,
864    EmptyEntityVersionBump,
865    StaleAcceptedHead,
866    PlanChanged,
867    UnknownFromObject,
868    UnknownToObject,
869    KindMismatch,
870    IdentityConflict,
871    UnexplainedSchemaDifference,
872    UnsupportedTransform,
873    PhysicalRunnerMissing,
874    MigrationInProgress,
875    AbortTooLate,
876    ProgressCorrupt,
877    CandidateMismatch,
878    PublicationRaceLost,
879}
880
881impl SchemaMigrationCode {
882    /// Return the broad diagnostic category for this migration result.
883    #[must_use]
884    pub const fn diagnostic_code(self) -> DiagnosticCode {
885        match self {
886            Self::StaleAcceptedHead
887            | Self::PlanChanged
888            | Self::IdentityConflict
889            | Self::MigrationInProgress
890            | Self::AbortTooLate
891            | Self::PublicationRaceLost => DiagnosticCode::RuntimeConflict,
892            Self::ProgressCorrupt | Self::CandidateMismatch => DiagnosticCode::RuntimeCorruption,
893            Self::Unadopted
894            | Self::MissingMigration
895            | Self::VersionGap
896            | Self::Downgrade
897            | Self::EmptyEntityVersionBump
898            | Self::UnknownFromObject
899            | Self::UnknownToObject
900            | Self::KindMismatch
901            | Self::UnexplainedSchemaDifference
902            | Self::UnsupportedTransform
903            | Self::PhysicalRunnerMissing => DiagnosticCode::RuntimeUnsupported,
904        }
905    }
906}
907
908impl fmt::Debug for SchemaMigrationCode {
909    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
910        fmt_compact_code(f, *self as u16)
911    }
912}
913
914///
915/// DiagnosticDetail
916///
917/// Small structured diagnostic payload for callers and CLI rendering.
918///
919
920#[remain::sorted]
921#[derive(Clone, Copy, Eq, PartialEq)]
922pub enum DiagnosticDetail {
923    QueryKind { kind: QueryErrorKind },
924    QueryProjection { reason: QueryProjectionCode },
925    QueryReadAdmission { reason: QueryReadAdmissionCode },
926    RuntimeBoundary { boundary: RuntimeBoundaryCode },
927    RuntimeKind { kind: RuntimeErrorKind },
928    SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
929    SchemaMigration { reason: SchemaMigrationCode },
930    SqlLowering { reason: SqlLoweringCode },
931    SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
932    SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
933    UnsupportedSqlFeature { feature: SqlFeatureCode },
934}
935
936impl fmt::Debug for DiagnosticDetail {
937    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
938        fmt_compact_code(
939            f,
940            ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
941        )
942    }
943}
944
945///
946/// Diagnostic
947///
948/// Compact public diagnostic payload.
949///
950
951#[derive(Clone, Eq, PartialEq)]
952pub struct Diagnostic {
953    code: DiagnosticCode,
954    origin: ErrorOrigin,
955    detail: Option<DiagnosticDetail>,
956}
957
958impl Diagnostic {
959    /// Build a compact diagnostic from a code and optional structured detail.
960    #[must_use]
961    pub const fn new(
962        code: DiagnosticCode,
963        origin: ErrorOrigin,
964        detail: Option<DiagnosticDetail>,
965    ) -> Self {
966        Self {
967            code,
968            origin,
969            detail,
970        }
971    }
972
973    /// Build a compact diagnostic using the code's default origin.
974    #[must_use]
975    pub const fn from_code(code: DiagnosticCode) -> Self {
976        Self::new(code, code.origin(), None)
977    }
978
979    /// Return the stable diagnostic code.
980    #[must_use]
981    pub const fn code(&self) -> DiagnosticCode {
982        self.code
983    }
984
985    /// Return the diagnostic class.
986    #[must_use]
987    pub const fn class(&self) -> ErrorClass {
988        self.code.class()
989    }
990
991    /// Return the subsystem origin.
992    #[must_use]
993    pub const fn origin(&self) -> ErrorOrigin {
994        self.origin
995    }
996
997    /// Return structured diagnostic detail, when available.
998    #[must_use]
999    pub const fn detail(&self) -> Option<&DiagnosticDetail> {
1000        self.detail.as_ref()
1001    }
1002
1003    /// Return the numeric public wire code for this diagnostic.
1004    #[must_use]
1005    pub const fn error_code(&self) -> ErrorCode {
1006        ErrorCode::from_parts(self.code, self.detail)
1007    }
1008}
1009
1010impl fmt::Debug for Diagnostic {
1011    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1012        write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
1013    }
1014}
1015
1016fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
1017    write!(f, "{raw}")
1018}
1019
1020#[cfg(test)]
1021mod tests {
1022    use super::{
1023        Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
1024        QueryProjectionCode, QueryReadAdmissionCode, SqlFeatureCode, SqlLoweringCode,
1025        SqlWriteBoundaryCode,
1026        registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
1027    };
1028
1029    #[test]
1030    fn diagnostic_from_code_uses_default_origin() {
1031        let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
1032
1033        assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
1034        assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
1035    }
1036
1037    #[test]
1038    fn diagnostic_code_reports_broad_class() {
1039        assert_eq!(
1040            DiagnosticCode::QueryUnsupportedSqlFeature.class(),
1041            ErrorClass::Unsupported
1042        );
1043        assert_eq!(
1044            DiagnosticCode::QuerySqlSurfaceMismatch.class(),
1045            ErrorClass::Unsupported
1046        );
1047        assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
1048        assert_eq!(
1049            DiagnosticCode::StoreCorruption.class(),
1050            ErrorClass::Corruption
1051        );
1052    }
1053
1054    #[test]
1055    fn class_and_origin_wire_codes_round_trip() {
1056        for (class, raw) in [
1057            (ErrorClass::Query, 1),
1058            (ErrorClass::Corruption, 2),
1059            (ErrorClass::IncompatiblePersistedFormat, 3),
1060            (ErrorClass::NotFound, 4),
1061            (ErrorClass::Internal, 5),
1062            (ErrorClass::Conflict, 6),
1063            (ErrorClass::Unsupported, 7),
1064            (ErrorClass::InvariantViolation, 8),
1065        ] {
1066            assert_eq!(class.wire_code(), raw);
1067            assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
1068            assert_eq!(format!("{class:?}"), raw.to_string());
1069        }
1070
1071        for (origin, raw) in [
1072            (ErrorOrigin::Cursor, 1),
1073            (ErrorOrigin::Executor, 2),
1074            (ErrorOrigin::Identity, 3),
1075            (ErrorOrigin::Index, 4),
1076            (ErrorOrigin::Interface, 5),
1077            (ErrorOrigin::Planner, 6),
1078            (ErrorOrigin::Query, 7),
1079            (ErrorOrigin::Recovery, 8),
1080            (ErrorOrigin::Response, 9),
1081            (ErrorOrigin::Runtime, 10),
1082            (ErrorOrigin::Serialize, 11),
1083            (ErrorOrigin::Store, 12),
1084        ] {
1085            assert_eq!(origin.wire_code(), raw);
1086            assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
1087            assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
1088            assert_eq!(format!("{origin:?}"), raw.to_string());
1089        }
1090
1091        assert_eq!(ErrorClass::from_wire_code(0), None);
1092        assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
1093        assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
1094    }
1095
1096    #[test]
1097    fn public_error_codes_are_sequential() {
1098        let first = ORDERED_ERROR_CODES
1099            .first()
1100            .expect("public error-code registry is non-empty")
1101            .raw();
1102
1103        assert_eq!(first, 1);
1104
1105        for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
1106            let expected = first + u16::try_from(index).expect("test error-code index fits u16");
1107            assert_eq!(code.raw(), expected);
1108            assert_eq!(ErrorCode::known(code.raw()), Some(*code));
1109            assert!(code.is_known());
1110        }
1111
1112        let last = ORDERED_ERROR_CODES
1113            .last()
1114            .expect("public error-code registry is non-empty")
1115            .raw();
1116
1117        assert_eq!(last, 292);
1118    }
1119
1120    #[test]
1121    fn all_public_error_codes_round_trip_through_diagnostic_parts() {
1122        let first = ORDERED_ERROR_CODES
1123            .first()
1124            .expect("public error-code registry is non-empty")
1125            .raw();
1126        let last = ORDERED_ERROR_CODES
1127            .last()
1128            .expect("public error-code registry is non-empty")
1129            .raw();
1130
1131        for raw in first..=last {
1132            let code = ErrorCode::from_raw(raw);
1133            let diagnostic_code = code.diagnostic_code();
1134            let diagnostic_detail = code.diagnostic_detail();
1135            let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
1136
1137            assert_eq!(rebuilt.raw(), raw);
1138
1139            let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
1140
1141            assert_eq!(diagnostic.code(), diagnostic_code);
1142            assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
1143            assert_eq!(diagnostic.error_code().raw(), raw);
1144        }
1145    }
1146
1147    #[test]
1148    fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
1149        let first_unknown = ORDERED_ERROR_CODES
1150            .last()
1151            .expect("public error-code registry is non-empty")
1152            .raw()
1153            .checked_add(1)
1154            .expect("public error-code registry retains an unknown successor");
1155
1156        for raw in [0, first_unknown, u16::MAX] {
1157            let code = ErrorCode::from_raw(raw);
1158
1159            assert_eq!(ErrorCode::known(raw), None);
1160            assert!(!code.is_known());
1161            assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
1162            assert_eq!(code.diagnostic_detail(), None);
1163            assert_eq!(code.class(), ErrorClass::Internal);
1164
1165            let diagnostic = code.diagnostic(ErrorOrigin::Query);
1166
1167            assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
1168            assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
1169            assert_eq!(diagnostic.detail(), None);
1170            assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
1171        }
1172    }
1173
1174    #[test]
1175    fn from_parts_requires_detail_to_match_broad_code() {
1176        let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
1177            feature: SqlFeatureCode::Join,
1178        });
1179
1180        assert_eq!(
1181            ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
1182            ErrorCode::SQL_FEATURE_JOIN
1183        );
1184        assert_eq!(
1185            ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
1186            ErrorCode::QUERY_PLAN
1187        );
1188    }
1189
1190    #[test]
1191    fn detail_bearing_registry_entries_round_trip_directly() {
1192        assert!(!DETAIL_ERROR_CODES.is_empty());
1193
1194        for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
1195            assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
1196            assert_eq!(code.diagnostic_code(), diagnostic_code);
1197            assert_eq!(code.diagnostic_detail(), Some(detail));
1198            assert_eq!(detail.diagnostic_code(), diagnostic_code);
1199        }
1200    }
1201
1202    #[test]
1203    fn diagnostic_detail_reports_generated_broad_code() {
1204        let detail = DiagnosticDetail::UnsupportedSqlFeature {
1205            feature: SqlFeatureCode::Join,
1206        };
1207
1208        assert_eq!(
1209            detail.diagnostic_code(),
1210            DiagnosticCode::QueryUnsupportedSqlFeature
1211        );
1212        assert_eq!(format!("{detail:?}"), "61");
1213    }
1214
1215    #[test]
1216    fn public_error_codes_reconstruct_shifted_details() {
1217        assert_eq!(
1218            ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
1219            DiagnosticCode::QueryUnknownAggregateTargetField
1220        );
1221        assert_eq!(
1222            ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
1223            Some(DiagnosticDetail::UnsupportedSqlFeature {
1224                feature: SqlFeatureCode::Join,
1225            })
1226        );
1227        assert_eq!(
1228            ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1229            Some(DiagnosticDetail::QueryProjection {
1230                reason: QueryProjectionCode::NumericLiteralRequired,
1231            })
1232        );
1233        assert_eq!(
1234            ErrorCode::QUERY_READ_PUBLIC_REQUIRES_LIMIT.diagnostic_detail(),
1235            Some(DiagnosticDetail::QueryReadAdmission {
1236                reason: QueryReadAdmissionCode::PublicQueryRequiresLimit,
1237            })
1238        );
1239        assert_eq!(
1240            ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1241            Some(DiagnosticDetail::SqlLowering {
1242                reason: SqlLoweringCode::DistinctOrderByProjection,
1243            })
1244        );
1245        assert_eq!(
1246            ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1247            Some(DiagnosticDetail::SqlWriteBoundary {
1248                boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1249            })
1250        );
1251        assert_eq!(
1252            ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1253            Some(DiagnosticDetail::SqlWriteBoundary {
1254                boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1255            })
1256        );
1257    }
1258}