@mikro-orm/core 7.3.0-dev.10 → 7.3.0-dev.12

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.
@@ -1,5 +1,5 @@
1
1
  import type { EntityManager } from '../EntityManager.js';
2
- import type { ColumnType, PropertyOptions, ReferenceOptions, EnumOptions, EmbeddedOptions, ManyToOneOptions, OneToManyOptions, OneToOneOptions, ManyToManyOptions, IndexColumnOptions } from '../metadata/types.js';
2
+ import type { ColumnType, PropertyOptions, ReferenceOptions, EnumOptions, EmbeddedOptions, ManyToOneOptions, OneToManyOptions, OneToOneOptions, ManyToManyOptions, IndexColumnOptions, SequenceOptions } from '../metadata/types.js';
3
3
  import type { AnyString, GeneratedColumnCallback, Constructor, CheckCallback, FilterQuery, EntityName, Dictionary, EntityMetadata, PrimaryKeyProp, EntityRepositoryType, Hidden, Opt, Primary, EntityClass, EntitySchemaWithMeta, InferEntity, MaybeReturnType, Ref, LazyRef, IndexCallback, TriggerCallback, FormulaCallback, EntityCtor, IsNever, IWrappedEntity, DefineConfig, Config, MaybePromise, IndexHints, OptionalProps, ExtractDefineEntityProperties } from '../typings.js';
4
4
  import type { Raw } from '../utils/RawQueryFragment.js';
5
5
  import type { ScalarReference } from './Reference.js';
