@mikro-orm/sql 7.0.0-dev.312 → 7.0.0-dev.314
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/AbstractSqlConnection.d.ts +5 -4
- package/AbstractSqlConnection.js +18 -5
- package/AbstractSqlDriver.d.ts +1 -1
- package/AbstractSqlDriver.js +34 -5
- package/AbstractSqlPlatform.d.ts +6 -0
- package/AbstractSqlPlatform.js +8 -0
- package/README.md +2 -1
- package/dialects/index.d.ts +1 -0
- package/dialects/index.js +1 -0
- package/dialects/oracledb/OracleDialect.d.ts +78 -0
- package/dialects/oracledb/OracleDialect.js +166 -0
- package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +19 -0
- package/dialects/oracledb/OracleNativeQueryBuilder.js +249 -0
- package/dialects/oracledb/index.d.ts +2 -0
- package/dialects/oracledb/index.js +2 -0
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +3 -3
- package/package.json +2 -2
- package/query/NativeQueryBuilder.d.ts +3 -3
- package/query/NativeQueryBuilder.js +4 -2
- package/query/QueryBuilder.d.ts +2 -1
- package/query/QueryBuilder.js +36 -1
- package/query/QueryBuilderHelper.d.ts +0 -1
- package/query/QueryBuilderHelper.js +14 -39
- package/schema/SchemaComparator.js +3 -1
- package/schema/SchemaHelper.d.ts +1 -0
- package/schema/SchemaHelper.js +1 -0
- package/schema/SqlSchemaGenerator.d.ts +1 -0
- package/schema/SqlSchemaGenerator.js +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { raw, RawQueryFragment, Utils } from '@mikro-orm/core';
|
|
2
|
+
import { QueryType } from '../../query/enums.js';
|
|
3
|
+
import { NativeQueryBuilder } from '../../query/NativeQueryBuilder.js';
|
|
4
|
+
/** @internal */
|
|
5
|
+
export function markOutBindings(obj) {
|
|
6
|
+
Object.defineProperty(obj, '__outBindings', {
|
|
7
|
+
value: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
configurable: true,
|
|
10
|
+
enumerable: false,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
/** @internal */
|
|
14
|
+
export class OracleNativeQueryBuilder extends NativeQueryBuilder {
|
|
15
|
+
as(alias) {
|
|
16
|
+
this.wrap('(', `) ${this.platform.quoteIdentifier(alias)}`);
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
19
|
+
compile() {
|
|
20
|
+
if (!this.type) {
|
|
21
|
+
throw new Error('No query type provided');
|
|
22
|
+
}
|
|
23
|
+
this.parts.length = 0;
|
|
24
|
+
this.params.length = 0;
|
|
25
|
+
/* v8 ignore next 3: query comment branch */
|
|
26
|
+
if (this.options.comment) {
|
|
27
|
+
this.parts.push(...this.options.comment.map(comment => `/* ${comment} */`));
|
|
28
|
+
}
|
|
29
|
+
let copy;
|
|
30
|
+
if (this.options.onConflict && !Utils.isEmpty(Utils.asArray(this.options.data)[0])) {
|
|
31
|
+
this.compileUpsert();
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
if (this.options.returning && Array.isArray(this.options.data) && this.options.data.length > 1) {
|
|
35
|
+
copy = [...this.options.data];
|
|
36
|
+
this.options.data.length = 1;
|
|
37
|
+
}
|
|
38
|
+
switch (this.type) {
|
|
39
|
+
case QueryType.SELECT:
|
|
40
|
+
case QueryType.COUNT:
|
|
41
|
+
this.compileSelect();
|
|
42
|
+
break;
|
|
43
|
+
case QueryType.INSERT:
|
|
44
|
+
this.compileInsert();
|
|
45
|
+
break;
|
|
46
|
+
case QueryType.UPDATE:
|
|
47
|
+
this.compileUpdate();
|
|
48
|
+
break;
|
|
49
|
+
case QueryType.DELETE:
|
|
50
|
+
this.compileDelete();
|
|
51
|
+
break;
|
|
52
|
+
case QueryType.TRUNCATE:
|
|
53
|
+
this.compileTruncate();
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
this.addOnConflictClause();
|
|
57
|
+
}
|
|
58
|
+
if (this.options.returning) {
|
|
59
|
+
const isUpsert = this.options.onConflict && !Utils.isEmpty(Utils.asArray(this.options.data)[0]);
|
|
60
|
+
const prefix = isUpsert ? `${this.getTableName()}.` : '';
|
|
61
|
+
const fields = this.options.returning.map(field => prefix + this.quote(Array.isArray(field) ? field[0] : field));
|
|
62
|
+
const into = this.options.returning.map(field => ':out_' + (Array.isArray(field) ? field[0] : field));
|
|
63
|
+
const outBindings = this.options.returning.map(field => {
|
|
64
|
+
const name = 'out_' + (Array.isArray(field) ? field[0] : field);
|
|
65
|
+
const type = Array.isArray(field) ? field[1] : 'string';
|
|
66
|
+
return [name, type];
|
|
67
|
+
});
|
|
68
|
+
markOutBindings(outBindings);
|
|
69
|
+
this.parts.push(`returning ${fields.join(', ')}`);
|
|
70
|
+
this.parts.push(`into ${into.join(', ')}`);
|
|
71
|
+
this.params.push(outBindings);
|
|
72
|
+
}
|
|
73
|
+
this.addLockClause();
|
|
74
|
+
if (!copy) {
|
|
75
|
+
return this.combineParts();
|
|
76
|
+
}
|
|
77
|
+
// multi insert with returning
|
|
78
|
+
const sql = this.parts.join(' ');
|
|
79
|
+
const blockLines = [];
|
|
80
|
+
const block2Lines = [];
|
|
81
|
+
const keys = Object.keys(copy[0]);
|
|
82
|
+
const last = this.params[this.params.length - 1];
|
|
83
|
+
/* v8 ignore next 3: defensive check — output bindings are always set by compile() */
|
|
84
|
+
if (!Array.isArray(last) || !('__outBindings' in last) || !last.__outBindings) {
|
|
85
|
+
throw new Error('Output bindings are required for multi insert with returning');
|
|
86
|
+
}
|
|
87
|
+
const outBindings = {};
|
|
88
|
+
markOutBindings(outBindings);
|
|
89
|
+
for (let i = 0; i < copy.length; i++) {
|
|
90
|
+
const params = [];
|
|
91
|
+
for (const key of keys) {
|
|
92
|
+
/* v8 ignore next 3: undefined value branch in multi-insert */
|
|
93
|
+
if (typeof copy[i][key] === 'undefined') {
|
|
94
|
+
params.push(this.platform.usesDefaultKeyword() ? raw('default') : null);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
params.push(copy[i][key]);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// we need to interpolate to allow proper escaping
|
|
101
|
+
const formatted = this.platform.formatQuery(sql, params).replaceAll(`'`, `''`);
|
|
102
|
+
/* v8 ignore next 3: returning field type branches */
|
|
103
|
+
const using = this.options.returning.map(field => {
|
|
104
|
+
const name = Array.isArray(field) ? field[0] : field;
|
|
105
|
+
const type = Array.isArray(field) ? field[1] : 'string';
|
|
106
|
+
outBindings[`out_${name}__${i}`] = {
|
|
107
|
+
dir: this.platform.mapToBindType('out'),
|
|
108
|
+
type: this.platform.mapToBindType(type),
|
|
109
|
+
};
|
|
110
|
+
return `out :out_${name}__${i}`;
|
|
111
|
+
});
|
|
112
|
+
blockLines.push(` execute immediate '${formatted}' using ${using.join(', ')};`);
|
|
113
|
+
block2Lines.push(` execute immediate '${sql}' using ${using.join(', ')};`);
|
|
114
|
+
}
|
|
115
|
+
const block = `begin\n${blockLines.join('\n')}\n end;`;
|
|
116
|
+
const block2 = `begin\n${block2Lines.join('\n')}\n end;`;
|
|
117
|
+
// save raw query without interpolation for logging,
|
|
118
|
+
Object.defineProperty(outBindings, '__rawQuery', {
|
|
119
|
+
value: block2,
|
|
120
|
+
writable: true,
|
|
121
|
+
configurable: true,
|
|
122
|
+
enumerable: false,
|
|
123
|
+
});
|
|
124
|
+
this.options.data = copy;
|
|
125
|
+
return { sql: block, params: [outBindings] };
|
|
126
|
+
}
|
|
127
|
+
compileTruncate() {
|
|
128
|
+
super.compileTruncate();
|
|
129
|
+
this.parts.push('drop all storage cascade');
|
|
130
|
+
}
|
|
131
|
+
combineParts() {
|
|
132
|
+
let sql = this.parts.join(' ');
|
|
133
|
+
const last = this.params[this.params.length - 1];
|
|
134
|
+
if (this.options.wrap) {
|
|
135
|
+
const [a, b] = this.options.wrap;
|
|
136
|
+
sql = `${a}${sql}${b}`;
|
|
137
|
+
}
|
|
138
|
+
if (!(Array.isArray(last) && '__outBindings' in last && last.__outBindings)) {
|
|
139
|
+
return { sql, params: this.params };
|
|
140
|
+
}
|
|
141
|
+
const out = this.params.pop();
|
|
142
|
+
const outBindings = {};
|
|
143
|
+
markOutBindings(outBindings);
|
|
144
|
+
this.params.push(outBindings);
|
|
145
|
+
for (const item of out) {
|
|
146
|
+
outBindings[item[0]] = {
|
|
147
|
+
dir: this.platform.mapToBindType('out'),
|
|
148
|
+
type: this.platform.mapToBindType(item[1]),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return { sql, params: this.params };
|
|
152
|
+
}
|
|
153
|
+
compileUpsert() {
|
|
154
|
+
const clause = this.options.onConflict;
|
|
155
|
+
const dataAsArray = Utils.asArray(this.options.data);
|
|
156
|
+
const keys = Object.keys(dataAsArray[0]);
|
|
157
|
+
const parts = [];
|
|
158
|
+
for (const data of dataAsArray) {
|
|
159
|
+
for (const key of keys) {
|
|
160
|
+
this.params.push(data[key]);
|
|
161
|
+
}
|
|
162
|
+
parts.push(`select ${keys.map(k => `? as ${this.quote(k)}`).join(', ')} from dual`);
|
|
163
|
+
}
|
|
164
|
+
this.parts.push(`merge into ${this.getTableName()}`);
|
|
165
|
+
this.parts.push(`using (${parts.join(' union all ')}) tsource`);
|
|
166
|
+
/* v8 ignore next 4: RawQueryFragment conflict fields branch */
|
|
167
|
+
if (clause.fields instanceof RawQueryFragment) {
|
|
168
|
+
this.parts.push(clause.fields.sql);
|
|
169
|
+
this.params.push(...clause.fields.params);
|
|
170
|
+
}
|
|
171
|
+
else if (clause.fields.length > 0) {
|
|
172
|
+
const fields = clause.fields.map(field => {
|
|
173
|
+
const col = this.quote(field);
|
|
174
|
+
return `${this.getTableName()}.${col} = tsource.${col}`;
|
|
175
|
+
});
|
|
176
|
+
this.parts.push(`on (${fields.join(' and ')})`);
|
|
177
|
+
}
|
|
178
|
+
const sourceColumns = keys.map(field => `tsource.${this.quote(field)}`).join(', ');
|
|
179
|
+
const destinationColumns = keys.map(field => this.quote(field)).join(', ');
|
|
180
|
+
this.parts.push(`when not matched then insert (${destinationColumns}) values (${sourceColumns})`);
|
|
181
|
+
if (!clause.ignore) {
|
|
182
|
+
/* v8 ignore next: merge type branch */
|
|
183
|
+
if (!clause.merge || Array.isArray(clause.merge)) {
|
|
184
|
+
const mergeParts = (clause.merge || keys)
|
|
185
|
+
.filter(field => !Array.isArray(clause.fields) || !clause.fields.includes(field))
|
|
186
|
+
.filter((field) => keys.includes(field)) // only reference columns present in the source data
|
|
187
|
+
.map((column) => `${this.quote(column)} = tsource.${this.quote(column)}`);
|
|
188
|
+
/* v8 ignore next 10: empty mergeParts branch */
|
|
189
|
+
if (mergeParts.length > 0) {
|
|
190
|
+
this.parts.push('when matched');
|
|
191
|
+
if (clause.where) {
|
|
192
|
+
this.parts.push(`and ${clause.where.sql}`);
|
|
193
|
+
this.params.push(...clause.where.params);
|
|
194
|
+
}
|
|
195
|
+
this.parts.push('then update set');
|
|
196
|
+
this.parts.push(mergeParts.join(', '));
|
|
197
|
+
}
|
|
198
|
+
} /* v8 ignore start: object-form merge branch */
|
|
199
|
+
else if (typeof clause.merge === 'object') {
|
|
200
|
+
this.parts.push('when matched');
|
|
201
|
+
if (clause.where) {
|
|
202
|
+
this.parts.push(`and ${clause.where.sql}`);
|
|
203
|
+
this.params.push(...clause.where.params);
|
|
204
|
+
}
|
|
205
|
+
this.parts.push('then update set');
|
|
206
|
+
const parts = Object.entries(clause.merge).map(([key, value]) => {
|
|
207
|
+
this.params.push(value);
|
|
208
|
+
return `${this.getTableName()}.${this.quote(key)} = ?`;
|
|
209
|
+
});
|
|
210
|
+
this.parts.push(parts.join(', '));
|
|
211
|
+
}
|
|
212
|
+
/* v8 ignore stop */
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
compileSelect() {
|
|
216
|
+
this.parts.push('select');
|
|
217
|
+
this.addHintComment();
|
|
218
|
+
this.parts.push(`${this.getFields()} from ${this.getTableName()}`);
|
|
219
|
+
if (this.options.joins) {
|
|
220
|
+
for (const join of this.options.joins) {
|
|
221
|
+
this.parts.push(join.sql);
|
|
222
|
+
this.params.push(...join.params);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (this.options.where?.sql.trim()) {
|
|
226
|
+
this.parts.push(`where ${this.options.where.sql}`);
|
|
227
|
+
this.params.push(...this.options.where.params);
|
|
228
|
+
}
|
|
229
|
+
if (this.options.groupBy) {
|
|
230
|
+
const fields = this.options.groupBy.map(field => this.quote(field));
|
|
231
|
+
this.parts.push(`group by ${fields.join(', ')}`);
|
|
232
|
+
}
|
|
233
|
+
if (this.options.having) {
|
|
234
|
+
this.parts.push(`having ${this.options.having.sql}`);
|
|
235
|
+
this.params.push(...this.options.having.params);
|
|
236
|
+
}
|
|
237
|
+
if (this.options.orderBy) {
|
|
238
|
+
this.parts.push(`order by ${this.options.orderBy}`);
|
|
239
|
+
}
|
|
240
|
+
if (this.options.offset != null) {
|
|
241
|
+
this.parts.push(`offset ? rows`);
|
|
242
|
+
this.params.push(this.options.offset);
|
|
243
|
+
}
|
|
244
|
+
if (this.options.limit != null) {
|
|
245
|
+
this.parts.push(`fetch next ? rows only`);
|
|
246
|
+
this.params.push(this.options.limit);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -19,7 +19,7 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
|
|
|
19
19
|
return `set names '${charset}';\n\n`;
|
|
20
20
|
}
|
|
21
21
|
getCreateDatabaseSQL(name) {
|
|
22
|
-
return `create database ${name}`;
|
|
22
|
+
return `create database ${this.quote(name)}`;
|
|
23
23
|
}
|
|
24
24
|
getListTablesSQL() {
|
|
25
25
|
return (`select table_name, table_schema as schema_name, ` +
|
|
@@ -617,8 +617,8 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
|
|
|
617
617
|
appendComments(table) {
|
|
618
618
|
const sql = [];
|
|
619
619
|
if (table.comment) {
|
|
620
|
-
const comment = this.platform.quoteValue(table.comment)
|
|
621
|
-
sql.push(`comment on table ${table.getQuotedName()} is ${
|
|
620
|
+
const comment = this.platform.quoteValue(this.processComment(table.comment));
|
|
621
|
+
sql.push(`comment on table ${table.getQuotedName()} is ${comment}`);
|
|
622
622
|
}
|
|
623
623
|
for (const column of table.getColumns()) {
|
|
624
624
|
if (column.comment) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/sql",
|
|
3
|
-
"version": "7.0.0-dev.
|
|
3
|
+
"version": "7.0.0-dev.314",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"data-mapper",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@mikro-orm/core": "^6.6.8"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@mikro-orm/core": "7.0.0-dev.
|
|
56
|
+
"@mikro-orm/core": "7.0.0-dev.314"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
59
59
|
"node": ">= 22.17.0"
|
|
@@ -38,14 +38,14 @@ interface Options {
|
|
|
38
38
|
onConflict?: OnConflictClause;
|
|
39
39
|
lockMode?: LockMode;
|
|
40
40
|
lockTables?: string[];
|
|
41
|
-
returning?: (string | RawQueryFragment)[];
|
|
41
|
+
returning?: (string | RawQueryFragment | [name: string, type: unknown])[];
|
|
42
42
|
comment?: string[];
|
|
43
43
|
hintComment?: string[];
|
|
44
44
|
flags?: Set<QueryFlag>;
|
|
45
45
|
wrap?: [prefix: string, suffix: string];
|
|
46
46
|
ctes?: CteClause[];
|
|
47
47
|
}
|
|
48
|
-
interface TableOptions {
|
|
48
|
+
export interface TableOptions {
|
|
49
49
|
schema?: string;
|
|
50
50
|
indexHint?: string;
|
|
51
51
|
alias?: string;
|
|
@@ -108,7 +108,7 @@ export declare class NativeQueryBuilder implements Subquery {
|
|
|
108
108
|
distinct(): this;
|
|
109
109
|
distinctOn(fields: string[]): this;
|
|
110
110
|
onConflict(options: OnConflictClause): OnConflictClause;
|
|
111
|
-
returning(fields: (string | RawQueryFragment)[]): this;
|
|
111
|
+
returning(fields: (string | RawQueryFragment | [name: string, type: unknown])[]): this;
|
|
112
112
|
lockMode(lockMode: LockMode, lockTables?: string[]): this;
|
|
113
113
|
comment(comment: string | string[]): this;
|
|
114
114
|
hintComment(comment: string | string[]): this;
|
|
@@ -30,7 +30,8 @@ export class NativeQueryBuilder {
|
|
|
30
30
|
tableName = tableName.toRaw();
|
|
31
31
|
}
|
|
32
32
|
if (typeof tableName === 'string') {
|
|
33
|
-
const
|
|
33
|
+
const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
|
|
34
|
+
const alias = options?.alias ? `${asKeyword}${this.platform.quoteIdentifier(options.alias)}` : '';
|
|
34
35
|
const schema = options?.schema && options.schema !== this.platform.getDefaultSchemaName() ? `${options.schema}.` : '';
|
|
35
36
|
tableName = this.quote(schema + tableName) + alias;
|
|
36
37
|
}
|
|
@@ -474,7 +475,8 @@ export class NativeQueryBuilder {
|
|
|
474
475
|
const parts = id.split(/ as /i);
|
|
475
476
|
const a = this.platform.quoteIdentifier(parts[0]);
|
|
476
477
|
const b = this.platform.quoteIdentifier(parts[1]);
|
|
477
|
-
|
|
478
|
+
const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
|
|
479
|
+
return `${a}${asKeyword}${b}`;
|
|
478
480
|
}
|
|
479
481
|
if (id === '*') {
|
|
480
482
|
return id;
|
package/query/QueryBuilder.d.ts
CHANGED
|
@@ -682,6 +682,7 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
682
682
|
*/
|
|
683
683
|
from<Name extends string & keyof CTEs, Alias extends string = Name>(target: Name, aliasName?: Alias): SelectQueryBuilder<CTEs[Name], Alias, never, never, never, '*', CTEs>;
|
|
684
684
|
getNativeQuery(processVirtualEntity?: boolean): NativeQueryBuilder;
|
|
685
|
+
protected processReturningStatement(qb: NativeQueryBuilder, meta?: EntityMetadata, data?: Dictionary, returning?: Field<any>[]): void;
|
|
685
686
|
/**
|
|
686
687
|
* Returns the query with parameters as wildcards.
|
|
687
688
|
*/
|
|
@@ -877,7 +878,7 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
877
878
|
* For embeddeds: navigates into flattened embeddeds to return the correct field name.
|
|
878
879
|
*/
|
|
879
880
|
protected resolveNestedPath(field: string): string | string[];
|
|
880
|
-
|
|
881
|
+
protected init(type: QueryType, data?: any, cond?: any): this;
|
|
881
882
|
private getQueryBase;
|
|
882
883
|
private applyDiscriminatorCondition;
|
|
883
884
|
/**
|
package/query/QueryBuilder.js
CHANGED
|
@@ -775,9 +775,44 @@ export class QueryBuilder {
|
|
|
775
775
|
if (this.lockMode) {
|
|
776
776
|
this.helper.getLockSQL(qb, this.lockMode, this.lockTables, this._joins);
|
|
777
777
|
}
|
|
778
|
-
this.
|
|
778
|
+
this.processReturningStatement(qb, this.mainAlias.meta, this._data, this._returning);
|
|
779
779
|
return (this._query.qb = qb);
|
|
780
780
|
}
|
|
781
|
+
processReturningStatement(qb, meta, data, returning) {
|
|
782
|
+
const usesReturningStatement = this.platform.usesReturningStatement() || this.platform.usesOutputStatement();
|
|
783
|
+
if (!meta || !data || !usesReturningStatement) {
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
// always respect explicit returning hint
|
|
787
|
+
if (returning && returning.length > 0) {
|
|
788
|
+
qb.returning(returning.map(field => this.helper.mapper(field, this.type)));
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
if (this.type === QueryType.INSERT) {
|
|
792
|
+
const returningProps = meta.hydrateProps
|
|
793
|
+
.filter(prop => prop.returning || (prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw)))
|
|
794
|
+
.filter(prop => !(prop.name in data));
|
|
795
|
+
if (returningProps.length > 0) {
|
|
796
|
+
qb.returning(Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
797
|
+
}
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
if (this.type === QueryType.UPDATE) {
|
|
801
|
+
const returningProps = meta.hydrateProps.filter(prop => prop.fieldNames && isRaw(data[prop.fieldNames[0]]));
|
|
802
|
+
if (returningProps.length > 0) {
|
|
803
|
+
qb.returning(returningProps.flatMap((prop) => {
|
|
804
|
+
if (prop.hasConvertToJSValueSQL) {
|
|
805
|
+
const aliased = this.platform.quoteIdentifier(prop.fieldNames[0]);
|
|
806
|
+
const sql = prop.customType.convertToJSValueSQL(aliased, this.platform) +
|
|
807
|
+
' as ' +
|
|
808
|
+
this.platform.quoteIdentifier(prop.fieldNames[0]);
|
|
809
|
+
return [raw(sql)];
|
|
810
|
+
}
|
|
811
|
+
return prop.fieldNames;
|
|
812
|
+
}));
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
781
816
|
/**
|
|
782
817
|
* Returns the query with parameters as wildcards.
|
|
783
818
|
*/
|
|
@@ -54,7 +54,6 @@ export declare class QueryBuilderHelper {
|
|
|
54
54
|
validateQueryOrder<T>(orderBy: QueryOrderMap<T>): void;
|
|
55
55
|
getQueryOrder(type: QueryType, orderBy: FlatQueryOrderMap | FlatQueryOrderMap[], populate: Dictionary<string>, collation?: string): string[];
|
|
56
56
|
getQueryOrderFromObject(type: QueryType, orderBy: FlatQueryOrderMap, populate: Dictionary<string>, collation?: string): string[];
|
|
57
|
-
finalize(type: QueryType, qb: NativeQueryBuilder, meta?: EntityMetadata, data?: Dictionary, returning?: InternalField<any>[]): void;
|
|
58
57
|
splitField<T>(field: EntityKey<T>, greedyAlias?: boolean): [string, EntityKey<T>, string | undefined];
|
|
59
58
|
getLockSQL(qb: NativeQueryBuilder, lockMode: LockMode, lockTables?: string[], joinsMap?: Dictionary<JoinOptions>): void;
|
|
60
59
|
updateVersionProperty(qb: NativeQueryBuilder, data: Dictionary): void;
|
|
@@ -287,7 +287,8 @@ export class QueryBuilderHelper {
|
|
|
287
287
|
}
|
|
288
288
|
let sql = method + ' ';
|
|
289
289
|
if (join.nested) {
|
|
290
|
-
|
|
290
|
+
const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
|
|
291
|
+
sql += `(${this.platform.quoteIdentifier(table)}${asKeyword}${this.platform.quoteIdentifier(join.alias)}`;
|
|
291
292
|
for (const nested of join.nested) {
|
|
292
293
|
const { sql: nestedSql, params: nestedParams } = this.createJoinExpression(nested, joins, schema);
|
|
293
294
|
sql += ' ' + nestedSql;
|
|
@@ -296,10 +297,14 @@ export class QueryBuilderHelper {
|
|
|
296
297
|
sql += `)`;
|
|
297
298
|
}
|
|
298
299
|
else if (join.subquery) {
|
|
299
|
-
|
|
300
|
+
const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
|
|
301
|
+
sql += `(${join.subquery})${asKeyword}${this.platform.quoteIdentifier(join.alias)}`;
|
|
300
302
|
}
|
|
301
303
|
else {
|
|
302
|
-
sql +=
|
|
304
|
+
sql +=
|
|
305
|
+
this.platform.quoteIdentifier(table) +
|
|
306
|
+
(this.platform.usesAsKeyword() ? ' as ' : ' ') +
|
|
307
|
+
this.platform.quoteIdentifier(join.alias);
|
|
303
308
|
}
|
|
304
309
|
const oldAlias = this.alias;
|
|
305
310
|
this.alias = join.alias;
|
|
@@ -536,6 +541,12 @@ export class QueryBuilderHelper {
|
|
|
536
541
|
else if (['$in', '$nin'].includes(op) && Array.isArray(value[op]) && value[op].length === 0) {
|
|
537
542
|
parts.push(`1 = ${op === '$in' ? 0 : 1}`);
|
|
538
543
|
}
|
|
544
|
+
else if (op === '$re') {
|
|
545
|
+
const mappedKey = this.mapper(key, type, value[op], null);
|
|
546
|
+
const processed = this.platform.mapRegExpCondition(mappedKey, value);
|
|
547
|
+
parts.push(processed.sql);
|
|
548
|
+
params.push(...processed.params);
|
|
549
|
+
}
|
|
539
550
|
else if (value[op] instanceof Raw || typeof value[op]?.toRaw === 'function') {
|
|
540
551
|
const query = value[op] instanceof Raw ? value[op] : value[op].toRaw();
|
|
541
552
|
const mappedKey = this.mapper(key, type, query, null);
|
|
@@ -669,42 +680,6 @@ export class QueryBuilderHelper {
|
|
|
669
680
|
}
|
|
670
681
|
return ret;
|
|
671
682
|
}
|
|
672
|
-
finalize(type, qb, meta, data, returning) {
|
|
673
|
-
const usesReturningStatement = this.platform.usesReturningStatement() || this.platform.usesOutputStatement();
|
|
674
|
-
if (!meta || !data || !usesReturningStatement) {
|
|
675
|
-
return;
|
|
676
|
-
}
|
|
677
|
-
// always respect explicit returning hint
|
|
678
|
-
if (returning && returning.length > 0) {
|
|
679
|
-
qb.returning(returning.map(field => this.mapper(field, type)));
|
|
680
|
-
return;
|
|
681
|
-
}
|
|
682
|
-
if (type === QueryType.INSERT) {
|
|
683
|
-
const returningProps = meta.hydrateProps
|
|
684
|
-
.filter(prop => prop.returning || (prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw)))
|
|
685
|
-
.filter(prop => !(prop.name in data));
|
|
686
|
-
if (returningProps.length > 0) {
|
|
687
|
-
qb.returning(Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
688
|
-
}
|
|
689
|
-
return;
|
|
690
|
-
}
|
|
691
|
-
if (type === QueryType.UPDATE) {
|
|
692
|
-
const returningProps = meta.hydrateProps.filter(prop => prop.fieldNames && isRaw(data[prop.fieldNames[0]]));
|
|
693
|
-
if (returningProps.length > 0) {
|
|
694
|
-
const fields = returningProps.flatMap((prop) => {
|
|
695
|
-
if (prop.hasConvertToJSValueSQL) {
|
|
696
|
-
const aliased = this.platform.quoteIdentifier(prop.fieldNames[0]);
|
|
697
|
-
const sql = prop.customType.convertToJSValueSQL(aliased, this.platform) +
|
|
698
|
-
' as ' +
|
|
699
|
-
this.platform.quoteIdentifier(prop.fieldNames[0]);
|
|
700
|
-
return [raw(sql)];
|
|
701
|
-
}
|
|
702
|
-
return prop.fieldNames;
|
|
703
|
-
});
|
|
704
|
-
qb.returning(fields);
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
683
|
splitField(field, greedyAlias = false) {
|
|
709
684
|
const parts = field.split('.');
|
|
710
685
|
const ref = parts[parts.length - 1].split(':')[1];
|
|
@@ -429,7 +429,9 @@ export class SchemaComparator {
|
|
|
429
429
|
return (key[method] ?? defaultRule[0]).toLowerCase().replace(defaultRule[1], defaultRule[0]).replace(/"/g, '');
|
|
430
430
|
};
|
|
431
431
|
const compare = (method) => rule(key1, method) === rule(key2, method);
|
|
432
|
-
|
|
432
|
+
// Skip updateRule comparison for platforms that don't support ON UPDATE (e.g., Oracle)
|
|
433
|
+
const updateRuleDiffers = this.platform.supportsOnUpdate() && !compare('updateRule');
|
|
434
|
+
return updateRuleDiffers || !compare('deleteRule');
|
|
433
435
|
}
|
|
434
436
|
/**
|
|
435
437
|
* Returns the difference between the columns
|
package/schema/SchemaHelper.d.ts
CHANGED
|
@@ -82,6 +82,7 @@ export declare abstract class SchemaHelper {
|
|
|
82
82
|
managementDbName?: string;
|
|
83
83
|
defaultUpdateRule?: "cascade" | "no action" | "set null" | "set default" | "restrict";
|
|
84
84
|
defaultDeleteRule?: "cascade" | "no action" | "set null" | "set default" | "restrict";
|
|
85
|
+
tableSpace?: string;
|
|
85
86
|
};
|
|
86
87
|
protected processComment(comment: string): string;
|
|
87
88
|
protected quote(...keys: (string | undefined)[]): string;
|
package/schema/SchemaHelper.js
CHANGED
|
@@ -416,6 +416,7 @@ export class SchemaHelper {
|
|
|
416
416
|
return norm[0].replace('(?)', length != null ? `(${length})` : '');
|
|
417
417
|
}
|
|
418
418
|
getCreateDatabaseSQL(name) {
|
|
419
|
+
name = this.quote(name);
|
|
419
420
|
// two line breaks to force separate execution
|
|
420
421
|
return `create database ${name};\n\nuse ${name}`;
|
|
421
422
|
}
|
|
@@ -16,6 +16,7 @@ export declare class SqlSchemaGenerator extends AbstractSchemaGenerator<Abstract
|
|
|
16
16
|
managementDbName?: string;
|
|
17
17
|
defaultUpdateRule?: "cascade" | "no action" | "set null" | "set default" | "restrict";
|
|
18
18
|
defaultDeleteRule?: "cascade" | "no action" | "set null" | "set default" | "restrict";
|
|
19
|
+
tableSpace?: string;
|
|
19
20
|
};
|
|
20
21
|
protected lastEnsuredDatabase?: string;
|
|
21
22
|
static register(orm: MikroORM): void;
|
|
@@ -372,7 +372,7 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator {
|
|
|
372
372
|
*/
|
|
373
373
|
async createDatabase(name, options) {
|
|
374
374
|
name ??= this.config.get('dbName');
|
|
375
|
-
const sql = this.helper.getCreateDatabaseSQL(
|
|
375
|
+
const sql = this.helper.getCreateDatabaseSQL(name);
|
|
376
376
|
if (sql) {
|
|
377
377
|
await this.execute(sql);
|
|
378
378
|
}
|