@mikro-orm/knex 6.5.10-dev.1 → 6.5.10-dev.11

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.
@@ -847,10 +847,10 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
847
847
  */
848
848
  joinedProps(meta, populate, options) {
849
849
  return populate.filter(hint => {
850
- const [propName] = hint.field.split(':', 2);
850
+ const [propName, ref] = hint.field.split(':', 2);
851
851
  const prop = meta.properties[propName] || {};
852
852
  const strategy = (0, core_1.getLoadingStrategy)(hint.strategy || prop.strategy || options?.strategy || this.config.get('loadStrategy'), prop.kind);
853
- if (hint.filter && [core_1.ReferenceKind.ONE_TO_ONE, core_1.ReferenceKind.MANY_TO_ONE].includes(prop.kind) && !prop.nullable) {
853
+ if (ref && [core_1.ReferenceKind.ONE_TO_ONE, core_1.ReferenceKind.MANY_TO_ONE].includes(prop.kind)) {
854
854
  return true;
855
855
  }
856
856
  // skip redundant joins for 1:1 owner population hints when using `mapToPk`
@@ -21,7 +21,7 @@ class PostgreSqlTableCompiler extends pg_tablecompiler_1.default {
21
21
  }
22
22
  }
23
23
  addColumn(col) {
24
- const options = this.ormConfig.get('schemaGenerator');
24
+ const options = this.ormConfig?.get('schemaGenerator') ?? {};
25
25
  const quotedTableName = this.tableName();
26
26
  const type = col.getColumnType();
27
27
  const colName = this.client.wrapIdentifier(col.getColumnName(), col.columnBuilder.queryContext());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/knex",
3
- "version": "6.5.10-dev.1",
3
+ "version": "6.5.10-dev.11",
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
  "main": "index.js",
6
6
  "module": "index.mjs",
@@ -66,7 +66,7 @@
66
66
  "@mikro-orm/core": "^6.5.9"
67
67
  },
68
68
  "peerDependencies": {
69
- "@mikro-orm/core": "6.5.10-dev.1",
69
+ "@mikro-orm/core": "6.5.10-dev.11",
70
70
  "better-sqlite3": "*",
71
71
  "libsql": "*",
72
72
  "mariadb": "*"
@@ -7,4 +7,5 @@ export declare class ArrayCriteriaNode<T extends object> extends CriteriaNode<T>
7
7
  process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
8
8
  unwrap(): any;
9
9
  willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): any;
10
+ isStrict(): boolean;
10
11
  }
@@ -21,5 +21,8 @@ class ArrayCriteriaNode extends CriteriaNode_1.CriteriaNode {
21
21
  return node.willAutoJoin(qb, alias, options);
22
22
  });
23
23
  }
24
+ isStrict() {
25
+ return this.strict || this.payload.some((node) => node.isStrict());
26
+ }
24
27
  }
25
28
  exports.ArrayCriteriaNode = ArrayCriteriaNode;
@@ -11,10 +11,11 @@ export declare class CriteriaNode<T extends object> implements ICriteriaNode<T>
11
11
  readonly entityName: string;
12
12
  readonly parent?: ICriteriaNode<T> | undefined;
13
13
  readonly key?: EntityKey<T> | undefined;
14
+ readonly strict: boolean;
14
15
  payload: any;
15
16
  prop?: EntityProperty<T>;
16
17
  index?: number;
17
- constructor(metadata: MetadataStorage, entityName: string, parent?: ICriteriaNode<T> | undefined, key?: EntityKey<T> | undefined, validate?: boolean);
18
+ constructor(metadata: MetadataStorage, entityName: string, parent?: ICriteriaNode<T> | undefined, key?: EntityKey<T> | undefined, validate?: boolean, strict?: boolean);
18
19
  process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
19
20
  unwrap(): any;
20
21
  shouldInline(payload: any): boolean;
@@ -25,6 +26,7 @@ export declare class CriteriaNode<T extends object> implements ICriteriaNode<T>
25
26
  private isPivotJoin;
26
27
  getPivotPath(path: string): string;
27
28
  aliased(field: string, alias?: string): string;
29
+ isStrict(): boolean;
28
30
  /** @ignore */
