From 0d1fd0e90173c7964ff24bd99587969a6e57b5a1 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Thu, 6 Mar 2025 13:20:16 -0800 Subject: [PATCH 1/8] address #561 by dropping support for clojure 1.9 Signed-off-by: Sean Corfield --- CHANGELOG.md | 3 +++ README.md | 3 ++- build.clj | 5 ++--- deps.edn | 3 +-- doc/differences-from-1-x.md | 4 ++-- src/honey/sql.cljc | 13 +++---------- 6 files changed, 13 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21e0631..04b27e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changes +* 2.7.next in progress + * Drop support for Clojure 1.9 [#561](https://github.com/seancorfield/honeysql/issues/561). + * 2.6.1281 -- 2025-03-06 * Address [#568](https://github.com/seancorfield/honeysql/issues/568) by adding `honey.sql/semicolon` to merge multiple SQL+params vectors into one (with semicolons separating the SQL statements). * Address [#567](https://github.com/seancorfield/honeysql/issues/567) by adding support for `ASSERT` clause. diff --git a/README.md b/README.md index 139c754..724e6d3 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ This project follows the version scheme MAJOR.MINOR.COMMITS where MAJOR and MINO > Note: every commit to the **develop** branch runs CI (GitHub Actions) and successful runs push a MAJOR.MINOR.9999-SNAPSHOT build to Clojars so the very latest version of HoneySQL is always available either via that [snapshot on Clojars](https://clojars.org/com.github.seancorfield/honeysql) or via a git dependency on the latest SHA. -HoneySQL 2.x requires Clojure 1.9 or later. +HoneySQL 2.7.x requires Clojure 1.10.3 or later. +Earlier versions of HoneySQL support Clojure 1.9.0. It also supports recent versions of ClojureScript and Babashka. Compared to the [legacy 1.x version](#1.x), HoneySQL 2.x provides a streamlined codebase and a simpler method for extending the DSL. It also supports SQL dialects out-of-the-box and will be extended to support vendor-specific language features over time (unlike 1.x). diff --git a/build.clj b/build.clj index c8c6f89..3546b3a 100644 --- a/build.clj +++ b/build.clj @@ -47,8 +47,7 @@ "Generate and run doc tests. Optionally specify :aliases vector: - [:1.9] -- test against Clojure 1.9 (the default) - [:1.10] -- test against Clojure 1.10.3 + [:1.10] -- test against Clojure 1.10.3 (the default) [:1.11] -- test against Clojure 1.11.0 [:1.12] -- test against Clojure 1.12.0 [:cljs] -- test against ClojureScript" @@ -99,7 +98,7 @@ (defn ci "Run the CI pipeline of tests (and build the JAR). - Default Clojure version is 1.9.0 (:1.9) so :elide + Default Clojure version is 1.10.3 (:1.10) so :elide tests for #409 on that version." [opts] (let [aliases [:cljs :elide :1.10 :1.11 :1.12] diff --git a/deps.edn b/deps.edn index b8d2cf4..d384356 100644 --- a/deps.edn +++ b/deps.edn @@ -1,6 +1,6 @@ {:mvn/repos {"sonatype" {:url "https://oss.sonatype.org/content/repositories/snapshots/"}} :paths ["src"] - :deps {org.clojure/clojure {:mvn/version "1.9.0"}} + :deps {org.clojure/clojure {:mvn/version "1.10.3"}} :aliases {;; for help: clojure -A:deps -T:build help/doc :build {:deps {io.github.clojure/tools.build {:mvn/version "0.10.6"} @@ -8,7 +8,6 @@ :ns-default build} ;; versions to test against: - :1.9 {:override-deps {org.clojure/clojure {:mvn/version "1.9.0"}}} :1.10 {:override-deps {org.clojure/clojure {:mvn/version "1.10.3"}}} :1.11 {:override-deps {org.clojure/clojure {:mvn/version "1.11.4"}}} :1.12 {:override-deps {org.clojure/clojure {:mvn/version "1.12.0"}}} diff --git a/doc/differences-from-1-x.md b/doc/differences-from-1-x.md index edb9daa..246fe00 100644 --- a/doc/differences-from-1-x.md +++ b/doc/differences-from-1-x.md @@ -9,7 +9,7 @@ The DSL itself -- the data structures that both versions convert to SQL and para If you are using Clojure 1.11, you can invoke `format` with a mixture of named arguments and a trailing hash map of additional options, if you wish. -HoneySQL 1.x supported Clojure 1.7 and later. HoneySQL 2.x requires Clojure 1.9 or later. +HoneySQL 1.x supported Clojure 1.7 and later. HoneySQL 2.7.x requires Clojure 1.10.3 or later. Earlier versions of HoneySQL 2.x support Clojure 1.9.0. ## Group, Artifact, and Namespaces @@ -90,7 +90,7 @@ The new namespaces are: * `honey.sql` -- the primary API (just `format` now), * `honey.sql.helpers` -- helper functions to build the DSL. -Supported Clojure versions: 1.9 and later. +Supported Clojure versions: 1.10.3 and later. ## API Changes diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index 4bcf9f8..44fe883 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -436,9 +436,7 @@ (defn- format-simple-var ([x] (let [c (if (keyword? x) - #?(:bb (str (symbol x)) - :clj (str (.sym ^clojure.lang.Keyword x)) ;; Omits leading colon - :default (subs (str x) 1)) + (str (symbol x)) (str x))] (format-simple-var x c {}))) ([x c opts] @@ -455,9 +453,7 @@ ;; for multiple / in the %fun.call case so that ;; qualified column names can be used: (let [c (if (keyword? x) - #?(:bb (str (symbol x)) - :clj (str (.sym ^clojure.lang.Keyword x)) ;; Omits leading colon - :default (subs (str x) 1)) + (str (symbol x)) (str x))] (cond (str/starts-with? c "%") (let [[f & args] (split-by-separator (subs c 1) ".")] @@ -1737,10 +1733,7 @@ (if (keyword? k) (if-let [n (namespace k)] (symbol n (name k)) - ;; In CLJ runtime, reuse symbol that's already present in the keyword. - #?(:bb (symbol (name k)) - :clj (.sym ^clojure.lang.Keyword k) - :default (symbol (name k)))) + (symbol (name k))) k)) (defn format-dsl From 89f39be55ca2a3e5a6c10107ade22c52f1e6f9c4 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Thu, 6 Mar 2025 13:25:10 -0800 Subject: [PATCH 2/8] clarify 2.7 versions Signed-off-by: Sean Corfield --- README.md | 2 +- doc/differences-from-1-x.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 724e6d3..2daa225 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ This project follows the version scheme MAJOR.MINOR.COMMITS where MAJOR and MINO > Note: every commit to the **develop** branch runs CI (GitHub Actions) and successful runs push a MAJOR.MINOR.9999-SNAPSHOT build to Clojars so the very latest version of HoneySQL is always available either via that [snapshot on Clojars](https://clojars.org/com.github.seancorfield/honeysql) or via a git dependency on the latest SHA. -HoneySQL 2.7.x requires Clojure 1.10.3 or later. +HoneySQL 2.7.y requires Clojure 1.10.3 or later. Earlier versions of HoneySQL support Clojure 1.9.0. It also supports recent versions of ClojureScript and Babashka. diff --git a/doc/differences-from-1-x.md b/doc/differences-from-1-x.md index 246fe00..d12efc3 100644 --- a/doc/differences-from-1-x.md +++ b/doc/differences-from-1-x.md @@ -9,7 +9,7 @@ The DSL itself -- the data structures that both versions convert to SQL and para If you are using Clojure 1.11, you can invoke `format` with a mixture of named arguments and a trailing hash map of additional options, if you wish. -HoneySQL 1.x supported Clojure 1.7 and later. HoneySQL 2.7.x requires Clojure 1.10.3 or later. Earlier versions of HoneySQL 2.x support Clojure 1.9.0. +HoneySQL 1.x supported Clojure 1.7 and later. HoneySQL 2.7.y requires Clojure 1.10.3 or later. Earlier versions of HoneySQL 2.x support Clojure 1.9.0. ## Group, Artifact, and Namespaces From 67ea477a5cf28f89490508483b82bfdf6be61b0b Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 12 Mar 2025 13:36:49 -0700 Subject: [PATCH 3/8] part of #570 -- colon path selection Signed-off-by: Sean Corfield --- CHANGELOG.md | 1 + doc/special-syntax.md | 12 ++++++++-- src/honey/sql.cljc | 15 +++++++----- test/honey/sql_test.cljc | 49 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04b27e7..23aaaeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changes * 2.7.next in progress + * Address #570 by adding `:.:.` as special syntax for Snowflake's JSON path syntax. * Drop support for Clojure 1.9 [#561](https://github.com/seancorfield/honeysql/issues/561). * 2.6.1281 -- 2025-03-06 diff --git a/doc/special-syntax.md b/doc/special-syntax.md index 97d872c..5f109e0 100644 --- a/doc/special-syntax.md +++ b/doc/special-syntax.md @@ -211,15 +211,23 @@ Accepts a single expression and prefixes it with `DISTINCT `: ;;=> ["SELECT COUNT(DISTINCT status) AS n FROM table"] ``` -## dot . +## dot . .:. -Accepts an expression and a field (or column) selection: +Accepts an expression and one or more fields (or columns). Plain dot produces +plain dotted selection: ```clojure (sql/format {:select [ [[:. :t :c]] [[:. :s :t :c]] ]}) ;;=> ["SELECT t.c, s.t.c"] ``` +Dot colon dot produces Snowflake-style dotted selection: + +```clojure +(sql/format {:select [ [[:.:. :t :c]] [[:.:. :s :t :c]] ]}) +;;=> ["SELECT t:c, s:t.c"] +``` + Can be used with `:nest` for field selection from composites: ```clojure diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index 44fe883..9cb1fa6 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -1946,6 +1946,12 @@ (let [[sql & params] (format-expr x)] (into [(str sql " " (sql-kw k))] params))) +(defn dot-navigation [sep [expr col & subcols]] + (let [[sql & params] (format-expr expr)] + (into [(str sql sep (format-entity col) + (when (seq subcols) + (str "." (join "." (map format-entity subcols)))))] + params))) (def ^:private special-syntax (atom {;; these "functions" are mostly used in column @@ -1966,12 +1972,9 @@ :references #'function-1 :unique #'function-1-opt ;; dynamic dotted name creation: - :. (fn [_ [expr col subcol]] - (let [[sql & params] (format-expr expr)] - (into [(str sql "." (format-entity col) - (when subcol - (str "." (format-entity subcol))))] - params))) + :. (fn [_ data] (dot-navigation "." data)) + ;; snowflake variant #570: + :.:. (fn [_ data] (dot-navigation ":" data)) ;; used in DDL to force rendering as a SQL entity instead ;; of a SQL keyword: :entity (fn [_ [e]] [(format-entity e)]) diff --git a/test/honey/sql_test.cljc b/test/honey/sql_test.cljc index ba6bce4..f5c4301 100644 --- a/test/honey/sql_test.cljc +++ b/test/honey/sql_test.cljc @@ -1178,9 +1178,10 @@ ORDER BY id = ? DESC (deftest issue-474-dot-selection (testing "basic dot selection" - (is (= ["SELECT a.b, c.d, a.d.x"] + (is (= ["SELECT a.b, c.d, a.d.x, a.d.x.y"] (let [t :a c :d] - (sut/format {:select [[[:. t :b]] [[:. :c c]] [[:. t c :x]]]})))) + (sut/format {:select [[[:. t :b]] [[:. :c c]] + [[:. t c :x]] [[:. t c :x :y]]]})))) (is (= ["SELECT [a].[b], [c].[d], [a].[d].[x]"] (let [t :a c :d] (sut/format {:select [[[:. t :b]] [[:. :c c]] [[:. t c :x]]]} @@ -1194,6 +1195,45 @@ ORDER BY id = ? DESC (sut/format '{select (((. (nest v) *)) ((. (nest w) x)) ((. (nest (y z)) *)))} + {:dialect :mysql}))) + (is (= ["SELECT (v).*, (w).x, (Y(z)).*"] + (sut/format '{select (((get-in v *)) + ((get-in w x)) + ((get-in (y z) *)))}))) + (is (= ["SELECT (`v`).*, (`w`).`x`, (Y(`z`)).*"] + (sut/format '{select (((get-in v *)) + ((get-in w x)) + ((get-in (y z) *)))} + {:dialect :mysql}))))) + +(deftest issue-570-snowflake-dot-selection + (testing "basic colon selection" + (is (= ["SELECT a:b, c:d, a:d.x, a:d.x.y"] + (let [t :a c :d] + (sut/format {:select [[[:.:. t :b]] [[:.:. :c c]] + [[:.:. t c :x]] [[:.:. t c :x :y]]]})))) + (is (= ["SELECT [a]:[b], [c]:[d], [a]:[d].[x]"] + (let [t :a c :d] + (sut/format {:select [[[:.:. t :b]] [[:.:. :c c]] [[:.:. t c :x]]]} + {:dialect :sqlserver}))))) + (testing "basic field selection from composite" + (is (= ["SELECT (v):*, (w):x, (Y(z)):*"] + (sut/format '{select (((.:. (nest v) *)) + ((.:. (nest w) x)) + ((.:. (nest (y z)) *)))}))) + (is (= ["SELECT (`v`):*, (`w`):`x`, (Y(`z`)):*"] + (sut/format '{select (((.:. (nest v) *)) + ((.:. (nest w) x)) + ((.:. (nest (y z)) *)))} + {:dialect :mysql}))) + (is (= ["SELECT (v):*, (w):x, (Y(z)):*"] + (sut/format '{select (((get-in v *)) + ((get-in w x)) + ((get-in (y z) *)))}))) + (is (= ["SELECT (`v`):*, (`w`):`x`, (Y(`z`)):*"] + (sut/format '{select (((get-in v *)) + ((get-in w x)) + ((get-in (y z) *)))} {:dialect :mysql}))))) (deftest issue-476-raw @@ -1451,4 +1491,9 @@ ORDER BY id = ? DESC :select [:*] :from [:a-b.b-c.c-d]} (sut/format {:dialect :nrql})) + (sut/format {:select :a:b.c}) ; quotes a:b + (sut/format [:. :a :b :c]) ; a.b.c + (sut/format [:. :a :b :c :d]) ; drops d ; a.b.c + (sut/format [:.:. :a :b :c]) ; .(a, b, c) + (sut/format '(.:. a b c)) ; .(a, b, c) ) From 92e4e16b45219210fab0603b56cc5d8d694847ca Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 12 Mar 2025 15:28:19 -0700 Subject: [PATCH 4/8] a couple of minor build script fixes for dropping 1.9 Signed-off-by: Sean Corfield --- build.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.clj b/build.clj index 3546b3a..1c35ab5 100644 --- a/build.clj +++ b/build.clj @@ -19,7 +19,7 @@ [deps-deploy.deps-deploy :as dd])) (def lib 'com.github.seancorfield/honeysql) -(defn- the-version [patch] (format "2.6.%s" patch)) +(defn- the-version [patch] (format "2.7.%s" patch)) (def version (the-version (b/git-count-revs nil))) (def snapshot (the-version "9999-SNAPSHOT")) (def class-dir "target/classes") @@ -101,7 +101,7 @@ Default Clojure version is 1.10.3 (:1.10) so :elide tests for #409 on that version." [opts] - (let [aliases [:cljs :elide :1.10 :1.11 :1.12] + (let [aliases [:cljs :elide :1.11 :1.12] opts (jar-opts opts)] (b/delete {:path "target"}) (doseq [alias aliases] From 3906aa53c0892da43575409d41c9e97a0e059354 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 12 Mar 2025 15:30:30 -0700 Subject: [PATCH 5/8] remove accidentally duped test Signed-off-by: Sean Corfield --- test/honey/sql_test.cljc | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/test/honey/sql_test.cljc b/test/honey/sql_test.cljc index 9d610d6..3bd63bc 100644 --- a/test/honey/sql_test.cljc +++ b/test/honey/sql_test.cljc @@ -1206,36 +1206,6 @@ ORDER BY id = ? DESC ((get-in (y z) *)))} {:dialect :mysql}))))) -(deftest issue-570-snowflake-dot-selection - (testing "basic colon selection" - (is (= ["SELECT a:b, c:d, a:d.x, a:d.x.y"] - (let [t :a c :d] - (sut/format {:select [[[:.:. t :b]] [[:.:. :c c]] - [[:.:. t c :x]] [[:.:. t c :x :y]]]})))) - (is (= ["SELECT [a]:[b], [c]:[d], [a]:[d].[x]"] - (let [t :a c :d] - (sut/format {:select [[[:.:. t :b]] [[:.:. :c c]] [[:.:. t c :x]]]} - {:dialect :sqlserver}))))) - (testing "basic field selection from composite" - (is (= ["SELECT (v):*, (w):x, (Y(z)):*"] - (sut/format '{select (((.:. (nest v) *)) - ((.:. (nest w) x)) - ((.:. (nest (y z)) *)))}))) - (is (= ["SELECT (`v`):*, (`w`):`x`, (Y(`z`)):*"] - (sut/format '{select (((.:. (nest v) *)) - ((.:. (nest w) x)) - ((.:. (nest (y z)) *)))} - {:dialect :mysql}))) - (is (= ["SELECT (v):*, (w):x, (Y(z)):*"] - (sut/format '{select (((get-in v *)) - ((get-in w x)) - ((get-in (y z) *)))}))) - (is (= ["SELECT (`v`):*, (`w`):`x`, (Y(`z`)):*"] - (sut/format '{select (((get-in v *)) - ((get-in w x)) - ((get-in (y z) *)))} - {:dialect :mysql}))))) - (deftest issue-570-snowflake-dot-selection (testing "basic colon selection" (is (= ["SELECT a:b, c:d, a:d.x, a:d.x.y"] From 2c8fc30b1dda1f14dc5f0f2387b1133aaa20cd4f Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 12 Mar 2025 15:39:24 -0700 Subject: [PATCH 6/8] restore some clojure-only optimizations on keywords Signed-off-by: Sean Corfield --- src/honey/sql.cljc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index 3a8c3fe..6c78810 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -436,7 +436,8 @@ (defn- format-simple-var ([x] (let [c (if (keyword? x) - (str (symbol x)) + #?(:clj (str (.sym ^clojure.lang.Keyword x)) + :default (subs (str x) 1)) (str x))] (format-simple-var x c {}))) ([x c opts] @@ -453,7 +454,8 @@ ;; for multiple / in the %fun.call case so that ;; qualified column names can be used: (let [c (if (keyword? x) - (str (symbol x)) + #?(:clj (str (.sym ^clojure.lang.Keyword x)) + :default (subs (str x) 1)) (str x))] (cond (str/starts-with? c "%") (let [[f & args] (split-by-separator (subs c 1) ".")] @@ -1731,9 +1733,8 @@ qualifier, if any." [k] (if (keyword? k) - (if-let [n (namespace k)] - (symbol n (name k)) - (symbol (name k))) + #?(:clj (.sym ^clojure.lang.Keyword k) + :default (subs (str k) 1)) k)) (defn format-dsl From 74cf16c13419479692fb1486ac226e03383c37b4 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 12 Mar 2025 15:47:58 -0700 Subject: [PATCH 7/8] fix kw->sym for cljs! Signed-off-by: Sean Corfield --- src/honey/sql.cljc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index 6c78810..2ffa7fe 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -1734,7 +1734,9 @@ [k] (if (keyword? k) #?(:clj (.sym ^clojure.lang.Keyword k) - :default (subs (str k) 1)) + :default (if-let [n (namespace k)] + (symbol n (name k)) + (symbol (name k)))) k)) (defn format-dsl From a981ed9171a39117040afefae5693cb5c6516d97 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 12 Mar 2025 15:52:44 -0700 Subject: [PATCH 8/8] restore bb logic on sym/kw Signed-off-by: Sean Corfield --- src/honey/sql.cljc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index 2ffa7fe..b035ea1 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -436,7 +436,8 @@ (defn- format-simple-var ([x] (let [c (if (keyword? x) - #?(:clj (str (.sym ^clojure.lang.Keyword x)) + #?(:bb (subs (str x) 1) + :clj (str (.sym ^clojure.lang.Keyword x)) :default (subs (str x) 1)) (str x))] (format-simple-var x c {}))) @@ -454,7 +455,8 @@ ;; for multiple / in the %fun.call case so that ;; qualified column names can be used: (let [c (if (keyword? x) - #?(:clj (str (.sym ^clojure.lang.Keyword x)) + #?(:bb (subs (str x) 1) + :clj (str (.sym ^clojure.lang.Keyword x)) :default (subs (str x) 1)) (str x))] (cond (str/starts-with? c "%") @@ -1733,7 +1735,10 @@ qualifier, if any." [k] (if (keyword? k) - #?(:clj (.sym ^clojure.lang.Keyword k) + #?(:bb (if-let [n (namespace k)] + (symbol n (name k)) + (symbol (name k))) + :clj (.sym ^clojure.lang.Keyword k) :default (if-let [n (namespace k)] (symbol n (name k)) (symbol (name k))))