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