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}
434
435impl fmt::Debug for QueryReadAdmissionCode {
436 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
437 fmt_compact_code(f, *self as u16)
438 }
439}
440
441#[repr(u16)]
448#[derive(Clone, Copy, Eq, Hash, PartialEq)]
449pub enum RuntimeErrorKind {
450 Corruption,
451 IncompatiblePersistedFormat,
452 InvariantViolation,
453 Conflict,
454 NotFound,
455 Unsupported,
456 Internal,
457}
458
459impl fmt::Debug for RuntimeErrorKind {
460 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461 fmt_compact_code(f, *self as u16)
462 }
463}
464
465#[repr(u16)]
473#[derive(Clone, Copy, Eq, Hash, PartialEq)]
474pub enum RuntimeBoundaryCode {
475 SqlSurfaceControllerRequired,
476 SchemaSurfaceControllerRequired,
477 SqlQueryNoConfiguredEntities,
478 SqlQueryEntityNotFound,
479 SqlDdlTargetRequired,
480 SqlDdlEntityNotConfigured,
481 SqlIntrospectionDisabled,
482 MutationRequiredFieldMissing,
484 MutationManagedTimestampRegression,
486 PersistedRowLayoutOutsideAcceptedWindow,
488 PersistedRowSlotCountMismatch,
490 GeneratedFieldAfterDdlField,
492 JournalMutationRevisionExhausted,
494 ConstraintViolation,
496 AcceptedRowConstraintProgramCorrupt,
498 ConstraintActivationWriteBlocked,
500 GeneratedConstraintActivationStale,
502 MutationDatabaseOwnedFieldExplicit,
504 MutationBatchEmpty,
506 MutationBatchTooManyItems,
508 MutationBatchStagedBytesExceeded,
510 MutationBatchResultBytesExceeded,
512 MutationBatchStoreMismatch,
514 MutationBatchTooManyEntities,
516 MutationBatchDuplicateKey,
518 OperationalSurfaceControllerRequired,
520 ExactKeyBatchTooManyItems,
522 ExactKeyBatchInputBytesExceeded,
524 ExactKeyBatchStoredBytesExceeded,
526 ExactKeyBatchResultBytesExceeded,
528 ExecutionBudgetExceeded,
530 PageUnitTooLarge,
532 RequestExecutionScopeRequired,
534 RequestExecutionRootMismatch,
536 SqlQueryReplyBytesExceeded,
538 DatabaseStartupRecoveryPending,
540 SqlSurfacePolicyDenied,
542 SchemaSurfacePolicyDenied,
544 MutationBatchCommitWorkExceeded,
546 ConvergenceBacklogPressure,
548}
549
550impl fmt::Debug for RuntimeBoundaryCode {
551 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
552 fmt_compact_code(f, *self as u16)
553 }
554}
555
556#[repr(u16)]
564#[derive(Clone, Copy, Eq, Hash, PartialEq)]
565pub enum SqlFeatureCode {
566 AggregateFilterClause,
567 AlterStatementBeyondAlterTable,
568 AlterTableAddColumnDuplicateDefault,
569 AlterTableAddColumnModifiers,
570 AlterTableAddStatementBeyondAddColumn,
571 AlterTableAlterColumnDropUnsupportedAction,
572 AlterTableAlterColumnModifiers,
573 AlterTableAlterColumnSetUnsupportedAction,
574 AlterTableAlterColumnUnsupportedAction,
575 AlterTableAlterStatementBeyondAlterColumn,
576 AlterTableDropColumnIfExistsSyntax,
577 AlterTableDropColumnModifiers,
578 AlterTableDropStatementBeyondDropColumn,
579 AlterTableRenameColumnMissingTo,
580 AlterTableRenameColumnModifiers,
581 AlterTableRenameStatementBeyondRenameColumn,
582 AlterTableUnsupportedOperation,
583 ColumnAlias,
584 CreateIndexIfNotExistsSyntax,
585 CreateIndexKeyOrderingModifiers,
586 CreateIndexModifiers,
587 CreateStatementBeyondCreateIndex,
588 DescribeModifier,
589 DdlSchemaVersionDuplicateExpectedClause,
590 DdlSchemaVersionDuplicateSetClause,
591 DropIndexModifiers,
592 DropIndexIfExistsSyntax,
593 DropStatementBeyondDropIndex,
594 ExpressionIndexUnsupportedFunction,
595 Having,
596 Insert,
597 Join,
598 LikePatternBeyondTrailingPrefix,
599 LowerFieldPredicateUnsupported,
600 MultiStatementSql,
601 NestedAggregateInput,
602 NestedProjectionFunctionInArithmetic,
603 OrderByUnsupportedForm,
604 Other,
605 PredicateStartsWithFirstArgument,
606 QuotedIdentifiers,
607 ReturningUnsupportedShape,
608 ScalarFunctionExpressionPosition,
609 ScaleTakingNumericFunctionExpressionPosition,
610 ShowColumnsModifiers,
611 ShowEntitiesModifiers,
612 ShowIndexesModifiers,
613 ShowMemoryModifiers,
614 ShowStoresModifiers,
615 ShowUnsupportedCommand,
616 SimpleCaseExpression,
617 StandaloneLiteralProjectionItem,
618 UnionIntersectExcept,
619 UnsupportedFunctionNamespace,
620 Update,
621 UpperFieldPredicateUnsupported,
622 WindowFunction,
623 With,
624 NumericScaleFunctionArguments,
625 OrderByFieldNotOrderable,
626 ShowConstraintsModifiers,
627 AlterTableAddConstraintBeyondCheck,
628 AlterTableAddConstraintModifiers,
629 AlterTableDropConstraintIfExistsSyntax,
630 AlterTableDropConstraintModifiers,
631 AlterTableValidateBeyondConstraint,
632 AlterTableValidateConstraintModifiers,
633 ShowRelationsModifiers,
634}
635
636impl fmt::Debug for SqlFeatureCode {
637 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
638 fmt_compact_code(f, *self as u16)
639 }
640}
641
642#[repr(u16)]
651#[derive(Clone, Copy, Eq, Hash, PartialEq)]
652pub enum SqlLoweringCode {
653 EntityMismatch,
654 SelectProjectionShape,
655 SelectDistinct,
656 DistinctOrderByProjection,
657 GlobalAggregateProjection,
658 GlobalAggregateGroupBy,
659 SelectGroupByShape,
660 GroupedProjectionExplicitListRequired,
661 GroupedProjectionAggregateRequired,
662 GroupedProjectionNonGroupField,
663 GroupedProjectionScalarAfterAggregate,
664 HavingRequiresGroupBy,
665 SelectHavingShape,
666 AggregateInputExpressions,
667 WhereExpressionShape,
668 ParameterPlacement,
669 SqlDdlExecutionUnsupported,
670 BindingCount,
671 BindingFamily,
672 BindingLimit,
673}
674
675impl fmt::Debug for SqlLoweringCode {
676 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
677 fmt_compact_code(f, *self as u16)
678 }
679}
680
681#[repr(u16)]
689#[derive(Clone, Copy, Eq, Hash, PartialEq)]
690pub enum SqlSurfaceMismatchCode {
691 QueryRejectsInsert,
692 QueryRejectsUpdate,
693 QueryRejectsDelete,
694 MutationRejectsSelect,
695 MutationRejectsExplain,
696 MutationRejectsDescribe,
697 MutationRejectsShowIndexes,
698 MutationRejectsShowColumns,
699 MutationRejectsShowEntities,
700 MutationRejectsShowStores,
701 MutationRejectsShowMemory,
702 MutationRequiresExplicitUpdateIntent,
703 MutationRejectsShowConstraints,
704 MutationRejectsShowRelations,
705}
706
707impl fmt::Debug for SqlSurfaceMismatchCode {
708 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
709 fmt_compact_code(f, *self as u16)
710 }
711}
712
713#[repr(u16)]
721#[derive(Clone, Copy, Eq, Hash, PartialEq)]
722pub enum SqlWriteBoundaryCode {
723 PrimaryKeyLiteralIncompatible,
724 MissingPrimaryKey,
725 MissingRequiredFields,
726 ExplicitManagedField,
727 ExplicitGeneratedField,
728 InsertSelectRequiresScalar,
729 InsertSelectAggregateProjection,
730 InsertSelectWidthMismatch,
731 UpdatePrimaryKeyMutation,
732 InvalidFieldLiteral,
733 UnknownReturningField,
734 DuplicateReturningField,
735 UpdateMissingWherePredicate,
736 WriteOrderByUnsupportedShape,
737 ReturningResponseTooLarge,
738 ReturningRowsTooMany,
739 StagedRowsTooMany,
740 InsertDefaultRequiredField,
741 UpdateDefaultRequiredField,
742 UpdateDefaultDatabaseOwnedField,
743 ExactUpdateAssertionRequired,
744 ExactUpdateAssertionTooHigh,
745 ExactUpdateAffectedRowsExceeded,
746 ExactUpdateWindowUnsupported,
747 ExactUpdateScanBudgetExceeded,
748 ResumableUpdateWindowUnsupported,
749 ResumableUpdateReturningUnsupported,
750 ResumableUpdateRequiresJournaledStore,
751 ResumableUpdateAssignedFieldHasGlobalConstraint,
752 ResumableUpdateScopeDependsOnAssignedField,
753 ResumableUpdateScopeDependencyUnknown,
754 ResumableUpdateContinuationMalformed,
755 ResumableUpdateContinuationTargetMismatch,
756 ResumableUpdateContinuationSchemaMismatch,
757 ResumableUpdateContinuationScopeMismatch,
758 ResumableUpdateContinuationPatchMismatch,
759 ResumableUpdateContinuationBatchPolicyMismatch,
760 ResumableUpdateManagedFieldHasGlobalConstraint,
761}
762
763impl fmt::Debug for SqlWriteBoundaryCode {
764 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
765 fmt_compact_code(f, *self as u16)
766 }
767}
768
769#[repr(u16)]
777#[derive(Clone, Copy, Eq, Hash, PartialEq)]
778pub enum SchemaDdlAdmissionCode {
779 MissingExpectedSchemaVersion,
780 MissingNextSchemaVersion,
781 StaleExpectedSchemaVersion,
782 InvalidExpectedSchemaVersion,
783 InvalidNextSchemaVersion,
784 AcceptedSchemaChangeWithoutVersionBump,
785 EmptyVersionBump,
786 VersionGap,
787 VersionRollback,
788 FingerprintMethodMismatch,
789 UnsupportedTransitionClass,
790 PhysicalRunnerMissing,
791 ValidationFailed,
792 PublicationRaceLost,
793 InvalidAddColumnDefault,
794 InvalidAlterColumnDefault,
795 GeneratedIndexDropRejected,
796 SchemaRewriteRequiresMigration,
797 SchemaTransitionBudgetExceeded,
798 GeneratedFieldDefaultChangeRejected,
799 GeneratedFieldNullabilityChangeRejected,
800 RowLayoutVersionExhausted,
801}
802
803impl fmt::Debug for SchemaDdlAdmissionCode {
804 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
805 fmt_compact_code(f, *self as u16)
806 }
807}
808
809#[repr(u16)]
811#[derive(Clone, Copy, Eq, Hash, PartialEq)]
812pub enum SchemaMigrationCode {
813 Unadopted,
814 MissingMigration,
815 VersionGap,
816 Downgrade,
817 EmptyEntityVersionBump,
818 StaleAcceptedHead,
819 PlanChanged,
820 UnknownFromObject,
821 UnknownToObject,
822 KindMismatch,
823 IdentityConflict,
824 UnexplainedSchemaDifference,
825 UnsupportedTransform,
826 PhysicalRunnerMissing,
827 MigrationInProgress,
828 AbortTooLate,
829 ProgressCorrupt,
830 CandidateMismatch,
831 PublicationRaceLost,
832}
833
834impl SchemaMigrationCode {
835 #[must_use]
837 pub const fn diagnostic_code(self) -> DiagnosticCode {
838 match self {
839 Self::StaleAcceptedHead
840 | Self::PlanChanged
841 | Self::IdentityConflict
842 | Self::MigrationInProgress
843 | Self::AbortTooLate
844 | Self::PublicationRaceLost => DiagnosticCode::RuntimeConflict,
845 Self::ProgressCorrupt | Self::CandidateMismatch => DiagnosticCode::RuntimeCorruption,
846 Self::Unadopted
847 | Self::MissingMigration
848 | Self::VersionGap
849 | Self::Downgrade
850 | Self::EmptyEntityVersionBump
851 | Self::UnknownFromObject
852 | Self::UnknownToObject
853 | Self::KindMismatch
854 | Self::UnexplainedSchemaDifference
855 | Self::UnsupportedTransform
856 | Self::PhysicalRunnerMissing => DiagnosticCode::RuntimeUnsupported,
857 }
858 }
859}
860
861impl fmt::Debug for SchemaMigrationCode {
862 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
863 fmt_compact_code(f, *self as u16)
864 }
865}
866
867#[remain::sorted]
874#[derive(Clone, Copy, Eq, PartialEq)]
875pub enum DiagnosticDetail {
876 QueryKind { kind: QueryErrorKind },
877 QueryProjection { reason: QueryProjectionCode },
878 QueryReadAdmission { reason: QueryReadAdmissionCode },
879 RuntimeBoundary { boundary: RuntimeBoundaryCode },
880 RuntimeKind { kind: RuntimeErrorKind },
881 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
882 SchemaMigration { reason: SchemaMigrationCode },
883 SqlLowering { reason: SqlLoweringCode },
884 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
885 SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
886 UnsupportedSqlFeature { feature: SqlFeatureCode },
887}
888
889impl fmt::Debug for DiagnosticDetail {
890 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
891 fmt_compact_code(
892 f,
893 ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
894 )
895 }
896}
897
898#[derive(Clone, Eq, PartialEq)]
905pub struct Diagnostic {
906 code: DiagnosticCode,
907 origin: ErrorOrigin,
908 detail: Option<DiagnosticDetail>,
909}
910
911impl Diagnostic {
912 #[must_use]
914 pub const fn new(
915 code: DiagnosticCode,
916 origin: ErrorOrigin,
917 detail: Option<DiagnosticDetail>,
918 ) -> Self {
919 Self {
920 code,
921 origin,
922 detail,
923 }
924 }
925
926 #[must_use]
928 pub const fn from_code(code: DiagnosticCode) -> Self {
929 Self::new(code, code.origin(), None)
930 }
931
932 #[must_use]
934 pub const fn code(&self) -> DiagnosticCode {
935 self.code
936 }
937
938 #[must_use]
940 pub const fn class(&self) -> ErrorClass {
941 self.code.class()
942 }
943
944 #[must_use]
946 pub const fn origin(&self) -> ErrorOrigin {
947 self.origin
948 }
949
950 #[must_use]
952 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
953 self.detail.as_ref()
954 }
955
956 #[must_use]
958 pub const fn error_code(&self) -> ErrorCode {
959 ErrorCode::from_parts(self.code, self.detail)
960 }
961}
962
963impl fmt::Debug for Diagnostic {
964 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
965 write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
966 }
967}
968
969fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
970 write!(f, "{raw}")
971}
972
973#[cfg(test)]
974mod tests {
975 use super::{
976 Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
977 QueryProjectionCode, QueryReadAdmissionCode, SqlFeatureCode, SqlLoweringCode,
978 SqlWriteBoundaryCode,
979 registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
980 };
981
982 #[test]
983 fn diagnostic_from_code_uses_default_origin() {
984 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
985
986 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
987 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
988 }
989
990 #[test]
991 fn diagnostic_code_reports_broad_class() {
992 assert_eq!(
993 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
994 ErrorClass::Unsupported
995 );
996 assert_eq!(
997 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
998 ErrorClass::Unsupported
999 );
1000 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
1001 assert_eq!(
1002 DiagnosticCode::StoreCorruption.class(),
1003 ErrorClass::Corruption
1004 );
1005 }
1006
1007 #[test]
1008 fn class_and_origin_wire_codes_round_trip() {
1009 for (class, raw) in [
1010 (ErrorClass::Query, 1),
1011 (ErrorClass::Corruption, 2),
1012 (ErrorClass::IncompatiblePersistedFormat, 3),
1013 (ErrorClass::NotFound, 4),
1014 (ErrorClass::Internal, 5),
1015 (ErrorClass::Conflict, 6),
1016 (ErrorClass::Unsupported, 7),
1017 (ErrorClass::InvariantViolation, 8),
1018 ] {
1019 assert_eq!(class.wire_code(), raw);
1020 assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
1021 assert_eq!(format!("{class:?}"), raw.to_string());
1022 }
1023
1024 for (origin, raw) in [
1025 (ErrorOrigin::Cursor, 1),
1026 (ErrorOrigin::Executor, 2),
1027 (ErrorOrigin::Identity, 3),
1028 (ErrorOrigin::Index, 4),
1029 (ErrorOrigin::Interface, 5),
1030 (ErrorOrigin::Planner, 6),
1031 (ErrorOrigin::Query, 7),
1032 (ErrorOrigin::Recovery, 8),
1033 (ErrorOrigin::Response, 9),
1034 (ErrorOrigin::Runtime, 10),
1035 (ErrorOrigin::Serialize, 11),
1036 (ErrorOrigin::Store, 12),
1037 ] {
1038 assert_eq!(origin.wire_code(), raw);
1039 assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
1040 assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
1041 assert_eq!(format!("{origin:?}"), raw.to_string());
1042 }
1043
1044 assert_eq!(ErrorClass::from_wire_code(0), None);
1045 assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
1046 assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
1047 }
1048
1049 #[test]
1050 fn public_error_codes_are_sequential() {
1051 let first = ORDERED_ERROR_CODES
1052 .first()
1053 .expect("public error-code registry is non-empty")
1054 .raw();
1055
1056 assert_eq!(first, 1);
1057
1058 for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
1059 let expected = first + u16::try_from(index).expect("test error-code index fits u16");
1060 assert_eq!(code.raw(), expected);
1061 assert_eq!(ErrorCode::known(code.raw()), Some(*code));
1062 assert!(code.is_known());
1063 }
1064
1065 let last = ORDERED_ERROR_CODES
1066 .last()
1067 .expect("public error-code registry is non-empty")
1068 .raw();
1069
1070 assert_eq!(last, 270);
1071 }
1072
1073 #[test]
1074 fn all_public_error_codes_round_trip_through_diagnostic_parts() {
1075 let first = ORDERED_ERROR_CODES
1076 .first()
1077 .expect("public error-code registry is non-empty")
1078 .raw();
1079 let last = ORDERED_ERROR_CODES
1080 .last()
1081 .expect("public error-code registry is non-empty")
1082 .raw();
1083
1084 for raw in first..=last {
1085 let code = ErrorCode::from_raw(raw);
1086 let diagnostic_code = code.diagnostic_code();
1087 let diagnostic_detail = code.diagnostic_detail();
1088 let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
1089
1090 assert_eq!(rebuilt.raw(), raw);
1091
1092 let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
1093
1094 assert_eq!(diagnostic.code(), diagnostic_code);
1095 assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
1096 assert_eq!(diagnostic.error_code().raw(), raw);
1097 }
1098 }
1099
1100 #[test]
1101 fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
1102 let first_unknown = ORDERED_ERROR_CODES
1103 .last()
1104 .expect("public error-code registry is non-empty")
1105 .raw()
1106 .checked_add(1)
1107 .expect("public error-code registry retains an unknown successor");
1108
1109 for raw in [0, first_unknown, 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:?}"), "61");
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}