@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,146 @@
1
+ ---
2
+ id: db-result-reader
3
+ title: "DbResultReader"
4
+ description: "Reference for DbResultReader, the interface for reading typed column values from a SQL result set."
5
+ keywords:
6
+ - "DbResultReader Column Access"
7
+ - "Result Set Abstraction"
8
+ ---
9
+
10
+ `DbResultReader` is a trait for reading typed column values from a database result set. It provides methods to access columns by either their 1-based index (following JDBC convention) or by column label.
11
+
12
+ Application code does not use `DbResultReader` directly — the framework creates it automatically when a `Frag` is executed. It is the read-side counterpart to `DbParamWriter`, which writes values to prepared statements.
13
+
14
+ ## Core API
15
+
16
+ The core API reads numeric, text, binary, boolean, temporal, and UUID values, plus column metadata and NULL status. `getArray` also exists but defaults to throwing `UnsupportedOperationException` in the shared trait; only backends that override it (the JVM implementation does) actually support SQL array access:
17
+
18
+ ```scala
19
+ trait DbResultReader {
20
+ // Numeric types
21
+ def getInt(index: Int): Int
22
+ def getInt(label: String): Int
23
+ def getLong(index: Int): Long
24
+ def getLong(label: String): Long
25
+ def getDouble(index: Int): Double
26
+ def getDouble(label: String): Double
27
+ def getFloat(index: Int): Float
28
+ def getFloat(label: String): Float
29
+ def getShort(index: Int): Short
30
+ def getShort(label: String): Short
31
+ def getByte(index: Int): Byte
32
+ def getByte(label: String): Byte
33
+ def getBigDecimal(index: Int): java.math.BigDecimal
34
+ def getBigDecimal(label: String): java.math.BigDecimal
35
+
36
+ // Text and binary
37
+ def getString(index: Int): String
38
+ def getString(label: String): String
39
+ def getBytes(index: Int): Array[Byte]
40
+ def getBytes(label: String): Array[Byte]
41
+
42
+ // Boolean
43
+ def getBoolean(index: Int): Boolean
44
+ def getBoolean(label: String): Boolean
45
+
46
+ // Date and time
47
+ def getLocalDate(index: Int): java.time.LocalDate
48
+ def getLocalDate(label: String): java.time.LocalDate
49
+ def getLocalDateTime(index: Int): java.time.LocalDateTime
50
+ def getLocalDateTime(label: String): java.time.LocalDateTime
51
+ def getLocalTime(index: Int): java.time.LocalTime
52
+ def getLocalTime(label: String): java.time.LocalTime
53
+ def getInstant(index: Int): java.time.Instant
54
+ def getInstant(label: String): java.time.Instant
55
+ def getDuration(index: Int): java.time.Duration
56
+ def getDuration(label: String): java.time.Duration
57
+
58
+ // Other types
59
+ def getUUID(index: Int): java.util.UUID
60
+ def getUUID(label: String): java.util.UUID
61
+ def getArray(index: Int): java.sql.Array // default: throws UnsupportedOperationException
62
+ def getArray(label: String): java.sql.Array // default: throws UnsupportedOperationException
63
+
64
+ // Metadata and NULL detection
65
+ def columnLabel(index: Int): String
66
+ def hasColumn(label: String): Boolean
67
+ def wasNull: Boolean
68
+ }
69
+ ```
70
+
71
+ ## Usage
72
+
73
+ You access `DbResultReader` through `DbResultSet.reader` after calling `executeQuery` on a prepared statement:
74
+
75
+ ```scala
76
+ import zio.blocks.sql._
77
+
78
+ val transactor: Transactor = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
79
+
80
+ transactor.connect {
81
+ val con = summon[DbCon].connection
82
+ val stmt = con.prepareStatement("SELECT id, name, age FROM users")
83
+ val rs = stmt.executeQuery()
84
+
85
+ if (rs.next()) {
86
+ // Read by column label
87
+ val id: Long = rs.reader.getLong("id")
88
+ val name: String = rs.reader.getString("name")
89
+ val age: Int = rs.reader.getInt("age")
90
+ }
91
+
92
+ rs.close()
93
+ stmt.close()
94
+ }
95
+ ```
96
+
97
+ ## NULL Handling
98
+
99
+ Numeric and boolean getters return zero-like defaults (`0`, `0L`, `false`) for SQL `NULL`. String, decimal, and temporal getters return `null`. After any `get*` call, check `wasNull` to detect `NULL`:
100
+
101
+ ```scala
102
+ import zio.blocks.sql._
103
+
104
+ val transactor: Transactor = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
105
+
106
+ transactor.connect {
107
+ val con = summon[DbCon].connection
108
+ val stmt = con.prepareStatement("SELECT bio FROM users LIMIT 1")
109
+ val rs = stmt.executeQuery()
110
+
111
+ if (rs.next()) {
112
+ val bio: String = rs.reader.getString("bio")
113
+ val bioOption: Option[String] = if (rs.reader.wasNull) None else Some(bio)
114
+ }
115
+
116
+ rs.close()
117
+ stmt.close()
118
+ }
119
+ ```
120
+
121
+ :::caution
122
+ Always call `wasNull` immediately after the `get*` call whose NULL status you want to check. Do not call another `get*` method in between, or `wasNull` will reflect the wrong result.
123
+ :::
124
+
125
+ ## How It Works
126
+
127
+ When you use high-level operations like `Frag.query` or `Repo.find`, `DbCodec` internally calls `DbResultReader` methods to decode result rows. You only see this trait directly when dropping down to raw prepared statements for advanced use cases.
128
+
129
+ Label-based access (preferred) lets you ignore column order:
130
+
131
+ ```scala
132
+ import zio.blocks.sql._
133
+
134
+ val reader: DbResultReader = ???
135
+ val name = reader.getString("name") // Works regardless of SELECT order
136
+ ```
137
+
138
+ Index-based access (1-based per JDBC) is faster but requires stable column order:
139
+
140
+ ```scala
141
+ import zio.blocks.sql._
142
+
143
+ val reader: DbResultReader = ???
144
+ val name = reader.getString(2) // Assumes name is the 2nd column
145
+ ```
146
+
@@ -0,0 +1,82 @@
1
+ ---
2
+ id: db-tx
3
+ title: "DbTx"
4
+ description: "Reference for DbTx, the SQL module's transactional scope marker that extends DbCon with commit-on-success and rollback-on-failure semantics."
5
+ keywords:
6
+ - "DbTx Transaction Scope"
7
+ - "Transactor Transact Context"
8
+ - "DbCon Subtype Marker"
9
+ - "Auto-Commit Disabled Transaction"
10
+ - "Commit Rollback Semantics"
11
+ - "SQL Module Connection Context"
12
+ - "JDBC Transaction Lifecycle"
13
+ ---
14
+
15
+ `DbTx` is a marker trait in the `zio-blocks-sql` module that extends `DbCon` to signal a transactional execution scope. It declares no members of its own — its distinct type is what instructs `Transactor#transact` to disable auto-commit, commit the connection on success, and roll back on any thrown exception. You never construct a `DbTx` directly; the `Transactor` creates one and supplies it as a given context to the block passed to `transact`. The connection is always closed when the block exits, whether it commits, rolls back, or throws.
16
+
17
+ Key properties:
18
+ - **Transactional context marker** — A `DbTx` value in scope guarantees the underlying JDBC connection has auto-commit disabled.
19
+ - **Commit-on-success semantics** — The `Transactor` commits the connection when the `transact` block returns normally.
20
+ - **Rollback-on-failure semantics** — Any uncaught exception causes the `Transactor` to roll back the connection before re-throwing.
21
+
22
+ The structural declaration of `DbTx` is:
23
+
24
+ ```scala
25
+ trait DbTx extends DbCon
26
+ ```
27
+
28
+ Every context member that `DbTx` exposes is inherited from `DbCon`, which declares the three fields every SQL operation consumes:
29
+
30
+ ```scala
31
+ trait DbCon {
32
+ def connection: DbConnection
33
+ def dialect: SqlDialect
34
+ def logger: SqlLogger
35
+ }
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ The following example opens a transaction via `Transactor#transact`, accesses all three context members, and combines a `Repo` CRUD operation with a hand-written `Frag` query — both of which accept `DbTx` transparently in place of `DbCon`:
41
+
42
+ ```scala
43
+ import zio.blocks.sql._
44
+ import zio.blocks.schema.Schema
45
+
46
+ case class User(id: Int, name: String, email: String)
47
+ object User {
48
+ implicit val schema: Schema[User] = Schema.derived
49
+ }
50
+
51
+ val repo = Repo.derived[User, Int]("users", "id", _.id)
52
+ // repo: Repo[User, Int] = zio.blocks.sql.Repo$DerivedRepo@1b21124
53
+ val tx = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
54
+ // tx: JdbcTransactor = zio.blocks.sql.JdbcTransactor@1583ec4c
55
+
56
+ // On normal return: transaction commits and connection closes.
57
+ // On any exception: transaction rolls back, then the exception propagates.
58
+ tx.transact {
59
+ // All three context members are accessible via summon[DbTx]
60
+ val conn: DbConnection = summon[DbTx].connection // managed JDBC connection — do not close manually
61
+ val d: SqlDialect = summon[DbTx].dialect
62
+ val log: SqlLogger = summon[DbTx].logger
63
+
64
+ // Repo and Frag operations accept DbTx because DbTx extends DbCon
65
+ repo.table.createTable(summon[DbTx].dialect).update
66
+ repo.insert(User(1, "Alice", "alice@example.com"))
67
+ repo.insert(User(2, "Bob", "bob@example.com"))
68
+
69
+ val all: List[User] = repo.all
70
+ val custom: List[User] =
71
+ sql"SELECT id, name, email FROM users WHERE name LIKE ${"A%"}".query[User]
72
+
73
+ (all, custom)
74
+ }
75
+ // res1: Tuple2[List[User], List[User]] = (
76
+ // List(
77
+ // User(id = 1, name = "Alice", email = "alice@example.com"),
78
+ // User(id = 2, name = "Bob", email = "bob@example.com")
79
+ // ),
80
+ // List(User(id = 1, name = "Alice", email = "alice@example.com"))
81
+ // )
82
+ ```
@@ -0,0 +1,41 @@
1
+ ---
2
+ id: db-value
3
+ title: "DbValue"
4
+ description: "Reference for DbValue, the sealed ADT of typed SQL column values shared by Frag, DbCodec, and SqlDialect in the sql module."
5
+ keywords:
6
+ - "DbValue sealed ADT"
7
+ - "SQL column value types"
8
+ - "DbNull DbInt DbString"
9
+ - "typed database parameters"
10
+ - "SqlDialect typeName"
11
+ - "DbCodec toDbValues"
12
+ - "Frag params representation"
13
+ ---
14
+
15
+ `DbValue` is a sealed ADT representing every typed SQL column value the `sql` module handles, one `case class` per Scala type. `Frag` stores its bound parameters as `IndexedSeq[DbValue]`, `DbCodec#toDbValues` produces them, and `SqlDialect#typeName` maps a `DbValue` to its DDL type string — it is the common typed currency all three share, with no JDBC imports or dependencies of its own.
16
+
17
+ | Variant | Scala field type | PostgreSQL DDL | SQLite DDL |
18
+ |-------------------|----------------------------|---------------------|------------|
19
+ | `DbNull` | *(none)* | `NULL` | `NULL` |
20
+ | `DbInt` | `Int` | `INTEGER` | `INTEGER` |
21
+ | `DbLong` | `Long` | `BIGINT` | `INTEGER` |
22
+ | `DbDouble` | `Double` | `DOUBLE PRECISION` | `REAL` |
23
+ | `DbFloat` | `Float` | `REAL` | `REAL` |
24
+ | `DbBoolean` | `Boolean` | `BOOLEAN` | `INTEGER` |
25
+ | `DbString` | `String` | `TEXT` | `TEXT` |
26
+ | `DbBigDecimal` | `scala.BigDecimal` | `NUMERIC` | `TEXT` |
27
+ | `DbBytes` | `Array[Byte]` | `BYTEA` | `BLOB` |
28
+ | `DbShort` | `Short` | `SMALLINT` | `INTEGER` |
29
+ | `DbByte` | `Byte` | `SMALLINT` | `INTEGER` |
30
+ | `DbChar` | `Char` | `CHAR(1)` | `TEXT` |
31
+ | `DbLocalDate` | `java.time.LocalDate` | `DATE` | `TEXT` |
32
+ | `DbLocalDateTime` | `java.time.LocalDateTime` | `TIMESTAMP` | `TEXT` |
33
+ | `DbLocalTime` | `java.time.LocalTime` | `TIME` | `TEXT` |
34
+ | `DbInstant` | `java.time.Instant` | `TIMESTAMPTZ` | `TEXT` |
35
+ | `DbDuration` | `java.time.Duration` | `INTERVAL` | `TEXT` |
36
+ | `DbUUID` | `java.util.UUID` | `UUID` | `TEXT` |
37
+ | `DbArray` | `(String, IndexedSeq[Any])` | *(not in typeName)* | *(not in typeName)* |
38
+
39
+ `DbNull` is what `DbParam[Option[A]]` produces for `None` and `DbParam[Maybe[A]]` produces for `Maybe.absent`; `DbCodec` matches on it to decode nullable columns, throwing `IllegalStateException` if a non-optional codec hits it. `DbArray` is the one variant with two fields (`elementType`, `elements`) instead of `value`, used for collection-typed columns and not currently covered by `SqlDialect#typeName`.
40
+
41
+ See [Frag](./frag.md), [DbCodec](./db-codec.md), and the [SQL module index](./index.md).
@@ -0,0 +1,85 @@
1
+ ---
2
+ id: ddl
3
+ title: "Ddl"
4
+ description: "Reference for Ddl, the helper singleton that generates CREATE TABLE and DROP TABLE SQL fragments."
5
+ keywords:
6
+ - "Ddl createTable"
7
+ - "Ddl dropTable"
8
+ - "ColumnDef SQL type"
9
+ - "CREATE TABLE IF NOT EXISTS"
10
+ - "DROP TABLE IF EXISTS"
11
+ ---
12
+
13
+ `Ddl` is a helper object that generates DDL (Data Definition Language) SQL fragments for creating and dropping tables. `ColumnDef` is a simple data class pairing a column name with a SQL type string and a nullability flag.
14
+
15
+ Normally you don't call `Ddl` directly — `Table#createTable` and `Table#dropTable` use it internally. You may need `Ddl` directly when building custom DDL tooling.
16
+
17
+ ## Core API
18
+
19
+ ```scala
20
+ object Ddl {
21
+ def createTable(tableName: String, columns: IndexedSeq[ColumnDef]): Frag
22
+ def dropTable(tableName: String): Frag
23
+ }
24
+
25
+ final case class ColumnDef(name: String, sqlType: String, nullable: Boolean)
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ Create a `ColumnDef` for each column, then pass them to `Ddl.createTable`:
31
+
32
+ ```scala
33
+ import zio.blocks.sql._
34
+
35
+ val transactor: Transactor = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
36
+ // transactor: Transactor = zio.blocks.sql.JdbcTransactor@1bf2e7e
37
+
38
+ val columns = IndexedSeq(
39
+ ColumnDef("id", "INTEGER", nullable = false),
40
+ ColumnDef("name", "TEXT", nullable = false),
41
+ ColumnDef("created_at", "TEXT", nullable = true)
42
+ )
43
+ // columns: IndexedSeq[ColumnDef] = Vector(
44
+ // ColumnDef(name = "id", sqlType = "INTEGER", nullable = false),
45
+ // ColumnDef(name = "name", sqlType = "TEXT", nullable = false),
46
+ // ColumnDef(name = "created_at", sqlType = "TEXT", nullable = true)
47
+ // )
48
+
49
+ val createFrag = Ddl.createTable("users", columns)
50
+ // createFrag: Frag = Frag(
51
+ // parts = Vector(
52
+ // """CREATE TABLE IF NOT EXISTS users (
53
+ // id INTEGER NOT NULL,
54
+ // name TEXT NOT NULL,
55
+ // created_at TEXT
56
+ // )"""
57
+ // ),
58
+ // params = Vector()
59
+ // )
60
+ val dropFrag = Ddl.dropTable("users")
61
+ // dropFrag: Frag = Frag(
62
+ // parts = Vector("DROP TABLE IF EXISTS users"),
63
+ // params = Vector()
64
+ // )
65
+
66
+ // Execute the fragments
67
+ transactor.transact {
68
+ createFrag.update
69
+ // ... do work ...
70
+ dropFrag.update
71
+ }
72
+ // res1: Int = 0
73
+ ```
74
+
75
+ ## How It Works
76
+
77
+ `Ddl` is typically used by `Table#createTable`, which derives `ColumnDef` from schema metadata:
78
+
79
+ 1. `Table.derived[A]` builds a schema.
80
+ 2. `Table#createTable(dialect)` converts schema columns to `ColumnDef` using `dialect.typeName`.
81
+ 3. `Ddl.createTable` receives the `ColumnDef` list and generates the SQL fragment.
82
+
83
+ You call `Ddl` directly only when you need custom DDL that doesn't fit the `Table` abstraction.
84
+
85
+ See [Table](./table.md) for the high-level DDL API.
@@ -0,0 +1,254 @@
1
+ ---
2
+ id: frag
3
+ title: "Frag"
4
+ description: "Reference for Frag, the SQL fragment type with safe parameterization and JDBC execution."
5
+ keywords:
6
+ - "SQL Fragment"
7
+ - "Frag SQL Interpolator"
8
+ - "Type-safe SQL parameters"
9
+ - "SQL Injection Prevention"
10
+ ---
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`.
13
+
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
+
16
+ ## Core API
17
+
18
+ ```scala
19
+ final case class Frag(parts: IndexedSeq[String], params: IndexedSeq[DbValue]) {
20
+ def ++(other: Frag): Frag
21
+ def sql(dialect: SqlDialect): String
22
+ def queryParams: IndexedSeq[DbValue]
23
+ def isEmpty: Boolean
24
+ }
25
+
26
+ object Frag {
27
+ val empty: Frag
28
+ def literal(sqlStr: String): Frag
29
+ def sequence(frags: Frag*): Frag
30
+ def values[A](rows: Seq[A])(using codec: DbCodec[A]): Frag
31
+
32
+ extension (frag: Frag) {
33
+ def query[A](using DbCon, DbCodec[A]): List[A]
34
+ def queryOne[A](using DbCon, DbCodec[A]): Maybe[A]
35
+ def queryLimit[A](limit: Int)(using DbCon, DbCodec[A]): List[A]
36
+ def update(using DbCon): Int
37
+ def updateReturningKeys[A](using DbCon, DbCodec[A]): List[A]
38
+ }
39
+ }
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ Build fragments with the `sql"..."` interpolator and execute them inside a `Transactor` block:
45
+
46
+ ```scala
47
+ import zio.blocks.sql._
48
+ import zio.blocks.schema.Schema
49
+ import zio.blocks.maybe.Maybe
50
+
51
+ case class User(id: Int, name: String, email: String)
52
+ object User { implicit val schema: Schema[User] = Schema.derived }
53
+
54
+ val tx: Transactor = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
55
+
56
+ val userId = 42
57
+ val active = true
58
+
59
+ tx.connect {
60
+ // Query — returns all matching rows
61
+ val users: List[User] =
62
+ sql"SELECT id, name, email FROM users WHERE id > $userId AND active = $active".query[User]
63
+
64
+ // QueryOne — returns at most one row
65
+ val one: Maybe[User] =
66
+ sql"SELECT id, name, email FROM users WHERE id = ${1}".queryOne[User]
67
+
68
+ // Update — returns affected row count
69
+ val deleted: Int =
70
+ sql"DELETE FROM users WHERE active = ${false}".update
71
+
72
+ // UpdateReturningKeys — returns generated keys after INSERT
73
+ val keys: List[Long] =
74
+ sql"INSERT INTO users (name, email) VALUES (${"Alice"}, ${"alice@example.com"})".updateReturningKeys[Long]
75
+ }
76
+ ```
77
+
78
+ ## Building Fragments
79
+
80
+ **`sql"..."` interpolator** — The primary way to build fragments. Compile-time checked: every interpolated value must have a `DbParam` instance (provided for all common Scala and Java types).
81
+
82
+ ```scala
83
+ import zio.blocks.sql._
84
+
85
+ val userId = 42
86
+ // userId: Int = 42
87
+ val frag = sql"SELECT * FROM users WHERE id = $userId"
88
+ // frag: Frag = Frag(
89
+ // parts = ArraySeq("SELECT * FROM users WHERE id = ", ""),
90
+ // params = Vector(DbInt(42))
91
+ // )
92
+ frag.params
93
+ // res2: IndexedSeq[DbValue] = Vector(DbInt(42))
94
+ ```
95
+
96
+ **`Frag.literal`** — Wrap static SQL text (no parameters):
97
+
98
+ ```scala
99
+ val query = sql"SELECT * FROM users" ++ Frag.literal(" ORDER BY name")
100
+ // query: Frag = Frag(
101
+ // parts = ArraySeq("SELECT * FROM users ORDER BY name"),
102
+ // params = Vector()
103
+ // )
104
+ query.sql(SqlDialect.SQLite)
105
+ // res3: String = "SELECT * FROM users ORDER BY name"
106
+ ```
107
+
108
+ **`Frag.values`** — Build a multi-row INSERT VALUES clause:
109
+
110
+ ```scala
111
+ import zio.blocks.sql._
112
+
113
+ case class Product(name: String, price: BigDecimal) derives DbCodec
114
+
115
+ val products = List(Product("Widget", BigDecimal("9.99")), Product("Gadget", BigDecimal("24.99")))
116
+ // products: List[Product] = List(
117
+ // Product(name = "Widget", price = 9.99),
118
+ // Product(name = "Gadget", price = 24.99)
119
+ // )
120
+ val insert = Frag.literal("INSERT INTO product (name, price) VALUES ") ++ Frag.values(products)
121
+ // insert: Frag = Frag(
122
+ // parts = Vector(
123
+ // "INSERT INTO product (name, price) VALUES (",
124
+ // ", ",
125
+ // "), (",
126
+ // ", ",
127
+ // ")"
128
+ // ),
129
+ // params = Vector(
130
+ // DbString("Widget"),
131
+ // DbBigDecimal(9.99),
132
+ // DbString("Gadget"),
133
+ // DbBigDecimal(24.99)
134
+ // )
135
+ // )
136
+ insert.sql(SqlDialect.SQLite)
137
+ // res5: String = "INSERT INTO product (name, price) VALUES (?, ?), (?, ?)"
138
+ ```
139
+
140
+ **`Frag.empty`** — The identity fragment for composition. Useful for optional clauses:
141
+
142
+ ```scala
143
+ import zio.blocks.sql._
144
+
145
+ val hasFilter = true
146
+ // hasFilter: Boolean = true
147
+ val where = if (hasFilter) sql" WHERE active = ${true}" else Frag.empty
148
+ // where: Frag = Frag(
149
+ // parts = ArraySeq(" WHERE active = ", ""),
150
+ // params = Vector(DbBoolean(true))
151
+ // )
152
+ val query = sql"SELECT * FROM users" ++ where
153
+ // query: Frag = Frag(
154
+ // parts = ArraySeq("SELECT * FROM users WHERE active = ", ""),
155
+ // params = Vector(DbBoolean(true))
156
+ // )
157
+ query.sql(SqlDialect.SQLite)
158
+ // res7: String = "SELECT * FROM users WHERE active = ?"
159
+ ```
160
+
161
+ **`Frag.sequence`** — Concatenate multiple fragments with no separator:
162
+
163
+ ```scala
164
+ import zio.blocks.sql._
165
+
166
+ val status = "active"
167
+ // status: String = "active"
168
+ val base = sql"SELECT * FROM users"
169
+ // base: Frag = Frag(
170
+ // parts = ArraySeq("SELECT * FROM users"),
171
+ // params = Vector()
172
+ // )
173
+ val where = sql" WHERE status = $status"
174
+ // where: Frag = Frag(
175
+ // parts = ArraySeq(" WHERE status = ", ""),
176
+ // params = Vector(DbString("active"))
177
+ // )
178
+ val order = Frag.literal(" ORDER BY name")
179
+ // order: Frag = Frag(parts = Vector(" ORDER BY name"), params = Vector())
180
+ val full = Frag.sequence(base, where, order)
181
+ // full: Frag = Frag(
182
+ // parts = Vector("SELECT * FROM users WHERE status = ", " ORDER BY name"),
183
+ // params = Vector(DbString("active"))
184
+ // )
185
+ full.sql(SqlDialect.SQLite)
186
+ // res9: String = "SELECT * FROM users WHERE status = ? ORDER BY name"
187
+ ```
188
+
189
+ ## Execution Methods
190
+
191
+ All execution methods require an implicit `DbCon` (provided by `Transactor#connect` or `Transactor#transact`). They render the fragment to SQL, bind parameters, execute, and log the operation.
192
+
193
+ **`query[A]`** — Execute SELECT and return all rows:
194
+
195
+ ```scala
196
+ import zio.blocks.sql._
197
+ import zio.blocks.schema.Schema
198
+
199
+ case class User(id: Int, name: String)
200
+ object User { implicit val schema: Schema[User] = Schema.derived }
201
+
202
+ given DbCon = ???
203
+
204
+ val users: List[User] = sql"SELECT id, name FROM users".query[User]
205
+ ```
206
+
207
+ **`queryOne[A]`** — Execute SELECT and return at most one row:
208
+
209
+ ```scala
210
+ import zio.blocks.sql._
211
+ import zio.blocks.schema.Schema
212
+ import zio.blocks.maybe.Maybe
213
+
214
+ case class User(id: Int, name: String)
215
+ object User { implicit val schema: Schema[User] = Schema.derived }
216
+
217
+ given DbCon = ???
218
+
219
+ val user: Maybe[User] = sql"SELECT id, name FROM users WHERE id = ${1}".queryOne[User]
220
+ ```
221
+
222
+ **`queryLimit[A](n)`** — Execute SELECT and return up to n rows (fetched from Scala side):
223
+
224
+ ```scala
225
+ import zio.blocks.sql._
226
+ import zio.blocks.schema.Schema
227
+
228
+ case class User(id: Int, name: String)
229
+ object User { implicit val schema: Schema[User] = Schema.derived }
230
+
231
+ given DbCon = ???
232
+
233
+ val page: List[User] = sql"SELECT id, name FROM users ORDER BY name".queryLimit[User](10)
234
+ ```
235
+
236
+ **`update`** — Execute INSERT, UPDATE, or DELETE and return affected row count:
237
+
238
+ ```scala
239
+ import zio.blocks.sql._
240
+
241
+ given DbCon = ???
242
+
243
+ val deleted: Int = sql"DELETE FROM users WHERE inactive = ${true}".update
244
+ ```
245
+
246
+ **`updateReturningKeys[A]`** — Execute INSERT and return auto-generated primary key(s):
247
+
248
+ ```scala
249
+ import zio.blocks.sql._
250
+
251
+ given DbCon = ???
252
+
253
+ val keys: List[Long] = sql"INSERT INTO users (name) VALUES (${"Alice"})".updateReturningKeys[Long]
254
+ ```