@zio.dev/zio-blocks 0.0.51 → 0.0.56

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 (166) hide show
  1. package/adr/2026-07-18-data-migration.md +123 -0
  2. package/guides/async-getting-started.md +687 -0
  3. package/guides/compile-time-resource-safety-with-scope.md +6 -0
  4. package/guides/getting-started-with-mux.md +0 -112
  5. package/guides/query-dsl-extending.md +1 -1
  6. package/guides/query-dsl-fluent-builder.md +1 -1
  7. package/guides/query-dsl-reified-optics.md +1 -1
  8. package/guides/query-dsl-sql.md +395 -1
  9. package/guides/sql-checked-interpolation.md +173 -0
  10. package/guides/sql-transactions.md +286 -0
  11. package/guides/telemetry-guide.md +131 -70
  12. package/guides/zio-schema-migration.md +6 -6
  13. package/index.md +200 -559
  14. package/package.json +1 -1
  15. package/reference/async.md +1379 -531
  16. package/reference/chunk.md +3 -3
  17. package/reference/codegen/index.md +1 -1
  18. package/reference/combinators.md +4 -4
  19. package/reference/config/config-decoder.md +460 -0
  20. package/reference/config/config-source.md +489 -0
  21. package/reference/config/errors.md +278 -0
  22. package/reference/config/flags.md +369 -0
  23. package/reference/config/formats.md +314 -0
  24. package/reference/config/index.md +304 -0
  25. package/reference/config/rollout.md +336 -0
  26. package/reference/context.md +6 -49
  27. package/reference/data-migration.md +269 -0
  28. package/reference/datastar/attributes.md +302 -0
  29. package/reference/datastar/events.md +234 -0
  30. package/reference/datastar/index.md +256 -0
  31. package/reference/datastar/signals.md +230 -0
  32. package/reference/datastar/sse.md +295 -0
  33. package/reference/datastar.md +2 -2
  34. package/reference/docs.md +2 -2
  35. package/reference/endpoint/bulk-creation.md +96 -0
  36. package/reference/endpoint/endpoint.md +1 -0
  37. package/reference/endpoint/index.md +9 -89
  38. package/reference/endpoint/path-codec.md +12 -24
  39. package/reference/endpoint/route-pattern.md +4 -6
  40. package/reference/endpoint/segment-codec.md +19 -32
  41. package/reference/html.md +313 -9
  42. package/reference/htmx/index.md +4 -52
  43. package/reference/htmx/response-headers.md +240 -0
  44. package/reference/http-model/headers.md +735 -0
  45. package/reference/http-model/index.md +3 -1
  46. package/reference/http-model/model.md +107 -71
  47. package/reference/http-model/schema-codecs.md +522 -0
  48. package/reference/http-model/schema.md +6 -3
  49. package/reference/http-model/server-sent-event.md +341 -0
  50. package/reference/jwt.md +195 -0
  51. package/reference/maybe.md +128 -11
  52. package/reference/media-type.md +2 -2
  53. package/reference/mux.mdx +7 -2
  54. package/reference/openapi.md +3 -3
  55. package/reference/projection.md +654 -0
  56. package/reference/resource-management/index.md +1 -1
  57. package/reference/resource-management/resource.md +2 -98
  58. package/reference/resource-management/scope.md +1 -209
  59. package/reference/resource-management/wire.md +4 -50
  60. package/reference/ringbuffer/advanced.mdx +1 -1
  61. package/reference/ringbuffer/index.mdx +3 -3
  62. package/reference/ringbuffer/mpmc.mdx +38 -4
  63. package/reference/ringbuffer/mpsc.mdx +36 -4
  64. package/reference/ringbuffer/spmc.mdx +1 -1
  65. package/reference/ringbuffer/spsc.mdx +87 -15
  66. package/reference/schema/allows.md +0 -96
  67. package/reference/schema/binding.md +2 -2
  68. package/reference/schema/built-in-codecs/avro.md +2 -2
  69. package/reference/schema/built-in-codecs/bson.md +50 -20
  70. package/reference/schema/built-in-codecs/csv.md +2 -2
  71. package/reference/schema/built-in-codecs/index.md +3 -3
  72. package/reference/schema/built-in-codecs/json/index.md +2 -2
  73. package/reference/schema/built-in-codecs/json/json.md +1 -0
  74. package/reference/schema/built-in-codecs/messagepack.md +3 -3
  75. package/reference/schema/built-in-codecs/thrift.md +2 -2
  76. package/reference/schema/built-in-codecs/toon.md +3 -3
  77. package/reference/schema/built-in-codecs/yaml.md +2 -2
  78. package/reference/schema/codec.md +11 -11
  79. package/reference/schema/dynamic-optic.md +48 -3
  80. package/reference/schema/dynamic-schema.md +3 -3
  81. package/reference/schema/index.md +2 -0
  82. package/reference/schema/path-interpolator.md +2 -0
  83. package/reference/schema/reflect-transformer.md +140 -0
  84. package/reference/schema/schema-evolution/as.md +4 -4
  85. package/reference/schema/schema-evolution/into.md +2 -2
  86. package/reference/schema/schema-expr.md +2 -2
  87. package/reference/schema/schema-search.md +263 -0
  88. package/reference/schema/schema.md +10 -2
  89. package/reference/schema/type-class-derivation.md +1 -1
  90. package/reference/smithy.md +502 -3
  91. package/reference/sql/db-codec-deriver.md +3 -3
  92. package/reference/sql/db-codec.md +22 -22
  93. package/reference/sql/db-con.md +4 -4
  94. package/reference/sql/db-connection.md +1 -1
  95. package/reference/sql/db-param.md +1 -1
  96. package/reference/sql/db-result-reader.md +4 -2
  97. package/reference/sql/db-tx.md +46 -14
  98. package/reference/sql/ddl.md +1 -1
  99. package/reference/sql/frag.md +44 -10
  100. package/reference/sql/index.md +7 -7
  101. package/reference/sql/repo.md +15 -15
  102. package/reference/sql/sql-dialect.md +1 -1
  103. package/reference/sql/sql-logger.md +1 -1
  104. package/reference/sql/sql-name-mapper.md +3 -3
  105. package/reference/sql/table-metadata.md +3 -3
  106. package/reference/sql/table.md +10 -10
  107. package/reference/sql/transactor-zio.md +1 -1
  108. package/reference/sql/transactor.md +21 -11
  109. package/reference/sql-zio.md +2 -2
  110. package/reference/streams/core/index.md +32 -0
  111. package/reference/streams/{pipeline.md → core/pipeline.md} +210 -74
  112. package/reference/streams/{sink.md → core/sink.md} +331 -353
  113. package/reference/streams/{stream.md → core/stream.md} +919 -209
  114. package/reference/streams/execution-and-compatibility/async-execution.md +822 -0
  115. package/reference/streams/execution-and-compatibility/index.md +35 -0
  116. package/reference/streams/execution-and-compatibility/platform-differences.md +297 -0
  117. package/reference/streams/execution-and-compatibility/scala-2-compatibility.md +88 -0
  118. package/reference/streams/execution-and-compatibility/zero-boxing.md +393 -0
  119. package/reference/streams/index.md +140 -67
  120. package/reference/streams/primitives/index.md +30 -0
  121. package/reference/streams/primitives/reader.md +1992 -0
  122. package/reference/streams/{writer.md → primitives/writer.md} +254 -98
  123. package/reference/telemetry/common/any-value.md +90 -0
  124. package/reference/telemetry/common/attribute-key.md +87 -0
  125. package/reference/telemetry/common/attributes.md +118 -0
  126. package/reference/telemetry/common/index.md +39 -0
  127. package/reference/telemetry/common/instrumentation-scope.md +24 -0
  128. package/reference/telemetry/common/resource.md +34 -0
  129. package/reference/telemetry/index.md +311 -0
  130. package/reference/telemetry/logging/index.md +197 -0
  131. package/reference/telemetry/logging/log-enrichment.md +72 -0
  132. package/reference/telemetry/logging/log-formatter.md +100 -0
  133. package/reference/telemetry/logging/log-record-processor.md +56 -0
  134. package/reference/telemetry/logging/log-record.md +44 -0
  135. package/reference/telemetry/logging/log-writer.md +64 -0
  136. package/reference/telemetry/logging/logger-provider.md +142 -0
  137. package/reference/telemetry/logging/logger.md +83 -0
  138. package/reference/telemetry/logging/severity.md +62 -0
  139. package/reference/telemetry/metrics/index.md +150 -0
  140. package/reference/telemetry/metrics/instruments.md +183 -0
  141. package/reference/telemetry/metrics/labeled-instruments.md +74 -0
  142. package/reference/telemetry/metrics/meter-provider.md +76 -0
  143. package/reference/telemetry/metrics/meter.md +98 -0
  144. package/reference/telemetry/metrics/metric-data.md +57 -0
  145. package/reference/telemetry/otel/custom-exporter.md +216 -0
  146. package/reference/telemetry/otel/index.md +212 -0
  147. package/reference/telemetry/tracing/index.md +155 -0
  148. package/reference/telemetry/tracing/sampler.md +89 -0
  149. package/reference/telemetry/tracing/span-builder.md +57 -0
  150. package/reference/telemetry/tracing/span-context.md +39 -0
  151. package/reference/telemetry/tracing/span-data.md +32 -0
  152. package/reference/telemetry/tracing/span-kind.md +55 -0
  153. package/reference/telemetry/tracing/span-processor.md +53 -0
  154. package/reference/telemetry/tracing/span-status.md +47 -0
  155. package/reference/telemetry/tracing/span.md +117 -0
  156. package/reference/telemetry/tracing/tracer-provider.md +91 -0
  157. package/reference/telemetry/tracing/tracer.md +52 -0
  158. package/reference/typeid.md +0 -64
  159. package/sidebars.js +365 -185
  160. package/undocumented-report.md +528 -270
  161. package/reference/config.md +0 -158
  162. package/reference/streams/concurrent-operators.md +0 -106
  163. package/reference/streams/reader.md +0 -1284
  164. package/reference/streams/scala-2-compatibility.md +0 -55
  165. package/reference/streams/zero-boxing.md +0 -275
  166. package/reference/telemetry.md +0 -693
