metal-orm 1.0.13 → 1.0.15
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 +75 -82
- package/dist/decorators/index.cjs +1600 -27
- package/dist/decorators/index.cjs.map +1 -1
- package/dist/decorators/index.d.cts +6 -2
- package/dist/decorators/index.d.ts +6 -2
- package/dist/decorators/index.js +1599 -27
- package/dist/decorators/index.js.map +1 -1
- package/dist/index.cjs +4608 -3429
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +511 -159
- package/dist/index.d.ts +511 -159
- package/dist/index.js +4526 -3415
- package/dist/index.js.map +1 -1
- package/dist/{select-CCp1oz9p.d.cts → select-Bkv8g8u_.d.cts} +193 -67
- package/dist/{select-CCp1oz9p.d.ts → select-Bkv8g8u_.d.ts} +193 -67
- package/package.json +1 -1
- package/src/codegen/typescript.ts +38 -35
- package/src/core/ast/adapters.ts +21 -0
- package/src/core/ast/aggregate-functions.ts +13 -13
- package/src/core/ast/builders.ts +56 -43
- package/src/core/ast/expression-builders.ts +34 -34
- package/src/core/ast/expression-nodes.ts +18 -16
- package/src/core/ast/expression-visitor.ts +122 -69
- package/src/core/ast/expression.ts +6 -4
- package/src/core/ast/join-metadata.ts +15 -0
- package/src/core/ast/join-node.ts +22 -20
- package/src/core/ast/join.ts +5 -5
- package/src/core/ast/query.ts +52 -88
- package/src/core/ast/types.ts +20 -0
- package/src/core/ast/window-functions.ts +55 -55
- package/src/core/ddl/dialects/base-schema-dialect.ts +20 -6
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +32 -8
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +21 -10
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +52 -7
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +23 -9
- package/src/core/ddl/introspect/catalogs/index.ts +1 -0
- package/src/core/ddl/introspect/catalogs/postgres.ts +143 -0
- package/src/core/ddl/introspect/context.ts +9 -0
- package/src/core/ddl/introspect/functions/postgres.ts +26 -0
- package/src/core/ddl/introspect/mssql.ts +149 -149
- package/src/core/ddl/introspect/mysql.ts +99 -99
- package/src/core/ddl/introspect/postgres.ts +245 -154
- package/src/core/ddl/introspect/registry.ts +26 -0
- package/src/core/ddl/introspect/run-select.ts +25 -0
- package/src/core/ddl/introspect/sqlite.ts +7 -7
- package/src/core/ddl/introspect/types.ts +23 -19
- package/src/core/ddl/introspect/utils.ts +1 -1
- package/src/core/ddl/naming-strategy.ts +10 -0
- package/src/core/ddl/schema-dialect.ts +41 -0
- package/src/core/ddl/schema-diff.ts +211 -179
- package/src/core/ddl/schema-generator.ts +16 -90
- package/src/core/ddl/schema-introspect.ts +25 -32
- package/src/core/ddl/schema-plan-executor.ts +17 -0
- package/src/core/ddl/schema-types.ts +46 -39
- package/src/core/ddl/sql-writing.ts +170 -0
- package/src/core/dialect/abstract.ts +144 -126
- package/src/core/dialect/base/cte-compiler.ts +33 -0
- package/src/core/dialect/base/function-table-formatter.ts +132 -0
- package/src/core/dialect/base/groupby-compiler.ts +21 -0
- package/src/core/dialect/base/join-compiler.ts +26 -0
- package/src/core/dialect/base/orderby-compiler.ts +21 -0
- package/src/core/dialect/base/pagination-strategy.ts +32 -0
- package/src/core/dialect/base/returning-strategy.ts +56 -0
- package/src/core/dialect/base/sql-dialect.ts +181 -204
- package/src/core/dialect/dialect-factory.ts +91 -0
- package/src/core/dialect/mssql/functions.ts +101 -0
- package/src/core/dialect/mssql/index.ts +128 -126
- package/src/core/dialect/mysql/functions.ts +101 -0
- package/src/core/dialect/mysql/index.ts +20 -18
- package/src/core/dialect/postgres/functions.ts +95 -0
- package/src/core/dialect/postgres/index.ts +30 -28
- package/src/core/dialect/sqlite/functions.ts +115 -0
- package/src/core/dialect/sqlite/index.ts +30 -28
- package/src/core/driver/database-driver.ts +11 -0
- package/src/core/driver/mssql-driver.ts +20 -0
- package/src/core/driver/mysql-driver.ts +20 -0
- package/src/core/driver/postgres-driver.ts +20 -0
- package/src/core/driver/sqlite-driver.ts +20 -0
- package/src/core/execution/db-executor.ts +63 -0
- package/src/core/execution/executors/mssql-executor.ts +39 -0
- package/src/core/execution/executors/mysql-executor.ts +47 -0
- package/src/core/execution/executors/postgres-executor.ts +32 -0
- package/src/core/execution/executors/sqlite-executor.ts +31 -0
- package/src/core/functions/datetime.ts +132 -0
- package/src/core/functions/numeric.ts +179 -0
- package/src/core/functions/standard-strategy.ts +47 -0
- package/src/core/functions/text.ts +147 -0
- package/src/core/functions/types.ts +18 -0
- package/src/core/hydration/types.ts +57 -0
- package/src/decorators/bootstrap.ts +10 -0
- package/src/decorators/relations.ts +15 -0
- package/src/index.ts +30 -19
- package/src/orm/entity-metadata.ts +7 -0
- package/src/orm/entity.ts +58 -27
- package/src/orm/hydration.ts +25 -17
- package/src/orm/lazy-batch.ts +46 -2
- package/src/orm/orm-context.ts +60 -60
- package/src/orm/query-logger.ts +1 -1
- package/src/orm/relation-change-processor.ts +43 -2
- package/src/orm/relations/has-one.ts +139 -0
- package/src/orm/transaction-runner.ts +1 -1
- package/src/orm/unit-of-work.ts +60 -60
- package/src/query-builder/delete.ts +22 -5
- package/src/query-builder/hydration-manager.ts +2 -1
- package/src/query-builder/hydration-planner.ts +8 -7
- package/src/query-builder/insert.ts +22 -5
- package/src/query-builder/relation-conditions.ts +9 -8
- package/src/query-builder/relation-service.ts +3 -2
- package/src/query-builder/select.ts +66 -61
- package/src/query-builder/update.ts +22 -5
- package/src/schema/column.ts +246 -246
- package/src/schema/relation.ts +35 -1
- package/src/schema/table.ts +28 -28
- package/src/schema/types.ts +41 -31
- package/src/orm/db-executor.ts +0 -11
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// Pure AST Builders - No Dialect Logic Here!
|
|
2
|
+
|
|
3
|
+
import { ColumnDef } from '../../schema/column.js';
|
|
4
|
+
import { columnOperand, valueToOperand } from '../ast/expression-builders.js';
|
|
5
|
+
import { FunctionNode, OperandNode, isOperandNode } from '../ast/expression.js';
|
|
6
|
+
|
|
7
|
+
type OperandInput = OperandNode | ColumnDef | string | number | boolean | null;
|
|
8
|
+
|
|
9
|
+
const isColumnDef = (val: any): val is ColumnDef => !!val && typeof val === 'object' && 'type' in val && 'name' in val;
|
|
10
|
+
|
|
11
|
+
const toOperand = (input: OperandInput): OperandNode => {
|
|
12
|
+
if (isOperandNode(input)) return input;
|
|
13
|
+
if (isColumnDef(input)) return columnOperand(input);
|
|
14
|
+
return valueToOperand(input as any);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const fn = (key: string, args: OperandInput[]): FunctionNode => ({
|
|
18
|
+
type: 'Function',
|
|
19
|
+
name: key,
|
|
20
|
+
fn: key,
|
|
21
|
+
args: args.map(toOperand)
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// ----------------------
|
|
25
|
+
// Helper Functions
|
|
26
|
+
// ----------------------
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Helper: ABS(x) - Returns the absolute value of a number
|
|
30
|
+
*/
|
|
31
|
+
export const abs = (value: OperandInput): FunctionNode => fn('ABS', [value]);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Helper: ACOS(x) - Returns the arccosine (inverse cosine)
|
|
35
|
+
*/
|
|
36
|
+
export const acos = (value: OperandInput): FunctionNode => fn('ACOS', [value]);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Helper: ASIN(x) - Returns the arcsine (inverse sine)
|
|
40
|
+
*/
|
|
41
|
+
export const asin = (value: OperandInput): FunctionNode => fn('ASIN', [value]);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Helper: ATAN(x) - Returns the arctangent (inverse tangent)
|
|
45
|
+
*/
|
|
46
|
+
export const atan = (value: OperandInput): FunctionNode => fn('ATAN', [value]);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Helper: ATAN2(y, x) - Returns the arctangent of the two arguments
|
|
50
|
+
*/
|
|
51
|
+
export const atan2 = (y: OperandInput, x: OperandInput): FunctionNode => fn('ATAN2', [y, x]);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Helper: CEIL(x) / CEILING(x) - Returns the smallest integer >= x
|
|
55
|
+
*/
|
|
56
|
+
export const ceil = (value: OperandInput): FunctionNode => fn('CEIL', [value]);
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Helper: CEILING(x) - Alias for CEIL
|
|
60
|
+
*/
|
|
61
|
+
export const ceiling = (value: OperandInput): FunctionNode => fn('CEILING', [value]);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Helper: COS(x) - Returns the cosine of a number (in radians)
|
|
65
|
+
*/
|
|
66
|
+
export const cos = (value: OperandInput): FunctionNode => fn('COS', [value]);
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Helper: COT(x) - Returns the cotangent of a number
|
|
70
|
+
*/
|
|
71
|
+
export const cot = (value: OperandInput): FunctionNode => fn('COT', [value]);
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Helper: DEGREES(x) - Converts radians to degrees
|
|
75
|
+
*/
|
|
76
|
+
export const degrees = (value: OperandInput): FunctionNode => fn('DEGREES', [value]);
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Helper: EXP(x) - Returns e raised to the power of the argument
|
|
80
|
+
*/
|
|
81
|
+
export const exp = (value: OperandInput): FunctionNode => fn('EXP', [value]);
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Helper: FLOOR(x) - Returns the largest integer <= x
|
|
85
|
+
*/
|
|
86
|
+
export const floor = (value: OperandInput): FunctionNode => fn('FLOOR', [value]);
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Helper: LN(x) - Returns the natural logarithm (base e)
|
|
90
|
+
*/
|
|
91
|
+
export const ln = (value: OperandInput): FunctionNode => fn('LN', [value]);
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Helper: LOG(x) - Returns the base-10 logarithm
|
|
95
|
+
*/
|
|
96
|
+
export const log = (value: OperandInput): FunctionNode => fn('LOG', [value]);
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Helper: LOG10(x) - Returns the base-10 logarithm
|
|
100
|
+
*/
|
|
101
|
+
export const log10 = (value: OperandInput): FunctionNode => fn('LOG10', [value]);
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Helper: LOG(base, x) - Returns the logarithm of x for a specific base
|
|
105
|
+
*/
|
|
106
|
+
export const logBase = (base: OperandInput, value: OperandInput): FunctionNode => fn('LOG_BASE', [base, value]);
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Helper: MOD(x, y) - Returns the remainder of x/y
|
|
110
|
+
*/
|
|
111
|
+
export const mod = (x: OperandInput, y: OperandInput): FunctionNode => fn('MOD', [x, y]);
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Helper: PI() - Returns the value of PI (approx. 3.14159...)
|
|
115
|
+
*/
|
|
116
|
+
export const pi = (): FunctionNode => fn('PI', []);
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Helper: POWER(x, y) - Returns x raised to the power of y
|
|
120
|
+
*/
|
|
121
|
+
export const power = (x: OperandInput, y: OperandInput): FunctionNode => fn('POWER', [x, y]);
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Helper: POW(x, y) - Alias for POWER
|
|
125
|
+
*/
|
|
126
|
+
export const pow = (x: OperandInput, y: OperandInput): FunctionNode => fn('POW', [x, y]);
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Helper: RADIANS(x) - Converts degrees to radians
|
|
130
|
+
*/
|
|
131
|
+
export const radians = (value: OperandInput): FunctionNode => fn('RADIANS', [value]);
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Helper: RAND() / RANDOM() - Returns a random number
|
|
135
|
+
*/
|
|
136
|
+
export const random = (): FunctionNode => fn('RANDOM', []);
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Helper: RAND() - Alias for RANDOM (returns float 0-1)
|
|
140
|
+
*/
|
|
141
|
+
export const rand = (): FunctionNode => fn('RAND', []);
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Helper: ROUND(x[, decimals]) - Rounds a number to specified decimal places
|
|
145
|
+
*/
|
|
146
|
+
export const round = (value: OperandInput, decimals?: OperandInput): FunctionNode =>
|
|
147
|
+
decimals === undefined ? fn('ROUND', [value]) : fn('ROUND', [value, decimals]);
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Helper: SIGN(x) - Returns the sign of a number (-1, 0, 1)
|
|
151
|
+
*/
|
|
152
|
+
export const sign = (value: OperandInput): FunctionNode => fn('SIGN', [value]);
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Helper: SIN(x) - Returns the sine of a number (in radians)
|
|
156
|
+
*/
|
|
157
|
+
export const sin = (value: OperandInput): FunctionNode => fn('SIN', [value]);
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Helper: SQRT(x) - Returns the square root of a number
|
|
161
|
+
*/
|
|
162
|
+
export const sqrt = (value: OperandInput): FunctionNode => fn('SQRT', [value]);
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Helper: TAN(x) - Returns the tangent of a number (in radians)
|
|
166
|
+
*/
|
|
167
|
+
export const tan = (value: OperandInput): FunctionNode => fn('TAN', [value]);
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Helper: TRUNC(x[, decimals]) / TRUNCATE(x, decimals) - Truncates a number without rounding
|
|
171
|
+
*/
|
|
172
|
+
export const trunc = (value: OperandInput, decimals?: OperandInput): FunctionNode =>
|
|
173
|
+
decimals === undefined ? fn('TRUNC', [value]) : fn('TRUNC', [value, decimals]);
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Helper: TRUNCATE(x, decimals) - Alias for TRUNC
|
|
177
|
+
*/
|
|
178
|
+
export const truncate = (value: OperandInput, decimals: OperandInput): FunctionNode =>
|
|
179
|
+
fn('TRUNCATE', [value, decimals]);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { FunctionStrategy, FunctionRenderer, FunctionRenderContext } from './types.js';
|
|
2
|
+
|
|
3
|
+
export class StandardFunctionStrategy implements FunctionStrategy {
|
|
4
|
+
protected renderers: Map<string, FunctionRenderer> = new Map();
|
|
5
|
+
|
|
6
|
+
constructor() {
|
|
7
|
+
this.registerStandard();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
protected registerStandard() {
|
|
11
|
+
// Register ANSI standard implementations
|
|
12
|
+
this.add('ABS', ({ compiledArgs }) => `ABS(${compiledArgs[0]})`);
|
|
13
|
+
this.add('UPPER', ({ compiledArgs }) => `UPPER(${compiledArgs[0]})`);
|
|
14
|
+
this.add('LOWER', ({ compiledArgs }) => `LOWER(${compiledArgs[0]})`);
|
|
15
|
+
this.add('LENGTH', ({ compiledArgs }) => `LENGTH(${compiledArgs[0]})`);
|
|
16
|
+
this.add('TRIM', ({ compiledArgs }) => `TRIM(${compiledArgs[0]})`);
|
|
17
|
+
this.add('LTRIM', ({ compiledArgs }) => `LTRIM(${compiledArgs[0]})`);
|
|
18
|
+
this.add('RTRIM', ({ compiledArgs }) => `RTRIM(${compiledArgs[0]})`);
|
|
19
|
+
this.add('SUBSTRING', ({ compiledArgs }) => `SUBSTRING(${compiledArgs.join(', ')})`);
|
|
20
|
+
this.add('CONCAT', ({ compiledArgs }) => `CONCAT(${compiledArgs.join(', ')})`);
|
|
21
|
+
this.add('NOW', () => `NOW()`);
|
|
22
|
+
this.add('CURRENT_DATE', () => `CURRENT_DATE`);
|
|
23
|
+
this.add('CURRENT_TIME', () => `CURRENT_TIME`);
|
|
24
|
+
this.add('EXTRACT', ({ compiledArgs }) => `EXTRACT(${compiledArgs[0]} FROM ${compiledArgs[1]})`);
|
|
25
|
+
this.add('YEAR', ({ compiledArgs }) => `EXTRACT(YEAR FROM ${compiledArgs[0]})`);
|
|
26
|
+
this.add('MONTH', ({ compiledArgs }) => `EXTRACT(MONTH FROM ${compiledArgs[0]})`);
|
|
27
|
+
this.add('DAY', ({ compiledArgs }) => `EXTRACT(DAY FROM ${compiledArgs[0]})`);
|
|
28
|
+
this.add('DATE_ADD', ({ compiledArgs }) => `(${compiledArgs[0]} + INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
|
|
29
|
+
this.add('DATE_SUB', ({ compiledArgs }) => `(${compiledArgs[0]} - INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
|
|
30
|
+
this.add('DATE_DIFF', ({ compiledArgs }) => `DATEDIFF(${compiledArgs[0]}, ${compiledArgs[1]})`);
|
|
31
|
+
this.add('DATE_FORMAT', ({ compiledArgs }) => `DATE_FORMAT(${compiledArgs[0]}, ${compiledArgs[1]})`);
|
|
32
|
+
this.add('UNIX_TIMESTAMP', () => `UNIX_TIMESTAMP()`);
|
|
33
|
+
this.add('FROM_UNIXTIME', ({ compiledArgs }) => `FROM_UNIXTIME(${compiledArgs[0]})`);
|
|
34
|
+
this.add('END_OF_MONTH', ({ compiledArgs }) => `LAST_DAY(${compiledArgs[0]})`);
|
|
35
|
+
this.add('DAY_OF_WEEK', ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})`);
|
|
36
|
+
this.add('WEEK_OF_YEAR', ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})`);
|
|
37
|
+
this.add('DATE_TRUNC', ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
protected add(name: string, renderer: FunctionRenderer) {
|
|
41
|
+
this.renderers.set(name, renderer);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getRenderer(name: string): FunctionRenderer | undefined {
|
|
45
|
+
return this.renderers.get(name);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Pure AST Builders - No Dialect Logic Here!
|
|
2
|
+
|
|
3
|
+
import { ColumnDef } from '../../schema/column.js';
|
|
4
|
+
import { columnOperand, valueToOperand } from '../ast/expression-builders.js';
|
|
5
|
+
import { FunctionNode, OperandNode, isOperandNode } from '../ast/expression.js';
|
|
6
|
+
|
|
7
|
+
type OperandInput = OperandNode | ColumnDef | string | number | boolean | null;
|
|
8
|
+
|
|
9
|
+
const isColumnDef = (val: any): val is ColumnDef => !!val && typeof val === 'object' && 'type' in val && 'name' in val;
|
|
10
|
+
|
|
11
|
+
const toOperand = (input: OperandInput): OperandNode => {
|
|
12
|
+
if (isOperandNode(input)) return input;
|
|
13
|
+
if (isColumnDef(input)) return columnOperand(input);
|
|
14
|
+
return valueToOperand(input as any);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const fn = (key: string, args: OperandInput[]): FunctionNode => ({
|
|
18
|
+
type: 'Function',
|
|
19
|
+
name: key,
|
|
20
|
+
fn: key,
|
|
21
|
+
args: args.map(toOperand)
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Helper: LOWER(str)
|
|
26
|
+
*/
|
|
27
|
+
export const lower = (value: OperandInput): FunctionNode => fn('LOWER', [value]);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Helper: UPPER(str)
|
|
31
|
+
*/
|
|
32
|
+
export const upper = (value: OperandInput): FunctionNode => fn('UPPER', [value]);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Helper: ASCII(str)
|
|
36
|
+
*/
|
|
37
|
+
export const ascii = (value: OperandInput): FunctionNode => fn('ASCII', [value]);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Helper: CHAR(code[, code...])
|
|
41
|
+
*/
|
|
42
|
+
export const char = (...codes: OperandInput[]): FunctionNode => {
|
|
43
|
+
if (codes.length === 0) throw new Error('char() expects at least 1 argument');
|
|
44
|
+
return fn('CHAR', codes);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Helper: CHAR_LENGTH(str)
|
|
49
|
+
*/
|
|
50
|
+
export const charLength = (value: OperandInput): FunctionNode => fn('CHAR_LENGTH', [value]);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Helper: LENGTH(str)
|
|
54
|
+
*/
|
|
55
|
+
export const length = (value: OperandInput): FunctionNode => fn('LENGTH', [value]);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Helper: TRIM([chars FROM] str)
|
|
59
|
+
*/
|
|
60
|
+
export const trim = (value: OperandInput, chars?: OperandInput): FunctionNode =>
|
|
61
|
+
chars === undefined ? fn('TRIM', [value]) : fn('TRIM', [value, chars]);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Helper: LTRIM(str)
|
|
65
|
+
*/
|
|
66
|
+
export const ltrim = (value: OperandInput): FunctionNode => fn('LTRIM', [value]);
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Helper: RTRIM(str)
|
|
70
|
+
*/
|
|
71
|
+
export const rtrim = (value: OperandInput): FunctionNode => fn('RTRIM', [value]);
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Helper: CONCAT(arg1, arg2, ...)
|
|
75
|
+
*/
|
|
76
|
+
export const concat = (...args: OperandInput[]): FunctionNode => {
|
|
77
|
+
if (args.length < 2) throw new Error('concat() expects at least 2 arguments');
|
|
78
|
+
return fn('CONCAT', args);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Helper: CONCAT_WS(separator, arg1, arg2, ...)
|
|
83
|
+
*/
|
|
84
|
+
export const concatWs = (separator: OperandInput, ...args: OperandInput[]): FunctionNode => {
|
|
85
|
+
if (args.length < 1) throw new Error('concatWs() expects at least 2 arguments including the separator');
|
|
86
|
+
return fn('CONCAT_WS', [separator, ...args]);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Helper: SUBSTR(str, start[, length])
|
|
91
|
+
*/
|
|
92
|
+
export const substr = (value: OperandInput, start: OperandInput, length?: OperandInput): FunctionNode =>
|
|
93
|
+
length === undefined ? fn('SUBSTR', [value, start]) : fn('SUBSTR', [value, start, length]);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Helper: LEFT(str, length)
|
|
97
|
+
*/
|
|
98
|
+
export const left = (value: OperandInput, len: OperandInput): FunctionNode => fn('LEFT', [value, len]);
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Helper: RIGHT(str, length)
|
|
102
|
+
*/
|
|
103
|
+
export const right = (value: OperandInput, len: OperandInput): FunctionNode => fn('RIGHT', [value, len]);
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Helper: POSITION(substring IN string)
|
|
107
|
+
*/
|
|
108
|
+
export const position = (substring: OperandInput, value: OperandInput): FunctionNode => fn('POSITION', [substring, value]);
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Helper: INSTR(string, substring)
|
|
112
|
+
*/
|
|
113
|
+
export const instr = (value: OperandInput, substring: OperandInput): FunctionNode => fn('INSTR', [value, substring]);
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Helper: LOCATE(substring, string[, start])
|
|
117
|
+
*/
|
|
118
|
+
export const locate = (substring: OperandInput, value: OperandInput, start?: OperandInput): FunctionNode =>
|
|
119
|
+
start === undefined ? fn('LOCATE', [substring, value]) : fn('LOCATE', [substring, value, start]);
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Helper: REPLACE(string, search, replace)
|
|
123
|
+
*/
|
|
124
|
+
export const replace = (value: OperandInput, search: OperandInput, replacement: OperandInput): FunctionNode =>
|
|
125
|
+
fn('REPLACE', [value, search, replacement]);
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Helper: REPEAT(string, count)
|
|
129
|
+
*/
|
|
130
|
+
export const repeat = (value: OperandInput, count: OperandInput): FunctionNode => fn('REPEAT', [value, count]);
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Helper: LPAD(string, length, padstr)
|
|
134
|
+
*/
|
|
135
|
+
export const lpad = (value: OperandInput, len: OperandInput, pad: OperandInput): FunctionNode =>
|
|
136
|
+
fn('LPAD', [value, len, pad]);
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Helper: RPAD(string, length, padstr)
|
|
140
|
+
*/
|
|
141
|
+
export const rpad = (value: OperandInput, len: OperandInput, pad: OperandInput): FunctionNode =>
|
|
142
|
+
fn('RPAD', [value, len, pad]);
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Helper: SPACE(count)
|
|
146
|
+
*/
|
|
147
|
+
export const space = (count: OperandInput): FunctionNode => fn('SPACE', [count]);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { FunctionNode } from '../ast/expression.js';
|
|
2
|
+
|
|
3
|
+
export interface FunctionRenderContext {
|
|
4
|
+
node: FunctionNode;
|
|
5
|
+
compiledArgs: string[];
|
|
6
|
+
// Helper to allow dialects to call back into the compiler if needed
|
|
7
|
+
// compileOperand: (node: OperandNode) => string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type FunctionRenderer = (ctx: FunctionRenderContext) => string;
|
|
11
|
+
|
|
12
|
+
export interface FunctionStrategy {
|
|
13
|
+
/**
|
|
14
|
+
* Returns a renderer for a specific function name (e.g. "DATE_ADD").
|
|
15
|
+
* Returns undefined if this dialect doesn't support the function.
|
|
16
|
+
*/
|
|
17
|
+
getRenderer(functionName: string): FunctionRenderer | undefined;
|
|
18
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { RelationType } from '../../schema/relation.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plan describing pivot columns needed for hydration
|
|
5
|
+
*/
|
|
6
|
+
export interface HydrationPivotPlan {
|
|
7
|
+
table: string;
|
|
8
|
+
primaryKey: string;
|
|
9
|
+
aliasPrefix: string;
|
|
10
|
+
columns: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Plan for hydrating relationship data
|
|
15
|
+
*/
|
|
16
|
+
export interface HydrationRelationPlan {
|
|
17
|
+
/** Name of the relationship */
|
|
18
|
+
name: string;
|
|
19
|
+
/** Alias prefix for the relationship */
|
|
20
|
+
aliasPrefix: string;
|
|
21
|
+
/** Type of relationship */
|
|
22
|
+
type: RelationType;
|
|
23
|
+
/** Target table name */
|
|
24
|
+
targetTable: string;
|
|
25
|
+
/** Target table primary key */
|
|
26
|
+
targetPrimaryKey: string;
|
|
27
|
+
/** Foreign key column */
|
|
28
|
+
foreignKey: string;
|
|
29
|
+
/** Local key column */
|
|
30
|
+
localKey: string;
|
|
31
|
+
/** Columns to include */
|
|
32
|
+
columns: string[];
|
|
33
|
+
/** Optional pivot plan for many-to-many relationships */
|
|
34
|
+
pivot?: HydrationPivotPlan;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Complete hydration plan for a query
|
|
39
|
+
*/
|
|
40
|
+
export interface HydrationPlan {
|
|
41
|
+
/** Root table name */
|
|
42
|
+
rootTable: string;
|
|
43
|
+
/** Root table primary key */
|
|
44
|
+
rootPrimaryKey: string;
|
|
45
|
+
/** Root table columns */
|
|
46
|
+
rootColumns: string[];
|
|
47
|
+
/** Relationship hydration plans */
|
|
48
|
+
relations: HydrationRelationPlan[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Metadata bag for attaching hydration to query ASTs without coupling AST types.
|
|
53
|
+
*/
|
|
54
|
+
export interface HydrationMetadata {
|
|
55
|
+
hydration?: HydrationPlan;
|
|
56
|
+
[key: string]: unknown;
|
|
57
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SelectQueryBuilder } from '../query-builder/select.js';
|
|
2
2
|
import {
|
|
3
3
|
hasMany,
|
|
4
|
+
hasOne,
|
|
4
5
|
belongsTo,
|
|
5
6
|
belongsToMany,
|
|
6
7
|
RelationKinds,
|
|
@@ -51,6 +52,15 @@ const buildRelationDefinitions = (
|
|
|
51
52
|
|
|
52
53
|
for (const [name, relation] of Object.entries(meta.relations)) {
|
|
53
54
|
switch (relation.kind) {
|
|
55
|
+
case RelationKinds.HasOne: {
|
|
56
|
+
relations[name] = hasOne(
|
|
57
|
+
resolveTableTarget(relation.target, tableMap),
|
|
58
|
+
relation.foreignKey,
|
|
59
|
+
relation.localKey,
|
|
60
|
+
relation.cascade
|
|
61
|
+
);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
54
64
|
case RelationKinds.HasMany: {
|
|
55
65
|
relations[name] = hasMany(
|
|
56
66
|
resolveTableTarget(relation.target, tableMap),
|
|
@@ -16,6 +16,10 @@ export interface HasManyOptions extends BaseRelationOptions {
|
|
|
16
16
|
foreignKey: string;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
export interface HasOneOptions extends BaseRelationOptions {
|
|
20
|
+
foreignKey: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
19
23
|
export interface BelongsToOptions extends BaseRelationOptions {
|
|
20
24
|
foreignKey: string;
|
|
21
25
|
}
|
|
@@ -79,6 +83,17 @@ export function HasMany(options: HasManyOptions) {
|
|
|
79
83
|
}));
|
|
80
84
|
}
|
|
81
85
|
|
|
86
|
+
export function HasOne(options: HasOneOptions) {
|
|
87
|
+
return createFieldDecorator(propertyName => ({
|
|
88
|
+
kind: RelationKinds.HasOne,
|
|
89
|
+
propertyKey: propertyName,
|
|
90
|
+
target: options.target,
|
|
91
|
+
foreignKey: options.foreignKey,
|
|
92
|
+
localKey: options.localKey,
|
|
93
|
+
cascade: options.cascade
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
|
|
82
97
|
export function BelongsTo(options: BelongsToOptions) {
|
|
83
98
|
return createFieldDecorator(propertyName => ({
|
|
84
99
|
kind: RelationKinds.BelongsTo,
|
package/src/index.ts
CHANGED
|
@@ -1,28 +1,39 @@
|
|
|
1
|
-
|
|
2
1
|
export * from './schema/table.js';
|
|
3
2
|
export * from './schema/column.js';
|
|
4
3
|
export * from './schema/relation.js';
|
|
5
4
|
export * from './schema/types.js';
|
|
6
|
-
export * from './query-builder/select.js';
|
|
7
|
-
export * from './query-builder/insert.js';
|
|
8
|
-
export * from './query-builder/update.js';
|
|
9
|
-
export * from './query-builder/delete.js';
|
|
10
|
-
export * from './core/ast/expression.js';
|
|
11
|
-
export * from './core/
|
|
12
|
-
export * from './core/dialect/
|
|
13
|
-
export * from './core/dialect/
|
|
14
|
-
export * from './core/dialect/
|
|
15
|
-
export * from './core/
|
|
16
|
-
export * from './core/ddl/schema-
|
|
17
|
-
export * from './core/ddl/schema-
|
|
18
|
-
export * from './core/ddl/schema-
|
|
19
|
-
export * from './
|
|
20
|
-
export * from './
|
|
21
|
-
export * from './
|
|
22
|
-
export * from './
|
|
23
|
-
export * from './
|
|
5
|
+
export * from './query-builder/select.js';
|
|
6
|
+
export * from './query-builder/insert.js';
|
|
7
|
+
export * from './query-builder/update.js';
|
|
8
|
+
export * from './query-builder/delete.js';
|
|
9
|
+
export * from './core/ast/expression.js';
|
|
10
|
+
export * from './core/hydration/types.js';
|
|
11
|
+
export * from './core/dialect/mysql/index.js';
|
|
12
|
+
export * from './core/dialect/mssql/index.js';
|
|
13
|
+
export * from './core/dialect/sqlite/index.js';
|
|
14
|
+
export * from './core/dialect/postgres/index.js';
|
|
15
|
+
export * from './core/ddl/schema-generator.js';
|
|
16
|
+
export * from './core/ddl/schema-types.js';
|
|
17
|
+
export * from './core/ddl/schema-diff.js';
|
|
18
|
+
export * from './core/ddl/schema-introspect.js';
|
|
19
|
+
export * from './core/ddl/introspect/registry.js';
|
|
20
|
+
export * from './core/functions/text.js';
|
|
21
|
+
export * from './core/functions/numeric.js';
|
|
22
|
+
export * from './core/functions/datetime.js';
|
|
23
|
+
export * from './orm/als.js';
|
|
24
|
+
export * from './orm/hydration.js';
|
|
25
|
+
export * from './codegen/typescript.js';
|
|
26
|
+
export * from './orm/orm-context.js';
|
|
27
|
+
export * from './orm/entity.js';
|
|
24
28
|
export * from './orm/lazy-batch.js';
|
|
25
29
|
export * from './orm/relations/has-many.js';
|
|
26
30
|
export * from './orm/relations/belongs-to.js';
|
|
27
31
|
export * from './orm/relations/many-to-many.js';
|
|
28
32
|
export * from './orm/execute.js';
|
|
33
|
+
|
|
34
|
+
// NEW: execution abstraction + helpers
|
|
35
|
+
export * from './core/execution/db-executor.js';
|
|
36
|
+
export * from './core/execution/executors/postgres-executor.js';
|
|
37
|
+
export * from './core/execution/executors/mysql-executor.js';
|
|
38
|
+
export * from './core/execution/executors/sqlite-executor.js';
|
|
39
|
+
export * from './core/execution/executors/mssql-executor.js';
|
|
@@ -25,6 +25,12 @@ export interface HasManyRelationMetadata extends BaseRelationMetadata {
|
|
|
25
25
|
localKey?: string;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
export interface HasOneRelationMetadata extends BaseRelationMetadata {
|
|
29
|
+
kind: typeof RelationKinds.HasOne;
|
|
30
|
+
foreignKey: string;
|
|
31
|
+
localKey?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
28
34
|
export interface BelongsToRelationMetadata extends BaseRelationMetadata {
|
|
29
35
|
kind: typeof RelationKinds.BelongsTo;
|
|
30
36
|
foreignKey: string;
|
|
@@ -44,6 +50,7 @@ export interface BelongsToManyRelationMetadata extends BaseRelationMetadata {
|
|
|
44
50
|
|
|
45
51
|
export type RelationMetadata =
|
|
46
52
|
| HasManyRelationMetadata
|
|
53
|
+
| HasOneRelationMetadata
|
|
47
54
|
| BelongsToRelationMetadata
|
|
48
55
|
| BelongsToManyRelationMetadata;
|
|
49
56
|
|