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::ComponentIndex,
1070 DiagnosticFactTag::ComponentKind,
1071 DiagnosticFactTag::ActualLength,
1072 DiagnosticFactTag::Limit,
1073 ],
1074 ) && fact_at(3).1 == DiagnosticComponentKind::IndexKeyComponent.raw())
1075}
1076
1077fn runtime_internal_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1078 if fact_count >= 2
1081 && fact_at(0).0 == DiagnosticFactTag::EntityTag.raw()
1082 && fact_at(1).0 == DiagnosticFactTag::RelationId.raw()
1083 {
1084 return runtime_internal_detail_schema(fact_count - 2, &|index| fact_at(index + 2));
1085 }
1086 runtime_internal_detail_schema(fact_count, fact_at)
1087}
1088
1089fn runtime_internal_detail_schema(
1090 fact_count: usize,
1091 fact_at: &impl Fn(usize) -> (u8, u64),
1092) -> bool {
1093 fact_count == 0
1094 || tags_match(
1095 fact_count,
1096 fact_at,
1097 &[
1098 DiagnosticFactTag::ExpectedMemoryId,
1099 DiagnosticFactTag::ActualMemoryId,
1100 ],
1101 )
1102 || (tags_match(
1103 fact_count,
1104 fact_at,
1105 &[
1106 DiagnosticFactTag::ComponentKind,
1107 DiagnosticFactTag::ExpectedArity,
1108 DiagnosticFactTag::ActualArity,
1109 ],
1110 ) && fact_at(0).1 == DiagnosticComponentKind::RelationTargetPrimaryKey.raw())
1111}
1112
1113fn constraint_schema(
1114 fact_count: usize,
1115 fact_at: &impl Fn(usize) -> (u8, u64),
1116 allow_targeted_path: bool,
1117) -> bool {
1118 const COMMON: &[DiagnosticFactTag] = &[
1119 DiagnosticFactTag::AcceptedSchemaFingerprintMethod,
1120 DiagnosticFactTag::AcceptedSchemaFingerprintHigh,
1121 DiagnosticFactTag::AcceptedSchemaFingerprintLow,
1122 DiagnosticFactTag::EntityTag,
1123 DiagnosticFactTag::ConstraintId,
1124 DiagnosticFactTag::ConstraintKind,
1125 DiagnosticFactTag::ConstraintContext,
1126 ];
1127 if fact_count < COMMON.len()
1128 || !tags_prefix_matches(fact_count, fact_at, COMMON)
1129 || fact_at(6).1 != DiagnosticConstraintContext::WriteAdmission.raw()
1130 {
1131 return false;
1132 }
1133
1134 let constraint_kind = fact_at(5).1;
1135 if DiagnosticConstraintKind::known(constraint_kind).is_none() {
1136 return false;
1137 }
1138 let mut index = COMMON.len();
1139 if index < fact_count && fact_at(index).0 == DiagnosticFactTag::MutationOperation.raw() {
1140 index += 1;
1141 if index < fact_count && fact_at(index).0 == DiagnosticFactTag::BatchPosition.raw() {
1142 index += 1;
1143 }
1144 }
1145
1146 let path_len = fact_count - index;
1147 if path_len == 0 {
1148 return !allow_targeted_path
1149 || constraint_kind != DiagnosticConstraintKind::TargetedRule.raw();
1150 }
1151 allow_targeted_path
1152 && constraint_kind == DiagnosticConstraintKind::TargetedRule.raw()
1153 && path_len <= 64
1154 && (index..fact_count).all(|position| {
1155 DiagnosticFactTag::known(fact_at(position).0).is_some_and(is_value_path_tag)
1156 })
1157}
1158
1159fn mutation_schema(
1162 fact_count: usize,
1163 fact_at: &impl Fn(usize) -> (u8, u64),
1164 required: &[DiagnosticFactTag],
1165) -> bool {
1166 tags_prefix_matches(fact_count, fact_at, required)
1167 && (fact_count == required.len()
1168 || (fact_count == required.len() + 1
1169 && fact_at(required.len()).0 == DiagnosticFactTag::BatchPosition.raw()))
1170}
1171
1172fn tags_match(
1173 fact_count: usize,
1174 fact_at: &impl Fn(usize) -> (u8, u64),
1175 expected: &[DiagnosticFactTag],
1176) -> bool {
1177 fact_count == expected.len() && tags_prefix_matches(fact_count, fact_at, expected)
1178}
1179
1180fn tags_prefix_matches(
1181 fact_count: usize,
1182 fact_at: &impl Fn(usize) -> (u8, u64),
1183 expected: &[DiagnosticFactTag],
1184) -> bool {
1185 fact_count >= expected.len()
1186 && expected
1187 .iter()
1188 .enumerate()
1189 .all(|(index, tag)| fact_at(index).0 == tag.raw())
1190}
1191
1192const fn is_value_path_tag(tag: DiagnosticFactTag) -> bool {
1193 matches!(
1194 tag,
1195 DiagnosticFactTag::RootField
1196 | DiagnosticFactTag::RecordMember
1197 | DiagnosticFactTag::TupleElement
1198 | DiagnosticFactTag::Newtype
1199 | DiagnosticFactTag::EnumVariant
1200 | DiagnosticFactTag::ListElement
1201 | DiagnosticFactTag::SetElement
1202 | DiagnosticFactTag::MapEntryKey
1203 | DiagnosticFactTag::MapEntryValue
1204 )
1205}
1206
1207const fn diagnostic_fact_value_is_valid(tag: DiagnosticFactTag, value: u64) -> bool {
1208 match tag {
1209 DiagnosticFactTag::AcceptedSchemaFingerprintMethod
1210 | DiagnosticFactTag::ExpectedMemoryId
1211 | DiagnosticFactTag::ActualMemoryId => value <= u8::MAX as u64,
1212 DiagnosticFactTag::ConstraintId
1213 | DiagnosticFactTag::FieldId
1214 | DiagnosticFactTag::IndexId
1215 | DiagnosticFactTag::RelationId
1216 | DiagnosticFactTag::BatchPosition
1217 | DiagnosticFactTag::FirstBatchPosition
1218 | DiagnosticFactTag::DuplicateBatchPosition
1219 | DiagnosticFactTag::RowLayout
1220 | DiagnosticFactTag::HistoryFloor
1221 | DiagnosticFactTag::CurrentLayout
1222 | DiagnosticFactTag::RootField
1223 | DiagnosticFactTag::Newtype
1224 | DiagnosticFactTag::ListElement
1225 | DiagnosticFactTag::SetElement
1226 | DiagnosticFactTag::MapEntryKey
1227 | DiagnosticFactTag::MapEntryValue => value <= u32::MAX as u64,
1228 DiagnosticFactTag::ConstraintKind => DiagnosticConstraintKind::known(value).is_some(),
1229 DiagnosticFactTag::BacklogResource => DiagnosticBacklogResource::known(value).is_some(),
1230 DiagnosticFactTag::ConstraintContext => DiagnosticConstraintContext::known(value).is_some(),
1231 DiagnosticFactTag::TypeFamily => DiagnosticTypeFamily::known(value).is_some(),
1232 DiagnosticFactTag::FunctionKind => DiagnosticFunctionKind::known(value).is_some(),
1233 DiagnosticFactTag::OperatorKind => DiagnosticOperatorKind::known(value).is_some(),
1234 DiagnosticFactTag::AggregateKind => DiagnosticAggregateKind::known(value).is_some(),
1235 DiagnosticFactTag::ComponentKind => DiagnosticComponentKind::known(value).is_some(),
1236 DiagnosticFactTag::DecodeReason => DiagnosticDecodeReason::known(value).is_some(),
1237 DiagnosticFactTag::BudgetResource => {
1238 DiagnosticExecutionBudgetResource::known(value).is_some()
1239 }
1240 DiagnosticFactTag::ExecutionBudgetScope => {
1241 DiagnosticExecutionBudgetScope::known(value).is_some()
1242 }
1243 DiagnosticFactTag::ExecutionLane => DiagnosticExecutionLane::known(value).is_some(),
1244 DiagnosticFactTag::MutationOperation => DiagnosticMutationOperation::known(value).is_some(),
1245 _ => true,
1246 }
1247}
1248
1249#[cfg(test)]
1250mod tests {
1251 use super::{
1252 DiagnosticAggregateKind, DiagnosticBacklogResource, DiagnosticComponentKind,
1253 DiagnosticConstraintContext, DiagnosticConstraintKind, DiagnosticDecodeReason,
1254 DiagnosticExecutionBudgetResource, DiagnosticExecutionBudgetScope, DiagnosticExecutionLane,
1255 DiagnosticFactSchemaMismatch, DiagnosticFactTag, DiagnosticFunctionKind,
1256 DiagnosticMutationOperation, DiagnosticOperatorKind, DiagnosticTypeFamily,
1257 ORDERED_FACT_TAGS, pack_u32_pair, unpack_u32_pair, validate_known_diagnostic_fact_schema,
1258 validate_raw_diagnostic_fact_schema,
1259 };
1260 use crate::ErrorCode;
1261
1262 #[test]
1263 fn memory_admission_fact_schemas_keep_counts_and_ids_bounded() {
1264 for code in [
1265 ErrorCode::RUNTIME_BOUNDARY_MEMORY_ALLOCATION_RESOLUTION_FAILED,
1266 ErrorCode::RUNTIME_BOUNDARY_MEMORY_HISTORICAL_JOURNAL_UNAVAILABLE,
1267 ] {
1268 assert_eq!(validate_known_diagnostic_fact_schema(code, &[]), Ok(()));
1269 for id in [0, 42, 254] {
1270 assert_eq!(
1271 validate_known_diagnostic_fact_schema(
1272 code,
1273 &[(DiagnosticFactTag::ActualMemoryId, id)]
1274 ),
1275 Ok(())
1276 );
1277 }
1278 for facts in [
1279 vec![(DiagnosticFactTag::ActualMemoryId, 255)],
1280 vec![(DiagnosticFactTag::ExpectedMemoryId, 42)],
1281 ] {
1282 assert!(validate_known_diagnostic_fact_schema(code, &facts).is_err());
1283 }
1284 }
1285 let roles = ErrorCode::RUNTIME_BOUNDARY_MEMORY_ALLOCATION_ROLES_INCOMPLETE;
1286 for count in [3, 4] {
1287 assert_eq!(
1288 validate_known_diagnostic_fact_schema(
1289 roles,
1290 &[(DiagnosticFactTag::ExpectedCount, count)]
1291 ),
1292 Ok(())
1293 );
1294 }
1295 for facts in [
1296 vec![],
1297 vec![(DiagnosticFactTag::ExpectedCount, 0)],
1298 vec![(DiagnosticFactTag::ExpectedCount, 5)],
1299 vec![(DiagnosticFactTag::ActualCount, 3)],
1300 ] {
1301 assert!(validate_known_diagnostic_fact_schema(roles, &facts).is_err());
1302 }
1303 let mismatch = ErrorCode::RUNTIME_BOUNDARY_MEMORY_DECLARATION_SNAPSHOT_MISMATCH;
1304 assert_eq!(validate_known_diagnostic_fact_schema(mismatch, &[]), Ok(()));
1305 assert_eq!(
1306 validate_known_diagnostic_fact_schema(
1307 mismatch,
1308 &[
1309 (DiagnosticFactTag::ExpectedMemoryId, 0),
1310 (DiagnosticFactTag::ActualMemoryId, 254)
1311 ]
1312 ),
1313 Ok(())
1314 );
1315 for facts in [
1316 vec![(DiagnosticFactTag::ExpectedMemoryId, 42)],
1317 vec![
1318 (DiagnosticFactTag::ExpectedMemoryId, 42),
1319 (DiagnosticFactTag::ActualMemoryId, 42),
1320 ],
1321 vec![
1322 (DiagnosticFactTag::ActualMemoryId, 42),
1323 (DiagnosticFactTag::ExpectedMemoryId, 43),
1324 ],
1325 vec![
1326 (DiagnosticFactTag::ExpectedMemoryId, 255),
1327 (DiagnosticFactTag::ActualMemoryId, 42),
1328 ],
1329 ] {
1330 assert!(validate_known_diagnostic_fact_schema(mismatch, &facts).is_err());
1331 }
1332 for code in [
1333 ErrorCode::RUNTIME_BOUNDARY_MEMORY_NAMESPACE_REMOVED,
1334 ErrorCode::RUNTIME_BOUNDARY_MEMORY_DECLARATION_INVALID,
1335 ] {
1336 assert_eq!(validate_known_diagnostic_fact_schema(code, &[]), Ok(()));
1337 assert!(
1338 validate_known_diagnostic_fact_schema(
1339 code,
1340 &[(DiagnosticFactTag::ActualMemoryId, 42)]
1341 )
1342 .is_err()
1343 );
1344 }
1345 }
1346
1347 #[test]
1348 fn bucket_mismatch_requires_two_distinct_nonzero_page_counts() {
1349 let code = ErrorCode::RUNTIME_BOUNDARY_MEMORY_BUCKET_SIZE_MISMATCH;
1350 let expected = DiagnosticFactTag::Expected;
1351 let actual = DiagnosticFactTag::Actual;
1352 assert_eq!(
1353 validate_known_diagnostic_fact_schema(code, &[(expected, 16), (actual, 128)]),
1354 Ok(()),
1355 );
1356 for facts in [
1357 vec![],
1358 vec![(expected, 16)],
1359 vec![(actual, 128), (expected, 16)],
1360 vec![(expected, 16), (expected, 128)],
1361 vec![(expected, 16), (actual, 16)],
1362 vec![(expected, 0), (actual, 128)],
1363 vec![(expected, 16), (actual, 0)],
1364 vec![(expected, 65_536), (actual, 128)],
1365 vec![(expected, 16), (actual, 65_536)],
1366 ] {
1367 assert_eq!(
1368 validate_known_diagnostic_fact_schema(code, &facts),
1369 Err(DiagnosticFactSchemaMismatch::InvalidSequence),
1370 );
1371 }
1372 assert_eq!(
1373 validate_known_diagnostic_fact_schema(
1374 code,
1375 &[(expected, 16), (actual, 128), (actual, 128)],
1376 ),
1377 Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded),
1378 );
1379 }
1380
1381 #[test]
1382 fn fact_tag_registry_is_fixed_unique_and_contiguous() {
1383 for (index, tag) in ORDERED_FACT_TAGS.iter().copied().enumerate() {
1384 let expected = u8::try_from(index + 1).expect("fact-tag index fits u8");
1385 assert_eq!(tag.raw(), expected);
1386 assert_eq!(DiagnosticFactTag::known(expected), Some(tag));
1387 }
1388
1389 assert_eq!(DiagnosticFactTag::known(0), None);
1390 assert_eq!(DiagnosticFactTag::known(96), None);
1391 assert_eq!(DiagnosticFactTag::known(u8::MAX), None);
1392 }
1393
1394 #[test]
1395 fn execution_budget_fact_value_registries_are_fixed() {
1396 assert_eq!(DiagnosticBacklogResource::Batches.raw(), 1);
1397 assert_eq!(DiagnosticBacklogResource::Records.raw(), 2);
1398 assert_eq!(DiagnosticBacklogResource::EncodedBytes.raw(), 3);
1399 assert_eq!(DiagnosticBacklogResource::known(4), None);
1400
1401 for (index, resource) in DiagnosticExecutionBudgetResource::ALL
1402 .iter()
1403 .copied()
1404 .enumerate()
1405 {
1406 let expected = u64::try_from(index + 1).expect("resource index fits u64");
1407 assert_eq!(resource.raw(), expected);
1408 assert_eq!(
1409 DiagnosticExecutionBudgetResource::known(expected),
1410 Some(resource)
1411 );
1412 }
1413 assert_eq!(DiagnosticExecutionBudgetResource::known(0), None);
1414 assert_eq!(DiagnosticExecutionBudgetResource::known(21), None);
1415
1416 assert_eq!(DiagnosticExecutionBudgetScope::Execution.raw(), 1);
1417 assert_eq!(DiagnosticExecutionBudgetScope::Request.raw(), 2);
1418 assert_eq!(DiagnosticExecutionBudgetScope::known(3), None);
1419
1420 assert_eq!(DiagnosticExecutionLane::PublicRead.raw(), 1);
1421 assert_eq!(DiagnosticExecutionLane::TrustedRead.raw(), 2);
1422 assert_eq!(DiagnosticExecutionLane::Diagnostic.raw(), 3);
1423 assert_eq!(DiagnosticExecutionLane::Mutation.raw(), 4);
1424 assert_eq!(DiagnosticExecutionLane::Recovery.raw(), 5);
1425 assert_eq!(DiagnosticExecutionLane::known(6), None);
1426 }
1427
1428 #[test]
1429 fn accepted_identity_pair_packing_is_exact() {
1430 for pair in [
1431 (0, 0),
1432 (1, 2),
1433 (u32::MAX, 0),
1434 (0, u32::MAX),
1435 (u32::MAX, u32::MAX),
1436 ] {
1437 assert_eq!(unpack_u32_pair(pack_u32_pair(pair.0, pair.1)), pair);
1438 }
1439 }
1440
1441 #[test]
1442 fn constraint_fact_value_registries_are_fixed() {
1443 assert_eq!(DiagnosticConstraintKind::Check.raw(), 1);
1444 assert_eq!(DiagnosticConstraintKind::NotNull.raw(), 2);
1445 assert_eq!(DiagnosticConstraintKind::Relation.raw(), 3);
1446 assert_eq!(DiagnosticConstraintKind::TargetedRule.raw(), 4);
1447 assert_eq!(DiagnosticConstraintKind::Unique.raw(), 5);
1448 assert_eq!(DiagnosticConstraintKind::known(0), None);
1449 assert_eq!(DiagnosticConstraintKind::known(6), None);
1450
1451 assert_eq!(DiagnosticConstraintContext::Integrity.raw(), 1);
1452 assert_eq!(DiagnosticConstraintContext::MigrationValidation.raw(), 2);
1453 assert_eq!(DiagnosticConstraintContext::WriteAdmission.raw(), 3);
1454 assert_eq!(DiagnosticConstraintContext::known(0), None);
1455 assert_eq!(DiagnosticConstraintContext::known(4), None);
1456 }
1457
1458 #[test]
1459 fn component_kind_registry_is_fixed_and_numeric() {
1460 let kinds = [
1461 DiagnosticComponentKind::CommitDataKey,
1462 DiagnosticComponentKind::IndexKey,
1463 DiagnosticComponentKind::IndexKeyComponent,
1464 DiagnosticComponentKind::RelationTargetPrimaryKey,
1465 ];
1466
1467 for (index, kind) in kinds.iter().copied().enumerate() {
1468 let expected = (index + 1) as u64;
1469 assert_eq!(kind.raw(), expected);
1470 assert_eq!(DiagnosticComponentKind::known(expected), Some(kind));
1471 assert_eq!(format!("{kind:?}"), expected.to_string());
1472 }
1473 assert_eq!(DiagnosticComponentKind::known(0), None);
1474 assert_eq!(DiagnosticComponentKind::known(5), None);
1475 }
1476
1477 #[test]
1478 fn decode_reason_registry_is_fixed_and_numeric() {
1479 let reasons = [
1480 DiagnosticDecodeReason::CursorEmpty,
1481 DiagnosticDecodeReason::CursorTooLong,
1482 DiagnosticDecodeReason::CursorInvalidLength,
1483 DiagnosticDecodeReason::CursorInvalidBase64,
1484 DiagnosticDecodeReason::CursorGroupedDirectionMismatch,
1485 DiagnosticDecodeReason::CursorTokenEncode,
1486 DiagnosticDecodeReason::CursorTokenDecode,
1487 DiagnosticDecodeReason::RecoveryMarkerMagic,
1488 DiagnosticDecodeReason::RecoveryMarkerChecksum,
1489 DiagnosticDecodeReason::RecoveryMarkerState,
1490 DiagnosticDecodeReason::CursorGroupedContinuationRequiresLimit,
1491 DiagnosticDecodeReason::CursorGlobalDistinctContinuationUnsupported,
1492 ];
1493
1494 for (index, reason) in reasons.iter().copied().enumerate() {
1495 let expected = (index + 1) as u64;
1496 assert_eq!(reason.raw(), expected);
1497 assert_eq!(DiagnosticDecodeReason::known(expected), Some(reason));
1498 assert_eq!(format!("{reason:?}"), expected.to_string());
1499 }
1500
1501 assert_eq!(DiagnosticDecodeReason::known(0), None);
1502 assert_eq!(DiagnosticDecodeReason::known(13), None);
1503 }
1504
1505 #[test]
1506 fn mutation_operation_registry_is_fixed_and_numeric() {
1507 let operations = [
1508 DiagnosticMutationOperation::Insert,
1509 DiagnosticMutationOperation::Replace,
1510 DiagnosticMutationOperation::Update,
1511 DiagnosticMutationOperation::Delete,
1512 ];
1513
1514 for (index, operation) in operations.iter().copied().enumerate() {
1515 let expected = (index + 1) as u64;
1516 assert_eq!(operation.raw(), expected);
1517 assert_eq!(
1518 DiagnosticMutationOperation::known(expected),
1519 Some(operation)
1520 );
1521 assert_eq!(format!("{operation:?}"), expected.to_string());
1522 }
1523
1524 assert_eq!(DiagnosticMutationOperation::known(0), None);
1525 assert_eq!(DiagnosticMutationOperation::known(5), None);
1526 }
1527
1528 #[test]
1529 fn query_kind_registries_are_fixed_contiguous_and_numeric() {
1530 for raw in 1..=9 {
1531 let value = DiagnosticTypeFamily::known(raw).expect("type family should be known");
1532 assert_eq!(value.raw(), raw);
1533 assert_eq!(format!("{value:?}"), raw.to_string());
1534 }
1535 assert_eq!(DiagnosticTypeFamily::known(0), None);
1536 assert_eq!(DiagnosticTypeFamily::known(10), None);
1537
1538 for raw in 1..=39 {
1539 let value = DiagnosticFunctionKind::known(raw).expect("function kind should be known");
1540 assert_eq!(value.raw(), raw);
1541 assert_eq!(format!("{value:?}"), raw.to_string());
1542 }
1543 assert_eq!(DiagnosticFunctionKind::known(0), None);
1544 assert_eq!(DiagnosticFunctionKind::known(40), None);
1545
1546 for raw in 1..=18 {
1547 let value = DiagnosticOperatorKind::known(raw).expect("operator kind should be known");
1548 assert_eq!(value.raw(), raw);
1549 assert_eq!(format!("{value:?}"), raw.to_string());
1550 }
1551 assert_eq!(DiagnosticOperatorKind::known(0), None);
1552 assert_eq!(DiagnosticOperatorKind::known(19), None);
1553
1554 for raw in 1..=8 {
1555 let value =
1556 DiagnosticAggregateKind::known(raw).expect("aggregate kind should be known");
1557 assert_eq!(value.raw(), raw);
1558 assert_eq!(format!("{value:?}"), raw.to_string());
1559 }
1560 assert_eq!(DiagnosticAggregateKind::known(0), None);
1561 assert_eq!(DiagnosticAggregateKind::known(9), None);
1562 }
1563
1564 #[test]
1565 fn per_code_schema_rejects_missing_disallowed_and_noncanonical_tags() {
1566 let valid = [
1567 (DiagnosticFactTag::ActualCount, 5),
1568 (DiagnosticFactTag::Limit, 4),
1569 ];
1570 assert_eq!(
1571 validate_known_diagnostic_fact_schema(
1572 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1573 &valid,
1574 ),
1575 Ok(())
1576 );
1577 assert_eq!(
1578 validate_known_diagnostic_fact_schema(
1579 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1580 &valid[..1],
1581 ),
1582 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1583 );
1584 assert_eq!(
1585 validate_known_diagnostic_fact_schema(
1586 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1587 &[valid[1], valid[0]],
1588 ),
1589 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1590 );
1591 assert_eq!(
1592 validate_known_diagnostic_fact_schema(ErrorCode::QUERY_VALIDATE, &valid),
1593 Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded)
1594 );
1595
1596 assert_eq!(
1597 validate_known_diagnostic_fact_schema(
1598 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_COMMIT_WORK_EXCEEDED,
1599 &valid,
1600 ),
1601 Ok(())
1602 );
1603 assert_eq!(
1604 validate_known_diagnostic_fact_schema(
1605 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_COMMIT_WORK_EXCEEDED,
1606 &valid[1..],
1607 ),
1608 Ok(())
1609 );
1610 }
1611
1612 #[test]
1613 fn execution_budget_schema_requires_complete_typed_attribution() {
1614 let valid = [
1615 (
1616 DiagnosticFactTag::BudgetResource,
1617 DiagnosticExecutionBudgetResource::StoredBytesRead.raw(),
1618 ),
1619 (DiagnosticFactTag::Limit, 4_096),
1620 (DiagnosticFactTag::Actual, 4_097),
1621 (
1622 DiagnosticFactTag::ExecutionBudgetScope,
1623 DiagnosticExecutionBudgetScope::Request.raw(),
1624 ),
1625 (
1626 DiagnosticFactTag::ExecutionLane,
1627 DiagnosticExecutionLane::TrustedRead.raw(),
1628 ),
1629 (DiagnosticFactTag::QueryShapeFingerprintPrefix, 17),
1630 ];
1631 assert_eq!(
1632 validate_known_diagnostic_fact_schema(
1633 ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1634 &valid,
1635 ),
1636 Ok(())
1637 );
1638 assert_eq!(
1639 validate_known_diagnostic_fact_schema(
1640 ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1641 &valid[..5],
1642 ),
1643 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1644 );
1645
1646 let mut invalid_resource = valid;
1647 invalid_resource[0].1 = 0;
1648 assert_eq!(
1649 validate_known_diagnostic_fact_schema(
1650 ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1651 &invalid_resource,
1652 ),
1653 Err(DiagnosticFactSchemaMismatch::InvalidValue)
1654 );
1655 }
1656
1657 #[test]
1658 fn raw_schema_keeps_unknown_context_numeric_but_marks_it_invalid() {
1659 assert_eq!(
1660 validate_raw_diagnostic_fact_schema(
1661 ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
1662 &[(u8::MAX, 17)],
1663 ),
1664 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1665 );
1666 assert_eq!(
1667 validate_raw_diagnostic_fact_schema(
1668 ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
1669 &[(DiagnosticFactTag::DecodeReason.raw(), u64::MAX)],
1670 ),
1671 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1672 );
1673 }
1674
1675 #[test]
1676 fn constraint_schema_enforces_authority_operation_and_bounded_path_suffix() {
1677 let mut targeted = vec![
1678 (DiagnosticFactTag::AcceptedSchemaFingerprintMethod, 1),
1679 (DiagnosticFactTag::AcceptedSchemaFingerprintHigh, 2),
1680 (DiagnosticFactTag::AcceptedSchemaFingerprintLow, 3),
1681 (DiagnosticFactTag::EntityTag, 17),
1682 (DiagnosticFactTag::ConstraintId, 4),
1683 (
1684 DiagnosticFactTag::ConstraintKind,
1685 DiagnosticConstraintKind::TargetedRule.raw(),
1686 ),
1687 (
1688 DiagnosticFactTag::ConstraintContext,
1689 DiagnosticConstraintContext::WriteAdmission.raw(),
1690 ),
1691 (
1692 DiagnosticFactTag::MutationOperation,
1693 DiagnosticMutationOperation::Insert.raw(),
1694 ),
1695 (DiagnosticFactTag::BatchPosition, 0),
1696 ];
1697 targeted.extend((0..64).map(|index| (DiagnosticFactTag::ListElement, index)));
1698 assert_eq!(targeted.len(), 73);
1699 assert_eq!(
1700 validate_known_diagnostic_fact_schema(
1701 ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1702 targeted.as_slice(),
1703 ),
1704 Ok(())
1705 );
1706
1707 let mut overlong = targeted.clone();
1708 overlong.push((DiagnosticFactTag::ListElement, 64));
1709 assert_eq!(
1710 validate_known_diagnostic_fact_schema(
1711 ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1712 overlong.as_slice(),
1713 ),
1714 Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded)
1715 );
1716
1717 let mut non_targeted_path = targeted;
1718 non_targeted_path[5].1 = DiagnosticConstraintKind::Unique.raw();
1719 assert_eq!(
1720 validate_known_diagnostic_fact_schema(
1721 ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1722 non_targeted_path.as_slice(),
1723 ),
1724 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1725 );
1726 }
1727
1728 #[test]
1729 fn schema_enforces_global_ceiling_before_per_code_ceiling() {
1730 let facts = vec![(DiagnosticFactTag::ActualCount.raw(), 0); 81];
1731 assert_eq!(
1732 validate_raw_diagnostic_fact_schema(ErrorCode::QUERY_PLAN, facts.as_slice()),
1733 Err(DiagnosticFactSchemaMismatch::GlobalMaximumExceeded)
1734 );
1735 }
1736}