1use std::fmt;
13
14#[remain::sorted]
21#[derive(Clone, Copy, Eq, Hash, PartialEq)]
22pub enum DiagnosticCode {
23 QueryAccessRequirement,
24 QueryIntent,
25 QueryInvalidContinuationCursor,
26 QueryNotFound,
27 QueryNotUnique,
28 QueryNumericNotRepresentable,
29 QueryNumericOverflow,
30 QueryPlan,
31 QueryReadAdmission,
32 QueryResultShapeMismatch,
33 QuerySqlSurfaceMismatch,
34 QuerySqlWriteBoundary,
35 QueryUnknownAggregateTargetField,
36 QueryUnorderedPagination,
37 QueryUnsupportedProjection,
38 QueryUnsupportedSqlFeature,
39 QueryValidate,
40 RuntimeConflict,
41 RuntimeCorruption,
42 RuntimeIncompatiblePersistedFormat,
43 RuntimeInternal,
44 RuntimeInvariantViolation,
45 RuntimeNotFound,
46 RuntimeUnsupported,
47 SchemaDdlAdmission,
48 StoreCorruption,
49 StoreInvariantViolation,
50 StoreNotFound,
51}
52
53impl DiagnosticCode {
54 #[must_use]
56 pub const fn class(self) -> ErrorClass {
57 match self {
58 Self::StoreCorruption | Self::RuntimeCorruption => ErrorClass::Corruption,
59 Self::RuntimeIncompatiblePersistedFormat => ErrorClass::IncompatiblePersistedFormat,
60 Self::QueryNotFound | Self::StoreNotFound | Self::RuntimeNotFound => {
61 ErrorClass::NotFound
62 }
63 Self::RuntimeConflict => ErrorClass::Conflict,
64 Self::QueryUnsupportedSqlFeature
65 | Self::QueryUnknownAggregateTargetField
66 | Self::QueryUnsupportedProjection
67 | Self::QueryResultShapeMismatch
68 | Self::QuerySqlSurfaceMismatch
69 | Self::QuerySqlWriteBoundary
70 | Self::RuntimeUnsupported => ErrorClass::Unsupported,
71 Self::StoreInvariantViolation | Self::RuntimeInvariantViolation => {
72 ErrorClass::InvariantViolation
73 }
74 Self::RuntimeInternal => ErrorClass::Internal,
75 Self::QueryValidate
76 | Self::QueryIntent
77 | Self::QueryPlan
78 | Self::QueryReadAdmission
79 | Self::QueryAccessRequirement
80 | Self::QueryUnorderedPagination
81 | Self::QueryInvalidContinuationCursor
82 | Self::QueryNotUnique
83 | Self::QueryNumericOverflow
84 | Self::QueryNumericNotRepresentable
85 | Self::SchemaDdlAdmission => ErrorClass::Query,
86 }
87 }
88
89 #[must_use]
91 pub const fn origin(self) -> ErrorOrigin {
92 match self {
93 Self::StoreNotFound | Self::StoreCorruption | Self::StoreInvariantViolation => {
94 ErrorOrigin::Store
95 }
96 Self::RuntimeCorruption
97 | Self::RuntimeIncompatiblePersistedFormat
98 | Self::RuntimeInvariantViolation
99 | Self::RuntimeConflict
100 | Self::RuntimeNotFound
101 | Self::RuntimeUnsupported
102 | Self::RuntimeInternal => ErrorOrigin::Runtime,
103 Self::QueryValidate
104 | Self::QueryIntent
105 | Self::QueryPlan
106 | Self::QueryReadAdmission
107 | Self::QueryAccessRequirement
108 | Self::QueryUnorderedPagination
109 | Self::QueryInvalidContinuationCursor
110 | Self::QueryNotFound
111 | Self::QueryNotUnique
112 | Self::QueryNumericOverflow
113 | Self::QueryNumericNotRepresentable
114 | Self::QueryUnknownAggregateTargetField
115 | Self::QueryUnsupportedProjection
116 | Self::QueryResultShapeMismatch
117 | Self::QueryUnsupportedSqlFeature
118 | Self::QuerySqlSurfaceMismatch
119 | Self::QuerySqlWriteBoundary
120 | Self::SchemaDdlAdmission => ErrorOrigin::Query,
121 }
122 }
123
124 #[must_use]
126 pub const fn error_code(self) -> ErrorCode {
127 match self {
128 Self::QueryValidate => ErrorCode::QUERY_VALIDATE,
129 Self::QueryIntent => ErrorCode::QUERY_INTENT,
130 Self::QueryPlan => ErrorCode::QUERY_PLAN,
131 Self::QueryReadAdmission => ErrorCode::QUERY_READ_ADMISSION,
132 Self::QueryAccessRequirement => ErrorCode::QUERY_ACCESS_REQUIREMENT,
133 Self::QueryUnorderedPagination => ErrorCode::QUERY_UNORDERED_PAGINATION,
134 Self::QueryInvalidContinuationCursor => ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
135 Self::QueryNotFound => ErrorCode::QUERY_NOT_FOUND,
136 Self::QueryNotUnique => ErrorCode::QUERY_NOT_UNIQUE,
137 Self::QueryNumericOverflow => ErrorCode::QUERY_NUMERIC_OVERFLOW,
138 Self::QueryNumericNotRepresentable => ErrorCode::QUERY_NUMERIC_NOT_REPRESENTABLE,
139 Self::QueryUnknownAggregateTargetField => {
140 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD
141 }
142 Self::QueryUnsupportedProjection => ErrorCode::QUERY_UNSUPPORTED_PROJECTION,
143 Self::QueryResultShapeMismatch => ErrorCode::QUERY_RESULT_SHAPE_MISMATCH,
144 Self::QueryUnsupportedSqlFeature => ErrorCode::QUERY_UNSUPPORTED_SQL_FEATURE,
145 Self::QuerySqlSurfaceMismatch => ErrorCode::QUERY_SQL_SURFACE_MISMATCH,
146 Self::QuerySqlWriteBoundary => ErrorCode::QUERY_SQL_WRITE_BOUNDARY,
147 Self::SchemaDdlAdmission => ErrorCode::SCHEMA_DDL_ADMISSION,
148 Self::StoreNotFound => ErrorCode::STORE_NOT_FOUND,
149 Self::StoreCorruption => ErrorCode::STORE_CORRUPTION,
150 Self::StoreInvariantViolation => ErrorCode::STORE_INVARIANT_VIOLATION,
151 Self::RuntimeCorruption => ErrorCode::RUNTIME_CORRUPTION,
152 Self::RuntimeIncompatiblePersistedFormat => {
153 ErrorCode::RUNTIME_INCOMPATIBLE_PERSISTED_FORMAT
154 }
155 Self::RuntimeInvariantViolation => ErrorCode::RUNTIME_INVARIANT_VIOLATION,
156 Self::RuntimeConflict => ErrorCode::RUNTIME_CONFLICT,
157 Self::RuntimeNotFound => ErrorCode::RUNTIME_NOT_FOUND,
158 Self::RuntimeUnsupported => ErrorCode::RUNTIME_UNSUPPORTED,
159 Self::RuntimeInternal => ErrorCode::RUNTIME_INTERNAL,
160 }
161 }
162}
163
164impl fmt::Debug for DiagnosticCode {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 fmt_compact_code(f, self.error_code().raw())
167 }
168}
169
170#[derive(Clone, Copy, Eq, Hash, PartialEq)]
182pub struct ErrorCode(u16);
183
184mod registry;
185
186impl ErrorCode {
187 #[must_use]
189 pub const fn from_raw(raw: u16) -> Self {
190 Self(raw)
191 }
192
193 #[must_use]
195 pub const fn raw(self) -> u16 {
196 self.0
197 }
198}
199
200impl fmt::Debug for ErrorCode {
201 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202 fmt_compact_code(f, self.raw())
203 }
204}
205
206#[remain::sorted]
213#[derive(Clone, Copy, Eq, Hash, PartialEq)]
214pub enum ErrorClass {
215 Conflict,
216 Corruption,
217 IncompatiblePersistedFormat,
218 Internal,
219 InvariantViolation,
220 NotFound,
221 Query,
222 Unsupported,
223}
224
225impl ErrorClass {
226 #[must_use]
228 pub const fn wire_code(self) -> u8 {
229 match self {
230 Self::Query => 1,
231 Self::Corruption => 2,
232 Self::IncompatiblePersistedFormat => 3,
233 Self::NotFound => 4,
234 Self::Internal => 5,
235 Self::Conflict => 6,
236 Self::Unsupported => 7,
237 Self::InvariantViolation => 8,
238 }
239 }
240
241 #[must_use]
243 pub const fn from_wire_code(code: u8) -> Option<Self> {
244 match code {
245 1 => Some(Self::Query),
246 2 => Some(Self::Corruption),
247 3 => Some(Self::IncompatiblePersistedFormat),
248 4 => Some(Self::NotFound),
249 5 => Some(Self::Internal),
250 6 => Some(Self::Conflict),
251 7 => Some(Self::Unsupported),
252 8 => Some(Self::InvariantViolation),
253 _ => None,
254 }
255 }
256}
257
258impl fmt::Debug for ErrorClass {
259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260 fmt_compact_code(f, u16::from(self.wire_code()))
261 }
262}
263
264#[remain::sorted]
271#[derive(Clone, Copy, Eq, Hash, PartialEq)]
272pub enum ErrorOrigin {
273 Cursor,
274 Executor,
275 Identity,
276 Index,
277 Interface,
278 Planner,
279 Query,
280 Recovery,
281 Response,
282 Runtime,
283 Serialize,
284 Store,
285}
286
287impl ErrorOrigin {
288 #[must_use]
290 pub const fn wire_code(self) -> u8 {
291 match self {
292 Self::Cursor => 1,
293 Self::Executor => 2,
294 Self::Identity => 3,
295 Self::Index => 4,
296 Self::Interface => 5,
297 Self::Planner => 6,
298 Self::Query => 7,
299 Self::Recovery => 8,
300 Self::Response => 9,
301 Self::Runtime => 10,
302 Self::Serialize => 11,
303 Self::Store => 12,
304 }
305 }
306
307 #[must_use]
309 pub const fn from_known_wire_code(code: u8) -> Option<Self> {
310 match code {
311 1 => Some(Self::Cursor),
312 2 => Some(Self::Executor),
313 3 => Some(Self::Identity),
314 4 => Some(Self::Index),
315 5 => Some(Self::Interface),
316 6 => Some(Self::Planner),
317 7 => Some(Self::Query),
318 8 => Some(Self::Recovery),
319 9 => Some(Self::Response),
320 10 => Some(Self::Runtime),
321 11 => Some(Self::Serialize),
322 12 => Some(Self::Store),
323 _ => None,
324 }
325 }
326
327 #[must_use]
332 pub const fn from_wire_code(code: u8) -> Self {
333 match Self::from_known_wire_code(code) {
334 Some(origin) => origin,
335 None => Self::Runtime,
336 }
337 }
338}
339
340impl fmt::Debug for ErrorOrigin {
341 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342 fmt_compact_code(f, u16::from(self.wire_code()))
343 }
344}
345
346#[repr(u16)]
353#[derive(Clone, Copy, Eq, Hash, PartialEq)]
354pub enum QueryErrorKind {
355 Validate,
356 Intent,
357 Plan,
358 AccessRequirement,
359 UnorderedPagination,
360 InvalidContinuationCursor,
361 NotFound,
362 NotUnique,
363}
364
365impl fmt::Debug for QueryErrorKind {
366 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367 fmt_compact_code(f, *self as u16)
368 }
369}
370
371#[repr(u16)]
379#[derive(Clone, Copy, Eq, Hash, PartialEq)]
380pub enum QueryProjectionCode {
381 NumericLiteralRequired,
382 NumericScaleArguments,
383 NestedFieldPathPreview,
384 CaseConditionBooleanRequired,
385 NumericInputRequired,
386 TextOrBlobInputRequired,
387 TextInputRequired,
388 TextOrNullArgumentRequired,
389 IntegerOrNullArgumentRequired,
390 UnaryOperandIncompatible,
391 BinaryOperandsIncompatible,
392}
393
394impl fmt::Debug for QueryProjectionCode {
395 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396 fmt_compact_code(f, *self as u16)
397 }
398}
399
400#[repr(u16)]
408#[derive(Clone, Copy, Eq, Hash, PartialEq)]
409pub enum QueryReadAdmissionCode {
410 PublicQueryRequiresLimit,
411 PublicQueryRequiresIndex,
412 UnboundedFullScanRejected,
413 SortRequiresMaterialization,
414 GroupedQueryRequiresLimits,
415 GroupedQueryExceedsBudget,
416 DiagnosticLaneDoesNotExecute,
417 ReturnedRowBoundExceedsPolicy,
418 PrimaryKeyInputExceedsPolicy,
419}
420
421impl fmt::Debug for QueryReadAdmissionCode {
422 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
423 fmt_compact_code(f, *self as u16)
424 }
425}
426
427#[repr(u16)]
435#[derive(Clone, Copy, Eq, Hash, PartialEq)]
436pub enum QueryResultShapeCode {
437 ExpectedRows,
438 ExpectedGroupedRows,
439}
440
441impl fmt::Debug for QueryResultShapeCode {
442 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443 fmt_compact_code(f, *self as u16)
444 }
445}
446
447#[repr(u16)]
454#[derive(Clone, Copy, Eq, Hash, PartialEq)]
455pub enum RuntimeErrorKind {
456 Corruption,
457 IncompatiblePersistedFormat,
458 InvariantViolation,
459 Conflict,
460 NotFound,
461 Unsupported,
462 Internal,
463}
464
465impl fmt::Debug for RuntimeErrorKind {
466 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
467 fmt_compact_code(f, *self as u16)
468 }
469}
470
471#[repr(u16)]
479#[derive(Clone, Copy, Eq, Hash, PartialEq)]
480pub enum RuntimeBoundaryCode {
481 SqlSurfaceControllerRequired,
482 SchemaSurfaceControllerRequired,
483 SqlQueryNoConfiguredEntities,
484 SqlQueryEntityNotConfigured,
485 SqlDdlTargetRequired,
486 SqlDdlEntityNotConfigured,
487 QueryResponseRowsRequired,
488 QueryResponseGroupedRowsRequired,
489 RowProjectionFieldNotConfigured,
490 SqlIntrospectionDisabled,
491 MutationRequiredFieldMissing,
493 PersistedRowLayoutOutsideAcceptedWindow,
495 PersistedRowSlotCountMismatch,
497 GeneratedFieldAfterDdlField,
499 JournalMutationRevisionExhausted,
501 ConstraintViolation,
503 AcceptedRowConstraintProgramCorrupt,
505 ConstraintActivationWriteBlocked,
507 GeneratedConstraintActivationStale,
509}
510
511impl fmt::Debug for RuntimeBoundaryCode {
512 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
513 fmt_compact_code(f, *self as u16)
514 }
515}
516
517#[repr(u16)]
525#[derive(Clone, Copy, Eq, Hash, PartialEq)]
526pub enum SqlFeatureCode {
527 AggregateFilterClause,
528 AlterStatementBeyondAlterTable,
529 AlterTableAddColumnDuplicateDefault,
530 AlterTableAddColumnModifiers,
531 AlterTableAddStatementBeyondAddColumn,
532 AlterTableAlterColumnDropUnsupportedAction,
533 AlterTableAlterColumnModifiers,
534 AlterTableAlterColumnSetUnsupportedAction,
535 AlterTableAlterColumnUnsupportedAction,
536 AlterTableAlterStatementBeyondAlterColumn,
537 AlterTableDropColumnIfExistsSyntax,
538 AlterTableDropColumnModifiers,
539 AlterTableDropStatementBeyondDropColumn,
540 AlterTableRenameColumnMissingTo,
541 AlterTableRenameColumnModifiers,
542 AlterTableRenameStatementBeyondRenameColumn,
543 AlterTableUnsupportedOperation,
544 ColumnAlias,
545 CreateIndexIfNotExistsSyntax,
546 CreateIndexKeyOrderingModifiers,
547 CreateIndexModifiers,
548 CreateStatementBeyondCreateIndex,
549 DescribeModifier,
550 DdlSchemaVersionDuplicateExpectedClause,
551 DdlSchemaVersionDuplicateSetClause,
552 DropIndexModifiers,
553 DropIndexIfExistsSyntax,
554 DropStatementBeyondDropIndex,
555 ExpressionIndexUnsupportedFunction,
556 Having,
557 Insert,
558 Join,
559 LikePatternBeyondTrailingPrefix,
560 LowerFieldPredicateUnsupported,
561 MultiStatementSql,
562 NestedAggregateInput,
563 NestedProjectionFunctionInArithmetic,
564 OrderByUnsupportedForm,
565 Other,
566 PredicateStartsWithFirstArgument,
567 QuotedIdentifiers,
568 ReturningUnsupportedShape,
569 ScalarFunctionExpressionPosition,
570 ScaleTakingNumericFunctionExpressionPosition,
571 SearchedCaseGroupedOrderBy,
572 ShowColumnsModifiers,
573 ShowEntitiesModifiers,
574 ShowIndexesModifiers,
575 ShowMemoryModifiers,
576 ShowStoresModifiers,
577 ShowUnsupportedCommand,
578 SimpleCaseExpression,
579 StandaloneLiteralProjectionItem,
580 SupportedGroupedOrderByExpressionFamily,
581 SupportedOrderByExpressionFamily,
582 UnionIntersectExcept,
583 UnsupportedFunctionNamespace,
584 Update,
585 UpperFieldPredicateUnsupported,
586 WindowFunction,
587 With,
588 NumericScaleFunctionArguments,
589 OrderByFieldNotOrderable,
590 ShowConstraintsModifiers,
591 AlterTableAddConstraintBeyondCheck,
592 AlterTableAddConstraintModifiers,
593 AlterTableDropConstraintIfExistsSyntax,
594 AlterTableDropConstraintModifiers,
595 AlterTableValidateBeyondConstraint,
596 AlterTableValidateConstraintModifiers,
597}
598
599impl fmt::Debug for SqlFeatureCode {
600 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
601 fmt_compact_code(f, *self as u16)
602 }
603}
604
605#[repr(u16)]
614#[derive(Clone, Copy, Eq, Hash, PartialEq)]
615pub enum SqlLoweringCode {
616 EntityMismatch,
617 SelectProjectionShape,
618 SelectDistinct,
619 DistinctOrderByProjection,
620 GlobalAggregateProjection,
621 GlobalAggregateGroupBy,
622 SelectGroupByShape,
623 GroupedProjectionExplicitListRequired,
624 GroupedProjectionAggregateRequired,
625 GroupedProjectionNonGroupField,
626 GroupedProjectionScalarAfterAggregate,
627 HavingRequiresGroupBy,
628 SelectHavingShape,
629 AggregateInputExpressions,
630 WhereExpressionShape,
631 ParameterPlacement,
632 SqlDdlExecutionUnsupported,
633}
634
635impl fmt::Debug for SqlLoweringCode {
636 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
637 fmt_compact_code(f, *self as u16)
638 }
639}
640
641#[repr(u16)]
649#[derive(Clone, Copy, Eq, Hash, PartialEq)]
650pub enum SqlSurfaceMismatchCode {
651 QueryRejectsInsert,
652 QueryRejectsUpdate,
653 QueryRejectsDelete,
654 MutationRejectsSelect,
655 MutationRejectsExplain,
656 MutationRejectsDescribe,
657 MutationRejectsShowIndexes,
658 MutationRejectsShowColumns,
659 MutationRejectsShowEntities,
660 MutationRejectsShowStores,
661 MutationRejectsShowMemory,
662 MutationRequiresExplicitUpdateIntent,
663 MutationRejectsShowConstraints,
664}
665
666impl fmt::Debug for SqlSurfaceMismatchCode {
667 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
668 fmt_compact_code(f, *self as u16)
669 }
670}
671
672#[repr(u16)]
680#[derive(Clone, Copy, Eq, Hash, PartialEq)]
681pub enum SqlWriteBoundaryCode {
682 PrimaryKeyLiteralShape,
683 PrimaryKeyLiteralIncompatible,
684 MissingPrimaryKey,
685 MissingRequiredFields,
686 ExplicitManagedField,
687 ExplicitGeneratedField,
688 InsertSelectRequiresScalar,
689 InsertSelectAggregateProjection,
690 InsertSelectWidthMismatch,
691 UpdatePrimaryKeyMutation,
692 InvalidFieldLiteral,
693 UnknownReturningField,
694 DuplicateReturningField,
695 UpdateMissingWherePredicate,
696 WriteOrderByUnsupportedShape,
697 ReturningResponseTooLarge,
698 ReturningRowsTooMany,
699 StagedRowsTooMany,
700 InsertDefaultRequiredField,
701 UpdateDefaultRequiredField,
702 UpdateDefaultDatabaseOwnedField,
703 ExactUpdateAssertionRequired,
704 ExactUpdateAssertionTooHigh,
705 ExactUpdateAffectedRowsExceeded,
706 ExactUpdateWindowUnsupported,
707 ExactUpdateScanBudgetExceeded,
708 ResumableUpdateWindowUnsupported,
709 ResumableUpdateReturningUnsupported,
710 ResumableUpdateRequiresJournaledStore,
711 ResumableUpdateAssignedFieldHasGlobalConstraint,
712 ResumableUpdateScopeDependsOnAssignedField,
713 ResumableUpdateScopeDependencyUnknown,
714 ResumableUpdateContinuationMalformed,
715 ResumableUpdateContinuationTargetMismatch,
716 ResumableUpdateContinuationSchemaMismatch,
717 ResumableUpdateContinuationScopeMismatch,
718 ResumableUpdateContinuationPatchMismatch,
719 ResumableUpdateContinuationBatchPolicyMismatch,
720 ResumableUpdateSingleRowResourceExceeded,
721 ResumableUpdateApplicationCallbacksUnsupported,
722 ResumableUpdateManagedFieldHasGlobalConstraint,
723 ResumableUpdateContinuationOperationMismatch,
724}
725
726impl fmt::Debug for SqlWriteBoundaryCode {
727 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
728 fmt_compact_code(f, *self as u16)
729 }
730}
731
732#[repr(u16)]
740#[derive(Clone, Copy, Eq, Hash, PartialEq)]
741pub enum SchemaDdlAdmissionCode {
742 MissingExpectedSchemaVersion,
743 MissingNextSchemaVersion,
744 StaleExpectedSchemaVersion,
745 InvalidExpectedSchemaVersion,
746 InvalidNextSchemaVersion,
747 AcceptedSchemaChangeWithoutVersionBump,
748 EmptyVersionBump,
749 VersionGap,
750 VersionRollback,
751 FingerprintMethodMismatch,
752 UnsupportedTransitionClass,
753 PhysicalRunnerMissing,
754 ValidationFailed,
755 PublicationRaceLost,
756 InvalidAddColumnDefault,
757 InvalidAlterColumnDefault,
758 GeneratedIndexDropRejected,
759 SchemaRewriteRequiresMigration,
760 SchemaTransitionBudgetExceeded,
761 GeneratedFieldDefaultChangeRejected,
762 GeneratedFieldNullabilityChangeRejected,
763 RowLayoutVersionExhausted,
764}
765
766impl fmt::Debug for SchemaDdlAdmissionCode {
767 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
768 fmt_compact_code(f, *self as u16)
769 }
770}
771
772#[remain::sorted]
779#[derive(Clone, Copy, Eq, PartialEq)]
780pub enum DiagnosticDetail {
781 QueryKind { kind: QueryErrorKind },
782 QueryProjection { reason: QueryProjectionCode },
783 QueryReadAdmission { reason: QueryReadAdmissionCode },
784 QueryResultShape { reason: QueryResultShapeCode },
785 RuntimeBoundary { boundary: RuntimeBoundaryCode },
786 RuntimeKind { kind: RuntimeErrorKind },
787 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
788 SqlLowering { reason: SqlLoweringCode },
789 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
790 SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
791 UnsupportedSqlFeature { feature: SqlFeatureCode },
792}
793
794impl fmt::Debug for DiagnosticDetail {
795 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
796 fmt_compact_code(
797 f,
798 ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
799 )
800 }
801}
802
803#[derive(Clone, Eq, PartialEq)]
810pub struct Diagnostic {
811 code: DiagnosticCode,
812 origin: ErrorOrigin,
813 detail: Option<DiagnosticDetail>,
814}
815
816impl Diagnostic {
817 #[must_use]
819 pub const fn new(
820 code: DiagnosticCode,
821 origin: ErrorOrigin,
822 detail: Option<DiagnosticDetail>,
823 ) -> Self {
824 Self {
825 code,
826 origin,
827 detail,
828 }
829 }
830
831 #[must_use]
833 pub const fn from_code(code: DiagnosticCode) -> Self {
834 Self::new(code, code.origin(), None)
835 }
836
837 #[must_use]
839 pub const fn code(&self) -> DiagnosticCode {
840 self.code
841 }
842
843 #[must_use]
845 pub const fn class(&self) -> ErrorClass {
846 self.code.class()
847 }
848
849 #[must_use]
851 pub const fn origin(&self) -> ErrorOrigin {
852 self.origin
853 }
854
855 #[must_use]
857 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
858 self.detail.as_ref()
859 }
860
861 #[must_use]
863 pub const fn error_code(&self) -> ErrorCode {
864 ErrorCode::from_parts(self.code, self.detail)
865 }
866}
867
868impl fmt::Debug for Diagnostic {
869 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
870 write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
871 }
872}
873
874fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
875 write!(f, "{raw}")
876}
877
878#[cfg(test)]
879mod tests {
880 use super::{
881 Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
882 QueryProjectionCode, QueryReadAdmissionCode, SqlFeatureCode, SqlLoweringCode,
883 SqlWriteBoundaryCode,
884 registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
885 };
886
887 #[test]
888 fn diagnostic_from_code_uses_default_origin() {
889 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
890
891 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
892 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
893 }
894
895 #[test]
896 fn diagnostic_code_reports_broad_class() {
897 assert_eq!(
898 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
899 ErrorClass::Unsupported
900 );
901 assert_eq!(
902 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
903 ErrorClass::Unsupported
904 );
905 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
906 assert_eq!(
907 DiagnosticCode::StoreCorruption.class(),
908 ErrorClass::Corruption
909 );
910 }
911
912 #[test]
913 fn class_and_origin_wire_codes_round_trip() {
914 for (class, raw) in [
915 (ErrorClass::Query, 1),
916 (ErrorClass::Corruption, 2),
917 (ErrorClass::IncompatiblePersistedFormat, 3),
918 (ErrorClass::NotFound, 4),
919 (ErrorClass::Internal, 5),
920 (ErrorClass::Conflict, 6),
921 (ErrorClass::Unsupported, 7),
922 (ErrorClass::InvariantViolation, 8),
923 ] {
924 assert_eq!(class.wire_code(), raw);
925 assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
926 assert_eq!(format!("{class:?}"), raw.to_string());
927 }
928
929 for (origin, raw) in [
930 (ErrorOrigin::Cursor, 1),
931 (ErrorOrigin::Executor, 2),
932 (ErrorOrigin::Identity, 3),
933 (ErrorOrigin::Index, 4),
934 (ErrorOrigin::Interface, 5),
935 (ErrorOrigin::Planner, 6),
936 (ErrorOrigin::Query, 7),
937 (ErrorOrigin::Recovery, 8),
938 (ErrorOrigin::Response, 9),
939 (ErrorOrigin::Runtime, 10),
940 (ErrorOrigin::Serialize, 11),
941 (ErrorOrigin::Store, 12),
942 ] {
943 assert_eq!(origin.wire_code(), raw);
944 assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
945 assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
946 assert_eq!(format!("{origin:?}"), raw.to_string());
947 }
948
949 assert_eq!(ErrorClass::from_wire_code(0), None);
950 assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
951 assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
952 }
953
954 #[test]
955 fn public_error_codes_are_sequential() {
956 let first = ORDERED_ERROR_CODES
957 .first()
958 .expect("public error-code registry is non-empty")
959 .raw();
960
961 assert_eq!(first, 1);
962
963 for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
964 let expected = first + u16::try_from(index).expect("test error-code index fits u16");
965 assert_eq!(code.raw(), expected);
966 assert_eq!(ErrorCode::known(code.raw()), Some(*code));
967 assert!(code.is_known());
968 }
969
970 let last = ORDERED_ERROR_CODES
971 .last()
972 .expect("public error-code registry is non-empty")
973 .raw();
974
975 assert_eq!(last, 233);
976 }
977
978 #[test]
979 fn all_public_error_codes_round_trip_through_diagnostic_parts() {
980 let first = ORDERED_ERROR_CODES
981 .first()
982 .expect("public error-code registry is non-empty")
983 .raw();
984 let last = ORDERED_ERROR_CODES
985 .last()
986 .expect("public error-code registry is non-empty")
987 .raw();
988
989 for raw in first..=last {
990 let code = ErrorCode::from_raw(raw);
991 let diagnostic_code = code.diagnostic_code();
992 let diagnostic_detail = code.diagnostic_detail();
993 let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
994
995 assert_eq!(rebuilt.raw(), raw);
996
997 let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
998
999 assert_eq!(diagnostic.code(), diagnostic_code);
1000 assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
1001 assert_eq!(diagnostic.error_code().raw(), raw);
1002 }
1003 }
1004
1005 #[test]
1006 fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
1007 for raw in [0, 234, u16::MAX] {
1008 let code = ErrorCode::from_raw(raw);
1009
1010 assert_eq!(ErrorCode::known(raw), None);
1011 assert!(!code.is_known());
1012 assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
1013 assert_eq!(code.diagnostic_detail(), None);
1014 assert_eq!(code.class(), ErrorClass::Internal);
1015
1016 let diagnostic = code.diagnostic(ErrorOrigin::Query);
1017
1018 assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
1019 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
1020 assert_eq!(diagnostic.detail(), None);
1021 assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
1022 }
1023 }
1024
1025 #[test]
1026 fn from_parts_requires_detail_to_match_broad_code() {
1027 let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
1028 feature: SqlFeatureCode::Join,
1029 });
1030
1031 assert_eq!(
1032 ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
1033 ErrorCode::SQL_FEATURE_JOIN
1034 );
1035 assert_eq!(
1036 ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
1037 ErrorCode::QUERY_PLAN
1038 );
1039 }
1040
1041 #[test]
1042 fn detail_bearing_registry_entries_round_trip_directly() {
1043 assert!(!DETAIL_ERROR_CODES.is_empty());
1044
1045 for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
1046 assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
1047 assert_eq!(code.diagnostic_code(), diagnostic_code);
1048 assert_eq!(code.diagnostic_detail(), Some(detail));
1049 assert_eq!(detail.diagnostic_code(), diagnostic_code);
1050 }
1051 }
1052
1053 #[test]
1054 fn diagnostic_detail_reports_generated_broad_code() {
1055 let detail = DiagnosticDetail::UnsupportedSqlFeature {
1056 feature: SqlFeatureCode::Join,
1057 };
1058
1059 assert_eq!(
1060 detail.diagnostic_code(),
1061 DiagnosticCode::QueryUnsupportedSqlFeature
1062 );
1063 assert_eq!(format!("{detail:?}"), "65");
1064 }
1065
1066 #[test]
1067 fn public_error_codes_reconstruct_shifted_details() {
1068 assert_eq!(
1069 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
1070 DiagnosticCode::QueryUnknownAggregateTargetField
1071 );
1072 assert_eq!(
1073 ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
1074 Some(DiagnosticDetail::UnsupportedSqlFeature {
1075 feature: SqlFeatureCode::Join,
1076 })
1077 );
1078 assert_eq!(
1079 ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1080 Some(DiagnosticDetail::QueryProjection {
1081 reason: QueryProjectionCode::NumericLiteralRequired,
1082 })
1083 );
1084 assert_eq!(
1085 ErrorCode::QUERY_READ_PUBLIC_REQUIRES_LIMIT.diagnostic_detail(),
1086 Some(DiagnosticDetail::QueryReadAdmission {
1087 reason: QueryReadAdmissionCode::PublicQueryRequiresLimit,
1088 })
1089 );
1090 assert_eq!(
1091 ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1092 Some(DiagnosticDetail::SqlLowering {
1093 reason: SqlLoweringCode::DistinctOrderByProjection,
1094 })
1095 );
1096 assert_eq!(
1097 ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1098 Some(DiagnosticDetail::SqlWriteBoundary {
1099 boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1100 })
1101 );
1102 assert_eq!(
1103 ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1104 Some(DiagnosticDetail::SqlWriteBoundary {
1105 boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1106 })
1107 );
1108 }
1109}