From 429761f106ff485f26fc5bc44d29d911219c38df Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sat, 30 Jan 2021 12:35:51 -0800 Subject: [PATCH] Fixes #261 by re-implementing :raw --- README.md | 12 ++++++++++-- doc/differences-from-1-x.md | 3 +-- src/honey/sql.cljc | 11 ++++++++++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index cf25644..9998930 100644 --- a/README.md +++ b/README.md @@ -388,17 +388,25 @@ parameters. If you need a string parameter lifted, you must use `:param`. (from :foo) (where [:< :expired_at [:raw ["now() - '" 5 " seconds'"]]]) (sql/format)) -=> ["SELECT * FROM foo WHERE expired_at < now() - '? seconds'" 5] +=> ["SELECT * FROM foo WHERE expired_at < now() - '5 seconds'"] ``` ```clojure (-> (select :*) (from :foo) (where [:< :expired_at [:raw ["now() - '" [:param :t] " seconds'"]]]) - (sql/format {:t 5})) + (sql/format {:params {:t 5}})) => ["SELECT * FROM foo WHERE expired_at < now() - '? seconds'" 5] ``` +```clojure +(-> (select :*) + (from :foo) + (where [:< :expired_at [:raw ["now() - " [:inline (str 5 " seconds")]]]]) + (sql/format)) +=> ["SELECT * FROM foo WHERE expired_at < now() - '5 seconds'"] +``` + #### Identifiers To quote identifiers, pass the `:quoted true` option to `format` and they will diff --git a/doc/differences-from-1-x.md b/doc/differences-from-1-x.md index ab94f6a..6153972 100644 --- a/doc/differences-from-1-x.md +++ b/doc/differences-from-1-x.md @@ -96,11 +96,10 @@ The following new syntax has been added: * `:array` -- used as a function to replace the `sql/array` / `#sql/array` machinery, * `:inline` -- used as a function to replace the `sql/inline` / `#sql/inline` machinery, * `:interval` -- used as a function to support `INTERVAL `, e.g., `[:interval 30 :days]`. +* `:raw` -- used as a function to replace the `sql/raw` / `#sql/raw` machinery. Vector subexpressions inside a `[:raw ..]` expression are formatted to SQL and parameters. Other subexpressions are just turned into strings and concatenated. This is different to the v1 behavior but should be more flexible. > Note 1: in 1.x, inlining a string `"foo"` produced `foo` but in 2.x it produces `'foo'`, i.e., string literals become SQL strings without needing internal quotes (1.x required `"'foo'"`). -> Note 2: expect `:raw` to be added in some form before release. - ## Extensibility The protocols and multimethods in 1.x have all gone away. The primary extension point is `honey.sql/register-clause!` which lets you specify the new clause (keyword), the formatter function for it, and the existing clause that it should be ranked before (`format` processes the DSL in clause order). diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index f0a27bb..5ea1f58 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -556,7 +556,16 @@ :raw (fn [_ [s]] (if (sequential? s) - [(str/join " " s)] + (let [[sqls params] + (reduce (fn [[sqls params] s] + (if (vector? s) + (let [[sql & params'] (format-expr s)] + [(conj sqls sql) + (into params params')]) + [(conj sqls s) params])) + [[] []] + s)] + (into [(str/join sqls)] params)) [s]))})) (defn format-expr [x & [{:keys [nested?] :as opts}]]