Fixes #297 by adding into/bulk-collect-into
This commit is contained in:
parent
cff1e5b43c
commit
16d04a1dfd
5 changed files with 82 additions and 10 deletions
|
|
@ -7,6 +7,7 @@
|
||||||
* Fix #303 by supporting MySQL's `ON DUPLICATE KEY UPDATE`.
|
* Fix #303 by supporting MySQL's `ON DUPLICATE KEY UPDATE`.
|
||||||
* Fix #301 by adding support for `CREATE`/`DROP`/`REFRESH` on `MATERIALIZED VIEW`.
|
* Fix #301 by adding support for `CREATE`/`DROP`/`REFRESH` on `MATERIALIZED VIEW`.
|
||||||
* Add tests to confirm #299 does not affect v2.
|
* Add tests to confirm #299 does not affect v2.
|
||||||
|
* Fix #297 by adding both `SELECT .. INTO ..` and `SELECT .. BULK COLLECT INTO ..`.
|
||||||
* Confirm the whole of the [nilenso/honeysql-postgres](https://github.com/nilenso/honeysql-postgres) is implemented out-of-the-box (#293).
|
* Confirm the whole of the [nilenso/honeysql-postgres](https://github.com/nilenso/honeysql-postgres) is implemented out-of-the-box (#293).
|
||||||
* Fix #292 by adding support for `SELECT TOP` and `OFFSET`/`FETCH`.
|
* Fix #292 by adding support for `SELECT TOP` and `OFFSET`/`FETCH`.
|
||||||
* Fix #284 by adding support for `LATERAL` (as special syntax, with a helper).
|
* Fix #284 by adding support for `LATERAL` (as special syntax, with a helper).
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,28 @@ user=> (sql/format '{select-distinct-on [[a b] c d]
|
||||||
["SELECT DISTINCT ON(a, b) c, d FROM table"]
|
["SELECT DISTINCT ON(a, b) c, d FROM table"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## into
|
||||||
|
|
||||||
|
Used for selecting rows into a new table, optional in another database:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
user=> (sql/format '{select * into newtable from mytable})
|
||||||
|
["SELECT * INTO newtable FROM mytable"]
|
||||||
|
user=> (sql/format '{select * into [newtable otherdb] from mytable})
|
||||||
|
["SELECT * INTO newtable IN otherdb FROM mytable"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## bulk-collect-into
|
||||||
|
|
||||||
|
Used for selecting rows into an array variable, with an optional limit:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
user=> (sql/format '{select * bulk-collect-into arrv from mytable})
|
||||||
|
["SELECT * BULK COLLECT INTO arrv FROM mytable"]
|
||||||
|
user=> (sql/format '{select * bulk-collect-into [arrv 100] from mytable})
|
||||||
|
["SELECT * BULK COLLECT INTO arrv LIMIT ? FROM mytable" 100]
|
||||||
|
```
|
||||||
|
|
||||||
## insert-into
|
## insert-into
|
||||||
|
|
||||||
There are three use cases with `:insert-into`.
|
There are three use cases with `:insert-into`.
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@
|
||||||
;; then SQL clauses in priority order:
|
;; then SQL clauses in priority order:
|
||||||
:nest :with :with-recursive :intersect :union :union-all :except :except-all
|
:nest :with :with-recursive :intersect :union :union-all :except :except-all
|
||||||
:select :select-distinct :select-distinct-on :select-top :select-distinct-top
|
:select :select-distinct :select-distinct-on :select-top :select-distinct-top
|
||||||
|
:into :bulk-collect-into
|
||||||
:insert-into :update :delete :delete-from :truncate
|
:insert-into :update :delete :delete-from :truncate
|
||||||
:columns :set :from :using
|
:columns :set :from :using
|
||||||
:join-by
|
:join-by
|
||||||
|
|
@ -336,6 +337,17 @@
|
||||||
cols)]
|
cols)]
|
||||||
(-> [sql'] (into params) (into params'))))
|
(-> [sql'] (into params) (into params'))))
|
||||||
|
|
||||||
|
(defn- format-select-into [k xs]
|
||||||
|
(let [[v e] (if (sequential? xs) xs [xs])
|
||||||
|
[sql & params] (when e (format-expr e))]
|
||||||
|
(into [(str (sql-kw k) " " (format-entity v)
|
||||||
|
(when sql
|
||||||
|
(str " "
|
||||||
|
(sql-kw (if (= :into k) :in :limit))
|
||||||
|
" "
|
||||||
|
sql)))]
|
||||||
|
params)))
|
||||||
|
|
||||||
(defn- format-with-part [x]
|
(defn- format-with-part [x]
|
||||||
(if (sequential? x)
|
(if (sequential? x)
|
||||||
(let [[sql & params] (format-dsl (second x))]
|
(let [[sql & params] (format-dsl (second x))]
|
||||||
|
|
@ -752,6 +764,8 @@
|
||||||
:select-distinct-on #'format-selects-on
|
:select-distinct-on #'format-selects-on
|
||||||
:select-top #'format-select-top
|
:select-top #'format-select-top
|
||||||
:select-distinct-top #'format-select-top
|
:select-distinct-top #'format-select-top
|
||||||
|
:into #'format-select-into
|
||||||
|
:bulk-collect-into #'format-select-into
|
||||||
:insert-into #'format-insert
|
:insert-into #'format-insert
|
||||||
:update #'format-selector
|
:update #'format-selector
|
||||||
:delete #'format-selects
|
:delete #'format-selects
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,14 @@
|
||||||
|
|
||||||
(ns honey.sql.helpers
|
(ns honey.sql.helpers
|
||||||
"Helper functions for the built-in clauses in honey.sql."
|
"Helper functions for the built-in clauses in honey.sql."
|
||||||
(:refer-clojure :exclude [update set group-by for partition-by])
|
(:refer-clojure :exclude [into update set group-by for partition-by])
|
||||||
(:require [honey.sql]))
|
(:require [clojure.core :as c]
|
||||||
|
[honey.sql]))
|
||||||
|
|
||||||
;; implementation helpers:
|
;; implementation helpers:
|
||||||
|
|
||||||
(defn- default-merge [current args]
|
(defn- default-merge [current args]
|
||||||
(into (vec current) args))
|
(c/into (vec current) args))
|
||||||
|
|
||||||
(defn- and-merge
|
(defn- and-merge
|
||||||
[current arg]
|
[current arg]
|
||||||
|
|
@ -16,11 +17,11 @@
|
||||||
(ident? (first arg))
|
(ident? (first arg))
|
||||||
(#{:and :or} (keyword (first arg))))]
|
(#{:and :or} (keyword (first arg))))]
|
||||||
(cond (= conj' (first current))
|
(cond (= conj' (first current))
|
||||||
(into (vec current) (rest arg))
|
(c/into (vec current) (rest arg))
|
||||||
(seq current)
|
(seq current)
|
||||||
(into [conj' current] (rest arg))
|
(c/into [conj' current] (rest arg))
|
||||||
:else
|
:else
|
||||||
(into [conj'] (rest arg)))
|
(c/into [conj'] (rest arg)))
|
||||||
(cond (#{:and 'and} (first current))
|
(cond (#{:and 'and} (first current))
|
||||||
(conj (vec current) arg)
|
(conj (vec current) arg)
|
||||||
(seq current)
|
(seq current)
|
||||||
|
|
@ -346,6 +347,19 @@
|
||||||
[& args]
|
[& args]
|
||||||
(generic :select-distinct-top args))
|
(generic :select-distinct-top args))
|
||||||
|
|
||||||
|
(defn into
|
||||||
|
"Accepts table name, optionally followed a database name."
|
||||||
|
{:arglist '([table] [table dbname])}
|
||||||
|
[& args]
|
||||||
|
(generic :into args))
|
||||||
|
|
||||||
|
(defn bulk-collect-into
|
||||||
|
"Accepts a variable name, optionally followed by a limit
|
||||||
|
expression."
|
||||||
|
{:arglist '([varname] [varname n])}
|
||||||
|
[& args]
|
||||||
|
(generic :bulk-collect-into args))
|
||||||
|
|
||||||
(defn insert-into
|
(defn insert-into
|
||||||
"Accepts a table name or a table/alias pair. That
|
"Accepts a table name or a table/alias pair. That
|
||||||
can optionally be followed by a collection of
|
can optionally be followed by a collection of
|
||||||
|
|
@ -634,7 +648,7 @@
|
||||||
Produces: (a, ?)
|
Produces: (a, ?)
|
||||||
Parameters: 42"
|
Parameters: 42"
|
||||||
[& args]
|
[& args]
|
||||||
(into [:composite] args))
|
(c/into [:composite] args))
|
||||||
|
|
||||||
(defn lateral
|
(defn lateral
|
||||||
"Accepts a SQL clause or a SQL expression:
|
"Accepts a SQL clause or a SQL expression:
|
||||||
|
|
@ -647,7 +661,7 @@
|
||||||
LATERAL CALC_VALUE(bar)"
|
LATERAL CALC_VALUE(bar)"
|
||||||
{:arglists '([clause-or-expression])}
|
{:arglists '([clause-or-expression])}
|
||||||
[& args]
|
[& args]
|
||||||
(into [:lateral] args))
|
(c/into [:lateral] args))
|
||||||
|
|
||||||
;; to make this easy to use in a select, wrap it so it becomes a function:
|
;; to make this easy to use in a select, wrap it so it becomes a function:
|
||||||
(defn over
|
(defn over
|
||||||
|
|
@ -660,7 +674,7 @@
|
||||||
|
|
||||||
Produces: SELECT id, AVG(salary) OVER ()PARTITION BY department)"
|
Produces: SELECT id, AVG(salary) OVER ()PARTITION BY department)"
|
||||||
[& args]
|
[& args]
|
||||||
[(into [:over] args)])
|
[(c/into [:over] args)])
|
||||||
|
|
||||||
;; this helper is intended to ease the migration from nilenso:
|
;; this helper is intended to ease the migration from nilenso:
|
||||||
(defn upsert
|
(defn upsert
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,10 @@
|
||||||
(:require #?(:clj [clojure.test :refer [deftest is testing]]
|
(:require #?(:clj [clojure.test :refer [deftest is testing]]
|
||||||
:cljs [cljs.test :refer-macros [deftest is testing]])
|
:cljs [cljs.test :refer-macros [deftest is testing]])
|
||||||
[honey.sql :as sql]
|
[honey.sql :as sql]
|
||||||
[honey.sql.helpers
|
[honey.sql.helpers :as h
|
||||||
:refer [add-column add-index alter-table columns create-table create-table-as create-view
|
:refer [add-column add-index alter-table columns create-table create-table-as create-view
|
||||||
create-materialized-view drop-view drop-materialized-view
|
create-materialized-view drop-view drop-materialized-view
|
||||||
|
bulk-collect-into
|
||||||
cross-join do-update-set drop-column drop-index drop-table from full-join
|
cross-join do-update-set drop-column drop-index drop-table from full-join
|
||||||
group-by having insert-into
|
group-by having insert-into
|
||||||
join-by join lateral left-join limit offset on-conflict
|
join-by join lateral left-join limit offset on-conflict
|
||||||
|
|
@ -95,6 +96,26 @@
|
||||||
(from :bar)
|
(from :bar)
|
||||||
(order-by :quux)))))))
|
(order-by :quux)))))))
|
||||||
|
|
||||||
|
(deftest select-into-tests
|
||||||
|
(testing "SELECT INTO"
|
||||||
|
(is (= ["SELECT * INTO foo FROM bar"]
|
||||||
|
(sql/format {:select :* :into :foo :from :bar})))
|
||||||
|
(is (= ["SELECT * INTO foo IN otherdb FROM bar"]
|
||||||
|
(sql/format {:select :* :into [:foo :otherdb] :from :bar})))
|
||||||
|
(is (= ["SELECT * INTO foo FROM bar"]
|
||||||
|
(sql/format (-> (select '*) (h/into 'foo) (from 'bar)))))
|
||||||
|
(is (= ["SELECT * INTO foo IN otherdb FROM bar"]
|
||||||
|
(sql/format (-> (select :*) (h/into :foo :otherdb) (from :bar))))))
|
||||||
|
(testing "SELECT BULK COLLECT INTO"
|
||||||
|
(is (= ["SELECT * BULK COLLECT INTO foo FROM bar"]
|
||||||
|
(sql/format {:select :* :bulk-collect-into :foo :from :bar})))
|
||||||
|
(is (= ["SELECT * BULK COLLECT INTO foo LIMIT ? FROM bar" 100]
|
||||||
|
(sql/format {:select :* :bulk-collect-into [:foo 100] :from :bar})))
|
||||||
|
(is (= ["SELECT * BULK COLLECT INTO foo FROM bar"]
|
||||||
|
(sql/format (-> (select :*) (bulk-collect-into :foo) (from :bar)))))
|
||||||
|
(is (= ["SELECT * BULK COLLECT INTO foo LIMIT ? FROM bar" 100]
|
||||||
|
(sql/format (-> (select :*) (bulk-collect-into :foo 100) (from :bar)))))))
|
||||||
|
|
||||||
(deftest from-expression-tests
|
(deftest from-expression-tests
|
||||||
(testing "FROM can be a function invocation"
|
(testing "FROM can be a function invocation"
|
||||||
(is (= ["SELECT foo, bar FROM F(?) AS x" 1]
|
(is (= ["SELECT foo, bar FROM F(?) AS x" 1]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue