@mikro-orm/knex 6.0.0-dev.8 → 6.0.0-dev.80

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.
Files changed (38) hide show
  1. package/AbstractSqlConnection.d.ts +5 -2
  2. package/AbstractSqlConnection.js +25 -17
  3. package/AbstractSqlDriver.d.ts +18 -18
  4. package/AbstractSqlDriver.js +117 -48
  5. package/AbstractSqlPlatform.d.ts +1 -0
  6. package/AbstractSqlPlatform.js +20 -5
  7. package/MonkeyPatchable.d.ts +1 -0
  8. package/MonkeyPatchable.js +3 -0
  9. package/README.md +64 -107
  10. package/SqlEntityManager.d.ts +2 -5
  11. package/SqlEntityManager.js +5 -9
  12. package/SqlEntityRepository.d.ts +6 -4
  13. package/SqlEntityRepository.js +10 -8
  14. package/index.mjs +19 -6
  15. package/package.json +7 -7
  16. package/query/ArrayCriteriaNode.d.ts +3 -3
  17. package/query/CriteriaNode.d.ts +8 -8
  18. package/query/CriteriaNode.js +9 -9
  19. package/query/CriteriaNodeFactory.d.ts +6 -6
  20. package/query/CriteriaNodeFactory.js +10 -13
  21. package/query/ObjectCriteriaNode.d.ts +3 -3
  22. package/query/ObjectCriteriaNode.js +12 -8
  23. package/query/QueryBuilder.d.ts +25 -7
  24. package/query/QueryBuilder.js +237 -61
  25. package/query/QueryBuilderHelper.d.ts +3 -5
  26. package/query/QueryBuilderHelper.js +96 -74
  27. package/query/ScalarCriteriaNode.d.ts +3 -2
  28. package/query/ScalarCriteriaNode.js +9 -6
  29. package/schema/DatabaseSchema.d.ts +4 -1
  30. package/schema/DatabaseSchema.js +22 -6
  31. package/schema/DatabaseTable.d.ts +2 -1
  32. package/schema/DatabaseTable.js +25 -24
  33. package/schema/SchemaComparator.js +9 -2
  34. package/schema/SchemaHelper.d.ts +5 -3
  35. package/schema/SchemaHelper.js +10 -4
  36. package/schema/SqlSchemaGenerator.d.ts +3 -5
  37. package/schema/SqlSchemaGenerator.js +41 -20
  38. package/typings.d.ts +8 -5
@@ -1,5 +1,5 @@
1
1
  import type { Knex } from 'knex';
2
- import type { AnyEntity, Configuration, ConnectionOptions, EntityData, IsolationLevel, QueryResult, Transaction, TransactionEventBroadcaster } from '@mikro-orm/core';
2
+ import type { AnyEntity, Configuration, ConnectionOptions, EntityData, IsolationLevel, LoggingOptions, QueryResult, Transaction, TransactionEventBroadcaster } from '@mikro-orm/core';
3
3
  import { Connection } from '@mikro-orm/core';
4
4
  import type { AbstractSqlPlatform } from './AbstractSqlPlatform';
