@mikro-orm/sql 7.0.0-rc.3 → 7.0.1-dev.0

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 (59) hide show
  1. package/AbstractSqlConnection.d.ts +5 -4
  2. package/AbstractSqlConnection.js +18 -5
  3. package/AbstractSqlDriver.d.ts +1 -1
  4. package/AbstractSqlDriver.js +39 -10
  5. package/AbstractSqlPlatform.d.ts +34 -0
  6. package/AbstractSqlPlatform.js +47 -3
  7. package/PivotCollectionPersister.d.ts +2 -11
  8. package/PivotCollectionPersister.js +59 -59
  9. package/README.md +5 -4
  10. package/SqlEntityManager.d.ts +1 -1
  11. package/dialects/index.d.ts +1 -0
  12. package/dialects/index.js +1 -0
  13. package/dialects/mysql/BaseMySqlPlatform.d.ts +6 -0
  14. package/dialects/mysql/BaseMySqlPlatform.js +17 -0
  15. package/dialects/mysql/MySqlSchemaHelper.d.ts +1 -1
  16. package/dialects/mysql/MySqlSchemaHelper.js +6 -6
  17. package/dialects/oracledb/OracleDialect.d.ts +78 -0
  18. package/dialects/oracledb/OracleDialect.js +166 -0
  19. package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +19 -0
  20. package/dialects/oracledb/OracleNativeQueryBuilder.js +249 -0
  21. package/dialects/oracledb/index.d.ts +2 -0
  22. package/dialects/oracledb/index.js +2 -0
  23. package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +6 -0
  24. package/dialects/postgresql/BasePostgreSqlPlatform.js +12 -8
  25. package/dialects/postgresql/PostgreSqlSchemaHelper.js +13 -13
  26. package/dialects/sqlite/SqlitePlatform.d.ts +1 -0
  27. package/dialects/sqlite/SqlitePlatform.js +3 -0
  28. package/dialects/sqlite/SqliteSchemaHelper.js +12 -8
  29. package/index.d.ts +1 -1
  30. package/index.js +0 -1
  31. package/package.json +3 -3
  32. package/plugin/index.d.ts +1 -14
  33. package/plugin/index.js +13 -13
  34. package/plugin/transformer.d.ts +6 -22
  35. package/plugin/transformer.js +81 -73
  36. package/query/ArrayCriteriaNode.d.ts +1 -1
  37. package/query/CriteriaNodeFactory.js +15 -3
  38. package/query/NativeQueryBuilder.d.ts +3 -3
  39. package/query/NativeQueryBuilder.js +4 -2
  40. package/query/ObjectCriteriaNode.js +4 -4
  41. package/query/QueryBuilder.d.ts +58 -62
  42. package/query/QueryBuilder.js +377 -370
  43. package/query/QueryBuilderHelper.d.ts +14 -11
  44. package/query/QueryBuilderHelper.js +324 -137
  45. package/query/ScalarCriteriaNode.js +3 -1
  46. package/query/enums.d.ts +2 -0
  47. package/query/enums.js +2 -0
  48. package/schema/DatabaseSchema.d.ts +7 -5
  49. package/schema/DatabaseSchema.js +50 -33
  50. package/schema/DatabaseTable.d.ts +8 -6
  51. package/schema/DatabaseTable.js +84 -60
  52. package/schema/SchemaComparator.d.ts +1 -3
  53. package/schema/SchemaComparator.js +22 -20
  54. package/schema/SchemaHelper.d.ts +2 -13
  55. package/schema/SchemaHelper.js +2 -1
  56. package/schema/SqlSchemaGenerator.d.ts +4 -14
  57. package/schema/SqlSchemaGenerator.js +15 -7
  58. package/typings.d.ts +4 -1
  59. package/tsconfig.build.tsbuildinfo +0 -1
@@ -1,15 +1,15 @@
1
1
  import { type ControlledTransaction, type Dialect, Kysely } from 'kysely';
2
- import { type AnyEntity, Connection, type Dictionary, type EntityData, type IsolationLevel, type LogContext, type LoggingOptions, type QueryResult, RawQueryFragment, type Transaction, type TransactionEventBroadcaster } from '@mikro-orm/core';
2
+ import { type AnyEntity, Connection, type Dictionary, type EntityData, type IsolationLevel, type LogContext, type LoggingOptions, type MaybePromise, type QueryResult, RawQueryFragment, type Transaction, type TransactionEventBroadcaster } from '@mikro-orm/core';
3
3
  import type { AbstractSqlPlatform } from './AbstractSqlPlatform.js';
4
4
  import { NativeQueryBuilder } from './query/NativeQueryBuilder.js';
