@zio.dev/zio-blocks 0.0.51 → 0.0.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adr/2026-07-18-data-migration.md +123 -0
- package/guides/async-getting-started.md +687 -0
- package/guides/compile-time-resource-safety-with-scope.md +6 -0
- package/guides/getting-started-with-mux.md +0 -112
- package/guides/query-dsl-extending.md +1 -1
- package/guides/query-dsl-fluent-builder.md +1 -1
- package/guides/query-dsl-reified-optics.md +1 -1
- package/guides/query-dsl-sql.md +395 -1
- package/guides/sql-checked-interpolation.md +173 -0
- package/guides/sql-transactions.md +286 -0
- package/guides/telemetry-guide.md +131 -70
- package/guides/zio-schema-migration.md +6 -6
- package/index.md +200 -559
- package/package.json +1 -1
- package/reference/async.md +1379 -531
- package/reference/chunk.md +3 -3
- package/reference/codegen/index.md +1 -1
- package/reference/combinators.md +4 -4
- package/reference/config/config-decoder.md +460 -0
- package/reference/config/config-source.md +489 -0
- package/reference/config/errors.md +278 -0
- package/reference/config/flags.md +369 -0
- package/reference/config/formats.md +314 -0
- package/reference/config/index.md +304 -0
- package/reference/config/rollout.md +336 -0
- package/reference/context.md +6 -49
- package/reference/data-migration.md +269 -0
- package/reference/datastar/attributes.md +302 -0
- package/reference/datastar/events.md +234 -0
- package/reference/datastar/index.md +256 -0
- package/reference/datastar/signals.md +230 -0
- package/reference/datastar/sse.md +295 -0
- package/reference/datastar.md +2 -2
- package/reference/docs.md +2 -2
- package/reference/endpoint/bulk-creation.md +96 -0
- package/reference/endpoint/endpoint.md +1 -0
- package/reference/endpoint/index.md +9 -89
- package/reference/endpoint/path-codec.md +12 -24
- package/reference/endpoint/route-pattern.md +4 -6
- package/reference/endpoint/segment-codec.md +19 -32
- package/reference/html.md +313 -9
- package/reference/htmx/index.md +4 -52
- package/reference/htmx/response-headers.md +240 -0
- package/reference/http-model/headers.md +735 -0
- package/reference/http-model/index.md +3 -1
- package/reference/http-model/model.md +107 -71
- package/reference/http-model/schema-codecs.md +522 -0
- package/reference/http-model/schema.md +6 -3
- package/reference/http-model/server-sent-event.md +341 -0
- package/reference/jwt.md +195 -0
- package/reference/maybe.md +128 -11
- package/reference/media-type.md +2 -2
- package/reference/mux.mdx +7 -2
- package/reference/openapi.md +3 -3
- package/reference/projection.md +654 -0
- package/reference/resource-management/index.md +1 -1
- package/reference/resource-management/resource.md +2 -98
- package/reference/resource-management/scope.md +1 -209
- package/reference/resource-management/wire.md +4 -50
- package/reference/ringbuffer/advanced.mdx +1 -1
- package/reference/ringbuffer/index.mdx +3 -3
- package/reference/ringbuffer/mpmc.mdx +38 -4
- package/reference/ringbuffer/mpsc.mdx +36 -4
- package/reference/ringbuffer/spmc.mdx +1 -1
- package/reference/ringbuffer/spsc.mdx +87 -15
- package/reference/schema/allows.md +0 -96
- package/reference/schema/binding.md +2 -2
- package/reference/schema/built-in-codecs/avro.md +2 -2
- package/reference/schema/built-in-codecs/bson.md +50 -20
- package/reference/schema/built-in-codecs/csv.md +2 -2
- package/reference/schema/built-in-codecs/index.md +3 -3
- package/reference/schema/built-in-codecs/json/index.md +2 -2
- package/reference/schema/built-in-codecs/json/json.md +1 -0
- package/reference/schema/built-in-codecs/messagepack.md +3 -3
- package/reference/schema/built-in-codecs/thrift.md +2 -2
- package/reference/schema/built-in-codecs/toon.md +3 -3
- package/reference/schema/built-in-codecs/yaml.md +2 -2
- package/reference/schema/codec.md +11 -11
- package/reference/schema/dynamic-optic.md +48 -3
- package/reference/schema/dynamic-schema.md +3 -3
- package/reference/schema/index.md +2 -0
- package/reference/schema/path-interpolator.md +2 -0
- package/reference/schema/reflect-transformer.md +140 -0
- package/reference/schema/schema-evolution/as.md +4 -4
- package/reference/schema/schema-evolution/into.md +2 -2
- package/reference/schema/schema-expr.md +2 -2
- package/reference/schema/schema-search.md +263 -0
- package/reference/schema/schema.md +10 -2
- package/reference/schema/type-class-derivation.md +1 -1
- package/reference/smithy.md +502 -3
- package/reference/sql/db-codec-deriver.md +3 -3
- package/reference/sql/db-codec.md +22 -22
- package/reference/sql/db-con.md +4 -4
- package/reference/sql/db-connection.md +1 -1
- package/reference/sql/db-param.md +1 -1
- package/reference/sql/db-result-reader.md +4 -2
- package/reference/sql/db-tx.md +46 -14
- package/reference/sql/ddl.md +1 -1
- package/reference/sql/frag.md +44 -10
- package/reference/sql/index.md +7 -7
- package/reference/sql/repo.md +15 -15
- package/reference/sql/sql-dialect.md +1 -1
- package/reference/sql/sql-logger.md +1 -1
- package/reference/sql/sql-name-mapper.md +3 -3
- package/reference/sql/table-metadata.md +3 -3
- package/reference/sql/table.md +10 -10
- package/reference/sql/transactor-zio.md +1 -1
- package/reference/sql/transactor.md +21 -11
- package/reference/sql-zio.md +2 -2
- package/reference/streams/core/index.md +32 -0
- package/reference/streams/{pipeline.md → core/pipeline.md} +210 -74
- package/reference/streams/{sink.md → core/sink.md} +331 -353
- package/reference/streams/{stream.md → core/stream.md} +919 -209
- package/reference/streams/execution-and-compatibility/async-execution.md +822 -0
- package/reference/streams/execution-and-compatibility/index.md +35 -0
- package/reference/streams/execution-and-compatibility/platform-differences.md +297 -0
- package/reference/streams/execution-and-compatibility/scala-2-compatibility.md +88 -0
- package/reference/streams/execution-and-compatibility/zero-boxing.md +393 -0
- package/reference/streams/index.md +140 -67
- package/reference/streams/primitives/index.md +30 -0
- package/reference/streams/primitives/reader.md +1992 -0
- package/reference/streams/{writer.md → primitives/writer.md} +254 -98
- package/reference/telemetry/common/any-value.md +90 -0
- package/reference/telemetry/common/attribute-key.md +87 -0
- package/reference/telemetry/common/attributes.md +118 -0
- package/reference/telemetry/common/index.md +39 -0
- package/reference/telemetry/common/instrumentation-scope.md +24 -0
- package/reference/telemetry/common/resource.md +34 -0
- package/reference/telemetry/index.md +311 -0
- package/reference/telemetry/logging/index.md +197 -0
- package/reference/telemetry/logging/log-enrichment.md +72 -0
- package/reference/telemetry/logging/log-formatter.md +100 -0
- package/reference/telemetry/logging/log-record-processor.md +56 -0
- package/reference/telemetry/logging/log-record.md +44 -0
- package/reference/telemetry/logging/log-writer.md +64 -0
- package/reference/telemetry/logging/logger-provider.md +142 -0
- package/reference/telemetry/logging/logger.md +83 -0
- package/reference/telemetry/logging/severity.md +62 -0
- package/reference/telemetry/metrics/index.md +150 -0
- package/reference/telemetry/metrics/instruments.md +183 -0
- package/reference/telemetry/metrics/labeled-instruments.md +74 -0
- package/reference/telemetry/metrics/meter-provider.md +76 -0
- package/reference/telemetry/metrics/meter.md +98 -0
- package/reference/telemetry/metrics/metric-data.md +57 -0
- package/reference/telemetry/otel/custom-exporter.md +216 -0
- package/reference/telemetry/otel/index.md +212 -0
- package/reference/telemetry/tracing/index.md +155 -0
- package/reference/telemetry/tracing/sampler.md +89 -0
- package/reference/telemetry/tracing/span-builder.md +57 -0
- package/reference/telemetry/tracing/span-context.md +39 -0
- package/reference/telemetry/tracing/span-data.md +32 -0
- package/reference/telemetry/tracing/span-kind.md +55 -0
- package/reference/telemetry/tracing/span-processor.md +53 -0
- package/reference/telemetry/tracing/span-status.md +47 -0
- package/reference/telemetry/tracing/span.md +117 -0
- package/reference/telemetry/tracing/tracer-provider.md +91 -0
- package/reference/telemetry/tracing/tracer.md +52 -0
- package/reference/typeid.md +0 -64
- package/sidebars.js +365 -185
- package/undocumented-report.md +528 -270
- package/reference/config.md +0 -158
- package/reference/streams/concurrent-operators.md +0 -106
- package/reference/streams/reader.md +0 -1284
- package/reference/streams/scala-2-compatibility.md +0 -55
- package/reference/streams/zero-boxing.md +0 -275
- package/reference/telemetry.md +0 -693
|
@@ -634,22 +634,6 @@ This example creates a mux and opens your first stream, demonstrating the basic
|
|
|
634
634
|
<summary>mux-examples/src/main/scala/mux/Example1CreatingAMux.scala</summary>
|
|
635
635
|
|
|
636
636
|
```scala title="mux-examples/src/main/scala/mux/Example1CreatingAMux.scala" showLineNumbers
|
|
637
|
-
/*
|
|
638
|
-
* Copyright 2024-2026 John A. De Goes and the ZIO Contributors
|
|
639
|
-
*
|
|
640
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
641
|
-
* you may not use this file except in compliance with the License.
|
|
642
|
-
* You may obtain a copy of the License at
|
|
643
|
-
*
|
|
644
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
645
|
-
*
|
|
646
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
647
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
648
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
649
|
-
* See the License for the specific language governing permissions and
|
|
650
|
-
* limitations under the License.
|
|
651
|
-
*/
|
|
652
|
-
|
|
653
637
|
package mux
|
|
654
638
|
|
|
655
639
|
import zio.blocks.mux._
|
|
@@ -698,22 +682,6 @@ This example demonstrates the two-perspective communication model where applicat
|
|
|
698
682
|
<summary>mux-examples/src/main/scala/mux/Example2UnderstandingStreamsAndMessageQueues.scala</summary>
|
|
699
683
|
|
|
700
684
|
```scala title="mux-examples/src/main/scala/mux/Example2UnderstandingStreamsAndMessageQueues.scala" showLineNumbers
|
|
701
|
-
/*
|
|
702
|
-
* Copyright 2024-2026 John A. De Goes and the ZIO Contributors
|
|
703
|
-
*
|
|
704
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
705
|
-
* you may not use this file except in compliance with the License.
|
|
706
|
-
* You may obtain a copy of the License at
|
|
707
|
-
*
|
|
708
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
709
|
-
*
|
|
710
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
711
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
712
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
713
|
-
* See the License for the specific language governing permissions and
|
|
714
|
-
* limitations under the License.
|
|
715
|
-
*/
|
|
716
|
-
|
|
717
685
|
package mux
|
|
718
686
|
|
|
719
687
|
import zio.blocks.mux._
|
|
@@ -791,22 +759,6 @@ This example shows all three ways to close a stream: graceful two-phase closure
|
|
|
791
759
|
<summary>mux-examples/src/main/scala/mux/Example3StreamLifecycle.scala</summary>
|
|
792
760
|
|
|
793
761
|
```scala title="mux-examples/src/main/scala/mux/Example3StreamLifecycle.scala" showLineNumbers
|
|
794
|
-
/*
|
|
795
|
-
* Copyright 2024-2026 John A. De Goes and the ZIO Contributors
|
|
796
|
-
*
|
|
797
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
798
|
-
* you may not use this file except in compliance with the License.
|
|
799
|
-
* You may obtain a copy of the License at
|
|
800
|
-
*
|
|
801
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
802
|
-
*
|
|
803
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
804
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
805
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
806
|
-
* See the License for the specific language governing permissions and
|
|
807
|
-
* limitations under the License.
|
|
808
|
-
*/
|
|
809
|
-
|
|
810
762
|
package mux
|
|
811
763
|
|
|
812
764
|
import zio.blocks.mux._
|
|
@@ -904,22 +856,6 @@ This example opens three independent streams and exchanges messages on each one,
|
|
|
904
856
|
<summary>mux-examples/src/main/scala/mux/Example4WorkingWithMultipleStreams.scala</summary>
|
|
905
857
|
|
|
906
858
|
```scala title="mux-examples/src/main/scala/mux/Example4WorkingWithMultipleStreams.scala" showLineNumbers
|
|
907
|
-
/*
|
|
908
|
-
* Copyright 2024-2026 John A. De Goes and the ZIO Contributors
|
|
909
|
-
*
|
|
910
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
911
|
-
* you may not use this file except in compliance with the License.
|
|
912
|
-
* You may obtain a copy of the License at
|
|
913
|
-
*
|
|
914
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
915
|
-
*
|
|
916
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
917
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
918
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
919
|
-
* See the License for the specific language governing permissions and
|
|
920
|
-
* limitations under the License.
|
|
921
|
-
*/
|
|
922
|
-
|
|
923
859
|
package mux
|
|
924
860
|
|
|
925
861
|
import zio.blocks.mux._
|
|
@@ -1037,22 +973,6 @@ This example demonstrates both mux-level capacity (controlling concurrent stream
|
|
|
1037
973
|
<summary>mux-examples/src/main/scala/mux/Example5ManagingCapacity.scala</summary>
|
|
1038
974
|
|
|
1039
975
|
```scala title="mux-examples/src/main/scala/mux/Example5ManagingCapacity.scala" showLineNumbers
|
|
1040
|
-
/*
|
|
1041
|
-
* Copyright 2024-2026 John A. De Goes and the ZIO Contributors
|
|
1042
|
-
*
|
|
1043
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1044
|
-
* you may not use this file except in compliance with the License.
|
|
1045
|
-
* You may obtain a copy of the License at
|
|
1046
|
-
*
|
|
1047
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1048
|
-
*
|
|
1049
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
1050
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1051
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1052
|
-
* See the License for the specific language governing permissions and
|
|
1053
|
-
* limitations under the License.
|
|
1054
|
-
*/
|
|
1055
|
-
|
|
1056
976
|
package mux
|
|
1057
977
|
|
|
1058
978
|
import zio.blocks.mux._
|
|
@@ -1172,22 +1092,6 @@ This example documents which operations are thread-safe (mux-level and send/offe
|
|
|
1172
1092
|
<summary>mux-examples/src/main/scala/mux/Example6ThreadSafety.scala</summary>
|
|
1173
1093
|
|
|
1174
1094
|
```scala title="mux-examples/src/main/scala/mux/Example6ThreadSafety.scala"
|
|
1175
|
-
/*
|
|
1176
|
-
* Copyright 2024-2026 John A. De Goes and the ZIO Contributors
|
|
1177
|
-
*
|
|
1178
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1179
|
-
* you may not use this file except in compliance with the License.
|
|
1180
|
-
* You may obtain a copy of the License at
|
|
1181
|
-
*
|
|
1182
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1183
|
-
*
|
|
1184
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
1185
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1186
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1187
|
-
* See the License for the specific language governing permissions and
|
|
1188
|
-
* limitations under the License.
|
|
1189
|
-
*/
|
|
1190
|
-
|
|
1191
1095
|
package mux
|
|
1192
1096
|
|
|
1193
1097
|
import zio.blocks.mux._
|
|
@@ -1310,22 +1214,6 @@ This comprehensive example demonstrates a complete request-response system that
|
|
|
1310
1214
|
<summary>mux-examples/src/main/scala/mux/CompleteExample.scala</summary>
|
|
1311
1215
|
|
|
1312
1216
|
```scala title="mux-examples/src/main/scala/mux/CompleteExample.scala" showLineNumbers
|
|
1313
|
-
/*
|
|
1314
|
-
* Copyright 2024-2026 John A. De Goes and the ZIO Contributors
|
|
1315
|
-
*
|
|
1316
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1317
|
-
* you may not use this file except in compliance with the License.
|
|
1318
|
-
* You may obtain a copy of the License at
|
|
1319
|
-
*
|
|
1320
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1321
|
-
*
|
|
1322
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
1323
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1324
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1325
|
-
* See the License for the specific language governing permissions and
|
|
1326
|
-
* limitations under the License.
|
|
1327
|
-
*/
|
|
1328
|
-
|
|
1329
1217
|
package mux
|
|
1330
1218
|
|
|
1331
1219
|
import zio.blocks.mux._
|
|
@@ -52,7 +52,7 @@ None of these can be expressed with `SchemaExpr` alone. You could generate the S
|
|
|
52
52
|
This guide builds on [Part 1: Expressions](./query-dsl-reified-optics.md) and [Part 2: SQL Generation](./query-dsl-sql.md). You should be comfortable building `SchemaExpr` values and translating them to SQL.
|
|
53
53
|
|
|
54
54
|
```scala
|
|
55
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.
|
|
55
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.56"
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
```scala
|
|
@@ -57,7 +57,7 @@ In this guide, we solve both problems: bridge extensions eliminate `.toExpr`, an
|
|
|
57
57
|
This guide builds on [Part 1: Expressions](./query-dsl-reified-optics.md), [Part 2: SQL Generation](./query-dsl-sql.md), and [Part 3: Extending the Expression Language](./query-dsl-extending.md).
|
|
58
58
|
|
|
59
59
|
```scala
|
|
60
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.
|
|
60
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.56"
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
## Domain Setup
|
|
@@ -43,7 +43,7 @@ In this guide, we'll solve this by using ZIO Blocks' `SchemaExpr` and reified op
|
|
|
43
43
|
Add the ZIO Blocks Schema dependency:
|
|
44
44
|
|
|
45
45
|
```scala
|
|
46
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.
|
|
46
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.56"
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
```scala
|
package/guides/query-dsl-sql.md
CHANGED
|
@@ -43,7 +43,7 @@ This is fragile, repetitive, and vulnerable to SQL injection. Every new query sh
|
|
|
43
43
|
This guide builds on [Part 1: Expressions](./query-dsl-reified-optics.md). You should be comfortable building `SchemaExpr` values with optic operators (`===`, `>`, `&&`, etc.).
|
|
44
44
|
|
|
45
45
|
```scala
|
|
46
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.
|
|
46
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.56"
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
```scala
|
|
@@ -749,6 +749,400 @@ println(toSql(Product.price * 0.9))
|
|
|
749
749
|
// (price * 0.9)
|
|
750
750
|
```
|
|
751
751
|
|
|
752
|
+
## Upsert (ON CONFLICT)
|
|
753
|
+
|
|
754
|
+
Upsert (insert-or-update) combines an `INSERT` with a conflict handler so the
|
|
755
|
+
statement is idempotent. When a row with the conflicting key already exists, the
|
|
756
|
+
database either skips the insert or updates specified columns.
|
|
757
|
+
|
|
758
|
+
All identifiers (table name, column names, conflict column) are validated through
|
|
759
|
+
`SqlIdentifier.validate` and assignment columns are additionally checked against
|
|
760
|
+
`Table.columns`. Invalid or unknown names throw `IllegalArgumentException` at
|
|
761
|
+
build time, not at execution time.
|
|
762
|
+
|
|
763
|
+
### Table-aware builders
|
|
764
|
+
|
|
765
|
+
The high-level `Upsert` builders accept a `Table[A]` and an entity. The table
|
|
766
|
+
provides column names and a codec that extracts `DbValue` parameters.
|
|
767
|
+
|
|
768
|
+
`Upsert.insertDoNothing` builds `INSERT ... ON CONFLICT ("id") DO NOTHING`:
|
|
769
|
+
|
|
770
|
+
```scala
|
|
771
|
+
import zio.blocks.sql.*
|
|
772
|
+
import zio.blocks.schema.Schema
|
|
773
|
+
|
|
774
|
+
case class User(id: Int, name: String, email: String)
|
|
775
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
776
|
+
|
|
777
|
+
val table: Table[User] = Table.derived[User]
|
|
778
|
+
val user = User(42, "Alice", "alice@example.com")
|
|
779
|
+
|
|
780
|
+
// INSERT INTO "user" ("id", "name", "email") VALUES (?, ?, ?) ON CONFLICT ("id") DO NOTHING
|
|
781
|
+
val frag: Frag = Upsert.insertDoNothing(table, user, conflictColumn = "id")
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
`Upsert.insertDoUpdate` builds `INSERT ... ON CONFLICT ("id") DO UPDATE SET`
|
|
785
|
+
for **all** non-conflict columns:
|
|
786
|
+
|
|
787
|
+
```scala
|
|
788
|
+
import zio.blocks.sql.*
|
|
789
|
+
import zio.blocks.schema.Schema
|
|
790
|
+
|
|
791
|
+
case class User(id: Int, name: String, email: String)
|
|
792
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
793
|
+
|
|
794
|
+
val table: Table[User] = Table.derived[User]
|
|
795
|
+
val user = User(42, "Alice", "alice@example.com")
|
|
796
|
+
|
|
797
|
+
// INSERT INTO "user" ("id", "name", "email") VALUES (?, ?, ?)
|
|
798
|
+
// ON CONFLICT ("id") DO UPDATE SET "name" = ?, "email" = ?
|
|
799
|
+
val frag: Frag = Upsert.insertDoUpdate(table, user, conflictColumn = "id")
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
Pass `updateColumns` to restrict which columns are overwritten on conflict:
|
|
803
|
+
|
|
804
|
+
```scala
|
|
805
|
+
import zio.blocks.sql.*
|
|
806
|
+
import zio.blocks.schema.Schema
|
|
807
|
+
|
|
808
|
+
case class User(id: Int, name: String, email: String)
|
|
809
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
810
|
+
|
|
811
|
+
val table: Table[User] = Table.derived[User]
|
|
812
|
+
val user = User(42, "Alice", "alice@example.com")
|
|
813
|
+
|
|
814
|
+
// Only "name" is updated on conflict; "email" keeps its original value
|
|
815
|
+
val frag: Frag = Upsert.insertDoUpdate(table, user, conflictColumn = "id", updateColumns = Seq("name"))
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
### Low-level builders
|
|
819
|
+
|
|
820
|
+
When you need full control over column names and values (e.g. computed or
|
|
821
|
+
transformed data), use the low-level `Upsert.doNothing`, `Upsert.doNothingRaw`,
|
|
822
|
+
and `Upsert.doUpdate` builders:
|
|
823
|
+
|
|
824
|
+
```scala
|
|
825
|
+
import zio.blocks.sql.*
|
|
826
|
+
|
|
827
|
+
// Low-level DO NOTHING with explicit columns and values
|
|
828
|
+
val frag1: Frag = Upsert.doNothing(
|
|
829
|
+
tableName = "users",
|
|
830
|
+
columns = IndexedSeq("id", "name", "email"),
|
|
831
|
+
values = IndexedSeq(DbValue.DbInt(1), DbValue.DbString("Bob"), DbValue.DbString("bob@example.com")),
|
|
832
|
+
conflictColumn = "id"
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
// Comma-joined column string variant
|
|
836
|
+
val frag2: Frag = Upsert.doNothingRaw(
|
|
837
|
+
tableName = "users",
|
|
838
|
+
allColumns = "id, name, email",
|
|
839
|
+
values = IndexedSeq(DbValue.DbInt(1), DbValue.DbString("Bob"), DbValue.DbString("bob@example.com")),
|
|
840
|
+
conflictColumn = "id"
|
|
841
|
+
)
|
|
842
|
+
|
|
843
|
+
// Low-level DO UPDATE with explicit assignments
|
|
844
|
+
val frag3: Frag = Upsert.doUpdate(
|
|
845
|
+
tableName = "users",
|
|
846
|
+
columns = IndexedSeq("id", "name", "email"),
|
|
847
|
+
values = IndexedSeq(DbValue.DbInt(1), DbValue.DbString("Bob"), DbValue.DbString("bob@example.com")),
|
|
848
|
+
conflictColumn = "id",
|
|
849
|
+
assignments = IndexedSeq("name" -> DbValue.DbString("Bob"), "email" -> DbValue.DbString("bob@example.com"))
|
|
850
|
+
)
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
### Suffix builders
|
|
854
|
+
|
|
855
|
+
To append an `ON CONFLICT` clause to an existing `INSERT` `Frag`, use the suffix
|
|
856
|
+
builders:
|
|
857
|
+
|
|
858
|
+
```scala
|
|
859
|
+
import zio.blocks.sql.*
|
|
860
|
+
|
|
861
|
+
val base: Frag = Frag.literal("INSERT INTO users (id, name) VALUES (?, ?)")
|
|
862
|
+
|
|
863
|
+
// Append DO NOTHING suffix
|
|
864
|
+
val withNothing: Frag = base ++ Upsert.doNothingSuffix(conflictColumn = "id")
|
|
865
|
+
|
|
866
|
+
// Append DO UPDATE suffix with explicit assignments
|
|
867
|
+
val withUpdate: Frag = base ++ Upsert.doUpdateSuffix(
|
|
868
|
+
conflictColumn = "id",
|
|
869
|
+
assignments = IndexedSeq("name" -> DbValue.DbString("updated"))
|
|
870
|
+
)
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
### Repository integration
|
|
874
|
+
|
|
875
|
+
`Repo` provides `insertOrUpdate` and `insertOrUpdateBatch` as convenience
|
|
876
|
+
wrappers that use `Upsert.insertDoUpdate` under the hood. The conflict target is
|
|
877
|
+
the repository's validated ID column, and all non-ID columns are overwritten with
|
|
878
|
+
the entity's values.
|
|
879
|
+
|
|
880
|
+
```scala
|
|
881
|
+
import zio.blocks.sql.*
|
|
882
|
+
import zio.blocks.schema.Schema
|
|
883
|
+
|
|
884
|
+
case class User(id: Int, name: String, email: String)
|
|
885
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
886
|
+
|
|
887
|
+
given DbCon = ???
|
|
888
|
+
|
|
889
|
+
val table: Table[User] = Table.derived[User]
|
|
890
|
+
val repo: Repo[User, Int] = ???
|
|
891
|
+
|
|
892
|
+
val user = User(42, "Alice", "alice@example.com")
|
|
893
|
+
|
|
894
|
+
// Single upsert
|
|
895
|
+
val affected: Int = repo.insertOrUpdate(user)
|
|
896
|
+
|
|
897
|
+
// Batch upsert
|
|
898
|
+
val users: List[User] = List(user, User(43, "Bob", "bob@example.com"))
|
|
899
|
+
val totalAffected: Int = repo.insertOrUpdateBatch(users)
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
These generate SQL like:
|
|
903
|
+
|
|
904
|
+
```sql
|
|
905
|
+
INSERT INTO "user" ("id", "name", "email") VALUES (?, ?, ?)
|
|
906
|
+
ON CONFLICT ("id") DO UPDATE SET "name" = ?, "email" = ?
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
`insertOrUpdateBatch` uses a JDBC batch for efficiency, mirroring the pattern of
|
|
910
|
+
`insertBatch`. Both return the total affected row count.
|
|
911
|
+
|
|
912
|
+
## Keyset Pagination
|
|
913
|
+
|
|
914
|
+
Keyset (cursor) pagination avoids the cost and drift of `OFFSET` by seeking
|
|
915
|
+
after the last seen key: `WHERE id > ? ORDER BY id ASC LIMIT n`. The row
|
|
916
|
+
identified by the cursor is excluded (`>` not `>=`) so consecutive pages do
|
|
917
|
+
not duplicate the boundary row.
|
|
918
|
+
|
|
919
|
+
`Repo` exposes this directly for primary-key cursors:
|
|
920
|
+
|
|
921
|
+
```scala
|
|
922
|
+
import zio.blocks.sql.*
|
|
923
|
+
import zio.blocks.schema.Schema
|
|
924
|
+
|
|
925
|
+
case class User(id: Int, name: String, email: String)
|
|
926
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
927
|
+
|
|
928
|
+
given DbCon = ???
|
|
929
|
+
|
|
930
|
+
val repo: Repo[User, Int] = ??? // e.g. Repo(table, "id", idCodec, _.id)
|
|
931
|
+
|
|
932
|
+
val firstPage: List[User] = repo.pageAfter(cursorId = 0, limit = 20)
|
|
933
|
+
val nextPage: List[User] = repo.pageAfter(cursorId = firstPage.last.id, limit = 20)
|
|
934
|
+
// when cursorId == last id, nextPage is empty
|
|
935
|
+
```
|
|
936
|
+
|
|
937
|
+
It renders as:
|
|
938
|
+
|
|
939
|
+
```sql
|
|
940
|
+
SELECT "id", "name", "email" FROM "user" WHERE "id" > ? ORDER BY "id" ASC LIMIT 20
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
where `?` is bound via `idCodec.toDbValues(cursorId)`. `limit` must be `> 0`.
|
|
944
|
+
|
|
945
|
+
For non-ID orderings or ad-hoc queries, `Frag.keysetAfter` builds the
|
|
946
|
+
portable `WHERE col > ? ORDER BY col ASC LIMIT n` fragment without a `Repo`:
|
|
947
|
+
|
|
948
|
+
```scala
|
|
949
|
+
import zio.blocks.sql.*
|
|
950
|
+
|
|
951
|
+
case class User(id: Int, name: String, email: String)
|
|
952
|
+
import zio.blocks.schema.Schema
|
|
953
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
954
|
+
val table: Table[User] = Table.derived[User]
|
|
955
|
+
|
|
956
|
+
// Table-validated: rejects unknown columns
|
|
957
|
+
val frag: Frag = Frag.keysetAfter(table, orderCol = "id", lastValue = DbValue.DbInt(42), limit = 20)
|
|
958
|
+
// frag.sql(dialect) == " WHERE id > ? ORDER BY id ASC LIMIT 20"
|
|
959
|
+
|
|
960
|
+
// Without a table: identifier-only validation
|
|
961
|
+
val frag2: Frag = Frag.keysetAfter(orderCol = "created_at", lastValue = DbValue.DbLong(1000L), limit = 10)
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
- `Frag.keysetAfter(table, orderCol, lastValue, limit)` validates `orderCol`
|
|
965
|
+
with `SqlIdentifier.validate` and checks membership in `table.columns`;
|
|
966
|
+
unknown columns throw `IllegalArgumentException`.
|
|
967
|
+
- `Frag.keysetAfter(orderCol, lastValue, limit)` validates the identifier only.
|
|
968
|
+
- `limit` must be `> 0`; single-column cursors only (composite cursors are v2).
|
|
969
|
+
|
|
970
|
+
Compose with a base `SELECT`:
|
|
971
|
+
|
|
972
|
+
```scala
|
|
973
|
+
import zio.blocks.sql.*
|
|
974
|
+
import zio.blocks.schema.Schema
|
|
975
|
+
|
|
976
|
+
case class User(id: Int, name: String, email: String)
|
|
977
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
978
|
+
val table: Table[User] = Table.derived[User]
|
|
979
|
+
|
|
980
|
+
val base = Frag.literal("SELECT id, name, email FROM user")
|
|
981
|
+
val pageFrag = base ++ Frag.keysetAfter(table, "id", DbValue.DbInt(42), 20)
|
|
982
|
+
// Rendering the SQL does not require a DbCon:
|
|
983
|
+
val sql: String = pageFrag.sql(SqlDialect.SQLite) // SELECT id, name, email FROM user WHERE id > ? ORDER BY id ASC LIMIT 20
|
|
984
|
+
// Executing needs givens at the call site:
|
|
985
|
+
// given DbCon = ???
|
|
986
|
+
// given DbCodec[User] = table.codec
|
|
987
|
+
// val rows: List[User] = pageFrag.query[User]
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
## Inspecting SQL
|
|
991
|
+
|
|
992
|
+
The custom interpreter above produces raw strings useful for debugging. The `sql` module's query IR (`zio.blocks.sql.query.SqlQuery`) provides richer inspection APIs.
|
|
993
|
+
|
|
994
|
+
### explain(dialect): String
|
|
995
|
+
|
|
996
|
+
The `explain` method renders the full SQL text with numbered parameter placeholders (`?1`, `?2`, ...) and a trailing comment listing each parameter's position and type:
|
|
997
|
+
|
|
998
|
+
```scala
|
|
999
|
+
import zio.blocks.sql._
|
|
1000
|
+
import zio.blocks.sql.query.{Rel, SqlQuery => Select}
|
|
1001
|
+
|
|
1002
|
+
val userTable = Table.derived[User]
|
|
1003
|
+
val repoTable = Table.derived[Repo]
|
|
1004
|
+
|
|
1005
|
+
val q = Select
|
|
1006
|
+
.from(userTable)
|
|
1007
|
+
.innerJoin(Rel(repoTable, "owner_id", userTable, "id"))
|
|
1008
|
+
.filter(Frag(IndexedSeq("""t0."name" = """, ""), IndexedSeq(DbValue.DbString("alice"))))
|
|
1009
|
+
|
|
1010
|
+
println(q.explain(SqlDialect.PostgreSQL))
|
|
1011
|
+
// SELECT t0."id", t0."name", t1."id", t1."owner_id", t1."name" FROM "user" AS t0 INNER JOIN "repo" AS t1 ON t1."owner_id" = t0."id" WHERE t0."name" = ?1
|
|
1012
|
+
// -- params: 1:String
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
`explain` renders a single-line SQL string with numbered `?N` placeholders. The `?N`
|
|
1016
|
+
placeholders correspond one-to-one with the parameter list you can obtain separately via
|
|
1017
|
+
`toFrag(dialect).params`. This makes `explain` useful for logging and visual debugging without touching a
|
|
1018
|
+
database.
|
|
1019
|
+
|
|
1020
|
+
### Inspecting the query IR
|
|
1021
|
+
|
|
1022
|
+
The query itself is a case class, so every part is available programmatically:
|
|
1023
|
+
|
|
1024
|
+
```scala
|
|
1025
|
+
q.source // Table[User] for "user"
|
|
1026
|
+
q.joins // Vector(JoinNode(repoTable, "t1", Inner, <on-frag>))
|
|
1027
|
+
q.filters // Vector(Frag(t0."name" = ?, DbString(alice)))
|
|
1028
|
+
q.groupBy // Vector.empty
|
|
1029
|
+
q.orderBy // Vector.empty
|
|
1030
|
+
q.limit // None
|
|
1031
|
+
q.offset // None
|
|
1032
|
+
q.toFrag // Frag (re-renderable to SQL via frag.sql(dialect))
|
|
1033
|
+
```
|
|
1034
|
+
|
|
1035
|
+
Inspecting the IR directly lets you examine joins, filters, ordering, and limits programmatically. This is useful for building monitoring dashboards, query analyzers, or dynamic query modification layers.
|
|
1036
|
+
|
|
1037
|
+
### sql(dialect): String
|
|
1038
|
+
|
|
1039
|
+
The query IR also provides a simpler `sql` method that renders `?` placeholders for execution:
|
|
1040
|
+
|
|
1041
|
+
```scala
|
|
1042
|
+
println(q.sql(SqlDialect.PostgreSQL))
|
|
1043
|
+
// SELECT t0."id", t0."name", t1."id", t1."owner_id", t1."name" FROM "user" AS t0 INNER JOIN "repo" AS t1 ON t1."owner_id" = t0."id" WHERE t0."name" = ?
|
|
1044
|
+
```
|
|
1045
|
+
|
|
1046
|
+
### previewSql() for Migrations
|
|
1047
|
+
|
|
1048
|
+
When using `SmallMigrator` or `LargeMigrator` from the `data-migration` module, `previewSql()` returns the full sequence of SQL statements the migrator would execute, without opening any database connection:
|
|
1049
|
+
|
|
1050
|
+
```scala
|
|
1051
|
+
import zio.blocks.data.migration._
|
|
1052
|
+
|
|
1053
|
+
given transactor: Transactor = tx
|
|
1054
|
+
|
|
1055
|
+
val migrator = SmallMigrator(
|
|
1056
|
+
repoV1 = userRepo,
|
|
1057
|
+
repoV2 = userRepoV2,
|
|
1058
|
+
migration = userMigration,
|
|
1059
|
+
queueTable = "migration_queue",
|
|
1060
|
+
batchSize = 100,
|
|
1061
|
+
target = TargetStrategy.InPlace
|
|
1062
|
+
)
|
|
1063
|
+
|
|
1064
|
+
val statements: Vector[String] = migrator.previewSql()
|
|
1065
|
+
// Vector(
|
|
1066
|
+
// "CREATE TABLE ...", -- queue DDL
|
|
1067
|
+
// "CREATE TABLE ...", -- shadow table
|
|
1068
|
+
// "CREATE TRIGGER ...", -- capture triggers
|
|
1069
|
+
// "SELECT ...", -- dequeue template
|
|
1070
|
+
// "ALTER TABLE ..." -- finalize (rename)
|
|
1071
|
+
// )
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
This gives you a dry run of the migration SQL before any schema changes are applied.
|
|
1075
|
+
|
|
1076
|
+
## Compile-time SQL Dumps
|
|
1077
|
+
|
|
1078
|
+
The `Dump` object emits SQL files at compile time. When the JVM property `zib.sql.dumpDir` is set, inline macro calls to `Dump.dumpTable` or `Dump.dumpQuery` write `.sql` files to that directory. When the property is absent, the calls become no-ops with zero runtime cost.
|
|
1079
|
+
|
|
1080
|
+
### Enabling Dumps
|
|
1081
|
+
|
|
1082
|
+
The `Dump` macros read `System.getProperty("zib.sql.dumpDir")` at compile time from the JVM running sbt's compiler. This is a JVM system property, not a scalac flag. Passing it via `scalacOptions` does nothing.
|
|
1083
|
+
|
|
1084
|
+
The simplest way to set it is as a JVM flag on the sbt command line:
|
|
1085
|
+
|
|
1086
|
+
```bash
|
|
1087
|
+
sbt -Dzib.sql.dumpDir=target/sql-dumps "++3.8.3; sqlJVM/compile"
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
If you prefer not to type `-D` every time, export it through `SBT_OPTS`:
|
|
1091
|
+
|
|
1092
|
+
```bash
|
|
1093
|
+
SBT_OPTS="-Dzib.sql.dumpDir=target/sql-dumps" sbt compile
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
Both approaches set the property on the sbt process, which is the same JVM that runs the Scala compiler and the macros within it.
|
|
1097
|
+
|
|
1098
|
+
### Entry Points
|
|
1099
|
+
|
|
1100
|
+
There are two inline macro entry points, all in `zio.blocks.sql.Dump`:
|
|
1101
|
+
|
|
1102
|
+
```scala
|
|
1103
|
+
import zio.blocks.sql._
|
|
1104
|
+
|
|
1105
|
+
// Dump a Table's CREATE TABLE DDL (both PostgreSQL and SQLite)
|
|
1106
|
+
Dump.dumpTable(userTable)
|
|
1107
|
+
|
|
1108
|
+
// Dump a query IR's SELECT
|
|
1109
|
+
Dump.dumpQuery(queryIr)
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
Each call emits one file per dialect (PostgreSQL and SQLite by default).
|
|
1113
|
+
|
|
1114
|
+
### Naming Scheme
|
|
1115
|
+
|
|
1116
|
+
Files are named `<owner>-<dialect>.sql` where `<owner>` is derived from the enclosing symbol and `<dialect>` is the lowercased dialect name:
|
|
1117
|
+
|
|
1118
|
+
```
|
|
1119
|
+
target/sql-dumps/
|
|
1120
|
+
user-postgresql.sql
|
|
1121
|
+
user-sqlite.sql
|
|
1122
|
+
repo-postgresql.sql
|
|
1123
|
+
repo-sqlite.sql
|
|
1124
|
+
```
|
|
1125
|
+
|
|
1126
|
+
For `dumpTable`, the owner comes from the `Table`'s type name. For `dumpQuery`, the macro walks the call site to extract the enclosing method, val name, or argument name. If the macro cannot determine a meaningful name, it falls back to `query`.
|
|
1127
|
+
|
|
1128
|
+
### Content-hash Skip
|
|
1129
|
+
|
|
1130
|
+
Each dump file is written only when its content differs from the existing file. The macro compares the new bytes against any existing file at the target path. If they match, the write is skipped. This means incremental compilations do not produce noisy diffs or unnecessary filesystem writes.
|
|
1131
|
+
|
|
1132
|
+
All files use UTF-8 encoding with a trailing newline.
|
|
1133
|
+
|
|
1134
|
+
### Limitations
|
|
1135
|
+
|
|
1136
|
+
Compile-time dumps work well for statically constructed queries, but some SQL patterns cannot be dumped:
|
|
1137
|
+
|
|
1138
|
+
- **Dynamic `Frag` chains.** Fragments built at runtime from user input, database lookups, or conditional branching are invisible to the macro. Only the static structure known at compile time appears in the dump.
|
|
1139
|
+
- **Repo internals.** The `Repo` abstraction's generated queries (insert, update, delete, select-by-id) are assembled at runtime from the `DbCodec` and `Table` metadata. `Dump.dumpTable` captures the DDL, but the CRUD queries themselves are not emitted.
|
|
1140
|
+
- **Phase 2 note.** A future phase may extend `Dump` to cover `Repo`-level CRUD operations and dynamic fragment composition. For now, treat the dump as a DDL and static-query snapshot, not a complete representation of every SQL statement your application will execute.
|
|
1141
|
+
|
|
1142
|
+
:::tip
|
|
1143
|
+
Pair `Dump.dumpTable` with `previewSql()` for a fuller picture: `dumpTable` captures the schema DDL at compile time, while `previewSql` captures the migration SQL at runtime before execution.
|
|
1144
|
+
:::
|
|
1145
|
+
|
|
752
1146
|
## Going Further
|
|
753
1147
|
|
|
754
1148
|
- **[Part 1: Expressions](./query-dsl-reified-optics.md)** -- Building query expressions with reified optics
|