1use std::fmt;
13
14mod fact;
15
16pub use fact::{
17 DiagnosticAggregateKind, DiagnosticComponentKind, DiagnosticConstraintContext,
18 DiagnosticConstraintKind, DiagnosticDecodeReason, DiagnosticExecutionBudgetResource,
19 DiagnosticExecutionBudgetScope, DiagnosticExecutionLane, DiagnosticFactSchemaMismatch,
20 DiagnosticFactTag, DiagnosticFunctionKind, DiagnosticMutationOperation, DiagnosticOperatorKind,
21 DiagnosticTypeFamily, MAX_PUBLIC_DIAGNOSTIC_FACTS, pack_u32_pair, unpack_u32_pair,
22 validate_known_diagnostic_fact_schema, validate_raw_diagnostic_fact_schema,
23};
24
25#[remain::sorted]
32#[derive(Clone, Copy, Eq, Hash, PartialEq)]
33pub enum DiagnosticCode {
34 QueryAccessRequirement,
35 QueryIntent,
36 QueryInvalidContinuationCursor,
37 QueryNotFound,
38 QueryNotUnique,
39 QueryNumericNotRepresentable,
40 QueryNumericOverflow,
41 QueryPlan,
42 QueryReadAdmission,
43 QueryResultShapeMismatch,
44 QuerySqlSurfaceMismatch,
45 QuerySqlWriteBoundary,
46 QueryUnknownAggregateTargetField,
47 QueryUnorderedPagination,
48 QueryUnsupportedProjection,
49 QueryUnsupportedSqlFeature,
50 QueryValidate,
51 RuntimeConflict,
52 RuntimeCorruption,
53 RuntimeIncompatiblePersistedFormat,
54 RuntimeInternal,
55 RuntimeInvariantViolation,
56 RuntimeNotFound,
57 RuntimeUnsupported,
58 SchemaDdlAdmission,
59 StoreCorruption,
60 StoreInvariantViolation,
61 StoreNotFound,
62}
63
64impl DiagnosticCode {
65 #[must_use]
67 pub const fn class(self) -> ErrorClass {
68 match self {
69 Self::StoreCorruption | Self::RuntimeCorruption => ErrorClass::Corruption,
70 Self::RuntimeIncompatiblePersistedFormat => ErrorClass::IncompatiblePersistedFormat,
71 Self::QueryNotFound | Self::StoreNotFound | Self::RuntimeNotFound => {
72 ErrorClass::NotFound
73 }
74 Self::RuntimeConflict => ErrorClass::Conflict,
75 Self::QueryUnsupportedSqlFeature
76 | Self::QueryUnknownAggregateTargetField
77 | Self::QueryUnsupportedProjection
78 | Self::QueryResultShapeMismatch
79 | Self::QuerySqlSurfaceMismatch
80 | Self::QuerySqlWriteBoundary
81 | Self::RuntimeUnsupported => ErrorClass::Unsupported,
82 Self::StoreInvariantViolation | Self::RuntimeInvariantViolation => {
83 ErrorClass::InvariantViolation
84 }
85 Self::RuntimeInternal => ErrorClass::Internal,
86 Self::QueryValidate
87 | Self::QueryIntent
88 | Self::QueryPlan
89 | Self::QueryReadAdmission
90 | Self::QueryAccessRequirement
91 | Self::QueryUnorderedPagination
92 | Self::QueryInvalidContinuationCursor
93 | Self::QueryNotUnique
94 | Self::QueryNumericOverflow
95 | Self::QueryNumericNotRepresentable
96 | Self::SchemaDdlAdmission => ErrorClass::Query,
97 }
98 }
99
100 #[must_use]
102 pub const fn origin(self) -> ErrorOrigin {
103 match self {
104 Self::StoreNotFound | Self::StoreCorruption | Self::StoreInvariantViolation => {
105 ErrorOrigin::Store
106 }
107 Self::RuntimeCorruption
108 | Self::RuntimeIncompatiblePersistedFormat
109 | Self::RuntimeInvariantViolation
110 | Self::RuntimeConflict
111 | Self::RuntimeNotFound
112 | Self::RuntimeUnsupported
113 | Self::RuntimeInternal => ErrorOrigin::Runtime,
114 Self::QueryValidate
115 | Self::QueryIntent
116 | Self::QueryPlan
117 | Self::QueryReadAdmission
118 | Self::QueryAccessRequirement
119 | Self::QueryUnorderedPagination
120 | Self::QueryInvalidContinuationCursor
121 | Self::QueryNotFound
122 | Self::QueryNotUnique
123 | Self::QueryNumericOverflow
124 | Self::QueryNumericNotRepresentable
125 | Self::QueryUnknownAggregateTargetField
126 | Self::QueryUnsupportedProjection
127 | Self::QueryResultShapeMismatch
128 | Self::QueryUnsupportedSqlFeature
129 | Self::QuerySqlSurfaceMismatch
130 | Self::QuerySqlWriteBoundary
131 | Self::SchemaDdlAdmission => ErrorOrigin::Query,
132 }
133 }
134
135 #[must_use]
137 pub const fn error_code(self) -> ErrorCode {
138 match self {
139 Self::QueryValidate => ErrorCode::QUERY_VALIDATE,
140 Self::QueryIntent => ErrorCode::QUERY_INTENT,
141 Self::QueryPlan => ErrorCode::QUERY_PLAN,
142 Self::QueryReadAdmission => ErrorCode::QUERY_READ_ADMISSION,
143 Self::QueryAccessRequirement => ErrorCode::QUERY_ACCESS_REQUIREMENT,
144 Self::QueryUnorderedPagination => ErrorCode::QUERY_UNORDERED_PAGINATION,
145 Self::QueryInvalidContinuationCursor => ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
146 Self::QueryNotFound => ErrorCode::QUERY_NOT_FOUND,
147 Self::QueryNotUnique => ErrorCode::QUERY_NOT_UNIQUE,
148 Self::QueryNumericOverflow => ErrorCode::QUERY_NUMERIC_OVERFLOW,
149 Self::QueryNumericNotRepresentable => ErrorCode::QUERY_NUMERIC_NOT_REPRESENTABLE,
150 Self::QueryUnknownAggregateTargetField => {
151 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD
152 }
153 Self::QueryUnsupportedProjection => ErrorCode::QUERY_UNSUPPORTED_PROJECTION,
154 Self::QueryResultShapeMismatch => ErrorCode::QUERY_RESULT_SHAPE_MISMATCH,
155 Self::QueryUnsupportedSqlFeature => ErrorCode::QUERY_UNSUPPORTED_SQL_FEATURE,
156 Self::QuerySqlSurfaceMismatch => ErrorCode::QUERY_SQL_SURFACE_MISMATCH,
157 Self::QuerySqlWriteBoundary => ErrorCode::QUERY_SQL_WRITE_BOUNDARY,
158 Self::SchemaDdlAdmission => ErrorCode::SCHEMA_DDL_ADMISSION,
159 Self::StoreNotFound => ErrorCode::STORE_NOT_FOUND,
160 Self::StoreCorruption => ErrorCode::STORE_CORRUPTION,
161 Self::StoreInvariantViolation => ErrorCode::STORE_INVARIANT_VIOLATION,
162 Self::RuntimeCorruption => ErrorCode::RUNTIME_CORRUPTION,
163 Self::RuntimeIncompatiblePersistedFormat => {
164 ErrorCode::RUNTIME_INCOMPATIBLE_PERSISTED_FORMAT
165 }
166 Self::RuntimeInvariantViolation => ErrorCode::RUNTIME_INVARIANT_VIOLATION,
167 Self::RuntimeConflict => ErrorCode::RUNTIME_CONFLICT,
168 Self::RuntimeNotFound => ErrorCode::RUNTIME_NOT_FOUND,
169 Self::RuntimeUnsupported => ErrorCode::RUNTIME_UNSUPPORTED,
170 Self::RuntimeInternal => ErrorCode::RUNTIME_INTERNAL,
171 }
172 }
173}
174
175impl fmt::Debug for DiagnosticCode {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 fmt_compact_code(f, self.error_code().raw())
178 }
179}
180
181#[derive(Clone, Copy, Eq, Hash, PartialEq)]
193pub struct ErrorCode(u16);
194
195mod registry;
196
197impl ErrorCode {
198 #[must_use]
200 pub const fn from_raw(raw: u16) -> Self {
201 Self(raw)
202 }
203
204 #[must_use]
206 pub const fn raw(self) -> u16 {
207 self.0
208 }
209}
210
211impl fmt::Debug for ErrorCode {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 fmt_compact_code(f, self.raw())
214 }
215}
216
217#[remain::sorted]
224#[derive(Clone, Copy, Eq, Hash, PartialEq)]
225pub enum ErrorClass {
226 Conflict,
227 Corruption,
228 IncompatiblePersistedFormat,
229 Internal,
230 InvariantViolation,
231 NotFound,
232 Query,
233 Unsupported,
234}
235
236impl ErrorClass {
237 #[must_use]
239 pub const fn wire_code(self) -> u8 {
240 match self {
241 Self::Query => 1,
242 Self::Corruption => 2,
243 Self::IncompatiblePersistedFormat => 3,
244 Self::NotFound => 4,
245 Self::Internal => 5,
246 Self::Conflict => 6,
247 Self::Unsupported => 7,
248 Self::InvariantViolation => 8,
249 }
250 }
251
252 #[must_use]
254 pub const fn from_wire_code(code: u8) -> Option<Self> {
255 match code {
256 1 => Some(Self::Query),
257 2 => Some(Self::Corruption),
258 3 => Some(Self::IncompatiblePersistedFormat),
259 4 => Some(Self::NotFound),
260 5 => Some(Self::Internal),
261 6 => Some(Self::Conflict),
262 7 => Some(Self::Unsupported),
263 8 => Some(Self::InvariantViolation),
264 _ => None,
265 }
266 }
267}
268
269impl fmt::Debug for ErrorClass {
270 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
271 fmt_compact_code(f, u16::from(self.wire_code()))
272 }
273}
274
275#[remain::sorted]
282#[derive(Clone, Copy, Eq, Hash, PartialEq)]
283pub enum ErrorOrigin {
284 Cursor,
285 Executor,
286 Identity,
287 Index,
288 Interface,
289 Planner,
290 Query,
291 Recovery,
292 Response,
293 Runtime,
294 Serialize,
295 Store,
296}
297
298impl ErrorOrigin {
299 #[must_use]
301 pub const fn wire_code(self) -> u8 {
302 match self {
303 Self::Cursor => 1,
304 Self::Executor => 2,
305 Self::Identity => 3,
306 Self::Index => 4,
307 Self::Interface => 5,
308 Self::Planner => 6,
309 Self::Query => 7,
310 Self::Recovery => 8,
311 Self::Response => 9,
312 Self::Runtime => 10,
313 Self::Serialize => 11,
314 Self::Store => 12,
315 }
316 }
317
318 #[must_use]
320 pub const fn from_known_wire_code(code: u8) -> Option<Self> {
321 match code {
322 1 => Some(Self::Cursor),
323 2 => Some(Self::Executor),
324 3 => Some(Self::Identity),
325 4 => Some(Self::Index),
326 5 => Some(Self::Interface),
327 6 => Some(Self::Planner),
328 7 => Some(Self::Query),
329 8 => Some(Self::Recovery),
330 9 => Some(Self::Response),
331 10 => Some(Self::Runtime),
332 11 => Some(Self::Serialize),
333 12 => Some(Self::Store),
334 _ => None,
335 }
336 }
337
338 #[must_use]
343 pub const fn from_wire_code(code: u8) -> Self {
344 match Self::from_known_wire_code(code) {
345 Some(origin) => origin,
346 None => Self::Runtime,
347 }
348 }
349}
350
351impl fmt::Debug for ErrorOrigin {
352 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
353 fmt_compact_code(f, u16::from(self.wire_code()))
354 }
355}
356
357#[repr(u16)]
364#[derive(Clone, Copy, Eq, Hash, PartialEq)]
365pub enum QueryErrorKind {
366 Validate,
367 Intent,
368 Plan,
369 AccessRequirement,
370 UnorderedPagination,
371 InvalidContinuationCursor,
372 NotFound,
373 NotUnique,
374}
375
376impl fmt::Debug for QueryErrorKind {
377 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
378 fmt_compact_code(f, *self as u16)
379 }
380}
381
382#[repr(u16)]
390#[derive(Clone, Copy, Eq, Hash, PartialEq)]
391pub enum QueryProjectionCode {
392 NumericLiteralRequired,
393 NumericScaleArguments,
394 NestedFieldPathPreview,
395 CaseConditionBooleanRequired,
396 NumericInputRequired,
397 TextOrBlobInputRequired,
398 TextInputRequired,
399 TextOrNullArgumentRequired,
400 IntegerOrNullArgumentRequired,
401 UnaryOperandIncompatible,
402 BinaryOperandsIncompatible,
403}
404
405impl fmt::Debug for QueryProjectionCode {
406 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
407 fmt_compact_code(f, *self as u16)
408 }
409}
410
411#[repr(u16)]
419#[derive(Clone, Copy, Eq, Hash, PartialEq)]
420pub enum QueryReadAdmissionCode {
421 PublicQueryRequiresLimit,
422 PublicQueryRequiresIndex,
423 UnboundedFullScanRejected,
424 SortRequiresMaterialization,
425 GroupedQueryRequiresLimits,
426 GroupedQueryExceedsBudget,
427 DiagnosticLaneDoesNotExecute,
428 ReturnedRowBoundExceedsPolicy,
429 PrimaryKeyInputExceedsPolicy,
430}
431
432impl fmt::Debug for QueryReadAdmissionCode {
433 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
434 fmt_compact_code(f, *self as u16)
435 }
436}
437
438#[repr(u16)]
446#[derive(Clone, Copy, Eq, Hash, PartialEq)]
447pub enum QueryResultShapeCode {
448 ExpectedRows,
449 ExpectedGroupedRows,
450}
451
452impl fmt::Debug for QueryResultShapeCode {
453 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
454 fmt_compact_code(f, *self as u16)
455 }
456}
457
458#[repr(u16)]
465#[derive(Clone, Copy, Eq, Hash, PartialEq)]
466pub enum RuntimeErrorKind {
467 Corruption,
468 IncompatiblePersistedFormat,
469 InvariantViolation,
470 Conflict,
471 NotFound,
472 Unsupported,
473 Internal,
474}
475
476impl fmt::Debug for RuntimeErrorKind {
477 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
478 fmt_compact_code(f, *self as u16)
479 }
480}
481
482#[repr(u16)]
490#[derive(Clone, Copy, Eq, Hash, PartialEq)]
491pub enum RuntimeBoundaryCode {
492 SqlSurfaceControllerRequired,
493 SchemaSurfaceControllerRequired,
494 SqlQueryNoConfiguredEntities,
495 SqlQueryEntityNotFound,
496 SqlDdlTargetRequired,
497 SqlDdlEntityNotConfigured,
498 QueryResponseRowsRequired,
499 QueryResponseGroupedRowsRequired,
500 RowProjectionFieldNotConfigured,
501 SqlIntrospectionDisabled,
502 MutationRequiredFieldMissing,
504 MutationManagedTimestampRegression,
506 PersistedRowLayoutOutsideAcceptedWindow,
508 PersistedRowSlotCountMismatch,
510 GeneratedFieldAfterDdlField,
512 JournalMutationRevisionExhausted,
514 ConstraintViolation,
516 AcceptedRowConstraintProgramCorrupt,
518 ConstraintActivationWriteBlocked,
520 GeneratedConstraintActivationStale,
522 MutationDatabaseOwnedFieldExplicit,
524 MutationBatchEmpty,
526 MutationBatchTooManyItems,
528 MutationBatchStagedBytesExceeded,
530 MutationBatchResultBytesExceeded,
532 MutationBatchEntityMismatch,
534 MutationBatchDuplicateKey,
536 OperationalSurfaceControllerRequired,
538 ExactKeyBatchTooManyItems,
540 ExactKeyBatchInputBytesExceeded,
542 ExactKeyBatchStoredBytesExceeded,
544 ExactKeyBatchResultBytesExceeded,
546 ExecutionBudgetExceeded,
548 PageUnitTooLarge,
550 RequestExecutionScopeRequired,
552 RequestExecutionRootMismatch,
554}
555
556impl fmt::Debug for RuntimeBoundaryCode {
557 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
558 fmt_compact_code(f, *self as u16)
559 }
560}
561
562#[repr(u16)]
570#[derive(Clone, Copy, Eq, Hash, PartialEq)]
571pub enum SqlFeatureCode {
572 AggregateFilterClause,
573 AlterStatementBeyondAlterTable,
574 AlterTableAddColumnDuplicateDefault,
575 AlterTableAddColumnModifiers,
576 AlterTableAddStatementBeyondAddColumn,
577 AlterTableAlterColumnDropUnsupportedAction,
578 AlterTableAlterColumnModifiers,
579 AlterTableAlterColumnSetUnsupportedAction,
580 AlterTableAlterColumnUnsupportedAction,
581 AlterTableAlterStatementBeyondAlterColumn,
582 AlterTableDropColumnIfExistsSyntax,
583 AlterTableDropColumnModifiers,
584 AlterTableDropStatementBeyondDropColumn,
585 AlterTableRenameColumnMissingTo,
586 AlterTableRenameColumnModifiers,
587 AlterTableRenameStatementBeyondRenameColumn,
588 AlterTableUnsupportedOperation,
589 ColumnAlias,
590 CreateIndexIfNotExistsSyntax,
591 CreateIndexKeyOrderingModifiers,
592 CreateIndexModifiers,
593 CreateStatementBeyondCreateIndex,
594 DescribeModifier,
595 DdlSchemaVersionDuplicateExpectedClause,
596 DdlSchemaVersionDuplicateSetClause,
597 DropIndexModifiers,
598 DropIndexIfExistsSyntax,
599 DropStatementBeyondDropIndex,
600 ExpressionIndexUnsupportedFunction,
601 Having,
602 Insert,
603 Join,
604 LikePatternBeyondTrailingPrefix,
605 LowerFieldPredicateUnsupported,
606 MultiStatementSql,
607 NestedAggregateInput,
608 NestedProjectionFunctionInArithmetic,
609 OrderByUnsupportedForm,
610 Other,
611 PredicateStartsWithFirstArgument,
612 QuotedIdentifiers,
613 ReturningUnsupportedShape,
614 ScalarFunctionExpressionPosition,
615 ScaleTakingNumericFunctionExpressionPosition,
616 SearchedCaseGroupedOrderBy,
617 ShowColumnsModifiers,
618 ShowEntitiesModifiers,
619 ShowIndexesModifiers,
620 ShowMemoryModifiers,
621 ShowStoresModifiers,
622 ShowUnsupportedCommand,
623 SimpleCaseExpression,
624 StandaloneLiteralProjectionItem,
625 SupportedGroupedOrderByExpressionFamily,
626 SupportedOrderByExpressionFamily,
627 UnionIntersectExcept,
628 UnsupportedFunctionNamespace,
629 Update,
630 UpperFieldPredicateUnsupported,
631 WindowFunction,
632 With,
633 NumericScaleFunctionArguments,
634 OrderByFieldNotOrderable,
635 ShowConstraintsModifiers,
636 AlterTableAddConstraintBeyondCheck,
637 AlterTableAddConstraintModifiers,
638 AlterTableDropConstraintIfExistsSyntax,
639 AlterTableDropConstraintModifiers,
640 AlterTableValidateBeyondConstraint,
641 AlterTableValidateConstraintModifiers,
642}
643
644impl fmt::Debug for SqlFeatureCode {
645 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
646 fmt_compact_code(f, *self as u16)
647 }
648}
649
650#[repr(u16)]
659#[derive(Clone, Copy, Eq, Hash, PartialEq)]
660pub enum SqlLoweringCode {
661 EntityMismatch,
662 SelectProjectionShape,
663 SelectDistinct,
664 DistinctOrderByProjection,
665 GlobalAggregateProjection,
666 GlobalAggregateGroupBy,
667 SelectGroupByShape,
668 GroupedProjectionExplicitListRequired,
669 GroupedProjectionAggregateRequired,
670 GroupedProjectionNonGroupField,
671 GroupedProjectionScalarAfterAggregate,
672 HavingRequiresGroupBy,
673 SelectHavingShape,
674 AggregateInputExpressions,
675 WhereExpressionShape,
676 ParameterPlacement,
677 SqlDdlExecutionUnsupported,
678}
679
680impl fmt::Debug for SqlLoweringCode {
681 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
682 fmt_compact_code(f, *self as u16)
683 }
684}
685
686#[repr(u16)]
694#[derive(Clone, Copy, Eq, Hash, PartialEq)]
695pub enum SqlSurfaceMismatchCode {
696 QueryRejectsInsert,
697 QueryRejectsUpdate,
698 QueryRejectsDelete,
699 MutationRejectsSelect,
700 MutationRejectsExplain,
701 MutationRejectsDescribe,
702 MutationRejectsShowIndexes,
703 MutationRejectsShowColumns,
704 MutationRejectsShowEntities,
705 MutationRejectsShowStores,
706 MutationRejectsShowMemory,
707 MutationRequiresExplicitUpdateIntent,
708 MutationRejectsShowConstraints,
709}
710
711impl fmt::Debug for SqlSurfaceMismatchCode {
712 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
713 fmt_compact_code(f, *self as u16)
714 }
715}
716
717#[repr(u16)]
725#[derive(Clone, Copy, Eq, Hash, PartialEq)]
726pub enum SqlWriteBoundaryCode {
727 PrimaryKeyLiteralShape,
728 PrimaryKeyLiteralIncompatible,
729 MissingPrimaryKey,
730 MissingRequiredFields,
731 ExplicitManagedField,
732 ExplicitGeneratedField,
733 InsertSelectRequiresScalar,
734 InsertSelectAggregateProjection,
735 InsertSelectWidthMismatch,
736 UpdatePrimaryKeyMutation,
737 InvalidFieldLiteral,
738 UnknownReturningField,
739 DuplicateReturningField,
740 UpdateMissingWherePredicate,
741 WriteOrderByUnsupportedShape,
742 ReturningResponseTooLarge,
743 ReturningRowsTooMany,
744 StagedRowsTooMany,
745 InsertDefaultRequiredField,
746 UpdateDefaultRequiredField,
747 UpdateDefaultDatabaseOwnedField,
748 ExactUpdateAssertionRequired,
749 ExactUpdateAssertionTooHigh,
750 ExactUpdateAffectedRowsExceeded,
751 ExactUpdateWindowUnsupported,
752 ExactUpdateScanBudgetExceeded,
753 ResumableUpdateWindowUnsupported,
754 ResumableUpdateReturningUnsupported,
755 ResumableUpdateRequiresJournaledStore,
756 ResumableUpdateAssignedFieldHasGlobalConstraint,
757 ResumableUpdateScopeDependsOnAssignedField,
758 ResumableUpdateScopeDependencyUnknown,
759 ResumableUpdateContinuationMalformed,
760 ResumableUpdateContinuationTargetMismatch,
761 ResumableUpdateContinuationSchemaMismatch,
762 ResumableUpdateContinuationScopeMismatch,
763 ResumableUpdateContinuationPatchMismatch,
764 ResumableUpdateContinuationBatchPolicyMismatch,
765 ResumableUpdateSingleRowResourceExceeded,
766 ResumableUpdateManagedFieldHasGlobalConstraint,
767 ResumableUpdateContinuationOperationMismatch,
768}
769
770impl fmt::Debug for SqlWriteBoundaryCode {
771 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
772 fmt_compact_code(f, *self as u16)
773 }
774}
775
776#[repr(u16)]
784#[derive(Clone, Copy, Eq, Hash, PartialEq)]
785pub enum SchemaDdlAdmissionCode {
786 MissingExpectedSchemaVersion,
787 MissingNextSchemaVersion,
788 StaleExpectedSchemaVersion,
789 InvalidExpectedSchemaVersion,
790 InvalidNextSchemaVersion,
791 AcceptedSchemaChangeWithoutVersionBump,
792 EmptyVersionBump,
793 VersionGap,
794 VersionRollback,
795 FingerprintMethodMismatch,
796 UnsupportedTransitionClass,
797 PhysicalRunnerMissing,
798 ValidationFailed,
799 PublicationRaceLost,
800 InvalidAddColumnDefault,
801 InvalidAlterColumnDefault,
802 GeneratedIndexDropRejected,
803 SchemaRewriteRequiresMigration,
804 SchemaTransitionBudgetExceeded,
805 GeneratedFieldDefaultChangeRejected,
806 GeneratedFieldNullabilityChangeRejected,
807 RowLayoutVersionExhausted,
808}
809
810impl fmt::Debug for SchemaDdlAdmissionCode {
811 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
812 fmt_compact_code(f, *self as u16)
813 }
814}
815
816#[repr(u16)]
818#[derive(Clone, Copy, Eq, Hash, PartialEq)]
819pub enum SchemaMigrationCode {
820 Unadopted,
821 MissingMigration,
822 VersionGap,
823 Downgrade,
824 EmptyEntityVersionBump,
825 DuplicateEntityTransition,
826 StaleAcceptedHead,
827 PlanChanged,
828 DuplicateRenameSource,
829 DuplicateRenameTarget,
830 UnknownFromObject,
831 UnknownToObject,
832 KindMismatch,
833 IdentityConflict,
834 IncompleteRenameCoverage,
835 UnexplainedSchemaDifference,
836 UnsupportedTransform,
837 TransformFinding,
838 UniqueIndexFinding,
839 RelationFinding,
840 ConstraintFinding,
841 PhysicalRunnerMissing,
842 MigrationInProgress,
843 AbortTooLate,
844 ProgressCorrupt,
845 CandidateMismatch,
846 PublicationRaceLost,
847}
848
849impl SchemaMigrationCode {
850 #[must_use]
852 pub const fn diagnostic_code(self) -> DiagnosticCode {
853 match self {
854 Self::StaleAcceptedHead
855 | Self::PlanChanged
856 | Self::IdentityConflict
857 | Self::MigrationInProgress
858 | Self::AbortTooLate
859 | Self::PublicationRaceLost => DiagnosticCode::RuntimeConflict,
860 Self::ProgressCorrupt | Self::CandidateMismatch => DiagnosticCode::RuntimeCorruption,
861 Self::Unadopted
862 | Self::MissingMigration
863 | Self::VersionGap
864 | Self::Downgrade
865 | Self::EmptyEntityVersionBump
866 | Self::DuplicateEntityTransition
867 | Self::DuplicateRenameSource
868 | Self::DuplicateRenameTarget
869 | Self::UnknownFromObject
870 | Self::UnknownToObject
871 | Self::KindMismatch
872 | Self::IncompleteRenameCoverage
873 | Self::UnexplainedSchemaDifference
874 | Self::UnsupportedTransform
875 | Self::TransformFinding
876 | Self::UniqueIndexFinding
877 | Self::RelationFinding
878 | Self::ConstraintFinding
879 | Self::PhysicalRunnerMissing => DiagnosticCode::RuntimeUnsupported,
880 }
881 }
882}
883
884impl fmt::Debug for SchemaMigrationCode {
885 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
886 fmt_compact_code(f, *self as u16)
887 }
888}
889
890#[remain::sorted]
897#[derive(Clone, Copy, Eq, PartialEq)]
898pub enum DiagnosticDetail {
899 QueryKind { kind: QueryErrorKind },
900 QueryProjection { reason: QueryProjectionCode },
901 QueryReadAdmission { reason: QueryReadAdmissionCode },
902 QueryResultShape { reason: QueryResultShapeCode },
903 RuntimeBoundary { boundary: RuntimeBoundaryCode },
904 RuntimeKind { kind: RuntimeErrorKind },
905 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
906 SchemaMigration { reason: SchemaMigrationCode },
907 SqlLowering { reason: SqlLoweringCode },
908 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
909 SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
910 UnsupportedSqlFeature { feature: SqlFeatureCode },
911}
912
913impl fmt::Debug for DiagnosticDetail {
914 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
915 fmt_compact_code(
916 f,
917 ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
918 )
919 }
920}
921
922#[derive(Clone, Eq, PartialEq)]
929pub struct Diagnostic {
930 code: DiagnosticCode,
931 origin: ErrorOrigin,
932 detail: Option<DiagnosticDetail>,
933}
934
935impl Diagnostic {
936 #[must_use]
938 pub const fn new(
939 code: DiagnosticCode,
940 origin: ErrorOrigin,
941 detail: Option<DiagnosticDetail>,
942 ) -> Self {
943 Self {
944 code,
945 origin,
946 detail,
947 }
948 }
949
950 #[must_use]
952 pub const fn from_code(code: DiagnosticCode) -> Self {
953 Self::new(code, code.origin(), None)
954 }
955
956 #[must_use]
958 pub const fn code(&self) -> DiagnosticCode {
959 self.code
960 }
961
962 #[must_use]
964 pub const fn class(&self) -> ErrorClass {
965 self.code.class()
966 }
967
968 #[must_use]
970 pub const fn origin(&self) -> ErrorOrigin {
971 self.origin
972 }
973
974 #[must_use]
976 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
977 self.detail.as_ref()
978 }
979
980 #[must_use]
982 pub const fn error_code(&self) -> ErrorCode {
983 ErrorCode::from_parts(self.code, self.detail)
984 }
985}
986
987impl fmt::Debug for Diagnostic {
988 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
989 write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
990 }
991}
992
993fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
994 write!(f, "{raw}")
995}
996
997#[cfg(test)]
998mod tests {
999 use super::{
1000 Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
1001 QueryProjectionCode, QueryReadAdmissionCode, SqlFeatureCode, SqlLoweringCode,
1002 SqlWriteBoundaryCode,
1003 registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
1004 };
1005
1006 #[test]
1007 fn diagnostic_from_code_uses_default_origin() {
1008 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
1009
1010 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
1011 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
1012 }
1013
1014 #[test]
1015 fn diagnostic_code_reports_broad_class() {
1016 assert_eq!(
1017 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
1018 ErrorClass::Unsupported
1019 );
1020 assert_eq!(
1021 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
1022 ErrorClass::Unsupported
1023 );
1024 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
1025 assert_eq!(
1026 DiagnosticCode::StoreCorruption.class(),
1027 ErrorClass::Corruption
1028 );
1029 }
1030
1031 #[test]
1032 fn class_and_origin_wire_codes_round_trip() {
1033 for (class, raw) in [
1034 (ErrorClass::Query, 1),
1035 (ErrorClass::Corruption, 2),
1036 (ErrorClass::IncompatiblePersistedFormat, 3),
1037 (ErrorClass::NotFound, 4),
1038 (ErrorClass::Internal, 5),
1039 (ErrorClass::Conflict, 6),
1040 (ErrorClass::Unsupported, 7),
1041 (ErrorClass::InvariantViolation, 8),
1042 ] {
1043 assert_eq!(class.wire_code(), raw);
1044 assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
1045 assert_eq!(format!("{class:?}"), raw.to_string());
1046 }
1047
1048 for (origin, raw) in [
1049 (ErrorOrigin::Cursor, 1),
1050 (ErrorOrigin::Executor, 2),
1051 (ErrorOrigin::Identity, 3),
1052 (ErrorOrigin::Index, 4),
1053 (ErrorOrigin::Interface, 5),
1054 (ErrorOrigin::Planner, 6),
1055 (ErrorOrigin::Query, 7),
1056 (ErrorOrigin::Recovery, 8),
1057 (ErrorOrigin::Response, 9),
1058 (ErrorOrigin::Runtime, 10),
1059 (ErrorOrigin::Serialize, 11),
1060 (ErrorOrigin::Store, 12),
1061 ] {
1062 assert_eq!(origin.wire_code(), raw);
1063 assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
1064 assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
1065 assert_eq!(format!("{origin:?}"), raw.to_string());
1066 }
1067
1068 assert_eq!(ErrorClass::from_wire_code(0), None);
1069 assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
1070 assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
1071 }
1072
1073 #[test]
1074 fn public_error_codes_are_sequential() {
1075 let first = ORDERED_ERROR_CODES
1076 .first()
1077 .expect("public error-code registry is non-empty")
1078 .raw();
1079
1080 assert_eq!(first, 1);
1081
1082 for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
1083 let expected = first + u16::try_from(index).expect("test error-code index fits u16");
1084 assert_eq!(code.raw(), expected);
1085 assert_eq!(ErrorCode::known(code.raw()), Some(*code));
1086 assert!(code.is_known());
1087 }
1088
1089 let last = ORDERED_ERROR_CODES
1090 .last()
1091 .expect("public error-code registry is non-empty")
1092 .raw();
1093
1094 assert_eq!(last, 276);
1095 }
1096
1097 #[test]
1098 fn all_public_error_codes_round_trip_through_diagnostic_parts() {
1099 let first = ORDERED_ERROR_CODES
1100 .first()
1101 .expect("public error-code registry is non-empty")
1102 .raw();
1103 let last = ORDERED_ERROR_CODES
1104 .last()
1105 .expect("public error-code registry is non-empty")
1106 .raw();
1107
1108 for raw in first..=last {
1109 let code = ErrorCode::from_raw(raw);
1110 let diagnostic_code = code.diagnostic_code();
1111 let diagnostic_detail = code.diagnostic_detail();
1112 let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
1113
1114 assert_eq!(rebuilt.raw(), raw);
1115
1116 let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
1117
1118 assert_eq!(diagnostic.code(), diagnostic_code);
1119 assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
1120 assert_eq!(diagnostic.error_code().raw(), raw);
1121 }
1122 }
1123
1124 #[test]
1125 fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
1126 for raw in [0, 277, u16::MAX] {
1127 let code = ErrorCode::from_raw(raw);
1128
1129 assert_eq!(ErrorCode::known(raw), None);
1130 assert!(!code.is_known());
1131 assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
1132 assert_eq!(code.diagnostic_detail(), None);
1133 assert_eq!(code.class(), ErrorClass::Internal);
1134
1135 let diagnostic = code.diagnostic(ErrorOrigin::Query);
1136
1137 assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
1138 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
1139 assert_eq!(diagnostic.detail(), None);
1140 assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
1141 }
1142 }
1143
1144 #[test]
1145 fn from_parts_requires_detail_to_match_broad_code() {
1146 let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
1147 feature: SqlFeatureCode::Join,
1148 });
1149
1150 assert_eq!(
1151 ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
1152 ErrorCode::SQL_FEATURE_JOIN
1153 );
1154 assert_eq!(
1155 ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
1156 ErrorCode::QUERY_PLAN
1157 );
1158 }
1159
1160 #[test]
1161 fn detail_bearing_registry_entries_round_trip_directly() {
1162 assert!(!DETAIL_ERROR_CODES.is_empty());
1163
1164 for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
1165 assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
1166 assert_eq!(code.diagnostic_code(), diagnostic_code);
1167 assert_eq!(code.diagnostic_detail(), Some(detail));
1168 assert_eq!(detail.diagnostic_code(), diagnostic_code);
1169 }
1170 }
1171
1172 #[test]
1173 fn diagnostic_detail_reports_generated_broad_code() {
1174 let detail = DiagnosticDetail::UnsupportedSqlFeature {
1175 feature: SqlFeatureCode::Join,
1176 };
1177
1178 assert_eq!(
1179 detail.diagnostic_code(),
1180 DiagnosticCode::QueryUnsupportedSqlFeature
1181 );
1182 assert_eq!(format!("{detail:?}"), "65");
1183 }
1184
1185 #[test]
1186 fn public_error_codes_reconstruct_shifted_details() {
1187 assert_eq!(
1188 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
1189 DiagnosticCode::QueryUnknownAggregateTargetField
1190 );
1191 assert_eq!(
1192 ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
1193 Some(DiagnosticDetail::UnsupportedSqlFeature {
1194 feature: SqlFeatureCode::Join,
1195 })
1196 );
1197 assert_eq!(
1198 ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1199 Some(DiagnosticDetail::QueryProjection {
1200 reason: QueryProjectionCode::NumericLiteralRequired,
1201 })
1202 );
1203 assert_eq!(
1204 ErrorCode::QUERY_READ_PUBLIC_REQUIRES_LIMIT.diagnostic_detail(),
1205 Some(DiagnosticDetail::QueryReadAdmission {
1206 reason: QueryReadAdmissionCode::PublicQueryRequiresLimit,
1207 })
1208 );
1209 assert_eq!(
1210 ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1211 Some(DiagnosticDetail::SqlLowering {
1212 reason: SqlLoweringCode::DistinctOrderByProjection,
1213 })
1214 );
1215 assert_eq!(
1216 ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1217 Some(DiagnosticDetail::SqlWriteBoundary {
1218 boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1219 })
1220 );
1221 assert_eq!(
1222 ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1223 Some(DiagnosticDetail::SqlWriteBoundary {
1224 boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1225 })
1226 );
1227 }
1228}