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 MemoryBucketSizeMismatch,
555}
556
557impl fmt::Debug for RuntimeBoundaryCode {
558 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
559 fmt_compact_code(f, *self as u16)
560 }
561}
562
563#[repr(u16)]
571#[derive(Clone, Copy, Eq, Hash, PartialEq)]
572pub enum SqlFeatureCode {
573 AggregateFilterClause,
574 AlterStatementBeyondAlterTable,
575 AlterTableAddColumnDuplicateDefault,
576 AlterTableAddColumnModifiers,
577 AlterTableAddStatementBeyondAddColumn,
578 AlterTableAlterColumnDropUnsupportedAction,
579 AlterTableAlterColumnModifiers,
580 AlterTableAlterColumnSetUnsupportedAction,
581 AlterTableAlterColumnUnsupportedAction,
582 AlterTableAlterStatementBeyondAlterColumn,
583 AlterTableDropColumnIfExistsSyntax,
584 AlterTableDropColumnModifiers,
585 AlterTableDropStatementBeyondDropColumn,
586 AlterTableRenameColumnMissingTo,
587 AlterTableRenameColumnModifiers,
588 AlterTableRenameStatementBeyondRenameColumn,
589 AlterTableUnsupportedOperation,
590 ColumnAlias,
591 CreateIndexIfNotExistsSyntax,
592 CreateIndexKeyOrderingModifiers,
593 CreateIndexModifiers,
594 CreateStatementBeyondCreateIndex,
595 DescribeModifier,
596 DdlSchemaVersionDuplicateExpectedClause,
597 DdlSchemaVersionDuplicateSetClause,
598 DropIndexModifiers,
599 DropIndexIfExistsSyntax,
600 DropStatementBeyondDropIndex,
601 ExpressionIndexUnsupportedFunction,
602 Having,
603 Insert,
604 Join,
605 LikePatternBeyondTrailingPrefix,
606 LowerFieldPredicateUnsupported,
607 MultiStatementSql,
608 NestedAggregateInput,
609 NestedProjectionFunctionInArithmetic,
610 OrderByUnsupportedForm,
611 Other,
612 PredicateStartsWithFirstArgument,
613 QuotedIdentifiers,
614 ReturningUnsupportedShape,
615 ScalarFunctionExpressionPosition,
616 ScaleTakingNumericFunctionExpressionPosition,
617 ShowColumnsModifiers,
618 ShowEntitiesModifiers,
619 ShowIndexesModifiers,
620 ShowMemoryModifiers,
621 ShowStoresModifiers,
622 ShowUnsupportedCommand,
623 SimpleCaseExpression,
624 StandaloneLiteralProjectionItem,
625 UnionIntersectExcept,
626 UnsupportedFunctionNamespace,
627 Update,
628 UpperFieldPredicateUnsupported,
629 WindowFunction,
630 With,
631 NumericScaleFunctionArguments,
632 OrderByFieldNotOrderable,
633 ShowConstraintsModifiers,
634 AlterTableAddConstraintBeyondCheck,
635 AlterTableAddConstraintModifiers,
636 AlterTableDropConstraintIfExistsSyntax,
637 AlterTableDropConstraintModifiers,
638 AlterTableValidateBeyondConstraint,
639 AlterTableValidateConstraintModifiers,
640 ShowRelationsModifiers,
641}
642
643impl fmt::Debug for SqlFeatureCode {
644 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
645 fmt_compact_code(f, *self as u16)
646 }
647}
648
649#[repr(u16)]
658#[derive(Clone, Copy, Eq, Hash, PartialEq)]
659pub enum SqlLoweringCode {
660 EntityMismatch,
661 SelectProjectionShape,
662 SelectDistinct,
663 DistinctOrderByProjection,
664 GlobalAggregateProjection,
665 GlobalAggregateGroupBy,
666 SelectGroupByShape,
667 GroupedProjectionExplicitListRequired,
668 GroupedProjectionAggregateRequired,
669 GroupedProjectionNonGroupField,
670 GroupedProjectionScalarAfterAggregate,
671 HavingRequiresGroupBy,
672 SelectHavingShape,
673 AggregateInputExpressions,
674 WhereExpressionShape,
675 ParameterPlacement,
676 SqlDdlExecutionUnsupported,
677 BindingCount,
678 BindingFamily,
679 BindingLimit,
680}
681
682impl fmt::Debug for SqlLoweringCode {
683 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
684 fmt_compact_code(f, *self as u16)
685 }
686}
687
688#[repr(u16)]
696#[derive(Clone, Copy, Eq, Hash, PartialEq)]
697pub enum SqlSurfaceMismatchCode {
698 QueryRejectsInsert,
699 QueryRejectsUpdate,
700 QueryRejectsDelete,
701 MutationRejectsSelect,
702 MutationRejectsExplain,
703 MutationRejectsDescribe,
704 MutationRejectsShowIndexes,
705 MutationRejectsShowColumns,
706 MutationRejectsShowEntities,
707 MutationRejectsShowStores,
708 MutationRejectsShowMemory,
709 MutationRequiresExplicitUpdateIntent,
710 MutationRejectsShowConstraints,
711 MutationRejectsShowRelations,
712}
713
714impl fmt::Debug for SqlSurfaceMismatchCode {
715 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
716 fmt_compact_code(f, *self as u16)
717 }
718}
719
720#[repr(u16)]
728#[derive(Clone, Copy, Eq, Hash, PartialEq)]
729pub enum SqlWriteBoundaryCode {
730 PrimaryKeyLiteralIncompatible,
731 MissingPrimaryKey,
732 MissingRequiredFields,
733 ExplicitManagedField,
734 ExplicitGeneratedField,
735 InsertSelectRequiresScalar,
736 InsertSelectAggregateProjection,
737 InsertSelectWidthMismatch,
738 UpdatePrimaryKeyMutation,
739 InvalidFieldLiteral,
740 UnknownReturningField,
741 DuplicateReturningField,
742 UpdateMissingWherePredicate,
743 WriteOrderByUnsupportedShape,
744 ReturningResponseTooLarge,
745 ReturningRowsTooMany,
746 StagedRowsTooMany,
747 InsertDefaultRequiredField,
748 UpdateDefaultRequiredField,
749 UpdateDefaultDatabaseOwnedField,
750 ExactUpdateAssertionRequired,
751 ExactUpdateAssertionTooHigh,
752 ExactUpdateAffectedRowsExceeded,
753 ExactUpdateWindowUnsupported,
754 ExactUpdateScanBudgetExceeded,
755 ResumableUpdateWindowUnsupported,
756 ResumableUpdateReturningUnsupported,
757 ResumableUpdateRequiresJournaledStore,
758 ResumableUpdateAssignedFieldHasGlobalConstraint,
759 ResumableUpdateScopeDependsOnAssignedField,
760 ResumableUpdateScopeDependencyUnknown,
761 ResumableUpdateContinuationMalformed,
762 ResumableUpdateContinuationTargetMismatch,
763 ResumableUpdateContinuationSchemaMismatch,
764 ResumableUpdateContinuationScopeMismatch,
765 ResumableUpdateContinuationPatchMismatch,
766 ResumableUpdateContinuationBatchPolicyMismatch,
767 ResumableUpdateManagedFieldHasGlobalConstraint,
768}
769
770impl fmt::Debug for SqlWriteBoundaryCode {
771 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
772 fmt_compact_code(f, *self as u16)
773 }
774}
775
776#[repr(u16)]
784#[derive(Clone, Copy, Eq, Hash, PartialEq)]
785pub enum SchemaDdlAdmissionCode {
786 MissingExpectedSchemaVersion,
787 MissingNextSchemaVersion,
788 StaleExpectedSchemaVersion,
789 InvalidExpectedSchemaVersion,
790 InvalidNextSchemaVersion,
791 AcceptedSchemaChangeWithoutVersionBump,
792 EmptyVersionBump,
793 VersionGap,
794 VersionRollback,
795 FingerprintMethodMismatch,
796 UnsupportedTransitionClass,
797 PhysicalRunnerMissing,
798 ValidationFailed,
799 PublicationRaceLost,
800 InvalidAddColumnDefault,
801 InvalidAlterColumnDefault,
802 GeneratedIndexDropRejected,
803 SchemaRewriteRequiresMigration,
804 SchemaTransitionBudgetExceeded,
805 GeneratedFieldDefaultChangeRejected,
806 GeneratedFieldNullabilityChangeRejected,
807 RowLayoutVersionExhausted,
808}
809
810impl fmt::Debug for SchemaDdlAdmissionCode {
811 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
812 fmt_compact_code(f, *self as u16)
813 }
814}
815
816#[repr(u16)]
818#[derive(Clone, Copy, Eq, Hash, PartialEq)]
819pub enum SchemaMigrationCode {
820 Unadopted,
821 MissingMigration,
822 VersionGap,
823 Downgrade,
824 EmptyEntityVersionBump,
825 StaleAcceptedHead,
826 PlanChanged,
827 UnknownFromObject,
828 UnknownToObject,
829 KindMismatch,
830 IdentityConflict,
831 UnexplainedSchemaDifference,
832 UnsupportedTransform,
833 PhysicalRunnerMissing,
834 MigrationInProgress,
835 AbortTooLate,
836 ProgressCorrupt,
837 CandidateMismatch,
838 PublicationRaceLost,
839}
840
841impl SchemaMigrationCode {
842 #[must_use]
844 pub const fn diagnostic_code(self) -> DiagnosticCode {
845 match self {
846 Self::StaleAcceptedHead
847 | Self::PlanChanged
848 | Self::IdentityConflict
849 | Self::MigrationInProgress
850 | Self::AbortTooLate
851 | Self::PublicationRaceLost => DiagnosticCode::RuntimeConflict,
852 Self::ProgressCorrupt | Self::CandidateMismatch => DiagnosticCode::RuntimeCorruption,
853 Self::Unadopted
854 | Self::MissingMigration
855 | Self::VersionGap
856 | Self::Downgrade
857 | Self::EmptyEntityVersionBump
858 | Self::UnknownFromObject
859 | Self::UnknownToObject
860 | Self::KindMismatch
861 | Self::UnexplainedSchemaDifference
862 | Self::UnsupportedTransform
863 | Self::PhysicalRunnerMissing => DiagnosticCode::RuntimeUnsupported,
864 }
865 }
866}
867
868impl fmt::Debug for SchemaMigrationCode {
869 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
870 fmt_compact_code(f, *self as u16)
871 }
872}
873
874#[remain::sorted]
881#[derive(Clone, Copy, Eq, PartialEq)]
882pub enum DiagnosticDetail {
883 QueryKind { kind: QueryErrorKind },
884 QueryProjection { reason: QueryProjectionCode },
885 QueryReadAdmission { reason: QueryReadAdmissionCode },
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, 274);
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 let first_unknown = ORDERED_ERROR_CODES
1110 .last()
1111 .expect("public error-code registry is non-empty")
1112 .raw()
1113 .checked_add(1)
1114 .expect("public error-code registry retains an unknown successor");
1115
1116 for raw in [0, first_unknown, u16::MAX] {
1117 let code = ErrorCode::from_raw(raw);
1118
1119 assert_eq!(ErrorCode::known(raw), None);
1120 assert!(!code.is_known());
1121 assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
1122 assert_eq!(code.diagnostic_detail(), None);
1123 assert_eq!(code.class(), ErrorClass::Internal);
1124
1125 let diagnostic = code.diagnostic(ErrorOrigin::Query);
1126
1127 assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
1128 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
1129 assert_eq!(diagnostic.detail(), None);
1130 assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
1131 }
1132 }
1133
1134 #[test]
1135 fn from_parts_requires_detail_to_match_broad_code() {
1136 let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
1137 feature: SqlFeatureCode::Join,
1138 });
1139
1140 assert_eq!(
1141 ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
1142 ErrorCode::SQL_FEATURE_JOIN
1143 );
1144 assert_eq!(
1145 ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
1146 ErrorCode::QUERY_PLAN
1147 );
1148 }
1149
1150 #[test]
1151 fn detail_bearing_registry_entries_round_trip_directly() {
1152 assert!(!DETAIL_ERROR_CODES.is_empty());
1153
1154 for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
1155 assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
1156 assert_eq!(code.diagnostic_code(), diagnostic_code);
1157 assert_eq!(code.diagnostic_detail(), Some(detail));
1158 assert_eq!(detail.diagnostic_code(), diagnostic_code);
1159 }
1160 }
1161
1162 #[test]
1163 fn diagnostic_detail_reports_generated_broad_code() {
1164 let detail = DiagnosticDetail::UnsupportedSqlFeature {
1165 feature: SqlFeatureCode::Join,
1166 };
1167
1168 assert_eq!(
1169 detail.diagnostic_code(),
1170 DiagnosticCode::QueryUnsupportedSqlFeature
1171 );
1172 assert_eq!(format!("{detail:?}"), "61");
1173 }
1174
1175 #[test]
1176 fn public_error_codes_reconstruct_shifted_details() {
1177 assert_eq!(
1178 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
1179 DiagnosticCode::QueryUnknownAggregateTargetField
1180 );
1181 assert_eq!(
1182 ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
1183 Some(DiagnosticDetail::UnsupportedSqlFeature {
1184 feature: SqlFeatureCode::Join,
1185 })
1186 );
1187 assert_eq!(
1188 ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1189 Some(DiagnosticDetail::QueryProjection {
1190 reason: QueryProjectionCode::NumericLiteralRequired,
1191 })
1192 );
1193 assert_eq!(
1194 ErrorCode::QUERY_READ_PUBLIC_REQUIRES_LIMIT.diagnostic_detail(),
1195 Some(DiagnosticDetail::QueryReadAdmission {
1196 reason: QueryReadAdmissionCode::PublicQueryRequiresLimit,
1197 })
1198 );
1199 assert_eq!(
1200 ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1201 Some(DiagnosticDetail::SqlLowering {
1202 reason: SqlLoweringCode::DistinctOrderByProjection,
1203 })
1204 );
1205 assert_eq!(
1206 ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1207 Some(DiagnosticDetail::SqlWriteBoundary {
1208 boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1209 })
1210 );
1211 assert_eq!(
1212 ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1213 Some(DiagnosticDetail::SqlWriteBoundary {
1214 boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1215 })
1216 );
1217 }
1218}