@mikro-orm/knex 6.0.0-rc.2 → 6.0.0-rc.3

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.
@@ -40,7 +40,7 @@ export declare abstract class AbstractSqlConnection extends Connection {
40
40
  }): Promise<Knex.Transaction>;
41
41
  commit(ctx: Knex.Transaction, eventBroadcaster?: TransactionEventBroadcaster): Promise<void>;
42
42
  rollback(ctx: Knex.Transaction, eventBroadcaster?: TransactionEventBroadcaster): Promise<void>;
43
- 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>;
43
+ execute<T extends QueryResult | EntityData<AnyEntity> | EntityData<AnyEntity>[] = EntityData<AnyEntity>[]>(queryOrKnex: string | Knex.QueryBuilder | Knex.Raw, params?: unknown[], method?: 'all' | 'get' | 'run', ctx?: Transaction, loggerContext?: LoggingOptions): Promise<T>;
44
44
  /**
45
45
  * Execute raw SQL queries from file
46
46
  */
@@ -106,7 +106,7 @@ class AbstractSqlConnection extends core_1.Connection {
106
106
  await eventBroadcaster?.dispatchEvent(core_1.EventType.afterTransactionRollback, ctx);
107
107
  }
108
108
  }
109
- async execute(queryOrKnex, params = [], method = 'all', ctx, logging) {
109
+ async execute(queryOrKnex, params = [], method = 'all', ctx, loggerContext) {
110
110
  await this.ensureConnection();
111
111
  if (core_1.Utils.isObject(queryOrKnex)) {
112
112
  ctx ??= (queryOrKnex.client.transacting ? queryOrKnex : null);
@@ -115,7 +115,7 @@ class AbstractSqlConnection extends core_1.Connection {
115
115
  params = q.bindings;
116
116
  }
117
117
  const formatted = this.platform.formatQuery(queryOrKnex, params);
118
- const sql = this.getSql(queryOrKnex, formatted, logging);
118
+ const sql = this.getSql(queryOrKnex, formatted, loggerContext);
119
119
  return this.executeQuery(sql, async () => {
120
120
  const query = this.getKnex().raw(formatted);
121
121
  if (ctx) {
@@ -123,7 +123,7 @@ class AbstractSqlConnection extends core_1.Connection {
123
123
  }
124
124
  const res = await query;
125
125
  return this.transformRawResult(res, method);
126
- }, { query: queryOrKnex, params, ...logging });
126
+ }, { query: queryOrKnex, params, ...loggerContext });
127
127
  }
128
128
  /**
129
129
  * Execute raw SQL queries from file
@@ -13,10 +13,10 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
13
13
  protected constructor(config: Configuration, platform: Platform, connection: Constructor<Connection>, connector: string[]);
14
14
  getPlatform(): Platform;
15
15
  createEntityManager<D extends IDatabaseDriver = IDatabaseDriver>(useContext?: boolean): D[typeof EntityManagerType];
16
- find<T extends object, P extends string = never, F extends string = '*'>(entityName: string, where: FilterQuery<T>, options?: FindOptions<T, P, F>): Promise<EntityData<T>[]>;
17
- 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>;
16
+ find<T extends object, P extends string = never, F extends string = '*', E extends string = never>(entityName: string, where: FilterQuery<T>, options?: FindOptions<T, P, F, E>): Promise<EntityData<T>[]>;
17
+ findOne<T extends object, P extends string = never, F extends string = '*', E extends string = never>(entityName: string, where: FilterQuery<T>, options?: FindOneOptions<T, P, F, E>): Promise<EntityData<T> | null>;
18
18
  protected hasToManyJoins<T extends object>(hint: PopulateOptions<T>, meta: EntityMetadata<T>): boolean;
19
- findVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: FindOptions<T, any, any>): Promise<EntityData<T>[]>;
19
+ findVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: FindOptions<T, any, any, any>): Promise<EntityData<T>[]>;
20
20
  countVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: CountOptions<T, any>): Promise<number>;
21
21
  protected findFromVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: FindOptions<T, any> | CountOptions<T, any>, type: QueryType): Promise<EntityData<T>[] | number>;
22
22
  protected wrapVirtualExpressionInSubquery<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any>, type: QueryType): Promise<T[] | number>;
@@ -29,13 +29,13 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
29
29
  nativeUpdateMany<T extends object>(entityName: string, where: FilterQuery<T>[], data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T> & UpsertManyOptions<T>): Promise<QueryResult<T>>;
30
30
  nativeDelete<T extends object>(entityName: string, where: FilterQuery<T> | string | any, options?: DeleteOptions<T>): Promise<QueryResult<T>>;
31
31
  syncCollections<T extends object, O extends object>(collections: Iterable<Collection<T, O>>, options?: DriverMethodOptions): Promise<void>;
32
- 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>, pivotJoin?: boolean): Promise<Dictionary<T[]>>;
32
+ 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[]>>;
33
33
  private getPivotOrderBy;
34
34
  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>;
35
35
  /**
36
36
  * 1:1 owner side needs to be marked for population so QB auto-joins the owner id
37
37
  */
38
- protected autoJoinOneToOneOwner<T extends object, P extends string = never>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], fields?: readonly EntityField<T, P>[]): PopulateOptions<T>[];
38
+ protected autoJoinOneToOneOwner<T extends object, P extends string = never>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], fields?: readonly EntityField<T, any>[]): PopulateOptions<T>[];
39
39
  /**
40
40
  * @internal
41
41
  */
@@ -46,7 +46,7 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
46
46
  * @internal
47
47
  */
48
48
  mergeJoinedResult<T extends object>(rawResults: EntityData<T>[], meta: EntityMetadata<T>, joinedProps: PopulateOptions<T>[]): EntityData<T>[];
