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.
- package/README.md +6 -1431
- package/dist/mysql.js +61945 -3611
- package/dist/postgres/metadata.js +2818 -0
- package/dist/postgres.js +9942 -5591
- package/package.json +21 -10
- package/src/internal/aggregation-validation.ts +3 -3
- package/src/internal/case-analysis.d.ts +18 -0
- package/src/internal/case-analysis.ts +4 -4
- package/src/internal/coercion/analysis.d.ts +7 -0
- package/src/internal/{coercion-analysis.ts → coercion/analysis.ts} +3 -3
- package/src/internal/coercion/errors.d.ts +17 -0
- package/src/internal/{coercion-errors.ts → coercion/errors.ts} +1 -1
- package/src/internal/coercion/kind.d.ts +4 -0
- package/src/internal/{coercion-kind.ts → coercion/kind.ts} +2 -2
- package/src/internal/{coercion-normalize.ts → coercion/normalize.ts} +1 -1
- package/src/internal/coercion/rules.d.ts +6 -0
- package/src/internal/{coercion-rules.ts → coercion/rules.ts} +2 -2
- package/src/internal/column-state.d.ts +190 -0
- package/src/internal/column-state.ts +119 -56
- package/src/internal/column.ts +387 -149
- package/src/internal/datatypes/define.d.ts +17 -0
- package/src/internal/datatypes/define.ts +18 -34
- package/src/internal/datatypes/lookup.d.ts +44 -0
- package/src/internal/datatypes/lookup.ts +61 -152
- package/src/internal/datatypes/shape.d.ts +16 -0
- package/src/internal/datatypes/shape.ts +1 -1
- package/src/internal/derived-table.d.ts +4 -0
- package/src/internal/derived-table.ts +21 -16
- package/src/internal/dsl-mutation-runtime.ts +378 -0
- package/src/internal/dsl-plan-runtime.ts +387 -0
- package/src/internal/dsl-query-runtime.ts +160 -0
- package/src/internal/dsl-transaction-ddl-runtime.ts +263 -0
- package/src/internal/executor.ts +173 -38
- package/src/internal/expression-ast.ts +19 -5
- package/src/internal/grouping-key.d.ts +3 -0
- package/src/internal/grouping-key.ts +1 -1
- package/src/internal/implication-runtime.d.ts +15 -0
- package/src/internal/implication-runtime.ts +171 -0
- package/src/internal/json/ast.d.ts +30 -0
- package/src/internal/json/ast.ts +1 -1
- package/src/internal/json/errors.d.ts +8 -0
- package/src/internal/json/path.d.ts +75 -0
- package/src/internal/json/path.ts +1 -1
- package/src/internal/json/types.d.ts +62 -0
- package/src/internal/predicate/analysis.d.ts +20 -0
- package/src/internal/{predicate-analysis.ts → predicate/analysis.ts} +13 -3
- package/src/internal/predicate/atom.d.ts +28 -0
- package/src/internal/{predicate-branches.ts → predicate/branches.ts} +2 -2
- package/src/internal/predicate/context.d.ts +67 -0
- package/src/internal/{predicate-context.ts → predicate/context.ts} +111 -32
- package/src/internal/predicate/formula.d.ts +35 -0
- package/src/internal/{predicate-formula.ts → predicate/formula.ts} +32 -20
- package/src/internal/predicate/key.d.ts +11 -0
- package/src/internal/{predicate-key.ts → predicate/key.ts} +2 -2
- package/src/internal/{predicate-nnf.ts → predicate/nnf.ts} +2 -2
- package/src/internal/predicate/normalize.d.ts +53 -0
- package/src/internal/predicate/normalize.ts +273 -0
- package/src/internal/predicate/runtime.d.ts +31 -0
- package/src/internal/predicate/runtime.ts +679 -0
- package/src/internal/projection-alias.d.ts +13 -0
- package/src/internal/projections.d.ts +31 -0
- package/src/internal/projections.ts +1 -1
- package/src/internal/query-ast.d.ts +217 -0
- package/src/internal/query-ast.ts +1 -1
- package/src/internal/query-requirements.d.ts +20 -0
- package/src/internal/query.d.ts +775 -0
- package/src/internal/query.ts +767 -275
- package/src/internal/renderer.ts +7 -21
- package/src/internal/row-set.d.ts +53 -0
- package/src/internal/{plan.ts → row-set.ts} +23 -11
- package/src/internal/{runtime-normalize.ts → runtime/normalize.ts} +9 -31
- package/src/internal/{runtime-schema.ts → runtime/schema.ts} +84 -55
- package/src/internal/runtime/value.d.ts +22 -0
- package/src/internal/{runtime-value.ts → runtime/value.ts} +2 -2
- package/src/internal/scalar.d.ts +107 -0
- package/src/internal/scalar.ts +191 -0
- package/src/internal/schema-derivation.d.ts +105 -0
- package/src/internal/schema-derivation.ts +93 -21
- package/src/internal/schema-expression.d.ts +18 -0
- package/src/internal/schema-expression.ts +75 -0
- package/src/internal/table-options.d.ts +94 -0
- package/src/internal/table-options.ts +94 -8
- package/src/internal/table.d.ts +173 -0
- package/src/internal/table.ts +135 -54
- package/src/mysql/column.ts +95 -18
- package/src/mysql/datatypes/index.ts +58 -3
- package/src/mysql/errors/generated.ts +57336 -0
- package/src/mysql/errors/index.ts +1 -0
- package/src/mysql/errors/normalize.ts +55 -53
- package/src/mysql/errors/types.ts +74 -0
- package/src/mysql/executor.ts +69 -7
- package/src/mysql/function/aggregate.ts +1 -5
- package/src/mysql/function/core.ts +1 -3
- package/src/mysql/function/index.ts +1 -1
- package/src/mysql/function/string.ts +1 -5
- package/src/mysql/function/temporal.ts +12 -15
- package/src/mysql/function/window.ts +1 -6
- package/src/{internal/mysql-dialect.ts → mysql/internal/dialect.ts} +1 -1
- package/src/mysql/internal/dsl.ts +6115 -0
- package/src/{internal/mysql-renderer.ts → mysql/internal/renderer.ts} +6 -6
- package/src/mysql/internal/sql-expression-renderer.ts +1455 -0
- package/src/mysql/json.ts +2 -0
- package/src/mysql/query.ts +111 -86
- package/src/mysql/renderer.ts +1 -1
- package/src/mysql/table.ts +1 -1
- package/src/mysql.ts +6 -4
- package/src/postgres/cast.ts +30 -0
- package/src/postgres/column.ts +178 -20
- package/src/postgres/datatypes/index.d.ts +515 -0
- package/src/postgres/datatypes/index.ts +49 -5
- package/src/postgres/datatypes/spec.d.ts +412 -0
- package/src/postgres/errors/generated.ts +2636 -0
- package/src/postgres/errors/index.ts +1 -0
- package/src/postgres/errors/normalize.ts +47 -62
- package/src/postgres/errors/types.ts +92 -34
- package/src/postgres/executor.ts +37 -5
- package/src/postgres/function/aggregate.ts +1 -5
- package/src/postgres/function/core.ts +20 -2
- package/src/postgres/function/index.ts +1 -1
- package/src/postgres/function/string.ts +1 -5
- package/src/postgres/function/temporal.ts +12 -15
- package/src/postgres/function/window.ts +1 -6
- package/src/{internal/postgres-dialect.ts → postgres/internal/dialect.ts} +1 -1
- package/src/{internal/query-factory.ts → postgres/internal/dsl.ts} +1568 -2120
- package/src/{internal/postgres-renderer.ts → postgres/internal/renderer.ts} +6 -6
- package/src/postgres/internal/schema-ddl.ts +108 -0
- package/src/postgres/internal/schema-model.ts +150 -0
- package/src/{internal → postgres/internal}/sql-expression-renderer.ts +112 -46
- package/src/postgres/json.ts +493 -0
- package/src/postgres/metadata.ts +31 -0
- package/src/postgres/query.ts +113 -86
- package/src/postgres/renderer.ts +3 -13
- package/src/postgres/schema-expression.ts +17 -0
- package/src/postgres/schema-management.ts +204 -0
- package/src/postgres/schema.ts +35 -0
- package/src/postgres/table.ts +316 -42
- package/src/postgres/type.ts +31 -0
- package/src/postgres.ts +20 -4
- package/CHANGELOG.md +0 -134
- package/src/internal/expression.ts +0 -327
- package/src/internal/predicate-normalize.ts +0 -202
- package/src/mysql/function/json.ts +0 -4
- package/src/mysql/private/query.ts +0 -13
- package/src/postgres/function/json.ts +0 -4
- package/src/postgres/private/query.ts +0 -13
- /package/src/internal/{predicate-atom.ts → predicate/atom.ts} +0 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import * as Expression from "./scalar.js"
|
|
2
|
+
import * as Plan from "./row-set.js"
|
|
3
|
+
import * as Table from "./table.js"
|
|
4
|
+
|
|
5
|
+
type DslPlanRuntimeContext = {
|
|
6
|
+
readonly profile: {
|
|
7
|
+
readonly dialect: string
|
|
8
|
+
}
|
|
9
|
+
readonly makePlan: (...args: readonly any[]) => any
|
|
10
|
+
readonly getAst: (plan: any) => any
|
|
11
|
+
readonly getQueryState: (plan: any) => any
|
|
12
|
+
readonly currentRequiredList: (required: any) => readonly string[]
|
|
13
|
+
readonly toDialectExpression: (value: any) => Expression.Any
|
|
14
|
+
readonly toDialectNumericExpression: (value: any) => Expression.Any
|
|
15
|
+
readonly extractRequiredFromDialectInputRuntime: (value: any) => readonly string[]
|
|
16
|
+
readonly extractRequiredFromDialectNumericInputRuntime: (value: any) => readonly string[]
|
|
17
|
+
readonly formulaOfExpressionRuntime: (value: Expression.Any) => any
|
|
18
|
+
readonly assumeFormulaTrue: (assumptions: any, formula: any) => any
|
|
19
|
+
readonly trueFormula: () => any
|
|
20
|
+
readonly sourceDetails: (source: any) => { readonly sourceName: string; readonly sourceBaseName: string }
|
|
21
|
+
readonly presenceWitnessesOfSourceLike: (source: any) => readonly string[]
|
|
22
|
+
readonly attachInsertSource: (plan: any, source: any) => any
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const makeDslPlanRuntime = (ctx: DslPlanRuntimeContext) => {
|
|
26
|
+
const buildSetOperation = (kind: string, all: boolean, left: any, right: any) => {
|
|
27
|
+
const leftState = left[Plan.TypeId]
|
|
28
|
+
const leftAst = ctx.getAst(left)
|
|
29
|
+
const basePlan = leftAst.kind === "set"
|
|
30
|
+
? leftAst.setBase ?? left
|
|
31
|
+
: left
|
|
32
|
+
const leftOperations = leftAst.kind === "set"
|
|
33
|
+
? [...(leftAst.setOperations ?? [])]
|
|
34
|
+
: []
|
|
35
|
+
return ctx.makePlan({
|
|
36
|
+
selection: leftState.selection,
|
|
37
|
+
required: undefined,
|
|
38
|
+
available: {},
|
|
39
|
+
dialect: leftState.dialect ?? right[Plan.TypeId].dialect
|
|
40
|
+
}, {
|
|
41
|
+
kind: "set",
|
|
42
|
+
select: leftState.selection,
|
|
43
|
+
where: [],
|
|
44
|
+
having: [],
|
|
45
|
+
joins: [],
|
|
46
|
+
groupBy: [],
|
|
47
|
+
orderBy: [],
|
|
48
|
+
setBase: basePlan,
|
|
49
|
+
setOperations: [
|
|
50
|
+
...leftOperations,
|
|
51
|
+
{
|
|
52
|
+
kind,
|
|
53
|
+
all,
|
|
54
|
+
query: right
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}, undefined, undefined, "set")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const where = (predicate: any) =>
|
|
61
|
+
(plan: any) => {
|
|
62
|
+
const current = plan[Plan.TypeId]
|
|
63
|
+
const currentAst = ctx.getAst(plan)
|
|
64
|
+
const currentQuery = ctx.getQueryState(plan)
|
|
65
|
+
const predicateExpression = ctx.toDialectExpression(predicate)
|
|
66
|
+
const predicateRequired = ctx.extractRequiredFromDialectInputRuntime(predicate)
|
|
67
|
+
return ctx.makePlan({
|
|
68
|
+
selection: current.selection,
|
|
69
|
+
required: [...ctx.currentRequiredList(current.required), ...predicateRequired].filter((name, index, values) =>
|
|
70
|
+
!(name in current.available) && values.indexOf(name) === index),
|
|
71
|
+
available: current.available,
|
|
72
|
+
dialect: current.dialect ?? predicateExpression[Expression.TypeId].dialect
|
|
73
|
+
}, {
|
|
74
|
+
...currentAst,
|
|
75
|
+
where: [...currentAst.where, {
|
|
76
|
+
kind: "where",
|
|
77
|
+
predicate: predicateExpression
|
|
78
|
+
}]
|
|
79
|
+
}, ctx.assumeFormulaTrue(
|
|
80
|
+
currentQuery.assumptions,
|
|
81
|
+
ctx.formulaOfExpressionRuntime(predicateExpression)
|
|
82
|
+
), currentQuery.capabilities, currentQuery.statement)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const from = (source: any) =>
|
|
86
|
+
(plan: any) => {
|
|
87
|
+
const current = plan[Plan.TypeId]
|
|
88
|
+
const currentAst = ctx.getAst(plan)
|
|
89
|
+
const currentQuery = ctx.getQueryState(plan)
|
|
90
|
+
|
|
91
|
+
if (currentQuery.statement === "insert") {
|
|
92
|
+
return ctx.attachInsertSource(plan, source)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (
|
|
96
|
+
typeof source !== "object" ||
|
|
97
|
+
source === null ||
|
|
98
|
+
("kind" in source && source.kind === "values" && !("name" in source)) ||
|
|
99
|
+
(!(Table.TypeId in source) && !("name" in source && "baseName" in source))
|
|
100
|
+
) {
|
|
101
|
+
throw new Error("from(...) requires an aliased source in select/update statements")
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const sourceLike = source
|
|
105
|
+
const { sourceName, sourceBaseName } = ctx.sourceDetails(sourceLike)
|
|
106
|
+
const presenceWitnesses = ctx.presenceWitnessesOfSourceLike(sourceLike)
|
|
107
|
+
|
|
108
|
+
if (currentQuery.statement === "select") {
|
|
109
|
+
return ctx.makePlan({
|
|
110
|
+
selection: current.selection,
|
|
111
|
+
required: ctx.currentRequiredList(current.required).filter((name) => name !== sourceName),
|
|
112
|
+
available: {
|
|
113
|
+
[sourceName]: {
|
|
114
|
+
name: sourceName,
|
|
115
|
+
mode: "required",
|
|
116
|
+
baseName: sourceBaseName,
|
|
117
|
+
_presentFormula: ctx.trueFormula(),
|
|
118
|
+
_presenceWitnesses: presenceWitnesses
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
dialect: current.dialect
|
|
122
|
+
}, {
|
|
123
|
+
...currentAst,
|
|
124
|
+
from: {
|
|
125
|
+
kind: "from",
|
|
126
|
+
tableName: sourceName,
|
|
127
|
+
baseTableName: sourceBaseName,
|
|
128
|
+
source: sourceLike
|
|
129
|
+
}
|
|
130
|
+
}, currentQuery.assumptions, currentQuery.capabilities, currentQuery.statement)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (currentQuery.statement === "update") {
|
|
134
|
+
const nextAvailable = {
|
|
135
|
+
...current.available,
|
|
136
|
+
[sourceName]: {
|
|
137
|
+
name: sourceName,
|
|
138
|
+
mode: "required",
|
|
139
|
+
baseName: sourceBaseName,
|
|
140
|
+
_presentFormula: ctx.trueFormula(),
|
|
141
|
+
_presenceWitnesses: presenceWitnesses
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return ctx.makePlan({
|
|
145
|
+
selection: current.selection,
|
|
146
|
+
required: ctx.currentRequiredList(current.required).filter((name) => !(name in nextAvailable)),
|
|
147
|
+
available: nextAvailable,
|
|
148
|
+
dialect: current.dialect
|
|
149
|
+
}, {
|
|
150
|
+
...currentAst,
|
|
151
|
+
fromSources: [
|
|
152
|
+
...(currentAst.fromSources ?? []),
|
|
153
|
+
{
|
|
154
|
+
kind: "from",
|
|
155
|
+
tableName: sourceName,
|
|
156
|
+
baseTableName: sourceBaseName,
|
|
157
|
+
source: sourceLike
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
}, currentQuery.assumptions, currentQuery.capabilities, currentQuery.statement)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
throw new Error(`from(...) is not supported for ${currentQuery.statement} statements`)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const having = (predicate: any) =>
|
|
167
|
+
(plan: any) => {
|
|
168
|
+
const current = plan[Plan.TypeId]
|
|
169
|
+
const currentAst = ctx.getAst(plan)
|
|
170
|
+
const currentQuery = ctx.getQueryState(plan)
|
|
171
|
+
const predicateExpression = ctx.toDialectExpression(predicate)
|
|
172
|
+
const predicateRequired = ctx.extractRequiredFromDialectInputRuntime(predicate)
|
|
173
|
+
return ctx.makePlan({
|
|
174
|
+
selection: current.selection,
|
|
175
|
+
required: [...ctx.currentRequiredList(current.required), ...predicateRequired].filter((name, index, values) =>
|
|
176
|
+
!(name in current.available) && values.indexOf(name) === index),
|
|
177
|
+
available: current.available,
|
|
178
|
+
dialect: current.dialect ?? predicateExpression[Expression.TypeId].dialect
|
|
179
|
+
}, {
|
|
180
|
+
...currentAst,
|
|
181
|
+
having: [...currentAst.having, {
|
|
182
|
+
kind: "having",
|
|
183
|
+
predicate: predicateExpression
|
|
184
|
+
}]
|
|
185
|
+
}, ctx.assumeFormulaTrue(
|
|
186
|
+
currentQuery.assumptions,
|
|
187
|
+
ctx.formulaOfExpressionRuntime(predicateExpression)
|
|
188
|
+
), currentQuery.capabilities, currentQuery.statement)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const crossJoin = (table: any) =>
|
|
192
|
+
(plan: any) => {
|
|
193
|
+
const current = plan[Plan.TypeId]
|
|
194
|
+
const currentAst = ctx.getAst(plan)
|
|
195
|
+
const currentQuery = ctx.getQueryState(plan)
|
|
196
|
+
const { sourceName, sourceBaseName } = ctx.sourceDetails(table)
|
|
197
|
+
const presenceWitnesses = ctx.presenceWitnessesOfSourceLike(table)
|
|
198
|
+
const nextAvailable = {
|
|
199
|
+
...current.available,
|
|
200
|
+
[sourceName]: {
|
|
201
|
+
name: sourceName,
|
|
202
|
+
mode: "required",
|
|
203
|
+
baseName: sourceBaseName,
|
|
204
|
+
_presentFormula: ctx.trueFormula(),
|
|
205
|
+
_presenceWitnesses: presenceWitnesses
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return ctx.makePlan({
|
|
209
|
+
selection: current.selection,
|
|
210
|
+
required: ctx.currentRequiredList(current.required).filter((name) => !(name in nextAvailable)),
|
|
211
|
+
available: nextAvailable,
|
|
212
|
+
dialect: current.dialect ?? table[Plan.TypeId]?.dialect ?? table.dialect
|
|
213
|
+
}, {
|
|
214
|
+
...currentAst,
|
|
215
|
+
joins: [...currentAst.joins, {
|
|
216
|
+
kind: "cross",
|
|
217
|
+
tableName: sourceName,
|
|
218
|
+
baseTableName: sourceBaseName,
|
|
219
|
+
source: table
|
|
220
|
+
}]
|
|
221
|
+
}, currentQuery.assumptions, currentQuery.capabilities, currentQuery.statement)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const join = (kind: string, table: any, on: any) =>
|
|
225
|
+
(plan: any) => {
|
|
226
|
+
const current = plan[Plan.TypeId]
|
|
227
|
+
const currentAst = ctx.getAst(plan)
|
|
228
|
+
const currentQuery = ctx.getQueryState(plan)
|
|
229
|
+
const onExpression = ctx.toDialectExpression(on)
|
|
230
|
+
const onFormula = ctx.formulaOfExpressionRuntime(onExpression)
|
|
231
|
+
const { sourceName, sourceBaseName } = ctx.sourceDetails(table)
|
|
232
|
+
const presenceWitnesses = ctx.presenceWitnessesOfSourceLike(table)
|
|
233
|
+
const baseAvailable = (kind === "right" || kind === "full"
|
|
234
|
+
? Object.fromEntries(
|
|
235
|
+
Object.entries(current.available as Record<string, any>).map(([name, source]) => [name, {
|
|
236
|
+
name: source.name,
|
|
237
|
+
mode: "optional",
|
|
238
|
+
baseName: source.baseName,
|
|
239
|
+
_presentFormula: source._presentFormula,
|
|
240
|
+
_presenceWitnesses: source._presenceWitnesses
|
|
241
|
+
}])
|
|
242
|
+
)
|
|
243
|
+
: current.available) as Record<string, any>
|
|
244
|
+
const nextAvailable = {
|
|
245
|
+
...baseAvailable,
|
|
246
|
+
[sourceName]: {
|
|
247
|
+
name: sourceName,
|
|
248
|
+
mode: (kind === "left" || kind === "full") ? "optional" : "required",
|
|
249
|
+
baseName: sourceBaseName,
|
|
250
|
+
_presentFormula: (kind === "inner" || kind === "left") ? onFormula : ctx.trueFormula(),
|
|
251
|
+
_presenceWitnesses: presenceWitnesses
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return ctx.makePlan({
|
|
255
|
+
selection: current.selection,
|
|
256
|
+
required: [...ctx.currentRequiredList(current.required), ...ctx.extractRequiredFromDialectInputRuntime(on)].filter((name, index, values) =>
|
|
257
|
+
!(name in nextAvailable) && values.indexOf(name) === index),
|
|
258
|
+
available: nextAvailable,
|
|
259
|
+
dialect: current.dialect ?? table.dialect ?? onExpression[Expression.TypeId].dialect
|
|
260
|
+
}, {
|
|
261
|
+
...currentAst,
|
|
262
|
+
joins: [...currentAst.joins, {
|
|
263
|
+
kind,
|
|
264
|
+
tableName: sourceName,
|
|
265
|
+
baseTableName: sourceBaseName,
|
|
266
|
+
source: table,
|
|
267
|
+
on: onExpression
|
|
268
|
+
}]
|
|
269
|
+
}, (
|
|
270
|
+
kind === "inner"
|
|
271
|
+
? ctx.assumeFormulaTrue(currentQuery.assumptions, onFormula)
|
|
272
|
+
: currentQuery.assumptions
|
|
273
|
+
), currentQuery.capabilities, currentQuery.statement)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const orderBy = (value: any, direction: "asc" | "desc" = "asc") =>
|
|
277
|
+
(plan: any) => {
|
|
278
|
+
const current = plan[Plan.TypeId]
|
|
279
|
+
const currentAst = ctx.getAst(plan)
|
|
280
|
+
const currentQuery = ctx.getQueryState(plan)
|
|
281
|
+
const expression = ctx.toDialectExpression(value)
|
|
282
|
+
const required = ctx.extractRequiredFromDialectInputRuntime(value)
|
|
283
|
+
return ctx.makePlan({
|
|
284
|
+
selection: current.selection,
|
|
285
|
+
required: [...ctx.currentRequiredList(current.required), ...required].filter((name, index, values) =>
|
|
286
|
+
!(name in current.available) && values.indexOf(name) === index),
|
|
287
|
+
available: current.available,
|
|
288
|
+
dialect: current.dialect ?? expression[Expression.TypeId].dialect
|
|
289
|
+
}, {
|
|
290
|
+
...currentAst,
|
|
291
|
+
orderBy: [...currentAst.orderBy, {
|
|
292
|
+
kind: "orderBy",
|
|
293
|
+
value: expression,
|
|
294
|
+
direction
|
|
295
|
+
}]
|
|
296
|
+
}, currentQuery.assumptions, currentQuery.capabilities, currentQuery.statement)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const lock = (mode: string, options: { readonly nowait?: boolean; readonly skipLocked?: boolean } = {}) =>
|
|
300
|
+
(plan: any) => {
|
|
301
|
+
const current = plan[Plan.TypeId]
|
|
302
|
+
const currentAst = ctx.getAst(plan)
|
|
303
|
+
const currentQuery = ctx.getQueryState(plan)
|
|
304
|
+
return ctx.makePlan({
|
|
305
|
+
selection: current.selection,
|
|
306
|
+
required: current.required,
|
|
307
|
+
available: current.available,
|
|
308
|
+
dialect: current.dialect
|
|
309
|
+
}, {
|
|
310
|
+
...currentAst,
|
|
311
|
+
lock: {
|
|
312
|
+
kind: "lock",
|
|
313
|
+
mode,
|
|
314
|
+
nowait: options.nowait ?? false,
|
|
315
|
+
skipLocked: options.skipLocked ?? false
|
|
316
|
+
}
|
|
317
|
+
}, currentQuery.assumptions, currentQuery.capabilities, currentQuery.statement)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const distinct = () =>
|
|
321
|
+
(plan: any) => {
|
|
322
|
+
const current = plan[Plan.TypeId]
|
|
323
|
+
const currentAst = ctx.getAst(plan)
|
|
324
|
+
const currentQuery = ctx.getQueryState(plan)
|
|
325
|
+
return ctx.makePlan({
|
|
326
|
+
selection: current.selection,
|
|
327
|
+
required: current.required,
|
|
328
|
+
available: current.available,
|
|
329
|
+
dialect: current.dialect
|
|
330
|
+
}, {
|
|
331
|
+
...currentAst,
|
|
332
|
+
distinct: true
|
|
333
|
+
}, currentQuery.assumptions, currentQuery.capabilities, currentQuery.statement)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const limit = (value: any) =>
|
|
337
|
+
(plan: any) => {
|
|
338
|
+
const current = plan[Plan.TypeId]
|
|
339
|
+
const currentAst = ctx.getAst(plan)
|
|
340
|
+
const currentQuery = ctx.getQueryState(plan)
|
|
341
|
+
const expression = ctx.toDialectNumericExpression(value)
|
|
342
|
+
const required = ctx.extractRequiredFromDialectNumericInputRuntime(value)
|
|
343
|
+
return ctx.makePlan({
|
|
344
|
+
selection: current.selection,
|
|
345
|
+
required: [...ctx.currentRequiredList(current.required), ...required].filter((name, index, values) =>
|
|
346
|
+
!(name in current.available) && values.indexOf(name) === index),
|
|
347
|
+
available: current.available,
|
|
348
|
+
dialect: current.dialect ?? expression[Expression.TypeId].dialect
|
|
349
|
+
}, {
|
|
350
|
+
...currentAst,
|
|
351
|
+
limit: expression
|
|
352
|
+
}, currentQuery.assumptions, currentQuery.capabilities, currentQuery.statement)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const offset = (value: any) =>
|
|
356
|
+
(plan: any) => {
|
|
357
|
+
const current = plan[Plan.TypeId]
|
|
358
|
+
const currentAst = ctx.getAst(plan)
|
|
359
|
+
const currentQuery = ctx.getQueryState(plan)
|
|
360
|
+
const expression = ctx.toDialectNumericExpression(value)
|
|
361
|
+
const required = ctx.extractRequiredFromDialectNumericInputRuntime(value)
|
|
362
|
+
return ctx.makePlan({
|
|
363
|
+
selection: current.selection,
|
|
364
|
+
required: [...ctx.currentRequiredList(current.required), ...required].filter((name, index, values) =>
|
|
365
|
+
!(name in current.available) && values.indexOf(name) === index),
|
|
366
|
+
available: current.available,
|
|
367
|
+
dialect: current.dialect ?? expression[Expression.TypeId].dialect
|
|
368
|
+
}, {
|
|
369
|
+
...currentAst,
|
|
370
|
+
offset: expression
|
|
371
|
+
}, currentQuery.assumptions, currentQuery.capabilities, currentQuery.statement)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return {
|
|
375
|
+
buildSetOperation,
|
|
376
|
+
where,
|
|
377
|
+
from,
|
|
378
|
+
having,
|
|
379
|
+
crossJoin,
|
|
380
|
+
join,
|
|
381
|
+
orderBy,
|
|
382
|
+
lock,
|
|
383
|
+
distinct,
|
|
384
|
+
limit,
|
|
385
|
+
offset
|
|
386
|
+
}
|
|
387
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import * as Expression from "./scalar.js"
|
|
2
|
+
import * as Plan from "./row-set.js"
|
|
3
|
+
|
|
4
|
+
type DslQueryRuntimeContext = {
|
|
5
|
+
readonly profile: {
|
|
6
|
+
readonly dialect: string
|
|
7
|
+
}
|
|
8
|
+
readonly ValuesInputProto: object
|
|
9
|
+
readonly normalizeValuesRow: (row: any) => Record<string, Expression.Any>
|
|
10
|
+
readonly normalizeUnnestColumns: (columns: any) => Record<string, readonly Expression.Any[]>
|
|
11
|
+
readonly makeColumnReferenceSelection: (alias: string, selection: Record<string, Expression.Any>) => any
|
|
12
|
+
readonly toDialectNumericExpression: (value: any) => Expression.Any
|
|
13
|
+
readonly extractRequiredRuntime: (selection: any) => readonly string[]
|
|
14
|
+
readonly makePlan: (...args: readonly any[]) => any
|
|
15
|
+
readonly getAst: (plan: any) => any
|
|
16
|
+
readonly getQueryState: (plan: any) => any
|
|
17
|
+
readonly currentRequiredList: (required: any) => readonly string[]
|
|
18
|
+
readonly dedupeGroupedExpressions: (values: readonly any[]) => any
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const makeDslQueryRuntime = (ctx: DslQueryRuntimeContext) => {
|
|
22
|
+
const values = (rows: readonly [Record<string, any>, ...Record<string, any>[]]) => {
|
|
23
|
+
if (rows.length === 0) {
|
|
24
|
+
throw new Error("values(...) requires at least one row")
|
|
25
|
+
}
|
|
26
|
+
const normalizedRows = rows.map((row) => ctx.normalizeValuesRow(row)) as unknown as readonly [
|
|
27
|
+
Record<string, Expression.Any>,
|
|
28
|
+
...Record<string, Expression.Any>[]
|
|
29
|
+
]
|
|
30
|
+
const columnNames = Object.keys(normalizedRows[0]!)
|
|
31
|
+
for (const row of normalizedRows) {
|
|
32
|
+
const rowKeys = Object.keys(row)
|
|
33
|
+
if (rowKeys.length !== columnNames.length || !rowKeys.every((key, index) => key === columnNames[index])) {
|
|
34
|
+
throw new Error("values(...) rows must project the same columns in the same order")
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return Object.assign(Object.create(ctx.ValuesInputProto), {
|
|
38
|
+
kind: "values",
|
|
39
|
+
dialect: ctx.profile.dialect,
|
|
40
|
+
rows: normalizedRows,
|
|
41
|
+
selection: normalizedRows[0]!
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const unnest = (columns: Record<string, readonly any[]>, alias: string) => {
|
|
46
|
+
const normalizedColumns = ctx.normalizeUnnestColumns(columns)
|
|
47
|
+
const columnNames = Object.keys(normalizedColumns)
|
|
48
|
+
if (columnNames.length === 0) {
|
|
49
|
+
throw new Error("unnest(...) requires at least one column array")
|
|
50
|
+
}
|
|
51
|
+
const firstColumn = normalizedColumns[columnNames[0] as keyof typeof normalizedColumns]
|
|
52
|
+
const rowCount = firstColumn?.length ?? 0
|
|
53
|
+
if (rowCount === 0) {
|
|
54
|
+
throw new Error("unnest(...) requires at least one row")
|
|
55
|
+
}
|
|
56
|
+
for (const columnName of columnNames) {
|
|
57
|
+
const values = normalizedColumns[columnName]!
|
|
58
|
+
if (values.length !== rowCount) {
|
|
59
|
+
throw new Error("unnest(...) column arrays must have the same length")
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const firstRow = Object.fromEntries(
|
|
63
|
+
columnNames.map((columnName) => [columnName, normalizedColumns[columnName]![0]!])
|
|
64
|
+
) as Record<string, Expression.Any>
|
|
65
|
+
const columnsSelection = ctx.makeColumnReferenceSelection(alias, firstRow)
|
|
66
|
+
const source = {
|
|
67
|
+
kind: "unnest",
|
|
68
|
+
name: alias,
|
|
69
|
+
baseName: alias,
|
|
70
|
+
dialect: ctx.profile.dialect,
|
|
71
|
+
values: columns,
|
|
72
|
+
arrays: normalizedColumns,
|
|
73
|
+
columns: columnsSelection
|
|
74
|
+
}
|
|
75
|
+
return Object.assign(source, columnsSelection)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const generateSeries = (start: any, stop: any, step?: any, alias = "series") => {
|
|
79
|
+
const startExpression = ctx.toDialectNumericExpression(start)
|
|
80
|
+
const stopExpression = ctx.toDialectNumericExpression(stop)
|
|
81
|
+
const stepExpression = step === undefined ? undefined : ctx.toDialectNumericExpression(step)
|
|
82
|
+
const valueSelection = {
|
|
83
|
+
value: startExpression
|
|
84
|
+
} as Record<string, Expression.Any>
|
|
85
|
+
const columns = ctx.makeColumnReferenceSelection(alias, valueSelection)
|
|
86
|
+
const source = {
|
|
87
|
+
kind: "tableFunction",
|
|
88
|
+
name: alias,
|
|
89
|
+
baseName: alias,
|
|
90
|
+
dialect: ctx.profile.dialect,
|
|
91
|
+
functionName: "generate_series",
|
|
92
|
+
args: stepExpression === undefined
|
|
93
|
+
? [startExpression, stopExpression] as readonly Expression.Any[]
|
|
94
|
+
: [startExpression, stopExpression, stepExpression] as readonly Expression.Any[],
|
|
95
|
+
columns
|
|
96
|
+
}
|
|
97
|
+
return Object.assign(source, columns)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const select = (selection: any) =>
|
|
101
|
+
ctx.makePlan({
|
|
102
|
+
selection,
|
|
103
|
+
required: ctx.extractRequiredRuntime(selection),
|
|
104
|
+
available: {},
|
|
105
|
+
dialect: ctx.profile.dialect
|
|
106
|
+
}, {
|
|
107
|
+
kind: "select",
|
|
108
|
+
select: selection,
|
|
109
|
+
where: [],
|
|
110
|
+
having: [],
|
|
111
|
+
joins: [],
|
|
112
|
+
groupBy: [],
|
|
113
|
+
orderBy: []
|
|
114
|
+
}, undefined, "read", "select")
|
|
115
|
+
|
|
116
|
+
const groupBy = (...values: readonly Expression.Any[]) =>
|
|
117
|
+
(plan: any) => {
|
|
118
|
+
const current = plan[Plan.TypeId]
|
|
119
|
+
const currentAst = ctx.getAst(plan)
|
|
120
|
+
const currentQuery = ctx.getQueryState(plan)
|
|
121
|
+
const required = [...values.flatMap((value) => Object.keys(value[Expression.TypeId].dependencies))].filter((name, index, list) =>
|
|
122
|
+
!(name in current.available) && list.indexOf(name) === index)
|
|
123
|
+
return ctx.makePlan({
|
|
124
|
+
selection: current.selection,
|
|
125
|
+
required: [...ctx.currentRequiredList(current.required), ...required].filter((name, index, list) =>
|
|
126
|
+
!(name in current.available) && list.indexOf(name) === index),
|
|
127
|
+
available: current.available,
|
|
128
|
+
dialect: current.dialect
|
|
129
|
+
}, {
|
|
130
|
+
...currentAst,
|
|
131
|
+
groupBy: ctx.dedupeGroupedExpressions([...currentAst.groupBy, ...values])
|
|
132
|
+
}, currentQuery.assumptions, currentQuery.capabilities, currentQuery.statement)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const returning = (selection: any) =>
|
|
136
|
+
(plan: any) => {
|
|
137
|
+
const current = plan[Plan.TypeId]
|
|
138
|
+
const currentAst = ctx.getAst(plan)
|
|
139
|
+
const currentQuery = ctx.getQueryState(plan)
|
|
140
|
+
return ctx.makePlan({
|
|
141
|
+
selection,
|
|
142
|
+
required: [...ctx.currentRequiredList(current.required), ...ctx.extractRequiredRuntime(selection)].filter((name, index, list) =>
|
|
143
|
+
!(name in current.available) && list.indexOf(name) === index),
|
|
144
|
+
available: current.available,
|
|
145
|
+
dialect: current.dialect
|
|
146
|
+
}, {
|
|
147
|
+
...currentAst,
|
|
148
|
+
select: selection
|
|
149
|
+
}, currentQuery.assumptions, currentQuery.capabilities, currentQuery.statement, currentQuery.target, currentQuery.insertSource)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
values,
|
|
154
|
+
unnest,
|
|
155
|
+
generateSeries,
|
|
156
|
+
select,
|
|
157
|
+
groupBy,
|
|
158
|
+
returning
|
|
159
|
+
}
|
|
160
|
+
}
|