@mikro-orm/core 7.2.0-dev.9 → 7.2.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.
- package/EntityManager.d.ts +41 -7
- package/EntityManager.js +225 -45
- package/MikroORM.js +3 -0
- package/README.md +1 -0
- package/connections/Connection.d.ts +3 -1
- package/drivers/DatabaseDriver.d.ts +14 -5
- package/drivers/DatabaseDriver.js +151 -52
- package/drivers/IDatabaseDriver.d.ts +1 -0
- package/entity/Collection.js +4 -2
- package/entity/EntityFactory.js +6 -0
- package/entity/EntityLoader.d.ts +7 -1
- package/entity/EntityLoader.js +46 -11
- package/entity/EntityRepository.d.ts +4 -5
- package/entity/EntityRepository.js +7 -2
- package/entity/defineEntity.d.ts +48 -14
- package/entity/defineEntity.js +32 -1
- package/enums.d.ts +5 -1
- package/enums.js +2 -0
- package/errors.d.ts +36 -0
- package/errors.js +90 -0
- package/events/EventManager.js +6 -3
- package/exceptions.d.ts +5 -0
- package/exceptions.js +5 -0
- package/hydration/ObjectHydrator.d.ts +2 -0
- package/hydration/ObjectHydrator.js +12 -8
- package/index.d.ts +1 -1
- package/metadata/MetadataDiscovery.d.ts +3 -0
- package/metadata/MetadataDiscovery.js +127 -17
- package/metadata/MetadataStorage.js +17 -1
- package/metadata/types.d.ts +19 -3
- package/package.json +1 -1
- package/platforms/Platform.d.ts +22 -3
- package/platforms/Platform.js +59 -1
- package/types/BigIntType.d.ts +1 -0
- package/types/BigIntType.js +23 -0
- package/types/DateTimeType.d.ts +1 -0
- package/types/DateTimeType.js +8 -0
- package/types/StringType.d.ts +14 -3
- package/types/StringType.js +34 -4
- package/types/TextType.d.ts +2 -4
- package/types/TextType.js +2 -8
- package/types/Type.d.ts +11 -0
- package/types/Type.js +4 -4
- package/types/index.d.ts +2 -2
- package/typings.d.ts +56 -2
- package/typings.js +24 -1
- package/unit-of-work/ChangeSetPersister.js +17 -13
- package/unit-of-work/UnitOfWork.js +11 -4
- package/utils/Configuration.d.ts +15 -1
- package/utils/Configuration.js +11 -1
- package/utils/Cursor.d.ts +2 -0
- package/utils/Cursor.js +43 -33
- package/utils/DataloaderUtils.js +2 -1
- package/utils/EntityComparator.d.ts +2 -0
- package/utils/EntityComparator.js +7 -3
- package/utils/QueryHelper.d.ts +12 -0
- package/utils/QueryHelper.js +75 -4
- package/utils/RawQueryFragment.d.ts +6 -0
- package/utils/RawQueryFragment.js +15 -6
- package/utils/TransactionManager.js +1 -1
- package/utils/Utils.d.ts +14 -2
- package/utils/Utils.js +31 -4
- package/utils/env-vars.js +1 -0
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/rls-utils.d.ts +35 -0
- package/utils/rls-utils.js +97 -0
- package/utils/upsert-utils.d.ts +9 -1
- package/utils/upsert-utils.js +26 -3
package/EntityManager.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { EntityLoader, type EntityLoaderOptions } from './entity/EntityLoader.js
|
|
|
7
7
|
import { Reference } from './entity/Reference.js';
|
|
8
8
|
import { UnitOfWork } from './unit-of-work/UnitOfWork.js';
|
|
9
9
|
import type { CountByOptions, CountOptions, DeleteOptions, FilterOptions, FindAllOptions, FindByCursorOptions, FindOneOptions, FindOneOrFailOptions, FindOptions, GetReferenceOptions, IDatabaseDriver, LockOptions, NativeInsertUpdateOptions, StreamOptions, UpdateOptions, UpsertManyOptions, UpsertOptions } from './drivers/IDatabaseDriver.js';
|
|
10
|
-
import type { AnyString, ArrayElement, AutoPath, ConnectionType, Dictionary, EntityClass, EntityData, EntityDictionary, EntityDTO, EntityKey, EntityMetadata, EntityName, FilterDef, FilterQuery, FromEntityType, GetRepository, IHydrator, IsSubset, Loaded, MergeLoaded, MergeSelected, ObjectQuery, PopulateOptions, Primary, Ref, RequiredEntityData, RoutineArgs, RoutineReturn, UnboxArray, IndexFilterQuery, WithUsingOptions } from './typings.js';
|
|
10
|
+
import type { AnyString, ArrayElement, AutoPath, ConnectionType, Dictionary, EntityClass, EntityData, EntityDictionary, EntityDTO, EntityKey, EntityMetadata, EntityName, FilterDef, FilterQuery, FromEntityType, GetRepository, IHydrator, IsSubset, Loaded, MergeLoaded, MergeSelected, ObjectQuery, PopulateOptions, Primary, Ref, RequiredEntityData, RoutineArgs, RoutineReturn, SessionContext, UnboxArray, IndexFilterQuery, WithUsingOptions } from './typings.js';
|
|
11
11
|
import type { Routine } from './metadata/Routine.js';
|
|
12
12
|
import { FlushMode, LockMode, PopulatePath, type TransactionOptions } from './enums.js';
|
|
13
13
|
import type { MetadataStorage } from './metadata/MetadataStorage.js';
|
|
@@ -95,7 +95,7 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
95
95
|
/**
|
|
96
96
|
* Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
|
|
97
97
|
*/
|
|
98
|
-
addFilter<T extends EntityName | readonly EntityName[]>(options: FilterDef<T>): void;
|
|
98
|
+
addFilter<T extends EntityName | readonly EntityName[]>(options: Omit<FilterDef<T>, 'rls'>): void;
|
|
99
99
|
/**
|
|
100
100
|
* Sets filter parameter values globally inside context defined by this entity manager.
|
|
101
101
|
* If you want to set shared value for all contexts, be sure to use the root entity manager.
|
|
@@ -105,6 +105,32 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
105
105
|
* Returns filter parameters for given filter set in this context.
|
|
106
106
|
*/
|
|
107
107
|
getFilterParams<T extends Dictionary = Dictionary>(name: string): T;
|
|
108
|
+
/**
|
|
109
|
+
* Sets the database session context (row level security) for this entity manager. Session variables are merged
|
|
110
|
+
* with any previously set ones, while the role is replaced when one is provided. The variables are applied via `set_config()` and are
|
|
111
|
+
* typically referenced by RLS policies through `current_setting()`.
|
|
112
|
+
*/
|
|
113
|
+
setSessionContext(context: SessionContext): void;
|
|
114
|
+
private validateSessionContextStaging;
|
|
115
|
+
/** Merges into this exact instance — `fork()` must bypass context resolution to target the new fork. */
|
|
116
|
+
private mergeSessionContext;
|
|
117
|
+
/**
|
|
118
|
+
* Returns the database session context (row level security) set for this entity manager, or `undefined` if none.
|
|
119
|
+
*/
|
|
120
|
+
getSessionContext(): SessionContext | undefined;
|
|
121
|
+
/**
|
|
122
|
+
* Clears the database session context, since `setSessionContext()` only ever merges variables and updates the role.
|
|
123
|
+
*/
|
|
124
|
+
clearSessionContext(): void;
|
|
125
|
+
/** @internal session context to apply on `begin()` under the `'transaction'` strategy (`undefined` otherwise). */
|
|
126
|
+
getTransactionSessionContext(): SessionContext | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* Wraps a driver call in a short implicit transaction when the session context needs to apply, so `set local`
|
|
129
|
+
* takes effect. Resolves to a plain call when already inside a transaction or when no session context is set.
|
|
130
|
+
*
|
|
131
|
+
* @internal
|
|
132
|
+
*/
|
|
133
|
+
withSessionContext<T>(ctx: Transaction | undefined, cb: (ctx?: Transaction) => Promise<T>): Promise<T>;
|
|
108
134
|
/**
|
|
109
135
|
* Sets logger context for this entity manager.
|
|
110
136
|
*/
|
|
@@ -375,12 +401,10 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
375
401
|
*/
|
|
376
402
|
nativeDelete<Entity extends object>(entityName: EntityName<Entity>, where: FilterQuery<NoInfer<Entity>>, options?: DeleteOptions<Entity>): Promise<number>;
|
|
377
403
|
/**
|
|
378
|
-
* Maps raw database result to an entity and merges it to this EntityManager.
|
|
404
|
+
* Maps raw database result to an entity and merges it to this EntityManager by default.
|
|
405
|
+
* Use `disableIdentityMap` to return an isolated entity without affecting the current context.
|
|
379
406
|
*/
|
|
380
|
-
map<Entity extends object>(entityName: EntityName<Entity>, result: EntityDictionary<Entity>, options?:
|
|
381
|
-
schema?: string;
|
|
382
|
-
mapped?: boolean;
|
|
383
|
-
}): Entity;
|
|
407
|
+
map<Entity extends object>(entityName: EntityName<Entity>, result: EntityDictionary<Entity>, options?: MapOptions): Entity;
|
|
384
408
|
/**
|
|
385
409
|
* Merges given entity to this EntityManager so it becomes managed. You can force refreshing of existing entities
|
|
386
410
|
* via second parameter. By default, it will return already loaded entities without modifying them.
|
|
@@ -667,6 +691,14 @@ export interface CreateOptions<Convert extends boolean> {
|
|
|
667
691
|
*/
|
|
668
692
|
processOnCreateHooksEarly?: boolean;
|
|
669
693
|
}
|
|
694
|
+
export interface MapOptions {
|
|
695
|
+
/** schema to use when mapping the entity */
|
|
696
|
+
schema?: string;
|
|
697
|
+
/** set to true when the result is already mapped to entity property names */
|
|
698
|
+
mapped?: boolean;
|
|
699
|
+
/** map the entity in an isolated context without adding it to the identity map */
|
|
700
|
+
disableIdentityMap?: boolean;
|
|
701
|
+
}
|
|
670
702
|
export interface MergeOptions {
|
|
671
703
|
refresh?: boolean;
|
|
672
704
|
convertCustomTypes?: boolean;
|
|
@@ -694,6 +726,8 @@ export interface ForkOptions {
|
|
|
694
726
|
keepTransactionContext?: boolean;
|
|
695
727
|
/** default schema to use for this fork */
|
|
696
728
|
schema?: string;
|
|
729
|
+
/** database session context (row level security) for this fork; inherited from the parent when not set */
|
|
730
|
+
session?: SessionContext;
|
|
697
731
|
/** default logger context, can be overridden via {@apilink FindOptions} */
|
|
698
732
|
loggerContext?: Dictionary;
|
|
699
733
|
/**
|
package/EntityManager.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { getOnConflictReturningFields, getWhereCondition, resetUntouchedCollections } from './utils/upsert-utils.js';
|
|
1
|
+
import { getOnConflictReturningFields, getOnCreateGeneratedFields, getWhereCondition, resetUntouchedCollections, } from './utils/upsert-utils.js';
|
|
2
|
+
import { computeRemovedRlsVariables, computeRlsFilterVariables, findRlsFilterDefs } from './utils/rls-utils.js';
|
|
2
3
|
import { Utils } from './utils/Utils.js';
|
|
3
4
|
import { Cursor } from './utils/Cursor.js';
|
|
4
5
|
import { QueryHelper } from './utils/QueryHelper.js';
|
|
@@ -15,7 +16,7 @@ import { UnitOfWork } from './unit-of-work/UnitOfWork.js';
|
|
|
15
16
|
import { EventType, FlushMode, LoadStrategy, LockMode, PopulateHint, PopulatePath, QueryFlag, ReferenceKind, SCALAR_TYPES, } from './enums.js';
|
|
16
17
|
import { EventManager } from './events/EventManager.js';
|
|
17
18
|
import { TransactionEventBroadcaster } from './events/TransactionEventBroadcaster.js';
|
|
18
|
-
import { OptimisticLockError, ValidationError } from './errors.js';
|
|
19
|
+
import { MetadataError, OptimisticLockError, ValidationError } from './errors.js';
|
|
19
20
|
import { applyPopulateHints, getLoadingStrategy } from './entity/utils.js';
|
|
20
21
|
import { TransactionManager } from './utils/TransactionManager.js';
|
|
21
22
|
/**
|
|
@@ -44,6 +45,7 @@ export class EntityManager {
|
|
|
44
45
|
#resultCache;
|
|
45
46
|
#filters = {};
|
|
46
47
|
#filterParams = {};
|
|
48
|
+
#sessionContext;
|
|
47
49
|
loggerContext;
|
|
48
50
|
#transactionContext;
|
|
49
51
|
#disableTransactions;
|
|
@@ -199,6 +201,11 @@ export class EntityManager {
|
|
|
199
201
|
*/
|
|
200
202
|
async *stream(entityName, options = {}) {
|
|
201
203
|
const em = this.getContext();
|
|
204
|
+
// a stream never opens the implicit session-context transaction, so under the 'transaction' strategy the staged
|
|
205
|
+
// context would silently never apply outside an ambient transaction and other tenants' rows would leak — fail closed
|
|
206
|
+
if (!options.ctx && !em.#transactionContext && em.getTransactionSessionContext()) {
|
|
207
|
+
throw ValidationError.sessionContextStreamRequiresTransaction();
|
|
208
|
+
}
|
|
202
209
|
options = em.prepareOptions(options);
|
|
203
210
|
options.strategy = 'joined';
|
|
204
211
|
await em.tryFlush(entityName, options);
|
|
@@ -260,7 +267,15 @@ export class EntityManager {
|
|
|
260
267
|
addFilter(options) {
|
|
261
268
|
options = { ...options };
|
|
262
269
|
if (options.entity) {
|
|
263
|
-
options.entity = Utils.asArray(options.entity).map(n => Utils.
|
|
270
|
+
options.entity = Utils.asArray(options.entity).map(n => Utils.classOrName(n));
|
|
271
|
+
}
|
|
272
|
+
// runtime-registered filters are never part of entity metadata, so no policy can be generated for them — whether or
|
|
273
|
+
// not an `entity` was scoped, `rls` is only valid on filters declared in metadata via `@Filter()`. `rls` is excluded
|
|
274
|
+
// from the type above so TS users fail at compile time; the runtime guard still covers JS callers using `as any`
|
|
275
|
+
if (options.rls) {
|
|
276
|
+
throw options.entity
|
|
277
|
+
? MetadataError.rlsFilterCannotBeRegisteredAtRuntime(options.name)
|
|
278
|
+
: MetadataError.rlsFilterMustBeEntityScoped(options.name);
|
|
264
279
|
}
|
|
265
280
|
options.default ??= true;
|
|
266
281
|
this.getContext(false).#filters[options.name] = options;
|
|
@@ -270,7 +285,37 @@ export class EntityManager {
|
|
|
270
285
|
* If you want to set shared value for all contexts, be sure to use the root entity manager.
|
|
271
286
|
*/
|
|
272
287
|
setFilterParams(name, args) {
|
|
273
|
-
this.getContext()
|
|
288
|
+
const em = this.getContext();
|
|
289
|
+
// `rls` filters mirror their params as session variables, so the matching DB policies see the same values;
|
|
290
|
+
// the same filter name can be declared on multiple entities, so stage the union across all `rls`-flagged defs
|
|
291
|
+
const filters = findRlsFilterDefs(em.metadata, name);
|
|
292
|
+
if (filters.length === 0) {
|
|
293
|
+
em.#filterParams[name] = args;
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
// fail before storing the params and pruning stale variables below, so an invalid staging attempt leaves both
|
|
297
|
+
// the filter params and the session context untouched
|
|
298
|
+
em.validateSessionContextStaging();
|
|
299
|
+
const variables = computeRlsFilterVariables(filters, args);
|
|
300
|
+
const previousArgs = em.#filterParams[name];
|
|
301
|
+
em.#filterParams[name] = args;
|
|
302
|
+
// this call replaces the filter's params, so drop the exact variables a previous call for this filter staged but
|
|
303
|
+
// this one no longer sets (unless another filter's current params still stage them)
|
|
304
|
+
const staged = em.#sessionContext?.variables;
|
|
305
|
+
if (staged && previousArgs) {
|
|
306
|
+
for (const key of computeRemovedRlsVariables(em.metadata, name, filters, previousArgs, variables, em.#filterParams)) {
|
|
307
|
+
delete staged[key];
|
|
308
|
+
}
|
|
309
|
+
// pruning may have emptied the whole context — drop it, so it does not keep forcing the implicit
|
|
310
|
+
// transaction wrap (and a distinct cache key) while carrying no variables
|
|
311
|
+
if (Object.keys(staged).length === 0 && !em.#sessionContext.role) {
|
|
312
|
+
em.#sessionContext = undefined;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// an empty context would still switch on the implicit transaction wrapping
|
|
316
|
+
if (Object.keys(variables).length > 0) {
|
|
317
|
+
em.mergeSessionContext({ variables });
|
|
318
|
+
}
|
|
274
319
|
}
|
|
275
320
|
/**
|
|
276
321
|
* Returns filter parameters for given filter set in this context.
|
|
@@ -278,6 +323,90 @@ export class EntityManager {
|
|
|
278
323
|
getFilterParams(name) {
|
|
279
324
|
return this.getContext().#filterParams[name];
|
|
280
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* Sets the database session context (row level security) for this entity manager. Session variables are merged
|
|
328
|
+
* with any previously set ones, while the role is replaced when one is provided. The variables are applied via `set_config()` and are
|
|
329
|
+
* typically referenced by RLS policies through `current_setting()`.
|
|
330
|
+
*/
|
|
331
|
+
setSessionContext(context) {
|
|
332
|
+
// validate the global context like `setFilterParams` — a tenant context set on the global EM
|
|
333
|
+
// would be silently inherited by every later fork
|
|
334
|
+
this.getContext().mergeSessionContext(context);
|
|
335
|
+
}
|
|
336
|
+
validateSessionContextStaging() {
|
|
337
|
+
if (!this.getPlatform().supportsRowLevelSecurity()) {
|
|
338
|
+
throw ValidationError.sessionContextNotSupported();
|
|
339
|
+
}
|
|
340
|
+
// staging inside an open transaction is inert under both strategies (the 'transaction' context is only emitted at
|
|
341
|
+
// top-level begin; the 'connection' context was applied when the pinned connection was reserved), while
|
|
342
|
+
// `getSessionContext()` would still claim it is set — fail closed regardless of strategy
|
|
343
|
+
if (this.#transactionContext) {
|
|
344
|
+
throw ValidationError.sessionContextInsideTransaction();
|
|
345
|
+
}
|
|
346
|
+
if (this.config.get('sessionContext') === 'transaction') {
|
|
347
|
+
// the context is applied on transaction begin, so without implicit transactions writes run untransacted and
|
|
348
|
+
// silently bypass the policies — fail closed instead of leaking a base-role write
|
|
349
|
+
if (this.config.get('implicitTransactions') === false) {
|
|
350
|
+
throw ValidationError.sessionContextRequiresImplicitTransactions();
|
|
351
|
+
}
|
|
352
|
+
// same rationale for disabled transactions (config or fork option): the UoW flush would run untransacted
|
|
353
|
+
// and skip the context while reads still get the per-statement wrap — fail closed on the asymmetry
|
|
354
|
+
if (this.#disableTransactions) {
|
|
355
|
+
throw ValidationError.sessionContextWithDisabledTransactions();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/** Merges into this exact instance — `fork()` must bypass context resolution to target the new fork. */
|
|
360
|
+
mergeSessionContext(context) {
|
|
361
|
+
this.validateSessionContextStaging();
|
|
362
|
+
const current = this.#sessionContext;
|
|
363
|
+
const merged = {
|
|
364
|
+
variables: { ...current?.variables, ...context.variables },
|
|
365
|
+
role: context.role ?? current?.role,
|
|
366
|
+
};
|
|
367
|
+
// normalize an empty context away — it would still force the implicit transaction wrap and a distinct cache key
|
|
368
|
+
this.#sessionContext = Object.keys(merged.variables).length > 0 || merged.role ? merged : undefined;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Returns the database session context (row level security) set for this entity manager, or `undefined` if none.
|
|
372
|
+
*/
|
|
373
|
+
getSessionContext() {
|
|
374
|
+
return this.getContext(false).#sessionContext;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Clears the database session context, since `setSessionContext()` only ever merges variables and updates the role.
|
|
378
|
+
*/
|
|
379
|
+
clearSessionContext() {
|
|
380
|
+
const em = this.getContext(false);
|
|
381
|
+
// clearing inside an open transaction would be as inert (and cache-poisoning) as staging there — fail closed too,
|
|
382
|
+
// under both strategies (the 'connection' pinned connection was already reserved with the previous context)
|
|
383
|
+
if (em.#sessionContext && em.#transactionContext) {
|
|
384
|
+
throw ValidationError.sessionContextInsideTransaction('clear');
|
|
385
|
+
}
|
|
386
|
+
em.#sessionContext = undefined;
|
|
387
|
+
}
|
|
388
|
+
/** @internal session context to apply on `begin()` under the `'transaction'` strategy (`undefined` otherwise). */
|
|
389
|
+
getTransactionSessionContext() {
|
|
390
|
+
const em = this.getContext(false);
|
|
391
|
+
if (!em.#sessionContext || em.config.get('sessionContext') !== 'transaction') {
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
394
|
+
return em.#sessionContext;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Wraps a driver call in a short implicit transaction when the session context needs to apply, so `set local`
|
|
398
|
+
* takes effect. Resolves to a plain call when already inside a transaction or when no session context is set.
|
|
399
|
+
*
|
|
400
|
+
* @internal
|
|
401
|
+
*/
|
|
402
|
+
async withSessionContext(ctx, cb) {
|
|
403
|
+
const em = this.getContext(false);
|
|
404
|
+
const sessionContext = ctx ? undefined : em.getTransactionSessionContext();
|
|
405
|
+
if (!sessionContext) {
|
|
406
|
+
return cb(ctx);
|
|
407
|
+
}
|
|
408
|
+
return em.getConnection('write').transactional(trx => cb(trx), { sessionContext, loggerContext: em.loggerContext });
|
|
409
|
+
}
|
|
281
410
|
/**
|
|
282
411
|
* Sets logger context for this entity manager.
|
|
283
412
|
*/
|
|
@@ -383,6 +512,7 @@ export class EntityManager {
|
|
|
383
512
|
filters,
|
|
384
513
|
populate: hint.children,
|
|
385
514
|
populateWhere: PopulateHint.ALL,
|
|
515
|
+
populateFilter: undefined,
|
|
386
516
|
});
|
|
387
517
|
if (Utils.hasObjectKeys(where)) {
|
|
388
518
|
ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
|
|
@@ -793,6 +923,7 @@ export class EntityManager {
|
|
|
793
923
|
}
|
|
794
924
|
const meta = this.metadata.get(entityName);
|
|
795
925
|
const convertCustomTypes = !Utils.isEntity(data);
|
|
926
|
+
let generatedFields = [];
|
|
796
927
|
if (Utils.isEntity(data)) {
|
|
797
928
|
entity = data;
|
|
798
929
|
if (helper(entity).__managed && helper(entity).__em === em && !this.config.get('upsertManaged')) {
|
|
@@ -800,6 +931,7 @@ export class EntityManager {
|
|
|
800
931
|
return entity;
|
|
801
932
|
}
|
|
802
933
|
where = helper(entity).getPrimaryKey();
|
|
934
|
+
generatedFields = getOnCreateGeneratedFields(meta, entity);
|
|
803
935
|
em.#entityFactory.assignDefaultValues(entity, meta);
|
|
804
936
|
data = em.#comparator.prepareEntity(entity);
|
|
805
937
|
}
|
|
@@ -812,6 +944,7 @@ export class EntityManager {
|
|
|
812
944
|
return em.assign(exists, data);
|
|
813
945
|
}
|
|
814
946
|
}
|
|
947
|
+
generatedFields = getOnCreateGeneratedFields(meta, data);
|
|
815
948
|
em.#entityFactory.assignDefaultValues(data, meta, true);
|
|
816
949
|
for (const key of Object.keys(data)) {
|
|
817
950
|
const prop = meta.properties[key];
|
|
@@ -820,6 +953,10 @@ export class EntityManager {
|
|
|
820
953
|
}
|
|
821
954
|
}
|
|
822
955
|
}
|
|
956
|
+
// `onCreate` generated values are for the insert clause only, they must not overwrite an existing row
|
|
957
|
+
if (generatedFields.length > 0 && !options.onConflictMergeFields) {
|
|
958
|
+
options.onConflictExcludeFields = [...(options.onConflictExcludeFields ?? []), ...generatedFields];
|
|
959
|
+
}
|
|
823
960
|
where = getWhereCondition(meta, options.onConflictFields, data, where).where;
|
|
824
961
|
data = QueryHelper.processObjectParams(data);
|
|
825
962
|
validateParams(data, 'insert data');
|
|
@@ -829,12 +966,12 @@ export class EntityManager {
|
|
|
829
966
|
data = em.#comparator.prepareEntity(entity);
|
|
830
967
|
}
|
|
831
968
|
}
|
|
832
|
-
const ret = await em.driver.nativeUpdate(entityName, where, data, {
|
|
833
|
-
ctx: em.#transactionContext,
|
|
969
|
+
const ret = await em.withSessionContext(options.ctx ?? em.#transactionContext, ctx => em.driver.nativeUpdate(entityName, where, data, {
|
|
834
970
|
upsert: true,
|
|
835
971
|
convertCustomTypes,
|
|
836
972
|
...options,
|
|
837
|
-
|
|
973
|
+
ctx,
|
|
974
|
+
}));
|
|
838
975
|
em.#unitOfWork.getChangeSetPersister().mapReturnedValues(entity, data, ret.row, meta, true);
|
|
839
976
|
entity ??= em.#entityFactory.create(entityName, data, {
|
|
840
977
|
refresh: true,
|
|
@@ -866,13 +1003,13 @@ export class EntityManager {
|
|
|
866
1003
|
where[meta.primaryKeys[0]] = ret.insertId;
|
|
867
1004
|
}
|
|
868
1005
|
}
|
|
869
|
-
const data2 = await this.driver.findOne(meta.class, where, {
|
|
1006
|
+
const data2 = await em.withSessionContext(options.ctx ?? em.#transactionContext, ctx => this.driver.findOne(meta.class, where, {
|
|
870
1007
|
fields: returning.concat(...(options.onConflictMergeFields ?? [])),
|
|
871
|
-
ctx
|
|
1008
|
+
ctx,
|
|
872
1009
|
convertCustomTypes: true,
|
|
873
1010
|
connectionType: 'write',
|
|
874
1011
|
schema: options.schema,
|
|
875
|
-
});
|
|
1012
|
+
}));
|
|
876
1013
|
em.getHydrator().hydrate(entity, meta, data2, em.#entityFactory, 'full', false, true);
|
|
877
1014
|
}
|
|
878
1015
|
// recompute the data as there might be some values missing (e.g. those with db column defaults)
|
|
@@ -939,6 +1076,7 @@ export class EntityManager {
|
|
|
939
1076
|
}
|
|
940
1077
|
const meta = this.metadata.get(entityName);
|
|
941
1078
|
const convertCustomTypes = !Utils.isEntity(data[0]);
|
|
1079
|
+
const generatedFields = new Set();
|
|
942
1080
|
const allData = [];
|
|
943
1081
|
const allWhere = [];
|
|
944
1082
|
const entities = new Map();
|
|
@@ -956,6 +1094,7 @@ export class EntityManager {
|
|
|
956
1094
|
continue;
|
|
957
1095
|
}
|
|
958
1096
|
where = helper(entity).getPrimaryKey();
|
|
1097
|
+
getOnCreateGeneratedFields(meta, entity).forEach(field => generatedFields.add(field));
|
|
959
1098
|
em.#entityFactory.assignDefaultValues(entity, meta);
|
|
960
1099
|
entitiesByAllDataIdx.set(allData.length, entity);
|
|
961
1100
|
row = em.#comparator.prepareEntity(entity);
|
|
@@ -972,6 +1111,7 @@ export class EntityManager {
|
|
|
972
1111
|
continue;
|
|
973
1112
|
}
|
|
974
1113
|
}
|
|
1114
|
+
getOnCreateGeneratedFields(meta, row).forEach(field => generatedFields.add(field));
|
|
975
1115
|
em.#entityFactory.assignDefaultValues(row, meta, true);
|
|
976
1116
|
for (const key of Object.keys(row)) {
|
|
977
1117
|
const prop = meta.properties[key];
|
|
@@ -1000,6 +1140,10 @@ export class EntityManager {
|
|
|
1000
1140
|
if (entities.size === data.length) {
|
|
1001
1141
|
return [...entities.keys()];
|
|
1002
1142
|
}
|
|
1143
|
+
// `onCreate` generated values are for the insert clause only, they must not overwrite existing rows
|
|
1144
|
+
if (generatedFields.size > 0 && !options.onConflictMergeFields) {
|
|
1145
|
+
options.onConflictExcludeFields = [...(options.onConflictExcludeFields ?? []), ...generatedFields];
|
|
1146
|
+
}
|
|
1003
1147
|
if (em.eventManager.hasListeners(EventType.beforeUpsert, meta)) {
|
|
1004
1148
|
for (const dto of data) {
|
|
1005
1149
|
const entity = entitiesByData.get(dto) ?? dto;
|
|
@@ -1009,12 +1153,12 @@ export class EntityManager {
|
|
|
1009
1153
|
allData[idx] = em.#comparator.prepareEntity(entity);
|
|
1010
1154
|
}
|
|
1011
1155
|
}
|
|
1012
|
-
const res = await em.driver.nativeUpdateMany(entityName, allWhere, allData, {
|
|
1013
|
-
ctx: em.#transactionContext,
|
|
1156
|
+
const res = await em.withSessionContext(options.ctx ?? em.#transactionContext, ctx => em.driver.nativeUpdateMany(entityName, allWhere, allData, {
|
|
1014
1157
|
upsert: true,
|
|
1015
1158
|
convertCustomTypes,
|
|
1016
1159
|
...options,
|
|
1017
|
-
|
|
1160
|
+
ctx,
|
|
1161
|
+
}));
|
|
1018
1162
|
entities.clear();
|
|
1019
1163
|
entitiesByData.clear();
|
|
1020
1164
|
const loadPK = new Map();
|
|
@@ -1057,16 +1201,16 @@ export class EntityManager {
|
|
|
1057
1201
|
where.$or[idx][prop] = item[prop];
|
|
1058
1202
|
});
|
|
1059
1203
|
});
|
|
1060
|
-
const data2 = await this.driver.find(meta.class, where, {
|
|
1204
|
+
const data2 = await em.withSessionContext(options.ctx ?? em.#transactionContext, ctx => this.driver.find(meta.class, where, {
|
|
1061
1205
|
fields: returning
|
|
1062
1206
|
.concat(...add)
|
|
1063
1207
|
.concat(...(Array.isArray(uniqueFields) ? uniqueFields : []))
|
|
1064
1208
|
.concat(...(options.onConflictMergeFields ?? [])),
|
|
1065
|
-
ctx
|
|
1209
|
+
ctx,
|
|
1066
1210
|
convertCustomTypes: true,
|
|
1067
1211
|
connectionType: 'write',
|
|
1068
1212
|
schema: options.schema,
|
|
1069
|
-
});
|
|
1213
|
+
}));
|
|
1070
1214
|
for (const [entity, cond] of loadPK.entries()) {
|
|
1071
1215
|
const row = data2.find(row => {
|
|
1072
1216
|
const tmp = {};
|
|
@@ -1164,6 +1308,7 @@ export class EntityManager {
|
|
|
1164
1308
|
}
|
|
1165
1309
|
const em = this.getContext(false);
|
|
1166
1310
|
em.#transactionContext = await em.getConnection('write').begin({
|
|
1311
|
+
sessionContext: em.getTransactionSessionContext(),
|
|
1167
1312
|
...options,
|
|
1168
1313
|
eventBroadcaster: new TransactionEventBroadcaster(em, { topLevelTransaction: !options.ctx }),
|
|
1169
1314
|
});
|
|
@@ -1231,15 +1376,12 @@ export class EntityManager {
|
|
|
1231
1376
|
const meta = helper(data).__meta;
|
|
1232
1377
|
const payload = em.#comparator.prepareEntity(data);
|
|
1233
1378
|
const cs = new ChangeSet(data, ChangeSetType.CREATE, payload, meta);
|
|
1234
|
-
await em.#unitOfWork.getChangeSetPersister().executeInserts([cs], {
|
|
1379
|
+
await em.withSessionContext(options.ctx ?? em.#transactionContext, ctx => em.#unitOfWork.getChangeSetPersister().executeInserts([cs], { ...options, ctx }));
|
|
1235
1380
|
return cs.getPrimaryKey();
|
|
1236
1381
|
}
|
|
1237
1382
|
data = QueryHelper.processObjectParams(data);
|
|
1238
1383
|
validateParams(data, 'insert data');
|
|
1239
|
-
const res = await em.driver.nativeInsert(entityName, data, {
|
|
1240
|
-
ctx: em.#transactionContext,
|
|
1241
|
-
...options,
|
|
1242
|
-
});
|
|
1384
|
+
const res = await em.withSessionContext(options.ctx ?? em.#transactionContext, ctx => em.driver.nativeInsert(entityName, data, { ...options, ctx }));
|
|
1243
1385
|
return res.insertId;
|
|
1244
1386
|
}
|
|
1245
1387
|
/**
|
|
@@ -1281,10 +1423,7 @@ export class EntityManager {
|
|
|
1281
1423
|
options ??= {};
|
|
1282
1424
|
options = em.prepareOptions(options);
|
|
1283
1425
|
const meta = em.metadata.get(entityName);
|
|
1284
|
-
const res = await em.driver.nativeClone(entityName, where, overrides, {
|
|
1285
|
-
ctx: em.#transactionContext,
|
|
1286
|
-
...options,
|
|
1287
|
-
});
|
|
1426
|
+
const res = await em.withSessionContext(options.ctx ?? em.#transactionContext, ctx => em.driver.nativeClone(entityName, where, overrides, { ...options, ctx }));
|
|
1288
1427
|
const pk = res.insertId ?? res.row?.[meta.primaryKeys[0]];
|
|
1289
1428
|
return em.findOneOrFail(entityName, pk, {
|
|
1290
1429
|
schema: options.schema,
|
|
@@ -1319,15 +1458,12 @@ export class EntityManager {
|
|
|
1319
1458
|
const payload = em.#comparator.prepareEntity(row);
|
|
1320
1459
|
return new ChangeSet(row, ChangeSetType.CREATE, payload, meta);
|
|
1321
1460
|
});
|
|
1322
|
-
await em.#unitOfWork.getChangeSetPersister().executeInserts(css, {
|
|
1461
|
+
await em.withSessionContext(options.ctx ?? em.#transactionContext, ctx => em.#unitOfWork.getChangeSetPersister().executeInserts(css, { ...options, ctx }));
|
|
1323
1462
|
return css.map(cs => cs.getPrimaryKey());
|
|
1324
1463
|
}
|
|
1325
1464
|
data = data.map(row => QueryHelper.processObjectParams(row));
|
|
1326
1465
|
data.forEach(row => validateParams(row, 'insert data'));
|
|
1327
|
-
const res = await em.driver.nativeInsertMany(entityName, data, {
|
|
1328
|
-
ctx: em.#transactionContext,
|
|
1329
|
-
...options,
|
|
1330
|
-
});
|
|
1466
|
+
const res = await em.withSessionContext(options.ctx ?? em.#transactionContext, ctx => em.driver.nativeInsertMany(entityName, data, { ...options, ctx }));
|
|
1331
1467
|
if (res.insertedIds) {
|
|
1332
1468
|
return res.insertedIds;
|
|
1333
1469
|
}
|
|
@@ -1344,11 +1480,11 @@ export class EntityManager {
|
|
|
1344
1480
|
where = await em.processWhere(entityName, where, { ...options, convertCustomTypes: false }, 'update');
|
|
1345
1481
|
validateParams(data, 'update data');
|
|
1346
1482
|
validateParams(where, 'update condition');
|
|
1347
|
-
const res = await em.driver.nativeUpdate(entityName, where, data, {
|
|
1348
|
-
ctx: em.#transactionContext,
|
|
1483
|
+
const res = await em.withSessionContext(options.ctx ?? em.#transactionContext, ctx => em.driver.nativeUpdate(entityName, where, data, {
|
|
1349
1484
|
em,
|
|
1350
1485
|
...options,
|
|
1351
|
-
|
|
1486
|
+
ctx,
|
|
1487
|
+
}));
|
|
1352
1488
|
return res.affectedRows;
|
|
1353
1489
|
}
|
|
1354
1490
|
/**
|
|
@@ -1380,7 +1516,7 @@ export class EntityManager {
|
|
|
1380
1516
|
throw new Error(`Routine '${routine.name}' is not registered in the 'routines' config option.`);
|
|
1381
1517
|
}
|
|
1382
1518
|
const conn = em.driver.getConnection('write');
|
|
1383
|
-
return conn.callRoutine(routine, args,
|
|
1519
|
+
return em.withSessionContext(em.#transactionContext, ctx => conn.callRoutine(routine, args, ctx));
|
|
1384
1520
|
}
|
|
1385
1521
|
/**
|
|
1386
1522
|
* Fires native delete query. Calling this has no side effects on the context (identity map).
|
|
@@ -1391,18 +1527,26 @@ export class EntityManager {
|
|
|
1391
1527
|
await em.processUnionWhere(entityName, options, 'delete');
|
|
1392
1528
|
where = (await em.processWhere(entityName, where, options, 'delete'));
|
|
1393
1529
|
validateParams(where, 'delete condition');
|
|
1394
|
-
const res = await em.driver.nativeDelete(entityName, where, {
|
|
1395
|
-
ctx: em.#transactionContext,
|
|
1530
|
+
const res = await em.withSessionContext(options.ctx ?? em.#transactionContext, ctx => em.driver.nativeDelete(entityName, where, {
|
|
1396
1531
|
em,
|
|
1397
1532
|
...options,
|
|
1398
|
-
|
|
1533
|
+
ctx,
|
|
1534
|
+
}));
|
|
1399
1535
|
return res.affectedRows;
|
|
1400
1536
|
}
|
|
1401
1537
|
/**
|
|
1402
|
-
* Maps raw database result to an entity and merges it to this EntityManager.
|
|
1538
|
+
* Maps raw database result to an entity and merges it to this EntityManager by default.
|
|
1539
|
+
* Use `disableIdentityMap` to return an isolated entity without affecting the current context.
|
|
1403
1540
|
*/
|
|
1404
1541
|
map(entityName, result, options = {}) {
|
|
1405
|
-
|
|
1542
|
+
if (options.disableIdentityMap ?? this.config.get('disableIdentityMap')) {
|
|
1543
|
+
const em = this.getContext(false);
|
|
1544
|
+
const fork = em.fork({ keepTransactionContext: true });
|
|
1545
|
+
const ret = fork.map(entityName, result, { ...options, disableIdentityMap: false });
|
|
1546
|
+
fork.clear();
|
|
1547
|
+
return ret;
|
|
1548
|
+
}
|
|
1549
|
+
const { mapped, disableIdentityMap, ...rest } = options;
|
|
1406
1550
|
const meta = this.metadata.get(entityName);
|
|
1407
1551
|
const data = (mapped ? result : this.driver.mapResult(result, meta));
|
|
1408
1552
|
for (const k of Object.keys(data)) {
|
|
@@ -1526,7 +1670,7 @@ export class EntityManager {
|
|
|
1526
1670
|
if (cached?.data !== undefined) {
|
|
1527
1671
|
return cached.data;
|
|
1528
1672
|
}
|
|
1529
|
-
const count = await em.driver.count(entityName, where, {
|
|
1673
|
+
const count = await em.withSessionContext(options.ctx ?? em.#transactionContext, ctx => em.driver.count(entityName, where, { em, ...options, ctx }));
|
|
1530
1674
|
await em.storeCache(options.cache, cached, () => +count);
|
|
1531
1675
|
return +count;
|
|
1532
1676
|
}
|
|
@@ -1641,8 +1785,9 @@ export class EntityManager {
|
|
|
1641
1785
|
}
|
|
1642
1786
|
// For TPT inheritance, check the entity's own properties, not just the root's
|
|
1643
1787
|
// For STI, meta.properties includes all properties anyway
|
|
1644
|
-
|
|
1645
|
-
|
|
1788
|
+
// use an own-property check so inherited `Object.prototype` members (e.g. `__proto__`, `constructor`) are not treated as populatable
|
|
1789
|
+
const ret = Object.hasOwn(meta.properties, p);
|
|
1790
|
+
if (ret && parts.length > 0) {
|
|
1646
1791
|
return this.canPopulate(meta.properties[p].targetMeta.class, parts.join('.'));
|
|
1647
1792
|
}
|
|
1648
1793
|
return ret;
|
|
@@ -1690,6 +1835,18 @@ export class EntityManager {
|
|
|
1690
1835
|
}
|
|
1691
1836
|
fork.#filters = { ...em.#filters };
|
|
1692
1837
|
fork.#filterParams = Utils.copy(em.#filterParams);
|
|
1838
|
+
if (options.session) {
|
|
1839
|
+
// the fork's session replaces the parent context, but the copied `rls` filter params must stay consistent with
|
|
1840
|
+
// it — re-stage their variables underneath the explicit `session.variables`, which win on any conflict
|
|
1841
|
+
const variables = {};
|
|
1842
|
+
for (const name of Object.keys(fork.#filterParams)) {
|
|
1843
|
+
Object.assign(variables, computeRlsFilterVariables(findRlsFilterDefs(fork.metadata, name), fork.#filterParams[name]));
|
|
1844
|
+
}
|
|
1845
|
+
fork.mergeSessionContext({ ...options.session, variables: { ...variables, ...options.session.variables } });
|
|
1846
|
+
}
|
|
1847
|
+
else if (em.#sessionContext) {
|
|
1848
|
+
fork.#sessionContext = Utils.copy(em.#sessionContext);
|
|
1849
|
+
}
|
|
1693
1850
|
fork.loggerContext = Utils.merge({}, em.loggerContext, options.loggerContext);
|
|
1694
1851
|
fork.#schema = options.schema ?? em.#schema;
|
|
1695
1852
|
fork.signal = options.signal ?? em.signal;
|
|
@@ -2101,7 +2258,19 @@ export class EntityManager {
|
|
|
2101
2258
|
]) {
|
|
2102
2259
|
delete opts[k];
|
|
2103
2260
|
}
|
|
2104
|
-
|
|
2261
|
+
// the table name (plus discriminator value for STI) is stable across builds and processes,
|
|
2262
|
+
// unlike class names, which minifiers can mangle to the same short name for two entities
|
|
2263
|
+
const meta = this.metadata.find(entityName);
|
|
2264
|
+
const entityKey = meta?.tableName
|
|
2265
|
+
? [meta.schema, meta.tableName, meta.discriminatorValue]
|
|
2266
|
+
: Utils.className(entityName);
|
|
2267
|
+
const key = [entityKey, method, opts, where];
|
|
2268
|
+
// session context (row level security) scopes cached rows per tenant/role, avoiding cross-context serves
|
|
2269
|
+
const sessionContext = this.getContext(false).#sessionContext;
|
|
2270
|
+
if (sessionContext) {
|
|
2271
|
+
key.push(sessionContext);
|
|
2272
|
+
}
|
|
2273
|
+
return key;
|
|
2105
2274
|
}
|
|
2106
2275
|
/**
|
|
2107
2276
|
* @internal
|
|
@@ -2112,7 +2281,13 @@ export class EntityManager {
|
|
|
2112
2281
|
return undefined;
|
|
2113
2282
|
}
|
|
2114
2283
|
const em = this.getContext();
|
|
2115
|
-
|
|
2284
|
+
// a named cache key (`config[0]`) discards the computed `key`, which already carries the session context — so
|
|
2285
|
+
// scope it here too, otherwise a fork's rows would be served to another session context under the same name
|
|
2286
|
+
const cacheKey = Array.isArray(config)
|
|
2287
|
+
? em.#sessionContext
|
|
2288
|
+
? `${config[0]}|${JSON.stringify(em.#sessionContext)}`
|
|
2289
|
+
: config[0]
|
|
2290
|
+
: JSON.stringify(key);
|
|
2116
2291
|
const cached = await em.#resultCache.get(cacheKey);
|
|
2117
2292
|
if (!cached) {
|
|
2118
2293
|
return { key: cacheKey, data: cached };
|
|
@@ -2160,7 +2335,12 @@ export class EntityManager {
|
|
|
2160
2335
|
* ```
|
|
2161
2336
|
*/
|
|
2162
2337
|
async clearCache(cacheKey) {
|
|
2163
|
-
|
|
2338
|
+
const em = this.getContext();
|
|
2339
|
+
await em.#resultCache.remove(cacheKey);
|
|
2340
|
+
// named keys are scoped by the session context (see `tryCache`), so clear this context's variant too
|
|
2341
|
+
if (em.#sessionContext) {
|
|
2342
|
+
await em.#resultCache.remove(`${cacheKey}|${JSON.stringify(em.#sessionContext)}`);
|
|
2343
|
+
}
|
|
2164
2344
|
}
|
|
2165
2345
|
/**
|
|
2166
2346
|
* Returns the default schema of this EntityManager. Respects the context, so global EM will give you the contextual schema
|
package/MikroORM.js
CHANGED
|
@@ -2,6 +2,7 @@ import { MetadataDiscovery } from './metadata/MetadataDiscovery.js';
|
|
|
2
2
|
import { MetadataStorage } from './metadata/MetadataStorage.js';
|
|
3
3
|
import { Configuration } from './utils/Configuration.js';
|
|
4
4
|
import { loadEnvironmentVars } from './utils/env-vars.js';
|
|
5
|
+
import { clearRlsFilterDefsCache } from './utils/rls-utils.js';
|
|
5
6
|
import { Utils } from './utils/Utils.js';
|
|
6
7
|
import { colors } from './logging/colors.js';
|
|
7
8
|
async function tryRegisterExtension(name, pkg, extensions) {
|
|
@@ -195,6 +196,8 @@ export class MikroORM {
|
|
|
195
196
|
meta.root = this.#metadata.get(meta.root.class);
|
|
196
197
|
}
|
|
197
198
|
this.#metadata.decorate(this.em);
|
|
199
|
+
// the newly discovered entities may declare `rls` filters the cached lookup was built without
|
|
200
|
+
clearRlsFilterDefsCache(this.#metadata);
|
|
198
201
|
}
|
|
199
202
|
/**
|
|
200
203
|
* Gets the SchemaGenerator.
|
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@ npm install @mikro-orm/mysql # MySQL
|
|
|
24
24
|
npm install @mikro-orm/mariadb # MariaDB
|
|
25
25
|
npm install @mikro-orm/sqlite # SQLite
|
|
26
26
|
npm install @mikro-orm/libsql # libSQL / Turso
|
|
27
|
+
npm install @mikro-orm/sql-js # sql.js (in-memory SQLite in WASM)
|
|
27
28
|
npm install @mikro-orm/mongodb # MongoDB
|
|
28
29
|
npm install @mikro-orm/mssql # MS SQL Server
|
|
29
30
|
npm install @mikro-orm/oracledb # Oracle
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Configuration, type ConnectionOptions } from '../utils/Configuration.js';
|
|
2
2
|
import type { LogContext, Logger } from '../logging/Logger.js';
|
|
3
3
|
import type { MetadataStorage } from '../metadata/MetadataStorage.js';
|
|
4
|
-
import type { ConnectionType, Dictionary, MaybePromise, Primary, RoutineProperty } from '../typings.js';
|
|
4
|
+
import type { ConnectionType, Dictionary, MaybePromise, Primary, RoutineProperty, SessionContext } from '../typings.js';
|
|
5
5
|
import type { Routine } from '../metadata/Routine.js';
|
|
6
6
|
import type { Platform } from '../platforms/Platform.js';
|
|
7
7
|
import type { Type } from '../types/Type.js';
|
|
@@ -66,6 +66,7 @@ export declare abstract class Connection {
|
|
|
66
66
|
ctx?: Transaction;
|
|
67
67
|
eventBroadcaster?: TransactionEventBroadcaster;
|
|
68
68
|
loggerContext?: LogContext;
|
|
69
|
+
sessionContext?: SessionContext;
|
|
69
70
|
}): Promise<T>;
|
|
70
71
|
/** Begins a new database transaction and returns the transaction context. */
|
|
71
72
|
begin(options?: {
|
|
@@ -74,6 +75,7 @@ export declare abstract class Connection {
|
|
|
74
75
|
ctx?: Transaction;
|
|
75
76
|
eventBroadcaster?: TransactionEventBroadcaster;
|
|
76
77
|
loggerContext?: LogContext;
|
|
78
|
+
sessionContext?: SessionContext;
|
|
77
79
|
}): Promise<Transaction>;
|
|
78
80
|
/** Commits the given transaction. */
|
|
79
81
|
commit(ctx: Transaction, eventBroadcaster?: TransactionEventBroadcaster, loggerContext?: LogContext): Promise<void>;
|