@mikro-orm/knex 5.7.15-dev.4 → 5.7.15-dev.41

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.
@@ -12,11 +12,13 @@ export declare abstract class AbstractSqlConnection extends Connection {
12
12
  isConnected(): Promise<boolean>;
13
13
  transactional<T>(cb: (trx: Transaction<Knex.Transaction>) => Promise<T>, options?: {
14
14
  isolationLevel?: IsolationLevel;
15
+ readOnly?: boolean;
15
16
  ctx?: Knex.Transaction;
16
17
  eventBroadcaster?: TransactionEventBroadcaster;
17
18
  }): Promise<T>;
18
19
  begin(options?: {
19
20
  isolationLevel?: IsolationLevel;
21
+ readOnly?: boolean;
20
22
  ctx?: Knex.Transaction;
21
23
  eventBroadcaster?: TransactionEventBroadcaster;
22
24
  }): Promise<Knex.Transaction>;
@@ -46,7 +46,10 @@ class AbstractSqlConnection extends core_1.Connection {
46
46
  if (!options.ctx) {
47
47
  await options.eventBroadcaster?.dispatchEvent(core_1.EventType.beforeTransactionStart);
48
48
  }
49
- const trx = await (options.ctx || this.client).transaction(null, { isolationLevel: options.isolationLevel });
49
+ const trx = await (options.ctx || this.client).transaction(null, {
50
+ isolationLevel: options.isolationLevel,
51
+ readOnly: options.readOnly,
52
+ });
50
53
  if (!options.ctx) {
51
54
  await options.eventBroadcaster?.dispatchEvent(core_1.EventType.afterTransactionStart, trx);
52
55
  }
@@ -38,6 +38,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
38
38
  .orderBy([...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy])
39
39
  .groupBy(options.groupBy)
40
40
  .having(options.having)
41
+ .indexHint(options.indexHint)
42
+ .comment(options.comments)
43
+ .hintComment(options.hintComments)
41
44
  .withSchema(this.getSchemaName(meta, options));
42
45
  if (options.limit !== undefined) {
43
46
  qb.limit(options.limit, options.offset);
@@ -105,7 +108,10 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
105
108
  }
106
109
  async wrapVirtualExpressionInSubquery(meta, expression, where, options, type = query_1.QueryType.SELECT) {
107
110
  const qb = this.createQueryBuilder(meta.className, options?.ctx, options.connectionType, options.convertCustomTypes)
108
- .limit(options?.limit, options?.offset);
111
+ .limit(options?.limit, options?.offset)
112
+ .indexHint(options.indexHint)
113
+ .comment(options.comments)
114
+ .hintComment(options.hintComments);
109
115
  if (options.orderBy) {
110
116
  qb.orderBy(options.orderBy);
111
117
  }
@@ -207,6 +213,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
207
213
  return this.countVirtual(entityName, where, options);
208
214
  }
209
215
  const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false)
216
+ .indexHint(options.indexHint)
217
+ .comment(options.comments)
218
+ .hintComment(options.hintComments)
210
219
  .groupBy(options.groupBy)
211
220
  .having(options.having)
212
221
  .populate(options.populate ?? [])
@@ -335,7 +344,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
335
344
  const uniqueFields = core_1.Utils.isPlainObject(where) ? Object.keys(where) : meta.primaryKeys;
336
345
  /* istanbul ignore next */
337
346
  qb.insert(data)
338
- .onConflict(uniqueFields.map(p => meta?.properties[p]?.fieldNames[0] ?? p))
347
+ .onConflict(uniqueFields)
339
348
  .merge(Object.keys(data).filter(f => !uniqueFields.includes(f)))
340
349
  .returning(meta?.comparableProps.filter(p => !p.lazy && !p.embeddable && !(p.name in data)).map(p => p.name) ?? '*');
341
350
  }
@@ -357,7 +366,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
357
366
  const uniqueFields = core_1.Utils.isPlainObject(where[0]) ? Object.keys(where[0]) : meta.primaryKeys;
358
367
  const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
359
368
  qb.insert(data)
