@mikro-orm/sql 7.0.7-dev.9 → 7.0.8-dev.0

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.
@@ -900,10 +900,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
900
900
  sql = sql.substring(0, sql.length - 2) + ' where ';
901
901
  const pkProps = meta.primaryKeys.concat(...meta.concurrencyCheckKeys);
902
902
  const pks = Utils.flatten(pkProps.map(pk => meta.properties[pk].fieldNames));
903
- sql +=
904
- pks.length > 1
905
- ? `(${pks.map(pk => this.platform.quoteIdentifier(pk)).join(', ')})`
906
- : this.platform.quoteIdentifier(pks[0]);
903
+ const useTupleIn = pks.length <= 1 || this.platform.allowsComparingTuples();
904
+ const condTemplate = useTupleIn
905
+ ? `(${pks.map(() => '?').join(', ')})`
906
+ : `(${pks.map(pk => `${this.platform.quoteIdentifier(pk)} = ?`).join(' and ')})`;
907
907
  const conds = where.map(cond => {
908
908
  if (Utils.isPlainObject(cond) && Utils.getObjectKeysSize(cond) === 1) {
909
909
  cond = Object.values(cond)[0];
@@ -917,12 +917,21 @@ export class AbstractSqlDriver extends DatabaseDriver {
917
917
  params.push(cond[pk]);
918
918
  }
919
919
  });
920
- return `(${Array.from({ length: pks.length }).fill('?').join(', ')})`;
920
+ return condTemplate;
921
921
  }
922
922
  params.push(cond);
923
923
  return '?';
924
924
  });
