effect-qb 0.12.3
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 +1294 -0
- package/dist/mysql.js +57575 -0
- package/dist/postgres.js +6303 -0
- package/package.json +42 -0
- package/src/internal/aggregation-validation.ts +57 -0
- package/src/internal/case-analysis.ts +50 -0
- package/src/internal/coercion-analysis.ts +30 -0
- package/src/internal/coercion-errors.ts +29 -0
- package/src/internal/coercion-kind.ts +32 -0
- package/src/internal/coercion-normalize.ts +7 -0
- package/src/internal/coercion-rules.ts +25 -0
- package/src/internal/column-state.ts +453 -0
- package/src/internal/column.ts +417 -0
- package/src/internal/datatypes/define.ts +44 -0
- package/src/internal/datatypes/lookup.ts +280 -0
- package/src/internal/datatypes/shape.ts +72 -0
- package/src/internal/derived-table.ts +149 -0
- package/src/internal/dialect.ts +30 -0
- package/src/internal/executor.ts +390 -0
- package/src/internal/expression-ast.ts +349 -0
- package/src/internal/expression.ts +325 -0
- package/src/internal/grouping-key.ts +82 -0
- package/src/internal/json/ast.ts +63 -0
- package/src/internal/json/errors.ts +13 -0
- package/src/internal/json/path.ts +227 -0
- package/src/internal/json/shape.ts +1 -0
- package/src/internal/json/types.ts +386 -0
- package/src/internal/mysql-dialect.ts +39 -0
- package/src/internal/mysql-renderer.ts +37 -0
- package/src/internal/plan.ts +64 -0
- package/src/internal/postgres-dialect.ts +34 -0
- package/src/internal/postgres-renderer.ts +40 -0
- package/src/internal/predicate-analysis.ts +71 -0
- package/src/internal/predicate-atom.ts +43 -0
- package/src/internal/predicate-branches.ts +40 -0
- package/src/internal/predicate-context.ts +279 -0
- package/src/internal/predicate-formula.ts +100 -0
- package/src/internal/predicate-key.ts +28 -0
- package/src/internal/predicate-nnf.ts +12 -0
- package/src/internal/predicate-normalize.ts +202 -0
- package/src/internal/projection-alias.ts +15 -0
- package/src/internal/projections.ts +101 -0
- package/src/internal/query-ast.ts +297 -0
- package/src/internal/query-factory.ts +6757 -0
- package/src/internal/query-requirements.ts +40 -0
- package/src/internal/query.ts +1590 -0
- package/src/internal/renderer.ts +102 -0
- package/src/internal/runtime-normalize.ts +344 -0
- package/src/internal/runtime-schema.ts +428 -0
- package/src/internal/runtime-value.ts +85 -0
- package/src/internal/schema-derivation.ts +131 -0
- package/src/internal/sql-expression-renderer.ts +1353 -0
- package/src/internal/table-options.ts +225 -0
- package/src/internal/table.ts +674 -0
- package/src/mysql/column.ts +30 -0
- package/src/mysql/datatypes/index.ts +6 -0
- package/src/mysql/datatypes/spec.ts +180 -0
- package/src/mysql/errors/catalog.ts +51662 -0
- package/src/mysql/errors/fields.ts +21 -0
- package/src/mysql/errors/index.ts +18 -0
- package/src/mysql/errors/normalize.ts +232 -0
- package/src/mysql/errors/requirements.ts +73 -0
- package/src/mysql/executor.ts +134 -0
- package/src/mysql/query.ts +189 -0
- package/src/mysql/renderer.ts +19 -0
- package/src/mysql/table.ts +157 -0
- package/src/mysql.ts +18 -0
- package/src/postgres/column.ts +20 -0
- package/src/postgres/datatypes/index.ts +8 -0
- package/src/postgres/datatypes/spec.ts +264 -0
- package/src/postgres/errors/catalog.ts +452 -0
- package/src/postgres/errors/fields.ts +48 -0
- package/src/postgres/errors/index.ts +4 -0
- package/src/postgres/errors/normalize.ts +209 -0
- package/src/postgres/errors/requirements.ts +65 -0
- package/src/postgres/errors/types.ts +38 -0
- package/src/postgres/executor.ts +131 -0
- package/src/postgres/query.ts +189 -0
- package/src/postgres/renderer.ts +29 -0
- package/src/postgres/table.ts +157 -0
- package/src/postgres.ts +18 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import type * as Expression from "./expression.js"
|
|
2
|
+
import type * as ExpressionAst from "./expression-ast.js"
|
|
3
|
+
import type { ColumnKeyOfExpression, ValueKey } from "./predicate-key.js"
|
|
4
|
+
import type { AllFormula, AnyFormula, AtomFormula, FalseFormula, NotFormula, PredicateFormula, TrueFormula } from "./predicate-formula.js"
|
|
5
|
+
import type { EqColumnAtom, EqLiteralAtom, NeqLiteralAtom, NonNullAtom, NullAtom, UnknownAtom } from "./predicate-atom.js"
|
|
6
|
+
|
|
7
|
+
type AstOf<Value extends Expression.Any> = Value extends {
|
|
8
|
+
readonly [ExpressionAst.TypeId]: infer Ast extends ExpressionAst.Any
|
|
9
|
+
} ? Ast : never
|
|
10
|
+
|
|
11
|
+
type LiteralValueOfExpression<Value extends Expression.Any> = AstOf<Value> extends ExpressionAst.LiteralNode<infer Literal>
|
|
12
|
+
? Literal
|
|
13
|
+
: never
|
|
14
|
+
|
|
15
|
+
type True = TrueFormula
|
|
16
|
+
type False = FalseFormula
|
|
17
|
+
|
|
18
|
+
type UnknownTag<Tag extends string> = AtomFormula<UnknownAtom<Tag>>
|
|
19
|
+
type AtomOf<Atom extends import("./predicate-atom.js").PredicateAtom> = AtomFormula<Atom>
|
|
20
|
+
type FactOf<Atom extends import("./predicate-atom.js").PredicateAtom> = AtomFormula<Atom>
|
|
21
|
+
|
|
22
|
+
type NonNullFactsOfExpression<Value extends Expression.Any> =
|
|
23
|
+
[ColumnKeyOfExpression<Value>] extends [never]
|
|
24
|
+
? never
|
|
25
|
+
: FactOf<NonNullAtom<ColumnKeyOfExpression<Value>>>
|
|
26
|
+
|
|
27
|
+
type CombineFacts<
|
|
28
|
+
Left extends PredicateFormula,
|
|
29
|
+
Right extends PredicateFormula
|
|
30
|
+
> = [Left] extends [never]
|
|
31
|
+
? Right
|
|
32
|
+
: [Right] extends [never]
|
|
33
|
+
? Left
|
|
34
|
+
: import("./predicate-formula.js").NormalizeBooleanConstants<AllFormula<[Left, Right]>>
|
|
35
|
+
|
|
36
|
+
type FactsOfExpressions<Values extends readonly Expression.Any[]> =
|
|
37
|
+
Values extends readonly [
|
|
38
|
+
infer Head extends Expression.Any,
|
|
39
|
+
...infer Tail extends readonly Expression.Any[]
|
|
40
|
+
]
|
|
41
|
+
? CombineFacts<FormulaOfExpression<Head>, FactsOfExpressions<Tail>>
|
|
42
|
+
: never
|
|
43
|
+
|
|
44
|
+
type FormulaOfEq<
|
|
45
|
+
Left extends Expression.Any,
|
|
46
|
+
Right extends Expression.Any
|
|
47
|
+
> =
|
|
48
|
+
[ColumnKeyOfExpression<Left>] extends [never]
|
|
49
|
+
? [ColumnKeyOfExpression<Right>] extends [never]
|
|
50
|
+
? LiteralValueOfExpression<Left> extends infer LeftLiteral
|
|
51
|
+
? LiteralValueOfExpression<Right> extends infer RightLiteral
|
|
52
|
+
? [LeftLiteral] extends [never]
|
|
53
|
+
? UnknownTag<"eq:unsupported">
|
|
54
|
+
: [RightLiteral] extends [never]
|
|
55
|
+
? UnknownTag<"eq:unsupported">
|
|
56
|
+
: LeftLiteral extends null
|
|
57
|
+
? False
|
|
58
|
+
: RightLiteral extends null
|
|
59
|
+
? False
|
|
60
|
+
: [LeftLiteral] extends [RightLiteral]
|
|
61
|
+
? True
|
|
62
|
+
: False
|
|
63
|
+
: UnknownTag<"eq:unsupported">
|
|
64
|
+
: UnknownTag<"eq:unsupported">
|
|
65
|
+
: LiteralValueOfExpression<Left> extends infer LeftLiteral
|
|
66
|
+
? [LeftLiteral] extends [never]
|
|
67
|
+
? UnknownTag<"eq:unsupported">
|
|
68
|
+
: LeftLiteral extends null
|
|
69
|
+
? False
|
|
70
|
+
: AtomOf<EqLiteralAtom<ColumnKeyOfExpression<Right>, ValueKey<LeftLiteral>>>
|
|
71
|
+
: UnknownTag<"eq:unsupported">
|
|
72
|
+
: [ColumnKeyOfExpression<Right>] extends [never]
|
|
73
|
+
? LiteralValueOfExpression<Right> extends infer RightLiteral
|
|
74
|
+
? [RightLiteral] extends [never]
|
|
75
|
+
? UnknownTag<"eq:unsupported">
|
|
76
|
+
: RightLiteral extends null
|
|
77
|
+
? False
|
|
78
|
+
: AtomOf<EqLiteralAtom<ColumnKeyOfExpression<Left>, ValueKey<RightLiteral>>>
|
|
79
|
+
: UnknownTag<"eq:unsupported">
|
|
80
|
+
: AtomOf<import("./predicate-atom.js").EqColumnAtom<ColumnKeyOfExpression<Left>, ColumnKeyOfExpression<Right>>>
|
|
81
|
+
|
|
82
|
+
type FormulaOfVariadic<
|
|
83
|
+
Kind extends ExpressionAst.VariadicKind,
|
|
84
|
+
Values extends readonly Expression.Any[]
|
|
85
|
+
> = Kind extends "and"
|
|
86
|
+
? import("./predicate-formula.js").NormalizeBooleanConstants<import("./predicate-formula.js").AllFormula<{
|
|
87
|
+
readonly [K in keyof Values]: Values[K] extends Expression.Any ? FormulaOfExpression<Values[K]> : never
|
|
88
|
+
} & readonly PredicateFormula[]>>
|
|
89
|
+
: Kind extends "or"
|
|
90
|
+
? import("./predicate-formula.js").NormalizeBooleanConstants<import("./predicate-formula.js").AnyFormula<{
|
|
91
|
+
readonly [K in keyof Values]: Values[K] extends Expression.Any ? FormulaOfExpression<Values[K]> : never
|
|
92
|
+
} & readonly PredicateFormula[]>>
|
|
93
|
+
: Kind extends "in" | "notIn" | "between"
|
|
94
|
+
? FactsOfExpressions<Values> extends infer Facts extends PredicateFormula
|
|
95
|
+
? [Facts] extends [never]
|
|
96
|
+
? UnknownTag<`variadic:${Kind}`>
|
|
97
|
+
: Facts
|
|
98
|
+
: UnknownTag<`variadic:${Kind}`>
|
|
99
|
+
: UnknownTag<`variadic:${Kind}`>
|
|
100
|
+
|
|
101
|
+
export type FormulaOfExpression<Value extends Expression.Any> =
|
|
102
|
+
AstOf<Value> extends infer Ast extends ExpressionAst.Any
|
|
103
|
+
? Ast extends ExpressionAst.LiteralNode<infer Literal>
|
|
104
|
+
? Literal extends true
|
|
105
|
+
? True
|
|
106
|
+
: Literal extends false
|
|
107
|
+
? False
|
|
108
|
+
: UnknownTag<"literal:non-boolean">
|
|
109
|
+
: Ast extends ExpressionAst.UnaryNode<infer Kind extends ExpressionAst.UnaryKind, infer Inner extends Expression.Any>
|
|
110
|
+
? Kind extends "isNull"
|
|
111
|
+
? [ColumnKeyOfExpression<Inner>] extends [never]
|
|
112
|
+
? UnknownTag<"isNull:unsupported">
|
|
113
|
+
: AtomOf<NullAtom<ColumnKeyOfExpression<Inner>>>
|
|
114
|
+
: Kind extends "isNotNull"
|
|
115
|
+
? [ColumnKeyOfExpression<Inner>] extends [never]
|
|
116
|
+
? UnknownTag<"isNotNull:unsupported">
|
|
117
|
+
: AtomOf<NonNullAtom<ColumnKeyOfExpression<Inner>>>
|
|
118
|
+
: Kind extends "not"
|
|
119
|
+
? import("./predicate-formula.js").Not<FormulaOfExpression<Inner>>
|
|
120
|
+
: UnknownTag<`unary:${Kind}`>
|
|
121
|
+
: Ast extends ExpressionAst.BinaryNode<"eq", infer Left extends Expression.Any, infer Right extends Expression.Any>
|
|
122
|
+
? FormulaOfEq<Left, Right>
|
|
123
|
+
: Ast extends ExpressionAst.BinaryNode<"neq", infer Left extends Expression.Any, infer Right extends Expression.Any>
|
|
124
|
+
? Left extends Expression.Any
|
|
125
|
+
? Right extends Expression.Any
|
|
126
|
+
? [ColumnKeyOfExpression<Left>] extends [never]
|
|
127
|
+
? [ColumnKeyOfExpression<Right>] extends [never]
|
|
128
|
+
? LiteralValueOfExpression<Left> extends infer LeftLiteral
|
|
129
|
+
? LiteralValueOfExpression<Right> extends infer RightLiteral
|
|
130
|
+
? [LeftLiteral] extends [never]
|
|
131
|
+
? UnknownTag<"neq:unsupported">
|
|
132
|
+
: [RightLiteral] extends [never]
|
|
133
|
+
? UnknownTag<"neq:unsupported">
|
|
134
|
+
: LeftLiteral extends null
|
|
135
|
+
? False
|
|
136
|
+
: RightLiteral extends null
|
|
137
|
+
? False
|
|
138
|
+
: [LeftLiteral] extends [RightLiteral]
|
|
139
|
+
? False
|
|
140
|
+
: True
|
|
141
|
+
: UnknownTag<"neq:unsupported">
|
|
142
|
+
: UnknownTag<"neq:unsupported">
|
|
143
|
+
: CombineFacts<NonNullFactsOfExpression<Left>, NonNullFactsOfExpression<Right>>
|
|
144
|
+
: CombineFacts<NonNullFactsOfExpression<Left>, NonNullFactsOfExpression<Right>>
|
|
145
|
+
: UnknownTag<"neq:unsupported">
|
|
146
|
+
: UnknownTag<"neq:unsupported">
|
|
147
|
+
: Ast extends ExpressionAst.BinaryNode<infer Kind extends "lt" | "lte" | "gt" | "gte" | "like" | "ilike" | "isDistinctFrom" | "isNotDistinctFrom" | "contains" | "containedBy" | "overlaps", infer Left extends Expression.Any, infer Right extends Expression.Any>
|
|
148
|
+
? Kind extends "isNotDistinctFrom"
|
|
149
|
+
? Left extends Expression.Any
|
|
150
|
+
? Right extends Expression.Any
|
|
151
|
+
? LiteralValueOfExpression<Left> extends infer LeftLiteral
|
|
152
|
+
? LiteralValueOfExpression<Right> extends infer RightLiteral
|
|
153
|
+
? [LeftLiteral] extends [never]
|
|
154
|
+
? [RightLiteral] extends [never]
|
|
155
|
+
? UnknownTag<"isNotDistinctFrom:unsupported">
|
|
156
|
+
: RightLiteral extends null
|
|
157
|
+
? [ColumnKeyOfExpression<Left>] extends [never]
|
|
158
|
+
? UnknownTag<"isNotDistinctFrom:unsupported">
|
|
159
|
+
: AtomOf<NullAtom<ColumnKeyOfExpression<Left>>>
|
|
160
|
+
: UnknownTag<"isNotDistinctFrom:unsupported">
|
|
161
|
+
: LeftLiteral extends null
|
|
162
|
+
? [ColumnKeyOfExpression<Right>] extends [never]
|
|
163
|
+
? UnknownTag<"isNotDistinctFrom:unsupported">
|
|
164
|
+
: AtomOf<NullAtom<ColumnKeyOfExpression<Right>>>
|
|
165
|
+
: RightLiteral extends null
|
|
166
|
+
? [ColumnKeyOfExpression<Left>] extends [never]
|
|
167
|
+
? UnknownTag<"isNotDistinctFrom:unsupported">
|
|
168
|
+
: AtomOf<NullAtom<ColumnKeyOfExpression<Left>>>
|
|
169
|
+
: [ColumnKeyOfExpression<Left>] extends [never]
|
|
170
|
+
? [ColumnKeyOfExpression<Right>] extends [never]
|
|
171
|
+
? CombineFacts<NonNullFactsOfExpression<Left>, NonNullFactsOfExpression<Right>>
|
|
172
|
+
: AtomOf<EqLiteralAtom<ColumnKeyOfExpression<Right>, ValueKey<LeftLiteral>>>
|
|
173
|
+
: AtomOf<EqLiteralAtom<ColumnKeyOfExpression<Left>, ValueKey<RightLiteral>>>
|
|
174
|
+
: UnknownTag<"isNotDistinctFrom:unsupported">
|
|
175
|
+
: UnknownTag<"isNotDistinctFrom:unsupported">
|
|
176
|
+
: UnknownTag<"isNotDistinctFrom:unsupported">
|
|
177
|
+
: UnknownTag<"isNotDistinctFrom:unsupported">
|
|
178
|
+
: Kind extends "isDistinctFrom"
|
|
179
|
+
? UnknownTag<"isDistinctFrom:unsupported">
|
|
180
|
+
: CombineFacts<NonNullFactsOfExpression<Left>, NonNullFactsOfExpression<Right>>
|
|
181
|
+
: Ast extends ExpressionAst.VariadicNode<"and", infer Values extends readonly Expression.Any[]>
|
|
182
|
+
? import("./predicate-formula.js").NormalizeBooleanConstants<import("./predicate-formula.js").AllFormula<{
|
|
183
|
+
readonly [K in keyof Values]: Values[K] extends Expression.Any ? FormulaOfExpression<Values[K]> : never
|
|
184
|
+
} & readonly PredicateFormula[]>>
|
|
185
|
+
: Ast extends ExpressionAst.VariadicNode<"or", infer Values extends readonly Expression.Any[]>
|
|
186
|
+
? import("./predicate-formula.js").NormalizeBooleanConstants<import("./predicate-formula.js").AnyFormula<{
|
|
187
|
+
readonly [K in keyof Values]: Values[K] extends Expression.Any ? FormulaOfExpression<Values[K]> : never
|
|
188
|
+
} & readonly PredicateFormula[]>>
|
|
189
|
+
: Ast extends ExpressionAst.VariadicNode<infer Kind extends "in" | "notIn" | "between", infer Values extends readonly Expression.Any[]>
|
|
190
|
+
? CombineFacts<NonNullFactsOfExpression<Values[number]>, UnknownTag<`variadic:${Kind}`>>
|
|
191
|
+
: Ast extends ExpressionAst.BinaryNode<infer Kind extends ExpressionAst.BinaryKind, infer Left extends Expression.Any, infer Right extends Expression.Any>
|
|
192
|
+
? Kind extends "eq"
|
|
193
|
+
? FormulaOfEq<Left, Right>
|
|
194
|
+
: CombineFacts<NonNullFactsOfExpression<Left>, NonNullFactsOfExpression<Right>>
|
|
195
|
+
: UnknownTag<`expr:${Ast["kind"]}`>
|
|
196
|
+
: UnknownTag<"missing-ast">
|
|
197
|
+
|
|
198
|
+
export type FormulaOfPredicate<Value> =
|
|
199
|
+
Value extends true ? True :
|
|
200
|
+
Value extends false ? False :
|
|
201
|
+
Value extends Expression.Any ? FormulaOfExpression<Value> :
|
|
202
|
+
UnknownTag<"predicate:unsupported">
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Symbol used to attach explicit projection-alias metadata to an expression. */
|
|
2
|
+
export const TypeId: unique symbol = Symbol.for("effect-qb/ProjectionAlias")
|
|
3
|
+
|
|
4
|
+
export type TypeId = typeof TypeId
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Projection-alias metadata carried by a runtime expression wrapper.
|
|
8
|
+
*
|
|
9
|
+
* This is intentionally orthogonal to the scalar expression AST. Aliasing does
|
|
10
|
+
* not change SQL semantics; it only changes how a selected expression is named
|
|
11
|
+
* in the rendered `SELECT ... AS ...` list.
|
|
12
|
+
*/
|
|
13
|
+
export interface State<Alias extends string = string> {
|
|
14
|
+
readonly alias: Alias
|
|
15
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import * as Expression from "./expression.js"
|
|
2
|
+
import * as ProjectionAlias from "./projection-alias.js"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Flat projection metadata shared by renderers and executors.
|
|
6
|
+
*
|
|
7
|
+
* `path` identifies where a value should be decoded in the nested result row,
|
|
8
|
+
* while `alias` is the flat SQL column alias used by the rendered query.
|
|
9
|
+
*/
|
|
10
|
+
export interface Projection {
|
|
11
|
+
readonly path: readonly string[]
|
|
12
|
+
readonly alias: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Selection leaf paired with its resolved projection alias. */
|
|
16
|
+
export interface FlattenedProjection {
|
|
17
|
+
readonly path: readonly string[]
|
|
18
|
+
readonly expression: Expression.Any
|
|
19
|
+
readonly alias: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const aliasFromPath = (path: readonly string[]): string => path.join("__")
|
|
23
|
+
|
|
24
|
+
const isExpression = (value: unknown): value is Expression.Any =>
|
|
25
|
+
typeof value === "object" && value !== null && Expression.TypeId in value
|
|
26
|
+
|
|
27
|
+
const projectionAliasOf = (expression: Expression.Any): string | undefined =>
|
|
28
|
+
ProjectionAlias.TypeId in expression
|
|
29
|
+
? (expression as Expression.Any & {
|
|
30
|
+
readonly [ProjectionAlias.TypeId]: ProjectionAlias.State
|
|
31
|
+
})[ProjectionAlias.TypeId].alias
|
|
32
|
+
: undefined
|
|
33
|
+
|
|
34
|
+
const pathKeyOf = (path: readonly string[]): string => JSON.stringify(path)
|
|
35
|
+
|
|
36
|
+
const formatProjectionPath = (path: readonly string[]): string => path.join(".")
|
|
37
|
+
|
|
38
|
+
const isPrefixPath = (
|
|
39
|
+
left: readonly string[],
|
|
40
|
+
right: readonly string[]
|
|
41
|
+
): boolean =>
|
|
42
|
+
left.length < right.length && left.every((segment, index) => segment === right[index])
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Flattens a nested selection object into leaf expressions with decode paths
|
|
46
|
+
* and resolved SQL aliases.
|
|
47
|
+
*/
|
|
48
|
+
export const flattenSelection = (
|
|
49
|
+
selection: Record<string, unknown>,
|
|
50
|
+
path: readonly string[] = []
|
|
51
|
+
): ReadonlyArray<FlattenedProjection> => {
|
|
52
|
+
const fields: Array<FlattenedProjection> = []
|
|
53
|
+
for (const [key, value] of Object.entries(selection)) {
|
|
54
|
+
const nextPath = [...path, key]
|
|
55
|
+
if (isExpression(value)) {
|
|
56
|
+
fields.push({
|
|
57
|
+
path: nextPath,
|
|
58
|
+
expression: value,
|
|
59
|
+
alias: projectionAliasOf(value) ?? aliasFromPath(nextPath)
|
|
60
|
+
})
|
|
61
|
+
continue
|
|
62
|
+
}
|
|
63
|
+
fields.push(...flattenSelection(value as Record<string, unknown>, nextPath))
|
|
64
|
+
}
|
|
65
|
+
return fields
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Validates the flattened projection set shared by renderer and executor code.
|
|
70
|
+
*
|
|
71
|
+
* This rejects:
|
|
72
|
+
* - duplicate SQL aliases
|
|
73
|
+
* - duplicate decode paths
|
|
74
|
+
* - conflicting prefix paths like `profile` and `profile.id`
|
|
75
|
+
*/
|
|
76
|
+
export const validateProjections = (projections: readonly Projection[]): void => {
|
|
77
|
+
const seen = new Set<string>()
|
|
78
|
+
const pathKeys = new Set<string>()
|
|
79
|
+
for (const projection of projections) {
|
|
80
|
+
if (seen.has(projection.alias)) {
|
|
81
|
+
throw new Error(`Duplicate projection alias: ${projection.alias}`)
|
|
82
|
+
}
|
|
83
|
+
seen.add(projection.alias)
|
|
84
|
+
const pathKey = pathKeyOf(projection.path)
|
|
85
|
+
if (pathKeys.has(pathKey)) {
|
|
86
|
+
throw new Error(`Duplicate projection path: ${formatProjectionPath(projection.path)}`)
|
|
87
|
+
}
|
|
88
|
+
pathKeys.add(pathKey)
|
|
89
|
+
}
|
|
90
|
+
for (let index = 0; index < projections.length; index++) {
|
|
91
|
+
const current = projections[index]!
|
|
92
|
+
for (let compareIndex = index + 1; compareIndex < projections.length; compareIndex++) {
|
|
93
|
+
const other = projections[compareIndex]!
|
|
94
|
+
if (isPrefixPath(current.path, other.path) || isPrefixPath(other.path, current.path)) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Conflicting projection paths: ${formatProjectionPath(current.path)} conflicts with ${formatProjectionPath(other.path)}`
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import type * as Expression from "./expression.js"
|
|
2
|
+
|
|
3
|
+
/** Symbol used to attach query-clause AST metadata to query-plan values. */
|
|
4
|
+
export const TypeId: unique symbol = Symbol.for("effect-qb/QueryAst")
|
|
5
|
+
|
|
6
|
+
export type TypeId = typeof TypeId
|
|
7
|
+
|
|
8
|
+
/** Statement kinds supported by the current query AST. */
|
|
9
|
+
export type QueryStatement =
|
|
10
|
+
| "select"
|
|
11
|
+
| "set"
|
|
12
|
+
| "insert"
|
|
13
|
+
| "update"
|
|
14
|
+
| "delete"
|
|
15
|
+
| "truncate"
|
|
16
|
+
| "merge"
|
|
17
|
+
| "transaction"
|
|
18
|
+
| "commit"
|
|
19
|
+
| "rollback"
|
|
20
|
+
| "savepoint"
|
|
21
|
+
| "rollbackTo"
|
|
22
|
+
| "releaseSavepoint"
|
|
23
|
+
| "createTable"
|
|
24
|
+
| "createIndex"
|
|
25
|
+
| "dropIndex"
|
|
26
|
+
| "alterTable"
|
|
27
|
+
| "dropTable"
|
|
28
|
+
|
|
29
|
+
/** Base `FROM` clause recorded by the query AST. */
|
|
30
|
+
export interface FromClause<TableName extends string = string> {
|
|
31
|
+
readonly kind: "from"
|
|
32
|
+
readonly tableName: TableName
|
|
33
|
+
readonly baseTableName: string
|
|
34
|
+
readonly source: unknown
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Boolean predicate recorded in a `WHERE` clause. */
|
|
38
|
+
export interface WhereClause<Predicate extends Expression.Any = Expression.Any> {
|
|
39
|
+
readonly kind: "where"
|
|
40
|
+
readonly predicate: Predicate
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Boolean predicate recorded in a `HAVING` clause. */
|
|
44
|
+
export interface HavingClause<Predicate extends Expression.Any = Expression.Any> {
|
|
45
|
+
readonly kind: "having"
|
|
46
|
+
readonly predicate: Predicate
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Assignment recorded in a mutation statement. */
|
|
50
|
+
export interface AssignmentClause<Value extends Expression.Any = Expression.Any> {
|
|
51
|
+
readonly tableName?: string
|
|
52
|
+
readonly columnName: string
|
|
53
|
+
readonly value: Value
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** One row in a multi-row `values (...)` insert source. */
|
|
57
|
+
export interface InsertValuesRowClause<
|
|
58
|
+
Values extends readonly AssignmentClause[] = readonly AssignmentClause[]
|
|
59
|
+
> {
|
|
60
|
+
readonly values: Values
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Additional insert source kinds beyond a single literal row. */
|
|
64
|
+
export type InsertSourceClause =
|
|
65
|
+
| {
|
|
66
|
+
readonly kind: "values"
|
|
67
|
+
readonly columns: readonly [string, ...string[]]
|
|
68
|
+
readonly rows: readonly [InsertValuesRowClause, ...InsertValuesRowClause[]]
|
|
69
|
+
}
|
|
70
|
+
| {
|
|
71
|
+
readonly kind: "query"
|
|
72
|
+
readonly columns: readonly [string, ...string[]]
|
|
73
|
+
readonly query: unknown
|
|
74
|
+
}
|
|
75
|
+
| {
|
|
76
|
+
readonly kind: "unnest"
|
|
77
|
+
readonly columns: readonly [string, ...string[]]
|
|
78
|
+
readonly values: readonly {
|
|
79
|
+
readonly columnName: string
|
|
80
|
+
readonly values: readonly unknown[]
|
|
81
|
+
}[]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** One branch inside a `merge` statement. */
|
|
85
|
+
export type MergeMatchedClause<
|
|
86
|
+
Predicate extends Expression.Any | undefined = Expression.Any | undefined
|
|
87
|
+
> =
|
|
88
|
+
| {
|
|
89
|
+
readonly kind: "update"
|
|
90
|
+
readonly values: readonly AssignmentClause[]
|
|
91
|
+
readonly predicate?: Predicate
|
|
92
|
+
}
|
|
93
|
+
| {
|
|
94
|
+
readonly kind: "delete"
|
|
95
|
+
readonly predicate?: Predicate
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Insert branch inside a `merge` statement. */
|
|
99
|
+
export interface MergeNotMatchedClause<
|
|
100
|
+
Predicate extends Expression.Any | undefined = Expression.Any | undefined
|
|
101
|
+
> {
|
|
102
|
+
readonly kind: "insert"
|
|
103
|
+
readonly values: readonly AssignmentClause[]
|
|
104
|
+
readonly predicate?: Predicate
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Payload recorded by a `merge` statement. */
|
|
108
|
+
export interface MergeClause<
|
|
109
|
+
On extends Expression.Any = Expression.Any,
|
|
110
|
+
Predicate extends Expression.Any | undefined = Expression.Any | undefined
|
|
111
|
+
> {
|
|
112
|
+
readonly kind: "merge"
|
|
113
|
+
readonly on: On
|
|
114
|
+
readonly whenMatched?: MergeMatchedClause<Predicate>
|
|
115
|
+
readonly whenNotMatched?: MergeNotMatchedClause<Predicate>
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** DDL payload recorded by schema-manipulation statements. */
|
|
119
|
+
export type DdlClause =
|
|
120
|
+
| {
|
|
121
|
+
readonly kind: "createTable"
|
|
122
|
+
readonly ifNotExists: boolean
|
|
123
|
+
}
|
|
124
|
+
| {
|
|
125
|
+
readonly kind: "dropTable"
|
|
126
|
+
readonly ifExists: boolean
|
|
127
|
+
}
|
|
128
|
+
| {
|
|
129
|
+
readonly kind: "createIndex"
|
|
130
|
+
readonly name: string
|
|
131
|
+
readonly columns: readonly [string, ...string[]]
|
|
132
|
+
readonly unique: boolean
|
|
133
|
+
readonly ifNotExists: boolean
|
|
134
|
+
}
|
|
135
|
+
| {
|
|
136
|
+
readonly kind: "dropIndex"
|
|
137
|
+
readonly name: string
|
|
138
|
+
readonly ifExists: boolean
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Truncate payload recorded by a truncate statement. */
|
|
142
|
+
export interface TruncateClause {
|
|
143
|
+
readonly kind: "truncate"
|
|
144
|
+
readonly restartIdentity: boolean
|
|
145
|
+
readonly cascade: boolean
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Transaction-control payload recorded by transactional statements. */
|
|
149
|
+
export type TransactionClause =
|
|
150
|
+
| {
|
|
151
|
+
readonly kind: "transaction"
|
|
152
|
+
readonly isolationLevel?: "read committed" | "repeatable read" | "serializable"
|
|
153
|
+
readonly readOnly?: boolean
|
|
154
|
+
}
|
|
155
|
+
| {
|
|
156
|
+
readonly kind: "commit"
|
|
157
|
+
}
|
|
158
|
+
| {
|
|
159
|
+
readonly kind: "rollback"
|
|
160
|
+
}
|
|
161
|
+
| {
|
|
162
|
+
readonly kind: "savepoint"
|
|
163
|
+
readonly name: string
|
|
164
|
+
}
|
|
165
|
+
| {
|
|
166
|
+
readonly kind: "rollbackTo"
|
|
167
|
+
readonly name: string
|
|
168
|
+
}
|
|
169
|
+
| {
|
|
170
|
+
readonly kind: "releaseSavepoint"
|
|
171
|
+
readonly name: string
|
|
172
|
+
}
|
|
173
|
+
| {
|
|
174
|
+
readonly kind: "dropTable"
|
|
175
|
+
readonly ifExists: boolean
|
|
176
|
+
}
|
|
177
|
+
| {
|
|
178
|
+
readonly kind: "createIndex"
|
|
179
|
+
readonly name: string
|
|
180
|
+
readonly columns: readonly [string, ...string[]]
|
|
181
|
+
readonly unique: boolean
|
|
182
|
+
readonly ifNotExists: boolean
|
|
183
|
+
}
|
|
184
|
+
| {
|
|
185
|
+
readonly kind: "dropIndex"
|
|
186
|
+
readonly name: string
|
|
187
|
+
readonly ifExists: boolean
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Locking mode attached to a select statement. */
|
|
191
|
+
export interface LockClause {
|
|
192
|
+
readonly kind: "lock"
|
|
193
|
+
readonly mode: "update" | "share" | "lowPriority" | "ignore" | "quick"
|
|
194
|
+
readonly nowait?: boolean
|
|
195
|
+
readonly skipLocked?: boolean
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Conflict target attached to a Postgres insert statement. */
|
|
199
|
+
export type ConflictTargetClause =
|
|
200
|
+
| {
|
|
201
|
+
readonly kind: "columns"
|
|
202
|
+
readonly columns: readonly [string, ...string[]]
|
|
203
|
+
readonly where?: Expression.Any
|
|
204
|
+
}
|
|
205
|
+
| {
|
|
206
|
+
readonly kind: "constraint"
|
|
207
|
+
readonly name: string
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Conflict clause attached to an insert statement. */
|
|
211
|
+
export interface ConflictClause {
|
|
212
|
+
readonly kind: "conflict"
|
|
213
|
+
readonly target?: ConflictTargetClause
|
|
214
|
+
readonly action: "doNothing" | "doUpdate"
|
|
215
|
+
readonly values?: readonly AssignmentClause[]
|
|
216
|
+
readonly where?: Expression.Any
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Join kinds supported by the current query layer. */
|
|
220
|
+
export type JoinKind = "inner" | "left" | "right" | "full" | "cross"
|
|
221
|
+
|
|
222
|
+
/** Join clause recorded by the query AST. */
|
|
223
|
+
export interface JoinClause<
|
|
224
|
+
TableName extends string = string,
|
|
225
|
+
Kind extends JoinKind = JoinKind,
|
|
226
|
+
On extends Expression.Any | undefined = Expression.Any | undefined
|
|
227
|
+
> {
|
|
228
|
+
readonly kind: Kind
|
|
229
|
+
readonly tableName: TableName
|
|
230
|
+
readonly baseTableName: string
|
|
231
|
+
readonly source: unknown
|
|
232
|
+
readonly on?: On
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Sort direction recorded by an `ORDER BY` clause. */
|
|
236
|
+
export type OrderDirection = "asc" | "desc"
|
|
237
|
+
|
|
238
|
+
/** Ordering clause recorded by the query AST. */
|
|
239
|
+
export interface OrderByClause<Value extends Expression.Any = Expression.Any> {
|
|
240
|
+
readonly kind: "orderBy"
|
|
241
|
+
readonly value: Value
|
|
242
|
+
readonly direction: OrderDirection
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** Set-operator kinds supported by compound queries. */
|
|
246
|
+
export type SetOperatorKind = "union" | "intersect" | "except"
|
|
247
|
+
|
|
248
|
+
/** Compound-query clause recorded by the query AST. */
|
|
249
|
+
export interface SetOperationClause {
|
|
250
|
+
readonly kind: SetOperatorKind
|
|
251
|
+
readonly all?: boolean
|
|
252
|
+
readonly query: unknown
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Internal query AST stored alongside public `Plan` metadata.
|
|
257
|
+
*
|
|
258
|
+
* The public plan state tracks selection, required sources, available sources,
|
|
259
|
+
* and dialect. This AST captures the clause ordering needed to eventually
|
|
260
|
+
* render or optimize a SQL query.
|
|
261
|
+
*/
|
|
262
|
+
export interface Ast<
|
|
263
|
+
Selection = unknown,
|
|
264
|
+
Grouped extends string = never,
|
|
265
|
+
Statement extends QueryStatement = "select"
|
|
266
|
+
> {
|
|
267
|
+
readonly kind: Statement
|
|
268
|
+
readonly select: Selection
|
|
269
|
+
readonly distinct?: boolean
|
|
270
|
+
readonly distinctOn?: readonly Expression.Any[]
|
|
271
|
+
readonly setBase?: unknown
|
|
272
|
+
readonly recursive?: boolean
|
|
273
|
+
readonly from?: FromClause
|
|
274
|
+
readonly fromSources?: readonly FromClause[]
|
|
275
|
+
readonly into?: FromClause
|
|
276
|
+
readonly target?: FromClause
|
|
277
|
+
readonly targets?: readonly FromClause[]
|
|
278
|
+
readonly using?: FromClause
|
|
279
|
+
readonly values?: readonly AssignmentClause[]
|
|
280
|
+
readonly insertSource?: InsertSourceClause
|
|
281
|
+
readonly set?: readonly AssignmentClause[]
|
|
282
|
+
readonly ddl?: DdlClause
|
|
283
|
+
readonly truncate?: TruncateClause
|
|
284
|
+
readonly merge?: MergeClause
|
|
285
|
+
readonly transaction?: TransactionClause
|
|
286
|
+
readonly lock?: LockClause
|
|
287
|
+
readonly conflict?: ConflictClause
|
|
288
|
+
readonly where: readonly WhereClause[]
|
|
289
|
+
readonly having: readonly HavingClause[]
|
|
290
|
+
readonly joins: readonly JoinClause[]
|
|
291
|
+
readonly groupBy: readonly Expression.Any[]
|
|
292
|
+
readonly orderBy: readonly OrderByClause[]
|
|
293
|
+
readonly limit?: Expression.Any
|
|
294
|
+
readonly offset?: Expression.Any
|
|
295
|
+
readonly setOperations?: readonly SetOperationClause[]
|
|
296
|
+
readonly groupedSources?: Grouped
|
|
297
|
+
}
|