metal-orm 1.0.14 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +69 -67
  2. package/dist/decorators/index.cjs +1983 -224
  3. package/dist/decorators/index.cjs.map +1 -1
  4. package/dist/decorators/index.d.cts +6 -6
  5. package/dist/decorators/index.d.ts +6 -6
  6. package/dist/decorators/index.js +1982 -224
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +5284 -3751
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +524 -169
  11. package/dist/index.d.ts +524 -169
  12. package/dist/index.js +5197 -3736
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-CCp1oz9p.d.cts → select-BKZrMRCQ.d.cts} +555 -94
  15. package/dist/{select-CCp1oz9p.d.ts → select-BKZrMRCQ.d.ts} +555 -94
  16. package/package.json +1 -1
  17. package/src/codegen/naming-strategy.ts +64 -0
  18. package/src/codegen/typescript.ts +19 -21
  19. package/src/core/ast/adapters.ts +21 -0
  20. package/src/core/ast/aggregate-functions.ts +13 -13
  21. package/src/core/ast/builders.ts +56 -43
  22. package/src/core/ast/expression-builders.ts +34 -34
  23. package/src/core/ast/expression-nodes.ts +18 -16
  24. package/src/core/ast/expression-visitor.ts +122 -69
  25. package/src/core/ast/expression.ts +6 -4
  26. package/src/core/ast/join-metadata.ts +15 -0
  27. package/src/core/ast/join-node.ts +22 -20
  28. package/src/core/ast/join.ts +5 -5
  29. package/src/core/ast/query.ts +52 -88
  30. package/src/core/ast/types.ts +20 -0
  31. package/src/core/ast/window-functions.ts +55 -55
  32. package/src/core/ddl/dialects/base-schema-dialect.ts +20 -6
  33. package/src/core/ddl/dialects/mssql-schema-dialect.ts +32 -8
  34. package/src/core/ddl/dialects/mysql-schema-dialect.ts +21 -10
  35. package/src/core/ddl/dialects/postgres-schema-dialect.ts +52 -7
  36. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +23 -9
  37. package/src/core/ddl/introspect/catalogs/index.ts +1 -0
  38. package/src/core/ddl/introspect/catalogs/postgres.ts +143 -0
  39. package/src/core/ddl/introspect/context.ts +9 -0
  40. package/src/core/ddl/introspect/functions/postgres.ts +26 -0
  41. package/src/core/ddl/introspect/mssql.ts +149 -149
  42. package/src/core/ddl/introspect/mysql.ts +99 -99
  43. package/src/core/ddl/introspect/postgres.ts +245 -154
  44. package/src/core/ddl/introspect/registry.ts +26 -0
  45. package/src/core/ddl/introspect/run-select.ts +25 -0
  46. package/src/core/ddl/introspect/sqlite.ts +7 -7
  47. package/src/core/ddl/introspect/types.ts +23 -19
  48. package/src/core/ddl/introspect/utils.ts +1 -1
  49. package/src/core/ddl/naming-strategy.ts +10 -0
  50. package/src/core/ddl/schema-dialect.ts +41 -0
  51. package/src/core/ddl/schema-diff.ts +211 -179
  52. package/src/core/ddl/schema-generator.ts +17 -90
  53. package/src/core/ddl/schema-introspect.ts +25 -32
  54. package/src/core/ddl/schema-plan-executor.ts +17 -0
  55. package/src/core/ddl/schema-types.ts +46 -39
  56. package/src/core/ddl/sql-writing.ts +170 -0
  57. package/src/core/dialect/abstract.ts +172 -126
  58. package/src/core/dialect/base/cte-compiler.ts +33 -0
  59. package/src/core/dialect/base/function-table-formatter.ts +132 -0
  60. package/src/core/dialect/base/groupby-compiler.ts +21 -0
  61. package/src/core/dialect/base/join-compiler.ts +26 -0
  62. package/src/core/dialect/base/orderby-compiler.ts +21 -0
  63. package/src/core/dialect/base/pagination-strategy.ts +32 -0
  64. package/src/core/dialect/base/returning-strategy.ts +56 -0
  65. package/src/core/dialect/base/sql-dialect.ts +181 -204
  66. package/src/core/dialect/dialect-factory.ts +91 -0
  67. package/src/core/dialect/mssql/functions.ts +101 -0
  68. package/src/core/dialect/mssql/index.ts +128 -126
  69. package/src/core/dialect/mysql/functions.ts +101 -0
  70. package/src/core/dialect/mysql/index.ts +20 -18
  71. package/src/core/dialect/postgres/functions.ts +95 -0
  72. package/src/core/dialect/postgres/index.ts +30 -28
  73. package/src/core/dialect/sqlite/functions.ts +115 -0
  74. package/src/core/dialect/sqlite/index.ts +30 -28
  75. package/src/core/driver/database-driver.ts +11 -0
  76. package/src/core/driver/mssql-driver.ts +20 -0
  77. package/src/core/driver/mysql-driver.ts +20 -0
  78. package/src/core/driver/postgres-driver.ts +20 -0
  79. package/src/core/driver/sqlite-driver.ts +20 -0
  80. package/src/core/execution/db-executor.ts +63 -0
  81. package/src/core/execution/executors/mssql-executor.ts +39 -0
  82. package/src/core/execution/executors/mysql-executor.ts +47 -0
  83. package/src/core/execution/executors/postgres-executor.ts +32 -0
  84. package/src/core/execution/executors/sqlite-executor.ts +31 -0
  85. package/src/core/functions/datetime.ts +132 -0
  86. package/src/core/functions/numeric.ts +179 -0
  87. package/src/core/functions/standard-strategy.ts +47 -0
  88. package/src/core/functions/text.ts +147 -0
  89. package/src/core/functions/types.ts +18 -0
  90. package/src/core/hydration/types.ts +57 -0
  91. package/src/decorators/bootstrap.ts +10 -0
  92. package/src/decorators/column.ts +13 -4
  93. package/src/decorators/relations.ts +15 -0
  94. package/src/index.ts +37 -19
  95. package/src/orm/entity-context.ts +30 -0
  96. package/src/orm/entity-meta.ts +2 -2
  97. package/src/orm/entity-metadata.ts +8 -6
  98. package/src/orm/entity.ts +72 -41
  99. package/src/orm/execute.ts +42 -25
  100. package/src/orm/execution-context.ts +12 -0
  101. package/src/orm/hydration-context.ts +14 -0
  102. package/src/orm/hydration.ts +25 -17
  103. package/src/orm/identity-map.ts +4 -0
  104. package/src/orm/interceptor-pipeline.ts +29 -0
  105. package/src/orm/lazy-batch.ts +50 -6
  106. package/src/orm/orm-session.ts +234 -0
  107. package/src/orm/orm.ts +58 -0
  108. package/src/orm/query-logger.ts +1 -1
  109. package/src/orm/relation-change-processor.ts +48 -3
  110. package/src/orm/relations/belongs-to.ts +45 -44
  111. package/src/orm/relations/has-many.ts +44 -43
  112. package/src/orm/relations/has-one.ts +140 -0
  113. package/src/orm/relations/many-to-many.ts +46 -45
  114. package/src/orm/transaction-runner.ts +1 -1
  115. package/src/orm/unit-of-work.ts +66 -61
  116. package/src/query-builder/delete.ts +22 -5
  117. package/src/query-builder/hydration-manager.ts +2 -1
  118. package/src/query-builder/hydration-planner.ts +8 -7
  119. package/src/query-builder/insert.ts +22 -5
  120. package/src/query-builder/relation-conditions.ts +9 -8
  121. package/src/query-builder/relation-service.ts +3 -2
  122. package/src/query-builder/select.ts +575 -64
  123. package/src/query-builder/update.ts +22 -5
  124. package/src/schema/column.ts +246 -246
  125. package/src/schema/relation.ts +35 -1
  126. package/src/schema/table.ts +28 -28
  127. package/src/schema/types.ts +41 -31
  128. package/src/orm/db-executor.ts +0 -11
  129. package/src/orm/orm-context.ts +0 -159
