@mikro-orm/sql 7.2.1-dev.2 → 7.2.1-dev.21

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.
@@ -100,7 +100,7 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
100
100
  */
101
101
  private convertOwnerPksForPivotQuery;
102
102
  private getPivotOrderBy;
103
- execute<T extends QueryResult | EntityData<AnyEntity> | EntityData<AnyEntity>[] = EntityData<AnyEntity>[]>(query: string | NativeQueryBuilder | RawQueryFragment, params?: any[] | Dictionary, method?: 'all' | 'get' | 'run', ctx?: Transaction, loggerContext?: LoggingOptions): Promise<T>;
103
+ execute<T extends QueryResult | EntityData<AnyEntity> | EntityData<AnyEntity>[] = EntityData<AnyEntity>[]>(query: string | NativeQueryBuilder | RawQueryFragment, params?: any[] | Dictionary, method?: 'all' | 'get' | 'run', ctx?: Transaction, loggerContext?: LoggingOptions, connection?: AbstractSqlConnection): Promise<T>;
104
104
  stream<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, options: StreamOptions<T, any, any, any>): AsyncIterableIterator<T>;
105
105
  /**
106
106
  * 1:1 owner side needs to be marked for population so QB auto-joins the owner id
@@ -352,6 +352,13 @@ export class AbstractSqlDriver extends DatabaseDriver {
352
352
  if (!prop) {
353
353
  return;
354
354
  }
355
+ // A condition can join the target without selecting its columns. Keep the FK
356
+ // already mapped from the root row for reference-only owning to-one relations.
357
+ if (ref &&
358
+ !hint.filter &&
359
+ (prop.kind === ReferenceKind.MANY_TO_ONE || (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner))) {
360
+ return;
361
+ }
355
362
  // Polymorphic to-one: iterate targets, find the matching one, build entity from its columns.
356
363
  // Skip :ref hints — no JOINs were created, so the FK reference is already set by the result mapper.
357
364
  if (prop.polymorphic &&
@@ -400,7 +407,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
400
407
  }
401
408
  return;
402
409
  }
403
- const pivotRefJoin = prop.kind === ReferenceKind.MANY_TO_MANY && ref;
410
+ const pivotRefJoin = prop.kind === ReferenceKind.MANY_TO_MANY && ref && !hint.filter;
404
411
  const meta2 = prop.targetMeta;
405
412
  let path = parentJoinPath ? `${parentJoinPath}.${prop.name}` : `${meta.name}.${prop.name}`;
406
413
  if (!parentJoinPath) {
@@ -765,9 +772,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
765
772
  ? '(' + fields.map(k => this.platform.quoteIdentifier(k)).join(', ') + ')'
766
773
  : `(${this.platform.quoteIdentifier(pks[0])})`;
767
774
  if (this.platform.usesOutputStatement()) {
768
- const returningProps = this.getTableProps(meta)
769
- .filter(prop => (prop.persist !== false && prop.defaultRaw) || prop.autoincrement || prop.generated)
770
- .filter(prop => !(prop.name in data[0]) || isRaw(data[0][prop.name]));
775
+ const returningProps = this.getTableProps(meta).filter(prop => prop.returning ||
776
+ (((prop.persist !== false && prop.defaultRaw) || prop.autoincrement || prop.generated) &&
777
+ (!(prop.name in data[0]) || isRaw(data[0][prop.name]))));
771
778
  const returningFields = Utils.flatten(returningProps.map(prop => prop.fieldNames));
772
779
  sql +=
773
780
  returningFields.length > 0
@@ -885,9 +892,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
885
892
  .join(', ');
886
893
  }
887
894
  if (meta && this.platform.usesReturningStatement()) {
888
- const returningProps = this.getTableProps(meta)
889
- .filter(prop => (prop.persist !== false && prop.defaultRaw) || prop.autoincrement || prop.generated)
890
- .filter(prop => !(prop.name in data[0]) || isRaw(data[0][prop.name]));
895
+ const returningProps = this.getTableProps(meta).filter(prop => prop.returning ||
896
+ (((prop.persist !== false && prop.defaultRaw) || prop.autoincrement || prop.generated) &&
897
+ (!(prop.name in data[0]) || isRaw(data[0][prop.name]))));
891
898
  const returningFields = Utils.flatten(returningProps.map(prop => prop.fieldNames));
892
899
  /* v8 ignore next */
