@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,341 @@
1
+ ---
2
+ id: index
3
+ title: "SQL Module"
4
+ description: "Reference index for the zio-blocks-sql module: schema-driven SQL fragments, bidirectional codecs, CRUD repositories, and transaction management."
5
+ keywords:
6
+ - "Schema-driven SQL Operations"
7
+ - "DbCodec Schema Derivation"
8
+ - "SQL Fragments"
9
+ - "Bidirectional Codecs"
10
+ - "CRUD Repositories"
11
+ - "Transactor Connection Management"
12
+ - "ZIO Integration"
13
+ ---
14
+
15
+ The `zio-blocks-sql` module provides type-safe, schema-driven SQL query building and execution on relational databases. Its core types — `DbCodec`, `Frag`, `Table`, `Repo`, `Transactor`, `DbCon`, and `DbTx` — form a layered system: codecs map Scala types to database columns, fragments carry parameterized SQL built via string interpolation, repositories expose CRUD operations derived at compile time from a schema, and a transactor manages the connection lifecycle with automatic commit and rollback.
16
+
17
+ ## Motivation
18
+
19
+ Relational database access in Scala typically involves one of three trade-offs:
20
+
21
+ - Raw JDBC requires manual row mapping and string concatenation (SQL injection risk, tedious boilerplate)
22
+ - Reflection-based ORMs hide SQL entirely (opaque at compile time, difficult to optimize)
23
+ - Lifted-embedding DSLs like Slick require learning a parallel query language on top of SQL.
24
+
25
+ The `zio-blocks-sql` module takes a different path: it uses ZIO Blocks' `Schema` system for compile-time column metadata and familiar string interpolation for SQL text, giving you type safety without losing SQL's expressiveness.
26
+
27
+ Key advantages of the `zio-blocks-sql` module are:
28
+
29
+ - **Schema-driven derivation.** `DbCodec`, `Table`, and `Repo` all derive from the same `Schema[A]`, keeping column names, SQL types, and nullability in sync with your Scala data model automatically.
30
+ - **No runtime reflection.** Derivation runs at compile time through the `Deriver[DbCodec]` framework, producing zero-overhead codecs with no reflective calls at runtime.
31
+ - **Compile-time SQL parameterization.** The `sql"..."` macro interpolator verifies that every interpolated value has a `DbParam[A]` instance at compile time, converts it to a `DbValue`, and binds it to a `?` placeholder — no string concatenation, no SQL injection.
32
+ - **Composable fragments.** `Frag` values compose via `Frag#++`, letting you build reusable WHERE clauses, ORDER BY expressions, and pagination helpers that render correctly for any `SqlDialect`.
33
+ - **Explicit transaction boundaries.** `Transactor#transact` disables auto-commit, commits on success, and rolls back on any exception — the connection is always closed whether the block succeeds or throws.
34
+
35
+ ## Installation
36
+
37
+ The core SQL module and the ZIO integration module publish separately. Add the artifacts you need to your build:
38
+
39
+ ```scala
40
+ // Core SQL module (cross-built for JVM and Scala.js; the JDBC-backed
41
+ // JdbcTransactor implementation itself is JVM-only)
42
+ libraryDependencies += "dev.zio" %% "zio-blocks-sql" % "0.0.51"
43
+
44
+ // ZIO integration (lifts JDBC into ZIO effects; JVM-only)
45
+ libraryDependencies += "dev.zio" %% "zio-blocks-sql-zio" % "0.0.51"
46
+ ```
47
+
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.
49
+
50
+ ## Overview
51
+
52
+ The module is organized into three groups: core types you interact with directly in application code, supporting types that implement the protocol layer, and a ZIO integration layer that lifts synchronous operations into the effect system.
53
+
54
+ ### Core Types
55
+
56
+ These seven types form the public API for everyday SQL work:
57
+
58
+ - **[`DbCodec`](./db-codec.md)** — Converts between Scala values and database columns. Read values from result sets by column label or position; write values as bound parameters. Derived automatically from `Schema[A]`.
59
+ - **[`Frag`](./frag.md)** — An immutable SQL fragment built with the `sql"..."` interpolator. Holds literal SQL text and typed parameters separately, preventing SQL injection. Executed via `query`, `queryOne`, `update`, and other extension methods.
60
+ - **[`Table`](./table.md)** — Binds a Scala type to a database table: table name, codec, and column metadata. Derived from a `Schema` using naming conventions. Provides DDL generation via `createTable` and `dropTable`.
61
+ - **[`Repo`](./repo.md)** — Type-safe CRUD repository providing `all`, `find`, `insert`, `update`, `delete`, and other standard operations. All SQL is pre-built at construction time.
62
+ - **[`Transactor`](./transactor.md)** — Entry point for executing SQL. `connect` opens a connection; `transact` adds automatic commit/rollback. `JdbcTransactor` is the JDBC implementation.
63
+ - **[`DbCon`](./db-con.md)** — Implicit context available inside a `Transactor` block. Carries the connection, dialect, and logger. Threaded through all SQL operations automatically.
64
+ - **[`DbTx`](./db-tx.md)** — A `DbCon` subtype marking transactional scope (inside `Transactor#transact`). Extends `DbCon` so transactional and non-transactional operations compose freely.
65
+
66
+ ### Supporting Types
67
+
68
+ These types implement the protocol and derivation layers and are rarely used directly in application code:
69
+
70
+ - **[`DbValue`](./db-value.md)** — Sealed ADT representing typed database values. Used internally by `DbCodec` and `Frag` to hold parameters before binding.
71
+ - **[`DbParam`](./db-param.md)** — Typeclass converting Scala values to `DbValue` for the `sql"..."` interpolator. Instances provided for all common types, `Option[A]`, and `Maybe[A]`.
72
+ - **[`SqlDialect`](./sql-dialect.md)** — Encodes database-specific SQL: type names for DDL and parameter placeholder tokens. `PostgreSQL` and `SQLite` are built-in.
73
+ - **[`SqlLogger`](./sql-logger.md)** — Hook for observing query execution. Receives SQL, parameters, duration, and row count on success or error.
74
+ - **[`SqlNameMapper`](./sql-name-mapper.md)** — Maps Scala field names to SQL column names. `SnakeCase` (default converts `camelCase` to `snake_case`), `Identity`, or `Custom` function.
75
+ - **[`TableMetadata`](./table-metadata.md)** — Derives column metadata from a `Schema`: names, types, and nullability. Used by `Table.derived` to populate table structure.
76
+ - **[`Ddl`](./ddl.md)** — Generates `CREATE TABLE IF NOT EXISTS` and `DROP TABLE IF EXISTS` fragments from column definitions.
77
+ - **[`DbConnection`](./db-connection.md)** — Abstraction over JDBC `Connection`. Prepares statements, controls transactions, and manages lifecycle.
78
+ - **[`DbResultReader`](./db-result-reader.md)** — Reads column values from result sets by label or 1-based position. Supports null detection via `wasNull`. Used by `DbCodec`.
79
+ - **[`DbParamWriter`](./db-param-writer.md)** — Binds parameter values to prepared statements. Covers all primitive, temporal, and UUID types. Used by `DbCodec`.
80
+ - **[`DbCodecDeriver`](./db-codec-deriver.md)** — Schema-driven codec derivation engine. Converts `Schema[A]` to `DbCodec[A]`, handling primitives, records, options, and JSONB types.
81
+ - **[`TransactorZIO`](./transactor-zio.md)** — ZIO integration for `Transactor`. Runs SQL effects on the blocking thread pool with proper resource cleanup and interrupt handling. Includes `ZLayer` support.
82
+
83
+ ## How They Work Together
84
+
85
+ Five interconnected flows define how the module's types cooperate: codec derivation, SQL assembly, execution context, entity mapping, and transaction lifecycle. Understanding these flows shows why each type exists and how to compose them.
86
+
87
+ The typical workflow proceeds in this order:
88
+
89
+ 1. Define a `Schema[A]` for your domain type (usually with `Schema.derived`).
90
+ 2. Call `Repo.derived` (or `Table.derived` + `Repo.apply`) to produce a `Repo[E, ID]` — all SQL is pre-built here.
91
+ 3. Create a `JdbcTransactor` from a JDBC URL or `DataSource`.
92
+ 4. Open a connection with `Transactor#connect` (for reads) or `Transactor#transact` (for writes), which supplies a `DbCon` or `DbTx` context.
93
+ 5. Inside the context block, call `Repo` methods or execute `sql"..."` fragments directly.
94
+
95
+ The following diagram shows the data-flow relationships between types:
96
+
97
+ ```
98
+ Schema[A]
99
+ │
100
+ ┌────────────────┼──────────────────┐
101
+ │ │ │
102
+ ▼ ▼ ▼
103
+ DbCodecDeriver TableMetadata deriveTableName
104
+ │ .columnsFor() (TableNamingPolicy
105
+ ▼ │ + SqlNameMapper)
106
+ DbCodec[A] ◄──────────┘ │
107
+ │ │
108
+ └───────────────────────────────────┘
109
+ │
110
+ Table[A](name, codec, columnsMeta)
111
+ │
112
+ Repo[E, ID] sql"..." → Frag
113
+ ─ all / find ─ frag.query[A]
114
+ ─ insert / update / delete ─ frag.update
115
+ │ │
116
+ ┌───────┴─────────────────────────────┘
117
+ │
118
+ ▼
119
+ Transactor
120
+ ├─ .connect { ... } (non-transactional, DbCon in scope)
121
+ └─ .transact { ... } (commit / rollback, DbTx in scope)
122
+ │
123
+ DbCon / DbTx
124
+ ┌────────────┼────────────┐
125
+ ▼ ▼ ▼
126
+ DbConnection SqlDialect SqlLogger
127
+ (JDBC wrapper) (PostgreSQL (observability)
128
+ | SQLite)
129
+ ```
130
+
131
+ The `sql"..."` interpolator is defined as an extension on `StringContext`. At compile time the macro verifies that every interpolated expression has a `DbParam[A]` instance, converts it to a `DbValue`, and assembles a `Frag` with literal SQL in `parts` and the typed values in `params`. The `Frag` carries no dialect-specific rendering until `Frag#sql(dialect)` is called, so the same fragment works with PostgreSQL and SQLite unchanged.
132
+
133
+ A complete end-to-end example showing schema definition, table setup, repository construction, and mixed repository/fragment queries follows:
134
+
135
+ ```scala
136
+ import zio.blocks.sql._
137
+ import zio.blocks.schema.Schema
138
+ import zio.blocks.maybe.Maybe
139
+
140
+ case class User(id: Int, name: String, email: String)
141
+ object User {
142
+ implicit val schema: Schema[User] = Schema.derived
143
+ }
144
+
145
+ // 1. Create transactor and repository
146
+ val tx = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
147
+ // tx: JdbcTransactor = zio.blocks.sql.JdbcTransactor@3fe7357a
148
+ val repo = Repo.derived[User, Int]("users", "id", _.id)
149
+ // repo: Repo[User, Int] = zio.blocks.sql.Repo$DerivedRepo@4206125b
150
+
151
+ // 2. Set up schema and run a transactional workflow
152
+ tx.transact {
153
+ // Create table using derived DDL
154
+ repo.table.createTable(summon[DbTx].dialect).update
155
+
156
+ // Insert via repository (uses pre-built INSERT fragment)
157
+ repo.insert(User(1, "Alice", "alice@example.com"))
158
+ repo.insert(User(2, "Bob", "bob@example.com"))
159
+
160
+ // Read via repository
161
+ val alice: Maybe[User] = repo.find(1)
162
+
163
+ // Read via raw SQL fragment — composes freely with repo operations
164
+ val aUsers: List[User] =
165
+ sql"SELECT id, name, email FROM users WHERE name LIKE ${"A%"}".query[User]
166
+
167
+ // Update and delete
168
+ repo.update(User(1, "Alice Smith", "alice.smith@example.com"))
169
+ repo.delete(2)
170
+
171
+ (alice, aUsers)
172
+ }
173
+ // res1: Tuple2[Maybe[User], List[User]] = (
174
+ // User(id = 1, name = "Alice", email = "alice@example.com"),
175
+ // List(User(id = 1, name = "Alice", email = "alice@example.com"))
176
+ // )
177
+ ```
178
+
179
+ Inside `transact`, auto-commit is disabled. If any call throws, the entire block rolls back and the exception propagates. On normal return, the transaction commits and the connection closes.
180
+
181
+ ## Common Patterns
182
+
183
+ The module is designed around a small set of patterns that appear throughout most application code. Recognizing these patterns makes it easy to choose the right approach for each situation.
184
+
185
+ ### Schema-Driven Derivation
186
+
187
+ `DbCodec`, `Table`, and `Repo` all derive from a single `Schema[A]`. Derivation respects `@Modifier` annotations — `@Modifier.rename` overrides a column name, `@Modifier.transient` excludes a field from the codec, and `@Modifier.config("sql.table_name", "my_table")` overrides the table name. This means your Scala type definition is the single source of truth for column names, types, and nullability:
188
+
189
+ ```scala
190
+ import zio.blocks.sql._
191
+ import zio.blocks.schema.{Schema, Modifier}
192
+
193
+ case class BlogPost(
194
+ @Modifier.rename("post_id") id: Int,
195
+ title: String,
196
+ @Modifier.transient authorHandle: String = "" // excluded from SQL
197
+ )
198
+ object BlogPost {
199
+ implicit val schema: Schema[BlogPost] = Schema.derived
200
+ }
201
+
202
+ val repo = Repo.derived[BlogPost, Int]("post_id", _.id)
203
+ // repo: Repo[BlogPost, Int] = zio.blocks.sql.Repo$DerivedRepo@243d4111
204
+ repo.table.name
205
+ // res3: String = "blog_post"
206
+ repo.table.codec.columns
207
+ // res4: IndexedSeq[String] = Vector("post_id", "title")
208
+ ```
209
+
210
+ ### Implicit Context Threading
211
+
212
+ Every SQL operation — `Frag` execution and `Repo` CRUD — requires an implicit `DbCon` (or `DbTx`) in scope. The context carries the connection, dialect, and logger, but you never pass it explicitly. Calling code inside `Transactor#connect` or `Transactor#transact` automatically has the context available, and helper methods can propagate it with a `using` parameter:
213
+
214
+ ```scala
215
+ import zio.blocks.sql._
216
+ import zio.blocks.schema.Schema
217
+
218
+ case class Product(id: Int, name: String, price: BigDecimal)
219
+ object Product { implicit val schema: Schema[Product] = Schema.derived }
220
+
221
+ def cheapProducts(maxPrice: BigDecimal)(using DbCon): List[Product] =
222
+ sql"SELECT id, name, price FROM product WHERE price < $maxPrice".query[Product]
223
+
224
+ val tx = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
225
+ tx.connect {
226
+ // `cheapProducts` picks up the DbCon automatically
227
+ val items = cheapProducts(BigDecimal("9.99"))
228
+ }
229
+ ```
230
+
231
+ ### Multi-Column Codecs
232
+
233
+ A single `DbCodec[A]` can span multiple database columns. When you use a multi-column type directly in an `sql"..."` expression, the `fromDbCodec` `DbParam` instance throws at runtime because it cannot collapse multiple values into a single `?` placeholder. Instead, use `Frag.values` for multi-row inserts or write the columns explicitly in the fragment:
234
+
235
+ ```scala
236
+ import zio.blocks.sql._
237
+ import zio.blocks.schema.Schema
238
+
239
+ case class Point(x: Double, y: Double)
240
+ object Point { implicit val schema: Schema[Point] = Schema.derived }
241
+
242
+ // Multi-row insert using Frag.values — one (?, ?) tuple per row
243
+ val points = List(Point(1.0, 2.0), Point(3.0, 4.0))
244
+ // points: List[Point] = List(Point(x = 1.0, y = 2.0), Point(x = 3.0, y = 4.0))
245
+ val frag = Frag.literal("INSERT INTO point (x, y) VALUES ") ++ Frag.values(points)
246
+ // frag: Frag = Frag(
247
+ // parts = Vector("INSERT INTO point (x, y) VALUES (", ", ", "), (", ", ", ")"),
248
+ // params = Vector(DbDouble(1.0), DbDouble(2.0), DbDouble(3.0), DbDouble(4.0))
249
+ // )
250
+ frag.sql(SqlDialect.SQLite)
251
+ // res7: String = "INSERT INTO point (x, y) VALUES (?, ?), (?, ?)"
252
+ ```
253
+
254
+ ### Type-Safe SQL Parameterization
255
+
256
+ The `sql"..."` interpolator accepts any Scala value for which a `DbParam[A]` exists. The macro checks this at compile time and binds the value to a `?` placeholder, preventing SQL injection regardless of the value's content. All standard scalar types have built-in instances, `Option[A]` binds to `NULL` or the inner value, and custom types with a `DbCodec[A]` automatically gain a `DbParam[A]`:
257
+
258
+ ```scala
259
+ import zio.blocks.sql._
260
+
261
+ val userId: Int = 42
262
+ // userId: Int = 42
263
+ val namePattern: String = "%alice%"
264
+ // namePattern: String = "%alice%"
265
+ val active: Option[Boolean] = Some(true)
266
+ // active: Option[Boolean] = Some(true)
267
+
268
+ // All three are compile-time safe — no string concatenation
269
+ val frag =
270
+ sql"SELECT * FROM users WHERE id = $userId AND name LIKE $namePattern AND active = $active"
271
+ // frag: Frag = Frag(
272
+ // parts = ArraySeq(
273
+ // "SELECT * FROM users WHERE id = ",
274
+ // " AND name LIKE ",
275
+ // " AND active = ",
276
+ // ""
277
+ // ),
278
+ // params = Vector(DbInt(42), DbString("%alice%"), DbBoolean(true))
279
+ // )
280
+ frag.sql(SqlDialect.SQLite)
281
+ // res9: String = "SELECT * FROM users WHERE id = ? AND name LIKE ? AND active = ?"
282
+ frag.params
283
+ // res10: IndexedSeq[DbValue] = Vector(
284
+ // DbInt(42),
285
+ // DbString("%alice%"),
286
+ // DbBoolean(true)
287
+ // )
288
+ ```
289
+
290
+ ### JSONB Serialization for Complex Types
291
+
292
+ When a field's type is not directly representable as a single column — such as `List[A]`, `Map[K, V]`, or a sealed trait with multiple variants — `DbCodecDeriver` automatically uses `DbCodec.jsonb[A]` to store and retrieve the value as a JSON-encoded `TEXT` or `JSONB` column. This keeps complex nested data in a single column without requiring a separate table:
293
+
294
+ ```scala
295
+ import zio.blocks.sql._
296
+
297
+ case class Order(id: Int, tags: List[String], metadata: Map[String, String]) derives DbCodec
298
+
299
+ // `tags` and `metadata` are encoded via DbCodec.jsonb when read/written through Frag/Repo
300
+ val codec = DbCodec[Order]
301
+ // codec: DbCodec[Order] = zio.blocks.sql.DbCodecDeriver$$anon$20@10cf0d2d
302
+ codec.columns
303
+ // res12: IndexedSeq[String] = Vector("id", "tags", "metadata")
304
+ ```
305
+
306
+ ### Optional and Nullable Handling
307
+
308
+ `Option[A]` and `Maybe[A]` map to a single nullable column. Reading a `NULL` from the database produces `None` or `Maybe.absent`; writing `None` or `Maybe.absent` binds `NULL` to the parameter. For non-optional types, encountering an unexpected `NULL` throws `IllegalStateException` at read time, surfacing schema mismatches immediately rather than silently coercing `NULL` to a default:
309
+
310
+ ```scala
311
+ import zio.blocks.sql._
312
+ import zio.blocks.schema.Schema
313
+
314
+ case class Profile(userId: Int, bio: Option[String], avatarUrl: Option[String])
315
+ object Profile { implicit val schema: Schema[Profile] = Schema.derived }
316
+
317
+ // bio and avatar_url become nullable TEXT columns in the generated DDL
318
+ val repo = Repo.derived[Profile, Int]("user_id", _.userId)
319
+
320
+ given DbCon = ???
321
+ // repo.insert(Profile(1, None, None)) binds NULL for both optional columns
322
+ repo.insert(Profile(1, None, None))
323
+ ```
324
+
325
+ ## Integration Points
326
+
327
+ The `zio-blocks-sql` module sits at the intersection of several other ZIO Blocks modules and integrates with the JVM JDBC ecosystem.
328
+
329
+ The primary dependency is **`zio-blocks-schema`**. `Schema[A]` (specifically its `Reflect` tree) is the source of all compile-time type metadata: field names, types, nullability, and annotations. `DbCodecDeriver` extends `Deriver[DbCodec]`, the same framework used by JSON, Avro, and other codec modules in the schema module's built-in codec suite. The `@Modifier.rename`, `@Modifier.transient`, and `@Modifier.config` annotations attach SQL-specific configuration to individual fields and types without coupling the domain model to the `zio-blocks-sql` module.
330
+
331
+ The module also depends on **`zio-blocks-maybe`** for `Maybe[A]` support. `Maybe[A]` has distinct absent and present semantics (unlike `Option` which cannot distinguish `Some(null)` from a genuinely absent value), and both `DbCodec` and `DbParam` provide `Maybe[A]` instances alongside `Option[A]`.
332
+
333
+ For transparent opaque-type support the module integrates with **`As[A, B]`** from `zio-blocks-schema`. If an opaque type has a `DbCodec` for its underlying type and an `As` conversion, `DbCodec` derives an instance for the opaque type automatically via `dbCodecFromAs` without requiring a hand-written codec.
334
+
335
+ The JDBC layer is isolated behind three abstractions — `DbConnection`, `DbResultReader`, and `DbParamWriter` — so the `JdbcTransactor` can be replaced by an alternate backend (for example, a WebSQL or node-postgres adapter on Scala.js) without changing any application code that depends only on the shared `zio-blocks-sql` module.
336
+
337
+ The **`zio-blocks-sql-zio` module** provides `TransactorZIO`, a ZIO-aware wrapper around `JdbcTransactor`. It exposes two execution models: blocking wrappers (`TransactorZIO#connect`, `TransactorZIO#transact`) that run synchronous bodies with `ZIO.attemptBlocking`, and effect-aware methods (`TransactorZIO#connectZIO`, `TransactorZIO#transactZIO`) that bracket connections via `ZIO.acquireRelease` so connections are always released even on fiber interruption. A `ZLayer` constructor (`TransactorZIO.layer`) integrates with ZIO's dependency injection system.
338
+
339
+ ## See Also
340
+
341
+ - **[SQL-ZIO Integration Reference](../sql-zio.md)** — Reference page for `TransactorZIO` and the ZIO `ZLayer` integration.