Quelg: All Examples
Table
For example, we consider the products table, which has columns of product ID (pid), name, and price (price), and the orders table, which has columns of order ID (oid), product ID (pid), and quantity (qty). We assume the following simple database schema in our implementation:
val products : unit -> < pid:int; name:string; price:int > list val orders : unit -> < oid:int; pid:int; qty:int > list
The embedding has a bit of noise in the form of extra arguments of type unit, to delay the evaluation of conditional branches and to get around the value restriction.
Q1: Simple Query
From these tables, we consider a query for sales by grouping by oid.
We call this query \(Q_1\), and in Quelg we can write:
\(
Q_1 = \mathcal{G}_{({\rm oid}, \alpha)}({\rm for}~(p \leftarrow {\rm table}(“{\rm products}”)) \\
~~~~~~~~~~~~~~~~~~~~~~~{\rm for}~(o \leftarrow {\rm table}(“{\rm orders}”)) \\
~~~~~~~~~~~~~~~~~~~~~~~{\rm where}~(p.{\rm pid} = o.{\rm pid}) \\
~~~~~~~~~~~~~~~~~~~~~~~{\rm yield}~\{{\rm oid} = o.{\rm oid}, {\rm sales} = p.{\rm price} * o.{\rm qty}\}) \\
~~~~~~~~{\rm Where}~~\alpha = \{({\rm sales}, {\rm SUM}, {\rm sale\_sum})\}
\)
A query written in Quelg like the above query is represented
in our implementation code in OCaml as:
module Q1(S:SYM_SCHEMA) = struct
open S
let table_orders = table ("orders", orders ())
let table_products = table ("products", products ())
let key = ooid
let alpha = [(osale, (string "sum"), (string "sale_sum"))]
let query = foreach (fun () -> table_products) @@ fun p ->
foreach (fun () -> table_orders) @@ fun o ->
where ((pid p) =% (opid o)) @@ fun () ->
yield @@ osales (oid o) ((price p) *% (qty o))
let q1 = group key alpha query @@ fun v key -> q1_res key (sale_sum v)
let observe = observe
end
We can evaluate the above to translate an SQL string by the FixGenSQL interpreter. In our implementation, the queries for grouping are transformed as subqueries for clarity.
let module M = Q1(FixGenSQL) in M.observe (fun () -> M.q1);;
(*
- : < key : int; sale_sum : int > list Schema.FixGenSQL.obs =
"SELECT x.oid AS key, SUM(x.sale) AS sale_sum
FROM (SELECT z.oid AS oid, y.price * z.qty AS sale
FROM products AS y, orders AS z
WHERE true AND y.pid = z.pid) AS x
WHERE true
GROUP BY x.oid"
*)
Q2: Query with Nested Control Structures
We consider a query with Nested Control Structures.
We call this query \(Q_2\), and in Quelg we can write:
\(
Q'_2 = \mathcal{G}_{({\rm oid}, \alpha)}({\rm for}~(p \leftarrow {\rm table}(“{\rm products}”)) \\
~~~~~~~~~~~~~~~~~~~~~~~{\rm for}~(o \leftarrow {\rm table}(“{\rm orders}”)) \\
~~~~~~~~~~~~~~~~~~~~~~~{\rm where}~(p.{\rm pid} = o.{\rm pid}) \\
~~~~~~~~~~~~~~~~~~~~~~~{\rm yield}~\{{\rm oid} = o.{\rm oid}, {\rm sales} = p.{\rm price} * o.{\rm qty}\}) \\
~~~~~~~~{\rm Where}~~\alpha = \{({\rm sales}, {\rm SUM}, {\rm sale\_sum}), ({\rm qty}, {\rm SUM}, {\rm qty\_sum})\}
\)
\(
Q_2 = {\rm for}~(q \leftarrow Q'_2) \\
~~~~~~~~~~{\rm yield}~\{{\rm oid} = q.{\rm oid}, {\rm average} = q.{\rm sale\_sum}/q.{\rm qty\_sum}\}
\)
A query written in Quelg like the above query is represented
in our implementation code in OCaml as:
module Q2'(S:SYM_SCHEMA) = struct
open S
let table_orders = table ("orders", orders ())
let table_products = table ("products", products ())
let key = qoid
let alpha = [(qsale, (string "sum"), (string "sale_sum"));
(qqty, (string "sum"), (string "qty_sum"))]
let q2' = group key alpha (foreach (fun () -> table_products) @@ fun p ->
foreach (fun () -> table_orders) @@ fun o ->
where ((pid p) =% (opid o)) @@ fun () ->
yield @@ qsales (oid o) ((price p) *% (qty o)) (qty o))
(fun v key -> q2'_res key (sale_sum v) (qty_sum v))
end
module Q2(S:SYM_SCHEMA) = struct
open S
module M = Q2'(S)
let q2 = foreach (fun () -> M.q2') @@ fun q ->
yield @@ q2_res (q2_key q) ((q2_sale_sum q) /% (q2_qty_sum q))
let observe = observe
end
We can evaluate the above to translate an SQL string by the FixGenSQL interpreter as follows.
let module M = Q2(FixGenSQL) in M.observe (fun () -> M.q2);;
(*
- : < average : int; key : int > list Schema.FixGenSQL.obs =
"SELECT x.key AS key, x.sale_sum / x.qty_sum AS average
FROM (SELECT x.oid AS key, SUM(x.sale) AS sale_sum, SUM(x.qty) AS qty_sum
FROM (SELECT z.oid AS oid, y.price * z.qty AS sale, z.qty AS qty
FROM products AS y, orders AS z WHERE true AND y.pid = z.pid) AS x
WHERE true
GROUP BY x.oid) AS x
WHERE true"
*)
Q3: Compose
We consider a query that combines two queries.
We prepare two queries \(Q'_3\) and \(Q''_3\),
and we call the query composed of this queries \(Q_3\),
and in Quelg we can write:
\(
Q'_3 = \lambda oid.~{\rm for}~(o \leftarrow {\rm table}(“{\rm orders}”)) \\
~~~~~~~~~~~~~~~~~~~~~{\rm where}~(o.{\rm oid} = oid) \\
~~~~~~~~~~~~~~~~~~~~~{\rm yield}~o
\)
\(
Q''_3 = \mathcal{G}_{({\rm oid}, \alpha)}({\rm for}~(p \leftarrow {\rm table}(“{\rm products}”)) \\
~~~~~~~~~~~~~~~~~~~~~~~{\rm where}~(p.{\rm pid} = o.{\rm pid}) \\
~~~~~~~~~~~~~~~~~~~~~~~{\rm yield}~\{{\rm oid} = o.{\rm oid}, {\rm sales} = p.{\rm price} * o.{\rm qty}\}) \\
\)
\(
Q_3 = \lambda x.~\mathcal{G}_{({\rm oid}, \alpha)}({\rm for}~(y \leftarrow Q'_3~x)~Q''_3~y) \\
~~~~~~~~{\rm Where}~~\alpha = \{({\rm sales}, {\rm SUM}, {\rm sale\_sum})\}
\)
A query written in Quelg like the above query is represented
in our implementation code in OCaml as:
module Q3'(S:SYM_SCHEMA) = struct
open S
let table_orders = table ("orders", orders ())
let q3' = lam (fun xoid ->
foreach (fun () -> table_orders) @@ fun o ->
where ((oid o) >% xoid) @@ fun () ->
yield o)
end
module Q3''(S:SYM_SCHEMA) = struct
open S
let table_products = table ("products", products ())
let q3'' = lam (fun o ->
foreach (fun () -> table_products) @@ fun p ->
where ((pid p) =% (opid o)) @@ fun () ->
yield @@ osales (oid o) ((price p) *% (qty o)))
end
module Q3(S:SYM_SCHEMA) = struct
open S
module M1 = Q3'(S)
module M2 = Q3''(S)
let key = ooid
let alpha = [(osale, (string "sum"), (string "sale_sum"))]
let q3' = lam (fun x -> group key alpha
(foreach (fun () -> app M1.q3' x) @@ fun y ->
app M2.q3'' y) @@ fun v key -> q1_res key (sale_sum v))
let q3 = app q3' (int 1)
let observe = observe
end
We can evaluate the above to translate an SQL string by the FixGenSQL interpreter as follows.
let module M = Q3(FixGenSQL) in M.observe (fun () -> M.q3);;
(*
- : < key : int; sale_sum : int > list Schema.FixGenSQL.obs =
"SELECT x.oid AS key, SUM(x.sale) AS sale_sum
FROM (SELECT y.oid AS oid, z.price * y.qty AS sale
FROM orders AS y, products AS z
WHERE true AND y.oid > 1 AND z.pid = y.pid) AS x
WHERE true
GROUP BY x.oid"
*)
Q4: Lambda abstraction
We consider a query with Lambda abstraction.
We call this query \(Q_4\), and in Quelg we can write:
\(
Q'_4 = \lambda oid.~\mathcal{G}_{({\rm oid}, \alpha)}~({\rm for}~(o \leftarrow {\rm table}(“{\rm orders}”)) \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{\rm where}~(o.{\rm oid} = oid) \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{\rm yield}~o) \\
~~~~~~~~{\rm Where}~~\alpha = \{({\rm qty}, {\rm AVG}, {\rm qty\_avg})\}
\)
\(
Q_4 = Q'_4~~2
\)
A query written in Quelg like the above query is represented
in our implementation code in OCaml as:
module Q4'(S:SYM_SCHEMA) = struct
open S
let table_orders = table ("orders", orders ())
let key = oid
let alpha = [(qty, (string "avg"), (string "qty_avg"))]
let q4' = lam (fun xoid ->
group key alpha
(foreach (fun () -> table_orders) @@ fun o ->
where ((oid o) =% xoid) @@ fun () ->
yield o) @@ fun v key -> q4_res key (qty_avg v))
end
module Q4(S:SYM_SCHEMA) = struct
open S
module M = Q4'(S)
let q4 = app M.q4' (int 2)
let observe = observe
end
We can evaluate the above to translate an SQL string by the FixGenSQL interpreter as follows.
let module M = Q4(FixGenSQL) in M.observe (fun () -> M.q4);;
(*
- : < key : int; qty_avg : int > list Schema.FixGenSQL.obs =
"SELECT x.oid AS key, AVG(x.qty) AS qty_avg
FROM (SELECT y.*
FROM orders AS y
WHERE true AND y.oid = 2) AS x
WHERE true
GROUP BY x.oid"
*)
Q5: Predicate
We consider a query that is taken a predicate as the argument
and is grouped the results.
We call this query \(Q_5\), and in Quelg we can write:
\(
Q'_5 = \lambda p.~\mathcal{G}_{({\rm oid}, \alpha)}({\rm for}~(o \leftarrow {\rm table}(“{\rm orders}”)) \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{\rm where}~p(o.{\rm qty}) \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{\rm yield}~o) \\
~~~~~~~~{\rm Where}~~\alpha = \{({\rm qty}, {\rm COUNT}, {\rm qty\_count})\}
\)
\(
Q_5 = Q'_5~(\lambda x.~x~>~2)
\)
A query written in Quelg like the above query is represented
in our implementation code in OCaml as:
module Q5'(S:SYM_SCHEMA) = struct
open S
let table_orders = table ("orders", orders ())
let key = oid
let alpha = [(qty, (string "count"), (string "qty_count"))]
let q5' = fun p ->
group key alpha
(foreach (fun () -> table_orders) @@ fun o ->
where (p (qty o)) @@ fun () ->
yield o) @@ fun v key -> q5_res key (qty_count v)
end
module Q5(S:SYM_SCHEMA) = struct
open S
module M = Q5'(S)
let q5 = M.q5' (fun x -> x >% (int 2))
let observe = observe
end
We can evaluate the above to translate an SQL string by the FixGenSQL interpreter as follows.
let module M = Q5(FixGenSQL) in M.observe (fun () -> M.q5);;
(*
- : < key : int; qty_count : int > list Schema.FixGenSQL.obs =
"SELECT x.oid AS key, COUNT(x.qty) AS qty_count
FROM (SELECT y.*
FROM orders AS y
WHERE true AND y.qty > 2) AS x
WHERE true
GROUP BY x.oid"
*)
Q6: Predicate + Nested Control Structures
We consider a query with predicate and Nested Control Structures.
We call this query \(Q_6\), and in Quelg we can write:
\(
Q'_6 = \lambda p.~{\rm for}~(g \leftarrow Q_1) \\
~~~~~~~~~~~~~~~~~{\rm where}~p(g.{\rm sale\_sum}) \\
~~~~~~~~~~~~~~~~~{\rm yield}~g
\)
\(
Q_6 = Q'_6~(\lambda x.~x~>~10000)
\)
A query written in Quelg like the above query is represented
in our implementation code in OCaml as:
module Q6'(S:SYM_SCHEMA) = struct
open S
module M = Q1(S)
let q6' = fun p ->
foreach (fun () -> M.q1) @@ fun g ->
where (p (q1_sale_sum g)) @@ fun () ->
yield g
end
module Q6(S:SYM_SCHEMA) = struct
open S
module M = Q6'(S)
let q6 = M.q6' (fun x -> x >% (int 10000))
let observe = observe
end
We can evaluate the above to translate an SQL string by the FixGenSQL interpreter as follows.
let module M = Q6(FixGenSQL) in M.observe (fun () -> M.q6);;
(*
- : < key : int; sale_sum : int > list Schema.FixGenSQL.obs =
"SELECT x.*
FROM (SELECT x.oid AS key, SUM(x.sale) AS sale_sum
FROM (SELECT z.oid AS oid, y.price * z.qty AS sale
FROM products AS y, orders AS z
WHERE true AND y.pid = z.pid) AS x
WHERE true GROUP BY x.oid) AS x
WHERE true AND x.sale_sum > 10000"
*)
Q7: Double Nested Control Structures
We consider a query with double Nested Control Structures, that is
generated four subqueries.
We call this query \(Q_7\), and in Quelg we can write:
\(
Q'_7 = \mathcal{G}_{({\rm qty\_count}, \alpha)}({\rm for}~(x \leftarrow Q_5) \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{\rm yield}~x) \\
~~~~~~~~{\rm Where}~~\alpha = \{({\rm qty\_count}, {\rm COUNT}, {\rm count\_count})\}
\)
\(
Q_7 = {\rm for}~(g \leftarrow Q'_7) \\
~~~~~~~~~~{\rm where}~(g.{\rm count\_count}~>~1) \\
~~~~~~~~~~{\rm yield}~g
\)
A query written in Quelg like the above query is represented
in our implementation code in OCaml as:
module Q7'(S:SYM_SCHEMA) = struct
open S
module M = Q5(S)
let key = q5_qty_count
let alpha = [(q5_qty_count, (string "count"), (string "count_count"))]
let query = foreach (fun () -> M.q5) @@ fun x -> yield x
let q7' = group key alpha query @@ fun v key -> q7_res key (count_count v)
end
module Q7(S:SYM_SCHEMA) = struct
open S
module M = Q7'(S)
let q7 = foreach (fun () -> M.q7') @@ fun g ->
where ((q7_count_count g) >% (int 1)) @@ fun () ->
yield g
let observe = observe
end
We can evaluate the above to translate an SQL string by the FixGenSQL interpreter as follows.
let module M = Q7(FixGenSQL) in M.observe (fun () -> M.q7);;
(*
- : < key : int; count_count : int > list Schema.FixGenSQL.obs =
"SELECT x.*
FROM (SELECT x.qty_count AS key, COUNT(x.qty_count) AS count_count
FROM (SELECT y.*
FROM (SELECT y.oid AS key, COUNT(y.qty) AS qty_count
FROM (SELECT z.*
FROM orders AS z
WHERE true AND z.qty > 2) AS y
WHERE true
GROUP BY y.oid) AS y
WHERE true) AS x
WHERE true
GROUP BY x.qty_count) AS x
WHERE true AND x.count_count > 1"
*)
Q8: Double Nested Control Structures + Normalize
We consider a query with double Nested Control Structures
and using existing normalization rules.
We call this query \(Q_8\), and in Quelg we can write:
\(
Q'_8 = \mathcal{G}_{({\rm sale\_sum}, \alpha)}({\rm for}~(x \leftarrow Q_3) \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{\rm yield}~x) \\
~~~~~~~~{\rm Where}~~\alpha = \{({\rm sale\_sum}, {\rm COUNT}, {\rm sale\_count})\}
\)
\(
Q_8 = {\rm for}~(g \leftarrow Q'_8) \\
~~~~~~~~~~{\rm where}~(g.{\rm sale\_count}~=~1) \\
~~~~~~~~~~{\rm yield}~g
\)
A query written in Quelg like the above query is represented
in our implementation code in OCaml as:
module Q8'(S:SYM_SCHEMA) = struct
open S
module M = Q3(S)
let key = q3_sale_sum
let alpha = [(q3_sale_sum, (string "count"), (string "sale_count"))]
let query = foreach (fun () -> M.q3) @@ fun x -> yield x
let q8' = group key alpha query @@ fun v key -> q8_res key (sale_count v)
end
module Q8(S:SYM_SCHEMA) = struct
open S
module M = Q8'(S)
let q8 = foreach (fun () -> M.q8') @@ fun g ->
where ((q8_sale_count g) =% (int 1)) @@ fun () ->
yield g
let observe = observe
end
We can evaluate the above to translate an SQL string by the FixGenSQL interpreter as follows.
let module M = Q8(FixGenSQL) in M.observe (fun () -> M.q8);;
(*
- : < key : int; sale_count : int > list Schema.FixGenSQL.obs =
"SELECT x.*
FROM (SELECT x.sale_sum AS key, COUNT(x.sale_sum) AS sale_count
FROM (SELECT y.*
FROM (SELECT y.oid AS key, SUM(y.sale) AS sale_sum
FROM (SELECT z.oid AS oid, u.price * z.qty AS sale
FROM orders AS z, products AS u
WHERE true AND z.oid > 1 AND u.pid = z.pid) AS y
WHERE true
GROUP BY y.oid) AS y
WHERE true) AS x
WHERE true
GROUP BY x.sale_sum) AS x
WHERE true AND x.sale_count = 1"
*)
Q9: Nested Data Structures
We consider a query with Nested Data Structures.
We call this query \(Q_9\), and in Quelg we can write:
\(
nestedData = {\rm for}~(c \leftarrow {\rm table}(“{\rm classes}”)) \\
~~~~~~~~~~~~~~~~~~~~~~~~~~{\rm yield}~\{{\rm class} = c.{\rm class}, {\rm students} = \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\mathcal{G}_{({\rm name}, \alpha)}({\rm for}~(s \leftarrow {\rm table}(“{\rm students}”)) \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{\rm for}~(t \leftarrow {\rm table}(“{\rm tests}”)) \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{\rm where}~(s.{\rm name} = t.{\rm name}) \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{\rm yield}~\{{\rm name} = t.{\rm name}, {\rm score} = t.{\rm score}\})\} \\
~~~~~~~~~~~~~~~~{\rm Where}~~\alpha = \{({\rm score}, {\rm AVG}, {\rm score\_avg})\}
\)
\(
Q_9 = \mathcal{G}_{({\rm class}, \alpha)}({\rm for}~(x \leftarrow nestedData) \\
~~~~~~~~~~~~~~~~~~~~~~~~~{\rm for}~(y \leftarrow x.{\rm students}) \\
~~~~~~~~~~~~~~~~~~~~~~~~~{\rm yield}~\{{\rm class} = x.{\rm class}, {\rm score} = y.{\rm score}\}) \\
~~~~~~~~~~{\rm Where}~~\alpha = \{({\rm score}, {\rm AVG}, {\rm score\_avg})\}
\)
A query written in Quelg like the above query is represented
in our implementation code in OCaml as:
module NestedData(S:SYM_SCHEMA) = struct
open S
let table_classes = table ("classes", table_classes ())
let table_students = table ("students", table_students ())
let table_tests = table ("tests", table_tests ())
let key = test_name
let alpha = [(test_score, (string "avg"), (string "score_avg"))]
let nestedData = foreach (fun () -> table_classes) @@ fun c ->
yield @@ nested_res (cclass c) (group key alpha (foreach (fun () -> table_students) @@ fun s ->
foreach (fun () -> table_tests) @@ fun t ->
where ((stname s) =@ (tname t)) @@ fun () ->
yield @@ test (tname t) (tscore t)) (fun v key -> student_res key (score_avg v)))
end
module Q9(S:SYM_SCHEMA) = struct
open S
module M = NestedData(S)
let key = scores_class
let alpha = [(scores_score, (string "avg"), (string "score_avg"))]
let q9 = group key alpha (foreach (fun () -> M.nestedData) @@ fun x ->
foreach (fun () -> (nested_student x)) @@ fun y ->
yield @@ scores (nested_class x) (q9_score_avg y))
(fun v key -> q9_res key (score_avg v))
let observe = observe
end
We can evaluate the above to translate an SQL string by the FixGenSQL interpreter as follows.
let module M = Q8(FixGenSQL) in M.observe (fun () -> M.q8);;
(*
- : < key : int; score_avg : int > list Schema.FixGenSQL.obs =
"SELECT x.class AS key, AVG(x.score) AS score_avg
FROM (SELECT y.class AS class, z.score_avg AS score
FROM classes AS y, (SELECT z.name AS key, AVG(z.score) AS score_avg
FROM (SELECT v.name AS name, v.score AS score
FROM students AS u, tests AS v
WHERE true AND u.name = v.name) AS z
WHERE true
GROUP BY z.name) AS z
WHERE true) AS x
WHERE true
GROUP BY x.class"
*)