-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
execute.clj
133 lines (107 loc) · 5.17 KB
/
execute.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
(ns toucan2.execute
"Code for executing queries and statements, and reducing their results.
The functions here meant for use on a regular basis are:
* [[query]] -- resolve and compile a connectable, execute it using a connection from a connectable, and immediately
fully realize the results.
* [[query-one]] -- like [[query]], but only realizes the first result.
* [[reducible-query]] -- like [[query]], but returns a [[clojure.lang.IReduceInit]] that can be reduced later rather
than immediately realizing the results.
* [[with-call-count]] -- helper macro to count the number of queries executed within a `body`."
(:require
[clojure.spec.alpha :as s]
[toucan2.pipeline :as pipeline]))
(set! *warn-on-reflection* true)
(defn- query* [f]
(fn query**
([queryable]
;; `nil` connectable = use the current connection or `:default` if none is specified.
(query** nil queryable))
([connectable queryable]
(query** connectable nil queryable))
([connectable modelable queryable]
;; by passing `result-type/*` we'll basically get whatever the default is -- instances for `SELECT` or update counts
;; for `DML` stuff.
(query** connectable :toucan.result-type/* modelable queryable))
([connectable query-type modelable queryable]
(let [parsed-args {:connectable connectable
:modelable modelable
:queryable queryable}]
(f query-type parsed-args)))))
(def ^{:arglists '([queryable]
[connectable queryable]
[connectable modelable queryable]
[connectable query-type modelable queryable])}
reducible-query
"Create a reducible query that when reduced with resolve and compile `queryable`, open a connection using `connectable`
and [[toucan2.connection/with-connection]], execute the query, and reduce the results.
Note that this query can be something like a `SELECT` query, or something that doesn't normally return results, like
an `UPDATE`; this works either way. Normally something like an `UPDATE` will reduce to the number of rows
updated/inserted/deleted, e.g. `[5]`.
You can specify `modelable` to execute the query in the context of a specific model; for queries returning rows, the
rows will be returned as an [[toucan2.instance/instance]] of the resolved model.
See [[toucan2.connection]] for Connection resolution rules."
(query* pipeline/reducible-parsed-args))
;;;; Util functions for running queries and immediately realizing the results.
(def ^{:arglists '([queryable]
[connectable queryable]
[connectable modelable queryable]
[connectable query-type modelable queryable])}
query
"Like [[reducible-query]], but immediately executes and fully reduces the query.
```clj
(query ::my-connectable [\"SELECT * FROM venues;\"])
;; => [{:id 1, :name \"Tempest\"}
{:id 2, :name \"BevMo\"}]
```
Like [[reducible-query]], this may be used with either `SELECT` queries that return rows or things like `UPDATE` that
normally return the count of rows updated."
(query*
(fn [query-type parsed-args]
(let [rf (pipeline/default-rf query-type)]
(pipeline/transduce-parsed rf query-type parsed-args)))))
(def ^{:arglists '([queryable]
[connectable queryable]
[connectable modelable queryable]
[connectable query-type modelable queryable])}
query-one
"Like [[query]], and immediately executes the query, but realizes and returns only the first result.
```clj
(query-one ::my-connectable [\"SELECT * FROM venues;\"])
;; => {:id 1, :name \"Tempest\"}
```
Like [[reducible-query]], this may be used with either `SELECT` queries that return rows or things like `UPDATE` that
normally return the count of rows updated."
(query*
(fn [query-type parsed-args]
(let [rf (pipeline/default-rf query-type)
xform (pipeline/first-result-xform-fn query-type)]
(pipeline/transduce-parsed (xform rf) query-type parsed-args)))))
;;;; [[with-call-count]]
(defn ^:no-doc do-with-call-counts
"Impl for [[with-call-count]] macro; don't call this directly."
[f]
(let [call-count (atom 0)
old-thunk pipeline/*call-count-thunk*]
(binding [pipeline/*call-count-thunk* (fn []
(when old-thunk
(old-thunk))
(swap! call-count inc))]
(f (fn [] @call-count)))))
(defmacro with-call-count
"Execute `body`, trackingthe number of database queries and statements executed. This number can be fetched at any
time withing `body` by calling function bound to `call-count-fn-binding`:
```clj
(with-call-count [call-count]
(select ...)
(println \"CALLS:\" (call-count))
(insert! ...)
(println \"CALLS:\" (call-count)))
;; -> CALLS: 1
;; -> CALLS: 2
```"
[[call-count-fn-binding] & body]
`(do-with-call-counts (^:once fn* [~call-count-fn-binding] ~@body)))
(s/fdef with-call-count
:args (s/cat :bindings (s/spec (s/cat :call-count-binding symbol?))
:body (s/+ any?))
:ret any?)