fixes #558 by implementing patch-into
also fix records helper and document a some more xtdb support Signed-off-by: Sean Corfield <sean@corfield.org>
This commit is contained in:
parent
e0356bc9c5
commit
e4762a1a70
6 changed files with 66 additions and 15 deletions
|
|
@ -1,6 +1,7 @@
|
|||
# Changes
|
||||
|
||||
* 2.6.next in progress
|
||||
* Address [#558](https://github.com/seancorfield/honeysql/issues/558) by adding `:patch-into` (and `patch-into` helper) for XTDB (but in core).
|
||||
* Address [#555](https://github.com/seancorfield/honeysql/issues/555) by supporting `SETTING` clause for XTDB.
|
||||
* Replace `assert` calls with proper validation, throwing `ex-info` on failure (like other existing validation in HoneySQL).
|
||||
* Experimental `:xtdb` dialect removed (since XTDB no longer supports qualified column names).
|
||||
|
|
|
|||
|
|
@ -670,9 +670,9 @@ user=> (sql/format '{select * bulk-collect-into [arrv 100] from mytable})
|
|||
["SELECT * BULK COLLECT INTO arrv LIMIT ? FROM mytable" 100]
|
||||
```
|
||||
|
||||
## insert-into, replace-into
|
||||
## insert-into, replace-into, patch-into
|
||||
|
||||
There are three use cases with `:insert-into`.
|
||||
There are three use cases with `:insert-into` etc.
|
||||
|
||||
The first case takes just a table specifier (either a
|
||||
table name or a table/alias pair),
|
||||
|
|
@ -690,6 +690,10 @@ For the first and second cases, you'll use the `:values` clause
|
|||
to specify rows of values to insert. See [**values**](#values) below
|
||||
for more detail on the `:values` clause.
|
||||
|
||||
`:patch-into` is only supported by XTDB but is
|
||||
part of HoneySQL's "core" dialect anyway. It produces a `PATCH INTO`
|
||||
statement but otherwise has identical syntax to `:insert-into`.
|
||||
|
||||
`:replace-into` is only supported by MySQL and SQLite but is
|
||||
part of HoneySQL's "core" dialect anyway. It produces a `REPLACE INTO`
|
||||
statement but otherwise has identical syntax to `:insert-into`.
|
||||
|
|
@ -794,7 +798,7 @@ You can also `UPDATE .. FROM (VALUES ..) ..` where you might also need `:composi
|
|||
["UPDATE table SET a = v.a FROM (VALUES (?, ?, ?), (?, ?, ?)) AS v (a, b, c) WHERE (x = v.b) AND (y > v.c)" 1 2 3 4 5 6]
|
||||
```
|
||||
|
||||
## delete, delete-from
|
||||
## delete, delete-from, erase-from
|
||||
|
||||
`:delete-from` is the simple use case here, accepting just a
|
||||
SQL entity (table name). `:delete` allows for deleting from
|
||||
|
|
@ -811,6 +815,10 @@ user=> (sql/format {:delete [:order :item]
|
|||
["DELETE order, item FROM order INNER JOIN item ON order.item_id = item.id WHERE item.id = ?" 42]
|
||||
```
|
||||
|
||||
`:erase-from` is only supported by XTDB and produces an `ERASE FROM`
|
||||
statement but otherwise has identical syntax to `:delete-from`. It
|
||||
is a "hard" delete as opposed to a temporal delete.
|
||||
|
||||
## truncate
|
||||
|
||||
`:truncate` accepts a simple SQL entity (table name)
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@
|
|||
:records
|
||||
:distinct :expr :exclude :rename
|
||||
:into :bulk-collect-into
|
||||
:insert-into :replace-into :update :delete :delete-from :erase-from :truncate
|
||||
:insert-into :patch-into :replace-into :update :delete :delete-from :erase-from :truncate
|
||||
:columns :set :from :using
|
||||
:join-by
|
||||
:join :left-join :right-join :inner-join :outer-join :full-join
|
||||
|
|
@ -792,6 +792,7 @@
|
|||
(defn- format-columns [k xs]
|
||||
(if (and (= :columns k)
|
||||
(or (contains-clause? :insert-into)
|
||||
(contains-clause? :patch-into)
|
||||
(contains-clause? :replace-into)))
|
||||
[]
|
||||
(let [[sqls params] (format-expr-list xs {:drop-ns true})]
|
||||
|
|
@ -1213,6 +1214,7 @@
|
|||
;; [{:a 1 :b 2 :c 3}]
|
||||
(let [[cols cols-sql]
|
||||
(columns-from-values xs (or (contains-clause? :insert-into)
|
||||
(contains-clause? :patch-into)
|
||||
(contains-clause? :replace-into)
|
||||
(contains-clause? :columns)))
|
||||
[sqls params]
|
||||
|
|
@ -1684,6 +1686,7 @@
|
|||
:into #'format-select-into
|
||||
:bulk-collect-into #'format-select-into
|
||||
:insert-into #'format-insert
|
||||
:patch-into #'format-insert
|
||||
:replace-into #'format-insert
|
||||
:update (check-where #'format-selector)
|
||||
:delete (check-where #'format-selects)
|
||||
|
|
|
|||
|
|
@ -504,9 +504,10 @@
|
|||
(generic :select-distinct-top args))
|
||||
|
||||
(defn records
|
||||
"Produces RECORDS {...}, {...}, ..."
|
||||
"Produces RECORDS {...}, {...}, ...
|
||||
Like `values` so it accepts a collection of maps."
|
||||
[& args]
|
||||
(generic :records args))
|
||||
(generic-1 :records args))
|
||||
|
||||
(defn distinct
|
||||
"Like `select-distinct` but produces DISTINCT..."
|
||||
|
|
@ -541,6 +542,14 @@
|
|||
[& args]
|
||||
(generic :bulk-collect-into args))
|
||||
|
||||
(defn- stuff-into [k args]
|
||||
(let [[data & args :as args']
|
||||
(if (map? (first args)) args (cons {} args))
|
||||
[table cols statement] args]
|
||||
(if (and (sequential? cols) (map? statement))
|
||||
(generic k [data [table cols] statement])
|
||||
(generic k args'))))
|
||||
|
||||
(defn insert-into
|
||||
"Accepts a table name or a table/alias pair. That
|
||||
can optionally be followed by a collection of
|
||||
|
|
@ -556,12 +565,20 @@
|
|||
(-> (select :*) (from :other)))"
|
||||
{:arglists '([table] [table cols] [table statement] [table cols statement])}
|
||||
[& args]
|
||||
(let [[data & args :as args']
|
||||
(if (map? (first args)) args (cons {} args))
|
||||
[table cols statement] args]
|
||||
(if (and (sequential? cols) (map? statement))
|
||||
(generic :insert-into [data [table cols] statement])
|
||||
(generic :insert-into args'))))
|
||||
(stuff-into :insert-into args))
|
||||
|
||||
(defn patch-into
|
||||
"Accepts a table name or a table/alias pair. That
|
||||
can optionally be followed by a collection of
|
||||
column names. That can optionally be followed by
|
||||
a (select) statement clause.
|
||||
|
||||
The arguments are identical to insert-into.
|
||||
The PATCH INTO statement is only supported by
|
||||
XTDB."
|
||||
{:arglists '([table] [table cols] [table statement] [table cols statement])}
|
||||
[& args]
|
||||
(stuff-into :patch-into args))
|
||||
|
||||
(defn replace-into
|
||||
"Accepts a table name or a table/alias pair. That
|
||||
|
|
@ -574,7 +591,7 @@
|
|||
MySQL and SQLite."
|
||||
{:arglists '([table] [table cols] [table statement] [table cols statement])}
|
||||
[& args]
|
||||
(apply insert-into args))
|
||||
(stuff-into :replace-into args))
|
||||
|
||||
(defn update
|
||||
"Accepts either a table name or a table/alias pair.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
bulk-collect-into
|
||||
cross-join do-update-set drop-column drop-index drop-table
|
||||
filter from full-join
|
||||
group-by having insert-into
|
||||
group-by having insert-into replace-into
|
||||
join-by join lateral left-join limit offset on-conflict
|
||||
on-duplicate-key-update
|
||||
order-by over partition-by refresh-materialized-view
|
||||
|
|
@ -835,7 +835,10 @@
|
|||
["INSERT INTO transport (id, name) SELECT * FROM cars"]))
|
||||
;; three arguments with an alias and columns:
|
||||
(is (= (sql/format (insert-into '(transport t) '(id, name) '{select (*) from (cars)}))
|
||||
["INSERT INTO transport AS t (id, name) SELECT * FROM cars"])))
|
||||
["INSERT INTO transport AS t (id, name) SELECT * FROM cars"]))
|
||||
;; and again with replace-into:
|
||||
(is (= (sql/format (replace-into '(transport t) '(id, name) '{select (*) from (cars)}))
|
||||
["REPLACE INTO transport AS t (id, name) SELECT * FROM cars"])))
|
||||
|
||||
;; these tests are adapted from Cam Saul's PR #283
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,10 @@
|
|||
(is (= ["ERASE FROM foo WHERE foo.id = ?" 42]
|
||||
(-> {:erase-from :foo
|
||||
:where [:= :foo.id 42]}
|
||||
(sql/format))))
|
||||
(is (= ["ERASE FROM foo WHERE foo.id = ?" 42]
|
||||
(-> (h/erase-from :foo)
|
||||
(h/where [:= :foo.id 42])
|
||||
(sql/format)))))
|
||||
|
||||
(deftest inline-record-body
|
||||
|
|
@ -80,6 +84,21 @@
|
|||
{:records [{:_id 1 :name "cat"}
|
||||
{:_id 2 :name "dog"}]}]})))))
|
||||
|
||||
(deftest patch-statement
|
||||
(testing "patch with records"
|
||||
(is (= ["PATCH INTO foo RECORDS {_id: 1, name: 'cat'}, {_id: 2, name: 'dog'}"]
|
||||
(sql/format {:patch-into [:foo
|
||||
{:records [[:inline {:_id 1 :name "cat"}]
|
||||
[:inline {:_id 2 :name "dog"}]]}]})))
|
||||
(is (= ["PATCH INTO foo RECORDS ?, ?" {:_id 1 :name "cat"} {:_id 2 :name "dog"}]
|
||||
(sql/format {:patch-into [:foo
|
||||
{:records [{:_id 1 :name "cat"}
|
||||
{:_id 2 :name "dog"}]}]})))
|
||||
(is (= ["PATCH INTO foo RECORDS ?, ?" {:_id 1 :name "cat"} {:_id 2 :name "dog"}]
|
||||
(sql/format (h/patch-into :foo
|
||||
(h/records [{:_id 1 :name "cat"}
|
||||
{:_id 2 :name "dog"}])))))))
|
||||
|
||||
(deftest object-record-expr
|
||||
(testing "object literal"
|
||||
(is (= ["SELECT OBJECT (_id: 1, name: 'foo')"]
|
||||
|
|
|
|||
Loading…
Reference in a new issue