1use std::fmt;
13
14#[remain::sorted]
21#[derive(Clone, Copy, Eq, Hash, PartialEq)]
22pub enum DiagnosticCode {
23 QueryAccessRequirement,
24 QueryIntent,
25 QueryInvalidContinuationCursor,
26 QueryNotFound,
27 QueryNotUnique,
28 QueryNumericNotRepresentable,
29 QueryNumericOverflow,
30 QueryPlan,
31 QueryReadAdmission,
32 QueryResultShapeMismatch,
33 QuerySqlSurfaceMismatch,
34 QuerySqlWriteBoundary,
35 QueryUnknownAggregateTargetField,
36 QueryUnorderedPagination,
37 QueryUnsupportedProjection,
38 QueryUnsupportedSqlFeature,
39 QueryValidate,
40 RuntimeConflict,
41 RuntimeCorruption,
42 RuntimeIncompatiblePersistedFormat,
43 RuntimeInternal,
44 RuntimeInvariantViolation,
45 RuntimeNotFound,
46 RuntimeUnsupported,
47 SchemaDdlAdmission,
48 StoreCorruption,
49 StoreInvariantViolation,
50 StoreNotFound,
51}
52
53impl DiagnosticCode {
54 #[must_use]
56 pub const fn class(self) -> ErrorClass {
57 match self {
58 Self::StoreCorruption | Self::RuntimeCorruption => ErrorClass::Corruption,
59 Self::RuntimeIncompatiblePersistedFormat => ErrorClass::IncompatiblePersistedFormat,
60 Self::QueryNotFound | Self::StoreNotFound | Self::RuntimeNotFound => {
61 ErrorClass::NotFound
62 }
63 Self::RuntimeConflict => ErrorClass::Conflict,
64 Self::QueryUnsupportedSqlFeature
65 | Self::QueryUnknownAggregateTargetField
66 | Self::QueryUnsupportedProjection
67 | Self::QueryResultShapeMismatch
68 | Self::QuerySqlSurfaceMismatch
69 | Self::QuerySqlWriteBoundary
70 | Self::RuntimeUnsupported => ErrorClass::Unsupported,
71 Self::StoreInvariantViolation | Self::RuntimeInvariantViolation => {
72 ErrorClass::InvariantViolation
73 }
74 Self::RuntimeInternal => ErrorClass::Internal,
75 Self::QueryValidate
76 | Self::QueryIntent
77 | Self::QueryPlan
78 | Self::QueryReadAdmission
79 | Self::QueryAccessRequirement
80 | Self::QueryUnorderedPagination
81 | Self::QueryInvalidContinuationCursor
82 | Self::QueryNotUnique
83 | Self::QueryNumericOverflow
84 | Self::QueryNumericNotRepresentable
85 | Self::SchemaDdlAdmission => ErrorClass::Query,
86 }
87 }
88
89 #[must_use]
91 pub const fn origin(self) -> ErrorOrigin {
92 match self {
93 Self::StoreNotFound | Self::StoreCorruption | Self::StoreInvariantViolation => {
94 ErrorOrigin::Store
95 }
96 Self::RuntimeCorruption
97 | Self::RuntimeIncompatiblePersistedFormat
98 | Self::RuntimeInvariantViolation
99 | Self::RuntimeConflict
100 | Self::RuntimeNotFound
101 | Self::RuntimeUnsupported
102 | Self::RuntimeInternal => ErrorOrigin::Runtime,
103 Self::QueryValidate
104 | Self::QueryIntent
105 | Self::QueryPlan
106 | Self::QueryReadAdmission
107 | Self::QueryAccessRequirement
108 | Self::QueryUnorderedPagination
109 | Self::QueryInvalidContinuationCursor
110 | Self::QueryNotFound
111 | Self::QueryNotUnique
112 | Self::QueryNumericOverflow
113 | Self::QueryNumericNotRepresentable
114 | Self::QueryUnknownAggregateTargetField
115 | Self::QueryUnsupportedProjection
116 | Self::QueryResultShapeMismatch
117 | Self::QueryUnsupportedSqlFeature
118 | Self::QuerySqlSurfaceMismatch
119 | Self::QuerySqlWriteBoundary
120 | Self::SchemaDdlAdmission => ErrorOrigin::Query,
121 }
122 }
123
124 #[must_use]
126 pub const fn error_code(self) -> ErrorCode {
127 match self {
128 Self::QueryValidate => ErrorCode::QUERY_VALIDATE,
129 Self::QueryIntent => ErrorCode::QUERY_INTENT,
130 Self::QueryPlan => ErrorCode::QUERY_PLAN,
131 Self::QueryReadAdmission => ErrorCode::QUERY_READ_ADMISSION,
132 Self::QueryAccessRequirement => ErrorCode::QUERY_ACCESS_REQUIREMENT,
133 Self::QueryUnorderedPagination => ErrorCode::QUERY_UNORDERED_PAGINATION,
134 Self::QueryInvalidContinuationCursor => ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
135 Self::QueryNotFound => ErrorCode::QUERY_NOT_FOUND,
136 Self::QueryNotUnique => ErrorCode::QUERY_NOT_UNIQUE,
137 Self::QueryNumericOverflow => ErrorCode::QUERY_NUMERIC_OVERFLOW,
138 Self::QueryNumericNotRepresentable => ErrorCode::QUERY_NUMERIC_NOT_REPRESENTABLE,
139 Self::QueryUnknownAggregateTargetField => {
140 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD
141 }
142 Self::QueryUnsupportedProjection => ErrorCode::QUERY_UNSUPPORTED_PROJECTION,
143 Self::QueryResultShapeMismatch => ErrorCode::QUERY_RESULT_SHAPE_MISMATCH,
144 Self::QueryUnsupportedSqlFeature => ErrorCode::QUERY_UNSUPPORTED_SQL_FEATURE,
145 Self::QuerySqlSurfaceMismatch => ErrorCode::QUERY_SQL_SURFACE_MISMATCH,
146 Self::QuerySqlWriteBoundary => ErrorCode::QUERY_SQL_WRITE_BOUNDARY,
147 Self::SchemaDdlAdmission => ErrorCode::SCHEMA_DDL_ADMISSION,
148 Self::StoreNotFound => ErrorCode::STORE_NOT_FOUND,
149 Self::StoreCorruption => ErrorCode::STORE_CORRUPTION,
150 Self::StoreInvariantViolation => ErrorCode::STORE_INVARIANT_VIOLATION,
151 Self::RuntimeCorruption => ErrorCode::RUNTIME_CORRUPTION,
152 Self::RuntimeIncompatiblePersistedFormat => {
153 ErrorCode::RUNTIME_INCOMPATIBLE_PERSISTED_FORMAT
154 }
155 Self::RuntimeInvariantViolation => ErrorCode::RUNTIME_INVARIANT_VIOLATION,
156 Self::RuntimeConflict => ErrorCode::RUNTIME_CONFLICT,
157 Self::RuntimeNotFound => ErrorCode::RUNTIME_NOT_FOUND,
158 Self::RuntimeUnsupported => ErrorCode::RUNTIME_UNSUPPORTED,
159 Self::RuntimeInternal => ErrorCode::RUNTIME_INTERNAL,
160 }
161 }
162}
163
164impl fmt::Debug for DiagnosticCode {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 fmt_compact_code(f, self.error_code().raw())
167 }
168}
169
170#[derive(Clone, Copy, Eq, Hash, PartialEq)]
182pub struct ErrorCode(u16);
183
184mod registry;
185
186impl ErrorCode {
187 #[must_use]
189 pub const fn from_raw(raw: u16) -> Self {
190 Self(raw)
191 }
192
193 #[must_use]
195 pub const fn raw(self) -> u16 {
196 self.0
197 }
198}
199
200impl fmt::Debug for ErrorCode {
201 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202 fmt_compact_code(f, self.raw())
203 }
204}
205
206#[remain::sorted]
213#[derive(Clone, Copy, Eq, Hash, PartialEq)]
214pub enum ErrorClass {
215 Conflict,
216 Corruption,
217 IncompatiblePersistedFormat,
218 Internal,
219 InvariantViolation,
220 NotFound,
221 Query,
222 Unsupported,
223}
224
225impl ErrorClass {
226 #[must_use]
228 pub const fn wire_code(self) -> u8 {
229 match self {
230 Self::Query => 1,
231 Self::Corruption => 2,
232 Self::IncompatiblePersistedFormat => 3,
233 Self::NotFound => 4,
234 Self::Internal => 5,
235 Self::Conflict => 6,
236 Self::Unsupported => 7,
237 Self::InvariantViolation => 8,
238 }
239 }
240
241 #[must_use]
243 pub const fn from_wire_code(code: u8) -> Option<Self> {
244 match code {
245 1 => Some(Self::Query),
246 2 => Some(Self::Corruption),
247 3 => Some(Self::IncompatiblePersistedFormat),
248 4 => Some(Self::NotFound),
249 5 => Some(Self::Internal),
250 6 => Some(Self::Conflict),
251 7 => Some(Self::Unsupported),
252 8 => Some(Self::InvariantViolation),
253 _ => None,
254 }
255 }
256}
257
258impl fmt::Debug for ErrorClass {
259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260 fmt_compact_code(f, u16::from(self.wire_code()))
261 }
262}
263
264#[remain::sorted]
271#[derive(Clone, Copy, Eq, Hash, PartialEq)]
272pub enum ErrorOrigin {
273 Cursor,
274 Executor,
275 Identity,
276 Index,
277 Interface,
278 Planner,
279 Query,
280 Recovery,
281 Response,
282 Runtime,
283 Serialize,
284 Store,
285}
286
287impl ErrorOrigin {
288 #[must_use]
290 pub const fn wire_code(self) -> u8 {
291 match self {
292 Self::Cursor => 1,
293 Self::Executor => 2,
294 Self::Identity => 3,
295 Self::Index => 4,
296 Self::Interface => 5,
297 Self::Planner => 6,
298 Self::Query => 7,
299 Self::Recovery => 8,
300 Self::Response => 9,
301 Self::Runtime => 10,
302 Self::Serialize => 11,
303 Self::Store => 12,
304 }
305 }
306
307 #[must_use]
309 pub const fn from_known_wire_code(code: u8) -> Option<Self> {
310 match code {
311 1 => Some(Self::Cursor),
312 2 => Some(Self::Executor),
313 3 => Some(Self::Identity),
314 4 => Some(Self::Index),
315 5 => Some(Self::Interface),
316 6 => Some(Self::Planner),
317 7 => Some(Self::Query),
318 8 => Some(Self::Recovery),
319 9 => Some(Self::Response),
320 10 => Some(Self::Runtime),
321 11 => Some(Self::Serialize),
322 12 => Some(Self::Store),
323 _ => None,
324 }
325 }
326
327 #[must_use]
332 pub const fn from_wire_code(code: u8) -> Self {
333 match Self::from_known_wire_code(code) {
334 Some(origin) => origin,
335 None => Self::Runtime,
336 }
337 }
338}
339
340impl fmt::Debug for ErrorOrigin {
341 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342 fmt_compact_code(f, u16::from(self.wire_code()))
343 }
344}
345
346#[repr(u16)]
353#[derive(Clone, Copy, Eq, Hash, PartialEq)]
354pub enum QueryErrorKind {
355 Validate,
356 Intent,
357 Plan,
358 AccessRequirement,
359 UnorderedPagination,
360 InvalidContinuationCursor,
361 NotFound,
362 NotUnique,
363}
364
365impl fmt::Debug for QueryErrorKind {
366 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367 fmt_compact_code(f, *self as u16)
368 }
369}
370
371#[repr(u16)]
379#[derive(Clone, Copy, Eq, Hash, PartialEq)]
380pub enum QueryProjectionCode {
381 NumericLiteralRequired,
382 NumericScaleArguments,
383 NestedFieldPathPreview,
384 CaseConditionBooleanRequired,
385 NumericInputRequired,
386 TextOrBlobInputRequired,
387 TextInputRequired,
388 TextOrNullArgumentRequired,
389 IntegerOrNullArgumentRequired,
390 UnaryOperandIncompatible,
391 BinaryOperandsIncompatible,
392}
393
394impl fmt::Debug for QueryProjectionCode {
395 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396 fmt_compact_code(f, *self as u16)
397 }
398}
399
400#[repr(u16)]
408#[derive(Clone, Copy, Eq, Hash, PartialEq)]
409pub enum QueryReadAdmissionCode {
410 PublicQueryRequiresLimit,
411 PublicQueryRequiresIndex,
412 UnboundedFullScanRejected,
413 SortRequiresMaterialization,
414 GroupedQueryRequiresLimits,
415 GroupedQueryExceedsBudget,
416 DiagnosticLaneDoesNotExecute,
417 ReturnedRowBoundExceedsPolicy,
418 PrimaryKeyInputExceedsPolicy,
419}
420
421impl fmt::Debug for QueryReadAdmissionCode {
422 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
423 fmt_compact_code(f, *self as u16)
424 }
425}
426
427#[repr(u16)]
435#[derive(Clone, Copy, Eq, Hash, PartialEq)]
436pub enum QueryResultShapeCode {
437 ExpectedRows,
438 ExpectedGroupedRows,
439}
440
441impl fmt::Debug for QueryResultShapeCode {
442 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443 fmt_compact_code(f, *self as u16)
444 }
445}
446
447#[repr(u16)]
454#[derive(Clone, Copy, Eq, Hash, PartialEq)]
455pub enum RuntimeErrorKind {
456 Corruption,
457 IncompatiblePersistedFormat,
458 InvariantViolation,
459 Conflict,
460 NotFound,
461 Unsupported,
462 Internal,
463}
464
465impl fmt::Debug for RuntimeErrorKind {
466 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
467 fmt_compact_code(f, *self as u16)
468 }
469}
470
471#[repr(u16)]
479#[derive(Clone, Copy, Eq, Hash, PartialEq)]
480pub enum RuntimeBoundaryCode {
481 SqlSurfaceControllerRequired,
482 SchemaSurfaceControllerRequired,
483 SqlQueryNoConfiguredEntities,
484 SqlQueryEntityNotConfigured,
485 SqlDdlTargetRequired,
486 SqlDdlEntityNotConfigured,
487 QueryResponseRowsRequired,
488 QueryResponseGroupedRowsRequired,
489 RowProjectionFieldNotConfigured,
490 SqlIntrospectionDisabled,
491 MutationRequiredFieldMissing,
493 PersistedRowLayoutOutsideAcceptedWindow,
495 PersistedRowSlotCountMismatch,
497 GeneratedFieldAfterDdlField,
499}
500
501impl fmt::Debug for RuntimeBoundaryCode {
502 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
503 fmt_compact_code(f, *self as u16)
504 }
505}
506
507#[repr(u16)]
515#[derive(Clone, Copy, Eq, Hash, PartialEq)]
516pub enum SqlFeatureCode {
517 AggregateFilterClause,
518 AlterStatementBeyondAlterTable,
519 AlterTableAddColumnDuplicateDefault,
520 AlterTableAddColumnModifiers,
521 AlterTableAddStatementBeyondAddColumn,
522 AlterTableAlterColumnDropUnsupportedAction,
523 AlterTableAlterColumnModifiers,
524 AlterTableAlterColumnSetUnsupportedAction,
525 AlterTableAlterColumnUnsupportedAction,
526 AlterTableAlterStatementBeyondAlterColumn,
527 AlterTableDropColumnIfExistsSyntax,
528 AlterTableDropColumnModifiers,
529 AlterTableDropStatementBeyondDropColumn,
530 AlterTableRenameColumnMissingTo,
531 AlterTableRenameColumnModifiers,
532 AlterTableRenameStatementBeyondRenameColumn,
533 AlterTableUnsupportedOperation,
534 ColumnAlias,
535 CreateIndexIfNotExistsSyntax,
536 CreateIndexKeyOrderingModifiers,
537 CreateIndexModifiers,
538 CreateStatementBeyondCreateIndex,
539 DescribeModifier,
540 DdlSchemaVersionDuplicateExpectedClause,
541 DdlSchemaVersionDuplicateSetClause,
542 DropIndexModifiers,
543 DropIndexIfExistsSyntax,
544 DropStatementBeyondDropIndex,
545 ExpressionIndexUnsupportedFunction,
546 Having,
547 Insert,
548 Join,
549 LikePatternBeyondTrailingPrefix,
550 LowerFieldPredicateUnsupported,
551 MultiStatementSql,
552 NestedAggregateInput,
553 NestedProjectionFunctionInArithmetic,
554 OrderByUnsupportedForm,
555 Other,
556 PredicateStartsWithFirstArgument,
557 QuotedIdentifiers,
558 ReturningUnsupportedShape,
559 ScalarFunctionExpressionPosition,
560 ScaleTakingNumericFunctionExpressionPosition,
561 SearchedCaseGroupedOrderBy,
562 ShowColumnsModifiers,
563 ShowEntitiesModifiers,
564 ShowIndexesModifiers,
565 ShowMemoryModifiers,
566 ShowStoresModifiers,
567 ShowUnsupportedCommand,
568 SimpleCaseExpression,
569 StandaloneLiteralProjectionItem,
570 SupportedGroupedOrderByExpressionFamily,
571 SupportedOrderByExpressionFamily,
572 UnionIntersectExcept,
573 UnsupportedFunctionNamespace,
574 Update,
575 UpperFieldPredicateUnsupported,
576 WindowFunction,
577 With,
578 NumericScaleFunctionArguments,
579 OrderByFieldNotOrderable,
580}
581
582impl fmt::Debug for SqlFeatureCode {
583 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
584 fmt_compact_code(f, *self as u16)
585 }
586}
587
588#[repr(u16)]
597#[derive(Clone, Copy, Eq, Hash, PartialEq)]
598pub enum SqlLoweringCode {
599 EntityMismatch,
600 SelectProjectionShape,
601 SelectDistinct,
602 DistinctOrderByProjection,
603 GlobalAggregateProjection,
604 GlobalAggregateGroupBy,
605 SelectGroupByShape,
606 GroupedProjectionExplicitListRequired,
607 GroupedProjectionAggregateRequired,
608 GroupedProjectionNonGroupField,
609 GroupedProjectionScalarAfterAggregate,
610 HavingRequiresGroupBy,
611 SelectHavingShape,
612 AggregateInputExpressions,
613 WhereExpressionShape,
614 ParameterPlacement,
615 SqlDdlExecutionUnsupported,
616}
617
618impl fmt::Debug for SqlLoweringCode {
619 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
620 fmt_compact_code(f, *self as u16)
621 }
622}
623
624#[repr(u16)]
632#[derive(Clone, Copy, Eq, Hash, PartialEq)]
633pub enum SqlSurfaceMismatchCode {
634 QueryRejectsInsert,
635 QueryRejectsUpdate,
636 QueryRejectsDelete,
637 MutationRejectsSelect,
638 MutationRejectsExplain,
639 MutationRejectsDescribe,
640 MutationRejectsShowIndexes,
641 MutationRejectsShowColumns,
642 MutationRejectsShowEntities,
643 MutationRejectsShowStores,
644 MutationRejectsShowMemory,
645 MutationRequiresExplicitUpdateIntent,
646}
647
648impl fmt::Debug for SqlSurfaceMismatchCode {
649 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
650 fmt_compact_code(f, *self as u16)
651 }
652}
653
654#[repr(u16)]
662#[derive(Clone, Copy, Eq, Hash, PartialEq)]
663pub enum SqlWriteBoundaryCode {
664 PrimaryKeyLiteralShape,
665 PrimaryKeyLiteralIncompatible,
666 MissingPrimaryKey,
667 MissingRequiredFields,
668 ExplicitManagedField,
669 ExplicitGeneratedField,
670 InsertSelectRequiresScalar,
671 InsertSelectAggregateProjection,
672 InsertSelectWidthMismatch,
673 UpdatePrimaryKeyMutation,
674 InvalidFieldLiteral,
675 UnknownReturningField,
676 DuplicateReturningField,
677 UpdateMissingWherePredicate,
678 WriteOrderByUnsupportedShape,
679 ReturningResponseTooLarge,
680 ReturningRowsTooMany,
681 StagedRowsTooMany,
682 InsertDefaultRequiredField,
683 UpdateDefaultRequiredField,
684 UpdateDefaultDatabaseOwnedField,
685 ExactUpdateAssertionRequired,
686 ExactUpdateAssertionTooHigh,
687 ExactUpdateAffectedRowsExceeded,
688 ExactUpdateWindowUnsupported,
689 ExactUpdateScanBudgetExceeded,
690}
691
692impl fmt::Debug for SqlWriteBoundaryCode {
693 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
694 fmt_compact_code(f, *self as u16)
695 }
696}
697
698#[repr(u16)]
706#[derive(Clone, Copy, Eq, Hash, PartialEq)]
707pub enum SchemaDdlAdmissionCode {
708 MissingExpectedSchemaVersion,
709 MissingNextSchemaVersion,
710 StaleExpectedSchemaVersion,
711 InvalidExpectedSchemaVersion,
712 InvalidNextSchemaVersion,
713 AcceptedSchemaChangeWithoutVersionBump,
714 EmptyVersionBump,
715 VersionGap,
716 VersionRollback,
717 FingerprintMethodMismatch,
718 UnsupportedTransitionClass,
719 PhysicalRunnerMissing,
720 ValidationFailed,
721 PublicationRaceLost,
722 InvalidAddColumnDefault,
723 InvalidAlterColumnDefault,
724 GeneratedIndexDropRejected,
725 SchemaRewriteRequiresMigration,
726 SchemaTransitionBudgetExceeded,
727 GeneratedFieldDefaultChangeRejected,
728 GeneratedFieldNullabilityChangeRejected,
729 SetNotNullValidationFailed,
730 RowLayoutVersionExhausted,
731}
732
733impl fmt::Debug for SchemaDdlAdmissionCode {
734 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
735 fmt_compact_code(f, *self as u16)
736 }
737}
738
739#[remain::sorted]
746#[derive(Clone, Copy, Eq, PartialEq)]
747pub enum DiagnosticDetail {
748 QueryKind { kind: QueryErrorKind },
749 QueryProjection { reason: QueryProjectionCode },
750 QueryReadAdmission { reason: QueryReadAdmissionCode },
751 QueryResultShape { reason: QueryResultShapeCode },
752 RuntimeBoundary { boundary: RuntimeBoundaryCode },
753 RuntimeKind { kind: RuntimeErrorKind },
754 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
755 SqlLowering { reason: SqlLoweringCode },
756 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
757 SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
758 UnsupportedSqlFeature { feature: SqlFeatureCode },
759}
760
761impl fmt::Debug for DiagnosticDetail {
762 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
763 fmt_compact_code(
764 f,
765 ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
766 )
767 }
768}
769
770#[derive(Clone, Eq, PartialEq)]
777pub struct Diagnostic {
778 code: DiagnosticCode,
779 origin: ErrorOrigin,
780 detail: Option<DiagnosticDetail>,
781}
782
783impl Diagnostic {
784 #[must_use]
786 pub const fn new(
787 code: DiagnosticCode,
788 origin: ErrorOrigin,
789 detail: Option<DiagnosticDetail>,
790 ) -> Self {
791 Self {
792 code,
793 origin,
794 detail,
795 }
796 }
797
798 #[must_use]
800 pub const fn from_code(code: DiagnosticCode) -> Self {
801 Self::new(code, code.origin(), None)
802 }
803
804 #[must_use]
806 pub const fn code(&self) -> DiagnosticCode {
807 self.code
808 }
809
810 #[must_use]
812 pub const fn class(&self) -> ErrorClass {
813 self.code.class()
814 }
815
816 #[must_use]
818 pub const fn origin(&self) -> ErrorOrigin {
819 self.origin
820 }
821
822 #[must_use]
824 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
825 self.detail.as_ref()
826 }
827
828 #[must_use]
830 pub const fn error_code(&self) -> ErrorCode {
831 ErrorCode::from_parts(self.code, self.detail)
832 }
833}
834
835impl fmt::Debug for Diagnostic {
836 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
837 write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
838 }
839}
840
841fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
842 write!(f, "{raw}")
843}
844
845#[cfg(test)]
846mod tests {
847 use super::{
848 Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
849 QueryProjectionCode, QueryReadAdmissionCode, SqlFeatureCode, SqlLoweringCode,
850 SqlWriteBoundaryCode,
851 registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
852 };
853
854 #[test]
855 fn diagnostic_from_code_uses_default_origin() {
856 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
857
858 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
859 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
860 }
861
862 #[test]
863 fn diagnostic_code_reports_broad_class() {
864 assert_eq!(
865 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
866 ErrorClass::Unsupported
867 );
868 assert_eq!(
869 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
870 ErrorClass::Unsupported
871 );
872 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
873 assert_eq!(
874 DiagnosticCode::StoreCorruption.class(),
875 ErrorClass::Corruption
876 );
877 }
878
879 #[test]
880 fn class_and_origin_wire_codes_round_trip() {
881 for (class, raw) in [
882 (ErrorClass::Query, 1),
883 (ErrorClass::Corruption, 2),
884 (ErrorClass::IncompatiblePersistedFormat, 3),
885 (ErrorClass::NotFound, 4),
886 (ErrorClass::Internal, 5),
887 (ErrorClass::Conflict, 6),
888 (ErrorClass::Unsupported, 7),
889 (ErrorClass::InvariantViolation, 8),
890 ] {
891 assert_eq!(class.wire_code(), raw);
892 assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
893 assert_eq!(format!("{class:?}"), raw.to_string());
894 }
895
896 for (origin, raw) in [
897 (ErrorOrigin::Cursor, 1),
898 (ErrorOrigin::Executor, 2),
899 (ErrorOrigin::Identity, 3),
900 (ErrorOrigin::Index, 4),
901 (ErrorOrigin::Interface, 5),
902 (ErrorOrigin::Planner, 6),
903 (ErrorOrigin::Query, 7),
904 (ErrorOrigin::Recovery, 8),
905 (ErrorOrigin::Response, 9),
906 (ErrorOrigin::Runtime, 10),
907 (ErrorOrigin::Serialize, 11),
908 (ErrorOrigin::Store, 12),
909 ] {
910 assert_eq!(origin.wire_code(), raw);
911 assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
912 assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
913 assert_eq!(format!("{origin:?}"), raw.to_string());
914 }
915
916 assert_eq!(ErrorClass::from_wire_code(0), None);
917 assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
918 assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
919 }
920
921 #[test]
922 fn public_error_codes_are_sequential() {
923 let first = ORDERED_ERROR_CODES
924 .first()
925 .expect("public error-code registry is non-empty")
926 .raw();
927
928 assert_eq!(first, 1);
929
930 for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
931 let expected = first + u16::try_from(index).expect("test error-code index fits u16");
932 assert_eq!(code.raw(), expected);
933 assert_eq!(ErrorCode::known(code.raw()), Some(*code));
934 assert!(code.is_known());
935 }
936
937 let last = ORDERED_ERROR_CODES
938 .last()
939 .expect("public error-code registry is non-empty")
940 .raw();
941
942 assert_eq!(last, 205);
943 }
944
945 #[test]
946 fn all_public_error_codes_round_trip_through_diagnostic_parts() {
947 let first = ORDERED_ERROR_CODES
948 .first()
949 .expect("public error-code registry is non-empty")
950 .raw();
951 let last = ORDERED_ERROR_CODES
952 .last()
953 .expect("public error-code registry is non-empty")
954 .raw();
955
956 for raw in first..=last {
957 let code = ErrorCode::from_raw(raw);
958 let diagnostic_code = code.diagnostic_code();
959 let diagnostic_detail = code.diagnostic_detail();
960 let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
961
962 assert_eq!(rebuilt.raw(), raw);
963
964 let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
965
966 assert_eq!(diagnostic.code(), diagnostic_code);
967 assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
968 assert_eq!(diagnostic.error_code().raw(), raw);
969 }
970 }
971
972 #[test]
973 fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
974 for raw in [0, 206, u16::MAX] {
975 let code = ErrorCode::from_raw(raw);
976
977 assert_eq!(ErrorCode::known(raw), None);
978 assert!(!code.is_known());
979 assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
980 assert_eq!(code.diagnostic_detail(), None);
981 assert_eq!(code.class(), ErrorClass::Internal);
982
983 let diagnostic = code.diagnostic(ErrorOrigin::Query);
984
985 assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
986 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
987 assert_eq!(diagnostic.detail(), None);
988 assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
989 }
990 }
991
992 #[test]
993 fn from_parts_requires_detail_to_match_broad_code() {
994 let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
995 feature: SqlFeatureCode::Join,
996 });
997
998 assert_eq!(
999 ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
1000 ErrorCode::SQL_FEATURE_JOIN
1001 );
1002 assert_eq!(
1003 ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
1004 ErrorCode::QUERY_PLAN
1005 );
1006 }
1007
1008 #[test]
1009 fn detail_bearing_registry_entries_round_trip_directly() {
1010 assert!(!DETAIL_ERROR_CODES.is_empty());
1011
1012 for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
1013 assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
1014 assert_eq!(code.diagnostic_code(), diagnostic_code);
1015 assert_eq!(code.diagnostic_detail(), Some(detail));
1016 assert_eq!(detail.diagnostic_code(), diagnostic_code);
1017 }
1018 }
1019
1020 #[test]
1021 fn diagnostic_detail_reports_generated_broad_code() {
1022 let detail = DiagnosticDetail::UnsupportedSqlFeature {
1023 feature: SqlFeatureCode::Join,
1024 };
1025
1026 assert_eq!(
1027 detail.diagnostic_code(),
1028 DiagnosticCode::QueryUnsupportedSqlFeature
1029 );
1030 assert_eq!(format!("{detail:?}"), "65");
1031 }
1032
1033 #[test]
1034 fn public_error_codes_reconstruct_shifted_details() {
1035 assert_eq!(
1036 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
1037 DiagnosticCode::QueryUnknownAggregateTargetField
1038 );
1039 assert_eq!(
1040 ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
1041 Some(DiagnosticDetail::UnsupportedSqlFeature {
1042 feature: SqlFeatureCode::Join,
1043 })
1044 );
1045 assert_eq!(
1046 ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1047 Some(DiagnosticDetail::QueryProjection {
1048 reason: QueryProjectionCode::NumericLiteralRequired,
1049 })
1050 );
1051 assert_eq!(
1052 ErrorCode::QUERY_READ_PUBLIC_REQUIRES_LIMIT.diagnostic_detail(),
1053 Some(DiagnosticDetail::QueryReadAdmission {
1054 reason: QueryReadAdmissionCode::PublicQueryRequiresLimit,
1055 })
1056 );
1057 assert_eq!(
1058 ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1059 Some(DiagnosticDetail::SqlLowering {
1060 reason: SqlLoweringCode::DistinctOrderByProjection,
1061 })
1062 );
1063 assert_eq!(
1064 ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1065 Some(DiagnosticDetail::SqlWriteBoundary {
1066 boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1067 })
1068 );
1069 assert_eq!(
1070 ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1071 Some(DiagnosticDetail::SqlWriteBoundary {
1072 boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1073 })
1074 );
1075 }
1076}