effect-qb 0.13.0 → 0.15.0

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 (146) hide show
  1. package/README.md +6 -1431
  2. package/dist/mysql.js +61945 -3611
  3. package/dist/postgres/metadata.js +2818 -0
  4. package/dist/postgres.js +9942 -5591
  5. package/package.json +21 -10
  6. package/src/internal/aggregation-validation.ts +3 -3
  7. package/src/internal/case-analysis.d.ts +18 -0
  8. package/src/internal/case-analysis.ts +4 -4
  9. package/src/internal/coercion/analysis.d.ts +7 -0
  10. package/src/internal/{coercion-analysis.ts → coercion/analysis.ts} +3 -3
  11. package/src/internal/coercion/errors.d.ts +17 -0
  12. package/src/internal/{coercion-errors.ts → coercion/errors.ts} +1 -1
  13. package/src/internal/coercion/kind.d.ts +4 -0
  14. package/src/internal/{coercion-kind.ts → coercion/kind.ts} +2 -2
  15. package/src/internal/{coercion-normalize.ts → coercion/normalize.ts} +1 -1
  16. package/src/internal/coercion/rules.d.ts +6 -0
  17. package/src/internal/{coercion-rules.ts → coercion/rules.ts} +2 -2
  18. package/src/internal/column-state.d.ts +190 -0
  19. package/src/internal/column-state.ts +119 -56
  20. package/src/internal/column.ts +387 -149
  21. package/src/internal/datatypes/define.d.ts +17 -0
  22. package/src/internal/datatypes/define.ts +18 -34
  23. package/src/internal/datatypes/lookup.d.ts +44 -0
  24. package/src/internal/datatypes/lookup.ts +61 -152
  25. package/src/internal/datatypes/shape.d.ts +16 -0
  26. package/src/internal/datatypes/shape.ts +1 -1
  27. package/src/internal/derived-table.d.ts +4 -0
  28. package/src/internal/derived-table.ts +21 -16
  29. package/src/internal/dsl-mutation-runtime.ts +378 -0
  30. package/src/internal/dsl-plan-runtime.ts +387 -0
  31. package/src/internal/dsl-query-runtime.ts +160 -0
  32. package/src/internal/dsl-transaction-ddl-runtime.ts +263 -0
  33. package/src/internal/executor.ts +173 -38
  34. package/src/internal/expression-ast.ts +19 -5
  35. package/src/internal/grouping-key.d.ts +3 -0
  36. package/src/internal/grouping-key.ts +1 -1
  37. package/src/internal/implication-runtime.d.ts +15 -0
  38. package/src/internal/implication-runtime.ts +171 -0
  39. package/src/internal/json/ast.d.ts +30 -0
  40. package/src/internal/json/ast.ts +1 -1
  41. package/src/internal/json/errors.d.ts +8 -0
  42. package/src/internal/json/path.d.ts +75 -0
  43. package/src/internal/json/path.ts +1 -1
  44. package/src/internal/json/types.d.ts +62 -0
  45. package/src/internal/predicate/analysis.d.ts +20 -0
  46. package/src/internal/{predicate-analysis.ts → predicate/analysis.ts} +13 -3
  47. package/src/internal/predicate/atom.d.ts +28 -0
  48. package/src/internal/{predicate-branches.ts → predicate/branches.ts} +2 -2
  49. package/src/internal/predicate/context.d.ts +67 -0
  50. package/src/internal/{predicate-context.ts → predicate/context.ts} +111 -32
  51. package/src/internal/predicate/formula.d.ts +35 -0
  52. package/src/internal/{predicate-formula.ts → predicate/formula.ts} +32 -20
  53. package/src/internal/predicate/key.d.ts +11 -0
  54. package/src/internal/{predicate-key.ts → predicate/key.ts} +2 -2
  55. package/src/internal/{predicate-nnf.ts → predicate/nnf.ts} +2 -2
  56. package/src/internal/predicate/normalize.d.ts +53 -0
  57. package/src/internal/predicate/normalize.ts +273 -0
  58. package/src/internal/predicate/runtime.d.ts +31 -0
  59. package/src/internal/predicate/runtime.ts +679 -0
  60. package/src/internal/projection-alias.d.ts +13 -0
  61. package/src/internal/projections.d.ts +31 -0
  62. package/src/internal/projections.ts +1 -1
  63. package/src/internal/query-ast.d.ts +217 -0
  64. package/src/internal/query-ast.ts +1 -1
  65. package/src/internal/query-requirements.d.ts +20 -0
  66. package/src/internal/query.d.ts +775 -0
  67. package/src/internal/query.ts +767 -275
  68. package/src/internal/renderer.ts +7 -21
  69. package/src/internal/row-set.d.ts +53 -0
  70. package/src/internal/{plan.ts → row-set.ts} +23 -11
  71. package/src/internal/{runtime-normalize.ts → runtime/normalize.ts} +9 -31
  72. package/src/internal/{runtime-schema.ts → runtime/schema.ts} +84 -55
  73. package/src/internal/runtime/value.d.ts +22 -0
  74. package/src/internal/{runtime-value.ts → runtime/value.ts} +2 -2
  75. package/src/internal/scalar.d.ts +107 -0
  76. package/src/internal/scalar.ts +191 -0
  77. package/src/internal/schema-derivation.d.ts +105 -0
  78. package/src/internal/schema-derivation.ts +93 -21
  79. package/src/internal/schema-expression.d.ts +18 -0
  80. package/src/internal/schema-expression.ts +75 -0
  81. package/src/internal/table-options.d.ts +94 -0
  82. package/src/internal/table-options.ts +94 -8
  83. package/src/internal/table.d.ts +173 -0
  84. package/src/internal/table.ts +135 -54
  85. package/src/mysql/column.ts +95 -18
  86. package/src/mysql/datatypes/index.ts +58 -3
  87. package/src/mysql/errors/generated.ts +57336 -0
  88. package/src/mysql/errors/index.ts +1 -0
  89. package/src/mysql/errors/normalize.ts +55 -53
  90. package/src/mysql/errors/types.ts +74 -0
  91. package/src/mysql/executor.ts +69 -7
  92. package/src/mysql/function/aggregate.ts +1 -5
  93. package/src/mysql/function/core.ts +1 -3
  94. package/src/mysql/function/index.ts +1 -1
  95. package/src/mysql/function/string.ts +1 -5
  96. package/src/mysql/function/temporal.ts +12 -15
  97. package/src/mysql/function/window.ts +1 -6
  98. package/src/{internal/mysql-dialect.ts → mysql/internal/dialect.ts} +1 -1
  99. package/src/mysql/internal/dsl.ts +6115 -0
  100. package/src/{internal/mysql-renderer.ts → mysql/internal/renderer.ts} +6 -6
  101. package/src/mysql/internal/sql-expression-renderer.ts +1455 -0
  102. package/src/mysql/json.ts +2 -0
  103. package/src/mysql/query.ts +111 -86
  104. package/src/mysql/renderer.ts +1 -1
  105. package/src/mysql/table.ts +1 -1
  106. package/src/mysql.ts +6 -4
  107. package/src/postgres/cast.ts +30 -0
  108. package/src/postgres/column.ts +178 -20
  109. package/src/postgres/datatypes/index.d.ts +515 -0
  110. package/src/postgres/datatypes/index.ts +49 -5
  111. package/src/postgres/datatypes/spec.d.ts +412 -0
  112. package/src/postgres/errors/generated.ts +2636 -0
  113. package/src/postgres/errors/index.ts +1 -0
  114. package/src/postgres/errors/normalize.ts +47 -62
  115. package/src/postgres/errors/types.ts +92 -34
  116. package/src/postgres/executor.ts +37 -5
  117. package/src/postgres/function/aggregate.ts +1 -5
  118. package/src/postgres/function/core.ts +20 -2
  119. package/src/postgres/function/index.ts +1 -1
  120. package/src/postgres/function/string.ts +1 -5
  121. package/src/postgres/function/temporal.ts +12 -15
  122. package/src/postgres/function/window.ts +1 -6
  123. package/src/{internal/postgres-dialect.ts → postgres/internal/dialect.ts} +1 -1
  124. package/src/{internal/query-factory.ts → postgres/internal/dsl.ts} +1568 -2120
  125. package/src/{internal/postgres-renderer.ts → postgres/internal/renderer.ts} +6 -6
  126. package/src/postgres/internal/schema-ddl.ts +108 -0
  127. package/src/postgres/internal/schema-model.ts +150 -0
  128. package/src/{internal → postgres/internal}/sql-expression-renderer.ts +112 -46
  129. package/src/postgres/json.ts +493 -0
  130. package/src/postgres/metadata.ts +31 -0
  131. package/src/postgres/query.ts +113 -86
  132. package/src/postgres/renderer.ts +3 -13
  133. package/src/postgres/schema-expression.ts +17 -0
  134. package/src/postgres/schema-management.ts +204 -0
  135. package/src/postgres/schema.ts +35 -0
  136. package/src/postgres/table.ts +316 -42
  137. package/src/postgres/type.ts +31 -0
  138. package/src/postgres.ts +20 -4
  139. package/CHANGELOG.md +0 -134
  140. package/src/internal/expression.ts +0 -327
  141. package/src/internal/predicate-normalize.ts +0 -202
  142. package/src/mysql/function/json.ts +0 -4
  143. package/src/mysql/private/query.ts +0 -13
  144. package/src/postgres/function/json.ts +0 -4
  145. package/src/postgres/private/query.ts +0 -13
  146. /package/src/internal/{predicate-atom.ts → predicate/atom.ts} +0 -0
