The compositional closure of the rewriting rules #
ProvSQL rewrites whole queries in which classical blocks, GROUP BY … HAVING blocks and bare GROUP BY blocks occur as subqueries. The
relation AggQuery.RewritesTo below closes the three base rewritings –
the classical rules (AggQuery.rewritingOf), the HAVING site
(AggQuery.havingPredRew, the aggregate-exposing shape ProvSQL actually
emits, for an arbitrary Boolean combination of aggregate comparisons) and
the bare grouping (AggQuery.gammaRew) – under the operators that may sit
above them, and
AggQuery.rewritesTo_valid extends the correctness to every query so
obtained.
Token-bearing outputs #
The base rules do not share an output shape: a classical or HAVING
block produces all-regular data columns, whereas a bare grouping produces
aggregate-token columns. The relation is therefore indexed by the
rewritten query's own kind vector, and correctness is stated at the token
level, through GenRow.toCompositeRow. On all-regular outputs this
specializes to the all-regular statement, AggQuery.rewritesTo_valid_reg.
The natural rewritten kind vector of a query of kinds κ is
ColKind.rewKindsOf κ – the source kinds followed by the provenance
column. Casting terms, predicates and projection columns into it is
uniform: a column keeps its kind, so no all-regular hypothesis is
needed anywhere (TermG.castRew and friends), unlike the composite casts
of the classical rewriting. The gate TermG.cmpAgg, whose generic
semantics is the junk value 𝟘, casts to that constant.
Scope #
Selection, projection and union close over arbitrary kinds – in
particular over a bare grouping, which is the SELECT … FROM (GROUP BY …)
shape. Deduplication closes over any subquery whose output is
all-regular, which is what the kind discipline permits: the rewritten
rule AggQuery.dedupRew is ProvSQL's ε (group by the data columns,
⊕-sum the provenance column), proven correct against an arbitrary
rewritten subquery rather than only against rewriting's output.
Product closes over arbitrary kinds too. Reassembling a join needs a
projection column whose kind is read off the operand's kind vector –
ProjCol.copy, which dispatches on that kind – and its faithfulness
needs the operands' rows to conform; that comes for free from the
subderivations, since their rows are embeddings of rows of the general
evaluator, which conforms by AggQuery.evaluate_conform.
Difference closes as well (AggQuery.diffRew). The closure is therefore
complete for the operators the kind discipline admits above a grouping:
there is no remaining structural gap.
The rewritten kind vector #
The rewritten kind vector of a query of kinds κ: the source kinds,
followed by the provenance column.
Equations
- ColKind.rewKindsOf κ = Fin.append κ fun (x : Fin 1) => ColKind.prov
Instances For
Dependency graph
Dependency graph
Dependency graph
On all-regular kinds the uniform rewritten kind vector agrees
pointwise with the classical rewriting's ColKind.rewKinds.
Dependency graph
Uniform casts into the rewritten world #
A term over the source kinds, read on the rewritten schema: every
column keeps its kind and its position, so no all-regular hypothesis is
needed. The gate, whose generic semantics is the junk value 𝟘, casts to
that constant.
Equations
- (TermG.const a).castRew = TermG.const (Sum.inl a)
- (TermG.index k h).castRew = TermG.index (Fin.castAdd 1 k) ⋯
- (TermG.provIndex k h).castRew = TermG.provIndex (Fin.castAdd 1 k) ⋯
- (TermG.cmpAgg k a a_1 a_2).castRew = TermG.const (Sum.inl 0)
- (TermG.chiGate a a_1 a_2).castRew = TermG.const (Sum.inl 0)
- (t₁.add t₂).castRew = t₁.castRew.add t₂.castRew
- (t₁.sub t₂).castRew = t₁.castRew.sub t₂.castRew
- (t₁.mul t₂).castRew = t₁.castRew.mul t₂.castRew
Instances For
Dependency graph
An aggregate-atom-free predicate is unnecessary here: the cast is total, aggregate atoms comparing a token's deterministic reading.
Equations
Instances For
Dependency graph
A projection column, read on the rewritten schema.
Equations
- (ProjCol.term t).castRew = ProjCol.term t.castRew
- (ProjCol.token k h).castRew = ProjCol.token (Fin.castAdd 1 k) ⋯
- (ProjCol.provTerm t).castRew = ProjCol.provTerm t.castRew
Instances For
Dependency graph
Dependency graph
The casts evaluate faithfully #
Dependency graph
Dependency graph
Dependency graph
On an all-regular query the token-aware embedding is the embedding of
the classical and HAVING-site correctness statements.
Dependency graph
A data column of a rewritten kind vector, positionally.
Dependency graph
The trailing column of a rewritten kind vector.
Dependency graph
Copying a column of unknown kind #
Copy the i-th column verbatim, whatever its kind: a regular or
provenance column is read as a value term, a token column is a verbatim
token copy. This is the projection column a join reassembly needs, since
the operand's kind vector is not statically known there.
Equations
- ProjCol.copy i = match h : κ' i with | ColKind.reg => ProjCol.term (TermG.index i h) | ColKind.agg => ProjCol.token i h | ColKind.prov => ProjCol.provTerm (TermG.provIndex i h)
Instances For
Dependency graph
Dependency graph
The kind of a transported value is the kind of the value.
Dependency graph
Kind conformance of the token-aware embedding: a row conforming
to κ embeds to one conforming to ColKind.rewKindsOf κ.
Dependency graph
A copied column evaluates to the column, on a conformant row.
Dependency graph
HAVING predicates as gate terms #
The HAVING site rewriting of Provenance.AggQueryHavingRewriting takes
one aggregate comparison. An arbitrary Boolean combination of atoms is
just as expressible: the predicate provenance predsem is ∧ ↦ ⊗,
∨ ↦ ⊕ and ¬ pushed to the atoms by De Morgan duality with operator
complementation, and the rewritten world's terms have mul, add and
the two gates – provsql_having for an aggregate atom, the indicator
gate for a regular one. GenPred.gateTerm is that translation; it is
faithful for an arbitrary predicate (GenPred.gateTerm_evalRew), the
gates being the primitives the correctness is relative to.
Regular atoms do change the fate of the group guard. An aggregate atom's
predicate provenance ranges over non-empty worlds only, so it supersedes
the guard; a regular atom's χ does not entail the group's existence
(GenPred.entailsExistence), and mixing one in can leave the whole
predicate non-entailing – count(*) > 5 ∨ city = 'Paris' fires in worlds
where the group is empty. The site rewriting therefore keeps the guard as
a factor in that case (GenPred.siteProvTerm), reproducing what the
general evaluator does with the pending group factor.
A predicate all of whose atoms are aggregate comparisons.
Equations
Instances For
Dependency graph
An aggregate-only predicate entails its groups' existence, whatever the polarity: every atom does, and both connectives preserve that.
Dependency graph
A predicate with an aggregate atom compares at least one token column.
Dependency graph
The predicate provenance as a rewritten term: the predsem
algebra – aggregate atoms to provsql_having gates, regular atoms to
indicator gates, ∧ ↦ ⊗, ∨ ↦ ⊕, ¬ pushed down with operator
complementation.
Equations
- (GenPred.cmp op t₁ t₂).gateTerm x✝ = TermG.chiGate (if x✝ = true then op.negate else op) t₁.castRew t₂.castRew
- (GenPred.aggCmp k h op t).gateTerm x✝ = TermG.cmpAgg (Fin.castAdd 1 k) ⋯ (if x✝ = true then op.negate else op) t.castRew
- (φ.and ψ).gateTerm x✝ = if x✝ = true then (φ.gateTerm x✝).add (ψ.gateTerm x✝) else (φ.gateTerm x✝).mul (ψ.gateTerm x✝)
- (φ.or ψ).gateTerm x✝ = if x✝ = true then (φ.gateTerm x✝).mul (ψ.gateTerm x✝) else (φ.gateTerm x✝).add (ψ.gateTerm x✝)
- φ.not.gateTerm x✝ = φ.gateTerm !x✝
Instances For
Dependency graph
The gate term computes the predicate provenance, for an arbitrary predicate: relative to the two gate primitives, which is exactly the sense in which ProvSQL's own rewriting is correct.
Dependency graph
An aggregate-only predicate has an aggregate atom.
Dependency graph
Compared columns are token columns – by construction of the aggregate atom.
Dependency graph
Kind conformance of a grouping row.
Dependency graph
Kind conformance of the embedding of a grouping row.
Dependency graph
A token column of a grouping row carries the group's annotation list.
Dependency graph
Superseding the group guard: when the compared occurrence lists are non-empty and all equal to the single pending group's list, the selection's filter removes that group factor, so the row finalizes to its concrete part.
Dependency graph
The general HAVING site #
The provenance column of a rewritten HAVING site: the
predicate's gate term, times the group-existence guard δ(⊕ U) – which
the rewritten grouping has left in the provenance column – unless the
predicate already entails the group's existence, in which case the gate
term supersedes it. This is ProvSQL's having_entails_group_existence
test: the supersede of the δ gate is licensed exactly when every world
the predicate accepts has the group non-empty.
Equations
Instances For
Dependency graph
The output columns of a general HAVING site: the group keys and the
aggregate tokens copied verbatim, and the predicate's provenance term in
the provenance column.
Equations
- AggQuery.havingPredCols φ j = if hj : ↑j < n₁ + n₂ then ProjCol.copy (Fin.castAdd 1 ⟨↑j, hj⟩) else ProjCol.provTerm φ.siteProvTerm
Instances For
Dependency graph
The site's output columns have exactly the rewritten Gamma kinds.
Dependency graph
The rewritten HAVING site, for an arbitrary predicate: the
token-building grouping of AggQuery.gammaRew, with a projection keeping
the group keys and the aggregate tokens and replacing the group-existence
guard by the predicate's provenance term – the gate term alone when the
predicate entails the group's existence, the gate term times the guard
otherwise.
Equations
- AggQuery.havingPredRew is ts fs φ qg hq = AggQuery.Retag ⋯ (AggQuery.Proj (AggQuery.havingPredCols φ) (AggQuery.gammaRew is ts fs qg hq))
Instances For
Dependency graph
Correctness of the general HAVING site rewriting, relative to
the gate primitives, for an arbitrary predicate with an aggregate atom –
regular atoms mixed in included. The gate term computes the predicate
provenance; the group guard is superseded exactly when the predicate
entails the group's existence, and kept as a factor otherwise, matching
the general evaluator's treatment of the pending group factor.
Dependency graph
Duplicate elimination in the rewritten world #
Folding embedded annotations with the value-type addition is the annotation sum.
Dependency graph
The embedding of a row rebuilt from an annotated tuple.
Dependency graph
The data reading of an embedded row is the embedded data reading.
Dependency graph
The provenance column of an embedded row is its finalized annotation.
Dependency graph
The rewritten duplicate elimination: ProvSQL's ε rule – group by
the data columns and ⊕-sum the provenance column – applied to an
arbitrary rewritten subquery, as AggQuery.rewriting does for the
classical fragment.
Equations
- q'.dedupRew = AggQuery.Retag ⋯ (AggQuery.ProvSum (fun (k : Fin n) => Fin.castAdd 1 k) ⋯ (TermG.provIndex (Fin.last n) ⋯) q')
Instances For
Dependency graph
Correctness of the rewritten duplicate elimination, for an arbitrary rewritten subquery.
Dependency graph
Products of token-bearing blocks #
The reassembly columns of a rewritten product: the two operands' data columns copied verbatim (whatever their kinds), and the product of the two provenance columns.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
The reassembly columns have the rewritten kinds of the product.
Dependency graph
The rewritten product, over operands of arbitrary kinds: the two rewritten blocks joined, the data columns reassembled by kind-preserving copies, and the provenance columns multiplied.
Equations
- q₁'.prodRew q₂' = AggQuery.Retag ⋯ (AggQuery.Proj (AggQuery.prodRewCols κ₁ κ₂) (q₁'.Prod q₂'))
Instances For
Dependency graph
Correctness of the rewritten product, for arbitrary operand kinds: conformance of the operands' rows makes the kind-dispatched column copies faithful.
Dependency graph
Difference in the rewritten world #
The general evaluator keeps every row of the left operand, rewriting its
annotation to α ⊖ Σβ – the monus against the ⊕-sum of the matching
rows on the right. ProvSQL's rewriting encodes that missing left outer
join as a union of two branches: rows whose data part is absent from
the right operand keep their annotation, rows whose data part is present
are joined against the per-key sums and subtract them. The rewritten
world's TermG.sub supplies the monus directly, so both branches are
plain projections of joins, and the two semijoin identities of
Provenance.QueryRewriting reduce them to filters of the left operand.
The inl embedding commutes with appending rows.
Dependency graph
The embedding of a rebuilt annotated row is the inl-image of its
composite encoding.
Dependency graph
The data columns of a rewritten block, as an all-regular query: the provenance column dropped.
Equations
- q.diffKeyProj = AggQuery.Retag ⋯ (AggQuery.Proj (fun (j : Fin n) => ProjCol.term (TermG.index (Fin.castAdd 1 j) ⋯)) q)
Instances For
Dependency graph
The output columns of the unmatched branch: the left block copied verbatim, provenance column included.
Equations
- AggQuery.diffColsU j = if hj : ↑j < n then ProjCol.term (TermG.index (Fin.castAdd n (Fin.castAdd 1 ⟨↑j, hj⟩)) ⋯) else ProjCol.provTerm (TermG.provIndex (Fin.castAdd n (Fin.last n)) ⋯)
Instances For
Dependency graph
The output columns of the matched branch: the left block's data columns, and the monus of the two provenance columns.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
Dependency graph
Dependency graph
The unmatched branch: left rows whose data part is among the surviving keys, keeping their annotation.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
The matched branch: left rows joined against the per-key ⊕-sums,
subtracting them.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
The rewritten difference.
Equations
- q₁'.diffRew q₂' = (q₁'.diffBranchU (q₁'.diffKeyProj.Diff q₂'.diffKeyProj).Dedup).Sum (q₁'.diffBranchM q₂'.dedupRew)
Instances For
Dependency graph
The data embedding of tuples is injective.
Dependency graph
The data projection of a rewritten block.
Dependency graph
The surviving keys: the deduplicated data tuples of the left operand absent from the right one.
Dependency graph
The unmatched branch: by the semijoin identity, the left rows whose data part is a surviving key, with their annotation.
Dependency graph
The matched branch: by the keyed-projection semijoin, the left rows whose data part carries a per-key sum, with that sum subtracted.
Dependency graph
Correctness of the rewritten difference, for arbitrary rewritten
operands: the two branches partition the left operand by whether its data
part occurs on the right, and on the unmatched part the subtracted sum is
𝟘.
Dependency graph
The classical rewriting at the uniform kind vector #
The classical rewriting, retagged to ColKind.rewKindsOf κ.
AggQuery.rewriting targets ColKind.rewKinds n – the per-index
if k < n form – which is only pointwise equal to the uniform
Fin.append κ prov the congruences below consume. Retagging once here
(semantically the identity) lets a congruence sit directly above the
classical base rule instead of threading an explicit retag step.
Equations
- q.rewritingOf hq = AggQuery.Retag ⋯ (q.rewriting hq)
Instances For
Dependency graph
The closure #
The compositional closure of the rewriting rules: the three base
rewritings – classical blocks, fused HAVING sites and bare groupings –
composed under union, selection, projection, deduplication, product and
difference, with the kind-retagging of AggQuery.Retag available to
adapt a subderivation's output kinds. The HAVING-site rule
havingPred keeps the group keys and the aggregate tokens as output
columns and admits any predicate with an aggregate atom.
- classical {T : Type} [ValueType T] {K : Type} [CommSemiringWithMonus K] [HasAltLinearOrder K] {n : ℕ} {κ : Fin n → ColKind} (q : AggQuery T n κ) (hq : q.classical) : q.RewritesTo (q.rewritingOf hq)
- gamma {T : Type} [ValueType T] {K : Type} [CommSemiringWithMonus K] [HasAltLinearOrder K] {m n₁ n₂ : ℕ} (is : Tuple (Fin m) n₁) (ts : Tuple (Term T m) n₂) (fs : Tuple (SeqAggFunc T) n₂) (qg : AggQuery T m (ColKind.allReg m)) (hq : qg.classical) : (Gamma is ts fs qg).RewritesTo (gammaRew is ts fs qg hq)
- havingPred {T : Type} [ValueType T] {K : Type} [CommSemiringWithMonus K] [HasAltLinearOrder K] {m n₁ n₂ : ℕ} (is : Tuple (Fin m) n₁) (ts : Tuple (Term T m) n₂) (fs : Tuple (SeqAggFunc T) n₂) (φ : GenPred T (ColKind.gammaKinds n₁ n₂)) (hφ : φ.hasAggAtom = true) (qg : AggQuery T m (ColKind.allReg m)) (hq : qg.classical) : (Sel φ (Gamma is ts fs qg)).RewritesTo (havingPredRew is ts fs φ qg hq)
- retag {T : Type} [ValueType T] {K : Type} [CommSemiringWithMonus K] [HasAltLinearOrder K] {n : ℕ} {κ : Fin n → ColKind} {κ' κ'' : Fin (n + 1) → ColKind} {q : AggQuery T n κ} {q' : AggQuery (T ⊕ K) (n + 1) κ'} (h : ∀ (k : Fin (n + 1)), (κ' k).base = (κ'' k).base) : q.RewritesTo q' → q.RewritesTo (Retag h q')
- sum {T : Type} [ValueType T] {K : Type} [CommSemiringWithMonus K] [HasAltLinearOrder K] {n : ℕ} {κ : Fin n → ColKind} {κ' : Fin (n + 1) → ColKind} {q₁ q₂ : AggQuery T n κ} {q₁' q₂' : AggQuery (T ⊕ K) (n + 1) κ'} : q₁.RewritesTo q₁' → q₂.RewritesTo q₂' → (q₁.Sum q₂).RewritesTo (q₁'.Sum q₂')
- dedup {T : Type} [ValueType T] {K : Type} [CommSemiringWithMonus K] [HasAltLinearOrder K] {n : ℕ} {q : AggQuery T n (ColKind.allReg n)} {q' : AggQuery (T ⊕ K) (n + 1) (ColKind.rewKindsOf (ColKind.allReg n))} : q.RewritesTo q' → q.Dedup.RewritesTo q'.dedupRew
- diff {T : Type} [ValueType T] {K : Type} [CommSemiringWithMonus K] [HasAltLinearOrder K] {n : ℕ} {q₁ q₂ : AggQuery T n (ColKind.allReg n)} {q₁' q₂' : AggQuery (T ⊕ K) (n + 1) (ColKind.rewKindsOf (ColKind.allReg n))} : q₁.RewritesTo q₁' → q₂.RewritesTo q₂' → (q₁.Diff q₂).RewritesTo (q₁'.diffRew q₂')
- sel {T : Type} [ValueType T] {K : Type} [CommSemiringWithMonus K] [HasAltLinearOrder K] {n : ℕ} {κ : Fin n → ColKind} {q : AggQuery T n κ} {q' : AggQuery (T ⊕ K) (n + 1) (ColKind.rewKindsOf κ)} (φ : GenPred T κ) (hφ : φ.hasAggAtom = false) : q.RewritesTo q' → (Sel φ q).RewritesTo (Sel φ.castRew q')
- proj {T : Type} [ValueType T] {K : Type} [CommSemiringWithMonus K] [HasAltLinearOrder K] {n m : ℕ} {κ : Fin n → ColKind} {q : AggQuery T n κ} {q' : AggQuery (T ⊕ K) (n + 1) (ColKind.rewKindsOf κ)} (ps : Tuple (ProjCol T κ) m) : q.RewritesTo q' → (Proj ps q).RewritesTo (Retag ⋯ (Proj (fun (j : Fin (m + 1)) => if hj : ↑j < m then (ps ⟨↑j, hj⟩).castRew else ProjCol.provTerm (TermG.provIndex (Fin.last n) ⋯)) q'))
- prod {T : Type} [ValueType T] {K : Type} [CommSemiringWithMonus K] [HasAltLinearOrder K] {n₁ n₂ : ℕ} {κ₁ : Fin n₁ → ColKind} {κ₂ : Fin n₂ → ColKind} {q₁ : AggQuery T n₁ κ₁} {q₂ : AggQuery T n₂ κ₂} {q₁' : AggQuery (T ⊕ K) (n₁ + 1) (ColKind.rewKindsOf κ₁)} {q₂' : AggQuery (T ⊕ K) (n₂ + 1) (ColKind.rewKindsOf κ₂)} : q₁.RewritesTo q₁' → q₂.RewritesTo q₂' → (q₁.Prod q₂).RewritesTo (q₁'.prodRew q₂')
Instances For
Dependency graph
Whole-query correctness of the compositional rewriting: along the closure, the general evaluator's rows, embedded token-aware into the composite domain, are exactly the rewritten world's evaluation.
Dependency graph
On an all-regular source the correctness specializes to the shape of
the classical and HAVING-site statements: the annotated semantics,
folded into composite tuples and embedded by inl.