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}
428
429impl fmt::Debug for QueryReadAdmissionCode {
430 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
431 fmt_compact_code(f, *self as u16)
432 }
433}
434
435#[repr(u16)]
442#[derive(Clone, Copy, Eq, Hash, PartialEq)]
443pub enum RuntimeErrorKind {
444 Corruption,
445 IncompatiblePersistedFormat,
446 InvariantViolation,
447 Conflict,
448 NotFound,
449 Unsupported,
450 Internal,
451}
452
453impl fmt::Debug for RuntimeErrorKind {
454 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
455 fmt_compact_code(f, *self as u16)
456 }
457}
458
459#[repr(u16)]
467#[derive(Clone, Copy, Eq, Hash, PartialEq)]
468pub enum RuntimeBoundaryCode {
469 SqlSurfaceControllerRequired,
470 SchemaSurfaceControllerRequired,
471 SqlQueryNoConfiguredEntities,
472 SqlQueryEntityNotFound,
473 SqlDdlTargetRequired,
474 SqlDdlEntityNotConfigured,
475 SqlIntrospectionDisabled,
476 MutationRequiredFieldMissing,
478 MutationManagedTimestampRegression,
480 PersistedRowLayoutOutsideAcceptedWindow,
482 PersistedRowSlotCountMismatch,
484 GeneratedFieldAfterDdlField,
486 JournalMutationRevisionExhausted,
488 ConstraintViolation,
490 AcceptedRowConstraintProgramCorrupt,
492 ConstraintActivationWriteBlocked,
494 GeneratedConstraintActivationStale,
496 MutationDatabaseOwnedFieldExplicit,
498 MutationBatchEmpty,
500 MutationBatchTooManyItems,
502 MutationBatchStagedBytesExceeded,
504 MutationBatchResultBytesExceeded,
506 MutationBatchStoreMismatch,
508 MutationBatchTooManyEntities,
510 MutationBatchDuplicateKey,
512 OperationalSurfaceControllerRequired,
514 ExactKeyBatchTooManyItems,
516 ExactKeyBatchInputBytesExceeded,
518 ExactKeyBatchStoredBytesExceeded,
520 ExactKeyBatchResultBytesExceeded,
522 ExecutionBudgetExceeded,
524 PageUnitTooLarge,
526 RequestExecutionScopeRequired,
528 RequestExecutionRootMismatch,
530 SqlQueryReplyBytesExceeded,
532 DatabaseStartupRecoveryPending,
534 SqlSurfacePolicyDenied,
536 SchemaSurfacePolicyDenied,
538 MutationBatchCommitWorkExceeded,
540 ConvergenceBacklogPressure,
542}
543
544impl fmt::Debug for RuntimeBoundaryCode {
545 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
546 fmt_compact_code(f, *self as u16)
547 }
548}
549
550#[repr(u16)]
558#[derive(Clone, Copy, Eq, Hash, PartialEq)]
559pub enum SqlFeatureCode {
560 AggregateFilterClause,
561 AlterStatementBeyondAlterTable,
562 AlterTableAddColumnDuplicateDefault,
563 AlterTableAddColumnModifiers,
564 AlterTableAddStatementBeyondAddColumn,
565 AlterTableAlterColumnDropUnsupportedAction,
566 AlterTableAlterColumnModifiers,
567 AlterTableAlterColumnSetUnsupportedAction,
568 AlterTableAlterColumnUnsupportedAction,
569 AlterTableAlterStatementBeyondAlterColumn,
570 AlterTableDropColumnIfExistsSyntax,
571 AlterTableDropColumnModifiers,
572 AlterTableDropStatementBeyondDropColumn,
573 AlterTableRenameColumnMissingTo,
574 AlterTableRenameColumnModifiers,
575 AlterTableRenameStatementBeyondRenameColumn,
576 AlterTableUnsupportedOperation,
577 ColumnAlias,
578 CreateIndexIfNotExistsSyntax,
579 CreateIndexKeyOrderingModifiers,
580 CreateIndexModifiers,
581 CreateStatementBeyondCreateIndex,
582 DescribeModifier,
583 DdlSchemaVersionDuplicateExpectedClause,
584 DdlSchemaVersionDuplicateSetClause,
585 DropIndexModifiers,
586 DropIndexIfExistsSyntax,
587 DropStatementBeyondDropIndex,
588 ExpressionIndexUnsupportedFunction,
589 Having,
590 Insert,
591 Join,
592 LikePatternBeyondTrailingPrefix,
593 LowerFieldPredicateUnsupported,
594 MultiStatementSql,
595 NestedAggregateInput,
596 NestedProjectionFunctionInArithmetic,
597 OrderByUnsupportedForm,
598 Other,
599 PredicateStartsWithFirstArgument,
600 QuotedIdentifiers,
601 ReturningUnsupportedShape,
602 ScalarFunctionExpressionPosition,
603 ScaleTakingNumericFunctionExpressionPosition,
604 ShowColumnsModifiers,
605 ShowEntitiesModifiers,
606 ShowIndexesModifiers,
607 ShowMemoryModifiers,
608 ShowStoresModifiers,
609 ShowUnsupportedCommand,
610 SimpleCaseExpression,
611 StandaloneLiteralProjectionItem,
612 UnionIntersectExcept,
613 UnsupportedFunctionNamespace,
614 Update,
615 UpperFieldPredicateUnsupported,
616 WindowFunction,
617 With,
618 NumericScaleFunctionArguments,
619 OrderByFieldNotOrderable,
620 ShowConstraintsModifiers,
621 AlterTableAddConstraintBeyondCheck,
622 AlterTableAddConstraintModifiers,
623 AlterTableDropConstraintIfExistsSyntax,
624 AlterTableDropConstraintModifiers,
625 AlterTableValidateBeyondConstraint,
626 AlterTableValidateConstraintModifiers,
627 ShowRelationsModifiers,
628}
629
630impl fmt::Debug for SqlFeatureCode {
631 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
632 fmt_compact_code(f, *self as u16)
633 }
634}
635
636#[repr(u16)]
645#[derive(Clone, Copy, Eq, Hash, PartialEq)]
646pub enum SqlLoweringCode {
647 EntityMismatch,
648 SelectProjectionShape,
649 SelectDistinct,
650 DistinctOrderByProjection,
651 GlobalAggregateProjection,
652 GlobalAggregateGroupBy,
653 SelectGroupByShape,
654 GroupedProjectionExplicitListRequired,
655 GroupedProjectionAggregateRequired,
656 GroupedProjectionNonGroupField,
657 GroupedProjectionScalarAfterAggregate,
658 HavingRequiresGroupBy,
659 SelectHavingShape,
660 AggregateInputExpressions,
661 WhereExpressionShape,
662 ParameterPlacement,
663 SqlDdlExecutionUnsupported,
664}
665
666impl fmt::Debug for SqlLoweringCode {
667 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
668 fmt_compact_code(f, *self as u16)
669 }
670}
671
672#[repr(u16)]
680#[derive(Clone, Copy, Eq, Hash, PartialEq)]
681pub enum SqlSurfaceMismatchCode {
682 QueryRejectsInsert,
683 QueryRejectsUpdate,
684 QueryRejectsDelete,
685 MutationRejectsSelect,
686 MutationRejectsExplain,
687 MutationRejectsDescribe,
688 MutationRejectsShowIndexes,
689 MutationRejectsShowColumns,
690 MutationRejectsShowEntities,
691 MutationRejectsShowStores,
692 MutationRejectsShowMemory,
693 MutationRequiresExplicitUpdateIntent,
694 MutationRejectsShowConstraints,
695 MutationRejectsShowRelations,
696}
697
698impl fmt::Debug for SqlSurfaceMismatchCode {
699 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
700 fmt_compact_code(f, *self as u16)
701 }
702}
703
704#[repr(u16)]
712#[derive(Clone, Copy, Eq, Hash, PartialEq)]
713pub enum SqlWriteBoundaryCode {
714 PrimaryKeyLiteralIncompatible,
715 MissingPrimaryKey,
716 MissingRequiredFields,
717 ExplicitManagedField,
718 ExplicitGeneratedField,
719 InsertSelectRequiresScalar,
720 InsertSelectAggregateProjection,
721 InsertSelectWidthMismatch,
722 UpdatePrimaryKeyMutation,
723 InvalidFieldLiteral,
724 UnknownReturningField,
725 DuplicateReturningField,
726 UpdateMissingWherePredicate,
727 WriteOrderByUnsupportedShape,
728 ReturningResponseTooLarge,
729 ReturningRowsTooMany,
730 StagedRowsTooMany,
731 InsertDefaultRequiredField,
732 UpdateDefaultRequiredField,
733 UpdateDefaultDatabaseOwnedField,
734 ExactUpdateAssertionRequired,
735 ExactUpdateAssertionTooHigh,
736 ExactUpdateAffectedRowsExceeded,
737 ExactUpdateWindowUnsupported,
738 ExactUpdateScanBudgetExceeded,
739 ResumableUpdateWindowUnsupported,
740 ResumableUpdateReturningUnsupported,
741 ResumableUpdateRequiresJournaledStore,
742 ResumableUpdateAssignedFieldHasGlobalConstraint,
743 ResumableUpdateScopeDependsOnAssignedField,
744 ResumableUpdateScopeDependencyUnknown,
745 ResumableUpdateContinuationMalformed,
746 ResumableUpdateContinuationTargetMismatch,
747 ResumableUpdateContinuationSchemaMismatch,
748 ResumableUpdateContinuationScopeMismatch,
749 ResumableUpdateContinuationPatchMismatch,
750 ResumableUpdateContinuationBatchPolicyMismatch,
751 ResumableUpdateManagedFieldHasGlobalConstraint,
752}
753
754impl fmt::Debug for SqlWriteBoundaryCode {
755 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
756 fmt_compact_code(f, *self as u16)
757 }
758}
759
760#[repr(u16)]
768#[derive(Clone, Copy, Eq, Hash, PartialEq)]
769pub enum SchemaDdlAdmissionCode {
770 MissingExpectedSchemaVersion,
771 MissingNextSchemaVersion,
772 StaleExpectedSchemaVersion,
773 InvalidExpectedSchemaVersion,
774 InvalidNextSchemaVersion,
775 AcceptedSchemaChangeWithoutVersionBump,
776 EmptyVersionBump,
777 VersionGap,
778 VersionRollback,
779 FingerprintMethodMismatch,
780 UnsupportedTransitionClass,
781 PhysicalRunnerMissing,
782 ValidationFailed,
783 PublicationRaceLost,
784 InvalidAddColumnDefault,
785 InvalidAlterColumnDefault,
786 GeneratedIndexDropRejected,
787 SchemaRewriteRequiresMigration,
788 SchemaTransitionBudgetExceeded,
789 GeneratedFieldDefaultChangeRejected,
790 GeneratedFieldNullabilityChangeRejected,
791 RowLayoutVersionExhausted,
792}
793
794impl fmt::Debug for SchemaDdlAdmissionCode {
795 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
796 fmt_compact_code(f, *self as u16)
797 }
798}
799
800#[repr(u16)]
802#[derive(Clone, Copy, Eq, Hash, PartialEq)]
803pub enum SchemaMigrationCode {
804 Unadopted,
805 MissingMigration,
806 VersionGap,
807 Downgrade,
808 EmptyEntityVersionBump,
809 StaleAcceptedHead,
810 PlanChanged,
811 UnknownFromObject,
812 UnknownToObject,
813 KindMismatch,
814 IdentityConflict,
815 UnexplainedSchemaDifference,
816 UnsupportedTransform,
817 PhysicalRunnerMissing,
818 MigrationInProgress,
819 AbortTooLate,
820 ProgressCorrupt,
821 CandidateMismatch,
822 PublicationRaceLost,
823}
824
825impl SchemaMigrationCode {
826 #[must_use]
828 pub const fn diagnostic_code(self) -> DiagnosticCode {
829 match self {
830 Self::StaleAcceptedHead
831 | Self::PlanChanged
832 | Self::IdentityConflict
833 | Self::MigrationInProgress
834 | Self::AbortTooLate
835 | Self::PublicationRaceLost => DiagnosticCode::RuntimeConflict,
836 Self::ProgressCorrupt | Self::CandidateMismatch => DiagnosticCode::RuntimeCorruption,
837 Self::Unadopted
838 | Self::MissingMigration
839 | Self::VersionGap
840 | Self::Downgrade
841 | Self::EmptyEntityVersionBump
842 | Self::UnknownFromObject
843 | Self::UnknownToObject
844 | Self::KindMismatch
845 | Self::UnexplainedSchemaDifference
846 | Self::UnsupportedTransform
847 | Self::PhysicalRunnerMissing => DiagnosticCode::RuntimeUnsupported,
848 }
849 }
850}
851
852impl fmt::Debug for SchemaMigrationCode {
853 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
854 fmt_compact_code(f, *self as u16)
855 }
856}
857
858#[remain::sorted]
865#[derive(Clone, Copy, Eq, PartialEq)]
866pub enum DiagnosticDetail {
867 QueryKind { kind: QueryErrorKind },
868 QueryProjection { reason: QueryProjectionCode },
869 QueryReadAdmission { reason: QueryReadAdmissionCode },
870 RuntimeBoundary { boundary: RuntimeBoundaryCode },
871 RuntimeKind { kind: RuntimeErrorKind },
872 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
873 SchemaMigration { reason: SchemaMigrationCode },
874 SqlLowering { reason: SqlLoweringCode },
875 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
876 SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
877 UnsupportedSqlFeature { feature: SqlFeatureCode },
878}
879
880impl fmt::Debug for DiagnosticDetail {
881 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
882 fmt_compact_code(
883 f,
884 ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
885 )
886 }
887}
888
889#[derive(Clone, Eq, PartialEq)]
896pub struct Diagnostic {
897 code: DiagnosticCode,
898 origin: ErrorOrigin,
899 detail: Option<DiagnosticDetail>,
900}
901
902impl Diagnostic {
903 #[must_use]
905 pub const fn new(
906 code: DiagnosticCode,
907 origin: ErrorOrigin,
908 detail: Option<DiagnosticDetail>,
909 ) -> Self {
910 Self {
911 code,
912 origin,
913 detail,
914 }
915 }
916
917 #[must_use]
919 pub const fn from_code(code: DiagnosticCode) -> Self {
920 Self::new(code, code.origin(), None)
921 }
922
923 #[must_use]
925 pub const fn code(&self) -> DiagnosticCode {
926 self.code
927 }
928
929 #[must_use]
931 pub const fn class(&self) -> ErrorClass {
932 self.code.class()
933 }
934
935 #[must_use]
937 pub const fn origin(&self) -> ErrorOrigin {
938 self.origin
939 }
940
941 #[must_use]
943 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
944 self.detail.as_ref()
945 }
946
947 #[must_use]
949 pub const fn error_code(&self) -> ErrorCode {
950 ErrorCode::from_parts(self.code, self.detail)
951 }
952}
953
954impl fmt::Debug for Diagnostic {
955 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
956 write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
957 }
958}
959
960fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
961 write!(f, "{raw}")
962}
963
964#[cfg(test)]
965mod tests {
966 use super::{
967 Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
968 QueryProjectionCode, QueryReadAdmissionCode, SqlFeatureCode, SqlLoweringCode,
969 SqlWriteBoundaryCode,
970 registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
971 };
972
973 #[test]
974 fn diagnostic_from_code_uses_default_origin() {
975 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
976
977 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
978 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
979 }
980
981 #[test]
982 fn diagnostic_code_reports_broad_class() {
983 assert_eq!(
984 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
985 ErrorClass::Unsupported
986 );
987 assert_eq!(
988 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
989 ErrorClass::Unsupported
990 );
991 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
992 assert_eq!(
993 DiagnosticCode::StoreCorruption.class(),
994 ErrorClass::Corruption
995 );
996 }
997
998 #[test]
999 fn class_and_origin_wire_codes_round_trip() {
1000 for (class, raw) in [
1001 (ErrorClass::Query, 1),
1002 (ErrorClass::Corruption, 2),
1003 (ErrorClass::IncompatiblePersistedFormat, 3),
1004 (ErrorClass::NotFound, 4),
1005 (ErrorClass::Internal, 5),
1006 (ErrorClass::Conflict, 6),
1007 (ErrorClass::Unsupported, 7),
1008 (ErrorClass::InvariantViolation, 8),
1009 ] {
1010 assert_eq!(class.wire_code(), raw);
1011 assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
1012 assert_eq!(format!("{class:?}"), raw.to_string());
1013 }
1014
1015 for (origin, raw) in [
1016 (ErrorOrigin::Cursor, 1),
1017 (ErrorOrigin::Executor, 2),
1018 (ErrorOrigin::Identity, 3),
1019 (ErrorOrigin::Index, 4),
1020 (ErrorOrigin::Interface, 5),
1021 (ErrorOrigin::Planner, 6),
1022 (ErrorOrigin::Query, 7),
1023 (ErrorOrigin::Recovery, 8),
1024 (ErrorOrigin::Response, 9),
1025 (ErrorOrigin::Runtime, 10),
1026 (ErrorOrigin::Serialize, 11),
1027 (ErrorOrigin::Store, 12),
1028 ] {
1029 assert_eq!(origin.wire_code(), raw);
1030 assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
1031 assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
1032 assert_eq!(format!("{origin:?}"), raw.to_string());
1033 }
1034
1035 assert_eq!(ErrorClass::from_wire_code(0), None);
1036 assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
1037 assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
1038 }
1039
1040 #[test]
1041 fn public_error_codes_are_sequential() {
1042 let first = ORDERED_ERROR_CODES
1043 .first()
1044 .expect("public error-code registry is non-empty")
1045 .raw();
1046
1047 assert_eq!(first, 1);
1048
1049 for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
1050 let expected = first + u16::try_from(index).expect("test error-code index fits u16");
1051 assert_eq!(code.raw(), expected);
1052 assert_eq!(ErrorCode::known(code.raw()), Some(*code));
1053 assert!(code.is_known());
1054 }
1055
1056 let last = ORDERED_ERROR_CODES
1057 .last()
1058 .expect("public error-code registry is non-empty")
1059 .raw();
1060
1061 assert_eq!(last, 264);
1062 }
1063
1064 #[test]
1065 fn all_public_error_codes_round_trip_through_diagnostic_parts() {
1066 let first = ORDERED_ERROR_CODES
1067 .first()
1068 .expect("public error-code registry is non-empty")
1069 .raw();
1070 let last = ORDERED_ERROR_CODES
1071 .last()
1072 .expect("public error-code registry is non-empty")
1073 .raw();
1074
1075 for raw in first..=last {
1076 let code = ErrorCode::from_raw(raw);
1077 let diagnostic_code = code.diagnostic_code();
1078 let diagnostic_detail = code.diagnostic_detail();
1079 let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
1080
1081 assert_eq!(rebuilt.raw(), raw);
1082
1083 let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
1084
1085 assert_eq!(diagnostic.code(), diagnostic_code);
1086 assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
1087 assert_eq!(diagnostic.error_code().raw(), raw);
1088 }
1089 }
1090
1091 #[test]
1092 fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
1093 let first_unknown = ORDERED_ERROR_CODES
1094 .last()
1095 .expect("public error-code registry is non-empty")
1096 .raw()
1097 .checked_add(1)
1098 .expect("public error-code registry retains an unknown successor");
1099
1100 for raw in [0, first_unknown, u16::MAX] {
1101 let code = ErrorCode::from_raw(raw);
1102
1103 assert_eq!(ErrorCode::known(raw), None);
1104 assert!(!code.is_known());
1105 assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
1106 assert_eq!(code.diagnostic_detail(), None);
1107 assert_eq!(code.class(), ErrorClass::Internal);
1108
1109 let diagnostic = code.diagnostic(ErrorOrigin::Query);
1110
1111 assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
1112 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
1113 assert_eq!(diagnostic.detail(), None);
1114 assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
1115 }
1116 }
1117
1118 #[test]
1119 fn from_parts_requires_detail_to_match_broad_code() {
1120 let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
1121 feature: SqlFeatureCode::Join,
1122 });
1123
1124 assert_eq!(
1125 ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
1126 ErrorCode::SQL_FEATURE_JOIN
1127 );
1128 assert_eq!(
1129 ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
1130 ErrorCode::QUERY_PLAN
1131 );
1132 }
1133
1134 #[test]
1135 fn detail_bearing_registry_entries_round_trip_directly() {
1136 assert!(!DETAIL_ERROR_CODES.is_empty());
1137
1138 for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
1139 assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
1140 assert_eq!(code.diagnostic_code(), diagnostic_code);
1141 assert_eq!(code.diagnostic_detail(), Some(detail));
1142 assert_eq!(detail.diagnostic_code(), diagnostic_code);
1143 }
1144 }
1145
1146 #[test]
1147 fn diagnostic_detail_reports_generated_broad_code() {
1148 let detail = DiagnosticDetail::UnsupportedSqlFeature {
1149 feature: SqlFeatureCode::Join,
1150 };
1151
1152 assert_eq!(
1153 detail.diagnostic_code(),
1154 DiagnosticCode::QueryUnsupportedSqlFeature
1155 );
1156 assert_eq!(format!("{detail:?}"), "61");
1157 }
1158
1159 #[test]
1160 fn public_error_codes_reconstruct_shifted_details() {
1161 assert_eq!(
1162 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
1163 DiagnosticCode::QueryUnknownAggregateTargetField
1164 );
1165 assert_eq!(
1166 ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
1167 Some(DiagnosticDetail::UnsupportedSqlFeature {
1168 feature: SqlFeatureCode::Join,
1169 })
1170 );
1171 assert_eq!(
1172 ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1173 Some(DiagnosticDetail::QueryProjection {
1174 reason: QueryProjectionCode::NumericLiteralRequired,
1175 })
1176 );
1177 assert_eq!(
1178 ErrorCode::QUERY_READ_PUBLIC_REQUIRES_LIMIT.diagnostic_detail(),
1179 Some(DiagnosticDetail::QueryReadAdmission {
1180 reason: QueryReadAdmissionCode::PublicQueryRequiresLimit,
1181 })
1182 );
1183 assert_eq!(
1184 ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1185 Some(DiagnosticDetail::SqlLowering {
1186 reason: SqlLoweringCode::DistinctOrderByProjection,
1187 })
1188 );
1189 assert_eq!(
1190 ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1191 Some(DiagnosticDetail::SqlWriteBoundary {
1192 boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1193 })
1194 );
1195 assert_eq!(
1196 ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1197 Some(DiagnosticDetail::SqlWriteBoundary {
1198 boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1199 })
1200 );
1201 }
1202}