@mikro-orm/sql 7.1.0-dev.9 → 7.1.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/AbstractSqlConnection.d.ts +1 -1
- package/AbstractSqlConnection.js +27 -6
- package/AbstractSqlDriver.d.ts +15 -1
- package/AbstractSqlDriver.js +143 -26
- package/AbstractSqlPlatform.d.ts +15 -3
- package/AbstractSqlPlatform.js +25 -7
- package/PivotCollectionPersister.d.ts +2 -2
- package/PivotCollectionPersister.js +6 -1
- package/README.md +2 -1
- package/SqlEntityManager.d.ts +44 -5
- package/SqlEntityManager.js +41 -6
- package/SqlMikroORM.d.ts +23 -0
- package/SqlMikroORM.js +23 -0
- package/dialects/mysql/BaseMySqlPlatform.d.ts +3 -5
- package/dialects/mysql/BaseMySqlPlatform.js +6 -10
- package/dialects/mysql/MySqlSchemaHelper.d.ts +16 -3
- package/dialects/mysql/MySqlSchemaHelper.js +197 -49
- package/dialects/oracledb/OracleDialect.d.ts +1 -1
- package/dialects/oracledb/OracleDialect.js +2 -1
- package/dialects/postgresql/BasePostgreSqlEntityManager.d.ts +19 -0
- package/dialects/postgresql/BasePostgreSqlEntityManager.js +24 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +11 -5
- package/dialects/postgresql/BasePostgreSqlPlatform.js +75 -17
- package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +31 -1
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +269 -28
- package/dialects/postgresql/index.d.ts +2 -0
- package/dialects/postgresql/index.js +2 -0
- package/dialects/postgresql/typeOverrides.d.ts +14 -0
- package/dialects/postgresql/typeOverrides.js +12 -0
- package/dialects/sqlite/SqlitePlatform.d.ts +2 -1
- package/dialects/sqlite/SqlitePlatform.js +4 -0
- package/dialects/sqlite/SqliteSchemaHelper.d.ts +4 -1
- package/dialects/sqlite/SqliteSchemaHelper.js +49 -19
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/package.json +4 -4
- package/plugin/transformer.d.ts +11 -3
- package/plugin/transformer.js +138 -29
- package/query/CriteriaNode.d.ts +1 -1
- package/query/CriteriaNode.js +2 -2
- package/query/ObjectCriteriaNode.js +1 -1
- package/query/QueryBuilder.d.ts +42 -1
- package/query/QueryBuilder.js +78 -7
- package/schema/DatabaseSchema.d.ts +29 -2
- package/schema/DatabaseSchema.js +131 -4
- package/schema/DatabaseTable.d.ts +14 -1
- package/schema/DatabaseTable.js +165 -32
- package/schema/SchemaComparator.d.ts +18 -0
- package/schema/SchemaComparator.js +196 -1
- package/schema/SchemaHelper.d.ts +67 -1
- package/schema/SchemaHelper.js +255 -25
- package/schema/SqlSchemaGenerator.d.ts +2 -2
- package/schema/SqlSchemaGenerator.js +40 -10
- package/schema/partitioning.d.ts +13 -0
- package/schema/partitioning.js +326 -0
- package/typings.d.ts +59 -5
package/query/QueryBuilder.js
CHANGED
|
@@ -35,6 +35,7 @@ export class QueryBuilder {
|
|
|
35
35
|
#state = _a.createDefaultState();
|
|
36
36
|
#helper;
|
|
37
37
|
#query;
|
|
38
|
+
#abortOptions;
|
|
38
39
|
/** @internal */
|
|
39
40
|
static createDefaultState() {
|
|
40
41
|
return {
|
|
@@ -721,6 +722,12 @@ export class QueryBuilder {
|
|
|
721
722
|
hasFlag(flag) {
|
|
722
723
|
return this.#state.flags.has(flag);
|
|
723
724
|
}
|
|
725
|
+
/** @internal */
|
|
726
|
+
setPartitionLimit(opts) {
|
|
727
|
+
this.ensureNotFinalized();
|
|
728
|
+
this.#state.partitionLimit = opts;
|
|
729
|
+
return this;
|
|
730
|
+
}
|
|
724
731
|
cache(config = true) {
|
|
725
732
|
this.ensureNotFinalized();
|
|
726
733
|
this.#state.cache = config;
|
|
@@ -825,6 +832,9 @@ export class QueryBuilder {
|
|
|
825
832
|
this.helper.getLockSQL(qb, this.#state.lockMode, this.#state.lockTables, this.#state.joins);
|
|
826
833
|
}
|
|
827
834
|
this.processReturningStatement(qb, this.mainAlias.meta, this.#state.insertSubQuery ? undefined : this.#state.data, this.#state.returning);
|
|
835
|
+
if (this.#state.partitionLimit) {
|
|
836
|
+
return (this.#query.qb = this.wrapPartitionLimitSubQuery(qb));
|
|
837
|
+
}
|
|
828
838
|
return (this.#query.qb = qb);
|
|
829
839
|
}
|
|
830
840
|
processReturningStatement(qb, meta, data, returning) {
|
|
@@ -1001,7 +1011,7 @@ export class QueryBuilder {
|
|
|
1001
1011
|
if (cached?.data !== undefined) {
|
|
1002
1012
|
return cached.data;
|
|
1003
1013
|
}
|
|
1004
|
-
const loggerContext = { id: this.em?.id, ...this.loggerContext };
|
|
1014
|
+
const loggerContext = { id: this.em?.id, ...this.loggerContext, ...this.#abortOptions };
|
|
1005
1015
|
const res = await this.getConnection().execute(query.sql, query.params, method, this.context, loggerContext);
|
|
1006
1016
|
const meta = this.mainAlias.meta;
|
|
1007
1017
|
if (!options.mapResults || !meta) {
|
|
@@ -1055,9 +1065,10 @@ export class QueryBuilder {
|
|
|
1055
1065
|
options ??= {};
|
|
1056
1066
|
options.mergeResults ??= true;
|
|
1057
1067
|
options.mapResults ??= true;
|
|
1068
|
+
const chunkSize = options.chunkSize ?? 100;
|
|
1058
1069
|
const query = this.toQuery();
|
|
1059
|
-
const loggerContext = { id: this.em?.id, ...this.loggerContext };
|
|
1060
|
-
const res = this.getConnection().stream(query.sql, query.params, this.context, loggerContext);
|
|
1070
|
+
const loggerContext = { id: this.em?.id, ...this.loggerContext, ...this.#abortOptions };
|
|
1071
|
+
const res = this.getConnection().stream(query.sql, query.params, this.context, loggerContext, chunkSize);
|
|
1061
1072
|
const meta = this.mainAlias.meta;
|
|
1062
1073
|
if (options.rawResults || !meta) {
|
|
1063
1074
|
yield* res;
|
|
@@ -1280,6 +1291,7 @@ export class QueryBuilder {
|
|
|
1280
1291
|
qb.#state.finalized = false;
|
|
1281
1292
|
qb.#query = undefined;
|
|
1282
1293
|
qb.#helper = qb.createQueryBuilderHelper();
|
|
1294
|
+
qb.#abortOptions = this.#abortOptions;
|
|
1283
1295
|
return qb;
|
|
1284
1296
|
}
|
|
1285
1297
|
/**
|
|
@@ -1295,6 +1307,13 @@ export class QueryBuilder {
|
|
|
1295
1307
|
this.loggerContext ??= {};
|
|
1296
1308
|
return this.loggerContext;
|
|
1297
1309
|
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Configures cancellation behavior for this query builder. The signal is forwarded to the
|
|
1312
|
+
* underlying database client; see {@apilink AbortQueryOptions} for the available strategies.
|
|
1313
|
+
*/
|
|
1314
|
+
setAbortOptions(options) {
|
|
1315
|
+
this.#abortOptions = options;
|
|
1316
|
+
}
|
|
1298
1317
|
fromVirtual(meta) {
|
|
1299
1318
|
if (typeof meta.expression === 'string') {
|
|
1300
1319
|
return `(${meta.expression}) as ${this.platform.quoteIdentifier(this.alias)}`;
|
|
@@ -1385,6 +1404,7 @@ export class QueryBuilder {
|
|
|
1385
1404
|
aliased: [QueryType.SELECT, QueryType.COUNT].includes(this.type),
|
|
1386
1405
|
});
|
|
1387
1406
|
const criteriaNode = CriteriaNodeFactory.createNode(this.metadata, prop.targetMeta.class, cond);
|
|
1407
|
+
const joinCountBefore = Object.keys(this.#state.joins).length;
|
|
1388
1408
|
cond = criteriaNode.process(this, { ignoreBranching: true, alias });
|
|
1389
1409
|
let aliasedName = `${fromAlias}.${prop.name}#${alias}`;
|
|
1390
1410
|
path ??= `${Object.values(this.#state.joins).find(j => j.alias === fromAlias)?.path ?? Utils.className(entityName)}.${prop.name}`;
|
|
@@ -1414,6 +1434,20 @@ export class QueryBuilder {
|
|
|
1414
1434
|
this.#state.joins[aliasedName] = this.helper.joinManyToOneReference(prop, ownerAlias, alias, type, cond, schema);
|
|
1415
1435
|
this.#state.joins[aliasedName].path ??= path;
|
|
1416
1436
|
}
|
|
1437
|
+
// auto-joins added by cond processing that depend on the new alias would otherwise produce a
|
|
1438
|
+
// forward reference (the auto-join's ON refers to alias, while alias's ON refers back to it);
|
|
1439
|
+
// fold them into the new join so both aliases share scope in the outer ON clause (issue #7681)
|
|
1440
|
+
const condJoin = this.#state.joins[aliasedName];
|
|
1441
|
+
const joinKeys = Object.keys(this.#state.joins);
|
|
1442
|
+
for (let i = joinCountBefore; i < joinKeys.length; i++) {
|
|
1443
|
+
const j = this.#state.joins[joinKeys[i]];
|
|
1444
|
+
if (j === condJoin || j.ownerAlias !== alias) {
|
|
1445
|
+
continue;
|
|
1446
|
+
}
|
|
1447
|
+
const nested = (condJoin.nested ??= new Set());
|
|
1448
|
+
j.type = j.type === JoinType.innerJoin ? JoinType.nestedInnerJoin : JoinType.nestedLeftJoin;
|
|
1449
|
+
nested.add(j);
|
|
1450
|
+
}
|
|
1417
1451
|
return { prop, key: aliasedName };
|
|
1418
1452
|
}
|
|
1419
1453
|
prepareFields(fields, type = 'where', schema) {
|
|
@@ -1743,13 +1777,13 @@ export class QueryBuilder {
|
|
|
1743
1777
|
}
|
|
1744
1778
|
const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.get(cls));
|
|
1745
1779
|
const children = [];
|
|
1746
|
-
const lookUpChildren = (ret,
|
|
1747
|
-
const children = types.filter(meta2 => meta2.extends ===
|
|
1748
|
-
children.forEach(m => lookUpChildren(ret, m
|
|
1780
|
+
const lookUpChildren = (ret, parent) => {
|
|
1781
|
+
const children = types.filter(meta2 => meta2.extends && this.metadata.find(meta2.extends) === parent);
|
|
1782
|
+
children.forEach(m => lookUpChildren(ret, m));
|
|
1749
1783
|
ret.push(...children.filter(c => c.discriminatorValue));
|
|
1750
1784
|
return children;
|
|
1751
1785
|
};
|
|
1752
|
-
lookUpChildren(children, meta
|
|
1786
|
+
lookUpChildren(children, meta);
|
|
1753
1787
|
this.andWhere({
|
|
1754
1788
|
[meta.root.discriminatorColumn]: children.length > 0
|
|
1755
1789
|
? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] }
|
|
@@ -1899,6 +1933,9 @@ export class QueryBuilder {
|
|
|
1899
1933
|
(this.#state.limit > 0 || this.#state.offset > 0)) {
|
|
1900
1934
|
this.wrapPaginateSubQuery(meta);
|
|
1901
1935
|
}
|
|
1936
|
+
if (this.#state.partitionLimit) {
|
|
1937
|
+
this.preparePartitionLimit();
|
|
1938
|
+
}
|
|
1902
1939
|
if (meta &&
|
|
1903
1940
|
(this.#state.flags.has(QueryFlag.UPDATE_SUB_QUERY) || this.#state.flags.has(QueryFlag.DELETE_SUB_QUERY))) {
|
|
1904
1941
|
this.wrapModifySubQuery(meta);
|
|
@@ -2118,6 +2155,40 @@ export class QueryBuilder {
|
|
|
2118
2155
|
[Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: raw(sql, params) },
|
|
2119
2156
|
});
|
|
2120
2157
|
}
|
|
2158
|
+
/**
|
|
2159
|
+
* Wraps the inner query (which has ROW_NUMBER in SELECT) with an outer query
|
|
2160
|
+
* that filters by the __rn column to apply per-parent limiting.
|
|
2161
|
+
*/
|
|
2162
|
+
wrapPartitionLimitSubQuery(innerQb) {
|
|
2163
|
+
const { limit, offset = 0 } = this.#state.partitionLimit;
|
|
2164
|
+
const rnCol = this.platform.quoteIdentifier('__rn');
|
|
2165
|
+
innerQb.as(this.mainAlias.aliasName);
|
|
2166
|
+
const outerQb = this.platform.createNativeQueryBuilder();
|
|
2167
|
+
outerQb.select('*').from(innerQb);
|
|
2168
|
+
outerQb.where(`${rnCol} > ? and ${rnCol} <= ?`, [offset, offset + limit]);
|
|
2169
|
+
outerQb.orderBy(rnCol);
|
|
2170
|
+
return outerQb;
|
|
2171
|
+
}
|
|
2172
|
+
/**
|
|
2173
|
+
* Adds ROW_NUMBER() OVER (PARTITION BY ...) to the SELECT list and prepares
|
|
2174
|
+
* the query state for per-parent limiting. The actual wrapping into a subquery
|
|
2175
|
+
* with __rn filtering happens in getNativeQuery().
|
|
2176
|
+
*/
|
|
2177
|
+
preparePartitionLimit() {
|
|
2178
|
+
const { partitionBy } = this.#state.partitionLimit;
|
|
2179
|
+
// `partitionBy` is always a declared property name, so mapper returns a string here.
|
|
2180
|
+
const partitionCol = this.helper.mapper(partitionBy, this.type, undefined, null);
|
|
2181
|
+
const quotedPartition = partitionCol
|
|
2182
|
+
.split('.')
|
|
2183
|
+
.map(e => this.platform.quoteIdentifier(e))
|
|
2184
|
+
.join('.');
|
|
2185
|
+
const queryOrder = this.helper.getQueryOrder(this.type, this.#state.orderBy, this.#state.populateMap, this.#state.collation);
|
|
2186
|
+
const orderBySql = queryOrder.length > 0 ? Utils.unique(queryOrder).join(', ') : quotedPartition;
|
|
2187
|
+
const rnAlias = this.platform.quoteIdentifier('__rn');
|
|
2188
|
+
this.#state.fields.push(raw(`row_number() over (partition by ${quotedPartition} order by ${orderBySql}) as ${rnAlias}`));
|
|
2189
|
+
// Moved into the OVER clause; outer query re-applies via wrapPartitionLimitSubQuery
|
|
2190
|
+
this.#state.orderBy = [];
|
|
2191
|
+
}
|
|
2121
2192
|
/**
|
|
2122
2193
|
* Computes the set of populate paths from the _populate hints.
|
|
2123
2194
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type Configuration, type Dictionary, type EntityMetadata, type Transaction } from '@mikro-orm/core';
|
|
1
|
+
import { type Configuration, type Dictionary, type EntityMetadata, type Routine, type Transaction, type Type } from '@mikro-orm/core';
|
|
2
2
|
import { DatabaseTable } from './DatabaseTable.js';
|
|
3
3
|
import type { AbstractSqlConnection } from '../AbstractSqlConnection.js';
|
|
4
|
-
import type { DatabaseView } from '../typings.js';
|
|
4
|
+
import type { DatabaseView, SqlRoutineDef } from '../typings.js';
|
|
5
5
|
import type { AbstractSqlPlatform } from '../AbstractSqlPlatform.js';
|
|
6
6
|
/**
|
|
7
7
|
* @internal
|
|
@@ -24,6 +24,8 @@ export declare class DatabaseSchema {
|
|
|
24
24
|
setViews(views: DatabaseView[]): void;
|
|
25
25
|
getView(name: string): DatabaseView | undefined;
|
|
26
26
|
hasView(name: string): boolean;
|
|
27
|
+
addRoutine(routine: SqlRoutineDef): SqlRoutineDef;
|
|
28
|
+
getRoutines(): SqlRoutineDef[];
|
|
27
29
|
setNativeEnums(nativeEnums: Dictionary<{
|
|
28
30
|
name: string;
|
|
29
31
|
schema?: string;
|
|
@@ -43,7 +45,32 @@ export declare class DatabaseSchema {
|
|
|
43
45
|
hasNativeEnum(name: string): boolean;
|
|
44
46
|
getNamespaces(): string[];
|
|
45
47
|
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>;
|
|
48
|
+
/** Separate from `create()` so the comparator only pays for routine introspection when the user actually defined routines. SQLite/libSQL helpers return []. */
|
|
49
|
+
loadRoutines(connection: AbstractSqlConnection, platform: AbstractSqlPlatform, schemas?: string[]): Promise<void>;
|
|
46
50
|
static fromMetadata(metadata: EntityMetadata[], platform: AbstractSqlPlatform, config: Configuration, schemaName?: string, em?: any): DatabaseSchema;
|
|
51
|
+
/** Separate from {@link fromMetadata} so the comparator only walks routines when the user defined any. */
|
|
52
|
+
addRoutinesFromMetadata(routines: readonly Routine[], platform: AbstractSqlPlatform, em?: any): void;
|
|
53
|
+
/**
|
|
54
|
+
* Normalises a routine's `returns` config to the `{ type, runtimeType, nullable }` shape the
|
|
55
|
+
* DDL side and introspection-comparator both consume. Supports both `{ type: SomeType }`
|
|
56
|
+
* (Type drives column + runtime types) and `{ runtimeType, columnType, ... }` (explicit).
|
|
57
|
+
*
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
60
|
+
static normaliseRoutineReturns(returns: Routine['returns'], platform: AbstractSqlPlatform): {
|
|
61
|
+
type: string;
|
|
62
|
+
runtimeType: string;
|
|
63
|
+
nullable?: boolean;
|
|
64
|
+
} | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* Maps a routine param's declared `type` to a dialect-specific SQL column type. A `Type`
|
|
67
|
+
* instance (when the user passed a `Type` class/instance at `type`) routes through
|
|
68
|
+
* `Type.getColumnType` so its dialect-aware mapping wins. String aliases (`'string'`,
|
|
69
|
+
* `'number'`, …) go through the platform's type registry; literal SQL types pass through.
|
|
70
|
+
*
|
|
71
|
+
* @internal
|
|
72
|
+
*/
|
|
73
|
+
static resolveRoutineColumnType(type: string | Type<unknown>, platform: AbstractSqlPlatform): string;
|
|
47
74
|
private static getViewDefinition;
|
|
48
75
|
private static getSchemaName;
|
|
49
76
|
/**
|
package/schema/DatabaseSchema.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ReferenceKind, isRaw, } from '@mikro-orm/core';
|
|
2
2
|
import { DatabaseTable } from './DatabaseTable.js';
|
|
3
|
+
import { getTablePartitioning } from './partitioning.js';
|
|
3
4
|
/**
|
|
4
5
|
* @internal
|
|
5
6
|
*/
|
|
@@ -7,6 +8,7 @@ export class DatabaseSchema {
|
|
|
7
8
|
name;
|
|
8
9
|
#tables = [];
|
|
9
10
|
#views = [];
|
|
11
|
+
#routines = [];
|
|
10
12
|
#namespaces = new Set();
|
|
11
13
|
#nativeEnums = {}; // for postgres
|
|
12
14
|
#platform;
|
|
@@ -64,6 +66,16 @@ export class DatabaseSchema {
|
|
|
64
66
|
hasView(name) {
|
|
65
67
|
return !!this.getView(name);
|
|
66
68
|
}
|
|
69
|
+
addRoutine(routine) {
|
|
70
|
+
this.#routines.push(routine);
|
|
71
|
+
if (routine.schema != null) {
|
|
72
|
+
this.#namespaces.add(routine.schema);
|
|
73
|
+
}
|
|
74
|
+
return routine;
|
|
75
|
+
}
|
|
76
|
+
getRoutines() {
|
|
77
|
+
return this.#routines;
|
|
78
|
+
}
|
|
67
79
|
setNativeEnums(nativeEnums) {
|
|
68
80
|
this.#nativeEnums = nativeEnums;
|
|
69
81
|
for (const nativeEnum of Object.values(nativeEnums)) {
|
|
@@ -110,6 +122,10 @@ export class DatabaseSchema {
|
|
|
110
122
|
}
|
|
111
123
|
return schema;
|
|
112
124
|
}
|
|
125
|
+
/** Separate from `create()` so the comparator only pays for routine introspection when the user actually defined routines. SQLite/libSQL helpers return []. */
|
|
126
|
+
async loadRoutines(connection, platform, schemas = []) {
|
|
127
|
+
this.#routines = await platform.getSchemaHelper().getAllRoutines(connection, schemas);
|
|
128
|
+
}
|
|
113
129
|
static fromMetadata(metadata, platform, config, schemaName, em) {
|
|
114
130
|
const schema = new DatabaseSchema(platform, schemaName ?? config.get('schema'));
|
|
115
131
|
const nativeEnums = {};
|
|
@@ -173,6 +189,9 @@ export class DatabaseSchema {
|
|
|
173
189
|
}
|
|
174
190
|
const table = schema.addTable(meta.collection, this.getSchemaName(meta, config, schemaName));
|
|
175
191
|
table.comment = meta.comment;
|
|
192
|
+
if (meta.partitionBy) {
|
|
193
|
+
table.setPartitioning(getTablePartitioning(meta, this.getSchemaName(meta, config, schemaName), id => platform.quoteIdentifier(id)));
|
|
194
|
+
}
|
|
176
195
|
// For TPT child entities, only use ownProps (properties defined in this entity only)
|
|
177
196
|
// For all other entities (including TPT root), use all props
|
|
178
197
|
const propsToProcess = meta.inheritanceType === 'tpt' && meta.tptParent && meta.ownProps ? meta.ownProps : meta.props;
|
|
@@ -237,6 +256,110 @@ export class DatabaseSchema {
|
|
|
237
256
|
}
|
|
238
257
|
return schema;
|
|
239
258
|
}
|
|
259
|
+
/** Separate from {@link fromMetadata} so the comparator only walks routines when the user defined any. */
|
|
260
|
+
addRoutinesFromMetadata(routines, platform, em) {
|
|
261
|
+
const resolveBody = (raw) => {
|
|
262
|
+
if (raw == null) {
|
|
263
|
+
return undefined;
|
|
264
|
+
}
|
|
265
|
+
if (typeof raw === 'string') {
|
|
266
|
+
return raw;
|
|
267
|
+
}
|
|
268
|
+
if (isRaw(raw)) {
|
|
269
|
+
return platform.formatQuery(raw.sql, raw.params);
|
|
270
|
+
}
|
|
271
|
+
return undefined;
|
|
272
|
+
};
|
|
273
|
+
const helper = platform.getSchemaHelper();
|
|
274
|
+
for (const routine of routines) {
|
|
275
|
+
const paramMap = routine.params.reduce((o, p) => {
|
|
276
|
+
o[p.name] = helper.routineParamReference(p.name);
|
|
277
|
+
return o;
|
|
278
|
+
}, {});
|
|
279
|
+
const evaluated = typeof routine.body === 'function' ? routine.body(paramMap, em) : routine.body;
|
|
280
|
+
const body = resolveBody(evaluated);
|
|
281
|
+
const returns = DatabaseSchema.normaliseRoutineReturns(routine.returns, platform);
|
|
282
|
+
this.addRoutine({
|
|
283
|
+
name: routine.name,
|
|
284
|
+
// MySQL has no schema namespace for routines, so leave undefined to align with the introspection side.
|
|
285
|
+
schema: routine.schema ?? (platform.getDefaultSchemaName() != null ? this.name : undefined),
|
|
286
|
+
type: routine.type,
|
|
287
|
+
language: routine.language,
|
|
288
|
+
comment: routine.comment,
|
|
289
|
+
security: routine.security,
|
|
290
|
+
definer: routine.definer,
|
|
291
|
+
deterministic: routine.deterministic,
|
|
292
|
+
dataAccess: routine.dataAccess,
|
|
293
|
+
body,
|
|
294
|
+
expression: routine.expression,
|
|
295
|
+
ignoreSchemaChanges: routine.ignoreSchemaChanges,
|
|
296
|
+
params: routine.params.map(p => ({
|
|
297
|
+
name: p.name,
|
|
298
|
+
type: DatabaseSchema.resolveRoutineColumnType(p.type, platform),
|
|
299
|
+
direction: helper.normaliseRoutineParamDirection(p.direction),
|
|
300
|
+
nullable: p.nullable,
|
|
301
|
+
defaultRaw: p.defaultRaw,
|
|
302
|
+
})),
|
|
303
|
+
returns,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Normalises a routine's `returns` config to the `{ type, runtimeType, nullable }` shape the
|
|
309
|
+
* DDL side and introspection-comparator both consume. Supports both `{ type: SomeType }`
|
|
310
|
+
* (Type drives column + runtime types) and `{ runtimeType, columnType, ... }` (explicit).
|
|
311
|
+
*
|
|
312
|
+
* @internal
|
|
313
|
+
*/
|
|
314
|
+
static normaliseRoutineReturns(returns, platform) {
|
|
315
|
+
if (!returns || typeof returns !== 'object') {
|
|
316
|
+
return undefined;
|
|
317
|
+
}
|
|
318
|
+
if ('runtimeType' in returns) {
|
|
319
|
+
return {
|
|
320
|
+
type: (returns.columnType ?? returns.runtimeType),
|
|
321
|
+
runtimeType: returns.runtimeType,
|
|
322
|
+
nullable: returns.nullable,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
if ('type' in returns && returns.type) {
|
|
326
|
+
const instance = typeof returns.type === 'function' ? new returns.type() : returns.type;
|
|
327
|
+
return {
|
|
328
|
+
type: DatabaseSchema.resolveRoutineColumnType(instance, platform),
|
|
329
|
+
runtimeType: instance.runtimeType,
|
|
330
|
+
nullable: returns.nullable,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Maps a routine param's declared `type` to a dialect-specific SQL column type. A `Type`
|
|
337
|
+
* instance (when the user passed a `Type` class/instance at `type`) routes through
|
|
338
|
+
* `Type.getColumnType` so its dialect-aware mapping wins. String aliases (`'string'`,
|
|
339
|
+
* `'number'`, …) go through the platform's type registry; literal SQL types pass through.
|
|
340
|
+
*
|
|
341
|
+
* @internal
|
|
342
|
+
*/
|
|
343
|
+
static resolveRoutineColumnType(type, platform) {
|
|
344
|
+
if (typeof type !== 'string') {
|
|
345
|
+
return type.getColumnType({ columnTypes: [], runtimeType: 'any' }, platform);
|
|
346
|
+
}
|
|
347
|
+
const lower = type.toLowerCase();
|
|
348
|
+
const aliases = {
|
|
349
|
+
string: 'string',
|
|
350
|
+
number: 'integer',
|
|
351
|
+
bigint: 'bigint',
|
|
352
|
+
boolean: 'boolean',
|
|
353
|
+
date: 'datetime',
|
|
354
|
+
buffer: 'blob',
|
|
355
|
+
};
|
|
356
|
+
const mappedKey = aliases[lower];
|
|
357
|
+
if (!mappedKey) {
|
|
358
|
+
return type;
|
|
359
|
+
}
|
|
360
|
+
const t = platform.getMappedType(mappedKey);
|
|
361
|
+
return t.getColumnType({ type: mappedKey, length: undefined }, platform);
|
|
362
|
+
}
|
|
240
363
|
static getViewDefinition(meta, em, platform) {
|
|
241
364
|
if (typeof meta.expression === 'string') {
|
|
242
365
|
return meta.expression;
|
|
@@ -337,12 +460,16 @@ export class DatabaseSchema {
|
|
|
337
460
|
(prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner));
|
|
338
461
|
}
|
|
339
462
|
toJSON() {
|
|
463
|
+
// locale-independent comparison so the snapshot is stable across machines
|
|
464
|
+
const byString = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
465
|
+
const tableKey = (t) => `${t.schema ?? ''}.${t.name}`;
|
|
466
|
+
const byTable = (a, b) => byString(tableKey(a), tableKey(b));
|
|
340
467
|
return {
|
|
341
468
|
name: this.name,
|
|
342
|
-
namespaces: [...this.#namespaces],
|
|
343
|
-
tables: this.#tables,
|
|
344
|
-
views: this.#views,
|
|
345
|
-
nativeEnums: this.#nativeEnums,
|
|
469
|
+
namespaces: [...this.#namespaces].sort(),
|
|
470
|
+
tables: [...this.#tables].sort(byTable),
|
|
471
|
+
views: [...this.#views].sort(byTable),
|
|
472
|
+
nativeEnums: Object.fromEntries(Object.entries(this.#nativeEnums).sort(([a], [b]) => byString(a, b))),
|
|
346
473
|
};
|
|
347
474
|
}
|
|
348
475
|
prune(schema, wildcardSchemaTables) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Configuration, type DeferMode, type Dictionary, type EntityMetadata, type EntityProperty, type IndexCallback, type NamingStrategy } from '@mikro-orm/core';
|
|
2
2
|
import type { SchemaHelper } from './SchemaHelper.js';
|
|
3
|
-
import type { CheckDef, Column, ForeignKey, IndexDef, SqlTriggerDef } from '../typings.js';
|
|
3
|
+
import type { CheckDef, Column, ForeignKey, IndexDef, TablePartitioning, SqlTriggerDef } from '../typings.js';
|
|
4
4
|
import type { AbstractSqlPlatform } from '../AbstractSqlPlatform.js';
|
|
5
5
|
/**
|
|
6
6
|
* @internal
|
|
@@ -15,6 +15,14 @@ export declare class DatabaseTable {
|
|
|
15
15
|
items: string[];
|
|
16
16
|
}>;
|
|
17
17
|
comment?: string;
|
|
18
|
+
partitioning?: TablePartitioning;
|
|
19
|
+
/**
|
|
20
|
+
* Effective collation the column defaults to when no explicit `COLLATE` is set on a column.
|
|
21
|
+
* For MySQL/MariaDB this is the table collation; for PostgreSQL and MSSQL this is the database default;
|
|
22
|
+
* SQLite has no configurable default. Used by `SchemaComparator.diffCollation` to avoid flapping
|
|
23
|
+
* when a property explicitly names the default collation.
|
|
24
|
+
*/
|
|
25
|
+
collation?: string;
|
|
18
26
|
constructor(platform: AbstractSqlPlatform, name: string, schema?: string | undefined);
|
|
19
27
|
getQuotedName(): string;
|
|
20
28
|
getColumns(): Column[];
|
|
@@ -22,6 +30,9 @@ export declare class DatabaseTable {
|
|
|
22
30
|
removeColumn(name: string): void;
|
|
23
31
|
getIndexes(): IndexDef[];
|
|
24
32
|
getChecks(): CheckDef[];
|
|
33
|
+
getPartitioning(): TablePartitioning | undefined;
|
|
34
|
+
/** @internal */
|
|
35
|
+
setPartitioning(partitioning?: TablePartitioning): void;
|
|
25
36
|
getTriggers(): SqlTriggerDef[];
|
|
26
37
|
/** @internal */
|
|
27
38
|
setIndexes(indexes: IndexDef[]): void;
|
|
@@ -67,6 +78,7 @@ export declare class DatabaseTable {
|
|
|
67
78
|
name?: string;
|
|
68
79
|
type?: string;
|
|
69
80
|
expression?: string | IndexCallback<any>;
|
|
81
|
+
where?: string | Dictionary;
|
|
70
82
|
deferMode?: DeferMode | `${DeferMode}`;
|
|
71
83
|
options?: Dictionary;
|
|
72
84
|
columns?: {
|
|
@@ -82,6 +94,7 @@ export declare class DatabaseTable {
|
|
|
82
94
|
disabled?: boolean;
|
|
83
95
|
clustered?: boolean;
|
|
84
96
|
}, type: 'index' | 'unique' | 'primary'): void;
|
|
97
|
+
private processIndexWhere;
|
|
85
98
|
addCheck(check: CheckDef): void;
|
|
86
99
|
addTrigger(trigger: SqlTriggerDef): void;
|
|
87
100
|
toJSON(): Dictionary;
|