5
5
  export declare abstract class AbstractSqlConnection extends Connection {
@@ -7,6 +7,9 @@ export declare abstract class AbstractSqlConnection extends Connection {
7
7
  protected platform: AbstractSqlPlatform;
8
8
  protected client: Knex;
9
9
  constructor(config: Configuration, options?: ConnectionOptions, type?: 'read' | 'write');
10
+ abstract createKnex(): void;
11
+ /** @inheritDoc */
12
+ connect(): void | Promise<void>;
10
13
  getKnex(): Knex;
11
14
  close(force?: boolean): Promise<void>;
12
15
  isConnected(): Promise<boolean>;
@@ -22,7 +25,7 @@ export declare abstract class AbstractSqlConnection extends Connection {
22
25
  }): Promise<Knex.Transaction>;
23
26
  commit(ctx: Knex.Transaction, eventBroadcaster?: TransactionEventBroadcaster): Promise<void>;
24
27
  rollback(ctx: Knex.Transaction, eventBroadcaster?: TransactionEventBroadcaster): Promise<void>;
25
- execute<T extends QueryResult | EntityData<AnyEntity> | EntityData<AnyEntity>[] = EntityData<AnyEntity>[]>(queryOrKnex: string | Knex.QueryBuilder | Knex.Raw, params?: unknown[], method?: 'all' | 'get' | 'run', ctx?: Transaction): Promise<T>;
28
+ execute<T extends QueryResult | EntityData<AnyEntity> | EntityData<AnyEntity>[] = EntityData<AnyEntity>[]>(queryOrKnex: string | Knex.QueryBuilder | Knex.Raw, params?: unknown[], method?: 'all' | 'get' | 'run', ctx?: Transaction, logging?: LoggingOptions): Promise<T>;
26
29
  /**
27
30
  * Execute raw SQL queries from file
28
31
  */
@@ -14,16 +14,23 @@ class AbstractSqlConnection extends core_1.Connection {
14
14
  super(config, options, type);
15
15
  this.patchKnexClient();
16
16
  }
17
+ /** @inheritDoc */
18
+ connect() {
19
+ this.createKnex();
20
+ }
17
21
  getKnex() {
22
+ if (!this.client) {
23
+ this.createKnex();
24
+ }
18
25
  return this.client;
19
26
  }
20
27
  async close(force) {
21
28
  await super.close(force);
22
- await this.client.destroy();
29
+ await this.getKnex().destroy();
23
30
  }
24
31
  async isConnected() {
25
32
  try {
26
- await this.client.raw('select 1');
33
+ await this.getKnex().raw('select 1');
27
34
  return true;
28
35
  }
29
36
  catch {
@@ -46,7 +53,7 @@ class AbstractSqlConnection extends core_1.Connection {
46
53
  if (!options.ctx) {
47
54
  await options.eventBroadcaster?.dispatchEvent(core_1.EventType.beforeTransactionStart);
48
55
  }
49
- const trx = await (options.ctx || this.client).transaction(null, { isolationLevel: options.isolationLevel });
56
+ const trx = await (options.ctx || this.getKnex()).transaction(null, { isolationLevel: options.isolationLevel });
50
57
  if (!options.ctx) {
51
58
  await options.eventBroadcaster?.dispatchEvent(core_1.EventType.afterTransactionStart, trx);
52
59
  }
@@ -76,30 +83,31 @@ class AbstractSqlConnection extends core_1.Connection {
76
83
  await eventBroadcaster?.dispatchEvent(core_1.EventType.afterTransactionRollback, ctx);
77
84
  }
78
85
  }
79
- async execute(queryOrKnex, params = [], method = 'all', ctx) {
86
+ async execute(queryOrKnex, params = [], method = 'all', ctx, logging) {
87
+ await this.ensureConnection();
80
88
  if (core_1.Utils.isObject(queryOrKnex)) {
81
- ctx ?? (ctx = queryOrKnex.client.transacting ? queryOrKnex : null);
89
+ ctx ??= (queryOrKnex.client.transacting ? queryOrKnex : null);
82
90
  const q = queryOrKnex.toSQL();
83
91
  queryOrKnex = q.sql;
84
92
  params = q.bindings;
85
93
  }
86
94
  const formatted = this.platform.formatQuery(queryOrKnex, params);
87
- const sql = this.getSql(queryOrKnex, formatted);
88
- const res = await this.executeQuery(sql, () => {
89
- const query = this.client.raw(formatted);
95
+ const sql = this.getSql(queryOrKnex, formatted, logging);
96
+ return this.executeQuery(sql, async () => {
97
+ const query = this.getKnex().raw(formatted);
90
98
  if (ctx) {
91
99
  query.transacting(ctx);
92
100
  }
93
- return query;
94
- }, { query: queryOrKnex, params });
95
- return this.transformRawResult(res, method);
101
+ const res = await query;
102
+ return this.transformRawResult(res, method);
103
+ }, { query: queryOrKnex, params, ...logging });
96
104
  }
97
105
  /**
98
106
  * Execute raw SQL queries from file
99
107
  */
100
108
  async loadFile(path) {
101
109
  const buf = await (0, fs_extra_1.readFile)(path);
102
- await this.client.raw(buf.toString());
110
+ await this.getKnex().raw(buf.toString());
103
111
  }
104
112
  createKnexClient(type) {
105
113
  const driverOptions = this.config.get('driverOptions');
@@ -138,15 +146,15 @@ class AbstractSqlConnection extends core_1.Connection {
138
146
  };
139
147
  return config;
140
148
  }
141
- getSql(query, formatted) {
149
+ getSql(query, formatted, context) {
142
150
  const logger = this.config.getLogger();
143
- if (!logger.isEnabled('query')) {
151
+ if (!logger.isEnabled('query', context)) {
144
152
  return query;
145
153
  }
146
- if (logger.isEnabled('query-params')) {
154
+ if (logger.isEnabled('query-params', context)) {
147
155
  return formatted;
148
156
  }
149
- return this.client.client.positionBindings(query);
157
+ return this.getKnex().client.positionBindings(query);
150
158
  }
151
159
  /**
152
160
  * do not call `positionBindings` when there are no bindings - it was messing up with
@@ -177,5 +185,5 @@ class AbstractSqlConnection extends core_1.Connection {
177
185
  };
178
186
  }
179
187
  }
180
- exports.AbstractSqlConnection = AbstractSqlConnection;
181
188
  AbstractSqlConnection.__patched = false;
189
+ exports.AbstractSqlConnection = AbstractSqlConnection;
@@ -1,26 +1,26 @@
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 } from '@mikro-orm/core';
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
3
  import { DatabaseDriver, EntityManagerType } from '@mikro-orm/core';
4
4
  import type { AbstractSqlConnection } from './AbstractSqlConnection';
5
5
  import type { AbstractSqlPlatform } from './AbstractSqlPlatform';
6
6
  import { QueryBuilder, QueryType } from './query';
7
7
  import { SqlEntityManager } from './SqlEntityManager';
8
8
  import type { Field } from './typings';
9
- export declare abstract class AbstractSqlDriver<C extends AbstractSqlConnection = AbstractSqlConnection> extends DatabaseDriver<C> {
9
+ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlConnection = AbstractSqlConnection, Platform extends AbstractSqlPlatform = AbstractSqlPlatform> extends DatabaseDriver<Connection> {
10
10
  [EntityManagerType]: SqlEntityManager<this>;
11
- protected readonly connection: C;
12
- protected readonly replicas: C[];
13
- protected readonly platform: AbstractSqlPlatform;
14
- protected constructor(config: Configuration, platform: AbstractSqlPlatform, connection: Constructor<C>, connector: string[]);
15
- getPlatform(): AbstractSqlPlatform;
11
+ protected readonly connection: Connection;
12
+ protected readonly replicas: Connection[];
13
+ protected readonly platform: Platform;
14
+ protected constructor(config: Configuration, platform: Platform, connection: Constructor<Connection>, connector: string[]);
15
+ getPlatform(): Platform;
16
16
  createEntityManager<D extends IDatabaseDriver = IDatabaseDriver>(useContext?: boolean): D[typeof EntityManagerType];
17
- find<T extends object, P extends string = never>(entityName: string, where: FilterQuery<T>, options?: FindOptions<T, P>): Promise<EntityData<T>[]>;
18
- findOne<T extends object, P extends string = never>(entityName: string, where: FilterQuery<T>, options?: FindOneOptions<T, P>): Promise<EntityData<T> | null>;
19
- findVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: FindOptions<T, any>): Promise<EntityData<T>[]>;
17
+ find<T extends object, P extends string = never, F extends string = '*'>(entityName: string, where: FilterQuery<T>, options?: FindOptions<T, P, F>): Promise<EntityData<T>[]>;
18
+ findOne<T extends object, P extends string = never, F extends string = '*'>(entityName: string, where: FilterQuery<T>, options?: FindOneOptions<T, P, F>): Promise<EntityData<T> | null>;
19
+ findVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: FindOptions<T, any, any>): Promise<EntityData<T>[]>;
20
20
  countVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: CountOptions<T>): Promise<number>;
21
- protected wrapVirtualExpressionInSubquery<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any>, type: QueryType.COUNT): Promise<number>;
22
- protected wrapVirtualExpressionInSubquery<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any>, type: QueryType.SELECT): Promise<T[]>;
23
- protected wrapVirtualExpressionInSubquery<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any>): Promise<T[]>;
21
+ protected wrapVirtualExpressionInSubquery<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any, any>, type: QueryType.COUNT): Promise<number>;
22
+ protected wrapVirtualExpressionInSubquery<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any, any>, type: QueryType.SELECT): Promise<T[]>;
23
+ protected wrapVirtualExpressionInSubquery<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any, any>): Promise<T[]>;
24
24
  mapResult<T extends object>(result: EntityData<T>, meta: EntityMetadata<T>, populate?: PopulateOptions<T>[], qb?: QueryBuilder<T>, map?: Dictionary): EntityData<T> | null;
25
25
  private mapJoinedProps;
26
26
  private appendToCollection;
@@ -31,12 +31,12 @@ export declare abstract class AbstractSqlDriver<C extends AbstractSqlConnection
31
31
  nativeUpdateMany<T extends object>(entityName: string, where: FilterQuery<T>[], data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T>): Promise<QueryResult<T>>;
32
32
  nativeDelete<T extends object>(entityName: string, where: FilterQuery<T> | string | any, options?: DeleteOptions<T>): Promise<QueryResult<T>>;
33
33
  syncCollection<T extends object, O extends object>(coll: Collection<T, O>, options?: DriverMethodOptions): Promise<void>;
34
- loadFromPivotTable<T extends object, O extends object>(prop: EntityProperty, owners: Primary<O>[][], where?: FilterQuery<any>, orderBy?: QueryOrderMap<T>[], ctx?: Transaction, options?: FindOptions<T, any>): Promise<Dictionary<T[]>>;
34
+ 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[]>>;
35
35
  execute<T extends QueryResult | EntityData<AnyEntity> | EntityData<AnyEntity>[] = EntityData<AnyEntity>[]>(queryOrKnex: string | Knex.QueryBuilder | Knex.Raw, params?: any[], method?: 'all' | 'get' | 'run', ctx?: Transaction): Promise<T>;
36
36
  /**
37
37
  * 1:1 owner side needs to be marked for population so QB auto-joins the owner id
38
38
  */
39
- protected autoJoinOneToOneOwner<T extends object, P extends string = never>(meta: EntityMetadata, populate: PopulateOptions<T>[], fields?: readonly EntityField<T, P>[]): PopulateOptions<T>[];
39
+ protected autoJoinOneToOneOwner<T extends object, P extends string = never>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], fields?: readonly EntityField<T, P>[]): PopulateOptions<T>[];
40
40
  protected joinedProps<T>(meta: EntityMetadata, populate: PopulateOptions<T>[]): PopulateOptions<T>[];
41
41
  /**
42
42
  * @internal
@@ -48,17 +48,17 @@ export declare abstract class AbstractSqlDriver<C extends AbstractSqlConnection
48
48
  */
49
49
  mapPropToFieldNames<T extends object>(qb: QueryBuilder<T>, prop: EntityProperty<T>, tableAlias?: string): Field<T>[];
50
50
  /** @internal */
51
- createQueryBuilder<T extends object>(entityName: EntityName<T> | QueryBuilder<T>, ctx?: Transaction<Knex.Transaction>, preferredConnectionType?: ConnectionType, convertCustomTypes?: boolean): QueryBuilder<T>;
51
+ createQueryBuilder<T extends object>(entityName: EntityName<T> | QueryBuilder<T>, ctx?: Transaction<Knex.Transaction>, preferredConnectionType?: ConnectionType, convertCustomTypes?: boolean, logging?: LoggingOptions): QueryBuilder<T>;
52
52
  protected resolveConnectionType(args: {
53
53
  ctx?: Transaction<Knex.Transaction>;
54
54
  connectionType?: ConnectionType;
55
55
  }): ConnectionType;
56
56
  protected extractManyToMany<T>(entityName: string, data: EntityDictionary<T>): EntityData<T>;
57
57
  protected processManyToMany<T extends object>(meta: EntityMetadata<T> | undefined, pks: Primary<T>[], collections: EntityData<T>, clear: boolean, options?: DriverMethodOptions): Promise<void>;
58
- protected updateCollectionDiff<T extends object, O extends object>(meta: EntityMetadata<O>, prop: EntityProperty<T>, pks: Primary<O>[], deleteDiff: Primary<T>[][] | boolean, insertDiff: Primary<T>[][], options?: DriverMethodOptions & {
58
+ protected updateCollectionDiff<T extends object, O extends object>(meta: EntityMetadata<O>, prop: EntityProperty<O>, pks: Primary<O>[], deleteDiff: Primary<T>[][] | boolean, insertDiff: Primary<T>[][], options?: DriverMethodOptions & {
59
59
  ownerSchema?: string;
60
60
  }): Promise<void>;
61
- lockPessimistic<T>(entity: T, options: LockOptions): Promise<void>;
61
+ lockPessimistic<T extends object>(entity: T, options: LockOptions): Promise<void>;
62
62
  protected buildJoinedPropsOrderBy<T extends object>(entityName: string, qb: QueryBuilder<T>, meta: EntityMetadata<T>, populate: PopulateOptions<T>[], parentPath?: string): QueryOrderMap<T>[];
63
63
  protected normalizeFields<T extends object>(fields: Field<T>[], prefix?: string): string[];
64
64
  protected processField<T extends object>(meta: EntityMetadata<T>, prop: EntityProperty<T> | undefined, field: string, ret: Field<T>[], populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb: QueryBuilder<T>): void;
@@ -26,19 +26,28 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
26
26
  }
27
27
  const populate = this.autoJoinOneToOneOwner(meta, options.populate, options.fields);
28
28
  const joinedProps = this.joinedProps(meta, populate);
29
- const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false);
29
+ const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging);
30
30
  const fields = this.buildFields(meta, populate, joinedProps, qb, options.fields);
31
31
  const joinedPropsOrderBy = this.buildJoinedPropsOrderBy(entityName, qb, meta, joinedProps);
32
+ const orderBy = [...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy];
32
33
  if (core_1.Utils.isPrimaryKey(where, meta.compositePK)) {
33
34
  where = { [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
34
35
  }
36
+ const { first, last, before, after } = options;
37
+ const isCursorPagination = [first, last, before, after].some(v => v != null);
35
38
  qb.select(fields)
36
39
  .populate(populate, joinedProps.length > 0 ? options.populateWhere : undefined)
37
40
  .where(where)
38
- .orderBy([...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy])
39
41
  .groupBy(options.groupBy)
40
42
  .having(options.having)
41
43
  .withSchema(this.getSchemaName(meta, options));
44
+ if (isCursorPagination) {
45
+ const { orderBy: newOrderBy, where } = this.processCursorOptions(meta, options, orderBy);
46
+ qb.andWhere(where).orderBy(newOrderBy);
47
+ }
48
+ else {
49
+ qb.orderBy(orderBy);
50
+ }
42
51
  if (options.limit !== undefined) {
43
52
  qb.limit(options.limit, options.offset);
44
53
  }
@@ -47,6 +56,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
47
56
  }
48
57
  core_1.Utils.asArray(options.flags).forEach(flag => qb.setFlag(flag));
49
58
  const result = await this.rethrow(qb.execute('all'));
59
+ if (isCursorPagination && !first && !!last) {
60
+ result.reverse();
61
+ }
50
62
  if (joinedProps.length > 0) {
51
63
  return this.mergeJoinedResult(result, meta);
52
64
  }
@@ -61,7 +73,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
61
73
  opts.limit = 1;
62
74
  }
63
75
  if (opts.limit > 0 && !opts.flags?.includes(core_1.QueryFlag.DISABLE_PAGINATE)) {
64
- opts.flags ?? (opts.flags = []);
76
+ opts.flags ??= [];
65
77
  opts.flags.push(core_1.QueryFlag.DISABLE_PAGINATE);
66
78
  }
67
79
  const res = await this.find(entityName, where, opts);
@@ -112,7 +124,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
112
124
  qb.where(where);
113
125
  const kqb = qb.getKnexQuery().clear('select');
114
126
  if (type === query_1.QueryType.COUNT) {
115
- kqb.select(qb.raw('count(*) as count'));
127
+ kqb.select(this.connection.getKnex().raw('count(*) as count'));
116
128
  }
117
129
  else { // select
118
130
  kqb.select('*');
@@ -154,8 +166,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
154
166
  }));
155
167
  if (!hasPK) {
156
168
  // initialize empty collections
157
- if ([core_1.ReferenceType.MANY_TO_MANY, core_1.ReferenceType.ONE_TO_MANY].includes(relation.reference)) {
158
- result[relation.name] = result[relation.name] || [];
169
+ if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
170
+ result[relation.name] ??= [];
159
171
  }
160
172
  return;
161
173
  }
@@ -179,8 +191,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
179
191
  else {
180
192
  map[key] = result;
181
193
  }
182
- if ([core_1.ReferenceType.MANY_TO_MANY, core_1.ReferenceType.ONE_TO_MANY].includes(relation.reference)) {
183
- result[relation.name] = result[relation.name] || [];
194
+ if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
195
+ result[relation.name] ??= [];
184
196
  this.appendToCollection(meta2, result[relation.name], relationPojo);
185
197
  }
186
198
  else {
@@ -215,7 +227,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
215
227
  return this.rethrow(qb.getCount());
216
228
  }
217
229
  async nativeInsert(entityName, data, options = {}) {
218
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
230
+ options.convertCustomTypes ??= true;
219
231
  const meta = this.metadata.find(entityName);
220
232
  const collections = this.extractManyToMany(entityName, data);
221
233
  const pks = meta?.primaryKeys ?? [this.config.getNamingStrategy().referenceColumnName()];
@@ -235,16 +247,20 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
235
247
  return res;
236
248
  }
237
249
  async nativeInsertMany(entityName, data, options = {}) {
238
- options.processCollections ?? (options.processCollections = true);
239
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
250
+ options.processCollections ??= true;
251
+ options.convertCustomTypes ??= true;
240
252
  const meta = this.metadata.find(entityName);
241
253
  const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
242
254
  const pks = this.getPrimaryKeyFields(entityName);
243
255
  const set = new Set();
244
- data.forEach(row => Object.keys(row).forEach(k => set.add(k)));
256
+ data.forEach(row => core_1.Utils.keys(row).forEach(k => set.add(k)));
245
257
  const props = [...set].map(name => meta?.properties[name] ?? { name, fieldNames: [name] });
246
- const fields = core_1.Utils.flatten(props.map(prop => prop.fieldNames));
258
+ let fields = core_1.Utils.flatten(props.map(prop => prop.fieldNames));
259
+ const duplicates = core_1.Utils.findDuplicates(fields);
247
260
  const params = [];
261
+ if (duplicates.length) {
262
+ fields = core_1.Utils.unique(fields);
263
+ }
248
264
  /* istanbul ignore next */
249
265
  const tableName = meta ? this.getTableName(meta, options) : this.platform.quoteIdentifier(entityName);
250
266
  let sql = `insert into ${tableName} `;
@@ -255,29 +271,45 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
255
271
  else {
256
272
  sql += ' ' + data.map(() => `select null as ${this.platform.quoteIdentifier(pks[0])}`).join(' union all ');
257
273
  }
274
+ const addParams = (prop, row) => {
275
+ if (options.convertCustomTypes && prop.customType) {
276
+ params.push(prop.customType.convertToDatabaseValue(row[prop.name], this.platform, { key: prop.name, mode: 'query-data' }));
277
+ return;
278
+ }
279
+ params.push(row[prop.name]);
280
+ };
258
281
  if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
259
282
  sql += data.map(row => {
260
283
  const keys = [];
261
284
  props.forEach(prop => {
262
285
  if (prop.fieldNames.length > 1) {
263
- params.push(...(row[prop.name] ?? prop.fieldNames.map(() => null)));
264
- keys.push(...prop.fieldNames.map(_ => '?'));
286
+ const param = row[prop.name] ?? prop.fieldNames.map(() => null);
287
+ const key = (row[prop.name] ?? prop.fieldNames).map(() => '?');
288
+ prop.fieldNames.forEach((field, idx) => {
289
+ if (duplicates.includes(field)) {
290
+ param.splice(idx, 1);
291
+ key.splice(idx, 1);
292
+ }
293
+ });
294
+ params.push(...param);
295
+ keys.push(...key);
265
296
  }
266
297
  else if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(row[prop.name])) {
267
298
  keys.push(prop.customType.convertToDatabaseValueSQL('?', this.platform));
268
- params.push(row[prop.name]);
299
+ addParams(prop, row);
269
300
  }
270
301
  else {
271
- params.push(row[prop.name]);
302
+ addParams(prop, row);
272
303
  keys.push('?');
273
304
  }
274
305
  });
275
306
  return '(' + (keys.join(', ') || 'default') + ')';
276
307
  }).join(', ');
277
308
  }
278
- if (this.platform.usesReturningStatement()) {
279
- /* istanbul ignore next */
280
- const returningProps = meta.hydrateProps.filter(prop => prop.persist !== false && (prop.primary || prop.defaultRaw));
309
+ if (meta && this.platform.usesReturningStatement()) {
310
+ const returningProps = meta.props
311
+ .filter(prop => prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw))
312
+ .filter(prop => !(prop.name in data[0]) || core_1.Utils.isRawSql(data[0][prop.name]));
281
313
  const returningFields = core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames));
