use StringBuilder, not concatenation
This commit is contained in:
parent
ae7d42bd32
commit
73bc35aaee
2 changed files with 237 additions and 143 deletions
|
|
@ -8,15 +8,6 @@
|
||||||
|
|
||||||
;;;;
|
;;;;
|
||||||
|
|
||||||
(defn comma-join [s]
|
|
||||||
(string/join ", " s))
|
|
||||||
|
|
||||||
(defn space-join [s]
|
|
||||||
(string/join " " s))
|
|
||||||
|
|
||||||
(defn paren-wrap [x]
|
|
||||||
(str "(" x ")"))
|
|
||||||
|
|
||||||
(def ^:dynamic *clause*
|
(def ^:dynamic *clause*
|
||||||
"During formatting, *clause* is bound to :select, :from, :where, etc."
|
"During formatting, *clause* is bound to :select, :from, :where, etc."
|
||||||
nil)
|
nil)
|
||||||
|
|
@ -38,6 +29,8 @@
|
||||||
|
|
||||||
(def ^:dynamic *subquery?* false)
|
(def ^:dynamic *subquery?* false)
|
||||||
|
|
||||||
|
(def ^:dynamic *builder* nil)
|
||||||
|
|
||||||
(def ^:private quote-fns
|
(def ^:private quote-fns
|
||||||
{:ansi #(str \" % \")
|
{:ansi #(str \" % \")
|
||||||
:mysql #(str \` % \`)
|
:mysql #(str \` % \`)
|
||||||
|
|
@ -54,6 +47,30 @@
|
||||||
(defn- undasherize [s]
|
(defn- undasherize [s]
|
||||||
(string/replace s "-" "_"))
|
(string/replace s "-" "_"))
|
||||||
|
|
||||||
|
(defn append-str [^String s]
|
||||||
|
(.append ^StringBuilder *builder* s))
|
||||||
|
|
||||||
|
(defn append-obj [o]
|
||||||
|
(.append ^StringBuilder *builder* o))
|
||||||
|
|
||||||
|
(defn append-char [c]
|
||||||
|
(.append ^StringBuilder *builder* (char c)))
|
||||||
|
|
||||||
|
(defn do-join [sep appender xs]
|
||||||
|
(when (seq xs)
|
||||||
|
(appender (first xs))
|
||||||
|
(doseq [x (rest xs)]
|
||||||
|
(append-str sep)
|
||||||
|
(appender x))))
|
||||||
|
|
||||||
|
(defn close-paren []
|
||||||
|
(.append ^StringBuilder *builder* \)))
|
||||||
|
|
||||||
|
(defn do-paren-join [sep appender xs]
|
||||||
|
(append-char \()
|
||||||
|
(do-join sep appender xs)
|
||||||
|
(close-paren))
|
||||||
|
|
||||||
(defn quote-identifier [x & {:keys [style split] :or {split true}}]
|
(defn quote-identifier [x & {:keys [style split] :or {split true}}]
|
||||||
(let [qf (if style
|
(let [qf (if style
|
||||||
(quote-fns style)
|
(quote-fns style)
|
||||||
|
|
@ -63,12 +80,14 @@
|
||||||
(string? x) (if qf x (undasherize x))
|
(string? x) (if qf x (undasherize x))
|
||||||
:else (str x))]
|
:else (str x))]
|
||||||
(if-not qf
|
(if-not qf
|
||||||
s
|
(append-str s)
|
||||||
(let [qf* #(if (= "*" %) % (qf %))]
|
(let [qf* #(if (= "*" %) % (qf %))]
|
||||||
(if-not split
|
(if-not split
|
||||||
(qf* s)
|
(append-str (qf* s))
|
||||||
(let [parts (string/split s #"\.")]
|
(do-join
|
||||||
(string/join "." (map qf* parts))))))))
|
"."
|
||||||
|
append-str
|
||||||
|
(map qf* (string/split s #"\."))))))))
|
||||||
|
|
||||||
(def infix-fns
|
(def infix-fns
|
||||||
#{"+" "-" "*" "/" "%" "mod" "|" "&" "^"
|
#{"+" "-" "*" "/" "%" "mod" "|" "&" "^"
|
||||||
|
|
@ -89,83 +108,97 @@
|
||||||
(defmulti fn-handler (fn [op & args] op))
|
(defmulti fn-handler (fn [op & args] op))
|
||||||
|
|
||||||
(defn expand-binary-ops [op & args]
|
(defn expand-binary-ops [op & args]
|
||||||
(str "("
|
(do-paren-join
|
||||||
(string/join " AND "
|
" AND "
|
||||||
(for [[a b] (partition 2 1 args)]
|
(fn [[a b]] (fn-handler op a b))
|
||||||
(fn-handler op a b)))
|
(partition 2 1 args)))
|
||||||
")"))
|
|
||||||
|
|
||||||
(defmethod fn-handler :default [op & args]
|
(defmethod fn-handler :default [op & args]
|
||||||
(let [args (map to-sql args)]
|
(if (infix-fns op)
|
||||||
(if (infix-fns op)
|
(do-paren-join
|
||||||
(paren-wrap (string/join (str " " op " ") args))
|
(str " " op " ")
|
||||||
(str op (paren-wrap (comma-join args))))))
|
to-sql
|
||||||
|
args)
|
||||||
|
(do
|
||||||
|
(append-str op)
|
||||||
|
(do-paren-join ", " to-sql args))))
|
||||||
|
|
||||||
(defmethod fn-handler "count-distinct" [_ & args]
|
(defmethod fn-handler "count-distinct" [_ & args]
|
||||||
(str "COUNT(DISTINCT " (comma-join (map to-sql args)) ")"))
|
(append-str "COUNT(DISTINCT")
|
||||||
|
(do-join ", " to-sql args)
|
||||||
|
(close-paren)
|
||||||
|
)
|
||||||
|
|
||||||
(defmethod fn-handler "distinct-on" [_ & args]
|
(defmethod fn-handler "distinct-on" [_ & args]
|
||||||
(str "DISTINCT ON (" (comma-join (map to-sql args)) ")"))
|
(append-str "DISTINCT ON (")
|
||||||
|
(do-join ", " to-sql args)
|
||||||
|
(close-paren)
|
||||||
|
)
|
||||||
|
|
||||||
(defmethod fn-handler "cast" [_ field cast-to-type]
|
(defmethod fn-handler "cast" [_ field cast-to-type]
|
||||||
(str "CAST" (paren-wrap (str (to-sql field)
|
(append-str "CAST(")
|
||||||
" AS "
|
(to-sql field)
|
||||||
(to-sql cast-to-type)))))
|
(append-str " AS ")
|
||||||
|
(to-sql cast-to-type)
|
||||||
|
(close-paren))
|
||||||
|
|
||||||
(defmethod fn-handler "=" [_ a b & more]
|
(defmethod fn-handler "=" [_ a b & more]
|
||||||
(if (seq more)
|
(if (seq more)
|
||||||
(apply expand-binary-ops "=" a b more)
|
(apply expand-binary-ops "=" a b more)
|
||||||
(cond
|
(cond
|
||||||
(nil? a) (str (to-sql b) " IS NULL")
|
(nil? a) (do (to-sql b) (append-str " IS NULL"))
|
||||||
(nil? b) (str (to-sql a) " IS NULL")
|
(nil? b) (do (to-sql a) (append-str " IS NULL"))
|
||||||
:else (str (to-sql a) " = " (to-sql b)))))
|
:else (do (to-sql a) (append-str " = ") (to-sql b)))))
|
||||||
|
|
||||||
(defmethod fn-handler "<>" [_ a b & more]
|
(defmethod fn-handler "<>" [_ a b & more]
|
||||||
(if (seq more)
|
(if (seq more)
|
||||||
(apply expand-binary-ops "<>" a b more)
|
(apply expand-binary-ops "<>" a b more)
|
||||||
(cond
|
(cond
|
||||||
(nil? a) (str (to-sql b) " IS NOT NULL")
|
(nil? a) (do (to-sql b) (append-str " IS NOT NULL"))
|
||||||
(nil? b) (str (to-sql a) " IS NOT NULL")
|
(nil? b) (do (to-sql a) (append-str " IS NOT NULL"))
|
||||||
:else (str (to-sql a) " <> " (to-sql b)))))
|
:else (do (to-sql a) (append-str " <> ") (to-sql b)))))
|
||||||
|
|
||||||
(defmethod fn-handler "<" [_ a b & more]
|
(defmethod fn-handler "<" [_ a b & more]
|
||||||
(if (seq more)
|
(if (seq more)
|
||||||
(apply expand-binary-ops "<" a b more)
|
(apply expand-binary-ops "<" a b more)
|
||||||
(str (to-sql a) " < " (to-sql b))))
|
(do (to-sql a) (append-str " < ") (to-sql b))))
|
||||||
|
|
||||||
(defmethod fn-handler "<=" [_ a b & more]
|
(defmethod fn-handler "<=" [_ a b & more]
|
||||||
(if (seq more)
|
(if (seq more)
|
||||||
(apply expand-binary-ops "<=" a b more)
|
(apply expand-binary-ops "<=" a b more)
|
||||||
(str (to-sql a) " <= " (to-sql b))))
|
(do (to-sql a) (append-str " <= ") (to-sql b))))
|
||||||
|
|
||||||
(defmethod fn-handler ">" [_ a b & more]
|
(defmethod fn-handler ">" [_ a b & more]
|
||||||
(if (seq more)
|
(if (seq more)
|
||||||
(apply expand-binary-ops ">" a b more)
|
(apply expand-binary-ops ">" a b more)
|
||||||
(str (to-sql a) " > " (to-sql b))))
|
(do (to-sql a) (append-str " > ") (to-sql b))))
|
||||||
|
|
||||||
(defmethod fn-handler ">=" [_ a b & more]
|
(defmethod fn-handler ">=" [_ a b & more]
|
||||||
(if (seq more)
|
(if (seq more)
|
||||||
(apply expand-binary-ops ">=" a b more)
|
(apply expand-binary-ops ">=" a b more)
|
||||||
(str (to-sql a) " >= " (to-sql b))))
|
(do (to-sql a) (append-str " >= ") (to-sql b))))
|
||||||
|
|
||||||
(defmethod fn-handler "between" [_ field lower upper]
|
(defmethod fn-handler "between" [_ field lower upper]
|
||||||
(str (to-sql field) " BETWEEN " (to-sql lower) " AND " (to-sql upper)))
|
(to-sql field)
|
||||||
|
(append-str " BETWEEN ")
|
||||||
|
(to-sql lower)
|
||||||
|
(append-str " AND ")
|
||||||
|
(to-sql upper))
|
||||||
|
|
||||||
;; Handles MySql's MATCH (field) AGAINST (pattern). The third argument
|
;; Handles MySql's MATCH (field) AGAINST (pattern). The third argument
|
||||||
;; can be a set containing one or more of :boolean, :natural, or :expand.
|
;; can be a set containing one or more of :boolean, :natural, or :expand.
|
||||||
(defmethod fn-handler "match" [_ fields pattern & [opts]]
|
(defmethod fn-handler "match" [_ fields pattern & [opts]]
|
||||||
(str "MATCH ("
|
(append-str "MATCH (")
|
||||||
(comma-join
|
(do-join ", " to-sql (if (coll? fields) fields [fields]))
|
||||||
(map to-sql (if (coll? fields) fields [fields])))
|
(append-str ") AGAINST (")
|
||||||
") AGAINST ("
|
(to-sql pattern)
|
||||||
(to-sql pattern)
|
(doseq [opt opts]
|
||||||
(when (seq opts)
|
(append-str
|
||||||
(str " " (space-join (for [opt opts]
|
(case opt
|
||||||
(case opt
|
:boolean " IN BOOLEAN MODE"
|
||||||
:boolean "IN BOOLEAN MODE"
|
:natural " IN NATURAL LANGUAGE MODE"
|
||||||
:natural "IN NATURAL LANGUAGE MODE"
|
:expand " WITH QUERY EXPANSION")))
|
||||||
:expand "WITH QUERY EXPANSION")))))
|
(close-paren))
|
||||||
")"))
|
|
||||||
|
|
||||||
(def default-clause-priorities
|
(def default-clause-priorities
|
||||||
"Determines the order that clauses will be placed within generated SQL"
|
"Determines the order that clauses will be placed within generated SQL"
|
||||||
|
|
@ -226,8 +259,10 @@
|
||||||
*param-names* (atom [])
|
*param-names* (atom [])
|
||||||
*input-params* (atom params)
|
*input-params* (atom params)
|
||||||
*quote-identifier-fn* (quote-fns (:quoting opts))
|
*quote-identifier-fn* (quote-fns (:quoting opts))
|
||||||
*parameterizer* (parameterizers (or (:parameterizer opts) :jdbc))]
|
*parameterizer* (parameterizers (or (:parameterizer opts) :jdbc))
|
||||||
(let [sql-str (to-sql sql-map)]
|
*builder* (StringBuilder.)]
|
||||||
|
(to-sql sql-map)
|
||||||
|
(let [sql-str (str *builder*)]
|
||||||
(if (seq @*params*)
|
(if (seq @*params*)
|
||||||
(if (:return-param-names opts)
|
(if (:return-param-names opts)
|
||||||
[sql-str @*params* @*param-names*]
|
[sql-str @*params* @*param-names*]
|
||||||
|
|
@ -241,8 +276,10 @@
|
||||||
*param-counter* (atom 0)
|
*param-counter* (atom 0)
|
||||||
*param-names* (atom [])
|
*param-names* (atom [])
|
||||||
*quote-identifier-fn* (or (quote-fns quoting)
|
*quote-identifier-fn* (or (quote-fns quoting)
|
||||||
*quote-identifier-fn*)]
|
*quote-identifier-fn*)
|
||||||
(let [sql-str (format-predicate* pred)]
|
*builder* (StringBuilder.)]
|
||||||
|
(format-predicate* pred)
|
||||||
|
(let [sql-str (str *builder*)]
|
||||||
(if (seq @*params*)
|
(if (seq @*params*)
|
||||||
(into [sql-str] @*params*)
|
(into [sql-str] @*params*)
|
||||||
[sql-str]))))
|
[sql-str]))))
|
||||||
|
|
@ -264,24 +301,26 @@
|
||||||
clojure.lang.Symbol
|
clojure.lang.Symbol
|
||||||
(-to-sql [x] (quote-identifier x))
|
(-to-sql [x] (quote-identifier x))
|
||||||
java.lang.Number
|
java.lang.Number
|
||||||
(-to-sql [x] (str x))
|
(-to-sql [x] (append-obj x))
|
||||||
java.lang.Boolean
|
java.lang.Boolean
|
||||||
(-to-sql [x]
|
(-to-sql [x]
|
||||||
(if x "TRUE" "FALSE"))
|
(append-str (if x "TRUE" "FALSE")))
|
||||||
clojure.lang.Sequential
|
clojure.lang.Sequential
|
||||||
(-to-sql [x]
|
(-to-sql [x]
|
||||||
(if *fn-context?*
|
(if *fn-context?*
|
||||||
;; list argument in fn call
|
;; list argument in fn call
|
||||||
(paren-wrap (comma-join (map to-sql x)))
|
(do-paren-join ", " to-sql x)
|
||||||
;; alias
|
;; alias
|
||||||
(str (to-sql (first x))
|
(do
|
||||||
; Omit AS in FROM, JOIN, etc. - Oracle doesn't allow it
|
(to-sql (first x))
|
||||||
(if (= :select *clause*)
|
; Omit AS in FROM, JOIN, etc. - Oracle doesn't allow it
|
||||||
" AS "
|
(append-str
|
||||||
" ")
|
(if (= :select *clause*)
|
||||||
(if (string? (second x))
|
" AS "
|
||||||
(quote-identifier (second x))
|
" "))
|
||||||
(to-sql (second x))))))
|
(if (string? (second x))
|
||||||
|
(quote-identifier (second x))
|
||||||
|
(to-sql (second x))))))
|
||||||
SqlCall
|
SqlCall
|
||||||
(-to-sql [x]
|
(-to-sql [x]
|
||||||
(binding [*fn-context?* true]
|
(binding [*fn-context?* true]
|
||||||
|
|
@ -289,20 +328,21 @@
|
||||||
fn-name (fn-aliases fn-name fn-name)]
|
fn-name (fn-aliases fn-name fn-name)]
|
||||||
(apply fn-handler fn-name (.args x)))))
|
(apply fn-handler fn-name (.args x)))))
|
||||||
SqlRaw
|
SqlRaw
|
||||||
(-to-sql [x] (.s x))
|
(-to-sql [x] (append-str (.s x)))
|
||||||
clojure.lang.IPersistentMap
|
clojure.lang.IPersistentMap
|
||||||
(-to-sql [x]
|
(-to-sql [x]
|
||||||
(let [clause-ops (sort-clauses (keys x))
|
(when *subquery?*
|
||||||
sql-str (binding [*subquery?* true
|
(append-char \())
|
||||||
*fn-context?* false]
|
(binding [*subquery?* true
|
||||||
(space-join
|
*fn-context?* false]
|
||||||
(map (comp #(-format-clause % x) #(find x %))
|
(do-join
|
||||||
clause-ops)))]
|
" "
|
||||||
(if *subquery?*
|
(comp #(-format-clause % x) #(find x %))
|
||||||
(paren-wrap sql-str)
|
(sort-clauses (keys x))))
|
||||||
sql-str)))
|
(when *subquery?*
|
||||||
|
(close-paren)))
|
||||||
nil
|
nil
|
||||||
(-to-sql [x] "NULL")
|
(-to-sql [x] (append-str "NULL"))
|
||||||
Object
|
Object
|
||||||
(-to-sql [x]
|
(-to-sql [x]
|
||||||
(let [[x pname] (if (instance? SqlParam x)
|
(let [[x pname] (if (instance? SqlParam x)
|
||||||
|
|
@ -316,7 +356,7 @@
|
||||||
[x (keyword (str "_" (swap! *param-counter* inc)))])]
|
[x (keyword (str "_" (swap! *param-counter* inc)))])]
|
||||||
(swap! *param-names* conj pname)
|
(swap! *param-names* conj pname)
|
||||||
(swap! *params* conj x)
|
(swap! *params* conj x)
|
||||||
(*parameterizer*))))
|
(append-obj (*parameterizer*)))))
|
||||||
|
|
||||||
(defn sqlable? [x]
|
(defn sqlable? [x]
|
||||||
(satisfies? ToSql x))
|
(satisfies? ToSql x))
|
||||||
|
|
@ -332,11 +372,12 @@
|
||||||
(let [[op & args] pred
|
(let [[op & args] pred
|
||||||
op-name (name op)]
|
op-name (name op)]
|
||||||
(if (= "not" op-name)
|
(if (= "not" op-name)
|
||||||
(str "NOT " (format-predicate* (first args)))
|
(do (append-str "NOT ") (format-predicate* (first args)))
|
||||||
(if (#{"and" "or" "xor"} op-name)
|
(if (#{"and" "or" "xor"} op-name)
|
||||||
(paren-wrap
|
(do-paren-join
|
||||||
(string/join (str " " (string/upper-case op-name) " ")
|
(str " " (string/upper-case op-name) " ")
|
||||||
(map format-predicate* args)))
|
format-predicate*
|
||||||
|
args)
|
||||||
(to-sql (apply call pred)))))))
|
(to-sql (apply call pred)))))))
|
||||||
|
|
||||||
(defmulti format-clause
|
(defmulti format-clause
|
||||||
|
|
@ -348,111 +389,155 @@
|
||||||
(binding [*clause* (key clause)]
|
(binding [*clause* (key clause)]
|
||||||
(format-clause clause _)))
|
(format-clause clause _)))
|
||||||
|
|
||||||
(defmethod format-clause :default [& _]
|
(defmethod format-clause :default [& _])
|
||||||
"")
|
|
||||||
|
|
||||||
(defmethod format-clause :select [[_ fields] sql-map]
|
(defmethod format-clause :select [[_ fields] sql-map]
|
||||||
(str "SELECT "
|
(append-str "SELECT ")
|
||||||
(when (:modifiers sql-map)
|
(doseq [m (:modifiers sql-map)]
|
||||||
(str (space-join (map (comp string/upper-case name)
|
(append-str (string/upper-case (name m)))
|
||||||
(:modifiers sql-map)))
|
(append-char \space))
|
||||||
" "))
|
(do-join ", " to-sql fields))
|
||||||
(comma-join (map to-sql fields))))
|
|
||||||
|
|
||||||
(defmethod format-clause :from [[_ tables] _]
|
(defmethod format-clause :from [[_ tables] _]
|
||||||
(str "FROM " (comma-join (map to-sql tables))))
|
(append-str "FROM ")
|
||||||
|
(do-join ", " to-sql tables))
|
||||||
|
|
||||||
(defmethod format-clause :where [[_ pred] _]
|
(defmethod format-clause :where [[_ pred] _]
|
||||||
(str "WHERE " (format-predicate* pred)))
|
(append-str "WHERE ")
|
||||||
|
(format-predicate* pred))
|
||||||
|
|
||||||
(defn format-join [type table pred]
|
(defn format-join [type table pred]
|
||||||
(str (when type
|
(when type
|
||||||
(str (string/upper-case (name type)) " "))
|
(append-str (string/upper-case (name type)))
|
||||||
"JOIN " (to-sql table)
|
(append-char \space))
|
||||||
" ON " (format-predicate* pred)))
|
(append-str "JOIN ")
|
||||||
|
(to-sql table)
|
||||||
|
(append-str " ON ")
|
||||||
|
(format-predicate* pred))
|
||||||
|
|
||||||
(defmethod format-clause :join [[_ join-groups] _]
|
(defmethod format-clause :join [[_ join-groups] _]
|
||||||
(space-join (map #(apply format-join :inner %)
|
(do-join
|
||||||
(partition 2 join-groups))))
|
" "
|
||||||
|
#(apply format-join :inner %)
|
||||||
|
(partition 2 join-groups)))
|
||||||
|
|
||||||
(defmethod format-clause :left-join [[_ join-groups] _]
|
(defmethod format-clause :left-join [[_ join-groups] _]
|
||||||
(space-join (map #(apply format-join :left %)
|
(do-join
|
||||||
(partition 2 join-groups))))
|
" "
|
||||||
|
#(apply format-join :left %)
|
||||||
|
(partition 2 join-groups)))
|
||||||
|
|
||||||
(defmethod format-clause :right-join [[_ join-groups] _]
|
(defmethod format-clause :right-join [[_ join-groups] _]
|
||||||
(space-join (map #(apply format-join :right %)
|
(do-join
|
||||||
(partition 2 join-groups))))
|
" "
|
||||||
|
#(apply format-join :right %)
|
||||||
|
(partition 2 join-groups)))
|
||||||
|
|
||||||
(defmethod format-clause :full-join [[_ join-groups] _]
|
(defmethod format-clause :full-join [[_ join-groups] _]
|
||||||
(space-join (map #(apply format-join :full %)
|
(do-join
|
||||||
(partition 2 join-groups))))
|
" "
|
||||||
|
#(apply format-join :full %)
|
||||||
|
(partition 2 join-groups)))
|
||||||
|
|
||||||
(defmethod format-clause :group-by [[_ fields] _]
|
(defmethod format-clause :group-by [[_ fields] _]
|
||||||
(str "GROUP BY " (comma-join (map to-sql fields))))
|
(append-str "GROUP BY ")
|
||||||
|
(do-join ", " to-sql fields))
|
||||||
|
|
||||||
(defmethod format-clause :having [[_ pred] _]
|
(defmethod format-clause :having [[_ pred] _]
|
||||||
(str "HAVING " (format-predicate* pred)))
|
(append-str "HAVING ")
|
||||||
|
(format-predicate* pred))
|
||||||
|
|
||||||
(defmethod format-clause :order-by [[_ fields] _]
|
(defmethod format-clause :order-by [[_ fields] _]
|
||||||
(str "ORDER BY "
|
(append-str "ORDER BY ")
|
||||||
(comma-join (for [field fields]
|
(do-join
|
||||||
(if (sequential? field)
|
", "
|
||||||
(let [[field order] field]
|
(fn [field]
|
||||||
(str (to-sql field) " " (if (= :desc order)
|
(if (sequential? field)
|
||||||
"DESC" "ASC")))
|
(let [[field order] field]
|
||||||
(to-sql field))))))
|
(to-sql field)
|
||||||
|
(append-str
|
||||||
|
(if (= :desc order) " DESC" " ASC")))
|
||||||
|
(to-sql field)))
|
||||||
|
fields))
|
||||||
|
|
||||||
(defmethod format-clause :limit [[_ limit] _]
|
(defmethod format-clause :limit [[_ limit] _]
|
||||||
(str "LIMIT " (to-sql limit)))
|
(append-str "LIMIT ")
|
||||||
|
(to-sql limit))
|
||||||
|
|
||||||
(defmethod format-clause :offset [[_ offset] _]
|
(defmethod format-clause :offset [[_ offset] _]
|
||||||
(str "OFFSET " (to-sql offset)))
|
(append-str "OFFSET ")
|
||||||
|
(to-sql offset))
|
||||||
|
|
||||||
(defmethod format-clause :insert-into [[_ table] _]
|
(defmethod format-clause :insert-into [[_ table] _]
|
||||||
|
(append-str "INSERT INTO ")
|
||||||
(if (and (sequential? table) (sequential? (first table)))
|
(if (and (sequential? table) (sequential? (first table)))
|
||||||
(str "INSERT INTO "
|
(do
|
||||||
(to-sql (ffirst table))
|
(to-sql (ffirst table))
|
||||||
" (" (comma-join (map to-sql (second (first table)))) ") "
|
(append-char \space)
|
||||||
(to-sql (second table)))
|
(do-paren-join
|
||||||
(str "INSERT INTO " (to-sql table))))
|
", "
|
||||||
|
to-sql
|
||||||
|
(second (first table)))
|
||||||
|
(append-char \space)
|
||||||
|
(to-sql (second table)))
|
||||||
|
(to-sql table)))
|
||||||
|
|
||||||
(defmethod format-clause :columns [[_ fields] _]
|
(defmethod format-clause :columns [[_ fields] _]
|
||||||
(str "(" (comma-join (map to-sql fields)) ")"))
|
(do-paren-join ", " to-sql fields))
|
||||||
|
|
||||||
(defmethod format-clause :values [[_ values] _]
|
(defmethod format-clause :values [[_ values] _]
|
||||||
(if (sequential? (first values))
|
(if (sequential? (first values))
|
||||||
(str "VALUES " (comma-join (for [x values]
|
(do
|
||||||
(str "(" (comma-join (map to-sql x)) ")"))))
|
(append-str "VALUES ")
|
||||||
(str
|
(do-join
|
||||||
"(" (comma-join (map to-sql (keys (first values)))) ") VALUES "
|
", "
|
||||||
(comma-join (for [x values]
|
#(do-paren-join to-sql %)
|
||||||
(str "(" (comma-join (map to-sql (vals x))) ")"))))))
|
values))
|
||||||
|
(do
|
||||||
|
(do-paren-join ", " to-sql (keys (first values)))
|
||||||
|
(append-str " VALUES ")
|
||||||
|
(do-join
|
||||||
|
", "
|
||||||
|
#(do-paren-join ", " to-sql (vals %))
|
||||||
|
values))))
|
||||||
|
|
||||||
(defmethod format-clause :query-values [[_ query-values] _]
|
(defmethod format-clause :query-values [[_ query-values] _]
|
||||||
(to-sql query-values))
|
(to-sql query-values))
|
||||||
|
|
||||||
(defmethod format-clause :update [[_ table] _]
|
(defmethod format-clause :update [[_ table] _]
|
||||||
(str "UPDATE " (to-sql table)))
|
(append-str "UPDATE ")
|
||||||
|
(to-sql table))
|
||||||
|
|
||||||
(defmethod format-clause :set [[_ values] _]
|
(defmethod format-clause :set [[_ values] _]
|
||||||
(str "SET " (comma-join (for [[k v] values]
|
(append-str "SET ")
|
||||||
(str (to-sql k) " = " (to-sql v))))))
|
(do-join
|
||||||
|
", "
|
||||||
|
(fn [[k v]]
|
||||||
|
(to-sql k)
|
||||||
|
(append-str " = ")
|
||||||
|
(to-sql v))
|
||||||
|
values))
|
||||||
|
|
||||||
(defmethod format-clause :delete-from [[_ table] _]
|
(defmethod format-clause :delete-from [[_ table] _]
|
||||||
(str "DELETE FROM " (to-sql table)))
|
(append-str "DELETE FROM ")
|
||||||
|
(to-sql table))
|
||||||
|
|
||||||
(defn cte->sql
|
(defn cte->sql
|
||||||
[[cte-name query]]
|
[[cte-name query]]
|
||||||
(str (to-sql cte-name) " AS " (to-sql query)))
|
(to-sql cte-name)
|
||||||
|
(append-str " AS ")
|
||||||
|
(to-sql query))
|
||||||
|
|
||||||
(defmethod format-clause :with [[_ ctes] _]
|
(defmethod format-clause :with [[_ ctes] _]
|
||||||
(str "WITH " (comma-join (map cte->sql ctes))))
|
(append-str "WITH ")
|
||||||
|
(do-join ", " cte->sql ctes))
|
||||||
|
|
||||||
(defmethod format-clause :with-recursive [[_ ctes] _]
|
(defmethod format-clause :with-recursive [[_ ctes] _]
|
||||||
(str "WITH RECURSIVE " (comma-join (map cte->sql ctes))))
|
(append-str "WITH RECURSIVE ")
|
||||||
|
(do-join ", " cte->sql ctes))
|
||||||
|
|
||||||
(defmethod format-clause :union [[_ maps] _]
|
(defmethod format-clause :union [[_ maps] _]
|
||||||
(string/join " UNION " (map to-sql maps)))
|
(do-join " UNION " to-sql maps))
|
||||||
|
|
||||||
(defmethod format-clause :union-all [[_ maps] _]
|
(defmethod format-clause :union-all [[_ maps] _]
|
||||||
(string/join " UNION ALL " (map to-sql maps)))
|
(do-join " UNION ALL " to-sql maps))
|
||||||
|
|
|
||||||
|
|
@ -3,33 +3,42 @@
|
||||||
(:require [clojure.test :refer [deftest testing is are]]
|
(:require [clojure.test :refer [deftest testing is are]]
|
||||||
[honeysql.format :refer :all]))
|
[honeysql.format :refer :all]))
|
||||||
|
|
||||||
|
(defmacro with-builder [& forms]
|
||||||
|
`(binding [*builder* (StringBuilder.)]
|
||||||
|
~@forms
|
||||||
|
(str *builder*)))
|
||||||
|
|
||||||
(deftest test-quote
|
(deftest test-quote
|
||||||
(are
|
(are
|
||||||
[qx res]
|
[qx res]
|
||||||
(= (apply quote-identifier "foo.bar.baz" qx) res)
|
(= (with-builder (apply quote-identifier "foo.bar.baz" qx)) res)
|
||||||
[] "foo.bar.baz"
|
[] "foo.bar.baz"
|
||||||
[:style :mysql] "`foo`.`bar`.`baz`"
|
[:style :mysql] "`foo`.`bar`.`baz`"
|
||||||
[:style :mysql :split false] "`foo.bar.baz`")
|
[:style :mysql :split false] "`foo.bar.baz`")
|
||||||
(are
|
(are
|
||||||
[x res]
|
[x res]
|
||||||
(= (quote-identifier x) res)
|
(= (with-builder (quote-identifier x)) res)
|
||||||
3 "3"
|
3 "3"
|
||||||
'foo "foo"
|
'foo "foo"
|
||||||
:foo-bar "foo_bar")
|
:foo-bar "foo_bar")
|
||||||
(is (= (quote-identifier "*" :style :ansi) "*")))
|
(is (= (with-builder (quote-identifier "*" :style :ansi)) "*")))
|
||||||
|
|
||||||
|
(defn make-clause [& args]
|
||||||
|
(with-builder
|
||||||
|
(apply format-clause args)))
|
||||||
|
|
||||||
(deftest test-cte
|
(deftest test-cte
|
||||||
(is (= (format-clause
|
(is (= (make-clause
|
||||||
(first {:with [[:query {:select [:foo] :from [:bar]}]]}) nil)
|
(first {:with [[:query {:select [:foo] :from [:bar]}]]}) nil)
|
||||||
"WITH query AS SELECT foo FROM bar"))
|
"WITH query AS SELECT foo FROM bar"))
|
||||||
(is (= (format-clause
|
(is (= (make-clause
|
||||||
(first {:with-recursive [[:query {:select [:foo] :from [:bar]}]]}) nil)
|
(first {:with-recursive [[:query {:select [:foo] :from [:bar]}]]}) nil)
|
||||||
"WITH RECURSIVE query AS SELECT foo FROM bar")))
|
"WITH RECURSIVE query AS SELECT foo FROM bar")))
|
||||||
|
|
||||||
(deftest insert-into
|
(deftest insert-into
|
||||||
(is (= (format-clause (first {:insert-into :foo}) nil)
|
(is (= (make-clause (first {:insert-into :foo}) nil)
|
||||||
"INSERT INTO foo"))
|
"INSERT INTO foo"))
|
||||||
(is (= (format-clause (first {:insert-into [:foo {:select [:bar] :from [:baz]}]}) nil)
|
(is (= (make-clause (first {:insert-into [:foo {:select [:bar] :from [:baz]}]}) nil)
|
||||||
"INSERT INTO foo SELECT bar FROM baz"))
|
"INSERT INTO foo SELECT bar FROM baz"))
|
||||||
(is (= (format-clause (first {:insert-into [[:foo [:a :b :c]] {:select [:d :e :f] :from [:baz]}]}) nil)
|
(is (= (make-clause (first {:insert-into [[:foo [:a :b :c]] {:select [:d :e :f] :from [:baz]}]}) nil)
|
||||||
"INSERT INTO foo (a, b, c) SELECT d, e, f FROM baz")))
|
"INSERT INTO foo (a, b, c) SELECT d, e, f FROM baz")))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue