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
|
@@ -4,6 +4,7 @@ import * as Schema from "effect/Schema"
|
|
|
4
4
|
import { mysqlDatatypes } from "../datatypes/index.js"
|
|
5
5
|
|
|
6
6
|
import * as Expression from "../../internal/scalar.js"
|
|
7
|
+
import type * as FunctionConstraint from "../../internal/function-constraints.js"
|
|
7
8
|
import * as Plan from "../../internal/row-set.js"
|
|
8
9
|
import * as Table from "../../internal/table.js"
|
|
9
10
|
import type {
|
|
@@ -133,6 +134,7 @@ import type { FormulaOfPredicate } from "../../internal/predicate/normalize.js"
|
|
|
133
134
|
import type { TrueFormula } from "../../internal/predicate/formula.js"
|
|
134
135
|
import { assumeFormulaTrue, formulaOfExpression as formulaOfExpressionRuntime, trueFormula } from "../../internal/predicate/runtime.js"
|
|
135
136
|
import { dedupeGroupedExpressions } from "../../internal/grouping-key.js"
|
|
137
|
+
import { validateWindowFrame } from "../../internal/window-frame.js"
|
|
136
138
|
import { makeDslMutationRuntime } from "../../internal/dsl-mutation-runtime.js"
|
|
137
139
|
import { makeDslPlanRuntime } from "../../internal/dsl-plan-runtime.js"
|
|
138
140
|
import { makeDslQueryRuntime } from "../../internal/dsl-query-runtime.js"
|
|
@@ -1440,6 +1442,7 @@ type WindowSpecInput<
|
|
|
1440
1442
|
> = {
|
|
1441
1443
|
readonly partitionBy?: PartitionBy
|
|
1442
1444
|
readonly orderBy?: OrderBy
|
|
1445
|
+
readonly frame?: ExpressionAst.WindowFrameNode<"rows" | "range">
|
|
1443
1446
|
}
|
|
1444
1447
|
|
|
1445
1448
|
type OrderedWindowSpecInput<
|
|
@@ -1448,6 +1451,7 @@ type OrderedWindowSpecInput<
|
|
|
1448
1451
|
> = {
|
|
1449
1452
|
readonly partitionBy?: PartitionBy
|
|
1450
1453
|
readonly orderBy: OrderBy
|
|
1454
|
+
readonly frame?: never
|
|
1451
1455
|
}
|
|
1452
1456
|
|
|
1453
1457
|
type WindowOrderExpressionTuple<
|
|
@@ -1520,8 +1524,8 @@ type NumberWindowExpression<
|
|
|
1520
1524
|
PartitionBy extends readonly WindowPartitionInput[],
|
|
1521
1525
|
OrderBy extends NonEmptyWindowOrderTerms
|
|
1522
1526
|
> = AstBackedExpression<
|
|
1523
|
-
|
|
1524
|
-
|
|
1527
|
+
RuntimeOfDbType<ReturnType<typeof mysqlDatatypes.bigint>>,
|
|
1528
|
+
ReturnType<typeof mysqlDatatypes.bigint>,
|
|
1525
1529
|
"never",
|
|
1526
1530
|
NumberWindowDialectOf<PartitionBy, OrderBy>,
|
|
1527
1531
|
"window",
|
|
@@ -1812,6 +1816,7 @@ const profile: QueryDialectProfile<Dialect, TextDb, NumericDb, BoolDb, Timestamp
|
|
|
1812
1816
|
>(
|
|
1813
1817
|
spec: WindowSpecInput<PartitionBy, OrderBy> | OrderedWindowSpecInput<PartitionBy, Extract<OrderBy, NonEmptyWindowOrderTerms>> | undefined
|
|
1814
1818
|
) => {
|
|
1819
|
+
validateWindowFrame(spec?.frame)
|
|
1815
1820
|
const partitionBy = [...(spec?.partitionBy ?? [])] as unknown as PartitionBy
|
|
1816
1821
|
const orderBy = (spec?.orderBy ?? []).map((term) => {
|
|
1817
1822
|
const direction = term.direction ?? "asc"
|
|
@@ -1826,7 +1831,8 @@ const profile: QueryDialectProfile<Dialect, TextDb, NumericDb, BoolDb, Timestamp
|
|
|
1826
1831
|
} & readonly { readonly value: WindowOrderInput; readonly direction: OrderDirection }[]
|
|
1827
1832
|
return {
|
|
1828
1833
|
partitionBy,
|
|
1829
|
-
orderBy
|
|
1834
|
+
orderBy,
|
|
1835
|
+
frame: spec?.frame
|
|
1830
1836
|
}
|
|
1831
1837
|
}
|
|
1832
1838
|
|
|
@@ -2156,12 +2162,17 @@ type BinaryPredicateExpression<
|
|
|
2156
2162
|
}
|
|
2157
2163
|
|
|
2158
2164
|
const upper = <Value extends ExpressionInput>(
|
|
2159
|
-
value: Value
|
|
2165
|
+
value: Value & FunctionConstraint.CaseConversionInput<
|
|
2166
|
+
NoInfer<Value>,
|
|
2167
|
+
DialectDbTypeOfInput<NoInfer<Value>, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2168
|
+
Dialect,
|
|
2169
|
+
"upper"
|
|
2170
|
+
>
|
|
2160
2171
|
): AstBackedExpression<
|
|
2161
2172
|
string,
|
|
2162
2173
|
TextDb,
|
|
2163
2174
|
NullabilityOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2164
|
-
|
|
2175
|
+
Dialect,
|
|
2165
2176
|
KindOf<DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
|
|
2166
2177
|
DependenciesOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2167
2178
|
ExpressionAst.UnaryNode<"upper", DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>
|
|
@@ -2171,7 +2182,7 @@ type BinaryPredicateExpression<
|
|
|
2171
2182
|
runtime: "" as string,
|
|
2172
2183
|
dbType: profile.textDb as TextDb,
|
|
2173
2184
|
nullability: expression[Expression.TypeId].nullability as NullabilityOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2174
|
-
dialect:
|
|
2185
|
+
dialect: profile.dialect as Dialect,
|
|
2175
2186
|
kind: expression[Expression.TypeId].kind as KindOf<DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
|
|
2176
2187
|
|
|
2177
2188
|
dependencies: expression[Expression.TypeId].dependencies
|
|
@@ -2182,12 +2193,17 @@ type BinaryPredicateExpression<
|
|
|
2182
2193
|
}
|
|
2183
2194
|
|
|
2184
2195
|
const lower = <Value extends ExpressionInput>(
|
|
2185
|
-
value: Value
|
|
2196
|
+
value: Value & FunctionConstraint.CaseConversionInput<
|
|
2197
|
+
NoInfer<Value>,
|
|
2198
|
+
DialectDbTypeOfInput<NoInfer<Value>, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2199
|
+
Dialect,
|
|
2200
|
+
"lower"
|
|
2201
|
+
>
|
|
2186
2202
|
): AstBackedExpression<
|
|
2187
2203
|
string,
|
|
2188
2204
|
TextDb,
|
|
2189
2205
|
NullabilityOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2190
|
-
|
|
2206
|
+
Dialect,
|
|
2191
2207
|
KindOf<DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
|
|
2192
2208
|
DependenciesOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2193
2209
|
ExpressionAst.UnaryNode<"lower", DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>
|
|
@@ -2197,7 +2213,7 @@ type BinaryPredicateExpression<
|
|
|
2197
2213
|
runtime: "" as string,
|
|
2198
2214
|
dbType: profile.textDb as TextDb,
|
|
2199
2215
|
nullability: expression[Expression.TypeId].nullability as NullabilityOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2200
|
-
dialect:
|
|
2216
|
+
dialect: profile.dialect as Dialect,
|
|
2201
2217
|
kind: expression[Expression.TypeId].kind as KindOf<DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
|
|
2202
2218
|
|
|
2203
2219
|
dependencies: expression[Expression.TypeId].dependencies
|
|
@@ -3476,8 +3492,8 @@ type BinaryPredicateExpression<
|
|
|
3476
3492
|
const count = <Value extends ExpressionInput>(
|
|
3477
3493
|
value: Value
|
|
3478
3494
|
): AstBackedExpression<
|
|
3479
|
-
|
|
3480
|
-
|
|
3495
|
+
RuntimeOfDbType<ReturnType<typeof mysqlDatatypes.bigint>>,
|
|
3496
|
+
ReturnType<typeof mysqlDatatypes.bigint>,
|
|
3481
3497
|
"never",
|
|
3482
3498
|
DialectOfDialectInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
3483
3499
|
"aggregate",
|
|
@@ -3486,8 +3502,8 @@ type BinaryPredicateExpression<
|
|
|
3486
3502
|
> => {
|
|
3487
3503
|
const expression = toDialectExpression(value)
|
|
3488
3504
|
return makeExpression({
|
|
3489
|
-
runtime:
|
|
3490
|
-
dbType:
|
|
3505
|
+
runtime: undefined as unknown as RuntimeOfDbType<ReturnType<typeof mysqlDatatypes.bigint>>,
|
|
3506
|
+
dbType: mysqlDatatypes.bigint(),
|
|
3491
3507
|
nullability: "never",
|
|
3492
3508
|
dialect: expression[Expression.TypeId].dialect,
|
|
3493
3509
|
kind: "aggregate",
|
|
@@ -3699,7 +3715,8 @@ type BinaryPredicateExpression<
|
|
|
3699
3715
|
function: "over",
|
|
3700
3716
|
value,
|
|
3701
3717
|
partitionBy: normalized.partitionBy,
|
|
3702
|
-
orderBy: normalized.orderBy
|
|
3718
|
+
orderBy: normalized.orderBy,
|
|
3719
|
+
frame: normalized.frame
|
|
3703
3720
|
})
|
|
3704
3721
|
}
|
|
3705
3722
|
|
|
@@ -3714,8 +3731,8 @@ type BinaryPredicateExpression<
|
|
|
3714
3731
|
const normalized = normalizeWindowSpec(spec)
|
|
3715
3732
|
const expressions = mergeWindowExpressions(undefined, normalized.partitionBy, normalized.orderBy)
|
|
3716
3733
|
return makeExpression({
|
|
3717
|
-
runtime:
|
|
3718
|
-
dbType:
|
|
3734
|
+
runtime: undefined as unknown as RuntimeOfDbType<ReturnType<typeof mysqlDatatypes.bigint>>,
|
|
3735
|
+
dbType: mysqlDatatypes.bigint(),
|
|
3719
3736
|
nullability: "never",
|
|
3720
3737
|
dialect: (expressions.find((expression) => expression[Expression.TypeId].dialect !== undefined)?.[Expression.TypeId].dialect ?? profile.dialect) as NumberWindowDialectOf<PartitionBy, OrderBy>,
|
|
3721
3738
|
kind: "window",
|
|
@@ -3725,7 +3742,8 @@ type BinaryPredicateExpression<
|
|
|
3725
3742
|
kind: "window",
|
|
3726
3743
|
function: kind,
|
|
3727
3744
|
partitionBy: normalized.partitionBy,
|
|
3728
|
-
orderBy: normalized.orderBy
|
|
3745
|
+
orderBy: normalized.orderBy,
|
|
3746
|
+
frame: normalized.frame
|
|
3729
3747
|
})
|
|
3730
3748
|
}
|
|
3731
3749
|
|
|
@@ -3754,7 +3772,7 @@ type BinaryPredicateExpression<
|
|
|
3754
3772
|
buildNumberWindow("denseRank", spec)
|
|
3755
3773
|
|
|
3756
3774
|
const max = <Value extends Expression.Any>(
|
|
3757
|
-
value: Value
|
|
3775
|
+
value: Value & FunctionConstraint.OrderedInput<NoInfer<Value>, Dialect, "max">
|
|
3758
3776
|
): AstBackedExpression<
|
|
3759
3777
|
Expression.RuntimeOf<Value>,
|
|
3760
3778
|
Expression.DbTypeOf<Value>,
|
|
@@ -3778,7 +3796,7 @@ type BinaryPredicateExpression<
|
|
|
3778
3796
|
})
|
|
3779
3797
|
|
|
3780
3798
|
const min = <Value extends Expression.Any>(
|
|
3781
|
-
value: Value
|
|
3799
|
+
value: Value & FunctionConstraint.OrderedInput<NoInfer<Value>, Dialect, "min">
|
|
3782
3800
|
): AstBackedExpression<
|
|
3783
3801
|
Expression.RuntimeOf<Value>,
|
|
3784
3802
|
Expression.DbTypeOf<Value>,
|
|
@@ -3813,7 +3831,7 @@ type BinaryPredicateExpression<
|
|
|
3813
3831
|
const coalesce = <
|
|
3814
3832
|
Values extends readonly [ExpressionInput, ExpressionInput, ...ExpressionInput[]]
|
|
3815
3833
|
>(
|
|
3816
|
-
...values: Values
|
|
3834
|
+
...values: Values & FunctionConstraint.CoalesceConstraint<DialectExpressionTuple<Values, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>, Dialect>
|
|
3817
3835
|
): AstBackedExpression<
|
|
3818
3836
|
CoalesceRuntimeTuple<DialectExpressionTuple<Values, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
|
|
3819
3837
|
Expression.DbTypeOf<DialectExpressionTuple<Values, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>[number]>,
|
package/src/mysql.ts
CHANGED
|
@@ -4,6 +4,8 @@ export * as Column from "./mysql/column-extension.js"
|
|
|
4
4
|
export * as Datatypes from "./mysql/datatypes/index.js"
|
|
5
5
|
/** MySQL error catalog and error normalization helpers. */
|
|
6
6
|
export * as Errors from "./mysql/errors/index.js"
|
|
7
|
+
/** MySQL-specific SQL function expressions. */
|
|
8
|
+
export * as Function from "./mysql/function/index.js"
|
|
7
9
|
/** MySQL-specific JSON expression helpers. Portable JSON helpers are exported from the root package. */
|
|
8
10
|
export * as Json from "./mysql/json.js"
|
|
9
11
|
/** MySQL-specialized typed query execution contracts. */
|
package/src/postgres/executor.ts
CHANGED
|
@@ -26,6 +26,8 @@ export type Executor<Error = never, Context = never> = CoreExecutor.Executor<"po
|
|
|
26
26
|
/** Postgres-specialized renderer contract. */
|
|
27
27
|
export type Renderer = CoreRenderer.Renderer<"postgres">
|
|
28
28
|
export type ValueMappings = Expression.DriverValueMappingsFor<PostgresDatatypeKind, PostgresDatatypeFamily>
|
|
29
|
+
/** PostgreSQL EXPLAIN options. ANALYZE executes the read query. */
|
|
30
|
+
export type ExplainOptions = CoreExecutor.ExplainOptions
|
|
29
31
|
/** Optional renderer / driver overrides for the standard Postgres executor pipeline. */
|
|
30
32
|
export interface MakeOptions<Error = never, Context = never> {
|
|
31
33
|
readonly renderer?: Renderer
|
|
@@ -39,20 +41,37 @@ export type PostgresExecutorError = PostgresDriverError | RowDecodeError
|
|
|
39
41
|
export type PostgresQueryError<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
|
|
40
42
|
Exclude<CoreQuery.CapabilitiesOfPlan<PlanValue>, "read"> extends never ? PostgresReadQueryError : PostgresExecutorError
|
|
41
43
|
|
|
44
|
+
/** Pipeable execution cardinality helpers. */
|
|
45
|
+
export const atMostOne = CoreExecutor.atMostOne
|
|
46
|
+
export const exactlyOne = CoreExecutor.exactlyOne
|
|
47
|
+
export const nonEmpty = CoreExecutor.nonEmpty
|
|
48
|
+
|
|
42
49
|
/** Runs an effect within the ambient Postgres SQL transaction service. */
|
|
43
50
|
export const withTransaction = CoreExecutor.withTransaction
|
|
44
51
|
|
|
45
52
|
/** Postgres executor whose error channel narrows based on the query plan. */
|
|
46
|
-
export interface QueryExecutor<Context = never> {
|
|
53
|
+
export interface QueryExecutor<Context = never> extends CoreExecutor.Executor<"postgres", PostgresQueryError<any>, Context> {
|
|
47
54
|
readonly dialect: "postgres"
|
|
48
55
|
execute<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
|
|
49
56
|
plan: CoreQuery.DialectCompatiblePlan<PlanValue, "postgres">
|
|
50
57
|
): Effect.Effect<CoreQuery.ResultRows<PlanValue>, PostgresQueryError<PlanValue>, Context>
|
|
58
|
+
executeResult<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
|
|
59
|
+
plan: CoreQuery.DialectCompatiblePlan<PlanValue, "postgres">
|
|
60
|
+
): Effect.Effect<CoreExecutor.ExecutionResult<CoreQuery.ResultRow<PlanValue>>, PostgresQueryError<PlanValue>, Context>
|
|
61
|
+
prepare<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
|
|
62
|
+
plan: CoreQuery.DialectCompatiblePlan<PlanValue, "postgres">
|
|
63
|
+
): CoreExecutor.PreparedQuery<CoreQuery.ResultRow<PlanValue>, PostgresQueryError<PlanValue>, Context>
|
|
51
64
|
stream<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
|
|
52
65
|
plan: Exclude<CoreQuery.CapabilitiesOfPlan<PlanValue>, "read" | "locking"> extends never
|
|
53
66
|
? CoreQuery.DialectCompatiblePlan<PlanValue, "postgres">
|
|
54
67
|
: never
|
|
55
68
|
): Stream.Stream<CoreQuery.ResultRow<PlanValue>, PostgresQueryError<PlanValue>, Context>
|
|
69
|
+
explain<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
|
|
70
|
+
plan: Exclude<CoreQuery.CapabilitiesOfPlan<PlanValue>, "read" | "locking"> extends never
|
|
71
|
+
? CoreQuery.DialectCompatiblePlan<PlanValue, "postgres">
|
|
72
|
+
: never,
|
|
73
|
+
options?: ExplainOptions
|
|
74
|
+
): Effect.Effect<ReadonlyArray<FlatRow>, PostgresQueryError<PlanValue>, Context>
|
|
56
75
|
}
|
|
57
76
|
|
|
58
77
|
type DriverExecute<Error, Context> = <Row>(
|
|
@@ -61,6 +80,9 @@ type DriverExecute<Error, Context> = <Row>(
|
|
|
61
80
|
|
|
62
81
|
type DriverHandlers<Error, Context> = {
|
|
63
82
|
readonly execute: DriverExecute<Error, Context>
|
|
83
|
+
readonly executeResult?: <Row>(
|
|
84
|
+
query: CoreRenderer.RenderedQuery<Row, "postgres">
|
|
85
|
+
) => Effect.Effect<CoreExecutor.DriverResult, Error, Context>
|
|
64
86
|
readonly stream: <Row>(
|
|
65
87
|
query: CoreRenderer.RenderedQuery<Row, "postgres">
|
|
66
88
|
) => Stream.Stream<FlatRow, Error, Context>
|
|
@@ -114,57 +136,101 @@ const fromDriver = <
|
|
|
114
136
|
sqlDriver: Driver<Error, Context>,
|
|
115
137
|
driverMode: CoreExecutor.DriverMode = "raw",
|
|
116
138
|
valueMappings?: Expression.DriverValueMappings
|
|
117
|
-
): QueryExecutor<Context> =>
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
})
|
|
128
|
-
),
|
|
129
|
-
(error) => {
|
|
130
|
-
if (typeof error === "object" && error !== null && "_tag" in error && error._tag === "RowDecodeError") {
|
|
131
|
-
return error as RowDecodeError
|
|
132
|
-
}
|
|
133
|
-
const normalized = normalizePostgresDriverError(error, rendered)
|
|
134
|
-
return CoreExecutor.hasWriteCapability(plan)
|
|
135
|
-
? normalized
|
|
136
|
-
: narrowPostgresDriverErrorForReadQuery(normalized)
|
|
137
|
-
}
|
|
138
|
-
) as Effect.Effect<any, any, Context>
|
|
139
|
-
},
|
|
140
|
-
stream(plan) {
|
|
141
|
-
const rendered = renderer.render(plan)
|
|
142
|
-
return Stream.mapError(
|
|
143
|
-
Stream.mapArrayEffect(
|
|
144
|
-
sqlDriver.stream(rendered),
|
|
145
|
-
(rows) => Effect.try({
|
|
146
|
-
try: () => CoreExecutor.decodeRows(rendered, plan, rows, { driverMode, valueMappings }) as never,
|
|
147
|
-
catch: (error) => error as RowDecodeError
|
|
148
|
-
})
|
|
149
|
-
),
|
|
150
|
-
(error) => {
|
|
151
|
-
if (typeof error === "object" && error !== null && "_tag" in error && error._tag === "RowDecodeError") {
|
|
152
|
-
return error as RowDecodeError
|
|
153
|
-
}
|
|
154
|
-
const normalized = normalizePostgresDriverError(error, rendered)
|
|
155
|
-
return CoreExecutor.hasWriteCapability(plan)
|
|
156
|
-
? normalized
|
|
157
|
-
: narrowPostgresDriverErrorForReadQuery(normalized)
|
|
158
|
-
}
|
|
159
|
-
) as Stream.Stream<any, any, Context>
|
|
139
|
+
): QueryExecutor<Context> => {
|
|
140
|
+
const renderedCache = new WeakMap<object, CoreRenderer.RenderedQuery<any, "postgres">>()
|
|
141
|
+
const render = (plan: CoreQuery.Plan.Any) => {
|
|
142
|
+
const cached = renderedCache.get(plan)
|
|
143
|
+
if (cached !== undefined) {
|
|
144
|
+
return cached
|
|
145
|
+
}
|
|
146
|
+
const rendered = renderer.render(plan as any)
|
|
147
|
+
renderedCache.set(plan, rendered)
|
|
148
|
+
return rendered
|
|
160
149
|
}
|
|
161
|
-
|
|
150
|
+
const mapExecutionError = (
|
|
151
|
+
error: unknown,
|
|
152
|
+
rendered: CoreRenderer.RenderedQuery<any, "postgres">,
|
|
153
|
+
plan: CoreQuery.Plan.Any
|
|
154
|
+
) => {
|
|
155
|
+
if (typeof error === "object" && error !== null && "_tag" in error && error._tag === "RowDecodeError") {
|
|
156
|
+
return error as RowDecodeError
|
|
157
|
+
}
|
|
158
|
+
const normalized = normalizePostgresDriverError(error, rendered)
|
|
159
|
+
return CoreExecutor.hasWriteCapability(plan)
|
|
160
|
+
? normalized
|
|
161
|
+
: narrowPostgresDriverErrorForReadQuery(normalized)
|
|
162
|
+
}
|
|
163
|
+
return CoreExecutor.withResultContracts({
|
|
164
|
+
dialect: "postgres",
|
|
165
|
+
execute(plan) {
|
|
166
|
+
const rendered = render(plan)
|
|
167
|
+
return Effect.mapError(
|
|
168
|
+
Effect.flatMap(
|
|
169
|
+
sqlDriver.execute(rendered),
|
|
170
|
+
(rows) => Effect.try({
|
|
171
|
+
try: () => CoreExecutor.decodeRows(rendered, plan, rows, { driverMode, valueMappings }),
|
|
172
|
+
catch: (error) => error as RowDecodeError
|
|
173
|
+
})
|
|
174
|
+
),
|
|
175
|
+
(error) => mapExecutionError(error, rendered, plan)
|
|
176
|
+
) as Effect.Effect<any, any, Context>
|
|
177
|
+
},
|
|
178
|
+
executeResult(plan) {
|
|
179
|
+
const rendered = render(plan)
|
|
180
|
+
const result = sqlDriver.executeResult
|
|
181
|
+
? sqlDriver.executeResult(rendered)
|
|
182
|
+
: Effect.map(sqlDriver.execute(rendered), (rows) => ({ rows }))
|
|
183
|
+
return Effect.mapError(
|
|
184
|
+
Effect.flatMap(result, ({ rows, ...metadata }) => Effect.try({
|
|
185
|
+
try: () => ({
|
|
186
|
+
...metadata,
|
|
187
|
+
rows: CoreExecutor.decodeRows(rendered, plan, rows, { driverMode, valueMappings })
|
|
188
|
+
}),
|
|
189
|
+
catch: (error) => error as RowDecodeError
|
|
190
|
+
})),
|
|
191
|
+
(error) => mapExecutionError(error, rendered, plan)
|
|
192
|
+
) as Effect.Effect<any, any, Context>
|
|
193
|
+
},
|
|
194
|
+
stream(plan) {
|
|
195
|
+
const rendered = render(plan)
|
|
196
|
+
return Stream.mapError(
|
|
197
|
+
Stream.mapArrayEffect(
|
|
198
|
+
sqlDriver.stream(rendered),
|
|
199
|
+
(rows) => Effect.try({
|
|
200
|
+
try: () => CoreExecutor.decodeRows(rendered, plan, rows, { driverMode, valueMappings }) as never,
|
|
201
|
+
catch: (error) => error as RowDecodeError
|
|
202
|
+
})
|
|
203
|
+
),
|
|
204
|
+
(error) => mapExecutionError(error, rendered, plan)
|
|
205
|
+
) as Stream.Stream<any, any, Context>
|
|
206
|
+
},
|
|
207
|
+
explain(plan, options) {
|
|
208
|
+
const rendered = CoreExecutor.explainQuery(render(plan), options)
|
|
209
|
+
return Effect.mapError(
|
|
210
|
+
sqlDriver.execute(rendered),
|
|
211
|
+
(error) => mapExecutionError(error, rendered, plan)
|
|
212
|
+
) as Effect.Effect<any, any, Context>
|
|
213
|
+
}
|
|
214
|
+
}) as QueryExecutor<Context>
|
|
215
|
+
}
|
|
162
216
|
|
|
163
217
|
const sqlClientDriver = (): Driver<any, SqlClient.SqlClient> =>
|
|
164
218
|
driver({
|
|
165
219
|
execute: (query: CoreRenderer.RenderedQuery<any, "postgres">) =>
|
|
166
220
|
Effect.flatMap(SqlClient.SqlClient, (sql) =>
|
|
167
221
|
sql.unsafe<FlatRow>(query.sql, [...query.params])),
|
|
222
|
+
executeResult: (query: CoreRenderer.RenderedQuery<any, "postgres">) =>
|
|
223
|
+
Effect.flatMap(SqlClient.SqlClient, (sql) =>
|
|
224
|
+
Effect.map(sql.unsafe<FlatRow>(query.sql, [...query.params]).raw, (raw) => {
|
|
225
|
+
const result = raw as {
|
|
226
|
+
readonly rows?: ReadonlyArray<FlatRow>
|
|
227
|
+
readonly rowCount?: number | null
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
rows: result.rows ?? [],
|
|
231
|
+
...(typeof result.rowCount === "number" ? { affectedRows: result.rowCount } : {})
|
|
232
|
+
}
|
|
233
|
+
})),
|
|
168
234
|
stream: (query: CoreRenderer.RenderedQuery<any, "postgres">) =>
|
|
169
235
|
CoreExecutor.streamFromSqlClient(query)
|
|
170
236
|
})
|
|
@@ -1,2 +1,125 @@
|
|
|
1
|
-
|
|
1
|
+
import * as Numeric from "../../internal/dialect-numeric.js"
|
|
2
|
+
import * as Expression from "../../internal/scalar.js"
|
|
3
|
+
import { postgresDatatypes } from "../datatypes/index.js"
|
|
4
|
+
|
|
2
5
|
export { count, max, min } from "../internal/dsl.js"
|
|
6
|
+
|
|
7
|
+
type PgInt8 = ReturnType<typeof postgresDatatypes.int8>
|
|
8
|
+
type PgNumeric = ReturnType<typeof postgresDatatypes.numeric>
|
|
9
|
+
type PgFloat4 = ReturnType<typeof postgresDatatypes.float4>
|
|
10
|
+
type PgFloat8 = ReturnType<typeof postgresDatatypes.float8>
|
|
11
|
+
|
|
12
|
+
type IsAny<Value> = 0 extends (1 & Value) ? true : false
|
|
13
|
+
|
|
14
|
+
type BaseDb<Db extends Expression.DbType.Any> =
|
|
15
|
+
Db extends Expression.DbType.Domain<any, infer Base extends Expression.DbType.Any, any>
|
|
16
|
+
? BaseDb<Base>
|
|
17
|
+
: Db
|
|
18
|
+
|
|
19
|
+
type PgAggregateCategory<Db extends Expression.DbType.Any> =
|
|
20
|
+
BaseDb<Db> extends infer Base extends Expression.DbType.Any
|
|
21
|
+
? Base["dialect"] extends "standard"
|
|
22
|
+
? Base["kind"] extends "int" | "integer" ? "int4"
|
|
23
|
+
: Base["kind"] extends "bigint" ? "int8"
|
|
24
|
+
: Base["kind"] extends "numeric" | "decimal" ? "numeric"
|
|
25
|
+
: Base["kind"] extends "real" ? "float4"
|
|
26
|
+
: never
|
|
27
|
+
: Base["dialect"] extends "postgres"
|
|
28
|
+
? Base["kind"] extends "int2" | "int4" | "int8" | "numeric" | "float4" | "float8"
|
|
29
|
+
? Base["kind"]
|
|
30
|
+
: never
|
|
31
|
+
: never
|
|
32
|
+
: never
|
|
33
|
+
|
|
34
|
+
type AggregateInputError<
|
|
35
|
+
Operation extends "sum" | "avg",
|
|
36
|
+
Db extends Expression.DbType.Any
|
|
37
|
+
> = {
|
|
38
|
+
readonly __effect_qb_error__: "effect-qb: unsupported postgres aggregate input"
|
|
39
|
+
readonly __effect_qb_operation__: Operation
|
|
40
|
+
readonly __effect_qb_db_type__: Db
|
|
41
|
+
readonly __effect_qb_expected__: "smallint, integer, bigint, numeric, real, or double precision"
|
|
42
|
+
readonly __effect_qb_hint__: "Use Cast.to(...) with a supported Type or Pg.Type witness"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type PgAggregateConstraint<
|
|
46
|
+
Value extends Numeric.Input,
|
|
47
|
+
Operation extends "sum" | "avg"
|
|
48
|
+
> = IsAny<Value> extends true ? unknown
|
|
49
|
+
: Numeric.DialectConstraint<Value, PgFloat8, "postgres"> & (
|
|
50
|
+
PgAggregateCategory<Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">> extends never
|
|
51
|
+
? AggregateInputError<
|
|
52
|
+
Operation,
|
|
53
|
+
Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">
|
|
54
|
+
>
|
|
55
|
+
: unknown
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
type PgSumResultDb<Value extends Numeric.Input> =
|
|
59
|
+
PgAggregateCategory<Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">> extends "int2" | "int4"
|
|
60
|
+
? PgInt8
|
|
61
|
+
: PgAggregateCategory<Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">> extends "int8" | "numeric"
|
|
62
|
+
? PgNumeric
|
|
63
|
+
: PgAggregateCategory<Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">> extends "float4"
|
|
64
|
+
? PgFloat4
|
|
65
|
+
: PgFloat8
|
|
66
|
+
|
|
67
|
+
type PgAvgResultDb<Value extends Numeric.Input> =
|
|
68
|
+
PgAggregateCategory<Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">> extends "float4" | "float8"
|
|
69
|
+
? PgFloat8
|
|
70
|
+
: PgNumeric
|
|
71
|
+
|
|
72
|
+
const baseDb = (db: Expression.DbType.Any): Expression.DbType.Any =>
|
|
73
|
+
"base" in db ? baseDb(db.base) : db
|
|
74
|
+
|
|
75
|
+
const category = (
|
|
76
|
+
value: Numeric.Input
|
|
77
|
+
): "int2" | "int4" | "int8" | "numeric" | "float4" | "float8" => {
|
|
78
|
+
if (typeof value === "number") return "float8"
|
|
79
|
+
const db = baseDb(value[Expression.TypeId].dbType)
|
|
80
|
+
if (db.dialect === "standard") {
|
|
81
|
+
if (db.kind === "int" || db.kind === "integer") return "int4"
|
|
82
|
+
if (db.kind === "bigint") return "int8"
|
|
83
|
+
if (db.kind === "numeric" || db.kind === "decimal") return "numeric"
|
|
84
|
+
return "float4"
|
|
85
|
+
}
|
|
86
|
+
return db.kind as "int2" | "int4" | "int8" | "numeric" | "float4" | "float8"
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const sumResultDb = (value: Numeric.Input) => {
|
|
90
|
+
const valueCategory = category(value)
|
|
91
|
+
if (valueCategory === "int2" || valueCategory === "int4") {
|
|
92
|
+
return postgresDatatypes.int8()
|
|
93
|
+
}
|
|
94
|
+
if (valueCategory === "int8" || valueCategory === "numeric") {
|
|
95
|
+
return postgresDatatypes.numeric()
|
|
96
|
+
}
|
|
97
|
+
return valueCategory === "float4"
|
|
98
|
+
? postgresDatatypes.float4()
|
|
99
|
+
: postgresDatatypes.float8()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const avgResultDb = (value: Numeric.Input) => {
|
|
103
|
+
const valueCategory = category(value)
|
|
104
|
+
return valueCategory === "float4" || valueCategory === "float8"
|
|
105
|
+
? postgresDatatypes.float8()
|
|
106
|
+
: postgresDatatypes.numeric()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const sum = <Value extends Numeric.Input>(
|
|
110
|
+
value: Value & PgAggregateConstraint<NoInfer<Value>, "sum">
|
|
111
|
+
): Numeric.AggregateResult<"sum", Value, PgFloat8, PgSumResultDb<Value>, "postgres"> =>
|
|
112
|
+
(Numeric.aggregate as any)("sum", value, {
|
|
113
|
+
dialect: "postgres",
|
|
114
|
+
literalDb: postgresDatatypes.float8(),
|
|
115
|
+
resultDb: sumResultDb(value)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
export const avg = <Value extends Numeric.Input>(
|
|
119
|
+
value: Value & PgAggregateConstraint<NoInfer<Value>, "avg">
|
|
120
|
+
): Numeric.AggregateResult<"avg", Value, PgFloat8, PgAvgResultDb<Value>, "postgres"> =>
|
|
121
|
+
(Numeric.aggregate as any)("avg", value, {
|
|
122
|
+
dialect: "postgres",
|
|
123
|
+
literalDb: postgresDatatypes.float8(),
|
|
124
|
+
resultDb: avgResultDb(value)
|
|
125
|
+
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExpressionInput } from "../query.js"
|
|
2
2
|
import {
|
|
3
|
+
call,
|
|
3
4
|
cast,
|
|
4
5
|
literal,
|
|
5
6
|
nextVal as nextValInternal,
|
|
@@ -9,7 +10,7 @@ import {
|
|
|
9
10
|
import { isSequenceDefinition, type SequenceDefinition } from "../schema-management.js"
|
|
10
11
|
|
|
11
12
|
/** Postgres scalar core functions. */
|
|
12
|
-
export { uuidGenerateV4 }
|
|
13
|
+
export { call, uuidGenerateV4 }
|
|
13
14
|
|
|
14
15
|
const safeUnquotedIdentifier = /^[a-z_][a-z0-9_$]*$/
|
|
15
16
|
|
|
@@ -1,3 +1,21 @@
|
|
|
1
1
|
export * as core from "./core.js"
|
|
2
|
+
export * as string from "./string.js"
|
|
3
|
+
export * as aggregate from "./aggregate.js"
|
|
4
|
+
export * as numeric from "./numeric.js"
|
|
5
|
+
export * as window from "./window.js"
|
|
6
|
+
export * as temporal from "./temporal.js"
|
|
2
7
|
|
|
3
|
-
export { uuidGenerateV4, nextVal } from "./core.js"
|
|
8
|
+
export { call, uuidGenerateV4, nextVal } from "./core.js"
|
|
9
|
+
export { lower, upper, concat } from "./string.js"
|
|
10
|
+
export { avg, count, max, min, sum } from "./aggregate.js"
|
|
11
|
+
export { modulo, round } from "./numeric.js"
|
|
12
|
+
export { denseRank, firstValue, lastValue, over, rank, rowNumber } from "./window.js"
|
|
13
|
+
export type { WindowSpec } from "./window.js"
|
|
14
|
+
export {
|
|
15
|
+
currentDate,
|
|
16
|
+
currentTime,
|
|
17
|
+
currentTimestamp,
|
|
18
|
+
localTime,
|
|
19
|
+
localTimestamp,
|
|
20
|
+
now
|
|
21
|
+
} from "./temporal.js"
|