@mikro-orm/knex 7.0.0-dev.34 → 7.0.0-dev.36

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.
@@ -42,7 +42,9 @@ export declare abstract class AbstractSqlConnection extends Connection {
42
42
  }): Promise<ControlledTransaction<any, any>>;
43
43
  commit(ctx: ControlledTransaction<any, any>, eventBroadcaster?: TransactionEventBroadcaster, loggerContext?: LogContext): Promise<void>;
44
44
  rollback(ctx: ControlledTransaction<any, any>, eventBroadcaster?: TransactionEventBroadcaster, loggerContext?: LogContext): Promise<void>;
45
+ private prepareQuery;
45
46
  execute<T extends QueryResult | EntityData<AnyEntity> | EntityData<AnyEntity>[] = EntityData<AnyEntity>[]>(query: string | NativeQueryBuilder | RawQueryFragment, params?: readonly unknown[], method?: 'all' | 'get' | 'run', ctx?: Transaction, loggerContext?: LoggingOptions): Promise<T>;
47
+ stream<T extends EntityData<AnyEntity>>(query: string | NativeQueryBuilder | RawQueryFragment, params?: readonly unknown[], ctx?: Transaction<Kysely<any>>, loggerContext?: LoggingOptions): AsyncIterableIterator<T>;
46
48
  /**
47
49
  * Execute raw SQL queries from file
48
50
  */
@@ -1,6 +1,6 @@
1
- import { CompiledQuery, Kysely, } from 'kysely';
1
+ import { CompiledQuery, Kysely } from 'kysely';
2
2
  import { readFile } from 'node:fs/promises';
3
- import { Connection, EventType, RawQueryFragment, } from '@mikro-orm/core';
3
+ import { Connection, EventType, RawQueryFragment, Utils, } from '@mikro-orm/core';
4
4
  import { NativeQueryBuilder } from './query/NativeQueryBuilder.js';
5
5
  export class AbstractSqlConnection extends Connection {
6
6
  client;
@@ -135,8 +135,7 @@ export class AbstractSqlConnection extends Connection {
135
135
  }
136
136
  await eventBroadcaster?.dispatchEvent(EventType.afterTransactionRollback, ctx);
137
137
  }
138
- async execute(query, params = [], method = 'all', ctx, loggerContext) {
139
- await this.ensureConnection();
138
+ prepareQuery(query, params = []) {
140
139
  if (query instanceof NativeQueryBuilder) {
141
140
  query = query.toRaw();
142
141
  }
@@ -146,16 +145,47 @@ export class AbstractSqlConnection extends Connection {
146
145
  }
147
146
  query = this.config.get('onQuery')(query, params);
148
147
  const formatted = this.platform.formatQuery(query, params);
149
- const sql = this.getSql(query, formatted, loggerContext);
148
+ return { query, params, formatted };
149
+ }
150
+ async execute(query, params = [], method = 'all', ctx, loggerContext) {
151
+ await this.ensureConnection();
152
+ const q = this.prepareQuery(query, params);
153
+ const sql = this.getSql(q.query, q.formatted, loggerContext);
150
154
  return this.executeQuery(sql, async () => {
151
- const compiled = CompiledQuery.raw(formatted);
152
- if (ctx) {
153
- const res = await ctx.executeQuery(compiled);
154
- return this.transformRawResult(res, method);
155
- }
156
- const res = await this.client.executeQuery(compiled);
155
+ const compiled = CompiledQuery.raw(q.formatted);
156
+ const res = await (ctx ?? this.client).executeQuery(compiled);
157
157
  return this.transformRawResult(res, method);
158
- }, { query, params, ...loggerContext });
158
+ }, { ...q, ...loggerContext });
159
+ }
160
+ async *stream(query, params = [], ctx, loggerContext) {
161
+ await this.ensureConnection();
162
+ const q = this.prepareQuery(query, params);
163
+ const sql = this.getSql(q.query, q.formatted, loggerContext);
164
+ // construct the compiled query manually with `kind: 'SelectQueryNode'` to avoid sqlite validation for select queries when streaming
165
+ const compiled = {
166
+ query: {
167
+ kind: 'SelectQueryNode',
168
+ },
169
+ sql: q.formatted,
170
+ parameters: [],
171
+ };
172
+ try {
173
+ const res = (ctx ?? this.client).getExecutor().stream(compiled, 1);
174
+ this.logQuery(sql, {
175
+ sql, params,
176
+ ...loggerContext,
177
+ affected: Utils.isPlainObject(res) ? res.affectedRows : undefined,
178
+ });
179
+ for await (const items of res) {
180
+ for (const row of this.transformRawResult(items, 'all')) {
181
+ yield row;
182
+ }
183
+ }
184
+ }
185
+ catch (e) {
186
+ this.logQuery(sql, { sql, params, ...loggerContext, level: 'error' });
187
+ throw e;
188
+ }
159
189
  }
