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.
- package/README.md +69 -67
- package/dist/decorators/index.cjs +1983 -224
- package/dist/decorators/index.cjs.map +1 -1
- package/dist/decorators/index.d.cts +6 -6
- package/dist/decorators/index.d.ts +6 -6
- package/dist/decorators/index.js +1982 -224
- package/dist/decorators/index.js.map +1 -1
- package/dist/index.cjs +5284 -3751
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +524 -169
- package/dist/index.d.ts +524 -169
- package/dist/index.js +5197 -3736
- package/dist/index.js.map +1 -1
- package/dist/{select-CCp1oz9p.d.cts → select-BKZrMRCQ.d.cts} +555 -94
- package/dist/{select-CCp1oz9p.d.ts → select-BKZrMRCQ.d.ts} +555 -94
- package/package.json +1 -1
- package/src/codegen/naming-strategy.ts +64 -0
- package/src/codegen/typescript.ts +19 -21
- package/src/core/ast/adapters.ts +21 -0
- package/src/core/ast/aggregate-functions.ts +13 -13
- package/src/core/ast/builders.ts +56 -43
- package/src/core/ast/expression-builders.ts +34 -34
- package/src/core/ast/expression-nodes.ts +18 -16
- package/src/core/ast/expression-visitor.ts +122 -69
- package/src/core/ast/expression.ts +6 -4
- package/src/core/ast/join-metadata.ts +15 -0
- package/src/core/ast/join-node.ts +22 -20
- package/src/core/ast/join.ts +5 -5
- package/src/core/ast/query.ts +52 -88
- package/src/core/ast/types.ts +20 -0
- package/src/core/ast/window-functions.ts +55 -55
- package/src/core/ddl/dialects/base-schema-dialect.ts +20 -6
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +32 -8
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +21 -10
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +52 -7
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +23 -9
- package/src/core/ddl/introspect/catalogs/index.ts +1 -0
- package/src/core/ddl/introspect/catalogs/postgres.ts +143 -0
- package/src/core/ddl/introspect/context.ts +9 -0
- package/src/core/ddl/introspect/functions/postgres.ts +26 -0
- package/src/core/ddl/introspect/mssql.ts +149 -149
- package/src/core/ddl/introspect/mysql.ts +99 -99
- package/src/core/ddl/introspect/postgres.ts +245 -154
- package/src/core/ddl/introspect/registry.ts +26 -0
- package/src/core/ddl/introspect/run-select.ts +25 -0
- package/src/core/ddl/introspect/sqlite.ts +7 -7
- package/src/core/ddl/introspect/types.ts +23 -19
- package/src/core/ddl/introspect/utils.ts +1 -1
- package/src/core/ddl/naming-strategy.ts +10 -0
- package/src/core/ddl/schema-dialect.ts +41 -0
- package/src/core/ddl/schema-diff.ts +211 -179
- package/src/core/ddl/schema-generator.ts +17 -90
- package/src/core/ddl/schema-introspect.ts +25 -32
- package/src/core/ddl/schema-plan-executor.ts +17 -0
- package/src/core/ddl/schema-types.ts +46 -39
- package/src/core/ddl/sql-writing.ts +170 -0
- package/src/core/dialect/abstract.ts +172 -126
- package/src/core/dialect/base/cte-compiler.ts +33 -0
- package/src/core/dialect/base/function-table-formatter.ts +132 -0
- package/src/core/dialect/base/groupby-compiler.ts +21 -0
- package/src/core/dialect/base/join-compiler.ts +26 -0
- package/src/core/dialect/base/orderby-compiler.ts +21 -0
- package/src/core/dialect/base/pagination-strategy.ts +32 -0
- package/src/core/dialect/base/returning-strategy.ts +56 -0
- package/src/core/dialect/base/sql-dialect.ts +181 -204
- package/src/core/dialect/dialect-factory.ts +91 -0
- package/src/core/dialect/mssql/functions.ts +101 -0
- package/src/core/dialect/mssql/index.ts +128 -126
- package/src/core/dialect/mysql/functions.ts +101 -0
- package/src/core/dialect/mysql/index.ts +20 -18
- package/src/core/dialect/postgres/functions.ts +95 -0
- package/src/core/dialect/postgres/index.ts +30 -28
- package/src/core/dialect/sqlite/functions.ts +115 -0
- package/src/core/dialect/sqlite/index.ts +30 -28
- package/src/core/driver/database-driver.ts +11 -0
- package/src/core/driver/mssql-driver.ts +20 -0
- package/src/core/driver/mysql-driver.ts +20 -0
- package/src/core/driver/postgres-driver.ts +20 -0
- package/src/core/driver/sqlite-driver.ts +20 -0
- package/src/core/execution/db-executor.ts +63 -0
- package/src/core/execution/executors/mssql-executor.ts +39 -0
- package/src/core/execution/executors/mysql-executor.ts +47 -0
- package/src/core/execution/executors/postgres-executor.ts +32 -0
- package/src/core/execution/executors/sqlite-executor.ts +31 -0
- package/src/core/functions/datetime.ts +132 -0
- package/src/core/functions/numeric.ts +179 -0
- package/src/core/functions/standard-strategy.ts +47 -0
- package/src/core/functions/text.ts +147 -0
- package/src/core/functions/types.ts +18 -0
- package/src/core/hydration/types.ts +57 -0
- package/src/decorators/bootstrap.ts +10 -0
- package/src/decorators/column.ts +13 -4
- package/src/decorators/relations.ts +15 -0
- package/src/index.ts +37 -19
- package/src/orm/entity-context.ts +30 -0
- package/src/orm/entity-meta.ts +2 -2
- package/src/orm/entity-metadata.ts +8 -6
- package/src/orm/entity.ts +72 -41
- package/src/orm/execute.ts +42 -25
- package/src/orm/execution-context.ts +12 -0
- package/src/orm/hydration-context.ts +14 -0
- package/src/orm/hydration.ts +25 -17
- package/src/orm/identity-map.ts +4 -0
- package/src/orm/interceptor-pipeline.ts +29 -0
- package/src/orm/lazy-batch.ts +50 -6
- package/src/orm/orm-session.ts +234 -0
- package/src/orm/orm.ts +58 -0
- package/src/orm/query-logger.ts +1 -1
- package/src/orm/relation-change-processor.ts +48 -3
- package/src/orm/relations/belongs-to.ts +45 -44
- package/src/orm/relations/has-many.ts +44 -43
- package/src/orm/relations/has-one.ts +140 -0
- package/src/orm/relations/many-to-many.ts +46 -45
- package/src/orm/transaction-runner.ts +1 -1
- package/src/orm/unit-of-work.ts +66 -61
- package/src/query-builder/delete.ts +22 -5
- package/src/query-builder/hydration-manager.ts +2 -1
- package/src/query-builder/hydration-planner.ts +8 -7
- package/src/query-builder/insert.ts +22 -5
- package/src/query-builder/relation-conditions.ts +9 -8
- package/src/query-builder/relation-service.ts +3 -2
- package/src/query-builder/select.ts +575 -64
- package/src/query-builder/update.ts +22 -5
- package/src/schema/column.ts +246 -246
- package/src/schema/relation.ts +35 -1
- package/src/schema/table.ts +28 -28
- package/src/schema/types.ts +41 -31
- package/src/orm/db-executor.ts +0 -11
- 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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
+
}
|