Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update extending-data-types.md #718

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 52 additions & 31 deletions versioned_docs/version-6.x.x/other-topics/extending-data-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Most likely the type you are trying to implement is already included in [DataTyp

Sequelize doesn't create new datatypes in the database. This tutorial explains how to make Sequelize recognize new datatypes and assumes that those new datatypes are already created in the database.

To extend Sequelize datatypes, do it before any Sequelize instance is created.
To extend Sequelize datatypes, do it before any Sequelize model is created.

## Example

Expand All @@ -18,8 +18,28 @@ createTheNewDataType();
const sequelize = new Sequelize('sqlite::memory:');

function createTheNewDataType() {
// Strange behavior would result if we extended DataTypes.ABSTRACT because
// it's a class wrapped in a Proxy by Utils.classToInvokable.
class SOMETYPE extends DataTypes.ABSTRACT.prototype.constructor {
// Mandatory: set the type key
static key = 'SOMETYPE';
key = SOMETYPE.key;

// Optional: disable escaping after stringifier. Do this at your own risk, since this opens opportunity for SQL injections.
escape = false

// Optional: map dialect datatype names (mandatory if not creating dialect-specific datatype classes as in the example below)
types = {
postgres: ['pg_new_type'],
mysql: [ 'mysql_new_type' ],
mariadb: [ 'mariadb_new_type' ],
sqlite: [ 'sqlite_new_type' ],
mssql: false,
db2: false,
snowflake: [ 'snowflake_new_type' ],
oracle: [ 'oracle_new_type' ]
};

class SOMETYPE extends DataTypes.ABSTRACT {
// Mandatory: complete definition of the new type in the database
toSql() {
return 'INTEGER(11) UNSIGNED ZEROFILL'
Expand Down Expand Up @@ -47,52 +67,53 @@ function createTheNewDataType() {
}
}

// Mandatory: set the type key
SOMETYPE.prototype.key = SOMETYPE.key = 'SOMETYPE';

// Mandatory: add the new type to DataTypes. Optionally wrap it on `Utils.classToInvokable` to
// Optional: add the new type to DataTypes. Optionally wrap it on `Utils.classToInvokable` to
// be able to use this datatype directly without having to call `new` on it.
DataTypes.SOMETYPE = Utils.classToInvokable(SOMETYPE);

// Optional: disable escaping after stringifier. Do this at your own risk, since this opens opportunity for SQL injections.
// DataTypes.SOMETYPE.escape = false;

}
```

After creating this new datatype, you need to map this datatype in each database dialect and make some adjustments.
After creating this new datatype, you may wish to map this datatype in each database dialect and make some adjustments.

## PostgreSQL
## Dialect-specific customization

Let's say the name of the new datatype is `pg_new_type` in the postgres database. That name has to be mapped to `DataTypes.SOMETYPE`. Additionally, it is required to create a child postgres-specific datatype.
If you want to customize parsing, stringifying, etc. on a per-dialect basis then you can create dialect-specific subclasses
of your custom data type and add them to `DataTypes[dialect]`. Sequelize will replace the base type with `DataTypes[dialect][baseType.key]`.
For example for PostgreSQL:

```js
function createTheNewDataType() {
// [...]
class SOMETYPE extends DataTypes.ABSTRACT.prototype.constructor {
// [...]
}

const PgTypes = DataTypes.postgres;
DataTypes.SOMETYPE = Utils.classToInvokable(SOMETYPE);

// Mandatory: map postgres datatype name
DataTypes.SOMETYPE.types.postgres = ['pg_new_type'];
const PgTypes = DataTypes.postgres; // or .mysql, .mariadb, .sqlite, .mssql, .db2, .snowflake, .oracle

// Mandatory: create a postgres-specific child datatype with its own parse
// Optional: create a postgres-specific child datatype with its own parse
// method. The parser will be dynamically mapped to the OID of pg_new_type.
PgTypes.SOMETYPE = function SOMETYPE() {
if (!(this instanceof PgTypes.SOMETYPE)) {
return new PgTypes.SOMETYPE();
}
DataTypes.SOMETYPE.apply(this, arguments);
}
const util = require('util'); // Built-in Node package
util.inherits(PgTypes.SOMETYPE, DataTypes.SOMETYPE);

// Mandatory: create, override or reassign a postgres-specific parser
// PgTypes.SOMETYPE.parse = value => value;
PgTypes.SOMETYPE.parse = DataTypes.SOMETYPE.parse || x => x;
class PgSOMETYPE extends SOMETYPE {
// Mandatory: set the type key. Must match SOMETIME.key or the dialect-specific
// override won't work!
static key = SOMETYPE.key;
key = SOMETYPE.key;

// Mandatory: map postgres datatype name
types = { postgres: ['pg_new_type'] };

// Postgres-specific parser
static parse(value) {
// [...]
}

// Optional: add or override methods of the postgres-specific datatype
// like toSql, escape, validate, _stringify, _sanitize...
// Optional: add or override methods of the postgres-specific datatype
// like toSql, escape, validate, _stringify, _sanitize...
}

// Using classToInvokable is never necessary for dialect-specific types.
PgTypes.SOMETYPE = PgSOMETYPE;
}
```

Expand Down