Skip to content

Commit c2cb04f

Browse files
authored
Implement .getGeneratedKeys[R] (#9)
I hardcoded it into the base `DbApi`/`Query` APIs, rather than as a dialect. Sqlite doesn't support it, but it is a builtin part of the JDBC interface, so it's a bit on the fence which side it should go. But the fact that it does need special support in the core `DbApi` implementation makes me lean towards treating it as a builtin We need to pass an explicit `[R]` type parameter to `.getGeneratedKeys` because our `Table` model is not rich enough to tell us what the auto generated primary keys of the table are. This is in contrast to richer models like SLICK which do contain this information. We can streamline this later if necessary, but passing in `[Int]` or whatever is not a huge hardship.
1 parent e05cf30 commit c2cb04f

File tree

11 files changed

+700
-7
lines changed

11 files changed

+700
-7
lines changed

docs/reference.md

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,38 @@ dbClient.transaction { db =>
139139

140140

141141

142+
### DbApi.updateGetGeneratedKeysSql
143+
144+
Allows you to fetch the primary keys that were auto-generated for an INSERT
145+
defined as a `SqlStr`.
146+
Note: not supported by Sqlite https://github.com/xerial/sqlite-jdbc/issues/980
147+
148+
```scala
149+
dbClient.transaction { db =>
150+
val newName = "Moo Moo Cow"
151+
val newDateOfBirth = LocalDate.parse("2000-01-01")
152+
val generatedIds = db
153+
.updateGetGeneratedKeysSql[Int](
154+
sql"INSERT INTO buyer (name, date_of_birth) VALUES ($newName, $newDateOfBirth), ($newName, $newDateOfBirth)"
155+
)
156+
157+
assert(generatedIds == Seq(4, 5))
158+
159+
db.run(Buyer.select) ==> List(
160+
Buyer[Sc](1, "James Bond", LocalDate.parse("2001-02-03")),
161+
Buyer[Sc](2, "叉烧包", LocalDate.parse("1923-11-12")),
162+
Buyer[Sc](3, "Li Haoyi", LocalDate.parse("1965-08-09")),
163+
Buyer[Sc](4, "Moo Moo Cow", LocalDate.parse("2000-01-01")),
164+
Buyer[Sc](5, "Moo Moo Cow", LocalDate.parse("2000-01-01"))
165+
)
166+
}
167+
```
168+
169+
170+
171+
172+
173+
142174
### DbApi.runRaw
143175

144176
`runRawQuery` is similar to `runQuery` but allows you to pass in the SQL strings
@@ -183,6 +215,40 @@ dbClient.transaction { db =>
183215

184216

185217

218+
### DbApi.updateGetGeneratedKeysRaw
219+
220+
Allows you to fetch the primary keys that were auto-generated for an INSERT
221+
defined using a raw `java.lang.String` and variables.
222+
Note: not supported by Sqlite https://github.com/xerial/sqlite-jdbc/issues/980
223+
224+
```scala
225+
dbClient.transaction { db =>
226+
val generatedKeys = db.updateGetGeneratedKeysRaw[Int](
227+
"INSERT INTO buyer (name, date_of_birth) VALUES (?, ?), (?, ?)",
228+
Seq(
229+
"Moo Moo Cow",
230+
LocalDate.parse("2000-01-01"),
231+
"Moo Moo Cow",
232+
LocalDate.parse("2000-01-01")
233+
)
234+
)
235+
assert(generatedKeys == Seq(4, 5))
236+
237+
db.run(Buyer.select) ==> List(
238+
Buyer[Sc](1, "James Bond", LocalDate.parse("2001-02-03")),
239+
Buyer[Sc](2, "叉烧包", LocalDate.parse("1923-11-12")),
240+
Buyer[Sc](3, "Li Haoyi", LocalDate.parse("1965-08-09")),
241+
Buyer[Sc](4, "Moo Moo Cow", LocalDate.parse("2000-01-01")),
242+
Buyer[Sc](5, "Moo Moo Cow", LocalDate.parse("2000-01-01"))
243+
)
244+
}
245+
```
246+
247+
248+
249+
250+
251+
186252
### DbApi.stream
187253

188254
`db.stream` can be run on queries that return `Seq[T]`s, and makes them
@@ -5877,6 +5943,262 @@ Purchase.select.mapAggregate((p, ps) =>
58775943
58785944
58795945
5946+
## GetGeneratedKeys
5947+
`INSERT` operations with `.getGeneratedKeys`. Not supported by Sqlite (see https://github.com/xerial/sqlite-jdbc/issues/980)
5948+
### GetGeneratedKeys.single.values
5949+
5950+
`getGeneratedKeys` on an `insert` returns the primary key, even if it was provided
5951+
explicitly.
5952+
5953+
```scala
5954+
Buyer.insert
5955+
.values(
5956+
Buyer[Sc](17, "test buyer", LocalDate.parse("2023-09-09"))
5957+
)
5958+
.getGeneratedKeys[Int]
5959+
```
5960+
5961+
5962+
*
5963+
```sql
5964+
INSERT INTO buyer (id, name, date_of_birth) VALUES (?, ?, ?)
5965+
```
5966+
5967+
5968+
5969+
*
5970+
```scala
5971+
Seq(17)
5972+
```
5973+
5974+
5975+
5976+
----
5977+
5978+
5979+
5980+
```scala
5981+
Buyer.select.filter(_.name `=` "test buyer")
5982+
```
5983+
5984+
5985+
5986+
5987+
*
5988+
```scala
5989+
Seq(Buyer[Sc](17, "test buyer", LocalDate.parse("2023-09-09")))
5990+
```
5991+
5992+
5993+
5994+
### GetGeneratedKeys.single.columns
5995+
5996+
All styles of `INSERT` query support `.getGeneratedKeys`, with this example
5997+
using `insert.columns` rather than `insert.values`. You can also retrieve
5998+
the generated primary keys using any compatible type, here shown using `Long`
5999+
rather than `Int`
6000+
6001+
```scala
6002+
Buyer.insert
6003+
.columns(
6004+
_.name := "test buyer",
6005+
_.dateOfBirth := LocalDate.parse("2023-09-09"),
6006+
_.id := 4
6007+
)
6008+
.getGeneratedKeys[Long]
6009+
```
6010+
6011+
6012+
*
6013+
```sql
6014+
INSERT INTO buyer (name, date_of_birth, id) VALUES (?, ?, ?)
6015+
```
6016+
6017+
6018+
6019+
*
6020+
```scala
6021+
Seq(4L)
6022+
```
6023+
6024+
6025+
6026+
----
6027+
6028+
6029+
6030+
```scala
6031+
Buyer.select.filter(_.name `=` "test buyer")
6032+
```
6033+
6034+
6035+
6036+
6037+
*
6038+
```scala
6039+
Seq(Buyer[Sc](4, "test buyer", LocalDate.parse("2023-09-09")))
6040+
```
6041+
6042+
6043+
6044+
### GetGeneratedKeys.single.partial
6045+
6046+
If the primary key was not provided but was auto-generated by the database,
6047+
`getGeneratedKeys` returns the generated value
6048+
6049+
```scala
6050+
Buyer.insert
6051+
.columns(_.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09"))
6052+
.getGeneratedKeys[Int]
6053+
```
6054+
6055+
6056+
*
6057+
```sql
6058+
INSERT INTO buyer (name, date_of_birth) VALUES (?, ?)
6059+
```
6060+
6061+
6062+
6063+
*
6064+
```scala
6065+
Seq(4)
6066+
```
6067+
6068+
6069+
6070+
----
6071+
6072+
6073+
6074+
```scala
6075+
Buyer.select.filter(_.name `=` "test buyer")
6076+
```
6077+
6078+
6079+
6080+
6081+
*
6082+
```scala
6083+
Seq(Buyer[Sc](4, "test buyer", LocalDate.parse("2023-09-09")))
6084+
```
6085+
6086+
6087+
6088+
### GetGeneratedKeys.batch.partial
6089+
6090+
`getGeneratedKeys` can return multiple generated primary key values for
6091+
a batch insert statement
6092+
6093+
```scala
6094+
Buyer.insert
6095+
.batched(_.name, _.dateOfBirth)(
6096+
("test buyer A", LocalDate.parse("2001-04-07")),
6097+
("test buyer B", LocalDate.parse("2002-05-08")),
6098+
("test buyer C", LocalDate.parse("2003-06-09"))
6099+
)
6100+
.getGeneratedKeys[Int]
6101+
```
6102+
6103+
6104+
*
6105+
```sql
6106+
INSERT INTO buyer (name, date_of_birth)
6107+
VALUES (?, ?), (?, ?), (?, ?)
6108+
```
6109+
6110+
6111+
6112+
*
6113+
```scala
6114+
Seq(4, 5, 6)
6115+
```
6116+
6117+
6118+
6119+
----
6120+
6121+
6122+
6123+
```scala
6124+
Buyer.select
6125+
```
6126+
6127+
6128+
6129+
6130+
*
6131+
```scala
6132+
Seq(
6133+
Buyer[Sc](1, "James Bond", LocalDate.parse("2001-02-03")),
6134+
Buyer[Sc](2, "叉烧包", LocalDate.parse("1923-11-12")),
6135+
Buyer[Sc](3, "Li Haoyi", LocalDate.parse("1965-08-09")),
6136+
// id=4,5,6 comes from auto increment
6137+
Buyer[Sc](4, "test buyer A", LocalDate.parse("2001-04-07")),
6138+
Buyer[Sc](5, "test buyer B", LocalDate.parse("2002-05-08")),
6139+
Buyer[Sc](6, "test buyer C", LocalDate.parse("2003-06-09"))
6140+
)
6141+
```
6142+
6143+
6144+
6145+
### GetGeneratedKeys.select.simple
6146+
6147+
`getGeneratedKeys` can return multiple generated primary key values for
6148+
an `insert` based on a `select`
6149+
6150+
```scala
6151+
Buyer.insert
6152+
.select(
6153+
x => (x.name, x.dateOfBirth),
6154+
Buyer.select.map(x => (x.name, x.dateOfBirth)).filter(_._1 <> "Li Haoyi")
6155+
)
6156+
.getGeneratedKeys[Int]
6157+
```
6158+
6159+
6160+
*
6161+
```sql
6162+
INSERT INTO buyer (name, date_of_birth)
6163+
SELECT buyer0.name AS res_0, buyer0.date_of_birth AS res_1
6164+
FROM buyer buyer0
6165+
WHERE (buyer0.name <> ?)
6166+
```
6167+
6168+
6169+
6170+
*
6171+
```scala
6172+
Seq(4, 5)
6173+
```
6174+
6175+
6176+
6177+
----
6178+
6179+
6180+
6181+
```scala
6182+
Buyer.select
6183+
```
6184+
6185+
6186+
6187+
6188+
*
6189+
```scala
6190+
Seq(
6191+
Buyer[Sc](1, "James Bond", LocalDate.parse("2001-02-03")),
6192+
Buyer[Sc](2, "叉烧包", LocalDate.parse("1923-11-12")),
6193+
Buyer[Sc](3, "Li Haoyi", LocalDate.parse("1965-08-09")),
6194+
// id=4,5 comes from auto increment, 6 is filtered out in the select
6195+
Buyer[Sc](4, "James Bond", LocalDate.parse("2001-02-03")),
6196+
Buyer[Sc](5, "叉烧包", LocalDate.parse("1923-11-12"))
6197+
)
6198+
```
6199+
6200+
6201+
58806202
## SubQuery
58816203
Queries that explicitly use subqueries (e.g. for `JOIN`s) or require subqueries to preserve the Scala semantics of the various operators
58826204
### SubQuery.sortTakeJoin

mill

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
set -e
88

99
if [ -z "${DEFAULT_MILL_VERSION}" ] ; then
10-
DEFAULT_MILL_VERSION=0.11.6
10+
DEFAULT_MILL_VERSION=0.11.7-29-f2e220
1111
fi
1212

1313
if [ -z "$MILL_VERSION" ] ; then

0 commit comments

Comments
 (0)