@@ -2,137 +2,152 @@ package io.getquill.context
2
2
3
3
import com .typesafe .config .Config
4
4
import io .getquill .JdbcContextConfig
5
- import zio .{ Has , Task , ZIO , ZLayer , ZManaged }
5
+ import io .getquill .context .qzio .ImplicitSyntax .Implicit
6
+ import zio .{ Has , IO , Task , ZIO , ZLayer , ZManaged }
6
7
import zio .stream .ZStream
7
- import io .getquill .util .LoadConfig
8
- import izumi .reflect .Tag
8
+ import io .getquill .util .{ ContextLogger , LoadConfig }
9
9
10
10
import java .io .Closeable
11
11
import java .sql .{ Connection , SQLException }
12
12
import javax .sql .DataSource
13
13
14
14
object ZioJdbc {
15
- import zio .blocking ._
16
-
17
- /** Describes a single HOCON Jdbc Config block */
18
- case class Prefix (name : String )
19
-
20
- type QIO [T ] = ZIO [QConnection , SQLException , T ]
21
- type QStream [T ] = ZStream [Has [Connection ] with Blocking , SQLException , T ]
22
- type QConnection = Has [Connection ] with Blocking
23
- type QDataSource = Has [DataSource with Closeable ] with Blocking
15
+ type QIO [T ] = ZIO [Has [Connection ], SQLException , T ]
16
+ type QStream [T ] = ZStream [Has [Connection ], SQLException , T ]
24
17
25
18
object QIO {
26
19
def apply [T ](t : => T ): QIO [T ] = ZIO .effect(t).refineToOrDie[SQLException ]
27
20
}
28
21
29
- object QDataSource {
30
- object Managed {
31
- def fromDataSource (ds : => DataSource with Closeable ) =
32
- for {
33
- block <- ZManaged .environment[Blocking ]
34
- managedDs <- ZManaged .fromAutoCloseable(Task (ds))
35
- } yield (Has (managedDs) ++ block)
36
- }
22
+ object DataSourceLayer {
23
+ def live : ZLayer [Has [DataSource with Closeable ], SQLException , Has [Connection ]] = layer
37
24
38
- val toConnection : ZLayer [ QDataSource , SQLException , QConnection ] = {
25
+ private [getquill] val layer = {
39
26
val managed =
40
27
for {
41
- fromBlocking <- ZManaged .environment[Has [DataSource with Closeable ] with Blocking ]
42
- from = fromBlocking.get[DataSource with Closeable ]
43
- blocking = fromBlocking.get[Blocking .Service ]
44
- r <- ZManaged .fromAutoCloseable(ZIO .effect(from.getConnection).refineToOrDie[SQLException ]: ZIO [Any , SQLException , Connection ])
45
- } yield Has (r) ++ Has (blocking)
28
+ blockingExecutor <- ZIO .succeed(zio.blocking.Blocking .Service .live.blockingExecutor).toManaged_
29
+ from <- ZManaged .environment[Has [DataSource with Closeable ]]
30
+ r <- ZioJdbc .managedBestEffort(ZIO .effect(from.get.getConnection)).refineToOrDie[SQLException ].lock(blockingExecutor)
31
+ } yield Has (r)
46
32
ZLayer .fromManagedMany(managed)
47
33
}
48
34
49
- def fromDataSource (ds : => DataSource with Closeable ): ZLayer [Blocking , Throwable , QDataSource ] =
50
- ZLayer .fromEffectMany {
51
- for {
52
- block <- ZIO .environment[Blocking ]
53
- dst <- Task (ds)
54
- } yield (Has (dst) ++ block)
55
- }
35
+ def fromDataSource (ds : => DataSource with Closeable ): ZLayer [Any , Throwable , Has [DataSource with Closeable ]] =
36
+ ZLayer .fromEffect(Task (ds))
56
37
57
- def fromConfig (config : => Config ): ZLayer [Blocking , Throwable , QDataSource ] =
38
+ def fromConfig (config : => Config ): ZLayer [Any , Throwable , Has [ DataSource with Closeable ] ] =
58
39
fromJdbcConfig(JdbcContextConfig (config))
59
40
60
- def fromPrefix (prefix : Prefix ): ZLayer [Blocking , Throwable , QDataSource ] =
61
- fromJdbcConfig(JdbcContextConfig (LoadConfig (prefix.name)))
41
+ def fromPrefix (prefix : String ): ZLayer [Any , Throwable , Has [DataSource with Closeable ]] =
42
+ fromJdbcConfig(JdbcContextConfig (LoadConfig (prefix)))
43
+
44
+ def fromJdbcConfig (jdbcContextConfig : => JdbcContextConfig ): ZLayer [Any , Throwable , Has [DataSource with Closeable ]] =
45
+ ZLayer .fromManagedMany(
46
+ for {
47
+ conf <- ZManaged .fromEffect(Task (jdbcContextConfig))
48
+ ds <- managedBestEffort(Task (conf.dataSource: DataSource with Closeable ))
49
+ } yield Has (ds)
50
+ )
51
+ }
62
52
63
- def fromPrefix (prefix : String ): ZLayer [Blocking , Throwable , QDataSource ] =
53
+ object QDataSource {
54
+ object Managed {
55
+ @ deprecated(message = " Not needed anymore. Use: ZioJdbc.managedBestEffort(Task(ds)).map(Has(_))" , " 3.8.0" )
56
+ def fromDataSource (ds : => DataSource with Closeable ) =
57
+ managedBestEffort(Task (ds)).map(Has (_))
58
+ }
59
+
60
+ @ deprecated(" Use DataSourceLayer.live instead" , " 3.8.0" )
61
+ val toConnection : ZLayer [Has [DataSource with Closeable ], SQLException , Has [Connection ]] =
62
+ DataSourceLayer .live
63
+
64
+ @ deprecated(" Use ZLayer.fromEffect(Task(ds)) instead" , " 3.8.0" )
65
+ def fromDataSource (ds : => DataSource with Closeable ): ZLayer [Any , Throwable , Has [DataSource with Closeable ]] =
66
+ ZLayer .fromEffect(Task (ds))
67
+
68
+ def fromConfig (config : => Config ): ZLayer [Any , Throwable , Has [DataSource with Closeable ]] =
69
+ fromJdbcConfig(JdbcContextConfig (config))
70
+
71
+ def fromPrefix (prefix : String ): ZLayer [Any , Throwable , Has [DataSource with Closeable ]] =
64
72
fromJdbcConfig(JdbcContextConfig (LoadConfig (prefix)))
65
73
66
- def fromJdbcConfig (jdbcContextConfig : => JdbcContextConfig ): ZLayer [Blocking , Throwable , QDataSource ] =
74
+ def fromJdbcConfig (jdbcContextConfig : => JdbcContextConfig ): ZLayer [Any , Throwable , Has [ DataSource with Closeable ] ] =
67
75
ZLayer .fromManagedMany(
68
76
for {
69
- block <- ZManaged .environment[Blocking ]
70
77
conf <- ZManaged .fromEffect(Task (jdbcContextConfig))
71
- ds <- ZManaged .fromAutoCloseable (Task (conf.dataSource: DataSource with Closeable ))
72
- } yield ( Has (ds) ++ block )
78
+ ds <- managedBestEffort (Task (conf.dataSource: DataSource with Closeable ))
79
+ } yield Has (ds)
73
80
)
74
81
}
75
82
76
- implicit class ZioQuillThrowableExt [T ](qzio : ZIO [QConnection , Throwable , T ]) {
83
+ implicit class ZioQuillThrowableExt [T ](qzio : ZIO [Has [Connection ], Throwable , T ]) {
84
+ @ deprecated(" Use .refineToOrDie[SQLException]" , " 3.8.0" )
77
85
def justSqlEx = qzio.refineToOrDie[SQLException ]
78
86
}
79
87
80
88
object QConnection {
81
- def fromDataSource : ZLayer [QDataSource , SQLException , QConnection ] = QDataSource .toConnection
82
- def dependOnDataSource [T ](qzio : ZIO [QConnection , Throwable , T ]) =
83
- qzio.justSqlEx.provideLayer(QDataSource .toConnection)
84
- def provideConnection [T ](qzio : ZIO [QConnection , Throwable , T ])(conn : Connection ): ZIO [Blocking , SQLException , T ] =
85
- provideOne[Connection , T , SQLException , Blocking ](conn)(qzio.justSqlEx)
86
- def provideConnectionFrom [T ](qzio : ZIO [QConnection , Throwable , T ])(ds : DataSource with Closeable ): ZIO [Blocking , SQLException , T ] =
87
- provideOne[DataSource & Closeable , T , SQLException , Blocking ](ds)(QConnection .dependOnDataSource(qzio.justSqlEx))
88
- }
89
+ @ deprecated(" Use DataSourceLayer.live. If you need just SQLException add .refineToOrDie[SQLException]" , " 3.8.0" )
90
+ def fromDataSource : ZLayer [Has [DataSource with Closeable ], SQLException , Has [Connection ]] = DataSourceLayer .live
91
+
92
+ @ deprecated(" Use qzio.onDataSource." , " 3.8.0" )
93
+ def dependOnDataSource [T ](qzio : ZIO [Has [Connection ], Throwable , T ]) =
94
+ qzio.onDataSource
95
+
96
+ @ deprecated(" Use qzio.provide(Has(conn)). If you need just SQLException add .refineToOrDie[SQLException]" , " 3.8.0" )
97
+ def provideConnection [T ](qzio : ZIO [Has [Connection ], Throwable , T ])(conn : Connection ): ZIO [Any , SQLException , T ] =
98
+ qzio.provide(Has (conn)).refineToOrDie[SQLException ]
89
99
90
- implicit class DataSourceCloseableExt (ds : DataSource with Closeable ) {
91
- def withDefaultBlocking : QDataSource = Has (ds) ++ Has (Blocking .Service .live)
100
+ @ deprecated(" Use qzio.onDataSource.provide(Has(ds)). If you need just SQLException add .refineToOrDie[SQLException]" , " 3.8.0" )
101
+ def provideConnectionFrom [T ](qzio : ZIO [Has [Connection ], Throwable , T ])(ds : DataSource with Closeable ): ZIO [Any , SQLException , T ] =
102
+ qzio.onDataSource.provide(Has (ds)).refineToOrDie[SQLException ]
92
103
}
93
104
94
- implicit class QuillZioExt [T ](qzio : ZIO [QConnection , Throwable , T ]) {
95
- /**
96
- * Allows the user to specify `Has[DataSource]` instead of `Has[Connection]` for a Quill ZIO value i.e.
97
- * Converts:<br>
98
- * `ZIO[QConnection, Throwable, T]` to `ZIO[QDataSource, Throwable, T]` a.k.a.<br>
99
- * `ZIO[Has[Connection] with Blocking, Throwable, T]` to `ZIO[Has[DataSource] with Blocking, Throwable, T]` a.k.a.<br>
100
- */
101
- def dependOnDataSource (): ZIO [QDataSource , SQLException , T ] = QConnection .dependOnDataSource(qzio)
105
+ implicit class QuillZioExt [T ](qzio : ZIO [Has [Connection ], Throwable , T ]) {
106
+ import io .getquill .context .qzio .ImplicitSyntax ._
102
107
103
108
/**
104
- * Allows the user to specify JDBC `DataSource` instead of `QConnection` for a Quill ZIO value i.e.
105
- * Provides a DataSource object which internally brackets `dataSource.getConnection` and `connection.close()`.
106
- * This effectively converts:<br>
107
- * `ZIO[QConnection, Throwable, T]` to `ZIO[Blocking, Throwable, T]` a.k.a.<br>
108
- * `ZIO[Has[Connection] with Blocking, Throwable, T]` to `ZIO[Blocking, Throwable, T]` a.k.a.<br>
109
+ * Change `Has[Connection]` of a QIO to `Has[DataSource with Closeable]` by providing a `DataSourceLayer.live` instance
110
+ * which will grab a connection from the data-source, perform the QIO operation, and the immediately release the connection.
111
+ * This is used for data-sources that have pooled connections e.g. Hikari.
112
+ * {{{
113
+ * def ds: DataSource with Closeable = ...
114
+ * run(query[Person]).onDataSource.provide(Has(ds))
115
+ * }}}
109
116
*/
110
- def provideConnectionFrom ( ds : DataSource with Closeable ) : ZIO [ Blocking , SQLException , T ] =
111
- QConnection .provideConnectionFrom(qzio)(ds)
117
+ def onDataSource : ZIO [ Has [ DataSource with Closeable ] , SQLException , T ] =
118
+ qzio.provideLayer( DataSourceLayer .live).refineToOrDie[ SQLException ]
112
119
113
- /**
114
- * Allows the user to specify JDBC `Connection` instead of `QConnection` for a Quill ZIO value i.e.
115
- * Provides a Connection object which converts:<br>
116
- * `ZIO[QConnection, Throwable, T]` to `ZIO[Blocking, Throwable, T]` a.k.a.<br>
117
- * `ZIO[Has[Connection] with Blocking, Throwable, T]` to `ZIO[Blocking, Throwable, T]` a.k.a.<br>
118
- */
119
- def provideConnection (conn : Connection ): ZIO [Blocking , SQLException , T ] =
120
- QConnection .provideConnection(qzio)(conn)
120
+ /** Shortcut for `onDataSource` */
121
+ def onDS : ZIO [Has [DataSource with Closeable ], SQLException , T ] =
122
+ qzio.onDataSource
123
+
124
+ def implicitDS (implicit implicitEnv : Implicit [Has [DataSource with Closeable ]]): IO [SQLException , T ] =
125
+ qzio.onDataSource.implicitly
126
+
127
+ @ deprecated(" Use qzio.onDataSource." , " 3.8.0" )
128
+ def dependOnDataSource (): ZIO [Has [DataSource with Closeable ], SQLException , T ] =
129
+ qzio.onDataSource.refineToOrDie[SQLException ]
130
+
131
+ @ deprecated(" Use qzio.onDataSource.provide(Has(ds)). If you need just SQLException add .refineToOrDie[SQLException]" , " 3.8.0" )
132
+ def provideConnectionFrom (ds : DataSource with Closeable ): ZIO [Nothing , SQLException , T ] =
133
+ qzio.onDataSource.provide(Has (ds)).refineToOrDie[SQLException ]
134
+
135
+ @ deprecated(" Use qzio.provide(Has(conn)). If you need just SQLException add .refineToOrDie[SQLException]" , " 3.8.0" )
136
+ def provideConnection (conn : Connection ): ZIO [Any , SQLException , T ] =
137
+ qzio.provide(Has (conn)).refineToOrDie[SQLException ]
121
138
}
122
139
123
- // TODO Rewrite with -P-in and +Pout variant
124
- // private[getquill] case class Provide[+P: Tag](provision: P):
125
- // def to[Rest <: Has[_]: Tag, T, E: Tag](qzio: ZIO[Has[P] with Rest, E, T]): ZIO[Rest, E, T] =
126
- // for {
127
- // rest <- ZIO.environment[Rest]
128
- // env = Has(provision) ++ rest
129
- // result <- qzio.provide(env)
130
- // } yield result
131
-
132
- private [getquill] def provideOne [P : Tag , T , E : Tag , Rest <: Has [_]: Tag ](provision : P )(qzio : ZIO [Has [P ] with Rest , E , T ]): ZIO [Rest , E , T ] =
133
- for {
134
- rest <- ZIO .environment[Rest ]
135
- env = Has (provision) ++ rest
136
- result <- qzio.provide(env)
137
- } yield result
140
+ /**
141
+ * This is the same as `ZManaged.fromAutoCloseable` but if the `.close()` fails it will log `"close() of resource failed"`
142
+ * and continue instead of immediately throwing an error in the ZIO die-channel. That is because for JDBC purposes,
143
+ * a failure on the connection close is usually a recoverable failure. In the cases where it happens it occurs
144
+ * as the byproduct of a bad state (e.g. failing to close a transaction before closing the connection or failing to
145
+ * release a stale connection) which will eventually cause other operations (i.e. future reads/writes) to fail
146
+ * that have not occurred yet.
147
+ */
148
+ def managedBestEffort [R , E , A <: AutoCloseable ](effect : ZIO [R , E , A ]) =
149
+ ZManaged .make(effect)(resource =>
150
+ ZIO .effect(resource.close()).tapError(e => ZIO .effect(logger.underlying.error(s " close() of resource failed " , e)).ignore).ignore)
151
+
152
+ private [getquill] val logger = ContextLogger (ZioJdbc .getClass)
138
153
}
0 commit comments