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 CursorOddLength,
160 CursorInvalidHex,
161 CursorGroupedDirectionMismatch,
162 CursorTokenEncode,
163 CursorTokenDecode,
164 RecoveryMarkerMagic,
165 RecoveryMarkerChecksum,
166 RecoveryMarkerState,
167}
168
169#[derive(Clone, Copy, Eq, Hash, PartialEq)]
171pub enum DiagnosticMutationOperation {
172 Insert,
173 Replace,
174 Update,
175 Delete,
176}
177
178macro_rules! define_numeric_fact_value_registry {
179 (
180 $(#[$enum_meta:meta])*
181 pub enum $name:ident {
182 $($variant:ident = $raw:literal;)+
183 }
184 ) => {
185 $(#[$enum_meta])*
186 #[derive(Clone, Copy, Eq, Hash, PartialEq)]
187 pub enum $name {
188 $(
189 #[doc = concat!("Compact diagnostic value ", stringify!($raw), ".")]
190 $variant,
191 )+
192 }
193
194 impl $name {
195 #[must_use]
197 pub const fn raw(self) -> u64 {
198 match self {
199 $(Self::$variant => $raw,)+
200 }
201 }
202
203 #[must_use]
205 pub const fn known(raw: u64) -> Option<Self> {
206 match raw {
207 $($raw => Some(Self::$variant),)+
208 _ => None,
209 }
210 }
211 }
212
213 impl fmt::Debug for $name {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 write!(f, "{}", self.raw())
216 }
217 }
218 };
219}
220
221define_numeric_fact_value_registry! {
222 pub enum DiagnosticBacklogResource {
224 Batches = 1;
225 Records = 2;
226 EncodedBytes = 3;
227 }
228}
229
230define_numeric_fact_value_registry! {
231 pub enum DiagnosticExecutionBudgetResource {
233 QueryExecutions = 1;
234 PlanningSteps = 2;
235 PlanCompilations = 3;
236 KeyIndexEntriesVisited = 4;
237 RowsVisited = 5;
238 StoredBytesRead = 6;
239 PredicateExpressionSteps = 7;
240 NestedValueSteps = 8;
241 DecodedBytes = 9;
242 MaterializedBytes = 10;
243 SortEntries = 11;
244 SortComparisons = 12;
245 SortTemporaryBytes = 13;
246 GroupDistinctEntries = 14;
247 GroupDistinctStateBytes = 15;
248 CursorSteps = 16;
249 TemporaryBytes = 17;
250 DiagnosticSteps = 18;
251 ResultRows = 19;
252 ResultBytes = 20;
253 InstructionUnits = 21;
254 }
255}
256
257impl DiagnosticExecutionBudgetResource {
258 pub const ALL: [Self; 21] = [
260 Self::QueryExecutions,
261 Self::PlanningSteps,
262 Self::PlanCompilations,
263 Self::KeyIndexEntriesVisited,
264 Self::RowsVisited,
265 Self::StoredBytesRead,
266 Self::PredicateExpressionSteps,
267 Self::NestedValueSteps,
268 Self::DecodedBytes,
269 Self::MaterializedBytes,
270 Self::SortEntries,
271 Self::SortComparisons,
272 Self::SortTemporaryBytes,
273 Self::GroupDistinctEntries,
274 Self::GroupDistinctStateBytes,
275 Self::CursorSteps,
276 Self::TemporaryBytes,
277 Self::DiagnosticSteps,
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::CursorOddLength => 3,
439 Self::CursorInvalidHex => 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 }
447 }
448
449 #[must_use]
451 pub const fn known(raw: u64) -> Option<Self> {
452 match raw {
453 1 => Some(Self::CursorEmpty),
454 2 => Some(Self::CursorTooLong),
455 3 => Some(Self::CursorOddLength),
456 4 => Some(Self::CursorInvalidHex),
457 5 => Some(Self::CursorGroupedDirectionMismatch),
458 6 => Some(Self::CursorTokenEncode),
459 7 => Some(Self::CursorTokenDecode),
460 8 => Some(Self::RecoveryMarkerMagic),
461 9 => Some(Self::RecoveryMarkerChecksum),
462 10 => Some(Self::RecoveryMarkerState),
463 _ => None,
464 }
465 }
466}
467
468impl DiagnosticMutationOperation {
469 #[must_use]
471 pub const fn raw(self) -> u64 {
472 match self {
473 Self::Insert => 1,
474 Self::Replace => 2,
475 Self::Update => 3,
476 Self::Delete => 4,
477 }
478 }
479
480 #[must_use]
482 pub const fn known(raw: u64) -> Option<Self> {
483 match raw {
484 1 => Some(Self::Insert),
485 2 => Some(Self::Replace),
486 3 => Some(Self::Update),
487 4 => Some(Self::Delete),
488 _ => None,
489 }
490 }
491}
492
493impl fmt::Debug for DiagnosticFactTag {
494 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
495 write!(f, "{}", self.raw())
496 }
497}
498
499impl fmt::Debug for DiagnosticDecodeReason {
500 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
501 write!(f, "{}", self.raw())
502 }
503}
504
505impl fmt::Debug for DiagnosticMutationOperation {
506 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
507 write!(f, "{}", self.raw())
508 }
509}
510
511#[must_use]
513pub const fn pack_u32_pair(high: u32, low: u32) -> u64 {
514 (high as u64) << 32 | low as u64
515}
516
517#[must_use]
519pub const fn unpack_u32_pair(value: u64) -> (u32, u32) {
520 let bytes = value.to_be_bytes();
521 (
522 u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
523 u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
524 )
525}
526
527#[derive(Clone, Copy, Debug, Eq, PartialEq)]
531pub enum DiagnosticFactSchemaMismatch {
532 GlobalMaximumExceeded,
534 CodeMaximumExceeded,
536 InvalidSequence,
538 InvalidValue,
540}
541
542pub fn validate_known_diagnostic_fact_schema(
547 code: ErrorCode,
548 facts: &[(DiagnosticFactTag, u64)],
549) -> Result<(), DiagnosticFactSchemaMismatch> {
550 validate_diagnostic_fact_schema(code, facts.len(), |index| {
551 let (tag, value) = facts[index];
552 (tag.raw(), value)
553 })
554}
555
556pub fn validate_raw_diagnostic_fact_schema(
561 code: ErrorCode,
562 facts: &[(u8, u64)],
563) -> Result<(), DiagnosticFactSchemaMismatch> {
564 validate_diagnostic_fact_schema(code, facts.len(), |index| facts[index])
565}
566
567#[expect(
568 clippy::too_many_lines,
569 reason = "the frozen E-code schema registry keeps every numeric owner visible in one exhaustive dispatch"
570)]
571fn validate_diagnostic_fact_schema(
572 code: ErrorCode,
573 fact_count: usize,
574 fact_at: impl Fn(usize) -> (u8, u64),
575) -> Result<(), DiagnosticFactSchemaMismatch> {
576 if fact_count > MAX_PUBLIC_DIAGNOSTIC_FACTS {
577 return Err(DiagnosticFactSchemaMismatch::GlobalMaximumExceeded);
578 }
579
580 let maximum = diagnostic_fact_maximum(code);
581 if fact_count > maximum {
582 return Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded);
583 }
584
585 let valid_sequence = match code.raw() {
586 3 => query_plan_schema(fact_count, &fact_at),
587 6 => cursor_schema(fact_count, &fact_at),
588 16 => store_corruption_schema(fact_count, &fact_at),
589 18 => runtime_corruption_schema(fact_count, &fact_at),
590 19 => incompatible_format_schema(fact_count, &fact_at),
591 20 => runtime_invariant_schema(fact_count, &fact_at),
592 21 => runtime_conflict_schema(fact_count, &fact_at),
593 23 => runtime_unsupported_schema(fact_count, &fact_at),
594 24 => runtime_internal_schema(fact_count, &fact_at),
595 138 => tags_match(
596 fact_count,
597 &fact_at,
598 &[
599 DiagnosticFactTag::ExpectedArity,
600 DiagnosticFactTag::ActualArity,
601 ],
602 ),
603 141 | 142 | 169 | 170 => {
604 tags_match(fact_count, &fact_at, &[DiagnosticFactTag::ProjectionIndex])
605 }
606 175 => tags_match(fact_count, &fact_at, &[DiagnosticFactTag::ParameterIndex]),
607 177 | 237 => {
608 tags_match(fact_count, &fact_at, &[DiagnosticFactTag::Limit])
609 || tags_match(
610 fact_count,
611 &fact_at,
612 &[DiagnosticFactTag::ActualLength, DiagnosticFactTag::Limit],
613 )
614 }
615 178 | 180 | 202 | 203 | 205 | 236 | 269 => tags_match(
616 fact_count,
617 &fact_at,
618 &[DiagnosticFactTag::ActualCount, DiagnosticFactTag::Limit],
619 ),
620 283 => {
621 tags_match(fact_count, &fact_at, &[DiagnosticFactTag::Limit])
622 || tags_match(
623 fact_count,
624 &fact_at,
625 &[DiagnosticFactTag::ActualCount, DiagnosticFactTag::Limit],
626 )
627 }
628 284 => tags_match(
629 fact_count,
630 &fact_at,
631 &[
632 DiagnosticFactTag::BacklogResource,
633 DiagnosticFactTag::CurrentCount,
634 DiagnosticFactTag::ProposedCount,
635 DiagnosticFactTag::Limit,
636 ],
637 ),
638 196 | 234 => tags_match(
639 fact_count,
640 &fact_at,
641 &[
642 DiagnosticFactTag::EntityTag,
643 DiagnosticFactTag::FieldId,
644 DiagnosticFactTag::MutationOperation,
645 DiagnosticFactTag::BatchPosition,
646 ],
647 ),
648 197 => tags_match(
649 fact_count,
650 &fact_at,
651 &[
652 DiagnosticFactTag::RowLayout,
653 DiagnosticFactTag::HistoryFloor,
654 DiagnosticFactTag::CurrentLayout,
655 ],
656 ),
657 198 => tags_match(
658 fact_count,
659 &fact_at,
660 &[
661 DiagnosticFactTag::RowLayout,
662 DiagnosticFactTag::ExpectedSlotCount,
663 DiagnosticFactTag::ActualSlotCount,
664 ],
665 ),
666 201 => tags_match(
667 fact_count,
668 &fact_at,
669 &[DiagnosticFactTag::ActualCount, DiagnosticFactTag::Minimum],
670 ),
671 223 => constraint_schema(fact_count, &fact_at, true),
672 225 => constraint_schema(fact_count, &fact_at, false),
673 233 => tags_match(
674 fact_count,
675 &fact_at,
676 &[
677 DiagnosticFactTag::EntityTag,
678 DiagnosticFactTag::MutationOperation,
679 DiagnosticFactTag::BatchPosition,
680 ],
681 ),
682 235 => {
683 tags_match(fact_count, &fact_at, &[DiagnosticFactTag::ActualCount]) && fact_at(0).1 == 0
684 }
685 238 | 270 | 271 | 272 => tags_match(
686 fact_count,
687 &fact_at,
688 &[DiagnosticFactTag::ActualLength, DiagnosticFactTag::Limit],
689 ),
690 273 => tags_match(
691 fact_count,
692 &fact_at,
693 &[
694 DiagnosticFactTag::BudgetResource,
695 DiagnosticFactTag::Limit,
696 DiagnosticFactTag::Actual,
697 DiagnosticFactTag::ExecutionBudgetScope,
698 DiagnosticFactTag::ExecutionLane,
699 DiagnosticFactTag::QueryShapeFingerprintPrefix,
700 ],
701 ),
702 274 => tags_match(
703 fact_count,
704 &fact_at,
705 &[
706 DiagnosticFactTag::BudgetResource,
707 DiagnosticFactTag::Limit,
708 DiagnosticFactTag::Actual,
709 ],
710 ),
711 239 => tags_match(
712 fact_count,
713 &fact_at,
714 &[
715 DiagnosticFactTag::BatchPosition,
716 DiagnosticFactTag::ExpectedEntityTag,
717 DiagnosticFactTag::ActualEntityTag,
718 ],
719 ),
720 240 => tags_match(
721 fact_count,
722 &fact_at,
723 &[
724 DiagnosticFactTag::EntityTag,
725 DiagnosticFactTag::FirstBatchPosition,
726 DiagnosticFactTag::DuplicateBatchPosition,
727 ],
728 ),
729 _ => fact_count == 0,
730 };
731 if !valid_sequence {
732 return Err(DiagnosticFactSchemaMismatch::InvalidSequence);
733 }
734
735 for index in 0..fact_count {
736 let (raw_tag, value) = fact_at(index);
737 let Some(tag) = DiagnosticFactTag::known(raw_tag) else {
738 return Err(DiagnosticFactSchemaMismatch::InvalidSequence);
739 };
740 if !diagnostic_fact_value_is_valid(tag, value) {
741 return Err(DiagnosticFactSchemaMismatch::InvalidValue);
742 }
743 }
744 Ok(())
745}
746
747const fn diagnostic_fact_maximum(code: ErrorCode) -> usize {
748 match code.raw() {
749 6 | 16 | 18 | 24 | 197 | 198 | 233 | 239 | 240 | 274 => 3,
750 141 | 142 | 169 | 170 | 175 | 235 => 1,
751 19 | 21 | 138 | 177 | 178 | 180 | 201 | 202 | 203 | 205 | 236 | 237 | 238 | 269 | 270
752 | 271 | 272 | 283 => 2,
753 3 | 20 => 5,
754 23 | 273 => 6,
755 196 | 234 | 284 => 4,
756 223 => 73,
757 225 => 9,
758 _ => 0,
759 }
760}
761
762#[expect(
763 clippy::too_many_lines,
764 reason = "the query-plan E-code deliberately owns several exact finite fact sequences"
765)]
766fn query_plan_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
767 fact_count == 0
768 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::TermIndex])
769 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ComponentIndex])
770 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::GroupIndex])
771 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ClauseIndex])
772 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::AggregateIndex])
773 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::AggregateKind])
774 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ProjectionIndex])
775 || tags_match(
776 fact_count,
777 fact_at,
778 &[
779 DiagnosticFactTag::FirstTermIndex,
780 DiagnosticFactTag::DuplicateTermIndex,
781 ],
782 )
783 || tags_match(
784 fact_count,
785 fact_at,
786 &[
787 DiagnosticFactTag::ClauseIndex,
788 DiagnosticFactTag::OperatorKind,
789 ],
790 )
791 || tags_match(
792 fact_count,
793 fact_at,
794 &[
795 DiagnosticFactTag::AggregateIndex,
796 DiagnosticFactTag::AggregateKind,
797 ],
798 )
799 || tags_match(
800 fact_count,
801 fact_at,
802 &[
803 DiagnosticFactTag::AggregateKind,
804 DiagnosticFactTag::TypeFamily,
805 ],
806 )
807 || tags_match(
808 fact_count,
809 fact_at,
810 &[
811 DiagnosticFactTag::OperatorKind,
812 DiagnosticFactTag::TypeFamily,
813 ],
814 )
815 || tags_match(
816 fact_count,
817 fact_at,
818 &[
819 DiagnosticFactTag::BranchIndex,
820 DiagnosticFactTag::TypeFamily,
821 ],
822 )
823 || tags_match(
824 fact_count,
825 fact_at,
826 &[DiagnosticFactTag::TypeFamily, DiagnosticFactTag::TypeFamily],
827 )
828 || tags_match(
829 fact_count,
830 fact_at,
831 &[
832 DiagnosticFactTag::ClauseIndex,
833 DiagnosticFactTag::AggregateIndex,
834 DiagnosticFactTag::ActualCount,
835 ],
836 )
837 || tags_match(
838 fact_count,
839 fact_at,
840 &[
841 DiagnosticFactTag::FunctionKind,
842 DiagnosticFactTag::ExpectedArity,
843 DiagnosticFactTag::ActualArity,
844 ],
845 )
846 || tags_match(
847 fact_count,
848 fact_at,
849 &[
850 DiagnosticFactTag::FunctionKind,
851 DiagnosticFactTag::ArgumentIndex,
852 DiagnosticFactTag::TypeFamily,
853 ],
854 )
855 || tags_match(
856 fact_count,
857 fact_at,
858 &[
859 DiagnosticFactTag::OperatorKind,
860 DiagnosticFactTag::TypeFamily,
861 DiagnosticFactTag::TypeFamily,
862 ],
863 )
864 || tags_match(
865 fact_count,
866 fact_at,
867 &[
868 DiagnosticFactTag::BranchIndex,
869 DiagnosticFactTag::TypeFamily,
870 DiagnosticFactTag::TypeFamily,
871 ],
872 )
873 || tags_match(
874 fact_count,
875 fact_at,
876 &[
877 DiagnosticFactTag::TypeFamily,
878 DiagnosticFactTag::BranchIndex,
879 DiagnosticFactTag::TypeFamily,
880 ],
881 )
882 || tags_match(
883 fact_count,
884 fact_at,
885 &[
886 DiagnosticFactTag::BranchIndex,
887 DiagnosticFactTag::TypeFamily,
888 DiagnosticFactTag::BranchIndex,
889 DiagnosticFactTag::TypeFamily,
890 ],
891 )
892 || tags_match(
893 fact_count,
894 fact_at,
895 &[
896 DiagnosticFactTag::FunctionKind,
897 DiagnosticFactTag::ArgumentIndex,
898 DiagnosticFactTag::TypeFamily,
899 DiagnosticFactTag::ArgumentIndex,
900 DiagnosticFactTag::TypeFamily,
901 ],
902 )
903}
904
905fn cursor_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
906 if fact_count == 0 {
907 return true;
908 }
909 if tags_match(fact_count, fact_at, &[DiagnosticFactTag::DecodeReason]) {
910 return matches!(fact_at(0).1, 1 | 3 | 5 | 6 | 7);
911 }
912 if tags_match(
913 fact_count,
914 fact_at,
915 &[
916 DiagnosticFactTag::ActualLength,
917 DiagnosticFactTag::Maximum,
918 DiagnosticFactTag::DecodeReason,
919 ],
920 ) {
921 return fact_at(2).1 == DiagnosticDecodeReason::CursorTooLong.raw();
922 }
923 if tags_match(
924 fact_count,
925 fact_at,
926 &[
927 DiagnosticFactTag::ComponentIndex,
928 DiagnosticFactTag::DecodeReason,
929 ],
930 ) {
931 return matches!(fact_at(1).1, 4 | 6 | 7);
932 }
933 tags_match(
934 fact_count,
935 fact_at,
936 &[
937 DiagnosticFactTag::ExpectedSignaturePrefix,
938 DiagnosticFactTag::ActualSignaturePrefix,
939 ],
940 ) || tags_match(
941 fact_count,
942 fact_at,
943 &[
944 DiagnosticFactTag::ExpectedOffset,
945 DiagnosticFactTag::ActualOffset,
946 ],
947 )
948}
949
950fn store_corruption_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
951 fact_count == 0
952 || (tags_match(
953 fact_count,
954 fact_at,
955 &[
956 DiagnosticFactTag::ComponentKind,
957 DiagnosticFactTag::ActualLength,
958 DiagnosticFactTag::Limit,
959 ],
960 ) && fact_at(0).1 == DiagnosticComponentKind::CommitDataKey.raw())
961 || tags_match(
962 fact_count,
963 fact_at,
964 &[
965 DiagnosticFactTag::ExpectedEntityTag,
966 DiagnosticFactTag::ActualEntityTag,
967 ],
968 )
969}
970
971fn runtime_corruption_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
972 store_corruption_schema(fact_count, fact_at)
973 || (tags_match(fact_count, fact_at, &[DiagnosticFactTag::DecodeReason])
974 && matches!(fact_at(0).1, 8..=10))
975}
976
977fn incompatible_format_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
978 fact_count == 0
979 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ExpectedVersion])
980 || tags_match(
981 fact_count,
982 fact_at,
983 &[
984 DiagnosticFactTag::ExpectedVersion,
985 DiagnosticFactTag::ActualVersion,
986 ],
987 )
988}
989
990fn runtime_invariant_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
991 fact_count == 0
992 || (tags_match(
993 fact_count,
994 fact_at,
995 &[
996 DiagnosticFactTag::EntityTag,
997 DiagnosticFactTag::PhysicalGeneration,
998 DiagnosticFactTag::ComponentKind,
999 DiagnosticFactTag::ActualArity,
1000 DiagnosticFactTag::Maximum,
1001 ],
1002 ) && fact_at(2).1 == DiagnosticComponentKind::IndexKey.raw())
1003}
1004
1005fn runtime_conflict_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1006 fact_count == 0
1007 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ExpectedRevision])
1008 || tags_match(
1009 fact_count,
1010 fact_at,
1011 &[
1012 DiagnosticFactTag::ExpectedRevision,
1013 DiagnosticFactTag::CurrentRevision,
1014 ],
1015 )
1016}
1017
1018fn runtime_unsupported_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1019 fact_count == 0
1020 || (tags_match(
1021 fact_count,
1022 fact_at,
1023 &[
1024 DiagnosticFactTag::EntityTag,
1025 DiagnosticFactTag::PhysicalGeneration,
1026 DiagnosticFactTag::ComponentIndex,
1027 DiagnosticFactTag::ComponentKind,
1028 DiagnosticFactTag::ActualLength,
1029 DiagnosticFactTag::Limit,
1030 ],
1031 ) && fact_at(3).1 == DiagnosticComponentKind::IndexKeyComponent.raw())
1032}
1033
1034fn runtime_internal_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1035 fact_count == 0
1036 || tags_match(
1037 fact_count,
1038 fact_at,
1039 &[
1040 DiagnosticFactTag::ExpectedMemoryId,
1041 DiagnosticFactTag::ActualMemoryId,
1042 ],
1043 )
1044 || (tags_match(
1045 fact_count,
1046 fact_at,
1047 &[
1048 DiagnosticFactTag::ComponentKind,
1049 DiagnosticFactTag::ExpectedArity,
1050 DiagnosticFactTag::ActualArity,
1051 ],
1052 ) && fact_at(0).1 == DiagnosticComponentKind::RelationTargetPrimaryKey.raw())
1053}
1054
1055fn constraint_schema(
1056 fact_count: usize,
1057 fact_at: &impl Fn(usize) -> (u8, u64),
1058 allow_targeted_path: bool,
1059) -> bool {
1060 const COMMON: &[DiagnosticFactTag] = &[
1061 DiagnosticFactTag::AcceptedSchemaFingerprintMethod,
1062 DiagnosticFactTag::AcceptedSchemaFingerprintHigh,
1063 DiagnosticFactTag::AcceptedSchemaFingerprintLow,
1064 DiagnosticFactTag::EntityTag,
1065 DiagnosticFactTag::ConstraintId,
1066 DiagnosticFactTag::ConstraintKind,
1067 DiagnosticFactTag::ConstraintContext,
1068 ];
1069 if fact_count < COMMON.len()
1070 || !tags_prefix_matches(fact_count, fact_at, COMMON)
1071 || fact_at(6).1 != DiagnosticConstraintContext::WriteAdmission.raw()
1072 {
1073 return false;
1074 }
1075
1076 let constraint_kind = fact_at(5).1;
1077 if DiagnosticConstraintKind::known(constraint_kind).is_none() {
1078 return false;
1079 }
1080 let mut index = COMMON.len();
1081 if index < fact_count && fact_at(index).0 == DiagnosticFactTag::MutationOperation.raw() {
1082 index += 1;
1083 if index < fact_count && fact_at(index).0 == DiagnosticFactTag::BatchPosition.raw() {
1084 index += 1;
1085 }
1086 }
1087
1088 let path_len = fact_count - index;
1089 if path_len == 0 {
1090 return !allow_targeted_path
1091 || constraint_kind != DiagnosticConstraintKind::TargetedRule.raw();
1092 }
1093 allow_targeted_path
1094 && constraint_kind == DiagnosticConstraintKind::TargetedRule.raw()
1095 && path_len <= 64
1096 && (index..fact_count).all(|position| {
1097 DiagnosticFactTag::known(fact_at(position).0).is_some_and(is_value_path_tag)
1098 })
1099}
1100
1101fn tags_match(
1102 fact_count: usize,
1103 fact_at: &impl Fn(usize) -> (u8, u64),
1104 expected: &[DiagnosticFactTag],
1105) -> bool {
1106 fact_count == expected.len() && tags_prefix_matches(fact_count, fact_at, expected)
1107}
1108
1109fn tags_prefix_matches(
1110 fact_count: usize,
1111 fact_at: &impl Fn(usize) -> (u8, u64),
1112 expected: &[DiagnosticFactTag],
1113) -> bool {
1114 fact_count >= expected.len()
1115 && expected
1116 .iter()
1117 .enumerate()
1118 .all(|(index, tag)| fact_at(index).0 == tag.raw())
1119}
1120
1121const fn is_value_path_tag(tag: DiagnosticFactTag) -> bool {
1122 matches!(
1123 tag,
1124 DiagnosticFactTag::RootField
1125 | DiagnosticFactTag::RecordMember
1126 | DiagnosticFactTag::TupleElement
1127 | DiagnosticFactTag::Newtype
1128 | DiagnosticFactTag::EnumVariant
1129 | DiagnosticFactTag::ListElement
1130 | DiagnosticFactTag::SetElement
1131 | DiagnosticFactTag::MapEntryKey
1132 | DiagnosticFactTag::MapEntryValue
1133 )
1134}
1135
1136const fn diagnostic_fact_value_is_valid(tag: DiagnosticFactTag, value: u64) -> bool {
1137 match tag {
1138 DiagnosticFactTag::AcceptedSchemaFingerprintMethod
1139 | DiagnosticFactTag::ExpectedMemoryId
1140 | DiagnosticFactTag::ActualMemoryId => value <= u8::MAX as u64,
1141 DiagnosticFactTag::ConstraintId
1142 | DiagnosticFactTag::FieldId
1143 | DiagnosticFactTag::IndexId
1144 | DiagnosticFactTag::RelationId
1145 | DiagnosticFactTag::BatchPosition
1146 | DiagnosticFactTag::FirstBatchPosition
1147 | DiagnosticFactTag::DuplicateBatchPosition
1148 | DiagnosticFactTag::RowLayout
1149 | DiagnosticFactTag::HistoryFloor
1150 | DiagnosticFactTag::CurrentLayout
1151 | DiagnosticFactTag::RootField
1152 | DiagnosticFactTag::Newtype
1153 | DiagnosticFactTag::ListElement
1154 | DiagnosticFactTag::SetElement
1155 | DiagnosticFactTag::MapEntryKey
1156 | DiagnosticFactTag::MapEntryValue => value <= u32::MAX as u64,
1157 DiagnosticFactTag::ConstraintKind => DiagnosticConstraintKind::known(value).is_some(),
1158 DiagnosticFactTag::BacklogResource => DiagnosticBacklogResource::known(value).is_some(),
1159 DiagnosticFactTag::ConstraintContext => DiagnosticConstraintContext::known(value).is_some(),
1160 DiagnosticFactTag::TypeFamily => DiagnosticTypeFamily::known(value).is_some(),
1161 DiagnosticFactTag::FunctionKind => DiagnosticFunctionKind::known(value).is_some(),
1162 DiagnosticFactTag::OperatorKind => DiagnosticOperatorKind::known(value).is_some(),
1163 DiagnosticFactTag::AggregateKind => DiagnosticAggregateKind::known(value).is_some(),
1164 DiagnosticFactTag::ComponentKind => DiagnosticComponentKind::known(value).is_some(),
1165 DiagnosticFactTag::DecodeReason => DiagnosticDecodeReason::known(value).is_some(),
1166 DiagnosticFactTag::BudgetResource => {
1167 DiagnosticExecutionBudgetResource::known(value).is_some()
1168 }
1169 DiagnosticFactTag::ExecutionBudgetScope => {
1170 DiagnosticExecutionBudgetScope::known(value).is_some()
1171 }
1172 DiagnosticFactTag::ExecutionLane => DiagnosticExecutionLane::known(value).is_some(),
1173 DiagnosticFactTag::MutationOperation => DiagnosticMutationOperation::known(value).is_some(),
1174 _ => true,
1175 }
1176}
1177
1178#[cfg(test)]
1179mod tests {
1180 use super::{
1181 DiagnosticAggregateKind, DiagnosticBacklogResource, DiagnosticComponentKind,
1182 DiagnosticConstraintContext, DiagnosticConstraintKind, DiagnosticDecodeReason,
1183 DiagnosticExecutionBudgetResource, DiagnosticExecutionBudgetScope, DiagnosticExecutionLane,
1184 DiagnosticFactSchemaMismatch, DiagnosticFactTag, DiagnosticFunctionKind,
1185 DiagnosticMutationOperation, DiagnosticOperatorKind, DiagnosticTypeFamily,
1186 ORDERED_FACT_TAGS, pack_u32_pair, unpack_u32_pair, validate_known_diagnostic_fact_schema,
1187 validate_raw_diagnostic_fact_schema,
1188 };
1189 use crate::ErrorCode;
1190
1191 #[test]
1192 fn fact_tag_registry_is_fixed_unique_and_contiguous() {
1193 for (index, tag) in ORDERED_FACT_TAGS.iter().copied().enumerate() {
1194 let expected = u8::try_from(index + 1).expect("fact-tag index fits u8");
1195 assert_eq!(tag.raw(), expected);
1196 assert_eq!(DiagnosticFactTag::known(expected), Some(tag));
1197 }
1198
1199 assert_eq!(DiagnosticFactTag::known(0), None);
1200 assert_eq!(DiagnosticFactTag::known(96), None);
1201 assert_eq!(DiagnosticFactTag::known(u8::MAX), None);
1202 }
1203
1204 #[test]
1205 fn execution_budget_fact_value_registries_are_fixed() {
1206 assert_eq!(DiagnosticBacklogResource::Batches.raw(), 1);
1207 assert_eq!(DiagnosticBacklogResource::Records.raw(), 2);
1208 assert_eq!(DiagnosticBacklogResource::EncodedBytes.raw(), 3);
1209 assert_eq!(DiagnosticBacklogResource::known(4), None);
1210
1211 for (index, resource) in DiagnosticExecutionBudgetResource::ALL
1212 .iter()
1213 .copied()
1214 .enumerate()
1215 {
1216 let expected = u64::try_from(index + 1).expect("resource index fits u64");
1217 assert_eq!(resource.raw(), expected);
1218 assert_eq!(
1219 DiagnosticExecutionBudgetResource::known(expected),
1220 Some(resource)
1221 );
1222 }
1223 assert_eq!(DiagnosticExecutionBudgetResource::known(0), None);
1224 assert_eq!(DiagnosticExecutionBudgetResource::known(22), None);
1225
1226 assert_eq!(DiagnosticExecutionBudgetScope::Execution.raw(), 1);
1227 assert_eq!(DiagnosticExecutionBudgetScope::Request.raw(), 2);
1228 assert_eq!(DiagnosticExecutionBudgetScope::known(3), None);
1229
1230 assert_eq!(DiagnosticExecutionLane::PublicRead.raw(), 1);
1231 assert_eq!(DiagnosticExecutionLane::TrustedRead.raw(), 2);
1232 assert_eq!(DiagnosticExecutionLane::Diagnostic.raw(), 3);
1233 assert_eq!(DiagnosticExecutionLane::Mutation.raw(), 4);
1234 assert_eq!(DiagnosticExecutionLane::Recovery.raw(), 5);
1235 assert_eq!(DiagnosticExecutionLane::known(6), None);
1236 }
1237
1238 #[test]
1239 fn accepted_identity_pair_packing_is_exact() {
1240 for pair in [
1241 (0, 0),
1242 (1, 2),
1243 (u32::MAX, 0),
1244 (0, u32::MAX),
1245 (u32::MAX, u32::MAX),
1246 ] {
1247 assert_eq!(unpack_u32_pair(pack_u32_pair(pair.0, pair.1)), pair);
1248 }
1249 }
1250
1251 #[test]
1252 fn constraint_fact_value_registries_are_fixed() {
1253 assert_eq!(DiagnosticConstraintKind::Check.raw(), 1);
1254 assert_eq!(DiagnosticConstraintKind::NotNull.raw(), 2);
1255 assert_eq!(DiagnosticConstraintKind::Relation.raw(), 3);
1256 assert_eq!(DiagnosticConstraintKind::TargetedRule.raw(), 4);
1257 assert_eq!(DiagnosticConstraintKind::Unique.raw(), 5);
1258 assert_eq!(DiagnosticConstraintKind::known(0), None);
1259 assert_eq!(DiagnosticConstraintKind::known(6), None);
1260
1261 assert_eq!(DiagnosticConstraintContext::Integrity.raw(), 1);
1262 assert_eq!(DiagnosticConstraintContext::MigrationValidation.raw(), 2);
1263 assert_eq!(DiagnosticConstraintContext::WriteAdmission.raw(), 3);
1264 assert_eq!(DiagnosticConstraintContext::known(0), None);
1265 assert_eq!(DiagnosticConstraintContext::known(4), None);
1266 }
1267
1268 #[test]
1269 fn component_kind_registry_is_fixed_and_numeric() {
1270 let kinds = [
1271 DiagnosticComponentKind::CommitDataKey,
1272 DiagnosticComponentKind::IndexKey,
1273 DiagnosticComponentKind::IndexKeyComponent,
1274 DiagnosticComponentKind::RelationTargetPrimaryKey,
1275 ];
1276
1277 for (index, kind) in kinds.iter().copied().enumerate() {
1278 let expected = (index + 1) as u64;
1279 assert_eq!(kind.raw(), expected);
1280 assert_eq!(DiagnosticComponentKind::known(expected), Some(kind));
1281 assert_eq!(format!("{kind:?}"), expected.to_string());
1282 }
1283 assert_eq!(DiagnosticComponentKind::known(0), None);
1284 assert_eq!(DiagnosticComponentKind::known(5), None);
1285 }
1286
1287 #[test]
1288 fn decode_reason_registry_is_fixed_and_numeric() {
1289 let reasons = [
1290 DiagnosticDecodeReason::CursorEmpty,
1291 DiagnosticDecodeReason::CursorTooLong,
1292 DiagnosticDecodeReason::CursorOddLength,
1293 DiagnosticDecodeReason::CursorInvalidHex,
1294 DiagnosticDecodeReason::CursorGroupedDirectionMismatch,
1295 DiagnosticDecodeReason::CursorTokenEncode,
1296 DiagnosticDecodeReason::CursorTokenDecode,
1297 DiagnosticDecodeReason::RecoveryMarkerMagic,
1298 DiagnosticDecodeReason::RecoveryMarkerChecksum,
1299 DiagnosticDecodeReason::RecoveryMarkerState,
1300 ];
1301
1302 for (index, reason) in reasons.iter().copied().enumerate() {
1303 let expected = (index + 1) as u64;
1304 assert_eq!(reason.raw(), expected);
1305 assert_eq!(DiagnosticDecodeReason::known(expected), Some(reason));
1306 assert_eq!(format!("{reason:?}"), expected.to_string());
1307 }
1308
1309 assert_eq!(DiagnosticDecodeReason::known(0), None);
1310 assert_eq!(DiagnosticDecodeReason::known(11), None);
1311 }
1312
1313 #[test]
1314 fn mutation_operation_registry_is_fixed_and_numeric() {
1315 let operations = [
1316 DiagnosticMutationOperation::Insert,
1317 DiagnosticMutationOperation::Replace,
1318 DiagnosticMutationOperation::Update,
1319 DiagnosticMutationOperation::Delete,
1320 ];
1321
1322 for (index, operation) in operations.iter().copied().enumerate() {
1323 let expected = (index + 1) as u64;
1324 assert_eq!(operation.raw(), expected);
1325 assert_eq!(
1326 DiagnosticMutationOperation::known(expected),
1327 Some(operation)
1328 );
1329 assert_eq!(format!("{operation:?}"), expected.to_string());
1330 }
1331
1332 assert_eq!(DiagnosticMutationOperation::known(0), None);
1333 assert_eq!(DiagnosticMutationOperation::known(5), None);
1334 }
1335
1336 #[test]
1337 fn query_kind_registries_are_fixed_contiguous_and_numeric() {
1338 for raw in 1..=9 {
1339 let value = DiagnosticTypeFamily::known(raw).expect("type family should be known");
1340 assert_eq!(value.raw(), raw);
1341 assert_eq!(format!("{value:?}"), raw.to_string());
1342 }
1343 assert_eq!(DiagnosticTypeFamily::known(0), None);
1344 assert_eq!(DiagnosticTypeFamily::known(10), None);
1345
1346 for raw in 1..=39 {
1347 let value = DiagnosticFunctionKind::known(raw).expect("function kind should be known");
1348 assert_eq!(value.raw(), raw);
1349 assert_eq!(format!("{value:?}"), raw.to_string());
1350 }
1351 assert_eq!(DiagnosticFunctionKind::known(0), None);
1352 assert_eq!(DiagnosticFunctionKind::known(40), None);
1353
1354 for raw in 1..=18 {
1355 let value = DiagnosticOperatorKind::known(raw).expect("operator kind should be known");
1356 assert_eq!(value.raw(), raw);
1357 assert_eq!(format!("{value:?}"), raw.to_string());
1358 }
1359 assert_eq!(DiagnosticOperatorKind::known(0), None);
1360 assert_eq!(DiagnosticOperatorKind::known(19), None);
1361
1362 for raw in 1..=8 {
1363 let value =
1364 DiagnosticAggregateKind::known(raw).expect("aggregate kind should be known");
1365 assert_eq!(value.raw(), raw);
1366 assert_eq!(format!("{value:?}"), raw.to_string());
1367 }
1368 assert_eq!(DiagnosticAggregateKind::known(0), None);
1369 assert_eq!(DiagnosticAggregateKind::known(9), None);
1370 }
1371
1372 #[test]
1373 fn per_code_schema_rejects_missing_disallowed_and_noncanonical_tags() {
1374 let valid = [
1375 (DiagnosticFactTag::ActualCount, 5),
1376 (DiagnosticFactTag::Limit, 4),
1377 ];
1378 assert_eq!(
1379 validate_known_diagnostic_fact_schema(
1380 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1381 &valid,
1382 ),
1383 Ok(())
1384 );
1385 assert_eq!(
1386 validate_known_diagnostic_fact_schema(
1387 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1388 &valid[..1],
1389 ),
1390 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1391 );
1392 assert_eq!(
1393 validate_known_diagnostic_fact_schema(
1394 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1395 &[valid[1], valid[0]],
1396 ),
1397 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1398 );
1399 assert_eq!(
1400 validate_known_diagnostic_fact_schema(ErrorCode::QUERY_VALIDATE, &valid),
1401 Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded)
1402 );
1403
1404 assert_eq!(
1405 validate_known_diagnostic_fact_schema(
1406 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_COMMIT_WORK_EXCEEDED,
1407 &valid,
1408 ),
1409 Ok(())
1410 );
1411 assert_eq!(
1412 validate_known_diagnostic_fact_schema(
1413 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_COMMIT_WORK_EXCEEDED,
1414 &valid[1..],
1415 ),
1416 Ok(())
1417 );
1418 }
1419
1420 #[test]
1421 fn execution_budget_schema_requires_complete_typed_attribution() {
1422 let valid = [
1423 (
1424 DiagnosticFactTag::BudgetResource,
1425 DiagnosticExecutionBudgetResource::StoredBytesRead.raw(),
1426 ),
1427 (DiagnosticFactTag::Limit, 4_096),
1428 (DiagnosticFactTag::Actual, 4_097),
1429 (
1430 DiagnosticFactTag::ExecutionBudgetScope,
1431 DiagnosticExecutionBudgetScope::Request.raw(),
1432 ),
1433 (
1434 DiagnosticFactTag::ExecutionLane,
1435 DiagnosticExecutionLane::TrustedRead.raw(),
1436 ),
1437 (DiagnosticFactTag::QueryShapeFingerprintPrefix, 17),
1438 ];
1439 assert_eq!(
1440 validate_known_diagnostic_fact_schema(
1441 ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1442 &valid,
1443 ),
1444 Ok(())
1445 );
1446 assert_eq!(
1447 validate_known_diagnostic_fact_schema(
1448 ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1449 &valid[..5],
1450 ),
1451 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1452 );
1453
1454 let mut invalid_resource = valid;
1455 invalid_resource[0].1 = 0;
1456 assert_eq!(
1457 validate_known_diagnostic_fact_schema(
1458 ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1459 &invalid_resource,
1460 ),
1461 Err(DiagnosticFactSchemaMismatch::InvalidValue)
1462 );
1463 }
1464
1465 #[test]
1466 fn raw_schema_keeps_unknown_context_numeric_but_marks_it_invalid() {
1467 assert_eq!(
1468 validate_raw_diagnostic_fact_schema(
1469 ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
1470 &[(u8::MAX, 17)],
1471 ),
1472 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1473 );
1474 assert_eq!(
1475 validate_raw_diagnostic_fact_schema(
1476 ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
1477 &[(DiagnosticFactTag::DecodeReason.raw(), u64::MAX)],
1478 ),
1479 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1480 );
1481 }
1482
1483 #[test]
1484 fn constraint_schema_enforces_authority_operation_and_bounded_path_suffix() {
1485 let mut targeted = vec![
1486 (DiagnosticFactTag::AcceptedSchemaFingerprintMethod, 1),
1487 (DiagnosticFactTag::AcceptedSchemaFingerprintHigh, 2),
1488 (DiagnosticFactTag::AcceptedSchemaFingerprintLow, 3),
1489 (DiagnosticFactTag::EntityTag, 17),
1490 (DiagnosticFactTag::ConstraintId, 4),
1491 (
1492 DiagnosticFactTag::ConstraintKind,
1493 DiagnosticConstraintKind::TargetedRule.raw(),
1494 ),
1495 (
1496 DiagnosticFactTag::ConstraintContext,
1497 DiagnosticConstraintContext::WriteAdmission.raw(),
1498 ),
1499 (
1500 DiagnosticFactTag::MutationOperation,
1501 DiagnosticMutationOperation::Insert.raw(),
1502 ),
1503 (DiagnosticFactTag::BatchPosition, 0),
1504 ];
1505 targeted.extend((0..64).map(|index| (DiagnosticFactTag::ListElement, index)));
1506 assert_eq!(targeted.len(), 73);
1507 assert_eq!(
1508 validate_known_diagnostic_fact_schema(
1509 ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1510 targeted.as_slice(),
1511 ),
1512 Ok(())
1513 );
1514
1515 let mut overlong = targeted.clone();
1516 overlong.push((DiagnosticFactTag::ListElement, 64));
1517 assert_eq!(
1518 validate_known_diagnostic_fact_schema(
1519 ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1520 overlong.as_slice(),
1521 ),
1522 Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded)
1523 );
1524
1525 let mut non_targeted_path = targeted;
1526 non_targeted_path[5].1 = DiagnosticConstraintKind::Unique.raw();
1527 assert_eq!(
1528 validate_known_diagnostic_fact_schema(
1529 ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1530 non_targeted_path.as_slice(),
1531 ),
1532 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1533 );
1534 }
1535
1536 #[test]
1537 fn schema_enforces_global_ceiling_before_per_code_ceiling() {
1538 let facts = vec![(DiagnosticFactTag::ActualCount.raw(), 0); 81];
1539 assert_eq!(
1540 validate_raw_diagnostic_fact_schema(ErrorCode::QUERY_PLAN, facts.as_slice()),
1541 Err(DiagnosticFactSchemaMismatch::GlobalMaximumExceeded)
1542 );
1543 }
1544}