@@ -0,0 +1,263 @@
1
+ import * as Plan from "./row-set.js"
2
+
3
+ type DslTransactionDdlRuntimeContext = {
4
+ readonly profile: {
5
+ readonly dialect: string
6
+ }
7
+ readonly makePlan: (...args: readonly any[]) => any
8
+ readonly targetSourceDetails: (target: any) => { readonly sourceName: string; readonly sourceBaseName: string }
9
+ readonly normalizeColumnList: (columns: string | readonly string[]) => readonly string[]
10
+ readonly defaultIndexName: (tableName: string, columns: readonly string[], unique: boolean) => string
11
+ }
12
+
13
+ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContext) => {
14
+ const transaction = (options: { readonly isolationLevel?: any; readonly readOnly?: boolean } = {}) =>
15
+ ctx.makePlan({
16
+ selection: {},
17
+ required: [],
18
+ available: {},
19
+ dialect: ctx.profile.dialect
20
+ }, {
21
+ kind: "transaction",
22
+ select: {},
23
+ transaction: {
24
+ kind: "transaction",
25
+ isolationLevel: options.isolationLevel,
26
+ readOnly: options.readOnly
27
+ },
28
+ where: [],
29
+ having: [],
30
+ joins: [],
31
+ groupBy: [],
32
+ orderBy: []
33
+ }, undefined, "transaction", "transaction")
34
+
35
+ const commit = () =>
36
+ ctx.makePlan({
37
+ selection: {},
38
+ required: [],
39
+ available: {},
40
+ dialect: ctx.profile.dialect
41
+ }, {
42
+ kind: "commit",
43
+ select: {},
44
+ transaction: {
45
+ kind: "commit"
46
+ },
47
+ where: [],
48
+ having: [],
49
+ joins: [],
50
+ groupBy: [],
51
+ orderBy: []
52
+ }, undefined, "transaction", "commit")
53
+
54
+ const rollback = () =>
55
+ ctx.makePlan({
56
+ selection: {},
57
+ required: [],
58
+ available: {},
59
+ dialect: ctx.profile.dialect
60
+ }, {
61
+ kind: "rollback",
62
+ select: {},
63
+ transaction: {
64
+ kind: "rollback"
65
+ },
66
+ where: [],
67
+ having: [],
68
+ joins: [],
69
+ groupBy: [],
70
+ orderBy: []
71
+ }, undefined, "transaction", "rollback")
72
+
73
+ const savepoint = (name: string) =>
74
+ ctx.makePlan({
75
+ selection: {},
76
+ required: [],
77
+ available: {},
78
+ dialect: ctx.profile.dialect
79
+ }, {
80
+ kind: "savepoint",
81
+ select: {},
82
+ transaction: {
83
+ kind: "savepoint",
84
+ name
85
+ },
86
+ where: [],
87
+ having: [],
88
+ joins: [],
89
+ groupBy: [],
90
+ orderBy: []
91
+ }, undefined, "transaction", "savepoint")
92
+
93
+ const rollbackTo = (name: string) =>
94
+ ctx.makePlan({
95
+ selection: {},
96
+ required: [],
97
+ available: {},
98
+ dialect: ctx.profile.dialect
99
+ }, {
100
+ kind: "rollbackTo",
101
+ select: {},
102
+ transaction: {
103
+ kind: "rollbackTo",
104
+ name
105
+ },
106
+ where: [],
107
+ having: [],
108
+ joins: [],
109
+ groupBy: [],
110
+ orderBy: []
111
+ }, undefined, "transaction", "rollbackTo")
112
+
113
+ const releaseSavepoint = (name: string) =>
114
+ ctx.makePlan({
115
+ selection: {},
116
+ required: [],
117
+ available: {},
118
+ dialect: ctx.profile.dialect
119
+ }, {
120
+ kind: "releaseSavepoint",
121
+ select: {},
122
+ transaction: {
123
+ kind: "releaseSavepoint",
124
+ name
125
+ },
126
+ where: [],
127
+ having: [],
128
+ joins: [],
129
+ groupBy: [],
130
+ orderBy: []
131
+ }, undefined, "transaction", "releaseSavepoint")
132
+
133
+ const createTable = (target: any, options: { readonly ifNotExists?: boolean } = {}) => {
134
+ const { sourceName, sourceBaseName } = ctx.targetSourceDetails(target)
135
+ return ctx.makePlan({
136
+ selection: {},
137
+ required: [],
138
+ available: {},
139
+ dialect: target[Plan.TypeId].dialect
140
+ }, {
141
+ kind: "createTable",
142
+ select: {},
143
+ target: {
144
+ kind: "from",
145
+ tableName: sourceName,
146
+ baseTableName: sourceBaseName,
147
+ source: target
148
+ },
149
+ ddl: {
150
+ kind: "createTable",
151
+ ifNotExists: options.ifNotExists ?? false
152
+ },
153
+ where: [],
154
+ having: [],
155
+ joins: [],
156
+ groupBy: [],
157
+ orderBy: []
158
+ }, undefined, "ddl", "createTable")
159
+ }
160
+
161
+ const dropTable = (target: any, options: { readonly ifExists?: boolean } = {}) => {
162
+ const { sourceName, sourceBaseName } = ctx.targetSourceDetails(target)
163
+ return ctx.makePlan({
164
+ selection: {},
165
+ required: [],
166
+ available: {},
167
+ dialect: target[Plan.TypeId].dialect
168
+ }, {
169
+ kind: "dropTable",
170
+ select: {},
171
+ target: {
172
+ kind: "from",
173
+ tableName: sourceName,
174
+ baseTableName: sourceBaseName,
175
+ source: target
176
+ },
177
+ ddl: {
178
+ kind: "dropTable",
179
+ ifExists: options.ifExists ?? false
180
+ },
181
+ where: [],
182
+ having: [],
183
+ joins: [],
184
+ groupBy: [],
185
+ orderBy: []
186
+ }, undefined, "ddl", "dropTable")
187
+ }
188
+
189
+ const createIndex = (target: any, columns: string | readonly string[], options: { readonly name?: string; readonly unique?: boolean; readonly ifNotExists?: boolean } = {}) => {
190
+ const normalizedColumns = ctx.normalizeColumnList(columns)
191
+ const { sourceName, sourceBaseName } = ctx.targetSourceDetails(target)
192
+ return ctx.makePlan({
193
+ selection: {},
194
+ required: [],
195
+ available: {},
196
+ dialect: target[Plan.TypeId].dialect
197
+ }, {
198
+ kind: "createIndex",
199
+ select: {},
200
+ target: {
201
+ kind: "from",
202
+ tableName: sourceName,
203
+ baseTableName: sourceBaseName,
204
+ source: target
205
+ },
206
+ ddl: {
207
+ kind: "createIndex",
208
+ name: options.name ?? ctx.defaultIndexName(sourceBaseName, normalizedColumns, options.unique ?? false),
209
+ columns: normalizedColumns,
210
+ unique: options.unique ?? false,
211
+ ifNotExists: options.ifNotExists ?? false
212
+ },
213
+ where: [],
214
+ having: [],
215
+ joins: [],
216
+ groupBy: [],
217
+ orderBy: []
218
+ }, undefined, "ddl", "createIndex")
219
+ }
220
+
221
+ const dropIndex = (target: any, columns: string | readonly string[], options: { readonly name?: string; readonly ifExists?: boolean } = {}) => {
222
+ const normalizedColumns = ctx.normalizeColumnList(columns)
223
+ const { sourceName, sourceBaseName } = ctx.targetSourceDetails(target)
224
+ return ctx.makePlan({
225
+ selection: {},
226
+ required: [],
227
+ available: {},
228
+ dialect: target[Plan.TypeId].dialect
229
+ }, {
230
+ kind: "dropIndex",
231
+ select: {},
232
+ target: {
233
+ kind: "from",
234
+ tableName: sourceName,
235
+ baseTableName: sourceBaseName,
236
+ source: target
237
+ },
238
+ ddl: {
239
+ kind: "dropIndex",
240
+ name: options.name ?? ctx.defaultIndexName(sourceBaseName, normalizedColumns, false),
241
+ ifExists: options.ifExists ?? false
242
+ },
243
+ where: [],
244
+ having: [],
245
+ joins: [],
246
+ groupBy: [],
247
+ orderBy: []
248
+ }, undefined, "ddl", "dropIndex")
249
+ }
250
+
251
+ return {
252
+ transaction,
253
+ commit,
254
+ rollback,
255
+ savepoint,
256
+ rollbackTo,
257
+ releaseSavepoint,
258
+ createTable,
259
+ dropTable,
260
+ createIndex,
261
+ dropIndex
262
+ }
263
+ }
@@ -1,21 +1,30 @@
1
+ import * as Chunk from "effect/Chunk"
1
2
  import * as Effect from "effect/Effect"