29
31
  [inspect.custom](): string;
30
32
  }
@@ -13,14 +13,16 @@ class CriteriaNode {
13
13
  entityName;
14
14
  parent;
15
15
  key;
16
+ strict;
16
17
  payload;
17
18
  prop;
18
19
  index;
19
- constructor(metadata, entityName, parent, key, validate = true) {
20
+ constructor(metadata, entityName, parent, key, validate = true, strict = false) {
20
21
  this.metadata = metadata;
21
22
  this.entityName = entityName;
22
23
  this.parent = parent;
23
24
  this.key = key;
25
+ this.strict = strict;
24
26
  const meta = parent && metadata.find(parent.entityName);
25
27
  if (meta && key) {
26
28
  const pks = core_1.Utils.splitPrimaryKeys(key);
@@ -109,6 +111,9 @@ class CriteriaNode {
109
111
  aliased(field, alias) {
110
112
  return alias ? `${alias}.${field}` : field;
111
113
  }
114
+ isStrict() {
115
+ return this.strict;
116
+ }
112
117
  /** @ignore */
113
118
  [node_util_1.inspect.custom]() {
114
119
  const o = {};
@@ -39,7 +39,7 @@ class CriteriaNodeFactory {
39
39
  }
40
40
  static createObjectNode(metadata, entityName, payload, parent, key) {
41
41
  const meta = metadata.find(entityName);
42
- const node = new ObjectCriteriaNode_1.ObjectCriteriaNode(metadata, entityName, parent, key);
42
+ const node = new ObjectCriteriaNode_1.ObjectCriteriaNode(metadata, entityName, parent, key, true, payload.__strict);
43
43
  node.payload = Object.keys(payload).reduce((o, item) => {
44
44
  o[item] = this.createObjectItemNode(metadata, entityName, node, payload, item, meta);
45
45
  return o;
@@ -5,6 +5,7 @@ import type { ICriteriaNodeProcessOptions, IQueryBuilder } from '../typings';
5
5
  */
6
6
  export declare class ObjectCriteriaNode<T extends object> extends CriteriaNode<T> {
7
7
  process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
8
+ isStrict(): boolean;
8
9
  unwrap(): any;
9
10
  willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): boolean;
10
11
  shouldInline(payload: any): boolean;
@@ -61,6 +61,22 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
61
61
  }
62
62
  alias = this.autoJoin(qb, ownerAlias, options);
63
63
  }
64
+ if (this.prop && nestedAlias) {
65
+ const toOneProperty = [core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
66
+ // if the property is nullable and the filter is strict, we need to use left join, so we mimic the inner join behaviour
67
+ // with an exclusive condition on the join columns:
68
+ // - if the owning column is null, the row is missing, we don't apply the filter
69
+ // - if the target column is not null, the row is matched, we apply the filter
70
+ if (toOneProperty && this.prop.nullable && this.isStrict()) {
71
+ const key = this.prop.owner ? this.prop.name : this.prop.referencedPKs;
72
+ qb.andWhere({
73
+ $or: [
74
+ { [ownerAlias + '.' + key]: null },
75
+ { [nestedAlias + '.' + core_1.Utils.getPrimaryKeyHash(this.prop.referencedPKs)]: { $ne: null } },
76
+ ],
77
+ });
78
+ }
79
+ }
64
80
  return keys.reduce((o, field) => {
65
81
  const childNode = this.payload[field];
66
82
  const payload = childNode.process(qb, { ...options, alias: this.prop ? alias : ownerAlias });
@@ -92,6 +108,11 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
92
108
  return o;
93
109
  }, {});
94
110
  }
111
+ isStrict() {
112
+ return this.strict || Object.keys(this.payload).some(key => {
113
+ return this.payload[key].isStrict();
114
+ });
115
+ }
95
116
  unwrap() {
96
117
  return Object.keys(this.payload).reduce((o, field) => {
97
118
  o[field] = this.payload[field].unwrap();
@@ -216,19 +237,6 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
216
237
  ? enums_1.JoinType.innerJoin
217
238
  : enums_1.JoinType.leftJoin;
218
239
  qb[method](field, nestedAlias, undefined, joinType, path);
219
- // if the property is nullable, we need to use left join, so we mimic the inner join behaviour
220
- // with an exclusive condition on the join columns:
221
- // - if the owning column is null, the row is missing, we don't apply the filter
222
- // - if the target column is not null, the row is matched, we apply the filter
223
- if (toOneProperty && this.prop.nullable && options?.filter) {
224
- const key = this.prop.owner ? this.prop.name : this.prop.referencedPKs;
225
- qb.andWhere({
226
- $or: [
227
- { [alias + '.' + key]: null },
228
- { [nestedAlias + '.' + core_1.Utils.getPrimaryKeyHash(this.prop.referencedPKs)]: { $ne: null } },
229
- ],
230
- });
231
- }
232
240
  if (!qb.hasFlag(core_1.QueryFlag.INFER_POPULATE)) {
233
241
  qb._fields = prev;
234
242
  }
@@ -572,7 +572,7 @@ class QueryBuilder {
572
572
  return this._query.qb = this.knex.raw(qb.toSQL().toNative().sql + ' cascade');
573
573
  }
574
574
  if (this.lockMode) {
575
- this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
575
+ this.helper.getLockSQL(qb, this.lockMode, this.lockTables, this._joins);
576
576
  }
577
577
  this.helper.finalize(type, qb, this.mainAlias.metadata, this._data, this._returning);
578
578
  this.clearRawFragmentsCache();
@@ -51,7 +51,7 @@ export declare class QueryBuilderHelper {
51
51
  getQueryOrderFromObject(type: QueryType, orderBy: FlatQueryOrderMap, populate: Dictionary<string>): string[];
52
52
  finalize(type: QueryType, qb: Knex.QueryBuilder, meta?: EntityMetadata, data?: Dictionary, returning?: Field<any>[]): void;
53
53
  splitField<T>(field: EntityKey<T>, greedyAlias?: boolean): [string, EntityKey<T>, string | undefined];
54
- getLockSQL(qb: Knex.QueryBuilder, lockMode: LockMode, lockTables?: string[]): void;
54
+ getLockSQL(qb: Knex.QueryBuilder, lockMode: LockMode, lockTables?: string[], joinsMap?: Dictionary<JoinOptions>): void;
55
55
  updateVersionProperty(qb: Knex.QueryBuilder, data: Dictionary): void;
56
56
  private prefix;
57
57
  private appendGroupCondition;
@@ -629,11 +629,18 @@ class QueryBuilderHelper {
629
629
  const fromField = parts.join('.');
630
630
  return [fromAlias, fromField, ref];
631
631
  }
632
- getLockSQL(qb, lockMode, lockTables = []) {
632
+ getLockSQL(qb, lockMode, lockTables = [], joinsMap) {
633
633
  const meta = this.metadata.find(this.entityName);
634
634
  if (lockMode === core_1.LockMode.OPTIMISTIC && meta && !meta.versionProperty) {
635
635
  throw core_1.OptimisticLockError.lockFailed(this.entityName);
636
636
  }
637
+ if (lockMode !== core_1.LockMode.OPTIMISTIC && lockTables.length === 0 && joinsMap) {
638
+ const joins = Object.values(joinsMap);
639
+ const innerJoins = joins.filter(join => [enums_1.JoinType.innerJoin, enums_1.JoinType.innerJoinLateral, enums_1.JoinType.nestedInnerJoin].includes(join.type));
640
+ if (joins.length > innerJoins.length) {
641
+ lockTables.push(this.alias, ...innerJoins.map(join => join.alias));
642
+ }
643
+ }
637
644
  switch (lockMode) {
638
645
  case core_1.LockMode.PESSIMISTIC_READ: return void qb.forShare(...lockTables);
639
646
  case core_1.LockMode.PESSIMISTIC_WRITE: return void qb.forUpdate(...lockTables);
package/typings.d.ts CHANGED
@@ -178,6 +178,7 @@ export interface ICriteriaNode<T extends object> {
178
178
  readonly entityName: string;
179
179
  readonly parent?: ICriteriaNode<T> | undefined;
180
180
  readonly key?: string | undefined;
181
+ readonly strict?: boolean;
181
182
  payload: any;
182
183
  prop?: EntityProperty;
183
184
  index?: number;