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 PredicateStartsWithFirstArgument,
561 QuotedIdentifiers,
562 ReturningUnsupportedShape,
563 ScalarFunctionExpressionPosition,
564 ScaleTakingNumericFunctionExpressionPosition,
565 SearchedCaseGroupedOrderBy,
566 ShowColumnsModifiers,
567 ShowEntitiesModifiers,
568 ShowIndexesModifiers,
569 ShowMemoryModifiers,
570 ShowStoresModifiers,
571 ShowUnsupportedCommand,
572 SimpleCaseExpression,
573 StandaloneLiteralProjectionItem,
574 SupportedGroupedOrderByExpressionFamily,
575 SupportedOrderByExpressionFamily,
576 UnionIntersectExcept,
577 UnsupportedFunctionNamespace,
578 Update,
579 UpperFieldPredicateUnsupported,
580 WindowFunction,
581 With,
582 NumericScaleFunctionArguments,
583 OrderByFieldNotOrderable,
584}
585
586impl fmt::Debug for SqlFeatureCode {
587 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
588 fmt_compact_code(f, *self as u16)
589 }
590}
591
592#[repr(u16)]
601#[derive(Clone, Copy, Eq, Hash, PartialEq)]
602pub enum SqlLoweringCode {
603 EntityMismatch,
604 SelectProjectionShape,
605 SelectDistinct,
606 DistinctOrderByProjection,
607 GlobalAggregateProjection,
608 GlobalAggregateGroupBy,
609 SelectGroupByShape,
610 GroupedProjectionExplicitListRequired,
611 GroupedProjectionAggregateRequired,
612 GroupedProjectionNonGroupField,
613 GroupedProjectionScalarAfterAggregate,
614 HavingRequiresGroupBy,
615 SelectHavingShape,
616 AggregateInputExpressions,
617 WhereExpressionShape,
618 ParameterPlacement,
619 SqlDdlExecutionUnsupported,
620}
621
622impl fmt::Debug for SqlLoweringCode {
623 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
624 fmt_compact_code(f, *self as u16)
625 }
626}
627
628#[repr(u16)]
636#[derive(Clone, Copy, Eq, Hash, PartialEq)]
637pub enum SqlSurfaceMismatchCode {
638 QueryRejectsInsert,
639 QueryRejectsUpdate,
640 QueryRejectsDelete,
641 UpdateRejectsSelect,
642 UpdateRejectsExplain,
643 UpdateRejectsDescribe,
644 UpdateRejectsShowIndexes,
645 UpdateRejectsShowColumns,
646 UpdateRejectsShowEntities,
647 UpdateRejectsShowStores,
648 UpdateRejectsShowMemory,
649}
650
651impl fmt::Debug for SqlSurfaceMismatchCode {
652 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
653 fmt_compact_code(f, *self as u16)
654 }
655}
656
657#[repr(u16)]
665#[derive(Clone, Copy, Eq, Hash, PartialEq)]
666pub enum SqlWriteBoundaryCode {
667 PrimaryKeyLiteralShape,
668 PrimaryKeyLiteralIncompatible,
669 MissingPrimaryKey,
670 MissingRequiredFields,
671 ExplicitManagedField,
672 ExplicitGeneratedField,
673 InsertSelectRequiresScalar,
674 InsertSelectAggregateProjection,
675 InsertSelectWidthMismatch,
676 UpdatePrimaryKeyMutation,
677 InvalidFieldLiteral,
678 UnknownReturningField,
679 DuplicateReturningField,
680 UpdateMissingWherePredicate,
681 WriteOrderByUnsupportedShape,
682 ReturningResponseTooLarge,
683 ReturningRowsTooMany,
684 StagedRowsTooMany,
685}
686
687impl fmt::Debug for SqlWriteBoundaryCode {
688 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
689 fmt_compact_code(f, *self as u16)
690 }
691}
692
693#[repr(u16)]
701#[derive(Clone, Copy, Eq, Hash, PartialEq)]
702pub enum SchemaDdlAdmissionCode {
703 MissingExpectedSchemaVersion,
704 MissingNextSchemaVersion,
705 StaleExpectedSchemaVersion,
706 InvalidExpectedSchemaVersion,
707 InvalidNextSchemaVersion,
708 AcceptedSchemaChangeWithoutVersionBump,
709 EmptyVersionBump,
710 VersionGap,
711 VersionRollback,
712 FingerprintMethodMismatch,
713 UnsupportedTransitionClass,
714 PhysicalRunnerMissing,
715 ValidationFailed,
716 PublicationRaceLost,
717 InvalidAddColumnDefault,
718 InvalidAlterColumnDefault,
719 GeneratedIndexDropRejected,
720 RequiredDropDefaultUnsupported,
721 GeneratedFieldDefaultChangeRejected,
722 GeneratedFieldNullabilityChangeRejected,
723 SetNotNullValidationFailed,
724}
725
726impl fmt::Debug for SchemaDdlAdmissionCode {
727 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
728 fmt_compact_code(f, *self as u16)
729 }
730}
731
732#[remain::sorted]
739#[derive(Clone, Copy, Eq, PartialEq)]
740pub enum DiagnosticDetail {
741 QueryKind { kind: QueryErrorKind },
742 QueryProjection { reason: QueryProjectionCode },
743 QueryReadAdmission { reason: QueryReadAdmissionCode },
744 QueryResultShape { reason: QueryResultShapeCode },
745 RuntimeBoundary { boundary: RuntimeBoundaryCode },
746 RuntimeKind { kind: RuntimeErrorKind },
747 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
748 SqlLowering { reason: SqlLoweringCode },
749 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
750 SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
751 UnsupportedSqlFeature { feature: SqlFeatureCode },
752}
753
754impl fmt::Debug for DiagnosticDetail {
755 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
756 fmt_compact_code(
757 f,
758 ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
759 )
760 }
761}
762
763#[derive(Clone, Eq, PartialEq)]
770pub struct Diagnostic {
771 code: DiagnosticCode,
772 origin: ErrorOrigin,
773 detail: Option<DiagnosticDetail>,
774}
775
776impl Diagnostic {
777 #[must_use]
779 pub const fn new(
780 code: DiagnosticCode,
781 origin: ErrorOrigin,
782 detail: Option<DiagnosticDetail>,
783 ) -> Self {
784 Self {
785 code,
786 origin,
787 detail,
788 }
789 }
790
791 #[must_use]
793 pub const fn from_code(code: DiagnosticCode) -> Self {
794 Self::new(code, code.origin(), None)
795 }
796
797 #[must_use]
799 pub const fn code(&self) -> DiagnosticCode {
800 self.code
801 }
802
803 #[must_use]
805 pub const fn class(&self) -> ErrorClass {
806 self.code.class()
807 }
808
809 #[must_use]
811 pub const fn origin(&self) -> ErrorOrigin {
812 self.origin
813 }
814
815 #[must_use]
817 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
818 self.detail.as_ref()
819 }
820
821 #[must_use]
823 pub const fn error_code(&self) -> ErrorCode {
824 ErrorCode::from_parts(self.code, self.detail)
825 }
826}
827
828impl fmt::Debug for Diagnostic {
829 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
830 write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
831 }
832}
833
834fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
835 write!(f, "{raw}")
836}
837
838#[cfg(test)]
839mod tests {
840 use super::{
841 Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
842 QueryProjectionCode, QueryReadAdmissionCode, SqlFeatureCode, SqlLoweringCode,
843 SqlWriteBoundaryCode,
844 registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
845 };
846
847 #[test]
848 fn diagnostic_from_code_uses_default_origin() {
849 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
850
851 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
852 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
853 }
854
855 #[test]
856 fn diagnostic_code_reports_broad_class() {
857 assert_eq!(
858 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
859 ErrorClass::Unsupported
860 );
861 assert_eq!(
862 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
863 ErrorClass::Unsupported
864 );
865 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
866 assert_eq!(
867 DiagnosticCode::StoreCorruption.class(),
868 ErrorClass::Corruption
869 );
870 }
871
872 #[test]
873 fn class_and_origin_wire_codes_round_trip() {
874 for (class, raw) in [
875 (ErrorClass::Query, 1),
876 (ErrorClass::Corruption, 2),
877 (ErrorClass::IncompatiblePersistedFormat, 3),
878 (ErrorClass::NotFound, 4),
879 (ErrorClass::Internal, 5),
880 (ErrorClass::Conflict, 6),
881 (ErrorClass::Unsupported, 7),
882 (ErrorClass::InvariantViolation, 8),
883 ] {
884 assert_eq!(class.wire_code(), raw);
885 assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
886 assert_eq!(format!("{class:?}"), raw.to_string());
887 }
888
889 for (origin, raw) in [
890 (ErrorOrigin::Cursor, 1),
891 (ErrorOrigin::Executor, 2),
892 (ErrorOrigin::Identity, 3),
893 (ErrorOrigin::Index, 4),
894 (ErrorOrigin::Interface, 5),
895 (ErrorOrigin::Planner, 6),
896 (ErrorOrigin::Query, 7),
897 (ErrorOrigin::Recovery, 8),
898 (ErrorOrigin::Response, 9),
899 (ErrorOrigin::Runtime, 10),
900 (ErrorOrigin::Serialize, 11),
901 (ErrorOrigin::Store, 12),
902 ] {
903 assert_eq!(origin.wire_code(), raw);
904 assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
905 assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
906 assert_eq!(format!("{origin:?}"), raw.to_string());
907 }
908
909 assert_eq!(ErrorClass::from_wire_code(0), None);
910 assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
911 assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
912 }
913
914 #[test]
915 fn public_error_codes_are_sequential() {
916 let first = ORDERED_ERROR_CODES
917 .first()
918 .expect("public error-code registry is non-empty")
919 .raw();
920
921 assert_eq!(first, 1);
922
923 for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
924 let expected = first + u16::try_from(index).expect("test error-code index fits u16");
925 assert_eq!(code.raw(), expected);
926 assert_eq!(ErrorCode::known(code.raw()), Some(*code));
927 assert!(code.is_known());
928 }
929
930 let last = ORDERED_ERROR_CODES
931 .last()
932 .expect("public error-code registry is non-empty")
933 .raw();
934
935 assert_eq!(last, 202);
936 }
937
938 #[test]
939 fn all_public_error_codes_round_trip_through_diagnostic_parts() {
940 let first = ORDERED_ERROR_CODES
941 .first()
942 .expect("public error-code registry is non-empty")
943 .raw();
944 let last = ORDERED_ERROR_CODES
945 .last()
946 .expect("public error-code registry is non-empty")
947 .raw();
948
949 for raw in first..=last {
950 let code = ErrorCode::from_raw(raw);
951 let diagnostic_code = code.diagnostic_code();
952 let diagnostic_detail = code.diagnostic_detail();
953 let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
954
955 assert_eq!(rebuilt.raw(), raw);
956
957 let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
958
959 assert_eq!(diagnostic.code(), diagnostic_code);
960 assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
961 assert_eq!(diagnostic.error_code().raw(), raw);
962 }
963 }
964
965 #[test]
966 fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
967 for raw in [0, 203, u16::MAX] {
968 let code = ErrorCode::from_raw(raw);
969
970 assert_eq!(ErrorCode::known(raw), None);
971 assert!(!code.is_known());
972 assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
973 assert_eq!(code.diagnostic_detail(), None);
974 assert_eq!(code.class(), ErrorClass::Internal);
975
976 let diagnostic = code.diagnostic(ErrorOrigin::Query);
977
978 assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
979 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
980 assert_eq!(diagnostic.detail(), None);
981 assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
982 }
983 }
984
985 #[test]
986 fn from_parts_requires_detail_to_match_broad_code() {
987 let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
988 feature: SqlFeatureCode::Join,
989 });
990
991 assert_eq!(
992 ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
993 ErrorCode::SQL_FEATURE_JOIN
994 );
995 assert_eq!(
996 ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
997 ErrorCode::QUERY_PLAN
998 );
999 }
1000
1001 #[test]
1002 fn detail_bearing_registry_entries_round_trip_directly() {
1003 assert!(!DETAIL_ERROR_CODES.is_empty());
1004
1005 for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
1006 assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
1007 assert_eq!(code.diagnostic_code(), diagnostic_code);
1008 assert_eq!(code.diagnostic_detail(), Some(detail));
1009 assert_eq!(detail.diagnostic_code(), diagnostic_code);
1010 }
1011 }
1012
1013 #[test]
1014 fn diagnostic_detail_reports_generated_broad_code() {
1015 let detail = DiagnosticDetail::UnsupportedSqlFeature {
1016 feature: SqlFeatureCode::Join,
1017 };
1018
1019 assert_eq!(
1020 detail.diagnostic_code(),
1021 DiagnosticCode::QueryUnsupportedSqlFeature
1022 );
1023 assert_eq!(format!("{detail:?}"), "69");
1024 }
1025
1026 #[test]
1027 fn public_error_codes_reconstruct_shifted_details() {
1028 assert_eq!(
1029 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
1030 DiagnosticCode::QueryUnknownAggregateTargetField
1031 );
1032 assert_eq!(
1033 ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
1034 Some(DiagnosticDetail::UnsupportedSqlFeature {
1035 feature: SqlFeatureCode::Join,
1036 })
1037 );
1038 assert_eq!(
1039 ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1040 Some(DiagnosticDetail::QueryProjection {
1041 reason: QueryProjectionCode::NumericLiteralRequired,
1042 })
1043 );
1044 assert_eq!(
1045 ErrorCode::QUERY_READ_PUBLIC_REQUIRES_LIMIT.diagnostic_detail(),
1046 Some(DiagnosticDetail::QueryReadAdmission {
1047 reason: QueryReadAdmissionCode::PublicQueryRequiresLimit,
1048 })
1049 );
1050 assert_eq!(
1051 ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1052 Some(DiagnosticDetail::SqlLowering {
1053 reason: SqlLoweringCode::DistinctOrderByProjection,
1054 })
1055 );
1056 assert_eq!(
1057 ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1058 Some(DiagnosticDetail::SqlWriteBoundary {
1059 boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1060 })
1061 );
1062 assert_eq!(
1063 ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1064 Some(DiagnosticDetail::SqlWriteBoundary {
1065 boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1066 })
1067 );
1068 }
1069}