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 196 | 234 => tags_match(
609 fact_count,
610 &fact_at,
611 &[
612 DiagnosticFactTag::EntityTag,
613 DiagnosticFactTag::FieldId,
614 DiagnosticFactTag::MutationOperation,
615 DiagnosticFactTag::BatchPosition,
616 ],
617 ),
618 197 => tags_match(
619 fact_count,
620 &fact_at,
621 &[
622 DiagnosticFactTag::RowLayout,
623 DiagnosticFactTag::HistoryFloor,
624 DiagnosticFactTag::CurrentLayout,
625 ],
626 ),
627 198 => tags_match(
628 fact_count,
629 &fact_at,
630 &[
631 DiagnosticFactTag::RowLayout,
632 DiagnosticFactTag::ExpectedSlotCount,
633 DiagnosticFactTag::ActualSlotCount,
634 ],
635 ),
636 201 => tags_match(
637 fact_count,
638 &fact_at,
639 &[DiagnosticFactTag::ActualCount, DiagnosticFactTag::Minimum],
640 ),
641 223 => constraint_schema(fact_count, &fact_at, true),
642 225 => constraint_schema(fact_count, &fact_at, false),
643 233 => tags_match(
644 fact_count,
645 &fact_at,
646 &[
647 DiagnosticFactTag::EntityTag,
648 DiagnosticFactTag::MutationOperation,
649 DiagnosticFactTag::BatchPosition,
650 ],
651 ),
652 235 => {
653 tags_match(fact_count, &fact_at, &[DiagnosticFactTag::ActualCount]) && fact_at(0).1 == 0
654 }
655 238 | 270 | 271 | 272 => tags_match(
656 fact_count,
657 &fact_at,
658 &[DiagnosticFactTag::ActualLength, DiagnosticFactTag::Limit],
659 ),
660 273 => tags_match(
661 fact_count,
662 &fact_at,
663 &[
664 DiagnosticFactTag::BudgetResource,
665 DiagnosticFactTag::Limit,
666 DiagnosticFactTag::Actual,
667 DiagnosticFactTag::ExecutionBudgetScope,
668 DiagnosticFactTag::ExecutionLane,
669 DiagnosticFactTag::QueryShapeFingerprintPrefix,
670 ],
671 ),
672 274 => tags_match(
673 fact_count,
674 &fact_at,
675 &[
676 DiagnosticFactTag::BudgetResource,
677 DiagnosticFactTag::Limit,
678 DiagnosticFactTag::Actual,
679 ],
680 ),
681 239 => tags_match(
682 fact_count,
683 &fact_at,
684 &[
685 DiagnosticFactTag::BatchPosition,
686 DiagnosticFactTag::ExpectedEntityTag,
687 DiagnosticFactTag::ActualEntityTag,
688 ],
689 ),
690 240 => tags_match(
691 fact_count,
692 &fact_at,
693 &[
694 DiagnosticFactTag::EntityTag,
695 DiagnosticFactTag::FirstBatchPosition,
696 DiagnosticFactTag::DuplicateBatchPosition,
697 ],
698 ),
699 _ => fact_count == 0,
700 };
701 if !valid_sequence {
702 return Err(DiagnosticFactSchemaMismatch::InvalidSequence);
703 }
704
705 for index in 0..fact_count {
706 let (raw_tag, value) = fact_at(index);
707 let Some(tag) = DiagnosticFactTag::known(raw_tag) else {
708 return Err(DiagnosticFactSchemaMismatch::InvalidSequence);
709 };
710 if !diagnostic_fact_value_is_valid(tag, value) {
711 return Err(DiagnosticFactSchemaMismatch::InvalidValue);
712 }
713 }
714 Ok(())
715}
716
717const fn diagnostic_fact_maximum(code: ErrorCode) -> usize {
718 match code.raw() {
719 6 | 16 | 18 | 24 | 197 | 198 | 233 | 239 | 240 | 274 => 3,
720 141 | 142 | 169 | 170 | 175 | 235 => 1,
721 19 | 21 | 138 | 177 | 178 | 180 | 201 | 202 | 203 | 205 | 236 | 237 | 238 | 269 | 270
722 | 271 | 272 => 2,
723 3 | 20 => 5,
724 23 | 273 => 6,
725 196 | 234 => 4,
726 223 => 73,
727 225 => 9,
728 _ => 0,
729 }
730}
731
732#[expect(
733 clippy::too_many_lines,
734 reason = "the query-plan E-code deliberately owns several exact finite fact sequences"
735)]
736fn query_plan_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
737 fact_count == 0
738 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::TermIndex])
739 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ComponentIndex])
740 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::GroupIndex])
741 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ClauseIndex])
742 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::AggregateIndex])
743 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::AggregateKind])
744 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ProjectionIndex])
745 || tags_match(
746 fact_count,
747 fact_at,
748 &[
749 DiagnosticFactTag::FirstTermIndex,
750 DiagnosticFactTag::DuplicateTermIndex,
751 ],
752 )
753 || tags_match(
754 fact_count,
755 fact_at,
756 &[
757 DiagnosticFactTag::ClauseIndex,
758 DiagnosticFactTag::OperatorKind,
759 ],
760 )
761 || tags_match(
762 fact_count,
763 fact_at,
764 &[
765 DiagnosticFactTag::AggregateIndex,
766 DiagnosticFactTag::AggregateKind,
767 ],
768 )
769 || tags_match(
770 fact_count,
771 fact_at,
772 &[
773 DiagnosticFactTag::AggregateKind,
774 DiagnosticFactTag::TypeFamily,
775 ],
776 )
777 || tags_match(
778 fact_count,
779 fact_at,
780 &[
781 DiagnosticFactTag::OperatorKind,
782 DiagnosticFactTag::TypeFamily,
783 ],
784 )
785 || tags_match(
786 fact_count,
787 fact_at,
788 &[
789 DiagnosticFactTag::BranchIndex,
790 DiagnosticFactTag::TypeFamily,
791 ],
792 )
793 || tags_match(
794 fact_count,
795 fact_at,
796 &[DiagnosticFactTag::TypeFamily, DiagnosticFactTag::TypeFamily],
797 )
798 || tags_match(
799 fact_count,
800 fact_at,
801 &[
802 DiagnosticFactTag::ClauseIndex,
803 DiagnosticFactTag::AggregateIndex,
804 DiagnosticFactTag::ActualCount,
805 ],
806 )
807 || tags_match(
808 fact_count,
809 fact_at,
810 &[
811 DiagnosticFactTag::FunctionKind,
812 DiagnosticFactTag::ExpectedArity,
813 DiagnosticFactTag::ActualArity,
814 ],
815 )
816 || tags_match(
817 fact_count,
818 fact_at,
819 &[
820 DiagnosticFactTag::FunctionKind,
821 DiagnosticFactTag::ArgumentIndex,
822 DiagnosticFactTag::TypeFamily,
823 ],
824 )
825 || tags_match(
826 fact_count,
827 fact_at,
828 &[
829 DiagnosticFactTag::OperatorKind,
830 DiagnosticFactTag::TypeFamily,
831 DiagnosticFactTag::TypeFamily,
832 ],
833 )
834 || tags_match(
835 fact_count,
836 fact_at,
837 &[
838 DiagnosticFactTag::BranchIndex,
839 DiagnosticFactTag::TypeFamily,
840 DiagnosticFactTag::TypeFamily,
841 ],
842 )
843 || tags_match(
844 fact_count,
845 fact_at,
846 &[
847 DiagnosticFactTag::TypeFamily,
848 DiagnosticFactTag::BranchIndex,
849 DiagnosticFactTag::TypeFamily,
850 ],
851 )
852 || tags_match(
853 fact_count,
854 fact_at,
855 &[
856 DiagnosticFactTag::BranchIndex,
857 DiagnosticFactTag::TypeFamily,
858 DiagnosticFactTag::BranchIndex,
859 DiagnosticFactTag::TypeFamily,
860 ],
861 )
862 || tags_match(
863 fact_count,
864 fact_at,
865 &[
866 DiagnosticFactTag::FunctionKind,
867 DiagnosticFactTag::ArgumentIndex,
868 DiagnosticFactTag::TypeFamily,
869 DiagnosticFactTag::ArgumentIndex,
870 DiagnosticFactTag::TypeFamily,
871 ],
872 )
873}
874
875fn cursor_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
876 if fact_count == 0 {
877 return true;
878 }
879 if tags_match(fact_count, fact_at, &[DiagnosticFactTag::DecodeReason]) {
880 return matches!(fact_at(0).1, 1 | 3 | 5 | 6 | 7);
881 }
882 if tags_match(
883 fact_count,
884 fact_at,
885 &[
886 DiagnosticFactTag::ActualLength,
887 DiagnosticFactTag::Maximum,
888 DiagnosticFactTag::DecodeReason,
889 ],
890 ) {
891 return fact_at(2).1 == DiagnosticDecodeReason::CursorTooLong.raw();
892 }
893 if tags_match(
894 fact_count,
895 fact_at,
896 &[
897 DiagnosticFactTag::ComponentIndex,
898 DiagnosticFactTag::DecodeReason,
899 ],
900 ) {
901 return matches!(fact_at(1).1, 4 | 6 | 7);
902 }
903 tags_match(
904 fact_count,
905 fact_at,
906 &[
907 DiagnosticFactTag::ExpectedSignaturePrefix,
908 DiagnosticFactTag::ActualSignaturePrefix,
909 ],
910 ) || tags_match(
911 fact_count,
912 fact_at,
913 &[
914 DiagnosticFactTag::ExpectedOffset,
915 DiagnosticFactTag::ActualOffset,
916 ],
917 )
918}
919
920fn store_corruption_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
921 fact_count == 0
922 || (tags_match(
923 fact_count,
924 fact_at,
925 &[
926 DiagnosticFactTag::ComponentKind,
927 DiagnosticFactTag::ActualLength,
928 DiagnosticFactTag::Limit,
929 ],
930 ) && fact_at(0).1 == DiagnosticComponentKind::CommitDataKey.raw())
931 || tags_match(
932 fact_count,
933 fact_at,
934 &[
935 DiagnosticFactTag::ExpectedEntityTag,
936 DiagnosticFactTag::ActualEntityTag,
937 ],
938 )
939}
940
941fn runtime_corruption_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
942 store_corruption_schema(fact_count, fact_at)
943 || (tags_match(fact_count, fact_at, &[DiagnosticFactTag::DecodeReason])
944 && matches!(fact_at(0).1, 8..=10))
945}
946
947fn incompatible_format_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
948 fact_count == 0
949 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ExpectedVersion])
950 || tags_match(
951 fact_count,
952 fact_at,
953 &[
954 DiagnosticFactTag::ExpectedVersion,
955 DiagnosticFactTag::ActualVersion,
956 ],
957 )
958}
959
960fn runtime_invariant_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
961 fact_count == 0
962 || (tags_match(
963 fact_count,
964 fact_at,
965 &[
966 DiagnosticFactTag::EntityTag,
967 DiagnosticFactTag::PhysicalGeneration,
968 DiagnosticFactTag::ComponentKind,
969 DiagnosticFactTag::ActualArity,
970 DiagnosticFactTag::Maximum,
971 ],
972 ) && fact_at(2).1 == DiagnosticComponentKind::IndexKey.raw())
973}
974
975fn runtime_conflict_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
976 fact_count == 0
977 || tags_match(fact_count, fact_at, &[DiagnosticFactTag::ExpectedRevision])
978 || tags_match(
979 fact_count,
980 fact_at,
981 &[
982 DiagnosticFactTag::ExpectedRevision,
983 DiagnosticFactTag::CurrentRevision,
984 ],
985 )
986}
987
988fn runtime_unsupported_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
989 fact_count == 0
990 || (tags_match(
991 fact_count,
992 fact_at,
993 &[
994 DiagnosticFactTag::EntityTag,
995 DiagnosticFactTag::PhysicalGeneration,
996 DiagnosticFactTag::ComponentIndex,
997 DiagnosticFactTag::ComponentKind,
998 DiagnosticFactTag::ActualLength,
999 DiagnosticFactTag::Limit,
1000 ],
1001 ) && fact_at(3).1 == DiagnosticComponentKind::IndexKeyComponent.raw())
1002}
1003
1004fn runtime_internal_schema(fact_count: usize, fact_at: &impl Fn(usize) -> (u8, u64)) -> bool {
1005 fact_count == 0
1006 || tags_match(
1007 fact_count,
1008 fact_at,
1009 &[
1010 DiagnosticFactTag::ExpectedMemoryId,
1011 DiagnosticFactTag::ActualMemoryId,
1012 ],
1013 )
1014 || (tags_match(
1015 fact_count,
1016 fact_at,
1017 &[
1018 DiagnosticFactTag::ComponentKind,
1019 DiagnosticFactTag::ExpectedArity,
1020 DiagnosticFactTag::ActualArity,
1021 ],
1022 ) && fact_at(0).1 == DiagnosticComponentKind::RelationTargetPrimaryKey.raw())
1023}
1024
1025fn constraint_schema(
1026 fact_count: usize,
1027 fact_at: &impl Fn(usize) -> (u8, u64),
1028 allow_targeted_path: bool,
1029) -> bool {
1030 const COMMON: &[DiagnosticFactTag] = &[
1031 DiagnosticFactTag::AcceptedSchemaFingerprintMethod,
1032 DiagnosticFactTag::AcceptedSchemaFingerprintHigh,
1033 DiagnosticFactTag::AcceptedSchemaFingerprintLow,
1034 DiagnosticFactTag::EntityTag,
1035 DiagnosticFactTag::ConstraintId,
1036 DiagnosticFactTag::ConstraintKind,
1037 DiagnosticFactTag::ConstraintContext,
1038 ];
1039 if fact_count < COMMON.len()
1040 || !tags_prefix_matches(fact_count, fact_at, COMMON)
1041 || fact_at(6).1 != DiagnosticConstraintContext::WriteAdmission.raw()
1042 {
1043 return false;
1044 }
1045
1046 let constraint_kind = fact_at(5).1;
1047 if DiagnosticConstraintKind::known(constraint_kind).is_none() {
1048 return false;
1049 }
1050 let mut index = COMMON.len();
1051 if index < fact_count && fact_at(index).0 == DiagnosticFactTag::MutationOperation.raw() {
1052 index += 1;
1053 if index < fact_count && fact_at(index).0 == DiagnosticFactTag::BatchPosition.raw() {
1054 index += 1;
1055 }
1056 }
1057
1058 let path_len = fact_count - index;
1059 if path_len == 0 {
1060 return !allow_targeted_path
1061 || constraint_kind != DiagnosticConstraintKind::TargetedRule.raw();
1062 }
1063 allow_targeted_path
1064 && constraint_kind == DiagnosticConstraintKind::TargetedRule.raw()
1065 && path_len <= 64
1066 && (index..fact_count).all(|position| {
1067 DiagnosticFactTag::known(fact_at(position).0).is_some_and(is_value_path_tag)
1068 })
1069}
1070
1071fn tags_match(
1072 fact_count: usize,
1073 fact_at: &impl Fn(usize) -> (u8, u64),
1074 expected: &[DiagnosticFactTag],
1075) -> bool {
1076 fact_count == expected.len() && tags_prefix_matches(fact_count, fact_at, expected)
1077}
1078
1079fn tags_prefix_matches(
1080 fact_count: usize,
1081 fact_at: &impl Fn(usize) -> (u8, u64),
1082 expected: &[DiagnosticFactTag],
1083) -> bool {
1084 fact_count >= expected.len()
1085 && expected
1086 .iter()
1087 .enumerate()
1088 .all(|(index, tag)| fact_at(index).0 == tag.raw())
1089}
1090
1091const fn is_value_path_tag(tag: DiagnosticFactTag) -> bool {
1092 matches!(
1093 tag,
1094 DiagnosticFactTag::RootField
1095 | DiagnosticFactTag::RecordMember
1096 | DiagnosticFactTag::TupleElement
1097 | DiagnosticFactTag::Newtype
1098 | DiagnosticFactTag::EnumVariant
1099 | DiagnosticFactTag::ListElement
1100 | DiagnosticFactTag::SetElement
1101 | DiagnosticFactTag::MapEntryKey
1102 | DiagnosticFactTag::MapEntryValue
1103 )
1104}
1105
1106const fn diagnostic_fact_value_is_valid(tag: DiagnosticFactTag, value: u64) -> bool {
1107 match tag {
1108 DiagnosticFactTag::AcceptedSchemaFingerprintMethod
1109 | DiagnosticFactTag::ExpectedMemoryId
1110 | DiagnosticFactTag::ActualMemoryId => value <= u8::MAX as u64,
1111 DiagnosticFactTag::ConstraintId
1112 | DiagnosticFactTag::FieldId
1113 | DiagnosticFactTag::IndexId
1114 | DiagnosticFactTag::RelationId
1115 | DiagnosticFactTag::BatchPosition
1116 | DiagnosticFactTag::FirstBatchPosition
1117 | DiagnosticFactTag::DuplicateBatchPosition
1118 | DiagnosticFactTag::RowLayout
1119 | DiagnosticFactTag::HistoryFloor
1120 | DiagnosticFactTag::CurrentLayout
1121 | DiagnosticFactTag::RootField
1122 | DiagnosticFactTag::Newtype
1123 | DiagnosticFactTag::ListElement
1124 | DiagnosticFactTag::SetElement
1125 | DiagnosticFactTag::MapEntryKey
1126 | DiagnosticFactTag::MapEntryValue => value <= u32::MAX as u64,
1127 DiagnosticFactTag::ConstraintKind => DiagnosticConstraintKind::known(value).is_some(),
1128 DiagnosticFactTag::ConstraintContext => DiagnosticConstraintContext::known(value).is_some(),
1129 DiagnosticFactTag::TypeFamily => DiagnosticTypeFamily::known(value).is_some(),
1130 DiagnosticFactTag::FunctionKind => DiagnosticFunctionKind::known(value).is_some(),
1131 DiagnosticFactTag::OperatorKind => DiagnosticOperatorKind::known(value).is_some(),
1132 DiagnosticFactTag::AggregateKind => DiagnosticAggregateKind::known(value).is_some(),
1133 DiagnosticFactTag::ComponentKind => DiagnosticComponentKind::known(value).is_some(),
1134 DiagnosticFactTag::DecodeReason => DiagnosticDecodeReason::known(value).is_some(),
1135 DiagnosticFactTag::BudgetResource => {
1136 DiagnosticExecutionBudgetResource::known(value).is_some()
1137 }
1138 DiagnosticFactTag::ExecutionBudgetScope => {
1139 DiagnosticExecutionBudgetScope::known(value).is_some()
1140 }
1141 DiagnosticFactTag::ExecutionLane => DiagnosticExecutionLane::known(value).is_some(),
1142 DiagnosticFactTag::MutationOperation => DiagnosticMutationOperation::known(value).is_some(),
1143 _ => true,
1144 }
1145}
1146
1147#[cfg(test)]
1148mod tests {
1149 use super::{
1150 DiagnosticAggregateKind, DiagnosticComponentKind, DiagnosticConstraintContext,
1151 DiagnosticConstraintKind, DiagnosticDecodeReason, DiagnosticExecutionBudgetResource,
1152 DiagnosticExecutionBudgetScope, DiagnosticExecutionLane, DiagnosticFactSchemaMismatch,
1153 DiagnosticFactTag, DiagnosticFunctionKind, DiagnosticMutationOperation,
1154 DiagnosticOperatorKind, DiagnosticTypeFamily, ORDERED_FACT_TAGS, pack_u32_pair,
1155 unpack_u32_pair, validate_known_diagnostic_fact_schema,
1156 validate_raw_diagnostic_fact_schema,
1157 };
1158 use crate::ErrorCode;
1159
1160 #[test]
1161 fn fact_tag_registry_is_fixed_unique_and_contiguous() {
1162 for (index, tag) in ORDERED_FACT_TAGS.iter().copied().enumerate() {
1163 let expected = u8::try_from(index + 1).expect("fact-tag index fits u8");
1164 assert_eq!(tag.raw(), expected);
1165 assert_eq!(DiagnosticFactTag::known(expected), Some(tag));
1166 }
1167
1168 assert_eq!(DiagnosticFactTag::known(0), None);
1169 assert_eq!(DiagnosticFactTag::known(93), None);
1170 assert_eq!(DiagnosticFactTag::known(u8::MAX), None);
1171 }
1172
1173 #[test]
1174 fn execution_budget_fact_value_registries_are_fixed() {
1175 for (index, resource) in DiagnosticExecutionBudgetResource::ALL
1176 .iter()
1177 .copied()
1178 .enumerate()
1179 {
1180 let expected = u64::try_from(index + 1).expect("resource index fits u64");
1181 assert_eq!(resource.raw(), expected);
1182 assert_eq!(
1183 DiagnosticExecutionBudgetResource::known(expected),
1184 Some(resource)
1185 );
1186 }
1187 assert_eq!(DiagnosticExecutionBudgetResource::known(0), None);
1188 assert_eq!(DiagnosticExecutionBudgetResource::known(22), None);
1189
1190 assert_eq!(DiagnosticExecutionBudgetScope::Execution.raw(), 1);
1191 assert_eq!(DiagnosticExecutionBudgetScope::Request.raw(), 2);
1192 assert_eq!(DiagnosticExecutionBudgetScope::known(3), None);
1193
1194 assert_eq!(DiagnosticExecutionLane::PublicRead.raw(), 1);
1195 assert_eq!(DiagnosticExecutionLane::TrustedRead.raw(), 2);
1196 assert_eq!(DiagnosticExecutionLane::Diagnostic.raw(), 3);
1197 assert_eq!(DiagnosticExecutionLane::Mutation.raw(), 4);
1198 assert_eq!(DiagnosticExecutionLane::Recovery.raw(), 5);
1199 assert_eq!(DiagnosticExecutionLane::known(6), None);
1200 }
1201
1202 #[test]
1203 fn accepted_identity_pair_packing_is_exact() {
1204 for pair in [
1205 (0, 0),
1206 (1, 2),
1207 (u32::MAX, 0),
1208 (0, u32::MAX),
1209 (u32::MAX, u32::MAX),
1210 ] {
1211 assert_eq!(unpack_u32_pair(pack_u32_pair(pair.0, pair.1)), pair);
1212 }
1213 }
1214
1215 #[test]
1216 fn constraint_fact_value_registries_are_fixed() {
1217 assert_eq!(DiagnosticConstraintKind::Check.raw(), 1);
1218 assert_eq!(DiagnosticConstraintKind::NotNull.raw(), 2);
1219 assert_eq!(DiagnosticConstraintKind::Relation.raw(), 3);
1220 assert_eq!(DiagnosticConstraintKind::TargetedRule.raw(), 4);
1221 assert_eq!(DiagnosticConstraintKind::Unique.raw(), 5);
1222 assert_eq!(DiagnosticConstraintKind::known(0), None);
1223 assert_eq!(DiagnosticConstraintKind::known(6), None);
1224
1225 assert_eq!(DiagnosticConstraintContext::Integrity.raw(), 1);
1226 assert_eq!(DiagnosticConstraintContext::MigrationValidation.raw(), 2);
1227 assert_eq!(DiagnosticConstraintContext::WriteAdmission.raw(), 3);
1228 assert_eq!(DiagnosticConstraintContext::known(0), None);
1229 assert_eq!(DiagnosticConstraintContext::known(4), None);
1230 }
1231
1232 #[test]
1233 fn component_kind_registry_is_fixed_and_numeric() {
1234 let kinds = [
1235 DiagnosticComponentKind::CommitDataKey,
1236 DiagnosticComponentKind::IndexKey,
1237 DiagnosticComponentKind::IndexKeyComponent,
1238 DiagnosticComponentKind::RelationTargetPrimaryKey,
1239 ];
1240
1241 for (index, kind) in kinds.iter().copied().enumerate() {
1242 let expected = (index + 1) as u64;
1243 assert_eq!(kind.raw(), expected);
1244 assert_eq!(DiagnosticComponentKind::known(expected), Some(kind));
1245 assert_eq!(format!("{kind:?}"), expected.to_string());
1246 }
1247 assert_eq!(DiagnosticComponentKind::known(0), None);
1248 assert_eq!(DiagnosticComponentKind::known(5), None);
1249 }
1250
1251 #[test]
1252 fn decode_reason_registry_is_fixed_and_numeric() {
1253 let reasons = [
1254 DiagnosticDecodeReason::CursorEmpty,
1255 DiagnosticDecodeReason::CursorTooLong,
1256 DiagnosticDecodeReason::CursorOddLength,
1257 DiagnosticDecodeReason::CursorInvalidHex,
1258 DiagnosticDecodeReason::CursorGroupedDirectionMismatch,
1259 DiagnosticDecodeReason::CursorTokenEncode,
1260 DiagnosticDecodeReason::CursorTokenDecode,
1261 DiagnosticDecodeReason::RecoveryMarkerMagic,
1262 DiagnosticDecodeReason::RecoveryMarkerChecksum,
1263 DiagnosticDecodeReason::RecoveryMarkerState,
1264 ];
1265
1266 for (index, reason) in reasons.iter().copied().enumerate() {
1267 let expected = (index + 1) as u64;
1268 assert_eq!(reason.raw(), expected);
1269 assert_eq!(DiagnosticDecodeReason::known(expected), Some(reason));
1270 assert_eq!(format!("{reason:?}"), expected.to_string());
1271 }
1272
1273 assert_eq!(DiagnosticDecodeReason::known(0), None);
1274 assert_eq!(DiagnosticDecodeReason::known(11), None);
1275 }
1276
1277 #[test]
1278 fn mutation_operation_registry_is_fixed_and_numeric() {
1279 let operations = [
1280 DiagnosticMutationOperation::Insert,
1281 DiagnosticMutationOperation::Replace,
1282 DiagnosticMutationOperation::Update,
1283 DiagnosticMutationOperation::Delete,
1284 ];
1285
1286 for (index, operation) in operations.iter().copied().enumerate() {
1287 let expected = (index + 1) as u64;
1288 assert_eq!(operation.raw(), expected);
1289 assert_eq!(
1290 DiagnosticMutationOperation::known(expected),
1291 Some(operation)
1292 );
1293 assert_eq!(format!("{operation:?}"), expected.to_string());
1294 }
1295
1296 assert_eq!(DiagnosticMutationOperation::known(0), None);
1297 assert_eq!(DiagnosticMutationOperation::known(5), None);
1298 }
1299
1300 #[test]
1301 fn query_kind_registries_are_fixed_contiguous_and_numeric() {
1302 for raw in 1..=9 {
1303 let value = DiagnosticTypeFamily::known(raw).expect("type family should be known");
1304 assert_eq!(value.raw(), raw);
1305 assert_eq!(format!("{value:?}"), raw.to_string());
1306 }
1307 assert_eq!(DiagnosticTypeFamily::known(0), None);
1308 assert_eq!(DiagnosticTypeFamily::known(10), None);
1309
1310 for raw in 1..=39 {
1311 let value = DiagnosticFunctionKind::known(raw).expect("function kind should be known");
1312 assert_eq!(value.raw(), raw);
1313 assert_eq!(format!("{value:?}"), raw.to_string());
1314 }
1315 assert_eq!(DiagnosticFunctionKind::known(0), None);
1316 assert_eq!(DiagnosticFunctionKind::known(40), None);
1317
1318 for raw in 1..=18 {
1319 let value = DiagnosticOperatorKind::known(raw).expect("operator kind should be known");
1320 assert_eq!(value.raw(), raw);
1321 assert_eq!(format!("{value:?}"), raw.to_string());
1322 }
1323 assert_eq!(DiagnosticOperatorKind::known(0), None);
1324 assert_eq!(DiagnosticOperatorKind::known(19), None);
1325
1326 for raw in 1..=8 {
1327 let value =
1328 DiagnosticAggregateKind::known(raw).expect("aggregate kind should be known");
1329 assert_eq!(value.raw(), raw);
1330 assert_eq!(format!("{value:?}"), raw.to_string());
1331 }
1332 assert_eq!(DiagnosticAggregateKind::known(0), None);
1333 assert_eq!(DiagnosticAggregateKind::known(9), None);
1334 }
1335
1336 #[test]
1337 fn per_code_schema_rejects_missing_disallowed_and_noncanonical_tags() {
1338 let valid = [
1339 (DiagnosticFactTag::ActualCount, 5),
1340 (DiagnosticFactTag::Limit, 4),
1341 ];
1342 assert_eq!(
1343 validate_known_diagnostic_fact_schema(
1344 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1345 &valid,
1346 ),
1347 Ok(())
1348 );
1349 assert_eq!(
1350 validate_known_diagnostic_fact_schema(
1351 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1352 &valid[..1],
1353 ),
1354 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1355 );
1356 assert_eq!(
1357 validate_known_diagnostic_fact_schema(
1358 ErrorCode::RUNTIME_BOUNDARY_MUTATION_BATCH_TOO_MANY_ITEMS,
1359 &[valid[1], valid[0]],
1360 ),
1361 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1362 );
1363 assert_eq!(
1364 validate_known_diagnostic_fact_schema(ErrorCode::QUERY_VALIDATE, &valid),
1365 Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded)
1366 );
1367 }
1368
1369 #[test]
1370 fn execution_budget_schema_requires_complete_typed_attribution() {
1371 let valid = [
1372 (
1373 DiagnosticFactTag::BudgetResource,
1374 DiagnosticExecutionBudgetResource::StoredBytesRead.raw(),
1375 ),
1376 (DiagnosticFactTag::Limit, 4_096),
1377 (DiagnosticFactTag::Actual, 4_097),
1378 (
1379 DiagnosticFactTag::ExecutionBudgetScope,
1380 DiagnosticExecutionBudgetScope::Request.raw(),
1381 ),
1382 (
1383 DiagnosticFactTag::ExecutionLane,
1384 DiagnosticExecutionLane::TrustedRead.raw(),
1385 ),
1386 (DiagnosticFactTag::QueryShapeFingerprintPrefix, 17),
1387 ];
1388 assert_eq!(
1389 validate_known_diagnostic_fact_schema(
1390 ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1391 &valid,
1392 ),
1393 Ok(())
1394 );
1395 assert_eq!(
1396 validate_known_diagnostic_fact_schema(
1397 ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1398 &valid[..5],
1399 ),
1400 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1401 );
1402
1403 let mut invalid_resource = valid;
1404 invalid_resource[0].1 = 0;
1405 assert_eq!(
1406 validate_known_diagnostic_fact_schema(
1407 ErrorCode::RUNTIME_BOUNDARY_EXECUTION_BUDGET_EXCEEDED,
1408 &invalid_resource,
1409 ),
1410 Err(DiagnosticFactSchemaMismatch::InvalidValue)
1411 );
1412 }
1413
1414 #[test]
1415 fn raw_schema_keeps_unknown_context_numeric_but_marks_it_invalid() {
1416 assert_eq!(
1417 validate_raw_diagnostic_fact_schema(
1418 ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
1419 &[(u8::MAX, 17)],
1420 ),
1421 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1422 );
1423 assert_eq!(
1424 validate_raw_diagnostic_fact_schema(
1425 ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
1426 &[(DiagnosticFactTag::DecodeReason.raw(), u64::MAX)],
1427 ),
1428 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1429 );
1430 }
1431
1432 #[test]
1433 fn constraint_schema_enforces_authority_operation_and_bounded_path_suffix() {
1434 let mut targeted = vec![
1435 (DiagnosticFactTag::AcceptedSchemaFingerprintMethod, 1),
1436 (DiagnosticFactTag::AcceptedSchemaFingerprintHigh, 2),
1437 (DiagnosticFactTag::AcceptedSchemaFingerprintLow, 3),
1438 (DiagnosticFactTag::EntityTag, 17),
1439 (DiagnosticFactTag::ConstraintId, 4),
1440 (
1441 DiagnosticFactTag::ConstraintKind,
1442 DiagnosticConstraintKind::TargetedRule.raw(),
1443 ),
1444 (
1445 DiagnosticFactTag::ConstraintContext,
1446 DiagnosticConstraintContext::WriteAdmission.raw(),
1447 ),
1448 (
1449 DiagnosticFactTag::MutationOperation,
1450 DiagnosticMutationOperation::Insert.raw(),
1451 ),
1452 (DiagnosticFactTag::BatchPosition, 0),
1453 ];
1454 targeted.extend((0..64).map(|index| (DiagnosticFactTag::ListElement, index)));
1455 assert_eq!(targeted.len(), 73);
1456 assert_eq!(
1457 validate_known_diagnostic_fact_schema(
1458 ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1459 targeted.as_slice(),
1460 ),
1461 Ok(())
1462 );
1463
1464 let mut overlong = targeted.clone();
1465 overlong.push((DiagnosticFactTag::ListElement, 64));
1466 assert_eq!(
1467 validate_known_diagnostic_fact_schema(
1468 ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1469 overlong.as_slice(),
1470 ),
1471 Err(DiagnosticFactSchemaMismatch::CodeMaximumExceeded)
1472 );
1473
1474 let mut non_targeted_path = targeted;
1475 non_targeted_path[5].1 = DiagnosticConstraintKind::Unique.raw();
1476 assert_eq!(
1477 validate_known_diagnostic_fact_schema(
1478 ErrorCode::RUNTIME_BOUNDARY_CONSTRAINT_VIOLATION,
1479 non_targeted_path.as_slice(),
1480 ),
1481 Err(DiagnosticFactSchemaMismatch::InvalidSequence)
1482 );
1483 }
1484
1485 #[test]
1486 fn schema_enforces_global_ceiling_before_per_code_ceiling() {
1487 let facts = vec![(DiagnosticFactTag::ActualCount.raw(), 0); 81];
1488 assert_eq!(
1489 validate_raw_diagnostic_fact_schema(ErrorCode::QUERY_PLAN, facts.as_slice()),
1490 Err(DiagnosticFactSchemaMismatch::GlobalMaximumExceeded)
1491 );
1492 }
1493}