3
+ import * as Option from "effect/Option"
2
4
  import * as Schema from "effect/Schema"
3
5
  import * as SqlClient from "@effect/sql/SqlClient"
4
6
  import * as SqlError from "@effect/sql/SqlError"
7
+ import * as Stream from "effect/Stream"
5
8
 
6
- import * as Expression from "./expression.js"
7
- import { normalizeDbValue } from "./runtime-normalize.js"
8
- import { expressionRuntimeSchema } from "./runtime-schema.js"
9
+ import * as Expression from "./scalar.js"
10
+ import * as ExpressionAst from "./expression-ast.js"
11
+ import { resolveImplicationScope, type ImplicationScope } from "./implication-runtime.js"
12
+ import { normalizeDbValue } from "./runtime/normalize.js"
13
+ import { expressionRuntimeSchema } from "./runtime/schema.js"
9
14
  import { flattenSelection } from "./projections.js"
10
15
  import * as Query from "./query.js"
11
16
  import * as QueryAst from "./query-ast.js"
12
17
  import * as Renderer from "./renderer.js"
13
- import * as Plan from "./plan.js"
18
+ import * as Plan from "./row-set.js"
14
19
 
15
20
  /** Flat database row keyed by rendered projection aliases. */
16
21
  export type FlatRow = Readonly<Record<string, unknown>>
