@zio.dev/zio-blocks 0.0.33 → 0.0.55

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 (215) 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 +21 -16
  4. package/guides/getting-started-with-mux.md +1395 -0
  5. package/guides/query-dsl-extending.md +161 -102
  6. package/guides/query-dsl-fluent-builder.md +217 -157
  7. package/guides/query-dsl-reified-optics.md +12 -10
  8. package/guides/query-dsl-sql.md +640 -165
  9. package/guides/sql-checked-interpolation.md +173 -0
  10. package/guides/sql-transactions.md +286 -0
  11. package/guides/telemetry-guide.md +1130 -0
  12. package/guides/zio-schema-migration.md +29 -22
  13. package/index.md +248 -389
  14. package/package.json +1 -1
  15. package/plans/config-follow-up-prs.md +188 -0
  16. package/plans/config-pr-assessment-roadmap.md +310 -0
  17. package/reference/MuxDataFlow.jsx +250 -0
  18. package/reference/async.md +1499 -0
  19. package/reference/chunk.md +3533 -308
  20. package/reference/codegen/case-class.md +436 -0
  21. package/reference/codegen/emitter-config.md +383 -0
  22. package/reference/codegen/examples.md +664 -0
  23. package/reference/codegen/field.md +316 -0
  24. package/reference/codegen/index.md +317 -0
  25. package/reference/codegen/scala-emitter.md +392 -0
  26. package/reference/codegen/scala-file.md +276 -0
  27. package/reference/codegen/sealed-trait.md +408 -0
  28. package/reference/codegen/type-definition.md +340 -0
  29. package/reference/codegen/type-ref.md +201 -0
  30. package/reference/combinators.md +347 -117
  31. package/reference/config/config-decoder.md +460 -0
  32. package/reference/config/config-source.md +489 -0
  33. package/reference/config/errors.md +278 -0
  34. package/reference/config/flags.md +369 -0
  35. package/reference/config/formats.md +314 -0
  36. package/reference/config/index.md +304 -0
  37. package/reference/config/rollout.md +336 -0
  38. package/reference/context.md +9 -52
  39. package/reference/data-migration.md +269 -0
  40. package/reference/datastar/attributes.md +302 -0
  41. package/reference/datastar/events.md +234 -0
  42. package/reference/datastar/index.md +256 -0
  43. package/reference/datastar/signals.md +230 -0
  44. package/reference/datastar/sse.md +295 -0
  45. package/reference/datastar.md +346 -0
  46. package/reference/docs.md +1461 -345
  47. package/reference/endpoint/auth-type.md +146 -0
  48. package/reference/endpoint/bulk-creation.md +96 -0
  49. package/reference/endpoint/endpoint.md +297 -0
  50. package/reference/endpoint/http-codec.md +249 -0
  51. package/reference/endpoint/index.md +745 -0
  52. package/reference/endpoint/path-codec.md +225 -0
  53. package/reference/endpoint/route-pattern.md +194 -0
  54. package/reference/endpoint/route-tree.md +111 -0
  55. package/reference/endpoint/segment-codec.md +199 -0
  56. package/reference/html.md +1424 -0
  57. package/reference/htmx/attribute-values.md +359 -0
  58. package/reference/htmx/hx-encoding.md +111 -0
  59. package/reference/htmx/hx-params.md +204 -0
  60. package/reference/htmx/hx-swap.md +276 -0
  61. package/reference/htmx/hx-sync.md +251 -0
  62. package/reference/htmx/hx-target.md +314 -0
  63. package/reference/htmx/hx-trigger.md +457 -0
  64. package/reference/htmx/hx-url-update.md +239 -0
  65. package/reference/htmx/index.md +807 -0
  66. package/reference/htmx/response-headers.md +240 -0
  67. package/reference/http-model/headers.md +735 -0
  68. package/reference/http-model/index.md +49 -0
  69. package/reference/http-model/model.md +1517 -0
  70. package/reference/http-model/schema-codecs.md +522 -0
  71. package/reference/http-model/schema.md +750 -0
  72. package/reference/http-model/server-sent-event.md +341 -0
  73. package/reference/jwt.md +195 -0
  74. package/reference/maybe.md +943 -0
  75. package/reference/media-type.md +2 -2
  76. package/reference/mux.md +254 -0
  77. package/reference/mux.mdx +828 -0
  78. package/reference/openapi.md +1351 -0
  79. package/reference/projection.md +654 -0
  80. package/reference/resource-management/defer-handle.md +1 -1
  81. package/reference/resource-management/resource.md +31 -98
  82. package/reference/resource-management/scope.md +28 -220
  83. package/reference/resource-management/wire.md +5 -55
  84. package/reference/ringbuffer/MpmcDiagram.jsx +717 -0
  85. package/reference/ringbuffer/MpscDiagram.jsx +618 -0
  86. package/reference/ringbuffer/SpmcDiagram.jsx +680 -0
  87. package/reference/ringbuffer/SpscDiagram.jsx +677 -0
  88. package/reference/ringbuffer/advanced.mdx +109 -0
  89. package/reference/ringbuffer/index.mdx +145 -0
  90. package/reference/ringbuffer/mpmc.mdx +185 -0
  91. package/reference/ringbuffer/mpsc.mdx +164 -0
  92. package/reference/ringbuffer/spmc.mdx +108 -0
  93. package/reference/ringbuffer/spsc.mdx +416 -0
  94. package/reference/{allows.md → schema/allows.md} +4 -100
  95. package/reference/{binding-resolver.md → schema/binding-resolver.md} +1 -1
  96. package/reference/{binding.md → schema/binding.md} +3 -4
  97. package/reference/schema/built-in-codecs/avro.md +451 -0
  98. package/reference/schema/built-in-codecs/bson.md +510 -0
  99. package/reference/schema/built-in-codecs/csv.md +564 -0
  100. package/reference/schema/built-in-codecs/index.md +77 -0
  101. package/reference/schema/built-in-codecs/json/index.md +295 -0
  102. package/reference/schema/built-in-codecs/json/json-config.md +217 -0
  103. package/reference/{json-patch.md → schema/built-in-codecs/json/json-patch.md} +5 -5
  104. package/reference/{json-schema.md → schema/built-in-codecs/json/json-schema.md} +14 -47
  105. package/reference/schema/built-in-codecs/json/json-selection.md +322 -0
  106. package/reference/{json.md → schema/built-in-codecs/json/json.md} +32 -64
  107. package/reference/schema/built-in-codecs/messagepack.md +508 -0
  108. package/reference/schema/built-in-codecs/thrift.md +433 -0
  109. package/reference/schema/built-in-codecs/toon.md +1078 -0
  110. package/reference/{xml.md → schema/built-in-codecs/xml.md} +13 -9
  111. package/reference/schema/built-in-codecs/yaml.md +552 -0
  112. package/reference/{codec.md → schema/codec.md} +11 -11
  113. package/reference/{dynamic-optic.md → schema/dynamic-optic.md} +196 -5
  114. package/reference/{dynamic-schema.md → schema/dynamic-schema.md} +8 -8
  115. package/reference/schema/format.md +92 -0
  116. package/reference/schema/index.md +52 -0
  117. package/reference/schema/migration.md +297 -0
  118. package/reference/{modifier.md → schema/modifier.md} +58 -7
  119. package/reference/{optics.md → schema/optics.md} +2 -2
  120. package/reference/{patch.md → schema/patch.md} +1 -1
  121. package/{path-interpolator.md → reference/schema/path-interpolator.md} +167 -72
  122. package/reference/schema/reflect-transformer.md +140 -0
  123. package/reference/{schema-evolution → schema/schema-evolution}/as.md +8 -8
  124. package/reference/{schema-evolution → schema/schema-evolution}/index.md +2 -2
  125. package/reference/{schema-evolution → schema/schema-evolution}/into.md +8 -8
  126. package/reference/{schema-expr.md → schema/schema-expr.md} +110 -175
  127. package/reference/schema/schema-search.md +263 -0
  128. package/reference/{schema.md → schema/schema.md} +22 -2
  129. package/reference/{structural-types.md → schema/structural-types.md} +1 -1
  130. package/reference/{type-class-derivation.md → schema/type-class-derivation.md} +63 -1
  131. package/reference/smithy.md +1032 -0
  132. package/reference/sql/db-codec-deriver.md +71 -0
  133. package/reference/sql/db-codec.md +687 -0
  134. package/reference/sql/db-con.md +271 -0
  135. package/reference/sql/db-connection.md +153 -0
  136. package/reference/sql/db-param-writer.md +77 -0
  137. package/reference/sql/db-param.md +66 -0
  138. package/reference/sql/db-result-reader.md +148 -0
  139. package/reference/sql/db-tx.md +114 -0
  140. package/reference/sql/db-value.md +41 -0
  141. package/reference/sql/ddl.md +85 -0
  142. package/reference/sql/frag.md +288 -0
  143. package/reference/sql/index.md +341 -0
  144. package/reference/sql/repo.md +600 -0
  145. package/reference/sql/sql-dialect.md +73 -0
  146. package/reference/sql/sql-logger.md +62 -0
  147. package/reference/sql/sql-name-mapper.md +70 -0
  148. package/reference/sql/table-metadata.md +134 -0
  149. package/reference/sql/table.md +448 -0
  150. package/reference/sql/transactor-zio.md +399 -0
  151. package/reference/sql/transactor.md +363 -0
  152. package/reference/sql-zio.md +112 -0
  153. package/reference/streams/core/index.md +32 -0
  154. package/reference/streams/core/pipeline.md +854 -0
  155. package/reference/streams/core/sink.md +1404 -0
  156. package/reference/streams/core/stream.md +3236 -0
  157. package/reference/streams/execution-and-compatibility/async-execution.md +822 -0
  158. package/reference/streams/execution-and-compatibility/index.md +35 -0
  159. package/reference/streams/execution-and-compatibility/platform-differences.md +297 -0
  160. package/reference/streams/execution-and-compatibility/scala-2-compatibility.md +88 -0
  161. package/reference/streams/execution-and-compatibility/zero-boxing.md +393 -0
  162. package/reference/streams/index.md +726 -0
  163. package/reference/streams/primitives/index.md +30 -0
  164. package/reference/streams/primitives/reader.md +1992 -0
  165. package/reference/streams/primitives/writer.md +1201 -0
  166. package/reference/telemetry/common/any-value.md +90 -0
  167. package/reference/telemetry/common/attribute-key.md +87 -0
  168. package/reference/telemetry/common/attributes.md +118 -0
  169. package/reference/telemetry/common/index.md +39 -0
  170. package/reference/telemetry/common/instrumentation-scope.md +24 -0
  171. package/reference/telemetry/common/resource.md +34 -0
  172. package/reference/telemetry/index.md +311 -0
  173. package/reference/telemetry/logging/index.md +197 -0
  174. package/reference/telemetry/logging/log-enrichment.md +72 -0
  175. package/reference/telemetry/logging/log-formatter.md +100 -0
  176. package/reference/telemetry/logging/log-record-processor.md +56 -0
  177. package/reference/telemetry/logging/log-record.md +44 -0
  178. package/reference/telemetry/logging/log-writer.md +64 -0
  179. package/reference/telemetry/logging/logger-provider.md +142 -0
  180. package/reference/telemetry/logging/logger.md +83 -0
  181. package/reference/telemetry/logging/severity.md +62 -0
  182. package/reference/telemetry/metrics/index.md +150 -0
  183. package/reference/telemetry/metrics/instruments.md +183 -0
  184. package/reference/telemetry/metrics/labeled-instruments.md +74 -0
  185. package/reference/telemetry/metrics/meter-provider.md +76 -0
  186. package/reference/telemetry/metrics/meter.md +98 -0
  187. package/reference/telemetry/metrics/metric-data.md +57 -0
  188. package/reference/telemetry/otel/custom-exporter.md +216 -0
  189. package/reference/telemetry/otel/index.md +212 -0
  190. package/reference/telemetry/tracing/index.md +155 -0
  191. package/reference/telemetry/tracing/sampler.md +89 -0
  192. package/reference/telemetry/tracing/span-builder.md +57 -0
  193. package/reference/telemetry/tracing/span-context.md +39 -0
  194. package/reference/telemetry/tracing/span-data.md +32 -0
  195. package/reference/telemetry/tracing/span-kind.md +55 -0
  196. package/reference/telemetry/tracing/span-processor.md +53 -0
  197. package/reference/telemetry/tracing/span-status.md +47 -0
  198. package/reference/telemetry/tracing/span.md +117 -0
  199. package/reference/telemetry/tracing/tracer-provider.md +91 -0
  200. package/reference/telemetry/tracing/tracer.md +52 -0
  201. package/reference/typeid.md +5 -83
  202. package/sidebars.js +376 -43
  203. package/undocumented-report.md +528 -270
  204. package/reference/formats.md +0 -694
  205. package/reference/http-model.md +0 -1716
  206. package/reference/streams.md +0 -989
  207. package/ringbuffer.md +0 -249
  208. /package/reference/{json-differ.md → schema/built-in-codecs/json/json-differ.md} +0 -0
  209. /package/reference/{dynamic-value.md → schema/dynamic-value.md} +0 -0
  210. /package/reference/{lazy.md → schema/lazy.md} +0 -0
  211. /package/reference/{reflect.md → schema/reflect.md} +0 -0
  212. /package/reference/{registers.md → schema/registers.md} +0 -0
  213. /package/reference/{schema-error.md → schema/schema-error.md} +0 -0
  214. /package/reference/{syntax.md → schema/syntax.md} +0 -0
  215. /package/reference/{validation.md → schema/validation.md} +0 -0
@@ -0,0 +1,448 @@
1
+ ---
2
+ id: table
3
+ title: "Table"
4
+ description: "Reference page for Table[A], the entity-to-table metadata binding in the sql module for schema-driven JDBC mapping and DDL generation."
5
+ keywords:
6
+ - "Table Schema Derivation"
7
+ - "DDL Generation SQL"
8
+ - "TableNamingPolicy Column Mapping"
9
+ - "ColumnMeta Metadata"
10
+ ---
11
+
12
+ `Table[A]` is the metadata binding between a Scala type `A` and a specific database table in the `sql` module. It holds the table name, a `DbCodec[A]` for reading and writing rows, and an `IndexedSeq[ColumnMeta]` describing each column's name, SQL type representative, and nullability. `Table` provides both type-safe column access and dialect-aware DDL generation without any ORM runtime or session lifecycle.
13
+
14
+ The structural shape of `Table` is:
15
+
16
+ ```scala
17
+ final case class Table[A](name: String, codec: DbCodec[A], columnsMeta: IndexedSeq[ColumnMeta]) {
18
+ def columns: IndexedSeq[String] = ???
19
+
20
+ def createTable(dialect: SqlDialect): Frag = ???
21
+ def dropTable: Frag = ???
22
+ }
23
+
24
+ object Table {
25
+ def derived[A](implicit schema: Schema[A]): Table[A] = ???
26
+ def derived[A](tableName: String)(implicit schema: Schema[A]): Table[A] = ???
27
+ def derived[A](namingPolicy: TableNamingPolicy)(implicit schema: Schema[A]): Table[A] = ???
28
+ }
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ The following example illustrates the core workflow: derive a table from a schema-equipped case class, inspect its column names, generate `CREATE TABLE` DDL, and finally generate `DROP TABLE` DDL:
34
+
35
+ ```scala
36
+ import zio.blocks.sql._
37
+ import zio.blocks.schema.Schema
38
+
39
+ case class User(id: Int, name: String, email: String)
40
+ object User {
41
+ implicit val schema: Schema[User] = Schema.derived
42
+ }
43
+
44
+ // Derive the table binding — columns come from the schema; "user" is a
45
+ // reserved word in PostgreSQL, so the table name is overridden explicitly
46
+ val table = Table.derived[User]("users")
47
+ // table: Table[User] = Table(
48
+ // name = "users",
49
+ // codec = zio.blocks.sql.DbCodecDeriver$$anon$10@485654ba,
50
+ // columnsMeta = Vector(
51
+ // ColumnMeta(name = "id", dbValue = DbInt(0), nullable = false),
52
+ // ColumnMeta(name = "name", dbValue = DbString(""), nullable = false),
53
+ // ColumnMeta(name = "email", dbValue = DbString(""), nullable = false)
54
+ // )
55
+ // )
56
+ table.name
57
+ // res1: String = "users"
58
+ table.columns
59
+ // res2: IndexedSeq[String] = Vector("id", "name", "email")
60
+
61
+ // Emit dialect-aware DDL as Frag values
62
+ val createSql = table.createTable(SqlDialect.PostgreSQL).sql(SqlDialect.PostgreSQL)
63
+ // createSql: String = """CREATE TABLE IF NOT EXISTS users (
64
+ // id INTEGER NOT NULL,
65
+ // name TEXT NOT NULL,
66
+ // email TEXT NOT NULL
67
+ // )"""
68
+ val dropSql = table.dropTable.sql(SqlDialect.PostgreSQL)
69
+ // dropSql: String = "DROP TABLE IF EXISTS users"
70
+ ```
71
+
72
+ ## Construction / Creating Instances
73
+
74
+ `Table` offers three `derived` factory methods on its companion object and one direct constructor via its primary constructor. All three `derived` overloads require a `Schema[A]` implicit.
75
+
76
+ ### `Table.derived` — Derive using the default naming policy
77
+
78
+ `Table.derived[A]` inspects the `Schema[A]` implicit and applies `TableNamingPolicy.Singular` to produce the table name. This policy converts `CamelCase` Scala type names to `snake_case` SQL identifiers (for example, `UserProfile` becomes `user_profile`). The table name can be overridden by annotating the type with `@Modifier.config("sql.table_name", "my_table")`.
79
+
80
+ ```scala
81
+ object Table {
82
+ def derived[A](implicit schema: Schema[A]): Table[A]
83
+ }
84
+ ```
85
+
86
+ The following example derives a table for a two-field case class and checks the resulting name and column list:
87
+
88
+ ```scala
89
+ import zio.blocks.sql._
90
+ import zio.blocks.schema.Schema
91
+
92
+ case class BlogPost(title: String, body: String)
93
+ object BlogPost {
94
+ implicit val schema: Schema[BlogPost] = Schema.derived
95
+ }
96
+
97
+ val table = Table.derived[BlogPost]
98
+ // table: Table[BlogPost] = Table(
99
+ // name = "blog_post",
100
+ // codec = zio.blocks.sql.DbCodecDeriver$$anon$10@51cbb015,
101
+ // columnsMeta = Vector(
102
+ // ColumnMeta(name = "title", dbValue = DbString(""), nullable = false),
103
+ // ColumnMeta(name = "body", dbValue = DbString(""), nullable = false)
104
+ // )
105
+ // )
106
+ table.name
107
+ // res4: String = "blog_post"
108
+ table.columns
109
+ // res5: IndexedSeq[String] = Vector("title", "body")
110
+ ```
111
+
112
+ ### `Table.derived` (with explicit table name) — Bypass naming policy and annotations
113
+
114
+ `Table.derived[A](tableName: String)` derives a table with the supplied name, ignoring both the `TableNamingPolicy` and any `@Modifier.config("sql.table_name", …)` annotation on the type. The column names and codec are still derived from the schema in the normal way. Use this overload when the desired SQL table name cannot be expressed by any naming policy.
115
+
116
+ ```scala
117
+ object Table {
118
+ def derived[A](tableName: String)(implicit schema: Schema[A]): Table[A]
119
+ }
120
+ ```
121
+
122
+ The following example maps `UserProfile` to a table called `profiles` rather than the default `user_profile`:
123
+
124
+ ```scala
125
+ import zio.blocks.sql._
126
+ import zio.blocks.schema.Schema
127
+
128
+ case class UserProfile(firstName: String, lastName: String)
129
+ object UserProfile {
130
+ implicit val schema: Schema[UserProfile] = Schema.derived
131
+ }
132
+
133
+ val table = Table.derived[UserProfile]("profiles")
134
+ // table: Table[UserProfile] = Table(
135
+ // name = "profiles",
136
+ // codec = zio.blocks.sql.DbCodecDeriver$$anon$10@1d451191,
137
+ // columnsMeta = Vector(
138
+ // ColumnMeta(name = "first_name", dbValue = DbString(""), nullable = false),
139
+ // ColumnMeta(name = "last_name", dbValue = DbString(""), nullable = false)
140
+ // )
141
+ // )
142
+ table.name
143
+ // res7: String = "profiles"
144
+ table.columns
145
+ // res8: IndexedSeq[String] = Vector("first_name", "last_name")
146
+ ```
147
+
148
+ :::caution
149
+ The table name is validated as a SQL identifier immediately at construction time. Spaces, hyphens, or any character outside `[A-Za-z0-9_]` (with a letter or underscore as the first character) will cause `Table.derived` to throw `IllegalArgumentException`. For example, `Table.derived[UserProfile]("user profile")` throws with the message `Invalid SQL table identifier 'user profile'. Only ASCII letters, digits, and underscores are supported, and the first character must be a letter or underscore.`
150
+ :::
151
+
152
+ ### `Table.derived` (with naming policy) — Control table name derivation
153
+
154
+ `Table.derived[A](namingPolicy: TableNamingPolicy)` derives a table and applies the supplied `TableNamingPolicy` to the type name when computing the table name. Use `TableNamingPolicy.Plural` for pluralized names, `TableNamingPolicy.Singular` (the default) for singular names, or `TableNamingPolicy.Custom(f)` for arbitrary transformations.
155
+
156
+ ```scala
157
+ object Table {
158
+ def derived[A](namingPolicy: TableNamingPolicy)(implicit schema: Schema[A]): Table[A]
159
+ }
160
+ ```
161
+
162
+ The following example uses `TableNamingPolicy.Plural` so that `Category` maps to the table `categories`:
163
+
164
+ ```scala
165
+ import zio.blocks.sql._
166
+ import zio.blocks.schema.Schema
167
+
168
+ case class Category(name: String)
169
+ object Category {
170
+ implicit val schema: Schema[Category] = Schema.derived
171
+ }
172
+
173
+ val singular = Table.derived[Category](TableNamingPolicy.Singular)
174
+ // singular: Table[Category] = Table(
175
+ // name = "category",
176
+ // codec = zio.blocks.sql.DbCodecDeriver$$anon$10@2e5dff9,
177
+ // columnsMeta = Vector(
178
+ // ColumnMeta(name = "name", dbValue = DbString(""), nullable = false)
179
+ // )
180
+ // )
181
+ singular.name
182
+ // res10: String = "category"
183
+
184
+ val plural = Table.derived[Category](TableNamingPolicy.Plural)
185
+ // plural: Table[Category] = Table(
186
+ // name = "categories",
187
+ // codec = zio.blocks.sql.DbCodecDeriver$$anon$10@54d4530a,
188
+ // columnsMeta = Vector(
189
+ // ColumnMeta(name = "name", dbValue = DbString(""), nullable = false)
190
+ // )
191
+ // )
192
+ plural.name
193
+ // res11: String = "categories"
194
+
195
+ val custom = Table.derived[Category](TableNamingPolicy.Custom(n => s"tbl_$n"))
196
+ // custom: Table[Category] = Table(
197
+ // name = "tbl_Category",
198
+ // codec = zio.blocks.sql.DbCodecDeriver$$anon$10@325f2633,
199
+ // columnsMeta = Vector(
200
+ // ColumnMeta(name = "name", dbValue = DbString(""), nullable = false)
201
+ // )
202
+ // )
203
+ custom.name
204
+ // res12: String = "tbl_Category"
205
+ ```
206
+
207
+ ### `Table.apply` — Construct directly from codec and column metadata
208
+
209
+ The primary constructor accepts the table name, a `DbCodec[A]`, and an `IndexedSeq[ColumnMeta]` explicitly. SQL identifier validation runs for the table name and every column name at construction time. Use this constructor when you have a hand-written or externally produced codec rather than a schema-derived one.
210
+
211
+ ```scala
212
+ final case class Table[A](name: String, codec: DbCodec[A], columnsMeta: IndexedSeq[ColumnMeta])
213
+ ```
214
+
215
+ The following example builds a `Table` manually, supplying a pre-existing `DbCodec` and explicit column metadata:
216
+
217
+ ```scala
218
+ import zio.blocks.sql._
219
+
220
+ case class Tag(id: Int, label: String) derives DbCodec
221
+
222
+ // columnsMeta must describe the same columns, in the same order, as the codec;
223
+ // nullable must match the field's actual optionality (Tag.label is non-optional)
224
+ val meta = IndexedSeq(
225
+ ColumnMeta("id", DbValue.DbInt(0), nullable = false),
226
+ ColumnMeta("label", DbValue.DbString(""), nullable = false)
227
+ )
228
+ // meta: IndexedSeq[ColumnMeta] = Vector(
229
+ // ColumnMeta(name = "id", dbValue = DbInt(0), nullable = false),
230
+ // ColumnMeta(name = "label", dbValue = DbString(""), nullable = false)
231
+ // )
232
+ val table = Table[Tag]("tag", DbCodec[Tag], meta)
233
+ // table: Table[Tag] = Table(
234
+ // name = "tag",
235
+ // codec = zio.blocks.sql.DbCodecDeriver$$anon$10@1a14f5c3,
236
+ // columnsMeta = Vector(
237
+ // ColumnMeta(name = "id", dbValue = DbInt(0), nullable = false),
238
+ // ColumnMeta(name = "label", dbValue = DbString(""), nullable = false)
239
+ // )
240
+ // )
241
+ table.name
242
+ // res14: String = "tag"
243
+ table.columns
244
+ // res15: IndexedSeq[String] = Vector("id", "label")
245
+ ```
246
+
247
+ :::note
248
+ When the column `nullable` flag is `true`, the generated `CREATE TABLE` statement omits the `NOT NULL` constraint for that column, allowing the database to store `NULL` in that position. `Table.derived` sets this flag automatically based on whether the corresponding schema field is `Option[A]` or `Maybe[A]`.
249
+ :::
250
+
251
+ ## Core Operations
252
+
253
+ ### Element Access
254
+
255
+ The Element Access category exposes `columns`, which returns the SQL column names carried by the table in codec order.
256
+
257
+ #### `columns` — Column names in codec order
258
+
259
+ `Table#columns` returns an `IndexedSeq[String]` of the SQL column names for this table, in the same order as the underlying `DbCodec[A]`. The names are drawn from the validated `columnsMeta` and have already been checked to be legal SQL identifiers at construction time. Access is O(1) since the sequence is built once during construction.
260
+
261
+ ```scala
262
+ final case class Table[A](...) {
263
+ def columns: IndexedSeq[String]
264
+ }
265
+ ```
266
+
267
+ The following example shows `columns` reflecting the snake_case field names derived from the schema:
268
+
269
+ ```scala
270
+ import zio.blocks.sql._
271
+ import zio.blocks.schema.Schema
272
+
273
+ case class OrderLine(productId: Int, quantity: Int, unitPrice: BigDecimal)
274
+ object OrderLine {
275
+ implicit val schema: Schema[OrderLine] = Schema.derived
276
+ }
277
+
278
+ val table = Table.derived[OrderLine]
279
+ // table: Table[OrderLine] = Table(
280
+ // name = "order_line",
281
+ // codec = zio.blocks.sql.DbCodecDeriver$$anon$10@7196247a,
282
+ // columnsMeta = Vector(
283
+ // ColumnMeta(name = "product_id", dbValue = DbInt(0), nullable = false),
284
+ // ColumnMeta(name = "quantity", dbValue = DbInt(0), nullable = false),
285
+ // ColumnMeta(name = "unit_price", dbValue = DbBigDecimal(0), nullable = false)
286
+ // )
287
+ // )
288
+ table.columns
289
+ // res17: IndexedSeq[String] = Vector("product_id", "quantity", "unit_price")
290
+ ```
291
+
292
+ ### DDL Generation
293
+
294
+ The DDL Generation category provides `createTable` and `dropTable`, which produce `Frag` values containing dialect-specific `CREATE TABLE IF NOT EXISTS` and `DROP TABLE IF EXISTS` statements. Both methods delegate to the `Ddl` helper, which constructs a `Frag` with no bound parameters — only literal SQL text.
295
+
296
+ #### `createTable` — Generate a CREATE TABLE statement
297
+
298
+ `Table#createTable` accepts a `SqlDialect` and returns a `Frag` whose SQL text is a `CREATE TABLE IF NOT EXISTS` statement. Each column definition uses the dialect's `typeName` method to convert the column's `DbValue` representative to the appropriate SQL type string (for example, `DbValue.DbString` becomes `TEXT` in PostgreSQL and `TEXT` in SQLite; `DbValue.DbInt` becomes `INTEGER` in both). Non-nullable columns carry a `NOT NULL` constraint; nullable columns do not.
299
+
300
+ ```scala
301
+ final case class Table[A](...) {
302
+ def createTable(dialect: SqlDialect): Frag
303
+ }
304
+ ```
305
+
306
+ The following example demonstrates the DDL generated for a record with a mix of column types and an optional field:
307
+
308
+ ```scala
309
+ import zio.blocks.sql._
310
+ import zio.blocks.schema.Schema
311
+
312
+ case class Product(sku: String, price: BigDecimal, stock: Option[Int])
313
+ object Product {
314
+ implicit val schema: Schema[Product] = Schema.derived
315
+ }
316
+
317
+ val table = Table.derived[Product]
318
+ // table: Table[Product] = Table(
319
+ // name = "product",
320
+ // codec = zio.blocks.sql.DbCodecDeriver$$anon$10@19a68e5b,
321
+ // columnsMeta = Vector(
322
+ // ColumnMeta(name = "sku", dbValue = DbString(""), nullable = false),
323
+ // ColumnMeta(name = "price", dbValue = DbBigDecimal(0), nullable = false),
324
+ // ColumnMeta(name = "stock", dbValue = DbInt(0), nullable = true)
325
+ // )
326
+ // )
327
+ val createPg = table.createTable(SqlDialect.PostgreSQL).sql(SqlDialect.PostgreSQL)
328
+ // createPg: String = """CREATE TABLE IF NOT EXISTS product (
329
+ // sku TEXT NOT NULL,
330
+ // price NUMERIC NOT NULL,
331
+ // stock INTEGER
332
+ // )"""
333
+ val createSq = table.createTable(SqlDialect.SQLite).sql(SqlDialect.SQLite)
334
+ // createSq: String = """CREATE TABLE IF NOT EXISTS product (
335
+ // sku TEXT NOT NULL,
336
+ // price TEXT NOT NULL,
337
+ // stock INTEGER
338
+ // )"""
339
+ ```
340
+
341
+ :::caution
342
+ `Table#createTable` only supports column types whose `DbValue` representative maps to a primitive SQL type. Fields whose codec falls back to JSONB serialization (for example, `List[A]` or a sealed trait with multiple variants) will produce a `TEXT` or `JSONB` column — the DDL column type depends on the representative `DbValue` assigned during column metadata derivation, which in those cases is `DbValue.DbString`. Nested records that are flattened into multiple columns are fully supported.
343
+ :::
344
+
345
+ #### `dropTable` — Generate a DROP TABLE statement
346
+
347
+ `Table#dropTable` returns a `Frag` whose SQL text is `DROP TABLE IF EXISTS <name>`, with no parameters and no dialect argument. Because `DROP TABLE` syntax is uniform across the supported dialects, a single `Frag` is correct for any `SqlDialect`. Render the fragment with `Frag#sql` to obtain the final SQL string.
348
+
349
+ ```scala
350
+ final case class Table[A](...) {
351
+ def dropTable: Frag
352
+ }
353
+ ```
354
+
355
+ The following example shows the drop statement for a table derived from a simple case class:
356
+
357
+ ```scala
358
+ import zio.blocks.sql._
359
+ import zio.blocks.schema.Schema
360
+
361
+ case class Session(token: String, userId: Int)
362
+ object Session {
363
+ implicit val schema: Schema[Session] = Schema.derived
364
+ }
365
+
366
+ val table = Table.derived[Session]
367
+ // table: Table[Session] = Table(
368
+ // name = "session",
369
+ // codec = zio.blocks.sql.DbCodecDeriver$$anon$10@7081afa2,
370
+ // columnsMeta = Vector(
371
+ // ColumnMeta(name = "token", dbValue = DbString(""), nullable = false),
372
+ // ColumnMeta(name = "user_id", dbValue = DbInt(0), nullable = false)
373
+ // )
374
+ // )
375
+ table.dropTable.sql(SqlDialect.PostgreSQL)
376
+ // res20: String = "DROP TABLE IF EXISTS session"
377
+ table.dropTable.sql(SqlDialect.SQLite)
378
+ // res21: String = "DROP TABLE IF EXISTS session"
379
+ ```
380
+
381
+ ## Supporting Types
382
+
383
+ `Table` is a monomorphic final case class with no subtypes of its own, but it depends on two supporting types — `ColumnMeta` and `TableNamingPolicy` — that control how column metadata is captured and how table names are derived.
384
+
385
+ ### `ColumnMeta`
386
+
387
+ `ColumnMeta` is a final case class that carries the per-column metadata consumed by `Table#createTable` for DDL generation. Each field in a schema-derived type produces exactly one `ColumnMeta` (nested records are flattened; optional fields set `nullable = true`).
388
+
389
+ ```scala
390
+ final case class ColumnMeta(name: String, dbValue: DbValue, nullable: Boolean)
391
+ ```
392
+
393
+ The three fields serve distinct roles. `name` is the SQL column name after `SqlNameMapper` has been applied and SQL identifier validation has been run. `dbValue` is a representative instance of the column's `DbValue` variant (for example, `DbValue.DbInt(0)` for an `Int` column) — it carries no runtime data and is used only to dispatch to `SqlDialect#typeName` during DDL generation. `nullable` reflects whether the Scala field is `Option[A]` or `Maybe[A]`.
394
+
395
+ ### `TableNamingPolicy`
396
+
397
+ `TableNamingPolicy` is a sealed trait that controls how a Scala type name is translated into a SQL table name when using `Table.derived`. All three `derived` overloads use a naming policy either explicitly or implicitly.
398
+
399
+ ```scala
400
+ sealed trait TableNamingPolicy {
401
+ def defaultName(typeName: String): String
402
+ }
403
+
404
+ object TableNamingPolicy {
405
+ case object Singular extends TableNamingPolicy
406
+ case object Plural extends TableNamingPolicy
407
+ final case class Custom(f: String => String) extends TableNamingPolicy
408
+ }
409
+ ```
410
+
411
+ The three variants cover the most common conventions:
412
+
413
+ - **`Singular`** (the default) — converts the Scala type name to `snake_case` using `SqlNameMapper.SnakeCase`. `UserProfile` becomes `user_profile`, `Category` becomes `category`.
414
+ - **`Plural`** — applies the same `snake_case` conversion and then appends a simple English pluralization suffix. `Category` becomes `categories`, `User` becomes `users`, `Quiz` becomes `quizzes`.
415
+ - **`Custom(f)`** — applies the function `f` to the type name, giving full control over the mapping. The function receives the raw Scala type name (before any case conversion) and must return a valid SQL identifier.
416
+
417
+ The `Singular` policy is chosen because most databases treat table names as singular nouns by convention, but `Plural` is equally idiomatic in many teams. Pass the desired policy explicitly to `Table.derived[A](namingPolicy)` when the default does not match your project's convention.
418
+
419
+ ## Comparison
420
+
421
+ ### Slick and Doobie
422
+
423
+ `Table` takes a narrower scope than lifted-embedding ORMs like Slick and functional query builders like Doobie:
424
+
425
+ | Concern | `Table` (this module) | Slick | Doobie |
426
+ |--------------------------|--------------------------------------------------------------|-------------------------------------------------------------|------------------------------------------------------|
427
+ | Schema source of truth | `Schema[A]` (compile-time derivation) | `Table` class extending `TableQuery` (explicit column defs) | Hand-written `Get`/`Put` instances or Doobie macros |
428
+ | Query DSL | Plain SQL via `sql"..."` interpolator + `Frag` composition | Lifted Scala expressions compiled to SQL | Plain SQL via `sql"..."` interpolator |
429
+ | DDL generation | `Table#createTable` / `Table#dropTable` return `Frag` values | Via `schema.create` / `schema.drop` (requires lifted query) | Not built-in; usually handled by Flyway or Liquibase |
430
+ | Runtime overhead | Zero — derivation is compile-time; no reflection at runtime | JVM reflection + query compilation per session | Minimal; `Get`/`Put` are materialized type classes |
431
+ | Effect system dependency | None (the shared API abstracts over `DbConnection`; no JDBC dependency in shared code) | Slick's `DBIO` monad | Cats `IO` or `Sync[F]` |
432
+
433
+ `Table` does not model relationships, joins, or query projection — those concerns belong to hand-written `sql"..."` fragments and the `Repo` type. When you need rich relational queries, compose `Frag` values manually rather than using a lifted embedding.
434
+
435
+ ### Hibernate JPA
436
+
437
+ `Table` and Hibernate address the same problem from opposite directions:
438
+
439
+ | Concern | `Table` (this module) | Hibernate / JPA |
440
+ |---------------------|------------------------------------------------------------------|-------------------------------------------------------------------------|
441
+ | Configuration style | Immutable value derived from `Schema[A]` at compile time | Annotations on mutable entity classes at runtime |
442
+ | Session lifecycle | None — connections managed explicitly by `Transactor` | `EntityManager`, `Session`, first-level cache, lazy proxies |
443
+ | Lazy loading | Not supported — all column values are loaded eagerly | Supported via proxy objects and byte-code instrumentation |
444
+ | SQL control | Full — every query is a `Frag` of literal SQL + typed parameters | Partial — JPQL / Criteria API abstracts SQL; native SQL as escape hatch |
445
+ | DDL generation | `Table#createTable` returns a `Frag`; you execute it explicitly | `hbm2ddl.auto` may run DDL automatically at startup |
446
+ | Scala compatibility | First-class; no mutable beans required | Requires JavaBean conventions (default constructor, mutable fields) |
447
+
448
+ `Table` never manages entity identity, caching, or lazy associations. It is a thin, transparent layer over JDBC — what you write in `sql"..."` is exactly what the database executes.