@simplysm/orm-common 13.0.100 → 14.0.1
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/create-db-context.d.ts +10 -10
- package/dist/create-db-context.js +312 -276
- package/dist/create-db-context.js.map +1 -6
- package/dist/ddl/column-ddl.d.ts +4 -4
- package/dist/ddl/column-ddl.js +41 -35
- package/dist/ddl/column-ddl.js.map +1 -6
- package/dist/ddl/initialize.d.ts +17 -17
- package/dist/ddl/initialize.js +200 -142
- package/dist/ddl/initialize.js.map +1 -6
- package/dist/ddl/relation-ddl.d.ts +6 -6
- package/dist/ddl/relation-ddl.js +55 -48
- package/dist/ddl/relation-ddl.js.map +1 -6
- package/dist/ddl/schema-ddl.d.ts +4 -4
- package/dist/ddl/schema-ddl.js +21 -15
- package/dist/ddl/schema-ddl.js.map +1 -6
- package/dist/ddl/table-ddl.d.ts +20 -20
- package/dist/ddl/table-ddl.js +139 -93
- package/dist/ddl/table-ddl.js.map +1 -6
- package/dist/define-db-context.js +10 -13
- package/dist/define-db-context.js.map +1 -6
- package/dist/errors/db-transaction-error.d.ts +15 -15
- package/dist/errors/db-transaction-error.d.ts.map +1 -1
- package/dist/errors/db-transaction-error.js +53 -19
- package/dist/errors/db-transaction-error.js.map +1 -6
- package/dist/exec/executable.d.ts +23 -23
- package/dist/exec/executable.js +94 -40
- package/dist/exec/executable.js.map +1 -6
- package/dist/exec/queryable.d.ts +97 -97
- package/dist/exec/queryable.js +1310 -1204
- package/dist/exec/queryable.js.map +1 -6
- package/dist/exec/search-parser.d.ts +31 -31
- package/dist/exec/search-parser.d.ts.map +1 -1
- package/dist/exec/search-parser.js +158 -59
- package/dist/exec/search-parser.js.map +1 -6
- package/dist/expr/expr-unit.d.ts +4 -4
- package/dist/expr/expr-unit.js +24 -18
- package/dist/expr/expr-unit.js.map +1 -6
- package/dist/expr/expr.d.ts +6 -6
- package/dist/expr/expr.js +1872 -1844
- package/dist/expr/expr.js.map +1 -6
- package/dist/index.js +23 -1
- package/dist/index.js.map +1 -6
- package/dist/models/system-migration.js +7 -7
- package/dist/models/system-migration.js.map +1 -6
- package/dist/query-builder/base/expr-renderer-base.d.ts +10 -10
- package/dist/query-builder/base/expr-renderer-base.js +27 -21
- package/dist/query-builder/base/expr-renderer-base.js.map +1 -6
- package/dist/query-builder/base/query-builder-base.d.ts +21 -21
- package/dist/query-builder/base/query-builder-base.d.ts.map +1 -1
- package/dist/query-builder/base/query-builder-base.js +90 -80
- package/dist/query-builder/base/query-builder-base.js.map +1 -6
- package/dist/query-builder/mssql/mssql-expr-renderer.d.ts +4 -4
- package/dist/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -1
- package/dist/query-builder/mssql/mssql-expr-renderer.js +447 -420
- package/dist/query-builder/mssql/mssql-expr-renderer.js.map +1 -6
- package/dist/query-builder/mssql/mssql-query-builder.js +483 -443
- package/dist/query-builder/mssql/mssql-query-builder.js.map +1 -6
- package/dist/query-builder/mysql/mysql-expr-renderer.d.ts +4 -4
- package/dist/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -1
- package/dist/query-builder/mysql/mysql-expr-renderer.js +451 -419
- package/dist/query-builder/mysql/mysql-expr-renderer.js.map +1 -6
- package/dist/query-builder/mysql/mysql-query-builder.js +570 -479
- package/dist/query-builder/mysql/mysql-query-builder.js.map +1 -6
- package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts +4 -4
- package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -1
- package/dist/query-builder/postgresql/postgresql-expr-renderer.js +449 -422
- package/dist/query-builder/postgresql/postgresql-expr-renderer.js.map +1 -6
- package/dist/query-builder/postgresql/postgresql-query-builder.js +511 -460
- package/dist/query-builder/postgresql/postgresql-query-builder.js.map +1 -6
- package/dist/query-builder/query-builder.d.ts +1 -1
- package/dist/query-builder/query-builder.js +13 -13
- package/dist/query-builder/query-builder.js.map +1 -6
- package/dist/schema/factory/column-builder.d.ts +84 -84
- package/dist/schema/factory/column-builder.js +248 -185
- package/dist/schema/factory/column-builder.js.map +1 -6
- package/dist/schema/factory/index-builder.d.ts +38 -38
- package/dist/schema/factory/index-builder.js +144 -85
- package/dist/schema/factory/index-builder.js.map +1 -6
- package/dist/schema/factory/relation-builder.d.ts +91 -91
- package/dist/schema/factory/relation-builder.d.ts.map +1 -1
- package/dist/schema/factory/relation-builder.js +274 -136
- package/dist/schema/factory/relation-builder.js.map +1 -6
- package/dist/schema/procedure-builder.d.ts +51 -51
- package/dist/schema/procedure-builder.d.ts.map +1 -1
- package/dist/schema/procedure-builder.js +205 -131
- package/dist/schema/procedure-builder.js.map +1 -6
- package/dist/schema/table-builder.d.ts +55 -55
- package/dist/schema/table-builder.d.ts.map +1 -1
- package/dist/schema/table-builder.js +274 -205
- package/dist/schema/table-builder.js.map +1 -6
- package/dist/schema/view-builder.d.ts +44 -44
- package/dist/schema/view-builder.d.ts.map +1 -1
- package/dist/schema/view-builder.js +189 -116
- package/dist/schema/view-builder.js.map +1 -6
- package/dist/types/column.js +60 -30
- package/dist/types/column.js.map +1 -6
- package/dist/types/db-context-def.d.ts +9 -9
- package/dist/types/db-context-def.js +2 -1
- package/dist/types/db-context-def.js.map +1 -6
- package/dist/types/db.d.ts +47 -47
- package/dist/types/db.js +15 -5
- package/dist/types/db.js.map +1 -6
- package/dist/types/expr.d.ts +81 -81
- package/dist/types/expr.d.ts.map +1 -1
- package/dist/types/expr.js +3 -1
- package/dist/types/expr.js.map +1 -6
- package/dist/types/query-def.d.ts +46 -46
- package/dist/types/query-def.d.ts.map +1 -1
- package/dist/types/query-def.js +31 -24
- package/dist/types/query-def.js.map +1 -6
- package/dist/utils/result-parser.js +362 -221
- package/dist/utils/result-parser.js.map +1 -6
- package/package.json +5 -7
- package/src/create-db-context.ts +31 -31
- package/src/ddl/column-ddl.ts +4 -4
- package/src/ddl/initialize.ts +38 -38
- package/src/ddl/relation-ddl.ts +6 -6
- package/src/ddl/schema-ddl.ts +4 -4
- package/src/ddl/table-ddl.ts +24 -24
- package/src/errors/db-transaction-error.ts +13 -13
- package/src/exec/executable.ts +25 -25
- package/src/exec/queryable.ts +134 -134
- package/src/exec/search-parser.ts +50 -50
- package/src/expr/expr-unit.ts +4 -4
- package/src/expr/expr.ts +13 -13
- package/src/index.ts +8 -8
- package/src/models/system-migration.ts +1 -1
- package/src/query-builder/base/expr-renderer-base.ts +21 -21
- package/src/query-builder/base/query-builder-base.ts +33 -33
- package/src/query-builder/mssql/mssql-expr-renderer.ts +11 -11
- package/src/query-builder/mssql/mssql-query-builder.ts +11 -11
- package/src/query-builder/mysql/mysql-expr-renderer.ts +15 -15
- package/src/query-builder/mysql/mysql-query-builder.ts +3 -3
- package/src/query-builder/postgresql/postgresql-expr-renderer.ts +9 -9
- package/src/query-builder/postgresql/postgresql-query-builder.ts +7 -7
- package/src/query-builder/query-builder.ts +1 -1
- package/src/schema/factory/column-builder.ts +86 -86
- package/src/schema/factory/index-builder.ts +38 -38
- package/src/schema/factory/relation-builder.ts +93 -93
- package/src/schema/procedure-builder.ts +52 -52
- package/src/schema/table-builder.ts +56 -56
- package/src/schema/view-builder.ts +45 -45
- package/src/types/column.ts +1 -1
- package/src/types/db-context-def.ts +15 -15
- package/src/types/db.ts +50 -50
- package/src/types/expr.ts +103 -103
- package/src/types/query-def.ts +50 -50
- package/src/utils/result-parser.ts +39 -39
- package/README.md +0 -192
- package/docs/core.md +0 -234
- package/docs/expression.md +0 -234
- package/docs/query-builder.md +0 -93
- package/docs/queryable.md +0 -198
- package/docs/schema-builders.md +0 -463
- package/docs/types.md +0 -445
- package/docs/utilities.md +0 -27
- package/tests/db-context/create-db-context.spec.ts +0 -193
- package/tests/db-context/define-db-context.spec.ts +0 -17
- package/tests/ddl/basic.expected.ts +0 -341
- package/tests/ddl/basic.spec.ts +0 -557
- package/tests/ddl/column-builder.expected.ts +0 -310
- package/tests/ddl/column-builder.spec.ts +0 -525
- package/tests/ddl/index-builder.expected.ts +0 -38
- package/tests/ddl/index-builder.spec.ts +0 -148
- package/tests/ddl/procedure-builder.expected.ts +0 -52
- package/tests/ddl/procedure-builder.spec.ts +0 -128
- package/tests/ddl/relation-builder.expected.ts +0 -36
- package/tests/ddl/relation-builder.spec.ts +0 -171
- package/tests/ddl/table-builder.expected.ts +0 -113
- package/tests/ddl/table-builder.spec.ts +0 -399
- package/tests/ddl/view-builder.expected.ts +0 -38
- package/tests/ddl/view-builder.spec.ts +0 -116
- package/tests/dml/delete.expected.ts +0 -96
- package/tests/dml/delete.spec.ts +0 -127
- package/tests/dml/insert.expected.ts +0 -192
- package/tests/dml/insert.spec.ts +0 -210
- package/tests/dml/update.expected.ts +0 -176
- package/tests/dml/update.spec.ts +0 -222
- package/tests/dml/upsert.expected.ts +0 -215
- package/tests/dml/upsert.spec.ts +0 -190
- package/tests/errors/queryable-errors.spec.ts +0 -126
- package/tests/escape.spec.ts +0 -59
- package/tests/examples/pivot.expected.ts +0 -211
- package/tests/examples/pivot.spec.ts +0 -200
- package/tests/examples/sampling.expected.ts +0 -69
- package/tests/examples/sampling.spec.ts +0 -42
- package/tests/examples/unpivot.expected.ts +0 -120
- package/tests/examples/unpivot.spec.ts +0 -161
- package/tests/exec/search-parser.spec.ts +0 -267
- package/tests/executable/basic.expected.ts +0 -18
- package/tests/executable/basic.spec.ts +0 -54
- package/tests/expr/comparison.expected.ts +0 -282
- package/tests/expr/comparison.spec.ts +0 -334
- package/tests/expr/conditional.expected.ts +0 -134
- package/tests/expr/conditional.spec.ts +0 -249
- package/tests/expr/date.expected.ts +0 -332
- package/tests/expr/date.spec.ts +0 -459
- package/tests/expr/math.expected.ts +0 -62
- package/tests/expr/math.spec.ts +0 -59
- package/tests/expr/string.expected.ts +0 -218
- package/tests/expr/string.spec.ts +0 -300
- package/tests/expr/utility.expected.ts +0 -147
- package/tests/expr/utility.spec.ts +0 -155
- package/tests/select/basic.expected.ts +0 -322
- package/tests/select/basic.spec.ts +0 -433
- package/tests/select/filter.expected.ts +0 -357
- package/tests/select/filter.spec.ts +0 -954
- package/tests/select/group.expected.ts +0 -169
- package/tests/select/group.spec.ts +0 -159
- package/tests/select/join.expected.ts +0 -582
- package/tests/select/join.spec.ts +0 -692
- package/tests/select/order.expected.ts +0 -150
- package/tests/select/order.spec.ts +0 -140
- package/tests/select/recursive-cte.expected.ts +0 -244
- package/tests/select/recursive-cte.spec.ts +0 -514
- package/tests/select/result-meta.spec.ts +0 -270
- package/tests/select/subquery.expected.ts +0 -363
- package/tests/select/subquery.spec.ts +0 -441
- package/tests/select/view.expected.ts +0 -155
- package/tests/select/view.spec.ts +0 -235
- package/tests/select/window.expected.ts +0 -345
- package/tests/select/window.spec.ts +0 -433
- package/tests/setup/MockExecutor.ts +0 -18
- package/tests/setup/TestDbContext.ts +0 -59
- package/tests/setup/models/Company.ts +0 -13
- package/tests/setup/models/Employee.ts +0 -10
- package/tests/setup/models/MonthlySales.ts +0 -11
- package/tests/setup/models/Post.ts +0 -16
- package/tests/setup/models/Sales.ts +0 -10
- package/tests/setup/models/User.ts +0 -19
- package/tests/setup/procedure/GetAllUsers.ts +0 -9
- package/tests/setup/procedure/GetUserById.ts +0 -12
- package/tests/setup/test-utils.ts +0 -72
- package/tests/setup/views/ActiveUsers.ts +0 -8
- package/tests/setup/views/UserSummary.ts +0 -11
- package/tests/types/nullable-queryable-record.spec.ts +0 -97
- package/tests/utils/result-parser-perf.spec.ts +0 -143
- package/tests/utils/result-parser.spec.ts +0 -667
package/dist/expr/expr.js
CHANGED
|
@@ -1,1857 +1,1885 @@
|
|
|
1
1
|
import { ArgumentError } from "@simplysm/core-common";
|
|
2
|
-
import {
|
|
3
|
-
dataTypeStrToColumnPrimitiveStr,
|
|
4
|
-
inferColumnPrimitiveStr
|
|
5
|
-
} from "../types/column.js";
|
|
2
|
+
import { dataTypeStrToColumnPrimitiveStr, inferColumnPrimitiveStr, } from "../types/column.js";
|
|
6
3
|
import { ExprUnit, WhereExprUnit } from "./expr-unit.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Dialect 독립적 SQL expression builder
|
|
6
|
+
*
|
|
7
|
+
* SQL 문자열 대신 JSON AST(Expr)를 생성하며, QueryBuilder가
|
|
8
|
+
* 각 DBMS(MySQL, MSSQL, PostgreSQL)로 변환
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* // WHERE condition
|
|
13
|
+
* db.user().where((u) => [
|
|
14
|
+
* expr.eq(u.status, "active"),
|
|
15
|
+
* expr.gt(u.age, 18),
|
|
16
|
+
* ])
|
|
17
|
+
*
|
|
18
|
+
* // SELECT expression
|
|
19
|
+
* db.user().select((u) => ({
|
|
20
|
+
* name: expr.concat(u.firstName, " ", u.lastName),
|
|
21
|
+
* age: expr.dateDiff("year", u.birthDate, expr.val("DateOnly", DateOnly.today())),
|
|
22
|
+
* }))
|
|
23
|
+
*
|
|
24
|
+
* // Aggregate function
|
|
25
|
+
* db.order().groupBy((o) => o.userId).select((o) => ({
|
|
26
|
+
* userId: o.userId,
|
|
27
|
+
* total: expr.sum(o.amount),
|
|
28
|
+
* }))
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @see {@link Queryable} Query builder 클래스
|
|
32
|
+
* @see {@link ExprUnit} Expression 래퍼 클래스
|
|
33
|
+
*/
|
|
34
|
+
export const expr = {
|
|
35
|
+
//#region ========== 값 생성 ==========
|
|
36
|
+
/**
|
|
37
|
+
* Wrap literal value as ExprUnit
|
|
38
|
+
*
|
|
39
|
+
* Widen to base type matching dataType, remove literal type
|
|
40
|
+
*
|
|
41
|
+
* @param dataType - The data type of the value ("string", "number", "boolean", "DateTime", "DateOnly", "Time", "Uuid", "Buffer")
|
|
42
|
+
* @param value - Value to wrap (undefined allowed)
|
|
43
|
+
* @returns Wrapped ExprUnit instance
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* // String value
|
|
48
|
+
* expr.val("string", "active")
|
|
49
|
+
*
|
|
50
|
+
* // Number value
|
|
51
|
+
* expr.val("number", 100)
|
|
52
|
+
*
|
|
53
|
+
* // Date value
|
|
54
|
+
* expr.val("DateOnly", DateOnly.today())
|
|
55
|
+
*
|
|
56
|
+
* // undefined value
|
|
57
|
+
* expr.val("string", undefined)
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
val(dataType, value) {
|
|
61
|
+
return new ExprUnit(dataType, { type: "value", value });
|
|
62
|
+
},
|
|
63
|
+
/**
|
|
64
|
+
* Generate column reference
|
|
65
|
+
*
|
|
66
|
+
* Typically proxy objects are used inside Queryable callbacks
|
|
67
|
+
*
|
|
68
|
+
* @param dataType - Column data type
|
|
69
|
+
* @param path - Column path (table alias, column name, etc.)
|
|
70
|
+
* @returns Column reference ExprUnit instance
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* // Direct column reference (internal use)
|
|
75
|
+
* expr.col("string", "T1", "name")
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
col(dataType, ...path) {
|
|
79
|
+
return new ExprUnit(dataType, { type: "column", path });
|
|
80
|
+
},
|
|
81
|
+
/**
|
|
82
|
+
* Raw SQL expression Generate (escape hatch)
|
|
83
|
+
*
|
|
84
|
+
* Use when you need to directly use DB-specific functions or syntax not supported by the ORM.
|
|
85
|
+
* Used as tagged template literal, interpolated values are automatically parameterized
|
|
86
|
+
*
|
|
87
|
+
* @param dataType - Data type of the returned value
|
|
88
|
+
* @returns Tagged template function
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* // Using MySQL JSON function
|
|
93
|
+
* db.user().select((u) => ({
|
|
94
|
+
* name: u.name,
|
|
95
|
+
* data: expr.raw("string")`JSON_EXTRACT(${u.metadata}, '$.email')`,
|
|
96
|
+
* }))
|
|
97
|
+
*
|
|
98
|
+
* // PostgreSQL array function
|
|
99
|
+
* expr.raw("number")`ARRAY_LENGTH(${u.tags}, 1)`
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
raw(dataType) {
|
|
103
|
+
return (strings, ...values) => {
|
|
104
|
+
const sql = strings.reduce((acc, str, i) => {
|
|
105
|
+
if (i < values.length) {
|
|
106
|
+
return acc + str + `$${i + 1}`; // placeholder (transformed by ExprRenderer)
|
|
107
|
+
}
|
|
108
|
+
return acc + str;
|
|
109
|
+
}, "");
|
|
110
|
+
const params = values.map((v) => toExpr(v));
|
|
111
|
+
return new ExprUnit(dataType, { type: "raw", sql, params });
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region ========== WHERE - Comparison operators ==========
|
|
116
|
+
/**
|
|
117
|
+
* Equality comparison (NULL-safe)
|
|
118
|
+
*
|
|
119
|
+
* Safely compare even NULL values (MySQL: `<=>`, MSSQL/PostgreSQL: `IS NULL OR =`)
|
|
120
|
+
*
|
|
121
|
+
* @param source - Column or expression to compare
|
|
122
|
+
* @param target - Target value or expression for comparison
|
|
123
|
+
* @returns WHERE condition expression
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* db.user().where((u) => [expr.eq(u.status, "active")])
|
|
128
|
+
* // WHERE status <=> 'active' (MySQL)
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
eq(source, target) {
|
|
132
|
+
return new WhereExprUnit({
|
|
133
|
+
type: "eq",
|
|
134
|
+
source: toExpr(source),
|
|
135
|
+
target: toExpr(target),
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
/**
|
|
139
|
+
* Greater than comparison (>)
|
|
140
|
+
*
|
|
141
|
+
* @param source - Column or expression to compare
|
|
142
|
+
* @param target - Target value or expression for comparison
|
|
143
|
+
* @returns WHERE condition expression
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```typescript
|
|
147
|
+
* db.user().where((u) => [expr.gt(u.age, 18)])
|
|
148
|
+
* // WHERE age > 18
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
gt(source, target) {
|
|
152
|
+
return new WhereExprUnit({
|
|
153
|
+
type: "gt",
|
|
154
|
+
source: toExpr(source),
|
|
155
|
+
target: toExpr(target),
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
/**
|
|
159
|
+
* Less than comparison (<)
|
|
160
|
+
*
|
|
161
|
+
* @param source - Column or expression to compare
|
|
162
|
+
* @param target - Target value or expression for comparison
|
|
163
|
+
* @returns WHERE condition expression
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```typescript
|
|
167
|
+
* db.user().where((u) => [expr.lt(u.score, 60)])
|
|
168
|
+
* // WHERE score < 60
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
lt(source, target) {
|
|
172
|
+
return new WhereExprUnit({
|
|
173
|
+
type: "lt",
|
|
174
|
+
source: toExpr(source),
|
|
175
|
+
target: toExpr(target),
|
|
176
|
+
});
|
|
177
|
+
},
|
|
178
|
+
/**
|
|
179
|
+
* Greater than or equal comparison (>=)
|
|
180
|
+
*
|
|
181
|
+
* @param source - Column or expression to compare
|
|
182
|
+
* @param target - Target value or expression for comparison
|
|
183
|
+
* @returns WHERE condition expression
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```typescript
|
|
187
|
+
* db.user().where((u) => [expr.gte(u.age, 18)])
|
|
188
|
+
* // WHERE age >= 18
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
gte(source, target) {
|
|
192
|
+
return new WhereExprUnit({
|
|
193
|
+
type: "gte",
|
|
194
|
+
source: toExpr(source),
|
|
195
|
+
target: toExpr(target),
|
|
196
|
+
});
|
|
197
|
+
},
|
|
198
|
+
/**
|
|
199
|
+
* Less than or equal comparison (<=)
|
|
200
|
+
*
|
|
201
|
+
* @param source - Column or expression to compare
|
|
202
|
+
* @param target - Target value or expression for comparison
|
|
203
|
+
* @returns WHERE condition expression
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```typescript
|
|
207
|
+
* db.user().where((u) => [expr.lte(u.score, 100)])
|
|
208
|
+
* // WHERE score <= 100
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
lte(source, target) {
|
|
212
|
+
return new WhereExprUnit({
|
|
213
|
+
type: "lte",
|
|
214
|
+
source: toExpr(source),
|
|
215
|
+
target: toExpr(target),
|
|
216
|
+
});
|
|
217
|
+
},
|
|
218
|
+
/**
|
|
219
|
+
* range comparison (BETWEEN)
|
|
220
|
+
*
|
|
221
|
+
* If from/to is undefined, that direction is unbounded
|
|
222
|
+
*
|
|
223
|
+
* @param source - Column or expression to compare
|
|
224
|
+
* @param from - Start value (no lower bound if undefined)
|
|
225
|
+
* @param to - End value (no upper bound if undefined)
|
|
226
|
+
* @returns WHERE condition expression
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```typescript
|
|
230
|
+
* // Specify range
|
|
231
|
+
* db.user().where((u) => [expr.between(u.age, 18, 65)])
|
|
232
|
+
* // WHERE age BETWEEN 18 AND 65
|
|
233
|
+
*
|
|
234
|
+
* // Specify only lower bound
|
|
235
|
+
* db.user().where((u) => [expr.between(u.age, 18, undefined)])
|
|
236
|
+
* // WHERE age >= 18
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
between(source, from, to) {
|
|
240
|
+
return new WhereExprUnit({
|
|
241
|
+
type: "between",
|
|
242
|
+
source: toExpr(source),
|
|
243
|
+
from: from != null ? toExpr(from) : undefined,
|
|
244
|
+
to: to != null ? toExpr(to) : undefined,
|
|
245
|
+
});
|
|
246
|
+
},
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region ========== WHERE - NULL check ==========
|
|
249
|
+
/**
|
|
250
|
+
* NULL check (IS NULL)
|
|
251
|
+
*
|
|
252
|
+
* @param source - Column or expression to check
|
|
253
|
+
* @returns WHERE condition expression
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```typescript
|
|
257
|
+
* db.user().where((u) => [expr.null(u.deletedAt)])
|
|
258
|
+
* // WHERE deletedAt IS NULL
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
null(source) {
|
|
262
|
+
return new WhereExprUnit({
|
|
263
|
+
type: "null",
|
|
264
|
+
arg: toExpr(source),
|
|
265
|
+
});
|
|
266
|
+
},
|
|
267
|
+
//#endregion
|
|
268
|
+
//#region ========== WHERE - String search ==========
|
|
269
|
+
/**
|
|
270
|
+
* LIKE pattern matching
|
|
271
|
+
*
|
|
272
|
+
* `%` matches zero or more characters, `_` matches a single character.
|
|
273
|
+
* Special characters are escaped with `\`
|
|
274
|
+
*
|
|
275
|
+
* @param source - Column or expression to search
|
|
276
|
+
* @param pattern - Search pattern (%, _ wildcards available)
|
|
277
|
+
* @returns WHERE condition expression
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* ```typescript
|
|
281
|
+
* // Prefix search
|
|
282
|
+
* db.user().where((u) => [expr.like(u.name, "John%")])
|
|
283
|
+
* // WHERE name LIKE 'John%' ESCAPE '\'
|
|
284
|
+
*
|
|
285
|
+
* // Contains search
|
|
286
|
+
* db.user().where((u) => [expr.like(u.email, "%@gmail.com")])
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
like(source, pattern) {
|
|
290
|
+
return new WhereExprUnit({
|
|
291
|
+
type: "like",
|
|
292
|
+
source: toExpr(source),
|
|
293
|
+
pattern: toExpr(pattern),
|
|
294
|
+
});
|
|
295
|
+
},
|
|
296
|
+
/**
|
|
297
|
+
* Regular expression pattern matching
|
|
298
|
+
*
|
|
299
|
+
* Note: regex syntax may differ between DBMS implementations
|
|
300
|
+
*
|
|
301
|
+
* @param source - Column or expression to search
|
|
302
|
+
* @param pattern - Regular expression pattern
|
|
303
|
+
* @returns WHERE condition expression
|
|
304
|
+
*
|
|
305
|
+
* @example
|
|
306
|
+
* ```typescript
|
|
307
|
+
* db.user().where((u) => [expr.regexp(u.email, "^[a-z]+@")])
|
|
308
|
+
* // MySQL: WHERE email REGEXP '^[a-z]+@'
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
regexp(source, pattern) {
|
|
312
|
+
return new WhereExprUnit({
|
|
313
|
+
type: "regexp",
|
|
314
|
+
source: toExpr(source),
|
|
315
|
+
pattern: toExpr(pattern),
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
//#endregion
|
|
319
|
+
//#region ========== WHERE - IN ==========
|
|
320
|
+
/**
|
|
321
|
+
* IN operator - Compare against a list of values
|
|
322
|
+
*
|
|
323
|
+
* @param source - Column or expression to compare
|
|
324
|
+
* @param values - List of values to compare against
|
|
325
|
+
* @returns WHERE condition expression
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```typescript
|
|
329
|
+
* db.user().where((u) => [expr.in(u.status, ["active", "pending"])])
|
|
330
|
+
* // WHERE status IN ('active', 'pending')
|
|
331
|
+
* ```
|
|
332
|
+
*/
|
|
333
|
+
in(source, values) {
|
|
334
|
+
return new WhereExprUnit({
|
|
335
|
+
type: "in",
|
|
336
|
+
source: toExpr(source),
|
|
337
|
+
values: values.map((v) => toExpr(v)),
|
|
338
|
+
});
|
|
339
|
+
},
|
|
340
|
+
/**
|
|
341
|
+
* IN (SELECT ...) - Compare against subquery results
|
|
342
|
+
*
|
|
343
|
+
* The subquery must SELECT only a single column
|
|
344
|
+
*
|
|
345
|
+
* @param source - Column or expression to compare
|
|
346
|
+
* @param query - Queryable that returns a single column
|
|
347
|
+
* @returns WHERE condition expression
|
|
348
|
+
* @throws {Error} When the subquery does not return a single column
|
|
349
|
+
*
|
|
350
|
+
* @example
|
|
351
|
+
* ```typescript
|
|
352
|
+
* db.user().where((u) => [
|
|
353
|
+
* expr.inQuery(
|
|
354
|
+
* u.id,
|
|
355
|
+
* db.order()
|
|
356
|
+
* .where((o) => [expr.gt(o.amount, 1000)])
|
|
357
|
+
* .select((o) => ({ userId: o.userId }))
|
|
358
|
+
* ),
|
|
359
|
+
* ])
|
|
360
|
+
* // WHERE id IN (SELECT userId FROM Order WHERE amount > 1000)
|
|
361
|
+
* ```
|
|
362
|
+
*/
|
|
363
|
+
inQuery(source, query) {
|
|
364
|
+
const queryDef = query.getSelectQueryDef();
|
|
365
|
+
if (queryDef.select == null || Object.keys(queryDef.select).length !== 1) {
|
|
366
|
+
throw new Error("inQuery subquery must SELECT only a single column.");
|
|
80
367
|
}
|
|
81
|
-
return
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
*/
|
|
1532
|
-
ntile(n, spec) {
|
|
1533
|
-
return new ExprUnit("number", {
|
|
1534
|
-
type: "window",
|
|
1535
|
-
fn: { type: "ntile", n },
|
|
1536
|
-
spec: toWinSpec(spec)
|
|
1537
|
-
});
|
|
1538
|
-
},
|
|
1539
|
-
/**
|
|
1540
|
-
* LAG() - Reference value from a previous row
|
|
1541
|
-
*
|
|
1542
|
-
* @param column - Column to reference
|
|
1543
|
-
* @param spec - Window spec (partitionBy, orderBy)
|
|
1544
|
-
* @param options - offset (default 1), default (default value when no previous row)
|
|
1545
|
-
* @returns Previous row's value (or default value/NULL)
|
|
1546
|
-
*
|
|
1547
|
-
* @example
|
|
1548
|
-
* ```typescript
|
|
1549
|
-
* db.stock().select((s) => ({
|
|
1550
|
-
* date: s.date,
|
|
1551
|
-
* price: s.price,
|
|
1552
|
-
* prevPrice: expr.lag(s.price, {
|
|
1553
|
-
* partitionBy: [s.symbol],
|
|
1554
|
-
* orderBy: [[s.date, "ASC"]],
|
|
1555
|
-
* }),
|
|
1556
|
-
* }))
|
|
1557
|
-
* ```
|
|
1558
|
-
*/
|
|
1559
|
-
lag(column, spec, options) {
|
|
1560
|
-
return new ExprUnit(column.dataType, {
|
|
1561
|
-
type: "window",
|
|
1562
|
-
fn: {
|
|
1563
|
-
type: "lag",
|
|
1564
|
-
column: toExpr(column),
|
|
1565
|
-
offset: options == null ? void 0 : options.offset,
|
|
1566
|
-
default: (options == null ? void 0 : options.default) != null ? toExpr(options.default) : void 0
|
|
1567
|
-
},
|
|
1568
|
-
spec: toWinSpec(spec)
|
|
1569
|
-
});
|
|
1570
|
-
},
|
|
1571
|
-
/**
|
|
1572
|
-
* LEAD() - Reference value from a following row
|
|
1573
|
-
*
|
|
1574
|
-
* @param column - Column to reference
|
|
1575
|
-
* @param spec - Window spec (partitionBy, orderBy)
|
|
1576
|
-
* @param options - offset (default 1), default (default value when no following row)
|
|
1577
|
-
* @returns Following row's value (or default value/NULL)
|
|
1578
|
-
*
|
|
1579
|
-
* @example
|
|
1580
|
-
* ```typescript
|
|
1581
|
-
* db.stock().select((s) => ({
|
|
1582
|
-
* date: s.date,
|
|
1583
|
-
* price: s.price,
|
|
1584
|
-
* nextPrice: expr.lead(s.price, {
|
|
1585
|
-
* partitionBy: [s.symbol],
|
|
1586
|
-
* orderBy: [[s.date, "ASC"]],
|
|
1587
|
-
* }),
|
|
1588
|
-
* }))
|
|
1589
|
-
* ```
|
|
1590
|
-
*/
|
|
1591
|
-
lead(column, spec, options) {
|
|
1592
|
-
return new ExprUnit(column.dataType, {
|
|
1593
|
-
type: "window",
|
|
1594
|
-
fn: {
|
|
1595
|
-
type: "lead",
|
|
1596
|
-
column: toExpr(column),
|
|
1597
|
-
offset: options == null ? void 0 : options.offset,
|
|
1598
|
-
default: (options == null ? void 0 : options.default) != null ? toExpr(options.default) : void 0
|
|
1599
|
-
},
|
|
1600
|
-
spec: toWinSpec(spec)
|
|
1601
|
-
});
|
|
1602
|
-
},
|
|
1603
|
-
/**
|
|
1604
|
-
* FIRST_VALUE() - First value in the partition/frame
|
|
1605
|
-
*
|
|
1606
|
-
* @param column - Column to reference
|
|
1607
|
-
* @param spec - Window spec (partitionBy, orderBy)
|
|
1608
|
-
* @returns First value
|
|
1609
|
-
*
|
|
1610
|
-
* @example
|
|
1611
|
-
* ```typescript
|
|
1612
|
-
* db.order().select((o) => ({
|
|
1613
|
-
* ...o,
|
|
1614
|
-
* firstOrderAmount: expr.firstValue(o.amount, {
|
|
1615
|
-
* partitionBy: [o.userId],
|
|
1616
|
-
* orderBy: [[o.createdAt, "ASC"]],
|
|
1617
|
-
* }),
|
|
1618
|
-
* }))
|
|
1619
|
-
* ```
|
|
1620
|
-
*/
|
|
1621
|
-
firstValue(column, spec) {
|
|
1622
|
-
return new ExprUnit(column.dataType, {
|
|
1623
|
-
type: "window",
|
|
1624
|
-
fn: { type: "firstValue", column: toExpr(column) },
|
|
1625
|
-
spec: toWinSpec(spec)
|
|
1626
|
-
});
|
|
1627
|
-
},
|
|
1628
|
-
/**
|
|
1629
|
-
* LAST_VALUE() - Last value in the partition/frame
|
|
1630
|
-
*
|
|
1631
|
-
* @param column - Column to reference
|
|
1632
|
-
* @param spec - Window spec (partitionBy, orderBy)
|
|
1633
|
-
* @returns Last value
|
|
1634
|
-
*
|
|
1635
|
-
* @example
|
|
1636
|
-
* ```typescript
|
|
1637
|
-
* db.order().select((o) => ({
|
|
1638
|
-
* ...o,
|
|
1639
|
-
* lastOrderAmount: expr.lastValue(o.amount, {
|
|
1640
|
-
* partitionBy: [o.userId],
|
|
1641
|
-
* orderBy: [[o.createdAt, "ASC"]],
|
|
1642
|
-
* }),
|
|
1643
|
-
* }))
|
|
1644
|
-
* ```
|
|
1645
|
-
*/
|
|
1646
|
-
lastValue(column, spec) {
|
|
1647
|
-
return new ExprUnit(column.dataType, {
|
|
1648
|
-
type: "window",
|
|
1649
|
-
fn: { type: "lastValue", column: toExpr(column) },
|
|
1650
|
-
spec: toWinSpec(spec)
|
|
1651
|
-
});
|
|
1652
|
-
},
|
|
1653
|
-
/**
|
|
1654
|
-
* SUM() OVER - Window sum
|
|
1655
|
-
*
|
|
1656
|
-
* @param column - Column to sum
|
|
1657
|
-
* @param spec - Window spec (partitionBy, orderBy)
|
|
1658
|
-
* @returns Sum within window
|
|
1659
|
-
*
|
|
1660
|
-
* @example
|
|
1661
|
-
* ```typescript
|
|
1662
|
-
* // Running total
|
|
1663
|
-
* db.order().select((o) => ({
|
|
1664
|
-
* ...o,
|
|
1665
|
-
* runningTotal: expr.sumOver(o.amount, {
|
|
1666
|
-
* partitionBy: [o.userId],
|
|
1667
|
-
* orderBy: [[o.createdAt, "ASC"]],
|
|
1668
|
-
* }),
|
|
1669
|
-
* }))
|
|
1670
|
-
* ```
|
|
1671
|
-
*/
|
|
1672
|
-
sumOver(column, spec) {
|
|
1673
|
-
return new ExprUnit("number", {
|
|
1674
|
-
type: "window",
|
|
1675
|
-
fn: { type: "sum", column: toExpr(column) },
|
|
1676
|
-
spec: toWinSpec(spec)
|
|
1677
|
-
});
|
|
1678
|
-
},
|
|
1679
|
-
/**
|
|
1680
|
-
* AVG() OVER - Window average
|
|
1681
|
-
*
|
|
1682
|
-
* @param column - Column to average
|
|
1683
|
-
* @param spec - Window spec (partitionBy, orderBy)
|
|
1684
|
-
* @returns Average within window
|
|
1685
|
-
*
|
|
1686
|
-
* @example
|
|
1687
|
-
* ```typescript
|
|
1688
|
-
* // Moving average
|
|
1689
|
-
* db.stock().select((s) => ({
|
|
1690
|
-
* ...s,
|
|
1691
|
-
* movingAvg: expr.avgOver(s.price, {
|
|
1692
|
-
* partitionBy: [s.symbol],
|
|
1693
|
-
* orderBy: [[s.date, "ASC"]],
|
|
1694
|
-
* }),
|
|
1695
|
-
* }))
|
|
1696
|
-
* ```
|
|
1697
|
-
*/
|
|
1698
|
-
avgOver(column, spec) {
|
|
1699
|
-
return new ExprUnit("number", {
|
|
1700
|
-
type: "window",
|
|
1701
|
-
fn: { type: "avg", column: toExpr(column) },
|
|
1702
|
-
spec: toWinSpec(spec)
|
|
1703
|
-
});
|
|
1704
|
-
},
|
|
1705
|
-
/**
|
|
1706
|
-
* COUNT() OVER - Window count
|
|
1707
|
-
*
|
|
1708
|
-
* @param spec - Window spec (partitionBy, orderBy)
|
|
1709
|
-
* @param column - Column to count (all rows if omitted)
|
|
1710
|
-
* @returns Row count within window
|
|
1711
|
-
*
|
|
1712
|
-
* @example
|
|
1713
|
-
* ```typescript
|
|
1714
|
-
* db.order().select((o) => ({
|
|
1715
|
-
* ...o,
|
|
1716
|
-
* totalOrdersPerUser: expr.countOver({
|
|
1717
|
-
* partitionBy: [o.userId],
|
|
1718
|
-
* }),
|
|
1719
|
-
* }))
|
|
1720
|
-
* ```
|
|
1721
|
-
*/
|
|
1722
|
-
countOver(spec, column) {
|
|
1723
|
-
return new ExprUnit("number", {
|
|
1724
|
-
type: "window",
|
|
1725
|
-
fn: { type: "count", column: column != null ? toExpr(column) : void 0 },
|
|
1726
|
-
spec: toWinSpec(spec)
|
|
1727
|
-
});
|
|
1728
|
-
},
|
|
1729
|
-
/**
|
|
1730
|
-
* MIN() OVER - Window minimum
|
|
1731
|
-
*
|
|
1732
|
-
* @param column - Column to find minimum of
|
|
1733
|
-
* @param spec - Window spec (partitionBy, orderBy)
|
|
1734
|
-
* @returns Minimum value within window
|
|
1735
|
-
*
|
|
1736
|
-
* @example
|
|
1737
|
-
* ```typescript
|
|
1738
|
-
* db.stock().select((s) => ({
|
|
1739
|
-
* ...s,
|
|
1740
|
-
* minPriceInPeriod: expr.minOver(s.price, {
|
|
1741
|
-
* partitionBy: [s.symbol],
|
|
1742
|
-
* }),
|
|
1743
|
-
* }))
|
|
1744
|
-
* ```
|
|
1745
|
-
*/
|
|
1746
|
-
minOver(column, spec) {
|
|
1747
|
-
return new ExprUnit(column.dataType, {
|
|
1748
|
-
type: "window",
|
|
1749
|
-
fn: { type: "min", column: toExpr(column) },
|
|
1750
|
-
spec: toWinSpec(spec)
|
|
1751
|
-
});
|
|
1752
|
-
},
|
|
1753
|
-
/**
|
|
1754
|
-
* MAX() OVER - Window maximum
|
|
1755
|
-
*
|
|
1756
|
-
* @param column - Column to find maximum of
|
|
1757
|
-
* @param spec - Window spec (partitionBy, orderBy)
|
|
1758
|
-
* @returns Maximum value within window
|
|
1759
|
-
*
|
|
1760
|
-
* @example
|
|
1761
|
-
* ```typescript
|
|
1762
|
-
* db.stock().select((s) => ({
|
|
1763
|
-
* ...s,
|
|
1764
|
-
* maxPriceInPeriod: expr.maxOver(s.price, {
|
|
1765
|
-
* partitionBy: [s.symbol],
|
|
1766
|
-
* }),
|
|
1767
|
-
* }))
|
|
1768
|
-
* ```
|
|
1769
|
-
*/
|
|
1770
|
-
maxOver(column, spec) {
|
|
1771
|
-
return new ExprUnit(column.dataType, {
|
|
1772
|
-
type: "window",
|
|
1773
|
-
fn: { type: "max", column: toExpr(column) },
|
|
1774
|
-
spec: toWinSpec(spec)
|
|
1775
|
-
});
|
|
1776
|
-
},
|
|
1777
|
-
//#endregion
|
|
1778
|
-
//#region ========== Helper ==========
|
|
1779
|
-
/**
|
|
1780
|
-
* Transform ExprInput to Expr (internal use)
|
|
1781
|
-
*
|
|
1782
|
-
* @param value - Value to transform
|
|
1783
|
-
* @returns Expr JSON AST
|
|
1784
|
-
*/
|
|
1785
|
-
toExpr(value) {
|
|
1786
|
-
return toExpr(value);
|
|
1787
|
-
}
|
|
1788
|
-
//#endregion
|
|
368
|
+
return new WhereExprUnit({
|
|
369
|
+
type: "inQuery",
|
|
370
|
+
source: toExpr(source),
|
|
371
|
+
query: queryDef,
|
|
372
|
+
});
|
|
373
|
+
},
|
|
374
|
+
/**
|
|
375
|
+
* EXISTS (SELECT ...) - Check if subquery returns any rows
|
|
376
|
+
*
|
|
377
|
+
* Returns true if the subquery returns one or more rows
|
|
378
|
+
*
|
|
379
|
+
* @param query - Queryable to check for existence
|
|
380
|
+
* @returns WHERE condition expression
|
|
381
|
+
*
|
|
382
|
+
* @example
|
|
383
|
+
* ```typescript
|
|
384
|
+
* // Query users who have orders
|
|
385
|
+
* db.user().where((u) => [
|
|
386
|
+
* expr.exists(
|
|
387
|
+
* db.order().where((o) => [expr.eq(o.userId, u.id)])
|
|
388
|
+
* ),
|
|
389
|
+
* ])
|
|
390
|
+
* // WHERE EXISTS (SELECT 1 FROM Order WHERE userId = User.id)
|
|
391
|
+
* ```
|
|
392
|
+
*/
|
|
393
|
+
exists(query) {
|
|
394
|
+
const { select: _, ...queryDefWithoutSelect } = query.getSelectQueryDef(); // EXISTS does not need SELECT clause, saves packet size
|
|
395
|
+
return new WhereExprUnit({
|
|
396
|
+
type: "exists",
|
|
397
|
+
query: queryDefWithoutSelect,
|
|
398
|
+
});
|
|
399
|
+
},
|
|
400
|
+
//#endregion
|
|
401
|
+
//#region ========== WHERE - Logical operators ==========
|
|
402
|
+
/**
|
|
403
|
+
* NOT operator - Negate a condition
|
|
404
|
+
*
|
|
405
|
+
* @param arg - Condition to negate
|
|
406
|
+
* @returns Negated WHERE condition expression
|
|
407
|
+
*
|
|
408
|
+
* @example
|
|
409
|
+
* ```typescript
|
|
410
|
+
* db.user().where((u) => [expr.not(expr.eq(u.status, "deleted"))])
|
|
411
|
+
* // WHERE NOT (status <=> 'deleted')
|
|
412
|
+
* ```
|
|
413
|
+
*/
|
|
414
|
+
not(arg) {
|
|
415
|
+
return new WhereExprUnit({
|
|
416
|
+
type: "not",
|
|
417
|
+
arg: arg.expr,
|
|
418
|
+
});
|
|
419
|
+
},
|
|
420
|
+
/**
|
|
421
|
+
* AND operator - All conditions must be satisfied
|
|
422
|
+
*
|
|
423
|
+
* Combines multiple conditions with AND. Passing an array to where() automatically applies AND
|
|
424
|
+
*
|
|
425
|
+
* @param conditions - List of conditions to combine with AND
|
|
426
|
+
* @returns Combined WHERE condition expression
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* ```typescript
|
|
430
|
+
* db.user().where((u) => [
|
|
431
|
+
* expr.and([
|
|
432
|
+
* expr.eq(u.status, "active"),
|
|
433
|
+
* expr.gte(u.age, 18),
|
|
434
|
+
* ]),
|
|
435
|
+
* ])
|
|
436
|
+
* // WHERE (status <=> 'active' AND age >= 18)
|
|
437
|
+
* ```
|
|
438
|
+
*/
|
|
439
|
+
and(conditions) {
|
|
440
|
+
if (conditions.length === 0) {
|
|
441
|
+
throw new ArgumentError({ conditions: "empty arrays are not allowed" });
|
|
442
|
+
}
|
|
443
|
+
return new WhereExprUnit({
|
|
444
|
+
type: "and",
|
|
445
|
+
conditions: conditions.map((c) => c.expr),
|
|
446
|
+
});
|
|
447
|
+
},
|
|
448
|
+
/**
|
|
449
|
+
* OR operator - At least one condition must be satisfied
|
|
450
|
+
*
|
|
451
|
+
* @param conditions - List of conditions to combine with OR
|
|
452
|
+
* @returns Combined WHERE condition expression
|
|
453
|
+
*
|
|
454
|
+
* @example
|
|
455
|
+
* ```typescript
|
|
456
|
+
* db.user().where((u) => [
|
|
457
|
+
* expr.or([
|
|
458
|
+
* expr.eq(u.status, "active"),
|
|
459
|
+
* expr.eq(u.status, "pending"),
|
|
460
|
+
* ]),
|
|
461
|
+
* ])
|
|
462
|
+
* // WHERE (status <=> 'active' OR status <=> 'pending')
|
|
463
|
+
* ```
|
|
464
|
+
*/
|
|
465
|
+
or(conditions) {
|
|
466
|
+
if (conditions.length === 0) {
|
|
467
|
+
throw new ArgumentError({ conditions: "empty arrays are not allowed" });
|
|
468
|
+
}
|
|
469
|
+
return new WhereExprUnit({
|
|
470
|
+
type: "or",
|
|
471
|
+
conditions: conditions.map((c) => c.expr),
|
|
472
|
+
});
|
|
473
|
+
},
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region ========== SELECT - String ==========
|
|
476
|
+
/**
|
|
477
|
+
* String concatenation (CONCAT)
|
|
478
|
+
*
|
|
479
|
+
* NULL values are treated as empty strings (auto-transformed per DBMS)
|
|
480
|
+
*
|
|
481
|
+
* @param args - Strings to concatenate
|
|
482
|
+
* @returns Concatenated string expression
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* ```typescript
|
|
486
|
+
* db.user().select((u) => ({
|
|
487
|
+
* fullName: expr.concat(u.firstName, " ", u.lastName),
|
|
488
|
+
* }))
|
|
489
|
+
* // SELECT CONCAT(firstName, ' ', lastName) AS fullName
|
|
490
|
+
* ```
|
|
491
|
+
*/
|
|
492
|
+
concat(...args) {
|
|
493
|
+
return new ExprUnit("string", {
|
|
494
|
+
type: "concat",
|
|
495
|
+
args: args.map((arg) => toExpr(arg)),
|
|
496
|
+
});
|
|
497
|
+
},
|
|
498
|
+
/**
|
|
499
|
+
* Extract specified length from the left of a string (LEFT)
|
|
500
|
+
*
|
|
501
|
+
* @param source - Original string
|
|
502
|
+
* @param length - Number of characters to extract
|
|
503
|
+
* @returns Extracted string expression
|
|
504
|
+
*
|
|
505
|
+
* @example
|
|
506
|
+
* ```typescript
|
|
507
|
+
* db.user().select((u) => ({
|
|
508
|
+
* initial: expr.left(u.name, 1),
|
|
509
|
+
* }))
|
|
510
|
+
* // SELECT LEFT(name, 1) AS initial
|
|
511
|
+
* ```
|
|
512
|
+
*/
|
|
513
|
+
left(source, length) {
|
|
514
|
+
return new ExprUnit("string", {
|
|
515
|
+
type: "left",
|
|
516
|
+
source: toExpr(source),
|
|
517
|
+
length: toExpr(length),
|
|
518
|
+
});
|
|
519
|
+
},
|
|
520
|
+
/**
|
|
521
|
+
* Extract specified length from the right of a string (RIGHT)
|
|
522
|
+
*
|
|
523
|
+
* @param source - Original string
|
|
524
|
+
* @param length - Number of characters to extract
|
|
525
|
+
* @returns Extracted string expression
|
|
526
|
+
*
|
|
527
|
+
* @example
|
|
528
|
+
* ```typescript
|
|
529
|
+
* db.phone().select((p) => ({
|
|
530
|
+
* lastFour: expr.right(p.number, 4),
|
|
531
|
+
* }))
|
|
532
|
+
* // SELECT RIGHT(number, 4) AS lastFour
|
|
533
|
+
* ```
|
|
534
|
+
*/
|
|
535
|
+
right(source, length) {
|
|
536
|
+
return new ExprUnit("string", {
|
|
537
|
+
type: "right",
|
|
538
|
+
source: toExpr(source),
|
|
539
|
+
length: toExpr(length),
|
|
540
|
+
});
|
|
541
|
+
},
|
|
542
|
+
/**
|
|
543
|
+
* Remove whitespace from both sides of a string (TRIM)
|
|
544
|
+
*
|
|
545
|
+
* @param source - Original string
|
|
546
|
+
* @returns String expression with whitespace removed
|
|
547
|
+
*
|
|
548
|
+
* @example
|
|
549
|
+
* ```typescript
|
|
550
|
+
* db.user().select((u) => ({
|
|
551
|
+
* name: expr.trim(u.name),
|
|
552
|
+
* }))
|
|
553
|
+
* // SELECT TRIM(name) AS name
|
|
554
|
+
* ```
|
|
555
|
+
*/
|
|
556
|
+
trim(source) {
|
|
557
|
+
return new ExprUnit("string", {
|
|
558
|
+
type: "trim",
|
|
559
|
+
arg: toExpr(source),
|
|
560
|
+
});
|
|
561
|
+
},
|
|
562
|
+
/**
|
|
563
|
+
* Left padding (LPAD)
|
|
564
|
+
*
|
|
565
|
+
* Repeatedly adds fillString on the left until the target length is reached
|
|
566
|
+
*
|
|
567
|
+
* @param source - Original string
|
|
568
|
+
* @param length - Target length
|
|
569
|
+
* @param fillString - String to use for padding
|
|
570
|
+
* @returns Padded string expression
|
|
571
|
+
*
|
|
572
|
+
* @example
|
|
573
|
+
* ```typescript
|
|
574
|
+
* db.order().select((o) => ({
|
|
575
|
+
* orderNo: expr.padStart(expr.cast(o.id, { type: "varchar", length: 10 }), 8, "0"),
|
|
576
|
+
* }))
|
|
577
|
+
* // SELECT LPAD(CAST(id AS VARCHAR(10)), 8, '0') AS orderNo
|
|
578
|
+
* // Result: "00000123"
|
|
579
|
+
* ```
|
|
580
|
+
*/
|
|
581
|
+
padStart(source, length, fillString) {
|
|
582
|
+
return new ExprUnit("string", {
|
|
583
|
+
type: "padStart",
|
|
584
|
+
source: toExpr(source),
|
|
585
|
+
length: toExpr(length),
|
|
586
|
+
fillString: toExpr(fillString),
|
|
587
|
+
});
|
|
588
|
+
},
|
|
589
|
+
/**
|
|
590
|
+
* String replacement (REPLACE)
|
|
591
|
+
*
|
|
592
|
+
* @param source - Original string
|
|
593
|
+
* @param from - String to find
|
|
594
|
+
* @param to - Replacement string
|
|
595
|
+
* @returns Replaced string expression
|
|
596
|
+
*
|
|
597
|
+
* @example
|
|
598
|
+
* ```typescript
|
|
599
|
+
* db.user().select((u) => ({
|
|
600
|
+
* phone: expr.replace(u.phone, "-", ""),
|
|
601
|
+
* }))
|
|
602
|
+
* // SELECT REPLACE(phone, '-', '') AS phone
|
|
603
|
+
* ```
|
|
604
|
+
*/
|
|
605
|
+
replace(source, from, to) {
|
|
606
|
+
return new ExprUnit("string", {
|
|
607
|
+
type: "replace",
|
|
608
|
+
source: toExpr(source),
|
|
609
|
+
from: toExpr(from),
|
|
610
|
+
to: toExpr(to),
|
|
611
|
+
});
|
|
612
|
+
},
|
|
613
|
+
/**
|
|
614
|
+
* Convert string to uppercase (UPPER)
|
|
615
|
+
*
|
|
616
|
+
* @param source - Original string
|
|
617
|
+
* @returns Uppercase string expression
|
|
618
|
+
*
|
|
619
|
+
* @example
|
|
620
|
+
* ```typescript
|
|
621
|
+
* db.user().select((u) => ({
|
|
622
|
+
* code: expr.upper(u.code),
|
|
623
|
+
* }))
|
|
624
|
+
* // SELECT UPPER(code) AS code
|
|
625
|
+
* ```
|
|
626
|
+
*/
|
|
627
|
+
upper(source) {
|
|
628
|
+
return new ExprUnit("string", {
|
|
629
|
+
type: "upper",
|
|
630
|
+
arg: toExpr(source),
|
|
631
|
+
});
|
|
632
|
+
},
|
|
633
|
+
/**
|
|
634
|
+
* Convert string to lowercase (LOWER)
|
|
635
|
+
*
|
|
636
|
+
* @param source - Original string
|
|
637
|
+
* @returns Lowercase string expression
|
|
638
|
+
*
|
|
639
|
+
* @example
|
|
640
|
+
* ```typescript
|
|
641
|
+
* db.user().select((u) => ({
|
|
642
|
+
* email: expr.lower(u.email),
|
|
643
|
+
* }))
|
|
644
|
+
* // SELECT LOWER(email) AS email
|
|
645
|
+
* ```
|
|
646
|
+
*/
|
|
647
|
+
lower(source) {
|
|
648
|
+
return new ExprUnit("string", {
|
|
649
|
+
type: "lower",
|
|
650
|
+
arg: toExpr(source),
|
|
651
|
+
});
|
|
652
|
+
},
|
|
653
|
+
/**
|
|
654
|
+
* String length (character count)
|
|
655
|
+
*
|
|
656
|
+
* @param source - Original string
|
|
657
|
+
* @returns Character count
|
|
658
|
+
*
|
|
659
|
+
* @example
|
|
660
|
+
* ```typescript
|
|
661
|
+
* db.user().select((u) => ({
|
|
662
|
+
* nameLength: expr.length(u.name),
|
|
663
|
+
* }))
|
|
664
|
+
* // SELECT CHAR_LENGTH(name) AS nameLength
|
|
665
|
+
* ```
|
|
666
|
+
*/
|
|
667
|
+
length(source) {
|
|
668
|
+
return new ExprUnit("number", {
|
|
669
|
+
type: "length",
|
|
670
|
+
arg: toExpr(source),
|
|
671
|
+
});
|
|
672
|
+
},
|
|
673
|
+
/**
|
|
674
|
+
* String byte length
|
|
675
|
+
*
|
|
676
|
+
* In UTF-8, CJK characters are 3 bytes each
|
|
677
|
+
*
|
|
678
|
+
* @param source - Original string
|
|
679
|
+
* @returns Byte count
|
|
680
|
+
*
|
|
681
|
+
* @example
|
|
682
|
+
* ```typescript
|
|
683
|
+
* db.user().select((u) => ({
|
|
684
|
+
* byteLen: expr.byteLength(u.name),
|
|
685
|
+
* }))
|
|
686
|
+
* // SELECT OCTET_LENGTH(name) AS byteLen
|
|
687
|
+
* ```
|
|
688
|
+
*/
|
|
689
|
+
byteLength(source) {
|
|
690
|
+
return new ExprUnit("number", {
|
|
691
|
+
type: "byteLength",
|
|
692
|
+
arg: toExpr(source),
|
|
693
|
+
});
|
|
694
|
+
},
|
|
695
|
+
/**
|
|
696
|
+
* Extract part of a string (SUBSTRING)
|
|
697
|
+
*
|
|
698
|
+
* Uses 1-based index per SQL standard
|
|
699
|
+
*
|
|
700
|
+
* @param source - Original string
|
|
701
|
+
* @param start - Start position (starting from 1)
|
|
702
|
+
* @param length - Length to extract (to the end if omitted)
|
|
703
|
+
* @returns Extracted string expression
|
|
704
|
+
*
|
|
705
|
+
* @example
|
|
706
|
+
* ```typescript
|
|
707
|
+
* db.user().select((u) => ({
|
|
708
|
+
* // From "Hello World", 5 characters starting at index 1: "Hello"
|
|
709
|
+
* prefix: expr.substring(u.name, 1, 5),
|
|
710
|
+
* }))
|
|
711
|
+
* // SELECT SUBSTRING(name, 1, 5) AS prefix
|
|
712
|
+
* ```
|
|
713
|
+
*/
|
|
714
|
+
substring(source, start, length) {
|
|
715
|
+
return new ExprUnit("string", {
|
|
716
|
+
type: "substring",
|
|
717
|
+
source: toExpr(source),
|
|
718
|
+
start: toExpr(start),
|
|
719
|
+
...(length != null ? { length: toExpr(length) } : {}),
|
|
720
|
+
});
|
|
721
|
+
},
|
|
722
|
+
/**
|
|
723
|
+
* Find position within a string (LOCATE/CHARINDEX)
|
|
724
|
+
*
|
|
725
|
+
* Returns 1-based index, or 0 if not found
|
|
726
|
+
*
|
|
727
|
+
* @param source - String to search in
|
|
728
|
+
* @param search - String to find
|
|
729
|
+
* @returns Position (starting from 1, 0 if not found)
|
|
730
|
+
*
|
|
731
|
+
* @example
|
|
732
|
+
* ```typescript
|
|
733
|
+
* db.user().select((u) => ({
|
|
734
|
+
* atPos: expr.indexOf(u.email, "@"),
|
|
735
|
+
* }))
|
|
736
|
+
* // SELECT LOCATE('@', email) AS atPos (MySQL)
|
|
737
|
+
* // "john@example.com" → 5
|
|
738
|
+
* ```
|
|
739
|
+
*/
|
|
740
|
+
indexOf(source, search) {
|
|
741
|
+
return new ExprUnit("number", {
|
|
742
|
+
type: "indexOf",
|
|
743
|
+
source: toExpr(source),
|
|
744
|
+
search: toExpr(search),
|
|
745
|
+
});
|
|
746
|
+
},
|
|
747
|
+
//#endregion
|
|
748
|
+
//#region ========== SELECT - Number ==========
|
|
749
|
+
/**
|
|
750
|
+
* Absolute value (ABS)
|
|
751
|
+
*
|
|
752
|
+
* @param source - Original number
|
|
753
|
+
* @returns Absolute value expression
|
|
754
|
+
*
|
|
755
|
+
* @example
|
|
756
|
+
* ```typescript
|
|
757
|
+
* db.account().select((a) => ({
|
|
758
|
+
* balance: expr.abs(a.balance),
|
|
759
|
+
* }))
|
|
760
|
+
* // SELECT ABS(balance) AS balance
|
|
761
|
+
* ```
|
|
762
|
+
*/
|
|
763
|
+
abs(source) {
|
|
764
|
+
return new ExprUnit("number", {
|
|
765
|
+
type: "abs",
|
|
766
|
+
arg: toExpr(source),
|
|
767
|
+
});
|
|
768
|
+
},
|
|
769
|
+
/**
|
|
770
|
+
* Round (ROUND)
|
|
771
|
+
*
|
|
772
|
+
* @param source - Original number
|
|
773
|
+
* @param digits - Number of decimal places
|
|
774
|
+
* @returns Rounded number expression
|
|
775
|
+
*
|
|
776
|
+
* @example
|
|
777
|
+
* ```typescript
|
|
778
|
+
* db.product().select((p) => ({
|
|
779
|
+
* price: expr.round(p.price, 2),
|
|
780
|
+
* }))
|
|
781
|
+
* // SELECT ROUND(price, 2) AS price
|
|
782
|
+
* // 123.456 → 123.46
|
|
783
|
+
* ```
|
|
784
|
+
*/
|
|
785
|
+
round(source, digits) {
|
|
786
|
+
return new ExprUnit("number", {
|
|
787
|
+
type: "round",
|
|
788
|
+
arg: toExpr(source),
|
|
789
|
+
digits,
|
|
790
|
+
});
|
|
791
|
+
},
|
|
792
|
+
/**
|
|
793
|
+
* Ceiling (CEILING)
|
|
794
|
+
*
|
|
795
|
+
* @param source - Original number
|
|
796
|
+
* @returns Ceiling number expression
|
|
797
|
+
*
|
|
798
|
+
* @example
|
|
799
|
+
* ```typescript
|
|
800
|
+
* db.order().select((o) => ({
|
|
801
|
+
* pages: expr.ceil(expr.divide(o.itemCount, 10)),
|
|
802
|
+
* }))
|
|
803
|
+
* // SELECT CEILING(itemCount / 10) AS pages
|
|
804
|
+
* // 25 / 10 = 2.5 → 3
|
|
805
|
+
* ```
|
|
806
|
+
*/
|
|
807
|
+
ceil(source) {
|
|
808
|
+
return new ExprUnit("number", {
|
|
809
|
+
type: "ceil",
|
|
810
|
+
arg: toExpr(source),
|
|
811
|
+
});
|
|
812
|
+
},
|
|
813
|
+
/**
|
|
814
|
+
* Floor (FLOOR)
|
|
815
|
+
*
|
|
816
|
+
* @param source - Original number
|
|
817
|
+
* @returns Floor number expression
|
|
818
|
+
*
|
|
819
|
+
* @example
|
|
820
|
+
* ```typescript
|
|
821
|
+
* db.user().select((u) => ({
|
|
822
|
+
* ageGroup: expr.floor(expr.divide(u.age, 10)),
|
|
823
|
+
* }))
|
|
824
|
+
* // SELECT FLOOR(age / 10) AS ageGroup
|
|
825
|
+
* // 25 / 10 = 2.5 → 2
|
|
826
|
+
* ```
|
|
827
|
+
*/
|
|
828
|
+
floor(source) {
|
|
829
|
+
return new ExprUnit("number", {
|
|
830
|
+
type: "floor",
|
|
831
|
+
arg: toExpr(source),
|
|
832
|
+
});
|
|
833
|
+
},
|
|
834
|
+
//#endregion
|
|
835
|
+
//#region ========== SELECT - Date ==========
|
|
836
|
+
/**
|
|
837
|
+
* Extract year (YEAR)
|
|
838
|
+
*
|
|
839
|
+
* @param source - DateTime or DateOnly expression
|
|
840
|
+
* @returns Year (4-digit number)
|
|
841
|
+
*
|
|
842
|
+
* @example
|
|
843
|
+
* ```typescript
|
|
844
|
+
* db.user().select((u) => ({
|
|
845
|
+
* birthYear: expr.year(u.birthDate),
|
|
846
|
+
* }))
|
|
847
|
+
* // SELECT YEAR(birthDate) AS birthYear
|
|
848
|
+
* ```
|
|
849
|
+
*/
|
|
850
|
+
year(source) {
|
|
851
|
+
return new ExprUnit("number", {
|
|
852
|
+
type: "year",
|
|
853
|
+
arg: toExpr(source),
|
|
854
|
+
});
|
|
855
|
+
},
|
|
856
|
+
/**
|
|
857
|
+
* Extract month (MONTH)
|
|
858
|
+
*
|
|
859
|
+
* @param source - DateTime or DateOnly expression
|
|
860
|
+
* @returns Month (1~12)
|
|
861
|
+
*
|
|
862
|
+
* @example
|
|
863
|
+
* ```typescript
|
|
864
|
+
* db.order().select((o) => ({
|
|
865
|
+
* orderMonth: expr.month(o.createdAt),
|
|
866
|
+
* }))
|
|
867
|
+
* // SELECT MONTH(createdAt) AS orderMonth
|
|
868
|
+
* ```
|
|
869
|
+
*/
|
|
870
|
+
month(source) {
|
|
871
|
+
return new ExprUnit("number", {
|
|
872
|
+
type: "month",
|
|
873
|
+
arg: toExpr(source),
|
|
874
|
+
});
|
|
875
|
+
},
|
|
876
|
+
/**
|
|
877
|
+
* Extract day (DAY)
|
|
878
|
+
*
|
|
879
|
+
* @param source - DateTime or DateOnly expression
|
|
880
|
+
* @returns Day (1~31)
|
|
881
|
+
*
|
|
882
|
+
* @example
|
|
883
|
+
* ```typescript
|
|
884
|
+
* db.user().select((u) => ({
|
|
885
|
+
* birthDay: expr.day(u.birthDate),
|
|
886
|
+
* }))
|
|
887
|
+
* // SELECT DAY(birthDate) AS birthDay
|
|
888
|
+
* ```
|
|
889
|
+
*/
|
|
890
|
+
day(source) {
|
|
891
|
+
return new ExprUnit("number", {
|
|
892
|
+
type: "day",
|
|
893
|
+
arg: toExpr(source),
|
|
894
|
+
});
|
|
895
|
+
},
|
|
896
|
+
/**
|
|
897
|
+
* Extract hour (HOUR)
|
|
898
|
+
*
|
|
899
|
+
* @param source - DateTime or Time expression
|
|
900
|
+
* @returns Hour (0~23)
|
|
901
|
+
*
|
|
902
|
+
* @example
|
|
903
|
+
* ```typescript
|
|
904
|
+
* db.log().select((l) => ({
|
|
905
|
+
* logHour: expr.hour(l.createdAt),
|
|
906
|
+
* }))
|
|
907
|
+
* // SELECT HOUR(createdAt) AS logHour
|
|
908
|
+
* ```
|
|
909
|
+
*/
|
|
910
|
+
hour(source) {
|
|
911
|
+
return new ExprUnit("number", {
|
|
912
|
+
type: "hour",
|
|
913
|
+
arg: toExpr(source),
|
|
914
|
+
});
|
|
915
|
+
},
|
|
916
|
+
/**
|
|
917
|
+
* Extract minute (MINUTE)
|
|
918
|
+
*
|
|
919
|
+
* @param source - DateTime or Time expression
|
|
920
|
+
* @returns Minute (0~59)
|
|
921
|
+
*
|
|
922
|
+
* @example
|
|
923
|
+
* ```typescript
|
|
924
|
+
* db.log().select((l) => ({
|
|
925
|
+
* logMinute: expr.minute(l.createdAt),
|
|
926
|
+
* }))
|
|
927
|
+
* // SELECT MINUTE(createdAt) AS logMinute
|
|
928
|
+
* ```
|
|
929
|
+
*/
|
|
930
|
+
minute(source) {
|
|
931
|
+
return new ExprUnit("number", {
|
|
932
|
+
type: "minute",
|
|
933
|
+
arg: toExpr(source),
|
|
934
|
+
});
|
|
935
|
+
},
|
|
936
|
+
/**
|
|
937
|
+
* Extract second (SECOND)
|
|
938
|
+
*
|
|
939
|
+
* @param source - DateTime or Time expression
|
|
940
|
+
* @returns Second (0~59)
|
|
941
|
+
*
|
|
942
|
+
* @example
|
|
943
|
+
* ```typescript
|
|
944
|
+
* db.log().select((l) => ({
|
|
945
|
+
* logSecond: expr.second(l.createdAt),
|
|
946
|
+
* }))
|
|
947
|
+
* // SELECT SECOND(createdAt) AS logSecond
|
|
948
|
+
* ```
|
|
949
|
+
*/
|
|
950
|
+
second(source) {
|
|
951
|
+
return new ExprUnit("number", {
|
|
952
|
+
type: "second",
|
|
953
|
+
arg: toExpr(source),
|
|
954
|
+
});
|
|
955
|
+
},
|
|
956
|
+
/**
|
|
957
|
+
* Extract ISO week number
|
|
958
|
+
*
|
|
959
|
+
* ISO 8601 week number (starts Monday, 1~53)
|
|
960
|
+
*
|
|
961
|
+
* @param source - DateOnly expression
|
|
962
|
+
* @returns ISO week number
|
|
963
|
+
*
|
|
964
|
+
* @example
|
|
965
|
+
* ```typescript
|
|
966
|
+
* db.order().select((o) => ({
|
|
967
|
+
* weekNum: expr.isoWeek(o.orderDate),
|
|
968
|
+
* }))
|
|
969
|
+
* // SELECT WEEK(orderDate, 3) AS weekNum (MySQL)
|
|
970
|
+
* ```
|
|
971
|
+
*/
|
|
972
|
+
isoWeek(source) {
|
|
973
|
+
return new ExprUnit("number", {
|
|
974
|
+
type: "isoWeek",
|
|
975
|
+
arg: toExpr(source),
|
|
976
|
+
});
|
|
977
|
+
},
|
|
978
|
+
/**
|
|
979
|
+
* ISO week start date (Monday)
|
|
980
|
+
*
|
|
981
|
+
* Returns the Monday of the week the given date belongs to
|
|
982
|
+
*
|
|
983
|
+
* @param source - DateOnly expression
|
|
984
|
+
* @returns Week start date (Monday)
|
|
985
|
+
*
|
|
986
|
+
* @example
|
|
987
|
+
* ```typescript
|
|
988
|
+
* db.order().select((o) => ({
|
|
989
|
+
* weekStart: expr.isoWeekStartDate(o.orderDate),
|
|
990
|
+
* }))
|
|
991
|
+
* // 2024-01-10 (Wed) → 2024-01-08 (Mon)
|
|
992
|
+
* ```
|
|
993
|
+
*/
|
|
994
|
+
isoWeekStartDate(source) {
|
|
995
|
+
return new ExprUnit("DateOnly", {
|
|
996
|
+
type: "isoWeekStartDate",
|
|
997
|
+
arg: toExpr(source),
|
|
998
|
+
});
|
|
999
|
+
},
|
|
1000
|
+
/**
|
|
1001
|
+
* ISO year-month (first day of the month)
|
|
1002
|
+
*
|
|
1003
|
+
* Returns the first day of the month for the given date
|
|
1004
|
+
*
|
|
1005
|
+
* @param source - DateOnly expression
|
|
1006
|
+
* @returns First day of the month
|
|
1007
|
+
*
|
|
1008
|
+
* @example
|
|
1009
|
+
* ```typescript
|
|
1010
|
+
* db.order().select((o) => ({
|
|
1011
|
+
* yearMonth: expr.isoYearMonth(o.orderDate),
|
|
1012
|
+
* }))
|
|
1013
|
+
* // 2024-01-15 → 2024-01-01
|
|
1014
|
+
* ```
|
|
1015
|
+
*/
|
|
1016
|
+
isoYearMonth(source) {
|
|
1017
|
+
return new ExprUnit("DateOnly", {
|
|
1018
|
+
type: "isoYearMonth",
|
|
1019
|
+
arg: toExpr(source),
|
|
1020
|
+
});
|
|
1021
|
+
},
|
|
1022
|
+
/**
|
|
1023
|
+
* Calculate date difference (DATEDIFF)
|
|
1024
|
+
*
|
|
1025
|
+
* @param unit - Unit ("year", "month", "day", "hour", "minute", "second")
|
|
1026
|
+
* @param from - Start date
|
|
1027
|
+
* @param to - End date
|
|
1028
|
+
* @returns Difference value (to - from)
|
|
1029
|
+
*
|
|
1030
|
+
* @example
|
|
1031
|
+
* ```typescript
|
|
1032
|
+
* db.user().select((u) => ({
|
|
1033
|
+
* age: expr.dateDiff("year", u.birthDate, expr.val("DateOnly", DateOnly.today())),
|
|
1034
|
+
* }))
|
|
1035
|
+
* // SELECT DATEDIFF(year, birthDate, '2024-01-15') AS age
|
|
1036
|
+
* ```
|
|
1037
|
+
*/
|
|
1038
|
+
dateDiff(unit, from, to) {
|
|
1039
|
+
return new ExprUnit("number", {
|
|
1040
|
+
type: "dateDiff",
|
|
1041
|
+
unit,
|
|
1042
|
+
from: toExpr(from),
|
|
1043
|
+
to: toExpr(to),
|
|
1044
|
+
});
|
|
1045
|
+
},
|
|
1046
|
+
/**
|
|
1047
|
+
* Add to date (DATEADD)
|
|
1048
|
+
*
|
|
1049
|
+
* @param unit - Unit ("year", "month", "day", "hour", "minute", "second")
|
|
1050
|
+
* @param source - Original date
|
|
1051
|
+
* @param value - Value to add (negative allowed)
|
|
1052
|
+
* @returns Calculated date
|
|
1053
|
+
*
|
|
1054
|
+
* @example
|
|
1055
|
+
* ```typescript
|
|
1056
|
+
* db.subscription().select((s) => ({
|
|
1057
|
+
* expiresAt: expr.dateAdd("month", s.startDate, 12),
|
|
1058
|
+
* }))
|
|
1059
|
+
* // SELECT DATEADD(month, 12, startDate) AS expiresAt
|
|
1060
|
+
* ```
|
|
1061
|
+
*/
|
|
1062
|
+
dateAdd(unit, source, value) {
|
|
1063
|
+
return new ExprUnit(source.dataType, {
|
|
1064
|
+
type: "dateAdd",
|
|
1065
|
+
unit,
|
|
1066
|
+
source: toExpr(source),
|
|
1067
|
+
value: toExpr(value),
|
|
1068
|
+
});
|
|
1069
|
+
},
|
|
1070
|
+
/**
|
|
1071
|
+
* Date format (DATE_FORMAT)
|
|
1072
|
+
*
|
|
1073
|
+
* Format string rules may differ between DBMS implementations
|
|
1074
|
+
*
|
|
1075
|
+
* @param source - Date expression
|
|
1076
|
+
* @param format - Format string (e.g., "%Y-%m-%d")
|
|
1077
|
+
* @returns Formatted string expression
|
|
1078
|
+
*
|
|
1079
|
+
* @example
|
|
1080
|
+
* ```typescript
|
|
1081
|
+
* db.order().select((o) => ({
|
|
1082
|
+
* orderDate: expr.formatDate(o.createdAt, "%Y-%m-%d"),
|
|
1083
|
+
* }))
|
|
1084
|
+
* // SELECT DATE_FORMAT(createdAt, '%Y-%m-%d') AS orderDate (MySQL)
|
|
1085
|
+
* // 2024-01-15 10:30:00 → "2024-01-15"
|
|
1086
|
+
* ```
|
|
1087
|
+
*/
|
|
1088
|
+
formatDate(source, format) {
|
|
1089
|
+
return new ExprUnit("string", {
|
|
1090
|
+
type: "formatDate",
|
|
1091
|
+
source: toExpr(source),
|
|
1092
|
+
format,
|
|
1093
|
+
});
|
|
1094
|
+
},
|
|
1095
|
+
//#endregion
|
|
1096
|
+
//#region ========== SELECT - Condition ==========
|
|
1097
|
+
/**
|
|
1098
|
+
* NULL replacement (COALESCE/IFNULL)
|
|
1099
|
+
*
|
|
1100
|
+
* Returns the first non-null value. If the last argument is non-nullable, the result is also non-nullable
|
|
1101
|
+
*
|
|
1102
|
+
* @param args - Values to inspect (last is default value)
|
|
1103
|
+
* @returns First non-null value
|
|
1104
|
+
*
|
|
1105
|
+
* @example
|
|
1106
|
+
* ```typescript
|
|
1107
|
+
* db.user().select((u) => ({
|
|
1108
|
+
* displayName: expr.coalesce(u.nickname, u.name, "Guest"),
|
|
1109
|
+
* }))
|
|
1110
|
+
* // SELECT COALESCE(nickname, name, 'Guest') AS displayName
|
|
1111
|
+
* ```
|
|
1112
|
+
*/
|
|
1113
|
+
coalesce,
|
|
1114
|
+
/**
|
|
1115
|
+
* Return NULL if value matches (NULLIF)
|
|
1116
|
+
*
|
|
1117
|
+
* Returns NULL if source === value, otherwise returns source
|
|
1118
|
+
*
|
|
1119
|
+
* @param source - Original value
|
|
1120
|
+
* @param value - Value to compare
|
|
1121
|
+
* @returns NULL or original value
|
|
1122
|
+
*
|
|
1123
|
+
* @example
|
|
1124
|
+
* ```typescript
|
|
1125
|
+
* db.user().select((u) => ({
|
|
1126
|
+
* // Convert empty string to NULL
|
|
1127
|
+
* bio: expr.nullIf(u.bio, ""),
|
|
1128
|
+
* }))
|
|
1129
|
+
* // SELECT NULLIF(bio, '') AS bio
|
|
1130
|
+
* ```
|
|
1131
|
+
*/
|
|
1132
|
+
nullIf(source, value) {
|
|
1133
|
+
return new ExprUnit(source.dataType, {
|
|
1134
|
+
type: "nullIf",
|
|
1135
|
+
source: toExpr(source),
|
|
1136
|
+
value: toExpr(value),
|
|
1137
|
+
});
|
|
1138
|
+
},
|
|
1139
|
+
/**
|
|
1140
|
+
* Transform WHERE expression to boolean
|
|
1141
|
+
*
|
|
1142
|
+
* Used when condition results should be used as a boolean column in SELECT clause
|
|
1143
|
+
*
|
|
1144
|
+
* @param condition - Condition to transform
|
|
1145
|
+
* @returns boolean expression
|
|
1146
|
+
*
|
|
1147
|
+
* @example
|
|
1148
|
+
* ```typescript
|
|
1149
|
+
* db.user().select((u) => ({
|
|
1150
|
+
* isActive: expr.is(expr.eq(u.status, "active")),
|
|
1151
|
+
* }))
|
|
1152
|
+
* // SELECT (status <=> 'active') AS isActive
|
|
1153
|
+
* ```
|
|
1154
|
+
*/
|
|
1155
|
+
is(condition) {
|
|
1156
|
+
return new ExprUnit("boolean", {
|
|
1157
|
+
type: "is",
|
|
1158
|
+
condition: condition.expr,
|
|
1159
|
+
});
|
|
1160
|
+
},
|
|
1161
|
+
/**
|
|
1162
|
+
* CASE WHEN expression builder
|
|
1163
|
+
*
|
|
1164
|
+
* Build conditional branches using method chaining
|
|
1165
|
+
*
|
|
1166
|
+
* @returns SwitchExprBuilder instance
|
|
1167
|
+
*
|
|
1168
|
+
* @example
|
|
1169
|
+
* ```typescript
|
|
1170
|
+
* db.user().select((u) => ({
|
|
1171
|
+
* grade: expr.switch<string>()
|
|
1172
|
+
* .case(expr.gte(u.score, 90), "A")
|
|
1173
|
+
* .case(expr.gte(u.score, 80), "B")
|
|
1174
|
+
* .case(expr.gte(u.score, 70), "C")
|
|
1175
|
+
* .default("F"),
|
|
1176
|
+
* }))
|
|
1177
|
+
* // SELECT CASE WHEN score >= 90 THEN 'A' ... ELSE 'F' END AS grade
|
|
1178
|
+
* ```
|
|
1179
|
+
*/
|
|
1180
|
+
switch() {
|
|
1181
|
+
return createSwitchBuilder();
|
|
1182
|
+
},
|
|
1183
|
+
/**
|
|
1184
|
+
* Simple IF condition (ternary operator)
|
|
1185
|
+
*
|
|
1186
|
+
* @param condition - Condition
|
|
1187
|
+
* @param then - Value when condition is true
|
|
1188
|
+
* @param else_ - Value when condition is false
|
|
1189
|
+
* @returns Conditional value expression
|
|
1190
|
+
*
|
|
1191
|
+
* @example
|
|
1192
|
+
* ```typescript
|
|
1193
|
+
* db.user().select((u) => ({
|
|
1194
|
+
* type: expr.if(expr.gte(u.age, 18), "adult", "minor"),
|
|
1195
|
+
* }))
|
|
1196
|
+
* // SELECT IF(age >= 18, 'adult', 'minor') AS type
|
|
1197
|
+
* ```
|
|
1198
|
+
*/
|
|
1199
|
+
if(condition, then, else_) {
|
|
1200
|
+
const allValues = [then, else_];
|
|
1201
|
+
// 1. Find dataType from ExprUnit
|
|
1202
|
+
const exprUnit = allValues.find((v) => v instanceof ExprUnit);
|
|
1203
|
+
if (exprUnit) {
|
|
1204
|
+
return new ExprUnit(exprUnit.dataType, {
|
|
1205
|
+
type: "if",
|
|
1206
|
+
condition: condition.expr,
|
|
1207
|
+
then: toExpr(then),
|
|
1208
|
+
else: toExpr(else_),
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
// 2. Infer from non-null literal
|
|
1212
|
+
const nonNullLiteral = allValues.find((v) => v != null);
|
|
1213
|
+
if (nonNullLiteral == null) {
|
|
1214
|
+
throw new Error("At least one of if's then/else must be non-null.");
|
|
1215
|
+
}
|
|
1216
|
+
return new ExprUnit(inferColumnPrimitiveStr(nonNullLiteral), {
|
|
1217
|
+
type: "if",
|
|
1218
|
+
condition: condition.expr,
|
|
1219
|
+
then: toExpr(then),
|
|
1220
|
+
else: toExpr(else_),
|
|
1221
|
+
});
|
|
1222
|
+
},
|
|
1223
|
+
//#endregion
|
|
1224
|
+
//#region ========== SELECT - Aggregate ==========
|
|
1225
|
+
// SUM, AVG, MAX 등 집계는 모든 값이 NULL이거나 행이 없을 때만 NULL 반환 (NULL 값을 가진 행은 무시됨)
|
|
1226
|
+
/**
|
|
1227
|
+
* Count rows (COUNT)
|
|
1228
|
+
*
|
|
1229
|
+
* @param arg - Column to count (all rows if omitted)
|
|
1230
|
+
* @param distinct - If true, remove duplicates
|
|
1231
|
+
* @returns Row count
|
|
1232
|
+
*
|
|
1233
|
+
* @example
|
|
1234
|
+
* ```typescript
|
|
1235
|
+
* // Total row count
|
|
1236
|
+
* db.user().select(() => ({ total: expr.count() }))
|
|
1237
|
+
*
|
|
1238
|
+
* // Distinct count
|
|
1239
|
+
* db.order().select((o) => ({
|
|
1240
|
+
* uniqueCustomers: expr.count(o.customerId, true),
|
|
1241
|
+
* }))
|
|
1242
|
+
* ```
|
|
1243
|
+
*/
|
|
1244
|
+
count(arg, distinct) {
|
|
1245
|
+
return new ExprUnit("number", {
|
|
1246
|
+
type: "count",
|
|
1247
|
+
arg: arg != null ? toExpr(arg) : undefined,
|
|
1248
|
+
distinct,
|
|
1249
|
+
});
|
|
1250
|
+
},
|
|
1251
|
+
/**
|
|
1252
|
+
* Sum (SUM)
|
|
1253
|
+
*
|
|
1254
|
+
* NULL values are ignored. Returns NULL if all values are NULL
|
|
1255
|
+
*
|
|
1256
|
+
* @param arg - Number column to sum
|
|
1257
|
+
* @returns Sum (or NULL)
|
|
1258
|
+
*
|
|
1259
|
+
* @example
|
|
1260
|
+
* ```typescript
|
|
1261
|
+
* db.order().groupBy((o) => o.userId).select((o) => ({
|
|
1262
|
+
* userId: o.userId,
|
|
1263
|
+
* totalAmount: expr.sum(o.amount),
|
|
1264
|
+
* }))
|
|
1265
|
+
* ```
|
|
1266
|
+
*/
|
|
1267
|
+
sum(arg) {
|
|
1268
|
+
return new ExprUnit("number", {
|
|
1269
|
+
type: "sum",
|
|
1270
|
+
arg: toExpr(arg),
|
|
1271
|
+
});
|
|
1272
|
+
},
|
|
1273
|
+
/**
|
|
1274
|
+
* Average (AVG)
|
|
1275
|
+
*
|
|
1276
|
+
* NULL values are ignored. Returns NULL if all values are NULL
|
|
1277
|
+
*
|
|
1278
|
+
* @param arg - Number column to average
|
|
1279
|
+
* @returns Average (or NULL)
|
|
1280
|
+
*
|
|
1281
|
+
* @example
|
|
1282
|
+
* ```typescript
|
|
1283
|
+
* db.product().groupBy((p) => p.categoryId).select((p) => ({
|
|
1284
|
+
* categoryId: p.categoryId,
|
|
1285
|
+
* avgPrice: expr.avg(p.price),
|
|
1286
|
+
* }))
|
|
1287
|
+
* ```
|
|
1288
|
+
*/
|
|
1289
|
+
avg(arg) {
|
|
1290
|
+
return new ExprUnit("number", {
|
|
1291
|
+
type: "avg",
|
|
1292
|
+
arg: toExpr(arg),
|
|
1293
|
+
});
|
|
1294
|
+
},
|
|
1295
|
+
/**
|
|
1296
|
+
* Maximum value (MAX)
|
|
1297
|
+
*
|
|
1298
|
+
* NULL values are ignored. Returns NULL if all values are NULL
|
|
1299
|
+
*
|
|
1300
|
+
* @param arg - Column to find maximum of
|
|
1301
|
+
* @returns Maximum value (or NULL)
|
|
1302
|
+
*
|
|
1303
|
+
* @example
|
|
1304
|
+
* ```typescript
|
|
1305
|
+
* db.order().groupBy((o) => o.userId).select((o) => ({
|
|
1306
|
+
* userId: o.userId,
|
|
1307
|
+
* lastOrderDate: expr.max(o.createdAt),
|
|
1308
|
+
* }))
|
|
1309
|
+
* ```
|
|
1310
|
+
*/
|
|
1311
|
+
max(arg) {
|
|
1312
|
+
return new ExprUnit(arg.dataType, {
|
|
1313
|
+
type: "max",
|
|
1314
|
+
arg: toExpr(arg),
|
|
1315
|
+
});
|
|
1316
|
+
},
|
|
1317
|
+
/**
|
|
1318
|
+
* Minimum value (MIN)
|
|
1319
|
+
*
|
|
1320
|
+
* NULL values are ignored. Returns NULL if all values are NULL
|
|
1321
|
+
*
|
|
1322
|
+
* @param arg - Column to find minimum of
|
|
1323
|
+
* @returns Minimum value (or NULL)
|
|
1324
|
+
*
|
|
1325
|
+
* @example
|
|
1326
|
+
* ```typescript
|
|
1327
|
+
* db.product().groupBy((p) => p.categoryId).select((p) => ({
|
|
1328
|
+
* categoryId: p.categoryId,
|
|
1329
|
+
* minPrice: expr.min(p.price),
|
|
1330
|
+
* }))
|
|
1331
|
+
* ```
|
|
1332
|
+
*/
|
|
1333
|
+
min(arg) {
|
|
1334
|
+
return new ExprUnit(arg.dataType, {
|
|
1335
|
+
type: "min",
|
|
1336
|
+
arg: toExpr(arg),
|
|
1337
|
+
});
|
|
1338
|
+
},
|
|
1339
|
+
//#endregion
|
|
1340
|
+
//#region ========== SELECT - Other ==========
|
|
1341
|
+
/**
|
|
1342
|
+
* Greatest value among multiple values (GREATEST)
|
|
1343
|
+
*
|
|
1344
|
+
* @param args - Values to compare
|
|
1345
|
+
* @returns Greatest value
|
|
1346
|
+
*
|
|
1347
|
+
* @example
|
|
1348
|
+
* ```typescript
|
|
1349
|
+
* db.product().select((p) => ({
|
|
1350
|
+
* effectivePrice: expr.greatest(p.price, p.minPrice),
|
|
1351
|
+
* }))
|
|
1352
|
+
* // SELECT GREATEST(price, minPrice) AS effectivePrice
|
|
1353
|
+
* ```
|
|
1354
|
+
*/
|
|
1355
|
+
greatest(...args) {
|
|
1356
|
+
return new ExprUnit(findDataType(args), {
|
|
1357
|
+
type: "greatest",
|
|
1358
|
+
args: args.map((a) => toExpr(a)),
|
|
1359
|
+
});
|
|
1360
|
+
},
|
|
1361
|
+
/**
|
|
1362
|
+
* Least value among multiple values (LEAST)
|
|
1363
|
+
*
|
|
1364
|
+
* @param args - Values to compare
|
|
1365
|
+
* @returns Least value
|
|
1366
|
+
*
|
|
1367
|
+
* @example
|
|
1368
|
+
* ```typescript
|
|
1369
|
+
* db.product().select((p) => ({
|
|
1370
|
+
* effectivePrice: expr.least(p.price, p.maxDiscount),
|
|
1371
|
+
* }))
|
|
1372
|
+
* // SELECT LEAST(price, maxDiscount) AS effectivePrice
|
|
1373
|
+
* ```
|
|
1374
|
+
*/
|
|
1375
|
+
least(...args) {
|
|
1376
|
+
return new ExprUnit(findDataType(args), {
|
|
1377
|
+
type: "least",
|
|
1378
|
+
args: args.map((a) => toExpr(a)),
|
|
1379
|
+
});
|
|
1380
|
+
},
|
|
1381
|
+
/**
|
|
1382
|
+
* Row number (sequential number for all rows without ROW_NUMBER)
|
|
1383
|
+
*
|
|
1384
|
+
* @returns Row number (starting from 1)
|
|
1385
|
+
*
|
|
1386
|
+
* @example
|
|
1387
|
+
* ```typescript
|
|
1388
|
+
* db.user().select((u) => ({
|
|
1389
|
+
* rowNum: expr.rowNum(),
|
|
1390
|
+
* name: u.name,
|
|
1391
|
+
* }))
|
|
1392
|
+
* ```
|
|
1393
|
+
*/
|
|
1394
|
+
rowNum() {
|
|
1395
|
+
return new ExprUnit("number", {
|
|
1396
|
+
type: "rowNum",
|
|
1397
|
+
});
|
|
1398
|
+
},
|
|
1399
|
+
/**
|
|
1400
|
+
* Generate random number (RAND/RANDOM)
|
|
1401
|
+
*
|
|
1402
|
+
* Returns a random number between 0 and 1. Mainly used for random ordering in ORDER BY
|
|
1403
|
+
*
|
|
1404
|
+
* @returns Random number between 0 and 1
|
|
1405
|
+
*
|
|
1406
|
+
* @example
|
|
1407
|
+
* ```typescript
|
|
1408
|
+
* // Random sorting
|
|
1409
|
+
* db.user().orderBy(() => expr.random()).limit(10)
|
|
1410
|
+
* ```
|
|
1411
|
+
*/
|
|
1412
|
+
random() {
|
|
1413
|
+
return new ExprUnit("number", {
|
|
1414
|
+
type: "random",
|
|
1415
|
+
});
|
|
1416
|
+
},
|
|
1417
|
+
/**
|
|
1418
|
+
* type transformation (CAST)
|
|
1419
|
+
*
|
|
1420
|
+
* @param source - Expression to transform
|
|
1421
|
+
* @param targetType - Target data type
|
|
1422
|
+
* @returns Transformed expression
|
|
1423
|
+
*
|
|
1424
|
+
* @example
|
|
1425
|
+
* ```typescript
|
|
1426
|
+
* db.order().select((o) => ({
|
|
1427
|
+
* idStr: expr.cast(o.id, { type: "varchar", length: 20 }),
|
|
1428
|
+
* }))
|
|
1429
|
+
* // SELECT CAST(id AS VARCHAR(20)) AS idStr
|
|
1430
|
+
* ```
|
|
1431
|
+
*/
|
|
1432
|
+
cast(source, targetType) {
|
|
1433
|
+
return new ExprUnit(dataTypeStrToColumnPrimitiveStr[targetType.type], {
|
|
1434
|
+
type: "cast",
|
|
1435
|
+
source: toExpr(source),
|
|
1436
|
+
targetType,
|
|
1437
|
+
});
|
|
1438
|
+
},
|
|
1439
|
+
/**
|
|
1440
|
+
* Scalar Subquery - Subquery that returns a single value in SELECT clause
|
|
1441
|
+
*
|
|
1442
|
+
* The subquery must return exactly one row and one column
|
|
1443
|
+
*
|
|
1444
|
+
* @param dataType - Data type of the returned value
|
|
1445
|
+
* @param queryable - Queryable that returns a scalar value
|
|
1446
|
+
* @returns Subquery result expression
|
|
1447
|
+
*
|
|
1448
|
+
* @example
|
|
1449
|
+
* ```typescript
|
|
1450
|
+
* db.user().select((u) => ({
|
|
1451
|
+
* id: u.id,
|
|
1452
|
+
* postCount: expr.subquery(
|
|
1453
|
+
* "number",
|
|
1454
|
+
* db.post()
|
|
1455
|
+
* .where((p) => [expr.eq(p.userId, u.id)])
|
|
1456
|
+
* .select(() => ({ cnt: expr.count() }))
|
|
1457
|
+
* ),
|
|
1458
|
+
* }))
|
|
1459
|
+
* // SELECT id, (SELECT COUNT(*) FROM Post WHERE userId = User.id) AS postCount
|
|
1460
|
+
* ```
|
|
1461
|
+
*/
|
|
1462
|
+
subquery(dataType, queryable) {
|
|
1463
|
+
return new ExprUnit(dataType, {
|
|
1464
|
+
type: "subquery",
|
|
1465
|
+
queryDef: queryable.getSelectQueryDef(),
|
|
1466
|
+
});
|
|
1467
|
+
},
|
|
1468
|
+
//#endregion
|
|
1469
|
+
//#region ========== SELECT - Window Functions ==========
|
|
1470
|
+
/**
|
|
1471
|
+
* ROW_NUMBER() - Row number within a partition
|
|
1472
|
+
*
|
|
1473
|
+
* Assigns sequential numbers starting from 1 within each partition
|
|
1474
|
+
*
|
|
1475
|
+
* @param spec - Window spec (partitionBy, orderBy)
|
|
1476
|
+
* @returns Row number (starting from 1)
|
|
1477
|
+
*
|
|
1478
|
+
* @example
|
|
1479
|
+
* ```typescript
|
|
1480
|
+
* db.order().select((o) => ({
|
|
1481
|
+
* ...o,
|
|
1482
|
+
* rowNum: expr.rowNumber({
|
|
1483
|
+
* partitionBy: [o.userId],
|
|
1484
|
+
* orderBy: [[o.createdAt, "DESC"]],
|
|
1485
|
+
* }),
|
|
1486
|
+
* }))
|
|
1487
|
+
* // SELECT *, ROW_NUMBER() OVER (PARTITION BY userId ORDER BY createdAt DESC)
|
|
1488
|
+
* ```
|
|
1489
|
+
*/
|
|
1490
|
+
rowNumber(spec) {
|
|
1491
|
+
return new ExprUnit("number", {
|
|
1492
|
+
type: "window",
|
|
1493
|
+
fn: { type: "rowNumber" },
|
|
1494
|
+
spec: toWinSpec(spec),
|
|
1495
|
+
});
|
|
1496
|
+
},
|
|
1497
|
+
/**
|
|
1498
|
+
* RANK() - Rank within a partition (ties get same rank, next rank is skipped)
|
|
1499
|
+
*
|
|
1500
|
+
* @param spec - Window spec (partitionBy, orderBy)
|
|
1501
|
+
* @returns Rank (skips after ties: 1, 1, 3)
|
|
1502
|
+
*
|
|
1503
|
+
* @example
|
|
1504
|
+
* ```typescript
|
|
1505
|
+
* db.student().select((s) => ({
|
|
1506
|
+
* name: s.name,
|
|
1507
|
+
* rank: expr.rank({
|
|
1508
|
+
* orderBy: [[s.score, "DESC"]],
|
|
1509
|
+
* }),
|
|
1510
|
+
* }))
|
|
1511
|
+
* ```
|
|
1512
|
+
*/
|
|
1513
|
+
rank(spec) {
|
|
1514
|
+
return new ExprUnit("number", {
|
|
1515
|
+
type: "window",
|
|
1516
|
+
fn: { type: "rank" },
|
|
1517
|
+
spec: toWinSpec(spec),
|
|
1518
|
+
});
|
|
1519
|
+
},
|
|
1520
|
+
/**
|
|
1521
|
+
* DENSE_RANK() - Dense rank within a partition (ties get same rank, next rank is consecutive)
|
|
1522
|
+
*
|
|
1523
|
+
* @param spec - Window spec (partitionBy, orderBy)
|
|
1524
|
+
* @returns Dense rank (consecutive after ties: 1, 1, 2)
|
|
1525
|
+
*
|
|
1526
|
+
* @example
|
|
1527
|
+
* ```typescript
|
|
1528
|
+
* db.student().select((s) => ({
|
|
1529
|
+
* name: s.name,
|
|
1530
|
+
* denseRank: expr.denseRank({
|
|
1531
|
+
* orderBy: [[s.score, "DESC"]],
|
|
1532
|
+
* }),
|
|
1533
|
+
* }))
|
|
1534
|
+
* ```
|
|
1535
|
+
*/
|
|
1536
|
+
denseRank(spec) {
|
|
1537
|
+
return new ExprUnit("number", {
|
|
1538
|
+
type: "window",
|
|
1539
|
+
fn: { type: "denseRank" },
|
|
1540
|
+
spec: toWinSpec(spec),
|
|
1541
|
+
});
|
|
1542
|
+
},
|
|
1543
|
+
/**
|
|
1544
|
+
* NTILE(n) - Split partition into n groups
|
|
1545
|
+
*
|
|
1546
|
+
* @param n - Number of groups to split into
|
|
1547
|
+
* @param spec - Window spec (partitionBy, orderBy)
|
|
1548
|
+
* @returns Group number (1 ~ n)
|
|
1549
|
+
*
|
|
1550
|
+
* @example
|
|
1551
|
+
* ```typescript
|
|
1552
|
+
* // Quartile split to find top 25%
|
|
1553
|
+
* db.user().select((u) => ({
|
|
1554
|
+
* name: u.name,
|
|
1555
|
+
* quartile: expr.ntile(4, {
|
|
1556
|
+
* orderBy: [[u.score, "DESC"]],
|
|
1557
|
+
* }),
|
|
1558
|
+
* }))
|
|
1559
|
+
* ```
|
|
1560
|
+
*/
|
|
1561
|
+
ntile(n, spec) {
|
|
1562
|
+
return new ExprUnit("number", {
|
|
1563
|
+
type: "window",
|
|
1564
|
+
fn: { type: "ntile", n },
|
|
1565
|
+
spec: toWinSpec(spec),
|
|
1566
|
+
});
|
|
1567
|
+
},
|
|
1568
|
+
/**
|
|
1569
|
+
* LAG() - Reference value from a previous row
|
|
1570
|
+
*
|
|
1571
|
+
* @param column - Column to reference
|
|
1572
|
+
* @param spec - Window spec (partitionBy, orderBy)
|
|
1573
|
+
* @param options - offset (default 1), default (default value when no previous row)
|
|
1574
|
+
* @returns Previous row's value (or default value/NULL)
|
|
1575
|
+
*
|
|
1576
|
+
* @example
|
|
1577
|
+
* ```typescript
|
|
1578
|
+
* db.stock().select((s) => ({
|
|
1579
|
+
* date: s.date,
|
|
1580
|
+
* price: s.price,
|
|
1581
|
+
* prevPrice: expr.lag(s.price, {
|
|
1582
|
+
* partitionBy: [s.symbol],
|
|
1583
|
+
* orderBy: [[s.date, "ASC"]],
|
|
1584
|
+
* }),
|
|
1585
|
+
* }))
|
|
1586
|
+
* ```
|
|
1587
|
+
*/
|
|
1588
|
+
lag(column, spec, options) {
|
|
1589
|
+
return new ExprUnit(column.dataType, {
|
|
1590
|
+
type: "window",
|
|
1591
|
+
fn: {
|
|
1592
|
+
type: "lag",
|
|
1593
|
+
column: toExpr(column),
|
|
1594
|
+
offset: options?.offset,
|
|
1595
|
+
default: options?.default != null ? toExpr(options.default) : undefined,
|
|
1596
|
+
},
|
|
1597
|
+
spec: toWinSpec(spec),
|
|
1598
|
+
});
|
|
1599
|
+
},
|
|
1600
|
+
/**
|
|
1601
|
+
* LEAD() - Reference value from a following row
|
|
1602
|
+
*
|
|
1603
|
+
* @param column - Column to reference
|
|
1604
|
+
* @param spec - Window spec (partitionBy, orderBy)
|
|
1605
|
+
* @param options - offset (default 1), default (default value when no following row)
|
|
1606
|
+
* @returns Following row's value (or default value/NULL)
|
|
1607
|
+
*
|
|
1608
|
+
* @example
|
|
1609
|
+
* ```typescript
|
|
1610
|
+
* db.stock().select((s) => ({
|
|
1611
|
+
* date: s.date,
|
|
1612
|
+
* price: s.price,
|
|
1613
|
+
* nextPrice: expr.lead(s.price, {
|
|
1614
|
+
* partitionBy: [s.symbol],
|
|
1615
|
+
* orderBy: [[s.date, "ASC"]],
|
|
1616
|
+
* }),
|
|
1617
|
+
* }))
|
|
1618
|
+
* ```
|
|
1619
|
+
*/
|
|
1620
|
+
lead(column, spec, options) {
|
|
1621
|
+
return new ExprUnit(column.dataType, {
|
|
1622
|
+
type: "window",
|
|
1623
|
+
fn: {
|
|
1624
|
+
type: "lead",
|
|
1625
|
+
column: toExpr(column),
|
|
1626
|
+
offset: options?.offset,
|
|
1627
|
+
default: options?.default != null ? toExpr(options.default) : undefined,
|
|
1628
|
+
},
|
|
1629
|
+
spec: toWinSpec(spec),
|
|
1630
|
+
});
|
|
1631
|
+
},
|
|
1632
|
+
/**
|
|
1633
|
+
* FIRST_VALUE() - First value in the partition/frame
|
|
1634
|
+
*
|
|
1635
|
+
* @param column - Column to reference
|
|
1636
|
+
* @param spec - Window spec (partitionBy, orderBy)
|
|
1637
|
+
* @returns First value
|
|
1638
|
+
*
|
|
1639
|
+
* @example
|
|
1640
|
+
* ```typescript
|
|
1641
|
+
* db.order().select((o) => ({
|
|
1642
|
+
* ...o,
|
|
1643
|
+
* firstOrderAmount: expr.firstValue(o.amount, {
|
|
1644
|
+
* partitionBy: [o.userId],
|
|
1645
|
+
* orderBy: [[o.createdAt, "ASC"]],
|
|
1646
|
+
* }),
|
|
1647
|
+
* }))
|
|
1648
|
+
* ```
|
|
1649
|
+
*/
|
|
1650
|
+
firstValue(column, spec) {
|
|
1651
|
+
return new ExprUnit(column.dataType, {
|
|
1652
|
+
type: "window",
|
|
1653
|
+
fn: { type: "firstValue", column: toExpr(column) },
|
|
1654
|
+
spec: toWinSpec(spec),
|
|
1655
|
+
});
|
|
1656
|
+
},
|
|
1657
|
+
/**
|
|
1658
|
+
* LAST_VALUE() - Last value in the partition/frame
|
|
1659
|
+
*
|
|
1660
|
+
* @param column - Column to reference
|
|
1661
|
+
* @param spec - Window spec (partitionBy, orderBy)
|
|
1662
|
+
* @returns Last value
|
|
1663
|
+
*
|
|
1664
|
+
* @example
|
|
1665
|
+
* ```typescript
|
|
1666
|
+
* db.order().select((o) => ({
|
|
1667
|
+
* ...o,
|
|
1668
|
+
* lastOrderAmount: expr.lastValue(o.amount, {
|
|
1669
|
+
* partitionBy: [o.userId],
|
|
1670
|
+
* orderBy: [[o.createdAt, "ASC"]],
|
|
1671
|
+
* }),
|
|
1672
|
+
* }))
|
|
1673
|
+
* ```
|
|
1674
|
+
*/
|
|
1675
|
+
lastValue(column, spec) {
|
|
1676
|
+
return new ExprUnit(column.dataType, {
|
|
1677
|
+
type: "window",
|
|
1678
|
+
fn: { type: "lastValue", column: toExpr(column) },
|
|
1679
|
+
spec: toWinSpec(spec),
|
|
1680
|
+
});
|
|
1681
|
+
},
|
|
1682
|
+
/**
|
|
1683
|
+
* SUM() OVER - Window sum
|
|
1684
|
+
*
|
|
1685
|
+
* @param column - Column to sum
|
|
1686
|
+
* @param spec - Window spec (partitionBy, orderBy)
|
|
1687
|
+
* @returns Sum within window
|
|
1688
|
+
*
|
|
1689
|
+
* @example
|
|
1690
|
+
* ```typescript
|
|
1691
|
+
* // Running total
|
|
1692
|
+
* db.order().select((o) => ({
|
|
1693
|
+
* ...o,
|
|
1694
|
+
* runningTotal: expr.sumOver(o.amount, {
|
|
1695
|
+
* partitionBy: [o.userId],
|
|
1696
|
+
* orderBy: [[o.createdAt, "ASC"]],
|
|
1697
|
+
* }),
|
|
1698
|
+
* }))
|
|
1699
|
+
* ```
|
|
1700
|
+
*/
|
|
1701
|
+
sumOver(column, spec) {
|
|
1702
|
+
return new ExprUnit("number", {
|
|
1703
|
+
type: "window",
|
|
1704
|
+
fn: { type: "sum", column: toExpr(column) },
|
|
1705
|
+
spec: toWinSpec(spec),
|
|
1706
|
+
});
|
|
1707
|
+
},
|
|
1708
|
+
/**
|
|
1709
|
+
* AVG() OVER - Window average
|
|
1710
|
+
*
|
|
1711
|
+
* @param column - Column to average
|
|
1712
|
+
* @param spec - Window spec (partitionBy, orderBy)
|
|
1713
|
+
* @returns Average within window
|
|
1714
|
+
*
|
|
1715
|
+
* @example
|
|
1716
|
+
* ```typescript
|
|
1717
|
+
* // Moving average
|
|
1718
|
+
* db.stock().select((s) => ({
|
|
1719
|
+
* ...s,
|
|
1720
|
+
* movingAvg: expr.avgOver(s.price, {
|
|
1721
|
+
* partitionBy: [s.symbol],
|
|
1722
|
+
* orderBy: [[s.date, "ASC"]],
|
|
1723
|
+
* }),
|
|
1724
|
+
* }))
|
|
1725
|
+
* ```
|
|
1726
|
+
*/
|
|
1727
|
+
avgOver(column, spec) {
|
|
1728
|
+
return new ExprUnit("number", {
|
|
1729
|
+
type: "window",
|
|
1730
|
+
fn: { type: "avg", column: toExpr(column) },
|
|
1731
|
+
spec: toWinSpec(spec),
|
|
1732
|
+
});
|
|
1733
|
+
},
|
|
1734
|
+
/**
|
|
1735
|
+
* COUNT() OVER - Window count
|
|
1736
|
+
*
|
|
1737
|
+
* @param spec - Window spec (partitionBy, orderBy)
|
|
1738
|
+
* @param column - Column to count (all rows if omitted)
|
|
1739
|
+
* @returns Row count within window
|
|
1740
|
+
*
|
|
1741
|
+
* @example
|
|
1742
|
+
* ```typescript
|
|
1743
|
+
* db.order().select((o) => ({
|
|
1744
|
+
* ...o,
|
|
1745
|
+
* totalOrdersPerUser: expr.countOver({
|
|
1746
|
+
* partitionBy: [o.userId],
|
|
1747
|
+
* }),
|
|
1748
|
+
* }))
|
|
1749
|
+
* ```
|
|
1750
|
+
*/
|
|
1751
|
+
countOver(spec, column) {
|
|
1752
|
+
return new ExprUnit("number", {
|
|
1753
|
+
type: "window",
|
|
1754
|
+
fn: { type: "count", column: column != null ? toExpr(column) : undefined },
|
|
1755
|
+
spec: toWinSpec(spec),
|
|
1756
|
+
});
|
|
1757
|
+
},
|
|
1758
|
+
/**
|
|
1759
|
+
* MIN() OVER - Window minimum
|
|
1760
|
+
*
|
|
1761
|
+
* @param column - Column to find minimum of
|
|
1762
|
+
* @param spec - Window spec (partitionBy, orderBy)
|
|
1763
|
+
* @returns Minimum value within window
|
|
1764
|
+
*
|
|
1765
|
+
* @example
|
|
1766
|
+
* ```typescript
|
|
1767
|
+
* db.stock().select((s) => ({
|
|
1768
|
+
* ...s,
|
|
1769
|
+
* minPriceInPeriod: expr.minOver(s.price, {
|
|
1770
|
+
* partitionBy: [s.symbol],
|
|
1771
|
+
* }),
|
|
1772
|
+
* }))
|
|
1773
|
+
* ```
|
|
1774
|
+
*/
|
|
1775
|
+
minOver(column, spec) {
|
|
1776
|
+
return new ExprUnit(column.dataType, {
|
|
1777
|
+
type: "window",
|
|
1778
|
+
fn: { type: "min", column: toExpr(column) },
|
|
1779
|
+
spec: toWinSpec(spec),
|
|
1780
|
+
});
|
|
1781
|
+
},
|
|
1782
|
+
/**
|
|
1783
|
+
* MAX() OVER - Window maximum
|
|
1784
|
+
*
|
|
1785
|
+
* @param column - Column to find maximum of
|
|
1786
|
+
* @param spec - Window spec (partitionBy, orderBy)
|
|
1787
|
+
* @returns Maximum value within window
|
|
1788
|
+
*
|
|
1789
|
+
* @example
|
|
1790
|
+
* ```typescript
|
|
1791
|
+
* db.stock().select((s) => ({
|
|
1792
|
+
* ...s,
|
|
1793
|
+
* maxPriceInPeriod: expr.maxOver(s.price, {
|
|
1794
|
+
* partitionBy: [s.symbol],
|
|
1795
|
+
* }),
|
|
1796
|
+
* }))
|
|
1797
|
+
* ```
|
|
1798
|
+
*/
|
|
1799
|
+
maxOver(column, spec) {
|
|
1800
|
+
return new ExprUnit(column.dataType, {
|
|
1801
|
+
type: "window",
|
|
1802
|
+
fn: { type: "max", column: toExpr(column) },
|
|
1803
|
+
spec: toWinSpec(spec),
|
|
1804
|
+
});
|
|
1805
|
+
},
|
|
1806
|
+
//#endregion
|
|
1807
|
+
//#region ========== Helper ==========
|
|
1808
|
+
/**
|
|
1809
|
+
* Transform ExprInput to Expr (internal use)
|
|
1810
|
+
*
|
|
1811
|
+
* @param value - Value to transform
|
|
1812
|
+
* @returns Expr JSON AST
|
|
1813
|
+
*/
|
|
1814
|
+
toExpr(value) {
|
|
1815
|
+
return toExpr(value);
|
|
1816
|
+
},
|
|
1817
|
+
//#endregion
|
|
1789
1818
|
};
|
|
1790
1819
|
function coalesce(...args) {
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1820
|
+
return new ExprUnit(findDataType(args), {
|
|
1821
|
+
type: "coalesce",
|
|
1822
|
+
args: args.map((a) => toExpr(a)),
|
|
1823
|
+
});
|
|
1795
1824
|
}
|
|
1796
1825
|
function createSwitchBuilder() {
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1826
|
+
const cases = [];
|
|
1827
|
+
const thenValues = []; // then 값 저장
|
|
1828
|
+
return {
|
|
1829
|
+
case(condition, then) {
|
|
1830
|
+
cases.push({
|
|
1831
|
+
when: condition.expr,
|
|
1832
|
+
then: toExpr(then),
|
|
1833
|
+
});
|
|
1834
|
+
thenValues.push(then);
|
|
1835
|
+
return this;
|
|
1836
|
+
},
|
|
1837
|
+
default(value) {
|
|
1838
|
+
const allValues = [...thenValues, value];
|
|
1839
|
+
// 1. ExprUnit에서 dataType 찾기
|
|
1840
|
+
const exprUnit = allValues.find((v) => v instanceof ExprUnit);
|
|
1841
|
+
if (exprUnit) {
|
|
1842
|
+
return new ExprUnit(exprUnit.dataType, {
|
|
1843
|
+
type: "switch",
|
|
1844
|
+
cases,
|
|
1845
|
+
else: toExpr(value),
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
// 2. non-null 리터럴에서 추론
|
|
1849
|
+
const nonNullLiteral = allValues.find((v) => v != null);
|
|
1850
|
+
if (nonNullLiteral == null) {
|
|
1851
|
+
throw new Error("At least one of switch's case/default must be non-null.");
|
|
1852
|
+
}
|
|
1853
|
+
return new ExprUnit(inferColumnPrimitiveStr(nonNullLiteral), {
|
|
1854
|
+
type: "switch",
|
|
1855
|
+
cases,
|
|
1856
|
+
else: toExpr(value),
|
|
1857
|
+
});
|
|
1858
|
+
},
|
|
1859
|
+
};
|
|
1829
1860
|
}
|
|
1830
|
-
function toExpr(value) {
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1861
|
+
export function toExpr(value) {
|
|
1862
|
+
if (value instanceof ExprUnit) {
|
|
1863
|
+
return value.expr;
|
|
1864
|
+
}
|
|
1865
|
+
return { type: "value", value };
|
|
1835
1866
|
}
|
|
1836
1867
|
function findDataType(args) {
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1868
|
+
const exprUnit = args.find((a) => a instanceof ExprUnit);
|
|
1869
|
+
if (!exprUnit) {
|
|
1870
|
+
throw new Error("At least one of the arguments must be an ExprUnit.");
|
|
1871
|
+
}
|
|
1872
|
+
return exprUnit.dataType;
|
|
1842
1873
|
}
|
|
1843
1874
|
function toWinSpec(spec) {
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1875
|
+
const result = {};
|
|
1876
|
+
if (spec.partitionBy != null) {
|
|
1877
|
+
result.partitionBy = spec.partitionBy.map((e) => toExpr(e));
|
|
1878
|
+
}
|
|
1879
|
+
if (spec.orderBy != null) {
|
|
1880
|
+
result.orderBy = spec.orderBy.map(([e, dir]) => [toExpr(e), dir]);
|
|
1881
|
+
}
|
|
1882
|
+
return result;
|
|
1852
1883
|
}
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
toExpr
|
|
1856
|
-
};
|
|
1857
|
-
//# sourceMappingURL=expr.js.map
|
|
1884
|
+
//#endregion
|
|
1885
|
+
//# sourceMappingURL=expr.js.map
|