5
5
  export declare abstract class AbstractSqlConnection extends Connection {
6
6
  #private;
7
7
  protected platform: AbstractSqlPlatform;
8
- abstract createKyselyDialect(overrides: Dictionary): Dialect;
8
+ abstract createKyselyDialect(overrides: Dictionary): MaybePromise<Dialect>;
9
9
  connect(options?: {
10
10
  skipOnConnect?: boolean;
11
11
  }): Promise<void>;
12
- createKysely(): void;
12
+ createKysely(): MaybePromise<void>;
13
13
  /**
14
14
  * @inheritDoc
15
15
  */
@@ -29,6 +29,7 @@ export declare abstract class AbstractSqlConnection extends Connection {
29
29
  error?: Error;
30
30
  }>;
31
31
  getClient<T = any>(): Kysely<T>;
32
+ initClient(): Promise<void>;
32
33
  transactional<T>(cb: (trx: Transaction<ControlledTransaction<any, any>>) => Promise<T>, options?: {
33
34
  isolationLevel?: IsolationLevel;
34
35
  readOnly?: boolean;
@@ -50,6 +51,6 @@ export declare abstract class AbstractSqlConnection extends Connection {
50
51
  stream<T extends EntityData<AnyEntity>>(query: string | NativeQueryBuilder | RawQueryFragment, params?: readonly unknown[], ctx?: Transaction<Kysely<any>>, loggerContext?: LoggingOptions): AsyncIterableIterator<T>;
51
52
  /** @inheritDoc */
52
53
  executeDump(dump: string): Promise<void>;
53
- private getSql;
54
+ protected getSql(query: string, formatted: string, context?: LogContext): string;
54
55
  protected transformRawResult<T>(res: any, method?: 'all' | 'get' | 'run'): T;
55
56
  }
@@ -4,7 +4,7 @@ import { NativeQueryBuilder } from './query/NativeQueryBuilder.js';
4
4
  export class AbstractSqlConnection extends Connection {
5
5
  #client;
6
6
  async connect(options) {
7
- this.getClient();
7
+ await this.initClient();
8
8
  this.connected = true;
9
9
  if (options?.skipOnConnect !== true) {
10
10
  await this.onConnect();
@@ -24,9 +24,13 @@ export class AbstractSqlConnection extends Connection {
24
24
  this.#client = new Kysely({ dialect: driverOptions });
25
25
  }
26
26
  else {
27
- this.#client = new Kysely({
28
- dialect: this.createKyselyDialect(driverOptions),
29
- });
27
+ const dialect = this.createKyselyDialect(driverOptions);
28
+ if (dialect instanceof Promise) {
29
+ return dialect.then(d => {
30
+ this.#client = new Kysely({ dialect: d });
31
+ });
32
+ }
33
+ this.#client = new Kysely({ dialect });
30
34
  }
31
35
  }
32
36
  /**
@@ -62,10 +66,19 @@ export class AbstractSqlConnection extends Connection {
62
66
  }
63
67
  getClient() {
64
68
  if (!this.#client) {
65
- this.createKysely();
69
+ const maybePromise = this.createKysely();
70
+ /* v8 ignore next */
71
+ if (maybePromise instanceof Promise) {
72
+ throw new Error('Current driver requires async initialization, use `MikroORM.init()` instead of the constructor');
73
+ }
66
74
  }
67
75
  return this.#client;
68
76
  }
77
+ async initClient() {
78
+ if (!this.#client) {
79
+ await this.createKysely();
80
+ }
81
+ }
69
82
  async transactional(cb, options = {}) {
70
83
  const trx = await this.begin(options);
71
84
  try {
@@ -53,7 +53,7 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
53
53
  nativeInsert<T extends object>(entityName: EntityName<T>, data: EntityDictionary<T>, options?: NativeInsertUpdateOptions<T>): Promise<QueryResult<T>>;
54
54
  nativeInsertMany<T extends object>(entityName: EntityName<T>, data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T>, transform?: (sql: string) => string): Promise<QueryResult<T>>;
55
55
  nativeUpdate<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, data: EntityDictionary<T>, options?: NativeInsertUpdateOptions<T> & UpsertOptions<T>): Promise<QueryResult<T>>;
56
- nativeUpdateMany<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>[], data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T> & UpsertManyOptions<T>): Promise<QueryResult<T>>;
56
+ nativeUpdateMany<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>[], data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T> & UpsertManyOptions<T>, transform?: (sql: string, params: any[]) => string): Promise<QueryResult<T>>;
57
57
  nativeDelete<T extends object>(entityName: EntityName<T>, where: FilterQuery<T> | string | any, options?: DeleteOptions<T>): Promise<QueryResult<T>>;