@@ -78,7 +78,7 @@ import zio.blocks.schema.Schema
78
78
  case class User(id: Int, name: String, email: Option[String]) derives DbCodec
79
79
 
80
80
  val codec = DbCodec[User]
81
- // codec: DbCodec[User] = zio.blocks.sql.DbCodecDeriver$$anon$20@55869d8
81
+ // codec: DbCodec[User] = zio.blocks.sql.DbCodecDeriver$$anon$10@28731048
82
82
 
83
83
  codec.columns
84
84
  // res1: IndexedSeq[String] = Vector("id", "name", "email")
@@ -100,7 +100,7 @@ val nullParams = codec.toDbValues(User(2, "Bob", None))
100
100
  // Adapt any codec to a newtype with transform — no full Schema needed
101
101
  case class UserId(value: Int)
102
102
  val userIdCodec: DbCodec[UserId] = DbCodec[Int].transform(UserId(_))(_.value)
103
- // userIdCodec: DbCodec[UserId] = zio.blocks.sql.DbCodec$$anon$1@2f13d28a
103
+ // userIdCodec: DbCodec[UserId] = zio.blocks.sql.DbCodec$$anon$1@7c9318fc
104
104
 
105
105
  userIdCodec.columns
106
106
  // res3: IndexedSeq[String] = Vector("value")
@@ -173,13 +173,13 @@ object Product { implicit val schema: Schema[Product] = Schema.derived }
173
173
  // tags is stored as a JSON string in the "tags" column
174
174
  val tagsCodec: DbCodec[Tags] =
175
175
  DbCodec[String].transform(json => Tags(json.split(",").toList))(_.values.mkString(","))
176
- // tagsCodec: DbCodec[Tags] = zio.blocks.sql.DbCodec$$anon$1@3592645d
176
+ // tagsCodec: DbCodec[Tags] = zio.blocks.sql.DbCodec$$anon$1@5596e273
177
177
 
178
178
  val productCodec: DbCodec[Product] =
179
179
  DbCodec.derivedWith[Product](
180
180
  _.instance(TypeId.of[Product], "tags", tagsCodec)
181
181
  )
182
- // productCodec: DbCodec[Product] = zio.blocks.sql.DbCodecDeriver$$anon$20@73120731
182
+ // productCodec: DbCodec[Product] = zio.blocks.sql.DbCodecDeriver$$anon$10@34140b70
183
183
 
184
184
  productCodec.columns
185
185
  // res7: IndexedSeq[String] = Vector("id", "tags")
@@ -189,31 +189,31 @@ productCodec.columnCount
189
189
 
190
190
  ### `DbCodec.jsonb` — JSONB column codec
191
191
 
192
- `DbCodec.jsonb` creates a `DbCodec[A]` that stores and retrieves a value of type `A` as a JSON string in a single database column. Two overloads are available: one using an implicit `JsonSchemaCodec[A]` for the encode/decode pair, and one accepting explicit functions.
192
+ `DbCodec.jsonb` creates a `DbCodec[A]` that stores and retrieves a value of type `A` as a JSON string in a single database column. Two overloads are available: one using an implicit `JsonCodec[A]` for the encode/decode pair, and one accepting explicit functions.
193
193
 
194
194
  ```scala
195
195
  object DbCodec {
196
- def jsonb[A](using jsonCodec: JsonSchemaCodec[A]): DbCodec[A]
196
+ def jsonb[A](using jsonCodec: JsonCodec[A]): DbCodec[A]
197
197
  def jsonb[A](encode: A => String, decode: String => A): DbCodec[A]
198
198
  }
199
199
  ```
200
200
 
201
- The first overload requires a `JsonSchemaCodec[A]` (aliased from `zio.blocks.schema.json.JsonCodec`) in implicit scope:
201
+ The first overload requires a `JsonCodec[A]` (`zio.blocks.schema.json.JsonCodec`) in implicit scope:
202
202
 
203
203
  ```scala
204
204
  import zio.blocks.sql._
205
205
  import zio.blocks.schema.Schema
206
- import zio.blocks.schema.json.{JsonCodec => JsonSchemaCodec, JsonCodecDeriver}
206
+ import zio.blocks.schema.json.{JsonCodec, JsonCodecDeriver}
207
207
 
208
208
  case class Address(street: String, city: String)
209
209
  object Address {
210
- implicit val schema: Schema[Address] = Schema.derived
211
- implicit val jsonCodec: JsonSchemaCodec[Address] = schema.deriving(JsonCodecDeriver).derive
210
+ implicit val schema: Schema[Address] = Schema.derived
211
+ implicit val jsonCodec: JsonCodec[Address] = schema.deriving(JsonCodecDeriver).derive
212
212
  }
213
213
 
214
214
  // Address is stored as a JSON string in a single TEXT/JSONB column
215
215
  val codec: DbCodec[Address] = DbCodec.jsonb[Address]
216
- // codec: DbCodec[Address] = zio.blocks.sql.DbCodec$$anon$1@68d986d1
216
+ // codec: DbCodec[Address] = zio.blocks.sql.DbCodec$$anon$1@43b1650c
217
217
 
218
218
  codec.columns
219
219
  // res10: IndexedSeq[String] = Vector("value")
@@ -223,7 +223,7 @@ codec.toDbValues(Address("Main St", "NYC"))
223
223
  // )
224
224
  ```
225
225
 
226
- Use the two-argument overload when you supply custom encode/decode logic instead of relying on `JsonSchemaCodec`:
226
+ Use the two-argument overload when you supply custom encode/decode logic instead of relying on `JsonCodec`:
227
227
 
228
228
  ```scala
229
229
  import zio.blocks.sql._
@@ -235,7 +235,7 @@ val pointCodec: DbCodec[Point] = DbCodec.jsonb[Point](
235
235
  p => s"${p.x},${p.y}",
236
236
  s => { val parts = s.split(","); Point(parts(0).toDouble, parts(1).toDouble) }
237
237
  )
238
- // pointCodec: DbCodec[Point] = zio.blocks.sql.DbCodec$$anon$1@1144ae3c
238
+ // pointCodec: DbCodec[Point] = zio.blocks.sql.DbCodec$$anon$1@2634e34a
239
239
 
240
240
  pointCodec.toDbValues(Point(1.0, 2.0))
241
241
  // res12: IndexedSeq[DbValue] = Vector(DbString("1.0,2.0"))
@@ -243,11 +243,11 @@ pointCodec.toDbValues(Point(1.0, 2.0))
243
243
 
244
244
  ### `DbCodec.jsonbOption` — Nullable JSONB column codec
245
245
 
246
- `DbCodec.jsonbOption` creates a `DbCodec[Option[A]]` that stores `Some(a)` as a JSON string and `None` as SQL `NULL`. Like `jsonb`, it has an implicit `JsonSchemaCodec[A]` overload and a two-argument overload:
246
+ `DbCodec.jsonbOption` creates a `DbCodec[Option[A]]` that stores `Some(a)` as a JSON string and `None` as SQL `NULL`. Like `jsonb`, it has an implicit `JsonCodec[A]` overload and a two-argument overload:
247
247
 
248
248
  ```scala
249
249
  object DbCodec {
250
- def jsonbOption[A](using jsonCodec: JsonSchemaCodec[A]): DbCodec[Option[A]]
250
+ def jsonbOption[A](using jsonCodec: JsonCodec[A]): DbCodec[Option[A]]
251
251
  def jsonbOption[A](encode: A => String, decode: String => A): DbCodec[Option[A]]
252
252
  }
253
253
  ```
@@ -256,11 +256,11 @@ The codec delegates to `DbCodec[Option[String]]` and applies the JSON encode/dec
256
256
 
257
257
  ```scala
258
258
  import zio.blocks.sql._
259
- import zio.blocks.schema.json.{JsonCodec => JsonSchemaCodec}
259
+ import zio.blocks.schema.json.JsonCodec
260
260
 
261
- // Assume JsonSchemaCodec[Address] is in scope from the previous example
261
+ // Assume JsonCodec[Address] is in scope from the previous example
262
262
  val nullableCodec: DbCodec[Option[Address]] = DbCodec.jsonbOption[Address]
263
- // nullableCodec: DbCodec[Option[Address]] = zio.blocks.sql.DbCodec$$anon$1@4c7b464f
263
+ // nullableCodec: DbCodec[Option[Address]] = zio.blocks.sql.DbCodec$$anon$1@5a2ab2eb
264
264
 
265
265
  nullableCodec.toDbValues(Some(Address("Elm St", "LA")))
266
266
  // res13: IndexedSeq[DbValue] = Vector(
