1use std::fmt;
13
14mod fact;
15mod query_field;
16
17pub use fact::{
18 DiagnosticAggregateKind, DiagnosticBacklogResource, DiagnosticComponentKind,
19 DiagnosticConstraintContext, DiagnosticConstraintKind, DiagnosticDecodeReason,
20 DiagnosticExecutionBudgetResource, DiagnosticExecutionBudgetScope, DiagnosticExecutionLane,
21 DiagnosticFactSchemaMismatch, DiagnosticFactTag, DiagnosticFunctionKind,
22 DiagnosticMutationOperation, DiagnosticOperatorKind, DiagnosticTypeFamily,
23 MAX_PUBLIC_DIAGNOSTIC_FACTS, pack_u32_pair, unpack_u32_pair,
24 validate_known_diagnostic_fact_schema, validate_raw_diagnostic_fact_schema,
25};
26pub use query_field::{
27 MAX_PUBLIC_QUERY_FIELD_BYTES, QueryFieldRole, QueryFieldSchemaMismatch,
28 validate_query_field_schema,
29};
30
31#[remain::sorted]
38#[derive(Clone, Copy, Eq, Hash, PartialEq)]
39pub enum DiagnosticCode {
40 QueryIntent,
41 QueryInvalidContinuationCursor,
42 QueryNotFound,
43 QueryNotUnique,
44 QueryNumericNotRepresentable,
45 QueryNumericOverflow,
46 QueryPlan,
47 QueryReadAdmission,
48 QuerySqlSurfaceMismatch,
49 QuerySqlWriteBoundary,
50 QueryUnknownAggregateTargetField,
51 QueryUnorderedPagination,
52 QueryUnsupportedProjection,
53 QueryUnsupportedSqlFeature,
54 QueryValidate,
55 RuntimeConflict,
56 RuntimeCorruption,
57 RuntimeIncompatiblePersistedFormat,
58 RuntimeInternal,
59 RuntimeInvariantViolation,
60 RuntimeNotFound,
61 RuntimeUnsupported,
62 SchemaDdlAdmission,
63 StoreCorruption,
64 StoreInvariantViolation,
65 StoreNotFound,
66}
67
68impl DiagnosticCode {
69 #[must_use]
71 pub const fn class(self) -> ErrorClass {
72 match self {
73 Self::StoreCorruption | Self::RuntimeCorruption => ErrorClass::Corruption,
74 Self::RuntimeIncompatiblePersistedFormat => ErrorClass::IncompatiblePersistedFormat,
75 Self::QueryNotFound | Self::StoreNotFound | Self::RuntimeNotFound => {
76 ErrorClass::NotFound
77 }
78 Self::RuntimeConflict => ErrorClass::Conflict,
79 Self::QueryUnsupportedSqlFeature
80 | Self::QueryUnknownAggregateTargetField
81 | Self::QueryUnsupportedProjection
82 | Self::QuerySqlSurfaceMismatch
83 | Self::QuerySqlWriteBoundary
84 | Self::RuntimeUnsupported => ErrorClass::Unsupported,
85 Self::StoreInvariantViolation | Self::RuntimeInvariantViolation => {
86 ErrorClass::InvariantViolation
87 }
88 Self::RuntimeInternal => ErrorClass::Internal,
89 Self::QueryValidate
90 | Self::QueryIntent
91 | Self::QueryPlan
92 | Self::QueryReadAdmission
93 | Self::QueryUnorderedPagination
94 | Self::QueryInvalidContinuationCursor
95 | Self::QueryNotUnique
96 | Self::QueryNumericOverflow
97 | Self::QueryNumericNotRepresentable
98 | Self::SchemaDdlAdmission => ErrorClass::Query,
99 }
100 }
101
102 #[must_use]
104 pub const fn origin(self) -> ErrorOrigin {
105 match self {
106 Self::StoreNotFound | Self::StoreCorruption | Self::StoreInvariantViolation => {
107 ErrorOrigin::Store
108 }
109 Self::RuntimeCorruption
110 | Self::RuntimeIncompatiblePersistedFormat
111 | Self::RuntimeInvariantViolation
112 | Self::RuntimeConflict
113 | Self::RuntimeNotFound
114 | Self::RuntimeUnsupported
115 | Self::RuntimeInternal => ErrorOrigin::Runtime,
116 Self::QueryValidate
117 | Self::QueryIntent
118 | Self::QueryPlan
119 | Self::QueryReadAdmission
120 | Self::QueryUnorderedPagination
121 | Self::QueryInvalidContinuationCursor
122 | Self::QueryNotFound
123 | Self::QueryNotUnique
124 | Self::QueryNumericOverflow
125 | Self::QueryNumericNotRepresentable
126 | Self::QueryUnknownAggregateTargetField
127 | Self::QueryUnsupportedProjection
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::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::QueryUnsupportedSqlFeature => ErrorCode::QUERY_UNSUPPORTED_SQL_FEATURE,
154 Self::QuerySqlSurfaceMismatch => ErrorCode::QUERY_SQL_SURFACE_MISMATCH,
155 Self::QuerySqlWriteBoundary => ErrorCode::QUERY_SQL_WRITE_BOUNDARY,
156 Self::SchemaDdlAdmission => ErrorCode::SCHEMA_DDL_ADMISSION,
157 Self::StoreNotFound => ErrorCode::STORE_NOT_FOUND,
158 Self::StoreCorruption => ErrorCode::STORE_CORRUPTION,
159 Self::StoreInvariantViolation => ErrorCode::STORE_INVARIANT_VIOLATION,
160 Self::RuntimeCorruption => ErrorCode::RUNTIME_CORRUPTION,
161 Self::RuntimeIncompatiblePersistedFormat => {
162 ErrorCode::RUNTIME_INCOMPATIBLE_PERSISTED_FORMAT
163 }
164 Self::RuntimeInvariantViolation => ErrorCode::RUNTIME_INVARIANT_VIOLATION,
165 Self::RuntimeConflict => ErrorCode::RUNTIME_CONFLICT,
166 Self::RuntimeNotFound => ErrorCode::RUNTIME_NOT_FOUND,
167 Self::RuntimeUnsupported => ErrorCode::RUNTIME_UNSUPPORTED,
168 Self::RuntimeInternal => ErrorCode::RUNTIME_INTERNAL,
169 }
170 }
171}
172
173impl fmt::Debug for DiagnosticCode {
174 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175 fmt_compact_code(f, self.error_code().raw())
176 }
177}
178
179#[derive(Clone, Copy, Eq, Hash, PartialEq)]
191pub struct ErrorCode(u16);
192
193mod registry;
194
195impl ErrorCode {
196 #[must_use]
198 pub const fn from_raw(raw: u16) -> Self {
199 Self(raw)
200 }
201
202 #[must_use]
204 pub const fn raw(self) -> u16 {
205 self.0
206 }
207}
208
209impl fmt::Debug for ErrorCode {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 fmt_compact_code(f, self.raw())
212 }
213}
214
215#[remain::sorted]
222#[derive(Clone, Copy, Eq, Hash, PartialEq)]
223pub enum ErrorClass {
224 Conflict,
225 Corruption,
226 IncompatiblePersistedFormat,
227 Internal,
228 InvariantViolation,
229 NotFound,
230 Query,
231 Unsupported,
232}
233
234impl ErrorClass {
235 #[must_use]
237 pub const fn wire_code(self) -> u8 {
238 match self {
239 Self::Query => 1,
240 Self::Corruption => 2,
241 Self::IncompatiblePersistedFormat => 3,
242 Self::NotFound => 4,
243 Self::Internal => 5,
244 Self::Conflict => 6,
245 Self::Unsupported => 7,
246 Self::InvariantViolation => 8,
247 }
248 }
249
250 #[must_use]
252 pub const fn from_wire_code(code: u8) -> Option<Self> {
253 match code {
254 1 => Some(Self::Query),
255 2 => Some(Self::Corruption),
256 3 => Some(Self::IncompatiblePersistedFormat),
257 4 => Some(Self::NotFound),
258 5 => Some(Self::Internal),
259 6 => Some(Self::Conflict),
260 7 => Some(Self::Unsupported),
261 8 => Some(Self::InvariantViolation),
262 _ => None,
263 }
264 }
265}
266
267impl fmt::Debug for ErrorClass {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 fmt_compact_code(f, u16::from(self.wire_code()))
270 }
271}
272
273#[remain::sorted]
280#[derive(Clone, Copy, Eq, Hash, PartialEq)]
281pub enum ErrorOrigin {
282 Cursor,
283 Executor,
284 Identity,
285 Index,
286 Interface,
287 Planner,
288 Query,
289 Recovery,
290 Response,
291 Runtime,
292 Serialize,
293 Store,
294}
295
296impl ErrorOrigin {
297 #[must_use]
299 pub const fn wire_code(self) -> u8 {
300 match self {
301 Self::Cursor => 1,
302 Self::Executor => 2,
303 Self::Identity => 3,
304 Self::Index => 4,
305 Self::Interface => 5,
306 Self::Planner => 6,
307 Self::Query => 7,
308 Self::Recovery => 8,
309 Self::Response => 9,
310 Self::Runtime => 10,
311 Self::Serialize => 11,
312 Self::Store => 12,
313 }
314 }
315
316 #[must_use]
318 pub const fn from_known_wire_code(code: u8) -> Option<Self> {
319 match code {
320 1 => Some(Self::Cursor),
321 2 => Some(Self::Executor),
322 3 => Some(Self::Identity),
323 4 => Some(Self::Index),
324 5 => Some(Self::Interface),
325 6 => Some(Self::Planner),
326 7 => Some(Self::Query),
327 8 => Some(Self::Recovery),
328 9 => Some(Self::Response),
329 10 => Some(Self::Runtime),
330 11 => Some(Self::Serialize),
331 12 => Some(Self::Store),
332 _ => None,
333 }
334 }
335
336 #[must_use]
341 pub const fn from_wire_code(code: u8) -> Self {
342 match Self::from_known_wire_code(code) {
343 Some(origin) => origin,
344 None => Self::Runtime,
345 }
346 }
347}
348
349impl fmt::Debug for ErrorOrigin {
350 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
351 fmt_compact_code(f, u16::from(self.wire_code()))
352 }
353}
354
355#[repr(u16)]
362#[derive(Clone, Copy, Eq, Hash, PartialEq)]
363pub enum QueryErrorKind {
364 Validate,
365 Intent,
366 Plan,
367 UnorderedPagination,
368 InvalidContinuationCursor,
369 NotFound,
370 NotUnique,
371}
372
373impl fmt::Debug for QueryErrorKind {
374 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375 fmt_compact_code(f, *self as u16)
376 }
377}
378
379#[repr(u16)]
387#[derive(Clone, Copy, Eq, Hash, PartialEq)]
388pub enum QueryProjectionCode {
389 NumericLiteralRequired,
390 NumericScaleArguments,
391 NestedFieldPathPreview,
392 CaseConditionBooleanRequired,
393 NumericInputRequired,
394 TextOrBlobInputRequired,
395 TextInputRequired,
396 TextOrNullArgumentRequired,
397 IntegerOrNullArgumentRequired,
398 UnaryOperandIncompatible,
399 BinaryOperandsIncompatible,
400}
401
402impl fmt::Debug for QueryProjectionCode {
403 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
404 fmt_compact_code(f, *self as u16)
405 }
406}
407
408#[repr(u16)]
416#[derive(Clone, Copy, Eq, Hash, PartialEq)]
417pub enum QueryReadAdmissionCode {
418 PublicQueryRequiresLimit,
419 PublicQueryRequiresIndex,
420 UnboundedFullScanRejected,
421 SortRequiresMaterialization,
422 GroupedQueryRequiresLimits,
423 GroupedQueryExceedsBudget,
424 DiagnosticLaneDoesNotExecute,
425 ReturnedRowBoundExceedsPolicy,
426 PrimaryKeyInputExceedsPolicy,
427 InputDepthExceeded,
429 InputNodesExceeded,
431 InputBytesExceeded,
433 ExplainDoesNotAcceptCursor,
435}
436
437impl fmt::Debug for QueryReadAdmissionCode {
438 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439 fmt_compact_code(f, *self as u16)
440 }
441}
442
443#[repr(u16)]
450#[derive(Clone, Copy, Eq, Hash, PartialEq)]
451pub enum RuntimeErrorKind {
452 Corruption,
453 IncompatiblePersistedFormat,
454 InvariantViolation,
455 Conflict,
456 NotFound,
457 Unsupported,
458 Internal,
459}
460
461impl fmt::Debug for RuntimeErrorKind {
462 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
463 fmt_compact_code(f, *self as u16)
464 }
465}
466
467#[repr(u16)]
475#[derive(Clone, Copy, Eq, Hash, PartialEq)]
476pub enum RuntimeBoundaryCode {
477 SqlSurfaceControllerRequired,
478 SchemaSurfaceControllerRequired,
479 SqlQueryNoConfiguredEntities,
480 SqlQueryEntityNotFound,
481 SqlDdlTargetRequired,
482 SqlDdlEntityNotConfigured,
483 SqlIntrospectionDisabled,
484 MutationRequiredFieldMissing,
486 MutationManagedTimestampRegression,
488 PersistedRowLayoutOutsideAcceptedWindow,
490 PersistedRowSlotCountMismatch,
492 GeneratedFieldAfterDdlField,
494 JournalMutationRevisionExhausted,
496 ConstraintViolation,
498 AcceptedRowConstraintProgramCorrupt,
500 ConstraintActivationWriteBlocked,
502 GeneratedConstraintActivationStale,
504 MutationDatabaseOwnedFieldExplicit,
506 MutationBatchEmpty,
508 MutationBatchTooManyItems,
510 MutationBatchStagedBytesExceeded,
512 MutationBatchResultBytesExceeded,
514 MutationBatchStoreMismatch,
516 MutationBatchTooManyEntities,
518 MutationBatchDuplicateKey,
520 OperationalSurfaceControllerRequired,
522 ExactKeyBatchTooManyItems,
524 ExactKeyBatchInputBytesExceeded,
526 ExactKeyBatchStoredBytesExceeded,
528 ExactKeyBatchResultBytesExceeded,
530 ExecutionBudgetExceeded,
532 PageUnitTooLarge,
534 RequestExecutionScopeRequired,
536 RequestExecutionRootMismatch,
538 SqlQueryReplyBytesExceeded,
540 QueryExplainOutputExceeded,
541 QueryExplainDepthExceeded,
543 DatabaseStartupRecoveryPending,
545 SqlSurfacePolicyDenied,
547 SchemaSurfacePolicyDenied,
549 MutationBatchCommitWorkExceeded,
551 ConvergenceBacklogPressure,
553}
554
555impl fmt::Debug for RuntimeBoundaryCode {
556 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
557 fmt_compact_code(f, *self as u16)
558 }
559}
560
561#[repr(u16)]
569#[derive(Clone, Copy, Eq, Hash, PartialEq)]
570pub enum SqlFeatureCode {
571 AggregateFilterClause,
572 AlterStatementBeyondAlterTable,
573 AlterTableAddColumnDuplicateDefault,
574 AlterTableAddColumnModifiers,
575 AlterTableAddStatementBeyondAddColumn,
576 AlterTableAlterColumnDropUnsupportedAction,
577 AlterTableAlterColumnModifiers,
578 AlterTableAlterColumnSetUnsupportedAction,
579 AlterTableAlterColumnUnsupportedAction,
580 AlterTableAlterStatementBeyondAlterColumn,
581 AlterTableDropColumnIfExistsSyntax,
582 AlterTableDropColumnModifiers,
583 AlterTableDropStatementBeyondDropColumn,
584 AlterTableRenameColumnMissingTo,
585 AlterTableRenameColumnModifiers,
586 AlterTableRenameStatementBeyondRenameColumn,
587 AlterTableUnsupportedOperation,
588 ColumnAlias,
589 CreateIndexIfNotExistsSyntax,
590 CreateIndexKeyOrderingModifiers,
591 CreateIndexModifiers,
592 CreateStatementBeyondCreateIndex,
593 DescribeModifier,
594 DdlSchemaVersionDuplicateExpectedClause,
595 DdlSchemaVersionDuplicateSetClause,
596 DropIndexModifiers,
597 DropIndexIfExistsSyntax,
598 DropStatementBeyondDropIndex,
599 ExpressionIndexUnsupportedFunction,
600 Having,
601 Insert,
602 Join,
603 LikePatternBeyondTrailingPrefix,
604 LowerFieldPredicateUnsupported,
605 MultiStatementSql,
606 NestedAggregateInput,
607 NestedProjectionFunctionInArithmetic,
608 OrderByUnsupportedForm,
609 Other,
610 PredicateStartsWithFirstArgument,
611 QuotedIdentifiers,
612 ReturningUnsupportedShape,
613 ScalarFunctionExpressionPosition,
614 ScaleTakingNumericFunctionExpressionPosition,
615 ShowColumnsModifiers,
616 ShowEntitiesModifiers,
617 ShowIndexesModifiers,
618 ShowMemoryModifiers,
619 ShowStoresModifiers,
620 ShowUnsupportedCommand,
621 SimpleCaseExpression,
622 StandaloneLiteralProjectionItem,
623 UnionIntersectExcept,
624 UnsupportedFunctionNamespace,
625 Update,
626 UpperFieldPredicateUnsupported,
627 WindowFunction,
628 With,
629 NumericScaleFunctionArguments,
630 OrderByFieldNotOrderable,
631 ShowConstraintsModifiers,
632 AlterTableAddConstraintBeyondCheck,
633 AlterTableAddConstraintModifiers,
634 AlterTableDropConstraintIfExistsSyntax,
635 AlterTableDropConstraintModifiers,
636 AlterTableValidateBeyondConstraint,
637 AlterTableValidateConstraintModifiers,
638 ShowRelationsModifiers,
639}
640
641impl fmt::Debug for SqlFeatureCode {
642 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
643 fmt_compact_code(f, *self as u16)
644 }
645}
646
647#[repr(u16)]
656#[derive(Clone, Copy, Eq, Hash, PartialEq)]
657pub enum SqlLoweringCode {
658 EntityMismatch,
659 SelectProjectionShape,
660 SelectDistinct,
661 DistinctOrderByProjection,
662 GlobalAggregateProjection,
663 GlobalAggregateGroupBy,
664 SelectGroupByShape,
665 GroupedProjectionExplicitListRequired,
666 GroupedProjectionAggregateRequired,
667 GroupedProjectionNonGroupField,
668 GroupedProjectionScalarAfterAggregate,
669 HavingRequiresGroupBy,
670 SelectHavingShape,
671 AggregateInputExpressions,
672 WhereExpressionShape,
673 ParameterPlacement,
674 SqlDdlExecutionUnsupported,
675 BindingCount,
676 BindingFamily,
677 BindingLimit,
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 MutationRejectsShowRelations,
710}
711
712impl fmt::Debug for SqlSurfaceMismatchCode {
713 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
714 fmt_compact_code(f, *self as u16)
715 }
716}
717
718#[repr(u16)]
726#[derive(Clone, Copy, Eq, Hash, PartialEq)]
727pub enum SqlWriteBoundaryCode {
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 ResumableUpdateManagedFieldHasGlobalConstraint,
766}
767
768impl fmt::Debug for SqlWriteBoundaryCode {
769 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
770 fmt_compact_code(f, *self as u16)
771 }
772}
773
774#[repr(u16)]
782#[derive(Clone, Copy, Eq, Hash, PartialEq)]
783pub enum SchemaDdlAdmissionCode {
784 MissingExpectedSchemaVersion,
785 MissingNextSchemaVersion,
786 StaleExpectedSchemaVersion,
787 InvalidExpectedSchemaVersion,
788 InvalidNextSchemaVersion,
789 AcceptedSchemaChangeWithoutVersionBump,
790 EmptyVersionBump,
791 VersionGap,
792 VersionRollback,
793 FingerprintMethodMismatch,
794 UnsupportedTransitionClass,
795 PhysicalRunnerMissing,
796 ValidationFailed,
797 PublicationRaceLost,
798 InvalidAddColumnDefault,
799 InvalidAlterColumnDefault,
800 GeneratedIndexDropRejected,
801 SchemaRewriteRequiresMigration,
802 SchemaTransitionBudgetExceeded,
803 GeneratedFieldDefaultChangeRejected,
804 GeneratedFieldNullabilityChangeRejected,
805 RowLayoutVersionExhausted,
806}
807
808impl fmt::Debug for SchemaDdlAdmissionCode {
809 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
810 fmt_compact_code(f, *self as u16)
811 }
812}
813
814#[repr(u16)]
816#[derive(Clone, Copy, Eq, Hash, PartialEq)]
817pub enum SchemaMigrationCode {
818 Unadopted,
819 MissingMigration,
820 VersionGap,
821 Downgrade,
822 EmptyEntityVersionBump,
823 StaleAcceptedHead,
824 PlanChanged,
825 UnknownFromObject,
826 UnknownToObject,
827 KindMismatch,
828 IdentityConflict,
829 UnexplainedSchemaDifference,
830 UnsupportedTransform,
831 PhysicalRunnerMissing,
832 MigrationInProgress,
833 AbortTooLate,
834 ProgressCorrupt,
835 CandidateMismatch,
836 PublicationRaceLost,
837}
838
839impl SchemaMigrationCode {
840 #[must_use]
842 pub const fn diagnostic_code(self) -> DiagnosticCode {
843 match self {
844 Self::StaleAcceptedHead
845 | Self::PlanChanged
846 | Self::IdentityConflict
847 | Self::MigrationInProgress
848 | Self::AbortTooLate
849 | Self::PublicationRaceLost => DiagnosticCode::RuntimeConflict,
850 Self::ProgressCorrupt | Self::CandidateMismatch => DiagnosticCode::RuntimeCorruption,
851 Self::Unadopted
852 | Self::MissingMigration
853 | Self::VersionGap
854 | Self::Downgrade
855 | Self::EmptyEntityVersionBump
856 | Self::UnknownFromObject
857 | Self::UnknownToObject
858 | Self::KindMismatch
859 | Self::UnexplainedSchemaDifference
860 | Self::UnsupportedTransform
861 | Self::PhysicalRunnerMissing => DiagnosticCode::RuntimeUnsupported,
862 }
863 }
864}
865
866impl fmt::Debug for SchemaMigrationCode {
867 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
868 fmt_compact_code(f, *self as u16)
869 }
870}
871
872#[remain::sorted]
879#[derive(Clone, Copy, Eq, PartialEq)]
880pub enum DiagnosticDetail {
881 QueryKind { kind: QueryErrorKind },
882 QueryProjection { reason: QueryProjectionCode },
883 QueryReadAdmission { reason: QueryReadAdmissionCode },
884 RuntimeBoundary { boundary: RuntimeBoundaryCode },
885 RuntimeKind { kind: RuntimeErrorKind },
886 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
887 SchemaMigration { reason: SchemaMigrationCode },
888 SqlLowering { reason: SqlLoweringCode },
889 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
890 SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
891 UnsupportedSqlFeature { feature: SqlFeatureCode },
892}
893
894impl fmt::Debug for DiagnosticDetail {
895 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
896 fmt_compact_code(
897 f,
898 ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
899 )
900 }
901}
902
903#[derive(Clone, Eq, PartialEq)]
910pub struct Diagnostic {
911 code: DiagnosticCode,
912 origin: ErrorOrigin,
913 detail: Option<DiagnosticDetail>,
914}
915
916impl Diagnostic {
917 #[must_use]
919 pub const fn new(
920 code: DiagnosticCode,
921 origin: ErrorOrigin,
922 detail: Option<DiagnosticDetail>,
923 ) -> Self {
924 Self {
925 code,
926 origin,
927 detail,
928 }
929 }
930
931 #[must_use]
933 pub const fn from_code(code: DiagnosticCode) -> Self {
934 Self::new(code, code.origin(), None)
935 }
936
937 #[must_use]
939 pub const fn code(&self) -> DiagnosticCode {
940 self.code
941 }
942
943 #[must_use]
945 pub const fn class(&self) -> ErrorClass {
946 self.code.class()
947 }
948
949 #[must_use]
951 pub const fn origin(&self) -> ErrorOrigin {
952 self.origin
953 }
954
955 #[must_use]
957 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
958 self.detail.as_ref()
959 }
960
961 #[must_use]
963 pub const fn error_code(&self) -> ErrorCode {
964 ErrorCode::from_parts(self.code, self.detail)
965 }
966}
967
968impl fmt::Debug for Diagnostic {
969 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
970 write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
971 }
972}
973
974fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
975 write!(f, "{raw}")
976}
977
978#[cfg(test)]
979mod tests {
980 use super::{
981 Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
982 QueryProjectionCode, QueryReadAdmissionCode, SqlFeatureCode, SqlLoweringCode,
983 SqlWriteBoundaryCode,
984 registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
985 };
986
987 #[test]
988 fn diagnostic_from_code_uses_default_origin() {
989 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
990
991 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
992 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
993 }
994
995 #[test]
996 fn diagnostic_code_reports_broad_class() {
997 assert_eq!(
998 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
999 ErrorClass::Unsupported
1000 );
1001 assert_eq!(
1002 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
1003 ErrorClass::Unsupported
1004 );
1005 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
1006 assert_eq!(
1007 DiagnosticCode::StoreCorruption.class(),
1008 ErrorClass::Corruption
1009 );
1010 }
1011
1012 #[test]
1013 fn class_and_origin_wire_codes_round_trip() {
1014 for (class, raw) in [
1015 (ErrorClass::Query, 1),
1016 (ErrorClass::Corruption, 2),
1017 (ErrorClass::IncompatiblePersistedFormat, 3),
1018 (ErrorClass::NotFound, 4),
1019 (ErrorClass::Internal, 5),
1020 (ErrorClass::Conflict, 6),
1021 (ErrorClass::Unsupported, 7),
1022 (ErrorClass::InvariantViolation, 8),
1023 ] {
1024 assert_eq!(class.wire_code(), raw);
1025 assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
1026 assert_eq!(format!("{class:?}"), raw.to_string());
1027 }
1028
1029 for (origin, raw) in [
1030 (ErrorOrigin::Cursor, 1),
1031 (ErrorOrigin::Executor, 2),
1032 (ErrorOrigin::Identity, 3),
1033 (ErrorOrigin::Index, 4),
1034 (ErrorOrigin::Interface, 5),
1035 (ErrorOrigin::Planner, 6),
1036 (ErrorOrigin::Query, 7),
1037 (ErrorOrigin::Recovery, 8),
1038 (ErrorOrigin::Response, 9),
1039 (ErrorOrigin::Runtime, 10),
1040 (ErrorOrigin::Serialize, 11),
1041 (ErrorOrigin::Store, 12),
1042 ] {
1043 assert_eq!(origin.wire_code(), raw);
1044 assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
1045 assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
1046 assert_eq!(format!("{origin:?}"), raw.to_string());
1047 }
1048
1049 assert_eq!(ErrorClass::from_wire_code(0), None);
1050 assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
1051 assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
1052 }
1053
1054 #[test]
1055 fn public_error_codes_are_sequential() {
1056 let first = ORDERED_ERROR_CODES
1057 .first()
1058 .expect("public error-code registry is non-empty")
1059 .raw();
1060
1061 assert_eq!(first, 1);
1062
1063 for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
1064 let expected = first + u16::try_from(index).expect("test error-code index fits u16");
1065 assert_eq!(code.raw(), expected);
1066 assert_eq!(ErrorCode::known(code.raw()), Some(*code));
1067 assert!(code.is_known());
1068 }
1069
1070 let last = ORDERED_ERROR_CODES
1071 .last()
1072 .expect("public error-code registry is non-empty")
1073 .raw();
1074
1075 assert_eq!(last, 273);
1076 }
1077
1078 #[test]
1079 fn all_public_error_codes_round_trip_through_diagnostic_parts() {
1080 let first = ORDERED_ERROR_CODES
1081 .first()
1082 .expect("public error-code registry is non-empty")
1083 .raw();
1084 let last = ORDERED_ERROR_CODES
1085 .last()
1086 .expect("public error-code registry is non-empty")
1087 .raw();
1088
1089 for raw in first..=last {
1090 let code = ErrorCode::from_raw(raw);
1091 let diagnostic_code = code.diagnostic_code();
1092 let diagnostic_detail = code.diagnostic_detail();
1093 let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
1094
1095 assert_eq!(rebuilt.raw(), raw);
1096
1097 let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
1098
1099 assert_eq!(diagnostic.code(), diagnostic_code);
1100 assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
1101 assert_eq!(diagnostic.error_code().raw(), raw);
1102 }
1103 }
1104
1105 #[test]
1106 fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
1107 let first_unknown = ORDERED_ERROR_CODES
1108 .last()
1109 .expect("public error-code registry is non-empty")
1110 .raw()
1111 .checked_add(1)
1112 .expect("public error-code registry retains an unknown successor");
1113
1114 for raw in [0, first_unknown, u16::MAX] {
1115 let code = ErrorCode::from_raw(raw);
1116
1117 assert_eq!(ErrorCode::known(raw), None);
1118 assert!(!code.is_known());
1119 assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
1120 assert_eq!(code.diagnostic_detail(), None);
1121 assert_eq!(code.class(), ErrorClass::Internal);
1122
1123 let diagnostic = code.diagnostic(ErrorOrigin::Query);
1124
1125 assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
1126 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
1127 assert_eq!(diagnostic.detail(), None);
1128 assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
1129 }
1130 }
1131
1132 #[test]
1133 fn from_parts_requires_detail_to_match_broad_code() {
1134 let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
1135 feature: SqlFeatureCode::Join,
1136 });
1137
1138 assert_eq!(
1139 ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
1140 ErrorCode::SQL_FEATURE_JOIN
1141 );
1142 assert_eq!(
1143 ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
1144 ErrorCode::QUERY_PLAN
1145 );
1146 }
1147
1148 #[test]
1149 fn detail_bearing_registry_entries_round_trip_directly() {
1150 assert!(!DETAIL_ERROR_CODES.is_empty());
1151
1152 for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
1153 assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
1154 assert_eq!(code.diagnostic_code(), diagnostic_code);
1155 assert_eq!(code.diagnostic_detail(), Some(detail));
1156 assert_eq!(detail.diagnostic_code(), diagnostic_code);
1157 }
1158 }
1159
1160 #[test]
1161 fn diagnostic_detail_reports_generated_broad_code() {
1162 let detail = DiagnosticDetail::UnsupportedSqlFeature {
1163 feature: SqlFeatureCode::Join,
1164 };
1165
1166 assert_eq!(
1167 detail.diagnostic_code(),
1168 DiagnosticCode::QueryUnsupportedSqlFeature
1169 );
1170 assert_eq!(format!("{detail:?}"), "61");
1171 }
1172
1173 #[test]
1174 fn public_error_codes_reconstruct_shifted_details() {
1175 assert_eq!(
1176 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
1177 DiagnosticCode::QueryUnknownAggregateTargetField
1178 );
1179 assert_eq!(
1180 ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
1181 Some(DiagnosticDetail::UnsupportedSqlFeature {
1182 feature: SqlFeatureCode::Join,
1183 })
1184 );
1185 assert_eq!(
1186 ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1187 Some(DiagnosticDetail::QueryProjection {
1188 reason: QueryProjectionCode::NumericLiteralRequired,
1189 })
1190 );
1191 assert_eq!(
1192 ErrorCode::QUERY_READ_PUBLIC_REQUIRES_LIMIT.diagnostic_detail(),
1193 Some(DiagnosticDetail::QueryReadAdmission {
1194 reason: QueryReadAdmissionCode::PublicQueryRequiresLimit,
1195 })
1196 );
1197 assert_eq!(
1198 ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1199 Some(DiagnosticDetail::SqlLowering {
1200 reason: SqlLoweringCode::DistinctOrderByProjection,
1201 })
1202 );
1203 assert_eq!(
1204 ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1205 Some(DiagnosticDetail::SqlWriteBoundary {
1206 boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1207 })
1208 );
1209 assert_eq!(
1210 ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1211 Some(DiagnosticDetail::SqlWriteBoundary {
1212 boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1213 })
1214 );
1215 }
1216}