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