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" *)