@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.
- package/AbstractSqlDriver.js +15 -6
- package/dialects/mysql/MySqlSchemaHelper.d.ts +8 -8
- package/dialects/mysql/MySqlSchemaHelper.js +22 -22
- package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +10 -10
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +38 -26
- package/dialects/sqlite/SqliteSchemaHelper.d.ts +6 -6
- package/dialects/sqlite/SqliteSchemaHelper.js +38 -38
- package/package.json +4 -4
- package/query/QueryBuilderHelper.js +1 -1
- package/schema/DatabaseSchema.d.ts +2 -2
- package/schema/DatabaseSchema.js +26 -6
- package/schema/SchemaComparator.d.ts +1 -1
- package/schema/SchemaComparator.js +29 -4
- package/schema/SchemaHelper.d.ts +8 -7
- package/schema/SchemaHelper.js +15 -7
- package/schema/SqlSchemaGenerator.d.ts +1 -0
- package/schema/SqlSchemaGenerator.js +21 -25
- package/typings.d.ts +7 -2
package/AbstractSqlDriver.js
CHANGED
|
@@ -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
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
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
|
|
920
|
+
return condTemplate;
|
|
921
921
|
}
|
|
922
922
|
params.push(cond);
|
|
923
923
|
return '?';
|
|
924
924
|
});
|
|
925
|
-
|
|
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
|
-
}
|
|
51
|
-
getAllChecks(connection: AbstractSqlConnection, tablesBySchemas: Map<string | undefined, Table[]
|
|
52
|
-
getAllForeignKeys(connection: AbstractSqlConnection, tablesBySchemas: Map<string | undefined, Table[]
|
|
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
|
-
|
|
62
|
-
|
|
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(
|
|
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.
|
|
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.
|
|
50
|
+
"kysely": "0.28.15"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@mikro-orm/core": "^7.0.
|
|
53
|
+
"@mikro-orm/core": "^7.0.7"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@mikro-orm/core": "7.0.
|
|
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;
|
package/schema/DatabaseSchema.js
CHANGED
|
@@ -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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
/**
|
package/schema/SchemaHelper.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|
package/schema/SchemaHelper.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
189
|
-
const sql = this.helper.getDropNativeEnumSQL(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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;
|