Fixes #298 by restoring multi-CTE capability

Reverts code changes that stripped the sequence support.
Updates the docs to clarify how sequence support works.
This commit is contained in:
Sean Corfield 2021-02-10 12:04:53 -08:00
parent e02b5b5c82
commit 0a83601c3c
4 changed files with 54 additions and 3 deletions

View file

@ -12,7 +12,53 @@ Except as noted, these clauses apply to all the SQL
dialects that HoneySQL supports.
## nest
This is pseudo-syntax that lets you wrap a substatement
in an extra level of parentheses. It should rarely be
needed and it is mostly present to provide the same
functionality for clauses that `[:nest ..]` provides
for expressions.
## with, with-recursive
These provide CTE support for SQL Server. The argument to
`:with` (or `:with-recursive`) is a sequences of pairs, each of
a result set name (or description) and a basic SQL statement.
The result set can either be a SQL entity (a simple name)
or a pair of a SQL entity and a set of column names.
```clojure
user=> (sql/format '{with ((stuff {select (:*) from (foo)}),
(nonsense {select (:*) from (bar)}))
select (foo.id,bar.name)
from (stuff, nonsense)
where (= status 0)})
["WITH stuff AS (SELECT * FROM foo), nonsense AS (SELECT * FROM bar) SELECT foo.id, bar.name FROM stuff, nonsense WHERE status = ?" 0]
```
You can specify a list of columns for the CTE like this:
```clojure
user=> (sql/format {:with [[[:stuff {:columns [:id :name]}]
{:select [:*] :from [:foo]}]]
:select [:id :name]
:from [:stuff]
:where [:= :status 0]})
["WITH stuff (id, name) AS (SELECT * FROM foo) SELECT id, name FROM stuff WHERE status = ?" 0]
```
You can use a `VALUES` clause in the CTE:
```clojure
user=> (sql/format {:with [[[:stuff {:columns [:id :name]}]
{:values [[1 "Sean"] [2 "Jay"]]}]]
:select [:id :name]
:from [:stuff]})
["WITH stuff (id, name) AS (VALUES (?, ?), (?, ?)) SELECT id, name FROM stuff" 1 "Sean" 2 "Jay"]
```
`:with-recursive` follows the same rules as `:with` and produces `WITH RECURSIVE` instead of just `WITH`.
## intersect, union, union-all, except, except-all
These all expect a sequence of SQL clauses, those clauses

View file

@ -47,7 +47,7 @@ values identified in the SQL expressions:
;;=> ["SELECT * FROM table WHERE id = ?" 1]
```
Any values found in the data structure, that are not keywords
By default, any values found in the data structure, that are not keywords
or symbols, are treated as positional parameters and replaced
by `?` in the SQL string and lifted out into the vector that
is returned from `format`.

View file

@ -44,8 +44,8 @@
(assoc {} k data)))
(defn nest [& args] (generic :nest args))
(defn with [& args] (generic-1 :with args))
(defn with-recursive [& args] (generic-1 :with-recursive args))
(defn with [& args] (generic :with args))
(defn with-recursive [& args] (generic :with-recursive args))
;; these five need to supply an empty hash map since they wrap
;; all of their arguments:
(defn intersect [& args] (generic :intersect (cons {} args)))

View file

@ -99,6 +99,11 @@
(deftest test-cte
(is (= (format {:with [[:query {:select [:foo] :from [:bar]}]]})
["WITH query AS (SELECT foo FROM bar)"]))
(is (= (format {:with [[:query1 {:select [:foo] :from [:bar]}]
[:query2 {:select [:bar] :from [:quux]}]]
:select [:query1.id :query2.name]
:from [:query1 :query2]})
["WITH query1 AS (SELECT foo FROM bar), query2 AS (SELECT bar FROM quux) SELECT query1.id, query2.name FROM query1, query2"]))
(is (= (format {:with-recursive [[:query {:select [:foo] :from [:bar]}]]})
["WITH RECURSIVE query AS (SELECT foo FROM bar)"]))
(is (= (format {:with [[[:static {:columns [:a :b :c]}] {:values [[1 2 3] [4 5]]}]]})