58
58
  /**
59
59
  * Fast comparison for collection snapshots that are represented by PK arrays.
@@ -60,7 +60,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
60
60
  this.validateSqlOptions(options);
61
61
  const { first, last, before, after } = options;
62
62
  const isCursorPagination = [first, last, before, after].some(v => v != null);
63
- qb.__populateWhere = options._populateWhere;
63
+ qb.state.resolvedPopulateWhere = options._populateWhere;
64
64
  qb.select(fields)
65
65
  // only add populateWhere if we are populate-joining, as this will be used to add `on` conditions
66
66
  .populate(populate, joinedProps.length > 0 ? populateWhere : undefined, joinedProps.length > 0 ? options.populateFilter : undefined)
@@ -200,7 +200,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
200
200
  if (type === QueryType.COUNT) {
201
201
  native.clear('select').clear('limit').clear('offset').count();
202
202
  }
203
- native.from(raw(`(${expression}) as ${this.platform.quoteIdentifier(qb.alias)}`));
203
+ const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
204
+ native.from(raw(`(${expression})${asKeyword}${this.platform.quoteIdentifier(qb.alias)}`));
204
205
  const query = native.compile();
205
206
  const res = await this.execute(query.sql, query.params, 'all', options.ctx);
206
207
  if (type === QueryType.COUNT) {
@@ -215,7 +216,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
215
216
  const qb = await this.createQueryBuilderFromOptions(meta, where, this.forceBalancedStrategy(options));
216
217
  qb.unsetFlag(QueryFlag.DISABLE_PAGINATE);
217
218
  const native = qb.getNativeQuery(false);
218
- native.from(raw(`(${expression}) as ${this.platform.quoteIdentifier(qb.alias)}`));
219
+ const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
220
+ native.from(raw(`(${expression})${asKeyword}${this.platform.quoteIdentifier(qb.alias)}`));
219
221
  const query = native.compile();
220
222
  const connectionType = this.resolveConnectionType({ ctx: options.ctx, connectionType: options.connectionType });
221
223
  const res = this.getConnection(connectionType).stream(query.sql, query.params, options.ctx, options.loggerContext);
@@ -268,7 +270,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
268
270
  * and need to be renamed back to `column_name` for the result mapper to work.
269
271
  */
