From a732815b37fdc086b149d9dc61e679ff9b44af60 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sat, 7 Sep 2019 23:42:38 -0700 Subject: [PATCH] Fixes #249 by adding :namespace-as-table? option for 0.9.8 --- CHANGES.md | 4 ++++ README.md | 16 ++++++++++++++-- package.json | 2 +- src/honeysql/format.cljc | 20 ++++++++++++++------ test/honeysql/format_test.cljc | 5 +++++ 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4cb0833..e4968b1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +## 0.9.8 + +* Fix #249 by adding `honeysql.format/*namespace-as-table?*` and `:namespace-as-table?` option to `format`. (@seancorfield) + ## 0.9.7 * Fix #248 by treating alias as "not a subquery" when generating SQL for it. (@seancorfield) diff --git a/README.md b/README.md index c5a982c..c642613 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,8 @@ Everything is built on top of maps representing SQL queries: ```clojure (def sqlmap {:select [:a :b :c] - :from [:foo] - :where [:= :f.a "baz"]}) + :from [:foo] + :where [:= :f.a "baz"]}) ``` Column names can be provided as keywords or symbols (but not strings -- HoneySQL treats strings as values that should be lifted out of the SQL as parameters). @@ -46,6 +46,18 @@ Column names can be provided as keywords or symbols (but not strings -- HoneySQL => ["SELECT a, b, c FROM foo WHERE f.a = ?" "baz"] ``` +By default, namespace-qualified keywords as treated as simple keywords: their namespace portion is ignored. This was the behavior in HoneySQL prior to the 0.9.0 release and has been restored since the 0.9.7 release as this is considered the least surprising behavior. +As of version 0.9.7, `format` accepts `:allow-namespaced-names? true` to provide the somewhat unusual behavior of 0.9.0-0.9.6, namely that namespace-qualified keywords were passed through into the SQL "as-is", i.e., with the `/` in them (which generally required a quoting strategy as well). +As of version 0.9.8, `format` accepts `:namespace-as-table? true` to treat namespace-qualified keywords as if the `/` were `.`, allowing `:table/column` as an alternative to `:table.column`. This approach is likely to be more compatible with code that uses libraries like [`next.jdbc`](https://github.com/seancorfield/next-jdbc) and [`seql`](https://github.com/exoscale/seql), as well as being more convenient in a world of namespace-qualified keywords, following the example of `clojure.spec` etc. + +```clojure +(def q-sqlmap {:select [:foo/a :foo/b :foo/c] + :from [:foo] + :where [:= :foo/a "baz"]}) +(sql/format q-sqlmap :namespace-as-table? true) +=> ["SELECT foo.a, foo.b, foo.c FROM foo WHERE foo.a = ?" "baz"] +``` + Honeysql is a relatively "pure" library, it does not manage your sql connection or run queries for you, it simply generates SQL strings. You can then pass them to jdbc: diff --git a/package.json b/package.json index be5eba4..f3813de 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@honeysql/honeysql", - "version": "0.9.7", + "version": "0.9.8", "license": "EPL-1.0", "homepage": "https://github.com/jkk/honeysql", "repository": { diff --git a/src/honeysql/format.cljc b/src/honeysql/format.cljc index 6554245..f03801b 100644 --- a/src/honeysql/format.cljc +++ b/src/honeysql/format.cljc @@ -46,6 +46,8 @@ (def ^:dynamic *allow-namespaced-names?* false) +(def ^:dynamic *namespace-as-table?* false) + (def ^:dynamic *name-transform-fn* nil) (def ^:private quote-fns @@ -95,11 +97,16 @@ s (cond (or (keyword? x) (symbol? x)) (name-transform-fn - (if *allow-namespaced-names?* - (str (when-let [n (namespace x)] - (str n "/")) - (name x)) - (name x))) + (cond *namespace-as-table?* + (str (when-let [n (namespace x)] + (str n ".")) + (name x)) + *allow-namespaced-names?* + (str (when-let [n (namespace x)] + (str n "/")) + (name x)) + :else + (name x))) (string? x) (if qf x (name-transform-fn x)) :else (str x))] (if-not qf @@ -286,7 +293,8 @@ *quote-identifier-fn* (quote-fns (:quoting opts)) *parameterizer* (or (:parameterizer opts) :jdbc) *allow-dashed-names?* (:allow-dashed-names? opts) - *allow-namespaced-names?* (:allow-namespaced-names? opts)] + *allow-namespaced-names?* (:allow-namespaced-names? opts) + *namespace-as-table?* (:namespace-as-table? opts)] (let [sql-str (to-sql sql-map)] (if (and (seq @*params*) (not= :none (:parameterizer opts))) (if (:return-param-names opts) diff --git a/test/honeysql/format_test.cljc b/test/honeysql/format_test.cljc index ca1267c..d2d4195 100644 --- a/test/honeysql/format_test.cljc +++ b/test/honeysql/format_test.cljc @@ -6,6 +6,7 @@ [honeysql.types :as sql] [honeysql.format :refer [*allow-dashed-names?* *allow-namespaced-names?* + *namespace-as-table?* quote-identifier format-clause format parameterize]])) @@ -38,6 +39,10 @@ (deftest test-namespaced-identifier (is (= (quote-identifier :foo/bar) "bar")) (is (= (quote-identifier :foo/bar :style :ansi) "\"bar\"")) + (binding [*namespace-as-table?* true] + (is (= (quote-identifier :foo/bar) "foo.bar")) + (is (= (quote-identifier :foo/bar :style :ansi) "\"foo\".\"bar\"")) + (is (= (quote-identifier :foo/bar :style :ansi :split false) "\"foo.bar\""))) (binding [*allow-namespaced-names?* true] (is (= (quote-identifier :foo/bar) "foo/bar")) (is (= (quote-identifier :foo/bar :style :ansi) "\"foo/bar\""))))