360
- .onConflict(uniqueFields.map(p => meta.properties[p]?.fieldNames[0] ?? p))
369
+ .onConflict(uniqueFields)
361
370
  .merge(Object.keys(data[0]).filter(f => !uniqueFields.includes(f)))
362
371
  .returning(meta.comparableProps.filter(p => !p.lazy && !p.embeddable && !(p.name in data[0])).map(p => p.name) ?? '*');
363
372
  return qb.execute('run', false);
@@ -461,11 +470,23 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
461
470
  if (coll.property.reference === core_1.ReferenceType.ONE_TO_MANY) {
462
471
  const cols = coll.property.referencedColumnNames;
463
472
  const qb = this.createQueryBuilder(coll.property.type, ctx, 'write')
464
- .withSchema(this.getSchemaName(meta, options))
465
- .update({ [coll.property.mappedBy]: pks })
473
+ .withSchema(this.getSchemaName(meta, options));
474
+ if (coll.getSnapshot() === undefined) {
475
+ if (coll.property.orphanRemoval) {
476
+ const kqb = qb.delete({ [coll.property.mappedBy]: pks })
477
+ .getKnexQuery()
478
+ .whereNotIn(cols, insertDiff);
479
+ return this.rethrow(this.execute(kqb));
480
+ }
481
+ const kqb = qb.update({ [coll.property.mappedBy]: null })
482
+ .getKnexQuery()
483
+ .whereNotIn(cols, insertDiff);
484
+ return this.rethrow(this.execute(kqb));
485
+ }
486
+ const kqb = qb.update({ [coll.property.mappedBy]: pks })
466
487
  .getKnexQuery()
467
488
  .whereIn(cols, insertDiff);
468
- return this.rethrow(this.execute(qb));
489
+ return this.rethrow(this.execute(kqb));
469
490
  }
470
491
  /* istanbul ignore next */
471
492
  const ownerSchema = wrapped.getSchema() === '*' ? this.config.get('schema') : wrapped.getSchema();
@@ -489,17 +510,22 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
489
510
  else {
490
511
  where = { ...where, ...cond };
491
512
  }
513
+ /* istanbul ignore if */
514
+ options = { ...options };
492
515
  orderBy = this.getPivotOrderBy(prop, orderBy);
493
- const qb = this.createQueryBuilder(prop.type, ctx, options?.connectionType)
516
+ const qb = this.createQueryBuilder(prop.type, ctx, options.connectionType)
517
+ .indexHint(options.indexHint)
518
+ .comment(options.comments)
519
+ .hintComment(options.hintComments)
494
520
  .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES)
495
521
  .withSchema(this.getSchemaName(prop.targetMeta, options));
496
522
  const populate = this.autoJoinOneToOneOwner(prop.targetMeta, [{ field: prop.pivotEntity }]);
