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}
502
503impl fmt::Debug for RuntimeBoundaryCode {
504 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
505 fmt_compact_code(f, *self as u16)
506 }
507}
508
509#[repr(u16)]
517#[derive(Clone, Copy, Eq, Hash, PartialEq)]
518pub enum SqlFeatureCode {
519 AggregateFilterClause,
520 AlterStatementBeyondAlterTable,
521 AlterTableAddColumnDuplicateDefault,
522 AlterTableAddColumnModifiers,
523 AlterTableAddStatementBeyondAddColumn,
524 AlterTableAlterColumnDropUnsupportedAction,
525 AlterTableAlterColumnModifiers,
526 AlterTableAlterColumnSetUnsupportedAction,
527 AlterTableAlterColumnUnsupportedAction,
528 AlterTableAlterStatementBeyondAlterColumn,
529 AlterTableDropColumnIfExistsSyntax,
530 AlterTableDropColumnModifiers,
531 AlterTableDropStatementBeyondDropColumn,
532 AlterTableRenameColumnMissingTo,
533 AlterTableRenameColumnModifiers,
534 AlterTableRenameStatementBeyondRenameColumn,
535 AlterTableUnsupportedOperation,
536 ColumnAlias,
537 CreateIndexIfNotExistsSyntax,
538 CreateIndexKeyOrderingModifiers,
539 CreateIndexModifiers,
540 CreateStatementBeyondCreateIndex,
541 DescribeModifier,
542 DdlSchemaVersionDuplicateExpectedClause,
543 DdlSchemaVersionDuplicateSetClause,
544 DropIndexModifiers,
545 DropIndexIfExistsSyntax,
546 DropStatementBeyondDropIndex,
547 ExpressionIndexUnsupportedFunction,
548 Having,
549 Insert,
550 Join,
551 LikePatternBeyondTrailingPrefix,
552 LowerFieldPredicateUnsupported,
553 MultiStatementSql,
554 NestedAggregateInput,
555 NestedProjectionFunctionInArithmetic,
556 OrderByUnsupportedForm,
557 Other,
558 PredicateStartsWithFirstArgument,
559 QuotedIdentifiers,
560 ReturningUnsupportedShape,
561 ScalarFunctionExpressionPosition,
562 ScaleTakingNumericFunctionExpressionPosition,
563 SearchedCaseGroupedOrderBy,
564 ShowColumnsModifiers,
565 ShowEntitiesModifiers,
566 ShowIndexesModifiers,
567 ShowMemoryModifiers,
568 ShowStoresModifiers,
569 ShowUnsupportedCommand,
570 SimpleCaseExpression,
571 StandaloneLiteralProjectionItem,
572 SupportedGroupedOrderByExpressionFamily,
573 SupportedOrderByExpressionFamily,
574 UnionIntersectExcept,
575 UnsupportedFunctionNamespace,
576 Update,
577 UpperFieldPredicateUnsupported,
578 WindowFunction,
579 With,
580 NumericScaleFunctionArguments,
581 OrderByFieldNotOrderable,
582}
583
584impl fmt::Debug for SqlFeatureCode {
585 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
586 fmt_compact_code(f, *self as u16)
587 }
588}
589
590#[repr(u16)]
599#[derive(Clone, Copy, Eq, Hash, PartialEq)]
600pub enum SqlLoweringCode {
601 EntityMismatch,
602 SelectProjectionShape,
603 SelectDistinct,
604 DistinctOrderByProjection,
605 GlobalAggregateProjection,
606 GlobalAggregateGroupBy,
607 SelectGroupByShape,
608 GroupedProjectionExplicitListRequired,
609 GroupedProjectionAggregateRequired,
610 GroupedProjectionNonGroupField,
611 GroupedProjectionScalarAfterAggregate,
612 HavingRequiresGroupBy,
613 SelectHavingShape,
614 AggregateInputExpressions,
615 WhereExpressionShape,
616 ParameterPlacement,
617 SqlDdlExecutionUnsupported,
618}
619
620impl fmt::Debug for SqlLoweringCode {
621 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
622 fmt_compact_code(f, *self as u16)
623 }
624}
625
626#[repr(u16)]
634#[derive(Clone, Copy, Eq, Hash, PartialEq)]
635pub enum SqlSurfaceMismatchCode {
636 QueryRejectsInsert,
637 QueryRejectsUpdate,
638 QueryRejectsDelete,
639 MutationRejectsSelect,
640 MutationRejectsExplain,
641 MutationRejectsDescribe,
642 MutationRejectsShowIndexes,
643 MutationRejectsShowColumns,
644 MutationRejectsShowEntities,
645 MutationRejectsShowStores,
646 MutationRejectsShowMemory,
647 MutationRequiresExplicitUpdateIntent,
648}
649
650impl fmt::Debug for SqlSurfaceMismatchCode {
651 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
652 fmt_compact_code(f, *self as u16)
653 }
654}
655
656#[repr(u16)]
664#[derive(Clone, Copy, Eq, Hash, PartialEq)]
665pub enum SqlWriteBoundaryCode {
666 PrimaryKeyLiteralShape,
667 PrimaryKeyLiteralIncompatible,
668 MissingPrimaryKey,
669 MissingRequiredFields,
670 ExplicitManagedField,
671 ExplicitGeneratedField,
672 InsertSelectRequiresScalar,
673 InsertSelectAggregateProjection,
674 InsertSelectWidthMismatch,
675 UpdatePrimaryKeyMutation,
676 InvalidFieldLiteral,
677 UnknownReturningField,
678 DuplicateReturningField,
679 UpdateMissingWherePredicate,
680 WriteOrderByUnsupportedShape,
681 ReturningResponseTooLarge,
682 ReturningRowsTooMany,
683 StagedRowsTooMany,
684 InsertDefaultRequiredField,
685 UpdateDefaultRequiredField,
686 UpdateDefaultDatabaseOwnedField,
687 ExactUpdateAssertionRequired,
688 ExactUpdateAssertionTooHigh,
689 ExactUpdateAffectedRowsExceeded,
690 ExactUpdateWindowUnsupported,
691 ExactUpdateScanBudgetExceeded,
692 ResumableUpdateWindowUnsupported,
693 ResumableUpdateReturningUnsupported,
694 ResumableUpdateRequiresJournaledStore,
695 ResumableUpdateAssignedFieldHasGlobalConstraint,
696 ResumableUpdateScopeDependsOnAssignedField,
697 ResumableUpdateScopeDependencyUnknown,
698 ResumableUpdateContinuationMalformed,
699 ResumableUpdateContinuationTargetMismatch,
700 ResumableUpdateContinuationSchemaMismatch,
701 ResumableUpdateContinuationScopeMismatch,
702 ResumableUpdateContinuationPatchMismatch,
703 ResumableUpdateContinuationBatchPolicyMismatch,
704 ResumableUpdateSingleRowResourceExceeded,
705 ResumableUpdateApplicationCallbacksUnsupported,
706 ResumableUpdateManagedFieldHasGlobalConstraint,
707 ResumableUpdateContinuationOperationMismatch,
708}
709
710impl fmt::Debug for SqlWriteBoundaryCode {
711 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
712 fmt_compact_code(f, *self as u16)
713 }
714}
715
716#[repr(u16)]
724#[derive(Clone, Copy, Eq, Hash, PartialEq)]
725pub enum SchemaDdlAdmissionCode {
726 MissingExpectedSchemaVersion,
727 MissingNextSchemaVersion,
728 StaleExpectedSchemaVersion,
729 InvalidExpectedSchemaVersion,
730 InvalidNextSchemaVersion,
731 AcceptedSchemaChangeWithoutVersionBump,
732 EmptyVersionBump,
733 VersionGap,
734 VersionRollback,
735 FingerprintMethodMismatch,
736 UnsupportedTransitionClass,
737 PhysicalRunnerMissing,
738 ValidationFailed,
739 PublicationRaceLost,
740 InvalidAddColumnDefault,
741 InvalidAlterColumnDefault,
742 GeneratedIndexDropRejected,
743 SchemaRewriteRequiresMigration,
744 SchemaTransitionBudgetExceeded,
745 GeneratedFieldDefaultChangeRejected,
746 GeneratedFieldNullabilityChangeRejected,
747 SetNotNullValidationFailed,
748 RowLayoutVersionExhausted,
749}
750
751impl fmt::Debug for SchemaDdlAdmissionCode {
752 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
753 fmt_compact_code(f, *self as u16)
754 }
755}
756
757#[remain::sorted]
764#[derive(Clone, Copy, Eq, PartialEq)]
765pub enum DiagnosticDetail {
766 QueryKind { kind: QueryErrorKind },
767 QueryProjection { reason: QueryProjectionCode },
768 QueryReadAdmission { reason: QueryReadAdmissionCode },
769 QueryResultShape { reason: QueryResultShapeCode },
770 RuntimeBoundary { boundary: RuntimeBoundaryCode },
771 RuntimeKind { kind: RuntimeErrorKind },
772 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
773 SqlLowering { reason: SqlLoweringCode },
774 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
775 SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
776 UnsupportedSqlFeature { feature: SqlFeatureCode },
777}
778
779impl fmt::Debug for DiagnosticDetail {
780 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
781 fmt_compact_code(
782 f,
783 ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
784 )
785 }
786}
787
788#[derive(Clone, Eq, PartialEq)]
795pub struct Diagnostic {
796 code: DiagnosticCode,
797 origin: ErrorOrigin,
798 detail: Option<DiagnosticDetail>,
799}
800
801impl Diagnostic {
802 #[must_use]
804 pub const fn new(
805 code: DiagnosticCode,
806 origin: ErrorOrigin,
807 detail: Option<DiagnosticDetail>,
808 ) -> Self {
809 Self {
810 code,
811 origin,
812 detail,
813 }
814 }
815
816 #[must_use]
818 pub const fn from_code(code: DiagnosticCode) -> Self {
819 Self::new(code, code.origin(), None)
820 }
821
822 #[must_use]
824 pub const fn code(&self) -> DiagnosticCode {
825 self.code
826 }
827
828 #[must_use]
830 pub const fn class(&self) -> ErrorClass {
831 self.code.class()
832 }
833
834 #[must_use]
836 pub const fn origin(&self) -> ErrorOrigin {
837 self.origin
838 }
839
840 #[must_use]
842 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
843 self.detail.as_ref()
844 }
845
846 #[must_use]
848 pub const fn error_code(&self) -> ErrorCode {
849 ErrorCode::from_parts(self.code, self.detail)
850 }
851}
852
853impl fmt::Debug for Diagnostic {
854 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
855 write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
856 }
857}
858
859fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
860 write!(f, "{raw}")
861}
862
863#[cfg(test)]
864mod tests {
865 use super::{
866 Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
867 QueryProjectionCode, QueryReadAdmissionCode, SqlFeatureCode, SqlLoweringCode,
868 SqlWriteBoundaryCode,
869 registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
870 };
871
872 #[test]
873 fn diagnostic_from_code_uses_default_origin() {
874 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
875
876 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
877 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
878 }
879
880 #[test]
881 fn diagnostic_code_reports_broad_class() {
882 assert_eq!(
883 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
884 ErrorClass::Unsupported
885 );
886 assert_eq!(
887 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
888 ErrorClass::Unsupported
889 );
890 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
891 assert_eq!(
892 DiagnosticCode::StoreCorruption.class(),
893 ErrorClass::Corruption
894 );
895 }
896
897 #[test]
898 fn class_and_origin_wire_codes_round_trip() {
899 for (class, raw) in [
900 (ErrorClass::Query, 1),
901 (ErrorClass::Corruption, 2),
902 (ErrorClass::IncompatiblePersistedFormat, 3),
903 (ErrorClass::NotFound, 4),
904 (ErrorClass::Internal, 5),
905 (ErrorClass::Conflict, 6),
906 (ErrorClass::Unsupported, 7),
907 (ErrorClass::InvariantViolation, 8),
908 ] {
909 assert_eq!(class.wire_code(), raw);
910 assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
911 assert_eq!(format!("{class:?}"), raw.to_string());
912 }
913
914 for (origin, raw) in [
915 (ErrorOrigin::Cursor, 1),
916 (ErrorOrigin::Executor, 2),
917 (ErrorOrigin::Identity, 3),
918 (ErrorOrigin::Index, 4),
919 (ErrorOrigin::Interface, 5),
920 (ErrorOrigin::Planner, 6),
921 (ErrorOrigin::Query, 7),
922 (ErrorOrigin::Recovery, 8),
923 (ErrorOrigin::Response, 9),
924 (ErrorOrigin::Runtime, 10),
925 (ErrorOrigin::Serialize, 11),
926 (ErrorOrigin::Store, 12),
927 ] {
928 assert_eq!(origin.wire_code(), raw);
929 assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
930 assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
931 assert_eq!(format!("{origin:?}"), raw.to_string());
932 }
933
934 assert_eq!(ErrorClass::from_wire_code(0), None);
935 assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
936 assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
937 }
938
939 #[test]
940 fn public_error_codes_are_sequential() {
941 let first = ORDERED_ERROR_CODES
942 .first()
943 .expect("public error-code registry is non-empty")
944 .raw();
945
946 assert_eq!(first, 1);
947
948 for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
949 let expected = first + u16::try_from(index).expect("test error-code index fits u16");
950 assert_eq!(code.raw(), expected);
951 assert_eq!(ErrorCode::known(code.raw()), Some(*code));
952 assert!(code.is_known());
953 }
954
955 let last = ORDERED_ERROR_CODES
956 .last()
957 .expect("public error-code registry is non-empty")
958 .raw();
959
960 assert_eq!(last, 222);
961 }
962
963 #[test]
964 fn all_public_error_codes_round_trip_through_diagnostic_parts() {
965 let first = ORDERED_ERROR_CODES
966 .first()
967 .expect("public error-code registry is non-empty")
968 .raw();
969 let last = ORDERED_ERROR_CODES
970 .last()
971 .expect("public error-code registry is non-empty")
972 .raw();
973
974 for raw in first..=last {
975 let code = ErrorCode::from_raw(raw);
976 let diagnostic_code = code.diagnostic_code();
977 let diagnostic_detail = code.diagnostic_detail();
978 let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
979
980 assert_eq!(rebuilt.raw(), raw);
981
982 let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
983
984 assert_eq!(diagnostic.code(), diagnostic_code);
985 assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
986 assert_eq!(diagnostic.error_code().raw(), raw);
987 }
988 }
989
990 #[test]
991 fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
992 for raw in [0, 223, u16::MAX] {
993 let code = ErrorCode::from_raw(raw);
994
995 assert_eq!(ErrorCode::known(raw), None);
996 assert!(!code.is_known());
997 assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
998 assert_eq!(code.diagnostic_detail(), None);
999 assert_eq!(code.class(), ErrorClass::Internal);
1000
1001 let diagnostic = code.diagnostic(ErrorOrigin::Query);
1002
1003 assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
1004 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
1005 assert_eq!(diagnostic.detail(), None);
1006 assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
1007 }
1008 }
1009
1010 #[test]
1011 fn from_parts_requires_detail_to_match_broad_code() {
1012 let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
1013 feature: SqlFeatureCode::Join,
1014 });
1015
1016 assert_eq!(
1017 ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
1018 ErrorCode::SQL_FEATURE_JOIN
1019 );
1020 assert_eq!(
1021 ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
1022 ErrorCode::QUERY_PLAN
1023 );
1024 }
1025
1026 #[test]
1027 fn detail_bearing_registry_entries_round_trip_directly() {
1028 assert!(!DETAIL_ERROR_CODES.is_empty());
1029
1030 for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
1031 assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
1032 assert_eq!(code.diagnostic_code(), diagnostic_code);
1033 assert_eq!(code.diagnostic_detail(), Some(detail));
1034 assert_eq!(detail.diagnostic_code(), diagnostic_code);
1035 }
1036 }
1037
1038 #[test]
1039 fn diagnostic_detail_reports_generated_broad_code() {
1040 let detail = DiagnosticDetail::UnsupportedSqlFeature {
1041 feature: SqlFeatureCode::Join,
1042 };
1043
1044 assert_eq!(
1045 detail.diagnostic_code(),
1046 DiagnosticCode::QueryUnsupportedSqlFeature
1047 );
1048 assert_eq!(format!("{detail:?}"), "65");
1049 }
1050
1051 #[test]
1052 fn public_error_codes_reconstruct_shifted_details() {
1053 assert_eq!(
1054 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
1055 DiagnosticCode::QueryUnknownAggregateTargetField
1056 );
1057 assert_eq!(
1058 ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
1059 Some(DiagnosticDetail::UnsupportedSqlFeature {
1060 feature: SqlFeatureCode::Join,
1061 })
1062 );
1063 assert_eq!(
1064 ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1065 Some(DiagnosticDetail::QueryProjection {
1066 reason: QueryProjectionCode::NumericLiteralRequired,
1067 })
1068 );
1069 assert_eq!(
1070 ErrorCode::QUERY_READ_PUBLIC_REQUIRES_LIMIT.diagnostic_detail(),
1071 Some(DiagnosticDetail::QueryReadAdmission {
1072 reason: QueryReadAdmissionCode::PublicQueryRequiresLimit,
1073 })
1074 );
1075 assert_eq!(
1076 ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1077 Some(DiagnosticDetail::SqlLowering {
1078 reason: SqlLoweringCode::DistinctOrderByProjection,
1079 })
1080 );
1081 assert_eq!(
1082 ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1083 Some(DiagnosticDetail::SqlWriteBoundary {
1084 boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1085 })
1086 );
1087 assert_eq!(
1088 ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1089 Some(DiagnosticDetail::SqlWriteBoundary {
1090 boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1091 })
1092 );
1093 }
1094}