@zio.dev/zio-blocks 0.0.33 → 0.0.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/guides/compile-time-resource-safety-with-scope.md +16 -17
  2. package/guides/getting-started-with-mux.md +1507 -0
  3. package/guides/query-dsl-extending.md +161 -102
  4. package/guides/query-dsl-fluent-builder.md +217 -157
  5. package/guides/query-dsl-reified-optics.md +12 -10
  6. package/guides/query-dsl-sql.md +246 -165
  7. package/guides/telemetry-guide.md +1069 -0
  8. package/guides/zio-schema-migration.md +29 -22
  9. package/index.md +292 -50
  10. package/package.json +1 -1
  11. package/plans/config-follow-up-prs.md +188 -0
  12. package/plans/config-pr-assessment-roadmap.md +310 -0
  13. package/reference/MuxDataFlow.jsx +250 -0
  14. package/reference/async.md +651 -0
  15. package/reference/chunk.md +3533 -308
  16. package/reference/codegen/case-class.md +436 -0
  17. package/reference/codegen/emitter-config.md +383 -0
  18. package/reference/codegen/examples.md +664 -0
  19. package/reference/codegen/field.md +316 -0
  20. package/reference/codegen/index.md +317 -0
  21. package/reference/codegen/scala-emitter.md +392 -0
  22. package/reference/codegen/scala-file.md +276 -0
  23. package/reference/codegen/sealed-trait.md +408 -0
  24. package/reference/codegen/type-definition.md +340 -0
  25. package/reference/codegen/type-ref.md +201 -0
  26. package/reference/combinators.md +347 -117
  27. package/reference/config.md +158 -0
  28. package/reference/context.md +4 -4
  29. package/reference/datastar.md +346 -0
  30. package/reference/docs.md +1461 -345
  31. package/reference/endpoint/auth-type.md +146 -0
  32. package/reference/endpoint/endpoint.md +297 -0
  33. package/reference/endpoint/http-codec.md +249 -0
  34. package/reference/endpoint/index.md +825 -0
  35. package/reference/endpoint/path-codec.md +237 -0
  36. package/reference/endpoint/route-pattern.md +196 -0
  37. package/reference/endpoint/route-tree.md +111 -0
  38. package/reference/endpoint/segment-codec.md +212 -0
  39. package/reference/html.md +1120 -0
  40. package/reference/htmx/attribute-values.md +359 -0
  41. package/reference/htmx/hx-encoding.md +111 -0
  42. package/reference/htmx/hx-params.md +204 -0
  43. package/reference/htmx/hx-swap.md +276 -0
  44. package/reference/htmx/hx-sync.md +251 -0
  45. package/reference/htmx/hx-target.md +314 -0
  46. package/reference/htmx/hx-trigger.md +457 -0
  47. package/reference/htmx/hx-url-update.md +239 -0
  48. package/reference/htmx/index.md +855 -0
  49. package/reference/http-model/index.md +47 -0
  50. package/reference/http-model/model.md +1481 -0
  51. package/reference/http-model/schema.md +747 -0
  52. package/reference/maybe.md +826 -0
  53. package/reference/media-type.md +2 -2
  54. package/reference/mux.mdx +823 -0
  55. package/reference/openapi.md +1351 -0
  56. package/reference/resource-management/defer-handle.md +1 -1
  57. package/reference/resource-management/resource.md +31 -2
  58. package/reference/resource-management/scope.md +28 -12
  59. package/reference/resource-management/wire.md +3 -7
  60. package/reference/ringbuffer/MpmcDiagram.jsx +717 -0
  61. package/reference/ringbuffer/MpscDiagram.jsx +618 -0
  62. package/reference/ringbuffer/SpmcDiagram.jsx +680 -0
  63. package/reference/ringbuffer/SpscDiagram.jsx +677 -0
  64. package/reference/ringbuffer/advanced.mdx +109 -0
  65. package/reference/ringbuffer/index.mdx +145 -0
  66. package/reference/ringbuffer/mpmc.mdx +151 -0
  67. package/reference/ringbuffer/mpsc.mdx +132 -0
  68. package/reference/ringbuffer/spmc.mdx +108 -0
  69. package/reference/ringbuffer/spsc.mdx +344 -0
  70. package/reference/{allows.md → schema/allows.md} +4 -4
  71. package/reference/{binding-resolver.md → schema/binding-resolver.md} +1 -1
  72. package/reference/{binding.md → schema/binding.md} +2 -3
  73. package/reference/schema/built-in-codecs/avro.md +451 -0
  74. package/reference/schema/built-in-codecs/bson.md +480 -0
  75. package/reference/schema/built-in-codecs/csv.md +564 -0
  76. package/reference/schema/built-in-codecs/index.md +77 -0
  77. package/reference/schema/built-in-codecs/json/index.md +295 -0
  78. package/reference/schema/built-in-codecs/json/json-config.md +217 -0
  79. package/reference/{json-patch.md → schema/built-in-codecs/json/json-patch.md} +5 -5
  80. package/reference/{json-schema.md → schema/built-in-codecs/json/json-schema.md} +14 -47
  81. package/reference/schema/built-in-codecs/json/json-selection.md +322 -0
  82. package/reference/{json.md → schema/built-in-codecs/json/json.md} +32 -64
  83. package/reference/schema/built-in-codecs/messagepack.md +508 -0
  84. package/reference/schema/built-in-codecs/thrift.md +433 -0
  85. package/reference/schema/built-in-codecs/toon.md +1078 -0
  86. package/reference/{xml.md → schema/built-in-codecs/xml.md} +13 -9
  87. package/reference/schema/built-in-codecs/yaml.md +552 -0
  88. package/reference/{codec.md → schema/codec.md} +10 -10
  89. package/reference/{dynamic-optic.md → schema/dynamic-optic.md} +151 -5
  90. package/reference/{dynamic-schema.md → schema/dynamic-schema.md} +8 -8
  91. package/reference/schema/format.md +92 -0
  92. package/reference/schema/index.md +50 -0
  93. package/reference/schema/migration.md +297 -0
  94. package/reference/{modifier.md → schema/modifier.md} +58 -7
  95. package/reference/{optics.md → schema/optics.md} +2 -2
  96. package/reference/{patch.md → schema/patch.md} +1 -1
  97. package/{path-interpolator.md → reference/schema/path-interpolator.md} +165 -72
  98. package/reference/{schema-evolution → schema/schema-evolution}/as.md +8 -8
  99. package/reference/{schema-evolution → schema/schema-evolution}/index.md +2 -2
  100. package/reference/{schema-evolution → schema/schema-evolution}/into.md +8 -8
  101. package/reference/{schema-expr.md → schema/schema-expr.md} +110 -175
  102. package/reference/{schema.md → schema/schema.md} +12 -0
  103. package/reference/{structural-types.md → schema/structural-types.md} +1 -1
  104. package/reference/{type-class-derivation.md → schema/type-class-derivation.md} +63 -1
  105. package/reference/smithy.md +533 -0
  106. package/reference/sql/db-codec-deriver.md +71 -0
  107. package/reference/sql/db-codec.md +687 -0
  108. package/reference/sql/db-con.md +271 -0
  109. package/reference/sql/db-connection.md +153 -0
  110. package/reference/sql/db-param-writer.md +77 -0
  111. package/reference/sql/db-param.md +66 -0
  112. package/reference/sql/db-result-reader.md +146 -0
  113. package/reference/sql/db-tx.md +82 -0
  114. package/reference/sql/db-value.md +41 -0
  115. package/reference/sql/ddl.md +85 -0
  116. package/reference/sql/frag.md +254 -0
  117. package/reference/sql/index.md +341 -0
  118. package/reference/sql/repo.md +600 -0
  119. package/reference/sql/sql-dialect.md +73 -0
  120. package/reference/sql/sql-logger.md +62 -0
  121. package/reference/sql/sql-name-mapper.md +70 -0
  122. package/reference/sql/table-metadata.md +134 -0
  123. package/reference/sql/table.md +448 -0
  124. package/reference/sql/transactor-zio.md +399 -0
  125. package/reference/sql/transactor.md +353 -0
  126. package/reference/sql-zio.md +112 -0
  127. package/reference/streams/concurrent-operators.md +106 -0
  128. package/reference/streams/index.md +653 -0
  129. package/reference/streams/pipeline.md +718 -0
  130. package/reference/streams/reader.md +1284 -0
  131. package/reference/streams/scala-2-compatibility.md +55 -0
  132. package/reference/streams/sink.md +1426 -0
  133. package/reference/streams/stream.md +2526 -0
  134. package/reference/streams/writer.md +1045 -0
  135. package/reference/streams/zero-boxing.md +275 -0
  136. package/reference/telemetry.md +693 -0
  137. package/reference/typeid.md +5 -19
  138. package/sidebars.js +238 -43
  139. package/reference/formats.md +0 -694
  140. package/reference/http-model.md +0 -1716
  141. package/reference/streams.md +0 -989
  142. package/ringbuffer.md +0 -249
  143. /package/reference/{json-differ.md → schema/built-in-codecs/json/json-differ.md} +0 -0
  144. /package/reference/{dynamic-value.md → schema/dynamic-value.md} +0 -0
  145. /package/reference/{lazy.md → schema/lazy.md} +0 -0
  146. /package/reference/{reflect.md → schema/reflect.md} +0 -0
  147. /package/reference/{registers.md → schema/registers.md} +0 -0
  148. /package/reference/{schema-error.md → schema/schema-error.md} +0 -0
  149. /package/reference/{syntax.md → schema/syntax.md} +0 -0
  150. /package/reference/{validation.md → schema/validation.md} +0 -0