@@ -130,6 +130,7 @@ export interface PropertyChain<in out Value, in out Options> {
130
130
  unique(unique?: boolean): PropertyChain<Value, Options>;
131
131
  comment(comment: string): PropertyChain<Value, Options>;
132
132
  collation(collation: string): PropertyChain<Value, Options>;
133
+ sequence(sequence: SequenceOptions | number): PropertyChain<Value, Options>;
133
134
  accessor(accessor?: string | boolean): PropertyChain<Value, Options>;
134
135
  eager(eager?: boolean): HasKind<Options, 'm:1' | '1:m' | '1:1' | 'm:n'> extends true ? PropertyChain<Value, Options> : never;
135
136
  cascade(...cascade: Cascade[]): HasKind<Options, 'm:1' | '1:m' | '1:1' | 'm:n'> extends true ? PropertyChain<Value, Options> : never;
@@ -486,6 +487,12 @@ export declare class UniversalPropertyOptionsBuilder<in out Value, in out Option
486
487
  * Emits a `COLLATE` clause on the column definition; the accepted collation names are dialect-specific.
487
488
  */
488
489
  collation(collation: string): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
490
+ /**
491
+ * Set the start value and increment of an autoincrement column. (SQL only)
492
+ * A number is a shorthand for `{ startWith }`.
493
+ * Applied only when the column is created (and by `orm.schema.clear()`), never diffed by the schema generator.
494
+ */
495
+ sequence(sequence: SequenceOptions | number): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
489
496
  /** mysql only */
490
497
  extra(extra: string): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
491
498
  /**
@@ -323,6 +323,14 @@ export class UniversalPropertyOptionsBuilder {
323
323
  collation(collation) {
324
324
  return this.assignOptions({ collation });
325
325
  }
326
+ /**
327
+ * Set the start value and increment of an autoincrement column. (SQL only)
328
+ * A number is a shorthand for `{ startWith }`.
329
+ * Applied only when the column is created (and by `orm.schema.clear()`), never diffed by the schema generator.
330
+ */
331
+ sequence(sequence) {
332
+ return this.assignOptions({ sequence: typeof sequence === 'number' ? { startWith: sequence } : sequence });
333
+ }
326
334
  /** mysql only */
327
335
  extra(extra) {
328
336
  return this.assignOptions({ extra });
package/errors.d.ts CHANGED
@@ -91,6 +91,12 @@ export declare class MetadataError<T extends AnyEntity = AnyEntity> extends Vali
91
91
  static triggersNotSupportedByDriver(meta: EntityMetadata): MetadataError;
92
92
  /** Thrown when row level security is declared on an entity using a driver that does not support it. */
93
93
  static rowLevelSecurityNotSupportedByDriver(meta: EntityMetadata): MetadataError;
94
+ /** Thrown when the `sequence` option is declared on a property that is not autoincrement. */
95
+ static sequenceWithoutAutoincrement(meta: EntityMetadata, prop: EntityProperty): MetadataError;
96
+ /** Thrown when the `sequence` option is combined with an identity option list in `generated`. */
97
+ static sequenceWithIdentityOptions(meta: EntityMetadata, prop: EntityProperty): MetadataError;
98
+ /** Thrown when `sequence.incrementBy` is declared on a platform without a per-column increment. */
99
+ static sequenceIncrementNotSupported(meta: EntityMetadata, prop: EntityProperty, platform: string): MetadataError;
94
100
  /** Thrown when row level security is declared on a non-root entity of an STI hierarchy. */
95
101
  static rowLevelSecurityOnNonRootStiEntity(meta: EntityMetadata): MetadataError;
96
102
  /** Thrown when two policies on the same entity are given the same explicit name. */
package/errors.js CHANGED
@@ -296,6 +296,18 @@ export class MetadataError extends ValidationError {
296
296
  static rowLevelSecurityNotSupportedByDriver(meta) {
297
297
  return new MetadataError(`Entity ${meta.className} declares row level security which is not supported by the current driver. Row level security is only available with the PostgreSQL driver. To test a PostgreSQL app without a server, use the \`@mikro-orm/pglite\` driver instead of sqlite.`);
298
298
  }
299
+ /** Thrown when the `sequence` option is declared on a property that is not autoincrement. */
300
+ static sequenceWithoutAutoincrement(meta, prop) {
301
+ return MetadataError.fromMessage(meta, prop, `defines the 'sequence' option, but is not an autoincrement property`);
302
+ }
303
+ /** Thrown when the `sequence` option is combined with an identity option list in `generated`. */
304
+ static sequenceWithIdentityOptions(meta, prop) {
305
+ return MetadataError.fromMessage(meta, prop, `defines the 'sequence' option together with identity options in 'generated', use only one of them`);
306
+ }
307
+ /** Thrown when `sequence.incrementBy` is declared on a platform without a per-column increment. */
308
+ static sequenceIncrementNotSupported(meta, prop, platform) {
309
+ return MetadataError.fromMessage(meta, prop, `defines 'sequence.incrementBy', but ${platform} does not support a per-column increment`);
310
+ }
299
311
  /** Thrown when row level security is declared on a non-root entity of an STI hierarchy. */
300
312
  static rowLevelSecurityOnNonRootStiEntity(meta) {
301
313
  return new MetadataError(`Entity ${meta.className} declares row level security, but it is part of a single table inheritance hierarchy. Declare policies on the root entity ${meta.root.className} instead, as the whole hierarchy shares a single table.`);
@@ -176,6 +176,12 @@ export interface PropertyOptions<Owner> {
176
176
  * Explicitly specify the auto increment of the primary key.
177
177
  */
178
178
  autoincrement?: boolean;
179
+ /**
180
+ * Start value and increment of an autoincrement column. (SQL only)
181
+ * Applied only when the column is created (and by `orm.schema.clear()`), never diffed by the schema generator.
182
+ * `incrementBy` is not supported on MySQL, MariaDB and SQLite.
183
+ */
184
+ sequence?: SequenceOptions;
179
185
  /**
180
186
  * Add the property to the `returning` clause of inserts, updates and upserts, reloading the database value into the entity.
181
187
  */
@@ -587,6 +593,12 @@ export interface EnumOptions<T> extends PropertyOptions<T> {
587
593
  /** for postgres, by default it uses text column with check constraint */
588
594
  nativeEnumName?: string;
589
595
  }
596
+ export interface SequenceOptions {
597
+ /** The first value the autoincrement column generates. */
598
+ startWith?: number;
599
+ /** The step between generated values. Not supported on MySQL, MariaDB and SQLite. */
600
+ incrementBy?: number;
601
+ }
590
602
  export interface PrimaryKeyOptions<T> extends PropertyOptions<T> {
591
603
  }
592
604
  export interface SerializedPrimaryKeyOptions<T> extends PropertyOptions<T> {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "7.3.0-dev.10",
3
+ "version": "7.3.0-dev.12",
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",
@@ -325,6 +325,8 @@ export declare abstract class Platform {
325
325
  supportsDownMigrations(): boolean;
326
326
  /** Whether the platform supports deferred unique constraints. */
327
327
  supportsDeferredUniqueConstraints(): boolean;
328
+ /** Whether autoincrement columns support a per-column increment (`sequence.incrementBy`). */
329
+ supportsAutoincrementIncrement(): boolean;
328
330
  /** Whether the platform supports row level security (PostgreSQL). */
329
331
  supportsRowLevelSecurity(): boolean;
330
332
  /** Whether the driver can apply the session context on every pooled connection acquire (`sessionContext: 'connection'`). */
@@ -738,6 +738,10 @@ export class Platform {
738
738
  supportsDeferredUniqueConstraints() {
739
739
  return true;
740
740
  }
741
+ /** Whether autoincrement columns support a per-column increment (`sequence.incrementBy`). */
742
+ supportsAutoincrementIncrement() {
743
+ return true;
744
+ }
741
745
  /** Whether the platform supports row level security (PostgreSQL). */
742
746
  supportsRowLevelSecurity() {
743
747
  return false;
@@ -758,6 +762,20 @@ export class Platform {
758
762
  if (meta.partitionBy && !this.supportsPartitionedTables()) {
759
763
  throw new MetadataError(`Entity ${meta.className} uses partitionBy, but ${this.constructor.name} does not support partitioned tables`);
760
764
  }
765
+ for (const prop of Object.values(meta.properties)) {
766
+ if (!prop.sequence) {
767
+ continue;
768
+ }
769
+ if (!prop.autoincrement) {
770
+ throw MetadataError.sequenceWithoutAutoincrement(meta, prop);
771
+ }
772
+ if (typeof prop.generated === 'string' && /identity\s*\(/i.test(prop.generated)) {
773
+ throw MetadataError.sequenceWithIdentityOptions(meta, prop);
774
+ }
775
+ if (prop.sequence.incrementBy != null && !this.supportsAutoincrementIncrement()) {
776
+ throw MetadataError.sequenceIncrementNotSupported(meta, prop, this.constructor.name);
777
+ }
778
+ }
761
779
  const declaresRls = meta.policies.length > 0 || !!meta.rowLevelSecurity;
762
780
  if (declaresRls && !this.supportsRowLevelSecurity()) {
763
781
  throw MetadataError.rowLevelSecurityNotSupportedByDriver(meta);
@@ -1,4 +1,24 @@
1
1
  import { Type } from './Type.js';
2
+ /** Scales a plain decimal string to an integer rounded half away from zero at `scale`, like the DB does. */
3
+ function toScaledInteger(value, scale) {
4
+ if (typeof value !== 'string') {
5
+ return undefined;
6
+ }
7
+ const negative = value.startsWith('-');
8
+ const [int, fraction = ''] = (negative ? value.slice(1) : value).split('.');
9
+ const digits = int + fraction;
10
+ for (let i = 0; i < digits.length; i++) {
11
+ const code = digits.charCodeAt(i);
12
+ if (code < 48 || code > 57) {
13
+ return undefined;
14
+ }
15
+ }
16
+ let scaled = BigInt('0' + int + fraction.slice(0, scale).padEnd(scale, '0'));
17
+ if (fraction.charCodeAt(scale) >= 53) {
18
+ scaled++;
19
+ }
20
+ return negative ? -scaled : scaled;
21
+ }
2
22
  /**
3
23
  * Type that maps an SQL DECIMAL to a JS string or number.
4
24
  */
@@ -16,7 +36,20 @@ export class DecimalType extends Type {
16
36
  return String(value);
17
37
  }
18
38
  compareValues(a, b) {
19
- return this.platform.formatDecimal(a, this.prop?.scale) === this.platform.formatDecimal(b, this.prop?.scale);
39
+ if (a === b) {
40
+ return true;
41
+ }
42
+ const scale = this.prop?.scale;
43
+ if (this.platform.formatDecimal(a, scale) !== this.platform.formatDecimal(b, scale)) {
44
+ return false;
45
+ }
46
+ // doubles are exact only up to 15 significant digits, so only higher precision strings can differ past the numeric check
47
+ if ((this.prop?.precision ?? 0) <= 15 || this.compareAsType() !== 'string') {
48
+ return true;
49
+ }
50
+ const scaledA = toScaledInteger(a, scale ?? 0);
51
+ const scaledB = toScaledInteger(b, scale ?? 0);
52
+ return scaledA == null || scaledB == null || scaledA === scaledB;
20
53
  }
21
54
  getColumnType(prop, platform) {
22
55
  return platform.getDecimalTypeDeclarationSQL(prop);
package/typings.d.ts CHANGED
@@ -12,7 +12,7 @@ import type { SerializeOptions } from './serialization/EntitySerializer.js';
12
12
  import type { MetadataStorage } from './metadata/MetadataStorage.js';
13
13
  import type { Routine } from './metadata/Routine.js';
14
14
  import type { EntitySchema } from './metadata/EntitySchema.js';
15
- import type { EntityPartitionBy, IndexColumnOptions } from './metadata/types.js';
15
+ import type { EntityPartitionBy, IndexColumnOptions, SequenceOptions } from './metadata/types.js';
16
16
  import type { Type, types } from './types/index.js';
17
17
  import type { Platform } from './platforms/Platform.js';
18
18
  import type { Configuration } from './utils/Configuration.js';
@@ -986,6 +986,7 @@ export interface EntityProperty<Owner = any, Target = any> {
986
986
  hasConvertToJSValueSQL: boolean;
987
987
  hasConvertToDatabaseValueSQL: boolean;
988
988
  autoincrement?: boolean;
989
+ sequence?: SequenceOptions;
989
990
  returning?: boolean;
990
991
  primary?: boolean;
991
992
  serializedPrimaryKey: boolean;
package/utils/Utils.js CHANGED
@@ -153,7 +153,7 @@ export function parseJsonSafe(value) {
153
153
  /** Collection of general-purpose utility methods used throughout the ORM. */
154
154
  export class Utils {
155
155
  static PK_SEPARATOR = '~~~';
156
- static #ORM_VERSION = '7.3.0-dev.10';
156
+ static #ORM_VERSION = '7.3.0-dev.12';
157
157
  /** Default session variable name backing an RLS filter argument (`current_setting('mikro.<filter>.<arg>')`). */
158
158
  static getRlsSettingName(filterName, argName) {
159
159
  return `mikro.${filterName}.${argName}`;