Skip to content

Commit 7a395c4

Browse files
committed
Query' altered execution: Allow changing result type
1 parent d685883 commit 7a395c4

File tree

3 files changed

+118
-63
lines changed

3 files changed

+118
-63
lines changed

modules/core/src/main/scala/doobie/util/query.scala

Lines changed: 103 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,16 @@ object query {
105105
toConnectionIO(a, IHRS.build[F, B])
106106
}
107107

108-
/** Just like `to` but allowing to alter `PreparedExecution`.
108+
/** Like [[to]], but allow altering various parts of the execute steps before it is run
109+
* @tparam F
110+
* The collection type e.g. List, Vector
111+
* @tparam R
112+
* The result type (Changing PreparedExecution's process step may change the result type)
109113
*/
110-
def toAlteringExecution[F[_]](
114+
def toAlteringExecution[F[_], R](
111115
a: A,
112-
fn: PreparedExecution[F[B]] => PreparedExecution[F[B]]
113-
)(implicit f: FactoryCompat[B, F[B]]): ConnectionIO[F[B]] = {
116+
fn: PreparedExecution[F[B]] => PreparedExecution[R]
117+
)(implicit f: FactoryCompat[B, F[B]]): ConnectionIO[R] = {
114118
toConnectionIOAlteringExecution(a, IHRS.build[F, B], fn)
115119
}
116120

@@ -122,15 +126,21 @@ object query {
122126
def toMap[K, V](a: A)(implicit ev: B =:= (K, V), f: FactoryCompat[(K, V), Map[K, V]]): ConnectionIO[Map[K, V]] =
123127
toConnectionIO(a, IHRS.buildPair[Map, K, V](f, read.map(ev)))
124128

125-
/** Just like `toMap` but allowing to alter `PreparedExecution`.
129+
/** Like [[toMap]], but allow altering various parts of the execute steps before it is run
130+
* @tparam K
131+
* Type of the map's key
132+
* @tparam V
133+
* Type of the map's values
134+
* @tparam R
135+
* The result type (Changing PreparedExecution's process step may change the result type)
126136
*/
127-
def toMapAlteringExecution[K, V](
137+
def toMapAlteringExecution[K, V, R](
128138
a: A,
129-
fn: PreparedExecution[Map[K, V]] => PreparedExecution[Map[K, V]]
139+
fn: PreparedExecution[Map[K, V]] => PreparedExecution[R]
130140
)(implicit
131141
ev: B =:= (K, V),
132142
f: FactoryCompat[(K, V), Map[K, V]]
133-
): ConnectionIO[Map[K, V]] =
143+
): ConnectionIO[R] =
134144
toConnectionIOAlteringExecution(a, IHRS.buildPair[Map, K, V](f, read.map(ev)), fn)
135145

136146
/** Apply the argument `a` to construct a program in `[[doobie.free.connection.ConnectionIO ConnectionIO]]` yielding
@@ -140,12 +150,17 @@ object query {
140150
def accumulate[F[_]: Alternative](a: A): ConnectionIO[F[B]] =
141151
toConnectionIO(a, IHRS.accumulate[F, B])
142152

143-
/** Just like `accumulate` but allowing to alter `PreparedExecution`.
153+
/** Like [[accumulate]], but allow altering various parts of the execute steps before it is run
154+
*
155+
* @tparam F
156+
* The collection type e.g. List, Vector
157+
* @tparam R
158+
* The result type (Changing PreparedExecution's process step may change the result type)
144159
*/
145-
def accumulateAlteringExecution[F[_]: Alternative](
160+
def accumulateAlteringExecution[F[_]: Alternative, R](
146161
a: A,
147-
fn: PreparedExecution[F[B]] => PreparedExecution[F[B]]
148-
): ConnectionIO[F[B]] =
162+
fn: PreparedExecution[F[B]] => PreparedExecution[R]
163+
): ConnectionIO[R] =
149164
toConnectionIOAlteringExecution(a, IHRS.accumulate[F, B], fn)
150165

151166
/** Apply the argument `a` to construct a program in `[[doobie.free.connection.ConnectionIO ConnectionIO]]` yielding
@@ -155,9 +170,12 @@ object query {
155170
def unique(a: A): ConnectionIO[B] =
156171
toConnectionIO(a, IHRS.getUnique[B])
157172

158-
/** Just like `unique` but allowing to alter `PreparedExecution`.
173+
/** Like [[unique]], but allow altering various parts of the execute steps before it is run
174+
*
175+
* @tparam R
176+
* The result type (Changing PreparedExecution's process step may change the result type)
159177
*/
160-
def uniqueAlteringExecution(a: A, fn: PreparedExecution[B] => PreparedExecution[B]): ConnectionIO[B] =
178+
def uniqueAlteringExecution[R](a: A, fn: PreparedExecution[B] => PreparedExecution[R]): ConnectionIO[R] =
161179
toConnectionIOAlteringExecution(a, IHRS.getUnique[B], fn)
162180

163181
/** Apply the argument `a` to construct a program in `[[doobie.free.connection.ConnectionIO ConnectionIO]]` yielding
@@ -167,12 +185,14 @@ object query {
167185
def option(a: A): ConnectionIO[Option[B]] =
168186
toConnectionIO(a, IHRS.getOption[B])
169187

170-
/** Just like `option` but allowing to alter `PreparedExecution`.
188+
/** Like [[option]], but allow altering various parts of the execute steps before it is run
189+
* @tparam R
190+
* The result type (Changing PreparedExecution's process step may change the result type)
171191
*/
172-
def optionAlteringExecution(
192+
def optionAlteringExecution[R](
173193
a: A,
174-
fn: PreparedExecution[Option[B]] => PreparedExecution[Option[B]]
175-
): ConnectionIO[Option[B]] =
194+
fn: PreparedExecution[Option[B]] => PreparedExecution[R]
195+
): ConnectionIO[R] =
176196
toConnectionIOAlteringExecution(a, IHRS.getOption[B], fn)
177197

178198
/** Apply the argument `a` to construct a program in `[[doobie.free.connection.ConnectionIO ConnectionIO]]` yielding
@@ -183,22 +203,24 @@ object query {
183203
def nel(a: A): ConnectionIO[NonEmptyList[B]] =
184204
toConnectionIO(a, IHRS.nel[B])
185205

186-
/** Just like `nel` but allowing to alter `PreparedExecution`.
206+
/** Like [[nel]], but allow altering various parts of the execute steps before it is run
207+
* @tparam R
208+
* The result type (Changing PreparedExecution's process step may change the result type)
187209
*/
188-
def nelAlteringExecution(
210+
def nelAlteringExecution[R](
189211
a: A,
190-
fn: PreparedExecution[NonEmptyList[B]] => PreparedExecution[NonEmptyList[B]]
191-
): ConnectionIO[NonEmptyList[B]] =
212+
fn: PreparedExecution[NonEmptyList[B]] => PreparedExecution[R]
213+
): ConnectionIO[R] =
192214
toConnectionIOAlteringExecution(a, IHRS.nel[B], fn)
193215

194216
private def toConnectionIO[C](a: A, rsio: ResultSetIO[C]): ConnectionIO[C] =
195217
IHC.executeWithResultSet(preparedExecution(sql, a, rsio), mkLoggingInfo(a))
196218

197-
private def toConnectionIOAlteringExecution[C](
219+
private def toConnectionIOAlteringExecution[C, R](
198220
a: A,
199221
rsio: ResultSetIO[C],
200-
fn: PreparedExecution[C] => PreparedExecution[C]
201-
): ConnectionIO[C] =
222+
fn: PreparedExecution[C] => PreparedExecution[R]
223+
): ConnectionIO[R] =
202224
IHC.executeWithResultSet(fn(preparedExecution(sql, a, rsio)), mkLoggingInfo(a))
203225

204226
private def preparedExecution[C](sql: String, a: A, rsio: ResultSetIO[C]): PreparedExecution[C] =
@@ -248,34 +270,35 @@ object query {
248270
override def outputAnalysis: ConnectionIO[Analysis] = outer.outputAnalysis
249271
override def streamWithChunkSize(n: Int): Stream[ConnectionIO, B] = outer.streamWithChunkSize(a, n)
250272
override def accumulate[F[_]: Alternative]: ConnectionIO[F[B]] = outer.accumulate[F](a)
251-
override def accumulateAlteringExecution[F[_]: Alternative](
252-
fn: PreparedExecution[F[B]] => PreparedExecution[F[B]]
253-
): ConnectionIO[F[B]] = outer.accumulateAlteringExecution(a, fn)
273+
override def accumulateAlteringExecution[F[_]: Alternative, R](
274+
fn: PreparedExecution[F[B]] => PreparedExecution[R]
275+
): ConnectionIO[R] = outer.accumulateAlteringExecution(a, fn)
254276
override def to[F[_]](implicit f: FactoryCompat[B, F[B]]): ConnectionIO[F[B]] = outer.to[F](a)
255-
override def toAlteringExecution[F[_]](fn: PreparedExecution[F[B]] => PreparedExecution[F[B]])(implicit
277+
override def toAlteringExecution[F[_], R](fn: PreparedExecution[F[B]] => PreparedExecution[R])(implicit
256278
f: FactoryCompat[B, F[B]]
257-
): ConnectionIO[F[B]] =
279+
): ConnectionIO[R] =
258280
outer.toAlteringExecution(a, fn)
259281
override def toMap[K, V](implicit
260282
ev: B =:= (K, V),
261283
f: FactoryCompat[(K, V), Map[K, V]]
262284
): ConnectionIO[Map[K, V]] = outer.toMap(a)
263-
override def toMapAlteringExecution[K, V](fn: PreparedExecution[Map[K, V]] => PreparedExecution[Map[K, V]])(
285+
286+
override def toMapAlteringExecution[K, V, R](fn: PreparedExecution[Map[K, V]] => PreparedExecution[R])(
264287
implicit
265288
ev: B =:= (K, V),
266289
f: FactoryCompat[(K, V), Map[K, V]]
267-
): ConnectionIO[Map[K, V]] = outer.toMapAlteringExecution(a, fn)
290+
): ConnectionIO[R] = outer.toMapAlteringExecution(a, fn)
268291
override def unique: ConnectionIO[B] = outer.unique(a)
269-
override def uniqueAlteringExecution(fn: PreparedExecution[B] => PreparedExecution[B]): ConnectionIO[B] =
292+
override def uniqueAlteringExecution[R](fn: PreparedExecution[B] => PreparedExecution[R]): ConnectionIO[R] =
270293
outer.uniqueAlteringExecution(a, fn)
271294
override def option: ConnectionIO[Option[B]] = outer.option(a)
272-
override def optionAlteringExecution(fn: PreparedExecution[Option[B]] => PreparedExecution[Option[B]])
273-
: ConnectionIO[Option[B]] =
295+
override def optionAlteringExecution[R](fn: PreparedExecution[Option[B]] => PreparedExecution[R])
296+
: ConnectionIO[R] =
274297
outer.optionAlteringExecution(a, fn)
275298
override def nel: ConnectionIO[NonEmptyList[B]] = outer.nel(a)
276-
override def nelAlteringExecution(
277-
fn: PreparedExecution[NonEmptyList[B]] => PreparedExecution[NonEmptyList[B]]
278-
): ConnectionIO[NonEmptyList[B]] =
299+
override def nelAlteringExecution[R](
300+
fn: PreparedExecution[NonEmptyList[B]] => PreparedExecution[R]
301+
): ConnectionIO[R] =
279302
outer.nelAlteringExecution(a, fn)
280303
override def map[C](f: B => C): Query0[C] = outer.map(f).toQuery0(a)
281304
override def inspect[R](f: (String, PreparedStatementIO[Unit]) => ConnectionIO[R]): ConnectionIO[R] =
@@ -386,9 +409,15 @@ object query {
386409
*/
387410
def to[F[_]](implicit f: FactoryCompat[B, F[B]]): ConnectionIO[F[B]]
388411

389-
def toAlteringExecution[F[_]](
390-
fn: PreparedExecution[F[B]] => PreparedExecution[F[B]]
391-
)(implicit f: FactoryCompat[B, F[B]]): ConnectionIO[F[B]]
412+
/** Like [[to]], but allow altering various parts of the execute steps before it is run
413+
* @tparam F
414+
* The collection type e.g. List, Vector
415+
* @tparam R
416+
* The result type (Changing PreparedExecution's process step may change the result type)
417+
*/
418+
def toAlteringExecution[F[_], R](
419+
fn: PreparedExecution[F[B]] => PreparedExecution[R]
420+
)(implicit f: FactoryCompat[B, F[B]]): ConnectionIO[R]
392421

393422
/** Apply the argument `a` to construct a program in `[[doobie.free.connection.ConnectionIO ConnectionIO]]` yielding
394423
* an `Map[(K, V)]` accumulated via the provided `CanBuildFrom`. This is the fastest way to accumulate a
@@ -397,50 +426,70 @@ object query {
397426
*/
398427
def toMap[K, V](implicit ev: B =:= (K, V), f: FactoryCompat[(K, V), Map[K, V]]): ConnectionIO[Map[K, V]]
399428

400-
def toMapAlteringExecution[K, V](
401-
fn: PreparedExecution[Map[K, V]] => PreparedExecution[Map[K, V]]
429+
/** Like [[toMap]], but allow altering various parts of the execute steps before it is run
430+
* @tparam R
431+
* The result type (Changing PreparedExecution's process step may change the result type)
432+
*/
433+
def toMapAlteringExecution[K, V, R](
434+
fn: PreparedExecution[Map[K, V]] => PreparedExecution[R]
402435
)(implicit
403436
ev: B =:= (K, V),
404437
f: FactoryCompat[(K, V), Map[K, V]]
405-
): ConnectionIO[Map[K, V]]
438+
): ConnectionIO[R]
406439

407440
/** Program in `[[doobie.free.connection.ConnectionIO ConnectionIO]]` yielding an `F[B]` accumulated via `MonadPlus`
408441
* append. This method is more general but less efficient than `to`.
409442
* @group Results
410443
*/
411444
def accumulate[F[_]: Alternative]: ConnectionIO[F[B]]
412445

413-
def accumulateAlteringExecution[F[_]: Alternative](
414-
fn: PreparedExecution[F[B]] => PreparedExecution[F[B]]
415-
): ConnectionIO[F[B]]
446+
/** Like [[to]], but allow altering various parts of the execute steps before it is run
447+
* @tparam R
448+
* The result type (Changing PreparedExecution's process step may change the result type)
449+
*/
450+
def accumulateAlteringExecution[F[_]: Alternative, R](
451+
fn: PreparedExecution[F[B]] => PreparedExecution[R]
452+
): ConnectionIO[R]
416453

417454
/** Program in `[[doobie.free.connection.ConnectionIO ConnectionIO]]` yielding a unique `B` and raising an exception
418455
* if the resultset does not have exactly one row. See also `option`.
419456
* @group Results
420457
*/
421458
def unique: ConnectionIO[B]
422459

423-
def uniqueAlteringExecution(fn: PreparedExecution[B] => PreparedExecution[B]): ConnectionIO[B]
460+
/** Like [[unique]], but allow altering various parts of the execute steps before it is run
461+
* @tparam R
462+
* The result type (Changing PreparedExecution's process step may change the result type)
463+
*/
464+
def uniqueAlteringExecution[R](fn: PreparedExecution[B] => PreparedExecution[R]): ConnectionIO[R]
424465

425466
/** Program in `[[doobie.free.connection.ConnectionIO ConnectionIO]]` yielding an optional `B` and raising an
426467
* exception if the resultset has more than one row. See also `unique`.
427468
* @group Results
428469
*/
429470
def option: ConnectionIO[Option[B]]
430471

431-
def optionAlteringExecution(
432-
fn: PreparedExecution[Option[B]] => PreparedExecution[Option[B]]
433-
): ConnectionIO[Option[B]]
472+
/** Like [[option]], but allow altering various parts of the execute steps before it is run
473+
* @tparam R
474+
* The result type (Changing PreparedExecution's process step may change the result type)
475+
*/
476+
def optionAlteringExecution[R](
477+
fn: PreparedExecution[Option[B]] => PreparedExecution[R]
478+
): ConnectionIO[R]
434479

435480
/** Program in `[[doobie.free.connection.ConnectionIO ConnectionIO]]` yielding a `NonEmptyList[B]` and raising an
436481
* exception if the resultset does not have at least one row. See also `unique`.
437482
* @group Results
438483
*/
439484
def nel: ConnectionIO[NonEmptyList[B]]
440485

441-
def nelAlteringExecution(
442-
fn: PreparedExecution[NonEmptyList[B]] => PreparedExecution[NonEmptyList[B]]
443-
): ConnectionIO[NonEmptyList[B]]
486+
/** Like [[nel]], but allow altering various parts of the execute steps before it is run
487+
* @tparam R
488+
* The result type (Changing PreparedExecution's process step may change the result type)
489+
*/
490+
def nelAlteringExecution[R](
491+
fn: PreparedExecution[NonEmptyList[B]] => PreparedExecution[R]
492+
): ConnectionIO[R]
444493

445494
/** @group Transformations */
446495
def map[C](f: B => C): Query0[C]

modules/core/src/main/scala/doobie/util/update.scala

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,13 @@ object update {
193193
loggingInfoForUpdateWithGeneratedKeys(a)
194194
)
195195

196-
/** Just like `withUniqueGeneratedKeys` but allowing to alter `PreparedExecution`.
196+
/** Just like `withUniqueGeneratedKeys` but allowing to alter individual steps of the update execution.
197+
* @tparam R
198+
* Result type (Changing PreparedExecution's process step may change the result type)
197199
*/
198-
def withUniqueGeneratedKeysAlteringExecution[K: Read](columns: String*)(
200+
def withUniqueGeneratedKeysAlteringExecution[K: Read, R](columns: String*)(
199201
a: A,
200-
fn: PreparedExecution[K] => PreparedExecution[K]
202+
fn: PreparedExecution[K] => PreparedExecution[R]
201203
): ConnectionIO[K] =
202204
IHC.executeWithResultSet(
203205
fn(prepareExecutionForWithUniqueGeneratedKeys(columns*)(a)),
@@ -353,6 +355,10 @@ object update {
353355
*/
354356
def withUniqueGeneratedKeys[K: Read](columns: String*): ConnectionIO[K]
355357

358+
/** Just like `withUniqueGeneratedKeys` but allowing to alter individual steps of the update execution.
359+
* @tparam R
360+
* Result type (Changing PreparedExecution's process step may change the result type)
361+
*/
356362
def withUniqueGeneratedKeysAlteringExecution[K: Read](columns: String*)(
357363
fn: PreparedExecution[K] => PreparedExecution[K]
358364
): ConnectionIO[K]

modules/core/src/test/scala/doobie/util/QuerySuite.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class QuerySuite extends munit.CatsEffectSuite {
6464

6565
test("Query toAlteringExecution (result set operations)") {
6666
var didRun = false
67-
pairQuery.toAlteringExecution[List](
67+
pairQuery.toAlteringExecution[List, List[(String, Int)]](
6868
"x",
6969
{ preparedExec =>
7070
val process = IHRS.delay { didRun = true } *> preparedExec.process
@@ -78,7 +78,7 @@ class QuerySuite extends munit.CatsEffectSuite {
7878
}
7979
test("Query toMapAlteringExecution (result set operations)") {
8080
var didRun = false
81-
pairQuery.toMapAlteringExecution[String, Int](
81+
pairQuery.toMapAlteringExecution[String, Int, Map[String, Int]](
8282
"x",
8383
{ preparedExec =>
8484
val process = IHRS.delay { didRun = true } *> preparedExec.process
@@ -92,7 +92,7 @@ class QuerySuite extends munit.CatsEffectSuite {
9292
}
9393
test("Query accumulateAlteringExecution (result set operations)") {
9494
var didRun = false
95-
pairQuery.accumulateAlteringExecution[List](
95+
pairQuery.accumulateAlteringExecution[List, List[(String, Int)]](
9696
"x",
9797
{ preparedExec =>
9898
val process = IHRS.delay { didRun = true } *> preparedExec.process
@@ -223,7 +223,7 @@ class QuerySuite extends munit.CatsEffectSuite {
223223

224224
test("Query0 toAlteringExecution (result set operations)") {
225225
var didRun = false
226-
val result = pairQuery.toQuery0("x").toAlteringExecution[List]({ preparedExec =>
226+
val result = pairQuery.toQuery0("x").toAlteringExecution[List, List[(String, Int)]]({ preparedExec =>
227227
val process = IHRS.delay { didRun = true } *> preparedExec.process
228228
preparedExec.copy(process = process)
229229
})
@@ -234,7 +234,7 @@ class QuerySuite extends munit.CatsEffectSuite {
234234
}
235235
test("Query0 toMapAlteringExecution (result set operations)") {
236236
var didRun = false
237-
pairQuery.toQuery0("x").toMapAlteringExecution[String, Int]({ preparedExec =>
237+
pairQuery.toQuery0("x").toMapAlteringExecution[String, Int, Map[String, Int]]({ preparedExec =>
238238
val process = IHRS.delay { didRun = true } *> preparedExec.process
239239
preparedExec.copy(process = process)
240240
})
@@ -246,7 +246,7 @@ class QuerySuite extends munit.CatsEffectSuite {
246246
}
247247
test("Query0 accumulateAlteringExecution (result set operations)") {
248248
var didRun = false
249-
pairQuery.toQuery0("x").accumulateAlteringExecution[List]({ preparedExec =>
249+
pairQuery.toQuery0("x").accumulateAlteringExecution[List, List[(String, Int)]]({ preparedExec =>
250250
val process = IHRS.delay { didRun = true } *> preparedExec.process
251251
preparedExec.copy(process = process)
252252
})

0 commit comments

Comments
 (0)