@@ -0,0 +1,353 @@
1
+ ---
2
+ id: transactor
3
+ title: "Transactor"
4
+ description: "Reference for Transactor and JdbcTransactor: the sql module's entry point for connection lifecycle and transaction management."
5
+ keywords:
6
+ - "JdbcTransactor connection lifecycle"
7
+ - "Transactor transaction management"
8
+ - "DbCon connection context"
9
+ - "DbTx transaction scope"
10
+ - "JDBC connection pooling"
11
+ - "SQL commit rollback"
12
+ - "TransactorZIO ZIO integration"
13
+ ---
14
+
15
+ `Transactor` is the entry point for all SQL execution in the `sql` module. It declares two methods that acquire a JDBC connection, execute a provided body, and guarantee the connection is closed on return — whether the body succeeds or throws:
16
+
17
+ - **`Transactor#connect`** — supplies a `DbCon` implicit context for non-transactional queries; auto-commit remains at its default state.
18
+ - **`Transactor#transact`** — supplies a `DbTx` implicit context with auto-commit disabled; commits on success and rolls back on any exception.
19
+
20
+ `DbTx` extends `DbCon`, so every `Frag` execution method and every `Repo` CRUD operation that requires `DbCon` also works inside `transact`. The module's other core types — `Frag`, `Table`, and `Repo` — all depend on the context provided by `Transactor` to reach the database.
21
+
22
+ The structural shape of the trait is:
23
+
24
+ ```scala
25
+ trait Transactor {
26
+ def connect[A](f: DbCon ?=> A): A
27
+ def transact[A](f: DbTx ?=> A): A
28
+ }
29
+ ```
30
+
31
+ `JdbcTransactor` is the concrete JDBC-backed implementation. Its companion object provides factory methods for all common connection strategies:
32
+
33
+ ```scala
34
+ class JdbcTransactor(
35
+ connectionFactory: () => java.sql.Connection,
36
+ val dialect: SqlDialect,
37
+ val sqlLogger: SqlLogger
38
+ ) extends Transactor
39
+
40
+ object JdbcTransactor {
41
+ def fromDataSource(dataSource: javax.sql.DataSource, dialect: SqlDialect): JdbcTransactor
42
+ def fromUrl(url: String, dialect: SqlDialect): JdbcTransactor
43
+ def fromUrl(url: String, user: String, password: String, dialect: SqlDialect): JdbcTransactor
44
+ def postgres(dataSource: javax.sql.DataSource): JdbcTransactor
45
+ def sqlite(dataSource: javax.sql.DataSource): JdbcTransactor
46
+ }
47
+ ```
48
+
49
+ ## Usage
50
+
51
+ The following block demonstrates the complete workflow: create a transactor, open a transactional scope, and execute both `Repo` operations and raw `sql"..."` fragments inside it:
52
+
53
+ ```scala
54
+ import zio.blocks.sql._
55
+ import zio.blocks.schema.Schema
56
+
57
+ case class User(id: Int, name: String, email: String)
58
+ object User { implicit val schema: Schema[User] = Schema.derived }
59
+
60
+ implicit val userCodec: DbCodec[User] = User.schema.deriving(DbCodecDeriver).derive
61
+
62
+ val repo = Repo.derived[User, Int]("users", "id", _.id)
63
+ val transactor = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
64
+
65
+ // transact: auto-commit disabled; commits on success, rolls back on exception
66
+ transactor.transact {
67
+ repo.table.createTable(summon[DbTx].dialect).update
68
+ repo.insert(User(1, "Alice", "alice@example.com"))
69
+ repo.insert(User(2, "Bob", "bob@example.com"))
70
+
71
+ // Raw fragment composes freely with Repo operations inside the same scope
72
+ val active: List[User] =
73
+ sql"SELECT id, name, email FROM users WHERE id > ${0}".query[User]
74
+ }
75
+
76
+ // connect: auto-commit unchanged; no transaction overhead for pure reads
77
+ val users: List[User] = transactor.connect {
78
+ repo.all
79
+ }
80
+ ```
81
+
82
+ ## Construction / Creating Instances
83
+
84
+ We can create a `JdbcTransactor` from a `DataSource`, from a JDBC URL, or using database-specific convenience factories. In every case the third constructor parameter `sqlLogger` defaults to `SqlLogger.noop`, so it can be omitted unless query logging is required.
85
+
86
+ ### `JdbcTransactor.fromDataSource` — Create from a DataSource
87
+
88
+ `JdbcTransactor.fromDataSource` is the recommended factory for production use. It calls `dataSource.getConnection` once per `connect` or `transact` invocation, so connection pooling is fully controlled by the `DataSource` implementation (for example, HikariCP or c3p0):
89
+
90
+ ```scala
91
+ object JdbcTransactor {
92
+ def fromDataSource(dataSource: javax.sql.DataSource, dialect: SqlDialect): JdbcTransactor
93
+ }
94
+ ```
95
+
96
+ Pass the `DataSource` and the `SqlDialect` constant that matches your database. The following shows a typical setup where the data source is provided externally:
97
+
98
+ ```scala
99
+ import zio.blocks.sql._
100
+
101
+ val dataSource: javax.sql.DataSource = ???
102
+ val transactor: JdbcTransactor =
103
+ JdbcTransactor.fromDataSource(dataSource, SqlDialect.PostgreSQL)
104
+ ```
105
+
106
+ ### `JdbcTransactor.fromUrl` — Create from a JDBC URL
107
+
108
+ `JdbcTransactor.fromUrl` creates a transactor that opens each connection via `DriverManager.getConnection`. Two overloads are available: one without credentials and one that accepts a username and password:
109
+
110
+ ```scala
111
+ object JdbcTransactor {
112
+ def fromUrl(url: String, dialect: SqlDialect): JdbcTransactor
113
+ def fromUrl(url: String, user: String, password: String, dialect: SqlDialect): JdbcTransactor
114
+ }
115
+ ```
116
+
117
+ Use the credential-free overload for databases that embed authentication in the URL (for example, SQLite in-memory) or when the JDBC driver handles authentication separately:
118
+
119
+ ```scala
120
+ import zio.blocks.sql._
121
+
122
+ // Without credentials — useful for SQLite or URL-embedded auth
123
+ val sqlite: JdbcTransactor =
124
+ JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
125
+ // sqlite: JdbcTransactor = zio.blocks.sql.JdbcTransactor@6bc883b
126
+ ```
127
+
128
+ Use the three-argument overload when the database requires a username and password supplied separately:
129
+
130
+ ```scala
131
+ import zio.blocks.sql._
132
+
133
+ // With credentials — typical for PostgreSQL or MySQL
134
+ val postgres: JdbcTransactor =
135
+ JdbcTransactor.fromUrl(
136
+ "jdbc:postgresql://localhost/mydb",
137
+ "alice",
138
+ "secret",
139
+ SqlDialect.PostgreSQL
140
+ )
141
+ // postgres: JdbcTransactor = zio.blocks.sql.JdbcTransactor@40cda9e
142
+ ```
143
+
144
+ :::caution
145
+ `fromUrl` opens a new physical connection for every `connect` or `transact` call and does no pooling. For applications with concurrent workloads, prefer `fromDataSource` backed by a pooling `DataSource`.
146
+ :::
147
+
148
+ ### `JdbcTransactor.postgres` — PostgreSQL convenience factory
149
+
150
+ `JdbcTransactor.postgres` is shorthand for `fromDataSource(dataSource, SqlDialect.PostgreSQL)`. Use it to reduce boilerplate when working exclusively with PostgreSQL:
151
+
152
+ ```scala
153
+ object JdbcTransactor {
154
+ def postgres(dataSource: javax.sql.DataSource): JdbcTransactor
155
+ }
156
+ ```
157
+
158
+ The factory fixes the dialect to `SqlDialect.PostgreSQL` so DDL type names, parameter placeholders, and dialect-specific SQL all render correctly for PostgreSQL:
159
+
160
+ ```scala
161
+ import zio.blocks.sql._
162
+
163
+ val pgDataSource: javax.sql.DataSource = ???
164
+ val transactor: JdbcTransactor = JdbcTransactor.postgres(pgDataSource)
165
+ ```
166
+
167
+ ### `JdbcTransactor.sqlite` — SQLite convenience factory
168
+
169
+ `JdbcTransactor.sqlite` is shorthand for `fromDataSource(dataSource, SqlDialect.SQLite)`. Use it for SQLite databases where the `DataSource` is already available:
170
+
171
+ ```scala
172
+ object JdbcTransactor {
173
+ def sqlite(dataSource: javax.sql.DataSource): JdbcTransactor
174
+ }
175
+ ```
176
+
177
+ This factory is useful when you wire the `DataSource` through a dependency-injection layer and want to keep dialect selection close to the data source definition rather than at each call site:
178
+
179
+ ```scala
180
+ import zio.blocks.sql._
181
+
182
+ val sqDataSource: javax.sql.DataSource = ???
183
+ val transactor: JdbcTransactor = JdbcTransactor.sqlite(sqDataSource)
184
+ ```
185
+
186
+ ## Core Operations
187
+
188
+ `Transactor` exposes exactly two public methods. Together they cover the two modes of database access: non-transactional connections for reads and transactional connections for writes.
189
+
190
+ ### Connection Management
191
+
192
+ `Transactor#connect` is the lightweight entry point for database access without a transaction. It provides a `DbCon` context, which is the implicit argument required by every `Frag` extension method and every `Repo` CRUD operation.
193
+
194
+ It acquires a connection from the underlying factory, wraps it in a `JdbcConnection`, synthesizes a `DbCon` given value, and executes the provided body. The connection is closed in a `finally` block, so it is released whether the body returns normally or throws:
195
+
196
+ ```scala
197
+ trait Transactor {
198
+ def connect[A](f: DbCon ?=> A): A
199
+ }
200
+ ```
201
+
202
+ Because `DbTx extends DbCon`, a block that compiles under `connect` will also compile under `transact` — so we can promote non-transactional code to transactional scope without changing the body. The following example runs a read query inside `connect` without incurring transaction overhead:
203
+
204
+ ```scala
205
+ import zio.blocks.sql._
206
+ import zio.blocks.schema.Schema
207
+
208
+ case class User(id: Int, name: String, email: String)
209
+ object User { implicit val schema: Schema[User] = Schema.derived }
210
+
211
+ implicit val userCodec: DbCodec[User] = User.schema.deriving(DbCodecDeriver).derive
212
+
213
+ val repo = Repo.derived[User, Int]("users", "id", _.id)
214
+ val transactor = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
215
+
216
+ // The body receives DbCon as a given — no explicit passing required
217
+ val users: List[User] = transactor.connect {
218
+ repo.all
219
+ }
220
+ ```
221
+
222
+ :::caution
223
+ Inside `connect`, auto-commit is left at its default state (typically `true` for JDBC). Any DML statement executed here is committed immediately. If you need atomic multi-statement writes, use `transact` instead.
224
+ :::
225
+
226
+ ### Transaction Management
227
+
228
+ `Transactor#transact` builds on `connect` by adding full transaction semantics: it disables auto-commit before executing the body, commits when the body returns normally, and rolls back when the body throws. The connection is always closed after commit or rollback.
229
+
230
+ It acquires a connection, sets `autoCommit = false`, synthesizes a `DbTx` given value, and runs the body. On success it calls `conn.commit()`. On any exception it calls `conn.rollback()`, adds any rollback failure as a suppressed exception, and rethrows the original. The connection is closed in the `finally` block regardless of outcome:
231
+
232
+ ```scala
233
+ trait Transactor {
234
+ def transact[A](f: DbTx ?=> A): A
235
+ }
236
+ ```
237
+
238
+ Because `DbTx extends DbCon`, the `DbTx` given satisfies any `DbCon ?=>` requirement inside the block. We can mix `Repo` CRUD calls with raw `sql"..."` fragments freely:
239
+
240
+ ```scala
241
+ import zio.blocks.sql._
242
+ import zio.blocks.schema.Schema
243
+ import zio.blocks.maybe.Maybe
244
+
245
+ case class User(id: Int, name: String, email: String)
246
+ object User { implicit val schema: Schema[User] = Schema.derived }
247
+
248
+ implicit val userCodec: DbCodec[User] = User.schema.deriving(DbCodecDeriver).derive
249
+ // userCodec: DbCodec[User] = zio.blocks.sql.DbCodecDeriver$$anon$20@72550983
250
+
251
+ val repo = Repo.derived[User, Int]("users", "id", _.id)
252
+ // repo: Repo[User, Int] = zio.blocks.sql.Repo$DerivedRepo@144fc885
253
+ val transactor = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
254
+ // transactor: JdbcTransactor = zio.blocks.sql.JdbcTransactor@12877e41
255
+
256
+ transactor.transact {
257
+ repo.table.createTable(summon[DbTx].dialect).update
258
+ repo.insert(User(1, "Alice", "alice@example.com"))
259
+
260
+ // If this throws, the INSERT above is rolled back
261
+ val existing: Maybe[User] = repo.find(1)
262
+ existing
263
+ }
264
+ // res7: Maybe[User] = User(
265
+ // id = 1,
266
+ // name = "Alice",
267
+ // email = "alice@example.com"
268
+ // )
269
+ ```
270
+
271
+ :::caution
272
+ If `commit` itself throws after a successful body, the transaction is rolled back and the commit exception propagates. The body's return value is discarded in that case.
273
+ :::
274
+
275
+ ## JdbcTransactor
276
+
277
+ `JdbcTransactor` is the only concrete implementation of `Transactor` in the `sql` module. It holds three constructor parameters: `connectionFactory`, `dialect`, and `sqlLogger`. The factory methods on its companion object cover the most common configurations; the primary constructor is available for custom setups such as test harnesses that inject a pre-existing connection:
278
+
279
+ ```
280
+ Transactor (trait — shared, cross-platform)
281
+ └── JdbcTransactor (class — JVM only, JDBC-backed)
282
+ ```
283
+
284
+ You can subclass `JdbcTransactor` to override `connect` or `transact` — for example, to reuse a single shared connection across multiple calls in a test suite without closing it between calls, as the `TransactorSpec` test helper does internally.
285
+
286
+ ## Transactor vs TransactorZIO
287
+
288
+ `Transactor` and `JdbcTransactor` are synchronous and suitable for code that does not use the ZIO effect system. `TransactorZIO`, provided by the separate `zio-blocks-sql-zio` artifact, wraps a `JdbcTransactor` and exposes two execution models for ZIO applications:
289
+
290
+ | Aspect | `Transactor` / `JdbcTransactor` | `TransactorZIO` (sql-zio module) |
291
+ |--------------------------|------------------------------------------------|---------------------------------------------------------------------------------------------------|
292
+ | **Return type** | `A` (synchronous, blocking) | `Task[A]` (or `ZIO[R, E \| Throwable, A]` for ZIO bodies) |
293
+ | **Thread model** | Blocks the calling thread | `connect`/`transact` run on the blocking thread pool via `ZIO.attemptBlocking` |
294
+ | **Interruption safety** | None — the body runs to completion | `connectZIO`/`transactZIO` use `ZIO.acquireRelease`; connection closes even on fiber interruption |
295
+ | **ZIO dependency** | None — zero ZIO dependency | Requires ZIO runtime |
296
+ | **Dependency injection** | Manual construction | `TransactorZIO.layer` provides a `ZLayer` |
297
+ | **When to use** | Synchronous imperative code, scripts, or tests | ZIO-based applications where effects compose across the entire call stack |
298
+
299
+ The following diagram shows how the two types relate:
300
+
301
+ ```
302
+ JdbcTransactor ──────────── wraps ───────────► TransactorZIO
303
+ │ │
304
+ │ connect { DbCon ?=> A }: A │ connect { DbCon ?=> A }: Task[A]
305
+ │ transact { DbTx ?=> A }: A │ transact { DbTx ?=> A }: Task[A]
306
+ │ │ connectZIO { DbCon ?=> ZIO[R,E,A] }
307
+ │ │ transactZIO { DbTx ?=> ZIO[R,E,A] }
308
+ │ │
309
+ └── (no ZIO dep) ────────────────────────── (ZIO runtime required) ──┘
310
+ ```
311
+
312
+ Choose `JdbcTransactor` when the codebase is synchronous or when ZIO is not on the classpath. Choose `TransactorZIO` when you want connections bracketed by ZIO's `acquireRelease` so they survive fiber interruption, or when you need `ZLayer`-based dependency injection.
313
+
314
+ ## Advanced Usage: Composing the Context
315
+
316
+ `DbCon` carries three members — `connection`, `dialect`, and `logger` — and is passed implicitly through every SQL operation. This means application-level helper methods can declare `(using DbCon)` and they are automatically satisfied anywhere inside `connect` or `transact` with no explicit argument passing. The same pattern composes across multiple layers:
317
+
318
+ ```scala
319
+ import zio.blocks.sql._
320
+ import zio.blocks.schema.Schema
321
+
322
+ case class User(id: Int, name: String, email: String)
323
+ object User { implicit val schema: Schema[User] = Schema.derived }
324
+
325
+ implicit val userCodec: DbCodec[User] = User.schema.deriving(DbCodecDeriver).derive
326
+ // userCodec: DbCodec[User] = zio.blocks.sql.DbCodecDeriver$$anon$20@460334f6
327
+
328
+ val repo = Repo.derived[User, Int]("users", "id", _.id)
329
+ // repo: Repo[User, Int] = zio.blocks.sql.Repo$DerivedRepo@25787e5
330
+ val transactor = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
331
+ // transactor: JdbcTransactor = zio.blocks.sql.JdbcTransactor@7fefa5d1
332
+
333
+ // Helper that composes two Repo calls — requires only DbCon, not Transactor
334
+ def upsertUser(user: User)(using DbCon): Unit = {
335
+ if (repo.exists(user.id)) repo.update(user)
336
+ else repo.insert(user)
337
+ }
338
+
339
+ // The Transactor provides the DbCon; upsertUser picks it up automatically
340
+ transactor.transact {
341
+ repo.table.createTable(summon[DbTx].dialect).update
342
+ upsertUser(User(1, "Alice", "alice@example.com"))
343
+ upsertUser(User(1, "Alice Smith", "alice.smith@example.com"))
344
+ repo.find(1)
345
+ }
346
+ // res9: Maybe[User] = User(
347
+ // id = 1,
348
+ // name = "Alice Smith",
349
+ // email = "alice.smith@example.com"
350
+ // )
351
+ ```
352
+
353
+ Because `DbTx extends DbCon`, `upsertUser` works unchanged inside both `connect` and `transact`. This lets us design helper functions against the minimal context they need and promote them to transactional scope at the call site without modifying their signatures.
@@ -0,0 +1,112 @@
1
+ ---
2
+ id: sql-zio
3
+ title: "SQL — ZIO Integration"
4
+ ---
5
+
6
+ `zio-blocks-sql-zio` is the ZIO adapter for `zio-blocks-sql`. It wraps the
7
+ core JDBC transactor so ZIO applications can use the same SQL layer without
8
+ changing the underlying database API.
9
+
10
+ This guide covers two integration styles: `ZLayer`-based dependency injection
11
+ for the plain synchronous `Transactor` (via `JdbcTransactor.postgresLayer` /
12
+ `sqliteLayer`), and the `TransactorZIO` wrapper class for ZIO-native blocking
13
+ and effect-aware methods. For a full method-by-method reference on
14
+ `TransactorZIO`, see [TransactorZIO](./sql/transactor-zio.md).
15
+
16
+ ## Installation
17
+
18
+ ```scala
19
+ libraryDependencies += "dev.zio" %% "zio-blocks-sql-zio" % "0.0.51"
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ Create a `Transactor` layer from a JDBC `DataSource` and pick the dialect-specific helper that matches your database:
25
+
26
+ ```scala
27
+ import javax.sql.DataSource
28
+ import zio.*
29
+ import zio.blocks.maybe.Maybe
30
+ import zio.blocks.sql.*
31
+ import zio.blocks.sql.zio.*
32
+
33
+ val dataSource: DataSource = ???
34
+ val transactorLayer: ZLayer[Any, Nothing, Transactor] =
35
+ ZLayer.succeed(dataSource) >>> JdbcTransactor.postgresLayer
36
+
37
+ val program: ZIO[Transactor, Throwable, Maybe[Int]] =
38
+ ZIO.serviceWith[Transactor] { transactor =>
39
+ transactor.connect {
40
+ sql"SELECT 1".queryOne[Int]
41
+ }
42
+ }
43
+ ```
44
+
45
+ Use `JdbcTransactor.sqliteLayer` for SQLite databases.
46
+
47
+ ## TransactorZIO
48
+
49
+ `TransactorZIO` wraps the synchronous transactor and exposes ZIO-friendly
50
+ operations.
51
+
52
+ ```scala
53
+ import zio._
54
+ import zio.blocks.sql._
55
+ import zio.blocks.sql.zio._
56
+
57
+ val transactor = TransactorZIO.fromUrl(
58
+ "jdbc:postgresql://localhost/mydb",
59
+ "alice", "secret",
60
+ SqlDialect.PostgreSQL
61
+ )
62
+ ```
63
+
64
+ For production use, prefer `TransactorZIO.fromDataSource(...)` so connection
65
+ pooling is handled outside the library.
66
+
67
+ You can also create a transactor from a `DataSource` when you already have one:
68
+
69
+ ```scala
70
+ val transactor = TransactorZIO.fromDataSource(dataSource, SqlDialect.PostgreSQL)
71
+ ```
72
+
73
+ ## Blocking Wrappers
74
+
75
+ `connect` and `transact` run synchronous code on ZIO's blocking thread pool and
76
+ return `Task`.
77
+
78
+ ```scala
79
+ val users: Task[List[User]] = transactor.connect:
80
+ userRepo.all
81
+
82
+ val result: Task[User] = transactor.transact:
83
+ userRepo.insertReturning(newUser)
84
+ ```
85
+
86
+ ## Effect-Aware Methods
87
+
88
+ `connectZIO` and `transactZIO` let the body return a `ZIO` directly.
89
+
90
+ ```scala
91
+ val program: ZIO[Any, Throwable, User] =
92
+ transactor.transactZIO:
93
+ for
94
+ _ <- ZIO.attemptBlocking(userRepo.insert(newUser))
95
+ user <- ZIO.attemptBlocking(userRepo.find(newUser.id))
96
+ yield user.get
97
+ ```
98
+
99
+ ## ZLayer
100
+
101
+ Use `ZLayer` when you want to provide `TransactorZIO` through dependency
102
+ injection:
103
+
104
+ ```scala
105
+ val transactorLayer: ZLayer[Any, Nothing, TransactorZIO] =
106
+ TransactorZIO.layer("jdbc:postgresql://localhost/mydb", SqlDialect.PostgreSQL)
107
+ ```
108
+
109
+ ## Thread Safety
110
+
111
+ `TransactorZIO` is safe to share. It creates a new connection per
112
+ `connect` / `transact` invocation.
@@ -0,0 +1,106 @@
1
+ ---
2
+ id: concurrent-operators
3
+ title: "Concurrent Operators"
4
+ ---
5
+
6
+ ZIO Blocks Streams ships **three** concurrent operators that fan work across virtual threads while preserving the typed-error, synchronous, pull-based programming model. The calling thread still receives `Either[E, Z]` — no effect system is required.
7
+
8
+ | Operator | Purpose |
9
+ |---|---|
10
+ | `Stream#mapPar(n)(f)` | Apply `f` to each element on up to `n` worker threads. Output is **unordered** (arrival order, not input order). |
11
+ | `Stream.mergeAll(n)(streams)` | Drain up to `n` inner streams concurrently; interleave their elements as they arrive. |
12
+ | `Stream#flatMapPar(n)(f)` | Per element, produce a sub-stream via `f`; drain up to `n` sub-streams concurrently. |
13
+
14
+ All three operators are **JVM-only**. On Scala.js they degrade to sequential equivalents (`map`, `flatten`, `flatMap`).
15
+
16
+ ## Semantics
17
+
18
+ **Output order.** Concurrent output is **unordered** with respect to input position. Elements arrive as workers complete, not in input order. If you need input order, use sequential `map` / `flatMap`.
19
+
20
+ **Error propagation.** The first typed error from any worker or inner stream terminates all concurrent work and surfaces as `Left(e)` from the terminal operation. Defects (unexpected exceptions) propagate as thrown exceptions, same as sequential operators.
21
+
22
+ **Resource safety.** All worker threads and ring-buffer queues are cleaned up deterministically when the consumer closes the reader, the stream errors, or the scope finalizes.
23
+
24
+ **Primitive specialization.** Readers produced by concurrent operators preserve primitive specialization — `Int`, `Long`, `Float`, and `Double` streams use specialized lock-free queues internally, avoiding boxing in the concurrent handoff between threads.
25
+
26
+ ## Buffer sizing
27
+
28
+ Concurrent operators use internal ring-buffer queues (default size **64**). Override with `Stream.bufferSize(n) { ... }` where `n` is a positive power of two:
29
+
30
+ ```
31
+ Stream.bufferSize(256) {
32
+ Stream.range(0, 1_000_000).mapPar(8)(heavyComputation)
33
+ }.runCollect
34
+ ```
35
+
36
+ Larger buffers help when producers are bursty; smaller buffers reduce memory when many concurrent streams are active. The default is fine for most workloads.
37
+
38
+ `Pipeline.buffer(n)` inserts a buffer of `n` elements between upstream and downstream (async handoff on JVM, sync on JS).
39
+
40
+ ## Examples
41
+
42
+ ### `mapPar`
43
+
44
+ ```
45
+ // Apply an expensive function using 8 virtual threads.
46
+ // Output order varies between runs.
47
+ val result = Stream.range(0, 1000)
48
+ .mapPar(8)(n => { Thread.sleep(1); n * 2 })
49
+ .runCollect
50
+ // result: Right(Chunk(...)) -- all 1000 elements, but not in 0,2,4,... order
51
+ ```
52
+
53
+ ### `mergeAll`
54
+
55
+ ```
56
+ // Drain 10 streams concurrently, up to 4 at a time.
57
+ val streams = Stream.fromIterable(
58
+ (0 until 10).map(i => Stream.range(i * 100, (i + 1) * 100))
59
+ )
60
+ val merged = Stream.mergeAll(4)(streams).runFold(0L)(_ + _)
61
+ // merged: Right(499500) -- all elements consumed, order interleaved
62
+ ```
63
+
64
+ ### `flatMapPar`
65
+
66
+ ```
67
+ // Each element spawns a sub-stream; up to 8 drained concurrently.
68
+ val flat = Stream.range(0, 50)
69
+ .flatMapPar(8)(i => Stream.range(i * 20, (i + 1) * 20))
70
+ .runFold(0L)(_ + _)
71
+ // flat: Right(499500)
72
+ ```
73
+
74
+ ### Error behaviour
75
+
76
+ ```
77
+ // Typed error in a worker terminates all workers
78
+ val err1 = Stream.range(0, 1000)
79
+ .flatMap(n => if (n == 500) Stream.fail("bad element") else Stream.succeed(n))
80
+ .mapPar(4)(identity)
81
+ .runCollect
82
+ // err1: Left("bad element")
83
+
84
+ // Error in one inner stream terminates mergeAll
85
+ val err2 = Stream.mergeAll(4)(Stream.fromIterable(
86
+ List(Stream.range(0, 100), Stream.fail("inner error"), Stream.range(200, 300))
87
+ )).runCollect
88
+ // err2: Left("inner error")
89
+ ```
90
+
91
+ ## Guidelines
92
+
93
+ - **Use `mapPar(n)(f)` for expensive per-element work** — network calls, CPU-bound computation, blocking I/O. Do not use it for trivially cheap functions (e.g. `_ + 1`); the thread-handoff overhead exceeds the parallelism benefit.
94
+ - **Use `mergeAll(n)(streams)` for concurrent fan-in** — draining multiple independent sources (files, connections, partitions) simultaneously. Use `flatMapPar(n)(f)` when each input element produces a sub-stream to drain concurrently.
95
+ - **Concurrent output is unordered.** If you need sorted results, apply `.runCollect.map(_.sorted)` or accumulate into a structure that handles ordering. If you need input-order preservation, use sequential `map` / `flatMap`.
96
+ - **`mapPar`, `mergeAll`, and `flatMapPar` are JVM-only.** On JS they degrade to sequential equivalents.
97
+
98
+ ## Comparison with other libraries
99
+
100
+ | Feature | ZB Streams | fs2 | Kyo | Ox | Pekko |
101
+ |---|---|---|---|---|---|
102
+ | Concurrent operators | `mapPar`, `mergeAll`, `flatMapPar` | `parEvalMap` | `mapParUnordered`* | `mapPar` | `mapAsync`, `flatMapMerge` |
103
+ | Effect system required | No | Yes (cats-effect) | Yes (Kyo) | No (virtual threads) | Yes (Akka) |
104
+ | Typed errors | `Either[E, Z]` | ApplicativeError | Kyo effects | Exceptions | No |
105
+
106
+ \* Kyo's `mapParUnordered` forks a fiber per element (very slow for large streams). Kyo's `collectAll` merges streams but does not parallelize pure computation within them.