@mikro-orm/sql 7.1.9-dev.23 → 7.1.9-dev.25
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
CHANGED
|
@@ -1337,10 +1337,15 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1337
1337
|
async loadPolymorphicPivotOwnerSide(prop, owners, where, orderBy, ctx, options, pivotJoin, inverseProp) {
|
|
1338
1338
|
const pivotMeta = this.metadata.get(prop.pivotEntity);
|
|
1339
1339
|
const targetMeta = prop.targetMeta;
|
|
1340
|
-
//
|
|
1340
|
+
// `prop.discriminator` spans all owner FK columns but carries no target metadata, so a composite
|
|
1341
|
+
// owner PK neither expands to a tuple condition nor gets mapped back; the virtual M:1 relation
|
|
1342
|
+
// to this discriminator's owner describes the same columns as an actual relation
|
|
1343
|
+
const ownerMeta = this.metadata.get(pivotMeta.polymorphicDiscriminatorMap[prop.discriminatorValue]);
|
|
1344
|
+
const ownerProp = pivotMeta.properties[`${prop.discriminator}_${ownerMeta.tableName}`];
|
|
1345
|
+
// Build condition: discriminator = 'post' AND {owner} IN (...)
|
|
1341
1346
|
const cond = {
|
|
1342
1347
|
[prop.discriminatorColumn]: prop.discriminatorValue,
|
|
1343
|
-
[
|
|
1348
|
+
[ownerProp.name]: { $in: owners.length === 1 && owners[0].length === 1 ? owners.map(o => o[0]) : owners },
|
|
1344
1349
|
};
|
|
1345
1350
|
if (!Utils.isEmpty(where)) {
|
|
1346
1351
|
cond[inverseProp.name] = { ...where };
|
|
@@ -1351,9 +1356,13 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1351
1356
|
const childExclude = !Utils.isEmpty(options?.exclude)
|
|
1352
1357
|
? options.exclude.map(f => `${inverseProp.name}.${f}`)
|
|
1353
1358
|
: [];
|
|
1359
|
+
// the owner relation is virtual, so its FK columns have to be selected via the pivot props that
|
|
1360
|
+
// cover them; only the first owner of a shared pivot gets the flat prop named after the
|
|
1361
|
+
// discriminator, and it keeps that owner's columns, so later owners get per-column props instead
|
|
1362
|
+
const ownerFields = Utils.unique(prop.joinColumns.map(col => (pivotMeta.properties[col] ? col : prop.discriminator)));
|
|
1354
1363
|
const fields = pivotJoin
|
|
1355
|
-
? [inverseProp.name,
|
|
1356
|
-
: [inverseProp.name,
|
|
1364
|
+
? [inverseProp.name, ...ownerFields, prop.discriminatorColumn]
|
|
1365
|
+
: [inverseProp.name, ...ownerFields, prop.discriminatorColumn, ...childFields];
|
|
1357
1366
|
const res = await this.find(pivotMeta.class, cond, {
|
|
1358
1367
|
ctx,
|
|
1359
1368
|
...options,
|
|
@@ -1374,7 +1383,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1374
1383
|
_populateWhere: 'infer',
|
|
1375
1384
|
populateFilter: this.wrapPopulateFilter(options, inverseProp.name),
|
|
1376
1385
|
});
|
|
1377
|
-
return this.buildPivotResultMap(owners, res,
|
|
1386
|
+
return this.buildPivotResultMap(owners, res, ownerProp.name, inverseProp.name, ownerMeta);
|
|
1378
1387
|
}
|
|
1379
1388
|
/**
|
|
1380
1389
|
* Load from inverse side of polymorphic M:N (e.g., Tag -> Posts)
|
|
@@ -1671,7 +1680,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1671
1680
|
if (pivotRelations.length > 0) {
|
|
1672
1681
|
pk = Utils.getPrimaryKeyHash([
|
|
1673
1682
|
pk,
|
|
1674
|
-
...pivotRelations.
|
|
1683
|
+
...pivotRelations.flatMap(p => {
|
|
1684
|
+
const value = item[p.name];
|
|
1685
|
+
// composite FKs are mapped to an array of values, which `extractPK` does not accept
|
|
1686
|
+
return (Array.isArray(value) ? value : Utils.extractPK(value, p.targetMeta));
|
|
1687
|
+
}),
|
|
1675
1688
|
]);
|
|
1676
1689
|
}
|
|
1677
1690
|
if (map[pk]) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/sql",
|
|
3
|
-
"version": "7.1.9-dev.
|
|
3
|
+
"version": "7.1.9-dev.25",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"data-mapper",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@mikro-orm/core": "^7.1.8"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@mikro-orm/core": "7.1.9-dev.
|
|
56
|
+
"@mikro-orm/core": "7.1.9-dev.25"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
59
59
|
"node": ">= 22.17.0"
|
|
@@ -54,6 +54,8 @@ export declare class SqlSchemaGenerator extends AbstractSchemaGenerator<Abstract
|
|
|
54
54
|
wrap?: boolean;
|
|
55
55
|
ctx?: Transaction;
|
|
56
56
|
}): Promise<void>;
|
|
57
|
+
/** Splits the SQL on the separator, keeping the separators that fall inside a string literal. */
|
|
58
|
+
private splitOutsideLiterals;
|
|
57
59
|
/** Whether the statement has to be the first one in a query batch, e.g. `create trigger` on MSSQL. */
|
|
58
60
|
protected startsBatch(_statement: string): boolean;
|
|
59
61
|
dropTableIfExists(name: string, schema?: string): Promise<void>;
|
|
@@ -439,7 +439,7 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator {
|
|
|
439
439
|
}
|
|
440
440
|
async execute(sql, options = {}) {
|
|
441
441
|
options.wrap ??= false;
|
|
442
|
-
const lines = this.wrapSchema(sql, options)
|
|
442
|
+
const lines = this.splitOutsideLiterals(this.wrapSchema(sql, options), '\n');
|
|
443
443
|
const groups = [];
|
|
444
444
|
let i = 0;
|
|
445
445
|
for (const line of lines) {
|
|
@@ -468,14 +468,33 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator {
|
|
|
468
468
|
return;
|
|
469
469
|
}
|
|
470
470
|
const statements = groups.flatMap(group => {
|
|
471
|
-
return group
|
|
472
|
-
.join('\n')
|
|
473
|
-
.split(';\n')
|
|
471
|
+
return this.splitOutsideLiterals(group.join('\n'), ';\n')
|
|
474
472
|
.map(s => s.trim())
|
|
475
473
|
.filter(s => s);
|
|
476
474
|
});
|
|
477
475
|
await Utils.runSerial(statements, stmt => this.driver.execute(stmt));
|
|
478
476
|
}
|
|
477
|
+
/** Splits the SQL on the separator, keeping the separators that fall inside a string literal. */
|
|
478
|
+
splitOutsideLiterals(sql, separator) {
|
|
479
|
+
const [idOpen, idClose] = this.platform.quoteIdentifier('');
|
|
480
|
+
// mysql escapes quotes as `\'`, the other dialects double them, which pairs up on its own
|
|
481
|
+
const esc = this.platform.quoteValue(`'`).includes(`\\'`) ? '\\\\.|' : '';
|
|
482
|
+
// complete literals, quoted identifiers and `--` comments, so that an apostrophe inside an
|
|
483
|
+
// identifier or a comment is not mistaken for one opening a literal
|
|
484
|
+
const tokens = new RegExp(`'(?:${esc}[^'])*'|\\${idOpen}[^\\${idClose}]*\\${idClose}|--[^\n]*`, 'g');
|
|
485
|
+
const parts = [];
|
|
486
|
+
for (const chunk of sql.split(separator)) {
|
|
487
|
+
const prev = parts.at(-1);
|
|
488
|
+
// whatever quote is left once the complete tokens are gone opened a literal we are still inside of
|
|
489
|
+
if (prev?.replace(tokens, '').includes(`'`)) {
|
|
490
|
+
parts[parts.length - 1] = prev + separator + chunk;
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
parts.push(chunk);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return parts;
|
|
497
|
+
}
|
|
479
498
|
/** Whether the statement has to be the first one in a query batch, e.g. `create trigger` on MSSQL. */
|
|
480
499
|
startsBatch(_statement) {
|
|
481
500
|
return false;
|