@mikro-orm/oracledb 7.2.0-dev.3 → 7.2.0-dev.4

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.
@@ -31,10 +31,29 @@ export class OracleConnection extends AbstractSqlConnection {
31
31
  const password = options.password;
32
32
  const onCreateConnection = this.options.onCreateConnection ?? this.config.get('onCreateConnection');
33
33
  const initialPassword = typeof password === 'function' ? await password() : password;
34
+ const dbName = this.config.get('dbName');
35
+ // unqualified names resolve against the login user's schema, but we manage `dbName`
36
+ const setSchemaSql = this.config.get('user', dbName) === dbName ? undefined : this.platform.getSchemaHelper().getSetSchemaSQL(dbName);
34
37
  const poolOptions = {
35
38
  ...options,
36
39
  password: initialPassword,
37
- sessionCallback: onCreateConnection,
40
+ sessionCallback: setSchemaSql || onCreateConnection
41
+ ? (conn, _requestedTag, cb) => {
42
+ const initSession = async () => {
43
+ if (setSchemaSql) {
44
+ // ORA-01435: the schema might not exist yet, `ensureDatabase()` creates it later on
45
+ await conn.execute(setSchemaSql).catch((e) => {
46
+ /* v8 ignore next 3: other failures of `alter session` are not reproducible in tests */
47
+ if (e.errorNum !== 1435) {
48
+ throw e;
49
+ }
50
+ });
51
+ }
52
+ await onCreateConnection?.(conn);
53
+ };
54
+ initSession().then(() => cb(), cb);
55
+ }
56
+ : undefined,
38
57
  };
39
58
  // Retry pool creation for transient Oracle errors (e.g. ORA-01017 under load)
40
59
  let pool;
package/OracleDriver.d.ts CHANGED
@@ -10,6 +10,12 @@ export declare class OracleDriver extends AbstractSqlDriver<OracleConnection, Or
10
10
  createQueryBuilder<T extends AnyEntity<T>>(entityName: EntityName<T>, ctx?: Transaction, preferredConnectionType?: ConnectionType, convertCustomTypes?: boolean, loggerContext?: LoggingOptions, alias?: string, em?: SqlEntityManager): OracleQueryBuilder<T, any, any, any>;
11
11
  nativeInsertMany<T extends object>(entityName: EntityName<T>, data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T>): Promise<QueryResult<T>>;
12
12
  nativeUpdateMany<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>[], data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T> & UpsertManyOptions<T>): Promise<QueryResult<T>>;
13
+ /**
14
+ * Resolves the runtime type of every column a property maps to, aligned with its `fieldNames`.
15
+ * A relation covers one column per target PK column, and a target PK can be a relation itself,
16
+ * so we recurse down to the scalar leaves - `prop.runtimeType` is `unknown` for relations.
17
+ */
18
+ private getOutBindTypes;
13
19
  /** @inheritDoc */
14
20
  getORMClass(): Constructor<OracleMikroORM>;
15
21
  }
package/OracleDriver.js CHANGED
@@ -1,4 +1,4 @@
1
- import { isRaw, QueryFlag, Utils, } from '@mikro-orm/core';
1
+ import { isRaw, QueryFlag, ReferenceKind, Utils, } from '@mikro-orm/core';
2
2
  import { AbstractSqlDriver } from '@mikro-orm/sql';
3
3
  import { OracleConnection } from './OracleConnection.js';
4
4
  import { OracleMikroORM } from './OracleMikroORM.js';
