metal-orm 1.1.11 → 1.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +758 -769
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +100 -96
- package/scripts/generate-entities/schema.mjs +122 -122
- package/scripts/generate-entities/tree-detection.mjs +92 -92
- package/scripts/inflection/pt-br.mjs +468 -468
- package/scripts/run-eslint.mjs +33 -34
- package/scripts/show-sql-seed.sql +102 -0
- package/scripts/show-sql.mjs +321 -325
- package/src/codegen/naming-strategy.ts +54 -54
- package/src/codegen/typescript.ts +482 -482
- package/src/core/ast/adapters.ts +1 -1
- package/src/core/ast/builders.ts +123 -123
- package/src/core/ast/expression-builders.ts +745 -745
- package/src/core/ast/expression-nodes.ts +304 -304
- package/src/core/ast/expression-visitor.ts +212 -212
- package/src/core/ast/expression.ts +20 -20
- package/src/core/ast/helpers.ts +14 -14
- package/src/core/ast/join.ts +18 -18
- package/src/core/ast/query.ts +200 -200
- package/src/core/ddl/dialects/base-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/render-reference.test.ts +70 -70
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -1
- package/src/core/ddl/introspect/catalogs/index.ts +5 -5
- package/src/core/ddl/introspect/catalogs/postgres.ts +146 -146
- package/src/core/ddl/introspect/context.ts +15 -15
- package/src/core/ddl/introspect/functions/postgres.ts +35 -35
- package/src/core/ddl/introspect/registry.ts +40 -40
- package/src/core/ddl/introspect/run-select.ts +35 -35
- package/src/core/ddl/introspect/utils.ts +56 -56
- package/src/core/ddl/naming-strategy.ts +16 -16
- package/src/core/ddl/schema-dialect.ts +56 -56
- package/src/core/ddl/schema-diff.ts +234 -234
- package/src/core/ddl/schema-generator.ts +1 -1
- package/src/core/ddl/schema-introspect.ts +25 -25
- package/src/core/ddl/sql-writing.ts +171 -171
- package/src/core/dialect/abstract.ts +541 -541
- package/src/core/dialect/base/cte-compiler.ts +33 -33
- package/src/core/dialect/base/function-table-formatter.ts +103 -103
- package/src/core/dialect/base/groupby-compiler.ts +21 -21
- package/src/core/dialect/base/join-compiler.ts +25 -25
- package/src/core/dialect/base/orderby-compiler.ts +35 -35
- package/src/core/dialect/base/pagination-strategy.ts +32 -32
- package/src/core/dialect/base/returning-strategy.ts +57 -57
- package/src/core/dialect/base/sql-dialect.ts +294 -294
- package/src/core/dialect/dialect-factory.ts +91 -91
- package/src/core/dialect/mssql/functions.ts +120 -120
- package/src/core/dialect/mssql/index.ts +317 -317
- package/src/core/dialect/mysql/functions.ts +103 -103
- package/src/core/dialect/mysql/index.ts +105 -105
- package/src/core/dialect/postgres/functions.ts +108 -108
- package/src/core/dialect/postgres/index.ts +91 -91
- package/src/core/dialect/sqlite/functions.ts +129 -129
- package/src/core/dialect/sqlite/index.ts +67 -67
- package/src/core/driver/database-driver.ts +19 -19
- package/src/core/driver/mssql-driver.ts +23 -23
- package/src/core/driver/mysql-driver.ts +23 -23
- package/src/core/driver/postgres-driver.ts +23 -23
- package/src/core/driver/sqlite-driver.ts +23 -23
- package/src/core/execution/db-executor.ts +92 -92
- package/src/core/execution/executors/better-sqlite3-executor.ts +90 -90
- package/src/core/execution/executors/mssql-executor.ts +184 -184
- package/src/core/execution/executors/mysql-executor.ts +88 -88
- package/src/core/execution/executors/postgres-executor.ts +26 -26
- package/src/core/execution/executors/sqlite-executor.ts +42 -42
- package/src/core/execution/pooling/pool-types.ts +30 -30
- package/src/core/execution/pooling/pool.ts +275 -275
- package/src/core/functions/array.ts +35 -35
- package/src/core/functions/control-flow.ts +79 -79
- package/src/core/functions/datetime.ts +237 -237
- package/src/core/functions/json.ts +70 -70
- package/src/core/functions/numeric.ts +297 -297
- package/src/core/functions/text.ts +334 -334
- package/src/core/functions/types.ts +33 -33
- package/src/core/hydration/types.ts +51 -51
- package/src/core/sql/sql-operator-config.ts +39 -39
- package/src/core/sql/sql.ts +152 -152
- package/src/decorators/column-decorator.ts +87 -87
- package/src/decorators/decorator-metadata.ts +143 -143
- package/src/decorators/entity.ts +93 -93
- package/src/decorators/index.ts +39 -39
- package/src/decorators/relations.ts +231 -231
- package/src/decorators/transformers/built-in/string-transformers.ts +231 -231
- package/src/decorators/transformers/transformer-decorators.ts +134 -134
- package/src/decorators/transformers/transformer-executor.ts +171 -171
- package/src/decorators/transformers/transformer-metadata.ts +73 -73
- package/src/decorators/validators/built-in/br-cep-validator.ts +55 -55
- package/src/decorators/validators/built-in/br-cnpj-validator.ts +105 -105
- package/src/decorators/validators/built-in/br-cpf-validator.ts +103 -103
- package/src/decorators/validators/country-validator-registry.ts +64 -64
- package/src/decorators/validators/country-validators-decorators.ts +203 -203
- package/src/decorators/validators/country-validators.ts +105 -105
- package/src/decorators/validators/validator-metadata.ts +59 -59
- package/src/dto/dto-types.ts +226 -226
- package/src/dto/filter-types.ts +141 -141
- package/src/dto/index.ts +95 -95
- package/src/dto/openapi/generators/base.ts +29 -29
- package/src/dto/openapi/generators/column.ts +37 -37
- package/src/dto/openapi/generators/dto.ts +91 -91
- package/src/dto/openapi/generators/filter.ts +75 -75
- package/src/dto/openapi/generators/nested-dto.ts +618 -618
- package/src/dto/openapi/generators/pagination.ts +111 -111
- package/src/dto/openapi/generators/relation-filter.ts +228 -228
- package/src/dto/openapi/index.ts +12 -12
- package/src/dto/openapi/types.ts +101 -101
- package/src/dto/openapi/utilities.ts +95 -95
- package/src/dto/pagination-utils.ts +150 -150
- package/src/dto/transform.ts +197 -197
- package/src/index.ts +86 -86
- package/src/orm/als.ts +58 -58
- package/src/orm/domain-event-bus.ts +119 -119
- package/src/orm/entity-context.ts +86 -86
- package/src/orm/entity-materializer.ts +159 -159
- package/src/orm/entity-meta.ts +97 -97
- package/src/orm/entity-registry.ts +39 -39
- package/src/orm/entity-relation-cache.ts +39 -39
- package/src/orm/execute.ts +194 -194
- package/src/orm/execution-context.ts +18 -18
- package/src/orm/hydration-context.ts +26 -26
- package/src/orm/hydration.ts +112 -112
- package/src/orm/identity-map.ts +61 -61
- package/src/orm/interceptor-pipeline.ts +16 -16
- package/src/orm/lazy-batch/belongs-to-many.ts +119 -119
- package/src/orm/lazy-batch/belongs-to.ts +108 -108
- package/src/orm/lazy-batch/has-many.ts +69 -69
- package/src/orm/lazy-batch/has-one.ts +68 -68
- package/src/orm/lazy-batch/shared.ts +125 -125
- package/src/orm/orm-session.ts +620 -620
- package/src/orm/orm.ts +34 -34
- package/src/orm/pooled-executor-factory.ts +121 -121
- package/src/orm/query-logger.ts +40 -40
- package/src/orm/relation-change-processor.ts +420 -420
- package/src/orm/relations/belongs-to.ts +143 -143
- package/src/orm/relations/has-many.ts +204 -204
- package/src/orm/relations/has-one.ts +174 -174
- package/src/orm/relations/many-to-many.ts +288 -288
- package/src/orm/relations/morph-many.ts +156 -156
- package/src/orm/relations/morph-one.ts +151 -151
- package/src/orm/relations/morph-to.ts +162 -162
- package/src/orm/runtime-types.ts +92 -92
- package/src/orm/save-graph.ts +561 -561
- package/src/orm/transaction-runner.ts +24 -24
- package/src/orm/unit-of-work.ts +426 -426
- package/src/query/index.ts +69 -69
- package/src/query/target.ts +46 -46
- package/src/query-builder/column-selector.ts +81 -81
- package/src/query-builder/delete.ts +141 -141
- package/src/query-builder/insert-query-state.ts +149 -149
- package/src/query-builder/insert.ts +117 -117
- package/src/query-builder/query-ast-service.ts +287 -287
- package/src/query-builder/query-resolution.ts +78 -78
- package/src/query-builder/raw-column-parser.ts +38 -38
- package/src/query-builder/relation-filter-utils.ts +153 -153
- package/src/query-builder/relation-join-planner.ts +10 -10
- package/src/query-builder/relation-manager.ts +87 -87
- package/src/query-builder/relation-projection-helper.ts +102 -102
- package/src/query-builder/relation-types.ts +9 -9
- package/src/query-builder/relation-utils.ts +15 -15
- package/src/query-builder/select/cte-facet.ts +40 -40
- package/src/query-builder/select/from-facet.ts +80 -80
- package/src/query-builder/select/join-facet.ts +62 -62
- package/src/query-builder/select/predicate-facet.ts +104 -104
- package/src/query-builder/select/projection-facet.ts +70 -70
- package/src/query-builder/select/relation-facet.ts +81 -81
- package/src/query-builder/select/setop-facet.ts +36 -36
- package/src/query-builder/select-helpers.ts +1 -1
- package/src/query-builder/select-query-builder-deps.ts +130 -130
- package/src/query-builder/select-query-state.ts +207 -207
- package/src/query-builder/update.ts +151 -151
- package/src/schema/column-types.ts +322 -322
- package/src/schema/table-guards.ts +37 -37
- package/src/schema/table.ts +281 -281
- package/src/tree/index.ts +13 -13
- package/src/tree/nested-set-strategy.ts +545 -545
- package/src/tree/tree-decorator.ts +397 -397
- package/src/tree/tree-manager.ts +754 -754
- package/src/tree/tree-query.ts +427 -427
- package/src/tree/tree-types.ts +299 -299
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import { SelectQueryNode } from './query.js';
|
|
2
|
-
import { SqlOperator, BitwiseOperator } from '../sql/sql.js';
|
|
3
|
-
import { ColumnRef } from './types.js';
|
|
4
|
-
import {
|
|
5
|
-
ColumnNode,
|
|
6
|
-
LiteralNode,
|
|
7
|
-
JsonPathNode,
|
|
8
|
-
OperandNode,
|
|
9
|
-
CaseExpressionNode,
|
|
10
|
-
CastExpressionNode,
|
|
11
|
-
BinaryExpressionNode,
|
|
1
|
+
import { SelectQueryNode } from './query.js';
|
|
2
|
+
import { SqlOperator, BitwiseOperator } from '../sql/sql.js';
|
|
3
|
+
import { ColumnRef } from './types.js';
|
|
4
|
+
import {
|
|
5
|
+
ColumnNode,
|
|
6
|
+
LiteralNode,
|
|
7
|
+
JsonPathNode,
|
|
8
|
+
OperandNode,
|
|
9
|
+
CaseExpressionNode,
|
|
10
|
+
CastExpressionNode,
|
|
11
|
+
BinaryExpressionNode,
|
|
12
12
|
ExpressionNode,
|
|
13
13
|
LogicalExpressionNode,
|
|
14
14
|
NotExpressionNode,
|
|
15
15
|
NullExpressionNode,
|
|
16
16
|
InExpressionNode,
|
|
17
|
-
ExistsExpressionNode,
|
|
18
|
-
InExpressionRight,
|
|
19
|
-
ScalarSubqueryNode,
|
|
20
|
-
BetweenExpressionNode,
|
|
21
|
-
isOperandNode,
|
|
22
|
-
AliasRefNode,
|
|
23
|
-
ArithmeticExpressionNode,
|
|
24
|
-
BitwiseExpressionNode,
|
|
25
|
-
CollateExpressionNode,
|
|
26
|
-
IsDistinctExpressionNode
|
|
27
|
-
} from './expression-nodes.js';
|
|
28
|
-
|
|
17
|
+
ExistsExpressionNode,
|
|
18
|
+
InExpressionRight,
|
|
19
|
+
ScalarSubqueryNode,
|
|
20
|
+
BetweenExpressionNode,
|
|
21
|
+
isOperandNode,
|
|
22
|
+
AliasRefNode,
|
|
23
|
+
ArithmeticExpressionNode,
|
|
24
|
+
BitwiseExpressionNode,
|
|
25
|
+
CollateExpressionNode,
|
|
26
|
+
IsDistinctExpressionNode
|
|
27
|
+
} from './expression-nodes.js';
|
|
28
|
+
|
|
29
29
|
export type LiteralValue = LiteralNode['value'];
|
|
30
30
|
export type ValueOperandInput = OperandNode | LiteralValue;
|
|
31
31
|
type ScalarComparable = string | number | boolean | Date | Buffer | null;
|
|
@@ -33,53 +33,53 @@ type OrderedComparable = string | number | Date | Buffer;
|
|
|
33
33
|
type NotArray<T> = T extends readonly unknown[] ? never : T;
|
|
34
34
|
|
|
35
35
|
export type TypedLike<T> = { tsType?: T } | { __tsType: T };
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Type guard to check if a value is a literal value
|
|
39
|
-
*/
|
|
40
|
-
const isLiteralValue = (value: unknown): value is LiteralValue =>
|
|
41
|
-
value === null ||
|
|
42
|
-
typeof value === 'string' ||
|
|
43
|
-
typeof value === 'number' ||
|
|
44
|
-
typeof value === 'boolean' ||
|
|
45
|
-
value instanceof Date ||
|
|
46
|
-
Buffer.isBuffer(value);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Converts a primitive value to a LiteralNode
|
|
51
|
-
*/
|
|
52
|
-
const toLiteralNode = (value: LiteralValue): LiteralNode => ({
|
|
53
|
-
type: 'Literal',
|
|
54
|
-
value: value instanceof Date ? value.toISOString() : value
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Converts a ColumnRef to a ColumnNode
|
|
59
|
-
* @throws Error if the ColumnRef doesn't have a table specified
|
|
60
|
-
*/
|
|
61
|
-
const columnRefToNode = (col: ColumnRef): ColumnNode => {
|
|
62
|
-
if (!col.table) {
|
|
63
|
-
throw new Error(
|
|
64
|
-
`Column "${col.name}" requires a table reference. ` +
|
|
65
|
-
`Use columnOperand with a fully qualified ColumnRef or ColumnNode.`
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
return { type: 'Column', table: col.table, name: col.name };
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Unified conversion function: converts any valid input to an OperandNode
|
|
73
|
-
* @param value - Value to convert (OperandNode, ColumnRef, or literal value)
|
|
74
|
-
* @returns OperandNode representing the value
|
|
75
|
-
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Type guard to check if a value is a literal value
|
|
39
|
+
*/
|
|
40
|
+
const isLiteralValue = (value: unknown): value is LiteralValue =>
|
|
41
|
+
value === null ||
|
|
42
|
+
typeof value === 'string' ||
|
|
43
|
+
typeof value === 'number' ||
|
|
44
|
+
typeof value === 'boolean' ||
|
|
45
|
+
value instanceof Date ||
|
|
46
|
+
Buffer.isBuffer(value);
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Converts a primitive value to a LiteralNode
|
|
51
|
+
*/
|
|
52
|
+
const toLiteralNode = (value: LiteralValue): LiteralNode => ({
|
|
53
|
+
type: 'Literal',
|
|
54
|
+
value: value instanceof Date ? value.toISOString() : value
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Converts a ColumnRef to a ColumnNode
|
|
59
|
+
* @throws Error if the ColumnRef doesn't have a table specified
|
|
60
|
+
*/
|
|
61
|
+
const columnRefToNode = (col: ColumnRef): ColumnNode => {
|
|
62
|
+
if (!col.table) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Column "${col.name}" requires a table reference. ` +
|
|
65
|
+
`Use columnOperand with a fully qualified ColumnRef or ColumnNode.`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return { type: 'Column', table: col.table, name: col.name };
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Unified conversion function: converts any valid input to an OperandNode
|
|
73
|
+
* @param value - Value to convert (OperandNode, ColumnRef, or literal value)
|
|
74
|
+
* @returns OperandNode representing the value
|
|
75
|
+
*/
|
|
76
76
|
const toOperandNode = (value: OperandNode | ColumnRef | LiteralValue): OperandNode => {
|
|
77
77
|
// Already an operand node
|
|
78
78
|
if (isOperandNode(value)) {
|
|
79
79
|
return value;
|
|
80
80
|
}
|
|
81
|
-
|
|
82
|
-
// Literal value
|
|
81
|
+
|
|
82
|
+
// Literal value
|
|
83
83
|
if (isLiteralValue(value)) {
|
|
84
84
|
return toLiteralNode(value);
|
|
85
85
|
}
|
|
@@ -94,255 +94,255 @@ const toOperandNode = (value: OperandNode | ColumnRef | LiteralValue): OperandNo
|
|
|
94
94
|
// Must be ColumnRef
|
|
95
95
|
return columnRefToNode(value as ColumnRef);
|
|
96
96
|
};
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Converts a primitive or existing operand into an operand node
|
|
100
|
-
* @param value - Value or operand to normalize
|
|
101
|
-
* @returns OperandNode representing the value
|
|
102
|
-
*/
|
|
103
|
-
export const valueToOperand = (value: ValueOperandInput): OperandNode => {
|
|
104
|
-
if (isOperandNode(value)) {
|
|
105
|
-
return value;
|
|
106
|
-
}
|
|
107
|
-
return toLiteralNode(value);
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Converts various input types to an OperandNode
|
|
112
|
-
*/
|
|
113
|
-
const toOperand = (val: OperandNode | ColumnRef | LiteralValue): OperandNode => toOperandNode(val);
|
|
114
|
-
|
|
115
|
-
export const isValueOperandInput = (value: unknown): value is ValueOperandInput =>
|
|
116
|
-
isOperandNode(value) || isLiteralValue(value);
|
|
117
|
-
|
|
118
|
-
export type SelectQueryInput = SelectQueryNode | { getAST(): SelectQueryNode };
|
|
119
|
-
|
|
120
|
-
const hasQueryAst = (value: SelectQueryInput): value is { getAST(): SelectQueryNode } =>
|
|
121
|
-
typeof (value as { getAST?: unknown }).getAST === 'function';
|
|
122
|
-
|
|
123
|
-
const resolveSelectQueryNode = (query: SelectQueryInput): SelectQueryNode =>
|
|
124
|
-
hasQueryAst(query) ? query.getAST() : query;
|
|
125
|
-
|
|
126
|
-
const toScalarSubqueryNode = (query: SelectQueryInput): ScalarSubqueryNode => ({
|
|
127
|
-
type: 'ScalarSubquery',
|
|
128
|
-
query: resolveSelectQueryNode(query)
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Converts a ColumnRef or ColumnNode to a ColumnNode
|
|
133
|
-
* @param col - Column reference or node
|
|
134
|
-
* @returns ColumnNode
|
|
135
|
-
* @throws Error if ColumnRef doesn't have a table specified
|
|
136
|
-
*/
|
|
137
|
-
export const columnOperand = (col: ColumnRef | ColumnNode): ColumnNode => {
|
|
138
|
-
if (isOperandNode(col) && col.type === 'Column') {
|
|
139
|
-
return col;
|
|
140
|
-
}
|
|
141
|
-
return columnRefToNode(col as ColumnRef);
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Marks a column reference as an outer-scope reference for correlated subqueries.
|
|
146
|
-
* Primarily semantic; SQL rendering still uses the provided table/alias name.
|
|
147
|
-
*/
|
|
148
|
-
export const outerRef = (col: ColumnRef | ColumnNode): ColumnNode => ({
|
|
149
|
-
...columnOperand(col),
|
|
150
|
-
scope: 'outer'
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* References a SELECT alias (useful for ORDER BY / GROUP BY).
|
|
155
|
-
*/
|
|
156
|
-
export const aliasRef = (name: string): AliasRefNode => ({
|
|
157
|
-
type: 'AliasRef',
|
|
158
|
-
name
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Creates an outer-scoped column reference using a specific table or alias name.
|
|
163
|
-
*/
|
|
164
|
-
export const correlateBy = (table: string, column: string): ColumnNode => outerRef({ name: column, table });
|
|
165
|
-
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Converts a primitive or existing operand into an operand node
|
|
100
|
+
* @param value - Value or operand to normalize
|
|
101
|
+
* @returns OperandNode representing the value
|
|
102
|
+
*/
|
|
103
|
+
export const valueToOperand = (value: ValueOperandInput): OperandNode => {
|
|
104
|
+
if (isOperandNode(value)) {
|
|
105
|
+
return value;
|
|
106
|
+
}
|
|
107
|
+
return toLiteralNode(value);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Converts various input types to an OperandNode
|
|
112
|
+
*/
|
|
113
|
+
const toOperand = (val: OperandNode | ColumnRef | LiteralValue): OperandNode => toOperandNode(val);
|
|
114
|
+
|
|
115
|
+
export const isValueOperandInput = (value: unknown): value is ValueOperandInput =>
|
|
116
|
+
isOperandNode(value) || isLiteralValue(value);
|
|
117
|
+
|
|
118
|
+
export type SelectQueryInput = SelectQueryNode | { getAST(): SelectQueryNode };
|
|
119
|
+
|
|
120
|
+
const hasQueryAst = (value: SelectQueryInput): value is { getAST(): SelectQueryNode } =>
|
|
121
|
+
typeof (value as { getAST?: unknown }).getAST === 'function';
|
|
122
|
+
|
|
123
|
+
const resolveSelectQueryNode = (query: SelectQueryInput): SelectQueryNode =>
|
|
124
|
+
hasQueryAst(query) ? query.getAST() : query;
|
|
125
|
+
|
|
126
|
+
const toScalarSubqueryNode = (query: SelectQueryInput): ScalarSubqueryNode => ({
|
|
127
|
+
type: 'ScalarSubquery',
|
|
128
|
+
query: resolveSelectQueryNode(query)
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Converts a ColumnRef or ColumnNode to a ColumnNode
|
|
133
|
+
* @param col - Column reference or node
|
|
134
|
+
* @returns ColumnNode
|
|
135
|
+
* @throws Error if ColumnRef doesn't have a table specified
|
|
136
|
+
*/
|
|
137
|
+
export const columnOperand = (col: ColumnRef | ColumnNode): ColumnNode => {
|
|
138
|
+
if (isOperandNode(col) && col.type === 'Column') {
|
|
139
|
+
return col;
|
|
140
|
+
}
|
|
141
|
+
return columnRefToNode(col as ColumnRef);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Marks a column reference as an outer-scope reference for correlated subqueries.
|
|
146
|
+
* Primarily semantic; SQL rendering still uses the provided table/alias name.
|
|
147
|
+
*/
|
|
148
|
+
export const outerRef = (col: ColumnRef | ColumnNode): ColumnNode => ({
|
|
149
|
+
...columnOperand(col),
|
|
150
|
+
scope: 'outer'
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* References a SELECT alias (useful for ORDER BY / GROUP BY).
|
|
155
|
+
*/
|
|
156
|
+
export const aliasRef = (name: string): AliasRefNode => ({
|
|
157
|
+
type: 'AliasRef',
|
|
158
|
+
name
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Creates an outer-scoped column reference using a specific table or alias name.
|
|
163
|
+
*/
|
|
164
|
+
export const correlateBy = (table: string, column: string): ColumnNode => outerRef({ name: column, table });
|
|
165
|
+
|
|
166
166
|
const createBinaryExpression = (
|
|
167
167
|
operator: SqlOperator,
|
|
168
168
|
left: OperandNode | ColumnRef,
|
|
169
169
|
right: OperandNode | ColumnRef | LiteralValue,
|
|
170
170
|
escape?: string
|
|
171
171
|
): BinaryExpressionNode => {
|
|
172
|
-
const node: BinaryExpressionNode = {
|
|
173
|
-
type: 'BinaryExpression',
|
|
174
|
-
left: toOperandNode(left),
|
|
175
|
-
operator,
|
|
176
|
-
right: toOperand(right)
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
if (escape !== undefined) {
|
|
180
|
-
node.escape = toLiteralNode(escape);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return node;
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Creates an equality expression (`left = right`).
|
|
188
|
-
*
|
|
189
|
-
* Supports type safety when used with `ColumnDef` objects containing strict type information.
|
|
190
|
-
*
|
|
191
|
-
* @param left - The left operand (column or value).
|
|
192
|
-
* @param right - The right operand (column or value).
|
|
193
|
-
* @returns A `BinaryExpressionNode` representing the equality check.
|
|
194
|
-
*
|
|
195
|
-
* @example
|
|
196
|
-
* // Basic usage
|
|
197
|
-
* eq(users.id, 1);
|
|
198
|
-
*
|
|
199
|
-
* // With strict typing (typescript will error if types mismatch)
|
|
200
|
-
* eq(users.firstName, 'Ada');
|
|
201
|
-
*/
|
|
172
|
+
const node: BinaryExpressionNode = {
|
|
173
|
+
type: 'BinaryExpression',
|
|
174
|
+
left: toOperandNode(left),
|
|
175
|
+
operator,
|
|
176
|
+
right: toOperand(right)
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
if (escape !== undefined) {
|
|
180
|
+
node.escape = toLiteralNode(escape);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return node;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Creates an equality expression (`left = right`).
|
|
188
|
+
*
|
|
189
|
+
* Supports type safety when used with `ColumnDef` objects containing strict type information.
|
|
190
|
+
*
|
|
191
|
+
* @param left - The left operand (column or value).
|
|
192
|
+
* @param right - The right operand (column or value).
|
|
193
|
+
* @returns A `BinaryExpressionNode` representing the equality check.
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* // Basic usage
|
|
197
|
+
* eq(users.id, 1);
|
|
198
|
+
*
|
|
199
|
+
* // With strict typing (typescript will error if types mismatch)
|
|
200
|
+
* eq(users.firstName, 'Ada');
|
|
201
|
+
*/
|
|
202
202
|
export function eq<T extends ScalarComparable>(left: TypedLike<T>, right: T | TypedLike<T>): BinaryExpressionNode;
|
|
203
203
|
export function eq<T>(left: OperandNode | ColumnRef, right: T & NotArray<T>): BinaryExpressionNode;
|
|
204
204
|
export function eq(left: OperandNode | ColumnRef | TypedLike<unknown>, right: unknown): BinaryExpressionNode {
|
|
205
205
|
return createBinaryExpression('=', left as OperandNode | ColumnRef, right as OperandNode | ColumnRef | LiteralValue);
|
|
206
206
|
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Creates a not equal expression (`left != right`).
|
|
210
|
-
*
|
|
211
|
-
* @param left - The left operand (column or value).
|
|
212
|
-
* @param right - The right operand (column or value).
|
|
213
|
-
* @returns A `BinaryExpressionNode` representing the inequality check.
|
|
214
|
-
*
|
|
215
|
-
* @example
|
|
216
|
-
* neq(users.status, 'inactive');
|
|
217
|
-
*/
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Creates a not equal expression (`left != right`).
|
|
210
|
+
*
|
|
211
|
+
* @param left - The left operand (column or value).
|
|
212
|
+
* @param right - The right operand (column or value).
|
|
213
|
+
* @returns A `BinaryExpressionNode` representing the inequality check.
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* neq(users.status, 'inactive');
|
|
217
|
+
*/
|
|
218
218
|
export function neq<T extends ScalarComparable>(left: TypedLike<T>, right: T | TypedLike<T>): BinaryExpressionNode;
|
|
219
219
|
export function neq<T>(left: OperandNode | ColumnRef, right: T & NotArray<T>): BinaryExpressionNode;
|
|
220
220
|
export function neq(left: OperandNode | ColumnRef | TypedLike<unknown>, right: unknown): BinaryExpressionNode {
|
|
221
221
|
return createBinaryExpression('!=', left as OperandNode | ColumnRef, right as OperandNode | ColumnRef | LiteralValue);
|
|
222
222
|
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Creates a greater-than expression (`left > right`).
|
|
226
|
-
*
|
|
227
|
-
* @param left - The left operand.
|
|
228
|
-
* @param right - The right operand.
|
|
229
|
-
* @returns A `BinaryExpressionNode`.
|
|
230
|
-
*
|
|
231
|
-
* @example
|
|
232
|
-
* gt(users.age, 18);
|
|
233
|
-
*/
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Creates a greater-than expression (`left > right`).
|
|
226
|
+
*
|
|
227
|
+
* @param left - The left operand.
|
|
228
|
+
* @param right - The right operand.
|
|
229
|
+
* @returns A `BinaryExpressionNode`.
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* gt(users.age, 18);
|
|
233
|
+
*/
|
|
234
234
|
export function gt<T extends OrderedComparable>(left: TypedLike<T>, right: T | TypedLike<T>): BinaryExpressionNode;
|
|
235
235
|
export function gt<T>(left: OperandNode | ColumnRef, right: T & NotArray<T>): BinaryExpressionNode;
|
|
236
236
|
export function gt(left: OperandNode | ColumnRef | TypedLike<unknown>, right: unknown): BinaryExpressionNode {
|
|
237
237
|
return createBinaryExpression('>', left as OperandNode | ColumnRef, right as OperandNode | ColumnRef | LiteralValue);
|
|
238
238
|
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Creates a greater-than-or-equal expression (`left >= right`).
|
|
242
|
-
*
|
|
243
|
-
* @param left - The left operand.
|
|
244
|
-
* @param right - The right operand.
|
|
245
|
-
* @returns A `BinaryExpressionNode`.
|
|
246
|
-
*
|
|
247
|
-
* @example
|
|
248
|
-
* gte(users.score, 100);
|
|
249
|
-
*/
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Creates a greater-than-or-equal expression (`left >= right`).
|
|
242
|
+
*
|
|
243
|
+
* @param left - The left operand.
|
|
244
|
+
* @param right - The right operand.
|
|
245
|
+
* @returns A `BinaryExpressionNode`.
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* gte(users.score, 100);
|
|
249
|
+
*/
|
|
250
250
|
export function gte<T extends OrderedComparable>(left: TypedLike<T>, right: T | TypedLike<T>): BinaryExpressionNode;
|
|
251
251
|
export function gte<T>(left: OperandNode | ColumnRef, right: T & NotArray<T>): BinaryExpressionNode;
|
|
252
252
|
export function gte(left: OperandNode | ColumnRef | TypedLike<unknown>, right: unknown): BinaryExpressionNode {
|
|
253
253
|
return createBinaryExpression('>=', left as OperandNode | ColumnRef, right as OperandNode | ColumnRef | LiteralValue);
|
|
254
254
|
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Creates a less-than expression (`left < right`).
|
|
258
|
-
*
|
|
259
|
-
* @param left - The left operand.
|
|
260
|
-
* @param right - The right operand.
|
|
261
|
-
* @returns A `BinaryExpressionNode`.
|
|
262
|
-
*
|
|
263
|
-
* @example
|
|
264
|
-
* lt(inventory.stock, 5);
|
|
265
|
-
*/
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Creates a less-than expression (`left < right`).
|
|
258
|
+
*
|
|
259
|
+
* @param left - The left operand.
|
|
260
|
+
* @param right - The right operand.
|
|
261
|
+
* @returns A `BinaryExpressionNode`.
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* lt(inventory.stock, 5);
|
|
265
|
+
*/
|
|
266
266
|
export function lt<T extends OrderedComparable>(left: TypedLike<T>, right: T | TypedLike<T>): BinaryExpressionNode;
|
|
267
267
|
export function lt<T>(left: OperandNode | ColumnRef, right: T & NotArray<T>): BinaryExpressionNode;
|
|
268
268
|
export function lt(left: OperandNode | ColumnRef | TypedLike<unknown>, right: unknown): BinaryExpressionNode {
|
|
269
269
|
return createBinaryExpression('<', left as OperandNode | ColumnRef, right as OperandNode | ColumnRef | LiteralValue);
|
|
270
270
|
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Creates a less-than-or-equal expression (`left <= right`).
|
|
274
|
-
*
|
|
275
|
-
* @param left - The left operand.
|
|
276
|
-
* @param right - The right operand.
|
|
277
|
-
* @returns A `BinaryExpressionNode`.
|
|
278
|
-
*
|
|
279
|
-
* @example
|
|
280
|
-
* lte(products.price, 50.00);
|
|
281
|
-
*/
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Creates a less-than-or-equal expression (`left <= right`).
|
|
274
|
+
*
|
|
275
|
+
* @param left - The left operand.
|
|
276
|
+
* @param right - The right operand.
|
|
277
|
+
* @returns A `BinaryExpressionNode`.
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* lte(products.price, 50.00);
|
|
281
|
+
*/
|
|
282
282
|
export function lte<T extends OrderedComparable>(left: TypedLike<T>, right: T | TypedLike<T>): BinaryExpressionNode;
|
|
283
283
|
export function lte<T>(left: OperandNode | ColumnRef, right: T & NotArray<T>): BinaryExpressionNode;
|
|
284
284
|
export function lte(left: OperandNode | ColumnRef | TypedLike<unknown>, right: unknown): BinaryExpressionNode {
|
|
285
285
|
return createBinaryExpression('<=', left as OperandNode | ColumnRef, right as OperandNode | ColumnRef | LiteralValue);
|
|
286
286
|
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Creates a `LIKE` pattern matching expression.
|
|
290
|
-
*
|
|
291
|
-
* @param left - The column or expression to check.
|
|
292
|
-
* @param pattern - The pattern string (e.g., 'A%').
|
|
293
|
-
* @param escape - Optional escape character.
|
|
294
|
-
* @returns A `BinaryExpressionNode`.
|
|
295
|
-
*
|
|
296
|
-
* @example
|
|
297
|
-
* like(users.email, '%@gmail.com');
|
|
298
|
-
*/
|
|
299
|
-
export const like = (left: OperandNode | ColumnRef, pattern: string, escape?: string): BinaryExpressionNode =>
|
|
300
|
-
createBinaryExpression('LIKE', left, pattern, escape);
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Creates a `NOT LIKE` pattern matching expression.
|
|
304
|
-
*
|
|
305
|
-
* @param left - The column or expression to check.
|
|
306
|
-
* @param pattern - The pattern string.
|
|
307
|
-
* @param escape - Optional escape character.
|
|
308
|
-
* @returns A `BinaryExpressionNode`.
|
|
309
|
-
*
|
|
310
|
-
* @example
|
|
311
|
-
* notLike(users.email, 'test%');
|
|
312
|
-
*/
|
|
313
|
-
export const notLike = (left: OperandNode | ColumnRef, pattern: string, escape?: string): BinaryExpressionNode =>
|
|
314
|
-
createBinaryExpression('NOT LIKE', left, pattern, escape);
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Creates a logical AND expression to combine multiple conditions.
|
|
318
|
-
*
|
|
319
|
-
* @param operands - One or more conditions to combine.
|
|
320
|
-
* @returns A `LogicalExpressionNode`.
|
|
321
|
-
*
|
|
322
|
-
* @example
|
|
323
|
-
* and(
|
|
324
|
-
* eq(users.isActive, true),
|
|
325
|
-
* gt(users.age, 18)
|
|
326
|
-
* );
|
|
327
|
-
*/
|
|
328
|
-
export const and = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
|
|
329
|
-
type: 'LogicalExpression',
|
|
330
|
-
operator: 'AND',
|
|
331
|
-
operands
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Creates a logical OR expression to combine multiple conditions.
|
|
336
|
-
*
|
|
337
|
-
* @param operands - One or more conditions to combine.
|
|
338
|
-
* @returns A `LogicalExpressionNode`.
|
|
339
|
-
*
|
|
340
|
-
* @example
|
|
341
|
-
* or(
|
|
342
|
-
* eq(users.role, 'admin'),
|
|
343
|
-
* eq(users.role, 'moderator')
|
|
344
|
-
* );
|
|
345
|
-
*/
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Creates a `LIKE` pattern matching expression.
|
|
290
|
+
*
|
|
291
|
+
* @param left - The column or expression to check.
|
|
292
|
+
* @param pattern - The pattern string (e.g., 'A%').
|
|
293
|
+
* @param escape - Optional escape character.
|
|
294
|
+
* @returns A `BinaryExpressionNode`.
|
|
295
|
+
*
|
|
296
|
+
* @example
|
|
297
|
+
* like(users.email, '%@gmail.com');
|
|
298
|
+
*/
|
|
299
|
+
export const like = (left: OperandNode | ColumnRef, pattern: string, escape?: string): BinaryExpressionNode =>
|
|
300
|
+
createBinaryExpression('LIKE', left, pattern, escape);
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Creates a `NOT LIKE` pattern matching expression.
|
|
304
|
+
*
|
|
305
|
+
* @param left - The column or expression to check.
|
|
306
|
+
* @param pattern - The pattern string.
|
|
307
|
+
* @param escape - Optional escape character.
|
|
308
|
+
* @returns A `BinaryExpressionNode`.
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* notLike(users.email, 'test%');
|
|
312
|
+
*/
|
|
313
|
+
export const notLike = (left: OperandNode | ColumnRef, pattern: string, escape?: string): BinaryExpressionNode =>
|
|
314
|
+
createBinaryExpression('NOT LIKE', left, pattern, escape);
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Creates a logical AND expression to combine multiple conditions.
|
|
318
|
+
*
|
|
319
|
+
* @param operands - One or more conditions to combine.
|
|
320
|
+
* @returns A `LogicalExpressionNode`.
|
|
321
|
+
*
|
|
322
|
+
* @example
|
|
323
|
+
* and(
|
|
324
|
+
* eq(users.isActive, true),
|
|
325
|
+
* gt(users.age, 18)
|
|
326
|
+
* );
|
|
327
|
+
*/
|
|
328
|
+
export const and = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
|
|
329
|
+
type: 'LogicalExpression',
|
|
330
|
+
operator: 'AND',
|
|
331
|
+
operands
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Creates a logical OR expression to combine multiple conditions.
|
|
336
|
+
*
|
|
337
|
+
* @param operands - One or more conditions to combine.
|
|
338
|
+
* @returns A `LogicalExpressionNode`.
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* or(
|
|
342
|
+
* eq(users.role, 'admin'),
|
|
343
|
+
* eq(users.role, 'moderator')
|
|
344
|
+
* );
|
|
345
|
+
*/
|
|
346
346
|
export const or = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
|
|
347
347
|
type: 'LogicalExpression',
|
|
348
348
|
operator: 'OR',
|
|
@@ -365,470 +365,470 @@ export const not = (operand: ExpressionNode): NotExpressionNode => ({
|
|
|
365
365
|
type: 'NotExpression',
|
|
366
366
|
operand
|
|
367
367
|
});
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Creates an IS NULL check (`left IS NULL`).
|
|
371
|
-
*
|
|
372
|
-
* @param left - The operand to check.
|
|
373
|
-
* @returns A `NullExpressionNode`.
|
|
374
|
-
*
|
|
375
|
-
* @example
|
|
376
|
-
* isNull(users.deletedAt);
|
|
377
|
-
*/
|
|
378
|
-
export const isNull = (left: OperandNode | ColumnRef): NullExpressionNode => ({
|
|
379
|
-
type: 'NullExpression',
|
|
380
|
-
left: toOperandNode(left),
|
|
381
|
-
operator: 'IS NULL'
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Creates an IS NOT NULL check (`left IS NOT NULL`).
|
|
386
|
-
*
|
|
387
|
-
* @param left - The operand to check.
|
|
388
|
-
* @returns A `NullExpressionNode`.
|
|
389
|
-
*
|
|
390
|
-
* @example
|
|
391
|
-
* isNotNull(users.email);
|
|
392
|
-
*/
|
|
393
|
-
export const isNotNull = (left: OperandNode | ColumnRef): NullExpressionNode => ({
|
|
394
|
-
type: 'NullExpression',
|
|
395
|
-
left: toOperandNode(left),
|
|
396
|
-
operator: 'IS NOT NULL'
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
const createInExpression = (
|
|
400
|
-
operator: 'IN' | 'NOT IN',
|
|
401
|
-
left: OperandNode | ColumnRef,
|
|
402
|
-
right: InExpressionRight
|
|
403
|
-
): InExpressionNode => ({
|
|
404
|
-
type: 'InExpression',
|
|
405
|
-
left: toOperandNode(left),
|
|
406
|
-
operator,
|
|
407
|
-
right
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Creates an IN list check (`left IN (v1, v2, ...)`).
|
|
412
|
-
*
|
|
413
|
-
* @param left - The operand to check.
|
|
414
|
-
* @param values - An array of values to check against.
|
|
415
|
-
* @returns An `InExpressionNode`.
|
|
416
|
-
*
|
|
417
|
-
* @example
|
|
418
|
-
* inList(users.status, ['active', 'pending']);
|
|
419
|
-
*/
|
|
420
|
-
export const inList = (left: OperandNode | ColumnRef, values: (string | number | LiteralNode)[]): InExpressionNode =>
|
|
421
|
-
createInExpression('IN', left, values.map(v => toOperand(v)));
|
|
422
|
-
|
|
423
|
-
/**
|
|
424
|
-
* Creates a NOT IN list check (`left NOT IN (v1, v2, ...)`).
|
|
425
|
-
*
|
|
426
|
-
* @param left - The operand to check.
|
|
427
|
-
* @param values - An array of values to check against.
|
|
428
|
-
* @returns An `InExpressionNode`.
|
|
429
|
-
*
|
|
430
|
-
* @example
|
|
431
|
-
* notInList(users.id, [1, 2, 3]);
|
|
432
|
-
*/
|
|
433
|
-
export const notInList = (left: OperandNode | ColumnRef, values: (string | number | LiteralNode)[]): InExpressionNode =>
|
|
434
|
-
createInExpression('NOT IN', left, values.map(v => toOperand(v)));
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* Creates an IN subquery check (`left IN (SELECT ...)`).
|
|
438
|
-
*
|
|
439
|
-
* @param left - The operand to check.
|
|
440
|
-
* @param subquery - The subquery to run.
|
|
441
|
-
* @returns An `InExpressionNode`.
|
|
442
|
-
*
|
|
443
|
-
* @example
|
|
444
|
-
* inSubquery(
|
|
445
|
-
* posts.authorId,
|
|
446
|
-
* selectFromEntity(User).select({ id: users.id }).where(eq(users.isActive, true))
|
|
447
|
-
* );
|
|
448
|
-
*/
|
|
449
|
-
export const inSubquery = (left: OperandNode | ColumnRef, subquery: SelectQueryInput): InExpressionNode =>
|
|
450
|
-
createInExpression('IN', left, toScalarSubqueryNode(subquery));
|
|
451
|
-
|
|
452
|
-
/**
|
|
453
|
-
* Creates a NOT IN subquery check (`left NOT IN (SELECT ...)`).
|
|
454
|
-
*
|
|
455
|
-
* @param left - The operand to check.
|
|
456
|
-
* @param subquery - The subquery to run.
|
|
457
|
-
* @returns An `InExpressionNode`.
|
|
458
|
-
*
|
|
459
|
-
* @example
|
|
460
|
-
* notInSubquery(
|
|
461
|
-
* users.id,
|
|
462
|
-
* selectFromEntity(Blacklist).select({ userId: blacklist.userId })
|
|
463
|
-
* );
|
|
464
|
-
*/
|
|
465
|
-
export const notInSubquery = (left: OperandNode | ColumnRef, subquery: SelectQueryInput): InExpressionNode =>
|
|
466
|
-
createInExpression('NOT IN', left, toScalarSubqueryNode(subquery));
|
|
467
|
-
|
|
468
|
-
const createBetweenExpression = (
|
|
469
|
-
operator: 'BETWEEN' | 'NOT BETWEEN',
|
|
470
|
-
left: OperandNode | ColumnRef,
|
|
471
|
-
lower: OperandNode | ColumnRef | string | number,
|
|
472
|
-
upper: OperandNode | ColumnRef | string | number
|
|
473
|
-
): BetweenExpressionNode => ({
|
|
474
|
-
type: 'BetweenExpression',
|
|
475
|
-
left: toOperandNode(left),
|
|
476
|
-
operator,
|
|
477
|
-
lower: toOperand(lower),
|
|
478
|
-
upper: toOperand(upper)
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Creates a BETWEEN check (`left BETWEEN lower AND upper`).
|
|
483
|
-
*
|
|
484
|
-
* @param left - The operand to check.
|
|
485
|
-
* @param lower - The lower bound (inclusive).
|
|
486
|
-
* @param upper - The upper bound (inclusive).
|
|
487
|
-
* @returns A `BetweenExpressionNode`.
|
|
488
|
-
*
|
|
489
|
-
* @example
|
|
490
|
-
* between(products.price, 10, 100);
|
|
491
|
-
*/
|
|
492
|
-
export const between = (
|
|
493
|
-
left: OperandNode | ColumnRef,
|
|
494
|
-
lower: OperandNode | ColumnRef | string | number,
|
|
495
|
-
upper: OperandNode | ColumnRef | string | number
|
|
496
|
-
): BetweenExpressionNode => createBetweenExpression('BETWEEN', left, lower, upper);
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* Creates a NOT BETWEEN check (`left NOT BETWEEN lower AND upper`).
|
|
500
|
-
*
|
|
501
|
-
* @param left - The operand to check.
|
|
502
|
-
* @param lower - The lower bound (inclusive).
|
|
503
|
-
* @param upper - The upper bound (inclusive).
|
|
504
|
-
* @returns A `BetweenExpressionNode`.
|
|
505
|
-
*
|
|
506
|
-
* @example
|
|
507
|
-
* notBetween(users.age, 20, 30);
|
|
508
|
-
*/
|
|
509
|
-
export const notBetween = (
|
|
510
|
-
left: OperandNode | ColumnRef,
|
|
511
|
-
lower: OperandNode | ColumnRef | string | number,
|
|
512
|
-
upper: OperandNode | ColumnRef | string | number
|
|
513
|
-
): BetweenExpressionNode => createBetweenExpression('NOT BETWEEN', left, lower, upper);
|
|
514
|
-
|
|
515
|
-
const createArithmeticExpression = (
|
|
516
|
-
operator: '+' | '-' | '*' | '/',
|
|
517
|
-
left: OperandNode | ColumnRef,
|
|
518
|
-
right: OperandNode | ColumnRef | string | number
|
|
519
|
-
): ArithmeticExpressionNode => ({
|
|
520
|
-
type: 'ArithmeticExpression',
|
|
521
|
-
left: toOperand(left),
|
|
522
|
-
operator,
|
|
523
|
-
right: toOperand(right)
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
/**
|
|
527
|
-
* Creates an addition expression (`left + right`).
|
|
528
|
-
*
|
|
529
|
-
* @param left - The left operand.
|
|
530
|
-
* @param right - The right operand.
|
|
531
|
-
* @returns An `ArithmeticExpressionNode`.
|
|
532
|
-
*/
|
|
533
|
-
export const add = (
|
|
534
|
-
left: OperandNode | ColumnRef,
|
|
535
|
-
right: OperandNode | ColumnRef | string | number
|
|
536
|
-
): ArithmeticExpressionNode => createArithmeticExpression('+', left, right);
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* Creates a subtraction expression (`left - right`).
|
|
540
|
-
*
|
|
541
|
-
* @param left - The left operand.
|
|
542
|
-
* @param right - The right operand.
|
|
543
|
-
* @returns An `ArithmeticExpressionNode`.
|
|
544
|
-
*/
|
|
545
|
-
export const sub = (
|
|
546
|
-
left: OperandNode | ColumnRef,
|
|
547
|
-
right: OperandNode | ColumnRef | string | number
|
|
548
|
-
): ArithmeticExpressionNode => createArithmeticExpression('-', left, right);
|
|
549
|
-
|
|
550
|
-
/**
|
|
551
|
-
* Creates a multiplication expression (`left * right`).
|
|
552
|
-
*
|
|
553
|
-
* @param left - The left operand.
|
|
554
|
-
* @param right - The right operand.
|
|
555
|
-
* @returns An `ArithmeticExpressionNode`.
|
|
556
|
-
*/
|
|
557
|
-
export const mul = (
|
|
558
|
-
left: OperandNode | ColumnRef,
|
|
559
|
-
right: OperandNode | ColumnRef | string | number
|
|
560
|
-
): ArithmeticExpressionNode => createArithmeticExpression('*', left, right);
|
|
561
|
-
|
|
562
|
-
/**
|
|
563
|
-
* Creates a division expression (`left / right`).
|
|
564
|
-
*
|
|
565
|
-
* @param left - The left operand.
|
|
566
|
-
* @param right - The right operand.
|
|
567
|
-
* @returns An `ArithmeticExpressionNode`.
|
|
568
|
-
*/
|
|
569
|
-
export const div = (
|
|
570
|
-
left: OperandNode | ColumnRef,
|
|
571
|
-
right: OperandNode | ColumnRef | string | number
|
|
572
|
-
): ArithmeticExpressionNode => createArithmeticExpression('/', left, right);
|
|
573
|
-
|
|
574
|
-
const createBitwiseExpression = (
|
|
575
|
-
operator: BitwiseOperator,
|
|
576
|
-
left: OperandNode | ColumnRef,
|
|
577
|
-
right: OperandNode | ColumnRef | string | number
|
|
578
|
-
): BitwiseExpressionNode => ({
|
|
579
|
-
type: 'BitwiseExpression',
|
|
580
|
-
left: toOperand(left),
|
|
581
|
-
operator,
|
|
582
|
-
right: toOperand(right)
|
|
583
|
-
});
|
|
584
|
-
|
|
585
|
-
/**
|
|
586
|
-
* Creates a bitwise AND expression (`left & right`).
|
|
587
|
-
*
|
|
588
|
-
* @param left - The left operand.
|
|
589
|
-
* @param right - The right operand.
|
|
590
|
-
* @returns A `BitwiseExpressionNode`.
|
|
591
|
-
*/
|
|
592
|
-
export const bitAnd = (
|
|
593
|
-
left: OperandNode | ColumnRef,
|
|
594
|
-
right: OperandNode | ColumnRef | string | number
|
|
595
|
-
): BitwiseExpressionNode => createBitwiseExpression('&', left, right);
|
|
596
|
-
|
|
597
|
-
/**
|
|
598
|
-
* Creates a bitwise OR expression (`left | right`).
|
|
599
|
-
*
|
|
600
|
-
* @param left - The left operand.
|
|
601
|
-
* @param right - The right operand.
|
|
602
|
-
* @returns A `BitwiseExpressionNode`.
|
|
603
|
-
*/
|
|
604
|
-
export const bitOr = (
|
|
605
|
-
left: OperandNode | ColumnRef,
|
|
606
|
-
right: OperandNode | ColumnRef | string | number
|
|
607
|
-
): BitwiseExpressionNode => createBitwiseExpression('|', left, right);
|
|
608
|
-
|
|
609
|
-
/**
|
|
610
|
-
* Creates a bitwise XOR expression (`left ^ right`).
|
|
611
|
-
*
|
|
612
|
-
* @param left - The left operand.
|
|
613
|
-
* @param right - The right operand.
|
|
614
|
-
* @returns A `BitwiseExpressionNode`.
|
|
615
|
-
*/
|
|
616
|
-
export const bitXor = (
|
|
617
|
-
left: OperandNode | ColumnRef,
|
|
618
|
-
right: OperandNode | ColumnRef | string | number
|
|
619
|
-
): BitwiseExpressionNode => createBitwiseExpression('^', left, right);
|
|
620
|
-
|
|
621
|
-
/**
|
|
622
|
-
* Creates a bitwise left-shift expression (`left << right`).
|
|
623
|
-
*
|
|
624
|
-
* @param left - The left operand.
|
|
625
|
-
* @param right - The right operand (number of bits).
|
|
626
|
-
* @returns A `BitwiseExpressionNode`.
|
|
627
|
-
*/
|
|
628
|
-
export const shiftLeft = (
|
|
629
|
-
left: OperandNode | ColumnRef,
|
|
630
|
-
right: OperandNode | ColumnRef | string | number
|
|
631
|
-
): BitwiseExpressionNode => createBitwiseExpression('<<', left, right);
|
|
632
|
-
|
|
633
|
-
/**
|
|
634
|
-
* Creates a bitwise right-shift expression (`left >> right`).
|
|
635
|
-
*
|
|
636
|
-
* @param left - The left operand.
|
|
637
|
-
* @param right - The right operand (number of bits).
|
|
638
|
-
* @returns A `BitwiseExpressionNode`.
|
|
639
|
-
*/
|
|
640
|
-
export const shiftRight = (
|
|
641
|
-
left: OperandNode | ColumnRef,
|
|
642
|
-
right: OperandNode | ColumnRef | string | number
|
|
643
|
-
): BitwiseExpressionNode => createBitwiseExpression('>>', left, right);
|
|
644
|
-
|
|
645
|
-
/**
|
|
646
|
-
* Creates a JSON path extraction expression.
|
|
647
|
-
*
|
|
648
|
-
* @param col - The source column (should be a JSON/JSONB column).
|
|
649
|
-
* @param path - The JSON path expression (e.g., '$.address.city').
|
|
650
|
-
* @returns A `JsonPathNode`.
|
|
651
|
-
*
|
|
652
|
-
* @example
|
|
653
|
-
* jsonPath(users.profile, '$.settings.theme');
|
|
654
|
-
*/
|
|
655
|
-
export const jsonPath = (col: ColumnRef | ColumnNode, path: string): JsonPathNode => ({
|
|
656
|
-
type: 'JsonPath',
|
|
657
|
-
column: columnOperand(col),
|
|
658
|
-
path
|
|
659
|
-
});
|
|
660
|
-
|
|
661
|
-
/**
|
|
662
|
-
* Creates a CASE expression (`CASE WHEN ... THEN ... ELSE ... END`).
|
|
663
|
-
*
|
|
664
|
-
* @param conditions - An array of `{ when, then }` objects.
|
|
665
|
-
* @param elseValue - Optional value for the `ELSE` clause.
|
|
666
|
-
* @returns A `CaseExpressionNode`.
|
|
667
|
-
*
|
|
668
|
-
* @example
|
|
669
|
-
* caseWhen([
|
|
670
|
-
* { when: gt(users.age, 65), then: 'Senior' },
|
|
671
|
-
* { when: gt(users.age, 18), then: 'Adult' }
|
|
672
|
-
* ], 'Minor');
|
|
673
|
-
*/
|
|
674
|
-
export const caseWhen = (
|
|
675
|
-
conditions: { when: ExpressionNode; then: OperandNode | ColumnRef | string | number | boolean | null }[],
|
|
676
|
-
elseValue?: OperandNode | ColumnRef | string | number | boolean | null
|
|
677
|
-
): CaseExpressionNode => ({
|
|
678
|
-
type: 'CaseExpression',
|
|
679
|
-
conditions: conditions.map(c => ({
|
|
680
|
-
when: c.when,
|
|
681
|
-
then: toOperand(c.then)
|
|
682
|
-
})),
|
|
683
|
-
else: elseValue !== undefined ? toOperand(elseValue) : undefined
|
|
684
|
-
});
|
|
685
|
-
|
|
686
|
-
/**
|
|
687
|
-
* Creates a CAST expression (`CAST(expression AS type)`).
|
|
688
|
-
*
|
|
689
|
-
* @param expression - The expression to cast.
|
|
690
|
-
* @param castType - The target SQL type (e.g., 'VARCHAR', 'INTEGER').
|
|
691
|
-
* @returns A `CastExpressionNode`.
|
|
692
|
-
*
|
|
693
|
-
* @example
|
|
694
|
-
* cast(users.age, 'VARCHAR');
|
|
695
|
-
*/
|
|
696
|
-
export const cast = (
|
|
697
|
-
expression: OperandNode | ColumnRef | string | number | boolean | null,
|
|
698
|
-
castType: string
|
|
699
|
-
): CastExpressionNode => ({
|
|
700
|
-
type: 'Cast',
|
|
701
|
-
expression: toOperand(expression),
|
|
702
|
-
castType
|
|
703
|
-
});
|
|
704
|
-
|
|
705
|
-
/**
|
|
706
|
-
* Creates an EXISTS check (`EXISTS (SELECT ...)`).
|
|
707
|
-
*
|
|
708
|
-
* @param subquery - The subquery to check.
|
|
709
|
-
* @returns An `ExistsExpressionNode`.
|
|
710
|
-
*
|
|
711
|
-
* @example
|
|
712
|
-
* exists(
|
|
713
|
-
* selectFromEntity(Order).where(eq(orders.userId, users.id))
|
|
714
|
-
* );
|
|
715
|
-
*/
|
|
716
|
-
export const exists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
|
|
717
|
-
type: 'ExistsExpression',
|
|
718
|
-
operator: 'EXISTS',
|
|
719
|
-
subquery
|
|
720
|
-
});
|
|
721
|
-
|
|
722
|
-
/**
|
|
723
|
-
* Creates a NOT EXISTS check (`NOT EXISTS (SELECT ...)`).
|
|
724
|
-
*
|
|
725
|
-
* @param subquery - The subquery to check.
|
|
726
|
-
* @returns An `ExistsExpressionNode`.
|
|
727
|
-
*
|
|
728
|
-
* @example
|
|
729
|
-
* notExists(
|
|
730
|
-
* selectFromEntity(Subscription).where(eq(subscriptions.userId, users.id))
|
|
731
|
-
* );
|
|
732
|
-
*/
|
|
733
|
-
export const notExists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
|
|
734
|
-
type: 'ExistsExpression',
|
|
735
|
-
operator: 'NOT EXISTS',
|
|
736
|
-
subquery
|
|
737
|
-
});
|
|
738
|
-
|
|
739
|
-
/**
|
|
740
|
-
* Creates a COLLATE expression (`expression COLLATE collation`).
|
|
741
|
-
*
|
|
742
|
-
* @param expression - The expression string.
|
|
743
|
-
* @param collation - The collation name (e.g., 'nocase').
|
|
744
|
-
* @returns A `CollateExpressionNode`.
|
|
745
|
-
*
|
|
746
|
-
* @example
|
|
747
|
-
* collate(users.email, 'nocase');
|
|
748
|
-
*/
|
|
749
|
-
export const collate = (
|
|
750
|
-
expression: OperandNode | ColumnRef | string | number | boolean | null,
|
|
751
|
-
collation: string
|
|
752
|
-
): CollateExpressionNode => ({
|
|
753
|
-
type: 'Collate',
|
|
754
|
-
expression: toOperand(expression),
|
|
755
|
-
collation
|
|
756
|
-
});
|
|
757
|
-
|
|
758
|
-
/**
|
|
759
|
-
* Creates an `IS DISTINCT FROM` expression.
|
|
760
|
-
*
|
|
761
|
-
* Unlike `neq(a, b)`, this comparison is **null-safe**:
|
|
762
|
-
* returns `true` when values are different **including** when one
|
|
763
|
-
* (or both) is `NULL`. Never returns `NULL`.
|
|
764
|
-
*
|
|
765
|
-
* | left | right | neq | isDistinctFrom |
|
|
766
|
-
* |-------|-------|-------|----------------|
|
|
767
|
-
* | 1 | 2 | true | true |
|
|
768
|
-
* | 1 | 1 | false | false |
|
|
769
|
-
* | NULL | 1 | NULL | **true** |
|
|
770
|
-
* | NULL | NULL | NULL | **false** |
|
|
771
|
-
*
|
|
772
|
-
* Compilation by dialect:
|
|
773
|
-
* - PostgreSQL / SQLite / MSSQL → `left IS DISTINCT FROM right`
|
|
774
|
-
* - MySQL → `NOT (left <=> right)`
|
|
775
|
-
*
|
|
776
|
-
* @param left - The left operand.
|
|
777
|
-
* @param right - The right operand.
|
|
778
|
-
* @returns An `IsDistinctExpressionNode`.
|
|
779
|
-
*
|
|
780
|
-
* @example
|
|
781
|
-
* ```ts
|
|
782
|
-
* // Find users whose email has changed compared to backup
|
|
783
|
-
* where(isDistinctFrom(users.columns.email, backup.columns.email))
|
|
784
|
-
* ```
|
|
785
|
-
*/
|
|
786
|
-
export const isDistinctFrom = (
|
|
787
|
-
left: OperandNode | ColumnRef,
|
|
788
|
-
right: OperandNode | ColumnRef | LiteralValue
|
|
789
|
-
): IsDistinctExpressionNode => ({
|
|
790
|
-
type: 'IsDistinctExpression',
|
|
791
|
-
left: toOperandNode(left),
|
|
792
|
-
operator: 'IS DISTINCT FROM',
|
|
793
|
-
right: toOperand(right),
|
|
794
|
-
});
|
|
795
|
-
|
|
796
|
-
/**
|
|
797
|
-
* Creates an `IS NOT DISTINCT FROM` expression.
|
|
798
|
-
*
|
|
799
|
-
* The inverse of `isDistinctFrom`. Equivalent to a **null-safe** equality:
|
|
800
|
-
* returns `true` when values are equal **including** when both
|
|
801
|
-
* are `NULL`. Never returns `NULL`.
|
|
802
|
-
*
|
|
803
|
-
* | left | right | eq | isNotDistinctFrom |
|
|
804
|
-
* |-------|-------|-------|------------------|
|
|
805
|
-
* | 1 | 1 | true | true |
|
|
806
|
-
* | 1 | 2 | false | false |
|
|
807
|
-
* | NULL | 1 | NULL | **false** |
|
|
808
|
-
* | NULL | NULL | NULL | **true** |
|
|
809
|
-
*
|
|
810
|
-
* Compilation by dialect:
|
|
811
|
-
* - PostgreSQL / SQLite / MSSQL → `left IS NOT DISTINCT FROM right`
|
|
812
|
-
* - MySQL → `left <=> right`
|
|
813
|
-
*
|
|
814
|
-
* @param left - The left operand.
|
|
815
|
-
* @param right - The right operand.
|
|
816
|
-
* @returns An `IsDistinctExpressionNode`.
|
|
817
|
-
*
|
|
818
|
-
* @example
|
|
819
|
-
* ```ts
|
|
820
|
-
* // Rows where deletedAt is NULL or equal to a specific date
|
|
821
|
-
* where(isNotDistinctFrom(orders.columns.deletedAt, null))
|
|
822
|
-
* // → WHERE "orders"."deletedAt" IS NOT DISTINCT FROM NULL
|
|
823
|
-
* // equivalent to: WHERE deletedAt IS NULL (but works with any value)
|
|
824
|
-
* ```
|
|
825
|
-
*/
|
|
826
|
-
export const isNotDistinctFrom = (
|
|
827
|
-
left: OperandNode | ColumnRef,
|
|
828
|
-
right: OperandNode | ColumnRef | LiteralValue
|
|
829
|
-
): IsDistinctExpressionNode => ({
|
|
830
|
-
type: 'IsDistinctExpression',
|
|
831
|
-
left: toOperandNode(left),
|
|
832
|
-
operator: 'IS NOT DISTINCT FROM',
|
|
833
|
-
right: toOperand(right),
|
|
834
|
-
});
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Creates an IS NULL check (`left IS NULL`).
|
|
371
|
+
*
|
|
372
|
+
* @param left - The operand to check.
|
|
373
|
+
* @returns A `NullExpressionNode`.
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* isNull(users.deletedAt);
|
|
377
|
+
*/
|
|
378
|
+
export const isNull = (left: OperandNode | ColumnRef): NullExpressionNode => ({
|
|
379
|
+
type: 'NullExpression',
|
|
380
|
+
left: toOperandNode(left),
|
|
381
|
+
operator: 'IS NULL'
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Creates an IS NOT NULL check (`left IS NOT NULL`).
|
|
386
|
+
*
|
|
387
|
+
* @param left - The operand to check.
|
|
388
|
+
* @returns A `NullExpressionNode`.
|
|
389
|
+
*
|
|
390
|
+
* @example
|
|
391
|
+
* isNotNull(users.email);
|
|
392
|
+
*/
|
|
393
|
+
export const isNotNull = (left: OperandNode | ColumnRef): NullExpressionNode => ({
|
|
394
|
+
type: 'NullExpression',
|
|
395
|
+
left: toOperandNode(left),
|
|
396
|
+
operator: 'IS NOT NULL'
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const createInExpression = (
|
|
400
|
+
operator: 'IN' | 'NOT IN',
|
|
401
|
+
left: OperandNode | ColumnRef,
|
|
402
|
+
right: InExpressionRight
|
|
403
|
+
): InExpressionNode => ({
|
|
404
|
+
type: 'InExpression',
|
|
405
|
+
left: toOperandNode(left),
|
|
406
|
+
operator,
|
|
407
|
+
right
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Creates an IN list check (`left IN (v1, v2, ...)`).
|
|
412
|
+
*
|
|
413
|
+
* @param left - The operand to check.
|
|
414
|
+
* @param values - An array of values to check against.
|
|
415
|
+
* @returns An `InExpressionNode`.
|
|
416
|
+
*
|
|
417
|
+
* @example
|
|
418
|
+
* inList(users.status, ['active', 'pending']);
|
|
419
|
+
*/
|
|
420
|
+
export const inList = (left: OperandNode | ColumnRef, values: (string | number | LiteralNode)[]): InExpressionNode =>
|
|
421
|
+
createInExpression('IN', left, values.map(v => toOperand(v)));
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Creates a NOT IN list check (`left NOT IN (v1, v2, ...)`).
|
|
425
|
+
*
|
|
426
|
+
* @param left - The operand to check.
|
|
427
|
+
* @param values - An array of values to check against.
|
|
428
|
+
* @returns An `InExpressionNode`.
|
|
429
|
+
*
|
|
430
|
+
* @example
|
|
431
|
+
* notInList(users.id, [1, 2, 3]);
|
|
432
|
+
*/
|
|
433
|
+
export const notInList = (left: OperandNode | ColumnRef, values: (string | number | LiteralNode)[]): InExpressionNode =>
|
|
434
|
+
createInExpression('NOT IN', left, values.map(v => toOperand(v)));
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Creates an IN subquery check (`left IN (SELECT ...)`).
|
|
438
|
+
*
|
|
439
|
+
* @param left - The operand to check.
|
|
440
|
+
* @param subquery - The subquery to run.
|
|
441
|
+
* @returns An `InExpressionNode`.
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* inSubquery(
|
|
445
|
+
* posts.authorId,
|
|
446
|
+
* selectFromEntity(User).select({ id: users.id }).where(eq(users.isActive, true))
|
|
447
|
+
* );
|
|
448
|
+
*/
|
|
449
|
+
export const inSubquery = (left: OperandNode | ColumnRef, subquery: SelectQueryInput): InExpressionNode =>
|
|
450
|
+
createInExpression('IN', left, toScalarSubqueryNode(subquery));
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Creates a NOT IN subquery check (`left NOT IN (SELECT ...)`).
|
|
454
|
+
*
|
|
455
|
+
* @param left - The operand to check.
|
|
456
|
+
* @param subquery - The subquery to run.
|
|
457
|
+
* @returns An `InExpressionNode`.
|
|
458
|
+
*
|
|
459
|
+
* @example
|
|
460
|
+
* notInSubquery(
|
|
461
|
+
* users.id,
|
|
462
|
+
* selectFromEntity(Blacklist).select({ userId: blacklist.userId })
|
|
463
|
+
* );
|
|
464
|
+
*/
|
|
465
|
+
export const notInSubquery = (left: OperandNode | ColumnRef, subquery: SelectQueryInput): InExpressionNode =>
|
|
466
|
+
createInExpression('NOT IN', left, toScalarSubqueryNode(subquery));
|
|
467
|
+
|
|
468
|
+
const createBetweenExpression = (
|
|
469
|
+
operator: 'BETWEEN' | 'NOT BETWEEN',
|
|
470
|
+
left: OperandNode | ColumnRef,
|
|
471
|
+
lower: OperandNode | ColumnRef | string | number,
|
|
472
|
+
upper: OperandNode | ColumnRef | string | number
|
|
473
|
+
): BetweenExpressionNode => ({
|
|
474
|
+
type: 'BetweenExpression',
|
|
475
|
+
left: toOperandNode(left),
|
|
476
|
+
operator,
|
|
477
|
+
lower: toOperand(lower),
|
|
478
|
+
upper: toOperand(upper)
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Creates a BETWEEN check (`left BETWEEN lower AND upper`).
|
|
483
|
+
*
|
|
484
|
+
* @param left - The operand to check.
|
|
485
|
+
* @param lower - The lower bound (inclusive).
|
|
486
|
+
* @param upper - The upper bound (inclusive).
|
|
487
|
+
* @returns A `BetweenExpressionNode`.
|
|
488
|
+
*
|
|
489
|
+
* @example
|
|
490
|
+
* between(products.price, 10, 100);
|
|
491
|
+
*/
|
|
492
|
+
export const between = (
|
|
493
|
+
left: OperandNode | ColumnRef,
|
|
494
|
+
lower: OperandNode | ColumnRef | string | number,
|
|
495
|
+
upper: OperandNode | ColumnRef | string | number
|
|
496
|
+
): BetweenExpressionNode => createBetweenExpression('BETWEEN', left, lower, upper);
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Creates a NOT BETWEEN check (`left NOT BETWEEN lower AND upper`).
|
|
500
|
+
*
|
|
501
|
+
* @param left - The operand to check.
|
|
502
|
+
* @param lower - The lower bound (inclusive).
|
|
503
|
+
* @param upper - The upper bound (inclusive).
|
|
504
|
+
* @returns A `BetweenExpressionNode`.
|
|
505
|
+
*
|
|
506
|
+
* @example
|
|
507
|
+
* notBetween(users.age, 20, 30);
|
|
508
|
+
*/
|
|
509
|
+
export const notBetween = (
|
|
510
|
+
left: OperandNode | ColumnRef,
|
|
511
|
+
lower: OperandNode | ColumnRef | string | number,
|
|
512
|
+
upper: OperandNode | ColumnRef | string | number
|
|
513
|
+
): BetweenExpressionNode => createBetweenExpression('NOT BETWEEN', left, lower, upper);
|
|
514
|
+
|
|
515
|
+
const createArithmeticExpression = (
|
|
516
|
+
operator: '+' | '-' | '*' | '/',
|
|
517
|
+
left: OperandNode | ColumnRef,
|
|
518
|
+
right: OperandNode | ColumnRef | string | number
|
|
519
|
+
): ArithmeticExpressionNode => ({
|
|
520
|
+
type: 'ArithmeticExpression',
|
|
521
|
+
left: toOperand(left),
|
|
522
|
+
operator,
|
|
523
|
+
right: toOperand(right)
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Creates an addition expression (`left + right`).
|
|
528
|
+
*
|
|
529
|
+
* @param left - The left operand.
|
|
530
|
+
* @param right - The right operand.
|
|
531
|
+
* @returns An `ArithmeticExpressionNode`.
|
|
532
|
+
*/
|
|
533
|
+
export const add = (
|
|
534
|
+
left: OperandNode | ColumnRef,
|
|
535
|
+
right: OperandNode | ColumnRef | string | number
|
|
536
|
+
): ArithmeticExpressionNode => createArithmeticExpression('+', left, right);
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Creates a subtraction expression (`left - right`).
|
|
540
|
+
*
|
|
541
|
+
* @param left - The left operand.
|
|
542
|
+
* @param right - The right operand.
|
|
543
|
+
* @returns An `ArithmeticExpressionNode`.
|
|
544
|
+
*/
|
|
545
|
+
export const sub = (
|
|
546
|
+
left: OperandNode | ColumnRef,
|
|
547
|
+
right: OperandNode | ColumnRef | string | number
|
|
548
|
+
): ArithmeticExpressionNode => createArithmeticExpression('-', left, right);
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Creates a multiplication expression (`left * right`).
|
|
552
|
+
*
|
|
553
|
+
* @param left - The left operand.
|
|
554
|
+
* @param right - The right operand.
|
|
555
|
+
* @returns An `ArithmeticExpressionNode`.
|
|
556
|
+
*/
|
|
557
|
+
export const mul = (
|
|
558
|
+
left: OperandNode | ColumnRef,
|
|
559
|
+
right: OperandNode | ColumnRef | string | number
|
|
560
|
+
): ArithmeticExpressionNode => createArithmeticExpression('*', left, right);
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Creates a division expression (`left / right`).
|
|
564
|
+
*
|
|
565
|
+
* @param left - The left operand.
|
|
566
|
+
* @param right - The right operand.
|
|
567
|
+
* @returns An `ArithmeticExpressionNode`.
|
|
568
|
+
*/
|
|
569
|
+
export const div = (
|
|
570
|
+
left: OperandNode | ColumnRef,
|
|
571
|
+
right: OperandNode | ColumnRef | string | number
|
|
572
|
+
): ArithmeticExpressionNode => createArithmeticExpression('/', left, right);
|
|
573
|
+
|
|
574
|
+
const createBitwiseExpression = (
|
|
575
|
+
operator: BitwiseOperator,
|
|
576
|
+
left: OperandNode | ColumnRef,
|
|
577
|
+
right: OperandNode | ColumnRef | string | number
|
|
578
|
+
): BitwiseExpressionNode => ({
|
|
579
|
+
type: 'BitwiseExpression',
|
|
580
|
+
left: toOperand(left),
|
|
581
|
+
operator,
|
|
582
|
+
right: toOperand(right)
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Creates a bitwise AND expression (`left & right`).
|
|
587
|
+
*
|
|
588
|
+
* @param left - The left operand.
|
|
589
|
+
* @param right - The right operand.
|
|
590
|
+
* @returns A `BitwiseExpressionNode`.
|
|
591
|
+
*/
|
|
592
|
+
export const bitAnd = (
|
|
593
|
+
left: OperandNode | ColumnRef,
|
|
594
|
+
right: OperandNode | ColumnRef | string | number
|
|
595
|
+
): BitwiseExpressionNode => createBitwiseExpression('&', left, right);
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Creates a bitwise OR expression (`left | right`).
|
|
599
|
+
*
|
|
600
|
+
* @param left - The left operand.
|
|
601
|
+
* @param right - The right operand.
|
|
602
|
+
* @returns A `BitwiseExpressionNode`.
|
|
603
|
+
*/
|
|
604
|
+
export const bitOr = (
|
|
605
|
+
left: OperandNode | ColumnRef,
|
|
606
|
+
right: OperandNode | ColumnRef | string | number
|
|
607
|
+
): BitwiseExpressionNode => createBitwiseExpression('|', left, right);
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Creates a bitwise XOR expression (`left ^ right`).
|
|
611
|
+
*
|
|
612
|
+
* @param left - The left operand.
|
|
613
|
+
* @param right - The right operand.
|
|
614
|
+
* @returns A `BitwiseExpressionNode`.
|
|
615
|
+
*/
|
|
616
|
+
export const bitXor = (
|
|
617
|
+
left: OperandNode | ColumnRef,
|
|
618
|
+
right: OperandNode | ColumnRef | string | number
|
|
619
|
+
): BitwiseExpressionNode => createBitwiseExpression('^', left, right);
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Creates a bitwise left-shift expression (`left << right`).
|
|
623
|
+
*
|
|
624
|
+
* @param left - The left operand.
|
|
625
|
+
* @param right - The right operand (number of bits).
|
|
626
|
+
* @returns A `BitwiseExpressionNode`.
|
|
627
|
+
*/
|
|
628
|
+
export const shiftLeft = (
|
|
629
|
+
left: OperandNode | ColumnRef,
|
|
630
|
+
right: OperandNode | ColumnRef | string | number
|
|
631
|
+
): BitwiseExpressionNode => createBitwiseExpression('<<', left, right);
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Creates a bitwise right-shift expression (`left >> right`).
|
|
635
|
+
*
|
|
636
|
+
* @param left - The left operand.
|
|
637
|
+
* @param right - The right operand (number of bits).
|
|
638
|
+
* @returns A `BitwiseExpressionNode`.
|
|
639
|
+
*/
|
|
640
|
+
export const shiftRight = (
|
|
641
|
+
left: OperandNode | ColumnRef,
|
|
642
|
+
right: OperandNode | ColumnRef | string | number
|
|
643
|
+
): BitwiseExpressionNode => createBitwiseExpression('>>', left, right);
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Creates a JSON path extraction expression.
|
|
647
|
+
*
|
|
648
|
+
* @param col - The source column (should be a JSON/JSONB column).
|
|
649
|
+
* @param path - The JSON path expression (e.g., '$.address.city').
|
|
650
|
+
* @returns A `JsonPathNode`.
|
|
651
|
+
*
|
|
652
|
+
* @example
|
|
653
|
+
* jsonPath(users.profile, '$.settings.theme');
|
|
654
|
+
*/
|
|
655
|
+
export const jsonPath = (col: ColumnRef | ColumnNode, path: string): JsonPathNode => ({
|
|
656
|
+
type: 'JsonPath',
|
|
657
|
+
column: columnOperand(col),
|
|
658
|
+
path
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Creates a CASE expression (`CASE WHEN ... THEN ... ELSE ... END`).
|
|
663
|
+
*
|
|
664
|
+
* @param conditions - An array of `{ when, then }` objects.
|
|
665
|
+
* @param elseValue - Optional value for the `ELSE` clause.
|
|
666
|
+
* @returns A `CaseExpressionNode`.
|
|
667
|
+
*
|
|
668
|
+
* @example
|
|
669
|
+
* caseWhen([
|
|
670
|
+
* { when: gt(users.age, 65), then: 'Senior' },
|
|
671
|
+
* { when: gt(users.age, 18), then: 'Adult' }
|
|
672
|
+
* ], 'Minor');
|
|
673
|
+
*/
|
|
674
|
+
export const caseWhen = (
|
|
675
|
+
conditions: { when: ExpressionNode; then: OperandNode | ColumnRef | string | number | boolean | null }[],
|
|
676
|
+
elseValue?: OperandNode | ColumnRef | string | number | boolean | null
|
|
677
|
+
): CaseExpressionNode => ({
|
|
678
|
+
type: 'CaseExpression',
|
|
679
|
+
conditions: conditions.map(c => ({
|
|
680
|
+
when: c.when,
|
|
681
|
+
then: toOperand(c.then)
|
|
682
|
+
})),
|
|
683
|
+
else: elseValue !== undefined ? toOperand(elseValue) : undefined
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Creates a CAST expression (`CAST(expression AS type)`).
|
|
688
|
+
*
|
|
689
|
+
* @param expression - The expression to cast.
|
|
690
|
+
* @param castType - The target SQL type (e.g., 'VARCHAR', 'INTEGER').
|
|
691
|
+
* @returns A `CastExpressionNode`.
|
|
692
|
+
*
|
|
693
|
+
* @example
|
|
694
|
+
* cast(users.age, 'VARCHAR');
|
|
695
|
+
*/
|
|
696
|
+
export const cast = (
|
|
697
|
+
expression: OperandNode | ColumnRef | string | number | boolean | null,
|
|
698
|
+
castType: string
|
|
699
|
+
): CastExpressionNode => ({
|
|
700
|
+
type: 'Cast',
|
|
701
|
+
expression: toOperand(expression),
|
|
702
|
+
castType
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Creates an EXISTS check (`EXISTS (SELECT ...)`).
|
|
707
|
+
*
|
|
708
|
+
* @param subquery - The subquery to check.
|
|
709
|
+
* @returns An `ExistsExpressionNode`.
|
|
710
|
+
*
|
|
711
|
+
* @example
|
|
712
|
+
* exists(
|
|
713
|
+
* selectFromEntity(Order).where(eq(orders.userId, users.id))
|
|
714
|
+
* );
|
|
715
|
+
*/
|
|
716
|
+
export const exists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
|
|
717
|
+
type: 'ExistsExpression',
|
|
718
|
+
operator: 'EXISTS',
|
|
719
|
+
subquery
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Creates a NOT EXISTS check (`NOT EXISTS (SELECT ...)`).
|
|
724
|
+
*
|
|
725
|
+
* @param subquery - The subquery to check.
|
|
726
|
+
* @returns An `ExistsExpressionNode`.
|
|
727
|
+
*
|
|
728
|
+
* @example
|
|
729
|
+
* notExists(
|
|
730
|
+
* selectFromEntity(Subscription).where(eq(subscriptions.userId, users.id))
|
|
731
|
+
* );
|
|
732
|
+
*/
|
|
733
|
+
export const notExists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
|
|
734
|
+
type: 'ExistsExpression',
|
|
735
|
+
operator: 'NOT EXISTS',
|
|
736
|
+
subquery
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Creates a COLLATE expression (`expression COLLATE collation`).
|
|
741
|
+
*
|
|
742
|
+
* @param expression - The expression string.
|
|
743
|
+
* @param collation - The collation name (e.g., 'nocase').
|
|
744
|
+
* @returns A `CollateExpressionNode`.
|
|
745
|
+
*
|
|
746
|
+
* @example
|
|
747
|
+
* collate(users.email, 'nocase');
|
|
748
|
+
*/
|
|
749
|
+
export const collate = (
|
|
750
|
+
expression: OperandNode | ColumnRef | string | number | boolean | null,
|
|
751
|
+
collation: string
|
|
752
|
+
): CollateExpressionNode => ({
|
|
753
|
+
type: 'Collate',
|
|
754
|
+
expression: toOperand(expression),
|
|
755
|
+
collation
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Creates an `IS DISTINCT FROM` expression.
|
|
760
|
+
*
|
|
761
|
+
* Unlike `neq(a, b)`, this comparison is **null-safe**:
|
|
762
|
+
* returns `true` when values are different **including** when one
|
|
763
|
+
* (or both) is `NULL`. Never returns `NULL`.
|
|
764
|
+
*
|
|
765
|
+
* | left | right | neq | isDistinctFrom |
|
|
766
|
+
* |-------|-------|-------|----------------|
|
|
767
|
+
* | 1 | 2 | true | true |
|
|
768
|
+
* | 1 | 1 | false | false |
|
|
769
|
+
* | NULL | 1 | NULL | **true** |
|
|
770
|
+
* | NULL | NULL | NULL | **false** |
|
|
771
|
+
*
|
|
772
|
+
* Compilation by dialect:
|
|
773
|
+
* - PostgreSQL / SQLite / MSSQL → `left IS DISTINCT FROM right`
|
|
774
|
+
* - MySQL → `NOT (left <=> right)`
|
|
775
|
+
*
|
|
776
|
+
* @param left - The left operand.
|
|
777
|
+
* @param right - The right operand.
|
|
778
|
+
* @returns An `IsDistinctExpressionNode`.
|
|
779
|
+
*
|
|
780
|
+
* @example
|
|
781
|
+
* ```ts
|
|
782
|
+
* // Find users whose email has changed compared to backup
|
|
783
|
+
* where(isDistinctFrom(users.columns.email, backup.columns.email))
|
|
784
|
+
* ```
|
|
785
|
+
*/
|
|
786
|
+
export const isDistinctFrom = (
|
|
787
|
+
left: OperandNode | ColumnRef,
|
|
788
|
+
right: OperandNode | ColumnRef | LiteralValue
|
|
789
|
+
): IsDistinctExpressionNode => ({
|
|
790
|
+
type: 'IsDistinctExpression',
|
|
791
|
+
left: toOperandNode(left),
|
|
792
|
+
operator: 'IS DISTINCT FROM',
|
|
793
|
+
right: toOperand(right),
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Creates an `IS NOT DISTINCT FROM` expression.
|
|
798
|
+
*
|
|
799
|
+
* The inverse of `isDistinctFrom`. Equivalent to a **null-safe** equality:
|
|
800
|
+
* returns `true` when values are equal **including** when both
|
|
801
|
+
* are `NULL`. Never returns `NULL`.
|
|
802
|
+
*
|
|
803
|
+
* | left | right | eq | isNotDistinctFrom |
|
|
804
|
+
* |-------|-------|-------|------------------|
|
|
805
|
+
* | 1 | 1 | true | true |
|
|
806
|
+
* | 1 | 2 | false | false |
|
|
807
|
+
* | NULL | 1 | NULL | **false** |
|
|
808
|
+
* | NULL | NULL | NULL | **true** |
|
|
809
|
+
*
|
|
810
|
+
* Compilation by dialect:
|
|
811
|
+
* - PostgreSQL / SQLite / MSSQL → `left IS NOT DISTINCT FROM right`
|
|
812
|
+
* - MySQL → `left <=> right`
|
|
813
|
+
*
|
|
814
|
+
* @param left - The left operand.
|
|
815
|
+
* @param right - The right operand.
|
|
816
|
+
* @returns An `IsDistinctExpressionNode`.
|
|
817
|
+
*
|
|
818
|
+
* @example
|
|
819
|
+
* ```ts
|
|
820
|
+
* // Rows where deletedAt is NULL or equal to a specific date
|
|
821
|
+
* where(isNotDistinctFrom(orders.columns.deletedAt, null))
|
|
822
|
+
* // → WHERE "orders"."deletedAt" IS NOT DISTINCT FROM NULL
|
|
823
|
+
* // equivalent to: WHERE deletedAt IS NULL (but works with any value)
|
|
824
|
+
* ```
|
|
825
|
+
*/
|
|
826
|
+
export const isNotDistinctFrom = (
|
|
827
|
+
left: OperandNode | ColumnRef,
|
|
828
|
+
right: OperandNode | ColumnRef | LiteralValue
|
|
829
|
+
): IsDistinctExpressionNode => ({
|
|
830
|
+
type: 'IsDistinctExpression',
|
|
831
|
+
left: toOperandNode(left),
|
|
832
|
+
operator: 'IS NOT DISTINCT FROM',
|
|
833
|
+
right: toOperand(right),
|
|
834
|
+
});
|