effect-qb 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +663 -261
- package/dist/mysql.js +2840 -492
- package/dist/postgres/metadata.js +113 -14
- package/dist/postgres.js +2596 -285
- package/dist/sqlite.js +2783 -450
- package/dist/standard.js +663 -261
- package/package.json +2 -3
- package/src/internal/analytics.ts +264 -0
- package/src/internal/coercion/errors.d.ts +1 -1
- package/src/internal/coercion/errors.ts +1 -1
- package/src/internal/custom-sql-renderer.ts +20 -0
- package/src/internal/dialect-numeric.ts +332 -0
- package/src/internal/dialect-renderers/mysql.ts +68 -13
- package/src/internal/dialect-renderers/postgres.ts +83 -13
- package/src/internal/dialect-renderers/sqlite.ts +57 -13
- package/src/internal/dynamic.ts +131 -0
- package/src/internal/executor.ts +198 -7
- package/src/internal/expression-ast.ts +62 -1
- package/src/internal/fragment.ts +120 -0
- package/src/internal/function-constraints.ts +105 -0
- package/src/internal/grouping-key.ts +26 -0
- package/src/internal/numeric.ts +204 -0
- package/src/internal/query.ts +13 -1
- package/src/internal/runtime/normalize.ts +5 -2
- package/src/internal/runtime/schema.ts +2 -1
- package/src/internal/standard-dsl.ts +43 -20
- package/src/internal/window-frame.ts +30 -0
- package/src/internal/window-renderer.ts +25 -0
- package/src/mysql/executor.ts +126 -45
- package/src/mysql/function/aggregate.ts +87 -1
- package/src/mysql/function/index.ts +5 -2
- package/src/mysql/function/numeric.ts +185 -0
- package/src/mysql/function/temporal.ts +6 -6
- package/src/mysql/function/window.ts +12 -0
- package/src/mysql/internal/dsl.ts +38 -20
- package/src/mysql.ts +2 -0
- package/src/postgres/executor.ts +111 -45
- package/src/postgres/function/aggregate.ts +124 -1
- package/src/postgres/function/core.ts +2 -1
- package/src/postgres/function/index.ts +19 -1
- package/src/postgres/function/numeric.ts +246 -0
- package/src/postgres/function/window.ts +12 -0
- package/src/postgres/internal/dsl.ts +38 -20
- package/src/sqlite/executor.ts +96 -45
- package/src/sqlite/function/aggregate.ts +90 -1
- package/src/sqlite/function/index.ts +5 -2
- package/src/sqlite/function/numeric.ts +139 -0
- package/src/sqlite/function/window.ts +12 -0
- package/src/sqlite/internal/dsl.ts +38 -20
- package/src/sqlite.ts +2 -0
- package/src/standard/function/core.ts +8 -1
- package/src/standard/function/index.ts +18 -11
- package/src/standard/function/string.ts +1 -1
- package/src/standard/function/window.ts +10 -1
- package/src/standard/query.ts +19 -7
- package/src/standard/type.ts +16 -0
- package/src/standard.ts +4 -0
- package/src/standard/function/temporal.ts +0 -78
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema"
|
|
2
|
+
|
|
3
|
+
import { standardDatatypes } from "../standard/datatypes/index.js"
|
|
4
|
+
import * as ExpressionAst from "./expression-ast.js"
|
|
5
|
+
import * as Expression from "./scalar.js"
|
|
6
|
+
import {
|
|
7
|
+
makeExpression,
|
|
8
|
+
mergeAggregationManyRuntime,
|
|
9
|
+
mergeManyDependencies,
|
|
10
|
+
mergeNullabilityManyRuntime,
|
|
11
|
+
type MergeAggregation,
|
|
12
|
+
type MergeNullabilityTuple,
|
|
13
|
+
type NumericExpressionInput,
|
|
14
|
+
type TupleDependencies,
|
|
15
|
+
type TupleDialect
|
|
16
|
+
} from "./query.js"
|
|
17
|
+
|
|
18
|
+
type NumberLiteral<Value extends number> = Expression.Scalar<
|
|
19
|
+
Value,
|
|
20
|
+
ReturnType<typeof standardDatatypes.real>,
|
|
21
|
+
"never",
|
|
22
|
+
"standard",
|
|
23
|
+
"scalar",
|
|
24
|
+
never
|
|
25
|
+
> & {
|
|
26
|
+
readonly [ExpressionAst.TypeId]: ExpressionAst.LiteralNode<Value>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type AsExpression<Value extends NumericExpressionInput> =
|
|
30
|
+
Value extends Expression.Any ? Value : NumberLiteral<Extract<Value, number>>
|
|
31
|
+
|
|
32
|
+
type BinaryResult<
|
|
33
|
+
Kind extends Extract<ExpressionAst.BinaryKind, "add" | "subtract" | "multiply" | "divide">,
|
|
34
|
+
Left extends NumericExpressionInput,
|
|
35
|
+
Right extends NumericExpressionInput
|
|
36
|
+
> = Expression.Scalar<
|
|
37
|
+
number,
|
|
38
|
+
Expression.DbTypeOf<AsExpression<Left>>,
|
|
39
|
+
MergeNullabilityTuple<readonly [AsExpression<Left>, AsExpression<Right>]>,
|
|
40
|
+
TupleDialect<readonly [AsExpression<Left>, AsExpression<Right>]>,
|
|
41
|
+
MergeAggregation<Expression.KindOf<AsExpression<Left>>, Expression.KindOf<AsExpression<Right>>>,
|
|
42
|
+
TupleDependencies<readonly [AsExpression<Left>, AsExpression<Right>]>
|
|
43
|
+
> & {
|
|
44
|
+
readonly [ExpressionAst.TypeId]: ExpressionAst.BinaryNode<Kind, AsExpression<Left>, AsExpression<Right>>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const numberLiteral = <const Value extends number>(value: Value): NumberLiteral<Value> =>
|
|
48
|
+
makeExpression({
|
|
49
|
+
runtime: value,
|
|
50
|
+
dbType: standardDatatypes.real(),
|
|
51
|
+
runtimeSchema: Schema.Number,
|
|
52
|
+
nullability: "never",
|
|
53
|
+
dialect: "standard",
|
|
54
|
+
kind: "scalar",
|
|
55
|
+
dependencies: {}
|
|
56
|
+
}, {
|
|
57
|
+
kind: "literal",
|
|
58
|
+
value
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const toExpression = <Value extends NumericExpressionInput>(
|
|
62
|
+
value: Value
|
|
63
|
+
): AsExpression<Value> =>
|
|
64
|
+
(typeof value === "number" ? numberLiteral(value) : value) as AsExpression<Value>
|
|
65
|
+
|
|
66
|
+
const retargetLiteral = (
|
|
67
|
+
value: Expression.Any,
|
|
68
|
+
target: Expression.Any
|
|
69
|
+
): Expression.Any => {
|
|
70
|
+
const ast = (value as Expression.Any & {
|
|
71
|
+
readonly [ExpressionAst.TypeId]: ExpressionAst.Any
|
|
72
|
+
})[ExpressionAst.TypeId]
|
|
73
|
+
if (ast.kind !== "literal") {
|
|
74
|
+
return value
|
|
75
|
+
}
|
|
76
|
+
const state = target[Expression.TypeId]
|
|
77
|
+
return makeExpression({
|
|
78
|
+
runtime: value[Expression.TypeId].runtime,
|
|
79
|
+
dbType: state.dbType,
|
|
80
|
+
runtimeSchema: state.runtimeSchema,
|
|
81
|
+
driverValueMapping: state.driverValueMapping,
|
|
82
|
+
nullability: value[Expression.TypeId].nullability,
|
|
83
|
+
dialect: state.dialect,
|
|
84
|
+
kind: "scalar",
|
|
85
|
+
dependencies: {}
|
|
86
|
+
}, ast)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const binary = <
|
|
90
|
+
Kind extends Extract<ExpressionAst.BinaryKind, "add" | "subtract" | "multiply" | "divide">,
|
|
91
|
+
Left extends NumericExpressionInput,
|
|
92
|
+
Right extends NumericExpressionInput
|
|
93
|
+
>(
|
|
94
|
+
kind: Kind,
|
|
95
|
+
left: Left,
|
|
96
|
+
right: Right
|
|
97
|
+
): BinaryResult<Kind, Left, Right> => {
|
|
98
|
+
let leftExpression = toExpression(left)
|
|
99
|
+
let rightExpression = toExpression(right)
|
|
100
|
+
const leftAst = (leftExpression as Expression.Any & {
|
|
101
|
+
readonly [ExpressionAst.TypeId]: ExpressionAst.Any
|
|
102
|
+
})[ExpressionAst.TypeId]
|
|
103
|
+
const rightAst = (rightExpression as Expression.Any & {
|
|
104
|
+
readonly [ExpressionAst.TypeId]: ExpressionAst.Any
|
|
105
|
+
})[ExpressionAst.TypeId]
|
|
106
|
+
if (leftAst.kind === "literal" && rightAst.kind !== "literal") {
|
|
107
|
+
leftExpression = retargetLiteral(leftExpression, rightExpression) as AsExpression<Left>
|
|
108
|
+
} else if (rightAst.kind === "literal" && leftAst.kind !== "literal") {
|
|
109
|
+
rightExpression = retargetLiteral(rightExpression, leftExpression) as AsExpression<Right>
|
|
110
|
+
}
|
|
111
|
+
const values = [leftExpression, rightExpression] as const
|
|
112
|
+
return makeExpression({
|
|
113
|
+
runtime: 0,
|
|
114
|
+
dbType: leftExpression[Expression.TypeId].dbType,
|
|
115
|
+
runtimeSchema: Schema.Number,
|
|
116
|
+
driverValueMapping: leftExpression[Expression.TypeId].driverValueMapping,
|
|
117
|
+
nullability: mergeNullabilityManyRuntime(values),
|
|
118
|
+
dialect: values.find((value) => value[Expression.TypeId].dialect !== "standard")?.[Expression.TypeId].dialect ??
|
|
119
|
+
leftExpression[Expression.TypeId].dialect,
|
|
120
|
+
kind: mergeAggregationManyRuntime(values),
|
|
121
|
+
dependencies: mergeManyDependencies(values)
|
|
122
|
+
}, {
|
|
123
|
+
kind,
|
|
124
|
+
left: leftExpression,
|
|
125
|
+
right: rightExpression
|
|
126
|
+
}) as BinaryResult<Kind, Left, Right>
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export const add = <Left extends NumericExpressionInput, Right extends NumericExpressionInput>(
|
|
130
|
+
left: Left,
|
|
131
|
+
right: Right
|
|
132
|
+
): BinaryResult<"add", Left, Right> => binary("add", left, right)
|
|
133
|
+
|
|
134
|
+
export const subtract = <Left extends NumericExpressionInput, Right extends NumericExpressionInput>(
|
|
135
|
+
left: Left,
|
|
136
|
+
right: Right
|
|
137
|
+
): BinaryResult<"subtract", Left, Right> => binary("subtract", left, right)
|
|
138
|
+
|
|
139
|
+
export const multiply = <Left extends NumericExpressionInput, Right extends NumericExpressionInput>(
|
|
140
|
+
left: Left,
|
|
141
|
+
right: Right
|
|
142
|
+
): BinaryResult<"multiply", Left, Right> => binary("multiply", left, right)
|
|
143
|
+
|
|
144
|
+
export const divide = <Left extends NumericExpressionInput, Right extends NumericExpressionInput>(
|
|
145
|
+
left: Left,
|
|
146
|
+
right: Right
|
|
147
|
+
): BinaryResult<"divide", Left, Right> => binary("divide", left, right)
|
|
148
|
+
|
|
149
|
+
type UnaryResult<
|
|
150
|
+
Kind extends Extract<ExpressionAst.UnaryKind, "abs" | "negate">,
|
|
151
|
+
Value extends NumericExpressionInput,
|
|
152
|
+
Nullable extends Expression.Nullability,
|
|
153
|
+
ResultKind extends Expression.ScalarKind
|
|
154
|
+
> = Expression.Scalar<
|
|
155
|
+
number,
|
|
156
|
+
Expression.DbTypeOf<AsExpression<Value>>,
|
|
157
|
+
Nullable,
|
|
158
|
+
AsExpression<Value>[typeof Expression.TypeId]["dialect"],
|
|
159
|
+
ResultKind,
|
|
160
|
+
Expression.DependenciesOf<AsExpression<Value>>
|
|
161
|
+
> & {
|
|
162
|
+
readonly [ExpressionAst.TypeId]: ExpressionAst.UnaryNode<Kind, AsExpression<Value>>
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const unary = <
|
|
166
|
+
Kind extends Extract<ExpressionAst.UnaryKind, "abs" | "negate">,
|
|
167
|
+
Value extends NumericExpressionInput,
|
|
168
|
+
Nullable extends Expression.Nullability,
|
|
169
|
+
ResultKind extends Expression.ScalarKind
|
|
170
|
+
>(
|
|
171
|
+
kind: Kind,
|
|
172
|
+
value: Value,
|
|
173
|
+
nullability: Nullable,
|
|
174
|
+
resultKind: ResultKind
|
|
175
|
+
): UnaryResult<Kind, Value, Nullable, ResultKind> => {
|
|
176
|
+
const expression = toExpression(value)
|
|
177
|
+
return makeExpression({
|
|
178
|
+
runtime: 0,
|
|
179
|
+
dbType: expression[Expression.TypeId].dbType,
|
|
180
|
+
runtimeSchema: Schema.Number,
|
|
181
|
+
driverValueMapping: expression[Expression.TypeId].driverValueMapping,
|
|
182
|
+
nullability,
|
|
183
|
+
dialect: expression[Expression.TypeId].dialect,
|
|
184
|
+
kind: resultKind,
|
|
185
|
+
dependencies: expression[Expression.TypeId].dependencies
|
|
186
|
+
}, {
|
|
187
|
+
kind,
|
|
188
|
+
value: expression
|
|
189
|
+
}) as UnaryResult<Kind, Value, Nullable, ResultKind>
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export const abs = <Value extends NumericExpressionInput>(
|
|
193
|
+
value: Value
|
|
194
|
+
): UnaryResult<"abs", Value, Expression.NullabilityOf<AsExpression<Value>>, Expression.KindOf<AsExpression<Value>>> => {
|
|
195
|
+
const expression = toExpression(value)
|
|
196
|
+
return unary("abs", value, expression[Expression.TypeId].nullability, expression[Expression.TypeId].kind) as never
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export const negate = <Value extends NumericExpressionInput>(
|
|
200
|
+
value: Value
|
|
201
|
+
): UnaryResult<"negate", Value, Expression.NullabilityOf<AsExpression<Value>>, Expression.KindOf<AsExpression<Value>>> => {
|
|
202
|
+
const expression = toExpression(value)
|
|
203
|
+
return unary("negate", value, expression[Expression.TypeId].nullability, expression[Expression.TypeId].kind) as never
|
|
204
|
+
}
|
package/src/internal/query.ts
CHANGED
|
@@ -203,10 +203,20 @@ type LiteralExpression<Value extends LiteralValue> = Expression.Scalar<
|
|
|
203
203
|
*/
|
|
204
204
|
export type ExpressionInput = Expression.Any | LiteralValue
|
|
205
205
|
|
|
206
|
+
type NumericBaseDbType = Expression.DbType.Base<string, string> & {
|
|
207
|
+
readonly family: "numeric" | "integer" | "real"
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
type NumericDbType =
|
|
211
|
+
| NumericBaseDbType
|
|
212
|
+
| (Expression.DbType.Domain<string, any, string> & {
|
|
213
|
+
readonly base: NumericDbType
|
|
214
|
+
})
|
|
215
|
+
|
|
206
216
|
/** Input accepted by numeric clauses such as `limit(...)` and `offset(...)`. */
|
|
207
217
|
export type NumericExpressionInput = Expression.Scalar<
|
|
208
218
|
number,
|
|
209
|
-
|
|
219
|
+
NumericDbType,
|
|
210
220
|
Expression.Nullability,
|
|
211
221
|
string,
|
|
212
222
|
"scalar",
|
|
@@ -429,6 +439,8 @@ type GroupingKeyOfAst<Ast extends ExpressionAst.Any> =
|
|
|
429
439
|
? `function(${EscapeGroupingText<Name>},${JoinGroupingKeys<{
|
|
430
440
|
readonly [K in keyof Args]: Args[K] extends Expression.Any ? GroupingKeyOfExpression<Args[K]> : never
|
|
431
441
|
} & readonly string[]>})`
|
|
442
|
+
: Ast extends ExpressionAst.CustomSqlNode
|
|
443
|
+
? string
|
|
432
444
|
: Ast extends ExpressionAst.UnaryNode<infer Kind extends ExpressionAst.UnaryKind, infer Value extends Expression.Any>
|
|
433
445
|
? `${Kind}(${GroupingKeyOfExpression<Value>})`
|
|
434
446
|
: Ast extends ExpressionAst.BinaryNode<infer Kind extends ExpressionAst.BinaryKind, infer Left extends Expression.Any, infer Right extends Expression.Any>
|
|
@@ -164,8 +164,11 @@ const normalizeOffsetTime = (value: unknown): string => {
|
|
|
164
164
|
return `${formatLocalTime(validDate(value))}Z`
|
|
165
165
|
}
|
|
166
166
|
const raw = expectString(value, "offset time").trim()
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
const canonical = raw
|
|
168
|
+
.replace(/([+-]\d{2})$/, "$1:00")
|
|
169
|
+
.replace(/([+-]\d{2})(\d{2})$/, "$1:$2")
|
|
170
|
+
if (isValidOffsetTimeString(canonical)) {
|
|
171
|
+
return canonical
|
|
169
172
|
}
|
|
170
173
|
throw new Error("Expected an offset-time value")
|
|
171
174
|
}
|
|
@@ -379,6 +379,7 @@ const deriveRuntimeSchema = (
|
|
|
379
379
|
case "jsonTypeOf":
|
|
380
380
|
return Schema.String
|
|
381
381
|
case "count":
|
|
382
|
+
return runtimeSchemaForDbType(state.dbType)
|
|
382
383
|
case "jsonLength":
|
|
383
384
|
return FiniteNumberSchema
|
|
384
385
|
case "max":
|
|
@@ -396,7 +397,7 @@ const deriveRuntimeSchema = (
|
|
|
396
397
|
case "window":
|
|
397
398
|
return ast.function === "over" && ast.value !== undefined
|
|
398
399
|
? expressionRuntimeSchema(ast.value, context)
|
|
399
|
-
:
|
|
400
|
+
: runtimeSchemaForDbType(state.dbType)
|
|
400
401
|
case "jsonGet":
|
|
401
402
|
case "jsonPath":
|
|
402
403
|
case "jsonAccess":
|
|
@@ -4,6 +4,7 @@ import * as Schema from "effect/Schema"
|
|
|
4
4
|
import { standardDatatypes } from "../standard/datatypes/index.js"
|
|
5
5
|
|
|
6
6
|
import * as Expression from "./scalar.js"
|
|
7
|
+
import type * as FunctionConstraint from "./function-constraints.js"
|
|
7
8
|
import * as Plan from "./row-set.js"
|
|
8
9
|
import * as Table from "./table.js"
|
|
9
10
|
import type {
|
|
@@ -135,6 +136,7 @@ import type { FormulaOfPredicate } from "./predicate/normalize.js"
|
|
|
135
136
|
import type { TrueFormula } from "./predicate/formula.js"
|
|
136
137
|
import { assumeFormulaTrue, formulaOfExpression as formulaOfExpressionRuntime, trueFormula } from "./predicate/runtime.js"
|
|
137
138
|
import { dedupeGroupedExpressions } from "./grouping-key.js"
|
|
139
|
+
import { validateWindowFrame } from "./window-frame.js"
|
|
138
140
|
import { makeDslMutationRuntime } from "./dsl-mutation-runtime.js"
|
|
139
141
|
import { makeDslPlanRuntime } from "./dsl-plan-runtime.js"
|
|
140
142
|
import { makeDslQueryRuntime } from "./dsl-query-runtime.js"
|
|
@@ -1426,6 +1428,7 @@ type WindowSpecInput<
|
|
|
1426
1428
|
> = {
|
|
1427
1429
|
readonly partitionBy?: PartitionBy
|
|
1428
1430
|
readonly orderBy?: OrderBy
|
|
1431
|
+
readonly frame?: never
|
|
1429
1432
|
}
|
|
1430
1433
|
|
|
1431
1434
|
type OrderedWindowSpecInput<
|
|
@@ -1434,6 +1437,7 @@ type OrderedWindowSpecInput<
|
|
|
1434
1437
|
> = {
|
|
1435
1438
|
readonly partitionBy?: PartitionBy
|
|
1436
1439
|
readonly orderBy: OrderBy
|
|
1440
|
+
readonly frame?: never
|
|
1437
1441
|
}
|
|
1438
1442
|
|
|
1439
1443
|
type WindowOrderExpressionTuple<
|
|
@@ -1506,8 +1510,8 @@ type NumberWindowExpression<
|
|
|
1506
1510
|
PartitionBy extends readonly WindowPartitionInput[],
|
|
1507
1511
|
OrderBy extends NonEmptyWindowOrderTerms
|
|
1508
1512
|
> = AstBackedExpression<
|
|
1509
|
-
|
|
1510
|
-
|
|
1513
|
+
RuntimeOfDbType<ReturnType<typeof standardDatatypes.bigint>>,
|
|
1514
|
+
ReturnType<typeof standardDatatypes.bigint>,
|
|
1511
1515
|
"never",
|
|
1512
1516
|
NumberWindowDialectOf<PartitionBy, OrderBy>,
|
|
1513
1517
|
"window",
|
|
@@ -1798,6 +1802,12 @@ const profile: QueryDialectProfile<Dialect, TextDb, NumericDb, BoolDb, Timestamp
|
|
|
1798
1802
|
>(
|
|
1799
1803
|
spec: WindowSpecInput<PartitionBy, OrderBy> | OrderedWindowSpecInput<PartitionBy, Extract<OrderBy, NonEmptyWindowOrderTerms>> | undefined
|
|
1800
1804
|
) => {
|
|
1805
|
+
if ((spec as { readonly frame?: ExpressionAst.WindowFrameNode } | undefined)?.frame !== undefined) {
|
|
1806
|
+
throw new Error(
|
|
1807
|
+
"over does not accept an explicit frame on the portable Function API; use a dialect Function.over helper"
|
|
1808
|
+
)
|
|
1809
|
+
}
|
|
1810
|
+
validateWindowFrame(spec?.frame)
|
|
1801
1811
|
const partitionBy = [...(spec?.partitionBy ?? [])] as unknown as PartitionBy
|
|
1802
1812
|
const orderBy = (spec?.orderBy ?? []).map((term) => {
|
|
1803
1813
|
const direction = term.direction ?? "asc"
|
|
@@ -1812,7 +1822,8 @@ const profile: QueryDialectProfile<Dialect, TextDb, NumericDb, BoolDb, Timestamp
|
|
|
1812
1822
|
} & readonly { readonly value: WindowOrderInput; readonly direction: OrderDirection }[]
|
|
1813
1823
|
return {
|
|
1814
1824
|
partitionBy,
|
|
1815
|
-
orderBy
|
|
1825
|
+
orderBy,
|
|
1826
|
+
frame: spec?.frame
|
|
1816
1827
|
}
|
|
1817
1828
|
}
|
|
1818
1829
|
|
|
@@ -2142,12 +2153,17 @@ type BinaryPredicateExpression<
|
|
|
2142
2153
|
}
|
|
2143
2154
|
|
|
2144
2155
|
const upper = <Value extends ExpressionInput>(
|
|
2145
|
-
value: Value
|
|
2156
|
+
value: Value & FunctionConstraint.CaseConversionInput<
|
|
2157
|
+
NoInfer<Value>,
|
|
2158
|
+
DialectDbTypeOfInput<NoInfer<Value>, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2159
|
+
Dialect,
|
|
2160
|
+
"upper"
|
|
2161
|
+
>
|
|
2146
2162
|
): AstBackedExpression<
|
|
2147
2163
|
string,
|
|
2148
2164
|
TextDb,
|
|
2149
2165
|
NullabilityOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2150
|
-
|
|
2166
|
+
Dialect,
|
|
2151
2167
|
KindOf<DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
|
|
2152
2168
|
DependenciesOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2153
2169
|
ExpressionAst.UnaryNode<"upper", DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>
|
|
@@ -2157,7 +2173,7 @@ type BinaryPredicateExpression<
|
|
|
2157
2173
|
runtime: "" as string,
|
|
2158
2174
|
dbType: profile.textDb as TextDb,
|
|
2159
2175
|
nullability: expression[Expression.TypeId].nullability as NullabilityOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2160
|
-
dialect:
|
|
2176
|
+
dialect: profile.dialect as Dialect,
|
|
2161
2177
|
kind: expression[Expression.TypeId].kind as KindOf<DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
|
|
2162
2178
|
|
|
2163
2179
|
dependencies: expression[Expression.TypeId].dependencies
|
|
@@ -2168,12 +2184,17 @@ type BinaryPredicateExpression<
|
|
|
2168
2184
|
}
|
|
2169
2185
|
|
|
2170
2186
|
const lower = <Value extends ExpressionInput>(
|
|
2171
|
-
value: Value
|
|
2187
|
+
value: Value & FunctionConstraint.CaseConversionInput<
|
|
2188
|
+
NoInfer<Value>,
|
|
2189
|
+
DialectDbTypeOfInput<NoInfer<Value>, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2190
|
+
Dialect,
|
|
2191
|
+
"lower"
|
|
2192
|
+
>
|
|
2172
2193
|
): AstBackedExpression<
|
|
2173
2194
|
string,
|
|
2174
2195
|
TextDb,
|
|
2175
2196
|
NullabilityOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2176
|
-
|
|
2197
|
+
Dialect,
|
|
2177
2198
|
KindOf<DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
|
|
2178
2199
|
DependenciesOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2179
2200
|
ExpressionAst.UnaryNode<"lower", DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>
|
|
@@ -2183,7 +2204,7 @@ type BinaryPredicateExpression<
|
|
|
2183
2204
|
runtime: "" as string,
|
|
2184
2205
|
dbType: profile.textDb as TextDb,
|
|
2185
2206
|
nullability: expression[Expression.TypeId].nullability as NullabilityOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2186
|
-
dialect:
|
|
2207
|
+
dialect: profile.dialect as Dialect,
|
|
2187
2208
|
kind: expression[Expression.TypeId].kind as KindOf<DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
|
|
2188
2209
|
|
|
2189
2210
|
dependencies: expression[Expression.TypeId].dependencies
|
|
@@ -3494,8 +3515,8 @@ type BinaryPredicateExpression<
|
|
|
3494
3515
|
const count = <Value extends ExpressionInput>(
|
|
3495
3516
|
value: Value
|
|
3496
3517
|
): AstBackedExpression<
|
|
3497
|
-
|
|
3498
|
-
|
|
3518
|
+
RuntimeOfDbType<ReturnType<typeof standardDatatypes.bigint>>,
|
|
3519
|
+
ReturnType<typeof standardDatatypes.bigint>,
|
|
3499
3520
|
"never",
|
|
3500
3521
|
DialectOfDialectInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
3501
3522
|
"aggregate",
|
|
@@ -3504,8 +3525,8 @@ type BinaryPredicateExpression<
|
|
|
3504
3525
|
> => {
|
|
3505
3526
|
const expression = toDialectExpression(value)
|
|
3506
3527
|
return makeExpression({
|
|
3507
|
-
runtime:
|
|
3508
|
-
dbType:
|
|
3528
|
+
runtime: undefined as unknown as RuntimeOfDbType<ReturnType<typeof standardDatatypes.bigint>>,
|
|
3529
|
+
dbType: standardDatatypes.bigint(),
|
|
3509
3530
|
nullability: "never",
|
|
3510
3531
|
dialect: expression[Expression.TypeId].dialect,
|
|
3511
3532
|
kind: "aggregate",
|
|
@@ -3717,7 +3738,8 @@ type BinaryPredicateExpression<
|
|
|
3717
3738
|
function: "over",
|
|
3718
3739
|
value,
|
|
3719
3740
|
partitionBy: normalized.partitionBy,
|
|
3720
|
-
orderBy: normalized.orderBy
|
|
3741
|
+
orderBy: normalized.orderBy,
|
|
3742
|
+
frame: normalized.frame
|
|
3721
3743
|
})
|
|
3722
3744
|
}
|
|
3723
3745
|
|
|
@@ -3732,8 +3754,8 @@ type BinaryPredicateExpression<
|
|
|
3732
3754
|
const normalized = normalizeWindowSpec(spec)
|
|
3733
3755
|
const expressions = mergeWindowExpressions(undefined, normalized.partitionBy, normalized.orderBy)
|
|
3734
3756
|
return makeExpression({
|
|
3735
|
-
runtime:
|
|
3736
|
-
dbType:
|
|
3757
|
+
runtime: undefined as unknown as RuntimeOfDbType<ReturnType<typeof standardDatatypes.bigint>>,
|
|
3758
|
+
dbType: standardDatatypes.bigint(),
|
|
3737
3759
|
nullability: "never",
|
|
3738
3760
|
dialect: (expressions.find((expression) => expression[Expression.TypeId].dialect !== undefined)?.[Expression.TypeId].dialect ?? profile.dialect) as NumberWindowDialectOf<PartitionBy, OrderBy>,
|
|
3739
3761
|
kind: "window",
|
|
@@ -3743,7 +3765,8 @@ type BinaryPredicateExpression<
|
|
|
3743
3765
|
kind: "window",
|
|
3744
3766
|
function: kind,
|
|
3745
3767
|
partitionBy: normalized.partitionBy,
|
|
3746
|
-
orderBy: normalized.orderBy
|
|
3768
|
+
orderBy: normalized.orderBy,
|
|
3769
|
+
frame: normalized.frame
|
|
3747
3770
|
})
|
|
3748
3771
|
}
|
|
3749
3772
|
|
|
@@ -3772,7 +3795,7 @@ type BinaryPredicateExpression<
|
|
|
3772
3795
|
buildNumberWindow("denseRank", spec)
|
|
3773
3796
|
|
|
3774
3797
|
const max = <Value extends Expression.Any>(
|
|
3775
|
-
value: Value
|
|
3798
|
+
value: Value & FunctionConstraint.OrderedInput<NoInfer<Value>, Dialect, "max">
|
|
3776
3799
|
): AstBackedExpression<
|
|
3777
3800
|
Expression.RuntimeOf<Value>,
|
|
3778
3801
|
Expression.DbTypeOf<Value>,
|
|
@@ -3796,7 +3819,7 @@ type BinaryPredicateExpression<
|
|
|
3796
3819
|
})
|
|
3797
3820
|
|
|
3798
3821
|
const min = <Value extends Expression.Any>(
|
|
3799
|
-
value: Value
|
|
3822
|
+
value: Value & FunctionConstraint.OrderedInput<NoInfer<Value>, Dialect, "min">
|
|
3800
3823
|
): AstBackedExpression<
|
|
3801
3824
|
Expression.RuntimeOf<Value>,
|
|
3802
3825
|
Expression.DbTypeOf<Value>,
|
|
@@ -3831,7 +3854,7 @@ type BinaryPredicateExpression<
|
|
|
3831
3854
|
const coalesce = <
|
|
3832
3855
|
Values extends readonly [ExpressionInput, ExpressionInput, ...ExpressionInput[]]
|
|
3833
3856
|
>(
|
|
3834
|
-
...values: Values
|
|
3857
|
+
...values: Values & FunctionConstraint.CoalesceConstraint<DialectExpressionTuple<Values, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>, Dialect>
|
|
3835
3858
|
): AstBackedExpression<
|
|
3836
3859
|
CoalesceRuntimeTuple<DialectExpressionTuple<Values, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
|
|
3837
3860
|
Expression.DbTypeOf<DialectExpressionTuple<Values, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>[number]>,
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type * as ExpressionAst from "./expression-ast.js"
|
|
2
|
+
|
|
3
|
+
const validateBoundary = (
|
|
4
|
+
boundary: ExpressionAst.WindowFrameBoundary
|
|
5
|
+
): void => {
|
|
6
|
+
if (typeof boundary === "object") {
|
|
7
|
+
const value = "preceding" in boundary ? boundary.preceding : boundary.following
|
|
8
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
9
|
+
throw new Error("window frame offsets must be non-negative safe integers")
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const validateWindowFrame = (
|
|
15
|
+
frame: ExpressionAst.WindowFrameNode | undefined
|
|
16
|
+
): void => {
|
|
17
|
+
if (frame === undefined) {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
validateBoundary(frame.start)
|
|
21
|
+
if (frame.end !== undefined) {
|
|
22
|
+
validateBoundary(frame.end)
|
|
23
|
+
}
|
|
24
|
+
if (frame.start === "unboundedFollowing") {
|
|
25
|
+
throw new Error("window frame cannot start with unbounded following")
|
|
26
|
+
}
|
|
27
|
+
if (frame.end === "unboundedPreceding") {
|
|
28
|
+
throw new Error("window frame cannot end with unbounded preceding")
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type * as ExpressionAst from "./expression-ast.js"
|
|
2
|
+
|
|
3
|
+
const renderBoundary = (
|
|
4
|
+
boundary: ExpressionAst.WindowFrameBoundary
|
|
5
|
+
): string => {
|
|
6
|
+
if (boundary === "unboundedPreceding") {
|
|
7
|
+
return "unbounded preceding"
|
|
8
|
+
}
|
|
9
|
+
if (boundary === "currentRow") {
|
|
10
|
+
return "current row"
|
|
11
|
+
}
|
|
12
|
+
if (boundary === "unboundedFollowing") {
|
|
13
|
+
return "unbounded following"
|
|
14
|
+
}
|
|
15
|
+
if ("preceding" in boundary) {
|
|
16
|
+
return `${boundary.preceding} preceding`
|
|
17
|
+
}
|
|
18
|
+
return `${boundary.following} following`
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const renderWindowFrame = (
|
|
22
|
+
frame: ExpressionAst.WindowFrameNode
|
|
23
|
+
): string => frame.end === undefined
|
|
24
|
+
? `${frame.unit} ${renderBoundary(frame.start)}`
|
|
25
|
+
: `${frame.unit} between ${renderBoundary(frame.start)} and ${renderBoundary(frame.end)}`
|