@@ -32,7 +32,7 @@ export class OracleDriver extends AbstractSqlDriver {
32
32
  let pk;
33
33
  if (pks.length > 1) {
34
34
  // owner has composite pk
35
- pk = data.map(d => Utils.getPrimaryKeyCond(d, pks));
35
+ pk = data.map(d => Utils.getOrderedPrimaryKeys(d, meta));
36
36
  }
37
37
  else {
38
38
  res.row ??= {};
@@ -61,8 +61,12 @@ export class OracleDriver extends AbstractSqlDriver {
61
61
  meta.props.filter(prop => prop.generated || prop.version || prop.primary).forEach(prop => returning.add(prop.name));
62
62
  for (const propName of returning) {
63
63
  const prop = meta.properties[propName];
64
- into.push(`:out_${prop.fieldNames[0]}`);
65
- outBindingsMap[`out_${prop.fieldNames[0]}`] = prop.runtimeType;
64
+ // the parent builds the `returning` list from all field names, so every column needs its own OUT bind
65
+ const runtimeTypes = this.getOutBindTypes(prop);
66
+ prop.fieldNames.forEach((fieldName, idx) => {
67
+ into.push(`:out_${fieldName}`);
68
+ outBindingsMap[`out_${fieldName}`] = runtimeTypes[idx];
69
+ });
66
70
  }
67
71
  const outBindings = this.platform.createOutBindings(outBindingsMap);
68
72
  return super.nativeUpdateMany(entityName, where, data, options, (sql, params) => {
@@ -74,6 +78,19 @@ export class OracleDriver extends AbstractSqlDriver {
74
78
  return `${sql} into ${into.join(', ')}`;
75
79
  });
76
80
  }
81
+ /**
82
+ * Resolves the runtime type of every column a property maps to, aligned with its `fieldNames`.
83
+ * A relation covers one column per target PK column, and a target PK can be a relation itself,
84
+ * so we recurse down to the scalar leaves - `prop.runtimeType` is `unknown` for relations.
85
+ */
86
+ getOutBindTypes(prop) {
87
+ if (!prop.targetMeta || ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
88
+ return prop.fieldNames.map(() => prop.runtimeType);
89
+ }
90
+ const types = prop.referencedPKs.flatMap(pk => this.getOutBindTypes(prop.targetMeta.properties[pk]));
91
+ // `fieldNames` of a polymorphic relation start with the discriminator column
92
+ return prop.polymorphic ? ['string', ...types] : types;
93
+ }
77
94
  /** @inheritDoc */
78
95
  getORMClass() {
79
96
  return OracleMikroORM;
@@ -11,7 +11,13 @@ export declare class OracleSchemaGenerator extends SchemaGenerator {
11
11
  /**
12
12
  * creates new database and connects to it
13
13
  */
14
- createDatabase(name?: string): Promise<void>;
14
+ createDatabase(name?: string, options?: {
15
+ skipOnConnect?: boolean;
16
+ }): Promise<void>;
17
+ /**
18
+ * Drops the user representing the database. When dropping the user we are connected as, we stay
19
+ * connected as the management user, as the dropped one can no longer authenticate.
20
+ */
15
21
  dropDatabase(name?: string): Promise<void>;
16
22
  /**
17
23
  * Oracle uses CASCADE CONSTRAINT in DROP TABLE and has no native enums,
@@ -9,7 +9,7 @@ export class OracleSchemaGenerator extends SchemaGenerator {
9
9
  /**
10
10
  * creates new database and connects to it
11
11
  */
12
- async createDatabase(name) {
12
+ async createDatabase(name, options) {
13
13
  name ??= this.config.get('user');
14
14
  /* v8 ignore next: tableSpace fallback */
15
15
  const tableSpace = this.config.get('schemaGenerator').tableSpace ?? 'mikro_orm';
@@ -31,14 +31,24 @@ export class OracleSchemaGenerator extends SchemaGenerator {
31
31
  await this.execute(sql);
32
32
  await this.execute(`grant connect, resource to ${this.platform.quoteIdentifier(name)}`);
33
33
  this.config.set('user', name);
34
- await this.driver.reconnect();
34
+ await this.driver.reconnect(options);
35
35
  }
36
+ /**
37
+ * Drops the user representing the database. When dropping the user we are connected as, we stay
38
+ * connected as the management user, as the dropped one can no longer authenticate.
39
+ */
36
40
  async dropDatabase(name) {
37
41
  name ??= this.config.get('dbName');
42
+ const originalUser = this.getOriginalUser();
38
43
  this.config.set('user', this.helper.getManagementDbName());
39
- await this.driver.reconnect();
44
+ await this.driver.reconnect({ skipOnConnect: true });
40
45
  await this.execute(this.helper.getDropDatabaseSQL(name));
41
- this.config.set('user', name);
46
+ // the dropped user is gone, so a cached `ensureDatabase` result for it is stale
47
+ this.lastEnsuredDatabase = undefined;
48
+ if (originalUser !== name) {
49
+ this.config.set('user', originalUser);
50
+ await this.driver.reconnect({ skipOnConnect: true });
51
+ }
42
52
  }
43
53
  /**
44
54
  * Oracle uses CASCADE CONSTRAINT in DROP TABLE and has no native enums,
@@ -78,14 +88,14 @@ export class OracleSchemaGenerator extends SchemaGenerator {
78
88
  catch (e) {
79
89
  if (e.code === 'ORA-01017' && this.config.get('user') !== this.helper.getManagementDbName()) {
80
90
  this.config.set('user', this.helper.getManagementDbName());
81
- await this.driver.reconnect();
91
+ await this.driver.reconnect({ skipOnConnect: true });
82
92
  const result = await this.ensureDatabase();
83
93
  // Restore connection to the original user (createDatabase does this
84
94
  // when the user doesn't exist, but we must handle the case where
85
95
  // the user already exists and ensureDatabase returned early)
86
96
  if (this.config.get('user') !== dbName) {
87
97
  this.config.set('user', dbName);
88
- await this.driver.reconnect();
98
+ await this.driver.reconnect({ skipOnConnect: true });
89
99
  }
90
100
  return result;
91
101
  }
@@ -94,8 +104,8 @@ export class OracleSchemaGenerator extends SchemaGenerator {
94
104
  this.lastEnsuredDatabase = dbName;
95
105
  if (!exists) {
96
106
  this.config.set('user', this.helper.getManagementDbName());
97
- await this.driver.reconnect();
98
- await this.createDatabase(dbName);
107
+ await this.driver.reconnect({ skipOnConnect: true });
108
+ await this.createDatabase(dbName, { skipOnConnect: true });
99
109
  if (options?.create) {
100
110
  await this.create(options);
101
111
  }
@@ -125,7 +135,7 @@ export class OracleSchemaGenerator extends SchemaGenerator {
125
135
  }
126
136
  const originalUser = this.getOriginalUser();
127
137
  this.config.set('user', this.helper.getManagementDbName());
128
- await this.driver.reconnect();
138
+ await this.driver.reconnect({ skipOnConnect: true });
129
139
  return originalUser;
130
140
  }
131
141
  /**
@@ -136,7 +146,7 @@ export class OracleSchemaGenerator extends SchemaGenerator {
136
146
  return;
137
147
  }
138
148
  this.config.set('user', originalUser);
139
- await this.driver.reconnect();
149
+ await this.driver.reconnect({ skipOnConnect: true });
140
150
  }
141
151
  /**
142
152
  * Grants DBA (or fallback individual privileges) to the main user.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/oracledb",
3
- "version": "7.2.0-dev.3",
3
+ "version": "7.2.0-dev.4",
4
4
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, MariaDB, PostgreSQL, SQLite, MSSQL and Oracle databases.",
5
5
  "keywords": [
6
6
  "data-mapper",
@@ -42,15 +42,15 @@
42
42
  "copy": "node ../../scripts/copy.mjs"
43
43
  },
44
44
  "dependencies": {
45
- "@mikro-orm/sql": "7.2.0-dev.3",
45
+ "@mikro-orm/sql": "7.2.0-dev.4",
46
46
  "kysely": "0.29.4",
47
47
  "oracledb": "7.0.1"
48
48
  },
49
49
  "devDependencies": {
50
- "@mikro-orm/core": "^7.1.7"
50
+ "@mikro-orm/core": "^7.1.11"
51
51
  },
52
52
  "peerDependencies": {
53
- "@mikro-orm/core": "7.2.0-dev.3"
53
+ "@mikro-orm/core": "7.2.0-dev.4"
54
54
  },
55
55
  "engines": {
56
56
  "node": ">= 22.17.0"