160
190
  /**
161
191
  * Execute raw SQL queries from file
@@ -1,4 +1,4 @@
1
- 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 LockOptions, type LoggingOptions, type NativeInsertUpdateManyOptions, type NativeInsertUpdateOptions, type ObjectQuery, type Options, type OrderDefinition, type PopulateOptions, type PopulatePath, type Primary, type QueryOrderMap, type QueryResult, RawQueryFragment, type Transaction, type UpsertManyOptions, type UpsertOptions } from '@mikro-orm/core';
1
+ 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 LockOptions, type LoggingOptions, type NativeInsertUpdateManyOptions, type NativeInsertUpdateOptions, type ObjectQuery, type Options, type OrderDefinition, type PopulateOptions, type PopulatePath, type Primary, type QueryOrderMap, type QueryResult, RawQueryFragment, type StreamOptions, type Transaction, type UpsertManyOptions, type UpsertOptions } from '@mikro-orm/core';
2
2
  import type { AbstractSqlConnection } from './AbstractSqlConnection.js';
3
3
  import type { AbstractSqlPlatform } from './AbstractSqlPlatform.js';
4
4
  import { QueryBuilder } from './query/QueryBuilder.js';
@@ -14,13 +14,16 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
14
14
  protected constructor(config: Configuration, platform: Platform, connection: Constructor<Connection>, connector: string[]);
15
15
  getPlatform(): Platform;
16
16
  createEntityManager(useContext?: boolean): this[typeof EntityManagerType];
17
+ private createQueryBuilderFromOptions;
17
18
  find<T extends object, P extends string = never, F extends string = PopulatePath.ALL, E extends string = never>(entityName: string, where: ObjectQuery<T>, options?: FindOptions<T, P, F, E>): Promise<EntityData<T>[]>;
18
19
  findOne<T extends object, P extends string = never, F extends string = PopulatePath.ALL, E extends string = never>(entityName: string, where: ObjectQuery<T>, options?: FindOneOptions<T, P, F, E>): Promise<EntityData<T> | null>;
19
20
  protected hasToManyJoins<T extends object>(hint: PopulateOptions<T>, meta: EntityMetadata<T>): boolean;
20
21
  findVirtual<T extends object>(entityName: string, where: ObjectQuery<T>, options: FindOptions<T, any, any, any>): Promise<EntityData<T>[]>;
21
22
  countVirtual<T extends object>(entityName: string, where: ObjectQuery<T>, options: CountOptions<T, any>): Promise<number>;
22
23
  protected findFromVirtual<T extends object>(entityName: string, where: ObjectQuery<T>, options: FindOptions<T, any> | CountOptions<T, any>, type: QueryType): Promise<EntityData<T>[] | number>;
24
+ protected streamFromVirtual<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, options: StreamOptions<T, any>): AsyncIterableIterator<EntityData<T>>;
23
25
  protected wrapVirtualExpressionInSubquery<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any>, type: QueryType): Promise<T[] | number>;
26
+ protected wrapVirtualExpressionInSubqueryStream<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any, any, any>, type: QueryType.SELECT): AsyncIterableIterator<T>;
24
27
  mapResult<T extends object>(result: EntityData<T>, meta: EntityMetadata<T>, populate?: PopulateOptions<T>[], qb?: QueryBuilder<T, any, any, any>, map?: Dictionary): EntityData<T> | null;
25
28
  private mapJoinedProps;
26
29
  count<T extends object>(entityName: string, where: any, options?: CountOptions<T>): Promise<number>;
@@ -39,6 +42,7 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
39
42
  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, any>, pivotJoin?: boolean): Promise<Dictionary<T[]>>;
40
43
  private getPivotOrderBy;
41
44
  execute<T extends QueryResult | EntityData<AnyEntity> | EntityData<AnyEntity>[] = EntityData<AnyEntity>[]>(query: string | NativeQueryBuilder | RawQueryFragment, params?: any[], method?: 'all' | 'get' | 'run', ctx?: Transaction, loggerContext?: LoggingOptions): Promise<T>;
45
+ stream<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, options: StreamOptions<T, any, any, any>): AsyncIterableIterator<T>;
42
46
  /**
43
47
  * 1:1 owner side needs to be marked for population so QB auto-joins the owner id
44
48
  */
@@ -53,11 +57,12 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
53
57
  * @internal
54
58
  */
55
59
  mergeJoinedResult<T extends object>(rawResults: EntityData<T>[], meta: EntityMetadata<T>, joinedProps: PopulateOptions<T>[]): EntityData<T>[];
60
+ protected shouldHaveColumn<T, U>(meta: EntityMetadata<T>, prop: EntityProperty<U>, populate: readonly PopulateOptions<U>[], fields?: readonly Field<U>[], exclude?: readonly Field<U>[]): boolean;
56
61
  protected getFieldsForJoinedLoad<T extends object>(qb: QueryBuilder<T, any, any, any>, meta: EntityMetadata<T>, options: FieldsForJoinedLoadOptions<T>): Field<T>[];
57
62
  /**
58
63
  * @internal
59
64
  */
60
- mapPropToFieldNames<T extends object>(qb: QueryBuilder<T, any, any, any>, prop: EntityProperty<T>, tableAlias: string): Field<T>[];
65
+ mapPropToFieldNames<T extends object>(qb: QueryBuilder<T, any, any, any>, prop: EntityProperty<T>, tableAlias: string, explicitFields?: readonly Field<T>[]): Field<T>[];
61
66
  /** @internal */
62
67
  createQueryBuilder<T extends object>(entityName: EntityName<T> | QueryBuilder<T, any, any, any>, ctx?: Transaction, preferredConnectionType?: ConnectionType, convertCustomTypes?: boolean, loggerContext?: LoggingOptions, alias?: string, em?: SqlEntityManager): QueryBuilder<T, any, any, any>;