282
314
  /* istanbul ignore next */
283
315
  sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
@@ -289,8 +321,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
289
321
  pk = data.map(d => core_1.Utils.getPrimaryKeyCond(d, pks));
290
322
  }
291
323
  else {
292
- res.row ?? (res.row = {});
293
- res.rows ?? (res.rows = []);
324
+ res.row ??= {};
325
+ res.rows ??= [];
294
326
  pk = data.map((d, i) => d[pks[0]] ?? res.rows[i]?.[pks[0]]).map(d => [d]);
295
327
  res.insertId = res.insertId || res.row[pks[0]];
296
328
  }
@@ -300,7 +332,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
300
332
  return res;
301
333
  }
302
334
  async nativeUpdate(entityName, where, data, options = {}) {
303
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
335
+ options.convertCustomTypes ??= true;
304
336
  const meta = this.metadata.find(entityName);
305
337
  const pks = this.getPrimaryKeyFields(entityName);
306
338
  const collections = this.extractManyToMany(entityName, data);
@@ -314,11 +346,12 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
314
346
  .withSchema(this.getSchemaName(meta, options));
315
347
  if (options.upsert) {
316
348
  /* istanbul ignore next */
317
- const uniqueFields = core_1.Utils.isPlainObject(where) ? Object.keys(where) : meta.primaryKeys;
349
+ const uniqueFields = core_1.Utils.isPlainObject(where) ? core_1.Utils.keys(where) : meta.primaryKeys;
318
350
  /* istanbul ignore next */
319
351
  qb.insert(data)
320
352
  .onConflict(uniqueFields.map(p => meta?.properties[p]?.fieldNames[0] ?? p))
321
- .merge(Object.keys(data).filter(f => !uniqueFields.includes(f)));
353
+ .merge(core_1.Utils.keys(data).filter(f => !uniqueFields.includes(f)))
354
+ .returning(meta?.comparableProps.filter(p => !p.lazy && !(p.name in data)).map(p => p.name) ?? '*');
322
355
  }
323
356
  else {
324
357
  qb.update(data).where(where);
@@ -331,12 +364,29 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
331
364
  return res;
332
365
  }
333
366
  async nativeUpdateMany(entityName, where, data, options = {}) {
334
- options.processCollections ?? (options.processCollections = true);
335
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
367
+ options.processCollections ??= true;
368
+ options.convertCustomTypes ??= true;
336
369
  const meta = this.metadata.get(entityName);
370
+ if (options.upsert) {
371
+ const uniqueFields = core_1.Utils.isPlainObject(where[0]) ? Object.keys(where[0]) : meta.primaryKeys;
372
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
373
+ qb.insert(data)
374
+ .onConflict(uniqueFields.map(p => meta.properties[p]?.fieldNames[0] ?? p))
375
+ .merge(Object.keys(data[0]).filter(f => !uniqueFields.includes(f)))
376
+ .returning(meta.comparableProps.filter(p => !p.lazy && !(p.name in data[0])).map(p => p.name) ?? '*');
377
+ return qb.execute('run', false);
378
+ }
337
379
  const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
338
380
  const keys = new Set();
339
- data.forEach(row => Object.keys(row).forEach(k => keys.add(k)));
381
+ const returning = new Set();
382
+ data.forEach(row => {
383
+ core_1.Utils.keys(row).forEach(k => {
384
+ keys.add(k);
385
+ if (core_1.Utils.isRawSql(row[k])) {
386
+ returning.add(k);
387
+ }
388
+ });
389
+ });
340
390
  const pkCond = core_1.Utils.flatten(meta.primaryKeys.map(pk => meta.properties[pk].fieldNames)).map(pk => `${this.platform.quoteIdentifier(pk)} = ?`).join(' and ');
341
391
  const params = [];
342
392
  let sql = `update ${this.getTableName(meta, options)} set `;
@@ -396,6 +446,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
396
446
  return '?';
397
447
  });
