Merge pull request #545 from alexander-yakushev/perf-opt

Introduce more efficient implementations of str and join
This commit is contained in:
Sean Corfield 2024-09-26 14:17:12 -07:00 committed by GitHub
commit 8c93e287ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 229 additions and 106 deletions

View file

@ -27,10 +27,11 @@
and optionally set a global `:quoted` option. and optionally set a global `:quoted` option.
* `sql-kw` -- turns a Clojure keyword (or symbol) into SQL code (makes * `sql-kw` -- turns a Clojure keyword (or symbol) into SQL code (makes
it uppercase and replaces - with space). " it uppercase and replaces - with space). "
(:refer-clojure :exclude [format]) (:refer-clojure :exclude [format str])
(:require [clojure.string :as str] (:require [clojure.string :as str]
#?(:clj [clojure.template]) #?(:clj [clojure.template])
[honey.sql.protocols :as p])) [honey.sql.protocols :as p]
[honey.sql.util :refer [str join]]))
;; default formatting for known clauses ;; default formatting for known clauses
@ -317,7 +318,7 @@
[%] [%]
(str/split % #"\.")))) (str/split % #"\."))))
parts (parts-fn col-e) parts (parts-fn col-e)
entity (str/join "." (map #(cond-> % (not= "*" %) (quote-fn)) parts))] entity (join "." (map #(cond-> % (not= "*" %) (quote-fn))) parts)]
(suspicious-entity-check entity) (suspicious-entity-check entity)
entity)) entity))
@ -376,7 +377,7 @@
#?(:cljs Symbol :default clojure.lang.Symbol) #?(:cljs Symbol :default clojure.lang.Symbol)
(sqlize [x] (sql-kw x)) (sqlize [x] (sql-kw x))
#?(:cljs PersistentVector :default clojure.lang.IPersistentVector) #?(:cljs PersistentVector :default clojure.lang.IPersistentVector)
(sqlize [x] (str "[" (str/join ", " (map p/sqlize x)) "]")) (sqlize [x] (str "[" (join ", " (map p/sqlize) x) "]"))
#?@(:clj [java.util.UUID #?@(:clj [java.util.UUID
;; issue 385: quoted UUIDs for PostgreSQL/ANSI ;; issue 385: quoted UUIDs for PostgreSQL/ANSI
(sqlize [x] (str \' x \'))]) (sqlize [x] (str \' x \'))])
@ -420,10 +421,10 @@
;; qualified column names can be used: ;; qualified column names can be used:
(let [c (cond-> (str x) (keyword? x) (subs 1))] (let [c (cond-> (str x) (keyword? x) (subs 1))]
(cond (= \% (first c)) (cond (= \% (first c))
(let [[f & args] (str/split (subs c 1) #"\.") (let [[f & args] (str/split (subs c 1) #"\.")]
quoted-args (map #(format-entity (keyword %) opts) args)] [(str (format-fn-name f) "("
[(str (format-fn-name f) (join ", " (map #(format-entity (keyword %) opts)) args)
"(" (str/join ", " quoted-args) ")")]) ")")])
(= \? (first c)) (= \? (first c))
(let [k (keyword (subs c 1))] (let [k (keyword (subs c 1))]
(cond *inline* (cond *inline*
@ -483,7 +484,7 @@
(cond (and (ident? k) (= "except" (name k)) arg) (cond (and (ident? k) (= "except" (name k)) arg)
(let [[sqls params] (let [[sqls params]
(format-expr-list arg {:aliased true})] (format-expr-list arg {:aliased true})]
[(str (sql-kw k) " (" (str/join ", " sqls) ")") [(str (sql-kw k) " (" (join ", " sqls) ")")
params]) params])
(and (ident? k) (= "replace" (name k)) arg) (and (ident? k) (= "replace" (name k)) arg)
(let [[sql & params] (format-selects-common nil true arg)] (let [[sql & params] (format-selects-common nil true arg)]
@ -551,7 +552,7 @@
(into params params') (into params params')
more more
fmt)) fmt))
(into [(str/join " " sqls)] params))))) (into [(join " " sqls)] params)))))
(comment (comment
(format-temporal [:for :some-time :all]) (format-temporal [:for :some-time :all])
@ -587,7 +588,7 @@
:end-line :end-column] :end-line :end-column]
*ignored-metadata*)))] *ignored-metadata*)))]
(when (seq items) (when (seq items)
(str/join (str sep " ") (mapv sql-kw items)))))) (join (str sep " ") (map sql-kw) items)))))
(comment (comment
(format-meta ^{:foo true :bar :baz :original {:line 1} :top 10} []) (format-meta ^{:foo true :bar :baz :original {:line 1} :top 10} [])
@ -615,7 +616,7 @@
[sql' & params'] (when alias [sql' & params'] (when alias
(if (sequential? alias) (if (sequential? alias)
(let [[sqls params] (format-expr-list alias {:aliased true})] (let [[sqls params] (format-expr-list alias {:aliased true})]
(into [(str/join " " sqls)] params)) (into [(join " " sqls)] params))
(format-selectable-dsl alias {:aliased true}))) (format-selectable-dsl alias {:aliased true})))
[sql'' & params''] (when temporal [sql'' & params''] (when temporal
(format-temporal temporal))] (format-temporal temporal))]
@ -665,7 +666,7 @@
(defn- format-on-set-op [k xs] (defn- format-on-set-op [k xs]
(let [[sqls params] (reduce-sql (map #(format-dsl %) xs))] (let [[sqls params] (reduce-sql (map #(format-dsl %) xs))]
(into [(str/join (str " " (sql-kw k) " ") sqls)] params))) (into [(join (str " " (sql-kw k) " ") sqls)] params)))
(defn- inline-kw? (defn- inline-kw?
"Return true if the expression should be treated as an inline SQL keeyword." "Return true if the expression should be treated as an inline SQL keeyword."
@ -741,7 +742,7 @@
(contains-clause? :replace-into))) (contains-clause? :replace-into)))
[] []
(let [[sqls params] (format-expr-list xs {:drop-ns true})] (let [[sqls params] (format-expr-list xs {:drop-ns true})]
(into [(str "(" (str/join ", " sqls) ")")] params)))) (into [(str "(" (join ", " sqls) ")")] params))))
(defn- format-selects-common [prefix as xs] (defn- format-selects-common [prefix as xs]
(let [qualifier (format-meta xs) (let [qualifier (format-meta xs)
@ -754,7 +755,7 @@
(when (empty? xs) (when (empty? xs)
(throw (ex-info (str prefix " empty column list is illegal") (throw (ex-info (str prefix " empty column list is illegal")
{:clause (into [prefix] xs)})))) {:clause (into [prefix] xs)}))))
(into [(str (when prefix (str prefix " ")) (str/join ", " sqls))] params)) (into [(str (when prefix (str prefix " ")) (join ", " sqls))] params))
(let [[sql & params] (format-selectable-dsl xs {:as as})] (let [[sql & params] (format-selectable-dsl xs {:as as})]
(into [(str (when prefix (str prefix " ")) sql)] params))))) (into [(str (when prefix (str prefix " ")) sql)] params)))))
@ -800,7 +801,7 @@
(format-selects-common (format-selects-common
(str (sql-kw k) "(" sql ")" (str (sql-kw k) "(" sql ")"
(when (seq parts) " ") (when (seq parts) " ")
(str/join " " (map sql-kw parts))) (join " " (map sql-kw) parts))
true true
cols)] cols)]
(-> [sql'] (into params) (into params')))) (-> [sql'] (into params) (into params'))))
@ -849,7 +850,7 @@
params (into params) params (into params)
params' (into params'))))) params' (into params')))))
xs))] xs))]
(into [(str (sql-kw k) " " (str/join ", " sqls))] params))) (into [(str (sql-kw k) " " (join ", " sqls))] params)))
(defn- format-selector [k xs] (defn- format-selector [k xs]
(format-selects k [xs])) (format-selects k [xs]))
@ -881,7 +882,7 @@
" " " "
(cond (seq cols) (cond (seq cols)
(str "(" (str "("
(str/join ", " c-sqls) (join ", " c-sqls)
") ") ") ")
(seq cols') (seq cols')
(str cols-sql' " ")) (str cols-sql' " "))
@ -897,7 +898,7 @@
[c-sqls c-params] (reduce-sql (map #'format-entity-alias cols))] [c-sqls c-params] (reduce-sql (map #'format-entity-alias cols))]
(-> [(str (sql-kw k) " " t-sql (-> [(str (sql-kw k) " " t-sql
" (" " ("
(str/join ", " c-sqls) (join ", " c-sqls)
")" ")"
overriding)] overriding)]
(into t-params) (into t-params)
@ -937,7 +938,7 @@
[(conj sqls [(conj sqls
"USING" "USING"
(str "(" (str "("
(str/join ", " u-sqls) (join ", " u-sqls)
")")) ")"))
(-> params (into params-j) (into u-params))]) (-> params (into params-j) (into u-params))])
(let [[sql & params'] (when e (format-expr e))] (let [[sql & params'] (when e (format-expr e))]
@ -947,7 +948,7 @@
(into params'))])))) (into params'))]))))
[[] []] [[] []]
(partition-all 2 clauses))] (partition-all 2 clauses))]
(into [(str/join " " sqls)] params))) (into [(join " " sqls)] params)))
(def ^:private join-by-aliases (def ^:private join-by-aliases
"Map of shorthand to longhand join names." "Map of shorthand to longhand join names."
@ -989,7 +990,7 @@
[(conj sqls sql') (into params params')]))) [(conj sqls sql') (into params params')])))
[[] []] [[] []]
(partition 2 joins))] (partition 2 joins))]
(into [(str/join " " sqls)] params)))) (into [(join " " sqls)] params))))
(defn- format-on-expr [k e] (defn- format-on-expr [k e]
(if (or (not (sequential? e)) (seq e)) (if (or (not (sequential? e)) (seq e))
@ -999,7 +1000,7 @@
(defn- format-group-by [k xs] (defn- format-group-by [k xs]
(let [[sqls params] (format-expr-list (ensure-sequential xs))] (let [[sqls params] (format-expr-list (ensure-sequential xs))]
(into [(str (sql-kw k) " " (str/join ", " sqls))] params))) (into [(str (sql-kw k) " " (join ", " sqls))] params)))
(defn- format-order-by [k xs] (defn- format-order-by [k xs]
(let [xs (ensure-sequential xs) (let [xs (ensure-sequential xs)
@ -1007,7 +1008,7 @@
[sqls params] [sqls params]
(format-expr-list (map #(if (sequential? %) (first %) %) xs))] (format-expr-list (map #(if (sequential? %) (first %) %) xs))]
(into [(str (sql-kw k) " " (into [(str (sql-kw k) " "
(str/join ", " (map (fn [sql dir] (join ", " (map (fn [sql dir]
(str sql " " (sql-kw (or dir :asc)))) (str sql " " (sql-kw (or dir :asc))))
sqls sqls
dirs)))] params))) dirs)))] params)))
@ -1022,7 +1023,7 @@
(str " " (sql-kw tables)) (str " " (sql-kw tables))
(sequential? tables) (sequential? tables)
(str " OF " (str " OF "
(str/join ", " (map #'format-entity tables))) (join ", " (map #'format-entity) tables))
:else :else
(str " OF " (format-entity tables))) (str " OF " (format-entity tables)))
(when nowait (when nowait
@ -1040,8 +1041,7 @@
cols (if (= (set cols-1) cols-n) cols-1 cols-n)] cols (if (= (set cols-1) cols-n) cols-1 cols-n)]
[cols (when-not skip-cols-sql [cols (when-not skip-cols-sql
(str "(" (str "("
(str/join ", " (join ", " (map #(format-entity % {:drop-ns true})) cols)
(map #(format-entity % {:drop-ns true}) cols))
")"))])))) ")"))]))))
(defn- format-values [k xs] (defn- format-values [k xs]
@ -1066,7 +1066,7 @@
(reduce (fn [[sql params] [sqls' params']] (reduce (fn [[sql params] [sqls' params']]
[(conj sql [(conj sql
(if (sequential? sqls') (if (sequential? sqls')
(str "(" (str/join ", " sqls') ")") (str "(" (join ", " sqls') ")")
sqls')) sqls'))
(into params params')]) (into params params')])
[[] []] [[] []]
@ -1074,7 +1074,7 @@
(format-expr-list %) (format-expr-list %)
[(sql-kw %)]) [(sql-kw %)])
xs'))] xs'))]
(into [(str (sql-kw k) " " (str/join ", " sqls))] params)) (into [(str (sql-kw k) " " (join ", " sqls))] params))
(map? first-xs) (map? first-xs)
;; [{:a 1 :b 2 :c 3}] ;; [{:a 1 :b 2 :c 3}]
@ -1086,7 +1086,7 @@
(reduce (fn [[sql params] [sqls' params']] (reduce (fn [[sql params] [sqls' params']]
[(conj sql [(conj sql
(if (sequential? sqls') (if (sequential? sqls')
(str "(" (str/join ", " sqls') ")") (str "(" (join ", " sqls') ")")
sqls')) sqls'))
(if params' (into params params') params')]) (if params' (into params params') params')])
[[] []] [[] []]
@ -1107,7 +1107,7 @@
(str cols-sql " ")) (str cols-sql " "))
(sql-kw k) (sql-kw k)
" " " "
(str/join ", " sqls))] (join ", " sqls))]
params)) params))
:else :else
@ -1126,7 +1126,7 @@
(if params' (into params params') params)])) (if params' (into params params') params)]))
[[] []] [[] []]
xs)] xs)]
(into [(str (sql-kw k) " " (str/join ", " sqls))] params))) (into [(str (sql-kw k) " " (join ", " sqls))] params)))
(defn- format-on-conflict [k x] (defn- format-on-conflict [k x]
(if (sequential? x) (if (sequential? x)
@ -1144,7 +1144,7 @@
(format-dsl clause))] (format-dsl clause))]
(-> [(str (sql-kw k) (-> [(str (sql-kw k)
(when (pos? n) (when (pos? n)
(str " (" (str/join ", " sqls) ")")) (str " (" (join ", " sqls) ")"))
(when sql (when sql
(str " " sql)))] (str " " sql)))]
(into expr-params) (into expr-params)
@ -1159,11 +1159,11 @@
(if (map? fields) (if (map? fields)
(format-set-exprs k fields) (format-set-exprs k fields)
[(str (sql-kw k) " " [(str (sql-kw k) " "
(str/join ", " (join ", "
(map (fn [e] (map (fn [e]
(let [e (format-entity e {:drop-ns true})] (let [e (format-entity e {:drop-ns true})]
(str e " = EXCLUDED." e))) (str e " = EXCLUDED." e))))
fields)))]) fields))])
where (or (:where x) ('where x)) where (or (:where x) ('where x))
[sql & params] (when where (format-dsl {:where where}))] [sql & params] (when where (format-dsl {:where where}))]
(-> [(str sets (when sql (str " " sql)))] (-> [(str sets (when sql (str " " sql)))]
@ -1199,7 +1199,9 @@
(if (sequential? x) (if (sequential? x)
[(str (sql-kw k) " " (format-entity (first x)) [(str (sql-kw k) " " (format-entity (first x))
(when-let [clauses (next x)] (when-let [clauses (next x)]
(str " " (str/join ", " (map #(format-simple-clause % "column/index operations") clauses)))))] (str " " (join ", "
(map #(format-simple-clause % "column/index operations"))
clauses))))]
[(str (sql-kw k) " " (format-entity x))])) [(str (sql-kw k) " " (format-entity x))]))
(def ^:private special-ddl-keywords (def ^:private special-ddl-keywords
@ -1223,12 +1225,12 @@
(cond (map? opt) (cond (map? opt)
(format-simple-clause opt context) (format-simple-clause opt context)
(sequential? opt) (sequential? opt)
(str/join " " (join " "
(map (fn [e] (map (fn [e]
(if (ident? e) (if (ident? e)
(sql-kw-ddl e) (sql-kw-ddl e)
(format-simple-expr e context))) (format-simple-expr e context))))
opt)) opt)
(ident? opt) (ident? opt)
(sql-kw-ddl opt) (sql-kw-ddl opt)
:else :else
@ -1252,7 +1254,7 @@
[(cons ine (butlast (butlast coll))) (last (butlast coll)) nil] [(cons ine (butlast (butlast coll))) (last (butlast coll)) nil]
:else :else
[(butlast coll) (last coll) nil]))] [(butlast coll) (last coll) nil]))]
(into [(str/join " " (map sql-kw prequel)) (into [(join " " (map sql-kw) prequel)
(when table (when table
(let [[v & more] (format-var table)] (let [[v & more] (format-var table)]
(when (seq more) (when (seq more)
@ -1270,7 +1272,7 @@
[pre table ine options] (destructure-ddl-item [table options] "truncate")] [pre table ine options] (destructure-ddl-item [table options] "truncate")]
(when (seq pre) (throw (ex-info "TRUNCATE syntax error" {:unexpected pre}))) (when (seq pre) (throw (ex-info "TRUNCATE syntax error" {:unexpected pre})))
(when (seq ine) (throw (ex-info "TRUNCATE syntax error" {:unexpected ine}))) (when (seq ine) (throw (ex-info "TRUNCATE syntax error" {:unexpected ine})))
[(str/join " " (cond-> ["TRUNCATE TABLE" table] [(join " " (cond-> ["TRUNCATE TABLE" table]
(seq options) (seq options)
(conj options)))])) (conj options)))]))
@ -1285,7 +1287,7 @@
(defn- format-create [q k item as] (defn- format-create [q k item as]
(let [[pre entity ine & more] (let [[pre entity ine & more]
(destructure-ddl-item item (str (sql-kw q) " options"))] (destructure-ddl-item item (str (sql-kw q) " options"))]
[(str/join " " (remove nil? [(join " " (remove nil?)
(-> [(sql-kw q) (-> [(sql-kw q)
(when (and (= :create q) (seq pre)) pre) (when (and (= :create q) (seq pre)) pre)
(sql-kw k) (sql-kw k)
@ -1293,7 +1295,7 @@
(when (and (= :refresh q) (seq pre)) pre) (when (and (= :refresh q) (seq pre)) pre)
entity] entity]
(into more) (into more)
(conj (when as (sql-kw as))))))])) (conj (when as (sql-kw as)))))]))
(defn- format-create-index [k clauses] (defn- format-create-index [k clauses]
(let [[index-spec [table & exprs]] clauses (let [[index-spec [table & exprs]] clauses
@ -1302,20 +1304,19 @@
exprs exprs
(cons nil exprs)) (cons nil exprs))
[sqls params] (format-expr-list exprs)] [sqls params] (format-expr-list exprs)]
(into [(str/join " " (remove empty? (into [(join " " (remove empty?)
(-> ["CREATE" pre "INDEX" ine entity (-> ["CREATE" pre "INDEX" ine entity
"ON" (format-entity table) "ON" (format-entity table)
(when using (sql-kw using)) (when using (sql-kw using))
(str "(" (str/join ", " sqls) ")")] (str "(" (join ", " sqls) ")")]
(into more))))] (into more)))]
params))) params)))
(defn- format-with-data [_ data] (defn- format-with-data [_ data]
(let [data (if (sequential? data) (first data) data)] (let [data (if (sequential? data) (first data) data)]
[(str/join " " (remove nil? [(join " " (remove nil?) [(sql-kw :with)
[(sql-kw :with)
(when-not data (sql-kw :no)) (when-not data (sql-kw :no))
(sql-kw :data)]))])) (sql-kw :data)])]))
(defn- destructure-drop-items [tables context] (defn- destructure-drop-items [tables context]
(let [params (let [params
@ -1329,13 +1330,13 @@
coll coll
(cons nil coll))] (cons nil coll))]
(into [(when if-exists (sql-kw :if-exists)) (into [(when if-exists (sql-kw :if-exists))
(str/join ", " (map #'format-entity tables))] (join ", " (map #'format-entity) tables)]
(format-ddl-options opts context)))) (format-ddl-options opts context))))
(defn- format-drop-items (defn- format-drop-items
[k params] [k params]
(let [[if-exists tables & more] (destructure-drop-items params "DROP options")] (let [[if-exists tables & more] (destructure-drop-items params "DROP options")]
[(str/join " " (remove nil? (into [(sql-kw k) if-exists tables] more)))])) [(join " " (remove nil?) (into [(sql-kw k) if-exists tables] more))]))
(defn- format-single-column [xs] (defn- format-single-column [xs]
(let [[col & options] (if (ident? (first xs)) xs (cons nil xs)) (let [[col & options] (if (ident? (first xs)) xs (cons nil xs))
@ -1343,7 +1344,7 @@
(destructure-ddl-item [col options] "column operation")] (destructure-ddl-item [col options] "column operation")]
(when (seq pre) (throw (ex-info "column syntax error" {:unexpected pre}))) (when (seq pre) (throw (ex-info "column syntax error" {:unexpected pre})))
(when (seq ine) (throw (ex-info "column syntax error" {:unexpected ine}))) (when (seq ine) (throw (ex-info "column syntax error" {:unexpected ine})))
(str/join " " (filter seq (cons col options))))) (join " " (remove empty?) (cons col options))))
(comment (comment
(destructure-ddl-item [:foo [:abc [:continue :wibble] :identity]] "test") (destructure-ddl-item [:foo [:abc [:continue :wibble] :identity]] "test")
@ -1362,7 +1363,7 @@
(defn- format-table-columns [_ xs] (defn- format-table-columns [_ xs]
[(str "(" [(str "("
(str/join ", " (map #'format-single-column xs)) (join ", " (map #'format-single-column) xs)
")")]) ")")])
(defn- format-add-single-item [k spec] (defn- format-add-single-item [k spec]
@ -1372,7 +1373,7 @@
(defn- format-add-item [k spec] (defn- format-add-item [k spec]
(let [items (if (and (sequential? spec) (sequential? (first spec))) spec [spec])] (let [items (if (and (sequential? spec) (sequential? (first spec))) spec [spec])]
[(str/join ", " (for [item items] (format-add-single-item k item)))])) [(join ", " (map #(format-add-single-item k %)) items)]))
(comment (comment
(format-add-item :add-column [:address :text]) (format-add-item :add-column [:address :text])
@ -1394,7 +1395,7 @@
[(conj sqls s) params])) [(conj sqls s) params]))
[[] []] [[] []]
s)] s)]
(into [(str/join sqls)] params)) (into [(join "" sqls)] params))
[s])) [s]))
(defn- destructure-drop-columns [tables] (defn- destructure-drop-columns [tables]
@ -1421,7 +1422,7 @@
(defn- format-drop-columns (defn- format-drop-columns
[k params] [k params]
(let [tables (destructure-drop-columns params)] (let [tables (destructure-drop-columns params)]
[(str/join ", " (mapv #(str (sql-kw k) " " %) tables))])) [(join ", " (map #(str (sql-kw k) " " %)) tables)]))
(defn- format-interval (defn- format-interval
[k args] [k args]
@ -1430,7 +1431,7 @@
(if (seq units) (if (seq units)
(let [[sql & params] (format-expr n)] (let [[sql & params] (format-expr n)]
(into [(str (sql-kw k) " " sql " " (into [(str (sql-kw k) " " sql " "
(str/join " " (map sql-kw units)))] (join " " (map sql-kw) units))]
params)) params))
(binding [*inline* true] (binding [*inline* true]
(let [[sql & params] (format-expr n)] (let [[sql & params] (format-expr n)]
@ -1604,12 +1605,12 @@
*clause-order*)] *clause-order*)]
(if (seq leftover) (if (seq leftover)
(throw (ex-info (str "These SQL clauses are unknown or have nil values: " (throw (ex-info (str "These SQL clauses are unknown or have nil values: "
(str/join ", " (keys leftover)) (join ", " (keys leftover))
"(perhaps you need [:lift {" "(perhaps you need [:lift {"
(first (keys leftover)) (first (keys leftover))
" ...}] here?)") " ...}] here?)")
leftover)) leftover))
(into [(cond-> (str/join (if pretty "\n" " ") (filter seq sqls)) (into [(cond-> (join (if pretty "\n" " ") (remove empty?) sqls)
pretty pretty
(as-> s (str "\n" s "\n")) (as-> s (str "\n" s "\n"))
(and nested (not aliased)) (and nested (not aliased))
@ -1670,7 +1671,7 @@
(= "?" sql-y) (= "?" sql-y)
(= 1 (count params-y)) (= 1 (count params-y))
(coll? v1)) (coll? v1))
(let [sql (str "(" (str/join ", " (repeat (count v1) "?")) ")")] (let [sql (str "(" (join ", " (repeat (count v1) "?")) ")")]
(-> [(str sql-x " " (sql-kw in) " " sql)] (-> [(str sql-x " " (sql-kw in) " " sql)]
(into params-x) (into params-x)
(into v1))) (into v1)))
@ -1679,7 +1680,7 @@
(= 1 (count params-y)) (= 1 (count params-y))
(coll? v1)) (coll? v1))
(let [vs (for [v v1] (->numbered v)) (let [vs (for [v v1] (->numbered v))
sql (str "(" (str/join ", " (map first vs)) ")")] sql (str "(" (join ", " (map first) vs) ")")]
(-> [(str sql-x " " (sql-kw in) " " sql)] (-> [(str sql-x " " (sql-kw in) " " sql)]
(into params-x) (into params-x)
(conj nil) (conj nil)
@ -1693,9 +1694,9 @@
[(str (sql-kw k) [(str (sql-kw k)
(when (seq xs) (when (seq xs)
(str "(" (str "("
(str/join ", " (join ", "
(map #(format-simple-expr % "column/index operation") (map #(format-simple-expr % "column/index operation"))
xs)) xs)
")")))]) ")")))])
(defn- function-1 [k xs] (defn- function-1 [k xs]
@ -1705,9 +1706,9 @@
"column/index operation") "column/index operation")
(when-let [args (next xs)] (when-let [args (next xs)]
(str "(" (str "("
(str/join ", " (join ", "
(map #(format-simple-expr % "column/index operation") (map #(format-simple-expr % "column/index operation"))
args)) args)
")")))))]) ")")))))])
(defn- function-1-opt [k xs] (defn- function-1-opt [k xs]
@ -1717,9 +1718,9 @@
(str " " (format-simple-expr e "column/index operation"))) (str " " (format-simple-expr e "column/index operation")))
(when-let [args (next xs)] (when-let [args (next xs)]
(str "(" (str "("
(str/join ", " (join ", "
(map #(format-simple-expr % "column/index operation") (map #(format-simple-expr % "column/index operation"))
args)) args)
")")))))]) ")")))))])
(defn- expr-clause-pairs (defn- expr-clause-pairs
@ -1734,7 +1735,7 @@
(-> params (into params-e) (into params-c))])) (-> params (into params-e) (into params-c))]))
[[] []] [[] []]
(partition 2 pairs))] (partition 2 pairs))]
(into [(str/join ", " sqls)] params))) (into [(join ", " sqls)] params)))
(defn- case-clauses (defn- case-clauses
"For both :case and :case-expr." "For both :case and :case-expr."
@ -1756,7 +1757,7 @@
(-> [(str (sql-kw :case) " " (-> [(str (sql-kw :case) " "
(when case-expr? (when case-expr?
(str sqlx " ")) (str sqlx " "))
(str/join " " sqls) (join " " sqls)
" " (sql-kw :end))] " " (sql-kw :end))]
(into paramsx) (into paramsx)
(into params)))) (into params))))
@ -1799,11 +1800,11 @@
;; bigquery column types: ;; bigquery column types:
:bigquery/array (fn [_ spec] :bigquery/array (fn [_ spec]
[(str "ARRAY<" [(str "ARRAY<"
(str/join " " (map #(sql-kw %) spec)) (join " " (map sql-kw) spec)
">")]) ">")])
:bigquery/struct (fn [_ spec] :bigquery/struct (fn [_ spec]
[(str "STRUCT<" [(str "STRUCT<"
(str/join ", " (map format-single-column spec)) (join ", " (map format-single-column) spec)
">")]) ">")])
:array :array
(fn [_ [arr type]] (fn [_ [arr type]]
@ -1814,7 +1815,7 @@
;; allow for (unwrap arr) here? ;; allow for (unwrap arr) here?
(let [[sqls params] (format-expr-list arr) (let [[sqls params] (format-expr-list arr)
type-str (when type (str "::" (sql-kw type) "[]"))] type-str (when type (str "::" (sql-kw type) "[]"))]
(into [(str "ARRAY[" (str/join ", " sqls) "]" type-str)] params)))) (into [(str "ARRAY[" (join ", " sqls) "]" type-str)] params))))
:at-time-zone :at-time-zone
(fn [_ [expr tz]] (fn [_ [expr tz]]
(let [[sql & params] (format-expr expr {:nested true}) (let [[sql & params] (format-expr expr {:nested true})
@ -1845,7 +1846,7 @@
:composite :composite
(fn [_ [& args]] (fn [_ [& args]]
(let [[sqls params] (format-expr-list args)] (let [[sqls params] (format-expr-list args)]
(into [(str "(" (str/join ", " sqls) ")")] params))) (into [(str "(" (join ", " sqls) ")")] params)))
:distinct :distinct
(fn [_ [x]] (fn [_ [x]]
(let [[sql & params] (format-expr x {:nested true})] (let [[sql & params] (format-expr x {:nested true})]
@ -1862,14 +1863,13 @@
:inline :inline
(fn [_ xs] (fn [_ xs]
(binding [*inline* true] (binding [*inline* true]
(let [sqls (mapcat format-expr xs)] [(join " " (mapcat format-expr) xs)]))
[(str/join " " sqls)])))
:interval format-interval :interval format-interval
:join :join
(fn [_ [e & js]] (fn [_ [e & js]]
(let [[sqls params] (reduce-sql (cons (format-selectable-dsl e {:as true}) (let [[sqls params] (reduce-sql (cons (format-selectable-dsl e {:as true})
(map format-dsl js)))] (map format-dsl js)))]
(into [(str "(" (str/join " " sqls) ")")] params))) (into [(str "(" (join " " sqls) ")")] params)))
:lateral :lateral
(fn [_ [clause-or-expr]] (fn [_ [clause-or-expr]]
(if (map? clause-or-expr) (if (map? clause-or-expr)
@ -1917,7 +1917,7 @@
(-> params (into params-e) (into params-p))])) (-> params (into params-e) (into params-p))]))
[[] []] [[] []]
args)] args)]
(into [(str/join ", " sqls)] params))) (into [(join ", " sqls)] params)))
:param :param
(fn [_ [k]] (fn [_ [k]]
(let [k (sym->kw k)] (let [k (sym->kw k)]
@ -1975,7 +1975,7 @@
(when-not (pos? (count sqls)) (when-not (pos? (count sqls))
(throw (ex-info (str "no operands found for " op') (throw (ex-info (str "no operands found for " op')
{:expr expr}))) {:expr expr})))
(into [(cond-> (str/join (str " " (sql-kw op) " ") sqls) (into [(cond-> (join (str " " (sql-kw op) " ") sqls)
(and (contains? @op-can-be-unary op) (and (contains? @op-can-be-unary op)
(= 1 (count sqls))) (= 1 (count sqls)))
(as-> s (str (sql-kw op) " " s)) (as-> s (str (sql-kw op) " " s))
@ -1991,7 +1991,7 @@
(map? (first args)) (map? (first args))
(= 1 (count sqls))) (= 1 (count sqls)))
(str " " (first sqls)) (str " " (first sqls))
(str "(" (str/join ", " sqls) ")")))] (str "(" (join ", " sqls) ")")))]
params))) params)))
(defn format-expr (defn format-expr
@ -2025,7 +2025,7 @@
:else :else
(format-fn-call-expr op expr)) (format-fn-call-expr op expr))
(let [[sqls params] (format-expr-list expr)] (let [[sqls params] (format-expr-list expr)]
(into [(str "(" (str/join ", " sqls) ")")] params)))) (into [(str "(" (join ", " sqls) ")")] params))))
(boolean? expr) (boolean? expr)
[(upper-case (str expr))] [(upper-case (str expr))]
@ -2178,7 +2178,7 @@
[opts] [opts]
(let [unknowns (dissoc opts :checking :inline :numbered :quoted :quoted-snake)] (let [unknowns (dissoc opts :checking :inline :numbered :quoted :quoted-snake)]
(when (seq unknowns) (when (seq unknowns)
(throw (ex-info (str (str/join ", " (keys unknowns)) (throw (ex-info (str (join ", " (keys unknowns))
" are not options that can be set globally.") " are not options that can be set globally.")
unknowns))) unknowns)))
(when (contains? opts :checking) (when (contains? opts :checking)

78
src/honey/sql/util.cljc Normal file
View file

@ -0,0 +1,78 @@
(ns honey.sql.util
"Utility functions for the main honey.sql namespace."
(:refer-clojure :exclude [str])
(:require clojure.string))
(defn str
"More efficient implementation of `clojure.core/str` because it has more
non-variadic arities. Optimization is Clojure-only, on other platforms it
reverts back to `clojure.core/str`."
{:tag String}
(^String [] "")
(^String [^Object a]
#?(:clj (if (nil? a) "" (.toString a))
:default (clojure.core/str a)))
(^String [^Object a, ^Object b]
#?(:clj (if (nil? a)
(str b)
(if (nil? b)
(.toString a)
(.concat (.toString a) (.toString b))))
:default (clojure.core/str a b)))
(^String [a b c]
#?(:clj (let [sb (StringBuilder.)]
(.append sb (str a))
(.append sb (str b))
(.append sb (str c))
(.toString sb))
:default (clojure.core/str a b c)))
(^String [a b c d]
#?(:clj (let [sb (StringBuilder.)]
(.append sb (str a))
(.append sb (str b))
(.append sb (str c))
(.append sb (str d))
(.toString sb))
:default (clojure.core/str a b c d)))
(^String [a b c d e]
#?(:clj (let [sb (StringBuilder.)]
(.append sb (str a))
(.append sb (str b))
(.append sb (str c))
(.append sb (str d))
(.append sb (str e))
(.toString sb))
:default (clojure.core/str a b c d e)))
(^String [a b c d e & more]
#?(:clj (let [sb (StringBuilder.)]
(.append sb (str a))
(.append sb (str b))
(.append sb (str c))
(.append sb (str d))
(.append sb (str e))
(run! #(.append sb (str %)) more)
(.toString sb))
:default (apply clojure.core/str a b c d e more))))
(defn join
"More efficient implementation of `clojure.string/join`. May accept a transducer
`xform` to perform operations on each element before combining them together
into a string. Clojure-only, delegates to `clojure.string/join` on other
platforms."
([separator coll] (join separator identity coll))
([separator xform coll]
#?(:clj
(let [sb (StringBuilder.)
sep (str separator)]
(transduce xform
(fn
([] false)
([_] (.toString sb))
([add-sep? x]
(when add-sep? (.append sb sep))
(.append sb (str x))
true))
false coll))
:default
(clojure.string/join separator (transduce xform conj [] coll)))))

45
test/honey/util_test.cljc Normal file
View file

@ -0,0 +1,45 @@
(ns honey.util-test
(:refer-clojure :exclude [str])
(:require [clojure.test :refer [deftest is are]]
[honey.sql.util :as sut]))
(deftest str-test
(are [arg1 result] (= result (sut/str arg1))
nil ""
1 "1"
"foo" "foo"
:foo ":foo")
(are [arg1 arg2 result] (= result (sut/str arg1 arg2))
nil nil ""
nil 1 "1"
1 nil "1"
1 2 "12"
:foo "bar" ":foobar")
(are [arg1 arg2 arg3 result] (= result (sut/str arg1 arg2 arg3))
nil nil nil ""
nil 1 nil "1"
1 nil nil "1"
1 nil 2 "12"
:foo "bar" 'baz ":foobarbaz")
(are [args result] (= result (apply sut/str args))
(range 10) "0123456789"
[] ""))
(deftest join-test
(is (= "0123456789" (sut/join "" (range 10))))
(is (= "1" (sut/join "" [1])))
(is (= "" (sut/join "" [])))
(is (= "0, 1, 2, 3, 4, 5, 6, 7, 8, 9" (sut/join ", " (range 10))))
(is (= "1" (sut/join ", " [1])))
(is (= "" (sut/join ", " [])))
(is (= "0_0, 1_1, 2_2, 3_3, 4_4, 5_5, 6_6, 7_7, 8_8, 9_9"
(sut/join ", " (map #(sut/str % "_" %)) (range 10))))
(is (= "1_1"
(sut/join ", " (map #(sut/str % "_" %)) [1])))
(is (= ""
(sut/join ", " (map #(sut/str % "_" %)) [])))
(is (= "1, 2, 3, 4"
(sut/join ", " (remove nil?) [1 nil 2 nil 3 nil nil nil 4])))
(is (= "" (sut/join ", " (remove nil?) [nil nil nil nil]))))