Query evaluation by rewriting #
This file provides an alternative approach to evaluating queries on annotated databases:
instead of directly interpreting operators over annotated tuples, a query on T is
rewritten into a query on T ⊕ K that operates on plain tuples whose values encode
both data and provenance.
The rewriting implemented here realizes rules (R1)–(R5) from Sen, Maniu & Senellart, ProvSQL: A General System for Keeping Track of the Provenance and Probability of Data.
A correctness proof that Query.rewriting agrees with Query.evaluateAnnotated is
fully formalized for rules (R1)–(R4): each operator is machine-checked end-to-end.
The Diff case splits into an unmatched_eq half (proved via the semijoin
identity Multiset.semijoin_proj_eq_filter, after bridging the
LinearOrder.toDecidableEq vs instDecidableEqSum mismatch on the inner dedup
via Query.rewriting_valid_diff_inner_dd_inst) and a matched_eq half (proved
via the keyed-projection semijoin Multiset.semijoin_keyed_proj_eq_filter, after
substituting the inner aggregation with the closed-form
Query.evaluate_agg_rewriting_eq). Rule (R5) – aggregation – is not part of
this classical rewriting: it lives on the general syntax, in
Provenance.AggQueryGroupRewriting, where an aggregate output is a symbolic
token rather than a quotiented K-tensor.
References #
Equations
- One or more equations did not get rendered due to their size.
- (Query.Rel n s).rewriting hq_2 = Query.Rel (n + 1) s
- (Π ts q_2).rewriting hq_2 = (Π fun (k : Fin (n + 1)) => if h : ↑k < n then (ts ⟨↑k, h⟩).castToAnnotatedTuple else #(Fin.last q_2.arity)) (q_2.rewriting ⋯)
- (σ φ q_2).rewriting hq_2 = σ φ.castToAnnotatedTuple (q_2.rewriting ⋯)
- (q₁ ⊎ q₂).rewriting hq_2 = (q₁.rewriting ⋯ ⊎ q₂.rewriting ⋯)
- (ε q_2).rewriting hq_2 = Query.ProvSum (fun (k : Fin n) => Fin.castLE ⋯ k) (#(Fin.last n)) (q_2.rewriting ⋯)
- (Query.ProvSum a a_1 a_2).rewriting hq_2 = False.elim hq_2
Instances For
Dependency graph
Dependency graph
Dependency graph
Dependency graph
Dependency graph
Dependency graph
Dependency graph
Tuple.cast-flavored variant of rewriting_append_left. Both Tuple.cast's and ▸'s
Eq.rec motives must syntactically agree for rw to fire on Lean v4.29; this version
matches the motive produced by Tuple.cast.
Dependency graph
Tuple.cast-flavored variant of rewriting_append_right.
Dependency graph
Helper lemmas for the Dedup case of rewriting_valid #
Folding addFn over a multiset of Sum.inr k values in T⊕K reduces to the
Multiset.sum in K, wrapped in Sum.inr.
Dependency graph
Filtering ar.toComposite by “first-n columns match Sum.inl ∘ v” and projecting to the
last column yields the Sum.inr-wrapped annotations of the matching entries of ar.
Dependency graph
The dedup of the first-n projection of ar.toComposite is the Sum.inl-image of the dedup
of the first-projection of ar.
Dependency graph
Auxiliary: key set of groupByKey ar equals first-projection keys of ar.
Dependency graph
Auxiliary: if (v, w) ∈ (groupByKey ar).val, then w is the semiring-sum of annotations
of entries in ar with key v.
Dependency graph
groupByKey ar, as a multiset, equals the dedup of the first-projection of ar, with each
key paired with the semiring-sum of annotations sharing that key.
Dependency graph
Helper lemmas for the Diff case of rewriting_valid #
Multiset.dedup only depends on the DecidableEq instance up to subsingleton equality.
Dependency graph
Multiset.filter only depends on the DecidablePred instance up to subsingleton equality.
Dependency graph
Folded Selection.And over a mapped list is equivalent to the universal conjunction.
Dependency graph
The folded join condition (#k == #(k+n+1)) for k ∈ List.range n evaluates true iff the
tuple's values at indices ofNat k and ofNat (k+n+1) agree for every k < n.
Dependency graph
Semiring-sum over the filter, via groupByKey.find?-based lookup.
Dependency graph
Subtraction distributes over Sum.inr in T⊕K.
Dependency graph
Non-dedup form of dedup_toComposite_proj_first_n: the first-n projection of
ar.toComposite is the Sum.inl-lift of the first-projection of ar.
Dependency graph
Sum.inl-lift of tuples is injective.
Dependency graph
Filtering by “not a member of an injective image” pulls through the map.
Dependency graph
Helper: the data part Tuple.fromComposite and AnnotatedTuple.toComposite agree on data.
Dependency graph
The annotation part of p.toComposite is Sum.inr p.2.
Dependency graph
Roundtrip: Tuple.fromComposite ∘ AnnotatedTuple.toComposite = id. The
composite encoding loses no information: peeling the data columns and the
annotation column back out reconstructs the original annotated tuple.
Dependency graph
Pushforward version of Tuple.fromComposite_toComposite: mapping
Tuple.fromComposite over a composite-encoded annotated relation recovers
the original annotated relation.
Dependency graph
Reduction of the inner Dedup ∘ Diff ∘ Proj block of the Diff rewriting:
deduping the difference of first-n projections of AR₁.toComposite and AR₂.toComposite
yields the Sum.inl-lift of the deduped “unmatched-keys” filter over the data part.
Stated using Fin.castLE (function form) and dot notation (.dedup) so the LHS
pattern matches what simp only [evaluate] produces in the Diff case of
rewriting_valid.
Dependency graph
Relation.cast rewrites to a Multiset.map of Tuple.cast.
Dependency graph
Projecting the first n+1 columns of Tuple.cast h (Fin.append p q) (for
p : Tuple α (n+1), q : Tuple α n, h : n+1+n = 2*n+1) returns p.
Dependency graph
Reading Tuple.cast h (Fin.append p q) at index Fin.ofNat _ k.val (for k : Fin n)
returns p k.castSucc.
Dependency graph
Reading Tuple.cast h (Fin.append p q) at index Fin.ofNat _ (k.val+n+1) (for
k : Fin n) returns q k.
Dependency graph
selFilter on Tuple.cast h (Fin.append p q) characterizes the first-n
projection equality between p and q.
Dependency graph
Arity-(2n+2) analogue of cast_append_at_ofNat_left: reading
Tuple.cast h (Fin.append p q) at index Fin.ofNat _ k.val (for k : Fin n)
returns p (k.castLE (Nat.le_succ n)). Here q : Tuple α (n+1) (rather than
Tuple α n).
Dependency graph
Arity-(2n+2) analogue of cast_append_at_ofNat_right: reading
Tuple.cast h (Fin.append p q) at index Fin.ofNat _ (k.val+n+1) (for
k : Fin n) returns q (k.castLE (Nat.le_succ n)).
Dependency graph
Arity-(2n+2) helper: reading Tuple.cast h (Fin.append p q) at index
Fin.ofNat _ n returns p (Fin.last n).
Dependency graph
Arity-(2n+2) helper: reading Tuple.cast h (Fin.append p q) at index
Fin.last (2*n+1) (the last index of Fin (2*n+2)) returns q (Fin.last n).
Dependency graph
Arity-(2n+2) projection helper: reading Tuple.cast h (Fin.append p q) at index
k.castLE _ (for k : Fin (n+1)) returns p k. This is the analogue of
proj_outer_cast_append_eq_fst for the 2n+2 case (i.e., q : Tuple α (n+1)).
Dependency graph
Arity-(2n+2) analogue of selFilter_cast_append_iff: the join condition
on Tuple.cast h (Fin.append p q) with q : Tuple (T⊕K) (n+1) characterizes
equality of the first-n projections of p and q.
Dependency graph
Selection pushes through AnnotatedRelation.toComposite via the
Tuple.fromComposite ∘ AnnotatedTuple.toComposite = id roundtrip:
filtering before taking the composite encoding equals filtering the composite
encoding by the same predicate composed with Tuple.fromComposite.
Dependency graph
Semijoin reduction. Given multisets r : Multiset α and s : Multiset β and
a key function g : α → β, with s Nodup, the projection-after-filter of the
cartesian product (keeping pairs whose g-image matches) coincides with filtering
r to those a whose g a belongs to s. This is the multiset version of the
relational semijoin and is the structural identity behind the unmatched_eq
half of the Diff-case rewriting correctness.
Dependency graph
Keyed-projection semijoin. Generalizes Multiset.semijoin_proj_eq_filter in two
directions: the right multiset is the image S.map val of a Nodup keyset S under a
value function val : β → γ, and the projection is an arbitrary mk : α → γ → δ rather
than Prod.fst. The compatibility hypothesis h_val asserts that key_s ∘ val is the
identity on S (i.e., val reconstructs an element whose key_s-image is the original
key). The semijoin then reduces to filtering r by key_r a ∈ S and projecting through
mk a (val (key_r a)) (the unique matching γ-value). This is the structural identity
behind the matched_eq half of the Diff-case rewriting correctness.
Dependency graph
The ProvSum of q.rewriting (the inner ⊕-gate creation used in both
the Dedup and Diff rewritings) evaluates to a map over the deduped
data-projection of the inner annotated relation, with each row paired (via
AnnotatedTuple.toComposite) with the semiring sum of the matching
annotations.
Dependency graph
Instance-polymorphic restatement of Query.rewriting_valid_diff_inner_dd.
Inside the Diff case of rewriting_valid, Lean's instance synthesis picks
inconsistent DecidableEq (T⊕K) instances at different positions in the goal:
the inner Multiset.dedup is elaborated with LinearOrder.toDecidableEq (via
ValueType (T⊕K)), while the surrounding Multiset.filter's decidableMem
uses instDecidableEqSum. This wrapper accepts both as explicit parameters and
bridges to the canonical helper via Subsingleton.elim.