effect-qb 0.17.0 → 0.20.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.
- package/README.md +11 -1
- package/dist/index.js +9376 -0
- package/dist/mysql.js +4092 -2671
- package/dist/postgres/metadata.js +2589 -1402
- package/dist/postgres.js +3953 -3583
- package/dist/sqlite.js +5527 -4088
- package/dist/standard.js +9330 -0
- package/package.json +9 -4
- package/src/casing.ts +71 -0
- package/src/index.ts +2 -0
- package/src/internal/casing.ts +89 -0
- package/src/internal/coercion/rules.ts +13 -1
- package/src/internal/column-state.d.ts +3 -3
- package/src/internal/column-state.ts +24 -18
- package/src/internal/column.ts +52 -15
- package/src/internal/datatypes/define.ts +7 -1
- package/src/internal/datatypes/enrich.ts +23 -0
- package/src/internal/datatypes/lookup.ts +81 -25
- package/src/internal/datatypes/matrix.ts +903 -0
- package/src/internal/datatypes/shape.ts +2 -0
- package/src/internal/derived-table.ts +4 -36
- package/src/{mysql/internal/sql-expression-renderer.ts → internal/dialect-renderers/mysql.ts} +552 -361
- package/src/{postgres/internal/sql-expression-renderer.ts → internal/dialect-renderers/postgres.ts} +657 -400
- package/src/{sqlite/internal/sql-expression-renderer.ts → internal/dialect-renderers/sqlite.ts} +505 -347
- package/src/internal/dialect.ts +36 -1
- package/src/internal/dsl-mutation-runtime.ts +12 -162
- package/src/internal/dsl-plan-runtime.ts +10 -138
- package/src/internal/dsl-query-runtime.ts +5 -79
- package/src/internal/dsl-transaction-ddl-runtime.ts +41 -65
- package/src/internal/executor.ts +66 -49
- package/src/internal/grouping-key.ts +87 -20
- package/src/internal/implication-runtime.ts +1 -1
- package/src/internal/json/path-access.ts +351 -0
- package/src/internal/predicate/runtime.ts +3 -0
- package/src/internal/query.d.ts +39 -12
- package/src/internal/query.ts +65 -26
- package/src/internal/renderer.ts +26 -14
- package/src/internal/runtime/driver-value-mapping.ts +3 -3
- package/src/internal/runtime/normalize.ts +12 -5
- package/src/internal/runtime/schema.ts +28 -38
- package/src/internal/runtime/value.ts +20 -23
- package/src/internal/scalar.d.ts +1 -1
- package/src/internal/scalar.ts +8 -2
- package/src/internal/schema-derivation.d.ts +12 -61
- package/src/internal/schema-derivation.ts +95 -43
- package/src/internal/schema-expression.ts +2 -2
- package/src/internal/sql-expression-renderer.ts +19 -0
- package/src/internal/standard-dsl.ts +6978 -0
- package/src/internal/table-options.ts +126 -66
- package/src/internal/table.ts +819 -237
- package/src/mysql/column-extension.ts +3 -0
- package/src/mysql/column.ts +14 -15
- package/src/mysql/datatypes/index.ts +4 -2
- package/src/mysql/datatypes/spec.ts +6 -176
- package/src/mysql/errors/normalize.ts +0 -1
- package/src/mysql/executor.ts +11 -11
- package/src/mysql/function/temporal.ts +1 -1
- package/src/mysql/internal/dialect.ts +9 -4
- package/src/mysql/internal/dsl.ts +334 -170
- package/src/mysql/internal/renderer.ts +6 -2
- package/src/mysql/json.ts +7 -2
- package/src/mysql/query-extension.ts +16 -0
- package/src/mysql/renderer.ts +37 -3
- package/src/mysql/type.ts +60 -0
- package/src/mysql.ts +7 -13
- package/src/postgres/check.ts +1 -0
- package/src/postgres/column-extension.ts +28 -0
- package/src/postgres/column.ts +13 -19
- package/src/postgres/datatypes/index.d.ts +2 -1
- package/src/postgres/datatypes/index.ts +4 -2
- package/src/postgres/datatypes/spec.ts +6 -260
- package/src/postgres/errors/normalize.ts +0 -1
- package/src/postgres/executor.ts +11 -11
- package/src/postgres/foreign-key.ts +24 -0
- package/src/postgres/function/core.ts +1 -3
- package/src/postgres/function/index.ts +1 -17
- package/src/postgres/function/temporal.ts +1 -1
- package/src/postgres/index.ts +1 -0
- package/src/postgres/internal/dialect.ts +9 -4
- package/src/postgres/internal/dsl.ts +328 -179
- package/src/postgres/internal/renderer.ts +6 -2
- package/src/postgres/internal/schema-ddl.ts +22 -10
- package/src/postgres/internal/schema-model.ts +238 -7
- package/src/postgres/json-extension.ts +7 -0
- package/src/postgres/json.ts +762 -173
- package/src/postgres/jsonb.ts +37 -0
- package/src/postgres/primary-key.ts +24 -0
- package/src/postgres/query-extension.ts +2 -0
- package/src/postgres/renderer.ts +37 -3
- package/src/postgres/schema-management.ts +13 -13
- package/src/postgres/schema.ts +106 -15
- package/src/postgres/table.ts +205 -538
- package/src/postgres/type.ts +93 -10
- package/src/postgres/unique.ts +32 -0
- package/src/postgres.ts +20 -16
- package/src/sqlite/column-extension.ts +3 -0
- package/src/sqlite/column.ts +14 -15
- package/src/sqlite/datatypes/index.ts +4 -2
- package/src/sqlite/datatypes/spec.ts +6 -94
- package/src/sqlite/errors/normalize.ts +0 -1
- package/src/sqlite/executor.ts +11 -11
- package/src/sqlite/function/temporal.ts +1 -1
- package/src/sqlite/internal/dialect.ts +9 -4
- package/src/sqlite/internal/dsl.ts +307 -159
- package/src/sqlite/internal/renderer.ts +6 -2
- package/src/sqlite/json.ts +8 -2
- package/src/sqlite/query-extension.ts +2 -0
- package/src/sqlite/renderer.ts +37 -3
- package/src/sqlite/type.ts +40 -0
- package/src/sqlite.ts +7 -13
- package/src/standard/cast.ts +113 -0
- package/src/standard/check.ts +17 -0
- package/src/standard/column.ts +163 -0
- package/src/standard/datatypes/index.ts +83 -0
- package/src/standard/datatypes/spec.ts +12 -0
- package/src/standard/dialect.ts +40 -0
- package/src/standard/foreign-key.ts +37 -0
- package/src/standard/function/aggregate.ts +2 -0
- package/src/standard/function/core.ts +2 -0
- package/src/standard/function/index.ts +18 -0
- package/src/standard/function/string.ts +2 -0
- package/src/standard/function/temporal.ts +78 -0
- package/src/standard/function/window.ts +2 -0
- package/src/standard/index.ts +17 -0
- package/src/standard/internal/renderer.ts +45 -0
- package/src/standard/json.ts +883 -0
- package/src/standard/primary-key.ts +17 -0
- package/src/standard/query.ts +152 -0
- package/src/standard/renderer.ts +49 -0
- package/src/standard/table.ts +151 -0
- package/src/standard/unique.ts +17 -0
- package/src/standard.ts +32 -0
- package/src/internal/aggregation-validation.ts +0 -57
- package/src/internal/table.d.ts +0 -173
- package/src/mysql/table.ts +0 -183
- package/src/postgres/cast.ts +0 -45
- package/src/sqlite/table.ts +0 -183
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as Plan from "./row-set.js"
|
|
2
|
-
import * as Table from "./table.js"
|
|
3
2
|
|
|
4
3
|
type DslTransactionDdlRuntimeContext = {
|
|
5
4
|
readonly profile: {
|
|
@@ -11,16 +10,13 @@ type DslTransactionDdlRuntimeContext = {
|
|
|
11
10
|
readonly defaultIndexName: (tableName: string, columns: readonly string[], unique: boolean) => string
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
export const renderTransactionIsolationLevel = (
|
|
14
|
+
isolationLevel: unknown
|
|
15
|
+
): string => {
|
|
17
16
|
if (isolationLevel === undefined) {
|
|
18
17
|
return ""
|
|
19
18
|
}
|
|
20
|
-
|
|
21
|
-
throw new Error("Unsupported transaction isolation level")
|
|
22
|
-
}
|
|
23
|
-
return `isolation level ${isolationLevel}`
|
|
19
|
+
return `isolation level ${isolationLevel as string}`
|
|
24
20
|
}
|
|
25
21
|
|
|
26
22
|
export const expectDdlClauseKind = <
|
|
@@ -28,53 +24,29 @@ export const expectDdlClauseKind = <
|
|
|
28
24
|
Kind extends Ddl["kind"]
|
|
29
25
|
>(
|
|
30
26
|
ddl: Ddl | undefined,
|
|
31
|
-
|
|
32
|
-
): Extract<Ddl, { readonly kind: Kind }> =>
|
|
33
|
-
|
|
34
|
-
throw new Error("Unsupported DDL statement kind")
|
|
35
|
-
}
|
|
36
|
-
return ddl as Extract<Ddl, { readonly kind: Kind }>
|
|
37
|
-
}
|
|
27
|
+
_kind: Kind
|
|
28
|
+
): Extract<Ddl, { readonly kind: Kind }> =>
|
|
29
|
+
ddl as Extract<Ddl, { readonly kind: Kind }>
|
|
38
30
|
|
|
39
31
|
export const expectTruncateClause = <
|
|
40
32
|
Truncate extends { readonly kind: string }
|
|
41
33
|
>(
|
|
42
34
|
truncate: Truncate | undefined
|
|
43
|
-
): Extract<Truncate, { readonly kind: "truncate" }> =>
|
|
44
|
-
|
|
45
|
-
throw new Error("Unsupported truncate statement kind")
|
|
46
|
-
}
|
|
47
|
-
return truncate as Extract<Truncate, { readonly kind: "truncate" }>
|
|
48
|
-
}
|
|
35
|
+
): Extract<Truncate, { readonly kind: "truncate" }> =>
|
|
36
|
+
truncate as Extract<Truncate, { readonly kind: "truncate" }>
|
|
49
37
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContext) => {
|
|
55
|
-
const isRecord = (value: unknown): value is Record<PropertyKey, unknown> =>
|
|
56
|
-
typeof value === "object" && value !== null
|
|
57
|
-
|
|
58
|
-
const assertTableTarget = (target: unknown, apiName: string): void => {
|
|
59
|
-
if (!isRecord(target) || !(Table.TypeId in target) || !(Plan.TypeId in target)) {
|
|
60
|
-
throw new Error(`${apiName}(...) requires a table target`)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
38
|
+
export const normalizeStatementFlag = (value: unknown): boolean =>
|
|
39
|
+
(value as boolean | undefined) ?? false
|
|
63
40
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (!(columnName in fields)) {
|
|
71
|
-
throw new Error(`effect-qb: unknown index column '${columnName}'`)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
41
|
+
export const normalizeStatementIdentifier = (
|
|
42
|
+
_apiName: string,
|
|
43
|
+
_identifierName: string,
|
|
44
|
+
value: unknown
|
|
45
|
+
): string =>
|
|
46
|
+
value as string
|
|
75
47
|
|
|
48
|
+
export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContext) => {
|
|
76
49
|
const transaction = (options: { readonly isolationLevel?: any; readonly readOnly?: boolean } = {}) => {
|
|
77
|
-
validateIsolationLevel(options.isolationLevel)
|
|
78
50
|
return ctx.makePlan({
|
|
79
51
|
selection: {},
|
|
80
52
|
required: [],
|
|
@@ -134,8 +106,8 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
|
|
|
134
106
|
orderBy: []
|
|
135
107
|
}, undefined, "transaction", "rollback")
|
|
136
108
|
|
|
137
|
-
const savepoint = (name: string) =>
|
|
138
|
-
ctx.makePlan({
|
|
109
|
+
const savepoint = (name: string) => {
|
|
110
|
+
return ctx.makePlan({
|
|
139
111
|
selection: {},
|
|
140
112
|
required: [],
|
|
141
113
|
available: {},
|
|
@@ -153,9 +125,10 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
|
|
|
153
125
|
groupBy: [],
|
|
154
126
|
orderBy: []
|
|
155
127
|
}, undefined, "transaction", "savepoint")
|
|
128
|
+
}
|
|
156
129
|
|
|
157
|
-
const rollbackTo = (name: string) =>
|
|
158
|
-
ctx.makePlan({
|
|
130
|
+
const rollbackTo = (name: string) => {
|
|
131
|
+
return ctx.makePlan({
|
|
159
132
|
selection: {},
|
|
160
133
|
required: [],
|
|
161
134
|
available: {},
|
|
@@ -173,9 +146,10 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
|
|
|
173
146
|
groupBy: [],
|
|
174
147
|
orderBy: []
|
|
175
148
|
}, undefined, "transaction", "rollbackTo")
|
|
149
|
+
}
|
|
176
150
|
|
|
177
|
-
const releaseSavepoint = (name: string) =>
|
|
178
|
-
ctx.makePlan({
|
|
151
|
+
const releaseSavepoint = (name: string) => {
|
|
152
|
+
return ctx.makePlan({
|
|
179
153
|
selection: {},
|
|
180
154
|
required: [],
|
|
181
155
|
available: {},
|
|
@@ -193,9 +167,10 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
|
|
|
193
167
|
groupBy: [],
|
|
194
168
|
orderBy: []
|
|
195
169
|
}, undefined, "transaction", "releaseSavepoint")
|
|
170
|
+
}
|
|
196
171
|
|
|
197
172
|
const createTable = (target: any, options: { readonly ifNotExists?: boolean } = {}) => {
|
|
198
|
-
|
|
173
|
+
const ifNotExists = normalizeStatementFlag(options.ifNotExists)
|
|
199
174
|
const { sourceName, sourceBaseName } = ctx.targetSourceDetails(target)
|
|
200
175
|
return ctx.makePlan({
|
|
201
176
|
selection: {},
|
|
@@ -213,7 +188,7 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
|
|
|
213
188
|
},
|
|
214
189
|
ddl: {
|
|
215
190
|
kind: "createTable",
|
|
216
|
-
ifNotExists
|
|
191
|
+
ifNotExists
|
|
217
192
|
},
|
|
218
193
|
where: [],
|
|
219
194
|
having: [],
|
|
@@ -224,7 +199,7 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
|
|
|
224
199
|
}
|
|
225
200
|
|
|
226
201
|
const dropTable = (target: any, options: { readonly ifExists?: boolean } = {}) => {
|
|
227
|
-
|
|
202
|
+
const ifExists = normalizeStatementFlag(options.ifExists)
|
|
228
203
|
const { sourceName, sourceBaseName } = ctx.targetSourceDetails(target)
|
|
229
204
|
return ctx.makePlan({
|
|
230
205
|
selection: {},
|
|
@@ -242,7 +217,7 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
|
|
|
242
217
|
},
|
|
243
218
|
ddl: {
|
|
244
219
|
kind: "dropTable",
|
|
245
|
-
ifExists
|
|
220
|
+
ifExists
|
|
246
221
|
},
|
|
247
222
|
where: [],
|
|
248
223
|
having: [],
|
|
@@ -253,9 +228,10 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
|
|
|
253
228
|
}
|
|
254
229
|
|
|
255
230
|
const createIndex = (target: any, columns: string | readonly string[], options: { readonly name?: string; readonly unique?: boolean; readonly ifNotExists?: boolean } = {}) => {
|
|
256
|
-
assertTableTarget(target, "createIndex")
|
|
257
231
|
const normalizedColumns = ctx.normalizeColumnList(columns)
|
|
258
|
-
|
|
232
|
+
const unique = normalizeStatementFlag(options.unique)
|
|
233
|
+
const ifNotExists = normalizeStatementFlag(options.ifNotExists)
|
|
234
|
+
const name = options.name
|
|
259
235
|
const { sourceName, sourceBaseName } = ctx.targetSourceDetails(target)
|
|
260
236
|
return ctx.makePlan({
|
|
261
237
|
selection: {},
|
|
@@ -273,10 +249,10 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
|
|
|
273
249
|
},
|
|
274
250
|
ddl: {
|
|
275
251
|
kind: "createIndex",
|
|
276
|
-
name:
|
|
252
|
+
name: name ?? ctx.defaultIndexName(sourceBaseName, normalizedColumns, unique),
|
|
277
253
|
columns: normalizedColumns,
|
|
278
|
-
unique
|
|
279
|
-
ifNotExists
|
|
254
|
+
unique,
|
|
255
|
+
ifNotExists
|
|
280
256
|
},
|
|
281
257
|
where: [],
|
|
282
258
|
having: [],
|
|
@@ -287,9 +263,9 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
|
|
|
287
263
|
}
|
|
288
264
|
|
|
289
265
|
const dropIndex = (target: any, columns: string | readonly string[], options: { readonly name?: string; readonly ifExists?: boolean } = {}) => {
|
|
290
|
-
assertTableTarget(target, "dropIndex")
|
|
291
266
|
const normalizedColumns = ctx.normalizeColumnList(columns)
|
|
292
|
-
|
|
267
|
+
const ifExists = normalizeStatementFlag(options.ifExists)
|
|
268
|
+
const name = options.name
|
|
293
269
|
const { sourceName, sourceBaseName } = ctx.targetSourceDetails(target)
|
|
294
270
|
return ctx.makePlan({
|
|
295
271
|
selection: {},
|
|
@@ -307,8 +283,8 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
|
|
|
307
283
|
},
|
|
308
284
|
ddl: {
|
|
309
285
|
kind: "dropIndex",
|
|
310
|
-
name:
|
|
311
|
-
ifExists
|
|
286
|
+
name: name ?? ctx.defaultIndexName(sourceBaseName, normalizedColumns, false),
|
|
287
|
+
ifExists
|
|
312
288
|
},
|
|
313
289
|
where: [],
|
|
314
290
|
having: [],
|
package/src/internal/executor.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as Chunk from "effect/Chunk"
|
|
2
2
|
import * as Effect from "effect/Effect"
|
|
3
|
+
import * as Exit from "effect/Exit"
|
|
3
4
|
import * as Option from "effect/Option"
|
|
4
5
|
import * as Schema from "effect/Schema"
|
|
5
|
-
import * as SqlClient from "
|
|
6
|
-
import * as SqlError from "
|
|
6
|
+
import * as SqlClient from "effect/unstable/sql/SqlClient"
|
|
7
|
+
import * as SqlError from "effect/unstable/sql/SqlError"
|
|
7
8
|
import * as Stream from "effect/Stream"
|
|
8
9
|
|
|
9
10
|
import * as Expression from "./scalar.js"
|
|
@@ -44,6 +45,10 @@ export interface RowDecodeError {
|
|
|
44
45
|
readonly normalized?: unknown
|
|
45
46
|
readonly stage: "normalize" | "schema"
|
|
46
47
|
readonly cause: unknown
|
|
48
|
+
readonly schemaError?: {
|
|
49
|
+
readonly message: string
|
|
50
|
+
readonly issue: unknown
|
|
51
|
+
}
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
/**
|
|
@@ -183,26 +188,35 @@ const makeRowDecodeError = (
|
|
|
183
188
|
stage: RowDecodeError["stage"],
|
|
184
189
|
cause: unknown,
|
|
185
190
|
normalized?: unknown
|
|
186
|
-
): RowDecodeError =>
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
191
|
+
): RowDecodeError => {
|
|
192
|
+
const schemaError = Schema.isSchemaError(cause)
|
|
193
|
+
? {
|
|
194
|
+
message: cause.message,
|
|
195
|
+
issue: cause.issue
|
|
196
|
+
}
|
|
197
|
+
: undefined
|
|
198
|
+
return {
|
|
199
|
+
_tag: "RowDecodeError",
|
|
200
|
+
message: stage === "normalize"
|
|
201
|
+
? `Failed to normalize projection '${projection.alias}'`
|
|
202
|
+
: `Failed to decode projection '${projection.alias}' against its runtime schema`,
|
|
203
|
+
dialect: rendered.dialect,
|
|
204
|
+
query: {
|
|
205
|
+
sql: rendered.sql,
|
|
206
|
+
params: rendered.params
|
|
207
|
+
},
|
|
208
|
+
projection: {
|
|
209
|
+
alias: projection.alias,
|
|
210
|
+
path: projection.path
|
|
211
|
+
},
|
|
212
|
+
dbType: expression[Expression.TypeId].dbType,
|
|
213
|
+
raw,
|
|
214
|
+
normalized,
|
|
215
|
+
stage,
|
|
216
|
+
cause,
|
|
217
|
+
...(schemaError === undefined ? {} : { schemaError })
|
|
218
|
+
}
|
|
219
|
+
}
|
|
206
220
|
|
|
207
221
|
const hasOptionalSourceDependency = (
|
|
208
222
|
expression: Expression.Any,
|
|
@@ -249,7 +263,7 @@ const dbTypeAllowsTopLevelJsonNull = (
|
|
|
249
263
|
}
|
|
250
264
|
|
|
251
265
|
const schemaAcceptsNull = (
|
|
252
|
-
schema: Schema.
|
|
266
|
+
schema: Schema.Top | undefined
|
|
253
267
|
): boolean =>
|
|
254
268
|
schema !== undefined && (Schema.is(schema) as (value: unknown) => boolean)(null)
|
|
255
269
|
|
|
@@ -325,15 +339,20 @@ const decodeProjectionValue = (
|
|
|
325
339
|
return normalized
|
|
326
340
|
}
|
|
327
341
|
|
|
328
|
-
if ((Schema.is(schema as Schema.
|
|
342
|
+
if ((Schema.is(schema as Schema.Top) as (value: unknown) => boolean)(normalized)) {
|
|
329
343
|
return normalized
|
|
330
344
|
}
|
|
331
345
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
throw makeRowDecodeError(rendered, projection, expression, raw, "schema", cause, normalized)
|
|
346
|
+
const decoded = (Schema.decodeUnknownExit as any)(schema)(normalized)
|
|
347
|
+
if (Exit.isSuccess(decoded)) {
|
|
348
|
+
return decoded.value
|
|
336
349
|
}
|
|
350
|
+
|
|
351
|
+
const cause = Option.match(Exit.findErrorOption(decoded), {
|
|
352
|
+
onNone: () => decoded.cause,
|
|
353
|
+
onSome: (schemaError) => schemaError
|
|
354
|
+
})
|
|
355
|
+
throw makeRowDecodeError(rendered, projection, expression, raw, "schema", cause, normalized)
|
|
337
356
|
}
|
|
338
357
|
|
|
339
358
|
export const makeRowDecoder = (
|
|
@@ -390,7 +409,7 @@ export const decodeChunk = (
|
|
|
390
409
|
} = {}
|
|
391
410
|
): Chunk.Chunk<any> => {
|
|
392
411
|
const decodeRow = makeRowDecoder(rendered, plan, options)
|
|
393
|
-
return Chunk.
|
|
412
|
+
return Chunk.fromIterable(Chunk.toReadonlyArray(rows).map((row) => decodeRow(row)))
|
|
394
413
|
}
|
|
395
414
|
|
|
396
415
|
export const decodeRows = (
|
|
@@ -509,33 +528,37 @@ export const fromDriver = <
|
|
|
509
528
|
renderer: Renderer.Renderer<Dialect>,
|
|
510
529
|
sqlDriver: Driver<Dialect, Error, Context>
|
|
511
530
|
): Executor<Dialect, Error, Context> => {
|
|
512
|
-
const executor = {
|
|
531
|
+
const executor: Executor<Dialect, Error, Context> = {
|
|
513
532
|
dialect: renderer.dialect,
|
|
514
|
-
execute
|
|
533
|
+
execute<PlanValue extends Query.Plan.Any>(
|
|
534
|
+
plan: Query.DialectCompatiblePlan<PlanValue, Dialect>
|
|
535
|
+
) {
|
|
515
536
|
const rendered = renderer.render(plan) as Renderer.RenderedQuery<any, Dialect>
|
|
516
537
|
return Effect.map(
|
|
517
538
|
sqlDriver.execute(rendered),
|
|
518
539
|
(rows) => remapRows<any>(rendered, rows)
|
|
519
|
-
)
|
|
540
|
+
) as Effect.Effect<Query.ResultRows<PlanValue>, Error, Context>
|
|
520
541
|
},
|
|
521
|
-
stream
|
|
542
|
+
stream<PlanValue extends Query.Plan.Any>(
|
|
543
|
+
plan: Query.DialectCompatiblePlan<PlanValue, Dialect>
|
|
544
|
+
) {
|
|
522
545
|
const rendered = renderer.render(plan) as Renderer.RenderedQuery<any, Dialect>
|
|
523
|
-
return Stream.
|
|
546
|
+
return Stream.mapArray(
|
|
524
547
|
sqlDriver.stream(rendered),
|
|
525
|
-
(rows) =>
|
|
526
|
-
)
|
|
548
|
+
(rows) => remapRows<any>(rendered, rows) as never
|
|
549
|
+
) as Stream.Stream<Query.ResultRow<PlanValue>, Error, Context>
|
|
527
550
|
}
|
|
528
551
|
}
|
|
529
|
-
return executor
|
|
552
|
+
return executor
|
|
530
553
|
}
|
|
531
554
|
|
|
532
555
|
export const streamFromSqlClient = <Dialect extends string>(
|
|
533
556
|
query: Renderer.RenderedQuery<any, Dialect>
|
|
534
557
|
): Stream.Stream<FlatRow, SqlError.SqlError, SqlClient.SqlClient> =>
|
|
535
|
-
Stream.
|
|
558
|
+
Stream.unwrap(
|
|
536
559
|
Effect.flatMap(SqlClient.SqlClient, (sql) =>
|
|
537
560
|
Effect.flatMap(
|
|
538
|
-
Effect.serviceOption(
|
|
561
|
+
Effect.serviceOption(sql.transactionService),
|
|
539
562
|
Option.match({
|
|
540
563
|
onNone: () => sql.reserve,
|
|
541
564
|
onSome: ([connection]) => Effect.succeed(connection)
|
|
@@ -556,19 +579,13 @@ export const fromSqlClient = <Dialect extends string>(
|
|
|
556
579
|
stream: (query) => streamFromSqlClient(query)
|
|
557
580
|
}))
|
|
558
581
|
|
|
559
|
-
/** Runs an effect within the ambient `@effect/sql` transaction service. */
|
|
560
|
-
export const withTransaction = <A, E, R>(
|
|
561
|
-
effect: Effect.Effect<A, E, R>
|
|
562
|
-
): Effect.Effect<A, E | SqlError.SqlError, R | SqlClient.SqlClient> =>
|
|
563
|
-
Effect.flatMap(SqlClient.SqlClient, (sql) => sql.withTransaction(effect))
|
|
564
|
-
|
|
565
582
|
/**
|
|
566
|
-
* Runs an effect
|
|
583
|
+
* Runs an effect within the ambient `effect/unstable/sql` transaction service.
|
|
567
584
|
*
|
|
568
|
-
*
|
|
569
|
-
*
|
|
585
|
+
* Nested calls rely on the underlying client transaction implementation for
|
|
586
|
+
* savepoint behavior.
|
|
570
587
|
*/
|
|
571
|
-
export const
|
|
588
|
+
export const withTransaction = <A, E, R>(
|
|
572
589
|
effect: Effect.Effect<A, E, R>
|
|
573
590
|
): Effect.Effect<A, E | SqlError.SqlError, R | SqlClient.SqlClient> =>
|
|
574
591
|
Effect.flatMap(SqlClient.SqlClient, (sql) => sql.withTransaction(effect))
|
|
@@ -21,7 +21,9 @@ const subqueryPlanGroupingKey = (plan: unknown): string => {
|
|
|
21
21
|
|
|
22
22
|
const literalGroupingKey = (value: unknown): string => {
|
|
23
23
|
if (value instanceof Date) {
|
|
24
|
-
return
|
|
24
|
+
return Number.isNaN(value.getTime())
|
|
25
|
+
? "date:invalid"
|
|
26
|
+
: `date:${value.toISOString()}`
|
|
25
27
|
}
|
|
26
28
|
if (value === null) {
|
|
27
29
|
return "null"
|
|
@@ -44,6 +46,30 @@ const isExpression = (value: unknown): value is Expression.Any =>
|
|
|
44
46
|
const expressionGroupingKey = (value: unknown): string =>
|
|
45
47
|
isExpression(value) ? groupingKeyOfExpression(value) : "missing"
|
|
46
48
|
|
|
49
|
+
const requiredExpressionGroupingKey = (
|
|
50
|
+
_functionName: string,
|
|
51
|
+
value: unknown
|
|
52
|
+
): string => groupingKeyOfExpression(value as Expression.Any)
|
|
53
|
+
|
|
54
|
+
const requiredBinaryExpressionGroupingKey = (
|
|
55
|
+
_functionName: string,
|
|
56
|
+
left: unknown,
|
|
57
|
+
right: unknown
|
|
58
|
+
): string => `${groupingKeyOfExpression(left as Expression.Any)},${groupingKeyOfExpression(right as Expression.Any)}`
|
|
59
|
+
|
|
60
|
+
const functionCallArgsGroupingKey = (args: unknown): string =>
|
|
61
|
+
(args as readonly Expression.Any[]).map(groupingKeyOfExpression).join(",")
|
|
62
|
+
|
|
63
|
+
const variadicGroupingKey = (
|
|
64
|
+
_functionName: string,
|
|
65
|
+
values: unknown
|
|
66
|
+
): string => (values as readonly Expression.Any[]).map(groupingKeyOfExpression).join(",")
|
|
67
|
+
|
|
68
|
+
const castTargetGroupingKey = (target: unknown): string => {
|
|
69
|
+
const typed = target as { readonly dialect?: string; readonly kind?: string } | null | undefined
|
|
70
|
+
return `${typed?.dialect}:${typed?.kind}`
|
|
71
|
+
}
|
|
72
|
+
|
|
47
73
|
const escapeGroupingText = (value: string): string =>
|
|
48
74
|
value
|
|
49
75
|
.replace(/\\/g, "\\\\")
|
|
@@ -52,6 +78,30 @@ const escapeGroupingText = (value: string): string =>
|
|
|
52
78
|
.replace(/=/g, "\\=")
|
|
53
79
|
.replace(/>/g, "\\>")
|
|
54
80
|
|
|
81
|
+
const functionCallNameGroupingKey = (name: unknown): string =>
|
|
82
|
+
escapeGroupingText(name as string)
|
|
83
|
+
|
|
84
|
+
const quantifiedComparisonGroupingName = (
|
|
85
|
+
kind: "comparisonAny" | "comparisonAll"
|
|
86
|
+
): "compareAny" | "compareAll" =>
|
|
87
|
+
kind === "comparisonAny" ? "compareAny" : "compareAll"
|
|
88
|
+
|
|
89
|
+
const caseGroupingKey = (
|
|
90
|
+
branches: unknown,
|
|
91
|
+
fallback: unknown
|
|
92
|
+
): string => {
|
|
93
|
+
const typedBranches = branches as readonly ExpressionAst.CaseBranchNode[]
|
|
94
|
+
return `case(${typedBranches.map((branch) =>
|
|
95
|
+
`when:${groupingKeyOfExpression(branch.when)}=>${groupingKeyOfExpression(branch.then)}`).join("|")};else:${groupingKeyOfExpression(fallback as Expression.Any)})`
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const collationGroupingKey = (collation: unknown): string => {
|
|
99
|
+
if (Array.isArray(collation)) {
|
|
100
|
+
return collation.map((part) => escapeGroupingText(String(part))).join(",")
|
|
101
|
+
}
|
|
102
|
+
return escapeGroupingText(String(collation))
|
|
103
|
+
}
|
|
104
|
+
|
|
55
105
|
const jsonSegmentGroupingKey = (segment: unknown): string => {
|
|
56
106
|
if (segment !== null && typeof segment === "object" && "kind" in segment) {
|
|
57
107
|
switch ((segment as { readonly kind: string }).kind) {
|
|
@@ -78,8 +128,15 @@ const jsonSegmentGroupingKey = (segment: unknown): string => {
|
|
|
78
128
|
return "unknown"
|
|
79
129
|
}
|
|
80
130
|
|
|
81
|
-
const jsonPathGroupingKey = (segments:
|
|
82
|
-
(segments
|
|
131
|
+
const jsonPathGroupingKey = (segments: unknown): string => {
|
|
132
|
+
if (segments === undefined) {
|
|
133
|
+
return ""
|
|
134
|
+
}
|
|
135
|
+
if (!Array.isArray(segments)) {
|
|
136
|
+
return "unknown"
|
|
137
|
+
}
|
|
138
|
+
return segments.map(jsonSegmentGroupingKey).join(",")
|
|
139
|
+
}
|
|
83
140
|
|
|
84
141
|
const isJsonPath = (value: unknown): value is JsonPath.Path =>
|
|
85
142
|
value !== null && typeof value === "object" && JsonPath.TypeId in value
|
|
@@ -97,9 +154,20 @@ const jsonOpaquePathGroupingKey = (value: unknown): string => {
|
|
|
97
154
|
return "jsonpath:unknown"
|
|
98
155
|
}
|
|
99
156
|
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
157
|
+
const jsonKeysGroupingKey = (keys: unknown): string => {
|
|
158
|
+
if (!Array.isArray(keys) || keys.length === 0) {
|
|
159
|
+
return ""
|
|
160
|
+
}
|
|
161
|
+
return keys.map((key) => escapeGroupingText(String(key))).join(",")
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const jsonBuildObjectGroupingKey = (entries: unknown): string => {
|
|
165
|
+
return (entries as readonly { readonly key: string; readonly value: Expression.Any }[]).map((entry) =>
|
|
166
|
+
`${escapeGroupingText(entry.key)}=>${groupingKeyOfExpression(entry.value)}`).join("|")
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const jsonBuildArrayGroupingKey = (values: unknown): string =>
|
|
170
|
+
(values as readonly Expression.Any[]).map(groupingKeyOfExpression).join(",")
|
|
103
171
|
|
|
104
172
|
export const groupingKeyOfExpression = (expression: Expression.Any): string => {
|
|
105
173
|
const ast = (expression as Expression.Any & {
|
|
@@ -111,11 +179,11 @@ export const groupingKeyOfExpression = (expression: Expression.Any): string => {
|
|
|
111
179
|
case "literal":
|
|
112
180
|
return `literal:${literalGroupingKey(ast.value)}`
|
|
113
181
|
case "cast":
|
|
114
|
-
return `cast(${
|
|
182
|
+
return `cast(${requiredExpressionGroupingKey("cast", ast.value)} as ${castTargetGroupingKey(ast.target)})`
|
|
115
183
|
case "collate":
|
|
116
|
-
return `collate(${
|
|
184
|
+
return `collate(${requiredExpressionGroupingKey("collate", ast.value)},${collationGroupingKey(ast.collation)})`
|
|
117
185
|
case "function":
|
|
118
|
-
return `function(${
|
|
186
|
+
return `function(${functionCallNameGroupingKey(ast.name)},${functionCallArgsGroupingKey(ast.args)})`
|
|
119
187
|
case "isNull":
|
|
120
188
|
case "isNotNull":
|
|
121
189
|
case "not":
|
|
@@ -124,7 +192,7 @@ export const groupingKeyOfExpression = (expression: Expression.Any): string => {
|
|
|
124
192
|
case "count":
|
|
125
193
|
case "max":
|
|
126
194
|
case "min":
|
|
127
|
-
return `${ast.kind}(${
|
|
195
|
+
return `${ast.kind}(${requiredExpressionGroupingKey(ast.kind, ast.value)})`
|
|
128
196
|
case "eq":
|
|
129
197
|
case "neq":
|
|
130
198
|
case "lt":
|
|
@@ -142,7 +210,7 @@ export const groupingKeyOfExpression = (expression: Expression.Any): string => {
|
|
|
142
210
|
case "contains":
|
|
143
211
|
case "containedBy":
|
|
144
212
|
case "overlaps":
|
|
145
|
-
return `${ast.kind}(${
|
|
213
|
+
return `${ast.kind}(${requiredBinaryExpressionGroupingKey(ast.kind, ast.left, ast.right)})`
|
|
146
214
|
case "and":
|
|
147
215
|
case "or":
|
|
148
216
|
case "coalesce":
|
|
@@ -150,19 +218,18 @@ export const groupingKeyOfExpression = (expression: Expression.Any): string => {
|
|
|
150
218
|
case "in":
|
|
151
219
|
case "notIn":
|
|
152
220
|
case "between":
|
|
153
|
-
return `${ast.kind}(${ast.values
|
|
221
|
+
return `${ast.kind}(${variadicGroupingKey(ast.kind, ast.values)})`
|
|
154
222
|
case "case":
|
|
155
|
-
return
|
|
156
|
-
`when:${groupingKeyOfExpression(branch.when)}=>${groupingKeyOfExpression(branch.then)}`).join("|")};else:${groupingKeyOfExpression(ast.else)})`
|
|
223
|
+
return caseGroupingKey(ast.branches, ast.else)
|
|
157
224
|
case "exists":
|
|
158
225
|
return `exists(${subqueryPlanGroupingKey(ast.plan)})`
|
|
159
226
|
case "scalarSubquery":
|
|
160
227
|
return `scalarSubquery(${subqueryPlanGroupingKey(ast.plan)})`
|
|
161
228
|
case "inSubquery":
|
|
162
|
-
return `inSubquery(${
|
|
229
|
+
return `inSubquery(${requiredExpressionGroupingKey("inSubquery", ast.left)},${subqueryPlanGroupingKey(ast.plan)})`
|
|
163
230
|
case "comparisonAny":
|
|
164
231
|
case "comparisonAll":
|
|
165
|
-
return `${ast.kind}(${ast.operator},${
|
|
232
|
+
return `${ast.kind}(${ast.operator},${requiredExpressionGroupingKey(quantifiedComparisonGroupingName(ast.kind), ast.left)},${subqueryPlanGroupingKey(ast.plan)})`
|
|
166
233
|
case "jsonGet":
|
|
167
234
|
case "jsonPath":
|
|
168
235
|
case "jsonAccess":
|
|
@@ -176,7 +243,7 @@ export const groupingKeyOfExpression = (expression: Expression.Any): string => {
|
|
|
176
243
|
case "jsonKeyExists":
|
|
177
244
|
case "jsonHasAnyKeys":
|
|
178
245
|
case "jsonHasAllKeys":
|
|
179
|
-
return `json(${ast.kind},${expressionGroupingKey(ast.base)},${(ast.keys
|
|
246
|
+
return `json(${ast.kind},${expressionGroupingKey(ast.base)},${jsonKeysGroupingKey(ast.keys)})`
|
|
180
247
|
case "jsonConcat":
|
|
181
248
|
case "jsonMerge":
|
|
182
249
|
return `json(${ast.kind},${expressionGroupingKey(ast.left)},${expressionGroupingKey(ast.right)},)`
|
|
@@ -192,9 +259,9 @@ export const groupingKeyOfExpression = (expression: Expression.Any): string => {
|
|
|
192
259
|
case "jsonPathMatch":
|
|
193
260
|
return `json(${ast.kind},${expressionGroupingKey(ast.base)},${jsonOpaquePathGroupingKey(ast.query)})`
|
|
194
261
|
case "jsonBuildObject":
|
|
195
|
-
return `json(${ast.kind},${(ast.entries
|
|
262
|
+
return `json(${ast.kind},${jsonBuildObjectGroupingKey(ast.entries)})`
|
|
196
263
|
case "jsonBuildArray":
|
|
197
|
-
return `json(${ast.kind},${(ast.values
|
|
264
|
+
return `json(${ast.kind},${jsonBuildArrayGroupingKey(ast.values)})`
|
|
198
265
|
case "jsonToJson":
|
|
199
266
|
case "jsonToJsonb":
|
|
200
267
|
case "jsonTypeOf":
|
|
@@ -203,7 +270,7 @@ export const groupingKeyOfExpression = (expression: Expression.Any): string => {
|
|
|
203
270
|
case "jsonStripNulls":
|
|
204
271
|
return `json(${ast.kind},${expressionGroupingKey(ast.value)})`
|
|
205
272
|
default:
|
|
206
|
-
|
|
273
|
+
return `unknown:${typeof ast === "object" && ast !== null && "kind" in ast ? String((ast as { readonly kind: unknown }).kind) : "ast"}`
|
|
207
274
|
}
|
|
208
275
|
}
|
|
209
276
|
|
|
@@ -41,7 +41,7 @@ const collectPresenceWitnesses = (
|
|
|
41
41
|
return
|
|
42
42
|
}
|
|
43
43
|
if (Expression.TypeId in selection && ExpressionAst.TypeId in selection) {
|
|
44
|
-
const expression = selection as
|
|
44
|
+
const expression = selection as AstBackedExpression
|
|
45
45
|
const ast = expression[ExpressionAst.TypeId]
|
|
46
46
|
if (ast.kind === "column" && expression[Expression.TypeId].nullability === "never") {
|
|
47
47
|
output.add(columnPredicateKey(ast.tableName, ast.columnName))
|