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,1590 @@
|
|
|
1
|
+
import { pipeArguments, type Pipeable } from "effect/Pipeable"
|
|
2
|
+
|
|
3
|
+
import * as Expression from "./expression.js"
|
|
4
|
+
import * as Plan from "./plan.js"
|
|
5
|
+
import * as Table from "./table.js"
|
|
6
|
+
import * as ExpressionAst from "./expression-ast.js"
|
|
7
|
+
import * as QueryAst from "./query-ast.js"
|
|
8
|
+
import type { JsonNode } from "./json/ast.js"
|
|
9
|
+
import type * as JsonPath from "./json/path.js"
|
|
10
|
+
import type { QueryCapability } from "./query-requirements.js"
|
|
11
|
+
import type { CaseBranchAssumeFalse, CaseBranchAssumeTrue, CaseBranchDecision } from "./case-analysis.js"
|
|
12
|
+
import type { GuaranteedNonNullKeys, GuaranteedNullKeys, GuaranteedSourceNames } from "./predicate-analysis.js"
|
|
13
|
+
import type { PredicateFormula, TrueFormula } from "./predicate-formula.js"
|
|
14
|
+
|
|
15
|
+
export type {
|
|
16
|
+
MergeCapabilities,
|
|
17
|
+
MergeCapabilityTuple,
|
|
18
|
+
QueryCapability,
|
|
19
|
+
QueryRequirement
|
|
20
|
+
} from "./query-requirements.js"
|
|
21
|
+
export type {
|
|
22
|
+
ComparableDbType,
|
|
23
|
+
RuntimeOfDbType,
|
|
24
|
+
TextCompatibleDbType,
|
|
25
|
+
CastableDbType
|
|
26
|
+
} from "./coercion-analysis.js"
|
|
27
|
+
export type {
|
|
28
|
+
CanonicalSegment as JsonPathSegment,
|
|
29
|
+
DescendSegment as JsonPathDescendSegment,
|
|
30
|
+
ExactSegment as JsonExactPathSegment,
|
|
31
|
+
IndexSegment as JsonPathIndexSegment,
|
|
32
|
+
IsExactPath as IsExactJsonPath,
|
|
33
|
+
IsExactSegment as IsExactJsonPathSegment,
|
|
34
|
+
KeySegment as JsonPathKeySegment,
|
|
35
|
+
Path as JsonPath,
|
|
36
|
+
SegmentsOf as JsonPathSegments,
|
|
37
|
+
SliceSegment as JsonPathSliceSegment,
|
|
38
|
+
WildcardSegment as JsonPathWildcardSegment
|
|
39
|
+
} from "./json/path.js"
|
|
40
|
+
export type {
|
|
41
|
+
JsonPathUsageError
|
|
42
|
+
} from "./json/errors.js"
|
|
43
|
+
export type {
|
|
44
|
+
JsonConcatResult,
|
|
45
|
+
JsonDeleteAtPath,
|
|
46
|
+
JsonInsertAtPath,
|
|
47
|
+
JsonKeysResult,
|
|
48
|
+
JsonLengthResult,
|
|
49
|
+
JsonLiteralInput,
|
|
50
|
+
JsonPrimitive,
|
|
51
|
+
JsonSetAtPath,
|
|
52
|
+
JsonTextResult,
|
|
53
|
+
JsonTypeName,
|
|
54
|
+
JsonValue,
|
|
55
|
+
JsonValueAtPath,
|
|
56
|
+
NormalizeJsonLiteral
|
|
57
|
+
} from "./json/types.js"
|
|
58
|
+
export type {
|
|
59
|
+
CoercionKind,
|
|
60
|
+
CoercionKindOf
|
|
61
|
+
} from "./coercion-kind.js"
|
|
62
|
+
export type {
|
|
63
|
+
CanCastDbType,
|
|
64
|
+
CanCompareDbTypes,
|
|
65
|
+
CanContainDbTypes
|
|
66
|
+
} from "./coercion-rules.js"
|
|
67
|
+
export type {
|
|
68
|
+
ConflictClause,
|
|
69
|
+
LockClause,
|
|
70
|
+
QueryStatement,
|
|
71
|
+
SetOperatorKind as SetOperator
|
|
72
|
+
} from "./query-ast.js"
|
|
73
|
+
export { union_query_capabilities } from "./query-requirements.js"
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Shared prototype for runtime expression values created by query helpers.
|
|
77
|
+
*
|
|
78
|
+
* These objects are intentionally minimal. They only need to support
|
|
79
|
+
* `Pipeable.pipe(...)` plus the metadata stored under `Expression.TypeId`.
|
|
80
|
+
*/
|
|
81
|
+
const ExpressionProto = {
|
|
82
|
+
pipe(this: unknown) {
|
|
83
|
+
return pipeArguments(this, arguments)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Shared prototype for runtime plan values created by query helpers.
|
|
89
|
+
*
|
|
90
|
+
* Query plans behave like other Effect-style pipeable values so builders can be
|
|
91
|
+
* chained through `.pipe(...)`.
|
|
92
|
+
*/
|
|
93
|
+
const PlanProto = {
|
|
94
|
+
pipe(this: unknown) {
|
|
95
|
+
return pipeArguments(this, arguments)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Internal symbol used to preserve query-only phantom metadata through inference. */
|
|
100
|
+
const QueryTypeId: unique symbol = Symbol.for("effect-qb/Query/internal")
|
|
101
|
+
|
|
102
|
+
type InsertSourceState = "ready" | "missing"
|
|
103
|
+
|
|
104
|
+
/** Internal phantom state tracked on query plans. */
|
|
105
|
+
interface QueryState<
|
|
106
|
+
Outstanding extends string,
|
|
107
|
+
AvailableNames extends string,
|
|
108
|
+
Grouped extends string,
|
|
109
|
+
Assumptions extends PredicateFormula,
|
|
110
|
+
Capabilities extends QueryCapability,
|
|
111
|
+
Statement extends QueryAst.QueryStatement,
|
|
112
|
+
Target,
|
|
113
|
+
InsertState extends InsertSourceState
|
|
114
|
+
> {
|
|
115
|
+
readonly required: Outstanding
|
|
116
|
+
readonly availableNames: AvailableNames
|
|
117
|
+
readonly grouped: Grouped
|
|
118
|
+
readonly assumptions: Assumptions
|
|
119
|
+
readonly capabilities: Capabilities
|
|
120
|
+
readonly statement: Statement
|
|
121
|
+
readonly target: Target
|
|
122
|
+
readonly insertSource: InsertState
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Source provenance attached to an expression. */
|
|
126
|
+
export type SourceOf<Value extends Expression.Any> = Value[typeof Expression.TypeId]["source"]
|
|
127
|
+
/** Effective SQL dialect carried by an expression. */
|
|
128
|
+
export type DialectOf<Value extends Expression.Any> = Value[typeof Expression.TypeId]["dialect"]
|
|
129
|
+
/** Source dependency map carried by an expression. */
|
|
130
|
+
export type DependenciesOf<Value extends Expression.Any> = Expression.DependenciesOf<Value>
|
|
131
|
+
/** Aggregation kind carried by an expression. */
|
|
132
|
+
export type AggregationOf<Value extends Expression.Any> = Value[typeof Expression.TypeId]["aggregation"]
|
|
133
|
+
type AstOf<Value extends Expression.Any> = Value extends { readonly [ExpressionAst.TypeId]: infer Ast extends ExpressionAst.Any } ? Ast : never
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Primitive values that can be lifted directly into constant SQL expressions.
|
|
137
|
+
*
|
|
138
|
+
* This is the explicit surface today. Later coercion helpers can accept these
|
|
139
|
+
* primitives and normalize them through `literal(...)`.
|
|
140
|
+
*/
|
|
141
|
+
export type LiteralValue = string | number | boolean | null | Date
|
|
142
|
+
|
|
143
|
+
/** Runtime expression type produced by `literal(...)` for a primitive value. */
|
|
144
|
+
type LiteralExpression<Value extends LiteralValue> = Expression.Expression<
|
|
145
|
+
Value,
|
|
146
|
+
LiteralDbType<Value>,
|
|
147
|
+
LiteralNullability<Value>,
|
|
148
|
+
"postgres",
|
|
149
|
+
"scalar",
|
|
150
|
+
never
|
|
151
|
+
>
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Values accepted by scalar query operators.
|
|
155
|
+
*
|
|
156
|
+
* Raw primitives are automatically lifted into constant SQL expressions at the
|
|
157
|
+
* operator boundary.
|
|
158
|
+
*/
|
|
159
|
+
export type ExpressionInput = Expression.Any | LiteralValue
|
|
160
|
+
|
|
161
|
+
/** Input accepted by numeric clauses such as `limit(...)` and `offset(...)`. */
|
|
162
|
+
export type NumericExpressionInput = Expression.Expression<
|
|
163
|
+
number,
|
|
164
|
+
Expression.DbType.Any,
|
|
165
|
+
Expression.Nullability,
|
|
166
|
+
string,
|
|
167
|
+
"scalar",
|
|
168
|
+
any,
|
|
169
|
+
Expression.SourceDependencies,
|
|
170
|
+
Expression.SourceNullabilityMode
|
|
171
|
+
> | number
|
|
172
|
+
|
|
173
|
+
/** Values accepted by mutation payload fields. */
|
|
174
|
+
export type MutationValueInput<Value> =
|
|
175
|
+
| Value
|
|
176
|
+
| Expression.Expression<Value, Expression.DbType.Any, Expression.Nullability, string, Expression.AggregationKind, any, any, any>
|
|
177
|
+
|
|
178
|
+
/** Maps a payload shape to values or expressions of the same runtime type. */
|
|
179
|
+
export type MutationInputOf<Shape> = {
|
|
180
|
+
readonly [K in keyof Shape]: MutationValueInput<Shape[K]>
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
type Simplify<T> = { readonly [K in keyof T]: T[K] } & {}
|
|
184
|
+
|
|
185
|
+
/** Input accepted by boolean plan clauses such as `where(...)` and joins. */
|
|
186
|
+
export type PredicateInput = Expression.Expression<
|
|
187
|
+
boolean,
|
|
188
|
+
Expression.DbType.Any,
|
|
189
|
+
Expression.Nullability,
|
|
190
|
+
string,
|
|
191
|
+
"scalar",
|
|
192
|
+
any,
|
|
193
|
+
Expression.SourceDependencies,
|
|
194
|
+
Expression.SourceNullabilityMode
|
|
195
|
+
> | boolean
|
|
196
|
+
|
|
197
|
+
/** Input accepted by `having(...)`. */
|
|
198
|
+
export type HavingPredicateInput = Expression.Expression<
|
|
199
|
+
boolean,
|
|
200
|
+
Expression.DbType.Any,
|
|
201
|
+
Expression.Nullability,
|
|
202
|
+
string,
|
|
203
|
+
"scalar" | "aggregate",
|
|
204
|
+
any,
|
|
205
|
+
Expression.SourceDependencies,
|
|
206
|
+
Expression.SourceNullabilityMode
|
|
207
|
+
> | boolean
|
|
208
|
+
|
|
209
|
+
/** Input accepted by `GROUP BY`. */
|
|
210
|
+
export type GroupByInput = Expression.Expression<
|
|
211
|
+
any,
|
|
212
|
+
Expression.DbType.Any,
|
|
213
|
+
Expression.Nullability,
|
|
214
|
+
string,
|
|
215
|
+
"scalar",
|
|
216
|
+
any
|
|
217
|
+
>
|
|
218
|
+
|
|
219
|
+
/** Maps a literal runtime value to its SQL-level DB type descriptor. */
|
|
220
|
+
type LiteralDbType<Value extends LiteralValue> =
|
|
221
|
+
Value extends string ? Expression.DbType.PgText :
|
|
222
|
+
Value extends number ? Expression.DbType.PgNumeric :
|
|
223
|
+
Value extends boolean ? Expression.DbType.PgBool :
|
|
224
|
+
Value extends Date ? Expression.DbType.PgTimestamp :
|
|
225
|
+
Expression.DbType.Base<"postgres", "null">
|
|
226
|
+
|
|
227
|
+
/** Maps a literal runtime value to its static nullability state. */
|
|
228
|
+
type LiteralNullability<Value extends LiteralValue> = Value extends null ? "always" : "never"
|
|
229
|
+
/** Converts a supported input into its canonical expression type. */
|
|
230
|
+
type AsExpression<Value extends ExpressionInput> = Value extends Expression.Any ? Value : LiteralExpression<Extract<Value, LiteralValue>>
|
|
231
|
+
/** Extracts provenance from an operator input after coercion. */
|
|
232
|
+
type SourceOfInput<Value extends ExpressionInput> = SourceOf<AsExpression<Value>>
|
|
233
|
+
/** Extracts dialect from an operator input after coercion. */
|
|
234
|
+
type DialectOfInput<Value extends ExpressionInput> = DialectOf<AsExpression<Value>>
|
|
235
|
+
/** Extracts dependencies from an operator input after coercion. */
|
|
236
|
+
type DependenciesOfInput<Value extends ExpressionInput> = DependenciesOf<AsExpression<Value>>
|
|
237
|
+
/** Extracts required sources from an operator input after coercion. */
|
|
238
|
+
type RequiredFromInput<Value extends ExpressionInput> = RequiredFromDependencies<DependenciesOfInput<Value>>
|
|
239
|
+
/** String-valued expressions accepted by text operators. */
|
|
240
|
+
export type StringExpressionInput = Expression.Expression<
|
|
241
|
+
string | null,
|
|
242
|
+
Expression.DbType.Any,
|
|
243
|
+
Expression.Nullability,
|
|
244
|
+
string,
|
|
245
|
+
Expression.AggregationKind,
|
|
246
|
+
any,
|
|
247
|
+
Expression.SourceDependencies,
|
|
248
|
+
Expression.SourceNullabilityMode
|
|
249
|
+
> | string
|
|
250
|
+
/** Converts a string operator input into its canonical expression type. */
|
|
251
|
+
type AsStringExpression<Value extends StringExpressionInput> = Value extends Expression.Any ? Value : LiteralExpression<Extract<Value, string>>
|
|
252
|
+
/** Extracts provenance from a string operator input after coercion. */
|
|
253
|
+
type SourceOfStringInput<Value extends StringExpressionInput> = SourceOf<AsStringExpression<Value>>
|
|
254
|
+
/** Extracts dialect from a string operator input after coercion. */
|
|
255
|
+
type DialectOfStringInput<Value extends StringExpressionInput> = DialectOf<AsStringExpression<Value>>
|
|
256
|
+
/** Extracts dependencies from a string operator input after coercion. */
|
|
257
|
+
type DependenciesOfStringInput<Value extends StringExpressionInput> = DependenciesOf<AsStringExpression<Value>>
|
|
258
|
+
/** Extracts intrinsic nullability from a string operator input after coercion. */
|
|
259
|
+
type NullabilityOfStringInput<Value extends StringExpressionInput> = Expression.NullabilityOf<AsStringExpression<Value>>
|
|
260
|
+
|
|
261
|
+
/** Extracts a required table name from expression provenance. */
|
|
262
|
+
type RequiredFromSource<Source> = Source extends { readonly tableName: infer Name extends string } ? Name : never
|
|
263
|
+
/** Extracts required table names from an expression dependency map. */
|
|
264
|
+
export type RequiredFromDependencies<Dependencies extends Expression.SourceDependencies> = Extract<keyof Dependencies, string>
|
|
265
|
+
|
|
266
|
+
type LiteralGroupingKey<Value> =
|
|
267
|
+
Value extends string ? `string:${Value}` :
|
|
268
|
+
Value extends number ? `number:${Value}` :
|
|
269
|
+
Value extends boolean ? `boolean:${Value}` :
|
|
270
|
+
Value extends null ? "null" :
|
|
271
|
+
Value extends Date ? `date:${string}` :
|
|
272
|
+
"unknown"
|
|
273
|
+
|
|
274
|
+
type JoinGroupingKeys<Keys extends readonly string[]> = Keys extends readonly []
|
|
275
|
+
? ""
|
|
276
|
+
: Keys extends readonly [infer Head extends string]
|
|
277
|
+
? Head
|
|
278
|
+
: Keys extends readonly [infer Head extends string, ...infer Tail extends readonly string[]]
|
|
279
|
+
? `${Head},${JoinGroupingKeys<Tail>}`
|
|
280
|
+
: string
|
|
281
|
+
|
|
282
|
+
type JsonSegmentGroupingKey<Segment> =
|
|
283
|
+
Segment extends JsonPath.KeySegment<infer Key extends string> ? `key:${Key}` :
|
|
284
|
+
Segment extends JsonPath.IndexSegment<infer Index extends number> ? `index:${Index}` :
|
|
285
|
+
Segment extends JsonPath.WildcardSegment ? "wildcard" :
|
|
286
|
+
Segment extends JsonPath.SliceSegment<infer Start extends number | undefined, infer End extends number | undefined>
|
|
287
|
+
? `slice:${Start extends number ? Start : ""}:${End extends number ? End : ""}`
|
|
288
|
+
: Segment extends JsonPath.DescendSegment
|
|
289
|
+
? "descend"
|
|
290
|
+
: Segment extends string
|
|
291
|
+
? `key:${Segment}`
|
|
292
|
+
: Segment extends number
|
|
293
|
+
? `index:${Segment}`
|
|
294
|
+
: "unknown"
|
|
295
|
+
|
|
296
|
+
type JsonPathGroupingKey<Segments extends readonly any[]> = Segments extends readonly []
|
|
297
|
+
? ""
|
|
298
|
+
: Segments extends readonly [infer Head]
|
|
299
|
+
? JsonSegmentGroupingKey<Head>
|
|
300
|
+
: Segments extends readonly [infer Head, ...infer Tail extends readonly any[]]
|
|
301
|
+
? `${JsonSegmentGroupingKey<Head>},${JsonPathGroupingKey<Tail>}`
|
|
302
|
+
: string
|
|
303
|
+
|
|
304
|
+
type JsonOpaquePathGroupingKey<Value> =
|
|
305
|
+
Value extends JsonPath.Path<infer Segments extends readonly JsonPath.CanonicalSegment[]>
|
|
306
|
+
? `jsonpath:${JsonPathGroupingKey<Segments>}` :
|
|
307
|
+
Value extends string ? `jsonpath:${Value}` :
|
|
308
|
+
Value extends Expression.Any ? `jsonpath:${GroupingKeyOfExpression<Value>}` :
|
|
309
|
+
"jsonpath:unknown"
|
|
310
|
+
|
|
311
|
+
type JsonEntryGroupingKey<Entry> =
|
|
312
|
+
Entry extends { readonly key: infer Key extends string; readonly value: infer Value extends Expression.Any }
|
|
313
|
+
? `${Key}=>${GroupingKeyOfExpression<Value>}`
|
|
314
|
+
: "entry:unknown"
|
|
315
|
+
|
|
316
|
+
type JsonEntriesGroupingKey<Entries extends readonly { readonly key: string; readonly value: Expression.Any }[]> = Entries extends readonly []
|
|
317
|
+
? ""
|
|
318
|
+
: Entries extends readonly [infer Head]
|
|
319
|
+
? JsonEntryGroupingKey<Head>
|
|
320
|
+
: Entries extends readonly [infer Head, ...infer Tail extends readonly { readonly key: string; readonly value: Expression.Any }[]]
|
|
321
|
+
? `${JsonEntryGroupingKey<Head>}|${JsonEntriesGroupingKey<Tail>}`
|
|
322
|
+
: string
|
|
323
|
+
|
|
324
|
+
type BranchGroupingKeys<
|
|
325
|
+
Branches extends readonly ExpressionAst.CaseBranchNode[]
|
|
326
|
+
> = Branches extends readonly []
|
|
327
|
+
? ""
|
|
328
|
+
: Branches extends readonly [infer Head extends ExpressionAst.CaseBranchNode]
|
|
329
|
+
? `when:${GroupingKeyOfExpression<Head["when"]>}=>${GroupingKeyOfExpression<Head["then"]>}`
|
|
330
|
+
: Branches extends readonly [
|
|
331
|
+
infer Head extends ExpressionAst.CaseBranchNode,
|
|
332
|
+
...infer Tail extends readonly ExpressionAst.CaseBranchNode[]
|
|
333
|
+
]
|
|
334
|
+
? `when:${GroupingKeyOfExpression<Head["when"]>}=>${GroupingKeyOfExpression<Head["then"]>}|${BranchGroupingKeys<Tail>}`
|
|
335
|
+
: string
|
|
336
|
+
|
|
337
|
+
type GroupingKeyOfAst<Ast extends ExpressionAst.Any> =
|
|
338
|
+
Ast extends ExpressionAst.ColumnNode<infer TableName extends string, infer ColumnName extends string>
|
|
339
|
+
? `column:${TableName}.${ColumnName}`
|
|
340
|
+
: Ast extends ExpressionAst.LiteralNode<infer Value>
|
|
341
|
+
? `literal:${LiteralGroupingKey<Value>}`
|
|
342
|
+
: Ast extends ExpressionAst.ExcludedNode<infer ColumnName extends string>
|
|
343
|
+
? `excluded:${ColumnName}`
|
|
344
|
+
: Ast extends ExpressionAst.CastNode<infer Value extends Expression.Any, infer Target extends Expression.DbType.Any>
|
|
345
|
+
? `cast(${GroupingKeyOfExpression<Value>} as ${Target["dialect"]}:${Target["kind"]})`
|
|
346
|
+
: Ast extends ExpressionAst.UnaryNode<infer Kind extends ExpressionAst.UnaryKind, infer Value extends Expression.Any>
|
|
347
|
+
? `${Kind}(${GroupingKeyOfExpression<Value>})`
|
|
348
|
+
: Ast extends ExpressionAst.BinaryNode<infer Kind extends ExpressionAst.BinaryKind, infer Left extends Expression.Any, infer Right extends Expression.Any>
|
|
349
|
+
? `${Kind}(${GroupingKeyOfExpression<Left>},${GroupingKeyOfExpression<Right>})`
|
|
350
|
+
: Ast extends ExpressionAst.VariadicNode<infer Kind extends ExpressionAst.VariadicKind, infer Values extends readonly Expression.Any[]>
|
|
351
|
+
? `${Kind}(${JoinGroupingKeys<{
|
|
352
|
+
readonly [K in keyof Values]: Values[K] extends Expression.Any ? GroupingKeyOfExpression<Values[K]> : never
|
|
353
|
+
} & readonly string[]>})`
|
|
354
|
+
: Ast extends JsonNode<infer Kind>
|
|
355
|
+
? Kind extends "jsonGet" | "jsonPath" | "jsonAccess" | "jsonTraverse" | "jsonGetText" | "jsonPathText" | "jsonAccessText" | "jsonTraverseText"
|
|
356
|
+
? `json(${Kind},${GroupingKeyOfExpression<Extract<Ast["value"] | Ast["base"] | Ast["left"], Expression.Any>>},${JsonPathGroupingKey<Extract<Ast["segments"] | Ast["path"], readonly any[]>>})`
|
|
357
|
+
: Kind extends "jsonHasKey" | "jsonKeyExists" | "jsonHasAnyKeys" | "jsonHasAllKeys"
|
|
358
|
+
? `json(${Kind},${GroupingKeyOfExpression<Extract<Ast["value"] | Ast["base"] | Ast["left"], Expression.Any>>},${JoinGroupingKeys<Extract<Ast["keys"], readonly string[]> & readonly string[]>})`
|
|
359
|
+
: Kind extends "jsonConcat" | "jsonMerge" | "jsonDelete" | "jsonDeletePath" | "jsonRemove" | "jsonSet" | "jsonInsert"
|
|
360
|
+
? `json(${Kind},${GroupingKeyOfExpression<Extract<Ast["left"] | Ast["base"] | Ast["value"], Expression.Any>>},${GroupingKeyOfExpression<Extract<Ast["right"] | Ast["newValue"] | Ast["insert"], Expression.Any>>},${JsonPathGroupingKey<Extract<Ast["segments"] | Ast["path"], readonly any[]>>})`
|
|
361
|
+
: Kind extends "jsonPathExists" | "jsonPathMatch"
|
|
362
|
+
? `json(${Kind},${GroupingKeyOfExpression<Extract<Ast["value"] | Ast["base"], Expression.Any>>},${JsonOpaquePathGroupingKey<Ast["query"] | Ast["path"]>})`
|
|
363
|
+
: Kind extends "jsonBuildObject"
|
|
364
|
+
? `json(${Kind},${JsonEntriesGroupingKey<Extract<Ast["entries"], readonly { readonly key: string; readonly value: Expression.Any }[]>>})`
|
|
365
|
+
: Kind extends "jsonBuildArray"
|
|
366
|
+
? `json(${Kind},${JoinGroupingKeys<{
|
|
367
|
+
readonly [K in keyof Extract<Ast["values"], readonly Expression.Any[]>]:
|
|
368
|
+
Extract<Ast["values"], readonly Expression.Any[]>[K] extends Expression.Any ? GroupingKeyOfExpression<Extract<Ast["values"], readonly Expression.Any[]>[K]> : never
|
|
369
|
+
} & readonly string[]>})`
|
|
370
|
+
: Kind extends "jsonToJson" | "jsonToJsonb" | "jsonTypeOf" | "jsonLength" | "jsonKeys" | "jsonStripNulls"
|
|
371
|
+
? `json(${Kind},${GroupingKeyOfExpression<Extract<Ast["value"], Expression.Any>>})`
|
|
372
|
+
: never
|
|
373
|
+
: Ast extends ExpressionAst.CaseNode<infer Branches extends readonly ExpressionAst.CaseBranchNode[], infer Else extends Expression.Any>
|
|
374
|
+
? `case(${BranchGroupingKeys<Branches>};else:${GroupingKeyOfExpression<Else>})`
|
|
375
|
+
: Ast extends ExpressionAst.ExistsNode
|
|
376
|
+
? "exists(subquery)"
|
|
377
|
+
: never
|
|
378
|
+
|
|
379
|
+
/** Canonical grouping identity for an expression AST. */
|
|
380
|
+
export type GroupingKeyOfExpression<Value extends Expression.Any> = GroupingKeyOfAst<AstOf<Value>>
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Recursive selection tree accepted by `select(...)`.
|
|
384
|
+
*
|
|
385
|
+
* A selection can be either:
|
|
386
|
+
* - a leaf SQL expression
|
|
387
|
+
* - a nested object whose leaves are expressions
|
|
388
|
+
*/
|
|
389
|
+
export type SelectionShape =
|
|
390
|
+
| Expression.Any
|
|
391
|
+
| {
|
|
392
|
+
readonly [key: string]: SelectionShape
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** Walks a selection tree and unions the table names referenced by its leaves. */
|
|
396
|
+
export type ExtractRequired<Selection> = Selection extends Expression.Any
|
|
397
|
+
? RequiredFromDependencies<DependenciesOf<Selection>>
|
|
398
|
+
: Selection extends Record<string, any>
|
|
399
|
+
? {
|
|
400
|
+
[K in keyof Selection]: ExtractRequired<Selection[K]>
|
|
401
|
+
}[keyof Selection]
|
|
402
|
+
: never
|
|
403
|
+
|
|
404
|
+
/** Walks a selection tree and unions the dialects referenced by its leaves. */
|
|
405
|
+
export type ExtractDialect<Selection> = Selection extends Expression.Any
|
|
406
|
+
? DialectOf<Selection>
|
|
407
|
+
: Selection extends Record<string, any>
|
|
408
|
+
? {
|
|
409
|
+
[K in keyof Selection]: ExtractDialect<Selection[K]>
|
|
410
|
+
}[keyof Selection]
|
|
411
|
+
: never
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Minimal table-like shape required by `from(...)` and joins.
|
|
415
|
+
*
|
|
416
|
+
* The query layer only needs the plan metadata and the static table name. It
|
|
417
|
+
* deliberately avoids depending on the full table-definition surface.
|
|
418
|
+
*/
|
|
419
|
+
export type TableLike<Name extends string = string, Dialect extends string = string> = Plan.Plan<any, any, Record<string, Plan.Source>, Dialect> & {
|
|
420
|
+
readonly [Table.TypeId]: {
|
|
421
|
+
readonly name: Name
|
|
422
|
+
readonly baseName: string
|
|
423
|
+
readonly schemaName?: string
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/** Concrete schema table accepted by DDL builders. */
|
|
428
|
+
export type SchemaTableLike =
|
|
429
|
+
| Table.TableDefinition<any, any, any, "schema", any>
|
|
430
|
+
| Table.TableClassStatic<any, any, any, any>
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Wrapper returned by `as(subquery, alias)` for derived-table composition.
|
|
434
|
+
*
|
|
435
|
+
* The derived source exposes the subquery output under the new alias and can
|
|
436
|
+
* be passed to `from(...)` or join builders.
|
|
437
|
+
*/
|
|
438
|
+
export type DerivedSource<
|
|
439
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
440
|
+
Alias extends string
|
|
441
|
+
> = DerivedSelectionOf<SelectionOfPlan<PlanValue>, Alias> & {
|
|
442
|
+
readonly kind: "derived"
|
|
443
|
+
readonly name: Alias
|
|
444
|
+
readonly baseName: Alias
|
|
445
|
+
readonly dialect: PlanDialectOf<PlanValue>
|
|
446
|
+
readonly plan: CompletePlan<PlanValue>
|
|
447
|
+
readonly required?: never
|
|
448
|
+
readonly columns: DerivedSelectionOf<SelectionOfPlan<PlanValue>, Alias>
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/** Wrapper returned by `with(subquery, alias)` for common table expression composition. */
|
|
452
|
+
export type CteSource<
|
|
453
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
454
|
+
Alias extends string
|
|
455
|
+
> = DerivedSelectionOf<SelectionOfPlan<PlanValue>, Alias> & {
|
|
456
|
+
readonly kind: "cte"
|
|
457
|
+
readonly name: Alias
|
|
458
|
+
readonly baseName: Alias
|
|
459
|
+
readonly dialect: PlanDialectOf<PlanValue>
|
|
460
|
+
readonly plan: CompletePlan<PlanValue>
|
|
461
|
+
readonly recursive?: boolean
|
|
462
|
+
readonly columns: DerivedSelectionOf<SelectionOfPlan<PlanValue>, Alias>
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/** Wrapper returned by `lateral(subquery, alias)` for correlated derived sources. */
|
|
466
|
+
export type LateralSource<
|
|
467
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
468
|
+
Alias extends string
|
|
469
|
+
> = DerivedSelectionOf<SelectionOfPlan<PlanValue>, Alias> & {
|
|
470
|
+
readonly kind: "lateral"
|
|
471
|
+
readonly name: Alias
|
|
472
|
+
readonly baseName: Alias
|
|
473
|
+
readonly dialect: PlanDialectOf<PlanValue>
|
|
474
|
+
readonly plan: PlanValue
|
|
475
|
+
readonly required: RequiredOfPlan<PlanValue>
|
|
476
|
+
readonly columns: DerivedSelectionOf<SelectionOfPlan<PlanValue>, Alias>
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
type ValuesRowInput = Record<string, ExpressionInput>
|
|
480
|
+
|
|
481
|
+
/** Anonymous `values(...)` input that must be aliased through `as(...)` before use as a source. */
|
|
482
|
+
export type ValuesInput<
|
|
483
|
+
Rows extends readonly [ValuesRowInput, ...ValuesRowInput[]],
|
|
484
|
+
Selection extends SelectionShape,
|
|
485
|
+
Dialect extends string
|
|
486
|
+
> = Pipeable & {
|
|
487
|
+
readonly kind: "values"
|
|
488
|
+
readonly dialect: Dialect
|
|
489
|
+
readonly rows: readonly [Record<string, Expression.Any>, ...Record<string, Expression.Any>[]]
|
|
490
|
+
readonly selection: Selection
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/** Wrapper returned by `as(values(rows), alias)` for standalone row sources and insert sources. */
|
|
494
|
+
export type ValuesSource<
|
|
495
|
+
Rows extends readonly [ValuesRowInput, ...ValuesRowInput[]],
|
|
496
|
+
Selection extends SelectionShape,
|
|
497
|
+
Alias extends string,
|
|
498
|
+
Dialect extends string
|
|
499
|
+
> = DerivedSelectionOf<Selection, Alias> & {
|
|
500
|
+
readonly kind: "values"
|
|
501
|
+
readonly name: Alias
|
|
502
|
+
readonly baseName: Alias
|
|
503
|
+
readonly dialect: Dialect
|
|
504
|
+
readonly rows: readonly [Record<string, Expression.Any>, ...Record<string, Expression.Any>[]]
|
|
505
|
+
readonly columns: DerivedSelectionOf<Selection, Alias>
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/** Broad structural shape for anonymous `values(...)` inputs before aliasing. */
|
|
509
|
+
export type AnyValuesInput = {
|
|
510
|
+
readonly kind: "values"
|
|
511
|
+
readonly dialect: string
|
|
512
|
+
readonly rows: readonly Record<string, Expression.Any>[]
|
|
513
|
+
readonly selection: Record<string, Expression.Any>
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/** Broad structural shape for `values(...)` sources when used as composable inputs. */
|
|
517
|
+
export type AnyValuesSource = {
|
|
518
|
+
readonly kind: "values"
|
|
519
|
+
readonly name: string
|
|
520
|
+
readonly baseName: string
|
|
521
|
+
readonly dialect: string
|
|
522
|
+
readonly rows: readonly Record<string, Expression.Any>[]
|
|
523
|
+
readonly columns: Record<string, Expression.Any>
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/** Wrapper returned by `unnest(columns, alias)` for standalone array sources. */
|
|
527
|
+
export type UnnestSource<
|
|
528
|
+
Selection extends SelectionShape,
|
|
529
|
+
Alias extends string,
|
|
530
|
+
Dialect extends string
|
|
531
|
+
> = DerivedSelectionOf<Selection, Alias> & {
|
|
532
|
+
readonly kind: "unnest"
|
|
533
|
+
readonly name: Alias
|
|
534
|
+
readonly baseName: Alias
|
|
535
|
+
readonly dialect: Dialect
|
|
536
|
+
readonly values: Readonly<Record<string, readonly ExpressionInput[]>>
|
|
537
|
+
readonly arrays: Readonly<Record<string, readonly Expression.Any[]>>
|
|
538
|
+
readonly columns: DerivedSelectionOf<Selection, Alias>
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/** Wrapper returned by `generateSeries(...)` and similar table functions. */
|
|
542
|
+
export type TableFunctionSource<
|
|
543
|
+
Selection extends SelectionShape,
|
|
544
|
+
Alias extends string,
|
|
545
|
+
Dialect extends string,
|
|
546
|
+
FunctionName extends string = string
|
|
547
|
+
> = DerivedSelectionOf<Selection, Alias> & {
|
|
548
|
+
readonly kind: "tableFunction"
|
|
549
|
+
readonly name: Alias
|
|
550
|
+
readonly baseName: Alias
|
|
551
|
+
readonly dialect: Dialect
|
|
552
|
+
readonly functionName: FunctionName
|
|
553
|
+
readonly args: readonly Expression.Any[]
|
|
554
|
+
readonly columns: DerivedSelectionOf<Selection, Alias>
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/** Accepts either a physical table or a derived table source. */
|
|
558
|
+
type DerivedSourceShape = {
|
|
559
|
+
readonly kind: "derived"
|
|
560
|
+
readonly name: string
|
|
561
|
+
readonly baseName: string
|
|
562
|
+
readonly dialect: string
|
|
563
|
+
readonly plan: QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
564
|
+
readonly columns: Record<string, unknown>
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
type CteSourceShape = {
|
|
568
|
+
readonly kind: "cte"
|
|
569
|
+
readonly name: string
|
|
570
|
+
readonly baseName: string
|
|
571
|
+
readonly dialect: string
|
|
572
|
+
readonly plan: QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
573
|
+
readonly recursive?: boolean
|
|
574
|
+
readonly required?: never
|
|
575
|
+
readonly columns: Record<string, unknown>
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
type LateralSourceShape = {
|
|
579
|
+
readonly kind: "lateral"
|
|
580
|
+
readonly name: string
|
|
581
|
+
readonly baseName: string
|
|
582
|
+
readonly dialect: string
|
|
583
|
+
readonly plan: QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
584
|
+
readonly required: string
|
|
585
|
+
readonly columns: Record<string, unknown>
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
type ValuesSourceShape = {
|
|
589
|
+
readonly kind: "values"
|
|
590
|
+
readonly name: string
|
|
591
|
+
readonly baseName: string
|
|
592
|
+
readonly dialect: string
|
|
593
|
+
readonly rows: readonly Record<string, Expression.Any>[]
|
|
594
|
+
readonly columns: Record<string, unknown>
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
type UnnestSourceShape = {
|
|
598
|
+
readonly kind: "unnest"
|
|
599
|
+
readonly name: string
|
|
600
|
+
readonly baseName: string
|
|
601
|
+
readonly dialect: string
|
|
602
|
+
readonly values: Readonly<Record<string, readonly ExpressionInput[]>>
|
|
603
|
+
readonly arrays: Readonly<Record<string, readonly Expression.Any[]>>
|
|
604
|
+
readonly columns: Record<string, unknown>
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/** Broad structural shape for `unnest(...)` sources when used as composable inputs. */
|
|
608
|
+
export type AnyUnnestSource = {
|
|
609
|
+
readonly kind: "unnest"
|
|
610
|
+
readonly name: string
|
|
611
|
+
readonly baseName: string
|
|
612
|
+
readonly dialect: string
|
|
613
|
+
readonly values: Readonly<Record<string, readonly ExpressionInput[]>>
|
|
614
|
+
readonly arrays: Readonly<Record<string, readonly Expression.Any[]>>
|
|
615
|
+
readonly columns: Record<string, Expression.Any>
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
type TableFunctionSourceShape = {
|
|
619
|
+
readonly kind: "tableFunction"
|
|
620
|
+
readonly name: string
|
|
621
|
+
readonly baseName: string
|
|
622
|
+
readonly dialect: string
|
|
623
|
+
readonly functionName: string
|
|
624
|
+
readonly args: readonly Expression.Any[]
|
|
625
|
+
readonly columns: Record<string, unknown>
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
type DerivedSourceAliasError = DerivedSourceRequiredError<QueryPlan<any, any, any, any, any, any, any, any, any, any>>
|
|
629
|
+
|
|
630
|
+
export type SourceLike =
|
|
631
|
+
| TableLike<any, any>
|
|
632
|
+
| DerivedSourceShape
|
|
633
|
+
| CteSourceShape
|
|
634
|
+
| LateralSourceShape
|
|
635
|
+
| ValuesSourceShape
|
|
636
|
+
| UnnestSourceShape
|
|
637
|
+
| TableFunctionSourceShape
|
|
638
|
+
| DerivedSourceAliasError
|
|
639
|
+
|
|
640
|
+
/** Concrete table sources that can be targeted by mutation statements. */
|
|
641
|
+
export type MutationTargetLike = Table.AnyTable
|
|
642
|
+
export type MutationTargetTuple = readonly [MutationTargetLike, MutationTargetLike, ...MutationTargetLike[]]
|
|
643
|
+
export type MutationTargetInput = MutationTargetLike | MutationTargetTuple
|
|
644
|
+
|
|
645
|
+
/** Extracts a source name from either a table or a derived source. */
|
|
646
|
+
export type SourceNameOf<Source extends SourceLike> =
|
|
647
|
+
Source extends TableLike<infer Name, any> ? Name :
|
|
648
|
+
Source extends { readonly kind: "derived"; readonly name: infer Alias extends string } ? Alias :
|
|
649
|
+
Source extends { readonly kind: "cte"; readonly name: infer Alias extends string } ? Alias :
|
|
650
|
+
Source extends { readonly kind: "lateral"; readonly name: infer Alias extends string } ? Alias :
|
|
651
|
+
Source extends { readonly kind: "values"; readonly name: infer Alias extends string } ? Alias :
|
|
652
|
+
Source extends { readonly kind: "unnest"; readonly name: infer Alias extends string } ? Alias :
|
|
653
|
+
Source extends { readonly kind: "tableFunction"; readonly name: infer Alias extends string } ? Alias :
|
|
654
|
+
never
|
|
655
|
+
|
|
656
|
+
type MutationTargetByName<
|
|
657
|
+
Targets extends MutationTargetTuple,
|
|
658
|
+
Name extends string
|
|
659
|
+
> = Extract<Targets[number], { readonly [Table.TypeId]: { readonly name: Name } }>
|
|
660
|
+
|
|
661
|
+
export type MutationTargetNamesOf<Target extends MutationTargetInput> =
|
|
662
|
+
Target extends MutationTargetLike
|
|
663
|
+
? SourceNameOf<Target>
|
|
664
|
+
: Target extends MutationTargetTuple
|
|
665
|
+
? SourceNameOf<Target[number]>
|
|
666
|
+
: never
|
|
667
|
+
|
|
668
|
+
export type UpdateInputOfTarget<Target extends MutationTargetInput> =
|
|
669
|
+
Target extends MutationTargetLike
|
|
670
|
+
? MutationInputOf<Table.UpdateOf<Target>>
|
|
671
|
+
: Target extends MutationTargetTuple
|
|
672
|
+
? Simplify<{
|
|
673
|
+
readonly [K in MutationTargetNamesOf<Target>]?: MutationInputOf<Table.UpdateOf<MutationTargetByName<Target, K>>>
|
|
674
|
+
}>
|
|
675
|
+
: never
|
|
676
|
+
|
|
677
|
+
/** Extracts the effective dialect from a source. */
|
|
678
|
+
export type SourceDialectOf<Source extends SourceLike> =
|
|
679
|
+
Source extends TableLike<any, infer Dialect> ? Dialect :
|
|
680
|
+
Source extends { readonly kind: "derived"; readonly plan: infer PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any> } ? PlanDialectOf<PlanValue> :
|
|
681
|
+
Source extends { readonly kind: "cte"; readonly plan: infer PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any> } ? PlanDialectOf<PlanValue> :
|
|
682
|
+
Source extends { readonly kind: "lateral"; readonly plan: infer PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any> } ? PlanDialectOf<PlanValue> :
|
|
683
|
+
Source extends { readonly dialect: infer Dialect extends string } ? Dialect :
|
|
684
|
+
never
|
|
685
|
+
|
|
686
|
+
/** Extracts the base table name from a source. */
|
|
687
|
+
export type SourceBaseNameOf<Source extends SourceLike> =
|
|
688
|
+
Source extends TableLike<any, any> ? Source[typeof Table.TypeId]["baseName"] :
|
|
689
|
+
Source extends { readonly kind: "derived"; readonly baseName: infer BaseName extends string } ? BaseName :
|
|
690
|
+
Source extends { readonly kind: "cte"; readonly baseName: infer BaseName extends string } ? BaseName :
|
|
691
|
+
Source extends { readonly kind: "lateral"; readonly baseName: infer BaseName extends string } ? BaseName :
|
|
692
|
+
Source extends { readonly baseName: infer BaseName extends string } ? BaseName :
|
|
693
|
+
never
|
|
694
|
+
|
|
695
|
+
/** Extracts the outer-scope requirements carried by a source. */
|
|
696
|
+
export type SourceRequiredOf<Source extends SourceLike> =
|
|
697
|
+
Source extends TableLike<any, any> ? never :
|
|
698
|
+
Source extends { readonly kind: "derived" } ? never :
|
|
699
|
+
Source extends { readonly kind: "cte" } ? never :
|
|
700
|
+
Source extends { readonly kind: "lateral"; readonly required: infer Required extends string } ? Required :
|
|
701
|
+
never
|
|
702
|
+
|
|
703
|
+
/** Helper type used when a correlated source is used before its outer dependencies are in scope. */
|
|
704
|
+
export type SourceRequirementError<
|
|
705
|
+
Source extends SourceLike
|
|
706
|
+
> = Source & {
|
|
707
|
+
readonly __effect_qb_error__: "effect-qb: correlated source requires outer-scope tables to be in scope first"
|
|
708
|
+
readonly __effect_qb_required_sources__: SourceRequiredOf<Source>
|
|
709
|
+
readonly __effect_qb_hint__: "Join the outer tables first, then wrap the correlated plan in lateral(...)"
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/** Helper type used when a raw plan is passed where `as(...)` is required. */
|
|
713
|
+
export type DerivedSourceRequiredError<
|
|
714
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
715
|
+
> = PlanValue & {
|
|
716
|
+
readonly __effect_qb_error__: "effect-qb: subqueries must be aliased before they can be used as a source"
|
|
717
|
+
readonly __effect_qb_hint__: "Wrap the nested plan in as(subquery, alias) before passing it to from(...) or a join"
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
type JoinPath<Segments extends readonly string[]> = Segments extends readonly []
|
|
721
|
+
? ""
|
|
722
|
+
: Segments extends readonly [infer Head extends string]
|
|
723
|
+
? Head
|
|
724
|
+
: Segments extends readonly [infer Head extends string, ...infer Tail extends readonly string[]]
|
|
725
|
+
? `${Head}__${JoinPath<Tail>}`
|
|
726
|
+
: string
|
|
727
|
+
|
|
728
|
+
type DerivedLeafExpression<
|
|
729
|
+
Value extends Expression.Any,
|
|
730
|
+
Alias extends string,
|
|
731
|
+
ColumnName extends string
|
|
732
|
+
> = Expression.Expression<
|
|
733
|
+
Expression.RuntimeOf<Value>,
|
|
734
|
+
Expression.DbTypeOf<Value>,
|
|
735
|
+
Expression.NullabilityOf<Value>,
|
|
736
|
+
DialectOf<Value>,
|
|
737
|
+
"scalar",
|
|
738
|
+
Expression.ColumnSource<Alias, ColumnName, Alias>,
|
|
739
|
+
Record<Alias, true>,
|
|
740
|
+
"propagate"
|
|
741
|
+
> & {
|
|
742
|
+
readonly [ExpressionAst.TypeId]: ExpressionAst.ColumnNode<Alias, ColumnName>
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/** Rebinds a nested selection tree to a derived-table alias. */
|
|
746
|
+
export type DerivedSelectionOf<
|
|
747
|
+
Selection,
|
|
748
|
+
Alias extends string,
|
|
749
|
+
Path extends readonly string[] = []
|
|
750
|
+
> = Selection extends Expression.Any
|
|
751
|
+
? DerivedLeafExpression<Selection, Alias, JoinPath<Path>>
|
|
752
|
+
: Selection extends Record<string, any>
|
|
753
|
+
? {
|
|
754
|
+
readonly [K in keyof Selection]: DerivedSelectionOf<Selection[K], Alias, [...Path, Extract<K, string>]>
|
|
755
|
+
}
|
|
756
|
+
: never
|
|
757
|
+
|
|
758
|
+
/** Extracts the static SQL table name from a table-like value. */
|
|
759
|
+
export type TableNameOf<T extends TableLike> = T[typeof Table.TypeId]["name"]
|
|
760
|
+
/** Extracts the effective dialect from a table-like value. */
|
|
761
|
+
export type TableDialectOf<T extends TableLike> = T[typeof Plan.TypeId]["dialect"]
|
|
762
|
+
/** Names of sources already available to a plan. */
|
|
763
|
+
type AvailableNames<Available extends Record<string, Plan.Source>> = Extract<keyof Available, string>
|
|
764
|
+
/** Availability mode of a named source within the current plan scope. */
|
|
765
|
+
type SourceModeOf<
|
|
766
|
+
Available extends Record<string, Plan.Source>,
|
|
767
|
+
Name extends string
|
|
768
|
+
> = Name extends keyof Available ? Available[Name]["mode"] : never
|
|
769
|
+
type TrueAssumptions = TrueFormula
|
|
770
|
+
|
|
771
|
+
/** Extracts the selection carried by a query plan. */
|
|
772
|
+
export type SelectionOfPlan<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
|
|
773
|
+
PlanValue[typeof Plan.TypeId]["selection"]
|
|
774
|
+
/** Extracts the public required-source state carried by a query plan. */
|
|
775
|
+
export type RequiredOfPlan<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
|
|
776
|
+
PlanValue[typeof Plan.TypeId]["required"]
|
|
777
|
+
/** Extracts the available-source scope carried by a query plan. */
|
|
778
|
+
export type AvailableOfPlan<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
|
|
779
|
+
PlanValue[typeof Plan.TypeId]["available"]
|
|
780
|
+
/** Extracts the effective dialect carried by a query plan. */
|
|
781
|
+
export type PlanDialectOf<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
|
|
782
|
+
PlanValue[typeof Plan.TypeId]["dialect"]
|
|
783
|
+
/** Extracts the grouped-source phantom carried by a query plan. */
|
|
784
|
+
export type GroupedOfPlan<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
|
|
785
|
+
PlanValue extends QueryPlan<any, any, any, any, infer Grouped, any, any, any, any, any> ? Grouped : never
|
|
786
|
+
/** Extracts the available-name phantom carried by a query plan. */
|
|
787
|
+
export type ScopedNamesOfPlan<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
|
|
788
|
+
PlanValue extends QueryPlan<any, any, any, any, any, infer ScopedNames, any, any, any, any> ? ScopedNames : never
|
|
789
|
+
/** Extracts the outstanding-required-source phantom carried by a query plan. */
|
|
790
|
+
export type OutstandingOfPlan<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
|
|
791
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, infer Outstanding, any, any, any> ? Outstanding : never
|
|
792
|
+
export type AssumptionsOfPlan<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
|
|
793
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, infer Assumptions, any, any> ? Assumptions : TrueAssumptions
|
|
794
|
+
export type CapabilitiesOfPlan<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
|
|
795
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, infer Capabilities, any> ? Capabilities : never
|
|
796
|
+
/** Extracts capabilities contributed by a source wrapper. */
|
|
797
|
+
export type SourceCapabilitiesOf<Source extends SourceLike> =
|
|
798
|
+
Source extends TableLike<any, any> ? never :
|
|
799
|
+
Source extends { readonly kind: "derived"; readonly plan: infer PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any> } ? CapabilitiesOfPlan<PlanValue> :
|
|
800
|
+
Source extends { readonly kind: "cte"; readonly plan: infer PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any> } ? CapabilitiesOfPlan<PlanValue> :
|
|
801
|
+
Source extends { readonly kind: "lateral"; readonly plan: infer PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any> } ? CapabilitiesOfPlan<PlanValue> :
|
|
802
|
+
never
|
|
803
|
+
/** Extracts the statement kind carried by a query plan. */
|
|
804
|
+
export type StatementOfPlan<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
|
|
805
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, infer Statement> ? Statement : never
|
|
806
|
+
/** Extracts the mutation target phantom carried by a query plan. */
|
|
807
|
+
export type MutationTargetOfPlan<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
|
|
808
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any, infer Target> ? Target : never
|
|
809
|
+
/** Extracts whether an insert plan still needs a source attached. */
|
|
810
|
+
export type InsertSourceStateOfPlan<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
|
|
811
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any, any, infer InsertState> ? InsertState : "ready"
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Adds a single source entry to the set of available sources.
|
|
815
|
+
*
|
|
816
|
+
* This is used by `from(...)` and the join builders.
|
|
817
|
+
*/
|
|
818
|
+
export type AddAvailable<
|
|
819
|
+
Available extends Record<string, Plan.Source>,
|
|
820
|
+
Name extends string,
|
|
821
|
+
Mode extends Plan.SourceMode = "required"
|
|
822
|
+
> = Available & Record<Name, Plan.Source<Name, Mode>>
|
|
823
|
+
|
|
824
|
+
export type AddAvailableMany<
|
|
825
|
+
Available extends Record<string, Plan.Source>,
|
|
826
|
+
Names extends string,
|
|
827
|
+
Mode extends Plan.SourceMode = "required"
|
|
828
|
+
> = Available & {
|
|
829
|
+
readonly [K in Names]: Plan.Source<K, Mode>
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/** Join mode projected into the plan's source-scope mode lattice. */
|
|
833
|
+
export type JoinSourceMode<Kind extends QueryAst.JoinKind> = Kind extends "left" | "full"
|
|
834
|
+
? "optional"
|
|
835
|
+
: "required"
|
|
836
|
+
|
|
837
|
+
type DemoteAllAvailable<
|
|
838
|
+
Available extends Record<string, Plan.Source>
|
|
839
|
+
> = {
|
|
840
|
+
readonly [K in keyof Available]: Available[K] extends Plan.Source<infer Name extends string, any>
|
|
841
|
+
? Plan.Source<Name, "optional">
|
|
842
|
+
: never
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
export type ExistingAvailableAfterJoin<
|
|
846
|
+
Available extends Record<string, Plan.Source>,
|
|
847
|
+
Kind extends QueryAst.JoinKind
|
|
848
|
+
> = Kind extends "right" | "full"
|
|
849
|
+
? DemoteAllAvailable<Available>
|
|
850
|
+
: Available
|
|
851
|
+
|
|
852
|
+
export type AvailableAfterJoin<
|
|
853
|
+
Available extends Record<string, Plan.Source>,
|
|
854
|
+
JoinedName extends string,
|
|
855
|
+
Kind extends QueryAst.JoinKind
|
|
856
|
+
> = AddAvailable<ExistingAvailableAfterJoin<Available, Kind>, JoinedName, JoinSourceMode<Kind>>
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Computes the next `required` set after introducing an additional expression.
|
|
860
|
+
*
|
|
861
|
+
* Any sources already present in `available` are considered satisfied.
|
|
862
|
+
*/
|
|
863
|
+
export type AddExpressionRequired<
|
|
864
|
+
Required,
|
|
865
|
+
Available extends Record<string, Plan.Source>,
|
|
866
|
+
Value extends ExpressionInput
|
|
867
|
+
> = Exclude<Required | RequiredFromInput<Value>, AvailableNames<Available>>
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* Computes the next `required` set after a join is applied.
|
|
871
|
+
*
|
|
872
|
+
* The joined table becomes available immediately, so references to it are
|
|
873
|
+
* removed from the outstanding requirement set.
|
|
874
|
+
*/
|
|
875
|
+
export type AddJoinRequired<
|
|
876
|
+
Required,
|
|
877
|
+
Available extends Record<string, Plan.Source>,
|
|
878
|
+
JoinedName extends string,
|
|
879
|
+
Predicate extends PredicateInput | never,
|
|
880
|
+
Kind extends QueryAst.JoinKind = "inner"
|
|
881
|
+
> = Exclude<
|
|
882
|
+
Required | (Predicate extends never ? never : RequiredFromInput<Predicate>),
|
|
883
|
+
AvailableNames<AvailableAfterJoin<Available, JoinedName, Kind>>
|
|
884
|
+
>
|
|
885
|
+
|
|
886
|
+
/** Merges two aggregation kinds through a derived expression. */
|
|
887
|
+
export type MergeAggregation<
|
|
888
|
+
Left extends Expression.AggregationKind,
|
|
889
|
+
Right extends Expression.AggregationKind
|
|
890
|
+
> = Left extends "window"
|
|
891
|
+
? "window"
|
|
892
|
+
: Right extends "window"
|
|
893
|
+
? "window"
|
|
894
|
+
: Left extends "aggregate"
|
|
895
|
+
? "aggregate"
|
|
896
|
+
: Right extends "aggregate"
|
|
897
|
+
? "aggregate"
|
|
898
|
+
: "scalar"
|
|
899
|
+
|
|
900
|
+
/** Folds aggregation kinds across a tuple of expressions. */
|
|
901
|
+
type MergeAggregationTuple<
|
|
902
|
+
Values extends readonly Expression.Any[],
|
|
903
|
+
Current extends Expression.AggregationKind = "scalar"
|
|
904
|
+
> = Values extends readonly [infer Head extends Expression.Any, ...infer Tail extends readonly Expression.Any[]]
|
|
905
|
+
? MergeAggregationTuple<Tail, MergeAggregation<Current, AggregationOf<Head>>>
|
|
906
|
+
: Current
|
|
907
|
+
|
|
908
|
+
/** Merges two nullability states for null-propagating scalar operators. */
|
|
909
|
+
type MergeNullability<
|
|
910
|
+
Left extends Expression.Nullability,
|
|
911
|
+
Right extends Expression.Nullability
|
|
912
|
+
> = Left extends "always"
|
|
913
|
+
? "always"
|
|
914
|
+
: Right extends "always"
|
|
915
|
+
? "always"
|
|
916
|
+
: Left extends "maybe"
|
|
917
|
+
? "maybe"
|
|
918
|
+
: Right extends "maybe"
|
|
919
|
+
? "maybe"
|
|
920
|
+
: "never"
|
|
921
|
+
|
|
922
|
+
/** Folds nullability across a tuple for null-propagating scalar operators. */
|
|
923
|
+
export type MergeNullabilityTuple<
|
|
924
|
+
Values extends readonly Expression.Any[],
|
|
925
|
+
Current extends Expression.Nullability = "never"
|
|
926
|
+
> = Values extends readonly [infer Head extends Expression.Any, ...infer Tail extends readonly Expression.Any[]]
|
|
927
|
+
? MergeNullabilityTuple<Tail, MergeNullability<Current, Expression.NullabilityOf<Head>>>
|
|
928
|
+
: Current
|
|
929
|
+
|
|
930
|
+
/** Dialect union across a tuple of expressions. */
|
|
931
|
+
export type TupleDialect<
|
|
932
|
+
Values extends readonly Expression.Any[]
|
|
933
|
+
> = Values[number] extends never ? never : DialectOf<Values[number]>
|
|
934
|
+
|
|
935
|
+
/** Source union across a tuple of expressions. */
|
|
936
|
+
export type TupleSource<
|
|
937
|
+
Values extends readonly Expression.Any[]
|
|
938
|
+
> = Values[number] extends never ? never : SourceOf<Values[number]>
|
|
939
|
+
|
|
940
|
+
/** Converts a union into an intersection. */
|
|
941
|
+
type UnionToIntersection<Union> = (
|
|
942
|
+
Union extends any ? (value: Union) => void : never
|
|
943
|
+
) extends (value: infer Intersection) => void ? Intersection : never
|
|
944
|
+
|
|
945
|
+
/** Dependency-map intersection suitable for provenance unions across a tuple. */
|
|
946
|
+
export type TupleDependencies<
|
|
947
|
+
Values extends readonly Expression.Any[]
|
|
948
|
+
> = DependencyRecord<Values[number] extends never ? never : Extract<keyof DependenciesOf<Values[number]>, string>>
|
|
949
|
+
|
|
950
|
+
/** Builds a canonical dependency map from a string union of table names. */
|
|
951
|
+
export type DependencyRecord<Keys extends string> = [Keys] extends [never] ? {} : Record<Keys, true>
|
|
952
|
+
|
|
953
|
+
/** Grouped expression identities carried by a tuple of scalar expressions. */
|
|
954
|
+
export type GroupedKeysFromValues<
|
|
955
|
+
Values extends readonly Expression.Any[]
|
|
956
|
+
> = Values[number] extends never ? never : {
|
|
957
|
+
[K in keyof Values]: Values[K] extends Expression.Any ? GroupingKeyOfExpression<Values[K]> : never
|
|
958
|
+
}[number]
|
|
959
|
+
|
|
960
|
+
/** Whether a selection contains any aggregate expressions. */
|
|
961
|
+
type SelectionHasAggregate<Selection> = Selection extends Expression.Any
|
|
962
|
+
? AggregationOf<Selection> extends "aggregate" ? true : false
|
|
963
|
+
: Selection extends Record<string, any>
|
|
964
|
+
? Extract<{
|
|
965
|
+
[K in keyof Selection]: SelectionHasAggregate<Selection[K]>
|
|
966
|
+
}[keyof Selection], true> extends never ? false : true
|
|
967
|
+
: false
|
|
968
|
+
|
|
969
|
+
/** Whether a selection is valid for a specific grouped-column set. */
|
|
970
|
+
type IsGroupedSelectionValid<
|
|
971
|
+
Selection,
|
|
972
|
+
Grouped extends string
|
|
973
|
+
> = Selection extends Expression.Any
|
|
974
|
+
? AggregationOf<Selection> extends "aggregate"
|
|
975
|
+
? true
|
|
976
|
+
: AggregationOf<Selection> extends "window"
|
|
977
|
+
? false
|
|
978
|
+
: RequiredFromDependencies<DependenciesOf<Selection>> extends never
|
|
979
|
+
? true
|
|
980
|
+
: [GroupingKeyOfExpression<Selection>] extends [Grouped] ? true : false
|
|
981
|
+
: Selection extends Record<string, any>
|
|
982
|
+
? Extract<{
|
|
983
|
+
[K in keyof Selection]: IsGroupedSelectionValid<Selection[K], Grouped> extends true ? never : true
|
|
984
|
+
}[keyof Selection], true> extends never ? true : false
|
|
985
|
+
: false
|
|
986
|
+
|
|
987
|
+
/** Whether a selection is aggregation-safe for the current grouping state. */
|
|
988
|
+
type IsAggregationCompatibleSelection<
|
|
989
|
+
Selection,
|
|
990
|
+
Grouped extends string
|
|
991
|
+
> = SelectionHasAggregate<Selection> extends true
|
|
992
|
+
? IsGroupedSelectionValid<Selection, Grouped>
|
|
993
|
+
: [Grouped] extends [never]
|
|
994
|
+
? true
|
|
995
|
+
: IsGroupedSelectionValid<Selection, Grouped>
|
|
996
|
+
|
|
997
|
+
type MergeNullabilityStates<
|
|
998
|
+
Left extends Expression.Nullability,
|
|
999
|
+
Right extends Expression.Nullability
|
|
1000
|
+
> = Left extends "always"
|
|
1001
|
+
? "always"
|
|
1002
|
+
: Right extends "always"
|
|
1003
|
+
? "always"
|
|
1004
|
+
: Left extends "maybe"
|
|
1005
|
+
? "maybe"
|
|
1006
|
+
: Right extends "maybe"
|
|
1007
|
+
? "maybe"
|
|
1008
|
+
: "never"
|
|
1009
|
+
|
|
1010
|
+
type FoldEffectiveNullability<
|
|
1011
|
+
Values extends readonly Expression.Any[],
|
|
1012
|
+
Available extends Record<string, Plan.Source>,
|
|
1013
|
+
Assumptions extends PredicateFormula
|
|
1014
|
+
> = Extract<{
|
|
1015
|
+
[K in keyof Values]: Values[K] extends Expression.Any ? EffectiveNullability<Values[K], Available, Assumptions> : never
|
|
1016
|
+
}[number], "always"> extends never
|
|
1017
|
+
? Extract<{
|
|
1018
|
+
[K in keyof Values]: Values[K] extends Expression.Any ? EffectiveNullability<Values[K], Available, Assumptions> : never
|
|
1019
|
+
}[number], "maybe"> extends never
|
|
1020
|
+
? "never"
|
|
1021
|
+
: "maybe"
|
|
1022
|
+
: "always"
|
|
1023
|
+
|
|
1024
|
+
type CoalesceEffectiveNullability<
|
|
1025
|
+
Values extends readonly Expression.Any[],
|
|
1026
|
+
Available extends Record<string, Plan.Source>,
|
|
1027
|
+
Assumptions extends PredicateFormula
|
|
1028
|
+
> = Extract<{
|
|
1029
|
+
[K in keyof Values]: Values[K] extends Expression.Any ? EffectiveNullability<Values[K], Available, Assumptions> : never
|
|
1030
|
+
}[number], "never"> extends never
|
|
1031
|
+
? Extract<{
|
|
1032
|
+
[K in keyof Values]: Values[K] extends Expression.Any ? EffectiveNullability<Values[K], Available, Assumptions> : never
|
|
1033
|
+
}[number], "maybe"> extends never
|
|
1034
|
+
? "always"
|
|
1035
|
+
: "maybe"
|
|
1036
|
+
: "never"
|
|
1037
|
+
|
|
1038
|
+
type NullabilityOfOutput<Output> =
|
|
1039
|
+
null extends Output
|
|
1040
|
+
? Exclude<Output, null> extends never ? "always" : "maybe"
|
|
1041
|
+
: "never"
|
|
1042
|
+
|
|
1043
|
+
type RequiredTablesFromAssumptions<Assumptions extends PredicateFormula> =
|
|
1044
|
+
GuaranteedSourceNames<Assumptions>
|
|
1045
|
+
|
|
1046
|
+
type EffectiveAvailable<
|
|
1047
|
+
Available extends Record<string, Plan.Source>,
|
|
1048
|
+
Assumptions extends PredicateFormula
|
|
1049
|
+
> = {
|
|
1050
|
+
readonly [K in keyof Available]: Available[K] & {
|
|
1051
|
+
readonly mode: K extends RequiredTablesFromAssumptions<Assumptions> ? "required" : Available[K]["mode"]
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
type BaseEffectiveNullability<
|
|
1056
|
+
Value extends Expression.Any,
|
|
1057
|
+
Available extends Record<string, Plan.Source>,
|
|
1058
|
+
Assumptions extends PredicateFormula
|
|
1059
|
+
> = AstOf<Value> extends ExpressionAst.ColumnNode<infer TableName extends string, infer ColumnName extends string>
|
|
1060
|
+
? `${TableName}.${ColumnName}` extends GuaranteedNullKeys<Assumptions>
|
|
1061
|
+
? "always"
|
|
1062
|
+
: `${TableName}.${ColumnName}` extends GuaranteedNonNullKeys<Assumptions>
|
|
1063
|
+
? "never"
|
|
1064
|
+
: Expression.NullabilityOf<Value> extends "always" ? "always"
|
|
1065
|
+
: Expression.SourceNullabilityOf<Value> extends "resolved"
|
|
1066
|
+
? Expression.NullabilityOf<Value>
|
|
1067
|
+
: HasOptionalSource<DependenciesOf<Value>, Available> extends true ? "maybe"
|
|
1068
|
+
: Expression.NullabilityOf<Value>
|
|
1069
|
+
: Expression.NullabilityOf<Value> extends "always" ? "always"
|
|
1070
|
+
: Expression.SourceNullabilityOf<Value> extends "resolved"
|
|
1071
|
+
? Expression.NullabilityOf<Value>
|
|
1072
|
+
: HasOptionalSource<DependenciesOf<Value>, Available> extends true ? "maybe"
|
|
1073
|
+
: Expression.NullabilityOf<Value>
|
|
1074
|
+
|
|
1075
|
+
type CaseOutputOf<
|
|
1076
|
+
Branches extends readonly ExpressionAst.CaseBranchNode[],
|
|
1077
|
+
Else extends Expression.Any,
|
|
1078
|
+
Available extends Record<string, Plan.Source>,
|
|
1079
|
+
Assumptions extends PredicateFormula
|
|
1080
|
+
> = Branches extends readonly [
|
|
1081
|
+
infer Head extends ExpressionAst.CaseBranchNode,
|
|
1082
|
+
...infer Tail extends readonly ExpressionAst.CaseBranchNode[]
|
|
1083
|
+
]
|
|
1084
|
+
? Head extends ExpressionAst.CaseBranchNode<infer Predicate extends Expression.Any, infer Then extends Expression.Any>
|
|
1085
|
+
? CaseBranchDecision<Assumptions, Predicate> extends "skip"
|
|
1086
|
+
? CaseOutputOf<Tail, Else, Available, Assumptions>
|
|
1087
|
+
: CaseBranchDecision<Assumptions, Predicate> extends "take"
|
|
1088
|
+
? ExpressionOutput<Then, EffectiveAvailable<Available, CaseBranchAssumeTrue<Assumptions, Predicate>>, CaseBranchAssumeTrue<Assumptions, Predicate>>
|
|
1089
|
+
: ExpressionOutput<Then, EffectiveAvailable<Available, CaseBranchAssumeTrue<Assumptions, Predicate>>, CaseBranchAssumeTrue<Assumptions, Predicate>> |
|
|
1090
|
+
CaseOutputOf<Tail, Else, Available, CaseBranchAssumeFalse<Assumptions, Predicate>>
|
|
1091
|
+
: never
|
|
1092
|
+
: ExpressionOutput<Else, EffectiveAvailable<Available, Assumptions>, Assumptions>
|
|
1093
|
+
|
|
1094
|
+
/** Effective nullability of an expression after source-scope nullability is applied. */
|
|
1095
|
+
export type EffectiveNullability<
|
|
1096
|
+
Value extends Expression.Any,
|
|
1097
|
+
Available extends Record<string, Plan.Source>,
|
|
1098
|
+
Assumptions extends PredicateFormula = TrueAssumptions
|
|
1099
|
+
> =
|
|
1100
|
+
AstOf<Value> extends infer Ast extends ExpressionAst.Any
|
|
1101
|
+
? Ast extends ExpressionAst.ColumnNode<any, any>
|
|
1102
|
+
? BaseEffectiveNullability<Value, Available, Assumptions>
|
|
1103
|
+
: Ast extends ExpressionAst.LiteralNode<any>
|
|
1104
|
+
? Expression.NullabilityOf<Value>
|
|
1105
|
+
: Ast extends ExpressionAst.UnaryNode<infer Kind, infer UnaryValue extends Expression.Any>
|
|
1106
|
+
? Kind extends "upper" | "lower" | "not"
|
|
1107
|
+
? EffectiveNullability<UnaryValue, Available, Assumptions>
|
|
1108
|
+
: Kind extends "isNull" | "isNotNull" | "count"
|
|
1109
|
+
? "never"
|
|
1110
|
+
: Expression.NullabilityOf<Value>
|
|
1111
|
+
: Ast extends ExpressionAst.BinaryNode<"eq", infer Left extends Expression.Any, infer Right extends Expression.Any>
|
|
1112
|
+
? EffectiveNullability<Left, Available, Assumptions> extends "never"
|
|
1113
|
+
? EffectiveNullability<Right, Available, Assumptions> extends "never" ? "never" : "maybe"
|
|
1114
|
+
: "maybe"
|
|
1115
|
+
: Ast extends ExpressionAst.VariadicNode<infer Kind, infer Values extends readonly Expression.Any[]>
|
|
1116
|
+
? Kind extends "coalesce"
|
|
1117
|
+
? CoalesceEffectiveNullability<Values, Available, Assumptions>
|
|
1118
|
+
: Kind extends "and" | "or" | "concat"
|
|
1119
|
+
? FoldEffectiveNullability<Values, Available, Assumptions>
|
|
1120
|
+
: BaseEffectiveNullability<Value, Available, Assumptions>
|
|
1121
|
+
: Ast extends ExpressionAst.CaseNode<infer Branches extends readonly ExpressionAst.CaseBranchNode[], infer Else extends Expression.Any>
|
|
1122
|
+
? NullabilityOfOutput<CaseOutputOf<Branches, Else, Available, Assumptions>>
|
|
1123
|
+
: BaseEffectiveNullability<Value, Available, Assumptions>
|
|
1124
|
+
: BaseEffectiveNullability<Value, Available, Assumptions>
|
|
1125
|
+
|
|
1126
|
+
/** Result runtime type of an expression after effective nullability is resolved. */
|
|
1127
|
+
export type ExpressionOutput<
|
|
1128
|
+
Value extends Expression.Any,
|
|
1129
|
+
Available extends Record<string, Plan.Source>,
|
|
1130
|
+
Assumptions extends PredicateFormula = TrueAssumptions
|
|
1131
|
+
> = AstOf<Value> extends ExpressionAst.CaseNode<infer Branches extends readonly ExpressionAst.CaseBranchNode[], infer Else extends Expression.Any>
|
|
1132
|
+
? CaseOutputOf<Branches, Else, Available, Assumptions>
|
|
1133
|
+
: EffectiveNullability<Value, Available, Assumptions> extends "never"
|
|
1134
|
+
? Expression.RuntimeOf<Value>
|
|
1135
|
+
: EffectiveNullability<Value, Available, Assumptions> extends "always"
|
|
1136
|
+
? null
|
|
1137
|
+
: Expression.RuntimeOf<Value> | null
|
|
1138
|
+
|
|
1139
|
+
/** Result runtime type of a nested selection after source-scope nullability is resolved. */
|
|
1140
|
+
export type OutputOfSelection<
|
|
1141
|
+
Selection,
|
|
1142
|
+
Available extends Record<string, Plan.Source>,
|
|
1143
|
+
Assumptions extends PredicateFormula = TrueAssumptions
|
|
1144
|
+
> = Selection extends Expression.Any
|
|
1145
|
+
? ExpressionOutput<Selection, Available, Assumptions>
|
|
1146
|
+
: Selection extends Record<string, any>
|
|
1147
|
+
? {
|
|
1148
|
+
readonly [K in keyof Selection]: OutputOfSelection<Selection[K], Available, Assumptions>
|
|
1149
|
+
}
|
|
1150
|
+
: never
|
|
1151
|
+
|
|
1152
|
+
/** Resolved row type produced by a concrete query plan. */
|
|
1153
|
+
export type ResultRow<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> = OutputOfSelection<
|
|
1154
|
+
PlanValue[typeof Plan.TypeId]["selection"],
|
|
1155
|
+
EffectiveAvailable<PlanValue[typeof Plan.TypeId]["available"], AssumptionsOfPlan<PlanValue>>,
|
|
1156
|
+
AssumptionsOfPlan<PlanValue>
|
|
1157
|
+
>
|
|
1158
|
+
|
|
1159
|
+
/** Resolved row collection type produced by a concrete query plan. */
|
|
1160
|
+
export type ResultRows<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> = ReadonlyArray<ResultRow<PlanValue>>
|
|
1161
|
+
|
|
1162
|
+
/** Conservative runtime row shape produced by remapping projection aliases. */
|
|
1163
|
+
export type RuntimeResultRow<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> = OutputOfSelection<
|
|
1164
|
+
PlanValue[typeof Plan.TypeId]["selection"],
|
|
1165
|
+
PlanValue[typeof Plan.TypeId]["available"],
|
|
1166
|
+
TrueAssumptions
|
|
1167
|
+
>
|
|
1168
|
+
|
|
1169
|
+
/** Conservative runtime row collection type. */
|
|
1170
|
+
export type RuntimeResultRows<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> = ReadonlyArray<RuntimeResultRow<PlanValue>>
|
|
1171
|
+
|
|
1172
|
+
/** Narrows a query plan to aggregate-compatible selections. */
|
|
1173
|
+
type HasKnownOutstanding<Required> = [Required] extends [never]
|
|
1174
|
+
? false
|
|
1175
|
+
: string extends Required
|
|
1176
|
+
? false
|
|
1177
|
+
: true
|
|
1178
|
+
|
|
1179
|
+
type SourceCompletenessError<
|
|
1180
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1181
|
+
MissingSources extends string
|
|
1182
|
+
> = PlanValue & {
|
|
1183
|
+
readonly __effect_qb_error__: "effect-qb: query references sources that are not yet in scope"
|
|
1184
|
+
readonly __effect_qb_missing_sources__: MissingSources
|
|
1185
|
+
readonly __effect_qb_hint__: "Add from(...) or a join for each referenced source before render or execute"
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
type AggregationCompatibilityError<
|
|
1189
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1190
|
+
> = PlanValue & {
|
|
1191
|
+
readonly __effect_qb_error__: "effect-qb: invalid grouped selection"
|
|
1192
|
+
readonly __effect_qb_hint__: "Scalar selections must be covered by groupBy(...) when aggregates are present"
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
type DialectCompatibilityError<
|
|
1196
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1197
|
+
EngineDialect extends string
|
|
1198
|
+
> = PlanValue & {
|
|
1199
|
+
readonly __effect_qb_error__: "effect-qb: plan dialect is not compatible with the target renderer or executor"
|
|
1200
|
+
readonly __effect_qb_plan_dialect__: PlanValue[typeof Plan.TypeId]["dialect"]
|
|
1201
|
+
readonly __effect_qb_target_dialect__: EngineDialect
|
|
1202
|
+
readonly __effect_qb_hint__: "Use the matching dialect module or renderer/executor"
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
type InsertSourceCompletenessError<
|
|
1206
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1207
|
+
> = PlanValue & {
|
|
1208
|
+
readonly __effect_qb_error__: "effect-qb: insert plan is missing inline values or a source"
|
|
1209
|
+
readonly __effect_qb_hint__: "Pass values directly to insert(...), use defaultValues(...), or pipe the insert plan into from(...)"
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
/** Narrows a query plan to aggregate-compatible selections. */
|
|
1213
|
+
export type AggregationCompatiblePlan<
|
|
1214
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1215
|
+
> = PlanValue extends QueryPlan<infer Selection, any, any, any, infer Grouped, any, any, any, any, any>
|
|
1216
|
+
? IsAggregationCompatibleSelection<Selection, Grouped> extends true ? PlanValue : AggregationCompatibilityError<PlanValue>
|
|
1217
|
+
: never
|
|
1218
|
+
|
|
1219
|
+
/** Narrows a query plan to aggregate-compatible, source-complete plans. */
|
|
1220
|
+
export type CompletePlan<PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
|
|
1221
|
+
PlanValue extends QueryPlan<infer Selection, infer Required, any, any, infer Grouped, any, any, any, any, infer Statement, any, infer InsertState>
|
|
1222
|
+
? Statement extends "insert"
|
|
1223
|
+
? InsertState extends "missing"
|
|
1224
|
+
? InsertSourceCompletenessError<PlanValue>
|
|
1225
|
+
: HasKnownOutstanding<Required> extends true
|
|
1226
|
+
? SourceCompletenessError<PlanValue, Extract<Required, string>>
|
|
1227
|
+
: IsAggregationCompatibleSelection<Selection, Grouped> extends true ? PlanValue : AggregationCompatibilityError<PlanValue>
|
|
1228
|
+
: HasKnownOutstanding<Required> extends true
|
|
1229
|
+
? SourceCompletenessError<PlanValue, Extract<Required, string>>
|
|
1230
|
+
: IsAggregationCompatibleSelection<Selection, Grouped> extends true ? PlanValue : AggregationCompatibilityError<PlanValue>
|
|
1231
|
+
: never
|
|
1232
|
+
|
|
1233
|
+
/** Whether a plan dialect is compatible with a target engine dialect. */
|
|
1234
|
+
type IsDialectCompatible<
|
|
1235
|
+
PlanDialect extends string,
|
|
1236
|
+
EngineDialect extends string
|
|
1237
|
+
> = [PlanDialect] extends [never]
|
|
1238
|
+
? true
|
|
1239
|
+
: Extract<PlanDialect, EngineDialect> extends never
|
|
1240
|
+
? false
|
|
1241
|
+
: true
|
|
1242
|
+
|
|
1243
|
+
/** Narrows a complete plan to those compatible with a target engine dialect. */
|
|
1244
|
+
export type DialectCompatiblePlan<
|
|
1245
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1246
|
+
EngineDialect extends string
|
|
1247
|
+
> = IsDialectCompatible<PlanValue[typeof Plan.TypeId]["dialect"], EngineDialect> extends true
|
|
1248
|
+
? CompletePlan<PlanValue>
|
|
1249
|
+
: DialectCompatibilityError<PlanValue, EngineDialect>
|
|
1250
|
+
|
|
1251
|
+
/** Nested-plan compatibility used by subquery expressions such as `exists(...)`. */
|
|
1252
|
+
export type DialectCompatibleNestedPlan<
|
|
1253
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1254
|
+
EngineDialect extends string
|
|
1255
|
+
> = IsDialectCompatible<PlanValue[typeof Plan.TypeId]["dialect"], EngineDialect> extends true
|
|
1256
|
+
? AggregationCompatiblePlan<PlanValue>
|
|
1257
|
+
: DialectCompatibilityError<PlanValue, EngineDialect>
|
|
1258
|
+
|
|
1259
|
+
type SetOperandStatement = "select" | "set"
|
|
1260
|
+
type IsUnion<Value, All = Value> = Value extends any ? ([All] extends [Value] ? false : true) : never
|
|
1261
|
+
|
|
1262
|
+
type SingleSelectedExpressionError<
|
|
1263
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1264
|
+
> = PlanValue & {
|
|
1265
|
+
readonly __effect_qb_error__: "effect-qb: scalar and quantified subqueries must project exactly one top-level expression"
|
|
1266
|
+
readonly __effect_qb_hint__: "Project exactly one scalar expression like select({ value: expr }) before using this subquery as a scalar operand"
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
type SingleSelectedExpression<
|
|
1270
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1271
|
+
> = SelectionOfPlan<PlanValue> extends Record<string, infer Value>
|
|
1272
|
+
? IsUnion<Extract<keyof SelectionOfPlan<PlanValue>, string>> extends true
|
|
1273
|
+
? SingleSelectedExpressionError<PlanValue>
|
|
1274
|
+
: Value extends Expression.Any
|
|
1275
|
+
? Value
|
|
1276
|
+
: SingleSelectedExpressionError<PlanValue>
|
|
1277
|
+
: SingleSelectedExpressionError<PlanValue>
|
|
1278
|
+
|
|
1279
|
+
export type ScalarSubqueryPlan<
|
|
1280
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1281
|
+
EngineDialect extends string
|
|
1282
|
+
> = DialectCompatibleNestedPlan<PlanValue, EngineDialect> extends infer Compatible
|
|
1283
|
+
? Compatible extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1284
|
+
? SingleSelectedExpression<Compatible> extends Expression.Any ? Compatible : SingleSelectedExpression<Compatible>
|
|
1285
|
+
: Compatible
|
|
1286
|
+
: never
|
|
1287
|
+
|
|
1288
|
+
export type ScalarOutputOfPlan<
|
|
1289
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1290
|
+
> = SingleSelectedExpression<PlanValue> extends infer Value
|
|
1291
|
+
? Value extends Expression.Any ? Value : never
|
|
1292
|
+
: never
|
|
1293
|
+
|
|
1294
|
+
type SetOperandStatementError<
|
|
1295
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1296
|
+
> = PlanValue & {
|
|
1297
|
+
readonly __effect_qb_error__: "effect-qb: set operators only accept select-like query plans"
|
|
1298
|
+
readonly __effect_qb_statement__: StatementOfPlan<PlanValue>
|
|
1299
|
+
readonly __effect_qb_hint__: "Use select(...) or another set operator as each operand"
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
type SetOperandShapeError<
|
|
1303
|
+
Left extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1304
|
+
Right extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1305
|
+
> = Right & {
|
|
1306
|
+
readonly __effect_qb_error__: "effect-qb: set operator operands must have matching result rows"
|
|
1307
|
+
readonly __effect_qb_expected_selection__: SelectionOfPlan<Left>
|
|
1308
|
+
readonly __effect_qb_actual_selection__: SelectionOfPlan<Right>
|
|
1309
|
+
readonly __effect_qb_hint__: "Project the same nested object shape and compatible nullability from each operand"
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
type IsSameSelection<
|
|
1313
|
+
Left extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1314
|
+
Right extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1315
|
+
> = [SelectionOfPlan<Left>] extends [SelectionOfPlan<Right>]
|
|
1316
|
+
? [SelectionOfPlan<Right>] extends [SelectionOfPlan<Left>] ? true : false
|
|
1317
|
+
: false
|
|
1318
|
+
|
|
1319
|
+
/** Set-operator compatibility used by `union(...)`, `intersect(...)`, and `except(...)`. */
|
|
1320
|
+
export type SetCompatiblePlan<
|
|
1321
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1322
|
+
EngineDialect extends string
|
|
1323
|
+
> = StatementOfPlan<PlanValue> extends SetOperandStatement
|
|
1324
|
+
? DialectCompatiblePlan<PlanValue, EngineDialect>
|
|
1325
|
+
: SetOperandStatementError<PlanValue>
|
|
1326
|
+
|
|
1327
|
+
/** Right-hand operand compatibility for set operators. */
|
|
1328
|
+
export type SetCompatibleRightPlan<
|
|
1329
|
+
Left extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1330
|
+
Right extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1331
|
+
EngineDialect extends string
|
|
1332
|
+
> = StatementOfPlan<Right> extends SetOperandStatement
|
|
1333
|
+
? IsSameSelection<Left, Right> extends true
|
|
1334
|
+
? DialectCompatiblePlan<Right, EngineDialect>
|
|
1335
|
+
: SetOperandShapeError<Left, Right>
|
|
1336
|
+
: SetOperandStatementError<Right>
|
|
1337
|
+
|
|
1338
|
+
/** True when any of an expression's dependencies are optional in the current scope. */
|
|
1339
|
+
type HasOptionalSource<
|
|
1340
|
+
Dependencies extends Expression.SourceDependencies,
|
|
1341
|
+
Available extends Record<string, Plan.Source>
|
|
1342
|
+
> = Extract<{
|
|
1343
|
+
[K in keyof Dependencies & string]: SourceModeOf<Available, K> extends "optional" ? true : never
|
|
1344
|
+
}[keyof Dependencies & string], true> extends never ? false : true
|
|
1345
|
+
|
|
1346
|
+
/**
|
|
1347
|
+
* Concrete query-plan value produced by the query DSL.
|
|
1348
|
+
*
|
|
1349
|
+
* `Selection` is the output shape being built, `Required` tracks referenced
|
|
1350
|
+
* sources that are not yet in scope, `Available` tracks sources already in
|
|
1351
|
+
* scope, and `Dialect` tracks the effective SQL dialect for the plan.
|
|
1352
|
+
*/
|
|
1353
|
+
export type QueryPlan<
|
|
1354
|
+
Selection,
|
|
1355
|
+
Required = never,
|
|
1356
|
+
Available extends Record<string, Plan.Source> = {},
|
|
1357
|
+
Dialect extends string = never,
|
|
1358
|
+
Grouped extends string = never,
|
|
1359
|
+
ScopedNames extends string = Extract<keyof Available, string>,
|
|
1360
|
+
Outstanding extends string = Extract<Required, string>,
|
|
1361
|
+
Assumptions extends PredicateFormula = TrueAssumptions,
|
|
1362
|
+
Capabilities extends QueryCapability = "read",
|
|
1363
|
+
Statement extends QueryAst.QueryStatement = "select",
|
|
1364
|
+
Target = unknown,
|
|
1365
|
+
InsertState extends InsertSourceState = InsertSourceState
|
|
1366
|
+
> = Plan.Plan<Selection, Required, Available, Dialect> & {
|
|
1367
|
+
readonly [Plan.TypeId]: Plan.State<Selection, Required, Available, Dialect>
|
|
1368
|
+
readonly [QueryAst.TypeId]: QueryAst.Ast<Selection, Grouped, Statement>
|
|
1369
|
+
readonly [QueryTypeId]: QueryState<Outstanding, ScopedNames, Grouped, Assumptions, Capabilities, Statement, Target, InsertState>
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
/**
|
|
1373
|
+
* Normalizes expression provenance into a flat list.
|
|
1374
|
+
*
|
|
1375
|
+
* Single-source expressions store one provenance object, while derived
|
|
1376
|
+
* operators may carry multiple sources. This helper hides that shape
|
|
1377
|
+
* difference.
|
|
1378
|
+
*/
|
|
1379
|
+
const normalizeSources = (source: unknown): readonly unknown[] =>
|
|
1380
|
+
source === undefined ? [] : Array.isArray(source) ? source : [source]
|
|
1381
|
+
|
|
1382
|
+
/**
|
|
1383
|
+
* Merges the provenance of two expressions into one normalized runtime value.
|
|
1384
|
+
*
|
|
1385
|
+
* The result is:
|
|
1386
|
+
* - `undefined` when neither side is sourced
|
|
1387
|
+
* - a single provenance object when exactly one source exists
|
|
1388
|
+
* - an array when multiple sources are involved
|
|
1389
|
+
*/
|
|
1390
|
+
export const mergeSources = (left: unknown, right?: unknown): unknown => {
|
|
1391
|
+
const values = [...normalizeSources(left), ...normalizeSources(right)]
|
|
1392
|
+
if (values.length === 0) {
|
|
1393
|
+
return undefined
|
|
1394
|
+
}
|
|
1395
|
+
if (values.length === 1) {
|
|
1396
|
+
return values[0]
|
|
1397
|
+
}
|
|
1398
|
+
return values
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
/** Merges two expression dependency maps into a single normalized record. */
|
|
1402
|
+
export const mergeDependencies = (
|
|
1403
|
+
left: Expression.SourceDependencies,
|
|
1404
|
+
right: Expression.SourceDependencies = {}
|
|
1405
|
+
): Expression.SourceDependencies => ({
|
|
1406
|
+
...left,
|
|
1407
|
+
...right
|
|
1408
|
+
})
|
|
1409
|
+
|
|
1410
|
+
/** Merges expression aggregation kinds at runtime. */
|
|
1411
|
+
export const mergeAggregationRuntime = (
|
|
1412
|
+
left: Expression.AggregationKind,
|
|
1413
|
+
right: Expression.AggregationKind = "scalar"
|
|
1414
|
+
): Expression.AggregationKind =>
|
|
1415
|
+
left === "window" || right === "window"
|
|
1416
|
+
? "window"
|
|
1417
|
+
: left === "aggregate" || right === "aggregate"
|
|
1418
|
+
? "aggregate"
|
|
1419
|
+
: "scalar"
|
|
1420
|
+
|
|
1421
|
+
/** Folds runtime aggregation across a list of expressions. */
|
|
1422
|
+
export const mergeAggregationManyRuntime = (
|
|
1423
|
+
values: readonly Expression.Any[]
|
|
1424
|
+
): Expression.AggregationKind =>
|
|
1425
|
+
values.reduce(
|
|
1426
|
+
(current, value) => mergeAggregationRuntime(current, value[Expression.TypeId].aggregation),
|
|
1427
|
+
"scalar" as Expression.AggregationKind
|
|
1428
|
+
)
|
|
1429
|
+
|
|
1430
|
+
/** Merges expression nullability for null-propagating scalar operators. */
|
|
1431
|
+
const mergeNullabilityRuntime = (
|
|
1432
|
+
left: Expression.Nullability,
|
|
1433
|
+
right: Expression.Nullability = "never"
|
|
1434
|
+
): Expression.Nullability =>
|
|
1435
|
+
left === "always" || right === "always"
|
|
1436
|
+
? "always"
|
|
1437
|
+
: left === "maybe" || right === "maybe"
|
|
1438
|
+
? "maybe"
|
|
1439
|
+
: "never"
|
|
1440
|
+
|
|
1441
|
+
/** Folds runtime nullability across a list of expressions. */
|
|
1442
|
+
export const mergeNullabilityManyRuntime = (
|
|
1443
|
+
values: readonly Expression.Any[]
|
|
1444
|
+
): Expression.Nullability =>
|
|
1445
|
+
values.reduce(
|
|
1446
|
+
(current, value) => mergeNullabilityRuntime(current, value[Expression.TypeId].nullability),
|
|
1447
|
+
"never" as Expression.Nullability
|
|
1448
|
+
)
|
|
1449
|
+
|
|
1450
|
+
/** Merges provenance across a variadic expression input list. */
|
|
1451
|
+
export const mergeManySources = (values: readonly Expression.Any[]): unknown =>
|
|
1452
|
+
values.reduce<unknown>(
|
|
1453
|
+
(current, value) => mergeSources(current, value[Expression.TypeId].source),
|
|
1454
|
+
undefined
|
|
1455
|
+
)
|
|
1456
|
+
|
|
1457
|
+
/** Merges dependency maps across a variadic expression input list. */
|
|
1458
|
+
export const mergeManyDependencies = (values: readonly Expression.Any[]): Expression.SourceDependencies =>
|
|
1459
|
+
values.reduce(
|
|
1460
|
+
(current, value) => mergeDependencies(current, value[Expression.TypeId].dependencies),
|
|
1461
|
+
{} as Expression.SourceDependencies
|
|
1462
|
+
)
|
|
1463
|
+
|
|
1464
|
+
/**
|
|
1465
|
+
* Creates a runtime expression object from fully computed static metadata.
|
|
1466
|
+
*
|
|
1467
|
+
* The query helpers use this instead of exposing ad hoc object shapes, so every
|
|
1468
|
+
* produced expression has the same structural contract as bound columns.
|
|
1469
|
+
*/
|
|
1470
|
+
export const makeExpression = <
|
|
1471
|
+
Runtime,
|
|
1472
|
+
Db extends Expression.DbType.Any,
|
|
1473
|
+
Nullable extends Expression.Nullability,
|
|
1474
|
+
Dialect extends string,
|
|
1475
|
+
Aggregation extends Expression.AggregationKind,
|
|
1476
|
+
Source,
|
|
1477
|
+
Dependencies extends Expression.SourceDependencies = {},
|
|
1478
|
+
SourceNullability extends Expression.SourceNullabilityMode = "propagate",
|
|
1479
|
+
Ast extends ExpressionAst.Any = ExpressionAst.Any
|
|
1480
|
+
>(
|
|
1481
|
+
state: Expression.State<Runtime, Db, Nullable, Dialect, Aggregation, Source, Dependencies, SourceNullability>,
|
|
1482
|
+
ast: Ast
|
|
1483
|
+
): Expression.Expression<Runtime, Db, Nullable, Dialect, Aggregation, Source, Dependencies, SourceNullability> & {
|
|
1484
|
+
readonly [ExpressionAst.TypeId]: Ast
|
|
1485
|
+
} => {
|
|
1486
|
+
const expression = Object.create(ExpressionProto)
|
|
1487
|
+
expression[Expression.TypeId] = state
|
|
1488
|
+
expression[ExpressionAst.TypeId] = ast
|
|
1489
|
+
return expression
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
/**
|
|
1493
|
+
* Creates a runtime query-plan value from public plan metadata plus the
|
|
1494
|
+
* internal clause AST.
|
|
1495
|
+
*/
|
|
1496
|
+
export const makePlan = <
|
|
1497
|
+
Selection,
|
|
1498
|
+
Required,
|
|
1499
|
+
Available extends Record<string, Plan.Source>,
|
|
1500
|
+
Dialect extends string,
|
|
1501
|
+
Grouped extends string = never,
|
|
1502
|
+
ScopedNames extends string = Extract<keyof Available, string>,
|
|
1503
|
+
Outstanding extends string = Extract<Required, string>,
|
|
1504
|
+
Assumptions extends PredicateFormula = TrueAssumptions,
|
|
1505
|
+
Capabilities extends QueryCapability = "read",
|
|
1506
|
+
Statement extends QueryAst.QueryStatement = "select",
|
|
1507
|
+
Target = never,
|
|
1508
|
+
InsertState extends InsertSourceState = "ready"
|
|
1509
|
+
>(
|
|
1510
|
+
state: Plan.State<Selection, Required, Available, Dialect>,
|
|
1511
|
+
ast: QueryAst.Ast<Selection, Grouped, Statement>,
|
|
1512
|
+
_assumptions?: Assumptions,
|
|
1513
|
+
_capabilities?: Capabilities,
|
|
1514
|
+
_statement?: Statement,
|
|
1515
|
+
_target?: Target,
|
|
1516
|
+
_insertState?: InsertState
|
|
1517
|
+
): QueryPlan<Selection, Required, Available, Dialect, Grouped, ScopedNames, Outstanding, Assumptions, Capabilities, Statement, Target, InsertState> => {
|
|
1518
|
+
const plan = Object.create(PlanProto)
|
|
1519
|
+
plan[Plan.TypeId] = state
|
|
1520
|
+
plan[QueryAst.TypeId] = ast
|
|
1521
|
+
plan[QueryTypeId] = {
|
|
1522
|
+
required: undefined as unknown as Outstanding,
|
|
1523
|
+
availableNames: undefined as unknown as ScopedNames,
|
|
1524
|
+
grouped: undefined as unknown as Grouped,
|
|
1525
|
+
assumptions: undefined as unknown as Assumptions,
|
|
1526
|
+
capabilities: undefined as unknown as Capabilities,
|
|
1527
|
+
statement: (_statement ?? ("select" as Statement)) as Statement,
|
|
1528
|
+
target: (_target ?? (undefined as unknown as Target)) as Target,
|
|
1529
|
+
insertSource: (_insertState ?? ("ready" as InsertState)) as InsertState
|
|
1530
|
+
}
|
|
1531
|
+
return plan
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
/** Returns the internal AST carried by a query plan. */
|
|
1535
|
+
export const getAst = <
|
|
1536
|
+
Selection,
|
|
1537
|
+
Grouped extends string,
|
|
1538
|
+
Statement extends QueryAst.QueryStatement
|
|
1539
|
+
>(
|
|
1540
|
+
plan: QueryPlan<Selection, any, any, any, Grouped, any, any, any, any, Statement>
|
|
1541
|
+
): QueryAst.Ast<Selection, Grouped, Statement> => plan[QueryAst.TypeId]
|
|
1542
|
+
|
|
1543
|
+
/** Returns the internal phantom query state carried by a query plan. */
|
|
1544
|
+
export const getQueryState = (
|
|
1545
|
+
plan: QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1546
|
+
): QueryState<any, any, any, any, any, any, any, any> => plan[QueryTypeId]
|
|
1547
|
+
|
|
1548
|
+
/**
|
|
1549
|
+
* Collects the required table names referenced by a runtime selection object.
|
|
1550
|
+
*
|
|
1551
|
+
* This mirrors the `ExtractRequired<...>` type-level computation so runtime plan
|
|
1552
|
+
* metadata stays aligned with the static model.
|
|
1553
|
+
*/
|
|
1554
|
+
export const extractRequiredRuntime = (selection: SelectionShape): readonly string[] => {
|
|
1555
|
+
const required = new Set<string>()
|
|
1556
|
+
const visit = (value: SelectionShape): void => {
|
|
1557
|
+
if (Expression.TypeId in value) {
|
|
1558
|
+
for (const tableName of Object.keys(value[Expression.TypeId].dependencies)) {
|
|
1559
|
+
required.add(tableName)
|
|
1560
|
+
}
|
|
1561
|
+
return
|
|
1562
|
+
}
|
|
1563
|
+
for (const nested of Object.values(value)) {
|
|
1564
|
+
visit(nested)
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
visit(selection)
|
|
1568
|
+
return [...required]
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
/** Extracts the single top-level expression from a scalar subquery selection. */
|
|
1572
|
+
export const extractSingleSelectedExpressionRuntime = (selection: SelectionShape): Expression.Any => {
|
|
1573
|
+
const keys = Object.keys(selection)
|
|
1574
|
+
if (keys.length !== 1) {
|
|
1575
|
+
throw new Error("scalar subqueries must select exactly one top-level expression")
|
|
1576
|
+
}
|
|
1577
|
+
const record = selection as Record<string, unknown>
|
|
1578
|
+
const value = record[keys[0]!]
|
|
1579
|
+
if (value === null || typeof value !== "object" || !(Expression.TypeId in (value as object))) {
|
|
1580
|
+
throw new Error("scalar subqueries must select a scalar expression")
|
|
1581
|
+
}
|
|
1582
|
+
return value as unknown as Expression.Any
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
/** Converts the plan's runtime `required` metadata into a mutable string list. */
|
|
1586
|
+
export const currentRequiredList = (required: unknown): string[] =>
|
|
1587
|
+
Array.isArray(required) ? [...required] : required === undefined ? [] : [required as string]
|
|
1588
|
+
|
|
1589
|
+
/** Sort direction accepted by `orderBy(...)`. */
|
|
1590
|
+
export type OrderDirection = QueryAst.OrderDirection
|