@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.
- package/adr/2026-07-18-data-migration.md +123 -0
- package/guides/async-getting-started.md +687 -0
- package/guides/compile-time-resource-safety-with-scope.md +6 -0
- package/guides/getting-started-with-mux.md +0 -112
- package/guides/query-dsl-extending.md +1 -1
- package/guides/query-dsl-fluent-builder.md +1 -1
- package/guides/query-dsl-reified-optics.md +1 -1
- package/guides/query-dsl-sql.md +395 -1
- package/guides/sql-checked-interpolation.md +173 -0
- package/guides/sql-transactions.md +286 -0
- package/guides/telemetry-guide.md +131 -70
- package/guides/zio-schema-migration.md +6 -6
- package/index.md +200 -559
- package/package.json +1 -1
- package/reference/async.md +1379 -531
- package/reference/chunk.md +3 -3
- package/reference/codegen/index.md +1 -1
- package/reference/combinators.md +4 -4
- package/reference/config/config-decoder.md +460 -0
- package/reference/config/config-source.md +489 -0
- package/reference/config/errors.md +278 -0
- package/reference/config/flags.md +369 -0
- package/reference/config/formats.md +314 -0
- package/reference/config/index.md +304 -0
- package/reference/config/rollout.md +336 -0
- package/reference/context.md +6 -49
- package/reference/data-migration.md +269 -0
- package/reference/datastar/attributes.md +302 -0
- package/reference/datastar/events.md +234 -0
- package/reference/datastar/index.md +256 -0
- package/reference/datastar/signals.md +230 -0
- package/reference/datastar/sse.md +295 -0
- package/reference/datastar.md +2 -2
- package/reference/docs.md +2 -2
- package/reference/endpoint/bulk-creation.md +96 -0
- package/reference/endpoint/endpoint.md +1 -0
- package/reference/endpoint/index.md +9 -89
- package/reference/endpoint/path-codec.md +12 -24
- package/reference/endpoint/route-pattern.md +4 -6
- package/reference/endpoint/segment-codec.md +19 -32
- package/reference/html.md +313 -9
- package/reference/htmx/index.md +4 -52
- package/reference/htmx/response-headers.md +240 -0
- package/reference/http-model/headers.md +735 -0
- package/reference/http-model/index.md +3 -1
- package/reference/http-model/model.md +107 -71
- package/reference/http-model/schema-codecs.md +522 -0
- package/reference/http-model/schema.md +6 -3
- package/reference/http-model/server-sent-event.md +341 -0
- package/reference/jwt.md +195 -0
- package/reference/maybe.md +128 -11
- package/reference/media-type.md +2 -2
- package/reference/mux.mdx +7 -2
- package/reference/openapi.md +3 -3
- package/reference/projection.md +654 -0
- package/reference/resource-management/index.md +1 -1
- package/reference/resource-management/resource.md +2 -98
- package/reference/resource-management/scope.md +1 -209
- package/reference/resource-management/wire.md +4 -50
- package/reference/ringbuffer/advanced.mdx +1 -1
- package/reference/ringbuffer/index.mdx +3 -3
- package/reference/ringbuffer/mpmc.mdx +38 -4
- package/reference/ringbuffer/mpsc.mdx +36 -4
- package/reference/ringbuffer/spmc.mdx +1 -1
- package/reference/ringbuffer/spsc.mdx +87 -15
- package/reference/schema/allows.md +0 -96
- package/reference/schema/binding.md +2 -2
- package/reference/schema/built-in-codecs/avro.md +2 -2
- package/reference/schema/built-in-codecs/bson.md +50 -20
- package/reference/schema/built-in-codecs/csv.md +2 -2
- package/reference/schema/built-in-codecs/index.md +3 -3
- package/reference/schema/built-in-codecs/json/index.md +2 -2
- package/reference/schema/built-in-codecs/json/json.md +1 -0
- package/reference/schema/built-in-codecs/messagepack.md +3 -3
- package/reference/schema/built-in-codecs/thrift.md +2 -2
- package/reference/schema/built-in-codecs/toon.md +3 -3
- package/reference/schema/built-in-codecs/yaml.md +2 -2
- package/reference/schema/codec.md +11 -11
- package/reference/schema/dynamic-optic.md +48 -3
- package/reference/schema/dynamic-schema.md +3 -3
- package/reference/schema/index.md +2 -0
- package/reference/schema/path-interpolator.md +2 -0
- package/reference/schema/reflect-transformer.md +140 -0
- package/reference/schema/schema-evolution/as.md +4 -4
- package/reference/schema/schema-evolution/into.md +2 -2
- package/reference/schema/schema-expr.md +2 -2
- package/reference/schema/schema-search.md +263 -0
- package/reference/schema/schema.md +10 -2
- package/reference/schema/type-class-derivation.md +1 -1
- package/reference/smithy.md +502 -3
- package/reference/sql/db-codec-deriver.md +3 -3
- package/reference/sql/db-codec.md +22 -22
- package/reference/sql/db-con.md +4 -4
- package/reference/sql/db-connection.md +1 -1
- package/reference/sql/db-param.md +1 -1
- package/reference/sql/db-result-reader.md +4 -2
- package/reference/sql/db-tx.md +46 -14
- package/reference/sql/ddl.md +1 -1
- package/reference/sql/frag.md +44 -10
- package/reference/sql/index.md +7 -7
- package/reference/sql/repo.md +15 -15
- package/reference/sql/sql-dialect.md +1 -1
- package/reference/sql/sql-logger.md +1 -1
- package/reference/sql/sql-name-mapper.md +3 -3
- package/reference/sql/table-metadata.md +3 -3
- package/reference/sql/table.md +10 -10
- package/reference/sql/transactor-zio.md +1 -1
- package/reference/sql/transactor.md +21 -11
- package/reference/sql-zio.md +2 -2
- package/reference/streams/core/index.md +32 -0
- package/reference/streams/{pipeline.md → core/pipeline.md} +210 -74
- package/reference/streams/{sink.md → core/sink.md} +331 -353
- package/reference/streams/{stream.md → core/stream.md} +919 -209
- package/reference/streams/execution-and-compatibility/async-execution.md +822 -0
- package/reference/streams/execution-and-compatibility/index.md +35 -0
- package/reference/streams/execution-and-compatibility/platform-differences.md +297 -0
- package/reference/streams/execution-and-compatibility/scala-2-compatibility.md +88 -0
- package/reference/streams/execution-and-compatibility/zero-boxing.md +393 -0
- package/reference/streams/index.md +140 -67
- package/reference/streams/primitives/index.md +30 -0
- package/reference/streams/primitives/reader.md +1992 -0
- package/reference/streams/{writer.md → primitives/writer.md} +254 -98
- package/reference/telemetry/common/any-value.md +90 -0
- package/reference/telemetry/common/attribute-key.md +87 -0
- package/reference/telemetry/common/attributes.md +118 -0
- package/reference/telemetry/common/index.md +39 -0
- package/reference/telemetry/common/instrumentation-scope.md +24 -0
- package/reference/telemetry/common/resource.md +34 -0
- package/reference/telemetry/index.md +311 -0
- package/reference/telemetry/logging/index.md +197 -0
- package/reference/telemetry/logging/log-enrichment.md +72 -0
- package/reference/telemetry/logging/log-formatter.md +100 -0
- package/reference/telemetry/logging/log-record-processor.md +56 -0
- package/reference/telemetry/logging/log-record.md +44 -0
- package/reference/telemetry/logging/log-writer.md +64 -0
- package/reference/telemetry/logging/logger-provider.md +142 -0
- package/reference/telemetry/logging/logger.md +83 -0
- package/reference/telemetry/logging/severity.md +62 -0
- package/reference/telemetry/metrics/index.md +150 -0
- package/reference/telemetry/metrics/instruments.md +183 -0
- package/reference/telemetry/metrics/labeled-instruments.md +74 -0
- package/reference/telemetry/metrics/meter-provider.md +76 -0
- package/reference/telemetry/metrics/meter.md +98 -0
- package/reference/telemetry/metrics/metric-data.md +57 -0
- package/reference/telemetry/otel/custom-exporter.md +216 -0
- package/reference/telemetry/otel/index.md +212 -0
- package/reference/telemetry/tracing/index.md +155 -0
- package/reference/telemetry/tracing/sampler.md +89 -0
- package/reference/telemetry/tracing/span-builder.md +57 -0
- package/reference/telemetry/tracing/span-context.md +39 -0
- package/reference/telemetry/tracing/span-data.md +32 -0
- package/reference/telemetry/tracing/span-kind.md +55 -0
- package/reference/telemetry/tracing/span-processor.md +53 -0
- package/reference/telemetry/tracing/span-status.md +47 -0
- package/reference/telemetry/tracing/span.md +117 -0
- package/reference/telemetry/tracing/tracer-provider.md +91 -0
- package/reference/telemetry/tracing/tracer.md +52 -0
- package/reference/typeid.md +0 -64
- package/sidebars.js +365 -185
- package/undocumented-report.md +528 -270
- package/reference/config.md +0 -158
- package/reference/streams/concurrent-operators.md +0 -106
- package/reference/streams/reader.md +0 -1284
- package/reference/streams/scala-2-compatibility.md +0 -55
- package/reference/streams/zero-boxing.md +0 -275
- 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$
|
|
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@
|
|
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@
|
|
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$
|
|
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 `
|
|
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:
|
|
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 `
|
|
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
|
|
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]
|
|
211
|
-
implicit val jsonCodec:
|
|
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@
|
|
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 `
|
|
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@
|
|
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 `
|
|
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:
|
|
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.
|
|
259
|
+
import zio.blocks.schema.json.JsonCodec
|
|
260
260
|
|
|
261
|
-
// Assume
|
|
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@
|
|
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@
|
|
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$
|
|
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$
|
|
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@
|
|
598
|
+
// productIdCodec: DbCodec[ProductId] = zio.blocks.sql.DbCodec$$anon$1@41471826
|
|
599
599
|
|
|
600
600
|
productIdCodec.columns
|
|
601
601
|
// res32: IndexedSeq[String] = Vector("value")
|
package/reference/sql/db-con.md
CHANGED
|
@@ -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@
|
|
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@
|
|
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@
|
|
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@
|
|
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@
|
|
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 =
|
|
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):
|
|
62
|
-
def getArray(label: String):
|
|
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
|
|
package/reference/sql/db-tx.md
CHANGED
|
@@ -12,26 +12,28 @@ keywords:
|
|
|
12
12
|
- "JDBC Transaction Lifecycle"
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
-
`DbTx` is
|
|
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
|
|
19
|
-
- **Commit-on-success
|
|
20
|
-
- **
|
|
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
|
-
|
|
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
|
-
|
|
31
|
-
trait DbCon {
|
|
33
|
+
// inherited from DbCon
|
|
32
34
|
def connection: DbConnection
|
|
33
|
-
def dialect:
|
|
34
|
-
def logger:
|
|
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@
|
|
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@
|
|
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
|
+
:::
|
package/reference/sql/ddl.md
CHANGED
|
@@ -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@
|
|
36
|
+
// transactor: Transactor = zio.blocks.sql.JdbcTransactor@13b61c12
|
|
37
37
|
|
|
38
38
|
val columns = IndexedSeq(
|
|
39
39
|
ColumnDef("id", "INTEGER", nullable = false),
|
package/reference/sql/frag.md
CHANGED
|
@@ -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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
package/reference/sql/index.md
CHANGED
|
@@ -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.
|
|
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.
|
|
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@
|
|
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@
|
|
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@
|
|
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 =
|
|
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$
|
|
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
|
```
|
package/reference/sql/repo.md
CHANGED
|
@@ -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@
|
|
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@
|
|
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@
|
|
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@
|
|
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@
|
|
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
|
|
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
|
|
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
|
|
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@
|
|
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@
|
|
41
|
+
// myLogger: SqlLogger = repl.MdocSession$MdocApp0$$anon$1@3ea7a988
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
Pass the logger when creating a `JdbcTransactor`:
|