diff --git a/CHANGELOG.md b/CHANGELOG.md index 797aadb..09db2f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * 2.6.next in progress * 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. * 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 ab12166..51fa0ba 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -58,7 +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 - :distinct :expr + :distinct :expr :exclude :rename :into :bulk-collect-into :insert-into :replace-into :update :delete :delete-from :truncate :columns :set :from :using @@ -775,28 +775,34 @@ (let [[sqls params] (format-expr-list xs {:drop-ns true})] (into [(str "(" (join ", " sqls) ")")] params)))) -(defn- format-selects-common [prefix as xs] - (let [qualifier (format-meta xs) - prefix (if prefix - (cond-> prefix qualifier (str " " qualifier)) - qualifier)] - (if (sequential? xs) - (let [[sqls params] (reduce-sql (map #(format-selectable-dsl % {:as as})) xs)] - (when-not (= :none *checking*) - (when (empty? xs) - (throw (ex-info (str prefix " empty column list is illegal") - {:clause (into [prefix] xs)})))) - (into [(str (when prefix (str prefix " ")) (join ", " sqls))] params)) - (let [[sql & params] (format-selectable-dsl xs {:as as})] - (into [(str (when prefix (str prefix " ")) sql)] params))))) +(defn- format-selects-common + ([prefix as xs] (format-selects-common prefix as xs nil)) + ([prefix as xs wrap] + (let [qualifier (format-meta xs) + prefix (if prefix + (cond-> prefix qualifier (str " " qualifier)) + qualifier) + [pre post] (when (and wrap (sequential? xs) (< 1 (count xs))) + ["(" ")"])] + (if (sequential? xs) + (let [[sqls params] (reduce-sql (map #(format-selectable-dsl % {:as as})) xs)] + (when-not (= :none *checking*) + (when (empty? xs) + (throw (ex-info (str prefix " empty column list is illegal") + {:clause (into [prefix] xs)})))) + (into [(str (when prefix (str prefix " ")) pre (join ", " sqls) post)] params)) + (let [[sql & params] (format-selectable-dsl xs {:as as})] + (into [(str (when prefix (str prefix " ")) sql)] params)))))) (defn- format-selects [k xs] (format-selects-common (sql-kw k) - (#{:select :select-distinct :from :window :delete-from :facet - 'select 'select-distinct 'from 'window 'delete-from 'facet} + (#{:select :select-distinct :rename :from :window :delete-from :facet + 'select 'select-distinct 'rename 'from 'window 'delete-from 'facet + } k) - xs)) + xs + (#{:exclude :rename 'exclude 'rename} k))) (defn- format-selects-on [_ xs] (let [[on & cols] xs @@ -1610,6 +1616,8 @@ :select-distinct-on #'format-selects-on :select-top #'format-select-top :select-distinct-top #'format-select-top + :exclude #'format-selects + :rename #'format-selects :distinct (fn [k xs] (format-selects k [[xs]])) :expr (fn [_ xs] (format-selects nil [[xs]])) :into #'format-select-into diff --git a/src/honey/sql/helpers.cljc b/src/honey/sql/helpers.cljc index 3538ef4..0f51c1d 100644 --- a/src/honey/sql/helpers.cljc +++ b/src/honey/sql/helpers.cljc @@ -508,6 +508,16 @@ [& args] (generic-1 :expr args)) +(defn exclude + "Accepts one or more column names to exclude from a select list." + [& args] + (generic :exclude args)) + +(defn rename + "Accepts one or more column names with aliases to rename in a select list." + [& args] + (generic :rename args)) + (defn into "Accepts table name, optionally followed a database name." {:arglists '([table] [table dbname])} diff --git a/test/honey/sql/xtdb_test.cljc b/test/honey/sql/xtdb_test.cljc new file mode 100644 index 0000000..0524a1d --- /dev/null +++ b/test/honey/sql/xtdb_test.cljc @@ -0,0 +1,34 @@ +;; copyright (c) 2020-2024 sean corfield, all rights reserved + +(ns honey.sql.xtdb-test + (:require [clojure.test :refer [deftest is testing]] + [honey.sql :as sql] + [honey.sql.helpers :as h + :refer [select exclude rename from where]])) + +(deftest select-tests + (testing "select, exclude, rename" + (is (= ["SELECT * EXCLUDE _id RENAME value AS foo_value FROM foo"] + (sql/format (-> (select :*) (exclude :_id) (rename [:value :foo_value]) + (from :foo))))) + (is (= ["SELECT * EXCLUDE (_id, a) RENAME value AS foo_value FROM foo"] + (sql/format (-> (select :*) (exclude :_id :a) (rename [:value :foo_value]) + (from :foo))))) + (is (= ["SELECT * EXCLUDE _id RENAME (value AS foo_value, a AS b) FROM foo"] + (sql/format (-> (select :*) (exclude :_id) + (rename [:value :foo_value] + [:a :b]) + (from :foo)))))) + (testing "select, nest_one, nest_many" + (is (= ["SELECT a._id, NEST_ONE (SELECT * FROM foo AS b WHERE b_id = a._id) FROM bar AS a"] + (sql/format '{select (a._id, + ((nest_one {select * from ((foo b)) where (= b_id a._id)}))) + from ((bar a))}))) + (is (= ["SELECT a._id, NEST_MANY (SELECT * FROM foo AS b) FROM bar AS a"] + (sql/format '{select (a._id, + ((nest_many {select * from ((foo b))}))) + from ((bar a))}))))) + +(deftest dotted-array-access-tests + (is (= ["SELECT (a.b).c"] + (sql/format '{select (((. (nest :a.b) :c)))}))))