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