270
272
  mapTPTColumns(result, meta, qb) {
271
- const tptAliases = qb._tptAlias;
273
+ const tptAliases = qb.state.tptAlias;
272
274
  // Walk up the TPT hierarchy
273
275
  let parentMeta = meta.tptParent;
274
276
  while (parentMeta) {
@@ -526,7 +528,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
526
528
  this.buildFields(meta, populate, joinedProps, qb, qb.alias, options, schema);
527
529
  }
528
530
  this.validateSqlOptions(options);
529
- qb.__populateWhere = options._populateWhere;
531
+ qb.state.resolvedPopulateWhere = options._populateWhere;
530
532
  qb.indexHint(options.indexHint)
531
533
  .collation(options.collation)
532
534
  .comment(options.comments)
@@ -556,7 +558,12 @@ export class AbstractSqlDriver extends DatabaseDriver {
556
558
  else {
557
559
  /* v8 ignore next */
558
560
  res.insertId = data[meta.primaryKeys[0]] ?? res.insertId ?? res.row[meta.primaryKeys[0]];
559
- pk = [res.insertId];
561
+ if (options.convertCustomTypes && meta?.getPrimaryProp().customType) {
562
+ pk = [meta.getPrimaryProp().customType.convertToDatabaseValue(res.insertId, this.platform)];
563
+ }
564
+ else {
565
+ pk = [res.insertId];
566
+ }
560
567
  }
561
568
  await this.processManyToMany(meta, pk, collections, false, options);
562
569
  return res;
@@ -780,7 +787,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
780
787
  await this.processManyToMany(meta, pk, collections, true, options);
781
788
  return res;
782
789
  }
783
- async nativeUpdateMany(entityName, where, data, options = {}) {
790
+ async nativeUpdateMany(entityName, where, data, options = {}, transform) {
784
791
  options.processCollections ??= true;
785
792
  options.convertCustomTypes ??= true;
786
793
  const meta = this.metadata.get(entityName);
@@ -894,7 +901,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
894
901
  const pks = Utils.flatten(pkProps.map(pk => meta.properties[pk].fieldNames));
895
902
  sql +=
896
903
  pks.length > 1
897
- ? `(${pks.map(pk => `${this.platform.quoteIdentifier(pk)}`).join(', ')})`
904
+ ? `(${pks.map(pk => this.platform.quoteIdentifier(pk)).join(', ')})`
898
905
  : this.platform.quoteIdentifier(pks[0]);
899
906
  const conds = where.map(cond => {
900
907
  if (Utils.isPlainObject(cond) && Utils.getObjectKeysSize(cond) === 1) {
@@ -923,6 +930,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
923
930
  ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}`
924
931
  : '';
925
932
  }
933
+ if (transform) {
934
+ sql = transform(sql, params);
935
+ }
926
936
  const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx, options.loggerContext));
927
937
  for (let i = 0; i < collections.length; i++) {
928
938
  await this.processManyToMany(meta, where[i], collections[i], false, options);
@@ -1050,8 +1060,17 @@ export class AbstractSqlDriver extends DatabaseDriver {
1050
1060
  const pivotProp1 = pivotMeta.relations[prop.owner ? 1 : 0];
1051
1061
  const pivotProp2 = pivotMeta.relations[prop.owner ? 0 : 1];
1052
1062
  const ownerMeta = pivotProp2.targetMeta;
1063
+ // The pivot query builder doesn't convert custom types, so we need to manually
1064
+ // convert owner PKs to DB format for the query and convert result FKs back to
1065
+ // JS format for consistent key hashing in buildPivotResultMap.
1066
+ const pkProp = ownerMeta.properties[ownerMeta.primaryKeys[0]];
1067
+ const needsConversion = pkProp?.customType?.ensureComparable(ownerMeta, pkProp) && !ownerMeta.compositePK;
1068
+ let ownerPks = ownerMeta.compositePK ? owners : owners.map(o => o[0]);
1069
+ if (needsConversion) {
1070
+ ownerPks = ownerPks.map(v => pkProp.customType.convertToDatabaseValue(v, this.platform, { mode: 'query' }));
1071
+ }
1053
1072
  const cond = {
1054
- [pivotProp2.name]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) },
1073
+ [pivotProp2.name]: { $in: ownerPks },
1055
1074
  };
1056
1075
  if (!Utils.isEmpty(where)) {
1057
1076
  cond[pivotProp1.name] = { ...where };
@@ -1084,6 +1103,16 @@ export class AbstractSqlDriver extends DatabaseDriver {
1084
1103
  _populateWhere: 'infer',
1085
1104
  populateFilter: this.wrapPopulateFilter(options, pivotProp2.name),
1086
1105
  });
1106
+ // Convert result FK values back to JS format so key hashing
1107
+ // in buildPivotResultMap is consistent with the owner keys.
1108
+ if (needsConversion) {
1109
+ for (const item of res) {
1110
+ const fk = item[pivotProp2.name];
1111
+ if (fk != null) {
1112
+ item[pivotProp2.name] = pkProp.customType.convertToJSValue(fk, this.platform);
1113
+ }
1114
+ }
1115
+ }
1087
1116
  return this.buildPivotResultMap(owners, res, pivotProp2.name, pivotProp1.name);
1088
1117
  }
1089
1118
  /**
@@ -1509,7 +1538,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1509
1538
  * @internal
1510
1539
  */
1511
1540
  findTPTChildAlias(qb, childMeta) {
1512
- const joins = qb._joins;
1541
+ const joins = qb.state.joins;
1513
1542
  for (const key of Object.keys(joins)) {
1514
1543
  if (joins[key].table === childMeta.tableName && key.includes('[tpt]')) {
1515
1544
  return joins[key].alias;
@@ -4,6 +4,7 @@ import { type SchemaHelper } from './schema/SchemaHelper.js';
4
4
  import type { IndexDef } from './typings.js';
5
5
  import { NativeQueryBuilder } from './query/NativeQueryBuilder.js';
6
6
  export declare abstract class AbstractSqlPlatform extends Platform {
7
+ #private;
7
8
  protected readonly schemaHelper?: SchemaHelper;
8
9
  usesPivotTable(): boolean;
9
10
  indexForeignKeys(): boolean;
@@ -26,6 +27,12 @@ export declare abstract class AbstractSqlPlatform extends Platform {
26
27
  quoteValue(value: any): string;
27
28
  getSearchJsonPropertySQL(path: string, type: string, aliased: boolean): string | RawQueryFragment;
28
29
  getSearchJsonPropertyKey(path: string[], type: string, aliased: boolean, value?: unknown): string | RawQueryFragment;
30
+ /**
31
+ * Quotes a key for use inside a JSON path expression (e.g. `$.key`).
32
+ * Simple alphanumeric keys are left unquoted; others are wrapped in double quotes.
33
+ * @internal
34
+ */
35
+ quoteJsonKey(key: string): string;
29
36
  getJsonIndexDefinition(index: IndexDef): string[];
30
37
  supportsUnionWhere(): boolean;
31
38
  supportsSchemas(): boolean;
@@ -42,4 +49,31 @@ export declare abstract class AbstractSqlPlatform extends Platform {
42
49
  quoteCollation(collation: string): string;
43
50
  /** @internal */
44
51
  protected validateCollationName(collation: string): void;
52
+ /** @internal */
53
+ validateJsonPropertyName(name: string): void;
54
+ /**
55
+ * Returns FROM clause for JSON array iteration.
56
+ * @internal
57
+ */
58
+ getJsonArrayFromSQL(column: string, alias: string, _properties: {
59
+ name: string;
60
+ type: string;
61
+ }[]): string;
62
+ /**
63
+ * Returns SQL expression to access an element's property within a JSON array iteration.
64
+ * @internal
65
+ */
66
+ getJsonArrayElementPropertySQL(alias: string, property: string, _type: string): string;
67
+ /**
68
+ * Wraps JSON array FROM clause and WHERE condition into a full EXISTS condition.
69
+ * MySQL overrides this because `json_table` doesn't support correlated subqueries.
70
+ * @internal
71
+ */
72
+ getJsonArrayExistsSQL(from: string, where: string): string;
73
+ /**
74
+ * Maps a runtime type name (e.g. 'string', 'number') to a driver-specific bind type constant.
75
+ * Used by NativeQueryBuilder for output bindings.
76
+ * @internal
77
+ */
78
+ mapToBindType(type: string): unknown;
45
79
  }
@@ -3,6 +3,7 @@ import { SqlEntityRepository } from './SqlEntityRepository.js';
3
3
  import { SqlSchemaGenerator } from './schema/SqlSchemaGenerator.js';
4
4
  import { NativeQueryBuilder } from './query/NativeQueryBuilder.js';
5
5
  export class AbstractSqlPlatform extends Platform {
6
+ static #JSON_PROPERTY_NAME_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
6
7
  schemaHelper;
7
8
  usesPivotTable() {
8
9
  return true;
@@ -64,11 +65,18 @@ export class AbstractSqlPlatform extends Platform {
64
65
  }
65
66
  getSearchJsonPropertyKey(path, type, aliased, value) {
66
67
  const [a, ...b] = path;
67
- const quoteKey = (key) => (key.match(/^[a-z]\w*$/i) ? key : `"${key}"`);
68
68
  if (aliased) {
69
- return raw(alias => `json_extract(${this.quoteIdentifier(`${alias}.${a}`)}, '$.${b.map(quoteKey).join('.')}')`);
69
+ return raw(alias => `json_extract(${this.quoteIdentifier(`${alias}.${a}`)}, '$.${b.map(this.quoteJsonKey).join('.')}')`);
70
70
  }
71
- return raw(`json_extract(${this.quoteIdentifier(a)}, '$.${b.map(quoteKey).join('.')}')`);
71
+ return raw(`json_extract(${this.quoteIdentifier(a)}, '$.${b.map(this.quoteJsonKey).join('.')}')`);
72
+ }
73
+ /**
74
+ * Quotes a key for use inside a JSON path expression (e.g. `$.key`).
75
+ * Simple alphanumeric keys are left unquoted; others are wrapped in double quotes.
76
+ * @internal
77
+ */
78
+ quoteJsonKey(key) {
79
+ return /^[a-z]\w*$/i.exec(key) ? key : `"${key}"`;
72
80
  }
73
81
  getJsonIndexDefinition(index) {
74
82
  return index.columnNames.map(column => {
@@ -116,4 +124,40 @@ export class AbstractSqlPlatform extends Platform {
116
124
  throw new Error(`Invalid collation name: '${collation}'. Collation names must contain only word characters.`);
117
125
  }
118
126
  }
127
+ /** @internal */
128
+ validateJsonPropertyName(name) {
129
+ if (!AbstractSqlPlatform.#JSON_PROPERTY_NAME_RE.test(name)) {
130
+ throw new Error(`Invalid JSON property name: '${name}'. JSON property names must contain only alphanumeric characters and underscores.`);
131
+ }
132
+ }
133
+ /**
134
+ * Returns FROM clause for JSON array iteration.
135
+ * @internal
136
+ */
137
+ getJsonArrayFromSQL(column, alias, _properties) {
138
+ return `json_each(${column}) as ${this.quoteIdentifier(alias)}`;
139
+ }
140
+ /**
141
+ * Returns SQL expression to access an element's property within a JSON array iteration.
142
+ * @internal
143
+ */
144
+ getJsonArrayElementPropertySQL(alias, property, _type) {
145
+ return `${this.quoteIdentifier(alias)}.${this.quoteIdentifier(property)}`;
146
+ }
147
+ /**
148
+ * Wraps JSON array FROM clause and WHERE condition into a full EXISTS condition.
149
+ * MySQL overrides this because `json_table` doesn't support correlated subqueries.
150
+ * @internal
151
+ */
152
+ getJsonArrayExistsSQL(from, where) {
153
+ return `exists (select 1 from ${from} where ${where})`;
154
+ }
155
+ /**
156
+ * Maps a runtime type name (e.g. 'string', 'number') to a driver-specific bind type constant.
157
+ * Used by NativeQueryBuilder for output bindings.
158
+ * @internal
159
+ */
160
+ mapToBindType(type) {
161
+ return type;
162
+ }
119
163
  }
@@ -1,17 +1,8 @@
1
1
  import { type Dictionary, type EntityMetadata, type EntityProperty, type Primary, type Transaction } from '@mikro-orm/core';
2
2
  import { type AbstractSqlDriver } from './AbstractSqlDriver.js';
3
3
  export declare class PivotCollectionPersister<Entity extends object> {
4
- private readonly meta;
5
- private readonly driver;
6
- private readonly ctx?;
7
- private readonly schema?;
8
- private readonly loggerContext?;
9
- private readonly inserts;
10
- private readonly upserts;
11
- private readonly deletes;
12
- private readonly batchSize;
13
- private order;
14
- constructor(meta: EntityMetadata<Entity>, driver: AbstractSqlDriver, ctx?: Transaction | undefined, schema?: string | undefined, loggerContext?: Dictionary | undefined);
4
+ #private;
5
+ constructor(meta: EntityMetadata<Entity>, driver: AbstractSqlDriver, ctx?: Transaction, schema?: string, loggerContext?: Dictionary);
15
6
  enqueueUpdate(prop: EntityProperty<Entity>, insertDiff: Primary<Entity>[][], deleteDiff: Primary<Entity>[][] | boolean, pks: Primary<Entity>[], isInitialized?: boolean): void;
16
7
  private enqueueInsert;
17
8
  private enqueueUpsert;
@@ -1,55 +1,55 @@
1
1
  class InsertStatement {
2
- keys;
3
- data;
4
2
  order;
3
+ #keys;
4
+ #data;
5
5
  constructor(keys, data, order) {
6
- this.keys = keys;
7
- this.data = data;
8
6
  this.order = order;
7
+ this.#keys = keys;
8
+ this.#data = data;
9
9
  }
10
10
  getHash() {
11
- return JSON.stringify(this.data);
11
+ return JSON.stringify(this.#data);
12
12
  }
13
13
  getData() {
14
14
  const data = {};
15
- this.keys.forEach((key, idx) => (data[key] = this.data[idx]));
15
+ this.#keys.forEach((key, idx) => (data[key] = this.#data[idx]));
16
16
  return data;
17
17
  }
18
18
  }
19
19
  class DeleteStatement {
20
- keys;
21
- cond;
20
+ #keys;
21
+ #cond;
22
22
  constructor(keys, cond) {
23
- this.keys = keys;
24
- this.cond = cond;
23
+ this.#keys = keys;
24
+ this.#cond = cond;
25
25
  }
26
26
  getHash() {
27
- return JSON.stringify(this.cond);
27
+ return JSON.stringify(this.#cond);
28
28
  }
29
29
  getCondition() {
30
30
  const cond = {};
31
- this.keys.forEach((key, idx) => (cond[key] = this.cond[idx]));
31
+ this.#keys.forEach((key, idx) => (cond[key] = this.#cond[idx]));
32
32
  return cond;
33
33
  }
34
34
  }
35
35
  export class PivotCollectionPersister {
36
- meta;
37
- driver;
38
- ctx;
39
- schema;
40
- loggerContext;
41
- inserts = new Map();
42
- upserts = new Map();
43
- deletes = new Map();
44
- batchSize;
45
- order = 0;
36
+ #inserts = new Map();
37
+ #upserts = new Map();
38
+ #deletes = new Map();
39
+ #batchSize;
40
+ #order = 0;
41
+ #meta;
42
+ #driver;
43
+ #ctx;
44
+ #schema;
45
+ #loggerContext;
46
46
  constructor(meta, driver, ctx, schema, loggerContext) {
47
- this.meta = meta;
48
- this.driver = driver;
49
- this.ctx = ctx;
50
- this.schema = schema;
51
- this.loggerContext = loggerContext;
52
- this.batchSize = this.driver.config.get('batchSize');
47
+ this.#meta = meta;
48
+ this.#driver = driver;
49
+ this.#ctx = ctx;
50
+ this.#schema = schema;
51
+ this.#loggerContext = loggerContext;
52
+ this.#batchSize = this.#driver.config.get('batchSize');
53
53
  }
54
54
  enqueueUpdate(prop, insertDiff, deleteDiff, pks, isInitialized = true) {
55
55
  if (insertDiff.length) {
@@ -68,8 +68,8 @@ export class PivotCollectionPersister {
68
68
  for (const fks of insertDiff) {
69
69
  const statement = this.createInsertStatement(prop, fks, pks);
70
70
  const hash = statement.getHash();
71
- if (prop.owner || !this.inserts.has(hash)) {
72
- this.inserts.set(hash, statement);
71
+ if (prop.owner || !this.#inserts.has(hash)) {
72
+ this.#inserts.set(hash, statement);
73
73
  }
74
74
  }
75
75
  }
@@ -77,26 +77,26 @@ export class PivotCollectionPersister {
77
77
  for (const fks of insertDiff) {
78
78
  const statement = this.createInsertStatement(prop, fks, pks);
79
79
  const hash = statement.getHash();
80
- if (prop.owner || !this.upserts.has(hash)) {
81
- this.upserts.set(hash, statement);
80
+ if (prop.owner || !this.#upserts.has(hash)) {
81
+ this.#upserts.set(hash, statement);
82
82
  }
83
83
  }
84
84
  }
85
85
  createInsertStatement(prop, fks, pks) {
86
86
  const { data, keys } = this.buildPivotKeysAndData(prop, fks, pks);
87
- return new InsertStatement(keys, data, this.order++);
87
+ return new InsertStatement(keys, data, this.#order++);
88
88
  }
89
89
  enqueueDelete(prop, deleteDiff, pks) {
90
90
  if (deleteDiff === true) {
91
91
  const { data, keys } = this.buildPivotKeysAndData(prop, [], pks, true);
92
92
  const statement = new DeleteStatement(keys, data);
93
- this.deletes.set(statement.getHash(), statement);
93
+ this.#deletes.set(statement.getHash(), statement);
94
94
  return;
95
95
  }
96
96
  for (const fks of deleteDiff) {
97
97
  const { data, keys } = this.buildPivotKeysAndData(prop, fks, pks);
98
98
  const statement = new DeleteStatement(keys, data);
99
- this.deletes.set(statement.getHash(), statement);
99
+ this.#deletes.set(statement.getHash(), statement);
100
100
  }
101
101
  }
102
102
  /**
@@ -130,46 +130,46 @@ export class PivotCollectionPersister {
130
130
  return items.filter(Boolean);
131
131
  }
132
132
  async execute() {
133
- if (this.deletes.size > 0) {
134
- const deletes = [...this.deletes.values()];
135
- for (let i = 0; i < deletes.length; i += this.batchSize) {
136
- const chunk = deletes.slice(i, i + this.batchSize);
133
+ if (this.#deletes.size > 0) {
134
+ const deletes = [...this.#deletes.values()];
135
+ for (let i = 0; i < deletes.length; i += this.#batchSize) {
136
+ const chunk = deletes.slice(i, i + this.#batchSize);
137
137
  const cond = { $or: [] };
138
138
  for (const item of chunk) {
139
139
  cond.$or.push(item.getCondition());
140
140
  }
141
- await this.driver.nativeDelete(this.meta.class, cond, {
142
- ctx: this.ctx,
143
- schema: this.schema,
144
- loggerContext: this.loggerContext,
141
+ await this.#driver.nativeDelete(this.#meta.class, cond, {
142
+ ctx: this.#ctx,
143
+ schema: this.#schema,
144
+ loggerContext: this.#loggerContext,
145
145
  });
146
146
  }
147
147
  }
148
- if (this.inserts.size > 0) {
149
- const filtered = this.collectStatements(this.inserts);
150
- for (let i = 0; i < filtered.length; i += this.batchSize) {
151
- const chunk = filtered.slice(i, i + this.batchSize);
152
- await this.driver.nativeInsertMany(this.meta.class, chunk, {
153
- ctx: this.ctx,
154
- schema: this.schema,
148
+ if (this.#inserts.size > 0) {
149
+ const filtered = this.collectStatements(this.#inserts);
150
+ for (let i = 0; i < filtered.length; i += this.#batchSize) {
151
+ const chunk = filtered.slice(i, i + this.#batchSize);
152
+ await this.#driver.nativeInsertMany(this.#meta.class, chunk, {
153
+ ctx: this.#ctx,
154
+ schema: this.#schema,
155
155
  convertCustomTypes: false,
156
156
  processCollections: false,
157
- loggerContext: this.loggerContext,
157
+ loggerContext: this.#loggerContext,
158
158
  });
159
159
  }
160
160
  }
161
- if (this.upserts.size > 0) {
162
- const filtered = this.collectStatements(this.upserts);
163
- for (let i = 0; i < filtered.length; i += this.batchSize) {
164
- const chunk = filtered.slice(i, i + this.batchSize);
165
- await this.driver.nativeUpdateMany(this.meta.class, [], chunk, {
166
- ctx: this.ctx,
167
- schema: this.schema,
161
+ if (this.#upserts.size > 0) {
162
+ const filtered = this.collectStatements(this.#upserts);
163
+ for (let i = 0; i < filtered.length; i += this.#batchSize) {
164
+ const chunk = filtered.slice(i, i + this.#batchSize);
165
+ await this.#driver.nativeUpdateMany(this.#meta.class, [], chunk, {
166
+ ctx: this.#ctx,
167
+ schema: this.#schema,
168
168
  convertCustomTypes: false,
169
169
  processCollections: false,
170
170
  upsert: true,
171
171
  onConflictAction: 'ignore',
172
- loggerContext: this.loggerContext,
172
+ loggerContext: this.#loggerContext,
173
173
  });
174
174
  }
175
175
  }
package/README.md CHANGED
@@ -2,14 +2,14 @@
2
2
  <a href="https://mikro-orm.io"><img src="https://raw.githubusercontent.com/mikro-orm/mikro-orm/master/docs/static/img/logo-readme.svg?sanitize=true" alt="MikroORM" /></a>
3
3
  </h1>
4
4
 
5
- TypeScript ORM for Node.js based on Data Mapper, [Unit of Work](https://mikro-orm.io/docs/unit-of-work/) and [Identity Map](https://mikro-orm.io/docs/identity-map/) patterns. Supports MongoDB, MySQL, MariaDB, PostgreSQL and SQLite (including libSQL) databases.
5
+ TypeScript ORM for Node.js based on Data Mapper, [Unit of Work](https://mikro-orm.io/docs/unit-of-work/) and [Identity Map](https://mikro-orm.io/docs/identity-map/) patterns. Supports MongoDB, MySQL, MariaDB, PostgreSQL, SQLite (including libSQL), MSSQL and Oracle databases.
6
6
 
7
7
  > Heavily inspired by [Doctrine](https://www.doctrine-project.org/) and [Hibernate](https://hibernate.org/).
8
8
 
9
- [![NPM version](https://img.shields.io/npm/v/@mikro-orm/core.svg)](https://www.npmjs.com/package/@mikro-orm/core)
10
- [![NPM dev version](https://img.shields.io/npm/v/@mikro-orm/core/next.svg)](https://www.npmjs.com/package/@mikro-orm/core)
9
+ [![NPM version](https://img.shields.io/npm/v/@mikro-orm/core.svg)](https://npmx.dev/package/@mikro-orm/core)
10
+ [![NPM dev version](https://img.shields.io/npm/v/@mikro-orm/core/next.svg)](https://npmx.dev/package/@mikro-orm/core)
11
11
  [![Chat on discord](https://img.shields.io/discord/1214904142443839538?label=discord&color=blue)](https://discord.gg/w8bjxFHS7X)
12
- [![Downloads](https://img.shields.io/npm/dm/@mikro-orm/core.svg)](https://www.npmjs.com/package/@mikro-orm/core)
12
+ [![Downloads](https://img.shields.io/npm/dm/@mikro-orm/core.svg)](https://npmx.dev/package/@mikro-orm/core)
13
13
  [![Coverage Status](https://img.shields.io/coveralls/mikro-orm/mikro-orm.svg)](https://coveralls.io/r/mikro-orm/mikro-orm?branch=master)
14
14
  [![Build Status](https://github.com/mikro-orm/mikro-orm/workflows/tests/badge.svg?branch=master)](https://github.com/mikro-orm/mikro-orm/actions?workflow=tests)
15
15
 
@@ -181,6 +181,7 @@ yarn add @mikro-orm/core @mikro-orm/mysql # for mysql/mariadb
181
181
  yarn add @mikro-orm/core @mikro-orm/mariadb # for mysql/mariadb
182
182
  yarn add @mikro-orm/core @mikro-orm/postgresql # for postgresql
183
183
  yarn add @mikro-orm/core @mikro-orm/mssql # for mssql
184
+ yarn add @mikro-orm/core @mikro-orm/oracledb # for oracle
184
185
  yarn add @mikro-orm/core @mikro-orm/sqlite # for sqlite
185
186
  yarn add @mikro-orm/core @mikro-orm/libsql # for libsql
186
187
  ```
@@ -20,7 +20,7 @@ export declare class SqlEntityManager<Driver extends AbstractSqlDriver = Abstrac
20
20
  /**
21
21
  * Shortcut for `createQueryBuilder()`
22
22
  */
23
- qb<Entity extends object, RootAlias extends string = never>(entityName: EntityName<Entity>, alias?: RootAlias, type?: ConnectionType, loggerContext?: LoggingOptions): QueryBuilder<Entity, RootAlias, never, never, never, "*", {}>;
23
+ qb<Entity extends object, RootAlias extends string = never>(entityName: EntityName<Entity>, alias?: RootAlias, type?: ConnectionType, loggerContext?: LoggingOptions): QueryBuilder<Entity, RootAlias>;
24
24
  /**
25
25
  * Returns configured Kysely instance.
26
26
  */
@@ -2,3 +2,4 @@ export * from './mssql/index.js';
2
2
  export * from './mysql/index.js';
3
3
  export * from './postgresql/index.js';
4
4
  export * from './sqlite/index.js';
5
+ export * from './oracledb/index.js';
package/dialects/index.js CHANGED
@@ -2,3 +2,4 @@ export * from './mssql/index.js';
2
2
  export * from './mysql/index.js';
3
3
  export * from './postgresql/index.js';
4
4
  export * from './sqlite/index.js';
5
+ export * from './oracledb/index.js';
@@ -5,6 +5,7 @@ import { AbstractSqlPlatform } from '../../AbstractSqlPlatform.js';
5
5
  import type { IndexDef } from '../../typings.js';
6
6
  import { MySqlNativeQueryBuilder } from './MySqlNativeQueryBuilder.js';
7
7
  export declare class BaseMySqlPlatform extends AbstractSqlPlatform {
8
+ #private;
8
9
  protected readonly schemaHelper: MySqlSchemaHelper;
9
10
  protected readonly exceptionConverter: MySqlExceptionConverter;
10
11
  protected readonly ORDER_BY_NULLS_TRANSLATE: {
@@ -42,5 +43,10 @@ export declare class BaseMySqlPlatform extends AbstractSqlPlatform {
42
43
  getFullTextWhereClause(): string;
43
44
  getFullTextIndexExpression(indexName: string, schemaName: string | undefined, tableName: string, columns: SimpleColumnMeta[]): string;
44
45
  getOrderByExpression(column: string, direction: string, collation?: string): string[];
46
+ getJsonArrayFromSQL(column: string, alias: string, properties: {
47
+ name: string;
48
+ type: string;
49
+ }[]): string;
50
+ getJsonArrayExistsSQL(from: string, where: string): string;
45
51
  getDefaultClientUrl(): string;
46
52
  }