17
22
  export type DriverMode = "raw" | "normalized"
18
23
 
24
+ type AstBackedExpression = Expression.Any & {
25
+ readonly [ExpressionAst.TypeId]: ExpressionAst.Any
26
+ }
27
+
19
28
  export interface RowDecodeError {
20
29
  readonly _tag: "RowDecodeError"
21
30
  readonly message: string
@@ -52,6 +61,9 @@ export interface Driver<
52
61
  execute<Row>(
53
62
  query: Renderer.RenderedQuery<Row, Dialect>
54
63
  ): Effect.Effect<ReadonlyArray<FlatRow>, Error, Context>
64
+ stream<Row>(
65
+ query: Renderer.RenderedQuery<Row, Dialect>
66
+ ): Stream.Stream<FlatRow, Error, Context>
55
67
  }
56
68
 
57
69
  /**
@@ -67,9 +79,12 @@ export interface Executor<
67
79
  Context = never
68
80
  > {
69
81
  readonly dialect: Dialect
70
- execute<PlanValue extends Query.QueryPlan<any, any, any, any, any, any, any, any, any>>(
82
+ execute<PlanValue extends Query.Plan.Any>(
71
83
  plan: Query.DialectCompatiblePlan<PlanValue, Dialect>
72
84
  ): Effect.Effect<Query.ResultRows<PlanValue>, Error, Context>
85
+ stream<PlanValue extends Query.Plan.Any>(
86
+ plan: Query.DialectCompatiblePlan<PlanValue, Dialect>
87
+ ): Stream.Stream<Query.ResultRow<PlanValue>, Error, Context>
73
88
  }
74
89
 
75
90
  const setPath = (
@@ -111,21 +126,21 @@ const hasWriteStatement = (statement: QueryAst.QueryStatement): boolean =>
111
126
 
112
127
  const hasWriteCapabilityInSource = (source: unknown): boolean =>
113
128
  typeof source === "object" && source !== null && "plan" in source
114
- ? hasWriteCapability((source as { readonly plan: Query.QueryPlan<any, any, any, any, any, any, any, any, any, any> }).plan)
129
+ ? hasWriteCapability((source as { readonly plan: Query.Plan.Any }).plan)
115
130
  : false
116
131
 
117
132
  export const hasWriteCapability = (
118
- plan: Query.QueryPlan<any, any, any, any, any, any, any, any, any, any>
133
+ plan: Query.Plan.Any
119
134
  ): boolean => {
120
135
  const ast = Query.getAst(plan)
121
136
  if (hasWriteStatement(ast.kind)) {
122
137
  return true
123
138
  }
124
139
  if (ast.kind === "set") {
125
- if (ast.setBase && hasWriteCapability((ast.setBase as Query.QueryPlan<any, any, any, any, any, any, any, any, any, any>))) {
140
+ if (ast.setBase && hasWriteCapability((ast.setBase as Query.Plan.Any))) {
126
141
  return true
127
142
  }
128
- if ((ast.setOperations ?? []).some((entry) => hasWriteCapability(entry.query as Query.QueryPlan<any, any, any, any, any, any, any, any, any, any>))) {
143
+ if ((ast.setOperations ?? []).some((entry) => hasWriteCapability(entry.query as Query.Plan.Any))) {
129
144
  return true
130
145
  }
131
146
  }
@@ -189,24 +204,35 @@ const makeRowDecodeError = (
189
204
 
190
205
  const hasOptionalSourceDependency = (
191
206
  expression: Expression.Any,
192
- available: Readonly<Record<string, Plan.Source>>
207
+ scope: ImplicationScope
193
208
  ): boolean => {
194
209
  const state = expression[Expression.TypeId]
195
- if (state.sourceNullability === "resolved") {
196
- return false
197
- }
198
- return Object.keys(state.dependencies).some((sourceName) => available[sourceName]?.mode === "optional")
210
+ return Object.keys(state.dependencies).some((sourceName) =>
211
+ !scope.absentSourceNames.has(sourceName) && scope.sourceModes.get(sourceName) === "optional")
199
212
  }
200
213
 
201
214
  const effectiveRuntimeNullability = (
202
215
  expression: Expression.Any,
203
- available: Readonly<Record<string, Plan.Source>>
216
+ scope: ImplicationScope
204
217
  ): Expression.Nullability => {
205
218
  const nullability = expression[Expression.TypeId].nullability
219
+ const ast = (expression as AstBackedExpression)[ExpressionAst.TypeId]
206
220
  if (nullability === "always") {
207
221
  return "always"
208
222
  }
209
- return hasOptionalSourceDependency(expression, available)
223
+ if (ast.kind === "column") {
224
+ const key = `${ast.tableName}.${ast.columnName}`
225
+ if (scope.absentSourceNames.has(ast.tableName) || scope.nullKeys.has(key)) {
226
+ return "always"
227
+ }
228
+ if (scope.nonNullKeys.has(key)) {
229
+ return "never"
230
+ }
231
+ }
232
+ if (Object.keys(expression[Expression.TypeId].dependencies).some((sourceName) => scope.absentSourceNames.has(sourceName))) {
233
+ return "always"
234
+ }
235
+ return hasOptionalSourceDependency(expression, scope)
210
236
  ? "maybe"
211
237
  : nullability
212
238
  }
@@ -216,7 +242,7 @@ const decodeProjectionValue = (
216
242
  projection: Renderer.RenderedQuery<any, any>["projections"][number],
217
243
  expression: Expression.Any,
218
244
  raw: unknown,
219
- available: Readonly<Record<string, Plan.Source>>,
245
+ scope: ImplicationScope,
220
246
  driverMode: DriverMode
221
247
  ): unknown => {
222
248
  let normalized = raw
@@ -228,8 +254,9 @@ const decodeProjectionValue = (
228
254
  }
229
255
  }
230
256
 
257
+ const nullability = effectiveRuntimeNullability(expression, scope)
231
258
  if (normalized === null) {
232
- if (effectiveRuntimeNullability(expression, available) === "never") {
259
+ if (nullability === "never") {
233
260
  throw makeRowDecodeError(
234
261
  rendered,
235
262
  projection,
@@ -243,7 +270,19 @@ const decodeProjectionValue = (
243
270
  return null
244
271
  }
245
272
 
246
- const schema = expressionRuntimeSchema(expression)
273
+ if (nullability === "always") {
274
+ throw makeRowDecodeError(
275
+ rendered,
276
+ projection,
277
+ expression,
278
+ raw,
279
+ "schema",
280
+ new Error("Received non-null for an always-null projection"),
281
+ normalized
282
+ )
283
+ }
284
+
285
+ const schema = expressionRuntimeSchema(expression, { assumptions: scope.assumptions })
247
286
  if (schema === undefined) {
248
287
  return normalized
249
288
  }
@@ -259,14 +298,13 @@ const decodeProjectionValue = (
259
298
  }
260
299
  }
261
300
 
262
- export const decodeRows = (
301
+ export const makeRowDecoder = (
263
302
  rendered: Renderer.RenderedQuery<any, any>,
264
- plan: Query.QueryPlan<any, any, any, any, any, any, any, any, any, any>,
265
- rows: ReadonlyArray<FlatRow>,
303
+ plan: Query.Plan.Any,
266
304
  options: {
267
305
  readonly driverMode?: DriverMode
268
306
  } = {}
269
- ): ReadonlyArray<any> => {
307
+ ): ((row: FlatRow) => any) => {
270
308
  const projections = flattenSelection(
271
309
  Query.getAst(plan).select as Record<string, unknown>
272
310
  )
@@ -274,8 +312,8 @@ export const decodeRows = (
274
312
  projections.map((projection) => [projection.alias, projection.expression] as const)
275
313
  )
276
314
  const driverMode = options.driverMode ?? "raw"
277
- const available = plan[Plan.TypeId].available
278
- return rows.map((row) => {
315
+ const scope = resolveImplicationScope(plan[Plan.TypeId].available, Query.getQueryState(plan).assumptions)
316
+ return (row) => {
279
317
  const decoded: Record<string, unknown> = {}
280
318
  for (const projection of rendered.projections) {
281
319
  if (!(projection.alias in row)) {
@@ -288,11 +326,35 @@ export const decodeRows = (
288
326
  setPath(
289
327
  decoded,
290
328
  projection.path,
291
- decodeProjectionValue(rendered, projection, expression, row[projection.alias], available, driverMode)
329
+ decodeProjectionValue(rendered, projection, expression, row[projection.alias], scope, driverMode)
292
330
  )
293
331
  }
294
332
  return decoded
295
- })
333
+ }
334
+ }
335
+
336
+ export const decodeChunk = (
337
+ rendered: Renderer.RenderedQuery<any, any>,
338
+ plan: Query.Plan.Any,
339
+ rows: Chunk.Chunk<FlatRow>,
340
+ options: {
341
+ readonly driverMode?: DriverMode
342
+ } = {}
343
+ ): Chunk.Chunk<any> => {
344
+ const decodeRow = makeRowDecoder(rendered, plan, options)
345
+ return Chunk.unsafeFromArray(Chunk.toReadonlyArray(rows).map((row) => decodeRow(row)))
346
+ }
347
+
348
+ export const decodeRows = (
349
+ rendered: Renderer.RenderedQuery<any, any>,
350
+ plan: Query.Plan.Any,
351
+ rows: ReadonlyArray<FlatRow>,
352
+ options: {
353
+ readonly driverMode?: DriverMode
354
+ } = {}
355
+ ): ReadonlyArray<any> => {
356
+ const decodeRow = makeRowDecoder(rendered, plan, options)
357
+ return rows.map((row) => decodeRow(row))
296
358
  }
297
359
 
298
360
  /**
@@ -304,20 +366,23 @@ export const make = <
304
366
  Context = never
305
367
  >(
306
368
  dialect: Dialect,
307
- execute: <PlanValue extends Query.QueryPlan<any, any, any, any, any, any, any, any, any>>(
369
+ execute: <PlanValue extends Query.Plan.Any>(
308
370
  plan: Query.DialectCompatiblePlan<PlanValue, Dialect>
309
371
  ) => Effect.Effect<Query.ResultRows<PlanValue>, Error, Context>
310
372
  ): Executor<Dialect, Error, Context> => ({
311
373
  dialect,
312
374
  execute(plan) {
313
375
  return (execute as any)(plan)
376
+ },
377
+ stream(plan) {
378
+ return Stream.unwrap(Effect.map((execute as any)(plan), (rows: ReadonlyArray<any>) => Stream.fromIterable(rows)))
314
379
  }
315
380
  }) as Executor<Dialect, Error, Context>
316
381
 
317
382
  /**
318
383
  * Constructs a driver from a dialect and execution callback.
319
384
  */
