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 ScanBoundUnavailable,
414 ScanBoundExceedsPolicy,
415 EstimatedOnlyBoundRejected,
416 SortRequiresMaterialization,
417 MaterializationExceedsBudget,
418 ProjectionResponseMayExceedLimit,
419 GroupedQueryRequiresLimits,
420 GroupedQueryExceedsBudget,
421 DiagnosticLaneDoesNotExecute,
422 IntrospectionDisabledForLane,
423 UnsupportedStatementForQueryLane,
424 PublicQueryOffsetRejected,
425 ReturnedRowBoundExceedsPolicy,
426 PrimaryKeyInputExceedsPolicy,
427}
428
429impl fmt::Debug for QueryReadAdmissionCode {
430 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
431 fmt_compact_code(f, *self as u16)
432 }
433}
434
435#[repr(u16)]
443#[derive(Clone, Copy, Eq, Hash, PartialEq)]
444pub enum QueryResultShapeCode {
445 ExpectedRows,
446 ExpectedGroupedRows,
447}
448
449impl fmt::Debug for QueryResultShapeCode {
450 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451 fmt_compact_code(f, *self as u16)
452 }
453}
454
455#[repr(u16)]
462#[derive(Clone, Copy, Eq, Hash, PartialEq)]
463pub enum RuntimeErrorKind {
464 Corruption,
465 IncompatiblePersistedFormat,
466 InvariantViolation,
467 Conflict,
468 NotFound,
469 Unsupported,
470 Internal,
471}
472
473impl fmt::Debug for RuntimeErrorKind {
474 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
475 fmt_compact_code(f, *self as u16)
476 }
477}
478
479#[repr(u16)]
487#[derive(Clone, Copy, Eq, Hash, PartialEq)]
488pub enum RuntimeBoundaryCode {
489 SqlSurfaceControllerRequired,
490 SchemaSurfaceControllerRequired,
491 SqlQueryNoConfiguredEntities,
492 SqlQueryEntityNotConfigured,
493 SqlDdlTargetRequired,
494 SqlDdlEntityNotConfigured,
495 QueryResponseRowsRequired,
496 QueryResponseGroupedRowsRequired,
497 MutationResultEntityRequired,
498 MutationResultEntitiesRequired,
499 MutationResultIdRequired,
500 MutationResultIdsRequired,
501 RowProjectionFieldNotConfigured,
502 SqlIntrospectionDisabled,
503}
504
505impl fmt::Debug for RuntimeBoundaryCode {
506 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
507 fmt_compact_code(f, *self as u16)
508 }
509}
510
511#[repr(u16)]
519#[derive(Clone, Copy, Eq, Hash, PartialEq)]
520pub enum SqlFeatureCode {
521 AggregateFilterClause,
522 AlterStatementBeyondAlterTable,
523 AlterTableAddColumnDuplicateDefault,
524 AlterTableAddColumnModifiers,
525 AlterTableAddStatementBeyondAddColumn,
526 AlterTableAlterColumnDropUnsupportedAction,
527 AlterTableAlterColumnModifiers,
528 AlterTableAlterColumnSetUnsupportedAction,
529 AlterTableAlterColumnUnsupportedAction,
530 AlterTableAlterStatementBeyondAlterColumn,
531 AlterTableDropColumnIfExistsSyntax,
532 AlterTableDropColumnModifiers,
533 AlterTableDropStatementBeyondDropColumn,
534 AlterTableRenameColumnMissingTo,
535 AlterTableRenameColumnModifiers,
536 AlterTableRenameStatementBeyondRenameColumn,
537 AlterTableUnsupportedOperation,
538 ColumnAlias,
539 CreateIndexIfNotExistsSyntax,
540 CreateIndexKeyOrderingModifiers,
541 CreateIndexModifiers,
542 CreateStatementBeyondCreateIndex,
543 DescribeModifier,
544 DdlSchemaVersionDuplicateExpectedClause,
545 DdlSchemaVersionDuplicateSetClause,
546 DropIndexModifiers,
547 DropIndexIfExistsSyntax,
548 DropStatementBeyondDropIndex,
549 ExpressionIndexUnsupportedFunction,
550 Having,
551 Insert,
552 Join,
553 LikePatternBeyondTrailingPrefix,
554 LowerFieldPredicateUnsupported,
555 MultiStatementSql,
556 NestedAggregateInput,
557 NestedProjectionFunctionInArithmetic,
558 OrderByUnsupportedForm,
559 Other,
560 ParameterBinding,
561 ParameterizedSchemaVersion,
562 PredicateStartsWithFirstArgument,
563 QuotedIdentifiers,
564 ReturningUnsupportedShape,
565 ScalarFunctionExpressionPosition,
566 ScaleTakingNumericFunctionExpressionPosition,
567 SearchedCaseGroupedOrderBy,
568 ShowColumnsModifiers,
569 ShowEntitiesModifiers,
570 ShowIndexesModifiers,
571 ShowMemoryModifiers,
572 ShowStoresModifiers,
573 ShowUnsupportedCommand,
574 SimpleCaseExpression,
575 StandaloneLiteralProjectionItem,
576 SupportedGroupedOrderByExpressionFamily,
577 SupportedOrderByExpressionFamily,
578 UnionIntersectExcept,
579 UnsupportedFunctionNamespace,
580 Update,
581 UpperFieldPredicateUnsupported,
582 WindowFunction,
583 With,
584 NumericScaleFunctionArguments,
585 OrderByFieldNotOrderable,
586}
587
588impl fmt::Debug for SqlFeatureCode {
589 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
590 fmt_compact_code(f, *self as u16)
591 }
592}
593
594#[repr(u16)]
603#[derive(Clone, Copy, Eq, Hash, PartialEq)]
604pub enum SqlLoweringCode {
605 EntityMismatch,
606 SelectProjectionShape,
607 SelectDistinct,
608 DistinctOrderByProjection,
609 GlobalAggregateProjection,
610 GlobalAggregateGroupBy,
611 SelectGroupByShape,
612 GroupedProjectionExplicitListRequired,
613 GroupedProjectionAggregateRequired,
614 GroupedProjectionNonGroupField,
615 GroupedProjectionScalarAfterAggregate,
616 HavingRequiresGroupBy,
617 SelectHavingShape,
618 AggregateInputExpressions,
619 WhereExpressionShape,
620 ParameterPlacement,
621 SqlDdlExecutionUnsupported,
622}
623
624impl fmt::Debug for SqlLoweringCode {
625 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
626 fmt_compact_code(f, *self as u16)
627 }
628}
629
630#[repr(u16)]
638#[derive(Clone, Copy, Eq, Hash, PartialEq)]
639pub enum SqlSurfaceMismatchCode {
640 QueryRejectsInsert,
641 QueryRejectsUpdate,
642 QueryRejectsDelete,
643 UpdateRejectsSelect,
644 UpdateRejectsExplain,
645 UpdateRejectsDescribe,
646 UpdateRejectsShowIndexes,
647 UpdateRejectsShowColumns,
648 UpdateRejectsShowEntities,
649 UpdateRejectsShowStores,
650 UpdateRejectsShowMemory,
651}
652
653impl fmt::Debug for SqlSurfaceMismatchCode {
654 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
655 fmt_compact_code(f, *self as u16)
656 }
657}
658
659#[repr(u16)]
667#[derive(Clone, Copy, Eq, Hash, PartialEq)]
668pub enum SqlWriteBoundaryCode {
669 PrimaryKeyLiteralShape,
670 PrimaryKeyLiteralIncompatible,
671 MissingPrimaryKey,
672 MissingRequiredFields,
673 ExplicitManagedField,
674 ExplicitGeneratedField,
675 InsertSelectRequiresScalar,
676 InsertSelectAggregateProjection,
677 InsertSelectWidthMismatch,
678 UpdatePrimaryKeyMutation,
679 InvalidFieldLiteral,
680 UnknownReturningField,
681 DuplicateReturningField,
682 UpdateMissingWherePredicate,
683 WriteOrderByUnsupportedShape,
684 ReturningResponseTooLarge,
685 ReturningRowsTooMany,
686 StagedRowsTooMany,
687}
688
689impl fmt::Debug for SqlWriteBoundaryCode {
690 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
691 fmt_compact_code(f, *self as u16)
692 }
693}
694
695#[repr(u16)]
703#[derive(Clone, Copy, Eq, Hash, PartialEq)]
704pub enum SchemaDdlAdmissionCode {
705 MissingExpectedSchemaVersion,
706 MissingNextSchemaVersion,
707 StaleExpectedSchemaVersion,
708 InvalidExpectedSchemaVersion,
709 InvalidNextSchemaVersion,
710 AcceptedSchemaChangeWithoutVersionBump,
711 EmptyVersionBump,
712 VersionGap,
713 VersionRollback,
714 FingerprintMethodMismatch,
715 UnsupportedTransitionClass,
716 PhysicalRunnerMissing,
717 ValidationFailed,
718 PublicationRaceLost,
719 InvalidAddColumnDefault,
720 InvalidAlterColumnDefault,
721 GeneratedIndexDropRejected,
722 RequiredDropDefaultUnsupported,
723 GeneratedFieldDefaultChangeRejected,
724 GeneratedFieldNullabilityChangeRejected,
725 SetNotNullValidationFailed,
726}
727
728impl fmt::Debug for SchemaDdlAdmissionCode {
729 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
730 fmt_compact_code(f, *self as u16)
731 }
732}
733
734#[remain::sorted]
741#[derive(Clone, Copy, Eq, PartialEq)]
742pub enum DiagnosticDetail {
743 QueryKind { kind: QueryErrorKind },
744 QueryProjection { reason: QueryProjectionCode },
745 QueryReadAdmission { reason: QueryReadAdmissionCode },
746 QueryResultShape { reason: QueryResultShapeCode },
747 RuntimeBoundary { boundary: RuntimeBoundaryCode },
748 RuntimeKind { kind: RuntimeErrorKind },
749 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
750 SqlLowering { reason: SqlLoweringCode },
751 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
752 SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
753 UnsupportedSqlFeature { feature: SqlFeatureCode },
754}
755
756impl fmt::Debug for DiagnosticDetail {
757 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
758 fmt_compact_code(
759 f,
760 ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
761 )
762 }
763}
764
765#[derive(Clone, Eq, PartialEq)]
772pub struct Diagnostic {
773 code: DiagnosticCode,
774 origin: ErrorOrigin,
775 detail: Option<DiagnosticDetail>,
776}
777
778impl Diagnostic {
779 #[must_use]
781 pub const fn new(
782 code: DiagnosticCode,
783 origin: ErrorOrigin,
784 detail: Option<DiagnosticDetail>,
785 ) -> Self {
786 Self {
787 code,
788 origin,
789 detail,
790 }
791 }
792
793 #[must_use]
795 pub const fn from_code(code: DiagnosticCode) -> Self {
796 Self::new(code, code.origin(), None)
797 }
798
799 #[must_use]
801 pub const fn code(&self) -> DiagnosticCode {
802 self.code
803 }
804
805 #[must_use]
807 pub const fn class(&self) -> ErrorClass {
808 self.code.class()
809 }
810
811 #[must_use]
813 pub const fn origin(&self) -> ErrorOrigin {
814 self.origin
815 }
816
817 #[must_use]
819 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
820 self.detail.as_ref()
821 }
822
823 #[must_use]
825 pub const fn error_code(&self) -> ErrorCode {
826 ErrorCode::from_parts(self.code, self.detail)
827 }
828}
829
830impl fmt::Debug for Diagnostic {
831 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
832 write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
833 }
834}
835
836fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
837 write!(f, "{raw}")
838}
839
840#[cfg(test)]
841mod tests {
842 use super::{
843 Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
844 QueryProjectionCode, QueryReadAdmissionCode, SqlFeatureCode, SqlLoweringCode,
845 SqlWriteBoundaryCode,
846 registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
847 };
848
849 #[test]
850 fn diagnostic_from_code_uses_default_origin() {
851 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
852
853 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
854 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
855 }
856
857 #[test]
858 fn diagnostic_code_reports_broad_class() {
859 assert_eq!(
860 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
861 ErrorClass::Unsupported
862 );
863 assert_eq!(
864 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
865 ErrorClass::Unsupported
866 );
867 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
868 assert_eq!(
869 DiagnosticCode::StoreCorruption.class(),
870 ErrorClass::Corruption
871 );
872 }
873
874 #[test]
875 fn class_and_origin_wire_codes_round_trip() {
876 for (class, raw) in [
877 (ErrorClass::Query, 1),
878 (ErrorClass::Corruption, 2),
879 (ErrorClass::IncompatiblePersistedFormat, 3),
880 (ErrorClass::NotFound, 4),
881 (ErrorClass::Internal, 5),
882 (ErrorClass::Conflict, 6),
883 (ErrorClass::Unsupported, 7),
884 (ErrorClass::InvariantViolation, 8),
885 ] {
886 assert_eq!(class.wire_code(), raw);
887 assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
888 assert_eq!(format!("{class:?}"), raw.to_string());
889 }
890
891 for (origin, raw) in [
892 (ErrorOrigin::Cursor, 1),
893 (ErrorOrigin::Executor, 2),
894 (ErrorOrigin::Identity, 3),
895 (ErrorOrigin::Index, 4),
896 (ErrorOrigin::Interface, 5),
897 (ErrorOrigin::Planner, 6),
898 (ErrorOrigin::Query, 7),
899 (ErrorOrigin::Recovery, 8),
900 (ErrorOrigin::Response, 9),
901 (ErrorOrigin::Runtime, 10),
902 (ErrorOrigin::Serialize, 11),
903 (ErrorOrigin::Store, 12),
904 ] {
905 assert_eq!(origin.wire_code(), raw);
906 assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
907 assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
908 assert_eq!(format!("{origin:?}"), raw.to_string());
909 }
910
911 assert_eq!(ErrorClass::from_wire_code(0), None);
912 assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
913 assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
914 }
915
916 #[test]
917 fn public_error_codes_are_sequential() {
918 let first = ORDERED_ERROR_CODES
919 .first()
920 .expect("public error-code registry is non-empty")
921 .raw();
922
923 assert_eq!(first, 1);
924
925 for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
926 let expected = first + u16::try_from(index).expect("test error-code index fits u16");
927 assert_eq!(code.raw(), expected);
928 assert_eq!(ErrorCode::known(code.raw()), Some(*code));
929 assert!(code.is_known());
930 }
931
932 let last = ORDERED_ERROR_CODES
933 .last()
934 .expect("public error-code registry is non-empty")
935 .raw();
936
937 assert_eq!(last, 204);
938 }
939
940 #[test]
941 fn all_public_error_codes_round_trip_through_diagnostic_parts() {
942 let first = ORDERED_ERROR_CODES
943 .first()
944 .expect("public error-code registry is non-empty")
945 .raw();
946 let last = ORDERED_ERROR_CODES
947 .last()
948 .expect("public error-code registry is non-empty")
949 .raw();
950
951 for raw in first..=last {
952 let code = ErrorCode::from_raw(raw);
953 let diagnostic_code = code.diagnostic_code();
954 let diagnostic_detail = code.diagnostic_detail();
955 let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
956
957 assert_eq!(rebuilt.raw(), raw);
958
959 let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
960
961 assert_eq!(diagnostic.code(), diagnostic_code);
962 assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
963 assert_eq!(diagnostic.error_code().raw(), raw);
964 }
965 }
966
967 #[test]
968 fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
969 for raw in [0, 205, u16::MAX] {
970 let code = ErrorCode::from_raw(raw);
971
972 assert_eq!(ErrorCode::known(raw), None);
973 assert!(!code.is_known());
974 assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
975 assert_eq!(code.diagnostic_detail(), None);
976 assert_eq!(code.class(), ErrorClass::Internal);
977
978 let diagnostic = code.diagnostic(ErrorOrigin::Query);
979
980 assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
981 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
982 assert_eq!(diagnostic.detail(), None);
983 assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
984 }
985 }
986
987 #[test]
988 fn from_parts_requires_detail_to_match_broad_code() {
989 let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
990 feature: SqlFeatureCode::Join,
991 });
992
993 assert_eq!(
994 ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
995 ErrorCode::SQL_FEATURE_JOIN
996 );
997 assert_eq!(
998 ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
999 ErrorCode::QUERY_PLAN
1000 );
1001 }
1002
1003 #[test]
1004 fn detail_bearing_registry_entries_round_trip_directly() {
1005 assert!(!DETAIL_ERROR_CODES.is_empty());
1006
1007 for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
1008 assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
1009 assert_eq!(code.diagnostic_code(), diagnostic_code);
1010 assert_eq!(code.diagnostic_detail(), Some(detail));
1011 assert_eq!(detail.diagnostic_code(), diagnostic_code);
1012 }
1013 }
1014
1015 #[test]
1016 fn diagnostic_detail_reports_generated_broad_code() {
1017 let detail = DiagnosticDetail::UnsupportedSqlFeature {
1018 feature: SqlFeatureCode::Join,
1019 };
1020
1021 assert_eq!(
1022 detail.diagnostic_code(),
1023 DiagnosticCode::QueryUnsupportedSqlFeature
1024 );
1025 assert_eq!(format!("{detail:?}"), "69");
1026 }
1027
1028 #[test]
1029 fn public_error_codes_reconstruct_shifted_details() {
1030 assert_eq!(
1031 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
1032 DiagnosticCode::QueryUnknownAggregateTargetField
1033 );
1034 assert_eq!(
1035 ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
1036 Some(DiagnosticDetail::UnsupportedSqlFeature {
1037 feature: SqlFeatureCode::Join,
1038 })
1039 );
1040 assert_eq!(
1041 ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1042 Some(DiagnosticDetail::QueryProjection {
1043 reason: QueryProjectionCode::NumericLiteralRequired,
1044 })
1045 );
1046 assert_eq!(
1047 ErrorCode::QUERY_READ_PUBLIC_REQUIRES_LIMIT.diagnostic_detail(),
1048 Some(DiagnosticDetail::QueryReadAdmission {
1049 reason: QueryReadAdmissionCode::PublicQueryRequiresLimit,
1050 })
1051 );
1052 assert_eq!(
1053 ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1054 Some(DiagnosticDetail::SqlLowering {
1055 reason: SqlLoweringCode::DistinctOrderByProjection,
1056 })
1057 );
1058 assert_eq!(
1059 ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1060 Some(DiagnosticDetail::SqlWriteBoundary {
1061 boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1062 })
1063 );
1064 assert_eq!(
1065 ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1066 Some(DiagnosticDetail::SqlWriteBoundary {
1067 boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1068 })
1069 );
1070 }
1071}