@mikro-orm/knex 6.0.0-dev.92 → 6.0.0-dev.94

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.
@@ -1,6 +1,5 @@
1
- import type { Knex } from 'knex';
2
- import type { AnyEntity, Configuration, ConnectionOptions, EntityData, IsolationLevel, LoggingOptions, QueryResult, Transaction, TransactionEventBroadcaster } from '@mikro-orm/core';
3
- import { Connection } from '@mikro-orm/core';
1
+ import { type Knex } from 'knex';
2
+ import { Connection, type AnyEntity, type Configuration, type ConnectionOptions, type EntityData, type IsolationLevel, type QueryResult, type Transaction, type TransactionEventBroadcaster, type LoggingOptions } from '@mikro-orm/core';
4
3
  import type { AbstractSqlPlatform } from './AbstractSqlPlatform';
5
4
  export declare abstract class AbstractSqlConnection extends Connection {
6
5
  private static __patched;
@@ -15,11 +14,13 @@ export declare abstract class AbstractSqlConnection extends Connection {
15
14
  isConnected(): Promise<boolean>;
16
15
  transactional<T>(cb: (trx: Transaction<Knex.Transaction>) => Promise<T>, options?: {
17
16
  isolationLevel?: IsolationLevel;
17
+ readOnly?: boolean;
18
18
  ctx?: Knex.Transaction;
19
19
  eventBroadcaster?: TransactionEventBroadcaster;
20
20
  }): Promise<T>;
21
21
  begin(options?: {
22
22
  isolationLevel?: IsolationLevel;
23
+ readOnly?: boolean;
23
24
  ctx?: Knex.Transaction;
24
25
  eventBroadcaster?: TransactionEventBroadcaster;
25
26
  }): Promise<Knex.Transaction>;
@@ -10,6 +10,7 @@ function isRootTransaction(trx) {
10
10
  return !Object.getOwnPropertySymbols(trx).includes(parentTransactionSymbol);
11
11
  }
12
12
  class AbstractSqlConnection extends core_1.Connection {
13
+ static { this.__patched = false; }
13
14
  constructor(config, options, type) {
14
15
  super(config, options, type);
15
16
  this.patchKnexClient();
@@ -53,7 +54,10 @@ class AbstractSqlConnection extends core_1.Connection {
53
54
  if (!options.ctx) {
54
55
  await options.eventBroadcaster?.dispatchEvent(core_1.EventType.beforeTransactionStart);
55
56
  }
56
- const trx = await (options.ctx || this.getKnex()).transaction(null, { isolationLevel: options.isolationLevel });
57
+ const trx = await (options.ctx || this.getKnex()).transaction(null, {
58
+ isolationLevel: options.isolationLevel,
59
+ readOnly: options.readOnly,
60
+ });
57
61
  if (!options.ctx) {
58
62
  await options.eventBroadcaster?.dispatchEvent(core_1.EventType.afterTransactionStart, trx);
59
63
  }
@@ -186,4 +190,3 @@ class AbstractSqlConnection extends core_1.Connection {
186
190
  }
187
191
  }
188
192
  exports.AbstractSqlConnection = AbstractSqlConnection;
189
- AbstractSqlConnection.__patched = false;
@@ -1,6 +1,5 @@
1
1
  import type { Knex } from 'knex';
2
- import type { AnyEntity, Collection, ConnectionType, Configuration, Constructor, CountOptions, DeleteOptions, Dictionary, DriverMethodOptions, EntityData, EntityDictionary, EntityField, EntityMetadata, EntityName, EntityProperty, FilterQuery, FindOneOptions, FindOptions, IDatabaseDriver, LockOptions, NativeInsertUpdateManyOptions, NativeInsertUpdateOptions, PopulateOptions, Primary, QueryOrderMap, QueryResult, Transaction, OrderDefinition, LoggingOptions } from '@mikro-orm/core';
3
- import { DatabaseDriver, EntityManagerType } from '@mikro-orm/core';
2
+ import { type AnyEntity, type Collection, type Configuration, type ConnectionType, type Constructor, type CountOptions, DatabaseDriver, type DeleteOptions, type Dictionary, type DriverMethodOptions, type EntityData, type EntityDictionary, type EntityField, EntityManagerType, type EntityMetadata, type EntityName, type EntityProperty, type FilterQuery, type FindOneOptions, type FindOptions, type IDatabaseDriver, type LockOptions, type LoggingOptions, type NativeInsertUpdateManyOptions, type NativeInsertUpdateOptions, type PopulateOptions, type Primary, type QueryOrderMap, type QueryResult, type Transaction, type UpsertManyOptions, type UpsertOptions, type OrderDefinition } from '@mikro-orm/core';
4
3
  import type { AbstractSqlConnection } from './AbstractSqlConnection';
5
4
  import type { AbstractSqlPlatform } from './AbstractSqlPlatform';
6
5
  import { QueryBuilder, QueryType } from './query';
@@ -27,8 +26,8 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
27
26
  count<T extends object>(entityName: string, where: any, options?: CountOptions<T>): Promise<number>;
28
27
  nativeInsert<T extends object>(entityName: string, data: EntityDictionary<T>, options?: NativeInsertUpdateOptions<T>): Promise<QueryResult<T>>;
29
28
  nativeInsertMany<T extends object>(entityName: string, data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T>): Promise<QueryResult<T>>;
30
- nativeUpdate<T extends object>(entityName: string, where: FilterQuery<T>, data: EntityDictionary<T>, options?: NativeInsertUpdateOptions<T>): Promise<QueryResult<T>>;
31
- nativeUpdateMany<T extends object>(entityName: string, where: FilterQuery<T>[], data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T>): Promise<QueryResult<T>>;
29
+ nativeUpdate<T extends object>(entityName: string, where: FilterQuery<T>, data: EntityDictionary<T>, options?: NativeInsertUpdateOptions<T> & UpsertOptions<T>): Promise<QueryResult<T>>;
30
+ nativeUpdateMany<T extends object>(entityName: string, where: FilterQuery<T>[], data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T> & UpsertManyOptions<T>): Promise<QueryResult<T>>;
32
31
  nativeDelete<T extends object>(entityName: string, where: FilterQuery<T> | string | any, options?: DeleteOptions<T>): Promise<QueryResult<T>>;
33
32
  syncCollection<T extends object, O extends object>(coll: Collection<T, O>, options?: DriverMethodOptions): Promise<void>;
34
33
  loadFromPivotTable<T extends object, O extends object>(prop: EntityProperty, owners: Primary<O>[][], where?: FilterQuery<any>, orderBy?: OrderDefinition<T>, ctx?: Transaction, options?: FindOptions<T, any, any>): Promise<Dictionary<T[]>>;
@@ -42,7 +41,7 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
42
41
  /**
43
42
  * @internal
44
43
  */
45
- mergeJoinedResult<T extends object>(rawResults: EntityData<T>[], meta: EntityMetadata<T>): EntityData<T>[];
44
+ mergeJoinedResult<T extends object>(rawResults: EntityData<T>[], meta: EntityMetadata<T>, joinedProps: PopulateOptions<T>[]): EntityData<T>[];
46
45
  protected getFieldsForJoinedLoad<T extends object>(qb: QueryBuilder<T>, meta: EntityMetadata<T>, explicitFields?: Field<T>[], populate?: PopulateOptions<T>[], parentTableAlias?: string, parentJoinPath?: string): Field<T>[];
47
46
  /**
48
47
  * @internal
@@ -40,6 +40,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
40
40
  .where(where)
41
41
  .groupBy(options.groupBy)
42
42
  .having(options.having)
43
+ .indexHint(options.indexHint)
44
+ .comment(options.comments)
45
+ .hintComment(options.hintComments)
43
46
  .withSchema(this.getSchemaName(meta, options));
44
47
  if (isCursorPagination) {
45
48
  const { orderBy: newOrderBy, where } = this.processCursorOptions(meta, options, orderBy);
@@ -60,7 +63,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
60
63
  result.reverse();
61
64
  }
62
65
  if (joinedProps.length > 0) {
63
- return this.mergeJoinedResult(result, meta);
66
+ return this.mergeJoinedResult(result, meta, joinedProps);
64
67
  }
65
68
  return result;
66
69
  }
@@ -117,7 +120,10 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
117
120
  }
118
121
  async wrapVirtualExpressionInSubquery(meta, expression, where, options, type = query_1.QueryType.SELECT) {
119
122
  const qb = this.createQueryBuilder(meta.className, options?.ctx, options.connectionType, options.convertCustomTypes)
120
- .limit(options?.limit, options?.offset);
123
+ .limit(options?.limit, options?.offset)
124
+ .indexHint(options.indexHint)
125
+ .comment(options.comments)
126
+ .hintComment(options.hintComments);
121
127
  if (options.orderBy) {
122
128
  qb.orderBy(options.orderBy);
123
129
  }
@@ -225,6 +231,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
225
231
  return this.countVirtual(entityName, where, options);
226
232
  }
227
233
  const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false)
234
+ .indexHint(options.indexHint)
235
+ .comment(options.comments)
236
+ .hintComment(options.hintComments)
228
237
  .groupBy(options.groupBy)
229
238
  .having(options.having)
230
239
  .populate(options.populate ?? [])
@@ -289,7 +298,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
289
298
  const keys = [];
290
299
  props.forEach(prop => {
291
300
  if (prop.fieldNames.length > 1) {
292
- const param = row[prop.name] ?? prop.fieldNames.map(() => null);
301
+ const param = [...row[prop.name] ?? prop.fieldNames.map(() => null)];
293
302
  const key = (row[prop.name] ?? prop.fieldNames).map(() => '?');
294
303
  prop.fieldNames.forEach((field, idx) => {
295
304
  if (duplicates.includes(field)) {
@@ -352,12 +361,18 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
352
361
  .withSchema(this.getSchemaName(meta, options));
353
362
  if (options.upsert) {
354
363
  /* istanbul ignore next */
355
- const uniqueFields = core_1.Utils.isPlainObject(where) ? core_1.Utils.keys(where) : meta.primaryKeys;
356
- /* istanbul ignore next */
364
+ const uniqueFields = options.onConflictFields ?? (core_1.Utils.isPlainObject(where) ? core_1.Utils.keys(where) : meta.primaryKeys);
365
+ const returning = (0, core_1.getOnConflictReturningFields)(meta, data, uniqueFields, options);
357
366
  qb.insert(data)
358
- .onConflict(uniqueFields.map(p => meta?.properties[p]?.fieldNames[0] ?? p))
359
- .merge(core_1.Utils.keys(data).filter(f => !uniqueFields.includes(f)))
360
- .returning(meta?.comparableProps.filter(p => !p.lazy && !p.embeddable && !(p.name in data)).map(p => p.name) ?? '*');
367
+ .onConflict(uniqueFields)
368
+ .returning(returning);
369
+ if (!options.onConflictAction || options.onConflictAction === 'merge') {
370
+ const fields = (0, core_1.getOnConflictFields)(data, uniqueFields, options);
371
+ qb.merge(fields);
372
+ }
373
+ if (options.onConflictAction === 'ignore') {
374
+ qb.ignore();
375
+ }
361
376
  }
362
377
  else {
363
378
  qb.update(data).where(where);
@@ -374,12 +389,19 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
374
389
  options.convertCustomTypes ??= true;
375
390
  const meta = this.metadata.get(entityName);
376
391
  if (options.upsert) {
377
- const uniqueFields = core_1.Utils.isPlainObject(where[0]) ? Object.keys(where[0]) : meta.primaryKeys;
392
+ const uniqueFields = options.onConflictFields ?? (core_1.Utils.isPlainObject(where[0]) ? Object.keys(where[0]) : meta.primaryKeys);
378
393
  const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
394
+ const returning = (0, core_1.getOnConflictReturningFields)(meta, data[0], uniqueFields, options);
379
395
  qb.insert(data)
380
- .onConflict(uniqueFields.map(p => meta.properties[p]?.fieldNames[0] ?? p))
381
- .merge(Object.keys(data[0]).filter(f => !uniqueFields.includes(f)))
382
- .returning(meta.comparableProps.filter(p => !p.lazy && !p.embeddable && !(p.name in data[0])).map(p => p.name) ?? '*');
396
+ .onConflict(uniqueFields)
397
+ .returning(returning);
398
+ if (!options.onConflictAction || options.onConflictAction === 'merge') {
399
+ const fields = (0, core_1.getOnConflictFields)(data[0], uniqueFields, options);
400
+ qb.merge(fields);
401
+ }
402
+ if (options.onConflictAction === 'ignore') {
403
+ qb.ignore();
404
+ }
383
405
  return qb.execute('run', false);
384
406
  }
385
407
  const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
@@ -494,11 +516,23 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
494
516
  if (coll.property.kind === core_1.ReferenceKind.ONE_TO_MANY) {
495
517
  const cols = coll.property.referencedColumnNames;
496
518
  const qb = this.createQueryBuilder(coll.property.type, ctx, 'write')
497
- .withSchema(this.getSchemaName(meta, options))
498
- .update({ [coll.property.mappedBy]: pks })
519
+ .withSchema(this.getSchemaName(meta, options));
520
+ if (coll.getSnapshot() === undefined) {
521
+ if (coll.property.orphanRemoval) {
522
+ const kqb = qb.delete({ [coll.property.mappedBy]: pks })
523
+ .getKnexQuery()
524
+ .whereNotIn(cols, insertDiff);
525
+ return this.rethrow(this.execute(kqb));
526
+ }
527
+ const kqb = qb.update({ [coll.property.mappedBy]: null })
528
+ .getKnexQuery()
529
+ .whereNotIn(cols, insertDiff);
530
+ return this.rethrow(this.execute(kqb));
531
+ }
532
+ const kqb = qb.update({ [coll.property.mappedBy]: pks })
499
533
  .getKnexQuery()
500
534
  .whereIn(cols, insertDiff);
501
- return this.rethrow(this.execute(qb));
535
+ return this.rethrow(this.execute(kqb));
502
536
  }
503
537
  /* istanbul ignore next */
504
538
  const ownerSchema = wrapped.getSchema() === '*' ? this.config.get('schema') : wrapped.getSchema();
@@ -515,7 +549,12 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
515
549
  const prop2 = prop.targetMeta.properties[prop.mappedBy ?? prop.inversedBy];
516
550
  const ownerMeta = this.metadata.find(pivotProp2.type);
517
551
  const pivotMeta = this.metadata.find(prop.pivotEntity);
518
- const qb = this.createQueryBuilder(prop.type, ctx, options?.connectionType).withSchema(this.getSchemaName(prop.targetMeta, options));
552
+ options = { ...options };
553
+ const qb = this.createQueryBuilder(prop.type, ctx, options.connectionType)
554
+ .withSchema(this.getSchemaName(prop.targetMeta, options))
555
+ .indexHint(options.indexHint)
556
+ .comment(options.comments)
557
+ .hintComment(options.hintComments);
519
558
  const pivotAlias = qb.getNextAlias(pivotMeta.tableName);
520
559
  const pivotKey = pivotProp2.joinColumns.map(column => `${pivotAlias}.${column}`).join(core_1.Utils.PK_SEPARATOR);
521
560
  const cond = {
@@ -530,7 +569,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
530
569
  }
531
570
  orderBy = this.getPivotOrderBy(prop, pivotAlias, orderBy);
532
571
  const populate = this.autoJoinOneToOneOwner(prop.targetMeta, []);
533
- const fields = this.buildFields(prop.targetMeta, (options?.populate ?? []), [], qb, options?.fields);
572
+ const fields = this.buildFields(prop.targetMeta, (options.populate ?? []), [], qb, options.fields);
534
573
  const k1 = !prop.owner ? 'joinColumns' : 'inverseJoinColumns';
535
574
  const k2 = prop.owner ? 'joinColumns' : 'inverseJoinColumns';
536
575
  const cols = [
@@ -539,16 +578,16 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
539
578
  ];
540
579
  fields.push(...cols);
541
580
  qb.select(fields)
542
- .join(prop2.name, pivotAlias, {}, 'pivotJoin', undefined, options?.schema)
581
+ .join(prop2.name, pivotAlias, {}, 'pivotJoin', undefined, options.schema)
543
582
  .populate(populate)
544
583
  .where(where)
545
584
  .orderBy(orderBy)
546
- .setLockMode(options?.lockMode, options?.lockTableAliases);
547
- if (owners.length === 1 && (options?.offset != null || options?.limit != null)) {
585
+ .setLockMode(options.lockMode, options.lockTableAliases);
586
+ if (owners.length === 1 && (options.offset != null || options.limit != null)) {
548
587
  qb.limit(options.limit, options.offset);
549
588
  }
550
589
  const schema = this.config.get('schema');
551
- if (prop.targetMeta.schema !== '*' && pivotMeta.schema === '*' && options?.schema) {
590
+ if (prop.targetMeta.schema !== '*' && pivotMeta.schema === '*' && options.schema) {
552
591
  // eslint-disable-next-line dot-notation
553
592
  qb['finalize']();
554
593
  // eslint-disable-next-line dot-notation
@@ -615,17 +654,21 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
615
654
  /**
616
655
  * @internal
617
656
  */
618
- mergeJoinedResult(rawResults, meta) {
657
+ mergeJoinedResult(rawResults, meta, joinedProps) {
619
658
  // group by the root entity primary key first
620
659
  const map = {};
621
660
  const res = [];
622
661
  rawResults.forEach(item => {
623
662
  const pk = core_1.Utils.getCompositeKeyHash(item, meta);
624
663
  if (map[pk]) {
625
- map[pk].push(item);
664
+ joinedProps.forEach(hint => {
665
+ if (Array.isArray(map[pk][hint.field]) && Array.isArray(item[hint.field])) {
666
+ map[pk][hint.field].push(...item[hint.field]);
667
+ }
668
+ });
626
669
  }
627
670
  else {
628
- map[pk] = [item];
671
+ map[pk] = item;
629
672
  res.push(item);
630
673
  }
631
674
  });
@@ -734,8 +777,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
734
777
  if (!deleteDiff) {
735
778
  deleteDiff = [];
736
779
  }
780
+ const pivotMeta = this.metadata.find(prop.pivotEntity);
737
781
  if (deleteDiff === true || deleteDiff.length > 0) {
738
- const qb1 = this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write').withSchema(this.getSchemaName(meta, options));
782
+ const qb1 = this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write').withSchema(this.getSchemaName(pivotMeta, options));
739
783
  const knex = qb1.getKnex();
740
784
  if (Array.isArray(deleteDiff)) {
741
785
  knex.whereIn(prop.inverseJoinColumns, deleteDiff);
@@ -763,7 +807,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
763
807
  else {
764
808
  await core_1.Utils.runSerial(items, item => {
765
809
  return this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
766
- .withSchema(this.getSchemaName(meta, options))
810
+ .withSchema(this.getSchemaName(pivotMeta, options))
767
811
  .insert(item)
768
812
  .execute('run', false);
769
813
  });
@@ -817,7 +861,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
817
861
  }
818
862
  if (prop.kind === core_1.ReferenceKind.EMBEDDED) {
819
863
  if (prop.object) {
820
- ret.push(prop.fieldNames[0]);
864
+ ret.push(prop.name);
821
865
  return;
822
866
  }
823
867
  const parts = field.split('.');
@@ -829,13 +873,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
829
873
  }
830
874
  return;
831
875
  }
832
- if (prop.formula) {
833
- ret.push(prop.name);
834
- return;
835
- }
836
- if (prop.fieldNames) {
837
- ret.push(...prop.fieldNames);
838
- }
876
+ ret.push(prop.name);
839
877
  }
840
878
  buildFields(meta, populate, joinedProps, qb, fields) {
841
879
  const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => p.field === prop.name || p.all));
@@ -1,7 +1,5 @@
1
- import type { Constructor, EntityManager, EntityRepository, IDatabaseDriver, MikroORM } from '@mikro-orm/core';
2
- import { Platform } from '@mikro-orm/core';
3
- import type { SchemaHelper } from './schema';
4
- import { SqlSchemaGenerator } from './schema';
1
+ import { Platform, type Constructor, type EntityManager, type EntityRepository, type IDatabaseDriver, type MikroORM } from '@mikro-orm/core';
2
+ import { SqlSchemaGenerator, type SchemaHelper } from './schema';
5
3
  export declare abstract class AbstractSqlPlatform extends Platform {
6
4
  protected readonly schemaHelper?: SchemaHelper;
7
5
  usesPivotTable(): boolean;
@@ -1,6 +1,5 @@
1
1
  import type { Knex } from 'knex';
2
- import type { AnyEntity, ConnectionType, EntityData, EntityName, EntityRepository, GetRepository, QueryResult, FilterQuery } from '@mikro-orm/core';
3
- import { EntityManager } from '@mikro-orm/core';
2
+ import { EntityManager, type AnyEntity, type ConnectionType, type EntityData, type EntityName, type EntityRepository, type GetRepository, type QueryResult, type FilterQuery } from '@mikro-orm/core';
4
3
  import type { AbstractSqlDriver } from './AbstractSqlDriver';
5
4
  import { QueryBuilder } from './query';
6
5
  import type { SqlEntityRepository } from './SqlEntityRepository';
@@ -1,6 +1,5 @@
1
1
  import type { Knex } from 'knex';
2
- import type { ConnectionType, EntityName } from '@mikro-orm/core';
3
- import { EntityRepository } from '@mikro-orm/core';
2
+ import { EntityRepository, type ConnectionType, type EntityName } from '@mikro-orm/core';
4
3
  import type { SqlEntityManager } from './SqlEntityManager';
5
4
  import type { QueryBuilder } from './query';
6
5
  export declare class SqlEntityRepository<T extends object> extends EntityRepository<T> {
package/index.mjs CHANGED
@@ -47,8 +47,6 @@ export const DatabaseObjectExistsException = mod.DatabaseObjectExistsException;
47
47
  export const DatabaseObjectNotFoundException = mod.DatabaseObjectNotFoundException;
48
48
  export const DatabaseSchema = mod.DatabaseSchema;
49
49
  export const DatabaseTable = mod.DatabaseTable;
50
- export const Dataloader = mod.Dataloader;
51
- export const DataloaderUtils = mod.DataloaderUtils;
52
50
  export const DateTimeType = mod.DateTimeType;
53
51
  export const DateType = mod.DateType;
54
52
  export const DeadlockException = mod.DeadlockException;
@@ -195,6 +193,8 @@ export const compareBuffers = mod.compareBuffers;
195
193
  export const compareObjects = mod.compareObjects;
196
194
  export const defineConfig = mod.defineConfig;
197
195
  export const equals = mod.equals;
196
+ export const getOnConflictFields = mod.getOnConflictFields;
197
+ export const getOnConflictReturningFields = mod.getOnConflictReturningFields;
198
198
  export const helper = mod.helper;
199
199
  export const knex = mod.knex;
200
200
  export const parseJsonSafe = mod.parseJsonSafe;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/knex",
3
- "version": "6.0.0-dev.92",
3
+ "version": "6.0.0-dev.94",
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",
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "homepage": "https://mikro-orm.io",
48
48
  "engines": {
49
- "node": ">= 16.0.0"
49
+ "node": ">= 18.12.0"
50
50
  },
51
51
  "scripts": {
52
52
  "build": "yarn clean && yarn compile && yarn copy && yarn run -T gen-esm-wrapper index.js index.mjs",
@@ -59,13 +59,13 @@
59
59
  },
60
60
  "dependencies": {
61
61
  "fs-extra": "11.1.1",
62
- "knex": "2.4.2",
62
+ "knex": "2.5.1",
63
63
  "sqlstring": "2.3.3"
64
64
  },
65
65
  "devDependencies": {
66
- "@mikro-orm/core": "^5.7.12"
66
+ "@mikro-orm/core": "^5.8.0"
67
67
  },
68
68
  "peerDependencies": {
69
- "@mikro-orm/core": "~6.0.0-dev.92"
69
+ "@mikro-orm/core": "~6.0.0-dev.94"
70
70
  }
71
71
  }
@@ -1,6 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import { inspect } from 'util';
3
- import type { EntityKey, EntityProperty, MetadataStorage } from '@mikro-orm/core';
3
+ import { type EntityProperty, type MetadataStorage, type EntityKey } from '@mikro-orm/core';
4
4
  import type { ICriteriaNode, IQueryBuilder } from '../typings';
5
5
  /**
6
6
  * Helper for working with deeply nested where/orderBy/having criteria. Uses composite pattern to build tree from the payload.
@@ -1,4 +1,4 @@
1
- import type { Dictionary, EntityKey, EntityMetadata, MetadataStorage } from '@mikro-orm/core';
1
+ import { type Dictionary, type EntityMetadata, type MetadataStorage, type EntityKey } from '@mikro-orm/core';
2
2
  import type { ICriteriaNode } from '../typings';
3
3
  /**
4
4
  * @internal
@@ -1,12 +1,10 @@
1
1
  /// <reference types="node" />
2
2
  import { inspect } from 'util';
3
3
  import type { Knex } from 'knex';
4
- import type { AnyEntity, ConnectionType, Dictionary, EntityData, EntityName, EntityProperty, FlushMode, GroupOperator, LoggingOptions, MetadataStorage, ObjectQuery, PopulateOptions, QBFilterQuery, QBQueryOrderMap, QueryResult, RequiredEntityData } from '@mikro-orm/core';
5
- import { LockMode, PopulateHint, QueryFlag } from '@mikro-orm/core';
4
+ import { LockMode, PopulateHint, QueryFlag, type AnyEntity, type ConnectionType, type Dictionary, type EntityData, type EntityName, type EntityProperty, type FlushMode, type GroupOperator, type MetadataStorage, type ObjectQuery, type PopulateOptions, type QBFilterQuery, type QBQueryOrderMap, type QueryResult, type RequiredEntityData, type LoggingOptions } from '@mikro-orm/core';
6
5
  import { QueryType } from './enums';
7
6
  import type { AbstractSqlDriver } from '../AbstractSqlDriver';
8
- import type { Alias } from './QueryBuilderHelper';
9
- import { QueryBuilderHelper } from './QueryBuilderHelper';
7
+ import { QueryBuilderHelper, type Alias } from './QueryBuilderHelper';
10
8
  import type { SqlEntityManager } from '../SqlEntityManager';
11
9
  import type { Field } from '../typings';
12
10
  /**
@@ -36,7 +34,7 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
36
34
  private connectionType?;
37
35
  private readonly em?;
38
36
  private readonly logging?;
39
- get mainAlias(): Alias;
37
+ get mainAlias(): Alias<T>;
40
38
  get alias(): string;
41
39
  get helper(): QueryBuilderHelper;
42
40
  /** @internal */
@@ -68,6 +66,8 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
68
66
  private _joinedProps;
69
67
  private _cache?;
70
68
  private _indexHint?;
69
+ private _comments;
70
+ private _hintComments;
71
71
  private flushMode?;
72
72
  private lockMode?;
73
73
  private lockTables?;
@@ -127,6 +127,16 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
127
127
  * Adds index hint to the FROM clause.
128
128
  */
129
129
  indexHint(sql: string): this;
130
+ /**
131
+ * Prepend comment to the sql query using the syntax `/* ... *&#8205;/`. Some characters are forbidden such as `/*, *&#8205;/` and `?`.
132
+ */
133
+ comment(comment: string | string[]): this;
134
+ /**
135
+ * Add hints to the query using comment-like syntax `/*+ ... *&#8205;/`. MySQL and Oracle use this syntax for optimizer hints.
136
+ * Also various DB proxies and routers use this syntax to pass hints to alter their behavior. In other dialects the hints
137
+ * are ignored as simple comments.
138
+ */
139
+ hintComment(comment: string | string[]): this;
130
140
  /**
131
141
  * Specifies FROM which entity's table select/update/delete will be executed, removing all previously set FROM-s.
132
142
  * Allows setting a main string alias of the selection data.
@@ -1,16 +1,4 @@
1
1
  "use strict";
2
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
- };
7
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
- if (kind === "m") throw new TypeError("Private method is not writable");
9
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
- };
13
- var _QueryBuilder_query;
14
2
  Object.defineProperty(exports, "__esModule", { value: true });
15
3
  exports.QueryBuilder = void 0;
16
4
  const util_1 = require("util");
@@ -73,11 +61,12 @@ class QueryBuilder {
73
61
  this._groupBy = [];
74
62
  this._having = {};
75
63
  this._joinedProps = new Map();
64
+ this._comments = [];
65
+ this._hintComments = [];
76
66
  this.subQueries = {};
77
67
  this._aliases = {};
78
68
  this.platform = this.driver.getPlatform();
79
69
  this.knex = this.driver.getConnection(this.connectionType).getKnex();
80
- _QueryBuilder_query.set(this, void 0);
81
70
  if (alias) {
82
71
  this.aliasCounter++;
83
72
  this._explicitAlias = true;
@@ -264,9 +253,16 @@ class QueryBuilder {
264
253
  return this;
265
254
  }
266
255
  onConflict(fields = []) {
256
+ const meta = this.mainAlias.metadata;
267
257
  this.ensureNotFinalized();
268
- this._onConflict = this._onConflict || [];
269
- this._onConflict.push({ fields: core_1.Utils.asArray(fields).map(f => f.toString()) });
258
+ this._onConflict ??= [];
259
+ this._onConflict.push({
260
+ fields: core_1.Utils.asArray(fields).flatMap(f => {
261
+ const key = f.toString();
262
+ /* istanbul ignore next */
263
+ return meta.properties[key]?.fieldNames ?? [key];
264
+ }),
265
+ });
270
266
  return this;
271
267
  }
272
268
  ignore() {
@@ -354,6 +350,24 @@ class QueryBuilder {
354
350
  this._indexHint = sql;
355
351
  return this;
356
352
  }
353
+ /**
354
+ * Prepend comment to the sql query using the syntax `/* ... *&#8205;/`. Some characters are forbidden such as `/*, *&#8205;/` and `?`.
355
+ */
356
+ comment(comment) {
357
+ this.ensureNotFinalized();
358
+ this._comments.push(...core_1.Utils.asArray(comment));
359
+ return this;
360
+ }
361
+ /**
362
+ * Add hints to the query using comment-like syntax `/*+ ... *&#8205;/`. MySQL and Oracle use this syntax for optimizer hints.
363
+ * Also various DB proxies and routers use this syntax to pass hints to alter their behavior. In other dialects the hints
364
+ * are ignored as simple comments.
365
+ */
366
+ hintComment(comment) {
367
+ this.ensureNotFinalized();
368
+ this._hintComments.push(...core_1.Utils.asArray(comment));
369
+ return this;
370
+ }
357
371
  from(target, aliasName) {
358
372
  this.ensureNotFinalized();
359
373
  if (target instanceof QueryBuilder) {
@@ -384,6 +398,8 @@ class QueryBuilder {
384
398
  }, this._orderBy);
385
399
  core_1.Utils.runIfNotEmpty(() => qb.limit(this._limit), this._limit != null);
386
400
  core_1.Utils.runIfNotEmpty(() => qb.offset(this._offset), this._offset);
401
+ core_1.Utils.runIfNotEmpty(() => this._comments.forEach(comment => qb.comment(comment)), this._comments);
402
+ core_1.Utils.runIfNotEmpty(() => this._hintComments.forEach(comment => qb.hintComment(comment)), this._hintComments);
387
403
  core_1.Utils.runIfNotEmpty(() => this.helper.appendOnConflictClause(this.type ?? enums_1.QueryType.SELECT, this._onConflict, qb), this._onConflict);
388
404
  if (this.type === enums_1.QueryType.TRUNCATE && this.platform.usesCascadeStatement()) {
389
405
  return this.knex.raw(qb.toSQL().toNative().sql + ' cascade');
@@ -400,13 +416,14 @@ class QueryBuilder {
400
416
  getQuery() {
401
417
  return this.toQuery().sql;
402
418
  }
419
+ #query;
403
420
  toQuery() {
404
- if (__classPrivateFieldGet(this, _QueryBuilder_query, "f")) {
405
- return __classPrivateFieldGet(this, _QueryBuilder_query, "f");
421
+ if (this.#query) {
422
+ return this.#query;
406
423
  }
407
424
  const sql = this.getKnexQuery().toSQL();
408
425
  const query = sql.toNative();
409
- return __classPrivateFieldSet(this, _QueryBuilder_query, { sql: query.sql, params: query.bindings ?? [], _sql: sql }, "f");
426
+ return this.#query = { sql: query.sql, params: query.bindings ?? [], _sql: sql };
410
427
  }
411
428
  /**
412
429
  * Returns the list of all parameters for this query.
@@ -489,7 +506,7 @@ class QueryBuilder {
489
506
  await this.em.tryFlush(this.mainAlias.entityName, { flushMode: this.flushMode });
490
507
  let res = await this.execute('all', true);
491
508
  if (this._joinedProps.size > 0) {
492
- res = this.driver.mergeJoinedResult(res, this.mainAlias.metadata);
509
+ res = this.driver.mergeJoinedResult(res, this.mainAlias.metadata, [...this._joinedProps.values()]);
493
510
  }
494
511
  const entities = [];
495
512
  function propagatePopulateHint(entity, hint) {
@@ -587,6 +604,7 @@ class QueryBuilder {
587
604
  const properties = [
588
605
  'flags', '_populate', '_populateWhere', '_populateMap', '_joins', '_joinedProps', '_cond', '_data', '_orderBy',
589
606
  '_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables', '_groupBy', '_having', '_returning',
607
+ '_comments', '_hintComments',
590
608
  ];
591
609
  properties.forEach(prop => qb[prop] = core_1.Utils.copy(this[prop]));
592
610
  /* istanbul ignore else */
@@ -711,6 +729,10 @@ class QueryBuilder {
711
729
  }
712
730
  return;
713
731
  }
732
+ if (prop && prop.fieldNames.length > 1) {
733
+ ret.push(...prop.fieldNames.map(f => this.helper.mapper(f, this.type)));
734
+ return;
735
+ }
714
736
  ret.push(this.helper.mapper(field, this.type));
715
737
  });
716
738
  const meta = this.mainAlias.metadata;
@@ -1020,7 +1042,7 @@ class QueryBuilder {
1020
1042
  }
1021
1043
  }
1022
1044
  /* istanbul ignore next */
1023
- [(_QueryBuilder_query = new WeakMap(), util_1.inspect.custom)](depth) {
1045
+ [util_1.inspect.custom](depth) {
1024
1046
  const object = { ...this };
1025
1047
  const hidden = ['metadata', 'driver', 'context', 'platform', 'knex', 'type'];
1026
1048
  Object.keys(object).filter(k => k.startsWith('_')).forEach(k => delete object[k]);
@@ -1,6 +1,5 @@
1
1
  import type { Knex } from 'knex';
2
- import type { Dictionary, EntityData, EntityKey, EntityMetadata, EntityProperty, FlatQueryOrderMap, QBFilterQuery } from '@mikro-orm/core';
3
- import { LockMode } from '@mikro-orm/core';
2
+ import { LockMode, type Dictionary, type EntityData, type EntityKey, type EntityMetadata, type EntityProperty, type FlatQueryOrderMap, type QBFilterQuery } from '@mikro-orm/core';
4
3
  import { QueryType } from './enums';
5
4
  import type { Field, JoinOptions } from '../typings';
6
5
  import type { AbstractSqlDriver } from '../AbstractSqlDriver';
@@ -16,7 +15,7 @@ export declare class QueryBuilderHelper {
16
15
  private readonly driver;
17
16
  private readonly platform;
18
17
  private readonly metadata;
19
- constructor(entityName: string, alias: string, aliasMap: Dictionary<Alias>, subQueries: Dictionary<string>, knex: Knex, driver: AbstractSqlDriver);
18
+ constructor(entityName: string, alias: string, aliasMap: Dictionary<Alias<any>>, subQueries: Dictionary<string>, knex: Knex, driver: AbstractSqlDriver);
20
19
  mapper(field: string | Knex.Raw, type?: QueryType): string;
21
20
  mapper(field: string | Knex.Raw, type?: QueryType, value?: any, alias?: string | null): string;
22
21
  processData(data: Dictionary, convertCustomTypes: boolean, multi?: boolean): any;
@@ -58,9 +57,9 @@ export declare class QueryBuilderHelper {
58
57
  isTableNameAliasRequired(type?: QueryType): boolean;
59
58
  private mapData;
60
59
  }
61
- export interface Alias {
60
+ export interface Alias<T> {
62
61
  aliasName: string;
63
62
  entityName: string;
64
- metadata?: EntityMetadata;
63
+ metadata?: EntityMetadata<T>;
65
64
  subQuery?: Knex.QueryBuilder;
66
65
  }
@@ -112,10 +112,10 @@ class QueryBuilderHelper {
112
112
  const joinColumns = prop.owner ? prop.referencedColumnNames : prop2.joinColumns;
113
113
  const inverseJoinColumns = prop.referencedColumnNames;
114
114
  const primaryKeys = prop.owner ? prop.joinColumns : prop2.referencedColumnNames;
115
+ schema ??= prop.targetMeta?.schema === '*' ? '*' : this.driver.getSchemaName(prop.targetMeta);
115
116
  return {
116
- prop, type, cond, ownerAlias, alias, table,
117
+ prop, type, cond, ownerAlias, alias, table, schema,
117
118
  joinColumns, inverseJoinColumns, primaryKeys,
118
- schema: this.driver.getSchemaName(prop.targetMeta, { schema }),
119
119
  };
120
120
  }
121
121
  joinManyToOneReference(prop, ownerAlias, alias, type, cond = {}, schema) {
@@ -131,12 +131,13 @@ class QueryBuilderHelper {
131
131
  const pivotMeta = this.metadata.find(prop.pivotEntity);
132
132
  const ret = {
133
133
  [`${ownerAlias}.${prop.name}#${pivotAlias}`]: {
134
- prop, type, cond, ownerAlias,
134
+ prop, type, ownerAlias,
135
135
  alias: pivotAlias,
136
136
  inverseAlias: alias,
137
137
  joinColumns: prop.joinColumns,
138
138
  inverseJoinColumns: prop.inverseJoinColumns,
139
139
  primaryKeys: prop.referencedColumnNames,
140
+ cond: {},
140
141
  table: pivotMeta.tableName,
141
142
  schema: this.driver.getSchemaName(pivotMeta, { schema }),
142
143
  path: path.endsWith('[pivot]') ? path : `${path}[pivot]`,
@@ -146,7 +147,7 @@ class QueryBuilderHelper {
146
147
  return ret;
147
148
  }
148
149
  const prop2 = prop.owner ? pivotMeta.relations[1] : pivotMeta.relations[0];
149
- ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type, undefined, schema);
150
+ ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type, cond, schema);
150
151
  ret[`${pivotAlias}.${prop2.name}#${alias}`].path = path;
151
152
  return ret;
152
153
  }
@@ -622,7 +623,7 @@ class QueryBuilderHelper {
622
623
  if (prop.hasConvertToDatabaseValueSQL && !this.platform.isRaw(data[k])) {
623
624
  const quoted = this.platform.quoteValue(data[k]);
624
625
  const sql = prop.customType.convertToDatabaseValueSQL(quoted, this.platform);
625
- data[k] = this.knex.raw(sql.replace(/\?/, '\\?'));
626
+ data[k] = this.knex.raw(sql.replace(/\?/g, '\\?'));
626
627
  }
627
628
  if (!prop.customType && (Array.isArray(data[k]) || core_1.Utils.isPlainObject(data[k]))) {
628
629
  data[k] = JSON.stringify(data[k]);
@@ -1,4 +1,4 @@
1
- import type { Configuration, Dictionary, EntityMetadata } from '@mikro-orm/core';
1
+ import { type Configuration, type Dictionary, type EntityMetadata } from '@mikro-orm/core';
2
2
  import { DatabaseTable } from './DatabaseTable';
3
3
  import type { AbstractSqlConnection } from '../AbstractSqlConnection';
4
4
  import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
@@ -1,4 +1,4 @@
1
- import type { Dictionary, EntityMetadata, EntityProperty, NamingStrategy } from '@mikro-orm/core';
1
+ import { type Dictionary, type EntityMetadata, type EntityProperty, type NamingStrategy } from '@mikro-orm/core';
2
2
  import type { SchemaHelper } from './SchemaHelper';
3
3
  import type { CheckDef, Column, ForeignKey, IndexDef } from '../typings';
4
4
  import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
@@ -18,6 +18,7 @@ export declare class DatabaseTable {
18
18
  constructor(platform: AbstractSqlPlatform, name: string, schema?: string | undefined);
19
19
  getColumns(): Column[];
20
20
  getColumn(name: string): Column | undefined;
21
+ removeColumn(name: string): void;
21
22
  getIndexes(): IndexDef[];
22
23
  getChecks(): CheckDef[];
23
24
  init(cols: Column[], indexes: IndexDef[] | undefined, checks: CheckDef<unknown>[] | undefined, pks: string[], fks?: Dictionary<ForeignKey>, enums?: Dictionary<string[]>): void;
@@ -25,6 +25,9 @@ class DatabaseTable {
25
25
  getColumn(name) {
26
26
  return this.columns[name];
27
27
  }
28
+ removeColumn(name) {
29
+ delete this.columns[name];
30
+ }
28
31
  getIndexes() {
29
32
  return this.indexes;
30
33
  }
@@ -228,6 +231,8 @@ class DatabaseTable {
228
231
  primary: column.primary,
229
232
  fieldName: column.name,
230
233
  length: column.length,
234
+ precision: column.precision,
235
+ scale: column.scale,
231
236
  index: index ? index.keyName : undefined,
232
237
  unique: unique ? unique.keyName : undefined,
233
238
  enum: !!column.enumItems?.length,
@@ -1,5 +1,5 @@
1
1
  import type { Knex } from 'knex';
2
- import type { Connection, Dictionary } from '@mikro-orm/core';
2
+ import { type Connection, type Dictionary } from '@mikro-orm/core';
3
3
  import type { AbstractSqlConnection } from '../AbstractSqlConnection';
4
4
  import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
5
5
  import type { CheckDef, Column, IndexDef, Table, TableDifference } from '../typings';
@@ -1,5 +1,4 @@
1
- import type { ISchemaGenerator, MikroORM } from '@mikro-orm/core';
2
- import { AbstractSchemaGenerator } from '@mikro-orm/core';
1
+ import { AbstractSchemaGenerator, type MikroORM, type ISchemaGenerator } from '@mikro-orm/core';
3
2
  import type { SchemaDifference } from '../typings';
4
3
  import { DatabaseSchema } from './DatabaseSchema';
5
4
  import type { AbstractSqlDriver } from '../AbstractSqlDriver';