metal-orm 1.0.55 → 1.0.57
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 +21 -20
- package/dist/index.cjs +831 -113
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +524 -71
- package/dist/index.d.ts +524 -71
- package/dist/index.js +794 -113
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/codegen/naming-strategy.ts +3 -1
- package/src/codegen/typescript.ts +20 -10
- package/src/core/ast/aggregate-functions.ts +14 -0
- package/src/core/ast/builders.ts +38 -20
- package/src/core/ast/expression-builders.ts +70 -2
- package/src/core/ast/expression-nodes.ts +305 -274
- package/src/core/ast/expression-visitor.ts +11 -1
- package/src/core/ast/expression.ts +4 -0
- package/src/core/ast/query.ts +3 -0
- package/src/core/ddl/introspect/catalogs/mysql.ts +5 -0
- package/src/core/ddl/introspect/catalogs/sqlite.ts +3 -0
- package/src/core/ddl/introspect/functions/mssql.ts +13 -0
- package/src/core/ddl/introspect/mssql.ts +4 -0
- package/src/core/ddl/introspect/mysql.ts +4 -0
- package/src/core/ddl/introspect/sqlite.ts +4 -0
- package/src/core/dialect/abstract.ts +552 -531
- package/src/core/dialect/base/function-table-formatter.ts +9 -30
- package/src/core/dialect/base/sql-dialect.ts +24 -0
- package/src/core/dialect/mssql/functions.ts +40 -2
- package/src/core/dialect/mysql/functions.ts +16 -2
- package/src/core/dialect/postgres/functions.ts +66 -2
- package/src/core/dialect/postgres/index.ts +17 -4
- package/src/core/dialect/postgres/table-functions.ts +27 -0
- package/src/core/dialect/sqlite/functions.ts +34 -0
- package/src/core/dialect/sqlite/index.ts +17 -1
- package/src/core/driver/database-driver.ts +9 -1
- package/src/core/driver/mssql-driver.ts +3 -0
- package/src/core/driver/mysql-driver.ts +3 -0
- package/src/core/driver/postgres-driver.ts +3 -0
- package/src/core/driver/sqlite-driver.ts +3 -0
- package/src/core/execution/executors/mssql-executor.ts +5 -0
- package/src/core/execution/executors/mysql-executor.ts +5 -0
- package/src/core/execution/executors/postgres-executor.ts +5 -0
- package/src/core/execution/executors/sqlite-executor.ts +5 -0
- package/src/core/functions/array.ts +26 -0
- package/src/core/functions/control-flow.ts +69 -0
- package/src/core/functions/datetime.ts +50 -0
- package/src/core/functions/definitions/aggregate.ts +16 -0
- package/src/core/functions/definitions/control-flow.ts +24 -0
- package/src/core/functions/definitions/datetime.ts +36 -0
- package/src/core/functions/definitions/helpers.ts +29 -0
- package/src/core/functions/definitions/json.ts +49 -0
- package/src/core/functions/definitions/numeric.ts +55 -0
- package/src/core/functions/definitions/string.ts +43 -0
- package/src/core/functions/function-registry.ts +48 -0
- package/src/core/functions/group-concat-helpers.ts +57 -0
- package/src/core/functions/json.ts +38 -0
- package/src/core/functions/numeric.ts +14 -0
- package/src/core/functions/standard-strategy.ts +86 -115
- package/src/core/functions/standard-table-strategy.ts +13 -0
- package/src/core/functions/table-types.ts +15 -0
- package/src/core/functions/text.ts +57 -0
- package/src/core/sql/sql.ts +59 -38
- package/src/decorators/bootstrap.ts +5 -4
- package/src/index.ts +18 -11
- package/src/orm/hydration-context.ts +10 -0
- package/src/orm/identity-map.ts +19 -0
- package/src/orm/interceptor-pipeline.ts +4 -0
- package/src/orm/relations/belongs-to.ts +17 -0
- package/src/orm/relations/has-one.ts +17 -0
- package/src/orm/relations/many-to-many.ts +41 -0
- package/src/query-builder/select.ts +68 -68
- package/src/schema/table-guards.ts +6 -0
- package/src/schema/types.ts +8 -1
|
@@ -18,6 +18,7 @@ const toOperand = (input: OperandInput): OperandNode => {
|
|
|
18
18
|
const fn = (key: string, args: OperandInput[]): FunctionNode => ({
|
|
19
19
|
type: 'Function',
|
|
20
20
|
name: key,
|
|
21
|
+
fn: key,
|
|
21
22
|
args: args.map(toOperand)
|
|
22
23
|
});
|
|
23
24
|
|
|
@@ -49,6 +50,18 @@ export const currentTime = (): FunctionNode => fn('CURRENT_TIME', []);
|
|
|
49
50
|
*/
|
|
50
51
|
export const utcNow = (): FunctionNode => fn('UTC_NOW', []);
|
|
51
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Returns the current local time.
|
|
55
|
+
* @returns A FunctionNode representing the LOCALTIME SQL function.
|
|
56
|
+
*/
|
|
57
|
+
export const localTime = (): FunctionNode => fn('LOCALTIME', []);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Returns the current local timestamp.
|
|
61
|
+
* @returns A FunctionNode representing the LOCALTIMESTAMP SQL function.
|
|
62
|
+
*/
|
|
63
|
+
export const localTimestamp = (): FunctionNode => fn('LOCALTIMESTAMP', []);
|
|
64
|
+
|
|
52
65
|
/**
|
|
53
66
|
* Extracts a specified part from a date or datetime value.
|
|
54
67
|
* @param part - The date part to extract (e.g., 'YEAR', 'MONTH', 'DAY', 'HOUR', 'MINUTE', 'SECOND').
|
|
@@ -156,3 +169,40 @@ export const weekOfYear = (date: OperandInput): FunctionNode => fn('WEEK_OF_YEAR
|
|
|
156
169
|
*/
|
|
157
170
|
export const dateTrunc = (part: OperandInput, date: OperandInput): FunctionNode => fn('DATE_TRUNC', [part, date]);
|
|
158
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Returns the difference between two timestamps as an interval.
|
|
174
|
+
* @param timestamp - The end timestamp.
|
|
175
|
+
* @param baseTimestamp - The start timestamp (optional, defaults to current time).
|
|
176
|
+
* @returns A FunctionNode representing the AGE SQL function.
|
|
177
|
+
*/
|
|
178
|
+
export const age = (timestamp: OperandInput, baseTimestamp?: OperandInput): FunctionNode =>
|
|
179
|
+
baseTimestamp === undefined ? fn('AGE', [timestamp]) : fn('AGE', [timestamp, baseTimestamp]);
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Extracts the hour from a date or datetime value.
|
|
183
|
+
* @param date - The date or datetime value.
|
|
184
|
+
* @returns A FunctionNode representing the HOUR SQL function.
|
|
185
|
+
*/
|
|
186
|
+
export const hour = (date: OperandInput): FunctionNode => fn('HOUR', [date]);
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Extracts the minute from a date or datetime value.
|
|
190
|
+
* @param date - The date or datetime value.
|
|
191
|
+
* @returns A FunctionNode representing the MINUTE SQL function.
|
|
192
|
+
*/
|
|
193
|
+
export const minute = (date: OperandInput): FunctionNode => fn('MINUTE', [date]);
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Extracts the second from a date or datetime value.
|
|
197
|
+
* @param date - The date or datetime value.
|
|
198
|
+
* @returns A FunctionNode representing the SECOND SQL function.
|
|
199
|
+
*/
|
|
200
|
+
export const second = (date: OperandInput): FunctionNode => fn('SECOND', [date]);
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Extracts the quarter from a date or datetime value (1-4).
|
|
204
|
+
* @param date - The date or datetime value.
|
|
205
|
+
* @returns A FunctionNode representing the QUARTER SQL function.
|
|
206
|
+
*/
|
|
207
|
+
export const quarter = (date: OperandInput): FunctionNode => fn('QUARTER', [date]);
|
|
208
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FunctionDefinition } from '../function-registry.js';
|
|
2
|
+
import { unaryRenderer } from './helpers.js';
|
|
3
|
+
|
|
4
|
+
export const aggregateFunctionDefinitions: FunctionDefinition[] = [
|
|
5
|
+
{
|
|
6
|
+
name: 'COUNT',
|
|
7
|
+
renderer: ({ compiledArgs }) =>
|
|
8
|
+
compiledArgs.length ? `COUNT(${compiledArgs.join(', ')})` : 'COUNT(*)'
|
|
9
|
+
},
|
|
10
|
+
{ name: 'SUM', renderer: unaryRenderer('SUM') },
|
|
11
|
+
{ name: 'AVG', renderer: unaryRenderer('AVG') },
|
|
12
|
+
{ name: 'MIN', renderer: unaryRenderer('MIN') },
|
|
13
|
+
{ name: 'MAX', renderer: unaryRenderer('MAX') },
|
|
14
|
+
{ name: 'STDDEV', renderer: unaryRenderer('STDDEV') },
|
|
15
|
+
{ name: 'VARIANCE', renderer: unaryRenderer('VARIANCE') }
|
|
16
|
+
];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { FunctionDefinition } from '../function-registry.js';
|
|
2
|
+
|
|
3
|
+
export const controlFlowFunctionDefinitions: FunctionDefinition[] = [
|
|
4
|
+
{
|
|
5
|
+
name: 'COALESCE',
|
|
6
|
+
renderer: ({ compiledArgs }) => `COALESCE(${compiledArgs.join(', ')})`
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
name: 'NULLIF',
|
|
10
|
+
renderer: ({ compiledArgs }) => `NULLIF(${compiledArgs[0]}, ${compiledArgs[1]})`
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'GREATEST',
|
|
14
|
+
renderer: ({ compiledArgs }) => `GREATEST(${compiledArgs.join(', ')})`
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'LEAST',
|
|
18
|
+
renderer: ({ compiledArgs }) => `LEAST(${compiledArgs.join(', ')})`
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'IFNULL',
|
|
22
|
+
renderer: ({ compiledArgs }) => `IFNULL(${compiledArgs[0]}, ${compiledArgs[1]})`
|
|
23
|
+
}
|
|
24
|
+
];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { FunctionDefinition } from '../function-registry.js';
|
|
2
|
+
import { noArgsRenderer } from './helpers.js';
|
|
3
|
+
|
|
4
|
+
export const dateTimeFunctionDefinitions: FunctionDefinition[] = [
|
|
5
|
+
{ name: 'NOW', renderer: noArgsRenderer('NOW') },
|
|
6
|
+
{ name: 'CURRENT_DATE', renderer: () => 'CURRENT_DATE' },
|
|
7
|
+
{ name: 'CURRENT_TIME', renderer: () => 'CURRENT_TIME' },
|
|
8
|
+
{
|
|
9
|
+
name: 'EXTRACT',
|
|
10
|
+
renderer: ({ compiledArgs }) => `EXTRACT(${compiledArgs[0]} FROM ${compiledArgs[1]})`
|
|
11
|
+
},
|
|
12
|
+
{ name: 'YEAR', renderer: ({ compiledArgs }) => `EXTRACT(YEAR FROM ${compiledArgs[0]})` },
|
|
13
|
+
{ name: 'MONTH', renderer: ({ compiledArgs }) => `EXTRACT(MONTH FROM ${compiledArgs[0]})` },
|
|
14
|
+
{ name: 'DAY', renderer: ({ compiledArgs }) => `EXTRACT(DAY FROM ${compiledArgs[0]})` },
|
|
15
|
+
{ name: 'HOUR', renderer: ({ compiledArgs }) => `EXTRACT(HOUR FROM ${compiledArgs[0]})` },
|
|
16
|
+
{ name: 'MINUTE', renderer: ({ compiledArgs }) => `EXTRACT(MINUTE FROM ${compiledArgs[0]})` },
|
|
17
|
+
{ name: 'SECOND', renderer: ({ compiledArgs }) => `EXTRACT(SECOND FROM ${compiledArgs[0]})` },
|
|
18
|
+
{ name: 'QUARTER', renderer: ({ compiledArgs }) => `EXTRACT(QUARTER FROM ${compiledArgs[0]})` },
|
|
19
|
+
{ name: 'DATE_ADD', renderer: ({ compiledArgs }) => `(${compiledArgs[0]} + INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})` },
|
|
20
|
+
{ name: 'DATE_SUB', renderer: ({ compiledArgs }) => `(${compiledArgs[0]} - INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})` },
|
|
21
|
+
{ name: 'DATE_DIFF', renderer: ({ compiledArgs }) => `DATEDIFF(${compiledArgs[0]}, ${compiledArgs[1]})` },
|
|
22
|
+
{ name: 'DATE_FORMAT', renderer: ({ compiledArgs }) => `DATE_FORMAT(${compiledArgs[0]}, ${compiledArgs[1]})` },
|
|
23
|
+
{ name: 'UNIX_TIMESTAMP', renderer: noArgsRenderer('UNIX_TIMESTAMP') },
|
|
24
|
+
{ name: 'FROM_UNIXTIME', renderer: ({ compiledArgs }) => `FROM_UNIXTIME(${compiledArgs[0]})` },
|
|
25
|
+
{ name: 'END_OF_MONTH', renderer: ({ compiledArgs }) => `LAST_DAY(${compiledArgs[0]})` },
|
|
26
|
+
{ name: 'DAY_OF_WEEK', renderer: ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})` },
|
|
27
|
+
{ name: 'WEEK_OF_YEAR', renderer: ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})` },
|
|
28
|
+
{ name: 'DATE_TRUNC', renderer: ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})` },
|
|
29
|
+
{
|
|
30
|
+
name: 'AGE',
|
|
31
|
+
renderer: ({ compiledArgs }) =>
|
|
32
|
+
compiledArgs.length === 1 ? `AGE(${compiledArgs[0]})` : `AGE(${compiledArgs[0]}, ${compiledArgs[1]})`
|
|
33
|
+
},
|
|
34
|
+
{ name: 'LOCALTIME', renderer: () => 'LOCALTIME' },
|
|
35
|
+
{ name: 'LOCALTIMESTAMP', renderer: () => 'LOCALTIMESTAMP' }
|
|
36
|
+
];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { FunctionRenderer } from '../types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Simple renderer for functions that take one argument.
|
|
5
|
+
*/
|
|
6
|
+
export function unaryRenderer(name: string): FunctionRenderer {
|
|
7
|
+
return ({ compiledArgs }) => `${name}(${compiledArgs[0]})`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Simple renderer for functions that take two arguments.
|
|
12
|
+
*/
|
|
13
|
+
export function binaryRenderer(name: string): FunctionRenderer {
|
|
14
|
+
return ({ compiledArgs }) => `${name}(${compiledArgs[0]}, ${compiledArgs[1]})`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Renders functions that simply join all provided arguments.
|
|
19
|
+
*/
|
|
20
|
+
export function variadicRenderer(name: string): FunctionRenderer {
|
|
21
|
+
return ({ compiledArgs }) => `${name}(${compiledArgs.join(', ')})`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Renders parameterless functions that always include parentheses.
|
|
26
|
+
*/
|
|
27
|
+
export function noArgsRenderer(name: string): FunctionRenderer {
|
|
28
|
+
return () => `${name}()`;
|
|
29
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { FunctionDefinition } from '../function-registry.js';
|
|
2
|
+
|
|
3
|
+
export const jsonFunctionDefinitions: FunctionDefinition[] = [
|
|
4
|
+
{
|
|
5
|
+
name: 'JSON_LENGTH',
|
|
6
|
+
renderer: ({ compiledArgs }) => {
|
|
7
|
+
if (compiledArgs.length === 0 || compiledArgs.length > 2) {
|
|
8
|
+
throw new Error('JSON_LENGTH expects 1 or 2 arguments');
|
|
9
|
+
}
|
|
10
|
+
return `JSON_LENGTH(${compiledArgs.join(', ')})`;
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: 'JSON_SET',
|
|
15
|
+
renderer: ({ compiledArgs }) => {
|
|
16
|
+
if (compiledArgs.length < 3 || (compiledArgs.length - 1) % 2 !== 0) {
|
|
17
|
+
throw new Error('JSON_SET expects a JSON document followed by one or more path/value pairs');
|
|
18
|
+
}
|
|
19
|
+
return `JSON_SET(${compiledArgs.join(', ')})`;
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'JSON_ARRAYAGG',
|
|
24
|
+
renderer: ({ compiledArgs }) => {
|
|
25
|
+
if (compiledArgs.length !== 1) {
|
|
26
|
+
throw new Error('JSON_ARRAYAGG expects exactly one argument');
|
|
27
|
+
}
|
|
28
|
+
return `JSON_ARRAYAGG(${compiledArgs[0]})`;
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'JSON_CONTAINS',
|
|
33
|
+
renderer: ({ compiledArgs }) => {
|
|
34
|
+
if (compiledArgs.length < 2 || compiledArgs.length > 3) {
|
|
35
|
+
throw new Error('JSON_CONTAINS expects two or three arguments');
|
|
36
|
+
}
|
|
37
|
+
return `JSON_CONTAINS(${compiledArgs.join(', ')})`;
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'ARRAY_APPEND',
|
|
42
|
+
renderer: ({ compiledArgs }) => {
|
|
43
|
+
if (compiledArgs.length !== 2) {
|
|
44
|
+
throw new Error('ARRAY_APPEND expects exactly two arguments');
|
|
45
|
+
}
|
|
46
|
+
return `ARRAY_APPEND(${compiledArgs[0]}, ${compiledArgs[1]})`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
];
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { FunctionDefinition } from '../function-registry.js';
|
|
2
|
+
import { binaryRenderer, noArgsRenderer, unaryRenderer } from './helpers.js';
|
|
3
|
+
|
|
4
|
+
export const numericFunctionDefinitions: FunctionDefinition[] = [
|
|
5
|
+
{ name: 'ABS', renderer: unaryRenderer('ABS') },
|
|
6
|
+
{ name: 'BIT_LENGTH', renderer: unaryRenderer('BIT_LENGTH') },
|
|
7
|
+
{ name: 'OCTET_LENGTH', renderer: unaryRenderer('OCTET_LENGTH') },
|
|
8
|
+
{ name: 'CHR', renderer: unaryRenderer('CHR') },
|
|
9
|
+
{ name: 'LOG2', renderer: unaryRenderer('LOG2') },
|
|
10
|
+
{ name: 'CBRT', renderer: unaryRenderer('CBRT') },
|
|
11
|
+
{ name: 'ACOS', renderer: unaryRenderer('ACOS') },
|
|
12
|
+
{ name: 'ASIN', renderer: unaryRenderer('ASIN') },
|
|
13
|
+
{ name: 'ATAN', renderer: unaryRenderer('ATAN') },
|
|
14
|
+
{ name: 'ATAN2', renderer: binaryRenderer('ATAN2') },
|
|
15
|
+
{ name: 'CEIL', renderer: unaryRenderer('CEIL') },
|
|
16
|
+
{ name: 'CEILING', renderer: unaryRenderer('CEILING') },
|
|
17
|
+
{ name: 'COS', renderer: unaryRenderer('COS') },
|
|
18
|
+
{ name: 'COT', renderer: unaryRenderer('COT') },
|
|
19
|
+
{ name: 'DEGREES', renderer: unaryRenderer('DEGREES') },
|
|
20
|
+
{ name: 'EXP', renderer: unaryRenderer('EXP') },
|
|
21
|
+
{ name: 'FLOOR', renderer: unaryRenderer('FLOOR') },
|
|
22
|
+
{ name: 'LN', renderer: unaryRenderer('LN') },
|
|
23
|
+
{
|
|
24
|
+
name: 'LOG',
|
|
25
|
+
renderer: ({ compiledArgs }) =>
|
|
26
|
+
compiledArgs.length === 2 ? `LOG(${compiledArgs[0]}, ${compiledArgs[1]})` : `LOG(${compiledArgs[0]})`
|
|
27
|
+
},
|
|
28
|
+
{ name: 'LOG10', renderer: unaryRenderer('LOG10') },
|
|
29
|
+
{ name: 'LOG_BASE', renderer: binaryRenderer('LOG') },
|
|
30
|
+
{ name: 'MOD', renderer: binaryRenderer('MOD') },
|
|
31
|
+
{ name: 'PI', renderer: noArgsRenderer('PI') },
|
|
32
|
+
{ name: 'POWER', renderer: binaryRenderer('POWER') },
|
|
33
|
+
{ name: 'POW', renderer: binaryRenderer('POW') },
|
|
34
|
+
{ name: 'RADIANS', renderer: unaryRenderer('RADIANS') },
|
|
35
|
+
{ name: 'RANDOM', renderer: noArgsRenderer('RANDOM') },
|
|
36
|
+
{ name: 'RAND', renderer: noArgsRenderer('RAND') },
|
|
37
|
+
{
|
|
38
|
+
name: 'ROUND',
|
|
39
|
+
renderer: ({ compiledArgs }) =>
|
|
40
|
+
compiledArgs.length === 2 ? `ROUND(${compiledArgs[0]}, ${compiledArgs[1]})` : `ROUND(${compiledArgs[0]})`
|
|
41
|
+
},
|
|
42
|
+
{ name: 'SIGN', renderer: unaryRenderer('SIGN') },
|
|
43
|
+
{ name: 'SIN', renderer: unaryRenderer('SIN') },
|
|
44
|
+
{ name: 'SQRT', renderer: unaryRenderer('SQRT') },
|
|
45
|
+
{ name: 'TAN', renderer: unaryRenderer('TAN') },
|
|
46
|
+
{
|
|
47
|
+
name: 'TRUNC',
|
|
48
|
+
renderer: ({ compiledArgs }) =>
|
|
49
|
+
compiledArgs.length === 2 ? `TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})` : `TRUNC(${compiledArgs[0]})`
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'TRUNCATE',
|
|
53
|
+
renderer: ({ compiledArgs }) => `TRUNCATE(${compiledArgs[0]}, ${compiledArgs[1]})`
|
|
54
|
+
}
|
|
55
|
+
];
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { FunctionDefinition } from '../function-registry.js';
|
|
2
|
+
import { binaryRenderer, unaryRenderer, variadicRenderer } from './helpers.js';
|
|
3
|
+
|
|
4
|
+
export const stringFunctionDefinitions: FunctionDefinition[] = [
|
|
5
|
+
{ name: 'UPPER', renderer: unaryRenderer('UPPER') },
|
|
6
|
+
{ name: 'LOWER', renderer: unaryRenderer('LOWER') },
|
|
7
|
+
{ name: 'LENGTH', renderer: unaryRenderer('LENGTH') },
|
|
8
|
+
{ name: 'CHAR_LENGTH', renderer: unaryRenderer('CHAR_LENGTH') },
|
|
9
|
+
{ name: 'CHARACTER_LENGTH', renderer: unaryRenderer('CHARACTER_LENGTH') },
|
|
10
|
+
{ name: 'TRIM', renderer: unaryRenderer('TRIM') },
|
|
11
|
+
{ name: 'LTRIM', renderer: unaryRenderer('LTRIM') },
|
|
12
|
+
{ name: 'RTRIM', renderer: unaryRenderer('RTRIM') },
|
|
13
|
+
{ name: 'SUBSTRING', renderer: variadicRenderer('SUBSTRING') },
|
|
14
|
+
{ name: 'SUBSTR', renderer: variadicRenderer('SUBSTR') },
|
|
15
|
+
{ name: 'CONCAT', renderer: variadicRenderer('CONCAT') },
|
|
16
|
+
{ name: 'CONCAT_WS', renderer: variadicRenderer('CONCAT_WS') },
|
|
17
|
+
{ name: 'ASCII', renderer: unaryRenderer('ASCII') },
|
|
18
|
+
{ name: 'CHAR', renderer: variadicRenderer('CHAR') },
|
|
19
|
+
{
|
|
20
|
+
name: 'POSITION',
|
|
21
|
+
renderer: ({ compiledArgs }) => `POSITION(${compiledArgs[0]} IN ${compiledArgs[1]})`
|
|
22
|
+
},
|
|
23
|
+
{ name: 'REPLACE', renderer: ({ compiledArgs }) => `REPLACE(${compiledArgs[0]}, ${compiledArgs[1]}, ${compiledArgs[2]})` },
|
|
24
|
+
{ name: 'REPEAT', renderer: binaryRenderer('REPEAT') },
|
|
25
|
+
{ name: 'LPAD', renderer: ({ compiledArgs }) => `LPAD(${compiledArgs[0]}, ${compiledArgs[1]}, ${compiledArgs[2]})` },
|
|
26
|
+
{ name: 'RPAD', renderer: ({ compiledArgs }) => `RPAD(${compiledArgs[0]}, ${compiledArgs[1]}, ${compiledArgs[2]})` },
|
|
27
|
+
{ name: 'LEFT', renderer: binaryRenderer('LEFT') },
|
|
28
|
+
{ name: 'RIGHT', renderer: binaryRenderer('RIGHT') },
|
|
29
|
+
{ name: 'INSTR', renderer: binaryRenderer('INSTR') },
|
|
30
|
+
{
|
|
31
|
+
name: 'LOCATE',
|
|
32
|
+
renderer: ({ compiledArgs }) =>
|
|
33
|
+
compiledArgs.length === 3
|
|
34
|
+
? `LOCATE(${compiledArgs[0]}, ${compiledArgs[1]}, ${compiledArgs[2]})`
|
|
35
|
+
: `LOCATE(${compiledArgs[0]}, ${compiledArgs[1]})`
|
|
36
|
+
},
|
|
37
|
+
{ name: 'SPACE', renderer: unaryRenderer('SPACE') },
|
|
38
|
+
{ name: 'REVERSE', renderer: unaryRenderer('REVERSE') },
|
|
39
|
+
{ name: 'INITCAP', renderer: unaryRenderer('INITCAP') },
|
|
40
|
+
{ name: 'MD5', renderer: unaryRenderer('MD5') },
|
|
41
|
+
{ name: 'SHA1', renderer: unaryRenderer('SHA1') },
|
|
42
|
+
{ name: 'SHA2', renderer: ({ compiledArgs }) => `SHA2(${compiledArgs[0]}, ${compiledArgs[1]})` }
|
|
43
|
+
];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { FunctionRenderer } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Definition for a SQL function renderer.
|
|
5
|
+
*/
|
|
6
|
+
export interface FunctionDefinition {
|
|
7
|
+
name: string;
|
|
8
|
+
renderer: FunctionRenderer;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Registry that keeps track of function renderers and exposes them by name.
|
|
13
|
+
*/
|
|
14
|
+
export class FunctionRegistry {
|
|
15
|
+
private readonly renderers: Map<string, FunctionRenderer> = new Map();
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Registers or overrides a renderer for the given function name.
|
|
19
|
+
*/
|
|
20
|
+
add(name: string, renderer: FunctionRenderer): void {
|
|
21
|
+
this.renderers.set(name, renderer);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Registers a batch of definitions.
|
|
26
|
+
*/
|
|
27
|
+
register(definitions: Iterable<FunctionDefinition>): void {
|
|
28
|
+
for (const definition of definitions) {
|
|
29
|
+
this.add(definition.name, definition.renderer);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Merges another registry into this one, allowing overrides from the other source.
|
|
35
|
+
*/
|
|
36
|
+
merge(other: FunctionRegistry): void {
|
|
37
|
+
for (const [name, renderer] of other.renderers.entries()) {
|
|
38
|
+
this.renderers.set(name, renderer);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Retrieves a renderer by function name.
|
|
44
|
+
*/
|
|
45
|
+
get(name: string): FunctionRenderer | undefined {
|
|
46
|
+
return this.renderers.get(name);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { FunctionRenderContext } from './types.js';
|
|
2
|
+
import { isOperandNode, type LiteralNode, type OperandNode } from '../ast/expression.js';
|
|
3
|
+
|
|
4
|
+
/** Default separator used when GROUP_CONCAT has no explicit separator. */
|
|
5
|
+
export const DEFAULT_GROUP_CONCAT_SEPARATOR: LiteralNode = {
|
|
6
|
+
type: 'Literal',
|
|
7
|
+
value: ','
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Builds an ORDER BY clause for functions that support ordering (e.g. GROUP_CONCAT).
|
|
12
|
+
*/
|
|
13
|
+
export function buildGroupConcatOrderBy(ctx: FunctionRenderContext): string {
|
|
14
|
+
const orderBy = ctx.node.orderBy;
|
|
15
|
+
if (!orderBy || orderBy.length === 0) {
|
|
16
|
+
return '';
|
|
17
|
+
}
|
|
18
|
+
const parts = orderBy.map(order => {
|
|
19
|
+
const term = isOperandNode(order.term)
|
|
20
|
+
? ctx.compileOperand(order.term)
|
|
21
|
+
: (() => {
|
|
22
|
+
throw new Error('ORDER BY expressions inside functions must be operands');
|
|
23
|
+
})();
|
|
24
|
+
const collation = order.collation ? ` COLLATE ${order.collation}` : '';
|
|
25
|
+
const nulls = order.nulls ? ` NULLS ${order.nulls}` : '';
|
|
26
|
+
return `${term} ${order.direction}${collation}${nulls}`;
|
|
27
|
+
});
|
|
28
|
+
return `ORDER BY ${parts.join(', ')}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Formats the SEPARATOR clause for GROUP_CONCAT.
|
|
33
|
+
*/
|
|
34
|
+
export function formatGroupConcatSeparator(ctx: FunctionRenderContext): string {
|
|
35
|
+
if (!ctx.node.separator) {
|
|
36
|
+
return '';
|
|
37
|
+
}
|
|
38
|
+
return ` SEPARATOR ${ctx.compileOperand(ctx.node.separator)}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Returns the operand used as the separator for GROUP_CONCAT.
|
|
43
|
+
*/
|
|
44
|
+
export function getGroupConcatSeparatorOperand(ctx: FunctionRenderContext): OperandNode {
|
|
45
|
+
return ctx.node.separator ?? DEFAULT_GROUP_CONCAT_SEPARATOR;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Renders the default GROUP_CONCAT statement.
|
|
50
|
+
*/
|
|
51
|
+
export function renderStandardGroupConcat(ctx: FunctionRenderContext): string {
|
|
52
|
+
const arg = ctx.compiledArgs[0];
|
|
53
|
+
const orderClause = buildGroupConcatOrderBy(ctx);
|
|
54
|
+
const orderSegment = orderClause ? ` ${orderClause}` : '';
|
|
55
|
+
const separatorClause = formatGroupConcatSeparator(ctx);
|
|
56
|
+
return `GROUP_CONCAT(${arg}${orderSegment}${separatorClause})`;
|
|
57
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Pure AST Builders - No Dialect Logic Here!
|
|
2
|
+
|
|
3
|
+
import { ColumnDef } from '../../schema/column-types.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: unknown): 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
|
+
|
|
15
|
+
return valueToOperand(input);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const fn = (key: string, args: OperandInput[]): FunctionNode => ({
|
|
19
|
+
type: 'Function',
|
|
20
|
+
name: key,
|
|
21
|
+
fn: key,
|
|
22
|
+
args: args.map(toOperand)
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const jsonLength = (target: OperandInput, path?: OperandInput): FunctionNode =>
|
|
26
|
+
path === undefined ? fn('JSON_LENGTH', [target]) : fn('JSON_LENGTH', [target, path]);
|
|
27
|
+
|
|
28
|
+
export const jsonSet = (target: OperandInput, path: OperandInput, value: OperandInput): FunctionNode =>
|
|
29
|
+
fn('JSON_SET', [target, path, value]);
|
|
30
|
+
|
|
31
|
+
export const jsonArrayAgg = (value: OperandInput): FunctionNode => fn('JSON_ARRAYAGG', [value]);
|
|
32
|
+
|
|
33
|
+
export const jsonContains = (
|
|
34
|
+
target: OperandInput,
|
|
35
|
+
candidate: OperandInput,
|
|
36
|
+
path?: OperandInput
|
|
37
|
+
): FunctionNode =>
|
|
38
|
+
path === undefined ? fn('JSON_CONTAINS', [target, candidate]) : fn('JSON_CONTAINS', [target, candidate, path]);
|
|
@@ -244,3 +244,17 @@ export const trunc = (value: OperandInput, decimals?: OperandInput): FunctionNod
|
|
|
244
244
|
export const truncate = (value: OperandInput, decimals: OperandInput): FunctionNode =>
|
|
245
245
|
fn('TRUNCATE', [value, decimals]);
|
|
246
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Returns the base-2 logarithm of a number.
|
|
249
|
+
* @param value - The numeric value.
|
|
250
|
+
* @returns A FunctionNode representing the LOG2 SQL function.
|
|
251
|
+
*/
|
|
252
|
+
export const log2 = (value: OperandInput): FunctionNode => fn('LOG2', [value]);
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Returns the cube root of a number.
|
|
256
|
+
* @param value - The numeric value.
|
|
257
|
+
* @returns A FunctionNode representing the CBRT SQL function.
|
|
258
|
+
*/
|
|
259
|
+
export const cbrt = (value: OperandInput): FunctionNode => fn('CBRT', [value]);
|
|
260
|
+
|