Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More optimizations #560

Merged
merged 3 commits into from
Jan 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 42 additions & 78 deletions src/honey/sql.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
(:require [clojure.string :as str]
#?(:clj [clojure.template])
[honey.sql.protocols :as p]
[honey.sql.util :refer [str join]]))
[honey.sql.util :refer [str join split-by-separator into*]]))

;; default formatting for known clauses

Expand Down Expand Up @@ -316,7 +316,7 @@
[n %]
(if aliased
[%]
(str/split % #"\."))))
(split-by-separator % "."))))
parts (parts-fn col-e)
entity (join "." (map #(cond-> % (not= "*" %) (quote-fn))) parts)]
(suspicious-entity-check entity)
Expand Down Expand Up @@ -457,7 +457,7 @@
:default (subs (str x) 1))
(str x))]
(cond (str/starts-with? c "%")
(let [[f & args] (str/split (subs c 1) #"\.")]
(let [[f & args] (split-by-separator (subs c 1) ".")]
[(str (format-fn-name f) "("
(join ", " (map #(format-entity (keyword %) opts)) args)
")")])
Expand Down Expand Up @@ -525,14 +525,10 @@
:else
(throw (ex-info "bigquery * only supports except and replace"
{:clause k :arg arg})))]
(-> [(cond->> sql' sql (str sql " "))]
(into params)
(into params'))))
(into* [(cond->> sql' sql (str sql " "))] params params')))
[]
(partition-all 2 x))]
(-> [(str sql " " sql')]
(into params)
(into params'))))
(into* [(str sql " " sql')] params params')))

(comment
(bigquery-*-except-replace? [:* :except [:a :b :c]])
Expand Down Expand Up @@ -676,9 +672,7 @@
sql'))
(when hints
(str " WITH (" hints ")")))]
(into params)
(into params')
(into params'')))))
(into* params params' params'')))))

(defn- format-selectable-dsl
([x] (format-selectable-dsl x {}))
Expand Down Expand Up @@ -752,9 +746,8 @@
(let [[cur & params] (peek result)
[sql & params'] (first exprs)]
(recur (rest exprs) args' false (conj (pop result)
(-> [(str cur " " sql)]
(into params)
(into params')))))
(into* [(str cur " " sql)]
params params'))))
(recur (rest exprs) args' false (conj result (first exprs))))))
(reduce-sql result)))))

Expand Down Expand Up @@ -836,7 +829,7 @@
(str (sql-kw :select) " " sql)
true
cols)]
(-> [sql'] (into params) (into params'))))
(into* [sql'] params params')))

(defn- format-select-top [k xs]
(let [[top & cols] xs
Expand Down Expand Up @@ -864,7 +857,7 @@
(join " " (map sql-kw) parts))
true
cols)]
(-> [sql'] (into params) (into params'))))
(into* [sql'] params params')))

(defn- format-select-into [k xs]
(let [[v e] (ensure-sequential xs)
Expand Down Expand Up @@ -956,19 +949,14 @@
(format-dsl expr))
[sql'' & params'' :as sql-params'']
(if non-query-expr?
(cond-> [(str sql' " AS " sql)]
params' (into params')
params (into params))
(into* [(str sql' " AS " sql)] params' params)
;; according to docs, CTE should _always_ be wrapped:
(cond-> [(str sql " " (as-fn with) " " (str "(" sql' ")"))]
params (into params)
params' (into params')))
(into* [(str sql " " (as-fn with) " " (str "(" sql' ")"))]
params params'))
[tail-sql & tail-params]
(format-with-query-tail tail)]
(if (seq tail-sql)
(cond-> [(str sql'' " " tail-sql)]
params'' (into params'')
tail-params (into tail-params))
(into* [(str sql'' " " tail-sql)] params'' tail-params)
sql-params''))))
xs)]
(into [(str (sql-kw k) " " (join ", " sqls))] params)))
Expand Down Expand Up @@ -1012,10 +1000,7 @@
(str cols-sql' " "))
overriding
sql)]
(into t-params)
(into c-params)
(into cols-params')
(into params)))
(into* t-params c-params cols-params' params)))
(sequential? (second table))
(let [[table cols] table
[t-sql & t-params] (format-entity-alias table)
Expand All @@ -1025,23 +1010,20 @@
(join ", " c-sqls)
")"
overriding)]
(into t-params)
(into c-params)))
(into* t-params c-params)))
:else
(let [[sql & params] (format-entity-alias table)]
(-> [(str (sql-kw k) " " sql
(when (seq cols')
(str " " cols-sql'))
overriding)]
(into cols-params')
(into params))))
(into* cols-params' params))))
(let [[sql & params] (format-entity-alias table)]
(-> [(str (sql-kw k) " " sql
(when (seq cols')
(str " " cols-sql'))
overriding)]
(into cols-params')
(into params))))))
(into* cols-params' params))))))

(comment
(format-insert :insert-into [[[:raw ":foo"]] {:select :bar}])
Expand Down Expand Up @@ -1069,12 +1051,10 @@
(str "("
(join ", " u-sqls)
")"))
(-> params (into params-j) (into u-params))])
(into* params params-j u-params)])
(let [[sql & params'] (when e (format-expr e))]
[(cond-> sqls e (conj "ON" sql))
(-> params
(into params-j)
(into params'))])))))
(into* params params-j params')])))))
[[] []]
clauses)]
(into [(join " " sqls)] params)))
Expand Down Expand Up @@ -1282,8 +1262,7 @@
(str " (" (join ", " sqls) ")"))
(when sql
(str " " sql)))]
(into expr-params)
(into clause-params)))
(into* expr-params clause-params)))
(format-on-conflict k [x])))

