diff --git a/versioned_docs/version-6.x.x/other-topics/extending-data-types.md b/versioned_docs/version-6.x.x/other-topics/extending-data-types.md index c8e1dbf52..6ca0fcef3 100644 --- a/versioned_docs/version-6.x.x/other-topics/extending-data-types.md +++ b/versioned_docs/version-6.x.x/other-topics/extending-data-types.md @@ -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 @@ -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' @@ -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; } ```