@mikro-orm/sql 7.0.0-dev.311 → 7.0.0-dev.313

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.
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ export * from './OracleDialect.js';
2
+ export * from './OracleNativeQueryBuilder.js';
@@ -0,0 +1,2 @@
1
+ export * from './OracleDialect.js';
2
+ export * from './OracleNativeQueryBuilder.js';
@@ -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).replace(/^'|'$/g, '');
621
- sql.push(`comment on table ${table.getQuotedName()} is ${this.platform.quoteValue(this.processComment(comment))}`);
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.311",
3
+ "version": "7.0.0-dev.313",
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.311"
56
+ "@mikro-orm/core": "7.0.0-dev.313"
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 alias = options?.alias ? ` as ${this.platform.quoteIdentifier(options.alias)}` : '';
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
- return `${a} as ${b}`;
478
+ const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
479
+ return `${a}${asKeyword}${b}`;
478
480
  }
479
481
  if (id === '*') {
480
482
  return id;
@@ -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
- private init;
881
+ protected init(type: QueryType, data?: any, cond?: any): this;
881
882
  private getQueryBase;
882
883
  private applyDiscriminatorCondition;
883
884
  /**
@@ -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.helper.finalize(this.type, qb, this.mainAlias.meta, this._data, this._returning);
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
- sql += `(${this.platform.quoteIdentifier(table)} as ${this.platform.quoteIdentifier(join.alias)}`;
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
- sql += `(${join.subquery}) as ${this.platform.quoteIdentifier(join.alias)}`;
300
+ const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
301
+ sql += `(${join.subquery})${asKeyword}${this.platform.quoteIdentifier(join.alias)}`;
300
302
  }
301
303
  else {
302
- sql += `${this.platform.quoteIdentifier(table)} as ${this.platform.quoteIdentifier(join.alias)}`;
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
- return !compare('updateRule') || !compare('deleteRule');
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
@@ -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;
@@ -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('' + this.platform.quoteIdentifier(name));
375
+ const sql = this.helper.getCreateDatabaseSQL(name);
376
376
  if (sql) {
377
377
  await this.execute(sql);
378
378
  }