Skip to content

Commit

Permalink
feat: migrate constraints to typescript (#15962)
Browse files Browse the repository at this point in the history
  • Loading branch information
lohart13 committed Jun 22, 2023
1 parent 37dbf08 commit 0e17c07
Show file tree
Hide file tree
Showing 71 changed files with 3,161 additions and 1,380 deletions.
105 changes: 30 additions & 75 deletions packages/core/src/deferrable.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import isEqual from 'lodash/isEqual';
import type { AbstractQueryGenerator } from './dialects/abstract/query-generator.js';
import { classToInvokable } from './utils/class-to-invokable.js';
import { EMPTY_ARRAY } from './utils/object.js';

/**
* Can be used to
* make foreign key constraints deferrable and to set the constaints within a
* transaction. This is only supported in PostgreSQL.
* Can be used to make foreign key constraints deferrable.
* This is only supported in PostgreSQL.
*
* The foreign keys can be configured like this. It will create a foreign key
* that will check the constraints immediately when the data was inserted.
Expand All @@ -24,84 +22,49 @@ import { EMPTY_ARRAY } from './utils/object.js';
* }
* }, { sequelize });
* ```
*/
export enum Deferrable {
INITIALLY_DEFERRED = 'INITIALLY_DEFERRED',
INITIALLY_IMMEDIATE = 'INITIALLY_IMMEDIATE',
NOT = 'NOT',
}

/**
* Can be used to set constraints deferrable within a transaction.
* This is only supported in PostgreSQL.
*
* The constraints can be configured in a transaction like this. It will
* trigger a query once the transaction has been started and set the constraints
* The constraints can be configured to be deferrable in a transaction like this.
* It will trigger a query once the transaction has been started and set the constraints
* to be checked at the very end of the transaction.
*
* ```js
* sequelize.transaction({
* deferrable: Sequelize.Deferrable.SET_DEFERRED
* constraintChecking: Sequelize.ConstraintChecking.DEFERRED
* });
* ```
*/
export class Deferrable {
export class ConstraintChecking {
toString() {
return this.constructor.name;
}

toSql(_queryGenerator: AbstractQueryGenerator) {
throw new Error('toSql implementation missing');
}

isEqual(_other: unknown): boolean {
throw new Error('isEqual implementation missing');
}

static readonly INITIALLY_DEFERRED = classToInvokable(class INITIALLY_DEFERRED extends Deferrable {
toSql() {
return INITIALLY_DEFERRED.toSql();
}

static toSql() {
return 'DEFERRABLE INITIALLY DEFERRED';
}

isEqual(other: unknown): boolean {
return other instanceof INITIALLY_DEFERRED;
}
});

static readonly INITIALLY_IMMEDIATE = classToInvokable(class INITIALLY_IMMEDIATE extends Deferrable {
toSql() {
return INITIALLY_IMMEDIATE.toSql();
}

isEqual(other: unknown): boolean {
return other instanceof INITIALLY_IMMEDIATE;
}

static toSql() {
return 'DEFERRABLE INITIALLY IMMEDIATE';
}
});

/**
* Will set the constraints to not deferred. This is the default in PostgreSQL and it make
* it impossible to dynamically defer the constraints within a transaction.
*/
static readonly NOT = classToInvokable(class NOT extends Deferrable {
toSql() {
return NOT.toSql();
}

isEqual(other: unknown): boolean {
return other instanceof NOT;
}
static toString() {
return this.name;
}

static toSql() {
return 'NOT DEFERRABLE';
}
});
get constraints(): readonly string[] {
throw new Error('constraints getter implementation missing');
}

// TODO: move the following classes to their own namespace, as they are not related to the above classes
// the ones above are about configuring a constraint's deferrability when defining the constraint.
// The ones below are for configuring them during a transaction
/**
* Will trigger an additional query at the beginning of a
* transaction which sets the constraints to deferred.
*/
static readonly SET_DEFERRED = classToInvokable(class SET_DEFERRED extends Deferrable {
static readonly DEFERRED = classToInvokable(class DEFERRED extends ConstraintChecking {
readonly #constraints: readonly string[];

/**
Expand All @@ -112,24 +75,20 @@ export class Deferrable {
this.#constraints = Object.freeze([...constraints]);
}

toSql(queryGenerator: AbstractQueryGenerator): string {
return queryGenerator.setDeferredQuery(this.#constraints);
}

isEqual(other: unknown): boolean {
return other instanceof SET_DEFERRED && isEqual(this.#constraints, other.#constraints);
return other instanceof DEFERRED && isEqual(this.#constraints, other.#constraints);
}

static toSql(queryGenerator: AbstractQueryGenerator): string {
return queryGenerator.setDeferredQuery(EMPTY_ARRAY);
get constraints(): readonly string[] {
return this.#constraints;
}
});

/**
* Will trigger an additional query at the beginning of a
* transaction which sets the constraints to immediately.
*/
static readonly SET_IMMEDIATE = classToInvokable(class SET_IMMEDIATE extends Deferrable {
static readonly IMMEDIATE = classToInvokable(class IMMEDIATE extends ConstraintChecking {
readonly #constraints: readonly string[];

/**
Expand All @@ -140,16 +99,12 @@ export class Deferrable {
this.#constraints = Object.freeze([...constraints]);
}

toSql(queryGenerator: AbstractQueryGenerator): string {
return queryGenerator.setImmediateQuery(this.#constraints);
}

isEqual(other: unknown): boolean {
return other instanceof SET_IMMEDIATE && isEqual(this.#constraints, other.#constraints);
return other instanceof IMMEDIATE && isEqual(this.#constraints, other.#constraints);
}

static toSql(queryGenerator: AbstractQueryGenerator): string {
return queryGenerator.setImmediateQuery(EMPTY_ARRAY);
get constraints(): readonly string[] {
return this.#constraints;
}
});
}
29 changes: 18 additions & 11 deletions packages/core/src/dialects/abstract/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,11 @@ export type DialectSupports = {
},
constraints: {
restrict: boolean,
addConstraint: boolean,
dropConstraint: boolean,
/**
* This dialect supports marking a column's constraints as deferrable.
* e.g. 'DEFERRABLE' and 'INITIALLY DEFERRED'
*/
deferrable: boolean,
unique: boolean,
default: boolean,
check: boolean,
Expand All @@ -119,6 +122,12 @@ export type DialectSupports = {
foreignKeyChecksDisableable: boolean,
primaryKey: boolean,
onUpdate: boolean,
add: boolean,
remove: boolean,
removeOptions: {
cascade: boolean,
ifExists: boolean,
},
},
index: {
collate: boolean,
Expand Down Expand Up @@ -202,12 +211,6 @@ export type DialectSupports = {
tmpTableTrigger: boolean,
indexHints: boolean,
searchPath: boolean,
/**
* This dialect supports marking a column's constraints as deferrable.
* e.g. 'DEFERRABLE' and 'INITIALLY DEFERRED'
*/
deferrableConstraints: boolean,

/**
* This dialect supports E-prefixed strings, e.g. "E'foo'", which
* enables the ability to use backslash escapes inside of the string.
Expand Down Expand Up @@ -278,15 +281,20 @@ export abstract class AbstractDialect {
},
constraints: {
restrict: true,
addConstraint: true,
dropConstraint: true,
deferrable: false,
unique: true,
default: false,
check: true,
foreignKey: true,
foreignKeyChecksDisableable: false,
primaryKey: true,
onUpdate: true,
add: true,
remove: true,
removeOptions: {
cascade: false,
ifExists: false,
},
},
index: {
collate: true,
Expand Down Expand Up @@ -342,7 +350,6 @@ export abstract class AbstractDialect {
},
REGEXP: false,
IREGEXP: false,
deferrableConstraints: false,
tmpTableTrigger: false,
indexHints: false,
searchPath: false,
Expand Down

0 comments on commit 0e17c07

Please sign in to comment.