diff --git a/CHANGES.md b/CHANGES.md index c3bb466..1dd2744 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,6 @@ ## 0.9.3-SNAPSHOT (in progress) +* Support parameters in `#sql/raw`. Fix #219. (@seancorfield) * Add examples of table/column aliases to the README. Fix #215. (@seancorfield) * Refactor parameterizer to use multimethods. PR #214. (@xlevus) * Add examples of `raw` and `inline` to the README. Fix #213. (@seancorfield) diff --git a/README.md b/README.md index 106f0cc..449cce4 100644 --- a/README.md +++ b/README.md @@ -257,7 +257,8 @@ Keywords that begin with `?` are interpreted as bindable parameters: => ["SELECT id FROM foo WHERE a = ?" "BAZ"] ``` -There are helper functions and data literals for SQL function calls, field qualifiers, raw SQL fragments, inline values, and named input parameters: +There are helper functions and data literals for SQL function calls, field +qualifiers, raw SQL fragments, inline values, and named input parameters: ```clojure (def call-qualify-map @@ -274,7 +275,30 @@ call-qualify-map => ["SELECT foo(bar), foo.a, @var := foo.bar FROM foo WHERE (a = ? AND b = 42)" "BAZ"] ``` -Raw SQL fragments are treated exactly as-is when rendered into the formatted SQL string (with no parsing or parameterization). Inline values will not be lifted out as parameters, so they end up in the SQL string as-is. +Raw SQL fragments that are strings are treated exactly as-is when rendered into +the formatted SQL string (with no parsing or parameterization). Inline values +will not be lifted out as parameters, so they end up in the SQL string as-is. + +Raw SQL can also be supplied as a vector of strings and values. Strings are +rendered as-is into the formatted SQL string. Non-strings are lifted as +parameters. If you need a string parameter lifted, you must use `#sql/param` +or the `param` helper. + +```clojure +(-> (select :*) + (from :foo) + (where [:< :expired_at (sql/raw ["now() - '" 5 " seconds'"])]) + (sql/format {:foo 5})) +=> ["SELECT * FROM foo WHERE expired_at < now() - '? seconds'" 5] +``` + +```clojure +(-> (select :*) + (from :foo) + (where [:< :expired_at (sql/raw ["now() - '" #sql/param :t " seconds'"])]) + (sql/format {:t 5})) +=> ["SELECT * FROM foo WHERE expired_at < now() - '? seconds'" 5] +``` To quote identifiers, pass the `:quoting` keyword option to `format`. Valid options are `:ansi` (PostgreSQL), `:mysql`, or `:sqlserver`: diff --git a/src/honeysql/format.cljc b/src/honeysql/format.cljc index 6da4730..7f1d9b8 100644 --- a/src/honeysql/format.cljc +++ b/src/honeysql/format.cljc @@ -383,7 +383,11 @@ fn-name (fn-aliases fn-name fn-name)] (apply fn-handler fn-name (.-args x))))) SqlRaw - (to-sql [x] (.-s x)) + (to-sql [x] + (let [s (.-s x)] + (if (vector? s) + (string/join "" (map (fn [x] (if (string? x) x (to-sql x))) s)) + s))) #?(:clj clojure.lang.IPersistentMap :cljs cljs.core/PersistentArrayMap) (to-sql [x] diff --git a/src/honeysql/types.cljc b/src/honeysql/types.cljc index 0e76c2f..4849628 100644 --- a/src/honeysql/types.cljc +++ b/src/honeysql/types.cljc @@ -19,7 +19,7 @@ (defn raw "Represents a raw SQL string" [s] - (SqlRaw. (str s))) + (SqlRaw. (if (vector? s) s (str s)))) (defn read-sql-raw [form] ;; late bind, as above