63
68
  protected resolveConnectionType(args: {
@@ -1,4 +1,4 @@
1
- import { ALIAS_REPLACEMENT_RE, DatabaseDriver, EntityManagerType, getOnConflictFields, getOnConflictReturningFields, helper, isRaw, LoadStrategy, parseJsonSafe, QueryFlag, QueryHelper, QueryOrder, raw, RawQueryFragment, ReferenceKind, Utils, getLoadingStrategy, } from '@mikro-orm/core';
1
+ import { ALIAS_REPLACEMENT_RE, DatabaseDriver, EntityManagerType, getLoadingStrategy, getOnConflictFields, getOnConflictReturningFields, helper, isRaw, LoadStrategy, parseJsonSafe, QueryFlag, QueryHelper, QueryOrder, raw, RawQueryFragment, ReferenceKind, Utils, } from '@mikro-orm/core';
2
2
  import { QueryBuilder } from './query/QueryBuilder.js';
3
3
  import { JoinType, QueryType } from './query/enums.js';
4
4
  import { SqlEntityManager } from './SqlEntityManager.js';
@@ -21,15 +21,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
21
21
  const EntityManagerClass = this.config.get('entityManager', SqlEntityManager);
22
22
  return new EntityManagerClass(this.config, this, this.metadata, useContext);
23
23
  }
24
- async find(entityName, where, options = {}) {
25
- options = { populate: [], orderBy: [], ...options };
26
- const meta = this.metadata.find(entityName);
27
- if (meta?.virtual) {
28
- return this.findVirtual(entityName, where, options);
29
- }
24
+ async createQueryBuilderFromOptions(meta, where, options = {}) {
25
+ const connectionType = this.resolveConnectionType({ ctx: options.ctx, connectionType: options.connectionType });
30
26
  const populate = this.autoJoinOneToOneOwner(meta, options.populate, options.fields);
31
27
  const joinedProps = this.joinedProps(meta, populate, options);
32
- const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging);
28
+ const qb = this.createQueryBuilder(meta.className, options.ctx, connectionType, false, options.logging, undefined, options.em);
33
29
  const fields = this.buildFields(meta, populate, joinedProps, qb, qb.alias, options);
34
30
  const orderBy = this.buildOrderBy(qb, meta, populate, options);
35
31
  const populateWhere = this.buildPopulateWhere(meta, joinedProps, options);
@@ -66,8 +62,17 @@ export class AbstractSqlDriver extends DatabaseDriver {
66
62
  if (options.em) {
67
63
  await qb.applyJoinedFilters(options.em, options.filters);
68
64
  }
65
+ return qb;
66
+ }
67
+ async find(entityName, where, options = {}) {
68
+ options = { populate: [], orderBy: [], ...options };
69
+ const meta = this.metadata.find(entityName);
70
+ if (meta?.virtual) {
71
+ return this.findVirtual(entityName, where, options);
72
+ }
73
+ const qb = await this.createQueryBuilderFromOptions(meta, where, options);
69
74
  const result = await this.rethrow(qb.execute('all'));
70
- if (isCursorPagination && !first && !!last) {
75
+ if (options.last && !options.first) {
71
76
  result.reverse();
72
77
  }
73
78
  return result;
@@ -127,35 +132,49 @@ export class AbstractSqlDriver extends DatabaseDriver {
127
132
  const expr = this.platform.formatQuery(res.sql, res.params);
128
133
  return this.wrapVirtualExpressionInSubquery(meta, expr, where, options, type);
129
134
  }
130
- /* v8 ignore next */
135
+ /* v8 ignore next 2 */
131
136
  return res;
132
137
  }
133
- async wrapVirtualExpressionInSubquery(meta, expression, where, options, type) {
134
- const qb = this.createQueryBuilder(meta.className, options?.ctx, options.connectionType, options.convertCustomTypes, options.logging)
135
- .indexHint(options.indexHint)
136
- .comment(options.comments)
137
- .hintComment(options.hintComments);
138
- qb.where(where);
139
- const { first, last, before, after } = options;
140
- const isCursorPagination = [first, last, before, after].some(v => v != null);
141
- if (type !== QueryType.COUNT) {
142
- if (options.orderBy) {
143
- if (isCursorPagination) {
144
- const { orderBy: newOrderBy, where } = this.processCursorOptions(meta, options, options.orderBy);
145
- qb.andWhere(where).orderBy(newOrderBy);
146
- }
147
- else {
148
- qb.orderBy(options.orderBy);
149
- }
150
- }
151
- qb.limit(options?.limit, options?.offset);
138
+ async *streamFromVirtual(entityName, where, options) {
139
+ const meta = this.metadata.get(entityName);
140
+ /* v8 ignore next 3 */
141
+ if (!meta.expression) {
142
+ return;
152
143
  }
153
- const native = qb.getNativeQuery(false).clear('select');
154
- if (type === QueryType.COUNT) {
155
- native.count();
144
+ if (typeof meta.expression === 'string') {
145
+ yield* this.wrapVirtualExpressionInSubqueryStream(meta, meta.expression, where, options, QueryType.SELECT);
146
+ return;
147
+ }
148
+ const em = this.createEntityManager();
149
+ em.setTransactionContext(options.ctx);
150
+ const res = meta.expression(em, where, options, true);
151
+ if (typeof res === 'string') {
152
+ yield* this.wrapVirtualExpressionInSubqueryStream(meta, res, where, options, QueryType.SELECT);
153
+ return;
156
154
  }
157
- else { // select
158
- native.select('*');
155
+ if (res instanceof QueryBuilder) {
156
+ yield* this.wrapVirtualExpressionInSubqueryStream(meta, res.getFormattedQuery(), where, options, QueryType.SELECT);
157
+ return;
158
+ }
159
+ if (res instanceof RawQueryFragment) {
160
+ const expr = this.platform.formatQuery(res.sql, res.params);
161
+ yield* this.wrapVirtualExpressionInSubqueryStream(meta, expr, where, options, QueryType.SELECT);
162
+ return;
163
+ }
164
+ /* v8 ignore next 2 */
165
+ yield* res;
166
+ }
167
+ async wrapVirtualExpressionInSubquery(meta, expression, where, options, type) {
168
+ const qb = await this.createQueryBuilderFromOptions(meta, where, options);
169
+ qb.setFlag(QueryFlag.DISABLE_PAGINATE);
170
+ const isCursorPagination = [options.first, options.last, options.before, options.after].some(v => v != null);
171
+ const native = qb.getNativeQuery(false);
172
+ if (type === QueryType.COUNT) {
173
+ native
174
+ .clear('select')
175
+ .clear('limit')
176
+ .clear('offset')
177
+ .count();
159
178
  }
160
179
  native.from(raw(`(${expression}) as ${this.platform.quoteIdentifier(qb.alias)}`));
161
180
  const query = native.compile();
@@ -163,11 +182,23 @@ export class AbstractSqlDriver extends DatabaseDriver {
163
182
  if (type === QueryType.COUNT) {
164
183
  return res[0].count;
165
184
  }
166
- if (isCursorPagination && !first && !!last) {
185
+ if (isCursorPagination && !options.first && !!options.last) {
167
186
  res.reverse();
168
187
  }
169
188
  return res.map(row => this.mapResult(row, meta));
170
189
  }
190
+ async *wrapVirtualExpressionInSubqueryStream(meta, expression, where, options, type) {
191
+ const qb = await this.createQueryBuilderFromOptions(meta, where, options);
192
+ qb.unsetFlag(QueryFlag.DISABLE_PAGINATE);
193
+ const native = qb.getNativeQuery(false);
194
+ native.from(raw(`(${expression}) as ${this.platform.quoteIdentifier(qb.alias)}`));
195
+ const query = native.compile();
196
+ const connectionType = this.resolveConnectionType({ ctx: options.ctx, connectionType: options.connectionType });
197
+ const res = this.getConnection(connectionType).stream(query.sql, query.params, options.ctx, options.loggerContext);
198
+ for await (const row of res) {
199
+ yield this.mapResult(row, meta);
200
+ }
201
+ }
171
202
  mapResult(result, meta, populate = [], qb, map = {}) {
172
203
  const ret = super.mapResult(result, meta);
173
204
  /* v8 ignore next 3 */
@@ -221,9 +252,13 @@ export class AbstractSqlDriver extends DatabaseDriver {
221
252
  }
222
253
  return;
223
254
  }
255
+ const mapToPk = !!(ref || prop.mapToPk);
256
+ const targetProps = mapToPk
257
+ ? meta2.getPrimaryProps()
258
+ : meta2.props.filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []));
224
259
  // If the primary key value for the relation is null, we know we haven't joined to anything
225
260
  // and therefore we don't return any record (since all values would be null)
226
- const hasPK = meta2.primaryKeys.every(pk => meta2.properties[pk].fieldNames.every(name => {
261
+ const hasPK = meta2.getPrimaryProps().every(pk => pk.fieldNames.every(name => {
227
262
  return root[`${relationAlias}__${name}`] != null;
228
263
  }));
229
264
  if (!hasPK) {
@@ -233,6 +268,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
233
268
  if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
234
269
  result[prop.name] = null;
235
270
  }
271
+ for (const prop of targetProps) {
272
+ for (const name of prop.fieldNames) {
273
+ delete root[`${relationAlias}__${name}`];
274
+ }
275
+ }
236
276
  return;
237
277
  }
238
278
  let relationPojo = {};
@@ -248,10 +288,6 @@ export class AbstractSqlDriver extends DatabaseDriver {
248
288
  relationPojo[prop.name] = root[alias];
249
289
  }
250
290
  });
251
- const mapToPk = !!(ref || prop.mapToPk);
252
- const targetProps = mapToPk
253
- ? meta2.getPrimaryProps()
254
- : meta2.props.filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []));
255
291
  const tz = this.platform.getTimezone();
256
292
  for (const prop of targetProps) {
257
293
  if (prop.fieldNames.every(name => typeof root[`${relationAlias}__${name}`] === 'undefined')) {
@@ -292,7 +328,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
292
328
  // properties can be mapped to multiple places, e.g. when sharing a column in multiple FKs,
293
329
  // so we need to delete them after everything is mapped from given level
294
330
  for (const prop of targetProps) {
295
- prop.fieldNames.map(name => delete root[`${relationAlias}__${name}`]);
331
+ for (const name of prop.fieldNames) {
332
+ delete root[`${relationAlias}__${name}`];
333
+ }
296
334
  }
297
335
  if (mapToPk) {
298
336
  const tmp = Object.values(relationPojo);
@@ -825,6 +863,24 @@ export class AbstractSqlDriver extends DatabaseDriver {
825
863
  async execute(query, params = [], method = 'all', ctx, loggerContext) {
826
864
  return this.rethrow(this.connection.execute(query, params, method, ctx, loggerContext));
827
865
  }
866
+ async *stream(entityName, where, options) {
867
+ options = { populate: [], orderBy: [], ...options };
868
+ const meta = this.metadata.find(entityName);
869
+ if (meta?.virtual) {
870
+ yield* this.streamFromVirtual(entityName, where, options);
871
+ return;
872
+ }
873
+ const qb = await this.createQueryBuilderFromOptions(meta, where, options);
874
+ try {
875
+ const result = qb.stream(options);
876
+ for await (const item of result) {
877
+ yield item;
878
+ }
879
+ }
880
+ catch (e) {
881
+ throw this.convertException(e);
882
+ }
883
+ }
828
884
  /**
829
885
  * 1:1 owner side needs to be marked for population so QB auto-joins the owner id
830
886
  */
@@ -919,26 +975,26 @@ export class AbstractSqlDriver extends DatabaseDriver {
919
975
  }
920
976
  return res;
921
977
  }
978
+ shouldHaveColumn(meta, prop, populate, fields, exclude) {
979
+ if (!this.platform.shouldHaveColumn(prop, populate, exclude)) {
980
+ return false;
981
+ }
982
+ if (!fields || fields.includes('*') || prop.primary || meta.root.discriminatorColumn === prop.name) {
983
+ return true;
984
+ }
985
+ return fields.some(f => f === prop.name || f.toString().startsWith(prop.name + '.'));
986
+ }
922
987
  getFieldsForJoinedLoad(qb, meta, options) {
923
988
  const fields = [];
924
989
  const populate = options.populate ?? [];
925
990
  const joinedProps = this.joinedProps(meta, populate, options);
926
- const shouldHaveColumn = (meta, prop, populate, fields) => {
927
- if (!this.platform.shouldHaveColumn(prop, populate, options.exclude)) {
928
- return false;
929
- }
930
- if (!fields || fields.includes('*') || prop.primary || meta.root.discriminatorColumn === prop.name) {
931
- return true;
932
- }
933
- return fields.some(f => f === prop.name || f.toString().startsWith(prop.name + '.'));
934
- };
935
991
  const populateWhereAll = options?._populateWhere === 'all' || Utils.isEmpty(options?._populateWhere);
936
992
  // root entity is already handled, skip that
937
993
  if (options.parentJoinPath) {
938
994
  // alias all fields in the primary table
939
995
  meta.props
940
- .filter(prop => shouldHaveColumn(meta, prop, populate, options.explicitFields))
941
- .forEach(prop => fields.push(...this.mapPropToFieldNames(qb, prop, options.parentTableAlias)));
996
+ .filter(prop => this.shouldHaveColumn(meta, prop, populate, options.explicitFields, options.exclude))
997
+ .forEach(prop => fields.push(...this.mapPropToFieldNames(qb, prop, options.parentTableAlias, options.explicitFields)));
942
998
  }
943
999
  for (const hint of joinedProps) {
944
1000
  const [propName, ref] = hint.field.split(':', 2);
@@ -1003,14 +1059,18 @@ export class AbstractSqlDriver extends DatabaseDriver {
1003
1059
  /**
1004
1060
  * @internal
1005
1061
  */
1006
- mapPropToFieldNames(qb, prop, tableAlias) {
1062
+ mapPropToFieldNames(qb, prop, tableAlias, explicitFields) {
1007
1063
  if (prop.kind === ReferenceKind.EMBEDDED && !prop.object) {
1008
- return Object.values(prop.embeddedProps).flatMap(childProp => {
1009
- return this.mapPropToFieldNames(qb, childProp, tableAlias);
1064
+ return Object.entries(prop.embeddedProps).flatMap(([name, childProp]) => {
1065
+ const childFields = explicitFields ? Utils.extractChildElements(explicitFields, prop.name) : [];
1066
+ if (childFields.length > 0 && !this.shouldHaveColumn(prop.targetMeta, { ...childProp, name }, [], childFields)) {
1067
+ return [];
1068
+ }
1069
+ return this.mapPropToFieldNames(qb, childProp, tableAlias, childFields);
1010
1070
  });
1011
1071
  }
1012
1072
  const aliased = this.platform.quoteIdentifier(`${tableAlias}__${prop.fieldNames[0]}`);
1013
- if (prop.customTypes?.some(type => type?.convertToJSValueSQL)) {
1073
+ if (prop.customTypes?.some(type => !!type?.convertToJSValueSQL)) {
1014
1074
  return prop.fieldNames.map((col, idx) => {
1015
1075
  if (!prop.customTypes[idx]?.convertToJSValueSQL) {
1016
1076
  return col;
@@ -1295,7 +1355,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1295
1355
  }
1296
1356
  }
1297
1357
  else if (!Utils.isEmpty(options.exclude) || lazyProps.some(p => !p.formula && (p.kind !== '1:1' || p.owner))) {
1298
- const props = meta.props.filter(prop => this.platform.shouldHaveColumn(prop, populate, options.exclude, false));
1358
+ const props = meta.props.filter(prop => this.platform.shouldHaveColumn(prop, populate, options.exclude, false, false));
1299
1359
  ret.push(...props.filter(p => !lazyProps.includes(p)).map(p => p.name));
1300
1360
  addFormulas = true;
1301
1361
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/knex",
3
- "version": "7.0.0-dev.34",
3
+ "version": "7.0.0-dev.36",
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
  "type": "module",
6
6
  "exports": {
@@ -54,9 +54,9 @@
54
54
  "sqlstring": "2.3.3"
55
55
  },
56
56
  "devDependencies": {
57
- "@mikro-orm/core": "^6.5.7"
57
+ "@mikro-orm/core": "^6.5.8"
58
58
  },
59
59
  "peerDependencies": {
60
- "@mikro-orm/core": "7.0.0-dev.34"
60
+ "@mikro-orm/core": "7.0.0-dev.36"
61
61
  }
62
62
  }
@@ -11,6 +11,30 @@ export interface ExecuteOptions {
11
11
  mapResults?: boolean;
12
12
  mergeResults?: boolean;
13
13
  }
14
+ export interface QBStreamOptions {
15
+ /**
16
+ * Results are mapped to entities, if you set `mapResults: false` you will get POJOs instead.
17
+ *
18
+ * @default true
19
+ */
20
+ mapResults?: boolean;
21
+ /**
22
+ * When populating to-many relations, the ORM streams fully merged entities instead of yielding every row.
23
+ * You can opt out of this behavior by specifying `mergeResults: false`. This will yield every row from
24
+ * the SQL result, but still mapped to entities, meaning that to-many collections will contain at most
25
+ * one item, and you will get duplicate root entities when they have multiple items in the populated
26
+ * collection.
27
+ *
28
+ * @default true
29
+ */
30
+ mergeResults?: boolean;
31
+ /**
32
+ * When enabled, the driver will return the raw database results without renaming the fields to match the entity property names.
33
+ *
34
+ * @default false
35
+ */
36
+ rawResults?: boolean;
37
+ }
14
38
  type AnyString = string & {};
15
39
  type Compute<T> = {
16
40
  [K in keyof T]: T[K];
@@ -158,6 +182,8 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
158
182
  orWhere(cond: QBFilterQuery<Entity>): this;
159
183
  orWhere(cond: string, params?: any[]): this;
160
184
  orderBy(orderBy: QBQueryOrderMap<Entity> | QBQueryOrderMap<Entity>[]): SelectQueryBuilder<Entity, RootAlias, Hint, Context>;
185
+ andOrderBy(orderBy: QBQueryOrderMap<Entity> | QBQueryOrderMap<Entity>[]): SelectQueryBuilder<Entity, RootAlias, Hint, Context>;
186
+ private processOrderBy;
161
187
  groupBy(fields: EntityKeyOrString<Entity> | readonly EntityKeyOrString<Entity>[]): SelectQueryBuilder<Entity, RootAlias, Hint, Context>;
162
188
  having(cond?: QBFilterQuery | string, params?: any[], operator?: keyof typeof GroupOperator): SelectQueryBuilder<Entity, RootAlias, Hint, Context>;
163
189
  andHaving(cond?: QBFilterQuery | string, params?: any[]): SelectQueryBuilder<Entity, RootAlias, Hint, Context>;
@@ -182,17 +208,17 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
182
208
  /**
183
209
  * Adds index hint to the FROM clause.
184
210
  */
185
- indexHint(sql: string): this;
211
+ indexHint(sql: string | undefined): this;
186
212
  /**
187
213
  * Prepend comment to the sql query using the syntax `/* ... *&#8205;/`. Some characters are forbidden such as `/*, *&#8205;/` and `?`.
188
214
  */
189
- comment(comment: string | string[]): this;
215
+ comment(comment: string | string[] | undefined): this;
190
216
  /**
191
217
  * Add hints to the query using comment-like syntax `/*+ ... *&#8205;/`. MySQL and Oracle use this syntax for optimizer hints.
192
218
  * Also various DB proxies and routers use this syntax to pass hints to alter their behavior. In other dialects the hints
193
219
  * are ignored as simple comments.
194
220
  */
195
- hintComment(comment: string | string[]): this;
221
+ hintComment(comment: string | string[] | undefined): this;
196
222
  /**
197
223
  * Specifies FROM which entity's table select/update/delete will be executed, removing all previously set FROM-s.
198
224
  * Allows setting a main string alias of the selection data.
@@ -245,14 +271,35 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
245
271
  * Use `method` to specify what kind of result you want to get (array/single/meta).
246
272
  */
247
273
  execute<U = any>(method?: 'all' | 'get' | 'run', options?: ExecuteOptions | boolean): Promise<U>;
274
+ private getConnection;
275
+ /**
276
+ * Executes the query and returns an async iterable (async generator) that yields results one by one.
277
+ * By default, the results are merged and mapped to entity instances, without adding them to the identity map.
278
+ * You can disable merging and mapping by passing the options `{ mergeResults: false, mapResults: false }`.
279
+ * This is useful for processing large datasets without loading everything into memory at once.
280
+ *
281
+ * ```ts
282
+ * const qb = em.createQueryBuilder(Book, 'b');
283
+ * qb.select('*').where({ title: '1984' }).leftJoinAndSelect('b.author', 'a');
284
+ *
285
+ * for await (const book of qb.stream()) {
286
+ * // book is an instance of Book entity
287
+ * console.log(book.title, book.author.name);
288
+ * }
289
+ * ```
290
+ */
291
+ stream(options?: QBStreamOptions): AsyncIterableIterator<Loaded<Entity, Hint>>;
248
292
  /**
249
293
  * Alias for `qb.getResultList()`
250
294
  */
251
295
  getResult(): Promise<Loaded<Entity, Hint>[]>;
252
296
  /**
253
- * Executes the query, returning array of results
297
+ * Executes the query, returning array of results mapped to entity instances.
254
298
  */
255
299
  getResultList(limit?: number): Promise<Loaded<Entity, Hint>[]>;
300
+ private propagatePopulateHint;
301
+ private mapResult;
302
+ private mapResults;
256
303
  /**
257
304
  * Executes the query, returning the first result or null
258
305
  */
@@ -359,8 +359,16 @@ export class QueryBuilder {
359
359
  return this.where(cond, params, '$or');
360
360
  }
361
361
  orderBy(orderBy) {
362
+ return this.processOrderBy(orderBy, true);
363
+ }
364
+ andOrderBy(orderBy) {
365
+ return this.processOrderBy(orderBy, false);
366
+ }
367
+ processOrderBy(orderBy, reset = true) {
362
368
  this.ensureNotFinalized();
363
- this._orderBy = [];
369
+ if (reset) {
370
+ this._orderBy = [];
371
+ }
364
372
  Utils.asArray(orderBy).forEach(o => {
365
373
  const processed = QueryHelper.processWhere({
366
374
  where: o,
@@ -676,7 +684,7 @@ export class QueryBuilder {
676
684
  options.mapResults ??= true;
677
685
  const isRunType = [QueryType.INSERT, QueryType.UPDATE, QueryType.DELETE, QueryType.TRUNCATE].includes(this.type);
678
686
  method ??= isRunType ? 'run' : 'all';
679
- if (!this.connectionType && isRunType) {
687
+ if (!this.connectionType && (isRunType || this.context)) {
680
688
  this.connectionType = 'write';
681
689
  }
682
690
  if (!this.finalized && method === 'get' && this.type === QueryType.SELECT) {
@@ -687,10 +695,8 @@ export class QueryBuilder {
687
695
  if (cached?.data !== undefined) {
688
696
  return cached.data;
689
697
  }
690
- const write = method === 'run' || !this.platform.getConfig().get('preferReadReplicas');
691
- const type = this.connectionType || (write ? 'write' : 'read');
692
698
  const loggerContext = { id: this.em?.id, ...this.loggerContext };
693
- const res = await this.driver.getConnection(type).execute(query.sql, query.params, method, this.context, loggerContext);
699
+ const res = await this.getConnection().execute(query.sql, query.params, method, this.context, loggerContext);
694
700
  const meta = this.mainAlias.metadata;
695
701
  if (!options.mapResults || !meta) {
696
702
  await this.em?.storeCache(this._cache, cached, res);
@@ -718,6 +724,64 @@ export class QueryBuilder {
718
724
  await this.em?.storeCache(this._cache, cached, mapped);
719
725
  return mapped;
720
726
  }
727
+ getConnection() {
728
+ const write = !this.platform.getConfig().get('preferReadReplicas');
729
+ const type = this.connectionType || (write ? 'write' : 'read');
730
+ return this.driver.getConnection(type);
731
+ }
732
+ /**
733
+ * Executes the query and returns an async iterable (async generator) that yields results one by one.
734
+ * By default, the results are merged and mapped to entity instances, without adding them to the identity map.
735
+ * You can disable merging and mapping by passing the options `{ mergeResults: false, mapResults: false }`.
736
+ * This is useful for processing large datasets without loading everything into memory at once.
737
+ *
738
+ * ```ts
739
+ * const qb = em.createQueryBuilder(Book, 'b');
740
+ * qb.select('*').where({ title: '1984' }).leftJoinAndSelect('b.author', 'a');
741
+ *
742
+ * for await (const book of qb.stream()) {
743
+ * // book is an instance of Book entity
744
+ * console.log(book.title, book.author.name);
745
+ * }
746
+ * ```
747
+ */
748
+ async *stream(options) {
749
+ options ??= {};
750
+ options.mergeResults ??= true;
751
+ options.mapResults ??= true;
752
+ const query = this.toQuery();
753
+ const loggerContext = { id: this.em?.id, ...this.loggerContext };
754
+ const res = this.getConnection().stream(query.sql, query.params, this.context, loggerContext);
755
+ const meta = this.mainAlias.metadata;
756
+ if (options.rawResults || !meta) {
757
+ yield* res;
758
+ return;
759
+ }
760
+ const joinedProps = this.driver.joinedProps(meta, this._populate);
761
+ const stack = [];
762
+ const hash = (data) => {
763
+ return Utils.getPrimaryKeyHash(meta.primaryKeys.map(pk => data[pk]));
764
+ };
765
+ for await (const row of res) {
766
+ const mapped = this.driver.mapResult(row, meta, this._populate, this);
767
+ if (!options.mergeResults || joinedProps.length === 0) {
768
+ yield this.mapResult(mapped, options.mapResults);
769
+ continue;
770
+ }
771
+ if (stack.length > 0 && hash(stack[stack.length - 1]) !== hash(mapped)) {
772
+ const res = this.driver.mergeJoinedResult(stack, this.mainAlias.metadata, joinedProps);
773
+ for (const row of res) {
774
+ yield this.mapResult(row, options.mapResults);
775
+ }
776
+ stack.length = 0;
777
+ }
778
+ stack.push(mapped);
779
+ }
780
+ if (stack.length > 0) {
781
+ const merged = this.driver.mergeJoinedResult(stack, this.mainAlias.metadata, joinedProps);
782
+ yield this.mapResult(merged[0], options.mapResults);
783
+ }
784
+ }
721
785
  /**
722
786
  * Alias for `qb.getResultList()`
723
787
  */
@@ -725,29 +789,40 @@ export class QueryBuilder {
725
789
  return this.getResultList();
726
790
  }
727
791
  /**
728
- * Executes the query, returning array of results
792
+ * Executes the query, returning array of results mapped to entity instances.
729
793
  */
730
794
  async getResultList(limit) {
731
795
  await this.em.tryFlush(this.mainAlias.entityName, { flushMode: this.flushMode });
732
796
  const res = await this.execute('all', true);
733
- const entities = [];
734
- function propagatePopulateHint(entity, hint) {
735
- helper(entity).__serializationContext.populate = hint.concat(helper(entity).__serializationContext.populate ?? []);
736
- hint.forEach(hint => {
737
- const [propName] = hint.field.split(':', 2);
738
- const value = Reference.unwrapReference(entity[propName]);
739
- if (Utils.isEntity(value)) {
740
- propagatePopulateHint(value, hint.children ?? []);
741
- }
742
- else if (Utils.isCollection(value)) {
743
- value.populated();
744
- value.getItems(false).forEach(item => propagatePopulateHint(item, hint.children ?? []));
745
- }
746
- });
797
+ return this.mapResults(res, limit);
798
+ }
799
+ propagatePopulateHint(entity, hint) {
800
+ helper(entity).__serializationContext.populate = hint.concat(helper(entity).__serializationContext.populate ?? []);
801
+ hint.forEach(hint => {
802
+ const [propName] = hint.field.split(':', 2);
803
+ const value = Reference.unwrapReference(entity[propName]);
804
+ if (Utils.isEntity(value)) {
805
+ this.propagatePopulateHint(value, hint.children ?? []);
806
+ }
807
+ else if (Utils.isCollection(value)) {
808
+ value.populated();
809
+ value.getItems(false).forEach(item => this.propagatePopulateHint(item, hint.children ?? []));
810
+ }
811
+ });
812
+ }
813
+ mapResult(row, map = true) {
814
+ if (!map) {
815
+ return row;
747
816
  }
748
- for (const r of res) {
749
- const entity = this.em.map(this.mainAlias.entityName, r, { schema: this._schema });
750
- propagatePopulateHint(entity, this._populate);
817
+ const entity = this.em.map(this.mainAlias.entityName, row, { schema: this._schema });
818
+ this.propagatePopulateHint(entity, this._populate);
819
+ return entity;
820
+ }
821
+ mapResults(res, limit) {
822
+ const entities = [];
823
+ for (const row of res) {
824
+ const entity = this.mapResult(row);
825
+ this.propagatePopulateHint(entity, this._populate);
751
826
  entities.push(entity);
752
827
  if (limit != null && --limit === 0) {
753
828
  break;
@@ -1145,7 +1220,7 @@ export class QueryBuilder {
1145
1220
  if (!this.flags.has(QueryFlag.DISABLE_PAGINATE) && this._groupBy.length === 0 && this.hasToManyJoins()) {
1146
1221
  this.flags.add(QueryFlag.PAGINATE);
1147
1222
  }
1148
- if (meta && this.flags.has(QueryFlag.PAGINATE) && (this._limit > 0 || this._offset > 0)) {
1223
+ if (meta && this.flags.has(QueryFlag.PAGINATE) && !this.flags.has(QueryFlag.DISABLE_PAGINATE) && (this._limit > 0 || this._offset > 0)) {
1149
1224
  this.wrapPaginateSubQuery(meta);
1150
1225
  }
1151
1226
  if (meta && (this.flags.has(QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(QueryFlag.DELETE_SUB_QUERY))) {
@@ -1267,10 +1342,11 @@ export class QueryBuilder {
1267
1342
  }
1268
1343
  else if (join.parent?.type === JoinType.nestedInnerJoin) {
1269
1344
  const group = lookupParentGroup(join.parent);
1345
+ const nested = group ?? ((join.parent).nested ??= new Set());
1270
1346
  join.type = join.type === JoinType.innerJoin
1271
1347
  ? JoinType.nestedInnerJoin
1272
1348
  : JoinType.nestedLeftJoin;
1273
- group?.add(join);
1349
+ nested.add(join);
1274
1350
  }
1275
1351
  }
1276
1352
  }
@@ -1,4 +1,4 @@
1
- import { ReferenceKind } from '@mikro-orm/core';
1
+ import { ReferenceKind, RawQueryFragment, } from '@mikro-orm/core';
2
2
  import { DatabaseTable } from './DatabaseTable.js';
3
3
  /**
4
4
  * @internal
@@ -108,9 +108,14 @@ export class DatabaseSchema {
108
108
  table.addIndex(meta, { properties: meta.props.filter(prop => prop.primary).map(prop => prop.name) }, 'primary');
109
109
  for (const check of meta.checks) {
110
110
  const columnName = check.property ? meta.properties[check.property].fieldNames[0] : undefined;
111
+ let expression = check.expression;
112
+ const raw = RawQueryFragment.getKnownFragment(expression);
113
+ if (raw) {
114
+ expression = platform.formatQuery(raw.sql, raw.params);
115
+ }
111
116
  table.addCheck({
112
117
  name: check.name,
113
- expression: check.expression,
118
+ expression,
114
119
  definition: `check (${check.expression})`,
115
120
  columnName,
116
121
  });
@@ -109,33 +109,34 @@ export class DatabaseTable {
109
109
  if (prop.referencedTableName.includes('.')) {
110
110
  schema = undefined;
111
111
  }
112
- this.foreignKeys[constraintName] = {
113
- constraintName,
114
- columnNames: prop.fieldNames,
115
- localTableName: this.getShortestName(false),
116
- referencedColumnNames: prop.referencedColumnNames,
117
- referencedTableName: schema ? `${schema}.${prop.referencedTableName}` : prop.referencedTableName,
118
- createForeignKeyConstraint: prop.createForeignKeyConstraint,
119
- };
120
- const cascade = prop.cascade.includes(Cascade.REMOVE) || prop.cascade.includes(Cascade.ALL);
121
- if (prop.deleteRule || cascade || prop.nullable) {
122
- this.foreignKeys[constraintName].deleteRule = prop.deleteRule || (cascade ? 'cascade' : 'set null');
123
- }
124
- if (prop.updateRule) {
125
- this.foreignKeys[constraintName].updateRule = prop.updateRule || 'cascade';
126
- }
127
- if ((prop.cascade.includes(Cascade.PERSIST) || prop.cascade.includes(Cascade.ALL))) {
128
- const hasCascadePath = Object.values(this.foreignKeys).some(fk => {
129
- return fk.constraintName !== constraintName
130
- && ((fk.updateRule && fk.updateRule !== 'no action') || (fk.deleteRule && fk.deleteRule !== 'no action'))
131
- && fk.referencedTableName === this.foreignKeys[constraintName].referencedTableName;
132
- });
133
- if (!hasCascadePath || this.platform.supportsMultipleCascadePaths()) {
134
- this.foreignKeys[constraintName].updateRule ??= 'cascade';
112
+ if (prop.createForeignKeyConstraint) {
113
+ this.foreignKeys[constraintName] = {
114
+ constraintName,
115
+ columnNames: prop.fieldNames,
116
+ localTableName: this.getShortestName(false),
117
+ referencedColumnNames: prop.referencedColumnNames,
118
+ referencedTableName: schema ? `${schema}.${prop.referencedTableName}` : prop.referencedTableName,
119
+ };
120
+ const cascade = prop.cascade.includes(Cascade.REMOVE) || prop.cascade.includes(Cascade.ALL);
121
+ if (prop.deleteRule || cascade || prop.nullable) {
122
+ this.foreignKeys[constraintName].deleteRule = prop.deleteRule || (cascade ? 'cascade' : 'set null');
123
+ }
124
+ if (prop.updateRule) {
125
+ this.foreignKeys[constraintName].updateRule = prop.updateRule || 'cascade';
126
+ }
127
+ if ((prop.cascade.includes(Cascade.PERSIST) || prop.cascade.includes(Cascade.ALL))) {
128
+ const hasCascadePath = Object.values(this.foreignKeys).some(fk => {
129
+ return fk.constraintName !== constraintName
130
+ && ((fk.updateRule && fk.updateRule !== 'no action') || (fk.deleteRule && fk.deleteRule !== 'no action'))
131
+ && fk.referencedTableName === this.foreignKeys[constraintName].referencedTableName;
132
+ });
133
+ if (!hasCascadePath || this.platform.supportsMultipleCascadePaths()) {
134
+ this.foreignKeys[constraintName].updateRule ??= 'cascade';
135
+ }
136
+ }
137
+ if (prop.deferMode) {
138
+ this.foreignKeys[constraintName].deferMode = prop.deferMode;
135
139
  }
136
- }
137
- if (prop.deferMode) {
138
- this.foreignKeys[constraintName].deferMode = prop.deferMode;
139
140
  }
140
141
  }
141
142
  if (prop.index) {
@@ -269,7 +269,7 @@ export class SchemaComparator {
269
269
  }
270
270
  for (const toConstraint of Object.values(toForeignKeys)) {
271
271
  tableDifferences.addedForeignKeys[toConstraint.constraintName] = toConstraint;
272
- this.log(`FK constraint ${toConstraint.constraintName} added from table ${tableDifferences.name}`, { constraint: toConstraint });
272
+ this.log(`FK constraint ${toConstraint.constraintName} added to table ${tableDifferences.name}`, { constraint: toConstraint });
273
273
  changes++;
274
274
  }
275
275
  return changes ? tableDifferences : false;
@@ -426,7 +426,7 @@ export class SchemaHelper {
426
426
  return `alter table ${table.getQuotedName()} comment = ${this.platform.quoteValue(comment ?? '')}`;
427
427
  }
428
428
  createForeignKey(table, foreignKey, alterTable = true, inline = false) {
429
- if (!this.options.createForeignKeyConstraints || !foreignKey.createForeignKeyConstraint) {
429
+ if (!this.options.createForeignKeyConstraints) {
430
430
  return '';
431
431
  }
432
432
  const constraintName = this.quote(foreignKey.constraintName);
package/typings.d.ts CHANGED
@@ -60,7 +60,6 @@ export interface ForeignKey {
60
60
  updateRule?: string;
61
61
  deleteRule?: string;
62
62
  deferMode?: DeferMode;
63
- createForeignKeyConstraint: boolean;
64
63
  }
65
64
  export interface IndexDef {
66
65
  columnNames: string[];