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