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