49
- protected getFieldsForJoinedLoad<T extends object>(qb: QueryBuilder<T>, meta: EntityMetadata<T>, explicitFields?: Field<T>[], populate?: PopulateOptions<T>[], options?: {
49
+ protected getFieldsForJoinedLoad<T extends object>(qb: QueryBuilder<T>, meta: EntityMetadata<T>, explicitFields?: Field<T>[], exclude?: Field<T>[], populate?: PopulateOptions<T>[], options?: {
50
50
  strategy?: Options['loadStrategy'];
51
51
  populateWhere?: FindOptions<any>['populateWhere'];
52
52
  }, parentTableAlias?: string, parentJoinPath?: string): Field<T>[];
@@ -55,7 +55,7 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
55
55
  */
56
56
  mapPropToFieldNames<T extends object>(qb: QueryBuilder<T>, prop: EntityProperty<T>, tableAlias?: string): Field<T>[];
57
57
  /** @internal */
58
- createQueryBuilder<T extends object>(entityName: EntityName<T> | QueryBuilder<T>, ctx?: Transaction<Knex.Transaction>, preferredConnectionType?: ConnectionType, convertCustomTypes?: boolean, logging?: LoggingOptions): QueryBuilder<T>;
58
+ createQueryBuilder<T extends object>(entityName: EntityName<T> | QueryBuilder<T>, ctx?: Transaction<Knex.Transaction>, preferredConnectionType?: ConnectionType, convertCustomTypes?: boolean, loggerContext?: LoggingOptions): QueryBuilder<T>;
59
59
  protected resolveConnectionType(args: {
60
60
  ctx?: Transaction<Knex.Transaction>;
61
61
  connectionType?: ConnectionType;
@@ -68,7 +68,5 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
68
68
  protected buildJoinedPropsOrderBy<T extends object>(qb: QueryBuilder<T>, meta: EntityMetadata<T>, populate: PopulateOptions<T>[], options?: Pick<FindOptions<any>, 'strategy' | 'orderBy' | 'populateOrderBy'>, parentPath?: string): QueryOrderMap<T>[];
69
69
  protected normalizeFields<T extends object>(fields: Field<T>[], prefix?: string): string[];
70
70
  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;
71
- protected buildFields<T extends object>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb: QueryBuilder<T>, alias: string, fields?: Field<T>[], options?: {
72
- strategy?: Options['loadStrategy'];
73
- }): Field<T>[];
71
+ protected buildFields<T extends object>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb: QueryBuilder<T>, alias: string, options: Pick<FindOptions<T, any, any, any>, 'strategy' | 'fields' | 'exclude'>): Field<T>[];
74
72
  }
@@ -31,7 +31,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
31
31
  const populate = this.autoJoinOneToOneOwner(meta, options.populate, options.fields);
32
32
  const joinedProps = this.joinedProps(meta, populate, options);
33
33
  const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging);
34
- const fields = this.buildFields(meta, populate, joinedProps, qb, qb.alias, options.fields, options);
34
+ const fields = this.buildFields(meta, populate, joinedProps, qb, qb.alias, options);
35
35
  const orderBy = this.buildOrderBy(qb, meta, populate, options);
36
36
  core_1.Utils.asArray(options.flags).forEach(flag => qb.setFlag(flag));
37
37
  if (core_1.Utils.isPrimaryKey(where, meta.compositePK)) {
@@ -111,7 +111,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
111
111
  if (typeof meta.expression === 'string') {
112
112
  return this.wrapVirtualExpressionInSubquery(meta, meta.expression, where, options, type);
113
113
  }
114
- const em = this.createEntityManager(false);
114
+ const em = this.createEntityManager();
115
115
  em.setTransactionContext(options.ctx);
116
116
  const res = meta.expression(em, where, options);
117
117
  if (typeof res === 'string') {
@@ -217,9 +217,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
217
217
  }
218
218
  return;
219
219
  }
220
- const relationPojo = {};
220
+ let relationPojo = {};
221
221
  meta2.props
222
- .filter(prop => prop.persist === false && prop.fieldNames)
222
+ .filter(prop => !ref && prop.persist === false && prop.fieldNames)
223
223
  .filter(prop => !prop.lazy || populate.some(p => p.field === prop.name || p.all))
224
224
  .forEach(prop => {
225
225
  /* istanbul ignore if */
@@ -231,25 +231,42 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
231
231
  relationPojo[prop.name] = root[alias];
232
232
  }
233
233
  });
234
- meta2.props
235
- .filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []))
236
- .forEach(prop => {
234
+ const targetProps = ref
235
+ ? meta2.getPrimaryProps()
236
+ : meta2.props.filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []));
237
+ for (const prop of targetProps) {
237
238
  if (prop.fieldNames.length > 1) { // composite keys
238
239
  const fk = prop.fieldNames.map(name => root[`${relationAlias}__${name}`]);
239
- prop.fieldNames.map(name => delete root[`${relationAlias}__${name}`]);
240
240
  relationPojo[prop.name] = core_1.Utils.mapFlatCompositePrimaryKey(fk, prop);
241
241
  }
242
242
  else if (prop.runtimeType === 'Date') {
243
243
  const alias = `${relationAlias}__${prop.fieldNames[0]}`;
244
244
  relationPojo[prop.name] = (typeof root[alias] === 'string' ? new Date(root[alias]) : root[alias]);
245
- delete root[alias];
246
245
  }
247
246
  else {
248
247
  const alias = `${relationAlias}__${prop.fieldNames[0]}`;
249
248
  relationPojo[prop.name] = root[alias];
250
- delete root[alias];
249
+ if (prop.kind === core_1.ReferenceKind.EMBEDDED && (prop.object || meta.embeddable)) {
250
+ const item = (0, core_1.parseJsonSafe)(relationPojo[prop.name]);
251
+ if (Array.isArray(item)) {
252
+ relationPojo[prop.name] = item.map(row => row == null ? row : this.comparator.mapResult(prop.type, row));
253
+ }
254
+ else {
255
+ relationPojo[prop.name] = item == null ? item : this.comparator.mapResult(prop.type, item);
256
+ }
257
+ }
251
258
  }
252
- });
259
+ }
260
+ // properties can be mapped to multiple places, e.g. when sharing a column in multiple FKs,
261
+ // so we need to delete them after everything is mapped from given level
262
+ for (const prop of targetProps) {
263
+ prop.fieldNames.map(name => delete root[`${relationAlias}__${name}`]);
264
+ }
265
+ if (ref) {
266
+ const tmp = Object.values(relationPojo);
267
+ /* istanbul ignore next */
268
+ relationPojo = (meta2.compositePK ? tmp : tmp[0]);
269
+ }
253
270
  if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(prop.kind)) {
254
271
  result[prop.name] ??= [];
255
272
  result[prop.name].push(relationPojo);
@@ -465,6 +482,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
465
482
  }