320
- export const driver = <
385
+ export function driver<
321
386
  Dialect extends string,
322
387
  Error = never,
323
388
  Context = never
@@ -326,12 +391,58 @@ export const driver = <
326
391
  execute: <Row>(
327
392
  query: Renderer.RenderedQuery<Row, Dialect>
328
393
  ) => Effect.Effect<ReadonlyArray<FlatRow>, Error, Context>
329
- ): Driver<Dialect, Error, Context> => ({
394
+ ): Driver<Dialect, Error, Context>
395
+ export function driver<
396
+ Dialect extends string,
397
+ Error = never,
398
+ Context = never
399
+ >(
400
+ dialect: Dialect,
401
+ handlers: {
402
+ readonly execute: <Row>(
403
+ query: Renderer.RenderedQuery<Row, Dialect>
404
+ ) => Effect.Effect<ReadonlyArray<FlatRow>, Error, Context>
405
+ readonly stream: <Row>(
406
+ query: Renderer.RenderedQuery<Row, Dialect>
407
+ ) => Stream.Stream<FlatRow, Error, Context>
408
+ }
409
+ ): Driver<Dialect, Error, Context>
410
+ export function driver<
411
+ Dialect extends string,
412
+ Error = never,
413
+ Context = never
414
+ >(
415
+ dialect: Dialect,
416
+ executeOrHandlers:
417
+ | (<Row>(
418
+ query: Renderer.RenderedQuery<Row, Dialect>
419
+ ) => Effect.Effect<ReadonlyArray<FlatRow>, Error, Context>)
420
+ | {
421
+ readonly execute: <Row>(
422
+ query: Renderer.RenderedQuery<Row, Dialect>
423
+ ) => Effect.Effect<ReadonlyArray<FlatRow>, Error, Context>
424
+ readonly stream: <Row>(
425
+ query: Renderer.RenderedQuery<Row, Dialect>
426
+ ) => Stream.Stream<FlatRow, Error, Context>
427
+ }
428
+ ): Driver<Dialect, Error, Context> {
429
+ return {
330
430
  dialect,
331
431
  execute(query) {
332
- return execute(query)
432
+ return typeof executeOrHandlers === "function"
433
+ ? executeOrHandlers(query)
434
+ : executeOrHandlers.execute(query)
435
+ },
436
+ stream(query) {
437
+ if (typeof executeOrHandlers === "function") {
438
+ return Stream.unwrap(
439
+ Effect.map(executeOrHandlers(query), (rows) => Stream.fromIterable(rows))
440
+ )
441
+ }
442
+ return executeOrHandlers.stream(query)
333
443
  }
334
- })
444
+ }
445
+ }
335
446
 
