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 SqlQueryReplyBytesExceeded,
556}
557
558impl fmt::Debug for RuntimeBoundaryCode {
559 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
560 fmt_compact_code(f, *self as u16)
561 }
562}
563
564#[repr(u16)]
572#[derive(Clone, Copy, Eq, Hash, PartialEq)]
573pub enum SqlFeatureCode {
574 AggregateFilterClause,
575 AlterStatementBeyondAlterTable,
576 AlterTableAddColumnDuplicateDefault,
577 AlterTableAddColumnModifiers,
578 AlterTableAddStatementBeyondAddColumn,
579 AlterTableAlterColumnDropUnsupportedAction,
580 AlterTableAlterColumnModifiers,
581 AlterTableAlterColumnSetUnsupportedAction,
582 AlterTableAlterColumnUnsupportedAction,
583 AlterTableAlterStatementBeyondAlterColumn,
584 AlterTableDropColumnIfExistsSyntax,
585 AlterTableDropColumnModifiers,
586 AlterTableDropStatementBeyondDropColumn,
587 AlterTableRenameColumnMissingTo,
588 AlterTableRenameColumnModifiers,
589 AlterTableRenameStatementBeyondRenameColumn,
590 AlterTableUnsupportedOperation,
591 ColumnAlias,
592 CreateIndexIfNotExistsSyntax,
593 CreateIndexKeyOrderingModifiers,
594 CreateIndexModifiers,
595 CreateStatementBeyondCreateIndex,
596 DescribeModifier,
597 DdlSchemaVersionDuplicateExpectedClause,
598 DdlSchemaVersionDuplicateSetClause,
599 DropIndexModifiers,
600 DropIndexIfExistsSyntax,
601 DropStatementBeyondDropIndex,
602 ExpressionIndexUnsupportedFunction,
603 Having,
604 Insert,
605 Join,
606 LikePatternBeyondTrailingPrefix,
607 LowerFieldPredicateUnsupported,
608 MultiStatementSql,
609 NestedAggregateInput,
610 NestedProjectionFunctionInArithmetic,
611 OrderByUnsupportedForm,
612 Other,
613 PredicateStartsWithFirstArgument,
614 QuotedIdentifiers,
615 ReturningUnsupportedShape,
616 ScalarFunctionExpressionPosition,
617 ScaleTakingNumericFunctionExpressionPosition,
618 SearchedCaseGroupedOrderBy,
619 ShowColumnsModifiers,
620 ShowEntitiesModifiers,
621 ShowIndexesModifiers,
622 ShowMemoryModifiers,
623 ShowStoresModifiers,
624 ShowUnsupportedCommand,
625 SimpleCaseExpression,
626 StandaloneLiteralProjectionItem,
627 SupportedGroupedOrderByExpressionFamily,
628 SupportedOrderByExpressionFamily,
629 UnionIntersectExcept,
630 UnsupportedFunctionNamespace,
631 Update,
632 UpperFieldPredicateUnsupported,
633 WindowFunction,
634 With,
635 NumericScaleFunctionArguments,
636 OrderByFieldNotOrderable,
637 ShowConstraintsModifiers,
638 AlterTableAddConstraintBeyondCheck,
639 AlterTableAddConstraintModifiers,
640 AlterTableDropConstraintIfExistsSyntax,
641 AlterTableDropConstraintModifiers,
642 AlterTableValidateBeyondConstraint,
643 AlterTableValidateConstraintModifiers,
644 ShowRelationsModifiers,
645}
646
647impl fmt::Debug for SqlFeatureCode {
648 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
649 fmt_compact_code(f, *self as u16)
650 }
651}
652
653#[repr(u16)]
662#[derive(Clone, Copy, Eq, Hash, PartialEq)]
663pub enum SqlLoweringCode {
664 EntityMismatch,
665 SelectProjectionShape,
666 SelectDistinct,
667 DistinctOrderByProjection,
668 GlobalAggregateProjection,
669 GlobalAggregateGroupBy,
670 SelectGroupByShape,
671 GroupedProjectionExplicitListRequired,
672 GroupedProjectionAggregateRequired,
673 GroupedProjectionNonGroupField,
674 GroupedProjectionScalarAfterAggregate,
675 HavingRequiresGroupBy,
676 SelectHavingShape,
677 AggregateInputExpressions,
678 WhereExpressionShape,
679 ParameterPlacement,
680 SqlDdlExecutionUnsupported,
681}
682
683impl fmt::Debug for SqlLoweringCode {
684 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
685 fmt_compact_code(f, *self as u16)
686 }
687}
688
689#[repr(u16)]
697#[derive(Clone, Copy, Eq, Hash, PartialEq)]
698pub enum SqlSurfaceMismatchCode {
699 QueryRejectsInsert,
700 QueryRejectsUpdate,
701 QueryRejectsDelete,
702 MutationRejectsSelect,
703 MutationRejectsExplain,
704 MutationRejectsDescribe,
705 MutationRejectsShowIndexes,
706 MutationRejectsShowColumns,
707 MutationRejectsShowEntities,
708 MutationRejectsShowStores,
709 MutationRejectsShowMemory,
710 MutationRequiresExplicitUpdateIntent,
711 MutationRejectsShowConstraints,
712 MutationRejectsShowRelations,
713}
714
715impl fmt::Debug for SqlSurfaceMismatchCode {
716 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
717 fmt_compact_code(f, *self as u16)
718 }
719}
720
721#[repr(u16)]
729#[derive(Clone, Copy, Eq, Hash, PartialEq)]
730pub enum SqlWriteBoundaryCode {
731 PrimaryKeyLiteralShape,
732 PrimaryKeyLiteralIncompatible,
733 MissingPrimaryKey,
734 MissingRequiredFields,
735 ExplicitManagedField,
736 ExplicitGeneratedField,
737 InsertSelectRequiresScalar,
738 InsertSelectAggregateProjection,
739 InsertSelectWidthMismatch,
740 UpdatePrimaryKeyMutation,
741 InvalidFieldLiteral,
742 UnknownReturningField,
743 DuplicateReturningField,
744 UpdateMissingWherePredicate,
745 WriteOrderByUnsupportedShape,
746 ReturningResponseTooLarge,
747 ReturningRowsTooMany,
748 StagedRowsTooMany,
749 InsertDefaultRequiredField,
750 UpdateDefaultRequiredField,
751 UpdateDefaultDatabaseOwnedField,
752 ExactUpdateAssertionRequired,
753 ExactUpdateAssertionTooHigh,
754 ExactUpdateAffectedRowsExceeded,
755 ExactUpdateWindowUnsupported,
756 ExactUpdateScanBudgetExceeded,
757 ResumableUpdateWindowUnsupported,
758 ResumableUpdateReturningUnsupported,
759 ResumableUpdateRequiresJournaledStore,
760 ResumableUpdateAssignedFieldHasGlobalConstraint,
761 ResumableUpdateScopeDependsOnAssignedField,
762 ResumableUpdateScopeDependencyUnknown,
763 ResumableUpdateContinuationMalformed,
764 ResumableUpdateContinuationTargetMismatch,
765 ResumableUpdateContinuationSchemaMismatch,
766 ResumableUpdateContinuationScopeMismatch,
767 ResumableUpdateContinuationPatchMismatch,
768 ResumableUpdateContinuationBatchPolicyMismatch,
769 ResumableUpdateSingleRowResourceExceeded,
770 ResumableUpdateManagedFieldHasGlobalConstraint,
771 ResumableUpdateContinuationOperationMismatch,
772}
773
774impl fmt::Debug for SqlWriteBoundaryCode {
775 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
776 fmt_compact_code(f, *self as u16)
777 }
778}
779
780#[repr(u16)]
788#[derive(Clone, Copy, Eq, Hash, PartialEq)]
789pub enum SchemaDdlAdmissionCode {
790 MissingExpectedSchemaVersion,
791 MissingNextSchemaVersion,
792 StaleExpectedSchemaVersion,
793 InvalidExpectedSchemaVersion,
794 InvalidNextSchemaVersion,
795 AcceptedSchemaChangeWithoutVersionBump,
796 EmptyVersionBump,
797 VersionGap,
798 VersionRollback,
799 FingerprintMethodMismatch,
800 UnsupportedTransitionClass,
801 PhysicalRunnerMissing,
802 ValidationFailed,
803 PublicationRaceLost,
804 InvalidAddColumnDefault,
805 InvalidAlterColumnDefault,
806 GeneratedIndexDropRejected,
807 SchemaRewriteRequiresMigration,
808 SchemaTransitionBudgetExceeded,
809 GeneratedFieldDefaultChangeRejected,
810 GeneratedFieldNullabilityChangeRejected,
811 RowLayoutVersionExhausted,
812}
813
814impl fmt::Debug for SchemaDdlAdmissionCode {
815 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
816 fmt_compact_code(f, *self as u16)
817 }
818}
819
820#[repr(u16)]
822#[derive(Clone, Copy, Eq, Hash, PartialEq)]
823pub enum SchemaMigrationCode {
824 Unadopted,
825 MissingMigration,
826 VersionGap,
827 Downgrade,
828 EmptyEntityVersionBump,
829 DuplicateEntityTransition,
830 StaleAcceptedHead,
831 PlanChanged,
832 DuplicateRenameSource,
833 DuplicateRenameTarget,
834 UnknownFromObject,
835 UnknownToObject,
836 KindMismatch,
837 IdentityConflict,
838 IncompleteRenameCoverage,
839 UnexplainedSchemaDifference,
840 UnsupportedTransform,
841 TransformFinding,
842 UniqueIndexFinding,
843 RelationFinding,
844 ConstraintFinding,
845 PhysicalRunnerMissing,
846 MigrationInProgress,
847 AbortTooLate,
848 ProgressCorrupt,
849 CandidateMismatch,
850 PublicationRaceLost,
851}
852
853impl SchemaMigrationCode {
854 #[must_use]
856 pub const fn diagnostic_code(self) -> DiagnosticCode {
857 match self {
858 Self::StaleAcceptedHead
859 | Self::PlanChanged
860 | Self::IdentityConflict
861 | Self::MigrationInProgress
862 | Self::AbortTooLate
863 | Self::PublicationRaceLost => DiagnosticCode::RuntimeConflict,
864 Self::ProgressCorrupt | Self::CandidateMismatch => DiagnosticCode::RuntimeCorruption,
865 Self::Unadopted
866 | Self::MissingMigration
867 | Self::VersionGap
868 | Self::Downgrade
869 | Self::EmptyEntityVersionBump
870 | Self::DuplicateEntityTransition
871 | Self::DuplicateRenameSource
872 | Self::DuplicateRenameTarget
873 | Self::UnknownFromObject
874 | Self::UnknownToObject
875 | Self::KindMismatch
876 | Self::IncompleteRenameCoverage
877 | Self::UnexplainedSchemaDifference
878 | Self::UnsupportedTransform
879 | Self::TransformFinding
880 | Self::UniqueIndexFinding
881 | Self::RelationFinding
882 | Self::ConstraintFinding
883 | Self::PhysicalRunnerMissing => DiagnosticCode::RuntimeUnsupported,
884 }
885 }
886}
887
888impl fmt::Debug for SchemaMigrationCode {
889 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
890 fmt_compact_code(f, *self as u16)
891 }
892}
893
894#[remain::sorted]
901#[derive(Clone, Copy, Eq, PartialEq)]
902pub enum DiagnosticDetail {
903 QueryKind { kind: QueryErrorKind },
904 QueryProjection { reason: QueryProjectionCode },
905 QueryReadAdmission { reason: QueryReadAdmissionCode },
906 QueryResultShape { reason: QueryResultShapeCode },
907 RuntimeBoundary { boundary: RuntimeBoundaryCode },
908 RuntimeKind { kind: RuntimeErrorKind },
909 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
910 SchemaMigration { reason: SchemaMigrationCode },
911 SqlLowering { reason: SqlLoweringCode },
912 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
913 SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
914 UnsupportedSqlFeature { feature: SqlFeatureCode },
915}
916
917impl fmt::Debug for DiagnosticDetail {
918 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
919 fmt_compact_code(
920 f,
921 ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
922 )
923 }
924}
925
926#[derive(Clone, Eq, PartialEq)]
933pub struct Diagnostic {
934 code: DiagnosticCode,
935 origin: ErrorOrigin,
936 detail: Option<DiagnosticDetail>,
937}
938
939impl Diagnostic {
940 #[must_use]
942 pub const fn new(
943 code: DiagnosticCode,
944 origin: ErrorOrigin,
945 detail: Option<DiagnosticDetail>,
946 ) -> Self {
947 Self {
948 code,
949 origin,
950 detail,
951 }
952 }
953
954 #[must_use]
956 pub const fn from_code(code: DiagnosticCode) -> Self {
957 Self::new(code, code.origin(), None)
958 }
959
960 #[must_use]
962 pub const fn code(&self) -> DiagnosticCode {
963 self.code
964 }
965
966 #[must_use]
968 pub const fn class(&self) -> ErrorClass {
969 self.code.class()
970 }
971
972 #[must_use]
974 pub const fn origin(&self) -> ErrorOrigin {
975 self.origin
976 }
977
978 #[must_use]
980 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
981 self.detail.as_ref()
982 }
983
984 #[must_use]
986 pub const fn error_code(&self) -> ErrorCode {
987 ErrorCode::from_parts(self.code, self.detail)
988 }
989}
990
991impl fmt::Debug for Diagnostic {
992 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
993 write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
994 }
995}
996
997fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
998 write!(f, "{raw}")
999}
1000
1001#[cfg(test)]
1002mod tests {
1003 use super::{
1004 Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
1005 QueryProjectionCode, QueryReadAdmissionCode, SqlFeatureCode, SqlLoweringCode,
1006 SqlWriteBoundaryCode,
1007 registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
1008 };
1009
1010 #[test]
1011 fn diagnostic_from_code_uses_default_origin() {
1012 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
1013
1014 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
1015 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
1016 }
1017
1018 #[test]
1019 fn diagnostic_code_reports_broad_class() {
1020 assert_eq!(
1021 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
1022 ErrorClass::Unsupported
1023 );
1024 assert_eq!(
1025 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
1026 ErrorClass::Unsupported
1027 );
1028 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
1029 assert_eq!(
1030 DiagnosticCode::StoreCorruption.class(),
1031 ErrorClass::Corruption
1032 );
1033 }
1034
1035 #[test]
1036 fn class_and_origin_wire_codes_round_trip() {
1037 for (class, raw) in [
1038 (ErrorClass::Query, 1),
1039 (ErrorClass::Corruption, 2),
1040 (ErrorClass::IncompatiblePersistedFormat, 3),
1041 (ErrorClass::NotFound, 4),
1042 (ErrorClass::Internal, 5),
1043 (ErrorClass::Conflict, 6),
1044 (ErrorClass::Unsupported, 7),
1045 (ErrorClass::InvariantViolation, 8),
1046 ] {
1047 assert_eq!(class.wire_code(), raw);
1048 assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
1049 assert_eq!(format!("{class:?}"), raw.to_string());
1050 }
1051
1052 for (origin, raw) in [
1053 (ErrorOrigin::Cursor, 1),
1054 (ErrorOrigin::Executor, 2),
1055 (ErrorOrigin::Identity, 3),
1056 (ErrorOrigin::Index, 4),
1057 (ErrorOrigin::Interface, 5),
1058 (ErrorOrigin::Planner, 6),
1059 (ErrorOrigin::Query, 7),
1060 (ErrorOrigin::Recovery, 8),
1061 (ErrorOrigin::Response, 9),
1062 (ErrorOrigin::Runtime, 10),
1063 (ErrorOrigin::Serialize, 11),
1064 (ErrorOrigin::Store, 12),
1065 ] {
1066 assert_eq!(origin.wire_code(), raw);
1067 assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
1068 assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
1069 assert_eq!(format!("{origin:?}"), raw.to_string());
1070 }
1071
1072 assert_eq!(ErrorClass::from_wire_code(0), None);
1073 assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
1074 assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
1075 }
1076
1077 #[test]
1078 fn public_error_codes_are_sequential() {
1079 let first = ORDERED_ERROR_CODES
1080 .first()
1081 .expect("public error-code registry is non-empty")
1082 .raw();
1083
1084 assert_eq!(first, 1);
1085
1086 for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
1087 let expected = first + u16::try_from(index).expect("test error-code index fits u16");
1088 assert_eq!(code.raw(), expected);
1089 assert_eq!(ErrorCode::known(code.raw()), Some(*code));
1090 assert!(code.is_known());
1091 }
1092
1093 let last = ORDERED_ERROR_CODES
1094 .last()
1095 .expect("public error-code registry is non-empty")
1096 .raw();
1097
1098 assert_eq!(last, 279);
1099 }
1100
1101 #[test]
1102 fn all_public_error_codes_round_trip_through_diagnostic_parts() {
1103 let first = ORDERED_ERROR_CODES
1104 .first()
1105 .expect("public error-code registry is non-empty")
1106 .raw();
1107 let last = ORDERED_ERROR_CODES
1108 .last()
1109 .expect("public error-code registry is non-empty")
1110 .raw();
1111
1112 for raw in first..=last {
1113 let code = ErrorCode::from_raw(raw);
1114 let diagnostic_code = code.diagnostic_code();
1115 let diagnostic_detail = code.diagnostic_detail();
1116 let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
1117
1118 assert_eq!(rebuilt.raw(), raw);
1119
1120 let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
1121
1122 assert_eq!(diagnostic.code(), diagnostic_code);
1123 assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
1124 assert_eq!(diagnostic.error_code().raw(), raw);
1125 }
1126 }
1127
1128 #[test]
1129 fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
1130 let first_unknown = ORDERED_ERROR_CODES
1131 .last()
1132 .expect("public error-code registry is non-empty")
1133 .raw()
1134 .checked_add(1)
1135 .expect("public error-code registry retains an unknown successor");
1136
1137 for raw in [0, first_unknown, u16::MAX] {
1138 let code = ErrorCode::from_raw(raw);
1139
1140 assert_eq!(ErrorCode::known(raw), None);
1141 assert!(!code.is_known());
1142 assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
1143 assert_eq!(code.diagnostic_detail(), None);
1144 assert_eq!(code.class(), ErrorClass::Internal);
1145
1146 let diagnostic = code.diagnostic(ErrorOrigin::Query);
1147
1148 assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
1149 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
1150 assert_eq!(diagnostic.detail(), None);
1151 assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
1152 }
1153 }
1154
1155 #[test]
1156 fn from_parts_requires_detail_to_match_broad_code() {
1157 let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
1158 feature: SqlFeatureCode::Join,
1159 });
1160
1161 assert_eq!(
1162 ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
1163 ErrorCode::SQL_FEATURE_JOIN
1164 );
1165 assert_eq!(
1166 ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
1167 ErrorCode::QUERY_PLAN
1168 );
1169 }
1170
1171 #[test]
1172 fn detail_bearing_registry_entries_round_trip_directly() {
1173 assert!(!DETAIL_ERROR_CODES.is_empty());
1174
1175 for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
1176 assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
1177 assert_eq!(code.diagnostic_code(), diagnostic_code);
1178 assert_eq!(code.diagnostic_detail(), Some(detail));
1179 assert_eq!(detail.diagnostic_code(), diagnostic_code);
1180 }
1181 }
1182
1183 #[test]
1184 fn diagnostic_detail_reports_generated_broad_code() {
1185 let detail = DiagnosticDetail::UnsupportedSqlFeature {
1186 feature: SqlFeatureCode::Join,
1187 };
1188
1189 assert_eq!(
1190 detail.diagnostic_code(),
1191 DiagnosticCode::QueryUnsupportedSqlFeature
1192 );
1193 assert_eq!(format!("{detail:?}"), "65");
1194 }
1195
1196 #[test]
1197 fn public_error_codes_reconstruct_shifted_details() {
1198 assert_eq!(
1199 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
1200 DiagnosticCode::QueryUnknownAggregateTargetField
1201 );
1202 assert_eq!(
1203 ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
1204 Some(DiagnosticDetail::UnsupportedSqlFeature {
1205 feature: SqlFeatureCode::Join,
1206 })
1207 );
1208 assert_eq!(
1209 ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1210 Some(DiagnosticDetail::QueryProjection {
1211 reason: QueryProjectionCode::NumericLiteralRequired,
1212 })
1213 );
1214 assert_eq!(
1215 ErrorCode::QUERY_READ_PUBLIC_REQUIRES_LIMIT.diagnostic_detail(),
1216 Some(DiagnosticDetail::QueryReadAdmission {
1217 reason: QueryReadAdmissionCode::PublicQueryRequiresLimit,
1218 })
1219 );
1220 assert_eq!(
1221 ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1222 Some(DiagnosticDetail::SqlLowering {
1223 reason: SqlLoweringCode::DistinctOrderByProjection,
1224 })
1225 );
1226 assert_eq!(
1227 ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1228 Some(DiagnosticDetail::SqlWriteBoundary {
1229 boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1230 })
1231 );
1232 assert_eq!(
1233 ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1234 Some(DiagnosticDetail::SqlWriteBoundary {
1235 boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1236 })
1237 );
1238 }
1239}