diff --git a/doc/clause-reference.md b/doc/clause-reference.md index 613972d..5933e67 100644 --- a/doc/clause-reference.md +++ b/doc/clause-reference.md @@ -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 diff --git a/doc/getting-started.md b/doc/getting-started.md index 8941caa..e8b986e 100644 --- a/doc/getting-started.md +++ b/doc/getting-started.md @@ -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`. diff --git a/src/honey/sql/helpers.cljc b/src/honey/sql/helpers.cljc index d886ec5..e5ff1f9 100644 --- a/src/honey/sql/helpers.cljc +++ b/src/honey/sql/helpers.cljc @@ -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))) diff --git a/test/honey/sql_test.cljc b/test/honey/sql_test.cljc index 714f775..3ba5a5f 100644 --- a/test/honey/sql_test.cljc +++ b/test/honey/sql_test.cljc @@ -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]]}]]})