497
- const fields = this.buildFields(prop.targetMeta, (options?.populate ?? []), [], qb, options?.fields);
498
- qb.select(fields).populate(populate).where(where).orderBy(orderBy).setLockMode(options?.lockMode, options?.lockTableAliases);
499
- if (owners.length === 1 && (options?.offset != null || options?.limit != null)) {
523
+ const fields = this.buildFields(prop.targetMeta, (options.populate ?? []), [], qb, options.fields);
524
+ qb.select(fields).populate(populate).where(where).orderBy(orderBy).setLockMode(options.lockMode, options.lockTableAliases);
525
+ if (owners.length === 1 && (options.offset != null || options.limit != null)) {
500
526
  qb.limit(options.limit, options.offset);
501
527
  }
502
- if (prop.targetMeta.schema !== '*' && pivotMeta.schema === '*' && options?.schema) {
528
+ if (prop.targetMeta.schema !== '*' && pivotMeta.schema === '*' && options.schema) {
503
529
  // eslint-disable-next-line dot-notation
504
530
  qb['finalize']();
505
531
  // eslint-disable-next-line dot-notation
@@ -672,9 +698,10 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
672
698
  if (!deleteDiff) {
673
699
  deleteDiff = [];
674
700
  }
701
+ const pivotMeta = this.metadata.find(prop.pivotEntity);
675
702
  if (deleteDiff === true || deleteDiff.length > 0) {
676
703
  const qb1 = this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
677
- .withSchema(this.getSchemaName(meta, options))
704
+ .withSchema(this.getSchemaName(pivotMeta, options))
678
705
  .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
679
706
  const knex = qb1.getKnex();
680
707
  if (Array.isArray(deleteDiff)) {
@@ -700,7 +727,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
700
727
  await core_1.Utils.runSerial(items, item => {
701
728
  return this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
702
729
  .unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES)
703
- .withSchema(this.getSchemaName(meta, options))
730
+ .withSchema(this.getSchemaName(pivotMeta, options))
704
731
  .insert(item)
705
732
  .execute('run', false);
706
733
  });
@@ -754,7 +781,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
754
781
  }
755
782
  if (prop.reference === core_1.ReferenceType.EMBEDDED) {
756
783
  if (prop.object) {
757
- ret.push(prop.fieldNames[0]);
784
+ ret.push(prop.name);
758
785
  return;
759
786
  }
760
787
  const parts = field.split('.');
@@ -766,13 +793,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
766
793
  }
767
794
  return;
768
795
  }
769
- if (prop.formula) {
770
- ret.push(prop.name);
771
- return;
772
- }
773
- if (prop.fieldNames) {
774
- ret.push(...prop.fieldNames);
775
- }
796
+ ret.push(prop.name);
776
797
  }
777
798
  buildFields(meta, populate, joinedProps, qb, fields) {
778
799
  const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => p.field === prop.name || p.all));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/knex",
3
- "version": "5.7.15-dev.4",
3
+ "version": "5.7.15-dev.41",
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,9 +66,9 @@
66
66
  "@mikro-orm/core": "^5.7.14"
67
67
  },
68
68
  "peerDependencies": {
69
- "@mikro-orm/core": "~5.7.15-dev.4",
70
- "@mikro-orm/entity-generator": "~5.7.15-dev.4",
71
- "@mikro-orm/migrations": "~5.7.15-dev.4",
69
+ "@mikro-orm/core": "~5.7.15-dev.41",
70
+ "@mikro-orm/entity-generator": "~5.7.15-dev.41",
71
+ "@mikro-orm/migrations": "~5.7.15-dev.41",
72
72
  "better-sqlite3": "*",
73
73
  "mssql": "*",
74
74
  "mysql": "*",
@@ -34,7 +34,7 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
34
34
  private readonly context?;
35
35
  private connectionType?;
36
36
  private readonly em?;
37
- get mainAlias(): Alias;
37
+ get mainAlias(): Alias<T>;
38
38
  get alias(): string;
39
39
  get helper(): QueryBuilderHelper;
40
40
  /** @internal */
@@ -66,6 +66,8 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
66
66
  private _joinedProps;
67
67
  private _cache?;
68
68
  private _indexHint?;
69
+ private _comments;
70
+ private _hintComments;
69
71
  private flushMode?;
70
72
  private lockMode?;
71
73
  private lockTables?;
@@ -132,6 +134,16 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
132
134
  * Adds index hint to the FROM clause.
133
135
  */
134
136
  indexHint(sql: string): this;
137
+ /**
138
+ * Prepend comment to the sql query using the syntax `/* ... *&#8205;/`. Some characters are forbidden such as `/*, *&#8205;/` and `?`.
139
+ */
140
+ comment(comment: string | string[]): this;
141
+ /**
142
+ * Add hints to the query using comment-like syntax `/*+ ... *&#8205;/`. MySQL and Oracle use this syntax for optimizer hints.
143
+ * Also various DB proxies and routers use this syntax to pass hints to alter their behavior. In other dialects the hints
144
+ * are ignored as simple comments.
145
+ */
146
+ hintComment(comment: string | string[]): this;
135
147
  /**
136
148
  * Specifies FROM which entity's table select/update/delete will be executed, removing all previously set FROM-s.
137
149
  * Allows setting a main string alias of the selection data.
@@ -60,6 +60,8 @@ class QueryBuilder {
60
60
  this._groupBy = [];
61
61
  this._having = {};
62
62
  this._joinedProps = new Map();
63
+ this._comments = [];
64
+ this._hintComments = [];
63
65
  this.subQueries = {};
64
66
  this._aliases = {};
65
67
  this.platform = this.driver.getPlatform();
@@ -244,9 +246,16 @@ class QueryBuilder {
244
246
  return this;
245
247
  }
246
248
  onConflict(fields = []) {
249
+ const meta = this.mainAlias.metadata;
247
250
  this.ensureNotFinalized();
248
- this._onConflict = this._onConflict || [];
249
- this._onConflict.push({ fields: core_1.Utils.asArray(fields).map(f => f.toString()) });
251
+ this._onConflict ?? (this._onConflict = []);
252
+ this._onConflict.push({
253
+ fields: core_1.Utils.asArray(fields).flatMap(f => {
254
+ const key = f.toString();
255
+ /* istanbul ignore next */
256
+ return meta.properties[key]?.fieldNames ?? [key];
257
+ }),
258
+ });
250
259
  return this;
