identifier quoting
This commit is contained in:
parent
4590b93df5
commit
b3da410e38
2 changed files with 51 additions and 16 deletions
14
README.md
14
README.md
|
|
@ -104,7 +104,7 @@ Keywords that begin with `?` are interpreted as bindable parameters:
|
||||||
(-> (select :id)
|
(-> (select :id)
|
||||||
(from :foo)
|
(from :foo)
|
||||||
(where [:= :a :?baz])
|
(where [:= :a :?baz])
|
||||||
(sql/format {:baz "BAZ"}))
|
(sql/format :params {:baz "BAZ"}))
|
||||||
=> ["SELECT id FROM foo WHERE a = ?" "BAZ"]
|
=> ["SELECT id FROM foo WHERE a = ?" "BAZ"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -116,10 +116,20 @@ There are helper functions and data literals for SQL function calls, field quali
|
||||||
(where [:= :a (sql/param :baz)]))
|
(where [:= :a (sql/param :baz)]))
|
||||||
=> {:where [:= :a #sql/param :baz], :from (:foo), :select (#sql/call [:foo :bar] :foo.a #sql/raw "@var := foo.bar")}
|
=> {:where [:= :a #sql/param :baz], :from (:foo), :select (#sql/call [:foo :bar] :foo.a #sql/raw "@var := foo.bar")}
|
||||||
|
|
||||||
(sql/format *1 {:baz "BAZ"})
|
(sql/format *1 :params {:baz "BAZ"})
|
||||||
=> ["SELECT FOO(bar), foo.a, @var := foo.bar FROM foo WHERE a = ?" "BAZ"]
|
=> ["SELECT FOO(bar), foo.a, @var := foo.bar FROM foo WHERE a = ?" "BAZ"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To quote identifiers, pass the `:quoting` keyword option to `format`. Valid options are `:ansi`, `:mysql`, or `:sqlserver`:
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(-> (select :foo.a)
|
||||||
|
(from :foo)
|
||||||
|
(where [:= :foo.a "baz"])
|
||||||
|
(sql/format :quoting :mysql))
|
||||||
|
=> ["SELECT `foo`.`a` FROM `foo` WHERE `foo`.`a` = ?" "baz"]
|
||||||
|
```
|
||||||
|
|
||||||
Here's a big, complicated query. Note that Honey SQL makes no attempt to verify that your queries make any sense. It merely renders surface syntax.
|
Here's a big, complicated query. Note that Honey SQL makes no attempt to verify that your queries make any sense. It merely renders surface syntax.
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,20 @@
|
||||||
|
|
||||||
(def ^:dynamic *subquery?* false)
|
(def ^:dynamic *subquery?* false)
|
||||||
|
|
||||||
|
(def ^:private quote-fns
|
||||||
|
{:none identity
|
||||||
|
:ansi #(str \" % \")
|
||||||
|
:mysql #(str \` % \`)
|
||||||
|
:sqlserver #(str \[ % \])})
|
||||||
|
|
||||||
|
(def ^:dynamic *quote-identifier-fn* nil)
|
||||||
|
|
||||||
|
(defn quote-identifier [x]
|
||||||
|
(if-not *quote-identifier-fn*
|
||||||
|
x
|
||||||
|
(let [parts (string/split (name x) #"\.")]
|
||||||
|
(string/join "." (map #(*quote-identifier-fn* %) parts)))))
|
||||||
|
|
||||||
(def infix-fns
|
(def infix-fns
|
||||||
#{"+" "-" "*" "/" "%" "mod" "|" "&" "^"
|
#{"+" "-" "*" "/" "%" "mod" "|" "&" "^"
|
||||||
"and" "or" "xor"
|
"and" "or" "xor"
|
||||||
|
|
@ -129,19 +143,32 @@
|
||||||
of a SQL string and parameters, as expected by clojure.java.jdbc.
|
of a SQL string and parameters, as expected by clojure.java.jdbc.
|
||||||
|
|
||||||
Input parameters will be filled into designated spots according to
|
Input parameters will be filled into designated spots according to
|
||||||
name (if a map is provided) or by position (if a sequence is provided)."
|
name (if a map is provided) or by position (if a sequence is provided).
|
||||||
[sql-map & [params]]
|
|
||||||
(binding [*params* (atom [])
|
Instead of passing parameters, you can use keyword arguments:
|
||||||
*input-params* (atom params)]
|
:params - input parameters
|
||||||
(let [sql-str (to-sql sql-map)]
|
:quoting - quoting styling to use for identifiers; one of :ansi,
|
||||||
(if (seq @*params*)
|
:mysql, or :sqlserver"
|
||||||
(into [sql-str] @*params*)
|
[sql-map & params-or-opts]
|
||||||
[sql-str]))))
|
(let [opts (when (keyword? (first params-or-opts))
|
||||||
|
(apply hash-map params-or-opts))
|
||||||
|
params (if (coll? (first params-or-opts))
|
||||||
|
(first params-or-opts)
|
||||||
|
(:params opts))]
|
||||||
|
(binding [*params* (atom [])
|
||||||
|
*input-params* (atom params)
|
||||||
|
*quote-identifier-fn* (quote-fns (:quoting opts))]
|
||||||
|
(let [sql-str (to-sql sql-map)]
|
||||||
|
(if (seq @*params*)
|
||||||
|
(into [sql-str] @*params*)
|
||||||
|
[sql-str])))))
|
||||||
|
|
||||||
(defn format-predicate
|
(defn format-predicate
|
||||||
"Formats a predicate (e.g., for WHERE, JOIN, or HAVING) as a string."
|
"Formats a predicate (e.g., for WHERE, JOIN, or HAVING) as a string."
|
||||||
[pred]
|
[pred & {:keys [quoting]}]
|
||||||
(binding [*params* (atom [])]
|
(binding [*params* (atom [])
|
||||||
|
*quote-identifier-fn* (or (quote-fns quoting)
|
||||||
|
*quote-identifier-fn*)]
|
||||||
(let [sql-str (format-predicate* pred)]
|
(let [sql-str (format-predicate* pred)]
|
||||||
(if (seq @*params*)
|
(if (seq @*params*)
|
||||||
(into [sql-str] @*params*)
|
(into [sql-str] @*params*)
|
||||||
|
|
@ -159,7 +186,7 @@
|
||||||
\% (let [call-args (string/split (subs s 1) #"\." 2)]
|
\% (let [call-args (string/split (subs s 1) #"\." 2)]
|
||||||
(to-sql (apply call (map keyword call-args))))
|
(to-sql (apply call (map keyword call-args))))
|
||||||
\? (to-sql (param (keyword (subs s 1))))
|
\? (to-sql (param (keyword (subs s 1))))
|
||||||
(-> s (string/replace "-" "_")))))
|
(-> s (string/replace "-" "_") quote-identifier))))
|
||||||
clojure.lang.Symbol
|
clojure.lang.Symbol
|
||||||
(-to-sql [x] (-> x name (string/replace "-" "_")))
|
(-to-sql [x] (-> x name (string/replace "-" "_")))
|
||||||
java.lang.Number
|
java.lang.Number
|
||||||
|
|
@ -173,9 +200,7 @@
|
||||||
;; alias
|
;; alias
|
||||||
(str (to-sql (first x))
|
(str (to-sql (first x))
|
||||||
" AS "
|
" AS "
|
||||||
(if (string? (second x))
|
(str "\"" (name (second x)) "\""))))
|
||||||
(str "\"" (second x) "\"")
|
|
||||||
(to-sql (second x))))))
|
|
||||||
SqlCall
|
SqlCall
|
||||||
(-to-sql [x] (binding [*fn-context?* true]
|
(-to-sql [x] (binding [*fn-context?* true]
|
||||||
(let [fn-name (name (.name x))
|
(let [fn-name (name (.name x))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue