@mikro-orm/core 7.1.0-dev.8 → 7.1.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 (57) hide show
  1. package/EntityManager.d.ts +70 -5
  2. package/EntityManager.js +175 -23
  3. package/README.md +2 -1
  4. package/connections/Connection.d.ts +67 -1
  5. package/connections/Connection.js +64 -0
  6. package/drivers/IDatabaseDriver.d.ts +30 -6
  7. package/entity/BaseEntity.d.ts +68 -1
  8. package/entity/BaseEntity.js +18 -0
  9. package/entity/Collection.d.ts +4 -1
  10. package/entity/Collection.js +15 -4
  11. package/entity/EntityAssigner.js +8 -0
  12. package/entity/EntityFactory.js +5 -3
  13. package/entity/EntityLoader.d.ts +8 -1
  14. package/entity/EntityLoader.js +97 -29
  15. package/entity/EntityRepository.d.ts +12 -2
  16. package/entity/EntityRepository.js +12 -0
  17. package/entity/Reference.d.ts +42 -1
  18. package/entity/Reference.js +9 -0
  19. package/entity/defineEntity.d.ts +69 -16
  20. package/entity/defineEntity.js +17 -0
  21. package/entity/utils.js +4 -5
  22. package/enums.d.ts +8 -1
  23. package/index.d.ts +1 -1
  24. package/metadata/EntitySchema.js +18 -0
  25. package/metadata/MetadataDiscovery.d.ts +1 -0
  26. package/metadata/MetadataDiscovery.js +79 -15
  27. package/metadata/MetadataValidator.d.ts +26 -0
  28. package/metadata/MetadataValidator.js +228 -1
  29. package/metadata/Routine.d.ts +74 -0
  30. package/metadata/Routine.js +140 -0
  31. package/metadata/index.d.ts +1 -0
  32. package/metadata/index.js +1 -0
  33. package/metadata/types.d.ts +75 -8
  34. package/package.json +1 -1
  35. package/platforms/Platform.d.ts +13 -3
  36. package/platforms/Platform.js +51 -6
  37. package/serialization/EntitySerializer.js +4 -3
  38. package/typings.d.ts +323 -21
  39. package/typings.js +32 -12
  40. package/unit-of-work/UnitOfWork.js +43 -5
  41. package/utils/AbstractMigrator.d.ts +9 -4
  42. package/utils/AbstractMigrator.js +60 -28
  43. package/utils/AbstractSchemaGenerator.d.ts +1 -1
  44. package/utils/AbstractSchemaGenerator.js +4 -1
  45. package/utils/Configuration.d.ts +30 -0
  46. package/utils/Configuration.js +45 -0
  47. package/utils/DataloaderUtils.d.ts +10 -1
  48. package/utils/DataloaderUtils.js +78 -0
  49. package/utils/EntityComparator.js +1 -1
  50. package/utils/TransactionManager.js +2 -0
  51. package/utils/Utils.js +1 -1
  52. package/utils/index.d.ts +1 -0
  53. package/utils/index.js +1 -0
  54. package/utils/partition-utils.d.ts +17 -0
  55. package/utils/partition-utils.js +79 -0
  56. package/utils/upsert-utils.d.ts +2 -0
  57. package/utils/upsert-utils.js +26 -1
@@ -0,0 +1,74 @@
1
+ import type { RoutineArgsOf, RoutineConfig, RoutineDataAccess, RoutineIgnoreField, RoutineKind, RoutineParamMap, RoutineProperty, RoutineReturnOf, RoutineReturns, RoutineSecurity } from '../typings.js';
2
+ import type { Type } from '../types/Type.js';
3
+ import type { Raw } from '../utils/RawQueryFragment.js';
4
+ /**
5
+ * Stored procedure or function declaration. Register instances via the `routines` config option
6
+ * passed to `MikroORM.init`, then call them through `em.callRoutine(routine, args)`.
7
+ *
8
+ * `TArgs` and `TReturn` are inferred from the literal config, so `em.callRoutine` is fully typed
9
+ * without threading generics through the call site. Reach for {@link Routine.create} when the
10
+ * inferred type is too loose — typically an `object` return that should be a concrete shape
11
+ * rather than `Dictionary`.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const HashUser = new Routine({
16
+ * name: 'hash_user',
17
+ * type: 'function',
18
+ * params: {
19
+ * name: { type: 'varchar(255)' },
20
+ * salt: { type: 'varchar(255)' },
21
+ * },
22
+ * returns: { runtimeType: 'string', columnType: 'char(40)' },
23
+ * body: 'SELECT SHA1(CONCAT(name, salt))',
24
+ * });
25
+ *
26
+ * await MikroORM.init({ entities: [User], routines: [HashUser] });
27
+ *
28
+ * // args typed as `{ name: string; salt: string }`, result typed as `string`:
29
+ * const hash = await em.callRoutine(HashUser, { name: 'jon', salt: 'pepper' });
30
+ * ```
31
+ */
32
+ export declare class Routine<const TConfig extends RoutineConfig = RoutineConfig, TArgs = RoutineArgsOf<TConfig>, TReturn = RoutineReturnOf<TConfig>> {
33
+ readonly name: string;
34
+ readonly type: RoutineKind;
35
+ readonly schema?: string;
36
+ readonly comment?: string;
37
+ readonly security?: RoutineSecurity;
38
+ readonly definer?: string;
39
+ readonly deterministic?: boolean;
40
+ readonly dataAccess?: RoutineDataAccess;
41
+ readonly language?: string;
42
+ readonly body?: string | Raw | ((params: RoutineParamMap<any>, em: any) => string | Raw);
43
+ readonly expression?: string;
44
+ readonly bodyJs?: (params: any) => unknown;
45
+ readonly returns?: RoutineReturns;
46
+ readonly returnCustomType?: Type<unknown>;
47
+ readonly ignoreSchemaChanges?: RoutineIgnoreField[];
48
+ readonly params: RoutineProperty[];
49
+ constructor(config: TConfig);
50
+ /** @internal */
51
+ createParamMappingObject(): Record<string, string>;
52
+ /**
53
+ * Overrides the inferred TArgs/TReturn. Omit a generic to keep inference; pass `never` in the
54
+ * args slot to refine only the return type.
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * interface UserStats { totalOrders: number; lastOrderAt: Date }
59
+ * const GetStats = Routine.create<never, UserStats>({
60
+ * name: 'get_user_stats',
61
+ * type: 'function',
62
+ * params: { user_id: { type: 'int', runtimeType: 'number' } },
63
+ * returns: { runtimeType: 'object', columnType: 'json' },
64
+ * body: '...',
65
+ * });
66
+ * ```
67
+ */
68
+ static create<TArgs = never, TReturn = never, const TConfig extends RoutineConfig = RoutineConfig>(config: TConfig): Routine<TConfig, [
69
+ TArgs
70
+ ] extends [never] ? RoutineArgsOf<TConfig> : TArgs, [
71
+ TReturn
72
+ ] extends [never] ? RoutineReturnOf<TConfig> : TReturn>;
73
+ static is(item: unknown): item is Routine;
74
+ }
@@ -0,0 +1,140 @@
1
+ // Resolve a `Type<unknown> | Constructor<Type<unknown>>` to an instance. Each built-in `Type` has
2
+ // `__mappedType` on its prototype so any constructed instance carries it; a missing brand means a
3
+ // raw constructor that we instantiate.
4
+ const toTypeInstance = (value) => {
5
+ return value.__mappedType ? value : new value();
6
+ };
7
+ /**
8
+ * Stored procedure or function declaration. Register instances via the `routines` config option
9
+ * passed to `MikroORM.init`, then call them through `em.callRoutine(routine, args)`.
10
+ *
11
+ * `TArgs` and `TReturn` are inferred from the literal config, so `em.callRoutine` is fully typed
12
+ * without threading generics through the call site. Reach for {@link Routine.create} when the
13
+ * inferred type is too loose — typically an `object` return that should be a concrete shape
14
+ * rather than `Dictionary`.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const HashUser = new Routine({
19
+ * name: 'hash_user',
20
+ * type: 'function',
21
+ * params: {
22
+ * name: { type: 'varchar(255)' },
23
+ * salt: { type: 'varchar(255)' },
24
+ * },
25
+ * returns: { runtimeType: 'string', columnType: 'char(40)' },
26
+ * body: 'SELECT SHA1(CONCAT(name, salt))',
27
+ * });
28
+ *
29
+ * await MikroORM.init({ entities: [User], routines: [HashUser] });
30
+ *
31
+ * // args typed as `{ name: string; salt: string }`, result typed as `string`:
32
+ * const hash = await em.callRoutine(HashUser, { name: 'jon', salt: 'pepper' });
33
+ * ```
34
+ */
35
+ export class Routine {
36
+ name;
37
+ type;
38
+ schema;
39
+ comment;
40
+ security;
41
+ definer;
42
+ deterministic;
43
+ dataAccess;
44
+ language;
45
+ body;
46
+ expression;
47
+ bodyJs;
48
+ returns;
49
+ returnCustomType;
50
+ ignoreSchemaChanges;
51
+ params;
52
+ constructor(config) {
53
+ this.name = config.name;
54
+ this.type = config.type;
55
+ this.schema = config.schema;
56
+ this.comment = config.comment;
57
+ this.security = config.security;
58
+ this.definer = config.definer;
59
+ this.deterministic = config.deterministic;
60
+ this.dataAccess = config.dataAccess;
61
+ this.language = config.language;
62
+ this.body = config.body;
63
+ this.expression = config.expression;
64
+ this.bodyJs = config.bodyJs;
65
+ this.returns = config.returns;
66
+ this.ignoreSchemaChanges = config.ignoreSchemaChanges;
67
+ this.params = Object.entries(config.params ?? {}).map(([name, opts], index) => {
68
+ const explicitCustom = opts.customType ? toTypeInstance(opts.customType) : undefined;
69
+ const rawType = opts.type;
70
+ let type;
71
+ let customType;
72
+ if (rawType == null || typeof rawType === 'string') {
73
+ type = rawType ?? 'string';
74
+ customType = explicitCustom;
75
+ }
76
+ else {
77
+ // A `Type` instance or constructor at `type` both defines the column (via
78
+ // `getColumnType` at schema-gen time) and acts as the default `customType` for
79
+ // marshalling. An explicit `customType` on the same param overrides the marshalling side.
80
+ const instance = toTypeInstance(rawType);
81
+ type = instance;
82
+ customType = explicitCustom ?? instance;
83
+ }
84
+ return {
85
+ name,
86
+ direction: opts.direction ?? 'in',
87
+ type,
88
+ runtimeType: opts.runtimeType ?? 'any',
89
+ columnTypes: [typeof type === 'string' ? type : ''],
90
+ customType,
91
+ ref: opts.ref,
92
+ length: opts.length,
93
+ precision: opts.precision,
94
+ scale: opts.scale,
95
+ nullable: opts.nullable,
96
+ defaultRaw: opts.defaultRaw,
97
+ index,
98
+ };
99
+ });
100
+ if (config.returns && typeof config.returns === 'object') {
101
+ // `returns: { type: SomeType }` is the canonical scalar-return shape — the Type drives
102
+ // both the column type (via `getColumnType` at schema-gen time) and the marshalling.
103
+ if ('type' in config.returns && config.returns.type) {
104
+ this.returnCustomType = toTypeInstance(config.returns.type);
105
+ }
106
+ else if ('customType' in config.returns && config.returns.customType) {
107
+ this.returnCustomType = toTypeInstance(config.returns.customType);
108
+ }
109
+ }
110
+ }
111
+ /** @internal */
112
+ createParamMappingObject() {
113
+ return this.params.reduce((o, p) => {
114
+ o[p.name] = p.name;
115
+ return o;
116
+ }, {});
117
+ }
118
+ /**
119
+ * Overrides the inferred TArgs/TReturn. Omit a generic to keep inference; pass `never` in the
120
+ * args slot to refine only the return type.
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * interface UserStats { totalOrders: number; lastOrderAt: Date }
125
+ * const GetStats = Routine.create<never, UserStats>({
126
+ * name: 'get_user_stats',
127
+ * type: 'function',
128
+ * params: { user_id: { type: 'int', runtimeType: 'number' } },
129
+ * returns: { runtimeType: 'object', columnType: 'json' },
130
+ * body: '...',
131
+ * });
132
+ * ```
133
+ */
134
+ static create(config) {
135
+ return new Routine(config);
136
+ }
137
+ static is(item) {
138
+ return item instanceof Routine;
139
+ }
140
+ }
@@ -1,5 +1,6 @@
1
1
  export type * from './types.js';
2
2
  export * from './EntitySchema.js';
3
+ export * from './Routine.js';
3
4
  export * from './MetadataDiscovery.js';
4
5
  export * from './MetadataStorage.js';
5
6
  export * from './MetadataProvider.js';
package/metadata/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './EntitySchema.js';
2
+ export * from './Routine.js';
2
3
  export * from './MetadataDiscovery.js';
3
4
  export * from './MetadataStorage.js';
4
5
  export * from './MetadataProvider.js';
@@ -1,9 +1,52 @@
1
- import type { AnyEntity, Constructor, EntityName, AnyString, CheckCallback, GeneratedColumnCallback, FormulaCallback, FilterQuery, Dictionary, AutoPath, EntityClass, IndexCallback, ObjectQuery, Raw, TriggerDef } from '../typings.js';
1
+ import type { AnyEntity, Constructor, EntityName, AnyString, CheckCallback, GeneratedColumnCallback, FormulaCallback, FilterQuery, Dictionary, AutoPath, EntityClass, IndexCallback, ObjectQuery, Raw, SchemaColumns, TriggerDef } from '../typings.js';
2
2
  import type { Cascade, LoadStrategy, DeferMode, QueryOrderMap, EmbeddedPrefixMode } from '../enums.js';
3
3
  import type { Type, types } from '../types/index.js';
4
4
  import type { EntityManager } from '../EntityManager.js';
5
5
  import type { FilterOptions, FindOptions } from '../drivers/IDatabaseDriver.js';
6
6
  import type { SerializeOptions } from '../serialization/EntitySerializer.js';
7
+ /** Supported PostgreSQL partitioning strategies. */
8
+ export type EntityPartitionType = 'hash' | 'list' | 'range';
9
+ /**
10
+ * Partition key expression for PostgreSQL partitioned tables.
11
+ *
12
+ * You can use:
13
+ * - a property name, e.g. `'type'`
14
+ * - a list of property names for composite expressions, e.g. `['tenant', 'createdAt']`
15
+ * - a raw SQL expression string, e.g. `"date_trunc('month', created_at)"`
16
+ * - a callback that receives schema column mappings and returns SQL
17
+ */
18
+ export type EntityPartitionExpression<E = AnyEntity> = (keyof E & string) | readonly (keyof E & string)[] | AnyString | ((columns: SchemaColumns<E>) => string);
19
+ /** Explicit child partition definition for PostgreSQL `list` and `range` partitioning. */
20
+ export interface EntityPartition<E = AnyEntity> {
21
+ /**
22
+ * Optional child table name. Defaults to `<tableName>_<index>`. A single `.` is treated as a
23
+ * `schema.table` separator; names with more than one `.` are rejected to avoid ambiguity.
24
+ */
25
+ name?: string;
26
+ /** Partition bound clause, either as `for values ...` or the trailing part such as `in (...)`, `from ... to ...`, or `default`. */
27
+ values: string;
28
+ }
29
+ /**
30
+ * PostgreSQL table partitioning definition.
31
+ *
32
+ * - `hash` partitions generate child tables automatically from the partition count
33
+ * - `list` and `range` partitions require explicit child partition definitions
34
+ */
35
+ export type EntityPartitionBy<E = AnyEntity> = {
36
+ type: Extract<EntityPartitionType, 'hash'>;
37
+ expression: EntityPartitionExpression<E>;
38
+ /**
39
+ * Hash partition fan-out: either a number (auto-named as `${tableName}_N`) or an array of
40
+ * explicit partition names in remainder order. Names may be `schema.name` to create the
41
+ * child in a non-default schema.
42
+ */
43
+ partitions: number | readonly string[];
44
+ } | {
45
+ type: Exclude<EntityPartitionType, 'hash'>;
46
+ expression: EntityPartitionExpression<E>;
47
+ /** Explicit child partitions created in the order provided. */
48
+ partitions: EntityPartition<E>[];
49
+ };
7
50
  export type EntityOptions<T, E = T extends EntityClass<infer P> ? P : T> = {
8
51
  /** Override default collection/table name. Alias for `collection`. */
9
52
  tableName?: string;
@@ -22,8 +65,10 @@ export type EntityOptions<T, E = T extends EntityClass<infer P> ? P : T> = {
22
65
  * 3. Entity-level `@Entity({ orderBy })`
23
66
  */
24
67
  orderBy?: QueryOrderMap<E> | QueryOrderMap<E>[];
25
- /** For {@doclink inheritance-mapping#single-table-inheritance | Single Table Inheritance}. */
26
- discriminatorColumn?: (T extends EntityClass<infer P> ? keyof P : string) | AnyString;
68
+ /** For {@doclink inheritance-mapping#single-table-inheritance | Single Table Inheritance}. Property name on the entity that stores the discriminator value. */
69
+ discriminator?: (T extends EntityClass<infer P> ? keyof P : string) | AnyString;
70
+ /** For {@doclink inheritance-mapping#single-table-inheritance | Single Table Inheritance}. Override the discriminator column name (the property is named via `discriminator`; defaults to the naming-strategy-derived column). */
71
+ discriminatorColumn?: string;
27
72
  /** For {@doclink inheritance-mapping#single-table-inheritance | Single Table Inheritance}. */
28
73
  discriminatorMap?: Dictionary<string>;
29
74
  /** For {@doclink inheritance-mapping#single-table-inheritance | Single Table Inheritance}. */
@@ -59,6 +104,13 @@ export type EntityOptions<T, E = T extends EntityClass<infer P> ? P : T> = {
59
104
  hasTriggers?: boolean;
60
105
  /** Database triggers to create for this entity's table. (SQL drivers only) */
61
106
  triggers?: TriggerDef<E>[];
107
+ /**
108
+ * PostgreSQL partitioning definition for this table.
109
+ *
110
+ * Partitioned tables are tracked by the schema generator and introspected back from PostgreSQL.
111
+ * Changing an existing partition layout still requires a manual migration.
112
+ */
113
+ partitionBy?: EntityPartitionBy<E>;
62
114
  /** SQL query that maps to a {@doclink virtual-entities | virtual entity}, or for view entities, the view definition. */
63
115
  expression?: string | ((em: any, where: ObjectQuery<E>, options: FindOptions<E, any, any, any>, stream?: boolean) => object);
64
116
  /** Set {@doclink repositories#custom-repository | custom repository class}. */
@@ -331,6 +383,11 @@ export interface PropertyOptions<Owner> {
331
383
  * Specify comment of column for {@link https://mikro-orm.io/docs/schema-generator Schema Generator}. (SQL only)
332
384
  */
333
385
  comment?: string;
386
+ /**
387
+ * Specify column-level collation for {@link https://mikro-orm.io/docs/schema-generator Schema Generator}. (SQL only)
388
+ * Emits a `COLLATE` clause on the column definition; the accepted collation names are dialect-specific.
389
+ */
390
+ collation?: string;
334
391
  /** mysql only */
335
392
  extra?: string;
336
393
  /**
@@ -338,7 +395,7 @@ export interface PropertyOptions<Owner> {
338
395
  *
339
396
  * @see https://mikro-orm.io/docs/defining-entities#sql-generated-columns
340
397
  */
341
- ignoreSchemaChanges?: ('type' | 'extra' | 'default')[];
398
+ ignoreSchemaChanges?: ('type' | 'extra' | 'default' | 'collation')[];
342
399
  }
343
400
  export interface ReferenceOptions<Owner, Target> extends PropertyOptions<Owner> {
344
401
  /** Set target entity type. For polymorphic relations, pass an array of entity types. */
@@ -498,16 +555,16 @@ export interface EmbeddedOptions<Owner, Target> extends PropertyOptions<Owner> {
498
555
  export interface EmbeddableOptions<Owner> {
499
556
  /** Specify constructor parameters to be used in `em.create` or when `forceConstructor` is enabled. Those should be names of declared entity properties in the same order as your constructor uses them. The ORM tries to infer those automatically, use this option in case the inference fails. */
500
557
  constructorParams?: (Owner extends EntityClass<infer P> ? keyof P : string)[];
501
- /** For polymorphic embeddables. Specify the property name that stores the discriminator value. Alias for `discriminatorColumn`. */
558
+ /** For polymorphic embeddables. Property name on the embeddable class that stores the discriminator value. */
502
559
  discriminator?: (Owner extends EntityClass<infer P> ? keyof P : string) | AnyString;
503
- /** For polymorphic embeddables. @deprecated Use `discriminator` instead. */
504
- discriminatorColumn?: (Owner extends EntityClass<infer P> ? keyof P : string) | AnyString;
560
+ /** For polymorphic embeddables. Override the discriminator column name (the property is named via `discriminator`; defaults to the naming-strategy-derived column). */
561
+ discriminatorColumn?: string;
505
562
  discriminatorMap?: Dictionary<string>;
506
563
  discriminatorValue?: number | string;
507
564
  abstract?: boolean;
508
565
  }
509
566
  export interface EnumOptions<T> extends PropertyOptions<T> {
510
- items?: readonly (number | string)[] | (() => Dictionary);
567
+ items?: readonly (number | string)[] | Dictionary | (() => Dictionary);
511
568
  array?: boolean;
512
569
  /** for postgres, by default it uses text column with check constraint */
513
570
  nativeEnumName?: string;
@@ -551,6 +608,16 @@ interface BaseOptions<T, H extends string> {
551
608
  include?: T extends EntityClass<infer P> ? Properties<P, H> : Properties<T, H>;
552
609
  /** Fill factor for the index as a percentage 0-100 (PostgreSQL, MSSQL). */
553
610
  fillFactor?: number;
611
+ /**
612
+ * Predicate for partial indexes. Object form is a `FilterQuery` and is portable across
613
+ * SQL drivers and MongoDB; string form is a raw SQL fragment (SQL drivers only).
614
+ *
615
+ * Native support: PostgreSQL, SQLite, MSSQL (`WHERE`), MongoDB (`partialFilterExpression`).
616
+ * MySQL 8.0.13+ / Oracle: emulated via `CASE WHEN` functional index (uniqueness only enforced
617
+ * where the predicate holds). MariaDB is not supported — it has no inline expression indexes;
618
+ * define a virtual generated column and index that instead.
619
+ */
620
+ where?: string | FilterQuery<NoInfer<T> extends EntityClass<infer P> ? P : NoInfer<T>>;
554
621
  }
555
622
  export interface UniqueOptions<T, H extends string = string> extends BaseOptions<T, H> {
556
623
  deferMode?: DeferMode | `${DeferMode}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "7.1.0-dev.8",
3
+ "version": "7.1.0",
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
  "keywords": [
6
6
  "data-mapper",
@@ -43,6 +43,8 @@ export declare abstract class Platform {
43
43
  getEnumArrayCheckConstraintExpression(column: string, items: string[]): string | null;
44
44
  /** Whether this platform supports materialized views. */
45
45
  supportsMaterializedViews(): boolean;
46
+ /** Whether this platform supports declarative table partitioning in schema generation. */
47
+ supportsPartitionedTables(): boolean;
46
48
  /** Returns the schema helper instance for this platform, or undefined if not supported. */
47
49
  getSchemaHelper(): unknown;
48
50
  /** Whether the platform automatically creates indexes on foreign key columns. */
@@ -168,7 +170,7 @@ export declare abstract class Platform {
168
170
  getUuidTypeDeclarationSQL(column: {
169
171
  length?: number;
170
172
  }): string;
171
- /** Extracts the base type name from a full SQL type declaration (e.g. "varchar(255)" -> "varchar"). */
173
+ /** Extracts the base type name from a full SQL type declaration (e.g. `varchar(255)` -> `varchar`). */
172
174
  extractSimpleType(type: string): string;
173
175
  /**
174
176
  * This should be used only to compare types, it can strip some information like the length.
@@ -276,9 +278,17 @@ export declare abstract class Platform {
276
278
  /** Whether the platform supports unsigned integer columns. */
277
279
  supportsUnsigned(): boolean;
278
280
  /**
279
- * Returns the default name of index for the given columns
281
+ * Maximum length of identifiers (table, column, index, constraint, …) the platform supports.
282
+ * Names produced by {@link getIndexName} above this limit are hash-truncated. Defaults to
283
+ * `Infinity` (no truncation); SQL platforms override with their engine's limit
284
+ * (PG 63, MySQL/MariaDB 64, MSSQL/Oracle 128).
280
285
  */
281
- getIndexName(tableName: string, columns: string[], type: 'index' | 'unique' | 'foreign' | 'primary' | 'sequence'): string;
286
+ getMaxIdentifierLength(): number;
287
+ /**
288
+ * Returns the default name of index for the given columns, hash-truncated if it would
289
+ * exceed {@link getMaxIdentifierLength}.
290
+ */
291
+ getIndexName(tableName: string, columns: string[], type: 'index' | 'unique' | 'foreign' | 'primary' | 'sequence' | 'check'): string;
282
292
  /** Returns the default primary key constraint name. */
283
293
  getDefaultPrimaryName(tableName: string, columns: string[]): string;
284
294
  /** Whether the platform supports custom names for primary key constraints. */
@@ -2,12 +2,22 @@ import { clone } from '../utils/clone.js';
2
2
  import { EntityRepository } from '../entity/EntityRepository.js';
3
3
  import { UnderscoreNamingStrategy } from '../naming-strategy/UnderscoreNamingStrategy.js';
4
4
  import { ExceptionConverter } from './ExceptionConverter.js';
5
+ import { MetadataError } from '../errors.js';
5
6
  import { ArrayType, BigIntType, BlobType, BooleanType, CharacterType, DateTimeType, DateType, DecimalType, DoubleType, EnumType, FloatType, IntegerType, IntervalType, JsonType, MediumIntType, SmallIntType, StringType, TextType, TimeType, TinyIntType, Type, Uint8ArrayType, UnknownType, UuidType, } from '../types/index.js';
6
7
  import { parseJsonSafe, Utils } from '../utils/Utils.js';
7
8
  import { ReferenceKind } from '../enums.js';
8
9
  import { Raw } from '../utils/RawQueryFragment.js';
9
10
  /** Symbol used to tag cloned embeddable data for JSON serialization handling. */
10
11
  export const JsonProperty = Symbol('JsonProperty');
12
+ const MULTI_WORD_SQL_TYPES = [
13
+ 'timestamp with time zone',
14
+ 'timestamp without time zone',
15
+ 'time with time zone',
16
+ 'time without time zone',
17
+ 'character varying',
18
+ 'double precision',
19
+ 'bit varying',
20
+ ];
11
21
  /** Abstract base class providing database-specific behavior and SQL dialect differences. */
12
22
  export class Platform {
13
23
  exceptionConverter = new ExceptionConverter();
@@ -62,6 +72,10 @@ export class Platform {
62
72
  supportsMaterializedViews() {
63
73
  return false;
64
74
  }
75
+ /** Whether this platform supports declarative table partitioning in schema generation. */
76
+ supportsPartitionedTables() {
77
+ return false;
78
+ }
65
79
  /** Returns the schema helper instance for this platform, or undefined if not supported. */
66
80
  getSchemaHelper() {
67
81
  return undefined;
@@ -222,9 +236,15 @@ export class Platform {
222
236
  column.length ??= 36;
223
237
  return this.getVarcharTypeDeclarationSQL(column);
224
238
  }
225
- /** Extracts the base type name from a full SQL type declaration (e.g. "varchar(255)" -> "varchar"). */
239
+ /** Extracts the base type name from a full SQL type declaration (e.g. `varchar(255)` -> `varchar`). */
226
240
  extractSimpleType(type) {
227
- return /[^(), ]+/.exec(type.toLowerCase())[0];
241
+ const lower = type.toLowerCase();
242
+ for (const multiWord of MULTI_WORD_SQL_TYPES) {
243
+ if (lower.startsWith(multiWord)) {
244
+ return multiWord;
245
+ }
246
+ }
247
+ return /[^(), ]+/.exec(lower)[0];
228
248
  }
229
249
  /**
230
250
  * This should be used only to compare types, it can strip some information like the length.
@@ -248,6 +268,7 @@ export class Platform {
248
268
  return Type.getType(CharacterType);
249
269
  case 'string':
250
270
  case 'varchar':
271
+ case 'character varying':
251
272
  return Type.getType(StringType);
252
273
  case 'interval':
253
274
  return Type.getType(IntervalType);
@@ -267,6 +288,7 @@ export class Platform {
267
288
  case 'float':
268
289
  return Type.getType(FloatType);
269
290
  case 'double':
291
+ case 'double precision':
270
292
  return Type.getType(DoubleType);
271
293
  case 'integer':
272
294
  return Type.getType(IntegerType);
@@ -286,8 +308,12 @@ export class Platform {
286
308
  return Type.getType(DateType);
287
309
  case 'datetime':
288
310
  case 'timestamp':
311
+ case 'timestamp with time zone':
312
+ case 'timestamp without time zone':
289
313
  return Type.getType(DateTimeType);
290
314
  case 'time':
315
+ case 'time with time zone':
316
+ case 'time without time zone':
291
317
  return Type.getType(TimeType);
292
318
  case 'object':
293
319
  case 'json':
@@ -513,7 +539,8 @@ export class Platform {
513
539
  if (raw) {
514
540
  return this.formatQuery(raw.sql, raw.params);
515
541
  }
516
- return `${quote}${id.toString().replace('.', `${quote}.${quote}`)}${quote}`;
542
+ const escaped = id.toString().replaceAll(quote, quote + quote);
543
+ return `${quote}${escaped.replace('.', `${quote}.${quote}`)}${quote}`;
517
544
  }
518
545
  /** Quotes a literal value for safe embedding in SQL. */
519
546
  quoteValue(value) {
@@ -603,10 +630,26 @@ export class Platform {
603
630
  return false;
604
631
  }
605
632
  /**
606
- * Returns the default name of index for the given columns
633
+ * Maximum length of identifiers (table, column, index, constraint, …) the platform supports.
634
+ * Names produced by {@link getIndexName} above this limit are hash-truncated. Defaults to
635
+ * `Infinity` (no truncation); SQL platforms override with their engine's limit
636
+ * (PG 63, MySQL/MariaDB 64, MSSQL/Oracle 128).
637
+ */
638
+ getMaxIdentifierLength() {
639
+ return Infinity;
640
+ }
641
+ /**
642
+ * Returns the default name of index for the given columns, hash-truncated if it would
643
+ * exceed {@link getMaxIdentifierLength}.
607
644
  */
608
645
  getIndexName(tableName, columns, type) {
609
- return this.namingStrategy.indexName(tableName, columns, type);
646
+ const indexName = this.namingStrategy.indexName(tableName, columns, type);
647
+ const max = this.getMaxIdentifierLength();
648
+ if (indexName.length > max) {
649
+ const suffix = type === 'primary' ? 'pkey' : type;
650
+ return `${indexName.substring(0, max - 8 - type.length)}_${Utils.hash(indexName, 5)}_${suffix}`;
651
+ }
652
+ return indexName;
610
653
  }
611
654
  /** Returns the default primary key constraint name. */
612
655
  getDefaultPrimaryName(tableName, columns) {
@@ -658,7 +701,9 @@ export class Platform {
658
701
  }
659
702
  /** Platform-specific validation of entity metadata. */
660
703
  validateMetadata(meta) {
661
- return;
704
+ if (meta.partitionBy && !this.supportsPartitionedTables()) {
705
+ throw new MetadataError(`Entity ${meta.className} uses partitionBy, but ${this.constructor.name} does not support partitioned tables`);
706
+ }
662
707
  }
663
708
  /**
664
709
  * Generates a custom order by statement given a set of in order values, eg.
@@ -16,7 +16,8 @@ function isVisible(meta, propName, options) {
16
16
  if (Array.isArray(options.fields) && options.fields.length > 0 && !matchesPath(options.fields, propName)) {
17
17
  return false;
18
18
  }
19
- if (Array.isArray(options.populate) && matchesPath(options.populate, propName)) {
19
+ if (Array.isArray(options.populate) &&
20
+ options.populate.find(item => item === propName || item.startsWith(propName + '.'))) {
20
21
  return true;
21
22
  }
22
23
  if (options.exclude?.find(item => item === propName)) {
@@ -224,7 +225,7 @@ export class EntitySerializer {
224
225
  return this.serialize(child, childOptions);
225
226
  }
226
227
  const pk = this.processCustomType(wrapped.getPrimaryKey(), prop, wrapped.__platform, options.convertCustomTypes);
227
- if (options.forceObject || wrapped.__config.get('serialization').forceObject) {
228
+ if (options.forceObject ?? wrapped.__config.get('serialization').forceObject) {
228
229
  return Utils.primaryKeyToObject(meta, pk, visible);
229
230
  }
230
231
  if (Utils.isPlainObject(pk)) {
@@ -248,7 +249,7 @@ export class EntitySerializer {
248
249
  return this.serialize(item, this.extractChildOptions(options, prop.name));
249
250
  }
250
251
  const pk = this.processCustomType(wrapped.getPrimaryKey(), prop, wrapped.__platform, options.convertCustomTypes);
251
- if (options.forceObject || wrapped.__config.get('serialization').forceObject) {
252
+ if (options.forceObject ?? wrapped.__config.get('serialization').forceObject) {
252
253
  return Utils.primaryKeyToObject(wrapped.__meta, pk);
253
254
  }
254
255
  return pk;