metal-orm 1.1.11 → 1.1.18
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,106 +1,106 @@
|
|
|
1
|
-
import { StandardFunctionStrategy } from '../../functions/standard-strategy.js';
|
|
2
|
-
import { LiteralNode } from '../../ast/expression.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* MySQL/MariaDB specific function strategy.
|
|
6
|
-
* Implements and overrides SQL function compilation rules for MySQL.
|
|
7
|
-
*/
|
|
8
|
-
export class MysqlFunctionStrategy extends StandardFunctionStrategy {
|
|
9
|
-
constructor() {
|
|
10
|
-
super();
|
|
11
|
-
this.registerOverrides();
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
private registerOverrides() {
|
|
15
|
-
// Override Standard/Abstract definitions with MySQL specifics
|
|
16
|
-
|
|
17
|
-
// Date/Time functions
|
|
18
|
-
this.add('NOW', () => `NOW()`);
|
|
19
|
-
this.add('CURRENT_DATE', () => `CURDATE()`);
|
|
20
|
-
this.add('CURRENT_TIME', () => `CURTIME()`);
|
|
21
|
-
this.add('UTC_NOW', () => `UTC_TIMESTAMP()`);
|
|
22
|
-
|
|
23
|
-
this.add('EXTRACT', ({ compiledArgs }) => {
|
|
24
|
-
if (compiledArgs.length !== 2) throw new Error('EXTRACT expects 2 arguments (part, date)');
|
|
25
|
-
const [part, date] = compiledArgs;
|
|
26
|
-
const partClean = part.replace(/['"]/g, '');
|
|
27
|
-
return `EXTRACT(${partClean} FROM ${date})`;
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
this.add('YEAR', ({ compiledArgs }) => {
|
|
31
|
-
if (compiledArgs.length !== 1) throw new Error('YEAR expects 1 argument');
|
|
32
|
-
return `YEAR(${compiledArgs[0]})`;
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
this.add('MONTH', ({ compiledArgs }) => {
|
|
36
|
-
if (compiledArgs.length !== 1) throw new Error('MONTH expects 1 argument');
|
|
37
|
-
return `MONTH(${compiledArgs[0]})`;
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
this.add('DAY', ({ compiledArgs }) => {
|
|
41
|
-
if (compiledArgs.length !== 1) throw new Error('DAY expects 1 argument');
|
|
42
|
-
return `DAY(${compiledArgs[0]})`;
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
this.add('DATE_ADD', ({ node, compiledArgs }) => {
|
|
46
|
-
if (compiledArgs.length !== 3) throw new Error('DATE_ADD expects 3 arguments (date, interval, unit)');
|
|
47
|
-
const [date, interval] = compiledArgs;
|
|
48
|
-
const unitArg = node.args[2] as LiteralNode;
|
|
49
|
-
const unitClean = String(unitArg.value).replace(/['"]/g, '').toUpperCase();
|
|
50
|
-
return `DATE_ADD(${date}, INTERVAL ${interval} ${unitClean})`;
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
this.add('DATE_SUB', ({ node, compiledArgs }) => {
|
|
54
|
-
if (compiledArgs.length !== 3) throw new Error('DATE_SUB expects 3 arguments (date, interval, unit)');
|
|
55
|
-
const [date, interval] = compiledArgs;
|
|
56
|
-
const unitArg = node.args[2] as LiteralNode;
|
|
57
|
-
const unitClean = String(unitArg.value).replace(/['"]/g, '').toUpperCase();
|
|
58
|
-
return `DATE_SUB(${date}, INTERVAL ${interval} ${unitClean})`;
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
this.add('DATE_DIFF', ({ compiledArgs }) => {
|
|
62
|
-
if (compiledArgs.length !== 2) throw new Error('DATE_DIFF expects 2 arguments');
|
|
63
|
-
const [date1, date2] = compiledArgs;
|
|
64
|
-
return `DATEDIFF(${date1}, ${date2})`;
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
this.add('DATE_FORMAT', ({ compiledArgs }) => {
|
|
68
|
-
if (compiledArgs.length !== 2) throw new Error('DATE_FORMAT expects 2 arguments');
|
|
69
|
-
const [date, format] = compiledArgs;
|
|
70
|
-
return `DATE_FORMAT(${date}, ${format})`;
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
this.add('END_OF_MONTH', ({ compiledArgs }) => {
|
|
74
|
-
if (compiledArgs.length !== 1) throw new Error('END_OF_MONTH expects 1 argument');
|
|
75
|
-
return `LAST_DAY(${compiledArgs[0]})`;
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
this.add('DAY_OF_WEEK', ({ compiledArgs }) => {
|
|
79
|
-
if (compiledArgs.length !== 1) throw new Error('DAY_OF_WEEK expects 1 argument');
|
|
80
|
-
return `DAYOFWEEK(${compiledArgs[0]})`;
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
this.add('WEEK_OF_YEAR', ({ compiledArgs }) => {
|
|
84
|
-
if (compiledArgs.length !== 1) throw new Error('WEEK_OF_YEAR expects 1 argument');
|
|
85
|
-
return `WEEKOFYEAR(${compiledArgs[0]})`;
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
this.add('DATE_TRUNC', ({ node, compiledArgs }) => {
|
|
89
|
-
if (compiledArgs.length !== 2) throw new Error('DATE_TRUNC expects 2 arguments (part, date)');
|
|
90
|
-
const [, date] = compiledArgs;
|
|
91
|
-
const partArg = node.args[0] as LiteralNode;
|
|
92
|
-
const partClean = String(partArg.value).replace(/['"]/g, '').toLowerCase();
|
|
93
|
-
// MySQL doesn't have DATE_TRUNC, use DATE_FORMAT workaround
|
|
94
|
-
if (partClean === 'year') {
|
|
95
|
-
return `DATE_FORMAT(${date}, '%Y-01-01')`;
|
|
96
|
-
} else if (partClean === 'month') {
|
|
97
|
-
return `DATE_FORMAT(${date}, '%Y-%m-01')`;
|
|
98
|
-
} else if (partClean === 'day') {
|
|
99
|
-
return `DATE(${date})`;
|
|
100
|
-
}
|
|
101
|
-
return `DATE(${date})`;
|
|
102
|
-
});
|
|
103
|
-
|
|
1
|
+
import { StandardFunctionStrategy } from '../../functions/standard-strategy.js';
|
|
2
|
+
import { LiteralNode } from '../../ast/expression.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* MySQL/MariaDB specific function strategy.
|
|
6
|
+
* Implements and overrides SQL function compilation rules for MySQL.
|
|
7
|
+
*/
|
|
8
|
+
export class MysqlFunctionStrategy extends StandardFunctionStrategy {
|
|
9
|
+
constructor() {
|
|
10
|
+
super();
|
|
11
|
+
this.registerOverrides();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
private registerOverrides() {
|
|
15
|
+
// Override Standard/Abstract definitions with MySQL specifics
|
|
16
|
+
|
|
17
|
+
// Date/Time functions
|
|
18
|
+
this.add('NOW', () => `NOW()`);
|
|
19
|
+
this.add('CURRENT_DATE', () => `CURDATE()`);
|
|
20
|
+
this.add('CURRENT_TIME', () => `CURTIME()`);
|
|
21
|
+
this.add('UTC_NOW', () => `UTC_TIMESTAMP()`);
|
|
22
|
+
|
|
23
|
+
this.add('EXTRACT', ({ compiledArgs }) => {
|
|
24
|
+
if (compiledArgs.length !== 2) throw new Error('EXTRACT expects 2 arguments (part, date)');
|
|
25
|
+
const [part, date] = compiledArgs;
|
|
26
|
+
const partClean = part.replace(/['"]/g, '');
|
|
27
|
+
return `EXTRACT(${partClean} FROM ${date})`;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
this.add('YEAR', ({ compiledArgs }) => {
|
|
31
|
+
if (compiledArgs.length !== 1) throw new Error('YEAR expects 1 argument');
|
|
32
|
+
return `YEAR(${compiledArgs[0]})`;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
this.add('MONTH', ({ compiledArgs }) => {
|
|
36
|
+
if (compiledArgs.length !== 1) throw new Error('MONTH expects 1 argument');
|
|
37
|
+
return `MONTH(${compiledArgs[0]})`;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
this.add('DAY', ({ compiledArgs }) => {
|
|
41
|
+
if (compiledArgs.length !== 1) throw new Error('DAY expects 1 argument');
|
|
42
|
+
return `DAY(${compiledArgs[0]})`;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
this.add('DATE_ADD', ({ node, compiledArgs }) => {
|
|
46
|
+
if (compiledArgs.length !== 3) throw new Error('DATE_ADD expects 3 arguments (date, interval, unit)');
|
|
47
|
+
const [date, interval] = compiledArgs;
|
|
48
|
+
const unitArg = node.args[2] as LiteralNode;
|
|
49
|
+
const unitClean = String(unitArg.value).replace(/['"]/g, '').toUpperCase();
|
|
50
|
+
return `DATE_ADD(${date}, INTERVAL ${interval} ${unitClean})`;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
this.add('DATE_SUB', ({ node, compiledArgs }) => {
|
|
54
|
+
if (compiledArgs.length !== 3) throw new Error('DATE_SUB expects 3 arguments (date, interval, unit)');
|
|
55
|
+
const [date, interval] = compiledArgs;
|
|
56
|
+
const unitArg = node.args[2] as LiteralNode;
|
|
57
|
+
const unitClean = String(unitArg.value).replace(/['"]/g, '').toUpperCase();
|
|
58
|
+
return `DATE_SUB(${date}, INTERVAL ${interval} ${unitClean})`;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
this.add('DATE_DIFF', ({ compiledArgs }) => {
|
|
62
|
+
if (compiledArgs.length !== 2) throw new Error('DATE_DIFF expects 2 arguments');
|
|
63
|
+
const [date1, date2] = compiledArgs;
|
|
64
|
+
return `DATEDIFF(${date1}, ${date2})`;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
this.add('DATE_FORMAT', ({ compiledArgs }) => {
|
|
68
|
+
if (compiledArgs.length !== 2) throw new Error('DATE_FORMAT expects 2 arguments');
|
|
69
|
+
const [date, format] = compiledArgs;
|
|
70
|
+
return `DATE_FORMAT(${date}, ${format})`;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
this.add('END_OF_MONTH', ({ compiledArgs }) => {
|
|
74
|
+
if (compiledArgs.length !== 1) throw new Error('END_OF_MONTH expects 1 argument');
|
|
75
|
+
return `LAST_DAY(${compiledArgs[0]})`;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
this.add('DAY_OF_WEEK', ({ compiledArgs }) => {
|
|
79
|
+
if (compiledArgs.length !== 1) throw new Error('DAY_OF_WEEK expects 1 argument');
|
|
80
|
+
return `DAYOFWEEK(${compiledArgs[0]})`;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
this.add('WEEK_OF_YEAR', ({ compiledArgs }) => {
|
|
84
|
+
if (compiledArgs.length !== 1) throw new Error('WEEK_OF_YEAR expects 1 argument');
|
|
85
|
+
return `WEEKOFYEAR(${compiledArgs[0]})`;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
this.add('DATE_TRUNC', ({ node, compiledArgs }) => {
|
|
89
|
+
if (compiledArgs.length !== 2) throw new Error('DATE_TRUNC expects 2 arguments (part, date)');
|
|
90
|
+
const [, date] = compiledArgs;
|
|
91
|
+
const partArg = node.args[0] as LiteralNode;
|
|
92
|
+
const partClean = String(partArg.value).replace(/['"]/g, '').toLowerCase();
|
|
93
|
+
// MySQL doesn't have DATE_TRUNC, use DATE_FORMAT workaround
|
|
94
|
+
if (partClean === 'year') {
|
|
95
|
+
return `DATE_FORMAT(${date}, '%Y-01-01')`;
|
|
96
|
+
} else if (partClean === 'month') {
|
|
97
|
+
return `DATE_FORMAT(${date}, '%Y-%m-01')`;
|
|
98
|
+
} else if (partClean === 'day') {
|
|
99
|
+
return `DATE(${date})`;
|
|
100
|
+
}
|
|
101
|
+
return `DATE(${date})`;
|
|
102
|
+
});
|
|
103
|
+
|
|
104
104
|
this.add('HOUR', ({ compiledArgs }) => `HOUR(${compiledArgs[0]})`);
|
|
105
105
|
this.add('MINUTE', ({ compiledArgs }) => `MINUTE(${compiledArgs[0]})`);
|
|
106
106
|
this.add('SECOND', ({ compiledArgs }) => `SECOND(${compiledArgs[0]})`);
|
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
import { CompilerContext, CompiledProcedureCall } from '../abstract.js';
|
|
2
|
-
import { JsonPathNode, IsDistinctExpressionNode } from '../../ast/expression.js';
|
|
3
|
-
import { InsertQueryNode } from '../../ast/query.js';
|
|
4
|
-
import { SqlDialectBase } from '../base/sql-dialect.js';
|
|
5
|
-
import { MysqlFunctionStrategy } from './functions.js';
|
|
1
|
+
import { CompilerContext, CompiledProcedureCall } from '../abstract.js';
|
|
2
|
+
import { JsonPathNode, IsDistinctExpressionNode } from '../../ast/expression.js';
|
|
3
|
+
import { InsertQueryNode } from '../../ast/query.js';
|
|
4
|
+
import { SqlDialectBase } from '../base/sql-dialect.js';
|
|
5
|
+
import { MysqlFunctionStrategy } from './functions.js';
|
|
6
6
|
import { ProcedureCallNode } from '../../ast/procedure.js';
|
|
7
|
-
|
|
8
|
-
const sanitizeVariableSuffix = (value: string): string =>
|
|
9
|
-
value.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* MySQL dialect implementation
|
|
13
|
-
*/
|
|
14
|
-
export class MySqlDialect extends SqlDialectBase {
|
|
15
|
-
protected readonly dialect = 'mysql';
|
|
16
|
-
/**
|
|
17
|
-
* Creates a new MySqlDialect instance
|
|
18
|
-
*/
|
|
19
|
-
public constructor() {
|
|
20
|
-
super(new MysqlFunctionStrategy());
|
|
21
|
-
|
|
22
|
-
this.registerExpressionCompiler(
|
|
23
|
-
'IsDistinctExpression',
|
|
24
|
-
(node: IsDistinctExpressionNode, ctx: CompilerContext): string => {
|
|
25
|
-
const left = this.compileOperand(node.left, ctx);
|
|
26
|
-
const right = this.compileOperand(node.right, ctx);
|
|
27
|
-
const spaceship = `${left} <=> ${right}`;
|
|
28
|
-
|
|
29
|
-
if (node.operator === 'IS NOT DISTINCT FROM') {
|
|
30
|
-
return spaceship;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return `NOT (${spaceship})`;
|
|
34
|
-
}
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Quotes an identifier using MySQL backtick syntax
|
|
40
|
-
* @param id - Identifier to quote
|
|
41
|
-
* @returns Quoted identifier
|
|
42
|
-
*/
|
|
43
|
-
quoteIdentifier(id: string): string {
|
|
44
|
-
return `\`${id}\``;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Compiles JSON path expression using MySQL syntax
|
|
49
|
-
* @param node - JSON path node
|
|
50
|
-
* @returns MySQL JSON path expression
|
|
51
|
-
*/
|
|
7
|
+
|
|
8
|
+
const sanitizeVariableSuffix = (value: string): string =>
|
|
9
|
+
value.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* MySQL dialect implementation
|
|
13
|
+
*/
|
|
14
|
+
export class MySqlDialect extends SqlDialectBase {
|
|
15
|
+
protected readonly dialect = 'mysql';
|
|
16
|
+
/**
|
|
17
|
+
* Creates a new MySqlDialect instance
|
|
18
|
+
*/
|
|
19
|
+
public constructor() {
|
|
20
|
+
super(new MysqlFunctionStrategy());
|
|
21
|
+
|
|
22
|
+
this.registerExpressionCompiler(
|
|
23
|
+
'IsDistinctExpression',
|
|
24
|
+
(node: IsDistinctExpressionNode, ctx: CompilerContext): string => {
|
|
25
|
+
const left = this.compileOperand(node.left, ctx);
|
|
26
|
+
const right = this.compileOperand(node.right, ctx);
|
|
27
|
+
const spaceship = `${left} <=> ${right}`;
|
|
28
|
+
|
|
29
|
+
if (node.operator === 'IS NOT DISTINCT FROM') {
|
|
30
|
+
return spaceship;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return `NOT (${spaceship})`;
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Quotes an identifier using MySQL backtick syntax
|
|
40
|
+
* @param id - Identifier to quote
|
|
41
|
+
* @returns Quoted identifier
|
|
42
|
+
*/
|
|
43
|
+
quoteIdentifier(id: string): string {
|
|
44
|
+
return `\`${id}\``;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Compiles JSON path expression using MySQL syntax
|
|
49
|
+
* @param node - JSON path node
|
|
50
|
+
* @returns MySQL JSON path expression
|
|
51
|
+
*/
|
|
52
52
|
protected compileJsonPath(node: JsonPathNode): string {
|
|
53
53
|
const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
|
|
54
54
|
// MySQL 5.7+ uses col->'$.path'
|
|
@@ -86,58 +86,58 @@ export class MySqlDialect extends SqlDialectBase {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
compileProcedureCall(ast: ProcedureCallNode): CompiledProcedureCall {
|
|
89
|
-
const ctx = this.createCompilerContext();
|
|
90
|
-
const qualifiedName = ast.ref.schema
|
|
91
|
-
? `${this.quoteIdentifier(ast.ref.schema)}.${this.quoteIdentifier(ast.ref.name)}`
|
|
92
|
-
: this.quoteIdentifier(ast.ref.name);
|
|
93
|
-
|
|
94
|
-
const prelude: string[] = [];
|
|
95
|
-
const callArgs: string[] = [];
|
|
96
|
-
const outVars: Array<{ variable: string; name: string }> = [];
|
|
97
|
-
|
|
98
|
-
ast.params.forEach((param, index) => {
|
|
99
|
-
const suffix = sanitizeVariableSuffix(param.name || `p${index + 1}`);
|
|
100
|
-
const variable = `@__metal_${suffix}_${index + 1}`;
|
|
101
|
-
|
|
102
|
-
if (param.direction === 'in') {
|
|
103
|
-
if (!param.value) {
|
|
104
|
-
throw new Error(`Procedure parameter "${param.name}" requires a value for direction "in".`);
|
|
105
|
-
}
|
|
106
|
-
callArgs.push(this.compileOperand(param.value, ctx));
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (param.direction === 'inout') {
|
|
111
|
-
if (!param.value) {
|
|
112
|
-
throw new Error(`Procedure parameter "${param.name}" requires a value for direction "inout".`);
|
|
113
|
-
}
|
|
114
|
-
prelude.push(`SET ${variable} = ${this.compileOperand(param.value, ctx)};`);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
callArgs.push(variable);
|
|
118
|
-
outVars.push({ variable, name: param.name });
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
const statements: string[] = [];
|
|
122
|
-
if (prelude.length) {
|
|
123
|
-
statements.push(...prelude);
|
|
124
|
-
}
|
|
125
|
-
statements.push(`CALL ${qualifiedName}(${callArgs.join(', ')});`);
|
|
126
|
-
|
|
127
|
-
if (outVars.length) {
|
|
128
|
-
const selectOut = outVars
|
|
129
|
-
.map(({ variable, name }) => `${variable} AS ${this.quoteIdentifier(name)}`)
|
|
130
|
-
.join(', ');
|
|
131
|
-
statements.push(`SELECT ${selectOut};`);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return {
|
|
135
|
-
sql: statements.join(' '),
|
|
136
|
-
params: [...ctx.params],
|
|
137
|
-
outParams: {
|
|
138
|
-
source: outVars.length ? 'lastResultSet' : 'none',
|
|
139
|
-
names: outVars.map(item => item.name)
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
}
|
|
89
|
+
const ctx = this.createCompilerContext();
|
|
90
|
+
const qualifiedName = ast.ref.schema
|
|
91
|
+
? `${this.quoteIdentifier(ast.ref.schema)}.${this.quoteIdentifier(ast.ref.name)}`
|
|
92
|
+
: this.quoteIdentifier(ast.ref.name);
|
|
93
|
+
|
|
94
|
+
const prelude: string[] = [];
|
|
95
|
+
const callArgs: string[] = [];
|
|
96
|
+
const outVars: Array<{ variable: string; name: string }> = [];
|
|
97
|
+
|
|
98
|
+
ast.params.forEach((param, index) => {
|
|
99
|
+
const suffix = sanitizeVariableSuffix(param.name || `p${index + 1}`);
|
|
100
|
+
const variable = `@__metal_${suffix}_${index + 1}`;
|
|
101
|
+
|
|
102
|
+
if (param.direction === 'in') {
|
|
103
|
+
if (!param.value) {
|
|
104
|
+
throw new Error(`Procedure parameter "${param.name}" requires a value for direction "in".`);
|
|
105
|
+
}
|
|
106
|
+
callArgs.push(this.compileOperand(param.value, ctx));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (param.direction === 'inout') {
|
|
111
|
+
if (!param.value) {
|
|
112
|
+
throw new Error(`Procedure parameter "${param.name}" requires a value for direction "inout".`);
|
|
113
|
+
}
|
|
114
|
+
prelude.push(`SET ${variable} = ${this.compileOperand(param.value, ctx)};`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
callArgs.push(variable);
|
|
118
|
+
outVars.push({ variable, name: param.name });
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const statements: string[] = [];
|
|
122
|
+
if (prelude.length) {
|
|
123
|
+
statements.push(...prelude);
|
|
124
|
+
}
|
|
125
|
+
statements.push(`CALL ${qualifiedName}(${callArgs.join(', ')});`);
|
|
126
|
+
|
|
127
|
+
if (outVars.length) {
|
|
128
|
+
const selectOut = outVars
|
|
129
|
+
.map(({ variable, name }) => `${variable} AS ${this.quoteIdentifier(name)}`)
|
|
130
|
+
.join(', ');
|
|
131
|
+
statements.push(`SELECT ${selectOut};`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
sql: statements.join(' '),
|
|
136
|
+
params: [...ctx.params],
|
|
137
|
+
outParams: {
|
|
138
|
+
source: outVars.length ? 'lastResultSet' : 'none',
|
|
139
|
+
names: outVars.map(item => item.name)
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -1,111 +1,111 @@
|
|
|
1
|
-
import { StandardFunctionStrategy } from '../../functions/standard-strategy.js';
|
|
2
|
-
import { LiteralNode } from '../../ast/expression.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* PostgreSQL specific function strategy.
|
|
6
|
-
* Implements and overrides SQL function compilation rules for PostgreSQL.
|
|
7
|
-
*/
|
|
8
|
-
export class PostgresFunctionStrategy extends StandardFunctionStrategy {
|
|
9
|
-
constructor() {
|
|
10
|
-
super();
|
|
11
|
-
this.registerOverrides();
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
private registerOverrides() {
|
|
15
|
-
// Override Standard/Abstract definitions with PostgreSQL specifics
|
|
16
|
-
|
|
17
|
-
// Date/Time functions
|
|
18
|
-
this.add('UTC_NOW', () => `(NOW() AT TIME ZONE 'UTC')`);
|
|
19
|
-
this.add('UNIX_TIMESTAMP', () => `EXTRACT(EPOCH FROM NOW())::INTEGER`);
|
|
20
|
-
this.add('FROM_UNIXTIME', ({ compiledArgs }) => {
|
|
21
|
-
if (compiledArgs.length !== 1) throw new Error('FROM_UNIXTIME expects 1 argument');
|
|
22
|
-
return `to_timestamp(${compiledArgs[0]})`;
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
this.add('EXTRACT', ({ compiledArgs }) => {
|
|
26
|
-
if (compiledArgs.length !== 2) throw new Error('EXTRACT expects 2 arguments (part, date)');
|
|
27
|
-
const [part, date] = compiledArgs;
|
|
28
|
-
const partClean = part.replace(/['"]/g, '');
|
|
29
|
-
return `EXTRACT(${partClean} FROM ${date})`;
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
this.add('YEAR', ({ compiledArgs }) => {
|
|
33
|
-
if (compiledArgs.length !== 1) throw new Error('YEAR expects 1 argument');
|
|
34
|
-
return `EXTRACT(YEAR FROM ${compiledArgs[0]})`;
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
this.add('MONTH', ({ compiledArgs }) => {
|
|
38
|
-
if (compiledArgs.length !== 1) throw new Error('MONTH expects 1 argument');
|
|
39
|
-
return `EXTRACT(MONTH FROM ${compiledArgs[0]})`;
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
this.add('DAY', ({ compiledArgs }) => {
|
|
43
|
-
if (compiledArgs.length !== 1) throw new Error('DAY expects 1 argument');
|
|
44
|
-
return `EXTRACT(DAY FROM ${compiledArgs[0]})`;
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
this.add('DATE_ADD', ({ node, compiledArgs }) => {
|
|
48
|
-
if (compiledArgs.length !== 3) throw new Error('DATE_ADD expects 3 arguments (date, interval, unit)');
|
|
49
|
-
const [date, interval] = compiledArgs;
|
|
50
|
-
const unitArg = node.args[2] as LiteralNode;
|
|
51
|
-
const unitClean = String(unitArg.value).replace(/['"]/g, '').toLowerCase();
|
|
52
|
-
return `(${date} + (${interval} || ' ${unitClean}')::INTERVAL)`;
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
this.add('DATE_SUB', ({ node, compiledArgs }) => {
|
|
56
|
-
if (compiledArgs.length !== 3) throw new Error('DATE_SUB expects 3 arguments (date, interval, unit)');
|
|
57
|
-
const [date, interval] = compiledArgs;
|
|
58
|
-
const unitArg = node.args[2] as LiteralNode;
|
|
59
|
-
const unitClean = String(unitArg.value).replace(/['"]/g, '').toLowerCase();
|
|
60
|
-
return `(${date} - (${interval} || ' ${unitClean}')::INTERVAL)`;
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
this.add('DATE_DIFF', ({ compiledArgs }) => {
|
|
64
|
-
if (compiledArgs.length !== 2) throw new Error('DATE_DIFF expects 2 arguments');
|
|
65
|
-
const [date1, date2] = compiledArgs;
|
|
66
|
-
return `(${date1}::DATE - ${date2}::DATE)`;
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
this.add('DATE_FORMAT', ({ compiledArgs }) => {
|
|
70
|
-
if (compiledArgs.length !== 2) throw new Error('DATE_FORMAT expects 2 arguments');
|
|
71
|
-
const [date, format] = compiledArgs;
|
|
72
|
-
return `TO_CHAR(${date}, ${format})`;
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
this.add('END_OF_MONTH', ({ compiledArgs }) => {
|
|
76
|
-
if (compiledArgs.length !== 1) throw new Error('END_OF_MONTH expects 1 argument');
|
|
77
|
-
return `(date_trunc('month', ${compiledArgs[0]}) + interval '1 month' - interval '1 day')::DATE`;
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
this.add('DAY_OF_WEEK', ({ compiledArgs }) => {
|
|
81
|
-
if (compiledArgs.length !== 1) throw new Error('DAY_OF_WEEK expects 1 argument');
|
|
82
|
-
return `EXTRACT(DOW FROM ${compiledArgs[0]})`;
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
this.add('WEEK_OF_YEAR', ({ compiledArgs }) => {
|
|
86
|
-
if (compiledArgs.length !== 1) throw new Error('WEEK_OF_YEAR expects 1 argument');
|
|
87
|
-
return `EXTRACT(WEEK FROM ${compiledArgs[0]})`;
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
this.add('DATE_TRUNC', ({ node, compiledArgs }) => {
|
|
91
|
-
if (compiledArgs.length !== 2) throw new Error('DATE_TRUNC expects 2 arguments (part, date)');
|
|
92
|
-
const [, date] = compiledArgs;
|
|
93
|
-
const partArg = node.args[0] as LiteralNode;
|
|
94
|
-
const partClean = String(partArg.value).replace(/['"]/g, '').toLowerCase();
|
|
95
|
-
return `DATE_TRUNC('${partClean}', ${date})`;
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
this.add('GROUP_CONCAT', ctx => {
|
|
99
|
-
const arg = ctx.compiledArgs[0];
|
|
100
|
-
const orderClause = this.buildOrderByExpression(ctx);
|
|
101
|
-
const orderSegment = orderClause ? ` ${orderClause}` : '';
|
|
102
|
-
const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
|
|
103
|
-
const separator = ctx.compileOperand(separatorOperand);
|
|
104
|
-
return `STRING_AGG(${arg}, ${separator}${orderSegment})`;
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
this.add('CHR', ({ compiledArgs }) => `CHR(${compiledArgs[0]})`);
|
|
108
|
-
|
|
1
|
+
import { StandardFunctionStrategy } from '../../functions/standard-strategy.js';
|
|
2
|
+
import { LiteralNode } from '../../ast/expression.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* PostgreSQL specific function strategy.
|
|
6
|
+
* Implements and overrides SQL function compilation rules for PostgreSQL.
|
|
7
|
+
*/
|
|
8
|
+
export class PostgresFunctionStrategy extends StandardFunctionStrategy {
|
|
9
|
+
constructor() {
|
|
10
|
+
super();
|
|
11
|
+
this.registerOverrides();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
private registerOverrides() {
|
|
15
|
+
// Override Standard/Abstract definitions with PostgreSQL specifics
|
|
16
|
+
|
|
17
|
+
// Date/Time functions
|
|
18
|
+
this.add('UTC_NOW', () => `(NOW() AT TIME ZONE 'UTC')`);
|
|
19
|
+
this.add('UNIX_TIMESTAMP', () => `EXTRACT(EPOCH FROM NOW())::INTEGER`);
|
|
20
|
+
this.add('FROM_UNIXTIME', ({ compiledArgs }) => {
|
|
21
|
+
if (compiledArgs.length !== 1) throw new Error('FROM_UNIXTIME expects 1 argument');
|
|
22
|
+
return `to_timestamp(${compiledArgs[0]})`;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
this.add('EXTRACT', ({ compiledArgs }) => {
|
|
26
|
+
if (compiledArgs.length !== 2) throw new Error('EXTRACT expects 2 arguments (part, date)');
|
|
27
|
+
const [part, date] = compiledArgs;
|
|
28
|
+
const partClean = part.replace(/['"]/g, '');
|
|
29
|
+
return `EXTRACT(${partClean} FROM ${date})`;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
this.add('YEAR', ({ compiledArgs }) => {
|
|
33
|
+
if (compiledArgs.length !== 1) throw new Error('YEAR expects 1 argument');
|
|
34
|
+
return `EXTRACT(YEAR FROM ${compiledArgs[0]})`;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
this.add('MONTH', ({ compiledArgs }) => {
|
|
38
|
+
if (compiledArgs.length !== 1) throw new Error('MONTH expects 1 argument');
|
|
39
|
+
return `EXTRACT(MONTH FROM ${compiledArgs[0]})`;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
this.add('DAY', ({ compiledArgs }) => {
|
|
43
|
+
if (compiledArgs.length !== 1) throw new Error('DAY expects 1 argument');
|
|
44
|
+
return `EXTRACT(DAY FROM ${compiledArgs[0]})`;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
this.add('DATE_ADD', ({ node, compiledArgs }) => {
|
|
48
|
+
if (compiledArgs.length !== 3) throw new Error('DATE_ADD expects 3 arguments (date, interval, unit)');
|
|
49
|
+
const [date, interval] = compiledArgs;
|
|
50
|
+
const unitArg = node.args[2] as LiteralNode;
|
|
51
|
+
const unitClean = String(unitArg.value).replace(/['"]/g, '').toLowerCase();
|
|
52
|
+
return `(${date} + (${interval} || ' ${unitClean}')::INTERVAL)`;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
this.add('DATE_SUB', ({ node, compiledArgs }) => {
|
|
56
|
+
if (compiledArgs.length !== 3) throw new Error('DATE_SUB expects 3 arguments (date, interval, unit)');
|
|
57
|
+
const [date, interval] = compiledArgs;
|
|
58
|
+
const unitArg = node.args[2] as LiteralNode;
|
|
59
|
+
const unitClean = String(unitArg.value).replace(/['"]/g, '').toLowerCase();
|
|
60
|
+
return `(${date} - (${interval} || ' ${unitClean}')::INTERVAL)`;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
this.add('DATE_DIFF', ({ compiledArgs }) => {
|
|
64
|
+
if (compiledArgs.length !== 2) throw new Error('DATE_DIFF expects 2 arguments');
|
|
65
|
+
const [date1, date2] = compiledArgs;
|
|
66
|
+
return `(${date1}::DATE - ${date2}::DATE)`;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
this.add('DATE_FORMAT', ({ compiledArgs }) => {
|
|
70
|
+
if (compiledArgs.length !== 2) throw new Error('DATE_FORMAT expects 2 arguments');
|
|
71
|
+
const [date, format] = compiledArgs;
|
|
72
|
+
return `TO_CHAR(${date}, ${format})`;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
this.add('END_OF_MONTH', ({ compiledArgs }) => {
|
|
76
|
+
if (compiledArgs.length !== 1) throw new Error('END_OF_MONTH expects 1 argument');
|
|
77
|
+
return `(date_trunc('month', ${compiledArgs[0]}) + interval '1 month' - interval '1 day')::DATE`;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
this.add('DAY_OF_WEEK', ({ compiledArgs }) => {
|
|
81
|
+
if (compiledArgs.length !== 1) throw new Error('DAY_OF_WEEK expects 1 argument');
|
|
82
|
+
return `EXTRACT(DOW FROM ${compiledArgs[0]})`;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
this.add('WEEK_OF_YEAR', ({ compiledArgs }) => {
|
|
86
|
+
if (compiledArgs.length !== 1) throw new Error('WEEK_OF_YEAR expects 1 argument');
|
|
87
|
+
return `EXTRACT(WEEK FROM ${compiledArgs[0]})`;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
this.add('DATE_TRUNC', ({ node, compiledArgs }) => {
|
|
91
|
+
if (compiledArgs.length !== 2) throw new Error('DATE_TRUNC expects 2 arguments (part, date)');
|
|
92
|
+
const [, date] = compiledArgs;
|
|
93
|
+
const partArg = node.args[0] as LiteralNode;
|
|
94
|
+
const partClean = String(partArg.value).replace(/['"]/g, '').toLowerCase();
|
|
95
|
+
return `DATE_TRUNC('${partClean}', ${date})`;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
this.add('GROUP_CONCAT', ctx => {
|
|
99
|
+
const arg = ctx.compiledArgs[0];
|
|
100
|
+
const orderClause = this.buildOrderByExpression(ctx);
|
|
101
|
+
const orderSegment = orderClause ? ` ${orderClause}` : '';
|
|
102
|
+
const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
|
|
103
|
+
const separator = ctx.compileOperand(separatorOperand);
|
|
104
|
+
return `STRING_AGG(${arg}, ${separator}${orderSegment})`;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
this.add('CHR', ({ compiledArgs }) => `CHR(${compiledArgs[0]})`);
|
|
108
|
+
|
|
109
109
|
this.add('HOUR', ({ compiledArgs }) => `EXTRACT(HOUR FROM ${compiledArgs[0]})`);
|
|
110
110
|
this.add('MINUTE', ({ compiledArgs }) => `EXTRACT(MINUTE FROM ${compiledArgs[0]})`);
|
|
111
111
|
this.add('SECOND', ({ compiledArgs }) => `EXTRACT(SECOND FROM ${compiledArgs[0]})`);
|