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 StagedRowsTooMany,
648}
649
650impl fmt::Debug for SqlWriteBoundaryCode {
651 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
652 fmt_compact_code(f, *self as u16)
653 }
654}
655
656#[repr(u16)]
664#[derive(Clone, Copy, Eq, Hash, PartialEq)]
665pub enum SchemaDdlAdmissionCode {
666 MissingExpectedSchemaVersion,
667 MissingNextSchemaVersion,
668 StaleExpectedSchemaVersion,
669 InvalidExpectedSchemaVersion,
670 InvalidNextSchemaVersion,
671 AcceptedSchemaChangeWithoutVersionBump,
672 EmptyVersionBump,
673 VersionGap,
674 VersionRollback,
675 FingerprintMethodMismatch,
676 UnsupportedTransitionClass,
677 PhysicalRunnerMissing,
678 ValidationFailed,
679 PublicationRaceLost,
680 InvalidAddColumnDefault,
681 InvalidAlterColumnDefault,
682 GeneratedIndexDropRejected,
683 RequiredDropDefaultUnsupported,
684 GeneratedFieldDefaultChangeRejected,
685 GeneratedFieldNullabilityChangeRejected,
686 SetNotNullValidationFailed,
687}
688
689impl fmt::Debug for SchemaDdlAdmissionCode {
690 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
691 fmt_compact_code(f, *self as u16)
692 }
693}
694
695#[remain::sorted]
702#[derive(Clone, Copy, Eq, PartialEq)]
703pub enum DiagnosticDetail {
704 QueryKind { kind: QueryErrorKind },
705 QueryProjection { reason: QueryProjectionCode },
706 QueryResultShape { reason: QueryResultShapeCode },
707 RuntimeBoundary { boundary: RuntimeBoundaryCode },
708 RuntimeKind { kind: RuntimeErrorKind },
709 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
710 SqlLowering { reason: SqlLoweringCode },
711 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
712 SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
713 UnsupportedSqlFeature { feature: SqlFeatureCode },
714}
715
716impl fmt::Debug for DiagnosticDetail {
717 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
718 fmt_compact_code(
719 f,
720 ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
721 )
722 }
723}
724
725#[derive(Clone, Eq, PartialEq)]
732pub struct Diagnostic {
733 code: DiagnosticCode,
734 origin: ErrorOrigin,
735 detail: Option<DiagnosticDetail>,
736}
737
738impl Diagnostic {
739 #[must_use]
741 pub const fn new(
742 code: DiagnosticCode,
743 origin: ErrorOrigin,
744 detail: Option<DiagnosticDetail>,
745 ) -> Self {
746 Self {
747 code,
748 origin,
749 detail,
750 }
751 }
752
753 #[must_use]
755 pub const fn from_code(code: DiagnosticCode) -> Self {
756 Self::new(code, code.origin(), None)
757 }
758
759 #[must_use]
761 pub const fn code(&self) -> DiagnosticCode {
762 self.code
763 }
764
765 #[must_use]
767 pub const fn class(&self) -> ErrorClass {
768 self.code.class()
769 }
770
771 #[must_use]
773 pub const fn origin(&self) -> ErrorOrigin {
774 self.origin
775 }
776
777 #[must_use]
779 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
780 self.detail.as_ref()
781 }
782
783 #[must_use]
785 pub const fn error_code(&self) -> ErrorCode {
786 ErrorCode::from_parts(self.code, self.detail)
787 }
788}
789
790impl fmt::Debug for Diagnostic {
791 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
792 write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
793 }
794}
795
796fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
797 write!(f, "{raw}")
798}
799
800#[cfg(test)]
801mod tests {
802 use super::{
803 Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
804 QueryProjectionCode, SqlFeatureCode, SqlLoweringCode, SqlWriteBoundaryCode,
805 registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
806 };
807
808 #[test]
809 fn diagnostic_from_code_uses_default_origin() {
810 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
811
812 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
813 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
814 }
815
816 #[test]
817 fn diagnostic_code_reports_broad_class() {
818 assert_eq!(
819 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
820 ErrorClass::Unsupported
821 );
822 assert_eq!(
823 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
824 ErrorClass::Unsupported
825 );
826 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
827 assert_eq!(
828 DiagnosticCode::StoreCorruption.class(),
829 ErrorClass::Corruption
830 );
831 }
832
833 #[test]
834 fn class_and_origin_wire_codes_round_trip() {
835 for (class, raw) in [
836 (ErrorClass::Query, 1),
837 (ErrorClass::Corruption, 2),
838 (ErrorClass::IncompatiblePersistedFormat, 3),
839 (ErrorClass::NotFound, 4),
840 (ErrorClass::Internal, 5),
841 (ErrorClass::Conflict, 6),
842 (ErrorClass::Unsupported, 7),
843 (ErrorClass::InvariantViolation, 8),
844 ] {
845 assert_eq!(class.wire_code(), raw);
846 assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
847 assert_eq!(format!("{class:?}"), raw.to_string());
848 }
849
850 for (origin, raw) in [
851 (ErrorOrigin::Cursor, 1),
852 (ErrorOrigin::Executor, 2),
853 (ErrorOrigin::Identity, 3),
854 (ErrorOrigin::Index, 4),
855 (ErrorOrigin::Interface, 5),
856 (ErrorOrigin::Planner, 6),
857 (ErrorOrigin::Query, 7),
858 (ErrorOrigin::Recovery, 8),
859 (ErrorOrigin::Response, 9),
860 (ErrorOrigin::Runtime, 10),
861 (ErrorOrigin::Serialize, 11),
862 (ErrorOrigin::Store, 12),
863 ] {
864 assert_eq!(origin.wire_code(), raw);
865 assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
866 assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
867 assert_eq!(format!("{origin:?}"), raw.to_string());
868 }
869
870 assert_eq!(ErrorClass::from_wire_code(0), None);
871 assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
872 assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
873 }
874
875 #[test]
876 fn public_error_codes_are_sequential() {
877 let first = ORDERED_ERROR_CODES
878 .first()
879 .expect("public error-code registry is non-empty")
880 .raw();
881
882 assert_eq!(first, 1);
883
884 for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
885 let expected = first + u16::try_from(index).expect("test error-code index fits u16");
886 assert_eq!(code.raw(), expected);
887 assert_eq!(ErrorCode::known(code.raw()), Some(*code));
888 assert!(code.is_known());
889 }
890
891 let last = ORDERED_ERROR_CODES
892 .last()
893 .expect("public error-code registry is non-empty")
894 .raw();
895
896 assert_eq!(last, 186);
897 }
898
899 #[test]
900 fn all_public_error_codes_round_trip_through_diagnostic_parts() {
901 let first = ORDERED_ERROR_CODES
902 .first()
903 .expect("public error-code registry is non-empty")
904 .raw();
905 let last = ORDERED_ERROR_CODES
906 .last()
907 .expect("public error-code registry is non-empty")
908 .raw();
909
910 for raw in first..=last {
911 let code = ErrorCode::from_raw(raw);
912 let diagnostic_code = code.diagnostic_code();
913 let diagnostic_detail = code.diagnostic_detail();
914 let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
915
916 assert_eq!(rebuilt.raw(), raw);
917
918 let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
919
920 assert_eq!(diagnostic.code(), diagnostic_code);
921 assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
922 assert_eq!(diagnostic.error_code().raw(), raw);
923 }
924 }
925
926 #[test]
927 fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
928 for raw in [0, 187, u16::MAX] {
929 let code = ErrorCode::from_raw(raw);
930
931 assert_eq!(ErrorCode::known(raw), None);
932 assert!(!code.is_known());
933 assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
934 assert_eq!(code.diagnostic_detail(), None);
935 assert_eq!(code.class(), ErrorClass::Internal);
936
937 let diagnostic = code.diagnostic(ErrorOrigin::Query);
938
939 assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
940 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
941 assert_eq!(diagnostic.detail(), None);
942 assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
943 }
944 }
945
946 #[test]
947 fn from_parts_requires_detail_to_match_broad_code() {
948 let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
949 feature: SqlFeatureCode::Join,
950 });
951
952 assert_eq!(
953 ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
954 ErrorCode::SQL_FEATURE_JOIN
955 );
956 assert_eq!(
957 ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
958 ErrorCode::QUERY_PLAN
959 );
960 }
961
962 #[test]
963 fn detail_bearing_registry_entries_round_trip_directly() {
964 assert!(!DETAIL_ERROR_CODES.is_empty());
965
966 for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
967 assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
968 assert_eq!(code.diagnostic_code(), diagnostic_code);
969 assert_eq!(code.diagnostic_detail(), Some(detail));
970 assert_eq!(detail.diagnostic_code(), diagnostic_code);
971 }
972 }
973
974 #[test]
975 fn diagnostic_detail_reports_generated_broad_code() {
976 let detail = DiagnosticDetail::UnsupportedSqlFeature {
977 feature: SqlFeatureCode::Join,
978 };
979
980 assert_eq!(
981 detail.diagnostic_code(),
982 DiagnosticCode::QueryUnsupportedSqlFeature
983 );
984 assert_eq!(format!("{detail:?}"), "69");
985 }
986
987 #[test]
988 fn public_error_codes_reconstruct_shifted_details() {
989 assert_eq!(
990 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
991 DiagnosticCode::QueryUnknownAggregateTargetField
992 );
993 assert_eq!(
994 ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
995 Some(DiagnosticDetail::UnsupportedSqlFeature {
996 feature: SqlFeatureCode::Join,
997 })
998 );
999 assert_eq!(
1000 ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1001 Some(DiagnosticDetail::QueryProjection {
1002 reason: QueryProjectionCode::NumericLiteralRequired,
1003 })
1004 );
1005 assert_eq!(
1006 ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1007 Some(DiagnosticDetail::SqlLowering {
1008 reason: SqlLoweringCode::DistinctOrderByProjection,
1009 })
1010 );
1011 assert_eq!(
1012 ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1013 Some(DiagnosticDetail::SqlWriteBoundary {
1014 boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1015 })
1016 );
1017 assert_eq!(
1018 ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1019 Some(DiagnosticDetail::SqlWriteBoundary {
1020 boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1021 })
1022 );
1023 }
1024}