/
sql.clj
186 lines (154 loc) · 8.14 KB
/
sql.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
;; copyright (c) 2019-2022 Sean Corfield, all rights reserved
(ns next.jdbc.sql
"Some utility functions that make common operations easier by
providing some syntactic sugar over `execute!`/`execute-one!`.
This is intended to provide a minimal level of parity with
`clojure.java.jdbc` (`insert!`, `insert-multi!`, `query`, `find-by-keys`,
`get-by-id`, `update!`, and `delete!`).
For anything more complex, use a library like HoneySQL
https://github.com/seancorfield/honeysql to generate SQL + parameters.
The following options are supported:
* `:table-fn` -- specify a function used to convert table names (strings)
to SQL entity names -- see the `next.jdbc.quoted` namespace for the
most common quoting strategy functions,
* `:column-fn` -- specify a function used to convert column names (strings)
to SQL entity names -- see the `next.jdbc.quoted` namespace for the
most common quoting strategy functions.
In addition, `find-by-keys` supports `:order-by` to add an `ORDER BY`
clause to the generated SQL."
(:require [next.jdbc :refer [execute! execute-one! execute-batch!]]
[next.jdbc.sql.builder
:refer [for-delete for-insert for-insert-multi
for-query for-update]]))
(set! *warn-on-reflection* true)
(defn insert!
"Syntactic sugar over `execute-one!` to make inserting hash maps easier.
Given a connectable object, a table name, and a data hash map, inserts the
data as a single row in the database and attempts to return a map of generated
keys."
([connectable table key-map]
(insert! connectable table key-map {}))
([connectable table key-map opts]
(let [opts (merge (:options connectable) opts)]
(execute-one! connectable
(for-insert table key-map opts)
(merge {:return-keys true} opts)))))
(defn insert-multi!
"Syntactic sugar over `execute!` or `execute-batch!` to make inserting
columns/rows easier.
Given a connectable object, a table name, a sequence of column names, and
a vector of rows of data (vectors of column values), inserts the data as
multiple rows in the database and attempts to return a vector of maps of
generated keys.
Given a connectable object, a table name, a sequence of hash maps of data,
inserts the data as multiple rows in the database and attempts to return
a vector of maps of generated keys.
If called with `:batch` true will call `execute-batch!` - see its documentation
for situations in which the generated keys may or may not be returned as well as
additional options that can be passed.
Note: without `:batch` this expands to a single SQL statement with placeholders for
every value being inserted -- for large sets of rows, this may exceed the limits
on SQL string size and/or number of parameters for your JDBC driver or your
database!"
{:arglists '([connectable table hash-maps]
[connectable table hash-maps opts]
[connectable table cols rows]
[connectable table cols rows opts])}
([connectable table hash-maps]
(insert-multi! connectable table hash-maps {}))
([connectable table hash-maps-or-cols opts-or-rows]
(if (map? (first hash-maps-or-cols))
(let [cols (keys (first hash-maps-or-cols))
->row (fn ->row [m]
(map #(get m %) cols))]
(when-not (apply = (map (comp set keys) hash-maps-or-cols))
(throw (IllegalArgumentException.
"insert-multi! hash maps must all have the same keys")))
(insert-multi! connectable table cols (map ->row hash-maps-or-cols) opts-or-rows))
(insert-multi! connectable table hash-maps-or-cols opts-or-rows {})))
([connectable table cols rows opts]
(if (seq rows)
(let [opts (merge (:options connectable) opts)
batch? (:batch opts)]
(if batch?
(let [[sql & param-groups] (for-insert-multi table cols rows opts)]
(execute-batch! connectable sql param-groups
(merge {:return-keys true :return-generated-keys true} opts)))
(execute! connectable
(for-insert-multi table cols rows opts)
(merge {:return-keys true} opts))))
[])))
(defn query
"Syntactic sugar over `execute!` to provide a query alias.
Given a connectable object, and a vector of SQL and its parameters,
returns a vector of hash maps of rows that match."
([connectable sql-params]
(query connectable sql-params {}))
([connectable sql-params opts]
(let [opts (merge (:options connectable) opts)]
(execute! connectable sql-params opts))))
(defn find-by-keys
"Syntactic sugar over `execute!` to make certain common queries easier.
Given a connectable object, a table name, and either a hash map of
columns and values to search on or a vector of a SQL where clause and
parameters, returns a vector of hash maps of rows that match.
If `:all` is passed instead of a hash map or vector -- the query will
select all rows in the table, subject to any pagination options below.
If `:columns` is passed, only that specified subset of columns will be
returned in each row (otherwise all columns are selected).
If the `:order-by` option is present, add an `ORDER BY` clause. `:order-by`
should be a vector of column names or pairs of column name / direction,
which can be `:asc` or `:desc`.
If the `:top` option is present, the SQL Server `SELECT TOP ?` syntax
is used and the value of the option is inserted as an additional parameter.
If the `:limit` option is present, the MySQL `LIMIT ? OFFSET ?` syntax
is used (using the `:offset` option if present, else `OFFSET ?` is omitted).
PostgreSQL also supports this syntax.
If the `:offset` option is present (without `:limit`), the standard
`OFFSET ? ROWS FETCH NEXT ? ROWS ONLY` syntax is used (using the `:fetch`
option if present, else `FETCH...` is omitted)."
([connectable table key-map]
(find-by-keys connectable table key-map {}))
([connectable table key-map opts]
(let [opts (merge (:options connectable) opts)]
(execute! connectable (for-query table key-map opts) opts))))
(defn get-by-id
"Syntactic sugar over `execute-one!` to make certain common queries easier.
Given a connectable object, a table name, and a primary key value, returns
a hash map of the first row that matches.
By default, the primary key is assumed to be `id` but that can be overridden
in the five-argument call.
As with `find-by-keys`, you can specify `:columns` to return just a
subset of the columns in the returned row.
Technically, this also supports `:order-by`, `:top`, `:limit`, `:offset`,
and `:fetch` -- like `find-by-keys` -- but they don't make as much sense
here since only one row is ever returned."
([connectable table pk]
(get-by-id connectable table pk :id {}))
([connectable table pk opts]
(get-by-id connectable table pk :id opts))
([connectable table pk pk-name opts]
(let [opts (merge (:options connectable) opts)]
(execute-one! connectable (for-query table {pk-name pk} opts) opts))))
(defn update!
"Syntactic sugar over `execute-one!` to make certain common updates easier.
Given a connectable object, a table name, a hash map of columns and values
to set, and either a hash map of columns and values to search on or a vector
of a SQL where clause and parameters, perform an update on the table."
([connectable table key-map where-params]
(update! connectable table key-map where-params {}))
([connectable table key-map where-params opts]
(let [opts (merge (:options connectable) opts)]
(execute-one! connectable
(for-update table key-map where-params opts)
opts))))
(defn delete!
"Syntactic sugar over `execute-one!` to make certain common deletes easier.
Given a connectable object, a table name, and either a hash map of columns
and values to search on or a vector of a SQL where clause and parameters,
perform a delete on the table."
([connectable table where-params]
(delete! connectable table where-params {}))
([connectable table where-params opts]
(let [opts (merge (:options connectable) opts)]
(execute-one! connectable (for-delete table where-params opts) opts))))