@@ -1,16 +1,18 @@
1
- import { CompilerContext, Dialect } from '../abstract.js';
2
- import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode } from '../../ast/query.js';
3
- import { JsonPathNode } from '../../ast/expression.js';
1
+ import { CompilerContext, Dialect } from '../abstract.js';
2
+ import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode } from '../../ast/query.js';
3
+ import { JsonPathNode } from '../../ast/expression.js';
4
+ import { MssqlFunctionStrategy } from './functions.js';
4
5
 
5
6
  /**
6
7
  * Microsoft SQL Server dialect implementation
7
8
  */
8
9
  export class SqlServerDialect extends Dialect {
10
+ protected readonly dialect = 'mssql';
9
11
  /**
10
12
  * Creates a new SqlServerDialect instance
11
13
  */
12
14
  public constructor() {
13
- super();
15
+ super(new MssqlFunctionStrategy());
14
16
  }
15
17
 
16
18
  /**
@@ -42,36 +44,36 @@ export class SqlServerDialect extends Dialect {
42
44
  return `@p${index}`;
43
45
  }
44
46
 
45
- /**
46
- * Compiles SELECT query AST to SQL Server SQL
47
- * @param ast - Query AST
48
- * @param ctx - Compiler context
49
- * @returns SQL Server SQL string
50
- */
51
- protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
52
- const hasSetOps = !!(ast.setOps && ast.setOps.length);
53
- const ctes = this.compileCtes(ast, ctx);
54
-
55
- const baseAst: SelectQueryNode = hasSetOps
56
- ? { ...ast, setOps: undefined, orderBy: undefined, limit: undefined, offset: undefined }
57
- : ast;
58
-
59
- const baseSelect = this.compileSelectCore(baseAst, ctx);
60
-
61
- if (!hasSetOps) {
62
- return `${ctes}${baseSelect}`;
63
- }
64
-
65
- const compound = ast.setOps!
66
- .map(op => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`)
67
- .join(' ');
68
-
69
- const orderBy = this.compileOrderBy(ast);
70
- const pagination = this.compilePagination(ast, orderBy);
71
- const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
72
- const tail = pagination || orderBy;
73
- return `${ctes}${combined}${tail}`;
74
- }
47
+ /**
48
+ * Compiles SELECT query AST to SQL Server SQL
49
+ * @param ast - Query AST
50
+ * @param ctx - Compiler context
51
+ * @returns SQL Server SQL string
52
+ */
53
+ protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
54
+ const hasSetOps = !!(ast.setOps && ast.setOps.length);
55
+ const ctes = this.compileCtes(ast, ctx);
56
+
57
+ const baseAst: SelectQueryNode = hasSetOps
58
+ ? { ...ast, setOps: undefined, orderBy: undefined, limit: undefined, offset: undefined }
59
+ : ast;
60
+
61
+ const baseSelect = this.compileSelectCore(baseAst, ctx);
62
+
63
+ if (!hasSetOps) {
64
+ return `${ctes}${baseSelect}`;
65
+ }
66
+
67
+ const compound = ast.setOps!
68
+ .map(op => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`)
69
+ .join(' ');
70
+
71
+ const orderBy = this.compileOrderBy(ast);
72
+ const pagination = this.compilePagination(ast, orderBy);
73
+ const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
74
+ const tail = pagination || orderBy;
75
+ return `${ctes}${combined}${tail}`;
76
+ }
75
77
 
76
78
  protected compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string {
77
79
  const table = this.quoteIdentifier(ast.into.name);
@@ -92,95 +94,95 @@ export class SqlServerDialect extends Dialect {
92
94
  return `UPDATE ${table} SET ${assignments}${whereClause};`;
93
95
  }
94
96
 
95
- protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
96
- const table = this.quoteIdentifier(ast.from.name);
97
- const whereClause = this.compileWhere(ast.where, ctx);
98
- return `DELETE FROM ${table}${whereClause};`;
99
- }
100
-
101
- private compileSelectCore(ast: SelectQueryNode, ctx: CompilerContext): string {
102
- const columns = ast.columns.map(c => {
103
- let expr = '';
104
- if (c.type === 'Function') {
105
- expr = this.compileOperand(c, ctx);
106
- } else if (c.type === 'Column') {
107
- expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
108
- } else if (c.type === 'ScalarSubquery') {
109
- expr = this.compileOperand(c, ctx);
110
- } else if (c.type === 'WindowFunction') {
111
- expr = this.compileOperand(c, ctx);
112
- }
113
-
114
- if (c.alias) {
115
- if (c.alias.includes('(')) return c.alias;
116
- return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
117
- }
118
- return expr;
119
- }).join(', ');
120
-
121
- const distinct = ast.distinct ? 'DISTINCT ' : '';
122
- const from = `${this.quoteIdentifier(ast.from.name)}`;
123
-
124
- const joins = ast.joins.map(j => {
125
- const table = this.quoteIdentifier(j.table.name);
126
- const cond = this.compileExpression(j.condition, ctx);
127
- return `${j.kind} JOIN ${table} ON ${cond}`;
128
- }).join(' ');
129
- const whereClause = this.compileWhere(ast.where, ctx);
130
-
131
- const groupBy = ast.groupBy && ast.groupBy.length > 0
132
- ? ' GROUP BY ' + ast.groupBy.map(c => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(', ')
133
- : '';
134
-
135
- const having = ast.having
136
- ? ` HAVING ${this.compileExpression(ast.having, ctx)}`
137
- : '';
138
-
139
- const orderBy = this.compileOrderBy(ast);
140
- const pagination = this.compilePagination(ast, orderBy);
141
-
142
- if (pagination) {
143
- return `SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${pagination}`;
144
- }
145
-
146
- return `SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${orderBy}`;
147
- }
148
-
149
- private compileOrderBy(ast: SelectQueryNode): string {
150
- if (!ast.orderBy || ast.orderBy.length === 0) return '';
151
- return ' ORDER BY ' + ast.orderBy
152
- .map(o => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`)
153
- .join(', ');
154
- }
155
-
156
- private compilePagination(ast: SelectQueryNode, orderBy: string): string {
157
- const hasLimit = ast.limit !== undefined;
158
- const hasOffset = ast.offset !== undefined;
159
- if (!hasLimit && !hasOffset) return '';
160
-
161
- const off = ast.offset ?? 0;
162
- const orderClause = orderBy || ' ORDER BY (SELECT NULL)';
163
- let pagination = `${orderClause} OFFSET ${off} ROWS`;
164
- if (hasLimit) {
165
- pagination += ` FETCH NEXT ${ast.limit} ROWS ONLY`;
166
- }
167
- return pagination;
168
- }
169
-
170
- private compileCtes(ast: SelectQueryNode, ctx: CompilerContext): string {
171
- if (!ast.ctes || ast.ctes.length === 0) return '';
172
- // MSSQL does not use RECURSIVE keyword, but supports recursion when CTE references itself.
173
- const defs = ast.ctes.map(cte => {
174
- const name = this.quoteIdentifier(cte.name);
175
- const cols = cte.columns ? `(${cte.columns.map(c => this.quoteIdentifier(c)).join(', ')})` : '';
176
- const query = this.compileSelectAst(this.normalizeSelectAst(cte.query), ctx).trim().replace(/;$/, '');
177
- return `${name}${cols} AS (${query})`;
178
- }).join(', ');
179
- return `WITH ${defs} `;
180
- }
181
-
182
- private wrapSetOperand(sql: string): string {
183
- const trimmed = sql.trim().replace(/;$/, '');
184
- return `(${trimmed})`;
185
- }
186
- }
97
+ protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
98
+ const table = this.quoteIdentifier(ast.from.name);
99
+ const whereClause = this.compileWhere(ast.where, ctx);
100
+ return `DELETE FROM ${table}${whereClause};`;
101
+ }
102
+
103
+ private compileSelectCore(ast: SelectQueryNode, ctx: CompilerContext): string {
104
+ const columns = ast.columns.map(c => {
105
+ let expr = '';
106
+ if (c.type === 'Function') {
107
+ expr = this.compileOperand(c, ctx);
108
+ } else if (c.type === 'Column') {
109
+ expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
110
+ } else if (c.type === 'ScalarSubquery') {
111
+ expr = this.compileOperand(c, ctx);
112
+ } else if (c.type === 'WindowFunction') {
113
+ expr = this.compileOperand(c, ctx);
114
+ }
115
+
116
+ if (c.alias) {
117
+ if (c.alias.includes('(')) return c.alias;
118
+ return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
119
+ }
120
+ return expr;
121
+ }).join(', ');
122
+
123
+ const distinct = ast.distinct ? 'DISTINCT ' : '';
124
+ const from = `${this.quoteIdentifier(ast.from.name)}`;
125
+
126
+ const joins = ast.joins.map(j => {
127
+ const table = this.quoteIdentifier(j.table.name);
128
+ const cond = this.compileExpression(j.condition, ctx);
129
+ return `${j.kind} JOIN ${table} ON ${cond}`;
130
+ }).join(' ');
131
+ const whereClause = this.compileWhere(ast.where, ctx);
132
+
133
+ const groupBy = ast.groupBy && ast.groupBy.length > 0
134
+ ? ' GROUP BY ' + ast.groupBy.map(c => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(', ')
135
+ : '';
136
+
137
+ const having = ast.having
138
+ ? ` HAVING ${this.compileExpression(ast.having, ctx)}`
139
+ : '';
140
+
141
+ const orderBy = this.compileOrderBy(ast);
142
+ const pagination = this.compilePagination(ast, orderBy);
143
+
144
+ if (pagination) {
145
+ return `SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${pagination}`;
146
+ }
147
+
148
+ return `SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${orderBy}`;
149
+ }
150
+
151
+ private compileOrderBy(ast: SelectQueryNode): string {
152
+ if (!ast.orderBy || ast.orderBy.length === 0) return '';
153
+ return ' ORDER BY ' + ast.orderBy
154
+ .map(o => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`)
155
+ .join(', ');
156
+ }
157
+
158
+ private compilePagination(ast: SelectQueryNode, orderBy: string): string {
159
+ const hasLimit = ast.limit !== undefined;
160
+ const hasOffset = ast.offset !== undefined;
161
+ if (!hasLimit && !hasOffset) return '';
162
+
163
+ const off = ast.offset ?? 0;
164
+ const orderClause = orderBy || ' ORDER BY (SELECT NULL)';
165
+ let pagination = `${orderClause} OFFSET ${off} ROWS`;
166
+ if (hasLimit) {
167
+ pagination += ` FETCH NEXT ${ast.limit} ROWS ONLY`;
168
+ }
169
+ return pagination;
170
+ }
171
+
172
+ private compileCtes(ast: SelectQueryNode, ctx: CompilerContext): string {
173
+ if (!ast.ctes || ast.ctes.length === 0) return '';
174
+ // MSSQL does not use RECURSIVE keyword, but supports recursion when CTE references itself.
175
+ const defs = ast.ctes.map(cte => {
176
+ const name = this.quoteIdentifier(cte.name);
177
+ const cols = cte.columns ? `(${cte.columns.map(c => this.quoteIdentifier(c)).join(', ')})` : '';
178
+ const query = this.compileSelectAst(this.normalizeSelectAst(cte.query), ctx).trim().replace(/;$/, '');
179
+ return `${name}${cols} AS (${query})`;
180
+ }).join(', ');
181
+ return `WITH ${defs} `;
182
+ }
183
+
184
+ private wrapSetOperand(sql: string): string {
185
+ const trimmed = sql.trim().replace(/;$/, '');
186
+ return `(${trimmed})`;
187
+ }
188
+ }
@@ -0,0 +1,101 @@
1
+ import { StandardFunctionStrategy } from '../../functions/standard-strategy.js';
2
+ import { FunctionRenderContext } from '../../functions/types.js';
3
+ import { LiteralNode } from '../../ast/expression.js';
4
+
5
+ export class MysqlFunctionStrategy extends StandardFunctionStrategy {
6
+ constructor() {
7
+ super();
8
+ this.registerOverrides();
9
+ }
10
+
11
+ private registerOverrides() {
12
+ // Override Standard/Abstract definitions with MySQL specifics
13
+
14
+ // Date/Time functions
15
+ this.add('NOW', () => `NOW()`);
16
+ this.add('CURRENT_DATE', () => `CURDATE()`);
17
+ this.add('CURRENT_TIME', () => `CURTIME()`);
18
+ this.add('UTC_NOW', () => `UTC_TIMESTAMP()`);
19
+
20
+ this.add('EXTRACT', ({ compiledArgs }) => {
21
+ if (compiledArgs.length !== 2) throw new Error('EXTRACT expects 2 arguments (part, date)');
22
+ const [part, date] = compiledArgs;
23
+ const partClean = part.replace(/['"]/g, '');
24
+ return `EXTRACT(${partClean} FROM ${date})`;
25
+ });
26
+
27
+ this.add('YEAR', ({ compiledArgs }) => {
28
+ if (compiledArgs.length !== 1) throw new Error('YEAR expects 1 argument');
29
+ return `YEAR(${compiledArgs[0]})`;
30
+ });
31
+
32
+ this.add('MONTH', ({ compiledArgs }) => {
33
+ if (compiledArgs.length !== 1) throw new Error('MONTH expects 1 argument');
34
+ return `MONTH(${compiledArgs[0]})`;
35
+ });
36
+
37
+ this.add('DAY', ({ compiledArgs }) => {
38
+ if (compiledArgs.length !== 1) throw new Error('DAY expects 1 argument');
39
+ return `DAY(${compiledArgs[0]})`;
40
+ });
41
+
42
+ this.add('DATE_ADD', ({ node, compiledArgs }) => {
43
+ if (compiledArgs.length !== 3) throw new Error('DATE_ADD expects 3 arguments (date, interval, unit)');
44
+ const [date, interval] = compiledArgs;
45
+ const unitArg = node.args[2] as LiteralNode;
46
+ const unitClean = String(unitArg.value).replace(/['"]/g, '').toUpperCase();
47
+ return `DATE_ADD(${date}, INTERVAL ${interval} ${unitClean})`;
48
+ });
49
+
50
+ this.add('DATE_SUB', ({ node, compiledArgs }) => {
51
+ if (compiledArgs.length !== 3) throw new Error('DATE_SUB expects 3 arguments (date, interval, unit)');
52
+ const [date, interval] = compiledArgs;
53
+ const unitArg = node.args[2] as LiteralNode;
54
+ const unitClean = String(unitArg.value).replace(/['"]/g, '').toUpperCase();
55
+ return `DATE_SUB(${date}, INTERVAL ${interval} ${unitClean})`;
56
+ });
57
+
58
+ this.add('DATE_DIFF', ({ compiledArgs }) => {
59
+ if (compiledArgs.length !== 2) throw new Error('DATE_DIFF expects 2 arguments');
60
+ const [date1, date2] = compiledArgs;
61
+ return `DATEDIFF(${date1}, ${date2})`;
62
+ });
63
+
64
+ this.add('DATE_FORMAT', ({ compiledArgs }) => {
65
+ if (compiledArgs.length !== 2) throw new Error('DATE_FORMAT expects 2 arguments');
66
+ const [date, format] = compiledArgs;
67
+ return `DATE_FORMAT(${date}, ${format})`;
68
+ });
69
+
70
+ this.add('END_OF_MONTH', ({ compiledArgs }) => {
71
+ if (compiledArgs.length !== 1) throw new Error('END_OF_MONTH expects 1 argument');
72
+ return `LAST_DAY(${compiledArgs[0]})`;
73
+ });
74
+
75
+ this.add('DAY_OF_WEEK', ({ compiledArgs }) => {
76
+ if (compiledArgs.length !== 1) throw new Error('DAY_OF_WEEK expects 1 argument');
77
+ return `DAYOFWEEK(${compiledArgs[0]})`;
78
+ });
79
+
80
+ this.add('WEEK_OF_YEAR', ({ compiledArgs }) => {
81
+ if (compiledArgs.length !== 1) throw new Error('WEEK_OF_YEAR expects 1 argument');
82
+ return `WEEKOFYEAR(${compiledArgs[0]})`;
83
+ });
84
+
85
+ this.add('DATE_TRUNC', ({ node, compiledArgs }) => {
86
+ if (compiledArgs.length !== 2) throw new Error('DATE_TRUNC expects 2 arguments (part, date)');
87
+ const [, date] = compiledArgs;
88
+ const partArg = node.args[0] as LiteralNode;
89
+ const partClean = String(partArg.value).replace(/['"]/g, '').toLowerCase();
90
+ // MySQL doesn't have DATE_TRUNC, use DATE_FORMAT workaround
91
+ if (partClean === 'year') {
92
+ return `DATE_FORMAT(${date}, '%Y-01-01')`;
93
+ } else if (partClean === 'month') {
94
+ return `DATE_FORMAT(${date}, '%Y-%m-01')`;
95
+ } else if (partClean === 'day') {
96
+ return `DATE(${date})`;
97
+ }
98
+ return `DATE(${date})`;
99
+ });
100
+ }
101
+ }
@@ -1,15 +1,17 @@
1
- import { JsonPathNode } from '../../ast/expression.js';
2
- import { SqlDialectBase } from '../base/sql-dialect.js';
3
-
4
- /**
5
- * MySQL dialect implementation
6
- */
7
- export class MySqlDialect extends SqlDialectBase {
8
- /**
9
- * Creates a new MySqlDialect instance
10
- */
11
- public constructor() {
12
- super();
1
+ import { JsonPathNode } from '../../ast/expression.js';
2
+ import { SqlDialectBase } from '../base/sql-dialect.js';
3
+ import { MysqlFunctionStrategy } from './functions.js';
4
+
5
+ /**
6
+ * MySQL dialect implementation
7
+ */
8
+ export class MySqlDialect extends SqlDialectBase {
9
+ protected readonly dialect = 'mysql';
10
+ /**
11
+ * Creates a new MySqlDialect instance
12
+ */
13
+ public constructor() {
14
+ super(new MysqlFunctionStrategy());
13
15
  }
14
16
 
15
17
  /**
@@ -26,9 +28,9 @@ export class MySqlDialect extends SqlDialectBase {
26
28
  * @param node - JSON path node
27
29
  * @returns MySQL JSON path expression
28
30
  */
29
- protected compileJsonPath(node: JsonPathNode): string {
30
- const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
31
- // MySQL 5.7+ uses col->'$.path'
32
- return `${col}->'${node.path}'`;
33
- }
34
- }
31
+ protected compileJsonPath(node: JsonPathNode): string {
32
+ const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
33
+ // MySQL 5.7+ uses col->'$.path'
34
+ return `${col}->'${node.path}'`;
35
+ }
36
+ }
@@ -0,0 +1,95 @@
1
+ import { StandardFunctionStrategy } from '../../functions/standard-strategy.js';
2
+ import { FunctionRenderContext } from '../../functions/types.js';
3
+ import { LiteralNode } from '../../ast/expression.js';
4
+
5
+ export class PostgresFunctionStrategy extends StandardFunctionStrategy {
6
+ constructor() {
7
+ super();
8
+ this.registerOverrides();
9
+ }
10
+
11
+ private registerOverrides() {
12
+ // Override Standard/Abstract definitions with PostgreSQL specifics
13
+
14
+ // Date/Time functions
15
+ this.add('UTC_NOW', () => `(NOW() AT TIME ZONE 'UTC')`);
16
+ this.add('UNIX_TIMESTAMP', () => `EXTRACT(EPOCH FROM NOW())::INTEGER`);
17
+ this.add('FROM_UNIXTIME', ({ compiledArgs }) => {
18
+ if (compiledArgs.length !== 1) throw new Error('FROM_UNIXTIME expects 1 argument');
19
+ return `to_timestamp(${compiledArgs[0]})`;
20
+ });
21
+
22
+ this.add('EXTRACT', ({ compiledArgs }) => {
23
+ if (compiledArgs.length !== 2) throw new Error('EXTRACT expects 2 arguments (part, date)');
24
+ const [part, date] = compiledArgs;
25
+ const partClean = part.replace(/['"]/g, '');
26
+ return `EXTRACT(${partClean} FROM ${date})`;
27
+ });
28
+
29
+ this.add('YEAR', ({ compiledArgs }) => {
30
+ if (compiledArgs.length !== 1) throw new Error('YEAR expects 1 argument');
31
+ return `EXTRACT(YEAR FROM ${compiledArgs[0]})`;
32
+ });
33
+
34
+ this.add('MONTH', ({ compiledArgs }) => {
35
+ if (compiledArgs.length !== 1) throw new Error('MONTH expects 1 argument');
36
+ return `EXTRACT(MONTH FROM ${compiledArgs[0]})`;
37
+ });
38
+
39
+ this.add('DAY', ({ compiledArgs }) => {
40
+ if (compiledArgs.length !== 1) throw new Error('DAY expects 1 argument');
41
+ return `EXTRACT(DAY FROM ${compiledArgs[0]})`;
42
+ });
43
+
44
+ this.add('DATE_ADD', ({ node, compiledArgs }) => {
45
+ if (compiledArgs.length !== 3) throw new Error('DATE_ADD expects 3 arguments (date, interval, unit)');
46
+ const [date, interval] = compiledArgs;
47
+ const unitArg = node.args[2] as LiteralNode;
48
+ const unitClean = String(unitArg.value).replace(/['"]/g, '').toLowerCase();
49
+ return `(${date} + (${interval} || ' ${unitClean}')::INTERVAL)`;
50
+ });
51
+
52
+ this.add('DATE_SUB', ({ node, compiledArgs }) => {
53
+ if (compiledArgs.length !== 3) throw new Error('DATE_SUB expects 3 arguments (date, interval, unit)');
54
+ const [date, interval] = compiledArgs;
55
+ const unitArg = node.args[2] as LiteralNode;
56
+ const unitClean = String(unitArg.value).replace(/['"]/g, '').toLowerCase();
57
+ return `(${date} - (${interval} || ' ${unitClean}')::INTERVAL)`;
58
+ });
59
+
60
+ this.add('DATE_DIFF', ({ compiledArgs }) => {
61
+ if (compiledArgs.length !== 2) throw new Error('DATE_DIFF expects 2 arguments');
62
+ const [date1, date2] = compiledArgs;
63
+ return `(${date1}::DATE - ${date2}::DATE)`;
64
+ });
65
+
66
+ this.add('DATE_FORMAT', ({ compiledArgs }) => {
67
+ if (compiledArgs.length !== 2) throw new Error('DATE_FORMAT expects 2 arguments');
68
+ const [date, format] = compiledArgs;
69
+ return `TO_CHAR(${date}, ${format})`;
70
+ });
71
+
72
+ this.add('END_OF_MONTH', ({ compiledArgs }) => {
73
+ if (compiledArgs.length !== 1) throw new Error('END_OF_MONTH expects 1 argument');
74
+ return `(date_trunc('month', ${compiledArgs[0]}) + interval '1 month' - interval '1 day')::DATE`;
75
+ });
76
+
77
+ this.add('DAY_OF_WEEK', ({ compiledArgs }) => {
78
+ if (compiledArgs.length !== 1) throw new Error('DAY_OF_WEEK expects 1 argument');
79
+ return `EXTRACT(DOW FROM ${compiledArgs[0]})`;
80
+ });
81
+
82
+ this.add('WEEK_OF_YEAR', ({ compiledArgs }) => {
83
+ if (compiledArgs.length !== 1) throw new Error('WEEK_OF_YEAR expects 1 argument');
84
+ return `EXTRACT(WEEK FROM ${compiledArgs[0]})`;
85
+ });
86
+
87
+ this.add('DATE_TRUNC', ({ node, compiledArgs }) => {
88
+ if (compiledArgs.length !== 2) throw new Error('DATE_TRUNC expects 2 arguments (part, date)');
89
+ const [, date] = compiledArgs;
90
+ const partArg = node.args[0] as LiteralNode;
91
+ const partClean = String(partArg.value).replace(/['"]/g, '').toLowerCase();
92
+ return `DATE_TRUNC('${partClean}', ${date})`;
93
+ });
94
+ }
95
+ }
@@ -1,16 +1,18 @@
1
- import { CompilerContext } from '../abstract.js';
2
- import { JsonPathNode, ColumnNode } from '../../ast/expression.js';
3
- import { SqlDialectBase } from '../base/sql-dialect.js';
4
-
5
- /**
6
- * PostgreSQL dialect implementation
7
- */
8
- export class PostgresDialect extends SqlDialectBase {
9
- /**
10
- * Creates a new PostgresDialect instance
11
- */
12
- public constructor() {
13
- super();
1
+ import { CompilerContext } from '../abstract.js';
2
+ import { JsonPathNode, ColumnNode } from '../../ast/expression.js';
3
+ import { SqlDialectBase } from '../base/sql-dialect.js';
4
+ import { PostgresFunctionStrategy } from './functions.js';
5
+
6
+ /**
7
+ * PostgreSQL dialect implementation
8
+ */
9
+ export class PostgresDialect extends SqlDialectBase {
10
+ protected readonly dialect = 'postgres';
11
+ /**
12
+ * Creates a new PostgresDialect instance
13
+ */
14
+ public constructor() {
15
+ super(new PostgresFunctionStrategy());
14
16
  }
15
17
 
16
18
  /**
@@ -27,19 +29,19 @@ export class PostgresDialect extends SqlDialectBase {
27
29
  * @param node - JSON path node
28
30
  * @returns PostgreSQL JSON path expression
29
31
  */
30
- protected compileJsonPath(node: JsonPathNode): string {
31
- const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
32
- // Postgres uses col->>'path' for text extraction
33
- return `${col}->>'${node.path}'`;
34
- }
32
+ protected compileJsonPath(node: JsonPathNode): string {
33
+ const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
34
+ // Postgres uses col->>'path' for text extraction
35
+ return `${col}->>'${node.path}'`;
36
+ }
35
37
 
36
- protected compileReturning(returning: ColumnNode[] | undefined, ctx: CompilerContext): string {
37
- if (!returning || returning.length === 0) return '';
38
- const columns = this.formatReturningColumns(returning);
39
- return ` RETURNING ${columns}`;
40
- }
41
-
42
- supportsReturning(): boolean {
43
- return true;
44
- }
45
- }
38
+ protected compileReturning(returning: ColumnNode[] | undefined, ctx: CompilerContext): string {
39
+ if (!returning || returning.length === 0) return '';
40
+ const columns = this.formatReturningColumns(returning);
41
+ return ` RETURNING ${columns}`;
42
+ }
43
+
44
+ supportsReturning(): boolean {
45
+ return true;
46
+ }
47
+ }