893
900
  sql +=
@@ -1636,8 +1643,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
1636
1643
  }
1637
1644
  return [];
1638
1645
  }
1639
- async execute(query, params = [], method = 'all', ctx, loggerContext) {
1640
- return this.rethrow(this.connection.execute(query, params, method, ctx, loggerContext));
1646
+ async execute(query, params = [], method = 'all', ctx, loggerContext, connection = this.connection) {
1647
+ return this.rethrow(connection.execute(query, params, method, ctx, loggerContext));
1641
1648
  }
1642
1649
  async *stream(entityName, where, options) {
1643
1650
  options = { populate: [], orderBy: [], ...options };
@@ -1803,6 +1810,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
1803
1810
  if (options.parentJoinPath) {
1804
1811
  // alias all fields in the primary table
1805
1812
  meta.props
1813
+ // flattened children of inline embeddables are expanded via their parent, iterating them too would select the columns twice
1814
+ .filter(prop => !prop.embedded)
1806
1815
  .filter(prop => this.shouldHaveColumn(meta, prop, populate, options.explicitFields, options.exclude))
1807
1816
  .forEach(prop => fields.push(...this.mapPropToFieldNames(qb, prop, options.parentTableAlias, meta, options.schema, options.explicitFields)));
1808
1817
  }
@@ -1861,7 +1870,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1861
1870
  continue;
1862
1871
  }
1863
1872
  const meta2 = prop.targetMeta;
1864
- const pivotRefJoin = prop.kind === ReferenceKind.MANY_TO_MANY && ref;
1873
+ const pivotRefJoin = prop.kind === ReferenceKind.MANY_TO_MANY && ref && !hint.filter;
1865
1874
  const tableAlias = qb.getNextAlias(prop.name);
1866
1875
  const field = `${options.parentTableAlias}.${prop.name}`;
1867
1876
  let path = options.parentJoinPath ? `${options.parentJoinPath}.${prop.name}` : `${meta.name}.${prop.name}`;
@@ -1873,7 +1882,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1873
1882
  ? JoinType.pivotJoin
1874
1883
  : hint.joinType
1875
1884
  ? hint.joinType
1876
- : (hint.filter && !prop.nullable) || mandatoryToOneProperty
1885
+ : (hint.filter && !prop.nullable && prop.kind !== ReferenceKind.MANY_TO_MANY) || mandatoryToOneProperty
1877
1886
  ? JoinType.innerJoin
1878
1887
  : JoinType.leftJoin;
1879
1888
  const schema = prop.targetMeta.schema === '*' ? (options?.schema ?? this.config.get('schema')) : prop.targetMeta.schema;
@@ -1941,7 +1950,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1941
1950
  // Add fields from this child (only ownProps, skip PKs)
1942
1951
  const schema = childMeta.schema === '*' ? '*' : this.getSchemaName(childMeta);
1943
1952
  childMeta
1944
- .ownProps.filter(p => !p.primary && this.platform.shouldHaveColumn(p, []))
1953
+ .ownProps.filter(p => !p.primary && !p.embedded && this.platform.shouldHaveColumn(p, []))
1945
1954
  .forEach(prop => fields.push(...this.mapPropToFieldNames(qb, prop, childAlias, childMeta, schema)));
1946
1955
  }
1947
1956
  // Add computed discriminator (descendants already sorted by depth)
@@ -8,6 +8,8 @@ import type { InferClassEntityDB, InferKyselyDB } from './typings.js';
8
8
  import { type MikroKyselyPluginOptions } from './plugin/index.js';
9
9
  /** Options for the modern signature of `SqlEntityManager.execute()`. */