466
483
  const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
467
484
  const keys = new Set();
485
+ const fields = new Set();
468
486
  const returning = new Set();
469
487
  data.forEach(row => {
470
488
  core_1.Utils.keys(row).forEach(k => {
@@ -498,6 +516,10 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
498
516
  keys.forEach(key => {
499
517
  const prop = meta.properties[key];
500
518
  prop.fieldNames.forEach((fieldName, fieldNameIdx) => {
519
+ if (fields.has(fieldName)) {
520
+ return;
521
+ }
522
+ fields.add(fieldName);
501
523
  sql += `${this.platform.quoteIdentifier(fieldName)} = case`;
502
524
  where.forEach((cond, idx) => {
503
525
  if (key in data[idx]) {
@@ -668,7 +690,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
668
690
  const targetAlias = qb.getNextAlias(prop.targetMeta.tableName);
669
691
  const targetSchema = this.getSchemaName(prop.targetMeta, options) ?? this.platform.getDefaultSchemaName();
670
692
  qb.innerJoin(pivotProp1.name, targetAlias, {}, targetSchema);
671
- const targetFields = this.buildFields(prop.targetMeta, (options.populate ?? []), [], qb, targetAlias, options.fields, options);
693
+ const targetFields = this.buildFields(prop.targetMeta, (options.populate ?? []), [], qb, targetAlias, options);
672
694
  for (const field of targetFields) {
673
695
  const f = field.toString();
674
696
  fields.unshift(f.includes('.') ? field : `${targetAlias}.${f}`);
@@ -754,11 +776,13 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
754
776
  return populate.filter(hint => {
755
777
  const [propName, ref] = hint.field.split(':', 2);
756
778
  const prop = meta.properties[propName] || {};
779
+ if ((options?.strategy || hint.strategy || prop.strategy || this.config.get('loadStrategy')) !== core_1.LoadStrategy.JOINED) {
780
+ return false;
781
+ }
757
782
  if (ref) {
758
- // keep only pivot ref joins here, as that only triggers actual join
759
- return prop.kind === core_1.ReferenceKind.MANY_TO_MANY;
783
+ return [core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(prop.kind);
760
784
  }
761
- return (prop.strategy || options?.strategy || hint.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && ![core_1.ReferenceKind.SCALAR, core_1.ReferenceKind.EMBEDDED].includes(prop.kind);
785
+ return ![core_1.ReferenceKind.SCALAR, core_1.ReferenceKind.EMBEDDED].includes(prop.kind);
762
786
  });
763
787
  }
764
788
  /**
@@ -799,14 +823,14 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
799
823
  }
800
824
  return res;
801
825
  }
802
- getFieldsForJoinedLoad(qb, meta, explicitFields, populate = [], options, parentTableAlias, parentJoinPath) {
826
+ getFieldsForJoinedLoad(qb, meta, explicitFields, exclude, populate = [], options, parentTableAlias, parentJoinPath) {
803
827
  const fields = [];
804
828
  const joinedProps = this.joinedProps(meta, populate, options);
805
829
  if (explicitFields?.includes('*')) {
806
830
  fields.push('*');
807
831
  }
808
832
  const shouldHaveColumn = (prop, populate, fields) => {
809
- if (!this.platform.shouldHaveColumn(prop, populate)) {
833
+ if (!this.platform.shouldHaveColumn(prop, populate, exclude)) {
810
834
  return false;
811
835
  }
812
836
  if (!fields || prop.primary) {
@@ -838,14 +862,18 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
838
862
  if (pivotRefJoin) {
839
863
  fields.push(...prop.joinColumns.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)), ...prop.inverseJoinColumns.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)));
840
864
  }
865
+ if (prop.kind === core_1.ReferenceKind.ONE_TO_MANY && ref) {
866
+ fields.push(...this.getFieldsForJoinedLoad(qb, meta2, prop.referencedColumnNames, undefined, hint.children, options, tableAlias, path));
867
+ }
841
868
  const childExplicitFields = explicitFields?.filter(f => core_1.Utils.isPlainObject(f)).map(o => o[prop.name])[0] || [];
842
869
  explicitFields?.forEach(f => {
843
870
  if (typeof f === 'string' && f.startsWith(`${prop.name}.`)) {
844
871
  childExplicitFields.push(f.substring(prop.name.length + 1));
845
872
  }
846
873
  });
874
+ const childExclude = exclude ? core_1.Utils.extractChildElements(exclude, prop.name) : exclude;
847
875
  if (!ref) {
848
- fields.push(...this.getFieldsForJoinedLoad(qb, meta2, childExplicitFields.length === 0 ? undefined : childExplicitFields, hint.children, options, tableAlias, path));
876
+ fields.push(...this.getFieldsForJoinedLoad(qb, meta2, childExplicitFields.length === 0 ? undefined : childExplicitFields, childExclude, hint.children, options, tableAlias, path));
849
877
  }
850
878
  });
851
879
  return fields;
@@ -856,9 +884,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
856
884
  mapPropToFieldNames(qb, prop, tableAlias) {
857
885
  const knex = this.connection.getKnex();
858
886
  const aliased = knex.ref(tableAlias ? `${tableAlias}__${prop.fieldNames[0]}` : prop.fieldNames[0]).toString();
859
- if (tableAlias && prop.customTypes?.some(type => type.convertToJSValueSQL)) {
887
+ if (tableAlias && prop.customTypes?.some(type => type?.convertToJSValueSQL)) {
860
888
  return prop.fieldNames.map((col, idx) => {
861
- if (!prop.customTypes[idx].convertToJSValueSQL) {
889
+ if (!prop.customTypes[idx]?.convertToJSValueSQL) {
862
890
  return col;
863
891
  }
864
892
  const prefixed = knex.ref(col).withSchema(tableAlias).toString();
@@ -880,9 +908,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
880
908
  return prop.fieldNames;
881
909
  }
882
910
  /** @internal */
883
- createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes, logging) {
911
+ createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes, loggerContext) {
884
912
  const connectionType = this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
885
- const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType, undefined, logging);
913
+ const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType, undefined, loggerContext);
886
914
  if (!convertCustomTypes) {
887
915
  qb.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
888
916
  }
@@ -1047,16 +1075,16 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
1047
1075
  }
1048
1076
  ret.push(prop.name);
1049
1077
  }
1050
- buildFields(meta, populate, joinedProps, qb, alias, fields, options) {
1078
+ buildFields(meta, populate, joinedProps, qb, alias, options) {
1051
1079
  const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => p.field === prop.name || p.all));
1052
1080
  const hasLazyFormulas = meta.props.some(p => p.lazy && p.formula);
1053
1081
  const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL && p.persist !== false);
1054
- const hasExplicitFields = !!fields;
1082
+ const hasExplicitFields = !!options.fields;
1055
1083
  const ret = [];
1056
1084
  let addFormulas = false;
1057
1085
  // handle root entity properties first, this is used for both strategies in the same way
1058
- if (fields) {
1059
- for (const field of this.normalizeFields(fields)) {
1086
+ if (options.fields) {
1087
+ for (const field of this.normalizeFields(options.fields)) {
1060
1088
  if (field === '*') {
1061
1089
  ret.push('*');
1062
1090
  continue;
@@ -1072,12 +1100,12 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
1072
1100
  });
1073
1101
  this.processField(meta, prop, parts.join('.'), ret, populate, joinedProps, qb);
1074
1102
  }
1075
- if (!fields.includes('*') && !fields.includes(`${qb.alias}.*`)) {
1076
- ret.unshift(...meta.primaryKeys.filter(pk => !fields.includes(pk)));
1103
+ if (!options.fields.includes('*') && !options.fields.includes(`${qb.alias}.*`)) {
1104
+ ret.unshift(...meta.primaryKeys.filter(pk => !options.fields.includes(pk)));
1077
1105
  }
1078
1106
  }
1079
- else if (lazyProps.filter(p => !p.formula).length > 0) {
1080
- const props = meta.props.filter(prop => this.platform.shouldHaveColumn(prop, populate, false));
1107
+ else if (!core_1.Utils.isEmpty(options.exclude) || lazyProps.some(p => !p.formula)) {
1108
+ const props = meta.props.filter(prop => this.platform.shouldHaveColumn(prop, populate, options.exclude, false));
1081
1109
  ret.push(...core_1.Utils.flatten(props.filter(p => !lazyProps.includes(p)).map(p => p.fieldNames)));
1082
1110
  addFormulas = true;
1083
1111
  }
@@ -1102,7 +1130,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
1102
1130
  }
1103
1131
  // add joined relations after the root entity fields
1104
1132
  if (joinedProps.length > 0) {
1105
- ret.push(...this.getFieldsForJoinedLoad(qb, meta, fields, populate, options, alias));
1133
+ ret.push(...this.getFieldsForJoinedLoad(qb, meta, options.fields, options.exclude, populate, options, alias));
1106
1134
  }
1107
1135
  return core_1.Utils.unique(ret);
1108
1136
  }
@@ -13,10 +13,14 @@ export declare abstract class AbstractSqlPlatform extends Platform {
13
13
  quoteValue(value: any): string;
14
14
  formatQuery(sql: string, params: readonly any[]): string;
15
15
  getSearchJsonPropertySQL(path: string, type: string, aliased: boolean): string;
16
- getSearchJsonPropertyKey(path: string[], type: string, aliased: boolean): string;
16
+ getSearchJsonPropertyKey(path: string[], type: string, aliased: boolean, value?: unknown): string;
17
17
  getJsonIndexDefinition(index: IndexDef): string[];
18
18
  isRaw(value: any): boolean;
19
19
  supportsSchemas(): boolean;
20
20
  /** @inheritDoc */
21
21
  generateCustomOrder(escapedColumn: string, values: unknown[]): string;
22
+ /**
23
+ * @internal
24
+ */
25
+ getOrderByExpression(column: string, direction: string): string[];
22
26
  }
@@ -83,7 +83,7 @@ class AbstractSqlPlatform extends core_1.Platform {
83
83
  getSearchJsonPropertySQL(path, type, aliased) {
84
84
  return this.getSearchJsonPropertyKey(path.split('->'), type, aliased);
85
85
  }
86
- getSearchJsonPropertyKey(path, type, aliased) {
86
+ getSearchJsonPropertyKey(path, type, aliased, value) {
87
87
  const [a, ...b] = path;
88
88
  const quoteKey = (key) => key.match(/^[a-z]\w*$/i) ? key : `"${key}"`;
89
89
  if (aliased) {
@@ -112,5 +112,11 @@ class AbstractSqlPlatform extends core_1.Platform {
112
112
  });
113
113
  return ret + 'else null end)';
114
114
  }
115
+ /**
116
+ * @internal
117
+ */
118
+ getOrderByExpression(column, direction) {
119
+ return [`${column} ${direction.toLowerCase()}`];
120
+ }
115
121
  }
116
122
  exports.AbstractSqlPlatform = AbstractSqlPlatform;
@@ -1,5 +1,5 @@
1
1
  import type { Knex } from 'knex';
2
- import { EntityManager, type AnyEntity, type ConnectionType, type EntityData, type EntityName, type EntityRepository, type GetRepository, type QueryResult, type FilterQuery } from '@mikro-orm/core';
2
+ import { EntityManager, type AnyEntity, type ConnectionType, type EntityData, type EntityName, type EntityRepository, type GetRepository, type QueryResult, type FilterQuery, type LoggingOptions } from '@mikro-orm/core';
3
3
  import type { AbstractSqlDriver } from './AbstractSqlDriver';
4
4
  import { QueryBuilder } from './query';
5
5
  import type { SqlEntityRepository } from './SqlEntityRepository';
@@ -10,11 +10,11 @@ export declare class SqlEntityManager<D extends AbstractSqlDriver = AbstractSqlD
10
10
  /**
11
11
  * Creates a QueryBuilder instance
12
12
  */
13
- createQueryBuilder<T extends object>(entityName: EntityName<T> | QueryBuilder<T>, alias?: string, type?: ConnectionType): QueryBuilder<T>;
13
+ createQueryBuilder<T extends object>(entityName: EntityName<T> | QueryBuilder<T>, alias?: string, type?: ConnectionType, loggerContext?: LoggingOptions): QueryBuilder<T>;
14
14
  /**
15
15
  * Shortcut for `createQueryBuilder()`
16
16
  */
17
- qb<T extends object>(entityName: EntityName<T>, alias?: string, type?: ConnectionType): QueryBuilder<T>;
17
+ qb<T extends object>(entityName: EntityName<T>, alias?: string, type?: ConnectionType, loggerContext?: LoggingOptions): QueryBuilder<T>;
18
18
  /**
19
19
  * Returns configured knex instance.
20
20
  */
@@ -10,15 +10,15 @@ class SqlEntityManager extends core_1.EntityManager {
10
10
  /**
11
11
  * Creates a QueryBuilder instance
12
12
  */
13
- createQueryBuilder(entityName, alias, type) {
13
+ createQueryBuilder(entityName, alias, type, loggerContext) {
14
14
  const context = this.getContext();
15
- return new query_1.QueryBuilder(entityName, this.getMetadata(), this.getDriver(), context.getTransactionContext(), alias, type, context);
15
+ return new query_1.QueryBuilder(entityName, this.getMetadata(), this.getDriver(), context.getTransactionContext(), alias, type, context, loggerContext ?? context.loggerContext);
16
16
  }
17
17
  /**
18
18
  * Shortcut for `createQueryBuilder()`
19
19
  */
20
- qb(entityName, alias, type) {
21
- return this.createQueryBuilder(entityName, alias, type);
20
+ qb(entityName, alias, type, loggerContext) {
21
+ return this.createQueryBuilder(entityName, alias, type, loggerContext);
22
22
  }
23
23
  /**
24
24
  * Returns configured knex instance.
package/index.mjs CHANGED
@@ -47,7 +47,7 @@ export const DatabaseObjectExistsException = mod.DatabaseObjectExistsException;
47
47
  export const DatabaseObjectNotFoundException = mod.DatabaseObjectNotFoundException;
48
48
  export const DatabaseSchema = mod.DatabaseSchema;
49
49
  export const DatabaseTable = mod.DatabaseTable;
50
- export const Dataloader = mod.Dataloader;
50
+ export const DataloaderType = mod.DataloaderType;
51
51
  export const DataloaderUtils = mod.DataloaderUtils;
52
52
  export const DateTimeType = mod.DateTimeType;
53
53
  export const DateType = mod.DateType;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/knex",
3
- "version": "6.0.0-rc.2",
3
+ "version": "6.0.0-rc.3",
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",
@@ -58,14 +58,14 @@
58
58
  "access": "public"
59
59
  },
60
60
  "dependencies": {
61
- "fs-extra": "11.1.1",
62
- "knex": "3.0.1",
61
+ "fs-extra": "11.2.0",
62
+ "knex": "3.1.0",
63
63
  "sqlstring": "2.3.3"
64
64
  },
65
65
  "devDependencies": {
66
- "@mikro-orm/core": "^5.9.2"
66
+ "@mikro-orm/core": "^5.0.0"
67
67
  },
68
68
  "peerDependencies": {
69
- "@mikro-orm/core": "6.0.0-rc.2"
69
+ "@mikro-orm/core": "6.0.0-rc.3"
70
70
  }
71
71
  }
@@ -1,6 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import { inspect } from 'util';
3
- import { type EntityProperty, type MetadataStorage, type EntityKey } from '@mikro-orm/core';
3
+ import { type EntityKey, type EntityProperty, type MetadataStorage } from '@mikro-orm/core';
4
4
  import type { ICriteriaNode, ICriteriaNodeProcessOptions, IQueryBuilder } from '../typings';
5
5
  /**
6
6
  * Helper for working with deeply nested where/orderBy/having criteria. Uses composite pattern to build tree from the payload.
@@ -28,5 +28,4 @@ export declare class CriteriaNode<T extends object> implements ICriteriaNode<T>
28
28
  aliased(field: string, alias?: string): string;
29
29
  /** @ignore */
30
30
  [inspect.custom](): string;
31
- static isCustomExpression(field: string): boolean;
32
31
  }
@@ -31,7 +31,7 @@ class CriteriaNode {
31
31
  this.prop = meta.props.find(prop => prop.name === k || (prop.fieldNames?.length === 1 && prop.fieldNames[0] === k));
32
32
  const isProp = this.prop || meta.props.find(prop => (prop.fieldNames || []).includes(k));
33
33
  // do not validate if the key is prefixed or type casted (e.g. `k::text`)
34
- if (validate && !isProp && !k.includes('.') && !k.includes('::') && !core_1.Utils.isOperator(k) && !CriteriaNode.isCustomExpression(k)) {
34
+ if (validate && !isProp && !k.includes('.') && !k.includes('::') && !core_1.Utils.isOperator(k) && !core_1.RawQueryFragment.isKnownFragment(k)) {
35
35
  throw new Error(`Trying to query by not existing property ${entityName}.${k}`);
36
36
  }
37
37
  });
@@ -52,7 +52,7 @@ class CriteriaNode {
52
52
  shouldRename(payload) {
53
53
  const type = this.prop ? this.prop.kind : null;
54
54
  const composite = this.prop?.joinColumns ? this.prop.joinColumns.length > 1 : false;
55
- const customExpression = CriteriaNode.isCustomExpression(this.key);
55
+ const customExpression = core_1.RawQueryFragment.isKnownFragment(this.key);
56
56
  const scalar = payload === null || core_1.Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date || customExpression;
57
57
  const plainObject = core_1.Utils.isPlainObject(payload);
58
58
  const keys = plainObject ? Object.keys(payload) : [];
@@ -98,7 +98,7 @@ class CriteriaNode {
98
98
  if (!this.key || !this.prop) {
99
99
  return false;
100
100
  }
101
- const customExpression = CriteriaNode.isCustomExpression(this.key);
101
+ const customExpression = core_1.RawQueryFragment.isKnownFragment(this.key);
102
102
  const scalar = this.payload === null || core_1.Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date || customExpression;
103
103
  const operator = core_1.Utils.isObject(this.payload) && Object.keys(this.payload).every(k => core_1.Utils.isOperator(k, false));
104
104
  return this.prop.kind === core_1.ReferenceKind.MANY_TO_MANY && (scalar || operator);
@@ -117,8 +117,5 @@ class CriteriaNode {
117
117
  .forEach(k => o[k] = this[k]);
118
118
  return `${this.constructor.name} ${(0, util_1.inspect)(o)}`;
119
119
  }
120
- static isCustomExpression(field) {
121
- return !!field.match(/[ ?<>=()]|^\d/);
122
- }
123
120
  }
124
121
  exports.CriteriaNode = CriteriaNode;
@@ -1,4 +1,4 @@
1
- import { type Dictionary, type EntityMetadata, type MetadataStorage, type EntityKey } from '@mikro-orm/core';
1
+ import { type Dictionary, type EntityKey, type EntityMetadata, type MetadataStorage } from '@mikro-orm/core';
2
2
  import type { ICriteriaNode } from '../typings';
3
3
  /**
4
4
  * @internal
@@ -5,13 +5,12 @@ const core_1 = require("@mikro-orm/core");
5
5
  const ObjectCriteriaNode_1 = require("./ObjectCriteriaNode");
6
6
  const ArrayCriteriaNode_1 = require("./ArrayCriteriaNode");
7
7
  const ScalarCriteriaNode_1 = require("./ScalarCriteriaNode");
8
- const CriteriaNode_1 = require("./CriteriaNode");
9
8
  /**
10
9
  * @internal
11
10
  */
12
11
  class CriteriaNodeFactory {
13
12
  static createNode(metadata, entityName, payload, parent, key) {
14
- const customExpression = CriteriaNode_1.CriteriaNode.isCustomExpression(key || '');
13
+ const customExpression = core_1.RawQueryFragment.isKnownFragment(key || '');
15
14
  const scalar = core_1.Utils.isPrimaryKey(payload) || core_1.Utils.isRawSql(payload) || payload instanceof RegExp || payload instanceof Date || customExpression;
16
15
  if (Array.isArray(payload) && !scalar) {
17
16
  return this.createArrayNode(metadata, entityName, payload, parent, key);
@@ -1,5 +1,5 @@
1
1
  import { CriteriaNode } from './CriteriaNode';
2
- import type { IQueryBuilder, ICriteriaNodeProcessOptions } from '../typings';
2
+ import type { ICriteriaNodeProcessOptions, IQueryBuilder } from '../typings';
3
3
  /**
4
4
  * @internal
5
5
  */
@@ -16,7 +16,7 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
16
16
  if (nestedAlias) {
17
17
  alias = nestedAlias;
18
18
  }
19
- if (this.shouldAutoJoin(nestedAlias)) {
19
+ if (this.shouldAutoJoin(qb, nestedAlias)) {
20
20
  if (keys.some(k => ['$some', '$none', '$every'].includes(k))) {
21
21
  const $and = [];
22
22
  const primaryKeys = this.metadata.find(this.entityName).primaryKeys.map(pk => {
@@ -86,7 +86,7 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
86
86
  if (nestedAlias) {
87
87
  alias = nestedAlias;
88
88
  }
89
- if (this.shouldAutoJoin(nestedAlias)) {
89
+ if (this.shouldAutoJoin(qb, nestedAlias)) {
90
90
  return !keys.some(k => ['$some', '$none', '$every'].includes(k));
91
91
  }
92
92
  return keys.some(field => {
@@ -95,7 +95,7 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
95
95
  });
96
96
  }
97
97
  shouldInline(payload) {
98
- const customExpression = ObjectCriteriaNode.isCustomExpression(this.key);
98
+ const customExpression = core_1.RawQueryFragment.isKnownFragment(this.key);
99
99
  const scalar = core_1.Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date || customExpression;
100
100
  const operator = core_1.Utils.isObject(payload) && Object.keys(payload).every(k => core_1.Utils.isOperator(k, false));
101
101
  return !!this.prop && this.prop.kind !== core_1.ReferenceKind.SCALAR && !scalar && !operator;
@@ -128,7 +128,7 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
128
128
  o[key] = payload[k];
129
129
  }
130
130
  }
131
- else if (ObjectCriteriaNode.isCustomExpression(k)) {
131
+ else if (core_1.RawQueryFragment.isKnownFragment(k)) {
132
132
  o[k] = payload[k];
133
133
  }
134
134
  else {
@@ -137,15 +137,19 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
137
137
  }
138
138
  }
139
139
  }
140
- shouldAutoJoin(nestedAlias) {
140
+ shouldAutoJoin(qb, nestedAlias) {
141
141
  if (!this.prop || !this.parent) {
142
142
  return false;
143
143
  }
144
+ const keys = Object.keys(this.payload);
145
+ if (keys.every(k => k.includes('.') && k.startsWith(`${qb.alias}.`))) {
146
+ return false;
147
+ }
148
+ const meta = this.metadata.find(this.entityName);
144
149
  const embeddable = this.prop.kind === core_1.ReferenceKind.EMBEDDED;
145
150
  const knownKey = [core_1.ReferenceKind.SCALAR, core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.EMBEDDED].includes(this.prop.kind) || (this.prop.kind === core_1.ReferenceKind.ONE_TO_ONE && this.prop.owner);
146
- const operatorKeys = knownKey && Object.keys(this.payload).every(key => core_1.Utils.isOperator(key, false));
147
- const primaryKeys = knownKey && Object.keys(this.payload).every(key => {
148
- const meta = this.metadata.find(this.entityName);
151
+ const operatorKeys = knownKey && keys.every(key => core_1.Utils.isOperator(key, false));
152
+ const primaryKeys = knownKey && keys.every(key => {
149
153
  if (!meta.primaryKeys.includes(key)) {
150
154
  return false;
151
155
  }
@@ -158,7 +162,7 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
158
162
  }
159
163
  autoJoin(qb, alias) {
160
164
  const nestedAlias = qb.getNextAlias(this.prop?.pivotTable ?? this.entityName);
161
- const customExpression = ObjectCriteriaNode.isCustomExpression(this.key);
165
+ const customExpression = core_1.RawQueryFragment.isKnownFragment(this.key);
162
166
  const scalar = core_1.Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date || customExpression;
163
167
  const operator = core_1.Utils.isPlainObject(this.payload) && Object.keys(this.payload).every(k => core_1.Utils.isOperator(k, false));
164
168
  const field = `${alias}.${this.prop.name}`;
@@ -38,7 +38,7 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
38
38
  private readonly context?;
39
39
  private connectionType?;
40
40
  private readonly em?;
41
- private readonly logging?;
41
+ private readonly loggerContext?;
42
42
  get mainAlias(): Alias<T>;
43
43
  get alias(): string;
44
44
  get helper(): QueryBuilderHelper;
@@ -87,7 +87,7 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
87
87
  /**
88
88
  * @internal
89
89
  */
90
- constructor(entityName: EntityName<T> | QueryBuilder<T>, metadata: MetadataStorage, driver: AbstractSqlDriver, context?: Knex.Transaction<any, any[]> | undefined, alias?: string, connectionType?: ConnectionType | undefined, em?: SqlEntityManager<AbstractSqlDriver<import("..").AbstractSqlConnection, AbstractSqlPlatform>> | undefined, logging?: LoggingOptions | undefined);
90
+ constructor(entityName: EntityName<T> | QueryBuilder<T>, metadata: MetadataStorage, driver: AbstractSqlDriver, context?: Knex.Transaction<any, any[]> | undefined, alias?: string, connectionType?: ConnectionType | undefined, em?: SqlEntityManager<AbstractSqlDriver<import("..").AbstractSqlConnection, AbstractSqlPlatform>> | undefined, loggerContext?: LoggingOptions | undefined);
91
91
  select(fields: Field<T> | Field<T>[], distinct?: boolean): SelectQueryBuilder<T>;
92
92
  addSelect(fields: Field<T> | Field<T>[]): SelectQueryBuilder<T>;
93
93
  distinct(): SelectQueryBuilder<T>;
@@ -31,7 +31,7 @@ class QueryBuilder {
31
31
  context;
32
32
  connectionType;
33
33
  em;
34
- logging;
34
+ loggerContext;
35
35
  get mainAlias() {
36
36
  this.ensureFromClause();
37
37
  return this._mainAlias;
@@ -88,13 +88,13 @@ class QueryBuilder {
88
88
  /**
89
89
  * @internal
90
90
  */
91
- constructor(entityName, metadata, driver, context, alias, connectionType, em, logging) {
91
+ constructor(entityName, metadata, driver, context, alias, connectionType, em, loggerContext) {
92
92
  this.metadata = metadata;
93
93
  this.driver = driver;
94
94
  this.context = context;
95
95
  this.connectionType = connectionType;
96
96
  this.em = em;
97
- this.logging = logging;
97
+ this.loggerContext = loggerContext;
98
98
  this.platform = this.driver.getPlatform();
99
99
  this.knex = this.driver.getConnection(this.connectionType).getKnex();
100
100
  if (alias) {
@@ -587,7 +587,7 @@ class QueryBuilder {
587
587
  }
588
588
  const write = method === 'run' || !this.platform.getConfig().get('preferReadReplicas');
589
589
  const type = this.connectionType || (write ? 'write' : 'read');
590
- const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context, this.logging);
590
+ const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context, this.loggerContext);
591
591
  const meta = this.mainAlias.metadata;
592
592
  if (!options.mapResults || !meta) {
593
593
  await this.em?.storeCache(this._cache, cached, res);
@@ -496,7 +496,7 @@ class QueryBuilderHelper {
496
496
  order.forEach(part => ret.push(...this.getQueryOrderFromObject(type, part, populate)));
497
497
  }
498
498
  else {
499
- ret.push(`${colPart} ${order.toLowerCase()}`);
499
+ ret.push(...this.platform.getOrderByExpression(colPart, order));
500
500
  }
501
501
  }
502
502
  }
@@ -21,6 +21,12 @@ class ScalarCriteriaNode extends CriteriaNode_1.CriteriaNode {
21
21
  qb.addSelect(field);
22
22
  }
23
23
  }
24
+ if (this.payload && typeof this.payload === 'object') {
25
+ const keys = Object.keys(this.payload).filter(key => core_1.Utils.isArrayOperator(key) && Array.isArray(this.payload[key]));
26
+ for (const key of keys) {
27
+ this.payload[key] = JSON.stringify(this.payload[key]);
28
+ }
29
+ }
24
30
  return this.payload;
25
31
  }
26
32
  willAutoJoin(qb, alias) {
@@ -1,4 +1,4 @@
1
- import { type Configuration, type Dictionary, type EntityMetadata, type EntityProperty, type MikroORMOptions, type NamingStrategy } from '@mikro-orm/core';
1
+ import { type Configuration, type Dictionary, type EntityMetadata, type EntityProperty, type NamingStrategy } from '@mikro-orm/core';
2
2
  import type { SchemaHelper } from './SchemaHelper';
3
3
  import type { CheckDef, Column, ForeignKey, IndexDef } from '../typings';
4
4
  import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
@@ -25,7 +25,7 @@ export declare class DatabaseTable {
25
25
  addColumn(column: Column): void;
26
26
  addColumnFromProperty(prop: EntityProperty, meta: EntityMetadata, config: Configuration): void;
27
27
  private getIndexName;
28
- getEntityDeclaration(namingStrategy: NamingStrategy, schemaHelper: SchemaHelper, scalarPropertiesForRelations: NonNullable<MikroORMOptions['entityGenerator']['scalarPropertiesForRelations']>): EntityMetadata;
28
+ getEntityDeclaration(namingStrategy: NamingStrategy, schemaHelper: SchemaHelper, scalarPropertiesForRelations: 'always' | 'never' | 'smart'): EntityMetadata;
29
29
  private foreignKeysToProps;
30
30
  private findFkIndex;
31
31
  private getIndexProperties;
@@ -1,3 +1,4 @@
1
+ import { type Dictionary } from '@mikro-orm/core';
1
2
  import type { Column, ForeignKey, IndexDef, SchemaDifference, TableDifference } from '../typings';
2
3
  import type { DatabaseSchema } from './DatabaseSchema';
3
4
  import type { DatabaseTable } from './DatabaseTable';
@@ -51,6 +52,7 @@ export declare class SchemaComparator {
51
52
  */
52
53
  isIndexFulfilledBy(index1: IndexDef, index2: IndexDef): boolean;
53
54
  diffExpression(expr1: string, expr2: string): boolean;
55
+ parseJsonDefault(defaultValue?: string | null): Dictionary | string | null;
54
56
  hasSameDefaultValue(from: Column, to: Column): boolean;
55
57
  private mapColumnToProperty;
56
58
  private log;
@@ -449,6 +449,15 @@ class SchemaComparator {
449
449
  const simplify = (str) => str?.replace(/_\w+\\'(.*?)\\'/g, '$1').replace(/['"`()]|::\w+| +/g, '').toLowerCase();
450
450
  return simplify(expr1) !== simplify(expr2);
451
451
  }
452
+ parseJsonDefault(defaultValue) {
453
+ if (!defaultValue) {
454
+ return null;
455
+ }
456
+ const val = defaultValue
457
+ .replace(/^(_\w+\\)?'(.*?)\\?'$/, '$2')
458
+ .replace(/^\(?'(.*?)'\)?$/, '$1');
459
+ return (0, core_1.parseJsonSafe)(val);
460
+ }
452
461
  hasSameDefaultValue(from, to) {
453
462
  if (from.default == null || from.default.toString().toLowerCase() === 'null' || from.default.toString().startsWith('nextval(')) {
454
463
  return to.default == null || to.default.toLowerCase() === 'null';
@@ -459,8 +468,8 @@ class SchemaComparator {
459
468
  return defaultValueFrom === defaultValueTo;
460
469
  }
461
470
  if (to.mappedType instanceof core_1.JsonType) {
462
- const defaultValueFrom = (0, core_1.parseJsonSafe)(from.default.replace(/^(_\w+\\)?'(.*?)\\?'$/, '$2'));
463
- const defaultValueTo = (0, core_1.parseJsonSafe)(to.default?.replace(/^\(?'(.*?)'\)?$/, '$1'));
471
+ const defaultValueFrom = this.parseJsonDefault(from.default);
472
+ const defaultValueTo = this.parseJsonDefault(to.default);
464
473
  return core_1.Utils.equals(defaultValueFrom, defaultValueTo);
465
474
  }
466
475
  if (to.mappedType instanceof core_1.DateTimeType && from.default && to.default) {