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 QueryResultShapeMismatch,
32 QuerySqlSurfaceMismatch,
33 QuerySqlWriteBoundary,
34 QueryUnknownAggregateTargetField,
35 QueryUnorderedPagination,
36 QueryUnsupportedProjection,
37 QueryUnsupportedSqlFeature,
38 QueryValidate,
39 RuntimeConflict,
40 RuntimeCorruption,
41 RuntimeIncompatiblePersistedFormat,
42 RuntimeInternal,
43 RuntimeInvariantViolation,
44 RuntimeNotFound,
45 RuntimeUnsupported,
46 SchemaDdlAdmission,
47 StoreCorruption,
48 StoreInvariantViolation,
49 StoreNotFound,
50}
51
52impl DiagnosticCode {
53 #[must_use]
55 pub const fn class(self) -> ErrorClass {
56 match self {
57 Self::StoreCorruption | Self::RuntimeCorruption => ErrorClass::Corruption,
58 Self::RuntimeIncompatiblePersistedFormat => ErrorClass::IncompatiblePersistedFormat,
59 Self::QueryNotFound | Self::StoreNotFound | Self::RuntimeNotFound => {
60 ErrorClass::NotFound
61 }
62 Self::RuntimeConflict => ErrorClass::Conflict,
63 Self::QueryUnsupportedSqlFeature
64 | Self::QueryUnknownAggregateTargetField
65 | Self::QueryUnsupportedProjection
66 | Self::QueryResultShapeMismatch
67 | Self::QuerySqlSurfaceMismatch
68 | Self::QuerySqlWriteBoundary
69 | Self::RuntimeUnsupported => ErrorClass::Unsupported,
70 Self::StoreInvariantViolation | Self::RuntimeInvariantViolation => {
71 ErrorClass::InvariantViolation
72 }
73 Self::RuntimeInternal => ErrorClass::Internal,
74 Self::QueryValidate
75 | Self::QueryIntent
76 | Self::QueryPlan
77 | Self::QueryAccessRequirement
78 | Self::QueryUnorderedPagination
79 | Self::QueryInvalidContinuationCursor
80 | Self::QueryNotUnique
81 | Self::QueryNumericOverflow
82 | Self::QueryNumericNotRepresentable
83 | Self::SchemaDdlAdmission => ErrorClass::Query,
84 }
85 }
86
87 #[must_use]
89 pub const fn origin(self) -> ErrorOrigin {
90 match self {
91 Self::StoreNotFound | Self::StoreCorruption | Self::StoreInvariantViolation => {
92 ErrorOrigin::Store
93 }
94 Self::RuntimeCorruption
95 | Self::RuntimeIncompatiblePersistedFormat
96 | Self::RuntimeInvariantViolation
97 | Self::RuntimeConflict
98 | Self::RuntimeNotFound
99 | Self::RuntimeUnsupported
100 | Self::RuntimeInternal => ErrorOrigin::Runtime,
101 Self::QueryValidate
102 | Self::QueryIntent
103 | Self::QueryPlan
104 | Self::QueryAccessRequirement
105 | Self::QueryUnorderedPagination
106 | Self::QueryInvalidContinuationCursor
107 | Self::QueryNotFound
108 | Self::QueryNotUnique
109 | Self::QueryNumericOverflow
110 | Self::QueryNumericNotRepresentable
111 | Self::QueryUnknownAggregateTargetField
112 | Self::QueryUnsupportedProjection
113 | Self::QueryResultShapeMismatch
114 | Self::QueryUnsupportedSqlFeature
115 | Self::QuerySqlSurfaceMismatch
116 | Self::QuerySqlWriteBoundary
117 | Self::SchemaDdlAdmission => ErrorOrigin::Query,
118 }
119 }
120
121 #[must_use]
123 pub const fn error_code(self) -> ErrorCode {
124 match self {
125 Self::QueryValidate => ErrorCode::QUERY_VALIDATE,
126 Self::QueryIntent => ErrorCode::QUERY_INTENT,
127 Self::QueryPlan => ErrorCode::QUERY_PLAN,
128 Self::QueryAccessRequirement => ErrorCode::QUERY_ACCESS_REQUIREMENT,
129 Self::QueryUnorderedPagination => ErrorCode::QUERY_UNORDERED_PAGINATION,
130 Self::QueryInvalidContinuationCursor => ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
131 Self::QueryNotFound => ErrorCode::QUERY_NOT_FOUND,
132 Self::QueryNotUnique => ErrorCode::QUERY_NOT_UNIQUE,
133 Self::QueryNumericOverflow => ErrorCode::QUERY_NUMERIC_OVERFLOW,
134 Self::QueryNumericNotRepresentable => ErrorCode::QUERY_NUMERIC_NOT_REPRESENTABLE,
135 Self::QueryUnknownAggregateTargetField => {
136 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD
137 }
138 Self::QueryUnsupportedProjection => ErrorCode::QUERY_UNSUPPORTED_PROJECTION,
139 Self::QueryResultShapeMismatch => ErrorCode::QUERY_RESULT_SHAPE_MISMATCH,
140 Self::QueryUnsupportedSqlFeature => ErrorCode::QUERY_UNSUPPORTED_SQL_FEATURE,
141 Self::QuerySqlSurfaceMismatch => ErrorCode::QUERY_SQL_SURFACE_MISMATCH,
142 Self::QuerySqlWriteBoundary => ErrorCode::QUERY_SQL_WRITE_BOUNDARY,
143 Self::SchemaDdlAdmission => ErrorCode::SCHEMA_DDL_ADMISSION,
144 Self::StoreNotFound => ErrorCode::STORE_NOT_FOUND,
145 Self::StoreCorruption => ErrorCode::STORE_CORRUPTION,
146 Self::StoreInvariantViolation => ErrorCode::STORE_INVARIANT_VIOLATION,
147 Self::RuntimeCorruption => ErrorCode::RUNTIME_CORRUPTION,
148 Self::RuntimeIncompatiblePersistedFormat => {
149 ErrorCode::RUNTIME_INCOMPATIBLE_PERSISTED_FORMAT
150 }
151 Self::RuntimeInvariantViolation => ErrorCode::RUNTIME_INVARIANT_VIOLATION,
152 Self::RuntimeConflict => ErrorCode::RUNTIME_CONFLICT,
153 Self::RuntimeNotFound => ErrorCode::RUNTIME_NOT_FOUND,
154 Self::RuntimeUnsupported => ErrorCode::RUNTIME_UNSUPPORTED,
155 Self::RuntimeInternal => ErrorCode::RUNTIME_INTERNAL,
156 }
157 }
158}
159
160impl fmt::Debug for DiagnosticCode {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 fmt_compact_code(f, self.error_code().raw())
163 }
164}
165
166#[derive(Clone, Copy, Eq, Hash, PartialEq)]
178pub struct ErrorCode(u16);
179
180mod registry;
181
182impl ErrorCode {
183 #[must_use]
185 pub const fn from_raw(raw: u16) -> Self {
186 Self(raw)
187 }
188
189 #[must_use]
191 pub const fn raw(self) -> u16 {
192 self.0
193 }
194}
195
196impl fmt::Debug for ErrorCode {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 fmt_compact_code(f, self.raw())
199 }
200}
201
202#[remain::sorted]
209#[derive(Clone, Copy, Eq, Hash, PartialEq)]
210pub enum ErrorClass {
211 Conflict,
212 Corruption,
213 IncompatiblePersistedFormat,
214 Internal,
215 InvariantViolation,
216 NotFound,
217 Query,
218 Unsupported,
219}
220
221impl ErrorClass {
222 #[must_use]
224 pub const fn wire_code(self) -> u8 {
225 match self {
226 Self::Query => 1,
227 Self::Corruption => 2,
228 Self::IncompatiblePersistedFormat => 3,
229 Self::NotFound => 4,
230 Self::Internal => 5,
231 Self::Conflict => 6,
232 Self::Unsupported => 7,
233 Self::InvariantViolation => 8,
234 }
235 }
236
237 #[must_use]
239 pub const fn from_wire_code(code: u8) -> Option<Self> {
240 match code {
241 1 => Some(Self::Query),
242 2 => Some(Self::Corruption),
243 3 => Some(Self::IncompatiblePersistedFormat),
244 4 => Some(Self::NotFound),
245 5 => Some(Self::Internal),
246 6 => Some(Self::Conflict),
247 7 => Some(Self::Unsupported),
248 8 => Some(Self::InvariantViolation),
249 _ => None,
250 }
251 }
252}
253
254impl fmt::Debug for ErrorClass {
255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256 fmt_compact_code(f, u16::from(self.wire_code()))
257 }
258}
259
260#[remain::sorted]
267#[derive(Clone, Copy, Eq, Hash, PartialEq)]
268pub enum ErrorOrigin {
269 Cursor,
270 Executor,
271 Identity,
272 Index,
273 Interface,
274 Planner,
275 Query,
276 Recovery,
277 Response,
278 Runtime,
279 Serialize,
280 Store,
281}
282
283impl ErrorOrigin {
284 #[must_use]
286 pub const fn wire_code(self) -> u8 {
287 match self {
288 Self::Cursor => 1,
289 Self::Executor => 2,
290 Self::Identity => 3,
291 Self::Index => 4,
292 Self::Interface => 5,
293 Self::Planner => 6,
294 Self::Query => 7,
295 Self::Recovery => 8,
296 Self::Response => 9,
297 Self::Runtime => 10,
298 Self::Serialize => 11,
299 Self::Store => 12,
300 }
301 }
302
303 #[must_use]
305 pub const fn from_known_wire_code(code: u8) -> Option<Self> {
306 match code {
307 1 => Some(Self::Cursor),
308 2 => Some(Self::Executor),
309 3 => Some(Self::Identity),
310 4 => Some(Self::Index),
311 5 => Some(Self::Interface),
312 6 => Some(Self::Planner),
313 7 => Some(Self::Query),
314 8 => Some(Self::Recovery),
315 9 => Some(Self::Response),
316 10 => Some(Self::Runtime),
317 11 => Some(Self::Serialize),
318 12 => Some(Self::Store),
319 _ => None,
320 }
321 }
322
323 #[must_use]
328 pub const fn from_wire_code(code: u8) -> Self {
329 match Self::from_known_wire_code(code) {
330 Some(origin) => origin,
331 None => Self::Runtime,
332 }
333 }
334}
335
336impl fmt::Debug for ErrorOrigin {
337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338 fmt_compact_code(f, u16::from(self.wire_code()))
339 }
340}
341
342#[repr(u16)]
349#[derive(Clone, Copy, Eq, Hash, PartialEq)]
350pub enum QueryErrorKind {
351 Validate,
352 Intent,
353 Plan,
354 AccessRequirement,
355 UnorderedPagination,
356 InvalidContinuationCursor,
357 NotFound,
358 NotUnique,
359}
360
361impl fmt::Debug for QueryErrorKind {
362 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363 fmt_compact_code(f, *self as u16)
364 }
365}
366
367#[repr(u16)]
375#[derive(Clone, Copy, Eq, Hash, PartialEq)]
376pub enum QueryProjectionCode {
377 NumericLiteralRequired,
378 NumericScaleArguments,
379 NestedFieldPathPreview,
380 CaseConditionBooleanRequired,
381 NumericInputRequired,
382 TextOrBlobInputRequired,
383 TextInputRequired,
384 TextOrNullArgumentRequired,
385 IntegerOrNullArgumentRequired,
386 UnaryOperandIncompatible,
387 BinaryOperandsIncompatible,
388}
389
390impl fmt::Debug for QueryProjectionCode {
391 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
392 fmt_compact_code(f, *self as u16)
393 }
394}
395
396#[repr(u16)]
404#[derive(Clone, Copy, Eq, Hash, PartialEq)]
405pub enum QueryResultShapeCode {
406 ExpectedRows,
407 ExpectedGroupedRows,
408}
409
410impl fmt::Debug for QueryResultShapeCode {
411 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
412 fmt_compact_code(f, *self as u16)
413 }
414}
415
416#[repr(u16)]
423#[derive(Clone, Copy, Eq, Hash, PartialEq)]
424pub enum RuntimeErrorKind {
425 Corruption,
426 IncompatiblePersistedFormat,
427 InvariantViolation,
428 Conflict,
429 NotFound,
430 Unsupported,
431 Internal,
432}
433
434impl fmt::Debug for RuntimeErrorKind {
435 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436 fmt_compact_code(f, *self as u16)
437 }
438}
439
440#[repr(u16)]
448#[derive(Clone, Copy, Eq, Hash, PartialEq)]
449pub enum RuntimeBoundaryCode {
450 SqlSurfaceControllerRequired,
451 SchemaSurfaceControllerRequired,
452 SqlQueryNoConfiguredEntities,
453 SqlQueryEntityNotConfigured,
454 SqlDdlTargetRequired,
455 SqlDdlEntityNotConfigured,
456 QueryResponseRowsRequired,
457 QueryResponseGroupedRowsRequired,
458 MutationResultEntityRequired,
459 MutationResultEntitiesRequired,
460 MutationResultIdRequired,
461 MutationResultIdsRequired,
462 RowProjectionFieldNotConfigured,
463 SqlIntrospectionDisabled,
464}
465
466impl fmt::Debug for RuntimeBoundaryCode {
467 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
468 fmt_compact_code(f, *self as u16)
469 }
470}
471
472#[repr(u16)]
480#[derive(Clone, Copy, Eq, Hash, PartialEq)]
481pub enum SqlFeatureCode {
482 AggregateFilterClause,
483 AlterStatementBeyondAlterTable,
484 AlterTableAddColumnDuplicateDefault,
485 AlterTableAddColumnModifiers,
486 AlterTableAddStatementBeyondAddColumn,
487 AlterTableAlterColumnDropUnsupportedAction,
488 AlterTableAlterColumnModifiers,
489 AlterTableAlterColumnSetUnsupportedAction,
490 AlterTableAlterColumnUnsupportedAction,
491 AlterTableAlterStatementBeyondAlterColumn,
492 AlterTableDropColumnIfExistsSyntax,
493 AlterTableDropColumnModifiers,
494 AlterTableDropStatementBeyondDropColumn,
495 AlterTableRenameColumnMissingTo,
496 AlterTableRenameColumnModifiers,
497 AlterTableRenameStatementBeyondRenameColumn,
498 AlterTableUnsupportedOperation,
499 ColumnAlias,
500 CreateIndexIfNotExistsSyntax,
501 CreateIndexKeyOrderingModifiers,
502 CreateIndexModifiers,
503 CreateStatementBeyondCreateIndex,
504 DescribeModifier,
505 DdlSchemaVersionDuplicateExpectedClause,
506 DdlSchemaVersionDuplicateSetClause,
507 DropIndexModifiers,
508 DropIndexIfExistsSyntax,
509 DropStatementBeyondDropIndex,
510 ExpressionIndexUnsupportedFunction,
511 Having,
512 Insert,
513 Join,
514 LikePatternBeyondTrailingPrefix,
515 LowerFieldPredicateUnsupported,
516 MultiStatementSql,
517 NestedAggregateInput,
518 NestedProjectionFunctionInArithmetic,
519 OrderByUnsupportedForm,
520 Other,
521 ParameterBinding,
522 ParameterizedSchemaVersion,
523 PredicateStartsWithFirstArgument,
524 QuotedIdentifiers,
525 ReturningUnsupportedShape,
526 ScalarFunctionExpressionPosition,
527 ScaleTakingNumericFunctionExpressionPosition,
528 SearchedCaseGroupedOrderBy,
529 ShowColumnsModifiers,
530 ShowEntitiesModifiers,
531 ShowIndexesModifiers,
532 ShowMemoryModifiers,
533 ShowStoresModifiers,
534 ShowUnsupportedCommand,
535 SimpleCaseExpression,
536 StandaloneLiteralProjectionItem,
537 SupportedGroupedOrderByExpressionFamily,
538 SupportedOrderByExpressionFamily,
539 UnionIntersectExcept,
540 UnsupportedFunctionNamespace,
541 Update,
542 UpperFieldPredicateUnsupported,
543 WindowFunction,
544 With,
545 NumericScaleFunctionArguments,
546 OrderByFieldNotOrderable,
547}
548
549impl fmt::Debug for SqlFeatureCode {
550 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
551 fmt_compact_code(f, *self as u16)
552 }
553}
554
555#[repr(u16)]
564#[derive(Clone, Copy, Eq, Hash, PartialEq)]
565pub enum SqlLoweringCode {
566 EntityMismatch,
567 SelectProjectionShape,
568 SelectDistinct,
569 DistinctOrderByProjection,
570 GlobalAggregateProjection,
571 GlobalAggregateGroupBy,
572 SelectGroupByShape,
573 GroupedProjectionExplicitListRequired,
574 GroupedProjectionAggregateRequired,
575 GroupedProjectionNonGroupField,
576 GroupedProjectionScalarAfterAggregate,
577 HavingRequiresGroupBy,
578 SelectHavingShape,
579 AggregateInputExpressions,
580 WhereExpressionShape,
581 ParameterPlacement,
582 SqlDdlExecutionUnsupported,
583}
584
585impl fmt::Debug for SqlLoweringCode {
586 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
587 fmt_compact_code(f, *self as u16)
588 }
589}
590
591#[repr(u16)]
599#[derive(Clone, Copy, Eq, Hash, PartialEq)]
600pub enum SqlSurfaceMismatchCode {
601 QueryRejectsInsert,
602 QueryRejectsUpdate,
603 QueryRejectsDelete,
604 UpdateRejectsSelect,
605 UpdateRejectsExplain,
606 UpdateRejectsDescribe,
607 UpdateRejectsShowIndexes,
608 UpdateRejectsShowColumns,
609 UpdateRejectsShowEntities,
610 UpdateRejectsShowStores,
611 UpdateRejectsShowMemory,
612}
613
614impl fmt::Debug for SqlSurfaceMismatchCode {
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 SqlWriteBoundaryCode {
630 PrimaryKeyLiteralShape,
631 PrimaryKeyLiteralIncompatible,
632 MissingPrimaryKey,
633 MissingRequiredFields,
634 ExplicitManagedField,
635 ExplicitGeneratedField,
636 InsertSelectRequiresScalar,
637 InsertSelectAggregateProjection,
638 InsertSelectWidthMismatch,
639 UpdatePrimaryKeyMutation,
640 InvalidFieldLiteral,
641 UnknownReturningField,
642 DuplicateReturningField,
643 UpdateMissingWherePredicate,
644 WriteOrderByUnsupportedShape,
645 ReturningResponseTooLarge,
646 ReturningRowsTooMany,
647}
648
649impl fmt::Debug for SqlWriteBoundaryCode {
650 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
651 fmt_compact_code(f, *self as u16)
652 }
653}
654
655#[repr(u16)]
663#[derive(Clone, Copy, Eq, Hash, PartialEq)]
664pub enum SchemaDdlAdmissionCode {
665 MissingExpectedSchemaVersion,
666 MissingNextSchemaVersion,
667 StaleExpectedSchemaVersion,
668 InvalidExpectedSchemaVersion,
669 InvalidNextSchemaVersion,
670 AcceptedSchemaChangeWithoutVersionBump,
671 EmptyVersionBump,
672 VersionGap,
673 VersionRollback,
674 FingerprintMethodMismatch,
675 UnsupportedTransitionClass,
676 PhysicalRunnerMissing,
677 ValidationFailed,
678 PublicationRaceLost,
679 InvalidAddColumnDefault,
680 InvalidAlterColumnDefault,
681 GeneratedIndexDropRejected,
682 RequiredDropDefaultUnsupported,
683 GeneratedFieldDefaultChangeRejected,
684 GeneratedFieldNullabilityChangeRejected,
685 SetNotNullValidationFailed,
686}
687
688impl fmt::Debug for SchemaDdlAdmissionCode {
689 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
690 fmt_compact_code(f, *self as u16)
691 }
692}
693
694#[remain::sorted]
701#[derive(Clone, Copy, Eq, PartialEq)]
702pub enum DiagnosticDetail {
703 QueryKind { kind: QueryErrorKind },
704 QueryProjection { reason: QueryProjectionCode },
705 QueryResultShape { reason: QueryResultShapeCode },
706 RuntimeBoundary { boundary: RuntimeBoundaryCode },
707 RuntimeKind { kind: RuntimeErrorKind },
708 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
709 SqlLowering { reason: SqlLoweringCode },
710 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
711 SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
712 UnsupportedSqlFeature { feature: SqlFeatureCode },
713}
714
715impl fmt::Debug for DiagnosticDetail {
716 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
717 fmt_compact_code(
718 f,
719 ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
720 )
721 }
722}
723
724#[derive(Clone, Eq, PartialEq)]
731pub struct Diagnostic {
732 code: DiagnosticCode,
733 origin: ErrorOrigin,
734 detail: Option<DiagnosticDetail>,
735}
736
737impl Diagnostic {
738 #[must_use]
740 pub const fn new(
741 code: DiagnosticCode,
742 origin: ErrorOrigin,
743 detail: Option<DiagnosticDetail>,
744 ) -> Self {
745 Self {
746 code,
747 origin,
748 detail,
749 }
750 }
751
752 #[must_use]
754 pub const fn from_code(code: DiagnosticCode) -> Self {
755 Self::new(code, code.origin(), None)
756 }
757
758 #[must_use]
760 pub const fn code(&self) -> DiagnosticCode {
761 self.code
762 }
763
764 #[must_use]
766 pub const fn class(&self) -> ErrorClass {
767 self.code.class()
768 }
769
770 #[must_use]
772 pub const fn origin(&self) -> ErrorOrigin {
773 self.origin
774 }
775
776 #[must_use]
778 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
779 self.detail.as_ref()
780 }
781
782 #[must_use]
784 pub const fn error_code(&self) -> ErrorCode {
785 ErrorCode::from_parts(self.code, self.detail)
786 }
787}
788
789impl fmt::Debug for Diagnostic {
790 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
791 write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
792 }
793}
794
795fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
796 write!(f, "{raw}")
797}
798
799#[cfg(test)]
800mod tests {
801 use super::{
802 Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
803 QueryProjectionCode, SqlFeatureCode, SqlLoweringCode, SqlWriteBoundaryCode,
804 registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
805 };
806
807 #[test]
808 fn diagnostic_from_code_uses_default_origin() {
809 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
810
811 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
812 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
813 }
814
815 #[test]
816 fn diagnostic_code_reports_broad_class() {
817 assert_eq!(
818 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
819 ErrorClass::Unsupported
820 );
821 assert_eq!(
822 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
823 ErrorClass::Unsupported
824 );
825 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
826 assert_eq!(
827 DiagnosticCode::StoreCorruption.class(),
828 ErrorClass::Corruption
829 );
830 }
831
832 #[test]
833 fn class_and_origin_wire_codes_round_trip() {
834 for (class, raw) in [
835 (ErrorClass::Query, 1),
836 (ErrorClass::Corruption, 2),
837 (ErrorClass::IncompatiblePersistedFormat, 3),
838 (ErrorClass::NotFound, 4),
839 (ErrorClass::Internal, 5),
840 (ErrorClass::Conflict, 6),
841 (ErrorClass::Unsupported, 7),
842 (ErrorClass::InvariantViolation, 8),
843 ] {
844 assert_eq!(class.wire_code(), raw);
845 assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
846 assert_eq!(format!("{class:?}"), raw.to_string());
847 }
848
849 for (origin, raw) in [
850 (ErrorOrigin::Cursor, 1),
851 (ErrorOrigin::Executor, 2),
852 (ErrorOrigin::Identity, 3),
853 (ErrorOrigin::Index, 4),
854 (ErrorOrigin::Interface, 5),
855 (ErrorOrigin::Planner, 6),
856 (ErrorOrigin::Query, 7),
857 (ErrorOrigin::Recovery, 8),
858 (ErrorOrigin::Response, 9),
859 (ErrorOrigin::Runtime, 10),
860 (ErrorOrigin::Serialize, 11),
861 (ErrorOrigin::Store, 12),
862 ] {
863 assert_eq!(origin.wire_code(), raw);
864 assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
865 assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
866 assert_eq!(format!("{origin:?}"), raw.to_string());
867 }
868
869 assert_eq!(ErrorClass::from_wire_code(0), None);
870 assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
871 assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
872 }
873
874 #[test]
875 fn public_error_codes_are_sequential() {
876 let first = ORDERED_ERROR_CODES
877 .first()
878 .expect("public error-code registry is non-empty")
879 .raw();
880
881 assert_eq!(first, 1);
882
883 for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
884 let expected = first + u16::try_from(index).expect("test error-code index fits u16");
885 assert_eq!(code.raw(), expected);
886 assert_eq!(ErrorCode::known(code.raw()), Some(*code));
887 assert!(code.is_known());
888 }
889
890 let last = ORDERED_ERROR_CODES
891 .last()
892 .expect("public error-code registry is non-empty")
893 .raw();
894
895 assert_eq!(last, 185);
896 }
897
898 #[test]
899 fn all_public_error_codes_round_trip_through_diagnostic_parts() {
900 let first = ORDERED_ERROR_CODES
901 .first()
902 .expect("public error-code registry is non-empty")
903 .raw();
904 let last = ORDERED_ERROR_CODES
905 .last()
906 .expect("public error-code registry is non-empty")
907 .raw();
908
909 for raw in first..=last {
910 let code = ErrorCode::from_raw(raw);
911 let diagnostic_code = code.diagnostic_code();
912 let diagnostic_detail = code.diagnostic_detail();
913 let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
914
915 assert_eq!(rebuilt.raw(), raw);
916
917 let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
918
919 assert_eq!(diagnostic.code(), diagnostic_code);
920 assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
921 assert_eq!(diagnostic.error_code().raw(), raw);
922 }
923 }
924
925 #[test]
926 fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
927 for raw in [0, 186, u16::MAX] {
928 let code = ErrorCode::from_raw(raw);
929
930 assert_eq!(ErrorCode::known(raw), None);
931 assert!(!code.is_known());
932 assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
933 assert_eq!(code.diagnostic_detail(), None);
934 assert_eq!(code.class(), ErrorClass::Internal);
935
936 let diagnostic = code.diagnostic(ErrorOrigin::Query);
937
938 assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
939 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
940 assert_eq!(diagnostic.detail(), None);
941 assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
942 }
943 }
944
945 #[test]
946 fn from_parts_requires_detail_to_match_broad_code() {
947 let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
948 feature: SqlFeatureCode::Join,
949 });
950
951 assert_eq!(
952 ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
953 ErrorCode::SQL_FEATURE_JOIN
954 );
955 assert_eq!(
956 ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
957 ErrorCode::QUERY_PLAN
958 );
959 }
960
961 #[test]
962 fn detail_bearing_registry_entries_round_trip_directly() {
963 assert!(!DETAIL_ERROR_CODES.is_empty());
964
965 for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
966 assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
967 assert_eq!(code.diagnostic_code(), diagnostic_code);
968 assert_eq!(code.diagnostic_detail(), Some(detail));
969 assert_eq!(detail.diagnostic_code(), diagnostic_code);
970 }
971 }
972
973 #[test]
974 fn diagnostic_detail_reports_generated_broad_code() {
975 let detail = DiagnosticDetail::UnsupportedSqlFeature {
976 feature: SqlFeatureCode::Join,
977 };
978
979 assert_eq!(
980 detail.diagnostic_code(),
981 DiagnosticCode::QueryUnsupportedSqlFeature
982 );
983 assert_eq!(format!("{detail:?}"), "69");
984 }
985
986 #[test]
987 fn public_error_codes_reconstruct_shifted_details() {
988 assert_eq!(
989 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
990 DiagnosticCode::QueryUnknownAggregateTargetField
991 );
992 assert_eq!(
993 ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
994 Some(DiagnosticDetail::UnsupportedSqlFeature {
995 feature: SqlFeatureCode::Join,
996 })
997 );
998 assert_eq!(
999 ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1000 Some(DiagnosticDetail::QueryProjection {
1001 reason: QueryProjectionCode::NumericLiteralRequired,
1002 })
1003 );
1004 assert_eq!(
1005 ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1006 Some(DiagnosticDetail::SqlLowering {
1007 reason: SqlLoweringCode::DistinctOrderByProjection,
1008 })
1009 );
1010 assert_eq!(
1011 ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1012 Some(DiagnosticDetail::SqlWriteBoundary {
1013 boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1014 })
1015 );
1016 assert_eq!(
1017 ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1018 Some(DiagnosticDetail::SqlWriteBoundary {
1019 boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1020 })
1021 );
1022 }
1023}