10
10
  export interface EmExecuteOptions extends AbortQueryOptions {
11
+ /** Connection to use outside a transaction. Defaults to 'write'; an active transaction always takes precedence. */
12
+ connectionType?: ConnectionType;
11
13
  /** Result shape — `'all'` for rows, `'get'` for a single row, `'run'` for affected count. Defaults to `'all'`. */
12
14
  method?: 'all' | 'get' | 'run';
13
15
  /** Logger context payload forwarded to `Logger.logQuery`. */
@@ -62,8 +64,8 @@ export declare class SqlEntityManager<Driver extends AbstractSqlDriver = Abstrac
62
64
  */
63
65
  execute<T extends QueryResult | EntityData<AnyEntity> | EntityData<AnyEntity>[] = EntityData<AnyEntity>[]>(query: string | NativeQueryBuilder | RawQueryFragment, params?: any[] | Dictionary, method?: 'all' | 'get' | 'run', loggerContext?: LoggingOptions): Promise<T>;
64
66
  /**
65
- * Executes a raw SQL query with an options bag carrying `method`, `loggerContext`, `signal`
66
- * and `inflightQueryAbortStrategy`. Per-call `signal` / `inflightQueryAbortStrategy` override
67
+ * Executes a raw SQL query with an options bag carrying `method`, `loggerContext`, `signal`,
68
+ * `connectionType` and `inflightQueryAbortStrategy`. Per-call `signal` / `inflightQueryAbortStrategy` override
67
69
  * the fork-level defaults set via `em.fork({ signal })`. The current transaction context is
68
70
  * applied automatically.
69
71
  */
@@ -68,7 +68,10 @@ export class SqlEntityManager extends EntityManager {
68
68
  merged.signal = opts.signal ?? fork?.signal;
69
69
  merged.inflightQueryAbortStrategy = opts.inflightQueryAbortStrategy ?? fork?.inflightQueryAbortStrategy;
70
70
  }
71
- return context.withSessionContext(context.getTransactionContext(), ctx => this.getDriver().execute(query, params, opts.method ?? 'all', ctx, merged));
71
+ const transaction = context.getTransactionContext();
72
+ // Resolve once so implicit RLS transactions and query execution use the same replica.
73
+ const connection = context.getConnection(transaction ? 'write' : (opts.connectionType ?? 'write'));
74
+ return context.withSessionContext(transaction, ctx => this.getDriver().execute(query, params, opts.method ?? 'all', ctx, merged, connection), connection);
72
75
  }
73
76
  /**
74
77
  * @inheritDoc
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/sql",
3
- "version": "7.2.1-dev.2",
3
+ "version": "7.2.1-dev.21",
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.29.5"
50
+ "kysely": "0.29.6"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@mikro-orm/core": "^7.2.0"
54
54
  },
55
55
  "peerDependencies": {
56
- "@mikro-orm/core": "7.2.1-dev.2"
56
+ "@mikro-orm/core": "7.2.1-dev.21"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">= 22.17.0"
@@ -909,7 +909,7 @@ export class QueryBuilder {
909
909
  .filter(prop => prop.returning || (prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw)))
910
910
  // a TPT table can only return its own columns
911
911
  .filter(prop => meta.inheritanceType !== 'tpt' || prop.primary || meta.ownProps.some(p => p.name === prop.name))
912
- .filter(prop => !data || !(prop.name in data));
912
+ .filter(prop => prop.returning || !data || !(prop.name in data));
913
913
  if (returningProps.length > 0) {
914
914
  qb.returning(Utils.flatten(returningProps.map(prop => prop.fieldNames)));
915
915
  }
@@ -2505,7 +2505,7 @@ export class QueryBuilder {
2505
2505
  }
2506
2506
  }
2507
2507
  wrapModifySubQuery(meta) {
2508
- const subQuery = this.clone();
2508
+ const subQuery = this.clone(['returning']);
2509
2509
  subQuery.#state.finalized = true;
2510
2510
  // wrap one more time to get around MySQL limitations
2511
2511
  // https://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from-clause