251
260
  }
252
261
  ignore() {
@@ -345,6 +354,24 @@ class QueryBuilder {
345
354
  this._indexHint = sql;
346
355
  return this;
347
356
  }
357
+ /**
358
+ * Prepend comment to the sql query using the syntax `/* ... *&#8205;/`. Some characters are forbidden such as `/*, *&#8205;/` and `?`.
359
+ */
360
+ comment(comment) {
361
+ this.ensureNotFinalized();
362
+ this._comments.push(...core_1.Utils.asArray(comment));
363
+ return this;
364
+ }
365
+ /**
366
+ * Add hints to the query using comment-like syntax `/*+ ... *&#8205;/`. MySQL and Oracle use this syntax for optimizer hints.
367
+ * Also various DB proxies and routers use this syntax to pass hints to alter their behavior. In other dialects the hints
368
+ * are ignored as simple comments.
369
+ */
370
+ hintComment(comment) {
371
+ this.ensureNotFinalized();
372
+ this._hintComments.push(...core_1.Utils.asArray(comment));
373
+ return this;
374
+ }
348
375
  from(target, aliasName) {
349
376
  this.ensureNotFinalized();
350
377
  if (target instanceof QueryBuilder) {
@@ -374,6 +401,8 @@ class QueryBuilder {
374
401
  }, this._orderBy);
375
402
  core_1.Utils.runIfNotEmpty(() => qb.limit(this._limit), this._limit != null);
376
403
  core_1.Utils.runIfNotEmpty(() => qb.offset(this._offset), this._offset);
404
+ core_1.Utils.runIfNotEmpty(() => this._comments.forEach(comment => qb.comment(comment)), this._comments);
405
+ core_1.Utils.runIfNotEmpty(() => this._hintComments.forEach(comment => qb.hintComment(comment)), this._hintComments);
377
406
  core_1.Utils.runIfNotEmpty(() => this.helper.appendOnConflictClause(this.type ?? enums_1.QueryType.SELECT, this._onConflict, qb), this._onConflict);
378
407
  if (this.type === enums_1.QueryType.TRUNCATE && this.platform.usesCascadeStatement()) {
379
408
  return this.knex.raw(qb.toSQL().toNative().sql + ' cascade');
@@ -570,6 +599,7 @@ class QueryBuilder {
570
599
  const properties = [
571
600
  'flags', '_populate', '_populateWhere', '_populateMap', '_joins', '_joinedProps', '_cond', '_data', '_orderBy',
572
601
  '_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables', '_groupBy', '_having', '_returning',
602
+ '_comments', '_hintComments',
573
603
  ];
574
604
  properties.forEach(prop => qb[prop] = core_1.Utils.copy(this[prop]));
575
605
  /* istanbul ignore else */
@@ -686,6 +716,10 @@ class QueryBuilder {
686
716
  }
687
717
  return;
688
718
  }
719
+ if (prop && prop.fieldNames.length > 1) {
720
+ ret.push(...prop.fieldNames.map(f => this.helper.mapper(f, this.type)));
721
+ return;
722
+ }
689
723
  ret.push(this.helper.mapper(field, this.type));
690
724
  });
691
725
  const meta = this.mainAlias.metadata;
@@ -16,7 +16,7 @@ export declare class QueryBuilderHelper {
16
16
  private readonly driver;
17
17
  private readonly platform;
18
18
  private readonly metadata;
19
- constructor(entityName: string, alias: string, aliasMap: Dictionary<Alias>, subQueries: Dictionary<string>, knex: Knex, driver: AbstractSqlDriver);
19
+ constructor(entityName: string, alias: string, aliasMap: Dictionary<Alias<any>>, subQueries: Dictionary<string>, knex: Knex, driver: AbstractSqlDriver);
20
20
  mapper(field: string | Knex.Raw, type?: QueryType): string;
21
21
  mapper(field: string | Knex.Raw, type?: QueryType, value?: any, alias?: string | null): string;
22
22
  processData(data: Dictionary, convertCustomTypes: boolean, multi?: boolean): any;
@@ -61,9 +61,9 @@ export declare class QueryBuilderHelper {
61
61
  isTableNameAliasRequired(type?: QueryType): boolean;
62
62
  private mapData;
63
63
  }
64
- export interface Alias {
64
+ export interface Alias<T> {
65
65
  aliasName: string;
66
66
  entityName: string;
67
- metadata?: EntityMetadata;
67
+ metadata?: EntityMetadata<T>;
68
68
  subQuery?: Knex.QueryBuilder;
69
69
  }
@@ -128,12 +128,13 @@ class QueryBuilderHelper {
128
128
  const pivotMeta = this.metadata.find(prop.pivotEntity);
129
129
  const ret = {
130
130
  [`${ownerAlias}.${prop.name}#${pivotAlias}`]: {
131
- prop, type, cond, ownerAlias,
131
+ prop, type, ownerAlias,
132
132
  alias: pivotAlias,
133
133
  inverseAlias: alias,
134
134
  joinColumns: prop.joinColumns,
135
135
  inverseJoinColumns: prop.inverseJoinColumns,
136
136
  primaryKeys: prop.referencedColumnNames,
137
+ cond: {},
137
138
  table: pivotMeta.tableName,
138
139
  schema: this.driver.getSchemaName(pivotMeta),
139
140
  path: path.endsWith('[pivot]') ? path : `${path}[pivot]`,
@@ -143,7 +144,7 @@ class QueryBuilderHelper {
143
144
  return ret;
144
145
  }
145
146
  const prop2 = prop.owner ? pivotMeta.relations[1] : pivotMeta.relations[0];
146
- ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type);
147
+ ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type, cond);
147
148
  ret[`${pivotAlias}.${prop2.name}#${alias}`].path = path;
148
149
  return ret;
149
150
  }
@@ -17,6 +17,7 @@ export declare class DatabaseTable {
17
17
  constructor(platform: AbstractSqlPlatform, name: string, schema?: string | undefined);
18
18
  getColumns(): Column[];
19
19
  getColumn(name: string): Column | undefined;
20
+ removeColumn(name: string): void;
20
21
  getIndexes(): Index[];
21
22
  getChecks(): Check[];
22
23
  init(cols: Column[], indexes: Index[] | undefined, checks: Check<unknown>[] | undefined, pks: string[], fks?: Dictionary<ForeignKey>, enums?: Dictionary<string[]>): void;
@@ -24,6 +24,9 @@ class DatabaseTable {
24
24
  getColumn(name) {
25
25
  return this.columns[name];
26
26
  }
27
+ removeColumn(name) {
28
+ delete this.columns[name];
29
+ }
27
30
  getIndexes() {
28
31
  return this.indexes;
29
32
  }
@@ -227,6 +230,8 @@ class DatabaseTable {
227
230
  primary: column.primary,
228
231
  fieldName: column.name,
229
232
  length: column.length,
233
+ precision: column.precision,
234
+ scale: column.scale,
230
235
  index: index ? index.keyName : undefined,
231
236
  unique: unique ? unique.keyName : undefined,
232
237
  enum: !!column.enumItems?.length,