|
| 1 | +// SPDX-License-Identifier: PMPL-1.0-or-later |
| 2 | +// Copyright (c) 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk> |
| 3 | += VQL-UT: Supplementary Proof for Theorem [Composition Preservation] |
| 4 | +:toc: left |
| 5 | +:sectnums: |
| 6 | + |
| 7 | +This document is the supplementary material for §8 of the VQL-UT paper. |
| 8 | +It describes and locates the mechanised Idris 2 proof of Theorem |
| 9 | +[Composition Preservation]. |
| 10 | + |
| 11 | +== Source Location |
| 12 | + |
| 13 | +All proofs reside in the repository at: |
| 14 | + |
| 15 | +[source] |
| 16 | +---- |
| 17 | +src/core/Composition.idr -- The theorem and all helper lemmas |
| 18 | +src/core/Levels.idr -- SafetyCertificate type and level predicates |
| 19 | +src/core/Grammar.idr -- Statement, selectFieldRefs, exprFieldRefs |
| 20 | +src/core/Schema.idr -- OctadSchema, FieldBound, AllFieldsBound |
| 21 | +---- |
| 22 | + |
| 23 | +All files use `%default total`. No `believe_me`, `assert_total`, |
| 24 | +`postulate`, or `sorry` appears anywhere. |
| 25 | + |
| 26 | +== Proof Structure |
| 27 | + |
| 28 | +=== Composition Operation |
| 29 | + |
| 30 | +`composeJoin : Statement -> Statement -> Statement` (Composition.idr) |
| 31 | + |
| 32 | +Defines the relational join of two queries. Key design choices: |
| 33 | + |
| 34 | +- SELECT items: concatenated (`selectItems q1 ++ selectItems q2`) |
| 35 | +- WHERE clause: AND-conjoined via `joinWhere` (`ELogic And w1 w2`) |
| 36 | +- GROUP BY: concatenated |
| 37 | +- HAVING: dropped (not preserved through join) |
| 38 | +- LIMIT: `min n1 n2` (stricter bound) |
| 39 | +- EFFECTS: union |
| 40 | +- VERSION: tightened to most recent |
| 41 | +- LINEAR: stricter annotation |
| 42 | +- EPISTEMIC: merged agent sets and requirement lists |
| 43 | + |
| 44 | +=== Composable Predicate |
| 45 | + |
| 46 | +`data Composable : Statement -> Statement -> Type` — two queries are |
| 47 | +composable iff they target the same source octad |
| 48 | +(`source q1 = source q2`). |
| 49 | + |
| 50 | +=== Helper Lemmas |
| 51 | + |
| 52 | +[cols="1,3"] |
| 53 | +|=== |
| 54 | +|Lemma |Purpose |
| 55 | + |
| 56 | +|`appendNilRight` |xs ++ [] = xs (list identity) |
| 57 | +|`mapFstAppend` |map fst distributes over ++ |
| 58 | +|`selectFieldRefsAppend` |selectFieldRefs distributes over ++ |
| 59 | +|`joinWhereBoth` |exprFieldRefs of AND-join = union of both sides (by Refl) |
| 60 | +|`joinWhereNilR/LNil` |exprFieldRefs of half-Nothing join inherits the other side |
| 61 | +|`allFieldsBoundAppend` |AllFieldsBound closed under list append |
| 62 | +|`allFieldsBoundSubset` |AllFieldsBound respects membership subset |
| 63 | +|`allFieldsBoundFromElem` |Build AllFieldsBound from an element-wise lookup |
| 64 | +|`boundLookup` |Look up a FieldBound by Elem evidence |
| 65 | +|`elemAppendLeft/Right` |Elem in xs → Elem in xs ++ ys (and symmetric) |
| 66 | +|`elemAppendSplit` |Elem in xs ++ ys → Elem in xs or Elem in ys |
| 67 | +|`composeJoinFieldsSubset` |Every field ref in composeJoin q1 q2 is in refs(q1) ∪ refs(q2) |
| 68 | +|=== |
| 69 | + |
| 70 | +=== Level-by-Level Proof Summary |
| 71 | + |
| 72 | +[cols="1,2,3"] |
| 73 | +|=== |
| 74 | +|Level |Certificate constructor |Why it's preserved |
| 75 | + |
| 76 | +|L0 ParseSafe |
| 77 | +|`CertL0` |
| 78 | +|`MkL0 (composeJoin _ _)` — the join is parseable by construction. |
| 79 | + |
| 80 | +|L1 SchemaBound |
| 81 | +|`CertL1` |
| 82 | +|`composeJoinFieldsSubset` + `allFieldsBoundSubset` + `allFieldsBoundAppend`. |
| 83 | + Every field ref in the join comes from q1 or q2; both are schema-bound. |
| 84 | + |
| 85 | +|L2 TypeCompat |
| 86 | +|`CertL2` |
| 87 | +|The joined WHERE is `ELogic And w1 w2 TBool`. |
| 88 | + `LogicSafe : ExprTypeSafe (ELogic ...) schema` holds unconditionally. |
| 89 | + |
| 90 | +|L3 NullSafe |
| 91 | +|`CertL3` |
| 92 | +|`GuardedNull : AllNullableFieldsGuarded (Just expr) schema` holds |
| 93 | + for any expression — the predicate asserts existence of null guards |
| 94 | + without inspecting the expression structure. |
| 95 | + |
| 96 | +|L4 InjectionProof |
| 97 | +|`CertL4` |
| 98 | +|`AllParameterised : NoRawUserInput stmt` holds for any statement. |
| 99 | + `composeJoin` does not introduce raw user input (it only combines |
| 100 | + existing AST nodes, all of which were already parameterised). |
| 101 | + |
| 102 | +|L5 ResultTyped |
| 103 | +|`CertL5` |
| 104 | +|`ConsTyped/NilTyped` inductively covers any select list. |
| 105 | + The combined list `selectItems q1 ++ selectItems q2` is typed |
| 106 | + item-by-item using `selectTypedAppend`. |
| 107 | + |
| 108 | +|L6 CardinalitySafe |
| 109 | +|`CertL6` |
| 110 | +|`l6Compose`: `joinLimit (Just n1) (Just n2) = Just (min n1 n2)`, |
| 111 | + so the composed LIMIT is `Just` when both are. |
| 112 | + |
| 113 | +|L7 EffectTracked |
| 114 | +|`CertL7` |
| 115 | +|`l7Compose`: effect declarations are unioned; the union is `Just` |
| 116 | + when both operands are `Just`. |
| 117 | + |
| 118 | +|L8 TemporalSafe |
| 119 | +|`CertL8` |
| 120 | +|`l8Compose`: version constraints are tightened to the most recent; |
| 121 | + the result is `Just` when both are `Just`. |
| 122 | + |
| 123 | +|L9 LinearSafe |
| 124 | +|`CertL9` |
| 125 | +|`l9Compose`: the stricter linear annotation wins; |
| 126 | + the result is `Just` when both are `Just`. |
| 127 | + |
| 128 | +|L10 EpistemicSafe |
| 129 | +|`CertL10` |
| 130 | +|`l10Compose`: agent sets and requirement lists are merged. |
| 131 | + The result is `Just (EpClause (as1 ++ as2) (rs1 ++ rs2))`. |
| 132 | +|=== |
| 133 | + |
| 134 | +=== The Main Theorem |
| 135 | + |
| 136 | +[source,idris] |
| 137 | +---- |
| 138 | +compositionPreservation : |
| 139 | + (q1, q2 : Statement) -> |
| 140 | + (schema : OctadSchema) -> |
| 141 | + (k : SafetyLevel) -> |
| 142 | + Composable q1 q2 -> |
| 143 | + SafetyCertificate q1 schema k -> |
| 144 | + SafetyCertificate q2 schema k -> |
| 145 | + SafetyCertificate (composeJoin q1 q2) schema k |
| 146 | +---- |
| 147 | + |
| 148 | +Proof by case analysis on `k` (the safety level). Each case constructs |
| 149 | +the composed certificate from the two input certificates using the helpers |
| 150 | +above. The totality checker accepts all 11 cases. |
| 151 | + |
| 152 | +== Scope |
| 153 | + |
| 154 | +This proof covers the `composeJoin` operation (inner equi-join). |
| 155 | +Union, intersection, and subquery composition are conjectured to follow |
| 156 | +the same pattern but are not yet mechanised; this is future work. |
0 commit comments