925
- sql += ` in (${conds.join(', ')})`;
925
+ if (useTupleIn) {
926
+ sql +=
927
+ pks.length > 1
928
+ ? `(${pks.map(pk => this.platform.quoteIdentifier(pk)).join(', ')})`
929
+ : this.platform.quoteIdentifier(pks[0]);
930
+ sql += ` in (${conds.join(', ')})`;
931
+ }
932
+ else {
933
+ sql += conds.join(' or ');
934
+ }
926
935
  if (this.platform.usesReturningStatement() && returning.size > 0) {
927
936
  const returningFields = Utils.flatten([...returning].map(prop => meta.properties[prop].fieldNames));
928
937
  /* v8 ignore next */
@@ -1,4 +1,4 @@
1
- import { type Dictionary, type Type } from '@mikro-orm/core';
1
+ import { type Dictionary, type Transaction, type Type } from '@mikro-orm/core';
2
2
  import type { CheckDef, Column, ForeignKey, IndexDef, Table, TableDifference } from '../../typings.js';
3
3
  import type { AbstractSqlConnection } from '../../AbstractSqlConnection.js';
4
4
  import { SchemaHelper } from '../../schema/SchemaHelper.js';
@@ -17,9 +17,9 @@ export declare class MySqlSchemaHelper extends SchemaHelper {
17
17
  finalizeTable(table: DatabaseTable, charset: string, collate?: string): string;
18
18
  getListTablesSQL(): string;
19
19
  getListViewsSQL(): string;
20
- loadViews(schema: DatabaseSchema, connection: AbstractSqlConnection, schemaName?: string): Promise<void>;
21
- loadInformationSchema(schema: DatabaseSchema, connection: AbstractSqlConnection, tables: Table[]): Promise<void>;
22
- getAllIndexes(connection: AbstractSqlConnection, tables: Table[]): Promise<Dictionary<IndexDef[]>>;
20
+ loadViews(schema: DatabaseSchema, connection: AbstractSqlConnection, schemaName?: string, ctx?: Transaction): Promise<void>;
21
+ loadInformationSchema(schema: DatabaseSchema, connection: AbstractSqlConnection, tables: Table[], schemas?: string[], ctx?: Transaction): Promise<void>;
22
+ getAllIndexes(connection: AbstractSqlConnection, tables: Table[], ctx?: Transaction): Promise<Dictionary<IndexDef[]>>;
23
23
  getCreateIndexSQL(tableName: string, index: IndexDef, partialExpression?: boolean): string;
24
24
  /**
25
25
  * Build the column list for a MySQL index, with MySQL-specific handling for collation.
@@ -30,16 +30,16 @@ export declare class MySqlSchemaHelper extends SchemaHelper {
30
30
  * Append MySQL-specific index suffixes like INVISIBLE.
31
31
  */
32
32
  protected appendMySqlIndexSuffix(sql: string, index: IndexDef): string;
33
- getAllColumns(connection: AbstractSqlConnection, tables: Table[]): Promise<Dictionary<Column[]>>;
34
- getAllChecks(connection: AbstractSqlConnection, tables: Table[]): Promise<Dictionary<CheckDef[]>>;
35
- getAllForeignKeys(connection: AbstractSqlConnection, tables: Table[]): Promise<Dictionary<Dictionary<ForeignKey>>>;
33
+ getAllColumns(connection: AbstractSqlConnection, tables: Table[], ctx?: Transaction): Promise<Dictionary<Column[]>>;
34
+ getAllChecks(connection: AbstractSqlConnection, tables: Table[], ctx?: Transaction): Promise<Dictionary<CheckDef[]>>;
35
+ getAllForeignKeys(connection: AbstractSqlConnection, tables: Table[], ctx?: Transaction): Promise<Dictionary<Dictionary<ForeignKey>>>;
36
36
  getPreAlterTable(tableDiff: TableDifference, safe: boolean): string[];
37
37
  getRenameColumnSQL(tableName: string, oldColumnName: string, to: Column): string;
38
38
  getRenameIndexSQL(tableName: string, index: IndexDef, oldIndexName: string): string[];
39
39
  getChangeColumnCommentSQL(tableName: string, to: Column, schemaName?: string): string;
40
40
  alterTableColumn(column: Column, table: DatabaseTable, changedProperties: Set<string>): string[];
41
41
  private getColumnDeclarationSQL;
42
- getAllEnumDefinitions(connection: AbstractSqlConnection, tables: Table[]): Promise<Dictionary<Dictionary<string[]>>>;
42
+ getAllEnumDefinitions(connection: AbstractSqlConnection, tables: Table[], ctx?: Transaction): Promise<Dictionary<Dictionary<string[]>>>;
43
43
  private supportsCheckConstraints;
44
44
  protected getChecksSQL(tables: Table[]): string;
45
45
  normalizeDefaultValue(defaultValue: string, length: number): string | number;
@@ -36,14 +36,14 @@ export class MySqlSchemaHelper extends SchemaHelper {
36
36
  getListViewsSQL() {
37
37
  return `select table_name as view_name, nullif(table_schema, schema()) as schema_name, view_definition from information_schema.views where table_schema = schema()`;
38
38
  }
39
- async loadViews(schema, connection, schemaName) {
40
- const views = await connection.execute(this.getListViewsSQL());
39
+ async loadViews(schema, connection, schemaName, ctx) {
40
+ const views = await connection.execute(this.getListViewsSQL(), [], 'all', ctx);
41
41
  for (const view of views) {
42
42
  // MySQL information_schema.views.view_definition requires SHOW VIEW privilege
43
43
  // and may return NULL. Use SHOW CREATE VIEW as fallback.
44
44
  let definition = view.view_definition?.trim();
45
45
  if (!definition) {
46
- const createView = await connection.execute(`show create view \`${view.view_name}\``);
46
+ const createView = await connection.execute(`show create view \`${view.view_name}\``, [], 'all', ctx);
47
47
  if (createView[0]?.['Create View']) {
48
48
  // Extract SELECT statement from CREATE VIEW ... AS SELECT ...
49
49
  const match = /\bAS\s+(.+)$/is.exec(createView[0]['Create View']);
@@ -55,15 +55,15 @@ export class MySqlSchemaHelper extends SchemaHelper {
55
55
  }
56
56
  }
57
57
  }
58
- async loadInformationSchema(schema, connection, tables) {
58
+ async loadInformationSchema(schema, connection, tables, schemas, ctx) {
59
59
  if (tables.length === 0) {
60
60
  return;
61
61
  }
62
- const columns = await this.getAllColumns(connection, tables);
63
- const indexes = await this.getAllIndexes(connection, tables);
64
- const checks = await this.getAllChecks(connection, tables);
65
- const fks = await this.getAllForeignKeys(connection, tables);
66
- const enums = await this.getAllEnumDefinitions(connection, tables);
62
+ const columns = await this.getAllColumns(connection, tables, ctx);
63
+ const indexes = await this.getAllIndexes(connection, tables, ctx);
64
+ const checks = await this.getAllChecks(connection, tables, ctx);
65
+ const fks = await this.getAllForeignKeys(connection, tables, ctx);
66
+ const enums = await this.getAllEnumDefinitions(connection, tables, ctx);
67
67
  for (const t of tables) {
68
68
  const key = this.getTableKey(t);
69
69
  const table = schema.addTable(t.table_name, t.schema_name, t.table_comment);
@@ -71,12 +71,12 @@ export class MySqlSchemaHelper extends SchemaHelper {
71
71
  table.init(columns[key], indexes[key], checks[key], pks, fks[key], enums[key]);
72
72
  }
73
73
  }
74
- async getAllIndexes(connection, tables) {
74
+ async getAllIndexes(connection, tables, ctx) {
75
75
  const sql = `select table_name as table_name, nullif(table_schema, schema()) as schema_name, index_name as index_name, non_unique as non_unique, column_name as column_name, index_type as index_type, sub_part as sub_part, collation as sort_order /*!80013 , expression as expression, is_visible as is_visible */
76
76
  from information_schema.statistics where table_schema = database()
77
77
  and table_name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(', ')})
78
78
  order by schema_name, table_name, index_name, seq_in_index`;
79
- const allIndexes = await connection.execute(sql);
79
+ const allIndexes = await connection.execute(sql, [], 'all', ctx);
80
80
  const ret = {};
81
81
  for (const index of allIndexes) {
82
82
  const key = this.getTableKey(index);
@@ -191,7 +191,7 @@ export class MySqlSchemaHelper extends SchemaHelper {
191
191
  }
192
192
  return sql;
193
193
  }
194
- async getAllColumns(connection, tables) {
194
+ async getAllColumns(connection, tables, ctx) {
195
195
  const sql = `select table_name as table_name,
196
196
  nullif(table_schema, schema()) as schema_name,
197
197
  column_name as column_name,
@@ -208,7 +208,7 @@ export class MySqlSchemaHelper extends SchemaHelper {
208
208
  ifnull(datetime_precision, character_maximum_length) length
209
209
  from information_schema.columns where table_schema = database() and table_name in (${tables.map(t => this.platform.quoteValue(t.table_name))})
210
210
  order by ordinal_position`;
211
- const allColumns = await connection.execute(sql);
211
+ const allColumns = await connection.execute(sql, [], 'all', ctx);
212
212
  const str = (val) => (val != null ? '' + val : val);
213
213
  const extra = (val) => val.replace(/auto_increment|default_generated|(stored|virtual) generated/i, '').trim() || undefined;
214
214
  const ret = {};
@@ -244,13 +244,13 @@ export class MySqlSchemaHelper extends SchemaHelper {
244
244
  }
245
245
  return ret;
246
246
  }
247
- async getAllChecks(connection, tables) {
247
+ async getAllChecks(connection, tables, ctx) {
248
248
  /* v8 ignore next */
249
- if (!(await this.supportsCheckConstraints(connection))) {
249
+ if (!(await this.supportsCheckConstraints(connection, ctx))) {
250
250
  return {};
251
251
  }
252
252
  const sql = this.getChecksSQL(tables);
253
- const allChecks = await connection.execute(sql);
253
+ const allChecks = await connection.execute(sql, [], 'all', ctx);
254
254
  const ret = {};
255
255
  for (const check of allChecks) {
256
256
  const key = this.getTableKey(check);
@@ -264,14 +264,14 @@ export class MySqlSchemaHelper extends SchemaHelper {
264
264
  }
265
265
  return ret;
266
266
  }
267
- async getAllForeignKeys(connection, tables) {
267
+ async getAllForeignKeys(connection, tables, ctx) {
268
268
  const sql = `select k.constraint_name as constraint_name, nullif(k.table_schema, schema()) as schema_name, k.table_name as table_name, k.column_name as column_name, k.referenced_table_name as referenced_table_name, k.referenced_column_name as referenced_column_name, c.update_rule as update_rule, c.delete_rule as delete_rule
269
269
  from information_schema.key_column_usage k
270
270
  inner join information_schema.referential_constraints c on c.constraint_name = k.constraint_name and c.table_name = k.table_name
271
271
  where k.table_name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(', ')})
272
272
  and k.table_schema = database() and c.constraint_schema = database() and k.referenced_column_name is not null
273
273
  order by constraint_name, k.ordinal_position`;
274
- const allFks = await connection.execute(sql);
274
+ const allFks = await connection.execute(sql, [], 'all', ctx);
275
275
  const ret = {};
276
276
  for (const fk of allFks) {
277
277
  const key = this.getTableKey(fk);
@@ -329,11 +329,11 @@ export class MySqlSchemaHelper extends SchemaHelper {
329
329
  ret += col.comment ? ` comment ${this.platform.quoteValue(col.comment)}` : '';
330
330
  return ret;
331
331
  }
332
- async getAllEnumDefinitions(connection, tables) {
332
+ async getAllEnumDefinitions(connection, tables, ctx) {
333
333
  const sql = `select column_name as column_name, column_type as column_type, table_name as table_name
334
334
  from information_schema.columns
335
335
  where data_type = 'enum' and table_name in (${tables.map(t => `'${t.table_name}'`).join(', ')}) and table_schema = database()`;
336
- const enums = await connection.execute(sql);
336
+ const enums = await connection.execute(sql, [], 'all', ctx);
337
337
  return enums.reduce((o, item) => {
338
338
  o[item.table_name] ??= {};
339
339
  o[item.table_name][item.column_name] = item.column_type
@@ -343,12 +343,12 @@ export class MySqlSchemaHelper extends SchemaHelper {
343
343
  return o;
344
344
  }, {});
345
345
  }
346
- async supportsCheckConstraints(connection) {
346
+ async supportsCheckConstraints(connection, ctx) {
347
347
  if (this.#cache.supportsCheckConstraints != null) {
348
348
  return this.#cache.supportsCheckConstraints;
349
349
  }
350
350
  const sql = `select 1 from information_schema.tables where table_name = 'CHECK_CONSTRAINTS' and table_schema = 'information_schema'`;
351
- const res = await connection.execute(sql);
351
+ const res = await connection.execute(sql, [], 'all', ctx);
352
352
  return (this.#cache.supportsCheckConstraints = res.length > 0);
353
353
  }
354
354
  getChecksSQL(tables) {
@@ -1,4 +1,4 @@
1
- import { type Dictionary } from '@mikro-orm/core';
1
+ import { type Dictionary, type Transaction } from '@mikro-orm/core';
2
2
  import { SchemaHelper } from '../../schema/SchemaHelper.js';
3
3
  import type { AbstractSqlConnection } from '../../AbstractSqlConnection.js';
4
4
  import type { CheckDef, Column, ForeignKey, IndexDef, Table, TableDifference } from '../../typings.js';
@@ -19,16 +19,16 @@ export declare class PostgreSqlSchemaHelper extends SchemaHelper {
19
19
  getListTablesSQL(): string;
20
20
  private getIgnoredViewsCondition;
21
21
  getListViewsSQL(): string;
22
- loadViews(schema: DatabaseSchema, connection: AbstractSqlConnection): Promise<void>;
22
+ loadViews(schema: DatabaseSchema, connection: AbstractSqlConnection, schemaName?: string, ctx?: Transaction): Promise<void>;
23
23
  getListMaterializedViewsSQL(): string;
24
- loadMaterializedViews(schema: DatabaseSchema, connection: AbstractSqlConnection, schemaName?: string): Promise<void>;
24
+ loadMaterializedViews(schema: DatabaseSchema, connection: AbstractSqlConnection, schemaName?: string, ctx?: Transaction): Promise<void>;
25
25
  createMaterializedView(name: string, schema: string | undefined, definition: string, withData?: boolean): string;
26
26
  dropMaterializedViewIfExists(name: string, schema?: string): string;
27
27
  refreshMaterializedView(name: string, schema?: string, concurrently?: boolean): string;
28
- getNamespaces(connection: AbstractSqlConnection): Promise<string[]>;
28
+ getNamespaces(connection: AbstractSqlConnection, ctx?: Transaction): Promise<string[]>;
29
29
  private getIgnoredNamespacesConditionSQL;
30
- loadInformationSchema(schema: DatabaseSchema, connection: AbstractSqlConnection, tables: Table[], schemas?: string[]): Promise<void>;
31
- getAllIndexes(connection: AbstractSqlConnection, tables: Table[]): Promise<Dictionary<IndexDef[]>>;
30
+ loadInformationSchema(schema: DatabaseSchema, connection: AbstractSqlConnection, tables: Table[], schemas?: string[], ctx?: Transaction): Promise<void>;
31
+ getAllIndexes(connection: AbstractSqlConnection, tables: Table[], ctx?: Transaction): Promise<Dictionary<IndexDef[]>>;
32
32
  /**
33
33
  * Parses column definitions from the full CREATE INDEX expression.
34
34
  * Since pg_get_indexdef(oid, col_num, true) doesn't include sort modifiers,
@@ -47,10 +47,10 @@ export declare class PostgreSqlSchemaHelper extends SchemaHelper {
47
47
  name: string;
48
48
  schema?: string;
49
49
  items: string[];
50
- }>): Promise<Dictionary<Column[]>>;
51
- getAllChecks(connection: AbstractSqlConnection, tablesBySchemas: Map<string | undefined, Table[]>): Promise<Dictionary<CheckDef[]>>;
52
- getAllForeignKeys(connection: AbstractSqlConnection, tablesBySchemas: Map<string | undefined, Table[]>): Promise<Dictionary<Dictionary<ForeignKey>>>;
53
- getNativeEnumDefinitions(connection: AbstractSqlConnection, schemas: string[]): Promise<Dictionary<{
50
+ }>, ctx?: Transaction): Promise<Dictionary<Column[]>>;
51
+ getAllChecks(connection: AbstractSqlConnection, tablesBySchemas: Map<string | undefined, Table[]>, ctx?: Transaction): Promise<Dictionary<CheckDef[]>>;
52
+ getAllForeignKeys(connection: AbstractSqlConnection, tablesBySchemas: Map<string | undefined, Table[]>, ctx?: Transaction): Promise<Dictionary<Dictionary<ForeignKey>>>;
53
+ getNativeEnumDefinitions(connection: AbstractSqlConnection, schemas: string[], ctx?: Transaction): Promise<Dictionary<{
54
54
  name: string;
55
55
  schema?: string;
56
56
  items: string[];
@@ -41,8 +41,8 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
41
41
  `and ${this.getIgnoredViewsCondition()} ` +
42
42
  `order by table_name`);
43
43
  }
44
- async loadViews(schema, connection) {
45
- const views = await connection.execute(this.getListViewsSQL());
44
+ async loadViews(schema, connection, schemaName, ctx) {
45
+ const views = await connection.execute(this.getListViewsSQL(), [], 'all', ctx);
46
46
  for (const view of views) {
47
47
  const definition = view.view_definition?.trim().replace(/;$/, '') ?? '';
48
48
  if (definition) {
@@ -51,17 +51,26 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
51
51
  }
52
52
  }
53
53
  getListMaterializedViewsSQL() {
54
- return (`select matviewname as view_name, schemaname as schema_name, definition as view_definition ` +
54
+ return (`select matviewname as view_name, schemaname as schema_name, definition as view_definition, ispopulated as is_populated ` +
55
55
  `from pg_matviews ` +
56
56
  `where ${this.getIgnoredNamespacesConditionSQL('schemaname')} ` +
57
57
  `order by matviewname`);
58
58
  }
59
- async loadMaterializedViews(schema, connection, schemaName) {
60
- const views = await connection.execute(this.getListMaterializedViewsSQL());
61
- for (const view of views) {
62
- const definition = view.view_definition?.trim().replace(/;$/, '') ?? '';
59
+ async loadMaterializedViews(schema, connection, schemaName, ctx) {
60
+ const views = await connection.execute(this.getListMaterializedViewsSQL(), [], 'all', ctx);
61
+ if (views.length === 0) {
62
+ return;
63
+ }
64
+ const tables = views.map(v => ({ table_name: v.view_name, schema_name: v.schema_name }));
65
+ const indexes = await this.getAllIndexes(connection, tables, ctx);
66
+ for (let i = 0; i < views.length; i++) {
67
+ const definition = views[i].view_definition?.trim().replace(/;$/, '') ?? '';
63
68
  if (definition) {
64
- schema.addView(view.view_name, view.schema_name, definition, true);
69
+ const dbView = schema.addView(views[i].view_name, views[i].schema_name, definition, true, views[i].is_populated);
70
+ const key = this.getTableKey(tables[i]);
71
+ if (indexes[key]?.length) {
72
+ dbView.indexes = indexes[key];
73
+ }
65
74
  }
66
75
  }
67
76
  }
@@ -77,11 +86,11 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
77
86
  const concurrent = concurrently ? ' concurrently' : '';
78
87
  return `refresh materialized view${concurrent} ${this.quote(this.getTableName(name, schema))}`;
79
88
  }
80
- async getNamespaces(connection) {
89
+ async getNamespaces(connection, ctx) {
81
90
  const sql = `select schema_name from information_schema.schemata ` +
82
91
  `where ${this.getIgnoredNamespacesConditionSQL()} ` +
83
92
  `order by schema_name`;
84
- const res = await connection.execute(sql);
93
+ const res = await connection.execute(sql, [], 'all', ctx);
85
94
  return res.map(row => row.schema_name);
86
95
  }
87
96
  getIgnoredNamespacesConditionSQL(column = 'schema_name') {
@@ -97,18 +106,18 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
97
106
  const ignoredPrefixes = ['pg_', 'crdb_', '_timescaledb_'].map(p => `"${column}" not like '${p}%'`).join(' and ');
98
107
  return `${ignoredPrefixes} and "${column}" not in (${ignored})`;
99
108
  }
100
- async loadInformationSchema(schema, connection, tables, schemas) {
109
+ async loadInformationSchema(schema, connection, tables, schemas, ctx) {
101
110
  schemas ??= tables.length === 0 ? [schema.name] : tables.map(t => t.schema_name);
102
- const nativeEnums = await this.getNativeEnumDefinitions(connection, schemas);
111
+ const nativeEnums = await this.getNativeEnumDefinitions(connection, schemas, ctx);
103
112
  schema.setNativeEnums(nativeEnums);
104
113
  if (tables.length === 0) {
105
114
  return;
106
115
  }
107
116
  const tablesBySchema = this.getTablesGroupedBySchemas(tables);
108
- const columns = await this.getAllColumns(connection, tablesBySchema, nativeEnums);
109
- const indexes = await this.getAllIndexes(connection, tables);
110
- const checks = await this.getAllChecks(connection, tablesBySchema);
111
- const fks = await this.getAllForeignKeys(connection, tablesBySchema);
117
+ const columns = await this.getAllColumns(connection, tablesBySchema, nativeEnums, ctx);
118
+ const indexes = await this.getAllIndexes(connection, tables, ctx);
119
+ const checks = await this.getAllChecks(connection, tablesBySchema, ctx);
120
+ const fks = await this.getAllForeignKeys(connection, tablesBySchema, ctx);
112
121
  for (const t of tables) {
113
122
  const key = this.getTableKey(t);
114
123
  const table = schema.addTable(t.table_name, t.schema_name, t.table_comment);
@@ -119,10 +128,10 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
119
128
  }
120
129
  }
121
130
  }
122
- async getAllIndexes(connection, tables) {
131
+ async getAllIndexes(connection, tables, ctx) {
123
132
  const sql = this.getIndexesSQL(tables);
124
133
  const unquote = (str) => str.replace(/['"`]/g, '');
125
- const allIndexes = await connection.execute(sql);
134
+ const allIndexes = await connection.execute(sql, [], 'all', ctx);
126
135
  const ret = {};
127
136
  for (const index of allIndexes) {
128
137
  const key = this.getTableKey(index);
@@ -243,7 +252,7 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
243
252
  /* v8 ignore next - pg_get_indexdef always returns balanced parentheses */
244
253
  return '';
245
254
  }
246
- async getAllColumns(connection, tablesBySchemas, nativeEnums) {
255
+ async getAllColumns(connection, tablesBySchemas, nativeEnums, ctx) {
247
256
  const sql = `select table_schema as schema_name, table_name, column_name,
248
257
  column_default,
249
258
  is_nullable,
@@ -263,7 +272,7 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
263
272
  join pg_attribute pga on pgc.oid = pga.attrelid and cols.column_name = pga.attname
264
273
  where (${[...tablesBySchemas.entries()].map(([schema, tables]) => `(table_schema = ${this.platform.quoteValue(schema)} and table_name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(',')}))`).join(' or ')})
265
274
  order by ordinal_position`;
266
- const allColumns = await connection.execute(sql);
275
+ const allColumns = await connection.execute(sql, [], 'all', ctx);
267
276
  const str = (val) => (val != null ? '' + val : val);
268
277
  const ret = {};
269
278
  for (const col of allColumns) {
@@ -342,9 +351,9 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
342
351
  }
343
352
  return ret;
344
353
  }
345
- async getAllChecks(connection, tablesBySchemas) {
354
+ async getAllChecks(connection, tablesBySchemas, ctx) {
346
355
  const sql = this.getChecksSQL(tablesBySchemas);
347
- const allChecks = await connection.execute(sql);
356
+ const allChecks = await connection.execute(sql, [], 'all', ctx);
348
357
  const ret = {};
349
358
  const seen = new Set();
350
359
  for (const check of allChecks) {
@@ -366,7 +375,7 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
366
375
  }
367
376
  return ret;
368
377
  }
369
- async getAllForeignKeys(connection, tablesBySchemas) {
378
+ async getAllForeignKeys(connection, tablesBySchemas, ctx) {
370
379
  const sql = `select nsp1.nspname schema_name, cls1.relname table_name, nsp2.nspname referenced_schema_name,
371
380
  cls2.relname referenced_table_name, a.attname column_name, af.attname referenced_column_name, conname constraint_name,
372
381
  confupdtype update_rule, confdeltype delete_rule, array_position(con.conkey,a.attnum) as ord, condeferrable, condeferred,
@@ -381,7 +390,7 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
381
390
  where (${[...tablesBySchemas.entries()].map(([schema, tables]) => `(cls1.relname in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(',')}) and nsp1.nspname = ${this.platform.quoteValue(schema)})`).join(' or ')})
382
391
  and confrelid > 0
383
392
  order by nsp1.nspname, cls1.relname, constraint_name, ord`;
384
- const allFks = await connection.execute(sql);
393
+ const allFks = await connection.execute(sql, [], 'all', ctx);
385
394
  const ret = {};
386
395
  function mapReferentialIntegrity(value, def) {
387
396
  const match = ['n', 'd'].includes(value) && /ON DELETE (SET (NULL|DEFAULT) \(.*?\))/.exec(def);
@@ -419,14 +428,14 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
419
428
  });
420
429
  return ret;
421
430
  }
422
- async getNativeEnumDefinitions(connection, schemas) {
431
+ async getNativeEnumDefinitions(connection, schemas, ctx) {
423
432
  const uniqueSchemas = Utils.unique(schemas);
424
433
  const res = await connection.execute(`select t.typname as enum_name, n.nspname as schema_name, array_agg(e.enumlabel order by e.enumsortorder) as enum_value
425
434
  from pg_type t
426
435
  join pg_enum e on t.oid = e.enumtypid
427
436
  join pg_catalog.pg_namespace n on n.oid = t.typnamespace
428
437
  where n.nspname in (${Array(uniqueSchemas.length).fill('?').join(', ')})
429
- group by t.typname, n.nspname`, uniqueSchemas);
438
+ group by t.typname, n.nspname`, uniqueSchemas, 'all', ctx);
430
439
  return res.reduce((o, row) => {
431
440
  let name = row.enum_name;
432
441
  if (row.schema_name && row.schema_name !== this.platform.getDefaultSchemaName()) {
@@ -511,6 +520,9 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
511
520
  if (parts.length === 2 && parts[0] === '*') {
512
521
  columnType = `${table.schema}.${parts[1]}`;
513
522
  }
523
+ else if (parts.length === 1) {
524
+ columnType = this.getTableName(column.type, table.schema);
525
+ }
514
526
  if (columnType.endsWith('[]')) {
515
527
  columnType = this.quote(columnType.substring(0, columnType.length - 2)) + '[]';
516
528
  }
@@ -1,4 +1,4 @@
1
- import { type Connection } from '@mikro-orm/core';
1
+ import { type Connection, type Transaction } from '@mikro-orm/core';
2
2
  import type { AbstractSqlConnection } from '../../AbstractSqlConnection.js';
3
3
  import { SchemaHelper } from '../../schema/SchemaHelper.js';
4
4
  import type { Column, IndexDef, Table, TableDifference } from '../../typings.js';
@@ -11,13 +11,13 @@ export declare class SqliteSchemaHelper extends SchemaHelper {
11
11
  getCreateNamespaceSQL(name: string): string;
12
12
  getDropNamespaceSQL(name: string): string;
13
13
  getListTablesSQL(): string;
14
- getAllTables(connection: AbstractSqlConnection, schemas?: string[]): Promise<Table[]>;
15
- getNamespaces(connection: AbstractSqlConnection): Promise<string[]>;
14
+ getAllTables(connection: AbstractSqlConnection, schemas?: string[], ctx?: Transaction): Promise<Table[]>;
15
+ getNamespaces(connection: AbstractSqlConnection, ctx?: Transaction): Promise<string[]>;
16
16
  private getIgnoredViewsCondition;
17
17
  getListViewsSQL(): string;
18
- loadViews(schema: DatabaseSchema, connection: AbstractSqlConnection, schemaName?: string): Promise<void>;
18
+ loadViews(schema: DatabaseSchema, connection: AbstractSqlConnection, schemaName?: string, ctx?: Transaction): Promise<void>;
19
19
  getDropDatabaseSQL(name: string): string;
20
- loadInformationSchema(schema: DatabaseSchema, connection: AbstractSqlConnection, tables: Table[], schemas?: string[]): Promise<void>;
20
+ loadInformationSchema(schema: DatabaseSchema, connection: AbstractSqlConnection, tables: Table[], schemas?: string[], ctx?: Transaction): Promise<void>;
21
21
  createTable(table: DatabaseTable, alter?: boolean): string[];
22
22
  createTableColumn(column: Column, table: DatabaseTable, _changedProperties?: Set<string>): string | undefined;
23
23
  getAddColumnsSQL(table: DatabaseTable, columns: Column[], diff?: TableDifference): string[];
@@ -45,7 +45,7 @@ export declare class SqliteSchemaHelper extends SchemaHelper {
45
45
  */
46
46
  private wrapExpressionDefault;
47
47
  private getEnumDefinitions;
48
- getPrimaryKeys(connection: AbstractSqlConnection, indexes: IndexDef[], tableName: string, schemaName?: string): Promise<string[]>;
48
+ getPrimaryKeys(connection: AbstractSqlConnection, indexes: IndexDef[], tableName: string, schemaName?: string, ctx?: Transaction): Promise<string[]>;
49
49
  private getIndexes;
50
50
  private getChecks;
51
51
  private getColumnDefinitions;
@@ -34,12 +34,12 @@ export class SqliteSchemaHelper extends SchemaHelper {
34
34
  return (`select name as table_name from sqlite_master where type = 'table' and name != 'sqlite_sequence' and name != 'geometry_columns' and name != 'spatial_ref_sys' ` +
35
35
  `union all select name as table_name from sqlite_temp_master where type = 'table' order by name`);
36
36
  }
37
- async getAllTables(connection, schemas) {
38
- const databases = await this.getDatabaseList(connection);
37
+ async getAllTables(connection, schemas, ctx) {
38
+ const databases = await this.getDatabaseList(connection, ctx);
39
39
  const hasAttachedDbs = databases.length > 1; // More than just 'main'
40
40
  // If no attached databases, use original behavior
41
41
  if (!hasAttachedDbs && !schemas?.length) {
42
- return connection.execute(this.getListTablesSQL());
42
+ return connection.execute(this.getListTablesSQL(), [], 'all', ctx);
43
43
  }
44
44
  // With attached databases, query each one
45
45
  const targetSchemas = schemas?.length ? schemas : databases;
@@ -47,15 +47,15 @@ export class SqliteSchemaHelper extends SchemaHelper {
47
47
  for (const dbName of targetSchemas) {
48
48
  const prefix = this.getSchemaPrefix(dbName);
49
49
  const tables = await connection.execute(`select name from ${prefix}sqlite_master where type = 'table' ` +
50
- `and name != 'sqlite_sequence' and name != 'geometry_columns' and name != 'spatial_ref_sys'`);
50
+ `and name != 'sqlite_sequence' and name != 'geometry_columns' and name != 'spatial_ref_sys'`, [], 'all', ctx);
51
51
  for (const t of tables) {
52
52
  allTables.push({ table_name: t.name, schema_name: dbName });
53
53
  }
54
54
  }
55
55
  return allTables;
56
56
  }
57
- async getNamespaces(connection) {
58
- return this.getDatabaseList(connection);
57
+ async getNamespaces(connection, ctx) {
58
+ return this.getDatabaseList(connection, ctx);
59
59
  }
60
60
  getIgnoredViewsCondition() {
61
61
  return SPATIALITE_VIEWS.map(v => `name != '${v}'`).join(' and ');
@@ -63,12 +63,12 @@ export class SqliteSchemaHelper extends SchemaHelper {
63
63
  getListViewsSQL() {
64
64
  return `select name as view_name, sql as view_definition from sqlite_master where type = 'view' and ${this.getIgnoredViewsCondition()} order by name`;
65
65
  }
66
- async loadViews(schema, connection, schemaName) {
67
- const databases = await this.getDatabaseList(connection);
66
+ async loadViews(schema, connection, schemaName, ctx) {
67
+ const databases = await this.getDatabaseList(connection, ctx);
68
68
  const hasAttachedDbs = databases.length > 1; // More than just 'main'
69
69
  // If no attached databases and no specific schema, use original behavior
70
70
  if (!hasAttachedDbs && !schemaName) {
71
- const views = await connection.execute(this.getListViewsSQL());
71
+ const views = await connection.execute(this.getListViewsSQL(), [], 'all', ctx);
72
72
  for (const view of views) {
73
73
  schema.addView(view.view_name, schemaName, this.extractViewDefinition(view.view_definition));
74
74
  }
@@ -79,7 +79,7 @@ export class SqliteSchemaHelper extends SchemaHelper {
79
79
  const targetDbs = schemaName ? [schemaName] : databases;
80
80
  for (const dbName of targetDbs) {
81
81
  const prefix = this.getSchemaPrefix(dbName);
82
- const views = await connection.execute(`select name as view_name, sql as view_definition from ${prefix}sqlite_master where type = 'view' and ${this.getIgnoredViewsCondition()} order by name`);
82
+ const views = await connection.execute(`select name as view_name, sql as view_definition from ${prefix}sqlite_master where type = 'view' and ${this.getIgnoredViewsCondition()} order by name`, [], 'all', ctx);
83
83
  for (const view of views) {
84
84
  schema.addView(view.view_name, dbName, this.extractViewDefinition(view.view_definition));
85
85
  }
@@ -92,15 +92,15 @@ export class SqliteSchemaHelper extends SchemaHelper {
92
92
  /* v8 ignore next */
93
93
  return `drop database if exists ${this.quote(name)}`;
94
94
  }
95
- async loadInformationSchema(schema, connection, tables, schemas) {
95
+ async loadInformationSchema(schema, connection, tables, schemas, ctx) {
96
96
  for (const t of tables) {
97
97
  const table = schema.addTable(t.table_name, t.schema_name, t.table_comment);
98
- const cols = await this.getColumns(connection, table.name, table.schema);
99
- const indexes = await this.getIndexes(connection, table.name, table.schema);
100
- const checks = await this.getChecks(connection, table.name, table.schema);
101
- const pks = await this.getPrimaryKeys(connection, indexes, table.name, table.schema);
102
- const fks = await this.getForeignKeys(connection, table.name, table.schema);
103
- const enums = await this.getEnumDefinitions(connection, table.name, table.schema);
98
+ const cols = await this.getColumns(connection, table.name, table.schema, ctx);
99
+ const indexes = await this.getIndexes(connection, table.name, table.schema, ctx);
100
+ const checks = await this.getChecks(connection, table.name, table.schema, ctx);
101
+ const pks = await this.getPrimaryKeys(connection, indexes, table.name, table.schema, ctx);
102
+ const fks = await this.getForeignKeys(connection, table.name, table.schema, ctx);
103
+ const enums = await this.getEnumDefinitions(connection, table.name, table.schema, ctx);
104
104
  table.init(cols, indexes, checks, pks, fks, enums);
105
105
  }
106
106
  }
@@ -252,8 +252,8 @@ export class SqliteSchemaHelper extends SchemaHelper {
252
252
  /**
253
253
  * Returns all database names excluding 'temp'.
254
254
  */
255
- async getDatabaseList(connection) {
256
- const databases = await connection.execute('pragma database_list');
255
+ async getDatabaseList(connection, ctx) {
256
+ const databases = await connection.execute('pragma database_list', [], 'all', ctx);
257
257
  return databases.filter(d => d.name !== 'temp').map(d => d.name);
258
258
  }
259
259
  /**
@@ -264,11 +264,11 @@ export class SqliteSchemaHelper extends SchemaHelper {
264
264
  /* v8 ignore next - fallback for non-standard view definitions */
265
265
  return match ? match[1] : viewDefinition;
266
266
  }
267
- async getColumns(connection, tableName, schemaName) {
267
+ async getColumns(connection, tableName, schemaName, ctx) {
268
268
  const prefix = this.getSchemaPrefix(schemaName);
269
- const columns = await connection.execute(`pragma ${prefix}table_xinfo('${tableName}')`);
269
+ const columns = await connection.execute(`pragma ${prefix}table_xinfo('${tableName}')`, [], 'all', ctx);
270
270
  const sql = `select sql from ${prefix}sqlite_master where type = ? and name = ?`;
271
- const tableDefinition = await connection.execute(sql, ['table', tableName], 'get');
271
+ const tableDefinition = await connection.execute(sql, ['table', tableName], 'get', ctx);
272
272
  const composite = columns.reduce((count, col) => count + (col.pk ? 1 : 0), 0) > 1;
273
273
  // there can be only one, so naive check like this should be enough
274
274
  const hasAutoincrement = tableDefinition.sql.toLowerCase().includes('autoincrement');
@@ -321,10 +321,10 @@ export class SqliteSchemaHelper extends SchemaHelper {
321
321
  // everything else is an expression that had its outer parens stripped
322
322
  return `(${value})`;
323
323
  }
324
- async getEnumDefinitions(connection, tableName, schemaName) {
324
+ async getEnumDefinitions(connection, tableName, schemaName, ctx) {
325
325
  const prefix = this.getSchemaPrefix(schemaName);
326
326
  const sql = `select sql from ${prefix}sqlite_master where type = ? and name = ?`;
327
- const tableDefinition = await connection.execute(sql, ['table', tableName], 'get');
327
+ const tableDefinition = await connection.execute(sql, ['table', tableName], 'get', ctx);
328
328
  const checkConstraints = [...(tableDefinition.sql.match(/[`["'][^`\]"']+[`\]"'] text check \(.*?\)/gi) ?? [])];
329
329
  return checkConstraints.reduce((o, item) => {
330
330
  // check constraints are defined as (note that last closing paren is missing):
@@ -339,17 +339,17 @@ export class SqliteSchemaHelper extends SchemaHelper {
339
339
  return o;
340
340
  }, {});
341
341
  }
342
- async getPrimaryKeys(connection, indexes, tableName, schemaName) {
342
+ async getPrimaryKeys(connection, indexes, tableName, schemaName, ctx) {
343
343
  const prefix = this.getSchemaPrefix(schemaName);
344
344
  const sql = `pragma ${prefix}table_info(\`${tableName}\`)`;
345
- const cols = await connection.execute(sql);
345
+ const cols = await connection.execute(sql, [], 'all', ctx);
346
346
  return cols.filter(col => !!col.pk).map(col => col.name);
347
347
  }
348
- async getIndexes(connection, tableName, schemaName) {
348
+ async getIndexes(connection, tableName, schemaName, ctx) {
349
349
  const prefix = this.getSchemaPrefix(schemaName);
350
350
  const sql = `pragma ${prefix}table_info(\`${tableName}\`)`;
351
- const cols = await connection.execute(sql);
352
- const indexes = await connection.execute(`pragma ${prefix}index_list(\`${tableName}\`)`);
351
+ const cols = await connection.execute(sql, [], 'all', ctx);
352
+ const indexes = await connection.execute(`pragma ${prefix}index_list(\`${tableName}\`)`, [], 'all', ctx);
353
353
  const ret = [];
354
354
  for (const col of cols.filter(c => c.pk)) {
355
355
  ret.push({
@@ -361,7 +361,7 @@ export class SqliteSchemaHelper extends SchemaHelper {
361
361
  });
362
362
  }
363
363
  for (const index of indexes.filter(index => !this.isImplicitIndex(index.name))) {
364
- const res = await connection.execute(`pragma ${prefix}index_info(\`${index.name}\`)`);
364
+ const res = await connection.execute(`pragma ${prefix}index_info(\`${index.name}\`)`, [], 'all', ctx);
365
365
  ret.push(...res.map(row => ({
366
366
  columnNames: [row.name],
367
367
  keyName: index.name,
@@ -372,8 +372,8 @@ export class SqliteSchemaHelper extends SchemaHelper {
372
372
  }
373
373
  return this.mapIndexes(ret);
374
374
  }
375
- async getChecks(connection, tableName, schemaName) {
376
- const { columns, constraints } = await this.getColumnDefinitions(connection, tableName, schemaName);
375
+ async getChecks(connection, tableName, schemaName, ctx) {
376
+ const { columns, constraints } = await this.getColumnDefinitions(connection, tableName, schemaName, ctx);
377
377
  const checks = [];
378
378
  for (const key of Object.keys(columns)) {
379
379
  const column = columns[key];
@@ -399,17 +399,17 @@ export class SqliteSchemaHelper extends SchemaHelper {
399
399
  }
400
400
  return checks;
401
401
  }
402
- async getColumnDefinitions(connection, tableName, schemaName) {
402
+ async getColumnDefinitions(connection, tableName, schemaName, ctx) {
403
403
  const prefix = this.getSchemaPrefix(schemaName);
404
- const columns = await connection.execute(`pragma ${prefix}table_xinfo('${tableName}')`);
404
+ const columns = await connection.execute(`pragma ${prefix}table_xinfo('${tableName}')`, [], 'all', ctx);
405
405
  const sql = `select sql from ${prefix}sqlite_master where type = ? and name = ?`;
406
- const tableDefinition = await connection.execute(sql, ['table', tableName], 'get');
406
+ const tableDefinition = await connection.execute(sql, ['table', tableName], 'get', ctx);
407
407
  return this.parseTableDefinition(tableDefinition.sql, columns);
408
408
  }
409
- async getForeignKeys(connection, tableName, schemaName) {
410
- const { constraints } = await this.getColumnDefinitions(connection, tableName, schemaName);
409
+ async getForeignKeys(connection, tableName, schemaName, ctx) {
410
+ const { constraints } = await this.getColumnDefinitions(connection, tableName, schemaName, ctx);
411
411
  const prefix = this.getSchemaPrefix(schemaName);
412
- const fks = await connection.execute(`pragma ${prefix}foreign_key_list(\`${tableName}\`)`);
412
+ const fks = await connection.execute(`pragma ${prefix}foreign_key_list(\`${tableName}\`)`, [], 'all', ctx);
413
413
  const qualifiedTableName = schemaName ? `${schemaName}.${tableName}` : tableName;
414
414
  return fks.reduce((ret, fk) => {
415
415
  const constraintName = this.platform.getIndexName(tableName, [fk.from], 'foreign');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/sql",
3
- "version": "7.0.7-dev.9",
3
+ "version": "7.0.8-dev.0",
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",
@@ -47,13 +47,13 @@
47
47
  "copy": "node ../../scripts/copy.mjs"
48
48
  },
49
49
  "dependencies": {
50
- "kysely": "0.28.14"
50
+ "kysely": "0.28.15"
51
51
  },
52
52
  "devDependencies": {
53
- "@mikro-orm/core": "^7.0.6"
53
+ "@mikro-orm/core": "^7.0.7"
54
54
  },
55
55
  "peerDependencies": {
56
- "@mikro-orm/core": "7.0.7-dev.9"
56
+ "@mikro-orm/core": "7.0.8-dev.0"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">= 22.17.0"
@@ -572,7 +572,7 @@ export class QueryBuilderHelper {
572
572
  else if (value[op] instanceof Raw || typeof value[op]?.toRaw === 'function') {
573
573
  const query = value[op] instanceof Raw ? value[op] : value[op].toRaw();
574
574
  const mappedKey = this.mapper(key, type, query, null);
575
- let sql = query.sql;
575
+ let sql = query.sql.replaceAll(ALIAS_REPLACEMENT, this.#alias);
576
576
  if (['$in', '$nin'].includes(op)) {
577
577
  sql = `(${sql})`;
578
578
  }
@@ -1,4 +1,4 @@
1
- import { type Configuration, type Dictionary, type EntityMetadata } from '@mikro-orm/core';
1
+ import { type Configuration, type Dictionary, type EntityMetadata, type Transaction } from '@mikro-orm/core';
2
2
  import { DatabaseTable } from './DatabaseTable.js';
3
3
  import type { AbstractSqlConnection } from '../AbstractSqlConnection.js';
4
4
  import type { DatabaseView } from '../typings.js';
@@ -42,7 +42,7 @@ export declare class DatabaseSchema {
42
42
  hasNamespace(namespace: string): boolean;
43
43
  hasNativeEnum(name: string): boolean;
44
44
  getNamespaces(): string[];
45
- static create(connection: AbstractSqlConnection, platform: AbstractSqlPlatform, config: Configuration, schemaName?: string, schemas?: string[], takeTables?: (string | RegExp)[], skipTables?: (string | RegExp)[], skipViews?: (string | RegExp)[]): Promise<DatabaseSchema>;
45
+ static create(connection: AbstractSqlConnection, platform: AbstractSqlPlatform, config: Configuration, schemaName?: string, schemas?: string[], takeTables?: (string | RegExp)[], skipTables?: (string | RegExp)[], skipViews?: (string | RegExp)[], ctx?: Transaction): Promise<DatabaseSchema>;
46
46
  static fromMetadata(metadata: EntityMetadata[], platform: AbstractSqlPlatform, config: Configuration, schemaName?: string, em?: any): DatabaseSchema;
47
47
  private static getViewDefinition;
48
48
  private static getSchemaName;
@@ -87,9 +87,9 @@ export class DatabaseSchema {
87
87
  getNamespaces() {
88
88
  return [...this.#namespaces];
89
89
  }
90
- static async create(connection, platform, config, schemaName, schemas, takeTables, skipTables, skipViews) {
90
+ static async create(connection, platform, config, schemaName, schemas, takeTables, skipTables, skipViews, ctx) {
91
91
  const schema = new DatabaseSchema(platform, schemaName ?? config.get('schema') ?? platform.getDefaultSchemaName());
92
- const allTables = await platform.getSchemaHelper().getAllTables(connection, schemas);
92
+ const allTables = await platform.getSchemaHelper().getAllTables(connection, schemas, ctx);
93
93
  const parts = config.get('migrations').tableName.split('.');
94
94
  const migrationsTableName = parts[1] ?? parts[0];
95
95
  const migrationsSchemaName = parts.length > 1 ? parts[0] : config.get('schema', platform.getDefaultSchemaName());
@@ -97,12 +97,12 @@ export class DatabaseSchema {
97
97
  (t.table_name !== migrationsTableName || (t.schema_name && t.schema_name !== migrationsSchemaName)));
98
98
  await platform
99
99
  .getSchemaHelper()
100
- .loadInformationSchema(schema, connection, tables, schemas && schemas.length > 0 ? schemas : undefined);
100
+ .loadInformationSchema(schema, connection, tables, schemas && schemas.length > 0 ? schemas : undefined, ctx);
101
101
  // Load views from database
102
- await platform.getSchemaHelper().loadViews(schema, connection);
102
+ await platform.getSchemaHelper().loadViews(schema, connection, schemaName, ctx);
103
103
  // Load materialized views (PostgreSQL only)
104
104
  if (platform.supportsMaterializedViews()) {
105
- await platform.getSchemaHelper().loadMaterializedViews(schema, connection, schemaName);
105
+ await platform.getSchemaHelper().loadMaterializedViews(schema, connection, schemaName, ctx);
106
106
  }
107
107
  // Filter out skipped views
108
108
  if (skipViews && skipViews.length > 0) {
@@ -147,7 +147,27 @@ export class DatabaseSchema {
147
147
  if (meta.view) {
148
148
  const viewDefinition = this.getViewDefinition(meta, em, platform);
149
149
  if (viewDefinition) {
150
- schema.addView(meta.collection, this.getSchemaName(meta, config, schemaName), viewDefinition, meta.materialized, meta.withData);
150
+ const view = schema.addView(meta.collection, this.getSchemaName(meta, config, schemaName), viewDefinition, meta.materialized, meta.withData);
151
+ if (meta.materialized) {
152
+ // Use a DatabaseTable to resolve property names → field names for indexes.
153
+ // addIndex only needs meta + table name, not actual columns.
154
+ const indexTable = new DatabaseTable(platform, meta.collection, this.getSchemaName(meta, config, schemaName));
155
+ meta.indexes.forEach(index => indexTable.addIndex(meta, index, 'index'));
156
+ meta.uniques.forEach(index => indexTable.addIndex(meta, index, 'unique'));
157
+ const pkProps = meta.props.filter(prop => prop.primary);
158
+ indexTable.addIndex(meta, { properties: pkProps.map(prop => prop.name) }, 'primary');
159
+ // Materialized views don't have primary keys or constraints in the DB,
160
+ // convert to match what PostgreSQL stores.
161
+ view.indexes = indexTable.getIndexes().map(idx => {
162
+ if (idx.primary) {
163
+ return { ...idx, primary: false, unique: true, constraint: false };
164
+ }
165
+ if (idx.constraint) {
166
+ return { ...idx, constraint: false };
167
+ }
168
+ return idx;
169
+ });
170
+ }
151
171
  }
152
172
  continue;
153
173
  }
@@ -1,7 +1,7 @@
1
1
  import { type Dictionary } from '@mikro-orm/core';
2
2
  import type { Column, ForeignKey, IndexDef, SchemaDifference, TableDifference } from '../typings.js';
3
3
  import type { DatabaseSchema } from './DatabaseSchema.js';
4
- import type { DatabaseTable } from './DatabaseTable.js';
4
+ import { DatabaseTable } from './DatabaseTable.js';
5
5
  import type { AbstractSqlPlatform } from '../AbstractSqlPlatform.js';
6
6
  /**
7
7
  * Compares two Schemas and return an instance of SchemaDifference.
@@ -1,4 +1,5 @@
1
1
  import { ArrayType, BooleanType, DateTimeType, inspect, JsonType, parseJsonSafe, Utils, } from '@mikro-orm/core';
2
+ import { DatabaseTable } from './DatabaseTable.js';
2
3
  /**
3
4
  * Compares two Schemas and return an instance of SchemaDifference.
4
5
  */
@@ -114,15 +115,16 @@ export class SchemaComparator {
114
115
  }
115
116
  }
116
117
  }
117
- // Compare views
118
+ // Compare views — prefer schema-qualified lookup to avoid matching
119
+ // views with the same name in different schemas
118
120
  for (const toView of toSchema.getViews()) {
119
121
  const viewName = toView.schema ? `${toView.schema}.${toView.name}` : toView.name;
120
- if (!fromSchema.hasView(toView.name) && !fromSchema.hasView(viewName)) {
122
+ if (!fromSchema.hasView(viewName) && !fromSchema.hasView(toView.name)) {
121
123
  diff.newViews[viewName] = toView;
122
124
  this.log(`view ${viewName} added`);
123
125
  }
124
126
  else {
125
- const fromView = fromSchema.getView(toView.name) ?? fromSchema.getView(viewName);
127
+ const fromView = fromSchema.getView(viewName) ?? fromSchema.getView(toView.name);
126
128
  if (fromView && this.diffViewExpression(fromView.definition, toView.definition)) {
127
129
  diff.changedViews[viewName] = { from: fromView, to: toView };
128
130
  this.log(`view ${viewName} changed`);
@@ -132,11 +134,34 @@ export class SchemaComparator {
132
134
  // Check for removed views
133
135
  for (const fromView of fromSchema.getViews()) {
134
136
  const viewName = fromView.schema ? `${fromView.schema}.${fromView.name}` : fromView.name;
135
- if (!toSchema.hasView(fromView.name) && !toSchema.hasView(viewName)) {
137
+ if (!toSchema.hasView(viewName) && !toSchema.hasView(fromView.name)) {
136
138
  diff.removedViews[viewName] = fromView;
137
139
  this.log(`view ${viewName} removed`);
138
140
  }
139
141
  }
142
+ // Diff materialized view indexes using the existing table diff infrastructure.
143
+ // Build transient DatabaseTable objects from view indexes so diffTable handles
144
+ // added/removed/changed/renamed index detection and alterTable emits correct DDL.
145
+ for (const toView of toSchema.getViews()) {
146
+ if (!toView.materialized || toView.withData === false) {
147
+ continue;
148
+ }
149
+ const viewName = toView.schema ? `${toView.schema}.${toView.name}` : toView.name;
150
+ // New or definition-changed views have indexes handled during create/recreate
151
+ if (diff.newViews[viewName] || diff.changedViews[viewName]) {
152
+ continue;
153
+ }
154
+ // If we get here, the view exists in fromSchema (otherwise it would be in diff.newViews)
155
+ const fromView = fromSchema.getView(viewName) ?? fromSchema.getView(toView.name);
156
+ const fromTable = new DatabaseTable(this.#platform, fromView.name, fromView.schema);
157
+ fromTable.init([], fromView.indexes ?? [], [], []);
158
+ const toTable = new DatabaseTable(this.#platform, toView.name, toView.schema);
159
+ toTable.init([], toView.indexes ?? [], [], []);
160
+ const tableDiff = this.diffTable(fromTable, toTable);
161
+ if (tableDiff) {
162
+ diff.changedTables[viewName] = tableDiff;
163
+ }
164
+ }
140
165
  return diff;
141
166
  }
142
167
  /**
@@ -1,4 +1,4 @@
1
- import { type Connection, type Dictionary, type Options, RawQueryFragment } from '@mikro-orm/core';
1
+ import { type Connection, type Dictionary, type Options, type Transaction, RawQueryFragment } from '@mikro-orm/core';
2
2
  import type { AbstractSqlConnection } from '../AbstractSqlConnection.js';
3
3
  import type { AbstractSqlPlatform } from '../AbstractSqlPlatform.js';
4
4
  import type { CheckDef, Column, ForeignKey, IndexDef, Table, TableDifference } from '../typings.js';
@@ -26,13 +26,13 @@ export declare abstract class SchemaHelper {
26
26
  getDropNativeEnumSQL(name: string, schema?: string): string;
27
27
  getAlterNativeEnumSQL(name: string, schema?: string, value?: string, items?: string[], oldItems?: string[]): string;
28
28
  /** Loads table metadata (columns, indexes, foreign keys) from the database information schema. */
29
- abstract loadInformationSchema(schema: DatabaseSchema, connection: AbstractSqlConnection, tables: Table[], schemas?: string[]): Promise<void>;
29
+ abstract loadInformationSchema(schema: DatabaseSchema, connection: AbstractSqlConnection, tables: Table[], schemas?: string[], ctx?: Transaction): Promise<void>;
30
30
  /** Returns the SQL query to list all tables in the database. */
31
31
  getListTablesSQL(): string;
32
32
  /** Retrieves all tables from the database. */
33
- getAllTables(connection: AbstractSqlConnection, schemas?: string[]): Promise<Table[]>;
33
+ getAllTables(connection: AbstractSqlConnection, schemas?: string[], ctx?: Transaction): Promise<Table[]>;
34
34
  getListViewsSQL(): string;
35
- loadViews(schema: DatabaseSchema, connection: AbstractSqlConnection, schemaName?: string): Promise<void>;
35
+ loadViews(schema: DatabaseSchema, connection: AbstractSqlConnection, schemaName?: string, ctx?: Transaction): Promise<void>;
36
36
  /** Returns SQL to rename a column in a table. */
37
37
  getRenameColumnSQL(tableName: string, oldColumnName: string, to: Column, schemaName?: string): string;
38
38
  /** Returns SQL to create an index on a table. */
@@ -61,7 +61,7 @@ export declare abstract class SchemaHelper {
61
61
  getPreAlterTable(tableDiff: TableDifference, safe: boolean): string[];
62
62
  getPostAlterTable(tableDiff: TableDifference, safe: boolean): string[];
63
63
  getChangeColumnCommentSQL(tableName: string, to: Column, schemaName?: string): string;
64
- getNamespaces(connection: AbstractSqlConnection): Promise<string[]>;
64
+ getNamespaces(connection: AbstractSqlConnection, ctx?: Transaction): Promise<string[]>;
65
65
  protected mapIndexes(indexes: IndexDef[]): Promise<IndexDef[]>;
66
66
  mapForeignKeys(fks: any[], tableName: string, schemaName?: string): Dictionary;
67
67
  normalizeDefaultValue(defaultValue: string | RawQueryFragment, length?: number, defaultValues?: Dictionary<string[]>): string | number;
@@ -84,7 +84,8 @@ export declare abstract class SchemaHelper {
84
84
  getReferencedTableName(referencedTableName: string, schema?: string): string;
85
85
  createIndex(index: IndexDef, table: DatabaseTable, createPrimary?: boolean): string;
86
86
  createCheck(table: DatabaseTable, check: CheckDef): string;
87
- protected getTableName(table: string, schema?: string): string;
87
+ /** @internal */
88
+ getTableName(table: string, schema?: string): string;
88
89
  getTablesGroupedBySchemas(tables: Table[]): Map<string | undefined, Table[]>;
89
90
  get options(): NonNullable<Options['schemaGenerator']>;
90
91
  protected processComment(comment: string): string;
@@ -100,5 +101,5 @@ export declare abstract class SchemaHelper {
100
101
  dropMaterializedViewIfExists(name: string, schema?: string): string;
101
102
  refreshMaterializedView(name: string, schema?: string, concurrently?: boolean): string;
102
103
  getListMaterializedViewsSQL(): string;
103
- loadMaterializedViews(schema: DatabaseSchema, connection: AbstractSqlConnection, schemaName?: string): Promise<void>;
104
+ loadMaterializedViews(schema: DatabaseSchema, connection: AbstractSqlConnection, schemaName?: string, ctx?: Transaction): Promise<void>;
104
105
  }
@@ -1,4 +1,4 @@
1
- import { RawQueryFragment, Utils } from '@mikro-orm/core';
1
+ import { RawQueryFragment, Utils, } from '@mikro-orm/core';
2
2
  /** Base class for database-specific schema helpers. Provides SQL generation for DDL operations. */
3
3
  export class SchemaHelper {
4
4
  platform;
@@ -72,13 +72,13 @@ export class SchemaHelper {
72
72
  throw new Error('Not supported by given driver');
73
73
  }
74
74
  /** Retrieves all tables from the database. */
75
- async getAllTables(connection, schemas) {
76
- return connection.execute(this.getListTablesSQL());
75
+ async getAllTables(connection, schemas, ctx) {
76
+ return connection.execute(this.getListTablesSQL(), [], 'all', ctx);
77
77
  }
78
78
  getListViewsSQL() {
79
79
  throw new Error('Not supported by given driver');
80
80
  }
81
- async loadViews(schema, connection, schemaName) {
81
+ async loadViews(schema, connection, schemaName, ctx) {
82
82
  throw new Error('Not supported by given driver');
83
83
  }
84
84
  /** Returns SQL to rename a column in a table. */
@@ -302,7 +302,14 @@ export class SchemaHelper {
302
302
  if (changedProperties.has('type')) {
303
303
  let type = column.type + (column.generated ? ` generated always as ${column.generated}` : '');
304
304
  if (column.nativeEnumName) {
305
- type = this.quote(this.getTableName(type, table.schema));
305
+ const parts = type.split('.');
306
+ if (parts.length === 2 && parts[0] === '*' && table.schema) {
307
+ type = `${table.schema}.${parts[1]}`;
308
+ }
309
+ else if (parts.length === 1) {
310
+ type = this.getTableName(type, table.schema);
311
+ }
312
+ type = this.quote(type);
306
313
  }
307
314
  sql.push(`alter table ${table.getQuotedName()} alter column ${this.quote(column.name)} type ${type + this.castColumn(column.name, type)}`);
308
315
  }
@@ -362,7 +369,7 @@ export class SchemaHelper {
362
369
  getChangeColumnCommentSQL(tableName, to, schemaName) {
363
370
  return '';
364
371
  }
365
- async getNamespaces(connection) {
372
+ async getNamespaces(connection, ctx) {
366
373
  return [];
367
374
  }
368
375
  async mapIndexes(indexes) {
@@ -593,6 +600,7 @@ export class SchemaHelper {
593
600
  createCheck(table, check) {
594
601
  return `alter table ${table.getQuotedName()} add constraint ${this.quote(check.name)} check (${check.expression})`;
595
602
  }
603
+ /** @internal */
596
604
  getTableName(table, schema) {
597
605
  if (schema && schema !== this.platform.getDefaultSchemaName()) {
598
606
  return `${schema}.${table}`;
@@ -662,7 +670,7 @@ export class SchemaHelper {
662
670
  getListMaterializedViewsSQL() {
663
671
  throw new Error('Not supported by given driver');
664
672
  }
665
- async loadMaterializedViews(schema, connection, schemaName) {
673
+ async loadMaterializedViews(schema, connection, schemaName, ctx) {
666
674
  throw new Error('Not supported by given driver');
667
675
  }
668
676
  }
@@ -62,6 +62,7 @@ export declare class SqlSchemaGenerator extends AbstractSchemaGenerator<Abstract
62
62
  * Uses topological sort based on view definition string matching.
63
63
  */
64
64
  private sortViewsByDependencies;
65
+ private appendViewCreation;
65
66
  private escapeRegExp;
66
67
  }
67
68
  export { SqlSchemaGenerator as SchemaGenerator };
@@ -89,16 +89,9 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator {
89
89
  this.append(ret, fks, true);
90
90
  }
91
91
  }
92
- // Create views after tables (views may depend on tables)
93
- // Sort views by dependencies (views depending on other views come later)
94
92
  const sortedViews = this.sortViewsByDependencies(toSchema.getViews());
95
93
  for (const view of sortedViews) {
96
- if (view.materialized) {
97
- this.append(ret, this.helper.createMaterializedView(view.name, view.schema, view.definition, view.withData ?? true));
98
- }
99
- else {
100
- this.append(ret, this.helper.createView(view.name, view.schema, view.definition), true);
101
- }
94
+ this.appendViewCreation(ret, view);
102
95
  }
103
96
  return this.wrapSchema(ret, options);
104
97
  }
@@ -185,8 +178,8 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator {
185
178
  this.append(ret, this.helper.dropTableIfExists(meta.tableName, this.getSchemaName(meta, options)));
186
179
  }
187
180
  if (this.platform.supportsNativeEnums()) {
188
- for (const columnName of Object.keys(schema.getNativeEnums())) {
189
- const sql = this.helper.getDropNativeEnumSQL(columnName, options.schema ?? this.config.get('schema'));
181
+ for (const enumOptions of Object.values(schema.getNativeEnums())) {
182
+ const sql = this.helper.getDropNativeEnumSQL(enumOptions.name, this.getSchemaName(enumOptions, options));
190
183
  this.append(ret, sql);
191
184
  }
192
185
  }
@@ -338,27 +331,14 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator {
338
331
  this.append(ret, sql);
339
332
  }
340
333
  }
341
- // Create new views after all table changes are done
342
- // Sort views by dependencies (views depending on other views come later)
343
334
  const sortedNewViews = this.sortViewsByDependencies(Object.values(schemaDiff.newViews));
344
335
  for (const view of sortedNewViews) {
345
- if (view.materialized) {
346
- this.append(ret, this.helper.createMaterializedView(view.name, view.schema, view.definition, view.withData ?? true));
347
- }
348
- else {
349
- this.append(ret, this.helper.createView(view.name, view.schema, view.definition), true);
350
- }
336
+ this.appendViewCreation(ret, view);
351
337
  }
352
- // Recreate changed views (also sorted by dependencies)
353
338
  const changedViews = Object.values(schemaDiff.changedViews).map(v => v.to);
354
339
  const sortedChangedViews = this.sortViewsByDependencies(changedViews);
355
340
  for (const view of sortedChangedViews) {
356
- if (view.materialized) {
357
- this.append(ret, this.helper.createMaterializedView(view.name, view.schema, view.definition, view.withData ?? true));
358
- }
359
- else {
360
- this.append(ret, this.helper.createView(view.name, view.schema, view.definition), true);
361
- }
341
+ this.appendViewCreation(ret, view);
362
342
  }
363
343
  return this.wrapSchema(ret, options);
364
344
  }
@@ -511,6 +491,22 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator {
511
491
  // Sort and map back to views
512
492
  return calc.sort().map(index => indexToView.get(index));
513
493
  }
494
+ appendViewCreation(ret, view) {
495
+ if (view.materialized) {
496
+ this.append(ret, this.helper.createMaterializedView(view.name, view.schema, view.definition, view.withData ?? true));
497
+ // Skip indexes for WITH NO DATA views — they have no data to index yet.
498
+ // Indexes will be created on the next schema:update after REFRESH populates data.
499
+ if (view.withData !== false) {
500
+ const viewName = this.helper.getTableName(view.name, view.schema);
501
+ for (const index of view.indexes ?? []) {
502
+ this.append(ret, this.helper.getCreateIndexSQL(viewName, index));
503
+ }
504
+ }
505
+ }
506
+ else {
507
+ this.append(ret, this.helper.createView(view.name, view.schema, view.definition), true);
508
+ }
509
+ }
514
510
  escapeRegExp(string) {
515
511
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
516
512
  }
package/typings.d.ts CHANGED
@@ -142,6 +142,8 @@ export interface DatabaseView {
142
142
  materialized?: boolean;
143
143
  /** For materialized views, whether data was populated on creation. */
144
144
  withData?: boolean;
145
+ /** Indexes on the materialized view. Only materialized views support indexes. */
146
+ indexes?: IndexDef[];
145
147
  }
146
148
  export interface SchemaDifference {
147
149
  newNamespaces: Set<string>;
@@ -250,8 +252,9 @@ export type MapTableName<T extends {
250
252
  }, TOptions extends MikroKyselyPluginOptions = {}> = {
251
253
  [P in T as TOptions['tableNamingStrategy'] extends 'entity' ? P['name'] : PreferStringLiteral<NonNullable<P['tableName']>, P['name']>]: P;
252
254
  };
255
+ type ResolveTableNaming<TOptions extends MikroKyselyPluginOptions> = TOptions['tableNamingStrategy'] extends 'entity' ? 'entity' : 'underscore';
253
256
  export type MapValueAsTable<TMap extends Record<string, any>, TOptions extends MikroKyselyPluginOptions = {}> = {
254
- [K in keyof TMap as TransformName<K, TOptions['tableNamingStrategy'] extends 'entity' ? 'entity' : 'underscore'>]: InferKyselyTable<TMap[K], TOptions>;
257
+ [K in keyof TMap as TransformName<K, ResolveTableNaming<TOptions>>]: InferKyselyTable<TMap[K], TOptions>;
255
258
  };
256
259
  export type InferKyselyTable<TSchema extends EntitySchemaWithMeta, TOptions extends MikroKyselyPluginOptions = {}> = ExcludeNever<{
257
260
  -readonly [K in keyof InferEntityProperties<TSchema> as TransformColumnName<K, TOptions['columnNamingStrategy'] extends 'property' ? 'property' : 'underscore', MaybeReturnType<InferEntityProperties<TSchema>[K]>>]: InferColumnValue<MaybeReturnType<InferEntityProperties<TSchema>[K]>, TOptions['processOnCreateHooks'] extends true ? true : false>;
@@ -318,7 +321,9 @@ export type InferClassEntityDB<TEntities, TOptions extends MikroKyselyPluginOpti
318
321
  type ClassEntityDBMap<TEntities, TOptions extends MikroKyselyPluginOptions = {}> = {
319
322
  [T in TEntities as ClassEntityTableName<T, TOptions>]: ClassEntityColumns<T, TOptions>;
320
323
  };
321
- type ClassEntityTableName<T, TOptions extends MikroKyselyPluginOptions = {}> = T extends abstract new (...args: any[]) => infer Instance ? TransformName<InferEntityName<Instance>, TOptions['tableNamingStrategy'] extends 'entity' ? 'entity' : 'underscore'> : never;
324
+ type ClassEntityTableName<T, TOptions extends MikroKyselyPluginOptions = {}> = T extends {
325
+ '~entityName'?: infer Name extends string;
326
+ } ? TransformName<Name, ResolveTableNaming<TOptions>> : T extends abstract new (...args: any[]) => infer Instance ? TransformName<InferEntityName<Instance>, ResolveTableNaming<TOptions>> : never;
322
327
  type ClassEntityColumns<T, TOptions extends MikroKyselyPluginOptions = {}> = T extends abstract new (...args: any[]) => infer Instance ? {
323
328
  [K in keyof Instance as ClassEntityColumnName<K, Instance[K], TOptions>]: ClassEntityColumnValue<Instance[K]>;
324
329
  } : never;