1use std::fmt;
8
9use crate::ErrorCode;
10
11pub const MAX_PUBLIC_DIAGNOSTIC_FACTS: usize = 80;
13
14macro_rules! define_fact_tag_registry {
15 ($($name:ident = $raw:literal;)+) => {
16 #[derive(Clone, Copy, Eq, Hash, PartialEq)]
18 pub enum DiagnosticFactTag {
19 $(
20 #[doc = concat!("Public fact tag ", stringify!($raw), ".")]
21 $name,
22 )+
23 }
24
25 impl DiagnosticFactTag {
26 #[must_use]
28 pub const fn raw(self) -> u8 {
29 match self {
30 $(Self::$name => $raw,)+
31 }
32 }
33
34 #[must_use]
36 pub const fn known(raw: u8) -> Option<Self> {
37 match raw {
38 $($raw => Some(Self::$name),)+
39 _ => None,
40 }
41 }
42 }
43
44 #[cfg(test)]
45 const ORDERED_FACT_TAGS: &[DiagnosticFactTag] = &[
46 $(DiagnosticFactTag::$name,)+
47 ];
48 };
49}
50
51define_fact_tag_registry! {
54 AcceptedSchemaFingerprintMethod = 1;
55 AcceptedSchemaFingerprintHigh = 2;
56 AcceptedSchemaFingerprintLow = 3;
57 ExpectedFingerprintPrefix = 4;
58 ActualFingerprintPrefix = 5;
59 EntityTag = 6;
60 ExpectedEntityTag = 7;
61 ActualEntityTag = 8;
62 ConstraintId = 9;
63 FieldId = 10;
64 IndexId = 11;
65 RelationId = 12;
66 MutationOperation = 13;
67 RowOperation = 14;
68 BatchPosition = 15;
69 FirstBatchPosition = 16;
70 DuplicateBatchPosition = 17;
71 ClauseIndex = 18;
72 TermIndex = 19;
73 FirstTermIndex = 20;
74 DuplicateTermIndex = 21;
75 ProjectionIndex = 22;
76 GroupIndex = 23;
77 AggregateIndex = 24;
78 ArgumentIndex = 25;
79 BranchIndex = 26;
80 ComponentIndex = 27;
81 ParameterIndex = 28;
82 SourceSpanStart = 29;
83 SourceSpanEnd = 30;
84 Expected = 31;
85 Actual = 32;
86 Minimum = 33;
87 Maximum = 34;
88 Limit = 35;
89 ExpectedCount = 36;
90 ActualCount = 37;
91 ExpectedRevision = 38;
92 ActualRevision = 39;
93 CurrentRevision = 40;
94 RequestedRevision = 41;
95 ExpectedVersion = 42;
96 ActualVersion = 43;
97 CurrentVersion = 44;
98 RequestedVersion = 45;
99 ExpectedOffset = 46;
100 ActualOffset = 47;
101 ExpectedArity = 48;
102 ActualArity = 49;
103 ExpectedLength = 50;
104 ActualLength = 51;
105 ExpectedSlotCount = 52;
106 ActualSlotCount = 53;
107 RowLayout = 54;
108 HistoryFloor = 55;
109 CurrentLayout = 56;
110 PhysicalSlot = 57;
111 PhysicalGeneration = 58;
112 ExpectedMemoryId = 59;
113 ActualMemoryId = 60;
114 ConstraintKind = 61;
115 ConstraintContext = 62;
116 FieldKind = 63;
117 ValueKind = 64;
118 TypeFamily = 65;
119 FunctionKind = 66;
120 OperatorKind = 67;
121 AggregateKind = 68;
122 KeyNamespaceKind = 69;
123 ComponentKind = 70;
124 MismatchKind = 71;
125 DecodeReason = 72;
126 BudgetResource = 73;
127 MigrationPhase = 74;
128 DatabaseControlRecordKind = 75;
129 StateKind = 76;
130 PayloadComponent = 77;
131 ExpectedSignaturePrefix = 78;
132 ActualSignaturePrefix = 79;
133 FindingPosition = 80;
134 RootField = 81;
135 RecordMember = 82;
136 TupleElement = 83;
137 Newtype = 84;
138 EnumVariant = 85;
139 ListElement = 86;
140 SetElement = 87;
141 MapEntryKey = 88;
142 MapEntryValue = 89;
143 ExecutionBudgetScope = 90;
144 ExecutionLane = 91;
145 QueryShapeFingerprintPrefix = 92;
146 BacklogResource = 93;
147 CurrentCount = 94;
148 ProposedCount = 95;
149}
150
151#[derive(Clone, Copy, Eq, Hash, PartialEq)]
156pub enum DiagnosticDecodeReason {
157 CursorEmpty,
158 CursorTooLong,
159 CursorInvalidLength,
160 CursorInvalidBase64,
161 CursorGroupedDirectionMismatch,
162 CursorTokenEncode,
163 CursorTokenDecode,
164 RecoveryMarkerMagic,
165 RecoveryMarkerChecksum,
166 RecoveryMarkerState,
167 CursorGroupedContinuationRequiresLimit,
168 CursorGlobalDistinctContinuationUnsupported,
169}
170
171#[derive(Clone, Copy, Eq, Hash, PartialEq)]
173pub enum DiagnosticMutationOperation {
174 Insert,
175 Replace,
176 Update,
177 Delete,
178}
179
180macro_rules! define_numeric_fact_value_registry {
181 (
182 $(#[$enum_meta:meta])*
183 pub enum $name:ident {
184 $($variant:ident = $raw:literal;)+
185 }
186 ) => {
187 $(#[$enum_meta])*
188 #[derive(Clone, Copy, Eq, Hash, PartialEq)]
189 pub enum $name {
190 $(
191 #[doc = concat!("Compact diagnostic value ", stringify!($raw), ".")]
192 $variant,
193 )+
194 }
195
196 impl $name {
197 #[must_use]
199 pub const fn raw(self) -> u64 {
200 match self {
201 $(Self::$variant => $raw,)+
202 }
203 }
204
205 #[must_use]
207 pub const fn known(raw: u64) -> Option<Self> {
208 match raw {
209 $($raw => Some(Self::$variant),)+
210 _ => None,
211 }
212 }
213 }
214
215 impl fmt::Debug for $name {
216 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217 write!(f, "{}", self.raw())
218 }
219 }
220 };
221}
222
223define_numeric_fact_value_registry! {
224 pub enum DiagnosticBacklogResource {
226 Batches = 1;
227 Records = 2;
228 EncodedBytes = 3;
229 }
230}
231
232define_numeric_fact_value_registry! {
233 pub enum DiagnosticExecutionBudgetResource {
235 QueryExecutions = 1;
236 PlanningSteps = 2;
237 PlanCompilations = 3;
238 KeyIndexEntriesVisited = 4;
239 RowsVisited = 5;
240 StoredBytesRead = 6;
241 PredicateExpressionSteps = 7;
242 NestedValueSteps = 8;
243 DecodedBytes = 9;
244 MaterializedBytes = 10;
245 SortEntries = 11;
246 SortComparisons = 12;
247 SortTemporaryBytes = 13;
248 GroupDistinctEntries = 14;
249 GroupDistinctStateBytes = 15;
250 CursorSteps = 16;
251 TemporaryBytes = 17;
252 ResultRows = 18;
253 ResultBytes = 19;
254 InstructionUnits = 20;
255 }
256}
257
258impl DiagnosticExecutionBudgetResource {
259 pub const ALL: [Self; 20] = [
261 Self::QueryExecutions,
262 Self::PlanningSteps,
263 Self::PlanCompilations,
264 Self::KeyIndexEntriesVisited,
265 Self::RowsVisited,
266 Self::StoredBytesRead,
267 Self::PredicateExpressionSteps,
268 Self::NestedValueSteps,
269 Self::DecodedBytes,
270 Self::MaterializedBytes,
271 Self::SortEntries,
272 Self::SortComparisons,
273 Self::SortTemporaryBytes,
274 Self::GroupDistinctEntries,
275 Self::GroupDistinctStateBytes,
276 Self::CursorSteps,
277 Self::TemporaryBytes,
278 Self::ResultRows,
279 Self::ResultBytes,
280 Self::InstructionUnits,
281 ];
282}
283
284define_numeric_fact_value_registry! {
285 pub enum DiagnosticExecutionBudgetScope {
287 Execution = 1;
288 Request = 2;
289 }
290}
291
292define_numeric_fact_value_registry! {
293 pub enum DiagnosticExecutionLane {
295 PublicRead = 1;
296 TrustedRead = 2;
297 Diagnostic = 3;
298 Mutation = 4;
299 Recovery = 5;
300 }
301}
302
303define_numeric_fact_value_registry! {
304 pub enum DiagnosticConstraintKind {
306 Check = 1;
307 NotNull = 2;
308 Relation = 3;
309 TargetedRule = 4;
310 Unique = 5;
311 }
312}
313
314define_numeric_fact_value_registry! {
315 pub enum DiagnosticConstraintContext {
317 Integrity = 1;
318 MigrationValidation = 2;
319 WriteAdmission = 3;
320 }
321}
322
323define_numeric_fact_value_registry! {
324 pub enum DiagnosticComponentKind {
326 CommitDataKey = 1;
327 IndexKey = 2;
328 IndexKeyComponent = 3;
329 RelationTargetPrimaryKey = 4;
330 }
331}
332
333define_numeric_fact_value_registry! {
334 pub enum DiagnosticTypeFamily {
336 Blob = 1;
337 Bool = 2;
338 Collection = 3;
339 Null = 4;
340 Numeric = 5;
341 Opaque = 6;
342 Structured = 7;
343 Text = 8;
344 Unknown = 9;
345 }
346}
347
348define_numeric_fact_value_registry! {
349 pub enum DiagnosticFunctionKind {
351 Abs = 1;
352 Cbrt = 2;
353 Ceiling = 3;
354 Coalesce = 4;
355 CollectionContains = 5;
356 Contains = 6;
357 EndsWith = 7;
358 Exp = 8;
359 Floor = 9;
360 IsEmpty = 10;
361 IsMissing = 11;
362 IsNotEmpty = 12;
363 IsNotNull = 13;
364 IsNull = 14;
365 Left = 15;
366 Length = 16;
367 Ln = 17;
368 Log = 18;
369 Log2 = 19;
370 Log10 = 20;
371 Lower = 21;
372 Ltrim = 22;
373 Mod = 23;
374 NullIf = 24;
375 OctetLength = 25;
376 Position = 26;
377 Power = 27;
378 Replace = 28;
379 Right = 29;
380 Round = 30;
381 Rtrim = 31;
382 Sign = 32;
383 Sqrt = 33;
384 StartsWith = 34;
385 Substring = 35;
386 Trim = 36;
387 Trunc = 37;
388 Upper = 38;
389 InList = 39;
390 }
391}
392
393define_numeric_fact_value_registry! {
394 pub enum DiagnosticOperatorKind {
396 Not = 1;
397 Add = 2;
398 And = 3;
399 Div = 4;
400 Eq = 5;
401 Gt = 6;
402 Gte = 7;
403 Lt = 8;
404 Lte = 9;
405 Mul = 10;
406 Ne = 11;
407 Or = 12;
408 Sub = 13;
409 In = 14;
410 NotIn = 15;
411 Contains = 16;
412 StartsWith = 17;
413 EndsWith = 18;
414 }
415}
416
417define_numeric_fact_value_registry! {
418 pub enum DiagnosticAggregateKind {
420 Count = 1;
421 Sum = 2;
422 Avg = 3;
423 Exists = 4;
424 Min = 5;
425 Max = 6;
426 First = 7;
427 Last = 8;
428 }
429}
430
431impl DiagnosticDecodeReason {
432 #[must_use]
434 pub const fn raw(self) -> u64 {
435 match self {
436 Self::CursorEmpty => 1,
437 Self::CursorTooLong => 2,
438 Self::CursorInvalidLength => 3,
439 Self::CursorInvalidBase64 => 4,
440 Self::CursorGroupedDirectionMismatch => 5,
441 Self::CursorTokenEncode => 6,
442 Self::CursorTokenDecode => 7,
443 Self::RecoveryMarkerMagic => 8,
444 Self::RecoveryMarkerChecksum => 9,
445 Self::RecoveryMarkerState => 10,
446 Self::CursorGroupedContinuationRequiresLimit => 11,
447 Self::CursorGlobalDistinctContinuationUnsupported => 12,
448 }
449 }
450
451 #[must_use]
453 pub const fn known(raw: u64) -> Option<Self> {
454 match raw {
455 1 => Some(Self::CursorEmpty),
456 2 => Some(Self::CursorTooLong),
457 3 => Some(Self::CursorInvalidLength),
458 4 => Some(Self::CursorInvalidBase64),
459 5 => Some(Self::CursorGroupedDirectionMismatch),
460 6 => Some(Self::CursorTokenEncode),
461 7 => Some(Self::CursorTokenDecode),
462 8 => Some(Self::RecoveryMarkerMagic),
463 9 => Some(Self::RecoveryMarkerChecksum),
464 10 => Some(Self::RecoveryMarkerState),
465 11 => Some(Self::CursorGroupedContinuationRequiresLimit),
466 12 => Some(Self::CursorGlobalDistinctContinuationUnsupported),
467 _ => None,
468 }
469 }
470}
471
472impl DiagnosticMutationOperation {
473 #[must_use]
475 pub const fn raw(self) -> u64 {
476 match self {
477 Self::Insert => 1,
478 Self::Replace => 2,
479 Self::Update => 3,
480 Self::Delete => 4,
481 }
482 }
483
484 #[must_use]
486 pub const fn known(raw: u64) -> Option<Self> {
487 match raw {
488 1 => Some(Self::Insert),
489 2 => Some(Self::Replace),
490 3 => Some(Self::Update),
491 4 => Some(Self::Delete),
492 _ => None,
493 }
494 }
495}
496
497impl fmt::Debug for DiagnosticFactTag {
498 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
499 write!(f, "{}", self.raw())
500 }
501}
502
503impl fmt::Debug for DiagnosticDecodeReason {
504 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
505 write!(f, "{}", self.raw())
506 }
507}
508
509impl fmt::Debug for DiagnosticMutationOperation {
510 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
511 write!(f, "{}", self.raw())
512 }
513}
514
515#[must_use]
517pub const fn pack_u32_pair(high: u32, low: u32) -> u64 {
518 (high as u64) << 32 | low as u64
519}
520
521#[must_use]
523pub const fn unpack_u32_pair(value: u64) -> (u32, u32) {
524 let bytes = value.to_be_bytes();
525 (
526 u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
527 u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
528 )
529}
530
531#[derive(Clone, Copy, Debug, Eq, PartialEq)]
535pub enum DiagnosticFactSchemaMismatch {
536 GlobalMaximumExceeded,
538 CodeMaximumExceeded,
540 InvalidSequence,
542 InvalidValue,
544}
545
546pub fn validate_known_diagnostic_fact_schema(
551 code: ErrorCode,
552 facts: &[(DiagnosticFactTag, u64)],
553) -> Result<(), DiagnosticFactSchemaMismatch> {
554 validate_diagnostic_fact_schema(code, facts.len(), |index| {
555 let (tag, value) = facts[index];
556 (tag.raw(), value)
557 })
558}
559
560pub fn validate_raw_diagnostic_fact_schema(
565 code: ErrorCode,
566 facts: &[(u8, u64)],
567) -> Result<(), DiagnosticFactSchemaMismatch> {
568 validate_diagnostic_fact_schema(code, facts.len(), |index| facts[index])
569}
570
571#[expect(
572 clippy::too_many_lines,
573 reason = "the frozen E-code schema registry keeps every numeric owner visible in one exhaustive dispatch"
574)]
575fn validate_diagnostic_fact_schema(
576 code: ErrorCode,
577 fact_count: usize,
578 fact_at: impl Fn(usize) -> (u8, u64),
579) -> Result<(), DiagnosticFactSchemaMismatch> {
580 if fact_count > MAX_PUBLIC_DIAGNOSTIC_FACTS {
581 return Err(DiagnosticFactSchemaMismatch::GlobalMaximumExceeded);
582 }
583
584 let maximum = diagnostic_fact_maximum(code);
585 if fact_count > maximum {
586 return Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded);
587 }
588
589 let valid_sequence = match code.raw() {
590 3 => query_plan_schema(fact_count, &fact_at),
591 5 => cursor_schema(fact_count, &fact_at),
592 15 => store_corruption_schema(fact_count, &fact_at),
593 17 => runtime_corruption_schema(fact_count, &fact_at),
594 18 => incompatible_format_schema(fact_count, &fact_at),
595 19 => runtime_invariant_schema(fact_count, &fact_at),
596 20 => runtime_conflict_schema(fact_count, &fact_at),
597 22 => runtime_unsupported_schema(fact_count, &fact_at),
598 23 => runtime_internal_schema(fact_count, &fact_at),
599 130 => tags_match(
600 fact_count,
601 &fact_at,
602 &[
603 DiagnosticFactTag::ExpectedArity,
604 DiagnosticFactTag::ActualArity,
605 ],
606 ),
607 133 | 134 | 158 | 159 => {
608 tags_match(fact_count, &fact_at, &[DiagnosticFactTag::ProjectionIndex])
609 }
610 164 => tags_match(fact_count, &fact_at, &[DiagnosticFactTag::ParameterIndex]),
611 166 | 224 => {
612 tags_match(fact_count, &fact_at, &[DiagnosticFactTag::Limit])
613 || tags_match(
614 fact_count,
615 &fact_at,
616 &[DiagnosticFactTag::ActualLength, DiagnosticFactTag::Limit],
617 )
618 }
619 167 | 169 | 191 | 192 | 194 | 223 | 248 | 264 => tags_match(
620 fact_count,
621 &fact_at,
622 &[DiagnosticFactTag::ActualCount, DiagnosticFactTag::Limit],
623 ),
624 262 => {
625 tags_match(fact_count, &fact_at, &[DiagnosticFactTag::Limit])
626 || tags_match(
627 fact_count,
628 &fact_at,
629 &[DiagnosticFactTag::ActualCount, DiagnosticFactTag::Limit],
630 )
631 }
632 263 => tags_match(
633 fact_count,
634 &fact_at,
635 &[
636 DiagnosticFactTag::BacklogResource,
637 DiagnosticFactTag::CurrentCount,
638 DiagnosticFactTag::ProposedCount,
639 DiagnosticFactTag::Limit,
640 ],
641 ),
642 185 | 221 => mutation_schema(
643 fact_count,
644 &fact_at,
645 &[
646 DiagnosticFactTag::AcceptedSchemaFingerprintMethod,
647 DiagnosticFactTag::AcceptedSchemaFingerprintHigh,
648 DiagnosticFactTag::AcceptedSchemaFingerprintLow,
649 DiagnosticFactTag::EntityTag,
650 DiagnosticFactTag::FieldId,
651 DiagnosticFactTag::MutationOperation,
652 ],
653 ),
654 186 => tags_match(
655 fact_count,
656 &fact_at,
657 &[
658 DiagnosticFactTag::RowLayout,
659 DiagnosticFactTag::HistoryFloor,
660 DiagnosticFactTag::CurrentLayout,
661 ],
662 ),
663 187 => tags_match(
664 fact_count,
665 &fact_at,
666 &[
667 DiagnosticFactTag::RowLayout,
668 DiagnosticFactTag::ExpectedSlotCount,
669 DiagnosticFactTag::ActualSlotCount,
670 ],
671 ),
672 190 => tags_match(
673 fact_count,
674 &fact_at,
675 &[DiagnosticFactTag::ActualCount, DiagnosticFactTag::Minimum],
676 ),
677 210 => constraint_schema(fact_count, &fact_at, true),
678 212 => constraint_schema(fact_count, &fact_at, false),
679 220 => mutation_schema(
680 fact_count,
681 &fact_at,
682 &[
683 DiagnosticFactTag::AcceptedSchemaFingerprintMethod,
684 DiagnosticFactTag::AcceptedSchemaFingerprintHigh,
685 DiagnosticFactTag::AcceptedSchemaFingerprintLow,
686 DiagnosticFactTag::EntityTag,
687 DiagnosticFactTag::MutationOperation,
688 ],
689 ),
690 222 => {
691 tags_match(fact_count, &fact_at, &[DiagnosticFactTag::ActualCount]) && fact_at(0).1 == 0
692 }
693 225 | 249 | 250 | 251 => tags_match(
694 fact_count,
695 &fact_at,
696 &[DiagnosticFactTag::ActualLength, DiagnosticFactTag::Limit],
697 ),
698 252 => tags_match(
699 fact_count,
700 &fact_at,
701 &[
702 DiagnosticFactTag::BudgetResource,
703 DiagnosticFactTag::Limit,
704 DiagnosticFactTag::Actual,
705 DiagnosticFactTag::ExecutionBudgetScope,
706 DiagnosticFactTag::ExecutionLane,
707 DiagnosticFactTag::QueryShapeFingerprintPrefix,
708 ],
709 ),
710 253 => tags_match(
711 fact_count,
712 &fact_at,
713 &[
714 DiagnosticFactTag::BudgetResource,
715 DiagnosticFactTag::Limit,
716 DiagnosticFactTag::Actual,
717 ],
718 ),
719 271 | 272 => tags_match(
720 fact_count,
721 &fact_at,
722 &[DiagnosticFactTag::Limit, DiagnosticFactTag::Actual],
723 ),
724 274 => {
725 tags_match(
726 fact_count,
727 &fact_at,
728 &[DiagnosticFactTag::Expected, DiagnosticFactTag::Actual],
729 ) && (0..2).all(|index| (1..=u64::from(u16::MAX)).contains(&fact_at(index).1))
730 && fact_at(0).1 != fact_at(1).1
731 }
732 276 | 281 => {
733 fact_count == 0
734 || (tags_match(fact_count, &fact_at, &[DiagnosticFactTag::ActualMemoryId])
735 && fact_at(0).1 <= 254)
736 }
737 278 => {
738 tags_match(fact_count, &fact_at, &[DiagnosticFactTag::ExpectedCount])
739 && matches!(fact_at(0).1, 3 | 4)
740 }
741 280 => {
742 fact_count == 0
743 || (tags_match(
744 fact_count,
745 &fact_at,
746 &[
747 DiagnosticFactTag::ExpectedMemoryId,
748 DiagnosticFactTag::ActualMemoryId,
749 ],
750 ) && (0..2).all(|index| fact_at(index).1 <= 254)
751 && fact_at(0).1 != fact_at(1).1)
752 }
753 226 => tags_match(
754 fact_count,
755 &fact_at,
756 &[
757 DiagnosticFactTag::BatchPosition,
758 DiagnosticFactTag::ExpectedEntityTag,
759 DiagnosticFactTag::ActualEntityTag,
760 ],
761 ),
762 227 => tags_match(
763 fact_count,
764 &fact_at,
765 &[
766 DiagnosticFactTag::EntityTag,
767 DiagnosticFactTag::FirstBatchPosition,
768 DiagnosticFactTag::DuplicateBatchPosition,
769 ],
770 ),
771 _ => fact_count == 0,
772 };
773 if !valid_sequence {
774 return Err(DiagnosticFactSchemaMismatch::InvalidSequence);
775 }
776
777 for index in 0..fact_count {
778 let (raw_tag, value) = fact_at(index);
779 let Some(tag) = DiagnosticFactTag::known(raw_tag) else {
780 return Err(DiagnosticFactSchemaMismatch::InvalidSequence);
781 };
782 if !diagnostic_fact_value_is_valid(tag, value) {
783 return Err(DiagnosticFactSchemaMismatch::InvalidValue);
784 }
785 }
786 Ok(())
787}
788
789const fn diagnostic_fact_maximum(code: ErrorCode) -> usize {
790 match code.raw() {
791 5 | 15 | 17 | 186 | 187 | 226 | 227 | 253 => 3,
792 133 | 134 | 158 | 159 | 164 | 222 | 276 | 278 | 281 => 1,
793 18 | 20 | 130 | 166 | 167 | 169 | 190 | 191 | 192 | 194 | 223 | 224 | 225 | 248 | 249
794 | 250 | 251 | 262 | 264 | 271 | 272 | 274 | 280 => 2,
795 3 | 19 | 23 => 5,
796 22 | 220 | 252 => 6,
797 185 | 221 => 7,
798 263 => 4,
799 210 => 73,
800 212 => 9,
801 _ => 0,
802 }
803}
804
805#[expect(
806 clippy::too_many_lines,
807 reason = "the query-plan E-code deliberately owns several exact finite fact sequences"
808)]
809fn query_plan_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
810 fact_count == 0
811 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::TermIndex])
812 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ComponentIndex])
813 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::GroupIndex])
814 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ClauseIndex])
815 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::AggregateIndex])
816 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::AggregateKind])
817 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ProjectionIndex])
818 || tags_match(
819 fact_count,
820 fact_at,
821 &[
822 DiagnosticFactTag::FirstTermIndex,
823 DiagnosticFactTag::DuplicateTermIndex,
824 ],
825 )
826 || tags_match(
827 fact_count,
828 fact_at,
829 &[
830 DiagnosticFactTag::ClauseIndex,
831 DiagnosticFactTag::OperatorKind,
832 ],
833 )
834 || tags_match(
835 fact_count,
836 fact_at,
837 &[
838 DiagnosticFactTag::AggregateIndex,
839 DiagnosticFactTag::AggregateKind,
840 ],
841 )
842 || tags_match(
843 fact_count,
844 fact_at,
845 &[
846 DiagnosticFactTag::AggregateKind,
847 DiagnosticFactTag::TypeFamily,
848 ],
849 )
850 || tags_match(
851 fact_count,
852 fact_at,
853 &[
854 DiagnosticFactTag::OperatorKind,
855 DiagnosticFactTag::TypeFamily,
856 ],
857 )
858 || tags_match(
859 fact_count,
860 fact_at,
861 &[
862 DiagnosticFactTag::BranchIndex,
863 DiagnosticFactTag::TypeFamily,
864 ],
865 )
866 || tags_match(
867 fact_count,
868 fact_at,
869 &[DiagnosticFactTag::TypeFamily, DiagnosticFactTag::TypeFamily],
870 )
871 || tags_match(
872 fact_count,
873 fact_at,
874 &[
875 DiagnosticFactTag::ClauseIndex,
876 DiagnosticFactTag::AggregateIndex,
877 DiagnosticFactTag::ActualCount,
878 ],
879 )
880 || tags_match(
881 fact_count,
882 fact_at,
883 &[
884 DiagnosticFactTag::FunctionKind,
885 DiagnosticFactTag::ExpectedArity,
886 DiagnosticFactTag::ActualArity,
887 ],
888 )
889 || tags_match(
890 fact_count,
891 fact_at,
892 &[
893 DiagnosticFactTag::FunctionKind,
894 DiagnosticFactTag::ArgumentIndex,
895 DiagnosticFactTag::TypeFamily,
896 ],
897 )
898 || tags_match(
899 fact_count,
900 fact_at,
901 &[
902 DiagnosticFactTag::OperatorKind,
903 DiagnosticFactTag::TypeFamily,
904 DiagnosticFactTag::TypeFamily,
905 ],
906 )
907 || tags_match(
908 fact_count,
909 fact_at,
910 &[
911 DiagnosticFactTag::BranchIndex,
912 DiagnosticFactTag::TypeFamily,
913 DiagnosticFactTag::TypeFamily,
914 ],
915 )
916 || tags_match(
917 fact_count,
918 fact_at,
919 &[
920 DiagnosticFactTag::TypeFamily,
921 DiagnosticFactTag::BranchIndex,
922 DiagnosticFactTag::TypeFamily,
923 ],
924 )
925 || tags_match(
926 fact_count,
927 fact_at,
928 &[
929 DiagnosticFactTag::BranchIndex,
930 DiagnosticFactTag::TypeFamily,
931 DiagnosticFactTag::BranchIndex,
932 DiagnosticFactTag::TypeFamily,
933 ],
934 )
935 || tags_match(
936 fact_count,
937 fact_at,
938 &[
939 DiagnosticFactTag::FunctionKind,
940 DiagnosticFactTag::ArgumentIndex,
941 DiagnosticFactTag::TypeFamily,
942 DiagnosticFactTag::ArgumentIndex,
943 DiagnosticFactTag::TypeFamily,
944 ],
945 )
946}
947
948fn cursor_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
949 if fact_count == 0 {
950 return true;
951 }
952 if tags_match(fact_count, fact_at, &[DiagnosticFactTag::DecodeReason]) {
953 return matches!(fact_at(0).1, 1 | 3 | 5 | 6 | 7 | 11 | 12);
954 }
955 if tags_match(
956 fact_count,
957 fact_at,
958 &[
959 DiagnosticFactTag::ActualLength,
960 DiagnosticFactTag::Maximum,
961 DiagnosticFactTag::DecodeReason,
962 ],
963 ) {
964 return fact_at(2).1 == DiagnosticDecodeReason::CursorTooLong.raw();
965 }
966 if tags_match(
967 fact_count,
968 fact_at,
969 &[
970 DiagnosticFactTag::ComponentIndex,
971 DiagnosticFactTag::DecodeReason,
972 ],
973 ) {
974 return matches!(fact_at(1).1, 4 | 6 | 7);
975 }
976 tags_match(
977 fact_count,
978 fact_at,
979 &[
980 DiagnosticFactTag::ExpectedSignaturePrefix,
981 DiagnosticFactTag::ActualSignaturePrefix,
982 ],
983 ) || tags_match(
984 fact_count,
985 fact_at,
986 &[
987 DiagnosticFactTag::ExpectedOffset,
988 DiagnosticFactTag::ActualOffset,
989 ],
990 )
991}
992
993fn store_corruption_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
994 fact_count == 0
995 || (tags_match(
996 fact_count,
997 fact_at,
998 &[
999 DiagnosticFactTag::ComponentKind,
1000 DiagnosticFactTag::ActualLength,
1001 DiagnosticFactTag::Limit,
1002 ],
1003 ) && fact_at(0).1 == DiagnosticComponentKind::CommitDataKey.raw())
1004 || tags_match(
1005 fact_count,
1006 fact_at,
1007 &[
1008 DiagnosticFactTag::ExpectedEntityTag,
1009 DiagnosticFactTag::ActualEntityTag,
1010 ],
1011 )
1012}
1013
1014fn runtime_corruption_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1015 store_corruption_schema(fact_count, fact_at)
1016 || (tags_match(fact_count, fact_at, &[DiagnosticFactTag::DecodeReason])
1017 && matches!(fact_at(0).1, 8..=10))
1018}
1019
1020fn incompatible_format_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1021 fact_count == 0
1022 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ExpectedVersion])
1023 || tags_match(
1024 fact_count,
1025 fact_at,
1026 &[
1027 DiagnosticFactTag::ExpectedVersion,
1028 DiagnosticFactTag::ActualVersion,
1029 ],
1030 )
1031}
1032
1033fn runtime_invariant_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1034 fact_count == 0
1035 || (tags_match(
1036 fact_count,
1037 fact_at,
1038 &[
1039 DiagnosticFactTag::EntityTag,
1040 DiagnosticFactTag::PhysicalGeneration,
1041 DiagnosticFactTag::ComponentKind,
1042 DiagnosticFactTag::ActualArity,
1043 DiagnosticFactTag::Maximum,
1044 ],
1045 ) && fact_at(2).1 == DiagnosticComponentKind::IndexKey.raw())
1046}
1047
1048fn runtime_conflict_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1049 fact_count == 0
1050 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ExpectedRevision])
1051 || tags_match(
1052 fact_count,
1053 fact_at,
1054 &[
1055 DiagnosticFactTag::ExpectedRevision,
1056 DiagnosticFactTag::CurrentRevision,
1057 ],
1058 )
1059}
1060
1061fn runtime_unsupported_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1062 fact_count == 0
1063 || (tags_match(
1064 fact_count,
1065 fact_at,
1066 &[
1067 DiagnosticFactTag::EntityTag,
1068 DiagnosticFactTag::PhysicalGeneration,
1069 DiagnosticFactTag::ComponentKind,
1070 DiagnosticFactTag::ActualLength,
1071 DiagnosticFactTag::Limit,
1072 ],
1073 ) && fact_at(2).1 == DiagnosticComponentKind::IndexKey.raw())
1074 || (tags_match(
1075 fact_count,
1076 fact_at,
1077 &[
1078 DiagnosticFactTag::EntityTag,
1079 DiagnosticFactTag::PhysicalGeneration,
1080 DiagnosticFactTag::ComponentIndex,
1081 DiagnosticFactTag::ComponentKind,
1082 DiagnosticFactTag::ActualLength,
1083 DiagnosticFactTag::Limit,
1084 ],
1085 ) && fact_at(3).1 == DiagnosticComponentKind::IndexKeyComponent.raw())
1086}
1087
1088fn runtime_internal_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1089 if fact_count >= 2
1092 && fact_at(0).0 == DiagnosticFactTag::EntityTag.raw()
1093 && fact_at(1).0 == DiagnosticFactTag::RelationId.raw()
1094 {
1095 return runtime_internal_detail_schema(fact_count - 2, &|index| fact_at(index + 2));
1096 }
1097 runtime_internal_detail_schema(fact_count, fact_at)
1098}
1099
1100fn runtime_internal_detail_schema(
1101 fact_count: usize,
1102 fact_at: &impl Fn(usize) -> (u8, u64),
1103) -> bool {
1104 fact_count == 0
1105 || tags_match(
1106 fact_count,
1107 fact_at,
1108 &[
1109 DiagnosticFactTag::ExpectedMemoryId,
1110 DiagnosticFactTag::ActualMemoryId,
1111 ],
1112 )
1113 || (tags_match(
1114 fact_count,
1115 fact_at,
1116 &[
1117 DiagnosticFactTag::ComponentKind,
1118 DiagnosticFactTag::ExpectedArity,
1119 DiagnosticFactTag::ActualArity,
1120 ],
1121 ) && fact_at(0).1 == DiagnosticComponentKind::RelationTargetPrimaryKey.raw())
1122}
1123
1124fn constraint_schema(
1125 fact_count: usize,
1126 fact_at: &impl Fn(usize) -> (u8, u64),
1127 allow_targeted_path: bool,
1128) -> bool {
1129 const COMMON: &[DiagnosticFactTag] = &[
1130 DiagnosticFactTag::AcceptedSchemaFingerprintMethod,
1131 DiagnosticFactTag::AcceptedSchemaFingerprintHigh,
1132 DiagnosticFactTag::AcceptedSchemaFingerprintLow,
1133 DiagnosticFactTag::EntityTag,
1134 DiagnosticFactTag::ConstraintId,
1135 DiagnosticFactTag::ConstraintKind,
1136 DiagnosticFactTag::ConstraintContext,
1137 ];
1138 if fact_count < COMMON.len()
1139 || !tags_prefix_matches(fact_count, fact_at, COMMON)
1140 || fact_at(6).1 != DiagnosticConstraintContext::WriteAdmission.raw()
1141 {
1142 return false;
1143 }
1144
1145 let constraint_kind = fact_at(5).1;
1146 if DiagnosticConstraintKind::known(constraint_kind).is_none() {
1147 return false;
1148 }
1149 let mut index = COMMON.len();
1150 if index < fact_count && fact_at(index).0 == DiagnosticFactTag::MutationOperation.raw() {
1151 index += 1;
1152 if index < fact_count && fact_at(index).0 == DiagnosticFactTag::BatchPosition.raw() {
1153 index += 1;
1154 }
1155 }
1156
1157 let path_len = fact_count - index;
1158 if path_len == 0 {
1159 return !allow_targeted_path
1160 || constraint_kind != DiagnosticConstraintKind::TargetedRule.raw();
1161 }
1162 allow_targeted_path
1163 && constraint_kind == DiagnosticConstraintKind::TargetedRule.raw()
1164 && path_len <= 64
1165 && (index..fact_count).all(|position| {
1166 DiagnosticFactTag::known(fact_at(position).0).is_some_and(is_value_path_tag)
1167 })
1168}
1169
1170fn mutation_schema(
1173 fact_count: usize,
1174 fact_at: &impl Fn(usize) -> (u8, u64),
1175 required: &[DiagnosticFactTag],
1176) -> bool {
1177 tags_prefix_matches(fact_count, fact_at, required)
1178 && (fact_count == required.len()
1179 || (fact_count == required.len() + 1
1180 && fact_at(required.len()).0 == DiagnosticFactTag::BatchPosition.raw()))
1181}
1182
1183fn tags_match(
1184 fact_count: usize,
1185 fact_at: &impl Fn(usize) -> (u8, u64),
1186 expected: &[DiagnosticFactTag],
1187) -> bool {
1188 fact_count == expected.len() && tags_prefix_matches(fact_count, fact_at, expected)
1189}
1190
1191fn tags_prefix_matches(
1192 fact_count: usize,
1193 fact_at: &impl Fn(usize) -> (u8, u64),
1194 expected: &[DiagnosticFactTag],
1195) -> bool {
1196 fact_count >= expected.len()
1197 && expected
1198 .iter()
1199 .enumerate()
1200 .all(|(index, tag)| fact_at(index).0 == tag.raw())
1201}
1202
1203const fn is_value_path_tag(tag: DiagnosticFactTag) -> bool {
1204 matches!(
1205 tag,
1206 DiagnosticFactTag::RootField
1207 | DiagnosticFactTag::RecordMember
1208 | DiagnosticFactTag::TupleElement
1209 | DiagnosticFactTag::Newtype
1210 | DiagnosticFactTag::EnumVariant
1211 | DiagnosticFactTag::ListElement
1212 | DiagnosticFactTag::SetElement
1213 | DiagnosticFactTag::MapEntryKey
1214 | DiagnosticFactTag::MapEntryValue
1215 )
1216}
1217
1218const fn diagnostic_fact_value_is_valid(tag: DiagnosticFactTag, value: u64) -> bool {
1219 match tag {
1220 DiagnosticFactTag::AcceptedSchemaFingerprintMethod
1221 | DiagnosticFactTag::ExpectedMemoryId
1222 | DiagnosticFactTag::ActualMemoryId => value <= u8::MAX as u64,
1223 DiagnosticFactTag::ConstraintId
1224 | DiagnosticFactTag::FieldId
1225 | DiagnosticFactTag::IndexId
1226 | DiagnosticFactTag::RelationId
1227 | DiagnosticFactTag::BatchPosition
1228 | DiagnosticFactTag::FirstBatchPosition
1229 | DiagnosticFactTag::DuplicateBatchPosition
1230 | DiagnosticFactTag::RowLayout
1231 | DiagnosticFactTag::HistoryFloor
1232 | DiagnosticFactTag::CurrentLayout
1233 | DiagnosticFactTag::RootField
1234 | DiagnosticFactTag::Newtype
1235 | DiagnosticFactTag::ListElement
1236 | DiagnosticFactTag::SetElement
1237 | DiagnosticFactTag::MapEntryKey
1238 | DiagnosticFactTag::MapEntryValue => value <= u32::MAX as u64,
1239 DiagnosticFactTag::ConstraintKind => DiagnosticConstraintKind::known(value).is_some(),
1240 DiagnosticFactTag::BacklogResource => DiagnosticBacklogResource::known(value).is_some(),
1241 DiagnosticFactTag::ConstraintContext => DiagnosticConstraintContext::known(value).is_some(),
1242 DiagnosticFactTag::TypeFamily => DiagnosticTypeFamily::known(value).is_some(),
1243 DiagnosticFactTag::FunctionKind => DiagnosticFunctionKind::known(value).is_some(),
1244 DiagnosticFactTag::OperatorKind => DiagnosticOperatorKind::known(value).is_some(),
1245 DiagnosticFactTag::AggregateKind => DiagnosticAggregateKind::known(value).is_some(),
1246 DiagnosticFactTag::ComponentKind => DiagnosticComponentKind::known(value).is_some(),
1247 DiagnosticFactTag::DecodeReason => DiagnosticDecodeReason::known(value).is_some(),
1248 DiagnosticFactTag::BudgetResource => {
1249 DiagnosticExecutionBudgetResource::known(value).is_some()
1250 }
1251 DiagnosticFactTag::ExecutionBudgetScope => {
1252 DiagnosticExecutionBudgetScope::known(value).is_some()
1253 }
1254 DiagnosticFactTag::ExecutionLane => DiagnosticExecutionLane::known(value).is_some(),
1255 DiagnosticFactTag::MutationOperation => DiagnosticMutationOperation::known(value).is_some(),
1256 _ => true,
1257 }
1258}
1259
1260#[cfg(test)]
1261mod tests {
1262 use super::{
1263 DiagnosticAggregateKind, DiagnosticBacklogResource, DiagnosticComponentKind,
1264 DiagnosticConstraintContext, DiagnosticConstraintKind, DiagnosticDecodeReason,
1265 DiagnosticExecutionBudgetResource, DiagnosticExecutionBudgetScope, DiagnosticExecutionLane,
1266 DiagnosticFactSchemaMismatch, DiagnosticFactTag, DiagnosticFunctionKind,
1267 DiagnosticMutationOperation, DiagnosticOperatorKind, DiagnosticTypeFamily,
1268 ORDERED_FACT_TAGS, pack_u32_pair, unpack_u32_pair, validate_known_diagnostic_fact_schema,
1269 validate_raw_diagnostic_fact_schema,
1270 };
1271 use crate::ErrorCode;
1272
1273 #[test]
1274 fn memory_admission_fact_schemas_keep_counts_and_ids_bounded() {
1275 for code in [
1276 ErrorCode::RUNTIME_BOUNDARY_MEMORY_ALLOCATION_RESOLUTION_FAILED,
1277 ErrorCode::RUNTIME_BOUNDARY_MEMORY_HISTORICAL_JOURNAL_UNAVAILABLE,
1278 ] {
1279 assert_eq!(validate_known_diagnostic_fact_schema(code, &[]), Ok(()));
1280 for id in [0, 42, 254] {
1281 assert_eq!(
1282 validate_known_diagnostic_fact_schema(
1283 code,
1284 &[(DiagnosticFactTag::ActualMemoryId, id)]
1285 ),
1286 Ok(())
1287 );
1288 }
1289 for facts in [
1290 vec![(DiagnosticFactTag::ActualMemoryId, 255)],
1291 vec![(DiagnosticFactTag::ExpectedMemoryId, 42)],
1292 ] {
1293 assert!(validate_known_diagnostic_fact_schema(code, &facts).is_err());
1294 }
1295 }
1296 let roles = ErrorCode::RUNTIME_BOUNDARY_MEMORY_ALLOCATION_ROLES_INCOMPLETE;
1297 for count in [3, 4] {
1298 assert_eq!(
1299 validate_known_diagnostic_fact_schema(
1300 roles,
1301 &[(DiagnosticFactTag::ExpectedCount, count)]
1302 ),
1303 Ok(())
1304 );
1305 }
1306 for facts in [
1307 vec![],
1308 vec![(DiagnosticFactTag::ExpectedCount, 0)],
1309 vec![(DiagnosticFactTag::ExpectedCount, 5)],
1310 vec![(DiagnosticFactTag::ActualCount, 3)],
1311 ] {
1312 assert!(validate_known_diagnostic_fact_schema(roles, &facts).is_err());
1313 }
1314 let mismatch = ErrorCode::RUNTIME_BOUNDARY_MEMORY_DECLARATION_SNAPSHOT_MISMATCH;
1315 assert_eq!(validate_known_diagnostic_fact_schema(mismatch, &[]), Ok(()));
1316 assert_eq!(
1317 validate_known_diagnostic_fact_schema(
1318 mismatch,
1319 &[
1320 (DiagnosticFactTag::ExpectedMemoryId, 0),
1321 (DiagnosticFactTag::ActualMemoryId, 254)
1322 ]
1323 ),
1324 Ok(())
1325 );
1326 for facts in [
1327 vec![(DiagnosticFactTag::ExpectedMemoryId, 42)],
1328 vec![
1329 (DiagnosticFactTag::ExpectedMemoryId, 42),
1330 (DiagnosticFactTag::ActualMemoryId, 42),
1331 ],
1332 vec![
1333 (DiagnosticFactTag::ActualMemoryId, 42),
1334 (DiagnosticFactTag::ExpectedMemoryId, 43),
1335 ],
1336 vec![
1337 (DiagnosticFactTag::ExpectedMemoryId, 255),
1338 (DiagnosticFactTag::ActualMemoryId, 42),
1339 ],
1340 ] {
1341 assert!(validate_known_diagnostic_fact_schema(mismatch, &facts).is_err());
1342 }
1343 for code in [
1344 ErrorCode::RUNTIME_BOUNDARY_MEMORY_NAMESPACE_REMOVED,
1345 ErrorCode::RUNTIME_BOUNDARY_MEMORY_DECLARATION_INVALID,
1346 ] {
1347 assert_eq!(validate_known_diagnostic_fact_schema(code, &[]), Ok(()));
1348 assert!(
1349 validate_known_diagnostic_fact_schema(
1350 code,
1351 &[(DiagnosticFactTag::ActualMemoryId, 42)]
1352 )
1353 .is_err()
1354 );
1355 }
1356 }
1357
1358 #[test]
1359 fn bucket_mismatch_requires_two_distinct_nonzero_page_counts() {
1360 let code = ErrorCode::RUNTIME_BOUNDARY_MEMORY_BUCKET_SIZE_MISMATCH;
1361 let expected = DiagnosticFactTag::Expected;
1362 let actual = DiagnosticFactTag::Actual;
1363 assert_eq!(
1364 validate_known_diagnostic_fact_schema(code, &[(expected, 16), (actual, 128)]),
1365 Ok(()),
1366 );
1367 for facts in [
1368 vec![],
1369 vec![(expected, 16)],
1370 vec![(actual, 128), (expected, 16)],
1371 vec![(expected, 16), (expected, 128)],
1372 vec![(expected, 16), (actual, 16)],
1373 vec![(expected, 0), (actual, 128)],
1374 vec![(expected, 16), (actual, 0)],
1375 vec![(expected, 65_536), (actual, 128)],
1376 vec![(expected, 16), (actual, 65_536)],
1377 ] {
1378 assert_eq!(
1379 validate_known_diagnostic_fact_schema(code, &facts),
1380 Err(DiagnosticFactSchemaMismatch::InvalidSequence),
1381 );
1382 }
1383 assert_eq!(
1384 validate_known_diagnostic_fact_schema(
1385 code,
1386 &[(expected, 16), (actual, 128), (actual, 128)],
1387 ),
1388 Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded),
1389 );
1390 }
1391
1392 #[test]
1393 fn fact_tag_registry_is_fixed_unique_and_contiguous() {
1394 for (index, tag) in ORDERED_FACT_TAGS.iter().copied().enumerate() {
1395 let expected = u8::try_from(index + 1).expect("fact-tag index fits u8");
1396 assert_eq!(tag.raw(), expected);
1397 assert_eq!(DiagnosticFactTag::known(expected), Some(tag));
1398 }
1399
1400 assert_eq!(DiagnosticFactTag::known(0), None);
1401 assert_eq!(DiagnosticFactTag::known(96), None);
1402 assert_eq!(DiagnosticFactTag::known(u8::MAX), None);
1403 }
1404
1405 #[test]
1406 fn execution_budget_fact_value_registries_are_fixed() {
1407 assert_eq!(DiagnosticBacklogResource::Batches.raw(), 1);
1408 assert_eq!(DiagnosticBacklogResource::Records.raw(), 2);
1409 assert_eq!(DiagnosticBacklogResource::EncodedBytes.raw(), 3);
1410 assert_eq!(DiagnosticBacklogResource::known(4), None);
1411
1412 for (index, resource) in DiagnosticExecutionBudgetResource::ALL
1413 .iter()
1414 .copied()
1415 .enumerate()
1416 {
1417 let expected = u64::try_from(index + 1).expect("resource index fits u64");
1418 assert_eq!(resource.raw(), expected);
1419 assert_eq!(
1420 DiagnosticExecutionBudgetResource::known(expected),
1421 Some(resource)
1422 );
1423 }
1424 assert_eq!(DiagnosticExecutionBudgetResource::known(0), None);
1425 assert_eq!(DiagnosticExecutionBudgetResource::known(21), None);
1426
1427 assert_eq!(DiagnosticExecutionBudgetScope::Execution.raw(), 1);
1428 assert_eq!(DiagnosticExecutionBudgetScope::Request.raw(), 2);
1429 assert_eq!(DiagnosticExecutionBudgetScope::known(3), None);
1430
1431 assert_eq!(DiagnosticExecutionLane::PublicRead.raw(), 1);
1432 assert_eq!(DiagnosticExecutionLane::TrustedRead.raw(), 2);
1433 assert_eq!(DiagnosticExecutionLane::Diagnostic.raw(), 3);
1434 assert_eq!(DiagnosticExecutionLane::Mutation.raw(), 4);
1435 assert_eq!(DiagnosticExecutionLane::Recovery.raw(), 5);
1436 assert_eq!(DiagnosticExecutionLane::known(6), None);
1437 }
1438
1439 #[test]
1440 fn accepted_identity_pair_packing_is_exact() {
1441 for pair in [
1442 (0, 0),
1443 (1, 2),
1444 (u32::MAX, 0),
1445 (0, u32::MAX),
1446 (u32::MAX, u32::MAX),
1447 ] {
1448 assert_eq!(unpack_u32_pair(pack_u32_pair(pair.0, pair.1)), pair);
1449 }
1450 }
1451
1452 #[test]
1453 fn constraint_fact_value_registries_are_fixed() {
1454 assert_eq!(DiagnosticConstraintKind::Check.raw(), 1);
1455 assert_eq!(DiagnosticConstraintKind::NotNull.raw(), 2);
1456 assert_eq!(DiagnosticConstraintKind::Relation.raw(), 3);
1457 assert_eq!(DiagnosticConstraintKind::TargetedRule.raw(), 4);
1458 assert_eq!(DiagnosticConstraintKind::Unique.raw(), 5);
1459 assert_eq!(DiagnosticConstraintKind::known(0), None);
1460 assert_eq!(DiagnosticConstraintKind::known(6), None);
1461
1462 assert_eq!(DiagnosticConstraintContext::Integrity.raw(), 1);
1463 assert_eq!(DiagnosticConstraintContext::MigrationValidation.raw(), 2);
1464 assert_eq!(DiagnosticConstraintContext::WriteAdmission.raw(), 3);
1465 assert_eq!(DiagnosticConstraintContext::known(0), None);
1466 assert_eq!(DiagnosticConstraintContext::known(4), None);
1467 }
1468
1469 #[test]
1470 fn component_kind_registry_is_fixed_and_numeric() {
1471 let kinds = [
1472 DiagnosticComponentKind::CommitDataKey,
1473 DiagnosticComponentKind::IndexKey,
1474 DiagnosticComponentKind::IndexKeyComponent,
1475 DiagnosticComponentKind::RelationTargetPrimaryKey,
1476 ];
1477
1478 for (index, kind) in kinds.iter().copied().enumerate() {
1479 let expected = (index + 1) as u64;
1480 assert_eq!(kind.raw(), expected);
1481 assert_eq!(DiagnosticComponentKind::known(expected), Some(kind));
1482 assert_eq!(format!("{kind:?}"), expected.to_string());
1483 }
1484 assert_eq!(DiagnosticComponentKind::known(0), None);
1485 assert_eq!(DiagnosticComponentKind::known(5), None);
1486 }
1487
1488 #[test]
1489 fn decode_reason_registry_is_fixed_and_numeric() {
1490 let reasons = [
1491 DiagnosticDecodeReason::CursorEmpty,
1492 DiagnosticDecodeReason::CursorTooLong,
1493 DiagnosticDecodeReason::CursorInvalidLength,
1494 DiagnosticDecodeReason::CursorInvalidBase64,
1495 DiagnosticDecodeReason::CursorGroupedDirectionMismatch,
1496 DiagnosticDecodeReason::CursorTokenEncode,
1497 DiagnosticDecodeReason::CursorTokenDecode,
1498 DiagnosticDecodeReason::RecoveryMarkerMagic,
1499 DiagnosticDecodeReason::RecoveryMarkerChecksum,
1500 DiagnosticDecodeReason::RecoveryMarkerState,
1501 DiagnosticDecodeReason::CursorGroupedContinuationRequiresLimit,
1502 DiagnosticDecodeReason::CursorGlobalDistinctContinuationUnsupported,
1503 ];
1504
1505 for (index, reason) in reasons.iter().copied().enumerate() {
1506 let expected = (index + 1) as u64;
1507 assert_eq!(reason.raw(), expected);
1508 assert_eq!(DiagnosticDecodeReason::known(expected), Some(reason));
1509 assert_eq!(format!("{reason:?}"), expected.to_string());
1510 }
1511
1512 assert_eq!(DiagnosticDecodeReason::known(0), None);
1513 assert_eq!(DiagnosticDecodeReason::known(13), None);
1514 }
1515
1516 #[test]
1517 fn mutation_operation_registry_is_fixed_and_numeric() {
1518 let operations = [
1519 DiagnosticMutationOperation::Insert,
1520 DiagnosticMutationOperation::Replace,
1521 DiagnosticMutationOperation::Update,
1522 DiagnosticMutationOperation::Delete,
1523 ];
1524
1525 for (index, operation) in operations.iter().copied().enumerate() {
1526 let expected = (index + 1) as u64;
1527 assert_eq!(operation.raw(), expected);
1528 assert_eq!(
1529 DiagnosticMutationOperation::known(expected),
1530 Some(operation)
1531 );
1532 assert_eq!(format!("{operation:?}"), expected.to_string());
1533 }
1534
1535 assert_eq!(DiagnosticMutationOperation::known(0), None);
1536 assert_eq!(DiagnosticMutationOperation::known(5), None);
1537 }
1538
1539 #[test]
1540 fn query_kind_registries_are_fixed_contiguous_and_numeric() {
1541 for raw in 1..=9 {
1542 let value = DiagnosticTypeFamily::known(raw).expect("type family should be known");
1543 assert_eq!(value.raw(), raw);
1544 assert_eq!(format!("{value:?}"), raw.to_string());
1545 }
1546 assert_eq!(DiagnosticTypeFamily::known(0), None);
1547 assert_eq!(DiagnosticTypeFamily::known(10), None);
1548
1549 for raw in 1..=39 {
1550 let value = DiagnosticFunctionKind::known(raw).expect("function kind should be known");
1551 assert_eq!(value.raw(), raw);
1552 assert_eq!(format!("{value:?}"), raw.to_string());
1553 }
1554 assert_eq!(DiagnosticFunctionKind::known(0), None);
1555 assert_eq!(DiagnosticFunctionKind::known(40), None);
1556
1557 for raw in 1..=18 {
1558 let value = DiagnosticOperatorKind::known(raw).expect("operator kind should be known");
1559 assert_eq!(value.raw(), raw);
1560 assert_eq!(format!("{value:?}"), raw.to_string());
1561 }
1562 assert_eq!(DiagnosticOperatorKind::known(0), None);
1563 assert_eq!(DiagnosticOperatorKind::known(19), None);
1564
1565 for raw in 1..=8 {
1566 let value =
1567 DiagnosticAggregateKind::known(raw).expect("aggregate kind should be known");
1568 assert_eq!(value.raw(), raw);
1569 assert_eq!(format!("{value:?}"), raw.to_string());
1570 }
1571 assert_eq!(DiagnosticAggregateKind::known(0), None);
1572 assert_eq!(DiagnosticAggregateKind::known(9), None);
1573 }
1574
1575 #[test]
1576 fn per_code_schema_rejects_missing_disallowed_and_noncanonical_tags() {
1577 let valid = [
1578 (DiagnosticFactTag::ActualCount, 5),
1579 (DiagnosticFactTag::Limit, 4),
1580 ];
1581 assert_eq!(
1582 validate_known_diagnostic_fact_schema(
1583 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1584 &valid,
1585 ),
1586 Ok(())
1587 );
1588 assert_eq!(
1589 validate_known_diagnostic_fact_schema(
1590 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1591 &valid[..1],
1592 ),
1593 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1594 );
1595 assert_eq!(
1596 validate_known_diagnostic_fact_schema(
1597 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1598 &[valid[1], valid[0]],
1599 ),
1600 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1601 );
1602 assert_eq!(
1603 validate_known_diagnostic_fact_schema(ErrorCode::QUERY_VALIDATE, &valid),
1604 Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded)
1605 );
1606
1607 assert_eq!(
1608 validate_known_diagnostic_fact_schema(
1609 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_COMMIT_WORK_EXCEEDED,
1610 &valid,
1611 ),
1612 Ok(())
1613 );
1614 assert_eq!(
1615 validate_known_diagnostic_fact_schema(
1616 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_COMMIT_WORK_EXCEEDED,
1617 &valid[1..],
1618 ),
1619 Ok(())
1620 );
1621 }
1622
1623 #[test]
1624 fn execution_budget_schema_requires_complete_typed_attribution() {
1625 let valid = [
1626 (
1627 DiagnosticFactTag::BudgetResource,
1628 DiagnosticExecutionBudgetResource::StoredBytesRead.raw(),
1629 ),
1630 (DiagnosticFactTag::Limit, 4_096),
1631 (DiagnosticFactTag::Actual, 4_097),
1632 (
1633 DiagnosticFactTag::ExecutionBudgetScope,
1634 DiagnosticExecutionBudgetScope::Request.raw(),
1635 ),
1636 (
1637 DiagnosticFactTag::ExecutionLane,
1638 DiagnosticExecutionLane::TrustedRead.raw(),
1639 ),
1640 (DiagnosticFactTag::QueryShapeFingerprintPrefix, 17),
1641 ];
1642 assert_eq!(
1643 validate_known_diagnostic_fact_schema(
1644 ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1645 &valid,
1646 ),
1647 Ok(())
1648 );
1649 assert_eq!(
1650 validate_known_diagnostic_fact_schema(
1651 ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1652 &valid[..5],
1653 ),
1654 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1655 );
1656
1657 let mut invalid_resource = valid;
1658 invalid_resource[0].1 = 0;
1659 assert_eq!(
1660 validate_known_diagnostic_fact_schema(
1661 ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1662 &invalid_resource,
1663 ),
1664 Err(DiagnosticFactSchemaMismatch::InvalidValue)
1665 );
1666 }
1667
1668 #[test]
1669 fn raw_schema_keeps_unknown_context_numeric_but_marks_it_invalid() {
1670 assert_eq!(
1671 validate_raw_diagnostic_fact_schema(
1672 ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
1673 &[(u8::MAX, 17)],
1674 ),
1675 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1676 );
1677 assert_eq!(
1678 validate_raw_diagnostic_fact_schema(
1679 ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
1680 &[(DiagnosticFactTag::DecodeReason.raw(), u64::MAX)],
1681 ),
1682 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1683 );
1684 }
1685
1686 #[test]
1687 fn constraint_schema_enforces_authority_operation_and_bounded_path_suffix() {
1688 let mut targeted = vec![
1689 (DiagnosticFactTag::AcceptedSchemaFingerprintMethod, 1),
1690 (DiagnosticFactTag::AcceptedSchemaFingerprintHigh, 2),
1691 (DiagnosticFactTag::AcceptedSchemaFingerprintLow, 3),
1692 (DiagnosticFactTag::EntityTag, 17),
1693 (DiagnosticFactTag::ConstraintId, 4),
1694 (
1695 DiagnosticFactTag::ConstraintKind,
1696 DiagnosticConstraintKind::TargetedRule.raw(),
1697 ),
1698 (
1699 DiagnosticFactTag::ConstraintContext,
1700 DiagnosticConstraintContext::WriteAdmission.raw(),
1701 ),
1702 (
1703 DiagnosticFactTag::MutationOperation,
1704 DiagnosticMutationOperation::Insert.raw(),
1705 ),
1706 (DiagnosticFactTag::BatchPosition, 0),
1707 ];
1708 targeted.extend((0..64).map(|index| (DiagnosticFactTag::ListElement, index)));
1709 assert_eq!(targeted.len(), 73);
1710 assert_eq!(
1711 validate_known_diagnostic_fact_schema(
1712 ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1713 targeted.as_slice(),
1714 ),
1715 Ok(())
1716 );
1717
1718 let mut overlong = targeted.clone();
1719 overlong.push((DiagnosticFactTag::ListElement, 64));
1720 assert_eq!(
1721 validate_known_diagnostic_fact_schema(
1722 ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1723 overlong.as_slice(),
1724 ),
1725 Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded)
1726 );
1727
1728 let mut non_targeted_path = targeted;
1729 non_targeted_path[5].1 = DiagnosticConstraintKind::Unique.raw();
1730 assert_eq!(
1731 validate_known_diagnostic_fact_schema(
1732 ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1733 non_targeted_path.as_slice(),
1734 ),
1735 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1736 );
1737 }
1738
1739 #[test]
1740 fn schema_enforces_global_ceiling_before_per_code_ceiling() {
1741 let facts = vec![(DiagnosticFactTag::ActualCount.raw(), 0); 81];
1742 assert_eq!(
1743 validate_raw_diagnostic_fact_schema(ErrorCode::QUERY_PLAN, facts.as_slice()),
1744 Err(DiagnosticFactSchemaMismatch::GlobalMaximumExceeded)
1745 );
1746 }
1747}