398
448
  sql += ` in (${conds.join(', ')})`;
449
+ if (this.platform.usesReturningStatement() && returning.size > 0) {
450
+ const returningFields = core_1.Utils.flatten([...returning].map(prop => meta.properties[prop].fieldNames));
451
+ /* istanbul ignore next */
452
+ sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
453
+ }
399
454
  const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx));
400
455
  for (let i = 0; i < collections.length; i++) {
401
456
  await this.processManyToMany(meta, where[i], collections[i], false, options);
@@ -430,7 +485,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
430
485
  deleteDiff.push(...snapshot);
431
486
  insertDiff.push(...current);
432
487
  }
433
- if (coll.property.reference === core_1.ReferenceType.ONE_TO_MANY) {
488
+ if (coll.property.kind === core_1.ReferenceKind.ONE_TO_MANY) {
434
489
  const cols = coll.property.referencedColumnNames;
435
490
  const qb = this.createQueryBuilder(coll.property.type, ctx, 'write')
436
491
  .withSchema(this.getSchemaName(meta, options))
@@ -444,7 +499,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
444
499
  const pivotMeta = this.metadata.find(coll.property.pivotEntity);
445
500
  if (pivotMeta.schema === '*') {
446
501
  /* istanbul ignore next */
447
- options ?? (options = {});
502
+ options ??= {};
448
503
  options.schema = ownerSchema;
449
504
  }
450
505
  return this.rethrow(this.updateCollectionDiff(meta, coll.property, pks, deleteDiff, insertDiff, options));
@@ -512,7 +567,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
512
567
  }
513
568
  const relationsToPopulate = populate.map(({ field }) => field);
514
569
  const toPopulate = meta.relations
515
- .filter(prop => prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
570
+ .filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
516
571
  .filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
517
572
  .map(prop => ({ field: prop.name, strategy: prop.strategy }));
518
573
  return [...populate, ...toPopulate];
@@ -520,7 +575,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
520
575
  joinedProps(meta, populate) {
521
576
  return populate.filter(p => {
522
577
  const prop = meta.properties[p.field] || {};
523
- return (p.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && prop.reference !== core_1.ReferenceType.SCALAR;
578
+ return (p.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && prop.kind !== core_1.ReferenceKind.SCALAR;
524
579
  });
525
580
  }
526
581
  /**
@@ -545,14 +600,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
545
600
  getFieldsForJoinedLoad(qb, meta, explicitFields, populate = [], parentTableAlias, parentJoinPath) {
546
601
  const fields = [];
547
602
  const joinedProps = this.joinedProps(meta, populate);
603
+ if (explicitFields?.includes('*')) {
604
+ fields.push('*');
605
+ }
548
606
  const shouldHaveColumn = (prop, populate, fields) => {
549
607
  if (!this.platform.shouldHaveColumn(prop, populate)) {
550
608
  return false;
551
609
  }
552
610
  if (!fields || prop.primary) {
553
- return true;
611
+ return !fields?.includes('*');
554
612
  }
555
- return fields.includes(prop.name);
613
+ return fields.some(f => f === prop.name || f.toString().startsWith(prop.name + '.'));
556
614
  };
557
615
  // alias all fields in the primary table
558
616
  meta.props
@@ -582,11 +640,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
582
640
  const aliased = qb.ref(tableAlias ? `${tableAlias}__${prop.fieldNames[0]}` : prop.fieldNames[0]).toString();
583
641
  if (prop.customType?.convertToJSValueSQL && tableAlias) {
584
642
  const prefixed = qb.ref(prop.fieldNames[0]).withSchema(tableAlias).toString();
585
- return [qb.raw(prop.customType.convertToJSValueSQL(prefixed, this.platform) + ' as ' + aliased).toString()];
643
+ return [(0, core_1.raw)(`${prop.customType.convertToJSValueSQL(prefixed, this.platform)} as ${aliased}`)];
586
644
  }
587
645
  if (prop.formula) {
588
646
  const alias = qb.ref(tableAlias ?? qb.alias).toString();
589
- return [`${prop.formula(alias)} as ${aliased}`];
647
+ return [(0, core_1.raw)(`${prop.formula(alias)} as ${aliased}`)];
590
648
  }
591
649
  if (tableAlias) {
592
650
  return prop.fieldNames.map(fieldName => qb.ref(fieldName).withSchema(tableAlias).as(`${tableAlias}__${fieldName}`));
@@ -594,9 +652,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
594
652
  return prop.fieldNames;
595
653
  }
596
654
  /** @internal */
597
- createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes) {
655
+ createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes, logging) {
598
656
  const connectionType = this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
599
- const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType);
657
+ const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType, undefined, logging);
600
658
  if (!convertCustomTypes) {
601
659
  qb.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
602
660
  }
@@ -620,7 +678,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
620
678
  }
621
679
  const ret = {};
622
680
  this.metadata.find(entityName).relations.forEach(prop => {
623
- if (prop.reference === core_1.ReferenceType.MANY_TO_MANY && data[prop.name]) {
681
+ if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && data[prop.name]) {
624
682
  ret[prop.name] = data[prop.name].map((item) => core_1.Utils.asArray(item));
625
683
  delete data[prop.name];
626
684
  }
@@ -663,7 +721,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
663
721
  });
664
722
  /* istanbul ignore else */
665
723
  if (this.platform.allowsMultiInsert()) {
666
- await this.nativeInsertMany(prop.pivotEntity, items, { ...options, convertCustomTypes: false, processCollections: false });
724
+ await this.nativeInsertMany(prop.pivotEntity, items, {
725
+ ...options,
726
+ convertCustomTypes: false,
727
+ processCollections: false,
728
+ });
667
729
  }
668
730
  else {
669
731
  await core_1.Utils.runSerial(items, item => {
@@ -679,7 +741,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
679
741
  const meta = (0, core_1.helper)(entity).__meta;
680
742
  const qb = this.createQueryBuilder(entity.constructor.name, options.ctx).unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES).withSchema(options.schema ?? meta.schema);
681
743
  const cond = core_1.Utils.getPrimaryKeyCond(entity, meta.primaryKeys);
682
- qb.select('1').where(cond).setLockMode(options.lockMode, options.lockTableAliases);
744
+ qb.select((0, core_1.raw)('1')).where(cond).setLockMode(options.lockMode, options.lockTableAliases);
683
745
  await this.rethrow(qb.execute());
684
746
  }
685
747
  buildJoinedPropsOrderBy(entityName, qb, meta, populate, parentPath) {
@@ -691,7 +753,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
691
753
  const path = `${parentPath ? parentPath : entityName}.${relation.field}`;
692
754
  const propAlias = qb.getAliasForJoinPath(path);
693
755
  if (propOrderBy) {
694
- Object.keys(propOrderBy).forEach(field => {
756
+ core_1.Utils.keys(propOrderBy).forEach(field => {
695
757
  orderBy.push({ [`${propAlias}.${field}`]: propOrderBy[field] });
696
758
  });
697
759
  }
@@ -718,10 +780,10 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
718
780
  return ret;
719
781
  }
720
782
  processField(meta, prop, field, ret, populate, joinedProps, qb) {
721
- if (!prop || (prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner)) {
783
+ if (!prop || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner)) {
722
784
  return;
723
785
  }
724
- if (prop.reference === core_1.ReferenceType.EMBEDDED) {
786
+ if (prop.kind === core_1.ReferenceKind.EMBEDDED) {
725
787
  if (prop.object) {
726
788
  ret.push(prop.fieldNames[0]);
727
789
  return;
@@ -735,8 +797,12 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
735
797
  }
736
798
  return;
737
799
  }
800
+ if (prop.formula) {
801
+ ret.push(prop.name);
802
+ return;
803
+ }
738
804
  if (prop.fieldNames) {
739
- ret.push(prop.fieldNames[0]);
805
+ ret.push(...prop.fieldNames);
740
806
  }
741
807
  }
742
808
  buildFields(meta, populate, joinedProps, qb, fields) {
@@ -745,6 +811,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
745
811
  const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL);
746
812
  const hasExplicitFields = !!fields;
747
813
  const ret = [];
814
+ let addFormulas = false;
748
815
  if (joinedProps.length > 0) {
749
816
  ret.push(...this.getFieldsForJoinedLoad(qb, meta, fields, populate));
750
817
  }
@@ -770,20 +837,22 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
770
837
  else if (lazyProps.filter(p => !p.formula).length > 0) {
771
838
  const props = meta.props.filter(prop => this.platform.shouldHaveColumn(prop, populate, false));
772
839
  ret.push(...core_1.Utils.flatten(props.filter(p => !lazyProps.includes(p)).map(p => p.fieldNames)));
840
+ addFormulas = true;
773
841
  }
774
842
  else if (hasLazyFormulas || requiresSQLConversion) {
775
843
  ret.push('*');
844
+ addFormulas = true;
776
845
  }
777
- if (ret.length > 0 && !hasExplicitFields) {
846
+ if (ret.length > 0 && !hasExplicitFields && addFormulas) {
778
847
  meta.props
779
848
  .filter(prop => prop.formula && !lazyProps.includes(prop))
780
849
  .forEach(prop => {
781
850
  const alias = qb.ref(qb.alias).toString();
782
851
  const aliased = qb.ref(prop.fieldNames[0]).toString();
783
- ret.push(`${prop.formula(alias)} as ${aliased}`);
852
+ ret.push((0, core_1.raw)(`${prop.formula(alias)} as ${aliased}`));
784
853
  });
785
854
  meta.props
786
- .filter(prop => prop.customType?.convertToDatabaseValueSQL || prop.customType?.convertToJSValueSQL)
855
+ .filter(prop => prop.hasConvertToDatabaseValueSQL || prop.hasConvertToJSValueSQL)
787
856
  .forEach(prop => ret.push(prop.name));
788
857
  }
789
858
  return ret.length > 0 ? core_1.Utils.unique(ret) : ['*'];
@@ -14,6 +14,7 @@ export declare abstract class AbstractSqlPlatform extends Platform {
14
14
  quoteValue(value: any): string;
15
15
  formatQuery(sql: string, params: readonly any[]): string;
16
16
  getSearchJsonPropertySQL(path: string, type: string, aliased: boolean): string;
17
+ getSearchJsonPropertyKey(path: string[], type: string, aliased: boolean): string;
17
18
  isRaw(value: any): boolean;
18
19
  supportsSchemas(): boolean;
19
20
  /** @inheritDoc */