Skip to content

Commit 651dd55

Browse files
authored
Add insert-returning-pk! and insert-returning-instance! (#139)
(Singular versions of -pks! and -instances!)
1 parent a104567 commit 651dd55

File tree

3 files changed

+104
-78
lines changed

3 files changed

+104
-78
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,6 @@ Let's define another after-select method, `::without-created-at`, to remove the
228228
```clj
229229
(t2/define-after-select ::without-created-at
230230
[row]
231-
(println "row:" row) ; NOCOMMIT
232231
(dissoc row :created-at))
233232

234233
(derive :models/people.cool.without-created-at :models/people.cool)

src/toucan2/insert.clj

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,12 +138,21 @@
138138
[& unparsed-args]
139139
(pipeline/transduce-unparsed-with-default-rf :toucan.query-type/insert.pks unparsed-args))
140140

141+
(defn insert-returning-pk!
142+
"Like [[insert-returning-pks!]], but for one-row insertions. For models with a single primary key, this returns just the
143+
new primary key as a scalar value (e.g. `1`). For models with a composite primary key, it will return a single tuple
144+
as determined by [[model/primary-keys]] (e.g. `[1 \"Cam\"]`)."
145+
{:arglists '([modelable row-or-rows-or-queryable]
146+
[modelable k v & more]
147+
[modelable columns row-vectors]
148+
[:conn connectable modelable row-or-rows]
149+
[:conn connectable modelable k v & more]
150+
[:conn connectable modelable columns row-vectors])}
151+
[& unparsed-args]
152+
(first (apply insert-returning-pks! unparsed-args)))
153+
141154
(defn insert-returning-instances!
142-
"Like [[insert!]], but returns a vector of the primary keys of the newly inserted rows rather than the number of rows
143-
inserted. The primary keys are determined by [[model/primary-keys]]. For models with a single primary key, this
144-
returns a vector of single values, e.g. `[1 2]` if the primary key is `:id` and you've inserted rows 1 and 2; for
145-
composite primary keys this returns a vector of tuples where each tuple has the value of corresponding primary key as
146-
returned by [[model/primary-keys]], e.g. for composite PK `[:id :name]` you might get `[[1 \"Cam\"] [2 \"Sam\"]]`."
155+
"Like [[insert!]], but returns a vector of maps representing the inserted objects."
147156
{:arglists '([modelable-columns row-or-rows-or-queryable]
148157
[modelable-columns k v & more]
149158
[modelable-columns columns row-vectors]
@@ -152,3 +161,14 @@
152161
[:conn connectable modelable-columns columns row-vectors])}
153162
[& unparsed-args]
154163
(pipeline/transduce-unparsed-with-default-rf :toucan.query-type/insert.instances unparsed-args))
164+
165+
(defn insert-returning-instance!
166+
"Like [[insert-returning-instances!]], but for one-row insertions. Returns the inserted object as a map."
167+
{:arglists '([modelable row-or-rows-or-queryable]
168+
[modelable k v & more]
169+
[modelable columns row-vectors]
170+
[:conn connectable modelable row-or-rows]
171+
[:conn connectable modelable k v & more]
172+
[:conn connectable modelable columns row-vectors])}
173+
[& unparsed-args]
174+
(first (apply insert-returning-instances! unparsed-args)))

test/toucan2/insert_test.clj

Lines changed: 79 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -94,41 +94,47 @@
9494

9595
(deftest ^:synchronized include-pk-test
9696
(testing "If a value for the PK is explicitly specified, insert! and friends should still work correctly"
97-
(doseq [[insert! expected] {#'insert/insert! 1
98-
#'insert/insert-returning-pks! [4]
99-
#'insert/insert-returning-instances! [(instance/instance
100-
::test/venues
101-
{:id 4
102-
:name "Grant & Green"
103-
:category "bar"
104-
:created-at (LocalDateTime/parse "2017-01-01T00:00")
105-
:updated-at (LocalDateTime/parse "2017-01-01T00:00")})]}]
106-
(test/with-discarded-table-changes :venues
107-
(testing insert!
108-
(is (= expected
109-
(insert! ::test/venues {:id 4, :name "Grant & Green", :category "bar"})))
110-
(testing "Venue 4 should exist now"
111-
(is (= (instance/instance ::test/venues {:id 4
112-
:name "Grant & Green"
113-
:category "bar"
114-
:created-at (LocalDateTime/parse "2017-01-01T00:00")
115-
:updated-at (LocalDateTime/parse "2017-01-01T00:00")})
116-
(select/select-one ::test/venues 4)))))))))
97+
(let [new-venue (instance/instance
98+
::test/venues
99+
{:id 4
100+
:name "Grant & Green"
101+
:category "bar"
102+
:created-at (LocalDateTime/parse "2017-01-01T00:00")
103+
:updated-at (LocalDateTime/parse "2017-01-01T00:00")})]
104+
(doseq [[insert! expected] {#'insert/insert! 1
105+
#'insert/insert-returning-pk! 4
106+
#'insert/insert-returning-pks! [4]
107+
#'insert/insert-returning-instance! new-venue
108+
#'insert/insert-returning-instances! [new-venue]}]
109+
(test/with-discarded-table-changes :venues
110+
(testing insert!
111+
(is (= expected
112+
(insert! ::test/venues {:id 4, :name "Grant & Green", :category "bar"})))
113+
(testing "Venue 4 should exist now"
114+
(is (= (instance/instance ::test/venues {:id 4
115+
:name "Grant & Green"
116+
:category "bar"
117+
:created-at (LocalDateTime/parse "2017-01-01T00:00")
118+
:updated-at (LocalDateTime/parse "2017-01-01T00:00")})
119+
(select/select-one ::test/venues 4))))))))))
117120

118121
(deftest ^:synchronized include-pk-non-integer-test
119122
(testing "If a value for a *non-integer* PK is explicitly specified, insert! and friends should still work correctly"
120-
(doseq [[insert! expected] {#'insert/insert! 1
121-
#'insert/insert-returning-pks! ["012345678"]
122-
#'insert/insert-returning-instances! [(instance/instance
123-
::test/phone-number
124-
{:number "012345678", :country-code "US"})]}]
125-
(test/with-discarded-table-changes :phone_number
126-
(testing insert!
127-
(is (= expected
128-
(insert! ::test/phone-number {:number "012345678", :country-code "US"})))
129-
(testing "Phone Number 1 should exist now"
130-
(is (= (instance/instance ::test/phone-number {:number "012345678", :country-code "US"})
131-
(select/select-one ::test/phone-number :toucan/pk "012345678")))))))))
123+
(let [new-phone-number (instance/instance
124+
::test/phone-number
125+
{:number "012345678", :country-code "US"})]
126+
(doseq [[insert! expected] {#'insert/insert! 1
127+
#'insert/insert-returning-pk! "012345678"
128+
#'insert/insert-returning-pks! ["012345678"]
129+
#'insert/insert-returning-instance! new-phone-number
130+
#'insert/insert-returning-instances! [new-phone-number]}]
131+
(test/with-discarded-table-changes :phone_number
132+
(testing insert!
133+
(is (= expected
134+
(insert! ::test/phone-number {:number "012345678", :country-code "US"})))
135+
(testing "Phone Number 1 should exist now"
136+
(is (= (instance/instance ::test/phone-number {:number "012345678", :country-code "US"})
137+
(select/select-one ::test/phone-number :toucan/pk "012345678"))))))))))
132138

133139
(deftest ^:synchronized string-model-test
134140
(testing "insert! should work with string table names as the model"
@@ -275,19 +281,18 @@
275281

276282
(deftest ^:synchronized empty-rows-no-op-test
277283
(testing "insert! empty rows should no-op"
278-
(doseq [insert! [#'insert/insert!
279-
#'insert/insert-returning-pks!
280-
#'insert/insert-returning-instances!]
281-
model [::test/venues
282-
:venues]
283-
row-or-rows [nil
284-
[]]]
284+
(doseq [[insert! expected] [[#'insert/insert! 0]
285+
[#'insert/insert-returning-pk! nil]
286+
[#'insert/insert-returning-pks! []]
287+
[#'insert/insert-returning-instance! nil]
288+
[#'insert/insert-returning-instances! []]]
289+
model [::test/venues
290+
:venues]
291+
row-or-rows [nil
292+
[]]]
285293
(testing (pr-str (list insert! model row-or-rows))
286294
(execute/with-call-count [call-count]
287-
(is (= (condp = insert!
288-
#'insert/insert! 0
289-
#'insert/insert-returning-pks! []
290-
#'insert/insert-returning-instances! [])
295+
(is (= expected
291296
(insert! model row-or-rows)))
292297
(testing "\ncall count"
293298
(is (= 0
@@ -329,26 +334,26 @@
329334
{::test/venues :venue})
330335

331336
(deftest ^:synchronized namespaced-test
332-
(doseq [insert! [#'insert/insert!
333-
#'insert/insert-returning-pks!
334-
#'insert/insert-returning-instances!]]
335-
(test/with-discarded-table-changes :venues
336-
(testing insert!
337-
(is (= (condp = insert!
338-
#'insert/insert! 1
339-
#'insert/insert-returning-pks! [4]
340-
#'insert/insert-returning-instances! [(instance/instance
341-
::venues.namespaced
342-
{:venue/name "Grant & Green"
343-
:venue/category "bar"})])
344-
(insert! [::venues.namespaced :venue/name :venue/category]
345-
{:venue/name "Grant & Green", :venue/category "bar"})))
346-
(is (= (instance/instance
347-
::test/venues
348-
{:id 4
349-
:name "Grant & Green"
350-
:category "bar"})
351-
(select/select-one [::test/venues :id :name :category] :id 4)))))))
337+
(let [new-venue (instance/instance
338+
::venues.namespaced
339+
{:venue/name "Grant & Green"
340+
:venue/category "bar"})]
341+
(doseq [[insert! expected] [[#'insert/insert! 1]
342+
[#'insert/insert-returning-pk! 4]
343+
[#'insert/insert-returning-pks! [4]]
344+
[#'insert/insert-returning-instance! new-venue]
345+
[#'insert/insert-returning-instances! [new-venue]]]]
346+
(test/with-discarded-table-changes :venues
347+
(testing insert!
348+
(is (= expected
349+
(insert! [::venues.namespaced :venue/name :venue/category]
350+
{:venue/name "Grant & Green", :venue/category "bar"})))
351+
(is (= (instance/instance
352+
::test/venues
353+
{:id 4
354+
:name "Grant & Green"
355+
:category "bar"})
356+
(select/select-one [::test/venues :id :name :category] :id 4))))))))
352357

353358
(deftest ^:synchronized positional-connectable-test
354359
(testing "Support :conn positional connectable arg"
@@ -374,12 +379,14 @@
374379
(insert/insert! :conn :fake-db ::test/venues {:name "Grant & Green", :category "bar"})))))))
375380

376381
(deftest ^:synchronized insert-returning-pks-should-not-realize-all-columns-test
377-
(testing "insert-returning-pks! should only fetch the PK column(s) when fetching results"
378-
(test/with-discarded-table-changes :venues
379-
(test.track-realized/with-realized-columns [realized-columns]
380-
(is (= [4]
381-
(insert/insert-returning-pks! ::test.track-realized/venues {:name "Walgreens", :category "store"})))
382-
(is (= (case (test/current-db-type)
383-
(:postgres :h2) #{:venues/id}
384-
:mariadb #{:insert-id})
385-
(realized-columns)))))))
382+
(testing "insert-returning-pk(s)! should only fetch the PK column(s) when fetching results"
383+
(doseq [[insert! expected] [[insert/insert-returning-pk! 4]
384+
[insert/insert-returning-pks! [4]]]]
385+
(test/with-discarded-table-changes :venues
386+
(test.track-realized/with-realized-columns [realized-columns]
387+
(is (= expected
388+
(insert! ::test.track-realized/venues {:name "Walgreens", :category "store"})))
389+
(is (= (case (test/current-db-type)
390+
(:postgres :h2) #{:venues/id}
391+
:mariadb #{:insert-id})
392+
(realized-columns))))))))

0 commit comments

Comments
 (0)