@@ -293,7 +293,7 @@ object ProductId {
293
293
 
294
294
  // DbCodec[ProductId] is resolved automatically — no explicit given needed
295
295
  val codec = DbCodec[ProductId]
296
- // codec: DbCodec[ProductId] = zio.blocks.sql.DbCodec$$anon$6@3261707e
296
+ // codec: DbCodec[ProductId] = zio.blocks.sql.DbCodec$$anon$6@7ba8fb55
297
297
  codec.columns
298
298
  // res16: IndexedSeq[String] = Vector("value")
299
299
  ```
@@ -344,7 +344,7 @@ case class Order(id: Long, status: String) derives DbCodec
344
344
 
345
345
  // Summon the derived codec
346
346
  val codec: DbCodec[Order] = DbCodec[Order]
347
- // codec: DbCodec[Order] = zio.blocks.sql.DbCodecDeriver$$anon$20@678700e1
347
+ // codec: DbCodec[Order] = zio.blocks.sql.DbCodecDeriver$$anon$10@5b7defec
348
348
  codec.columns
349
349
  // res19: IndexedSeq[String] = Vector("id", "status")
350
350
  ```
@@ -560,7 +560,7 @@ import zio.blocks.sql._
560
560
  case class Item(id: Int, name: String, price: Option[BigDecimal]) derives DbCodec
561
561
 
562
562
  val codec = DbCodec[Item]
563
- // codec: DbCodec[Item] = zio.blocks.sql.DbCodecDeriver$$anon$20@1ffebf02
563
+ // codec: DbCodec[Item] = zio.blocks.sql.DbCodecDeriver$$anon$10@25059cd7
564
564
 
565
565
  codec.toDbValues(Item(1, "Widget", Some(BigDecimal("9.99"))))
566
566
  // res29: IndexedSeq[DbValue] = Vector(
@@ -595,7 +595,7 @@ case class ProductId(value: String)
595
595
  // Adapt the String codec to ProductId without a Schema
596
596
  val productIdCodec: DbCodec[ProductId] =
597
597
  DbCodec[String].transform(ProductId(_))(_.value)
598
- // productIdCodec: DbCodec[ProductId] = zio.blocks.sql.DbCodec$$anon$1@95d8a43
598
+ // productIdCodec: DbCodec[ProductId] = zio.blocks.sql.DbCodec$$anon$1@41471826
599
599
 
600
600
  productIdCodec.columns
601
601
  // res32: IndexedSeq[String] = Vector("value")
@@ -46,7 +46,7 @@ object User {
46
46
  }
47
47
 
48
48
  val tx: Transactor = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
49
- // tx: Transactor = zio.blocks.sql.JdbcTransactor@4c1ffa3c
49
+ // tx: Transactor = zio.blocks.sql.JdbcTransactor@1fecb11a
50
50
 
51
51
  // DbCon is supplied automatically by connect — no explicit argument needed
52
52
  tx.connect {
@@ -179,7 +179,7 @@ The following example shows reading the dialect from a context to render a `Frag
179
179
  import zio.blocks.sql._
180
180
 
181
181
  val tx: Transactor = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
182
- // tx: Transactor = zio.blocks.sql.JdbcTransactor@606f8c9a
182
+ // tx: Transactor = zio.blocks.sql.JdbcTransactor@51223a09
183
183
 
184
184
  tx.connect {
185
185
  val frag = sql"SELECT id FROM users WHERE id = ${42}"
@@ -209,7 +209,7 @@ val loggingLogger: SqlLogger = new SqlLogger {
209
209
  def onError(event: SqlLogger.ErrorEvent): Unit =
210
210
  println(s"ERR [${event.duration.toMillis} ms]: ${event.sql} — ${event.error.getMessage}")
211
211
  }
212
- // loggingLogger: SqlLogger = repl.MdocSession$MdocApp7$$anon$9@1d96b35a
212
+ // loggingLogger: SqlLogger = repl.MdocSession$MdocApp7$$anon$9@192cce5a
213
213
 
214
214
  val tx: Transactor =
215
215
  new JdbcTransactor(
@@ -217,7 +217,7 @@ val tx: Transactor =
217
217
  SqlDialect.SQLite,
218
218
  loggingLogger
219
219
  )
220
- // tx: Transactor = zio.blocks.sql.JdbcTransactor@7abee0ec
220
+ // tx: Transactor = zio.blocks.sql.JdbcTransactor@7407235d
221
221
 
222
222
  tx.connect {
223
223
  // Every Frag execution notifies loggingLogger automatically
@@ -133,7 +133,7 @@ transactor.connect {
133
133
  import zio.blocks.sql._
134
134
 
135
135
  val transactor: Transactor = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
136
- // transactor: Transactor = zio.blocks.sql.JdbcTransactor@7d102738
136
+ // transactor: Transactor = zio.blocks.sql.JdbcTransactor@4f5f053
137
137
 
138
138
  var capturedCon: DbConnection = null
139
139
  // capturedCon: DbConnection = null
@@ -47,7 +47,7 @@ val active = true
47
47
  // active: Boolean = true
48
48
  val query = sql"SELECT * FROM users WHERE id = $userId AND active = $active"
49
49
  // query: Frag = Frag(
50
- // parts = ArraySeq("SELECT * FROM users WHERE id = ", " AND active = ", ""),
50
+ // parts = Vector("SELECT * FROM users WHERE id = ", " AND active = ", ""),
51
51
  // params = Vector(DbInt(42), DbBoolean(true))
52
52
  // )
53
53
  query.params
@@ -58,13 +58,15 @@ trait DbResultReader {
58
58
  // Other types
59
59
  def getUUID(index: Int): java.util.UUID
60
60
  def getUUID(label: String): java.util.UUID
61
- def getArray(index: Int): java.sql.Array // default: throws UnsupportedOperationException
62
- def getArray(label: String): java.sql.Array // default: throws UnsupportedOperationException
61
+ def getArray(index: Int): Array[String] // default: throws UnsupportedOperationException
62
+ def getArray(label: String): Array[String] // default: throws UnsupportedOperationException
63
63
 
64
64
  // Metadata and NULL detection
65
65
  def columnLabel(index: Int): String
66
66
  def hasColumn(label: String): Boolean
67
67
  def wasNull: Boolean
68
+ def isNull(index: Int): Boolean // default: false
69
+ def isNull(label: String): Boolean // default: false
68
70
  }
69
71
  ```
70
72
 
@@ -12,26 +12,28 @@ keywords:
12
12
  - "JDBC Transaction Lifecycle"
13
13
  ---
14
14
 
15
- `DbTx` is a marker trait in the `zio-blocks-sql` module that extends `DbCon` to signal a transactional execution scope. It declares no members of its own — its distinct type is what instructs `Transactor#transact` to disable auto-commit, commit the connection on success, and roll back on any thrown exception. You never construct a `DbTx` directly; the `Transactor` creates one and supplies it as a given context to the block passed to `transact`. The connection is always closed when the block exits, whether it commits, rolls back, or throws.
15
+ `DbTx` is the transactional connection context supplied by `Transactor#transact`. It extends `DbCon` (so every `Frag`/`Repo` operation that needs `DbCon` also accepts `DbTx`) and adds savepoint-based nested transaction support. You never construct a `DbTx` directly; the `Transactor` creates one and supplies it as a given context to the block passed to `transact`. The connection is always closed when the outermost block exits, whether it commits, rolls back, or throws.
16
16
 
17
17
  Key properties:
18
- - **Transactional context marker** — A `DbTx` value in scope guarantees the underlying JDBC connection has auto-commit disabled.
19
- - **Commit-on-success semantics** — The `Transactor` commits the connection when the `transact` block returns normally.
20
- - **Rollback-on-failure semantics** — Any uncaught exception causes the `Transactor` to roll back the connection before re-throwing.
18
+ - **Transactional context** — A `DbTx` value in scope guarantees the underlying JDBC connection has auto-commit disabled.
19
+ - **Commit-on-success / rollback-on-failure** — The outermost `Transactor.transact` commits on normal return and rolls back on any uncaught exception (with suppressed rollback failures).
20
+ - **Savepoint-based nesting** — Inner blocks reuse the same connection via SQL savepoints (`SAVEPOINT` / `RELEASE SAVEPOINT` / `ROLLBACK TO SAVEPOINT`).
21
+ - **`transact(isolation, readOnly)`** — The two-arg `Transactor.transact` overload sets isolation level and `readOnly` before disabling auto-commit; `DbTx` nested blocks inherit those settings on the same connection.
21
22
 
22
23
  The structural declaration of `DbTx` is:
23
24
 
24
25
  ```scala
25
- trait DbTx extends DbCon
26
- ```
27
-
28
- Every context member that `DbTx` exposes is inherited from `DbCon`, which declares the three fields every SQL operation consumes:
26
+ trait DbTx extends DbCon {
27
+ def savepoint(name: String): Unit
28
+ def release(name: String): Unit
29
+ def rollbackTo(name: String): Unit
30
+ def currentDepth: Int
31
+ private[sql] def currentDepth_=(depth: Int): Unit
29
32
 
30
- ```scala
31
- trait DbCon {
33
+ // inherited from DbCon
32
34
  def connection: DbConnection
33
- def dialect: SqlDialect
34
- def logger: SqlLogger
35
+ def dialect: SqlDialect
36
+ def logger: SqlLogger
35
37
  }
36
38
  ```
37
39
 
@@ -49,9 +51,9 @@ object User {
49
51
  }
50
52
 
51
53
  val repo = Repo.derived[User, Int]("users", "id", _.id)
52
- // repo: Repo[User, Int] = zio.blocks.sql.Repo$DerivedRepo@1b21124
54
+ // repo: Repo[User, Int] = zio.blocks.sql.Repo$DerivedRepo@789742a3
53
55
  val tx = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
54
- // tx: JdbcTransactor = zio.blocks.sql.JdbcTransactor@1583ec4c
56
+ // tx: JdbcTransactor = zio.blocks.sql.JdbcTransactor@5276be33
55
57
 
56
58
  // On normal return: transaction commits and connection closes.
57
59
  // On any exception: transaction rolls back, then the exception propagates.
@@ -80,3 +82,33 @@ tx.transact {
80
82
  // List(User(id = 1, name = "Alice", email = "alice@example.com"))
81
83
  // )
82
84
  ```
85
+
86
+ ## Nested Transactions via Savepoints
87
+
88
+ Nested transactions reuse the same underlying JDBC connection via SQL savepoints. The `DbTx` given in scope exposes an extension `transact` and the `transactNested` helpers:
89
+
90
+ ```scala
91
+ import zio.blocks.sql._
92
+
93
+ val transactor = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
94
+
95
+ // Savepoint-based nesting — same connection, isolated rollback
96
+ transactor.transact {
97
+ sql"INSERT INTO t VALUES (1)".update
98
+
99
+ // Inner block runs inside SAVEPOINT zib_tx_1
100
+ summon[DbTx].transact {
101
+ sql"INSERT INTO t VALUES (2)".update
102
+ }
103
+
104
+ // Equivalent using the `using` helper
105
+ // DbTx.transactNested { sql"INSERT INTO t VALUES (3)".update }
106
+ // transactNested { sql"INSERT INTO t VALUES (4)".update }
107
+ }
108
+ ```
109
+
110
+ Savepoint names are `zib_tx_1 .. zib_tx_N` where `N` is the nesting depth tracked in `currentDepth`. On success the savepoint is released via `RELEASE SAVEPOINT`; on failure it is rolled back via `ROLLBACK TO SAVEPOINT` and the exception is rethrown (with any rollback failure added as suppressed). Depth is decremented in `finally`, so sibling nested blocks reuse the same name sequence without leaking savepoints. `savepoint`/`release`/`rollbackTo` are also available directly for manual control and validate identifiers via `SqlIdentifier` to prevent injection.
111
+
112
+ :::caution
113
+ Only the outermost `Transactor.transact` issues a real `COMMIT`/`ROLLBACK`. Inner `summon[DbTx].transact` blocks are savepoint-scoped — outer commit still decides the final persistence of all work, including inner blocks that succeeded.
114
+ :::
@@ -33,7 +33,7 @@ Create a `ColumnDef` for each column, then pass them to `Ddl.createTable`:
33
33
  import zio.blocks.sql._
34
34
 
35
35
  val transactor: Transactor = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
36
- // transactor: Transactor = zio.blocks.sql.JdbcTransactor@1bf2e7e
36
+ // transactor: Transactor = zio.blocks.sql.JdbcTransactor@13b61c12
37
37
 
38
38
  val columns = IndexedSeq(
39
39
  ColumnDef("id", "INTEGER", nullable = false),
@@ -9,7 +9,7 @@ keywords:
9
9
  - "SQL Injection Prevention"
10
10
  ---
11
11
 
12
- `Frag` is an immutable SQL fragment — a piece of SQL text with typed parameter values kept safely separate from the literal SQL. The `sql"..."` string interpolator builds fragments by checking at compile time that every interpolated expression can be bound as a parameter. Fragments compose with `++` and execute through methods like `query`, `update`, and `queryOne`.
12
+ `Frag` is an immutable SQL fragment — a piece of SQL text with typed parameter values kept safely separate from the literal SQL. The `sql"..."` string interpolator builds fragments by checking at compile time that every interpolated expression can be bound as a parameter. Fragments compose with `++` and execute through methods like `query`, `update`, and `queryOne`, plus the chunked streaming methods `queryStream` and `queryChunked`.
13
13
 
14
14
  `Frag` is safe from SQL injection because parameter values never appear in the SQL string — they are stored separately and bound to `?` placeholders at execution time.
15
15
 
@@ -33,6 +33,8 @@ object Frag {
33
33
  def query[A](using DbCon, DbCodec[A]): List[A]
34
34
  def queryOne[A](using DbCon, DbCodec[A]): Maybe[A]
35
35
  def queryLimit[A](limit: Int)(using DbCon, DbCodec[A]): List[A]
36
+ def queryStream[A](using DbCon, DbCodec[A]): Stream[Throwable, Chunk[A]]
37
+ def queryChunked[A](chunkSize: Int)(using DbCon, DbCodec[A]): Stream[Throwable, Chunk[A]]
36
38
  def update(using DbCon): Int
37
39
  def updateReturningKeys[A](using DbCon, DbCodec[A]): List[A]
38
40
  }
@@ -86,7 +88,7 @@ val userId = 42
86
88
  // userId: Int = 42
87
89
  val frag = sql"SELECT * FROM users WHERE id = $userId"
88
90
  // frag: Frag = Frag(
89
- // parts = ArraySeq("SELECT * FROM users WHERE id = ", ""),
91
+ // parts = Vector("SELECT * FROM users WHERE id = ", ""),
90
92
  // params = Vector(DbInt(42))
91
93
  // )
92
94
  frag.params
@@ -98,7 +100,7 @@ frag.params
98
100
  ```scala
99
101
  val query = sql"SELECT * FROM users" ++ Frag.literal(" ORDER BY name")
100
102
  // query: Frag = Frag(
101
- // parts = ArraySeq("SELECT * FROM users ORDER BY name"),
103
+ // parts = Vector("SELECT * FROM users ORDER BY name"),
102
104
  // params = Vector()
103
105
  // )
104
106
  query.sql(SqlDialect.SQLite)
@@ -146,12 +148,12 @@ val hasFilter = true
146
148
  // hasFilter: Boolean = true
147
149
  val where = if (hasFilter) sql" WHERE active = ${true}" else Frag.empty
148
150
  // where: Frag = Frag(
149
- // parts = ArraySeq(" WHERE active = ", ""),
151
+ // parts = Vector(" WHERE active = ", ""),
150
152
  // params = Vector(DbBoolean(true))
151
153
  // )
152
154
  val query = sql"SELECT * FROM users" ++ where
153
155
  // query: Frag = Frag(
154
- // parts = ArraySeq("SELECT * FROM users WHERE active = ", ""),
156
+ // parts = Vector("SELECT * FROM users WHERE active = ", ""),
155
157
  // params = Vector(DbBoolean(true))
156
158
  // )
157
159
  query.sql(SqlDialect.SQLite)
@@ -166,13 +168,10 @@ import zio.blocks.sql._
166
168
  val status = "active"
167
169
  // status: String = "active"
168
170
  val base = sql"SELECT * FROM users"
169
- // base: Frag = Frag(
170
- // parts = ArraySeq("SELECT * FROM users"),
171
- // params = Vector()
172
- // )
171
+ // base: Frag = Frag(parts = Vector("SELECT * FROM users"), params = Vector())
173
172
  val where = sql" WHERE status = $status"
174
173
  // where: Frag = Frag(
175
- // parts = ArraySeq(" WHERE status = ", ""),
174
+ // parts = Vector(" WHERE status = ", ""),
176
175
  // params = Vector(DbString("active"))
177
176
  // )
178
177
  val order = Frag.literal(" ORDER BY name")
@@ -233,6 +232,41 @@ given DbCon = ???
233
232
  val page: List[User] = sql"SELECT id, name FROM users ORDER BY name".queryLimit[User](10)
234
233
  ```
235
234
 
235
+ **`queryStream[A]`** — Execute SELECT and return rows as a chunked stream (`zio.blocks.streams.Stream`), batching `DefaultQueryChunkSize` (64) rows per chunk. Acquisition is lazy — nothing touches the connection until the first pull — so consume the stream within the scope that provides the `DbCon`; leaving `Transactor.connect`/`transact` closes the captured connection, and pulling afterwards fails.
236
+
237
+ ```scala
238
+ import zio.blocks.chunk.Chunk
239
+ import zio.blocks.sql._
240
+ import zio.blocks.schema.Schema
241
+ import zio.blocks.streams.Stream
242
+
243
+ case class User(id: Int, name: String)
244
+ object User { implicit val schema: Schema[User] = Schema.derived }
245
+
246
+ given DbCon = ???
247
+
248
+ val chunks: Stream[Throwable, Chunk[User]] =
249
+ sql"SELECT id, name FROM users".queryStream[User]
250
+ val all: Either[Throwable, List[User]] = chunks.runCollect.map(_.toList.flatten)
251
+ ```
252
+
253
+ **`queryChunked[A](n)`** — Like `queryStream`, but with an explicit batch size. The statement and result set are acquired lazily on the first pull and released when the stream is closed or fully drained; each chunk holds up to `n` rows, so memory stays bounded for large result sets. The same lifetime rule applies: consume the stream before the enclosing `Transactor.connect`/`transact` callback returns — afterwards the captured connection is closed and the first pull fails.
254
+
255
+ ```scala
256
+ import zio.blocks.chunk.Chunk
257
+ import zio.blocks.sql._
258
+ import zio.blocks.schema.Schema
259
+ import zio.blocks.streams.Stream
260
+
261
+ case class User(id: Int, name: String)
262
+ object User { implicit val schema: Schema[User] = Schema.derived }
263
+
264
+ given DbCon = ???
265
+
266
+ val batches: Stream[Throwable, Chunk[User]] =
267
+ sql"SELECT id, name FROM users".queryChunked[User](500)
268
+ ```
269
+
236
270
  **`update`** — Execute INSERT, UPDATE, or DELETE and return affected row count:
237
271
 
238
272
  ```scala
@@ -39,10 +39,10 @@ The core SQL module and the ZIO integration module publish separately. Add the a
39
39
  ```scala
40
40
  // Core SQL module (cross-built for JVM and Scala.js; the JDBC-backed
41
41
  // JdbcTransactor implementation itself is JVM-only)
42
- libraryDependencies += "dev.zio" %% "zio-blocks-sql" % "0.0.51"
42
+ libraryDependencies += "dev.zio" %% "zio-blocks-sql" % "0.0.56"
43
43
 
44
44
  // ZIO integration (lifts JDBC into ZIO effects; JVM-only)
45
- libraryDependencies += "dev.zio" %% "zio-blocks-sql-zio" % "0.0.51"
45
+ libraryDependencies += "dev.zio" %% "zio-blocks-sql-zio" % "0.0.56"
46
46
  ```
47
47
 
48
48
  Both modules require a JDBC driver on the classpath (for example, `org.xerial:sqlite-jdbc` for SQLite or `org.postgresql:postgresql` for PostgreSQL). Swap the JDBC URL and `SqlDialect` constant to switch databases.
@@ -144,9 +144,9 @@ object User {
144
144
 
145
145
  // 1. Create transactor and repository
146
146
  val tx = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
147
- // tx: JdbcTransactor = zio.blocks.sql.JdbcTransactor@3fe7357a
147
+ // tx: JdbcTransactor = zio.blocks.sql.JdbcTransactor@ec75b64
148
148
  val repo = Repo.derived[User, Int]("users", "id", _.id)
149
- // repo: Repo[User, Int] = zio.blocks.sql.Repo$DerivedRepo@4206125b
149
+ // repo: Repo[User, Int] = zio.blocks.sql.Repo$DerivedRepo@41c4138d
150
150
 
151
151
  // 2. Set up schema and run a transactional workflow
152
152
  tx.transact {
@@ -200,7 +200,7 @@ object BlogPost {
200
200
  }
201
201
 
202
202
  val repo = Repo.derived[BlogPost, Int]("post_id", _.id)
203
- // repo: Repo[BlogPost, Int] = zio.blocks.sql.Repo$DerivedRepo@243d4111
203
+ // repo: Repo[BlogPost, Int] = zio.blocks.sql.Repo$DerivedRepo@6abcba30
204
204
  repo.table.name
205
205
  // res3: String = "blog_post"
206
206
  repo.table.codec.columns
@@ -269,7 +269,7 @@ val active: Option[Boolean] = Some(true)
269
269
  val frag =
270
270
  sql"SELECT * FROM users WHERE id = $userId AND name LIKE $namePattern AND active = $active"
271
271
  // frag: Frag = Frag(
272
- // parts = ArraySeq(
272
+ // parts = Vector(
273
273
  // "SELECT * FROM users WHERE id = ",
274
274
  // " AND name LIKE ",
275
275
  // " AND active = ",
@@ -298,7 +298,7 @@ case class Order(id: Int, tags: List[String], metadata: Map[String, String]) der
298
298
 
299
299
  // `tags` and `metadata` are encoded via DbCodec.jsonb when read/written through Frag/Repo
300
300
  val codec = DbCodec[Order]
301
- // codec: DbCodec[Order] = zio.blocks.sql.DbCodecDeriver$$anon$20@10cf0d2d
301
+ // codec: DbCodec[Order] = zio.blocks.sql.DbCodecDeriver$$anon$10@4d46abe5
302
302
  codec.columns
303
303
  // res12: IndexedSeq[String] = Vector("id", "tags", "metadata")
304
304
  ```
@@ -49,9 +49,9 @@ object User {
49
49
 
50
50
  // All SQL is generated here, once, from User's Schema.
51
51
  val repo = Repo.derived[User, Int]("users", "id", _.id)
52
- // repo: Repo[User, Int] = zio.blocks.sql.Repo$DerivedRepo@16e9cebd
52
+ // repo: Repo[User, Int] = zio.blocks.sql.Repo$DerivedRepo@c90e314
53
53
  val tx = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
54
- // tx: JdbcTransactor = zio.blocks.sql.JdbcTransactor@aae0228
54
+ // tx: JdbcTransactor = zio.blocks.sql.JdbcTransactor@7c7933b1
55
55
 
56
56
  tx.transact {
57
57
  repo.table.createTable(summon[DbTx].dialect).update // CREATE TABLE IF NOT EXISTS users …
@@ -116,7 +116,7 @@ object Article {
116
116
  }
117
117
 
118
118
  val repo = Repo.derived[Article, Int]("id", _.id)
119
- // repo: Repo[Article, Int] = zio.blocks.sql.Repo$DerivedRepo@2f907618
119
+ // repo: Repo[Article, Int] = zio.blocks.sql.Repo$DerivedRepo@4462dfed
120
120
  repo.table.name
121
121
  // res3: String = "article"
122
122
  repo.table.codec.columns
@@ -150,7 +150,7 @@ object OrderLine {
150
150
 
151
151
  // Overrides the default "order_line" table name with the legacy one
152
152
  val repo = Repo.derived[OrderLine, Int]("tbl_order_lines", "line_id", _.lineId)
153
- // repo: Repo[OrderLine, Int] = zio.blocks.sql.Repo$DerivedRepo@2bc241ca
153
+ // repo: Repo[OrderLine, Int] = zio.blocks.sql.Repo$DerivedRepo@7cda6782
154
154
  repo.table.name
155
155
  // res6: String = "tbl_order_lines"
156
156
  ```
@@ -185,7 +185,7 @@ object Tag {
185
185
 
186
186
  // Inspects Tag's Schema, finds the unique Long field "id", maps it to column "id"
187
187
  val repo = Repo.derived[Tag, Long]
188
- // repo: Repo[Tag, Long] = zio.blocks.sql.Repo$DerivedRepo@589393f1
188
+ // repo: Repo[Tag, Long] = zio.blocks.sql.Repo$DerivedRepo@3cdba0a6
189
189
  repo.idColumn
190
190
  // res8: String = "id"
191
191
  ```
@@ -234,7 +234,7 @@ The read operations query the database without modifying it. `Repo#all`, `Repo#f
234
234
 
235
235
  #### `all` — Retrieve all rows
236
236
 
237
- `Repo#all` executes `SELECT <columns> FROM <table>` and decodes every result-set row into an `E` using the entity's `DbCodec`, returning all rows as a `List[E]` in database-native order.
237
+ `Repo#all` executes `SELECT "<columns>" FROM "<table>"` and decodes every result-set row into an `E` using the entity's `DbCodec`, returning all rows as a `List[E]` in database-native order.
238
238
 
239
239
  ```scala
240
240
  class Repo[E, ID] {
@@ -264,7 +264,7 @@ val users: List[User] = repo.all
264
264
 
265
265
  #### `findAll` — Retrieve rows by a set of primary keys
266
266
 
267
- `Repo#findAll` executes `SELECT <columns> FROM <table> WHERE <idColumn> IN (...)` for the given IDs and decodes every matching row into an `E`. It returns an empty `List` immediately, without executing any SQL, when `ids` is empty.
267
+ `Repo#findAll` executes `SELECT "<columns>" FROM "<table>" WHERE "<idColumn>" IN (...)` for the given IDs and decodes every matching row into an `E`. It returns an empty `List` immediately, without executing any SQL, when `ids` is empty.
268
268
 
269
269
  ```scala
270
270
  class Repo[E, ID] {
@@ -289,7 +289,7 @@ val users: List[User] = repo.findAll(List(1, 2, 3))
289
289
 
290
290
  #### `find` — Find a row by primary key
291
291
 
292
- `Repo#find` executes `SELECT <columns> FROM <table> WHERE <idColumn> = ?`, binding the ID through `idCodec`. It returns `Maybe.absent` if no row with the given key exists, or `Maybe(entity)` if a row is found.
292
+ `Repo#find` executes `SELECT "<columns>" FROM "<table>" WHERE "<idColumn>" = ?`, binding the ID through `idCodec`. It returns `Maybe.absent` if no row with the given key exists, or `Maybe(entity)` if a row is found.
293
293
 
294
294
  ```scala
295
295
  class Repo[E, ID] {
@@ -340,7 +340,7 @@ val exists: Boolean = repo.exists(99)
340
340
 
341
341
  #### `count` — Count all rows
342
342
 
343
- `Repo#count` executes `SELECT COUNT(*) FROM <table>` and returns the row count as a `Long`. The result is `0L` when the table is empty.
343
+ `Repo#count` executes `SELECT COUNT(*) FROM "<table>"` and returns the row count as a `Long`. The result is `0L` when the table is empty.
344
344
 
345
345
  ```scala
346
346
  class Repo[E, ID] {
@@ -369,7 +369,7 @@ The write operations insert, update, or delete rows in the database. `Repo#inser
369
369
 
370
370
  #### `insert` — Insert a single entity
371
371
 
372
- `Repo#insert` encodes the entity with `DbCodec[E]` and executes `INSERT INTO <table> (<columns>) VALUES (?, …, ?)`, returning the number of affected rows — normally 1 on success.
372
+ `Repo#insert` encodes the entity with `DbCodec[E]` and executes `INSERT INTO "<table>" ("<columns>") VALUES (?, …, ?)`, returning the number of affected rows — normally 1 on success.
373
373
 
374
374
  ```scala
375
375
  class Repo[E, ID] {
@@ -456,7 +456,7 @@ val rowsAffected: Int = repo.insertBatch(users)
456
456
 
457
457
  #### `insertAll` — Multi-row insert returning primary keys
458
458
 
459
- `Repo#insertAll` assembles a single `INSERT INTO <table> (<columns>) VALUES (?, …, ?), …, (?, …, ?)` statement covering all rows and executes it in one database round-trip. It then extracts the primary keys from the input entities via `getId` and returns them in input order.
459
+ `Repo#insertAll` assembles a single `INSERT INTO "<table>" ("<columns>") VALUES (?, …, ?), …, (?, …, ?)` statement covering all rows and executes it in one database round-trip. It then extracts the primary keys from the input entities via `getId` and returns them in input order.
460
460
 
461
461
  ```scala
462
462
  class Repo[E, ID] {
@@ -490,7 +490,7 @@ val ids: Seq[Int] = repo.insertAll(newUsers)
490
490
 
491
491
  #### `update` — Update an entity's non-ID columns
492
492
 
493
- `Repo#update` executes `UPDATE <table> SET <col1> = ?, …, <colN> = ? WHERE <idColumn> = ?` for all non-ID columns of the entity, identifying the target row by its primary key. It returns the number of affected rows — 0 when no row with that ID exists.
493
+ `Repo#update` executes `UPDATE "<table>" SET "<col1>" = ?, …, "<colN>" = ? WHERE "<idColumn>" = ?` for all non-ID columns of the entity, identifying the target row by its primary key. It returns the number of affected rows — 0 when no row with that ID exists.
494
494
 
495
495
  ```scala
496
496
  class Repo[E, ID] {
@@ -519,7 +519,7 @@ val rowsAffected: Int = repo.update(User(1, "Alice Smith", "alice.smith@example.
519
519
 
520
520
  #### `delete` — Delete by primary key
521
521
 
522
- `Repo#delete` executes `DELETE FROM <table> WHERE <idColumn> = ?`, binding the ID through `idCodec`. It returns the number of deleted rows — 0 if no row with the given ID exists.
522
+ `Repo#delete` executes `DELETE FROM "<table>" WHERE "<idColumn>" = ?`, binding the ID through `idCodec`. It returns the number of deleted rows — 0 if no row with the given ID exists.
523
523
 
524
524
  ```scala
525
525
  class Repo[E, ID] {
@@ -546,7 +546,7 @@ To delete by an entity value rather than a bare ID, extract the key with `getId`
546
546
 
547
547
  #### `deleteAll` — Delete rows by a set of primary keys
548
548
 
549
- `Repo#deleteAll` executes `DELETE FROM <table> WHERE <idColumn> IN (...)` for the given IDs in a single round-trip and returns the total number of deleted rows. It returns `0` immediately, without executing any SQL, when `ids` is empty.
549
+ `Repo#deleteAll` executes `DELETE FROM "<table>" WHERE "<idColumn>" IN (...)` for the given IDs in a single round-trip and returns the total number of deleted rows. It returns `0` immediately, without executing any SQL, when `ids` is empty.
550
550
 
551
551
  ```scala
552
552
  class Repo[E, ID] {
@@ -571,7 +571,7 @@ val rowsAffected: Int = repo.deleteAll(List(1, 2, 3))
571
571
 
572
572
  #### `clear` — Remove all rows
573
573
 
574
- `Repo#clear` executes `DELETE FROM <table>` without a `WHERE` clause and returns the number of deleted rows.
574
+ `Repo#clear` executes `DELETE FROM "<table>"` without a `WHERE` clause and returns the number of deleted rows.
575
575
 
576
576
  ```scala
577
577
  class Repo[E, ID] {
@@ -42,7 +42,7 @@ The dialect is available through `DbCon#dialect` inside a transaction:
42
42
  import zio.blocks.sql._
43
43
 
44
44
  val tx = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
45
- // tx: JdbcTransactor = zio.blocks.sql.JdbcTransactor@f150688
45
+ // tx: JdbcTransactor = zio.blocks.sql.JdbcTransactor@4f23e58f
46
46
 
47
47
  tx.connect {
48
48
  val dialect = summon[DbCon].dialect
@@ -38,7 +38,7 @@ val myLogger: SqlLogger = new SqlLogger {
38
38
  def onError(event: SqlLogger.ErrorEvent): Unit =
39
39
  println(s"FAIL: ${event.error.getMessage}")
40
40
  }
41
- // myLogger: SqlLogger = repl.MdocSession$MdocApp0$$anon$1@1b9203b8
41
+ // myLogger: SqlLogger = repl.MdocSession$MdocApp0$$anon$1@3ea7a988
42
42
  ```
43
43
 
44
44
  Pass the logger when creating a `JdbcTransactor`: