metal-orm 1.0.11 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -18
- package/dist/decorators/index.cjs +317 -34
- package/dist/decorators/index.cjs.map +1 -1
- package/dist/decorators/index.d.cts +1 -1
- package/dist/decorators/index.d.ts +1 -1
- package/dist/decorators/index.js +317 -34
- package/dist/decorators/index.js.map +1 -1
- package/dist/index.cjs +1965 -267
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +273 -23
- package/dist/index.d.ts +273 -23
- package/dist/index.js +1947 -267
- package/dist/index.js.map +1 -1
- package/dist/{select-654m4qy8.d.cts → select-CCp1oz9p.d.cts} +254 -4
- package/dist/{select-654m4qy8.d.ts → select-CCp1oz9p.d.ts} +254 -4
- package/package.json +3 -2
- package/src/core/ast/query.ts +40 -22
- package/src/core/ddl/dialects/base-schema-dialect.ts +48 -0
- package/src/core/ddl/dialects/index.ts +5 -0
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +97 -0
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +109 -0
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +99 -0
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +103 -0
- package/src/core/ddl/introspect/mssql.ts +149 -0
- package/src/core/ddl/introspect/mysql.ts +99 -0
- package/src/core/ddl/introspect/postgres.ts +154 -0
- package/src/core/ddl/introspect/sqlite.ts +66 -0
- package/src/core/ddl/introspect/types.ts +19 -0
- package/src/core/ddl/introspect/utils.ts +27 -0
- package/src/core/ddl/schema-diff.ts +179 -0
- package/src/core/ddl/schema-generator.ts +229 -0
- package/src/core/ddl/schema-introspect.ts +32 -0
- package/src/core/ddl/schema-types.ts +39 -0
- package/src/core/dialect/abstract.ts +122 -37
- package/src/core/dialect/base/sql-dialect.ts +204 -0
- package/src/core/dialect/mssql/index.ts +125 -80
- package/src/core/dialect/mysql/index.ts +18 -112
- package/src/core/dialect/postgres/index.ts +29 -126
- package/src/core/dialect/sqlite/index.ts +28 -129
- package/src/index.ts +4 -0
- package/src/orm/execute.ts +25 -16
- package/src/orm/orm-context.ts +60 -55
- package/src/orm/query-logger.ts +38 -0
- package/src/orm/relations/belongs-to.ts +42 -26
- package/src/orm/relations/has-many.ts +41 -25
- package/src/orm/relations/many-to-many.ts +43 -27
- package/src/orm/unit-of-work.ts +60 -23
- package/src/query-builder/hydration-manager.ts +229 -25
- package/src/query-builder/query-ast-service.ts +27 -12
- package/src/query-builder/select-query-state.ts +24 -12
- package/src/query-builder/select.ts +58 -14
- package/src/schema/column.ts +206 -27
- package/src/schema/table.ts +89 -32
- package/src/schema/types.ts +8 -5
|
@@ -1,6 +1,6 @@
|
|
|
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
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Microsoft SQL Server dialect implementation
|
|
@@ -42,77 +42,36 @@ export class SqlServerDialect extends Dialect {
|
|
|
42
42
|
return `@p${index}`;
|
|
43
43
|
}
|
|
44
44
|
|
|
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
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const table = this.quoteIdentifier(j.table.name);
|
|
76
|
-
const cond = this.compileExpression(j.condition, ctx);
|
|
77
|
-
return `${j.kind} JOIN ${table} ON ${cond}`;
|
|
78
|
-
}).join(' ');
|
|
79
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
80
|
-
|
|
81
|
-
const groupBy = ast.groupBy && ast.groupBy.length > 0
|
|
82
|
-
? ' GROUP BY ' + ast.groupBy.map(c => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(', ')
|
|
83
|
-
: '';
|
|
84
|
-
|
|
85
|
-
const having = ast.having
|
|
86
|
-
? ` HAVING ${this.compileExpression(ast.having, ctx)}`
|
|
87
|
-
: '';
|
|
88
|
-
|
|
89
|
-
const orderBy = ast.orderBy && ast.orderBy.length > 0
|
|
90
|
-
? ' ORDER BY ' + ast.orderBy.map(o => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(', ')
|
|
91
|
-
: '';
|
|
92
|
-
|
|
93
|
-
let pagination = '';
|
|
94
|
-
if (ast.limit || ast.offset) {
|
|
95
|
-
const off = ast.offset || 0;
|
|
96
|
-
const orderClause = orderBy || ' ORDER BY (SELECT NULL)';
|
|
97
|
-
pagination = `${orderClause} OFFSET ${off} ROWS`;
|
|
98
|
-
if (ast.limit) {
|
|
99
|
-
pagination += ` FETCH NEXT ${ast.limit} ROWS ONLY`;
|
|
100
|
-
}
|
|
101
|
-
return `SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${pagination};`;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const ctes = ast.ctes && ast.ctes.length > 0
|
|
105
|
-
? 'WITH ' + ast.ctes.map(cte => {
|
|
106
|
-
// MSSQL does not use RECURSIVE keyword
|
|
107
|
-
const name = this.quoteIdentifier(cte.name);
|
|
108
|
-
const cols = cte.columns ? `(${cte.columns.map(c => this.quoteIdentifier(c)).join(', ')})` : '';
|
|
109
|
-
const query = this.compileSelectAst(cte.query, ctx).trim().replace(/;$/, '');
|
|
110
|
-
return `${name}${cols} AS (${query})`;
|
|
111
|
-
}).join(', ') + ' '
|
|
112
|
-
: '';
|
|
113
|
-
|
|
114
|
-
return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${orderBy};`;
|
|
115
|
-
}
|
|
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
|
+
}
|
|
116
75
|
|
|
117
76
|
protected compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string {
|
|
118
77
|
const table = this.quoteIdentifier(ast.into.name);
|
|
@@ -133,9 +92,95 @@ export class SqlServerDialect extends Dialect {
|
|
|
133
92
|
return `UPDATE ${table} SET ${assignments}${whereClause};`;
|
|
134
93
|
}
|
|
135
94
|
|
|
136
|
-
protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
|
|
137
|
-
const table = this.quoteIdentifier(ast.from.name);
|
|
138
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
139
|
-
return `DELETE FROM ${table}${whereClause};`;
|
|
140
|
-
}
|
|
141
|
-
|
|
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
|
+
}
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
super();
|
|
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();
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
/**
|
|
@@ -27,102 +26,9 @@ export class MySqlDialect extends Dialect {
|
|
|
27
26
|
* @param node - JSON path node
|
|
28
27
|
* @returns MySQL JSON path expression
|
|
29
28
|
*/
|
|
30
|
-
protected compileJsonPath(node: JsonPathNode): string {
|
|
31
|
-
const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
|
|
32
|
-
// MySQL 5.7+ uses col->'$.path'
|
|
33
|
-
return `${col}->'${node.path}'`;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Compiles SELECT query AST to MySQL SQL
|
|
38
|
-
* @param ast - Query AST
|
|
39
|
-
* @param ctx - Compiler context
|
|
40
|
-
* @returns MySQL SQL string
|
|
41
|
-
*/
|
|
42
|
-
protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
|
|
43
|
-
const columns = ast.columns.map(c => {
|
|
44
|
-
let expr = '';
|
|
45
|
-
if (c.type === 'Function') {
|
|
46
|
-
expr = this.compileOperand(c, ctx);
|
|
47
|
-
} else if (c.type === 'Column') {
|
|
48
|
-
expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
|
|
49
|
-
} else if (c.type === 'ScalarSubquery') {
|
|
50
|
-
expr = this.compileOperand(c, ctx);
|
|
51
|
-
} else if (c.type === 'WindowFunction') {
|
|
52
|
-
expr = this.compileOperand(c, ctx);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (c.alias) {
|
|
56
|
-
if (c.alias.includes('(')) return c.alias;
|
|
57
|
-
return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
|
|
58
|
-
}
|
|
59
|
-
return expr;
|
|
60
|
-
}).join(', ');
|
|
61
|
-
|
|
62
|
-
const distinct = ast.distinct ? 'DISTINCT ' : '';
|
|
63
|
-
const from = `${this.quoteIdentifier(ast.from.name)}`;
|
|
64
|
-
|
|
65
|
-
const joins = ast.joins.map(j => {
|
|
66
|
-
const table = this.quoteIdentifier(j.table.name);
|
|
67
|
-
const cond = this.compileExpression(j.condition, ctx);
|
|
68
|
-
return `${j.kind} JOIN ${table} ON ${cond}`;
|
|
69
|
-
}).join(' ');
|
|
70
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
71
|
-
|
|
72
|
-
const groupBy = ast.groupBy && ast.groupBy.length > 0
|
|
73
|
-
? ' GROUP BY ' + ast.groupBy.map(c => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(', ')
|
|
74
|
-
: '';
|
|
75
|
-
|
|
76
|
-
const having = ast.having
|
|
77
|
-
? ` HAVING ${this.compileExpression(ast.having, ctx)}`
|
|
78
|
-
: '';
|
|
79
|
-
|
|
80
|
-
const orderBy = ast.orderBy && ast.orderBy.length > 0
|
|
81
|
-
? ' ORDER BY ' + ast.orderBy.map(o => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(', ')
|
|
82
|
-
: '';
|
|
83
|
-
|
|
84
|
-
const limit = ast.limit ? ` LIMIT ${ast.limit}` : '';
|
|
85
|
-
const offset = ast.offset ? ` OFFSET ${ast.offset}` : '';
|
|
86
|
-
|
|
87
|
-
const ctes = ast.ctes && ast.ctes.length > 0
|
|
88
|
-
? (() => {
|
|
89
|
-
const hasRecursive = ast.ctes.some(cte => cte.recursive);
|
|
90
|
-
const prefix = hasRecursive ? 'WITH RECURSIVE ' : 'WITH ';
|
|
91
|
-
const cteDefs = ast.ctes.map(cte => {
|
|
92
|
-
const name = this.quoteIdentifier(cte.name);
|
|
93
|
-
const cols = cte.columns ? `(${cte.columns.map(c => this.quoteIdentifier(c)).join(', ')})` : '';
|
|
94
|
-
const query = this.compileSelectAst(cte.query, ctx).trim().replace(/;$/, '');
|
|
95
|
-
return `${name}${cols} AS (${query})`;
|
|
96
|
-
}).join(', ');
|
|
97
|
-
return prefix + cteDefs + ' ';
|
|
98
|
-
})()
|
|
99
|
-
: '';
|
|
100
|
-
|
|
101
|
-
return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${orderBy}${limit}${offset};`;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
protected compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string {
|
|
105
|
-
const table = this.quoteIdentifier(ast.into.name);
|
|
106
|
-
const columnList = ast.columns.map(column => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(', ');
|
|
107
|
-
const values = ast.values.map(row => `(${row.map(value => this.compileOperand(value, ctx)).join(', ')})`).join(', ');
|
|
108
|
-
return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
protected compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string {
|
|
112
|
-
const table = this.quoteIdentifier(ast.table.name);
|
|
113
|
-
const assignments = ast.set.map(assignment => {
|
|
114
|
-
const col = assignment.column;
|
|
115
|
-
const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
|
|
116
|
-
const value = this.compileOperand(assignment.value, ctx);
|
|
117
|
-
return `${target} = ${value}`;
|
|
118
|
-
}).join(', ');
|
|
119
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
120
|
-
return `UPDATE ${table} SET ${assignments}${whereClause};`;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
|
|
124
|
-
const table = this.quoteIdentifier(ast.from.name);
|
|
125
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
126
|
-
return `DELETE FROM ${table}${whereClause};`;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
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
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { CompilerContext
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* PostgreSQL dialect implementation
|
|
7
|
-
*/
|
|
8
|
-
export class PostgresDialect extends
|
|
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
|
+
|
|
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();
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -27,116 +27,19 @@ export class PostgresDialect extends Dialect {
|
|
|
27
27
|
* @param node - JSON path node
|
|
28
28
|
* @returns PostgreSQL JSON path expression
|
|
29
29
|
*/
|
|
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
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
expr = this.compileOperand(c, ctx);
|
|
47
|
-
} else if (c.type === 'Column') {
|
|
48
|
-
expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
|
|
49
|
-
} else if (c.type === 'ScalarSubquery') {
|
|
50
|
-
expr = this.compileOperand(c, ctx);
|
|
51
|
-
} else if (c.type === 'WindowFunction') {
|
|
52
|
-
expr = this.compileOperand(c, ctx);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (c.alias) {
|
|
56
|
-
if (c.alias.includes('(')) return c.alias;
|
|
57
|
-
return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
|
|
58
|
-
}
|
|
59
|
-
return expr;
|
|
60
|
-
}).join(', ');
|
|
61
|
-
|
|
62
|
-
const distinct = ast.distinct ? 'DISTINCT ' : '';
|
|
63
|
-
const from = `${this.quoteIdentifier(ast.from.name)}`;
|
|
64
|
-
|
|
65
|
-
const joins = ast.joins.map(j => {
|
|
66
|
-
const table = this.quoteIdentifier(j.table.name);
|
|
67
|
-
const cond = this.compileExpression(j.condition, ctx);
|
|
68
|
-
return `${j.kind} JOIN ${table} ON ${cond}`;
|
|
69
|
-
}).join(' ');
|
|
70
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
71
|
-
|
|
72
|
-
const groupBy = ast.groupBy && ast.groupBy.length > 0
|
|
73
|
-
? ' GROUP BY ' + ast.groupBy.map(c => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(', ')
|
|
74
|
-
: '';
|
|
75
|
-
|
|
76
|
-
const having = ast.having
|
|
77
|
-
? ` HAVING ${this.compileExpression(ast.having, ctx)}`
|
|
78
|
-
: '';
|
|
79
|
-
|
|
80
|
-
const orderBy = ast.orderBy && ast.orderBy.length > 0
|
|
81
|
-
? ' ORDER BY ' + ast.orderBy.map(o => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(', ')
|
|
82
|
-
: '';
|
|
83
|
-
|
|
84
|
-
const limit = ast.limit ? ` LIMIT ${ast.limit}` : '';
|
|
85
|
-
const offset = ast.offset ? ` OFFSET ${ast.offset}` : '';
|
|
86
|
-
|
|
87
|
-
const ctes = ast.ctes && ast.ctes.length > 0
|
|
88
|
-
? (() => {
|
|
89
|
-
const hasRecursive = ast.ctes.some(cte => cte.recursive);
|
|
90
|
-
const prefix = hasRecursive ? 'WITH RECURSIVE ' : 'WITH ';
|
|
91
|
-
const cteDefs = ast.ctes.map(cte => {
|
|
92
|
-
const name = this.quoteIdentifier(cte.name);
|
|
93
|
-
const cols = cte.columns ? `(${cte.columns.map(c => this.quoteIdentifier(c)).join(', ')})` : '';
|
|
94
|
-
const query = this.compileSelectAst(cte.query, ctx).trim().replace(/;$/, '');
|
|
95
|
-
return `${name}${cols} AS (${query})`;
|
|
96
|
-
}).join(', ');
|
|
97
|
-
return prefix + cteDefs + ' ';
|
|
98
|
-
})()
|
|
99
|
-
: '';
|
|
100
|
-
|
|
101
|
-
return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${orderBy}${limit}${offset};`;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
protected compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string {
|
|
105
|
-
const table = this.quoteIdentifier(ast.into.name);
|
|
106
|
-
const columnList = ast.columns.map(column => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(', ');
|
|
107
|
-
const values = ast.values.map(row => `(${row.map(value => this.compileOperand(value, ctx)).join(', ')})`).join(', ');
|
|
108
|
-
const returning = this.compileReturning(ast.returning, ctx);
|
|
109
|
-
return `INSERT INTO ${table} (${columnList}) VALUES ${values}${returning};`;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
protected compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string {
|
|
113
|
-
const table = this.quoteIdentifier(ast.table.name);
|
|
114
|
-
const assignments = ast.set.map(assignment => {
|
|
115
|
-
const col = assignment.column;
|
|
116
|
-
const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
|
|
117
|
-
const value = this.compileOperand(assignment.value, ctx);
|
|
118
|
-
return `${target} = ${value}`;
|
|
119
|
-
}).join(', ');
|
|
120
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
121
|
-
const returning = this.compileReturning(ast.returning, ctx);
|
|
122
|
-
return `UPDATE ${table} SET ${assignments}${whereClause}${returning};`;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
|
|
126
|
-
const table = this.quoteIdentifier(ast.from.name);
|
|
127
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
128
|
-
const returning = this.compileReturning(ast.returning, ctx);
|
|
129
|
-
return `DELETE FROM ${table}${whereClause}${returning};`;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
protected compileReturning(returning: ColumnNode[] | undefined, ctx: CompilerContext): string {
|
|
133
|
-
if (!returning || returning.length === 0) return '';
|
|
134
|
-
const columns = returning
|
|
135
|
-
.map(column => {
|
|
136
|
-
const tablePart = column.table ? `${this.quoteIdentifier(column.table)}.` : '';
|
|
137
|
-
return `${tablePart}${this.quoteIdentifier(column.name)}`;
|
|
138
|
-
})
|
|
139
|
-
.join(', ');
|
|
140
|
-
return ` RETURNING ${columns}`;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
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
|
+
}
|
|
35
|
+
|
|
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
|
+
}
|