effect-qb 0.21.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 +1 -1
- 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
|
@@ -7,6 +7,8 @@ import * as QueryAst from "../query-ast.js"
|
|
|
7
7
|
import { renderDbTypeName, type RenderState, type RenderValueContext, type SqlDialect } from "../dialect.js"
|
|
8
8
|
import { renderPortableDatatypeCastType, renderPortableDatatypeDdlType } from "../datatypes/matrix.js"
|
|
9
9
|
import * as ExpressionAst from "../expression-ast.js"
|
|
10
|
+
import { renderCustomSql } from "../custom-sql-renderer.js"
|
|
11
|
+
import { renderWindowFrame } from "../window-renderer.js"
|
|
10
12
|
import * as JsonPath from "../json/path.js"
|
|
11
13
|
import { renderMysqlMutationLockMode, renderSelectLockMode } from "../dsl-plan-runtime.js"
|
|
12
14
|
import { expectConflictClause } from "../dsl-mutation-runtime.js"
|
|
@@ -1867,6 +1869,8 @@ export const renderExpression = (
|
|
|
1867
1869
|
return `cast(${renderExpression(expectValueExpression("cast", ast.value), state, dialect)} as ${renderCastType(dialect, ast.target)})`
|
|
1868
1870
|
case "function":
|
|
1869
1871
|
return renderFunctionCall(ast.name, ast.args, state, dialect)
|
|
1872
|
+
case "customSql":
|
|
1873
|
+
return renderCustomSql(ast, state, dialect, renderExpression)
|
|
1870
1874
|
case "eq":
|
|
1871
1875
|
return renderBinaryExpression("eq", "=", ast.left, ast.right, state, dialect)
|
|
1872
1876
|
case "neq":
|
|
@@ -1879,6 +1883,16 @@ export const renderExpression = (
|
|
|
1879
1883
|
return renderBinaryExpression("gt", ">", ast.left, ast.right, state, dialect)
|
|
1880
1884
|
case "gte":
|
|
1881
1885
|
return renderBinaryExpression("gte", ">=", ast.left, ast.right, state, dialect)
|
|
1886
|
+
case "add":
|
|
1887
|
+
return renderBinaryExpression("add", "+", ast.left, ast.right, state, dialect)
|
|
1888
|
+
case "subtract":
|
|
1889
|
+
return renderBinaryExpression("subtract", "-", ast.left, ast.right, state, dialect)
|
|
1890
|
+
case "multiply":
|
|
1891
|
+
return renderBinaryExpression("multiply", "*", ast.left, ast.right, state, dialect)
|
|
1892
|
+
case "divide":
|
|
1893
|
+
return renderBinaryExpression("divide", "/", ast.left, ast.right, state, dialect)
|
|
1894
|
+
case "modulo":
|
|
1895
|
+
return renderBinaryExpression("modulo", "%", ast.left, ast.right, state, dialect)
|
|
1882
1896
|
case "like":
|
|
1883
1897
|
return renderBinaryExpression("like", "like", ast.left, ast.right, state, dialect)
|
|
1884
1898
|
case "ilike": {
|
|
@@ -1983,6 +1997,16 @@ export const renderExpression = (
|
|
|
1983
1997
|
return `lower(${renderExpression(expectValueExpression("lower", ast.value), state, dialect)})`
|
|
1984
1998
|
case "count":
|
|
1985
1999
|
return `count(${renderExpression(expectValueExpression("count", ast.value), state, dialect)})`
|
|
2000
|
+
case "sum":
|
|
2001
|
+
return `sum(${renderExpression(expectValueExpression("sum", ast.value), state, dialect)})`
|
|
2002
|
+
case "avg":
|
|
2003
|
+
return `avg(${renderExpression(expectValueExpression("avg", ast.value), state, dialect)})`
|
|
2004
|
+
case "abs":
|
|
2005
|
+
return `abs(${renderExpression(expectValueExpression("abs", ast.value), state, dialect)})`
|
|
2006
|
+
case "round":
|
|
2007
|
+
return `round(${renderExpression(expectValueExpression("round", ast.value), state, dialect)})`
|
|
2008
|
+
case "negate":
|
|
2009
|
+
return `(-${renderExpression(expectValueExpression("negate", ast.value), state, dialect)})`
|
|
1986
2010
|
case "max":
|
|
1987
2011
|
return `max(${renderExpression(expectValueExpression("max", ast.value), state, dialect)})`
|
|
1988
2012
|
case "min":
|
|
@@ -2021,25 +2045,56 @@ export const renderExpression = (
|
|
|
2021
2045
|
readonly value: Expression.Any
|
|
2022
2046
|
readonly direction: string
|
|
2023
2047
|
}[]
|
|
2024
|
-
const
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2048
|
+
const renderSpecification = (): string => {
|
|
2049
|
+
const clauses: string[] = []
|
|
2050
|
+
if (partitionBy.length > 0) {
|
|
2051
|
+
clauses.push(`partition by ${partitionBy.map((value) => renderExpression(value, state, dialect)).join(", ")}`)
|
|
2052
|
+
}
|
|
2053
|
+
if (orderBy.length > 0) {
|
|
2054
|
+
clauses.push(`order by ${orderBy.map((entry) =>
|
|
2055
|
+
`${renderExpression(entry.value, state, dialect)} ${entry.direction}`
|
|
2056
|
+
).join(", ")}`)
|
|
2057
|
+
}
|
|
2058
|
+
if (ast.frame !== undefined) {
|
|
2059
|
+
clauses.push(renderWindowFrame(ast.frame))
|
|
2060
|
+
}
|
|
2061
|
+
return clauses.join(" ")
|
|
2032
2062
|
}
|
|
2033
|
-
const specification = clauses.join(" ")
|
|
2034
2063
|
switch (ast.function) {
|
|
2035
2064
|
case "rowNumber":
|
|
2036
|
-
return `row_number() over (${
|
|
2065
|
+
return `row_number() over (${renderSpecification()})`
|
|
2037
2066
|
case "rank":
|
|
2038
|
-
return `rank() over (${
|
|
2067
|
+
return `rank() over (${renderSpecification()})`
|
|
2039
2068
|
case "denseRank":
|
|
2040
|
-
return `dense_rank() over (${
|
|
2069
|
+
return `dense_rank() over (${renderSpecification()})`
|
|
2041
2070
|
case "over":
|
|
2042
|
-
return `${renderExpression(ast.value as Expression.Any, state, dialect)} over (${
|
|
2071
|
+
return `${renderExpression(ast.value as Expression.Any, state, dialect)} over (${renderSpecification()})`
|
|
2072
|
+
case "lag":
|
|
2073
|
+
case "lead": {
|
|
2074
|
+
const args = [renderExpression(ast.value as Expression.Any, state, dialect)]
|
|
2075
|
+
if (ast.offset !== undefined) {
|
|
2076
|
+
const offsetAst = (ast.offset as Expression.Any & {
|
|
2077
|
+
readonly [ExpressionAst.TypeId]: ExpressionAst.Any
|
|
2078
|
+
})[ExpressionAst.TypeId]
|
|
2079
|
+
if (
|
|
2080
|
+
offsetAst.kind !== "literal" ||
|
|
2081
|
+
typeof offsetAst.value !== "number" ||
|
|
2082
|
+
!Number.isSafeInteger(offsetAst.value) ||
|
|
2083
|
+
offsetAst.value < 0
|
|
2084
|
+
) {
|
|
2085
|
+
throw new Error(`${ast.function} offset must be a non-negative integer literal in MySQL`)
|
|
2086
|
+
}
|
|
2087
|
+
args.push(String(offsetAst.value))
|
|
2088
|
+
}
|
|
2089
|
+
if (ast.defaultValue !== undefined) {
|
|
2090
|
+
args.push(renderExpression(ast.defaultValue, state, dialect))
|
|
2091
|
+
}
|
|
2092
|
+
return `${ast.function}(${args.join(", ")}) over (${renderSpecification()})`
|
|
2093
|
+
}
|
|
2094
|
+
case "firstValue":
|
|
2095
|
+
return `first_value(${renderExpression(ast.value as Expression.Any, state, dialect)}) over (${renderSpecification()})`
|
|
2096
|
+
case "lastValue":
|
|
2097
|
+
return `last_value(${renderExpression(ast.value as Expression.Any, state, dialect)}) over (${renderSpecification()})`
|
|
2043
2098
|
}
|
|
2044
2099
|
break
|
|
2045
2100
|
}
|
|
@@ -7,6 +7,8 @@ import * as QueryAst from "../query-ast.js"
|
|
|
7
7
|
import { renderDbTypeName, type RenderState, type RenderValueContext, type SqlDialect } from "../dialect.js"
|
|
8
8
|
import { renderPortableDatatypeCastType, renderPortableDatatypeDdlType } from "../datatypes/matrix.js"
|
|
9
9
|
import * as ExpressionAst from "../expression-ast.js"
|
|
10
|
+
import { renderCustomSql } from "../custom-sql-renderer.js"
|
|
11
|
+
import { renderWindowFrame } from "../window-renderer.js"
|
|
10
12
|
import * as JsonPath from "../json/path.js"
|
|
11
13
|
import { renderSelectLockMode } from "../dsl-plan-runtime.js"
|
|
12
14
|
import { expectConflictClause } from "../dsl-mutation-runtime.js"
|
|
@@ -458,6 +460,32 @@ const renderBinaryExpression = (
|
|
|
458
460
|
return `(${renderExpression(leftExpression, state, dialect)} ${operator} ${renderExpression(rightExpression, state, dialect)})`
|
|
459
461
|
}
|
|
460
462
|
|
|
463
|
+
const renderNumericExpression = (
|
|
464
|
+
expression: Expression.Any,
|
|
465
|
+
state: RenderState,
|
|
466
|
+
dialect: SqlDialect
|
|
467
|
+
): string => {
|
|
468
|
+
const rendered = renderExpression(expression, state, dialect)
|
|
469
|
+
const ast = (expression as Expression.Any & {
|
|
470
|
+
readonly [ExpressionAst.TypeId]: ExpressionAst.Any
|
|
471
|
+
})[ExpressionAst.TypeId]
|
|
472
|
+
return dialect.name === "postgres" && ast.kind === "literal"
|
|
473
|
+
? `cast(${rendered} as ${renderCastType(dialect, expression[Expression.TypeId].dbType)})`
|
|
474
|
+
: rendered
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const renderNumericBinaryExpression = (
|
|
478
|
+
functionName: string,
|
|
479
|
+
operator: string,
|
|
480
|
+
left: unknown,
|
|
481
|
+
right: unknown,
|
|
482
|
+
state: RenderState,
|
|
483
|
+
dialect: SqlDialect
|
|
484
|
+
): string => {
|
|
485
|
+
const [leftExpression, rightExpression] = expectBinaryExpressions(functionName, left, right)
|
|
486
|
+
return `(${renderNumericExpression(leftExpression, state, dialect)} ${operator} ${renderNumericExpression(rightExpression, state, dialect)})`
|
|
487
|
+
}
|
|
488
|
+
|
|
461
489
|
const postgresRangeSubtypeByKind: Readonly<Record<string, string>> = {
|
|
462
490
|
int4range: "int4",
|
|
463
491
|
int8range: "int8",
|
|
@@ -1873,6 +1901,8 @@ export const renderExpression = (
|
|
|
1873
1901
|
return `(${renderExpression(expectValueExpression("collate", ast.value), state, dialect)} collate ${renderCollation(ast.collation)})`
|
|
1874
1902
|
case "function":
|
|
1875
1903
|
return renderFunctionCall(ast.name, ast.args, state, dialect)
|
|
1904
|
+
case "customSql":
|
|
1905
|
+
return renderCustomSql(ast, state, dialect, renderExpression)
|
|
1876
1906
|
case "eq":
|
|
1877
1907
|
return renderBinaryExpression("eq", "=", ast.left, ast.right, state, dialect)
|
|
1878
1908
|
case "neq":
|
|
@@ -1885,6 +1915,16 @@ export const renderExpression = (
|
|
|
1885
1915
|
return renderBinaryExpression("gt", ">", ast.left, ast.right, state, dialect)
|
|
1886
1916
|
case "gte":
|
|
1887
1917
|
return renderBinaryExpression("gte", ">=", ast.left, ast.right, state, dialect)
|
|
1918
|
+
case "add":
|
|
1919
|
+
return renderNumericBinaryExpression("add", "+", ast.left, ast.right, state, dialect)
|
|
1920
|
+
case "subtract":
|
|
1921
|
+
return renderNumericBinaryExpression("subtract", "-", ast.left, ast.right, state, dialect)
|
|
1922
|
+
case "multiply":
|
|
1923
|
+
return renderNumericBinaryExpression("multiply", "*", ast.left, ast.right, state, dialect)
|
|
1924
|
+
case "divide":
|
|
1925
|
+
return renderNumericBinaryExpression("divide", "/", ast.left, ast.right, state, dialect)
|
|
1926
|
+
case "modulo":
|
|
1927
|
+
return renderNumericBinaryExpression("modulo", "%", ast.left, ast.right, state, dialect)
|
|
1888
1928
|
case "like":
|
|
1889
1929
|
return renderBinaryExpression("like", "like", ast.left, ast.right, state, dialect)
|
|
1890
1930
|
case "ilike": {
|
|
@@ -2004,6 +2044,16 @@ export const renderExpression = (
|
|
|
2004
2044
|
return `lower(${renderExpression(expectValueExpression("lower", ast.value), state, dialect)})`
|
|
2005
2045
|
case "count":
|
|
2006
2046
|
return `count(${renderExpression(expectValueExpression("count", ast.value), state, dialect)})`
|
|
2047
|
+
case "sum":
|
|
2048
|
+
return `sum(${renderNumericExpression(expectValueExpression("sum", ast.value), state, dialect)})`
|
|
2049
|
+
case "avg":
|
|
2050
|
+
return `avg(${renderNumericExpression(expectValueExpression("avg", ast.value), state, dialect)})`
|
|
2051
|
+
case "abs":
|
|
2052
|
+
return `abs(${renderNumericExpression(expectValueExpression("abs", ast.value), state, dialect)})`
|
|
2053
|
+
case "round":
|
|
2054
|
+
return `round(${renderNumericExpression(expectValueExpression("round", ast.value), state, dialect)})`
|
|
2055
|
+
case "negate":
|
|
2056
|
+
return `(-${renderNumericExpression(expectValueExpression("negate", ast.value), state, dialect)})`
|
|
2007
2057
|
case "max":
|
|
2008
2058
|
return `max(${renderExpression(expectValueExpression("max", ast.value), state, dialect)})`
|
|
2009
2059
|
case "min":
|
|
@@ -2054,25 +2104,45 @@ export const renderExpression = (
|
|
|
2054
2104
|
readonly value: Expression.Any
|
|
2055
2105
|
readonly direction: string
|
|
2056
2106
|
}[]
|
|
2057
|
-
const
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2107
|
+
const renderSpecification = (): string => {
|
|
2108
|
+
const clauses: string[] = []
|
|
2109
|
+
if (partitionBy.length > 0) {
|
|
2110
|
+
clauses.push(`partition by ${partitionBy.map((value) => renderExpression(value, state, dialect)).join(", ")}`)
|
|
2111
|
+
}
|
|
2112
|
+
if (orderBy.length > 0) {
|
|
2113
|
+
clauses.push(`order by ${orderBy.map((entry) =>
|
|
2114
|
+
`${renderExpression(entry.value, state, dialect)} ${entry.direction}`
|
|
2115
|
+
).join(", ")}`)
|
|
2116
|
+
}
|
|
2117
|
+
if (ast.frame !== undefined) {
|
|
2118
|
+
clauses.push(renderWindowFrame(ast.frame))
|
|
2119
|
+
}
|
|
2120
|
+
return clauses.join(" ")
|
|
2065
2121
|
}
|
|
2066
|
-
const specification = clauses.join(" ")
|
|
2067
2122
|
switch (ast.function) {
|
|
2068
2123
|
case "rowNumber":
|
|
2069
|
-
return `row_number() over (${
|
|
2124
|
+
return `row_number() over (${renderSpecification()})`
|
|
2070
2125
|
case "rank":
|
|
2071
|
-
return `rank() over (${
|
|
2126
|
+
return `rank() over (${renderSpecification()})`
|
|
2072
2127
|
case "denseRank":
|
|
2073
|
-
return `dense_rank() over (${
|
|
2128
|
+
return `dense_rank() over (${renderSpecification()})`
|
|
2074
2129
|
case "over":
|
|
2075
|
-
return `${renderExpression(ast.value as Expression.Any, state, dialect)} over (${
|
|
2130
|
+
return `${renderExpression(ast.value as Expression.Any, state, dialect)} over (${renderSpecification()})`
|
|
2131
|
+
case "lag":
|
|
2132
|
+
case "lead": {
|
|
2133
|
+
const args = [renderExpression(ast.value as Expression.Any, state, dialect)]
|
|
2134
|
+
if (ast.offset !== undefined) {
|
|
2135
|
+
args.push(renderExpression(ast.offset, state, dialect))
|
|
2136
|
+
}
|
|
2137
|
+
if (ast.defaultValue !== undefined) {
|
|
2138
|
+
args.push(renderExpression(ast.defaultValue, state, dialect))
|
|
2139
|
+
}
|
|
2140
|
+
return `${ast.function}(${args.join(", ")}) over (${renderSpecification()})`
|
|
2141
|
+
}
|
|
2142
|
+
case "firstValue":
|
|
2143
|
+
return `first_value(${renderExpression(ast.value as Expression.Any, state, dialect)}) over (${renderSpecification()})`
|
|
2144
|
+
case "lastValue":
|
|
2145
|
+
return `last_value(${renderExpression(ast.value as Expression.Any, state, dialect)}) over (${renderSpecification()})`
|
|
2076
2146
|
}
|
|
2077
2147
|
break
|
|
2078
2148
|
}
|
|
@@ -7,6 +7,8 @@ import * as QueryAst from "../query-ast.js"
|
|
|
7
7
|
import { renderDbTypeName, type RenderState, type RenderValueContext, type SqlDialect } from "../dialect.js"
|
|
8
8
|
import { renderPortableDatatypeCastType, renderPortableDatatypeDdlType } from "../datatypes/matrix.js"
|
|
9
9
|
import * as ExpressionAst from "../expression-ast.js"
|
|
10
|
+
import { renderCustomSql } from "../custom-sql-renderer.js"
|
|
11
|
+
import { renderWindowFrame } from "../window-renderer.js"
|
|
10
12
|
import * as JsonPath from "../json/path.js"
|
|
11
13
|
import { expectConflictClause } from "../dsl-mutation-runtime.js"
|
|
12
14
|
import { expectDdlClauseKind, normalizeStatementFlag, normalizeStatementIdentifier } from "../dsl-transaction-ddl-runtime.js"
|
|
@@ -1795,6 +1797,8 @@ export const renderExpression = (
|
|
|
1795
1797
|
return `cast(${renderExpression(expectValueExpression("cast", ast.value), state, dialect)} as ${renderCastType(dialect, ast.target)})`
|
|
1796
1798
|
case "function":
|
|
1797
1799
|
return renderFunctionCall(ast.name, ast.args, state, dialect)
|
|
1800
|
+
case "customSql":
|
|
1801
|
+
return renderCustomSql(ast, state, dialect, renderExpression)
|
|
1798
1802
|
case "eq":
|
|
1799
1803
|
return renderBinaryExpression("eq", "=", ast.left, ast.right, state, dialect)
|
|
1800
1804
|
case "neq":
|
|
@@ -1807,6 +1811,16 @@ export const renderExpression = (
|
|
|
1807
1811
|
return renderBinaryExpression("gt", ">", ast.left, ast.right, state, dialect)
|
|
1808
1812
|
case "gte":
|
|
1809
1813
|
return renderBinaryExpression("gte", ">=", ast.left, ast.right, state, dialect)
|
|
1814
|
+
case "add":
|
|
1815
|
+
return renderBinaryExpression("add", "+", ast.left, ast.right, state, dialect)
|
|
1816
|
+
case "subtract":
|
|
1817
|
+
return renderBinaryExpression("subtract", "-", ast.left, ast.right, state, dialect)
|
|
1818
|
+
case "multiply":
|
|
1819
|
+
return renderBinaryExpression("multiply", "*", ast.left, ast.right, state, dialect)
|
|
1820
|
+
case "divide":
|
|
1821
|
+
return renderBinaryExpression("divide", "/", ast.left, ast.right, state, dialect)
|
|
1822
|
+
case "modulo":
|
|
1823
|
+
return renderBinaryExpression("modulo", "%", ast.left, ast.right, state, dialect)
|
|
1810
1824
|
case "like":
|
|
1811
1825
|
return renderBinaryExpression("like", "like", ast.left, ast.right, state, dialect)
|
|
1812
1826
|
case "ilike": {
|
|
@@ -1902,6 +1916,16 @@ export const renderExpression = (
|
|
|
1902
1916
|
return `lower(${renderExpression(expectValueExpression("lower", ast.value), state, dialect)})`
|
|
1903
1917
|
case "count":
|
|
1904
1918
|
return `count(${renderExpression(expectValueExpression("count", ast.value), state, dialect)})`
|
|
1919
|
+
case "sum":
|
|
1920
|
+
return `sum(${renderExpression(expectValueExpression("sum", ast.value), state, dialect)})`
|
|
1921
|
+
case "avg":
|
|
1922
|
+
return `avg(${renderExpression(expectValueExpression("avg", ast.value), state, dialect)})`
|
|
1923
|
+
case "abs":
|
|
1924
|
+
return `abs(${renderExpression(expectValueExpression("abs", ast.value), state, dialect)})`
|
|
1925
|
+
case "round":
|
|
1926
|
+
return `round(${renderExpression(expectValueExpression("round", ast.value), state, dialect)})`
|
|
1927
|
+
case "negate":
|
|
1928
|
+
return `(-${renderExpression(expectValueExpression("negate", ast.value), state, dialect)})`
|
|
1905
1929
|
case "max":
|
|
1906
1930
|
return `max(${renderExpression(expectValueExpression("max", ast.value), state, dialect)})`
|
|
1907
1931
|
case "min":
|
|
@@ -1952,25 +1976,45 @@ export const renderExpression = (
|
|
|
1952
1976
|
readonly value: Expression.Any
|
|
1953
1977
|
readonly direction: string
|
|
1954
1978
|
}[]
|
|
1955
|
-
const
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1979
|
+
const renderSpecification = (): string => {
|
|
1980
|
+
const clauses: string[] = []
|
|
1981
|
+
if (partitionBy.length > 0) {
|
|
1982
|
+
clauses.push(`partition by ${partitionBy.map((value) => renderExpression(value, state, dialect)).join(", ")}`)
|
|
1983
|
+
}
|
|
1984
|
+
if (orderBy.length > 0) {
|
|
1985
|
+
clauses.push(`order by ${orderBy.map((entry) =>
|
|
1986
|
+
`${renderExpression(entry.value, state, dialect)} ${entry.direction}`
|
|
1987
|
+
).join(", ")}`)
|
|
1988
|
+
}
|
|
1989
|
+
if (ast.frame !== undefined) {
|
|
1990
|
+
clauses.push(renderWindowFrame(ast.frame))
|
|
1991
|
+
}
|
|
1992
|
+
return clauses.join(" ")
|
|
1963
1993
|
}
|
|
1964
|
-
const specification = clauses.join(" ")
|
|
1965
1994
|
switch (ast.function) {
|
|
1966
1995
|
case "rowNumber":
|
|
1967
|
-
return `row_number() over (${
|
|
1996
|
+
return `row_number() over (${renderSpecification()})`
|
|
1968
1997
|
case "rank":
|
|
1969
|
-
return `rank() over (${
|
|
1998
|
+
return `rank() over (${renderSpecification()})`
|
|
1970
1999
|
case "denseRank":
|
|
1971
|
-
return `dense_rank() over (${
|
|
2000
|
+
return `dense_rank() over (${renderSpecification()})`
|
|
1972
2001
|
case "over":
|
|
1973
|
-
return `${renderExpression(ast.value as Expression.Any, state, dialect)} over (${
|
|
2002
|
+
return `${renderExpression(ast.value as Expression.Any, state, dialect)} over (${renderSpecification()})`
|
|
2003
|
+
case "lag":
|
|
2004
|
+
case "lead": {
|
|
2005
|
+
const args = [renderExpression(ast.value as Expression.Any, state, dialect)]
|
|
2006
|
+
if (ast.offset !== undefined) {
|
|
2007
|
+
args.push(renderExpression(ast.offset, state, dialect))
|
|
2008
|
+
}
|
|
2009
|
+
if (ast.defaultValue !== undefined) {
|
|
2010
|
+
args.push(renderExpression(ast.defaultValue, state, dialect))
|
|
2011
|
+
}
|
|
2012
|
+
return `${ast.function}(${args.join(", ")}) over (${renderSpecification()})`
|
|
2013
|
+
}
|
|
2014
|
+
case "firstValue":
|
|
2015
|
+
return `first_value(${renderExpression(ast.value as Expression.Any, state, dialect)}) over (${renderSpecification()})`
|
|
2016
|
+
case "lastValue":
|
|
2017
|
+
return `last_value(${renderExpression(ast.value as Expression.Any, state, dialect)}) over (${renderSpecification()})`
|
|
1974
2018
|
}
|
|
1975
2019
|
break
|
|
1976
2020
|
}
|
|
@@ -0,0 +1,131 @@
|
|
|
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
|
+
mergeManyDependencies,
|
|
9
|
+
mergeNullabilityManyRuntime,
|
|
10
|
+
type MergeNullabilityTuple,
|
|
11
|
+
type PredicateInput,
|
|
12
|
+
type TupleDependencies,
|
|
13
|
+
type TupleDialect
|
|
14
|
+
} from "./query.js"
|
|
15
|
+
|
|
16
|
+
type BooleanLiteral<Value extends boolean> = Expression.Scalar<
|
|
17
|
+
Value,
|
|
18
|
+
ReturnType<typeof standardDatatypes.boolean>,
|
|
19
|
+
"never",
|
|
20
|
+
"standard",
|
|
21
|
+
"scalar",
|
|
22
|
+
never
|
|
23
|
+
> & {
|
|
24
|
+
readonly [ExpressionAst.TypeId]: ExpressionAst.LiteralNode<Value>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type AsPredicate<Value extends PredicateInput> =
|
|
28
|
+
Value extends Expression.Any ? Value : BooleanLiteral<Extract<Value, boolean>>
|
|
29
|
+
|
|
30
|
+
type PredicateTuple<Values extends readonly PredicateInput[]> = {
|
|
31
|
+
readonly [Key in keyof Values]: AsPredicate<Values[Key]>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type CombinedPredicate<
|
|
35
|
+
Kind extends "and" | "or",
|
|
36
|
+
Values extends readonly PredicateInput[]
|
|
37
|
+
> = Values extends readonly [] ? BooleanLiteral<Kind extends "and" ? true : false> : Expression.Scalar<
|
|
38
|
+
boolean,
|
|
39
|
+
ReturnType<typeof standardDatatypes.boolean>,
|
|
40
|
+
MergeNullabilityTuple<PredicateTuple<Values>>,
|
|
41
|
+
TupleDialect<PredicateTuple<Values>>,
|
|
42
|
+
"scalar",
|
|
43
|
+
TupleDependencies<PredicateTuple<Values>>
|
|
44
|
+
> & {
|
|
45
|
+
readonly [ExpressionAst.TypeId]: ExpressionAst.VariadicNode<Kind, PredicateTuple<Values>>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const booleanLiteral = <const Value extends boolean>(
|
|
49
|
+
value: Value
|
|
50
|
+
): BooleanLiteral<Value> => makeExpression({
|
|
51
|
+
runtime: value,
|
|
52
|
+
dbType: standardDatatypes.boolean(),
|
|
53
|
+
runtimeSchema: Schema.Boolean,
|
|
54
|
+
nullability: "never",
|
|
55
|
+
dialect: "standard",
|
|
56
|
+
kind: "scalar",
|
|
57
|
+
dependencies: {}
|
|
58
|
+
}, {
|
|
59
|
+
kind: "literal",
|
|
60
|
+
value
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const combine = <
|
|
64
|
+
Kind extends "and" | "or",
|
|
65
|
+
const Values extends readonly PredicateInput[]
|
|
66
|
+
>(
|
|
67
|
+
kind: Kind,
|
|
68
|
+
values: Values,
|
|
69
|
+
identity: boolean
|
|
70
|
+
): CombinedPredicate<Kind, Values> => {
|
|
71
|
+
if (values.length === 0) {
|
|
72
|
+
return booleanLiteral(identity) as unknown as CombinedPredicate<Kind, Values>
|
|
73
|
+
}
|
|
74
|
+
const expressions = values.map((value) =>
|
|
75
|
+
typeof value === "boolean" ? booleanLiteral(value) : value) as PredicateTuple<Values>
|
|
76
|
+
return makeExpression({
|
|
77
|
+
runtime: identity,
|
|
78
|
+
dbType: standardDatatypes.boolean(),
|
|
79
|
+
runtimeSchema: Schema.Boolean,
|
|
80
|
+
nullability: mergeNullabilityManyRuntime(expressions),
|
|
81
|
+
dialect: expressions.find((value) => value[Expression.TypeId].dialect !== "standard")?.[Expression.TypeId].dialect ??
|
|
82
|
+
expressions[0]![Expression.TypeId].dialect,
|
|
83
|
+
kind: "scalar",
|
|
84
|
+
dependencies: mergeManyDependencies(expressions)
|
|
85
|
+
}, {
|
|
86
|
+
kind,
|
|
87
|
+
values: expressions
|
|
88
|
+
}) as CombinedPredicate<Kind, Values>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Combines a runtime list of predicates; an empty list is SQL true. */
|
|
92
|
+
export const andAll = <
|
|
93
|
+
const Values extends readonly PredicateInput[]
|
|
94
|
+
>(values: Values): CombinedPredicate<"and", Values> =>
|
|
95
|
+
combine("and", values, true)
|
|
96
|
+
|
|
97
|
+
/** Combines a runtime list of predicates; an empty list is SQL false. */
|
|
98
|
+
export const orAll = <
|
|
99
|
+
const Values extends readonly PredicateInput[]
|
|
100
|
+
>(values: Values): CombinedPredicate<"or", Values> =>
|
|
101
|
+
combine("or", values, false)
|
|
102
|
+
|
|
103
|
+
/** Applies a query modifier only when the condition is true. */
|
|
104
|
+
export const when = <
|
|
105
|
+
const Condition extends boolean,
|
|
106
|
+
Modifier extends (value: any) => any
|
|
107
|
+
>(
|
|
108
|
+
condition: Condition,
|
|
109
|
+
modifier: Modifier
|
|
110
|
+
) => <Value>(
|
|
111
|
+
value: Value
|
|
112
|
+
): Condition extends true ? ReturnType<Modifier> : Condition extends false ? Value : Value | ReturnType<Modifier> =>
|
|
113
|
+
(condition ? modifier(value) : value) as never
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Returns a selection fragment suitable for object spread.
|
|
117
|
+
*
|
|
118
|
+
* Dynamic conditions correctly make the selected fields optional.
|
|
119
|
+
*/
|
|
120
|
+
export const includeIf = <
|
|
121
|
+
const Condition extends boolean,
|
|
122
|
+
const Selection extends Readonly<Record<string, unknown>>
|
|
123
|
+
>(
|
|
124
|
+
condition: Condition,
|
|
125
|
+
selection: Selection
|
|
126
|
+
): Condition extends true
|
|
127
|
+
? Selection
|
|
128
|
+
: Condition extends false
|
|
129
|
+
? {}
|
|
130
|
+
: Partial<Selection> =>
|
|
131
|
+
(condition ? selection : {}) as never
|