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 ResumableUpdateWindowUnsupported,
691 ResumableUpdateReturningUnsupported,
692 ResumableUpdateRequiresJournaledStore,
693 ResumableUpdateAssignedFieldHasGlobalConstraint,
694 ResumableUpdateScopeDependsOnAssignedField,
695 ResumableUpdateScopeDependencyUnknown,
696 ResumableUpdateContinuationMalformed,
697 ResumableUpdateContinuationTargetMismatch,
698 ResumableUpdateContinuationSchemaMismatch,
699 ResumableUpdateContinuationScopeMismatch,
700 ResumableUpdateContinuationPatchMismatch,
701 ResumableUpdateContinuationBatchPolicyMismatch,
702 ResumableUpdateSingleRowResourceExceeded,
703 ResumableUpdateApplicationCallbacksUnsupported,
704 ResumableUpdateManagedFieldHasGlobalConstraint,
705}
706
707impl fmt::Debug for SqlWriteBoundaryCode {
708 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
709 fmt_compact_code(f, *self as u16)
710 }
711}
712
713#[repr(u16)]
721#[derive(Clone, Copy, Eq, Hash, PartialEq)]
722pub enum SchemaDdlAdmissionCode {
723 MissingExpectedSchemaVersion,
724 MissingNextSchemaVersion,
725 StaleExpectedSchemaVersion,
726 InvalidExpectedSchemaVersion,
727 InvalidNextSchemaVersion,
728 AcceptedSchemaChangeWithoutVersionBump,
729 EmptyVersionBump,
730 VersionGap,
731 VersionRollback,
732 FingerprintMethodMismatch,
733 UnsupportedTransitionClass,
734 PhysicalRunnerMissing,
735 ValidationFailed,
736 PublicationRaceLost,
737 InvalidAddColumnDefault,
738 InvalidAlterColumnDefault,
739 GeneratedIndexDropRejected,
740 SchemaRewriteRequiresMigration,
741 SchemaTransitionBudgetExceeded,
742 GeneratedFieldDefaultChangeRejected,
743 GeneratedFieldNullabilityChangeRejected,
744 SetNotNullValidationFailed,
745 RowLayoutVersionExhausted,
746}
747
748impl fmt::Debug for SchemaDdlAdmissionCode {
749 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
750 fmt_compact_code(f, *self as u16)
751 }
752}
753
754#[remain::sorted]
761#[derive(Clone, Copy, Eq, PartialEq)]
762pub enum DiagnosticDetail {
763 QueryKind { kind: QueryErrorKind },
764 QueryProjection { reason: QueryProjectionCode },
765 QueryReadAdmission { reason: QueryReadAdmissionCode },
766 QueryResultShape { reason: QueryResultShapeCode },
767 RuntimeBoundary { boundary: RuntimeBoundaryCode },
768 RuntimeKind { kind: RuntimeErrorKind },
769 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
770 SqlLowering { reason: SqlLoweringCode },
771 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
772 SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
773 UnsupportedSqlFeature { feature: SqlFeatureCode },
774}
775
776impl fmt::Debug for DiagnosticDetail {
777 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
778 fmt_compact_code(
779 f,
780 ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
781 )
782 }
783}
784
785#[derive(Clone, Eq, PartialEq)]
792pub struct Diagnostic {
793 code: DiagnosticCode,
794 origin: ErrorOrigin,
795 detail: Option<DiagnosticDetail>,
796}
797
798impl Diagnostic {
799 #[must_use]
801 pub const fn new(
802 code: DiagnosticCode,
803 origin: ErrorOrigin,
804 detail: Option<DiagnosticDetail>,
805 ) -> Self {
806 Self {
807 code,
808 origin,
809 detail,
810 }
811 }
812
813 #[must_use]
815 pub const fn from_code(code: DiagnosticCode) -> Self {
816 Self::new(code, code.origin(), None)
817 }
818
819 #[must_use]
821 pub const fn code(&self) -> DiagnosticCode {
822 self.code
823 }
824
825 #[must_use]
827 pub const fn class(&self) -> ErrorClass {
828 self.code.class()
829 }
830
831 #[must_use]
833 pub const fn origin(&self) -> ErrorOrigin {
834 self.origin
835 }
836
837 #[must_use]
839 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
840 self.detail.as_ref()
841 }
842
843 #[must_use]
845 pub const fn error_code(&self) -> ErrorCode {
846 ErrorCode::from_parts(self.code, self.detail)
847 }
848}
849
850impl fmt::Debug for Diagnostic {
851 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
852 write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
853 }
854}
855
856fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
857 write!(f, "{raw}")
858}
859
860#[cfg(test)]
861mod tests {
862 use super::{
863 Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
864 QueryProjectionCode, QueryReadAdmissionCode, SqlFeatureCode, SqlLoweringCode,
865 SqlWriteBoundaryCode,
866 registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
867 };
868
869 #[test]
870 fn diagnostic_from_code_uses_default_origin() {
871 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
872
873 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
874 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
875 }
876
877 #[test]
878 fn diagnostic_code_reports_broad_class() {
879 assert_eq!(
880 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
881 ErrorClass::Unsupported
882 );
883 assert_eq!(
884 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
885 ErrorClass::Unsupported
886 );
887 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
888 assert_eq!(
889 DiagnosticCode::StoreCorruption.class(),
890 ErrorClass::Corruption
891 );
892 }
893
894 #[test]
895 fn class_and_origin_wire_codes_round_trip() {
896 for (class, raw) in [
897 (ErrorClass::Query, 1),
898 (ErrorClass::Corruption, 2),
899 (ErrorClass::IncompatiblePersistedFormat, 3),
900 (ErrorClass::NotFound, 4),
901 (ErrorClass::Internal, 5),
902 (ErrorClass::Conflict, 6),
903 (ErrorClass::Unsupported, 7),
904 (ErrorClass::InvariantViolation, 8),
905 ] {
906 assert_eq!(class.wire_code(), raw);
907 assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
908 assert_eq!(format!("{class:?}"), raw.to_string());
909 }
910
911 for (origin, raw) in [
912 (ErrorOrigin::Cursor, 1),
913 (ErrorOrigin::Executor, 2),
914 (ErrorOrigin::Identity, 3),
915 (ErrorOrigin::Index, 4),
916 (ErrorOrigin::Interface, 5),
917 (ErrorOrigin::Planner, 6),
918 (ErrorOrigin::Query, 7),
919 (ErrorOrigin::Recovery, 8),
920 (ErrorOrigin::Response, 9),
921 (ErrorOrigin::Runtime, 10),
922 (ErrorOrigin::Serialize, 11),
923 (ErrorOrigin::Store, 12),
924 ] {
925 assert_eq!(origin.wire_code(), raw);
926 assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
927 assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
928 assert_eq!(format!("{origin:?}"), raw.to_string());
929 }
930
931 assert_eq!(ErrorClass::from_wire_code(0), None);
932 assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
933 assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
934 }
935
936 #[test]
937 fn public_error_codes_are_sequential() {
938 let first = ORDERED_ERROR_CODES
939 .first()
940 .expect("public error-code registry is non-empty")
941 .raw();
942
943 assert_eq!(first, 1);
944
945 for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
946 let expected = first + u16::try_from(index).expect("test error-code index fits u16");
947 assert_eq!(code.raw(), expected);
948 assert_eq!(ErrorCode::known(code.raw()), Some(*code));
949 assert!(code.is_known());
950 }
951
952 let last = ORDERED_ERROR_CODES
953 .last()
954 .expect("public error-code registry is non-empty")
955 .raw();
956
957 assert_eq!(last, 220);
958 }
959
960 #[test]
961 fn all_public_error_codes_round_trip_through_diagnostic_parts() {
962 let first = ORDERED_ERROR_CODES
963 .first()
964 .expect("public error-code registry is non-empty")
965 .raw();
966 let last = ORDERED_ERROR_CODES
967 .last()
968 .expect("public error-code registry is non-empty")
969 .raw();
970
971 for raw in first..=last {
972 let code = ErrorCode::from_raw(raw);
973 let diagnostic_code = code.diagnostic_code();
974 let diagnostic_detail = code.diagnostic_detail();
975 let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
976
977 assert_eq!(rebuilt.raw(), raw);
978
979 let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
980
981 assert_eq!(diagnostic.code(), diagnostic_code);
982 assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
983 assert_eq!(diagnostic.error_code().raw(), raw);
984 }
985 }
986
987 #[test]
988 fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
989 for raw in [0, 221, u16::MAX] {
990 let code = ErrorCode::from_raw(raw);
991
992 assert_eq!(ErrorCode::known(raw), None);
993 assert!(!code.is_known());
994 assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
995 assert_eq!(code.diagnostic_detail(), None);
996 assert_eq!(code.class(), ErrorClass::Internal);
997
998 let diagnostic = code.diagnostic(ErrorOrigin::Query);
999
1000 assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
1001 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
1002 assert_eq!(diagnostic.detail(), None);
1003 assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
1004 }
1005 }
1006
1007 #[test]
1008 fn from_parts_requires_detail_to_match_broad_code() {
1009 let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
1010 feature: SqlFeatureCode::Join,
1011 });
1012
1013 assert_eq!(
1014 ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
1015 ErrorCode::SQL_FEATURE_JOIN
1016 );
1017 assert_eq!(
1018 ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
1019 ErrorCode::QUERY_PLAN
1020 );
1021 }
1022
1023 #[test]
1024 fn detail_bearing_registry_entries_round_trip_directly() {
1025 assert!(!DETAIL_ERROR_CODES.is_empty());
1026
1027 for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
1028 assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
1029 assert_eq!(code.diagnostic_code(), diagnostic_code);
1030 assert_eq!(code.diagnostic_detail(), Some(detail));
1031 assert_eq!(detail.diagnostic_code(), diagnostic_code);
1032 }
1033 }
1034
1035 #[test]
1036 fn diagnostic_detail_reports_generated_broad_code() {
1037 let detail = DiagnosticDetail::UnsupportedSqlFeature {
1038 feature: SqlFeatureCode::Join,
1039 };
1040
1041 assert_eq!(
1042 detail.diagnostic_code(),
1043 DiagnosticCode::QueryUnsupportedSqlFeature
1044 );
1045 assert_eq!(format!("{detail:?}"), "65");
1046 }
1047
1048 #[test]
1049 fn public_error_codes_reconstruct_shifted_details() {
1050 assert_eq!(
1051 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
1052 DiagnosticCode::QueryUnknownAggregateTargetField
1053 );
1054 assert_eq!(
1055 ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
1056 Some(DiagnosticDetail::UnsupportedSqlFeature {
1057 feature: SqlFeatureCode::Join,
1058 })
1059 );
1060 assert_eq!(
1061 ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1062 Some(DiagnosticDetail::QueryProjection {
1063 reason: QueryProjectionCode::NumericLiteralRequired,
1064 })
1065 );
1066 assert_eq!(
1067 ErrorCode::QUERY_READ_PUBLIC_REQUIRES_LIMIT.diagnostic_detail(),
1068 Some(DiagnosticDetail::QueryReadAdmission {
1069 reason: QueryReadAdmissionCode::PublicQueryRequiresLimit,
1070 })
1071 );
1072 assert_eq!(
1073 ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1074 Some(DiagnosticDetail::SqlLowering {
1075 reason: SqlLoweringCode::DistinctOrderByProjection,
1076 })
1077 );
1078 assert_eq!(
1079 ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1080 Some(DiagnosticDetail::SqlWriteBoundary {
1081 boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1082 })
1083 );
1084 assert_eq!(
1085 ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1086 Some(DiagnosticDetail::SqlWriteBoundary {
1087 boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1088 })
1089 );
1090 }
1091}