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 MutationManagedTimestampRegression,
495 PersistedRowLayoutOutsideAcceptedWindow,
497 PersistedRowSlotCountMismatch,
499 GeneratedFieldAfterDdlField,
501 JournalMutationRevisionExhausted,
503 ConstraintViolation,
505 AcceptedRowConstraintProgramCorrupt,
507 ConstraintActivationWriteBlocked,
509 GeneratedConstraintActivationStale,
511 MutationDatabaseOwnedFieldExplicit,
513 MutationBatchEmpty,
515 MutationBatchTooManyItems,
517 MutationBatchStagedBytesExceeded,
519 MutationBatchResultBytesExceeded,
521 MutationBatchEntityMismatch,
523 MutationBatchDuplicateKey,
525}
526
527impl fmt::Debug for RuntimeBoundaryCode {
528 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
529 fmt_compact_code(f, *self as u16)
530 }
531}
532
533#[repr(u16)]
541#[derive(Clone, Copy, Eq, Hash, PartialEq)]
542pub enum SqlFeatureCode {
543 AggregateFilterClause,
544 AlterStatementBeyondAlterTable,
545 AlterTableAddColumnDuplicateDefault,
546 AlterTableAddColumnModifiers,
547 AlterTableAddStatementBeyondAddColumn,
548 AlterTableAlterColumnDropUnsupportedAction,
549 AlterTableAlterColumnModifiers,
550 AlterTableAlterColumnSetUnsupportedAction,
551 AlterTableAlterColumnUnsupportedAction,
552 AlterTableAlterStatementBeyondAlterColumn,
553 AlterTableDropColumnIfExistsSyntax,
554 AlterTableDropColumnModifiers,
555 AlterTableDropStatementBeyondDropColumn,
556 AlterTableRenameColumnMissingTo,
557 AlterTableRenameColumnModifiers,
558 AlterTableRenameStatementBeyondRenameColumn,
559 AlterTableUnsupportedOperation,
560 ColumnAlias,
561 CreateIndexIfNotExistsSyntax,
562 CreateIndexKeyOrderingModifiers,
563 CreateIndexModifiers,
564 CreateStatementBeyondCreateIndex,
565 DescribeModifier,
566 DdlSchemaVersionDuplicateExpectedClause,
567 DdlSchemaVersionDuplicateSetClause,
568 DropIndexModifiers,
569 DropIndexIfExistsSyntax,
570 DropStatementBeyondDropIndex,
571 ExpressionIndexUnsupportedFunction,
572 Having,
573 Insert,
574 Join,
575 LikePatternBeyondTrailingPrefix,
576 LowerFieldPredicateUnsupported,
577 MultiStatementSql,
578 NestedAggregateInput,
579 NestedProjectionFunctionInArithmetic,
580 OrderByUnsupportedForm,
581 Other,
582 PredicateStartsWithFirstArgument,
583 QuotedIdentifiers,
584 ReturningUnsupportedShape,
585 ScalarFunctionExpressionPosition,
586 ScaleTakingNumericFunctionExpressionPosition,
587 SearchedCaseGroupedOrderBy,
588 ShowColumnsModifiers,
589 ShowEntitiesModifiers,
590 ShowIndexesModifiers,
591 ShowMemoryModifiers,
592 ShowStoresModifiers,
593 ShowUnsupportedCommand,
594 SimpleCaseExpression,
595 StandaloneLiteralProjectionItem,
596 SupportedGroupedOrderByExpressionFamily,
597 SupportedOrderByExpressionFamily,
598 UnionIntersectExcept,
599 UnsupportedFunctionNamespace,
600 Update,
601 UpperFieldPredicateUnsupported,
602 WindowFunction,
603 With,
604 NumericScaleFunctionArguments,
605 OrderByFieldNotOrderable,
606 ShowConstraintsModifiers,
607 AlterTableAddConstraintBeyondCheck,
608 AlterTableAddConstraintModifiers,
609 AlterTableDropConstraintIfExistsSyntax,
610 AlterTableDropConstraintModifiers,
611 AlterTableValidateBeyondConstraint,
612 AlterTableValidateConstraintModifiers,
613}
614
615impl fmt::Debug for SqlFeatureCode {
616 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
617 fmt_compact_code(f, *self as u16)
618 }
619}
620
621#[repr(u16)]
630#[derive(Clone, Copy, Eq, Hash, PartialEq)]
631pub enum SqlLoweringCode {
632 EntityMismatch,
633 SelectProjectionShape,
634 SelectDistinct,
635 DistinctOrderByProjection,
636 GlobalAggregateProjection,
637 GlobalAggregateGroupBy,
638 SelectGroupByShape,
639 GroupedProjectionExplicitListRequired,
640 GroupedProjectionAggregateRequired,
641 GroupedProjectionNonGroupField,
642 GroupedProjectionScalarAfterAggregate,
643 HavingRequiresGroupBy,
644 SelectHavingShape,
645 AggregateInputExpressions,
646 WhereExpressionShape,
647 ParameterPlacement,
648 SqlDdlExecutionUnsupported,
649}
650
651impl fmt::Debug for SqlLoweringCode {
652 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
653 fmt_compact_code(f, *self as u16)
654 }
655}
656
657#[repr(u16)]
665#[derive(Clone, Copy, Eq, Hash, PartialEq)]
666pub enum SqlSurfaceMismatchCode {
667 QueryRejectsInsert,
668 QueryRejectsUpdate,
669 QueryRejectsDelete,
670 MutationRejectsSelect,
671 MutationRejectsExplain,
672 MutationRejectsDescribe,
673 MutationRejectsShowIndexes,
674 MutationRejectsShowColumns,
675 MutationRejectsShowEntities,
676 MutationRejectsShowStores,
677 MutationRejectsShowMemory,
678 MutationRequiresExplicitUpdateIntent,
679 MutationRejectsShowConstraints,
680}
681
682impl fmt::Debug for SqlSurfaceMismatchCode {
683 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
684 fmt_compact_code(f, *self as u16)
685 }
686}
687
688#[repr(u16)]
696#[derive(Clone, Copy, Eq, Hash, PartialEq)]
697pub enum SqlWriteBoundaryCode {
698 PrimaryKeyLiteralShape,
699 PrimaryKeyLiteralIncompatible,
700 MissingPrimaryKey,
701 MissingRequiredFields,
702 ExplicitManagedField,
703 ExplicitGeneratedField,
704 InsertSelectRequiresScalar,
705 InsertSelectAggregateProjection,
706 InsertSelectWidthMismatch,
707 UpdatePrimaryKeyMutation,
708 InvalidFieldLiteral,
709 UnknownReturningField,
710 DuplicateReturningField,
711 UpdateMissingWherePredicate,
712 WriteOrderByUnsupportedShape,
713 ReturningResponseTooLarge,
714 ReturningRowsTooMany,
715 StagedRowsTooMany,
716 InsertDefaultRequiredField,
717 UpdateDefaultRequiredField,
718 UpdateDefaultDatabaseOwnedField,
719 ExactUpdateAssertionRequired,
720 ExactUpdateAssertionTooHigh,
721 ExactUpdateAffectedRowsExceeded,
722 ExactUpdateWindowUnsupported,
723 ExactUpdateScanBudgetExceeded,
724 ResumableUpdateWindowUnsupported,
725 ResumableUpdateReturningUnsupported,
726 ResumableUpdateRequiresJournaledStore,
727 ResumableUpdateAssignedFieldHasGlobalConstraint,
728 ResumableUpdateScopeDependsOnAssignedField,
729 ResumableUpdateScopeDependencyUnknown,
730 ResumableUpdateContinuationMalformed,
731 ResumableUpdateContinuationTargetMismatch,
732 ResumableUpdateContinuationSchemaMismatch,
733 ResumableUpdateContinuationScopeMismatch,
734 ResumableUpdateContinuationPatchMismatch,
735 ResumableUpdateContinuationBatchPolicyMismatch,
736 ResumableUpdateSingleRowResourceExceeded,
737 ResumableUpdateManagedFieldHasGlobalConstraint,
738 ResumableUpdateContinuationOperationMismatch,
739}
740
741impl fmt::Debug for SqlWriteBoundaryCode {
742 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
743 fmt_compact_code(f, *self as u16)
744 }
745}
746
747#[repr(u16)]
755#[derive(Clone, Copy, Eq, Hash, PartialEq)]
756pub enum SchemaDdlAdmissionCode {
757 MissingExpectedSchemaVersion,
758 MissingNextSchemaVersion,
759 StaleExpectedSchemaVersion,
760 InvalidExpectedSchemaVersion,
761 InvalidNextSchemaVersion,
762 AcceptedSchemaChangeWithoutVersionBump,
763 EmptyVersionBump,
764 VersionGap,
765 VersionRollback,
766 FingerprintMethodMismatch,
767 UnsupportedTransitionClass,
768 PhysicalRunnerMissing,
769 ValidationFailed,
770 PublicationRaceLost,
771 InvalidAddColumnDefault,
772 InvalidAlterColumnDefault,
773 GeneratedIndexDropRejected,
774 SchemaRewriteRequiresMigration,
775 SchemaTransitionBudgetExceeded,
776 GeneratedFieldDefaultChangeRejected,
777 GeneratedFieldNullabilityChangeRejected,
778 RowLayoutVersionExhausted,
779}
780
781impl fmt::Debug for SchemaDdlAdmissionCode {
782 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
783 fmt_compact_code(f, *self as u16)
784 }
785}
786
787#[remain::sorted]
794#[derive(Clone, Copy, Eq, PartialEq)]
795pub enum DiagnosticDetail {
796 QueryKind { kind: QueryErrorKind },
797 QueryProjection { reason: QueryProjectionCode },
798 QueryReadAdmission { reason: QueryReadAdmissionCode },
799 QueryResultShape { reason: QueryResultShapeCode },
800 RuntimeBoundary { boundary: RuntimeBoundaryCode },
801 RuntimeKind { kind: RuntimeErrorKind },
802 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
803 SqlLowering { reason: SqlLoweringCode },
804 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
805 SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
806 UnsupportedSqlFeature { feature: SqlFeatureCode },
807}
808
809impl fmt::Debug for DiagnosticDetail {
810 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
811 fmt_compact_code(
812 f,
813 ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
814 )
815 }
816}
817
818#[derive(Clone, Eq, PartialEq)]
825pub struct Diagnostic {
826 code: DiagnosticCode,
827 origin: ErrorOrigin,
828 detail: Option<DiagnosticDetail>,
829}
830
831impl Diagnostic {
832 #[must_use]
834 pub const fn new(
835 code: DiagnosticCode,
836 origin: ErrorOrigin,
837 detail: Option<DiagnosticDetail>,
838 ) -> Self {
839 Self {
840 code,
841 origin,
842 detail,
843 }
844 }
845
846 #[must_use]
848 pub const fn from_code(code: DiagnosticCode) -> Self {
849 Self::new(code, code.origin(), None)
850 }
851
852 #[must_use]
854 pub const fn code(&self) -> DiagnosticCode {
855 self.code
856 }
857
858 #[must_use]
860 pub const fn class(&self) -> ErrorClass {
861 self.code.class()
862 }
863
864 #[must_use]
866 pub const fn origin(&self) -> ErrorOrigin {
867 self.origin
868 }
869
870 #[must_use]
872 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
873 self.detail.as_ref()
874 }
875
876 #[must_use]
878 pub const fn error_code(&self) -> ErrorCode {
879 ErrorCode::from_parts(self.code, self.detail)
880 }
881}
882
883impl fmt::Debug for Diagnostic {
884 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
885 write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
886 }
887}
888
889fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
890 write!(f, "{raw}")
891}
892
893#[cfg(test)]
894mod tests {
895 use super::{
896 Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
897 QueryProjectionCode, QueryReadAdmissionCode, SqlFeatureCode, SqlLoweringCode,
898 SqlWriteBoundaryCode,
899 registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
900 };
901
902 #[test]
903 fn diagnostic_from_code_uses_default_origin() {
904 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
905
906 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
907 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
908 }
909
910 #[test]
911 fn diagnostic_code_reports_broad_class() {
912 assert_eq!(
913 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
914 ErrorClass::Unsupported
915 );
916 assert_eq!(
917 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
918 ErrorClass::Unsupported
919 );
920 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
921 assert_eq!(
922 DiagnosticCode::StoreCorruption.class(),
923 ErrorClass::Corruption
924 );
925 }
926
927 #[test]
928 fn class_and_origin_wire_codes_round_trip() {
929 for (class, raw) in [
930 (ErrorClass::Query, 1),
931 (ErrorClass::Corruption, 2),
932 (ErrorClass::IncompatiblePersistedFormat, 3),
933 (ErrorClass::NotFound, 4),
934 (ErrorClass::Internal, 5),
935 (ErrorClass::Conflict, 6),
936 (ErrorClass::Unsupported, 7),
937 (ErrorClass::InvariantViolation, 8),
938 ] {
939 assert_eq!(class.wire_code(), raw);
940 assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
941 assert_eq!(format!("{class:?}"), raw.to_string());
942 }
943
944 for (origin, raw) in [
945 (ErrorOrigin::Cursor, 1),
946 (ErrorOrigin::Executor, 2),
947 (ErrorOrigin::Identity, 3),
948 (ErrorOrigin::Index, 4),
949 (ErrorOrigin::Interface, 5),
950 (ErrorOrigin::Planner, 6),
951 (ErrorOrigin::Query, 7),
952 (ErrorOrigin::Recovery, 8),
953 (ErrorOrigin::Response, 9),
954 (ErrorOrigin::Runtime, 10),
955 (ErrorOrigin::Serialize, 11),
956 (ErrorOrigin::Store, 12),
957 ] {
958 assert_eq!(origin.wire_code(), raw);
959 assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
960 assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
961 assert_eq!(format!("{origin:?}"), raw.to_string());
962 }
963
964 assert_eq!(ErrorClass::from_wire_code(0), None);
965 assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
966 assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
967 }
968
969 #[test]
970 fn public_error_codes_are_sequential() {
971 let first = ORDERED_ERROR_CODES
972 .first()
973 .expect("public error-code registry is non-empty")
974 .raw();
975
976 assert_eq!(first, 1);
977
978 for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
979 let expected = first + u16::try_from(index).expect("test error-code index fits u16");
980 assert_eq!(code.raw(), expected);
981 assert_eq!(ErrorCode::known(code.raw()), Some(*code));
982 assert!(code.is_known());
983 }
984
985 let last = ORDERED_ERROR_CODES
986 .last()
987 .expect("public error-code registry is non-empty")
988 .raw();
989
990 assert_eq!(last, 240);
991 }
992
993 #[test]
994 fn all_public_error_codes_round_trip_through_diagnostic_parts() {
995 let first = ORDERED_ERROR_CODES
996 .first()
997 .expect("public error-code registry is non-empty")
998 .raw();
999 let last = ORDERED_ERROR_CODES
1000 .last()
1001 .expect("public error-code registry is non-empty")
1002 .raw();
1003
1004 for raw in first..=last {
1005 let code = ErrorCode::from_raw(raw);
1006 let diagnostic_code = code.diagnostic_code();
1007 let diagnostic_detail = code.diagnostic_detail();
1008 let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
1009
1010 assert_eq!(rebuilt.raw(), raw);
1011
1012 let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
1013
1014 assert_eq!(diagnostic.code(), diagnostic_code);
1015 assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
1016 assert_eq!(diagnostic.error_code().raw(), raw);
1017 }
1018 }
1019
1020 #[test]
1021 fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
1022 for raw in [0, 241, u16::MAX] {
1023 let code = ErrorCode::from_raw(raw);
1024
1025 assert_eq!(ErrorCode::known(raw), None);
1026 assert!(!code.is_known());
1027 assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
1028 assert_eq!(code.diagnostic_detail(), None);
1029 assert_eq!(code.class(), ErrorClass::Internal);
1030
1031 let diagnostic = code.diagnostic(ErrorOrigin::Query);
1032
1033 assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
1034 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
1035 assert_eq!(diagnostic.detail(), None);
1036 assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
1037 }
1038 }
1039
1040 #[test]
1041 fn from_parts_requires_detail_to_match_broad_code() {
1042 let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
1043 feature: SqlFeatureCode::Join,
1044 });
1045
1046 assert_eq!(
1047 ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
1048 ErrorCode::SQL_FEATURE_JOIN
1049 );
1050 assert_eq!(
1051 ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
1052 ErrorCode::QUERY_PLAN
1053 );
1054 }
1055
1056 #[test]
1057 fn detail_bearing_registry_entries_round_trip_directly() {
1058 assert!(!DETAIL_ERROR_CODES.is_empty());
1059
1060 for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
1061 assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
1062 assert_eq!(code.diagnostic_code(), diagnostic_code);
1063 assert_eq!(code.diagnostic_detail(), Some(detail));
1064 assert_eq!(detail.diagnostic_code(), diagnostic_code);
1065 }
1066 }
1067
1068 #[test]
1069 fn diagnostic_detail_reports_generated_broad_code() {
1070 let detail = DiagnosticDetail::UnsupportedSqlFeature {
1071 feature: SqlFeatureCode::Join,
1072 };
1073
1074 assert_eq!(
1075 detail.diagnostic_code(),
1076 DiagnosticCode::QueryUnsupportedSqlFeature
1077 );
1078 assert_eq!(format!("{detail:?}"), "65");
1079 }
1080
1081 #[test]
1082 fn public_error_codes_reconstruct_shifted_details() {
1083 assert_eq!(
1084 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
1085 DiagnosticCode::QueryUnknownAggregateTargetField
1086 );
1087 assert_eq!(
1088 ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
1089 Some(DiagnosticDetail::UnsupportedSqlFeature {
1090 feature: SqlFeatureCode::Join,
1091 })
1092 );
1093 assert_eq!(
1094 ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1095 Some(DiagnosticDetail::QueryProjection {
1096 reason: QueryProjectionCode::NumericLiteralRequired,
1097 })
1098 );
1099 assert_eq!(
1100 ErrorCode::QUERY_READ_PUBLIC_REQUIRES_LIMIT.diagnostic_detail(),
1101 Some(DiagnosticDetail::QueryReadAdmission {
1102 reason: QueryReadAdmissionCode::PublicQueryRequiresLimit,
1103 })
1104 );
1105 assert_eq!(
1106 ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1107 Some(DiagnosticDetail::SqlLowering {
1108 reason: SqlLoweringCode::DistinctOrderByProjection,
1109 })
1110 );
1111 assert_eq!(
1112 ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1113 Some(DiagnosticDetail::SqlWriteBoundary {
1114 boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1115 })
1116 );
1117 assert_eq!(
1118 ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1119 Some(DiagnosticDetail::SqlWriteBoundary {
1120 boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1121 })
1122 );
1123 }
1124}