@mikro-orm/knex 6.1.13-dev.3 → 6.1.13-dev.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AbstractSqlConnection.js +7 -1
- package/AbstractSqlDriver.d.ts +5 -5
- package/AbstractSqlDriver.js +18 -9
- package/MonkeyPatchable.d.ts +5 -0
- package/MonkeyPatchable.js +15 -0
- package/README.md +17 -11
- package/SqlEntityManager.d.ts +1 -1
- package/SqlEntityManager.js +1 -2
- package/dialects/index.d.ts +2 -0
- package/dialects/index.js +18 -0
- package/dialects/mssql/MsSqlColumnCompiler.d.ts +4 -0
- package/dialects/mssql/MsSqlColumnCompiler.js +10 -0
- package/dialects/mssql/MsSqlKnexDialect.d.ts +6 -0
- package/dialects/mssql/MsSqlKnexDialect.js +22 -0
- package/dialects/mssql/MsSqlQueryCompiler.d.ts +12 -0
- package/dialects/mssql/MsSqlQueryCompiler.js +94 -0
- package/dialects/mssql/MsSqlTableCompiler.d.ts +9 -0
- package/dialects/mssql/MsSqlTableCompiler.js +40 -0
- package/dialects/mssql/index.d.ts +1 -0
- package/dialects/mssql/index.js +17 -0
- package/dialects/sqlite/BaseSqliteConnection.d.ts +10 -0
- package/dialects/sqlite/BaseSqliteConnection.js +56 -0
- package/dialects/sqlite/BaseSqlitePlatform.d.ts +49 -0
- package/dialects/sqlite/BaseSqlitePlatform.js +78 -0
- package/dialects/sqlite/BaseSqliteSchemaHelper.d.ts +27 -0
- package/dialects/sqlite/BaseSqliteSchemaHelper.js +172 -0
- package/dialects/sqlite/BetterSqliteKnexDialect.d.ts +5 -0
- package/dialects/sqlite/BetterSqliteKnexDialect.js +15 -0
- package/dialects/sqlite/LibSqlKnexDialect.d.ts +6 -0
- package/dialects/sqlite/LibSqlKnexDialect.js +18 -0
- package/dialects/sqlite/SqliteKnexDialect.d.ts +8 -0
- package/dialects/sqlite/SqliteKnexDialect.js +67 -0
- package/dialects/sqlite/SqliteTableCompiler.d.ts +5 -0
- package/dialects/sqlite/SqliteTableCompiler.js +45 -0
- package/dialects/sqlite/index.d.ts +7 -0
- package/dialects/sqlite/index.js +23 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/index.mjs +10 -0
- package/package.json +2 -2
- package/query/QueryBuilder.d.ts +57 -39
- package/query/QueryBuilder.js +64 -26
- package/query/QueryBuilderHelper.js +2 -1
- package/schema/DatabaseTable.js +20 -2
- package/schema/SchemaComparator.js +15 -2
- package/schema/SchemaHelper.d.ts +5 -1
- package/schema/SchemaHelper.js +20 -2
- package/schema/SqlSchemaGenerator.d.ts +12 -4
- package/schema/SqlSchemaGenerator.js +53 -28
- package/typings.d.ts +2 -1
package/query/QueryBuilder.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { inspect } from 'util';
|
|
3
3
|
import type { Knex } from 'knex';
|
|
4
|
-
import { type AnyEntity, type ConnectionType, type Dictionary, type EntityData, type EntityName, type EntityProperty, type FlushMode, type GroupOperator, LockMode, type LoggingOptions, type MetadataStorage, type ObjectQuery, PopulateHint, type PopulateOptions, type QBFilterQuery, type QBQueryOrderMap, QueryFlag, type QueryResult, type RequiredEntityData } from '@mikro-orm/core';
|
|
4
|
+
import { type AnyEntity, type ConnectionType, type Dictionary, type EntityData, type EntityName, type EntityProperty, type FlushMode, type GroupOperator, LockMode, type LoggingOptions, type MetadataStorage, type ObjectQuery, PopulateHint, type PopulateOptions, type QBFilterQuery, type QBQueryOrderMap, QueryFlag, type QueryOrderMap, type QueryResult, type RequiredEntityData } from '@mikro-orm/core';
|
|
5
5
|
import { JoinType, QueryType } from './enums';
|
|
6
6
|
import type { AbstractSqlDriver } from '../AbstractSqlDriver';
|
|
7
7
|
import { type Alias, QueryBuilderHelper } from './QueryBuilderHelper';
|
|
@@ -32,13 +32,12 @@ export interface ExecuteOptions {
|
|
|
32
32
|
* ```
|
|
33
33
|
*/
|
|
34
34
|
export declare class QueryBuilder<T extends object = AnyEntity> {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
private readonly loggerContext?;
|
|
35
|
+
protected readonly metadata: MetadataStorage;
|
|
36
|
+
protected readonly driver: AbstractSqlDriver;
|
|
37
|
+
protected readonly context?: Knex.Transaction<any, any[]> | undefined;
|
|
38
|
+
protected connectionType?: ConnectionType | undefined;
|
|
39
|
+
protected em?: SqlEntityManager<AbstractSqlDriver<import("..").AbstractSqlConnection, AbstractSqlPlatform>> | undefined;
|
|
40
|
+
protected loggerContext?: (LoggingOptions & Dictionary) | undefined;
|
|
42
41
|
get mainAlias(): Alias<T>;
|
|
43
42
|
get alias(): string;
|
|
44
43
|
get helper(): QueryBuilderHelper;
|
|
@@ -56,40 +55,51 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
|
|
|
56
55
|
_populateMap: Dictionary<string>;
|
|
57
56
|
/** @internal */
|
|
58
57
|
readonly rawFragments: Set<string>;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
58
|
+
protected aliasCounter: number;
|
|
59
|
+
protected flags: Set<QueryFlag>;
|
|
60
|
+
protected finalized: boolean;
|
|
61
|
+
protected _joins: Dictionary<JoinOptions>;
|
|
62
|
+
protected _explicitAlias: boolean;
|
|
63
|
+
protected _schema?: string;
|
|
64
|
+
protected _cond: Dictionary;
|
|
65
|
+
protected _data: Dictionary;
|
|
66
|
+
protected _orderBy: QueryOrderMap<T>[];
|
|
67
|
+
protected _groupBy: Field<T>[];
|
|
68
|
+
protected _having: Dictionary;
|
|
69
|
+
protected _returning?: Field<T>[];
|
|
70
|
+
protected _onConflict?: {
|
|
71
|
+
fields: string[];
|
|
72
|
+
ignore?: boolean;
|
|
73
|
+
merge?: EntityData<T> | Field<T>[];
|
|
74
|
+
where?: QBFilterQuery<T>;
|
|
75
|
+
}[];
|
|
76
|
+
protected _limit?: number;
|
|
77
|
+
protected _offset?: number;
|
|
78
|
+
protected _distinctOn?: string[];
|
|
79
|
+
protected _joinedProps: Map<string, PopulateOptions<any>>;
|
|
80
|
+
protected _cache?: boolean | number | [string, number];
|
|
81
|
+
protected _indexHint?: string;
|
|
82
|
+
protected _comments: string[];
|
|
83
|
+
protected _hintComments: string[];
|
|
84
|
+
protected flushMode?: FlushMode;
|
|
85
|
+
protected lockMode?: LockMode;
|
|
86
|
+
protected lockTables?: string[];
|
|
87
|
+
protected subQueries: Dictionary<string>;
|
|
88
|
+
protected _mainAlias?: Alias<T>;
|
|
89
|
+
protected _aliases: Dictionary<Alias<any>>;
|
|
90
|
+
protected _helper?: QueryBuilderHelper;
|
|
91
|
+
protected _query?: {
|
|
92
|
+
sql?: string;
|
|
93
|
+
_sql?: Knex.Sql;
|
|
94
|
+
params?: readonly unknown[];
|
|
95
|
+
qb: Knex.QueryBuilder<T>;
|
|
96
|
+
};
|
|
97
|
+
protected readonly platform: AbstractSqlPlatform;
|
|
98
|
+
protected readonly knex: Knex;
|
|
89
99
|
/**
|
|
90
100
|
* @internal
|
|
91
101
|
*/
|
|
92
|
-
constructor(entityName: EntityName<T> | QueryBuilder<T>, metadata: MetadataStorage, driver: AbstractSqlDriver, context?: Knex.Transaction<any, any[]> | undefined, alias?: string, connectionType?: ConnectionType | undefined, em?: SqlEntityManager<AbstractSqlDriver<import("..").AbstractSqlConnection, AbstractSqlPlatform>> | undefined, loggerContext?: LoggingOptions | undefined);
|
|
102
|
+
constructor(entityName: EntityName<T> | QueryBuilder<T>, metadata: MetadataStorage, driver: AbstractSqlDriver, context?: Knex.Transaction<any, any[]> | undefined, alias?: string, connectionType?: ConnectionType | undefined, em?: SqlEntityManager<AbstractSqlDriver<import("..").AbstractSqlConnection, AbstractSqlPlatform>> | undefined, loggerContext?: (LoggingOptions & Dictionary) | undefined);
|
|
93
103
|
select(fields: Field<T> | Field<T>[], distinct?: boolean): SelectQueryBuilder<T>;
|
|
94
104
|
addSelect(fields: Field<T> | Field<T>[]): SelectQueryBuilder<T>;
|
|
95
105
|
distinct(): SelectQueryBuilder<T>;
|
|
@@ -232,6 +242,14 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
|
|
|
232
242
|
as(alias: string): Knex.QueryBuilder;
|
|
233
243
|
clone(reset?: boolean | string[]): QueryBuilder<T>;
|
|
234
244
|
getKnex(processVirtualEntity?: boolean): Knex.QueryBuilder;
|
|
245
|
+
/**
|
|
246
|
+
* Sets logger context for this query builder.
|
|
247
|
+
*/
|
|
248
|
+
setLoggerContext(context: LoggingOptions & Dictionary): void;
|
|
249
|
+
/**
|
|
250
|
+
* Gets logger context for this query builder.
|
|
251
|
+
*/
|
|
252
|
+
getLoggerContext<T extends Dictionary & LoggingOptions = Dictionary>(): T;
|
|
235
253
|
private fromVirtual;
|
|
236
254
|
private joinReference;
|
|
237
255
|
private prepareFields;
|
package/query/QueryBuilder.js
CHANGED
|
@@ -85,6 +85,7 @@ class QueryBuilder {
|
|
|
85
85
|
_mainAlias;
|
|
86
86
|
_aliases = {};
|
|
87
87
|
_helper;
|
|
88
|
+
_query;
|
|
88
89
|
platform;
|
|
89
90
|
knex;
|
|
90
91
|
/**
|
|
@@ -459,10 +460,10 @@ class QueryBuilder {
|
|
|
459
460
|
return this;
|
|
460
461
|
}
|
|
461
462
|
getKnexQuery(processVirtualEntity = true) {
|
|
462
|
-
if (this
|
|
463
|
-
return this
|
|
463
|
+
if (this._query?.qb) {
|
|
464
|
+
return this._query.qb;
|
|
464
465
|
}
|
|
465
|
-
this
|
|
466
|
+
this._query = {};
|
|
466
467
|
this.finalize();
|
|
467
468
|
const qb = this.getQueryBase(processVirtualEntity);
|
|
468
469
|
const type = this.type ?? enums_1.QueryType.SELECT;
|
|
@@ -484,14 +485,14 @@ class QueryBuilder {
|
|
|
484
485
|
core_1.Utils.runIfNotEmpty(() => this._hintComments.forEach(comment => qb.hintComment(comment)), this._hintComments);
|
|
485
486
|
core_1.Utils.runIfNotEmpty(() => this.helper.appendOnConflictClause(type, this._onConflict, qb), this._onConflict);
|
|
486
487
|
if (this.type === enums_1.QueryType.TRUNCATE && this.platform.usesCascadeStatement()) {
|
|
487
|
-
return this
|
|
488
|
+
return this._query.qb = this.knex.raw(qb.toSQL().toNative().sql + ' cascade');
|
|
488
489
|
}
|
|
489
490
|
if (this.lockMode) {
|
|
490
491
|
this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
|
|
491
492
|
}
|
|
492
493
|
this.helper.finalize(type, qb, this.mainAlias.metadata, this._data, this._returning);
|
|
493
494
|
this.clearRawFragmentsCache();
|
|
494
|
-
return this
|
|
495
|
+
return this._query.qb = qb;
|
|
495
496
|
}
|
|
496
497
|
/**
|
|
497
498
|
* @internal
|
|
@@ -506,17 +507,16 @@ class QueryBuilder {
|
|
|
506
507
|
getQuery() {
|
|
507
508
|
return this.toQuery().sql;
|
|
508
509
|
}
|
|
509
|
-
#query;
|
|
510
510
|
toQuery() {
|
|
511
|
-
if (this
|
|
512
|
-
return { sql: this
|
|
511
|
+
if (this._query?.sql) {
|
|
512
|
+
return { sql: this._query.sql, _sql: this._query._sql, params: this._query.params };
|
|
513
513
|
}
|
|
514
514
|
const sql = this.getKnexQuery().toSQL();
|
|
515
515
|
const query = sql.toNative();
|
|
516
|
-
this
|
|
517
|
-
this
|
|
518
|
-
this
|
|
519
|
-
return { sql: this
|
|
516
|
+
this._query.sql = query.sql;
|
|
517
|
+
this._query._sql = sql;
|
|
518
|
+
this._query.params = query.bindings ?? [];
|
|
519
|
+
return { sql: this._query.sql, _sql: this._query._sql, params: this._query.params };
|
|
520
520
|
}
|
|
521
521
|
/**
|
|
522
522
|
* Returns the list of all parameters for this query.
|
|
@@ -598,17 +598,18 @@ class QueryBuilder {
|
|
|
598
598
|
if (!this.connectionType && method !== 'run' && [enums_1.QueryType.INSERT, enums_1.QueryType.UPDATE, enums_1.QueryType.DELETE, enums_1.QueryType.TRUNCATE].includes(this.type ?? enums_1.QueryType.SELECT)) {
|
|
599
599
|
this.connectionType = 'write';
|
|
600
600
|
}
|
|
601
|
+
if (!this.finalized && method === 'get' && this.type === enums_1.QueryType.SELECT) {
|
|
602
|
+
this.limit(1);
|
|
603
|
+
}
|
|
601
604
|
const query = this.toQuery()._sql;
|
|
602
605
|
const cached = await this.em?.tryCache(this.mainAlias.entityName, this._cache, ['qb.execute', query.sql, query.bindings, method]);
|
|
603
606
|
if (cached?.data) {
|
|
604
607
|
return cached.data;
|
|
605
608
|
}
|
|
606
|
-
if (!this.finalized && method === 'get') {
|
|
607
|
-
this.limit(1);
|
|
608
|
-
}
|
|
609
609
|
const write = method === 'run' || !this.platform.getConfig().get('preferReadReplicas');
|
|
610
610
|
const type = this.connectionType || (write ? 'write' : 'read');
|
|
611
|
-
const
|
|
611
|
+
const loggerContext = { id: this.em?.id, ...this.loggerContext };
|
|
612
|
+
const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context, loggerContext);
|
|
612
613
|
const meta = this.mainAlias.metadata;
|
|
613
614
|
if (!options.mapResults || !meta) {
|
|
614
615
|
await this.em?.storeCache(this._cache, cached, res);
|
|
@@ -652,8 +653,8 @@ class QueryBuilder {
|
|
|
652
653
|
(0, core_1.helper)(entity).__serializationContext.populate ??= hint;
|
|
653
654
|
hint.forEach(hint => {
|
|
654
655
|
const [propName] = hint.field.split(':', 2);
|
|
655
|
-
const value = entity[propName];
|
|
656
|
-
if (core_1.Utils.isEntity(value
|
|
656
|
+
const value = core_1.Reference.unwrapReference(entity[propName]);
|
|
657
|
+
if (core_1.Utils.isEntity(value)) {
|
|
657
658
|
(0, core_1.helper)(value).populated();
|
|
658
659
|
propagatePopulateHint(value, hint.children ?? []);
|
|
659
660
|
}
|
|
@@ -794,6 +795,19 @@ class QueryBuilder {
|
|
|
794
795
|
}
|
|
795
796
|
return qb;
|
|
796
797
|
}
|
|
798
|
+
/**
|
|
799
|
+
* Sets logger context for this query builder.
|
|
800
|
+
*/
|
|
801
|
+
setLoggerContext(context) {
|
|
802
|
+
this.loggerContext = context;
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Gets logger context for this query builder.
|
|
806
|
+
*/
|
|
807
|
+
getLoggerContext() {
|
|
808
|
+
this.loggerContext ??= {};
|
|
809
|
+
return this.loggerContext;
|
|
810
|
+
}
|
|
797
811
|
fromVirtual(meta) {
|
|
798
812
|
if (typeof meta.expression === 'string') {
|
|
799
813
|
return `(${meta.expression}) as ${this.platform.quoteIdentifier(this.alias)}`;
|
|
@@ -954,15 +968,19 @@ class QueryBuilder {
|
|
|
954
968
|
/* istanbul ignore next */
|
|
955
969
|
const requiresSQLConversion = meta?.props.filter(p => p.hasConvertToJSValueSQL && p.persist !== false) ?? [];
|
|
956
970
|
if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) && requiresSQLConversion.length > 0) {
|
|
957
|
-
|
|
971
|
+
for (const p of requiresSQLConversion) {
|
|
972
|
+
ret.push(this.helper.mapper(p.name, this.type));
|
|
973
|
+
}
|
|
958
974
|
}
|
|
959
|
-
Object.keys(this._populateMap)
|
|
960
|
-
if (
|
|
975
|
+
for (const f of Object.keys(this._populateMap)) {
|
|
976
|
+
if (type === 'where' && this._joins[f]) {
|
|
961
977
|
const cols = this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[f]);
|
|
962
|
-
|
|
978
|
+
for (const col of cols) {
|
|
979
|
+
ret.push(col);
|
|
980
|
+
}
|
|
963
981
|
}
|
|
964
|
-
}
|
|
965
|
-
return ret;
|
|
982
|
+
}
|
|
983
|
+
return core_1.Utils.unique(ret);
|
|
966
984
|
}
|
|
967
985
|
init(type, data, cond) {
|
|
968
986
|
this.ensureNotFinalized();
|
|
@@ -1204,8 +1222,7 @@ class QueryBuilder {
|
|
|
1204
1222
|
// not perfect, but should work most of the time, ideally we should check only the alias (`... as alias`)
|
|
1205
1223
|
return field.sql.includes(prop);
|
|
1206
1224
|
}
|
|
1207
|
-
|
|
1208
|
-
return field.toString().includes(prop);
|
|
1225
|
+
return false;
|
|
1209
1226
|
});
|
|
1210
1227
|
if (field instanceof core_1.RawQueryFragment) {
|
|
1211
1228
|
knexQuery.select(this.platform.formatQuery(field.sql, field.params));
|
|
@@ -1221,6 +1238,27 @@ class QueryBuilder {
|
|
|
1221
1238
|
subSubQuery.__raw = true; // tag it as there is now way to check via `instanceof`
|
|
1222
1239
|
this._limit = undefined;
|
|
1223
1240
|
this._offset = undefined;
|
|
1241
|
+
// remove joins that are not used for population or ordering to improve performance
|
|
1242
|
+
const populate = new Set();
|
|
1243
|
+
const orderByAliases = this._orderBy
|
|
1244
|
+
.flatMap(hint => Object.keys(hint))
|
|
1245
|
+
.map(k => k.split('.')[0]);
|
|
1246
|
+
function addPath(hints, prefix = '') {
|
|
1247
|
+
for (const hint of hints) {
|
|
1248
|
+
const field = hint.field.split(':')[0];
|
|
1249
|
+
populate.add((prefix ? prefix + '.' : '') + field);
|
|
1250
|
+
if (hint.children) {
|
|
1251
|
+
addPath(hint.children, (prefix ? prefix + '.' : '') + field);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
addPath(this._populate);
|
|
1256
|
+
for (const [key, join] of Object.entries(this._joins)) {
|
|
1257
|
+
const path = join.path?.replace(/\[populate]|\[pivot]|:ref/g, '').replace(new RegExp(`^${meta.className}.`), '');
|
|
1258
|
+
if (!populate.has(path ?? '') && !orderByAliases.includes(join.alias)) {
|
|
1259
|
+
delete this._joins[key];
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1224
1262
|
this.select(this._fields).where({ [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: subSubQuery } });
|
|
1225
1263
|
}
|
|
1226
1264
|
wrapModifySubQuery(meta) {
|
|
@@ -518,7 +518,8 @@ class QueryBuilderHelper {
|
|
|
518
518
|
return ret;
|
|
519
519
|
}
|
|
520
520
|
finalize(type, qb, meta, data, returning) {
|
|
521
|
-
|
|
521
|
+
const usesReturningStatement = this.platform.usesReturningStatement() || this.platform.usesOutputStatement();
|
|
522
|
+
if (!meta || !data || !usesReturningStatement) {
|
|
522
523
|
return;
|
|
523
524
|
}
|
|
524
525
|
// always respect explicit returning hint
|
package/schema/DatabaseTable.js
CHANGED
|
@@ -79,6 +79,9 @@ class DatabaseTable {
|
|
|
79
79
|
prop.length ??= this.platform.getDefaultDateTimeLength();
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
+
if (prop.length == null && prop.columnTypes[idx]) {
|
|
83
|
+
prop.length = this.platform.getSchemaHelper().inferLengthFromColumnType(prop.columnTypes[idx]);
|
|
84
|
+
}
|
|
82
85
|
const primary = !meta.compositePK && !!prop.primary && prop.kind === core_1.ReferenceKind.SCALAR && this.platform.isNumericColumn(mappedType);
|
|
83
86
|
this.columns[field] = {
|
|
84
87
|
name: prop.fieldNames[idx],
|
|
@@ -120,9 +123,22 @@ class DatabaseTable {
|
|
|
120
123
|
if (prop.deleteRule || cascade || prop.nullable) {
|
|
121
124
|
this.foreignKeys[constraintName].deleteRule = prop.deleteRule || (cascade ? 'cascade' : 'set null');
|
|
122
125
|
}
|
|
123
|
-
if (prop.updateRule
|
|
126
|
+
if (prop.updateRule) {
|
|
124
127
|
this.foreignKeys[constraintName].updateRule = prop.updateRule || 'cascade';
|
|
125
128
|
}
|
|
129
|
+
if ((prop.cascade.includes(core_1.Cascade.PERSIST) || prop.cascade.includes(core_1.Cascade.ALL))) {
|
|
130
|
+
const hasCascadePath = Object.values(this.foreignKeys).some(fk => {
|
|
131
|
+
return fk.constraintName !== constraintName
|
|
132
|
+
&& ((fk.updateRule && fk.updateRule !== 'no action') || (fk.deleteRule && fk.deleteRule !== 'no action'))
|
|
133
|
+
&& fk.referencedTableName === this.foreignKeys[constraintName].referencedTableName;
|
|
134
|
+
});
|
|
135
|
+
if (!hasCascadePath || this.platform.supportsMultipleCascadePaths()) {
|
|
136
|
+
this.foreignKeys[constraintName].updateRule ??= 'cascade';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (prop.deferMode) {
|
|
140
|
+
this.foreignKeys[constraintName].deferMode = prop.deferMode;
|
|
141
|
+
}
|
|
126
142
|
}
|
|
127
143
|
if (prop.index) {
|
|
128
144
|
this.indexes.push({
|
|
@@ -473,6 +489,7 @@ class DatabaseTable {
|
|
|
473
489
|
fkOptions.referencedColumnNames = fk.referencedColumnNames;
|
|
474
490
|
fkOptions.updateRule = fk.updateRule?.toLowerCase();
|
|
475
491
|
fkOptions.deleteRule = fk.deleteRule?.toLowerCase();
|
|
492
|
+
fkOptions.deferMode = fk.deferMode;
|
|
476
493
|
fkOptions.columnTypes = fk.columnNames.map(c => this.getColumn(c).type);
|
|
477
494
|
const columnOptions = {};
|
|
478
495
|
if (fk.columnNames.length === 1) {
|
|
@@ -520,6 +537,7 @@ class DatabaseTable {
|
|
|
520
537
|
fkOptions.referencedColumnNames = fk.referencedColumnNames;
|
|
521
538
|
fkOptions.updateRule = fk.updateRule?.toLowerCase();
|
|
522
539
|
fkOptions.deleteRule = fk.deleteRule?.toLowerCase();
|
|
540
|
+
fkOptions.deferMode = fk.deferMode;
|
|
523
541
|
}
|
|
524
542
|
return {
|
|
525
543
|
name: prop,
|
|
@@ -584,7 +602,7 @@ class DatabaseTable {
|
|
|
584
602
|
// The enum name will be a concatenation of the table name and the column name.
|
|
585
603
|
return namingStrategy.getClassName(this.name + '_' + column.name, '_');
|
|
586
604
|
}
|
|
587
|
-
return column.mappedType?.
|
|
605
|
+
return column.mappedType?.runtimeType ?? 'unknown';
|
|
588
606
|
}
|
|
589
607
|
getPropertyDefaultValue(schemaHelper, column, propType, raw = false) {
|
|
590
608
|
const empty = raw ? 'null' : undefined;
|
|
@@ -273,12 +273,19 @@ class SchemaComparator {
|
|
|
273
273
|
*/
|
|
274
274
|
detectColumnRenamings(tableDifferences, inverseTableDiff) {
|
|
275
275
|
const renameCandidates = {};
|
|
276
|
+
const oldFKs = Object.values(tableDifferences.fromTable.getForeignKeys());
|
|
277
|
+
const newFKs = Object.values(tableDifferences.toTable.getForeignKeys());
|
|
276
278
|
for (const addedColumn of Object.values(tableDifferences.addedColumns)) {
|
|
277
279
|
for (const removedColumn of Object.values(tableDifferences.removedColumns)) {
|
|
278
280
|
const diff = this.diffColumn(addedColumn, removedColumn);
|
|
279
281
|
if (diff.size !== 0) {
|
|
280
282
|
continue;
|
|
281
283
|
}
|
|
284
|
+
const wasFK = oldFKs.some(fk => fk.columnNames.includes(removedColumn.name));
|
|
285
|
+
const isFK = newFKs.some(fk => fk.columnNames.includes(addedColumn.name));
|
|
286
|
+
if (wasFK !== isFK) {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
282
289
|
const renamedColumn = inverseTableDiff?.renamedColumns[addedColumn.name];
|
|
283
290
|
if (renamedColumn && renamedColumn?.name !== removedColumn.name) {
|
|
284
291
|
continue;
|
|
@@ -348,6 +355,12 @@ class SchemaComparator {
|
|
|
348
355
|
if (key1.referencedTableName !== key2.referencedTableName) {
|
|
349
356
|
return true;
|
|
350
357
|
}
|
|
358
|
+
if (key1.deferMode !== key2.deferMode) {
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
if (key1.localTableName === key1.referencedTableName && !this.platform.supportsMultipleCascadePaths()) {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
351
364
|
const defaultRule = ['restrict', 'no action'];
|
|
352
365
|
const rule = (key, method) => {
|
|
353
366
|
return (key[method] ?? defaultRule[0]).toLowerCase().replace(defaultRule[1], defaultRule[0]);
|
|
@@ -373,8 +386,8 @@ class SchemaComparator {
|
|
|
373
386
|
}
|
|
374
387
|
};
|
|
375
388
|
if (fromColumnType !== toColumnType &&
|
|
376
|
-
!(fromColumn.ignoreSchemaChanges?.includes('type') ||
|
|
377
|
-
|
|
389
|
+
!(fromColumn.ignoreSchemaChanges?.includes('type') || toColumn.ignoreSchemaChanges?.includes('type')) &&
|
|
390
|
+
!fromColumn.generated && !toColumn.generated) {
|
|
378
391
|
log(`'type' changed for column ${tableName}.${fromColumn.name}`, { fromColumnType, toColumnType });
|
|
379
392
|
changedProperties.add('type');
|
|
380
393
|
}
|
package/schema/SchemaHelper.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare abstract class SchemaHelper {
|
|
|
15
15
|
finalizeTable(table: Knex.TableBuilder, charset: string, collate?: string): void;
|
|
16
16
|
supportsSchemaConstraints(): boolean;
|
|
17
17
|
getPrimaryKeys(connection: AbstractSqlConnection, indexes: IndexDef[] | undefined, tableName: string, schemaName?: string): Promise<string[]>;
|
|
18
|
+
inferLengthFromColumnType(type: string): number | undefined;
|
|
18
19
|
getForeignKeys(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<Dictionary>;
|
|
19
20
|
protected getTableKey(t: Table): string;
|
|
20
21
|
getEnumDefinitions(connection: AbstractSqlConnection, checks: CheckDef[], tableName: string, schemaName?: string): Promise<Dictionary<string[]>>;
|
|
@@ -27,8 +28,9 @@ export declare abstract class SchemaHelper {
|
|
|
27
28
|
getCreateIndexSQL(tableName: string, index: IndexDef, partialExpression?: boolean): string;
|
|
28
29
|
getDropIndexSQL(tableName: string, index: IndexDef): string;
|
|
29
30
|
getRenameIndexSQL(tableName: string, index: IndexDef, oldIndexName: string): string;
|
|
31
|
+
getDropColumnsSQL(tableName: string, columns: Column[], schemaName?: string): string;
|
|
30
32
|
hasNonDefaultPrimaryKeyName(table: DatabaseTable): boolean;
|
|
31
|
-
createTableColumn(table: Knex.TableBuilder, column: Column, fromTable: DatabaseTable, changedProperties?: Set<string>, alter?: boolean): Knex.ColumnBuilder;
|
|
33
|
+
createTableColumn(table: Knex.TableBuilder, column: Column, fromTable: DatabaseTable, changedProperties?: Set<string>, alter?: boolean): Knex.ColumnBuilder | undefined;
|
|
32
34
|
configureColumn(column: Column, col: Knex.ColumnBuilder, knex: Knex, changedProperties?: Set<string>): Knex.ColumnBuilder;
|
|
33
35
|
configureColumnDefault(column: Column, col: Knex.ColumnBuilder, knex: Knex, changedProperties?: Set<string>): Knex.ColumnBuilder;
|
|
34
36
|
getPreAlterTable(tableDiff: TableDifference, safe: boolean): string;
|
|
@@ -45,6 +47,8 @@ export declare abstract class SchemaHelper {
|
|
|
45
47
|
normalizeDefaultValue(defaultValue: string, length?: number, defaultValues?: Dictionary<string[]>): string | number;
|
|
46
48
|
getCreateDatabaseSQL(name: string): string;
|
|
47
49
|
getDropDatabaseSQL(name: string): string;
|
|
50
|
+
getCreateNamespaceSQL(name: string): string;
|
|
51
|
+
getDropNamespaceSQL(name: string): string;
|
|
48
52
|
getDatabaseExistsSQL(name: string): string;
|
|
49
53
|
getDatabaseNotExistsError(dbName: string): string;
|
|
50
54
|
getManagementDbName(): string;
|
package/schema/SchemaHelper.js
CHANGED
|
@@ -29,6 +29,9 @@ class SchemaHelper {
|
|
|
29
29
|
const pks = indexes.filter(i => i.primary).map(pk => pk.columnNames);
|
|
30
30
|
return core_1.Utils.flatten(pks);
|
|
31
31
|
}
|
|
32
|
+
inferLengthFromColumnType(type) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
32
35
|
async getForeignKeys(connection, tableName, schemaName) {
|
|
33
36
|
const fks = await connection.execute(this.getForeignKeysSQL(tableName, schemaName));
|
|
34
37
|
return this.mapForeignKeys(fks, tableName, schemaName);
|
|
@@ -99,6 +102,11 @@ class SchemaHelper {
|
|
|
99
102
|
getRenameIndexSQL(tableName, index, oldIndexName) {
|
|
100
103
|
return [this.getDropIndexSQL(tableName, { ...index, keyName: oldIndexName }), this.getCreateIndexSQL(tableName, index)].join(';\n');
|
|
101
104
|
}
|
|
105
|
+
getDropColumnsSQL(tableName, columns, schemaName) {
|
|
106
|
+
const name = this.platform.quoteIdentifier((schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName);
|
|
107
|
+
const drops = columns.map(column => `drop column ${this.platform.quoteIdentifier(column.name)}`).join(', ');
|
|
108
|
+
return `alter table ${name} ${drops}`;
|
|
109
|
+
}
|
|
102
110
|
hasNonDefaultPrimaryKeyName(table) {
|
|
103
111
|
const pkIndex = table.getPrimaryKey();
|
|
104
112
|
if (!pkIndex || !this.platform.supportsCustomPrimaryKeyNames()) {
|
|
@@ -199,6 +207,7 @@ class SchemaHelper {
|
|
|
199
207
|
referencedColumnNames: [fk.referenced_column_name],
|
|
200
208
|
updateRule: fk.update_rule.toLowerCase(),
|
|
201
209
|
deleteRule: fk.delete_rule.toLowerCase(),
|
|
210
|
+
deferMode: fk.defer_mode,
|
|
202
211
|
};
|
|
203
212
|
}
|
|
204
213
|
return ret;
|
|
@@ -220,10 +229,19 @@ class SchemaHelper {
|
|
|
220
229
|
return norm[0].replace('(?)', length != null ? `(${length})` : '');
|
|
221
230
|
}
|
|
222
231
|
getCreateDatabaseSQL(name) {
|
|
223
|
-
|
|
232
|
+
// two line breaks to force separate execution
|
|
233
|
+
return `create database ${name};\n\nuse ${name}`;
|
|
224
234
|
}
|
|
225
235
|
getDropDatabaseSQL(name) {
|
|
226
|
-
return `drop database if exists ${name}`;
|
|
236
|
+
return `drop database if exists ${this.platform.quoteIdentifier(name)}`;
|
|
237
|
+
}
|
|
238
|
+
/* istanbul ignore next */
|
|
239
|
+
getCreateNamespaceSQL(name) {
|
|
240
|
+
return `create schema if not exists ${this.platform.quoteIdentifier(name)}`;
|
|
241
|
+
}
|
|
242
|
+
/* istanbul ignore next */
|
|
243
|
+
getDropNamespaceSQL(name) {
|
|
244
|
+
return `drop schema if exists ${this.platform.quoteIdentifier(name)}`;
|
|
227
245
|
}
|
|
228
246
|
getDatabaseExistsSQL(name) {
|
|
229
247
|
return `select 1 from information_schema.schemata where schema_name = '${name}'`;
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { AbstractSchemaGenerator, type
|
|
1
|
+
import { AbstractSchemaGenerator, type ClearDatabaseOptions, type CreateSchemaOptions, type DropSchemaOptions, type EnsureDatabaseOptions, type ISchemaGenerator, type MikroORM, type Transaction, type UpdateSchemaOptions } from '@mikro-orm/core';
|
|
2
2
|
import type { SchemaDifference } from '../typings';
|
|
3
3
|
import { DatabaseSchema } from './DatabaseSchema';
|
|
4
4
|
import type { AbstractSqlDriver } from '../AbstractSqlDriver';
|
|
5
5
|
export declare class SqlSchemaGenerator extends AbstractSchemaGenerator<AbstractSqlDriver> implements ISchemaGenerator {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
protected readonly helper: import("./SchemaHelper").SchemaHelper;
|
|
7
|
+
protected readonly options: {
|
|
8
|
+
disableForeignKeys?: boolean | undefined;
|
|
9
|
+
createForeignKeyConstraints?: boolean | undefined;
|
|
10
|
+
ignoreSchema?: string[] | undefined;
|
|
11
|
+
managementDbName?: string | undefined;
|
|
12
|
+
};
|
|
8
13
|
protected lastEnsuredDatabase?: string;
|
|
9
14
|
static register(orm: MikroORM): void;
|
|
10
15
|
createSchema(options?: CreateSchemaOptions): Promise<void>;
|
|
@@ -15,6 +20,8 @@ export declare class SqlSchemaGenerator extends AbstractSchemaGenerator<Abstract
|
|
|
15
20
|
getTargetSchema(schema?: string): DatabaseSchema;
|
|
16
21
|
getCreateSchemaSQL(options?: CreateSchemaOptions): Promise<string>;
|
|
17
22
|
dropSchema(options?: DropSchemaOptions): Promise<void>;
|
|
23
|
+
createNamespace(name: string): Promise<void>;
|
|
24
|
+
dropNamespace(name: string): Promise<void>;
|
|
18
25
|
clearDatabase(options?: ClearDatabaseOptions): Promise<void>;
|
|
19
26
|
getDropSchemaSQL(options?: Omit<DropSchemaOptions, 'dropDb'>): Promise<string>;
|
|
20
27
|
private getSchemaName;
|
|
@@ -43,10 +50,11 @@ export declare class SqlSchemaGenerator extends AbstractSchemaGenerator<Abstract
|
|
|
43
50
|
/**
|
|
44
51
|
* creates new database and connects to it
|
|
45
52
|
*/
|
|
46
|
-
createDatabase(name
|
|
53
|
+
createDatabase(name?: string): Promise<void>;
|
|
47
54
|
dropDatabase(name?: string): Promise<void>;
|
|
48
55
|
execute(sql: string, options?: {
|
|
49
56
|
wrap?: boolean;
|
|
57
|
+
ctx?: Transaction;
|
|
50
58
|
}): Promise<void>;
|
|
51
59
|
private wrapSchema;
|
|
52
60
|
private createSchemaBuilder;
|