336
447
  /**
337
448
  * Creates an executor by composing a renderer with a rendered-query driver.
@@ -357,20 +468,44 @@ export const fromDriver = <
357
468
  sqlDriver.execute(rendered),
358
469
  (rows) => remapRows<any>(rendered, rows)
359
470
  )
471
+ },
472
+ stream(plan: any) {
473
+ const rendered = renderer.render(plan) as Renderer.RenderedQuery<any, Dialect>
474
+ return Stream.mapChunks(
475
+ sqlDriver.stream(rendered),
476
+ (rows) => Chunk.unsafeFromArray(remapRows<any>(rendered, Chunk.toReadonlyArray(rows)))
477
+ )
360
478
  }
361
479
  }
362
480
  return executor as unknown as Executor<Dialect, Error, Context>
363
481
  }
364
482
 
365
- /**
366
- * Creates an executor backed by `@effect/sql`'s `SqlClient`.
367
- */
483
+ export const streamFromSqlClient = <Dialect extends string>(
484
+ query: Renderer.RenderedQuery<any, Dialect>
485
+ ): Stream.Stream<FlatRow, SqlError.SqlError, SqlClient.SqlClient> =>
486
+ Stream.unwrapScoped(
487
+ Effect.flatMap(SqlClient.SqlClient, (sql) =>
488
+ Effect.flatMap(
489
+ Effect.serviceOption(SqlClient.TransactionConnection),
490
+ Option.match({
491
+ onNone: () => sql.reserve,
492
+ onSome: ([connection]) => Effect.succeed(connection)
493
+ })
494
+ ).pipe(
495
+ Effect.map((connection) => connection.executeStream(query.sql, [...query.params], undefined))
496
+ )
497
+ )
498
+ )
499
+
368
500
  export const fromSqlClient = <Dialect extends string>(
369
501
  renderer: Renderer.Renderer<Dialect>
370
502
  ): Executor<Dialect, unknown, SqlClient.SqlClient> =>
371
- fromDriver(renderer, driver(renderer.dialect, (query) =>
372
- Effect.flatMap(SqlClient.SqlClient, (sql) =>
373
- sql.unsafe<FlatRow>(query.sql, [...query.params]))))
503
+ fromDriver(renderer, driver(renderer.dialect, {
504
+ execute: (query) =>
505
+ Effect.flatMap(SqlClient.SqlClient, (sql) =>
506
+ sql.unsafe<FlatRow>(query.sql, [...query.params])),
507
+ stream: (query) => streamFromSqlClient(query)
508
+ }))
374
509
 
375
510
  /** Runs an effect within the ambient `@effect/sql` transaction service. */
376
511
  export const withTransaction = <A, E, R>(