(defn- format-do-update-set [k x]
Expand All @@ -1302,8 +1281,7 @@
where (or (:where x) ('where x))
[sql & params] (when where (format-dsl {:where where}))]
(-> [(str sets (when sql (str " " sql)))]
(into set-params)
(into params)))
(into* set-params params)))
(format-set-exprs k x))
(sequential? x)
(let [[cols clauses] (split-with (complement map?) x)]
Expand Down Expand Up @@ -1753,7 +1731,10 @@
(if (keyword? k)
(if-let [n (namespace k)]
(symbol n (name k))
(symbol (name k)))
;; In CLJ runtime, reuse symbol that's already present in the keyword.
#?(:bb (symbol (name k))
:clj (.sym ^clojure.lang.Keyword k)
:default (symbol (name k))))
k))

(defn format-dsl
Expand Down Expand Up @@ -1849,23 +1830,18 @@
(= 1 (count params-y))
(coll? v1))
(let [sql (str "(" (join ", " (repeat (count v1) "?")) ")")]
(-> [(str sql-x " " (sql-kw in) " " sql)]
(into params-x)
(into v1)))
(into* [(str sql-x " " (sql-kw in) " " sql)] params-x v1))
(and *numbered*
(= (str "$" (count @*numbered*)) sql-y)
(= 1 (count params-y))
(coll? v1))
(let [vs (for [v v1] (->numbered v))
sql (str "(" (join ", " (map first) vs) ")")]
(-> [(str sql-x " " (sql-kw in) " " sql)]
(into params-x)
(conj nil)
(into (map second vs))))
(into* [(str sql-x " " (sql-kw in) " " sql)]
params-x [nil] (map second vs)))
:else
(-> [(str sql-x " " (sql-kw in) " " sql-y)]
(into params-x)
(into (if *numbered* values params-y))))))
(into* [(str sql-x " " (sql-kw in) " " sql-y)]
params-x (if *numbered* values params-y)))))

(defn- function-0 [k xs]
[(str (sql-kw k)
Expand Down Expand Up @@ -1909,7 +1885,7 @@
(let [[sql-e & params-e] (format-expr e)
[sql-c & params-c] (format-dsl c {:nested true})]
[(conj sqls (str sql-e " " (sql-kw k) " " sql-c))
(-> params (into params-e) (into params-c))]))
(into* params params-e params-c)]))
[[] []]
(partition 2 pairs))]
(into [(join ", " sqls)] params)))
Expand All @@ -1928,27 +1904,24 @@
(= 'else condition))
(conj sqls (sql-kw :else) sqlv)
(conj sqls (sql-kw :when) sqlc (sql-kw :then) sqlv))
(-> params (into paramsc) (into paramsv))]))
(into* params paramsc paramsv)]))
[[] []]
(partition 2 (if case-expr? (rest clauses) clauses)))]
(-> [(str (sql-kw :case) " "
(when case-expr?
(str sqlx " "))
(join " " sqls)
" " (sql-kw :end))]
(into paramsx)
(into params))))
(into* paramsx params))))

(defn- between-fn
"For both :between and :not-between"
[k [x a b]]
(let [[sql-x & params-x] (format-expr x {:nested true})
[sql-a & params-a] (format-expr a {:nested true})
[sql-b & params-b] (format-expr b {:nested true})]
(-> [(str sql-x " " (sql-kw k) " " sql-a " AND " sql-b)]
(into params-x)
(into params-a)
(into params-b))))
(into* [(str sql-x " " (sql-kw k) " " sql-a " AND " sql-b)]
params-x params-a params-b)))

