improve performance of optional arguments
Signed-off-by: Sean Corfield <sean@corfield.org>
This commit is contained in:
parent
203e923f99
commit
a187ba98f1
2 changed files with 216 additions and 203 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
* 2.6.next in progress
|
* 2.6.next in progress
|
||||||
* Fix [#548](https://github.com/seancorfield/honeysql/issues/548) which was a regression introduced in [#526](https://github.com/seancorfield/honeysql/issues/526).
|
* Fix [#548](https://github.com/seancorfield/honeysql/issues/548) which was a regression introduced in [#526](https://github.com/seancorfield/honeysql/issues/526).
|
||||||
|
* Replace all optional argument destructuring with multiple arities to improve performance.
|
||||||
|
|
||||||
* 2.6.1196 -- 2024-10-06
|
* 2.6.1196 -- 2024-10-06
|
||||||
* Address [#547](https://github.com/seancorfield/honeysql/issues/547) by adding examples of conditional SQL building with the helpers to the README and the `honey.sql.helpers` ns docstring.
|
* Address [#547](https://github.com/seancorfield/honeysql/issues/547) by adding examples of conditional SQL building with the helpers to the README and the `honey.sql.helpers` ns docstring.
|
||||||
|
|
|
||||||
|
|
@ -279,48 +279,49 @@
|
||||||
return the equivalent SQL fragment (as a string -- no parameters).
|
return the equivalent SQL fragment (as a string -- no parameters).
|
||||||
|
|
||||||
Handles quoting, splitting at / or ., replacing - with _ etc."
|
Handles quoting, splitting at / or ., replacing - with _ etc."
|
||||||
[e & [{:keys [aliased drop-ns]}]]
|
([e] (format-entity e {}))
|
||||||
(let [e (if (and aliased (keyword? e) (str/starts-with? (name e) "'"))
|
([e {:keys [aliased drop-ns]}]
|
||||||
|
(let [e (if (and aliased (keyword? e) (str/starts-with? (name e) "'"))
|
||||||
;; #497 quoted alias support (should behave like string)
|
;; #497 quoted alias support (should behave like string)
|
||||||
(subs (name e) 1)
|
(subs (name e) 1)
|
||||||
e)
|
e)
|
||||||
col-fn (or (:col-fn *dialect*)
|
col-fn (or (:col-fn *dialect*)
|
||||||
(if (or *quoted* (string? e))
|
(if (or *quoted* (string? e))
|
||||||
(if *quoted-snake* name-_ name)
|
(if *quoted-snake* name-_ name)
|
||||||
name-_))
|
name-_))
|
||||||
col-e (col-fn e)
|
col-e (col-fn e)
|
||||||
dialect-q (:quote *dialect* identity)
|
dialect-q (:quote *dialect* identity)
|
||||||
quote-fn (cond (or *quoted* (string? e))
|
quote-fn (cond (or *quoted* (string? e))
|
||||||
dialect-q
|
dialect-q
|
||||||
;; #422: if default quoting and "unusual"
|
;; #422: if default quoting and "unusual"
|
||||||
;; characters in entity, then quote it:
|
;; characters in entity, then quote it:
|
||||||
(nil? *quoted*)
|
(nil? *quoted*)
|
||||||
(fn opt-quote [part]
|
(fn opt-quote [part]
|
||||||
(cond (and *quoted-always*
|
(cond (and *quoted-always*
|
||||||
(re-find *quoted-always* part))
|
(re-find *quoted-always* part))
|
||||||
(dialect-q part)
|
(dialect-q part)
|
||||||
(re-find alphanumeric part)
|
(re-find alphanumeric part)
|
||||||
part
|
part
|
||||||
:else
|
:else
|
||||||
(dialect-q part)))
|
(dialect-q part)))
|
||||||
*quoted-always*
|
*quoted-always*
|
||||||
(fn always-quote [part]
|
(fn always-quote [part]
|
||||||
(if (re-find *quoted-always* part)
|
(if (re-find *quoted-always* part)
|
||||||
(dialect-q part)
|
(dialect-q part)
|
||||||
part))
|
part))
|
||||||
:else
|
:else
|
||||||
identity)
|
identity)
|
||||||
parts-fn (or (:parts-fn *dialect*)
|
parts-fn (or (:parts-fn *dialect*)
|
||||||
#(if-let [n (when-not (or drop-ns (string? e))
|
#(if-let [n (when-not (or drop-ns (string? e))
|
||||||
(namespace-_ e))]
|
(namespace-_ e))]
|
||||||
[n %]
|
[n %]
|
||||||
(if aliased
|
(if aliased
|
||||||
[%]
|
[%]
|
||||||
(str/split % #"\."))))
|
(str/split % #"\."))))
|
||||||
parts (parts-fn col-e)
|
parts (parts-fn col-e)
|
||||||
entity (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)))
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(for [v [:foo-bar "foo-bar" ; symbol is the same as keyword
|
(for [v [:foo-bar "foo-bar" ; symbol is the same as keyword
|
||||||
|
|
@ -415,41 +416,45 @@
|
||||||
[x]
|
[x]
|
||||||
(upper-case (str/replace (name x) "-" "_")))
|
(upper-case (str/replace (name x) "-" "_")))
|
||||||
|
|
||||||
(defn- format-simple-var [x & [c opts]]
|
(defn- format-simple-var
|
||||||
(let [c (or c
|
([x]
|
||||||
(if (keyword? x)
|
(let [c (if (keyword? x)
|
||||||
#?(:clj (str (.sym ^clojure.lang.Keyword x)) ;; Omits leading colon
|
#?(:clj (str (.sym ^clojure.lang.Keyword x)) ;; Omits leading colon
|
||||||
:default (subs (str x) 1))
|
:default (subs (str x) 1))
|
||||||
(str x)))]
|
(str x))]
|
||||||
(if (str/starts-with? c "'")
|
(format-simple-var x c {})))
|
||||||
(do
|
([x c opts]
|
||||||
(reset! *formatted-column* true)
|
(if (str/starts-with? c "'")
|
||||||
[(subs c 1)])
|
(do
|
||||||
[(format-entity x opts)])))
|
(reset! *formatted-column* true)
|
||||||
|
[(subs c 1)])
|
||||||
|
[(format-entity x opts)])))
|
||||||
|
|
||||||
(defn- format-var [x & [opts]]
|
(defn- format-var
|
||||||
;; rather than name/namespace, we want to allow
|
([x] (format-var x {}))
|
||||||
;; for multiple / in the %fun.call case so that
|
([x opts]
|
||||||
;; qualified column names can be used:
|
;; rather than name/namespace, we want to allow
|
||||||
(let [c (if (keyword? x)
|
;; for multiple / in the %fun.call case so that
|
||||||
#?(:clj (str (.sym ^clojure.lang.Keyword x)) ;; Omits leading colon
|
;; qualified column names can be used:
|
||||||
:default (subs (str x) 1))
|
(let [c (if (keyword? x)
|
||||||
(str x))]
|
#?(:clj (str (.sym ^clojure.lang.Keyword x)) ;; Omits leading colon
|
||||||
(cond (str/starts-with? c "%")
|
:default (subs (str x) 1))
|
||||||
(let [[f & args] (str/split (subs c 1) #"\.")]
|
(str x))]
|
||||||
[(str (format-fn-name f) "("
|
(cond (str/starts-with? c "%")
|
||||||
(join ", " (map #(format-entity (keyword %) opts)) args)
|
(let [[f & args] (str/split (subs c 1) #"\.")]
|
||||||
")")])
|
[(str (format-fn-name f) "("
|
||||||
(str/starts-with? c "?")
|
(join ", " (map #(format-entity (keyword %) opts)) args)
|
||||||
(let [k (keyword (subs c 1))]
|
")")])
|
||||||
(cond *inline*
|
(str/starts-with? c "?")
|
||||||
[(sqlize-value (param-value k))]
|
(let [k (keyword (subs c 1))]
|
||||||
*numbered*
|
(cond *inline*
|
||||||
(->numbered-param k)
|
[(sqlize-value (param-value k))]
|
||||||
:else
|
*numbered*
|
||||||
["?" (->param k)]))
|
(->numbered-param k)
|
||||||
:else
|
:else
|
||||||
(format-simple-var x c opts))))
|
["?" (->param k)]))
|
||||||
|
:else
|
||||||
|
(format-simple-var x c opts)))))
|
||||||
|
|
||||||
(defn- format-entity-alias [x]
|
(defn- format-entity-alias [x]
|
||||||
(cond (sequential? x)
|
(cond (sequential? x)
|
||||||
|
|
@ -577,30 +582,31 @@
|
||||||
"If the expression has metadata, format it as a sequence of keywords,
|
"If the expression has metadata, format it as a sequence of keywords,
|
||||||
treating `:foo true` as `FOO` and `:foo :bar` as `FOO BAR`.
|
treating `:foo true` as `FOO` and `:foo :bar` as `FOO BAR`.
|
||||||
Return nil if there is no metadata."
|
Return nil if there is no metadata."
|
||||||
[x & [sep]]
|
([x] (format-meta x nil))
|
||||||
(when-let [data (meta x)]
|
([x sep]
|
||||||
(let [items (reduce-kv (fn [acc k v]
|
(when-let [data (meta x)]
|
||||||
(cond (number? v)
|
(let [items (reduce-kv (fn [acc k v]
|
||||||
(conj acc (str v))
|
(cond (number? v)
|
||||||
(true? v)
|
(conj acc (str v))
|
||||||
(conj acc k)
|
(true? v)
|
||||||
(ident? v)
|
(conj acc k)
|
||||||
(conj acc k v)
|
(ident? v)
|
||||||
(string? v)
|
(conj acc k v)
|
||||||
(do
|
(string? v)
|
||||||
(suspicious-entity-check v)
|
(do
|
||||||
(conj acc k v))
|
(suspicious-entity-check v)
|
||||||
:else ; quietly ignore other metadata
|
(conj acc k v))
|
||||||
acc))
|
:else ; quietly ignore other metadata
|
||||||
[]
|
acc))
|
||||||
(reduce dissoc
|
[]
|
||||||
data
|
(reduce dissoc
|
||||||
(into [; remove the somewhat "standard" metadata:
|
data
|
||||||
:line :column :file
|
(into [; remove the somewhat "standard" metadata:
|
||||||
:end-line :end-column]
|
:line :column :file
|
||||||
*ignored-metadata*)))]
|
:end-line :end-column]
|
||||||
(when (seq items)
|
*ignored-metadata*)))]
|
||||||
(join (str sep " ") (map sql-kw) items)))))
|
(when (seq 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} [])
|
||||||
|
|
@ -650,23 +656,25 @@
|
||||||
(into params')
|
(into params')
|
||||||
(into params'')))))
|
(into params'')))))
|
||||||
|
|
||||||
(defn- format-selectable-dsl [x & [{:keys [as aliased] :as opts}]]
|
(defn- format-selectable-dsl
|
||||||
(cond (map? x)
|
([x] (format-selectable-dsl x {}))
|
||||||
(format-dsl x {:nested true})
|
([x {:keys [as aliased] :as opts}]
|
||||||
|
(cond (map? x)
|
||||||
|
(format-dsl x {:nested true})
|
||||||
|
|
||||||
(sequential? x)
|
(sequential? x)
|
||||||
(format-item-selection x as)
|
(format-item-selection x as)
|
||||||
|
|
||||||
(ident? x)
|
(ident? x)
|
||||||
(if aliased
|
(if aliased
|
||||||
[(format-entity x opts)]
|
[(format-entity x opts)]
|
||||||
(format-var x opts))
|
(format-var x opts))
|
||||||
|
|
||||||
(and aliased (string? x))
|
(and aliased (string? x))
|
||||||
[(format-entity x opts)]
|
[(format-entity x opts)]
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(format-expr x)))
|
(format-expr x))))
|
||||||
|
|
||||||
(defn- reduce-sql
|
(defn- reduce-sql
|
||||||
([xs] (reduce-sql identity xs))
|
([xs] (reduce-sql identity xs))
|
||||||
|
|
@ -699,31 +707,32 @@
|
||||||
This allows for argument lists like:
|
This allows for argument lists like:
|
||||||
* [:overlay :foo :*placing :?subs :*from 3 :*for 4]
|
* [:overlay :foo :*placing :?subs :*from 3 :*for 4]
|
||||||
* [:trim :*leading-from :bar]"
|
* [:trim :*leading-from :bar]"
|
||||||
[args & [opts]]
|
([args] (format-interspersed-expr-list args {}))
|
||||||
(loop [exprs (keep #(when-not (inline-kw? %)
|
([args opts]
|
||||||
(format-expr % opts))
|
(loop [exprs (keep #(when-not (inline-kw? %)
|
||||||
args)
|
(format-expr % opts))
|
||||||
args args
|
args)
|
||||||
prev-in false
|
args args
|
||||||
result []]
|
prev-in false
|
||||||
(if (seq args)
|
result []]
|
||||||
(let [[arg & args'] args]
|
(if (seq args)
|
||||||
(if (inline-kw? arg)
|
(let [[arg & args'] args]
|
||||||
(let [sql (sql-kw (keyword (subs (name arg) 1)))]
|
(if (inline-kw? arg)
|
||||||
(if (seq result)
|
(let [sql (sql-kw (keyword (subs (name arg) 1)))]
|
||||||
(let [[cur & params] (peek result)]
|
(if (seq result)
|
||||||
(recur exprs args' true (conj (pop result)
|
(let [[cur & params] (peek result)]
|
||||||
(into [(str cur " " sql)] params))))
|
(recur exprs args' true (conj (pop result)
|
||||||
(recur exprs args' true (conj result [sql]))))
|
(into [(str cur " " sql)] params))))
|
||||||
(if prev-in
|
(recur exprs args' true (conj result [sql]))))
|
||||||
(let [[cur & params] (peek result)
|
(if prev-in
|
||||||
[sql & params'] (first exprs)]
|
(let [[cur & params] (peek result)
|
||||||
(recur (rest exprs) args' false (conj (pop result)
|
[sql & params'] (first exprs)]
|
||||||
(-> [(str cur " " sql)]
|
(recur (rest exprs) args' false (conj (pop result)
|
||||||
(into params)
|
(-> [(str cur " " sql)]
|
||||||
(into params')))))
|
(into params)
|
||||||
(recur (rest exprs) args' false (conj result (first exprs))))))
|
(into params')))))
|
||||||
(reduce-sql result))))
|
(recur (rest exprs) args' false (conj result (first exprs))))))
|
||||||
|
(reduce-sql result)))))
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(format-interspersed-expr-list [:foo :*placing :?subs :*from 3 :*for 4]
|
(format-interspersed-expr-list [:foo :*placing :?subs :*from 3 :*for 4]
|
||||||
|
|
@ -744,12 +753,13 @@
|
||||||
|
|
||||||
This is intended to be used when writing your own formatters to
|
This is intended to be used when writing your own formatters to
|
||||||
extend the DSL supported by HoneySQL."
|
extend the DSL supported by HoneySQL."
|
||||||
[exprs & [opts]]
|
([exprs] (format-expr-list exprs {}))
|
||||||
(when-not (sequential? exprs)
|
([exprs opts]
|
||||||
(throw (ex-info (str "format-expr-list expects a sequence of expressions, found: "
|
(when-not (sequential? exprs)
|
||||||
(type exprs))
|
(throw (ex-info (str "format-expr-list expects a sequence of expressions, found: "
|
||||||
{:exprs exprs})))
|
(type exprs))
|
||||||
(reduce-sql (map #(format-expr % opts)) exprs))
|
{:exprs exprs})))
|
||||||
|
(reduce-sql (map #(format-expr % opts)) exprs)))
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(format-expr-list :?tags)
|
(format-expr-list :?tags)
|
||||||
|
|
@ -1617,34 +1627,35 @@
|
||||||
|
|
||||||
This is intended to be used when writing your own formatters to
|
This is intended to be used when writing your own formatters to
|
||||||
extend the DSL supported by HoneySQL."
|
extend the DSL supported by HoneySQL."
|
||||||
[statement-map & [{:keys [aliased nested pretty]}]]
|
([statement-map] (format-dsl statement-map {}))
|
||||||
(binding [*dsl* statement-map]
|
([statement-map {:keys [aliased nested pretty]}]
|
||||||
(let [[sqls params leftover]
|
(binding [*dsl* statement-map]
|
||||||
(reduce (fn [[sql params leftover] k]
|
(let [[sqls params leftover]
|
||||||
(if-some [xs (if-some [xs (k leftover)]
|
(reduce (fn [[sql params leftover] k]
|
||||||
xs
|
(if-some [xs (if-some [xs (k leftover)]
|
||||||
(let [s (kw->sym k)]
|
xs
|
||||||
(get leftover s)))]
|
(let [s (kw->sym k)]
|
||||||
(let [formatter (k @clause-format)
|
(get leftover s)))]
|
||||||
[sql' & params'] (formatter k xs)]
|
(let [formatter (k @clause-format)
|
||||||
[(conj sql sql')
|
[sql' & params'] (formatter k xs)]
|
||||||
(if params' (into params params') params)
|
[(conj sql sql')
|
||||||
(dissoc leftover k (kw->sym k))])
|
(if params' (into params params') params)
|
||||||
[sql params leftover]))
|
(dissoc leftover k (kw->sym k))])
|
||||||
[[] [] statement-map]
|
[sql params leftover]))
|
||||||
*clause-order*)]
|
[[] [] statement-map]
|
||||||
(if (seq leftover)
|
*clause-order*)]
|
||||||
(throw (ex-info (str "These SQL clauses are unknown or have nil values: "
|
(if (seq leftover)
|
||||||
(join ", " (keys leftover))
|
(throw (ex-info (str "These SQL clauses are unknown or have nil values: "
|
||||||
"(perhaps you need [:lift {"
|
(join ", " (keys leftover))
|
||||||
(first (keys leftover))
|
"(perhaps you need [:lift {"
|
||||||
" ...}] here?)")
|
(first (keys leftover))
|
||||||
leftover))
|
" ...}] here?)")
|
||||||
(into [(cond-> (join (if pretty "\n" " ") (remove empty?) sqls)
|
leftover))
|
||||||
pretty
|
(into [(cond-> (join (if pretty "\n" " ") (remove empty?) sqls)
|
||||||
(as-> s (str "\n" s "\n"))
|
pretty
|
||||||
(and nested (not aliased))
|
(as-> s (str "\n" s "\n"))
|
||||||
(as-> s (str "(" s ")")))] params)))))
|
(and nested (not aliased))
|
||||||
|
(as-> s (str "(" s ")")))] params))))))
|
||||||
|
|
||||||
(def ^:private infix-aliases
|
(def ^:private infix-aliases
|
||||||
"Provided for backward compatibility with earlier HoneySQL versions."
|
"Provided for backward compatibility with earlier HoneySQL versions."
|
||||||
|
|
@ -2031,45 +2042,46 @@
|
||||||
|
|
||||||
This is intended to be used when writing your own formatters to
|
This is intended to be used when writing your own formatters to
|
||||||
extend the DSL supported by HoneySQL."
|
extend the DSL supported by HoneySQL."
|
||||||
[expr & [{:keys [nested] :as opts}]]
|
([expr] (format-expr expr {}))
|
||||||
(cond (ident? expr)
|
([expr {:keys [nested] :as opts}]
|
||||||
(format-var expr opts)
|
(cond (ident? expr)
|
||||||
|
(format-var expr opts)
|
||||||
|
|
||||||
(map? expr)
|
(map? expr)
|
||||||
(format-dsl expr (assoc opts :nested true))
|
(format-dsl expr (assoc opts :nested true))
|
||||||
|
|
||||||
(sequential? expr)
|
(sequential? expr)
|
||||||
(let [op' (sym->kw (first expr))
|
(let [op' (sym->kw (first expr))
|
||||||
op (get infix-aliases op' op')]
|
op (get infix-aliases op' op')]
|
||||||
(if (keyword? op')
|
(if (keyword? op')
|
||||||
(cond (contains? @infix-ops op')
|
(cond (contains? @infix-ops op')
|
||||||
(if (contains? #{:= :<>} op)
|
(if (contains? #{:= :<>} op)
|
||||||
(format-equality-expr op' op expr nested)
|
(format-equality-expr op' op expr nested)
|
||||||
(format-infix-expr op' op expr nested))
|
(format-infix-expr op' op expr nested))
|
||||||
(contains? #{:in :not-in} op)
|
(contains? #{:in :not-in} op)
|
||||||
(let [[sql & params] (format-in op (rest expr))]
|
(let [[sql & params] (format-in op (rest expr))]
|
||||||
(into [(if nested (str "(" sql ")") sql)] params))
|
(into [(if nested (str "(" sql ")") sql)] params))
|
||||||
(contains? @special-syntax op)
|
(contains? @special-syntax op)
|
||||||
(let [formatter (get @special-syntax op)]
|
(let [formatter (get @special-syntax op)]
|
||||||
(formatter op (rest expr)))
|
(formatter op (rest expr)))
|
||||||
: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 "(" (join ", " sqls) ")")] params))))
|
(into [(str "(" (join ", " sqls) ")")] params))))
|
||||||
|
|
||||||
(boolean? expr)
|
(boolean? expr)
|
||||||
[(upper-case (str expr))]
|
[(upper-case (str expr))]
|
||||||
|
|
||||||
(nil? expr)
|
(nil? expr)
|
||||||
["NULL"]
|
["NULL"]
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(cond *inline*
|
(cond *inline*
|
||||||
[(sqlize-value expr)]
|
[(sqlize-value expr)]
|
||||||
*numbered*
|
*numbered*
|
||||||
(->numbered expr)
|
(->numbered expr)
|
||||||
:else
|
:else
|
||||||
["?" expr])))
|
["?" expr]))))
|
||||||
|
|
||||||
(defn- check-dialect [dialect]
|
(defn- check-dialect [dialect]
|
||||||
(when-not (contains? @dialects dialect)
|
(when-not (contains? @dialects dialect)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue