diff --git a/CHANGELOG.md b/CHANGELOG.md index 7988c50..1fdb506 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ * Fix [#552](https://github.com/seancorfield/honeysql/issues/552) by changing the assert-on-load behavior into an explicit test in the test suite. * Fix [#551](https://github.com/seancorfield/honeysql/issues/551) by supporting multiple `WINDOW` clauses. * Fix [#549](https://github.com/seancorfield/honeysql/issues/549) by using `:bb` conditionals to support Babashka (and still support Clojure 1.9.0), and add testing against Babashka so it is fully-supported as a target via PR [#550](https://github.com/seancorfield/honeysql/pull/550) [@borkdude](https://github.com/borkdude) - * Address [#532](https://github.com/seancorfield/honeysql/issues/532) by adding support for `EXCLUDE` and `RENAME` and starting to write tests for XTDB compatibility. + * Address [#532](https://github.com/seancorfield/honeysql/issues/532) by adding support for XTDB SQL extensions `ERASE`, `EXCLUDE`, `RECORDS`, and `RENAME` and starting to write tests for XTDB compatibility. * 2.6.1203 -- 2024-10-22 * Fix [#548](https://github.com/seancorfield/honeysql/issues/548) which was a regression introduced in [#526](https://github.com/seancorfield/honeysql/issues/526) (in 2.6.1161). diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index 6de0a5e..a258c0e 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -58,6 +58,7 @@ :raw :nest :with :with-recursive :intersect :union :union-all :except :except-all :table :select :select-distinct :select-distinct-on :select-top :select-distinct-top + :records :distinct :expr :exclude :rename :into :bulk-collect-into :insert-into :replace-into :update :delete :delete-from :erase-from :truncate @@ -1572,6 +1573,21 @@ (let [[sql & params] (format-expr n)] (into [(str (sql-kw k) " " sql)] params))))) [(str (sql-kw k) " " (sql-kw args))])) + +(defn- format-records + "Records can take a single map or a sequence of maps. + + A map will be inherently treated as a lifted parameter. + Records can be inlined [:inline some-map]" + [k args] + (if (sequential? args) + (let [args (if (every? map? args) + (map #(vector :lift %) args) + args) + [sqls params] (format-expr-list args)] + (into [(str (sql-kw k) " " (join ", " sqls))] params)) + (format-records k [args]))) + (defn- check-where "Given a formatter function, performs a pre-flight check that there is a non-empty where clause if at least basic checking is enabled." @@ -1642,6 +1658,7 @@ :select-distinct-on #'format-selects-on :select-top #'format-select-top :select-distinct-top #'format-select-top + :records #'format-records :exclude #'format-selects :rename #'format-selects :distinct (fn [k xs] (format-selects k [[xs]])) diff --git a/src/honey/sql/helpers.cljc b/src/honey/sql/helpers.cljc index f2e2016..214a433 100644 --- a/src/honey/sql/helpers.cljc +++ b/src/honey/sql/helpers.cljc @@ -498,6 +498,11 @@ [& args] (generic :select-distinct-top args)) +(defn records + "Produces RECORDS {...}, {...}, ..." + [& args] + (generic :records args)) + (defn distinct "Like `select-distinct` but produces DISTINCT..." [& args] diff --git a/test/honey/sql/xtdb_test.cljc b/test/honey/sql/xtdb_test.cljc index 8a3db60..db02f1b 100644 --- a/test/honey/sql/xtdb_test.cljc +++ b/test/honey/sql/xtdb_test.cljc @@ -74,3 +74,22 @@ (sql/format [:inline {:_id 1 :name "foo" :info {:contact [{:loc "home" :tel "123"} {:loc "work" :tel "456"}]}}])))) + +(deftest records-statement + (testing "auto-lift maps" + (is (= ["RECORDS ?, ?" {:_id 1 :name "cat"} {:_id 2 :name "dog"}] + (sql/format {:records [{:_id 1 :name "cat"} + {:_id 2 :name "dog"}]})))) + (testing "explicit inline" + (is (= ["RECORDS {_id: 1, name: 'cat'}, {_id: 2, name: 'dog'}"] + (sql/format {:records [[:inline {:_id 1 :name "cat"}] + [:inline {:_id 2 :name "dog"}]]})))) + (testing "insert with records" + (is (= ["INSERT INTO foo RECORDS {_id: 1, name: 'cat'}, {_id: 2, name: 'dog'}"] + (sql/format {:insert-into [:foo + {:records [[:inline {:_id 1 :name "cat"}] + [:inline {:_id 2 :name "dog"}]]}]}))) + (is (= ["INSERT INTO foo RECORDS ?, ?" {:_id 1 :name "cat"} {:_id 2 :name "dog"}] + (sql/format {:insert-into [:foo + {:records [{:_id 1 :name "cat"} + {:_id 2 :name "dog"}]}]})))))