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