(defn- object-record-literal
[k [x]]
Expand All @@ -1967,9 +1940,7 @@
(let [[sql' & params'] (format-expr %)]
(cons (str "[" sql' "]") params')))
kix))]
(-> [(str "(" sql ")" (join "" sqls))]
(into params)
(into params'))))
(into* [(str "(" sql ")" (join "" sqls))] params params')))

(defn ignore-respect-nulls [k [x]]
(let [[sql & params] (format-expr x)]
Expand Down Expand Up @@ -2042,9 +2013,7 @@
[sql' & params'] (if (ident? type)
[(sql-kw type)]
(format-expr type))]
(-> [(str "CAST(" sql " AS " sql' ")")]
(into params)
(into params'))))
(into* [(str "CAST(" sql " AS " sql' ")")] params params')))
:composite
(fn [_ [& args]]
(let [[sqls params] (format-expr-list args)]
Expand All @@ -2057,9 +2026,7 @@
(fn [_ [pattern escape-chars]]
(let [[sql-p & params-p] (format-expr pattern)
[sql-e & params-e] (format-expr escape-chars)]
(-> [(str sql-p " " (sql-kw :escape) " " sql-e)]
(into params-p)
(into params-e))))
(into* [(str sql-p " " (sql-kw :escape) " " sql-e)] params-p params-e)))
:filter expr-clause-pairs
:get-in #'get-in-navigation
:ignore-nulls ignore-respect-nulls
Expand Down Expand Up @@ -2106,9 +2073,7 @@
(fn [k [e & qs]]
(let [[sql-e & params-e] (format-expr e)
[sql-q & params-q] (format-dsl {k qs})]
(-> [(str sql-e " " sql-q)]
(into params-e)
(into params-q))))
(into* [(str sql-e " " sql-q)] params-e params-q)))
:over
(fn [_ [& args]]
(let [[sqls params]
Expand All @@ -2119,7 +2084,7 @@
[(format-entity p)])]
[(conj sqls (str sql-e " OVER " sql-p
(when a (str " AS " (format-entity a)))))
(-> params (into params-e) (into params-p))]))
(into* params params-e params-p)]))
[[] []]
args)]
(into [(join ", " sqls)] params)))
Expand Down Expand Up @@ -2160,8 +2125,7 @@
(cond-> nested
(as-> s (str "(" s ")")))
(vector)
(into p1)
(into p2))))
(into* p1 p2))))

(defn- format-infix-expr [op' op expr nested]
(let [args (cond->> (rest expr)
Expand Down
29 changes: 29 additions & 0 deletions src/honey/sql/util.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,32 @@

:default
(clojure.string/join separator (transduce xform conj [] coll)))))

(defn split-by-separator
"More efficient implementation of `clojure.string/split` for cases when a
literal string (not regex) is used as a separator, and for cases where the
separator is not present in the haystack at all."
[s sep]
(loop [start 0, res []]
(if-let [sep-idx (clojure.string/index-of s sep start)]
(recur (inc sep-idx) (conj res (subs s start sep-idx)))
(if (= start 0)
;; Fastpath - zero separators in s
[s]
(conj res (subs s start))))))

(defn into*
"An extension of `clojure.core/into` that accepts multiple \"from\" arguments.
Doesn't support `xform`."
([to from1] (into* to from1 nil nil nil))
([to from1 from2] (into* to from1 from2 nil nil))
([to from1 from2 from3] (into* to from1 from2 from3 nil))
([to from1 from2 from3 from4]
(if (or from1 from2 from3 from4)
(as-> (transient to) to'
(reduce conj! to' from1)
(reduce conj! to' from2)
(reduce conj! to' from3)
(reduce conj! to' from4)
(persistent! to'))
to)))
17 changes: 17 additions & 0 deletions test/honey/util_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,20 @@
(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]))))

(deftest split-by-separator-test
seancorfield marked this conversation as resolved.
Show resolved Hide resolved
(is (= [""] (sut/split-by-separator "" ".")))
(is (= ["" ""] (sut/split-by-separator "." ".")))
(is (= ["hello"] (sut/split-by-separator "hello" ".")))
(is (= ["h" "e" "l" "l" "o"] (sut/split-by-separator "h.e.l.l.o" ".")))
(is (= ["" "h" "e" "" "" "l" "" "l" "o" ""]
(sut/split-by-separator ".h.e...l..l.o." "."))))

(deftest into*-test
(is (= [1] (sut/into* [1] nil)))
(is (= [1] (sut/into* [1] [])))
(is (= [1] (sut/into* [1] nil [] nil [])))
(is (= [1 2 3] (sut/into* [1] [2 3])))
(is (= [1 2 3 4 5 6] (sut/into* [1] [2 3] [4 5 6])))
(is (= [1 2 3 4 5 6 7] (sut/into* [1] [2 3] [4 5 6] [7])))
(is (= [1 2 3 4 5 6 7 8 9] (sut/into* [1] [2 3] [4 5 6] [7] [8 9]))))
Loading