不要怂,就是干,撸起袖子干!

Commit 3cd38910 by Simon Schick Committed by Sushant

refactor(data-types): move to classes (#10495)

1 parent 1b4a7bf8
...@@ -105,7 +105,8 @@ ...@@ -105,7 +105,8 @@
"camelcase": "warn", "camelcase": "warn",
"prefer-template": "error", "prefer-template": "error",
"no-else-return": ["error", { "allowElseIf": false }], "no-else-return": ["error", { "allowElseIf": false }],
"no-lonely-if": "error" "no-lonely-if": "error",
"no-this-before-super": "error"
}, },
"parserOptions": { "parserOptions": {
"ecmaVersion": 6, "ecmaVersion": 6,
......
...@@ -196,51 +196,49 @@ modules.exports = function sequelizeAdditions(Sequelize) { ...@@ -196,51 +196,49 @@ modules.exports = function sequelizeAdditions(Sequelize) {
/* /*
* Create new types * Create new types
*/ */
class NEWTYPE extends DataTypes.ABSTRACT {
// Mandatory, complete definition of the new type in the database
toSql() {
return 'INTEGER(11) UNSIGNED ZEROFILL'
}
// Create new type // Optional, validator function
DataTypes.NEWTYPE = function NEWTYPE() { validate(value, options) {
if (!(this instanceof DataTypes.NEWTYPE)) return new DataTypes.NEWTYPE() return (typeof value === 'number') && (! Number.isNaN(value))
} }
inherits(DataTypes.NEWTYPE, DataTypes.ABSTRACT)
// Mandatory, set key // Optional, sanitizer
DataTypes.NEWTYPE.prototype.key = DataTypes.NEWTYPE.key = 'NEWTYPE' _sanitize(value) {
// Force all numbers to be positive
if (value < 0) {
value = 0
}
// Mandatory, complete definition of the new type in the database return Math.round(value)
DataTypes.NEWTYPE.prototype.toSql = function toSql() { }
return 'INTEGER(11) UNSIGNED ZEROFILL'
}
// Optional, validator function
DataTypes.NEWTYPE.prototype.validate = function validate(value, options) {
return (typeof value === 'number') && (! Number.isNaN(value))
}
// Optional, sanitizer // Optional, value stringifier before sending to database
DataTypes.NEWTYPE.prototype._sanitize = function _sanitize(value) { _stringify(value) {
// Force all numbers to be positive return value.toString()
if (value < 0) {
value = 0
} }
return Math.round(value) // Optional, parser for values received from the database
static parse(value) {
return Number.parseInt(value)
}
} }
// Optional, value stringifier before sending to database // Mandatory, set key
DataTypes.NEWTYPE.prototype._stringify = function _stringify(value) { DataTypes.NEWTYPE.prototype.key = DataTypes.NEWTYPE.key = 'NEWTYPE'
return value.toString()
}
// Optional, disable escaping after stringifier. Not recommended. // Optional, disable escaping after stringifier. Not recommended.
// Warning: disables Sequelize protection against SQL injections // Warning: disables Sequelize protection against SQL injections
//DataTypes.NEWTYPE.escape = false //DataTypes.NEWTYPE.escape = false
// Optional, parser for values received from the database
DataTypes.NEWTYPE.parse = function parse(value) {
return Number.parseInt(value)
}
// For convenience // For convenience
Sequelize.NEWTYPE = DataTypes.NEWTYPE // `inferNew` allows you to use the datatype without `new`
Sequelize.NEWTYPE = Sequelize.Utils.inferNew(DataTypes.NEWTYPE)
} }
``` ```
......
'use strict'; 'use strict';
const util = require('util'); const util = require('util');
const inherits = require('./utils/inherits');
const _ = require('lodash'); const _ = require('lodash');
const wkx = require('wkx'); const wkx = require('wkx');
const sequelizeErrors = require('./errors'); const sequelizeErrors = require('./errors');
...@@ -9,37 +8,43 @@ const Validator = require('./utils/validator-extras').validator; ...@@ -9,37 +8,43 @@ const Validator = require('./utils/validator-extras').validator;
const momentTz = require('moment-timezone'); const momentTz = require('moment-timezone');
const moment = require('moment'); const moment = require('moment');
const logger = require('./utils/logger'); const logger = require('./utils/logger');
const warnings = {}; const warnings = {};
const { classToInvokable } = require('./utils/classToInvokable');
function ABSTRACT() {} class ABSTRACT {
toString(options) {
ABSTRACT.prototype.dialectTypes = ''; return this.toSql(options);
ABSTRACT.prototype.toString = function toString(options) {
return this.toSql(options);
};
ABSTRACT.prototype.toSql = function toSql() {
return this.key;
};
ABSTRACT.warn = function warn(link, text) {
if (!warnings[text]) {
warnings[text] = true;
logger.warn(`${text} \n>> Check: ${link}`);
} }
}; toSql() {
ABSTRACT.prototype.stringify = function stringify(value, options) { return this.key;
if (this._stringify) {
return this._stringify(value, options);
} }
return value; stringify(value, options) {
}; if (this._stringify) {
ABSTRACT.prototype.bindParam = function bindParam(value, options) { return this._stringify(value, options);
if (this._bindParam) { }
return this._bindParam(value, options); return value;
} }
return options.bindParam(this.stringify(value, options)); bindParam(value, options) {
}; if (this._bindParam) {
return this._bindParam(value, options);
}
return options.bindParam(this.stringify(value, options));
}
static toString() {
return this.name;
}
static warn(link, text) {
if (!warnings[text]) {
warnings[text] = true;
logger.warn(`${text} \n>> Check: ${link}`);
}
}
static extend(oldType) {
return new this(oldType.options);
};
}
ABSTRACT.prototype.dialectTypes = '';
/** /**
* STRING A variable length string * STRING A variable length string
...@@ -50,38 +55,37 @@ ABSTRACT.prototype.bindParam = function bindParam(value, options) { ...@@ -50,38 +55,37 @@ ABSTRACT.prototype.bindParam = function bindParam(value, options) {
* @namespace DataTypes.STRING * @namespace DataTypes.STRING
* *
*/ */
function STRING(length, binary) { class STRING extends ABSTRACT {
const options = typeof length === 'object' && length || { length, binary }; constructor(length, binary) {
super();
if (!(this instanceof STRING)) return new STRING(options); const options = typeof length === 'object' && length || { length, binary };
this.options = options;
this.options = options; this._binary = options.binary;
this._binary = options.binary; this._length = options.length || 255;
this._length = options.length || 255; }
} toSql() {
inherits(STRING, ABSTRACT); return `VARCHAR(${this._length})${this._binary ? ' BINARY' : ''}`;
}
STRING.prototype.key = STRING.key = 'STRING'; validate(value) {
STRING.prototype.toSql = function toSql() { if (Object.prototype.toString.call(value) !== '[object String]') {
return `VARCHAR(${this._length})${this._binary ? ' BINARY' : ''}`; if (this.options.binary && Buffer.isBuffer(value) || typeof value === 'number') {
}; return true;
STRING.prototype.validate = function validate(value) { }
if (Object.prototype.toString.call(value) !== '[object String]') { throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value));
if (this.options.binary && Buffer.isBuffer(value) || typeof value === 'number') {
return true;
} }
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value)); return true;
} }
return true; get BINARY() {
};
Object.defineProperty(STRING.prototype, 'BINARY', {
get() {
this._binary = true; this._binary = true;
this.options.binary = true; this.options.binary = true;
return this; return this;
} }
});
static get BINARY() {
return new this().BINARY;
}
}
/** /**
* CHAR A fixed length string * CHAR A fixed length string
...@@ -91,18 +95,14 @@ Object.defineProperty(STRING.prototype, 'BINARY', { ...@@ -91,18 +95,14 @@ Object.defineProperty(STRING.prototype, 'BINARY', {
* *
* @namespace DataTypes.CHAR * @namespace DataTypes.CHAR
*/ */
function CHAR(length, binary) { class CHAR extends STRING {
const options = typeof length === 'object' && length || { length, binary }; constructor(length, binary) {
super(typeof length === 'object' && length || { length, binary });
if (!(this instanceof CHAR)) return new CHAR(options); }
STRING.apply(this, arguments); toSql() {
return `CHAR(${this._length})${this._binary ? ' BINARY' : ''}`;
}
} }
inherits(CHAR, STRING);
CHAR.prototype.key = CHAR.key = 'CHAR';
CHAR.prototype.toSql = function toSql() {
return `CHAR(${this._length})${this._binary ? ' BINARY' : ''}`;
};
/** /**
* Unlimited length TEXT column * Unlimited length TEXT column
...@@ -111,34 +111,32 @@ CHAR.prototype.toSql = function toSql() { ...@@ -111,34 +111,32 @@ CHAR.prototype.toSql = function toSql() {
* *
* @namespace DataTypes.TEXT * @namespace DataTypes.TEXT
*/ */
function TEXT(length) { class TEXT extends ABSTRACT {
const options = typeof length === 'object' && length || { length }; constructor(length) {
if (!(this instanceof TEXT)) return new TEXT(options); super();
this.options = options; const options = typeof length === 'object' && length || { length };
this._length = options.length || ''; this.options = options;
} this._length = options.length || '';
inherits(TEXT, ABSTRACT);
TEXT.prototype.key = TEXT.key = 'TEXT';
TEXT.prototype.toSql = function toSql() {
switch (this._length.toLowerCase()) {
case 'tiny':
return 'TINYTEXT';
case 'medium':
return 'MEDIUMTEXT';
case 'long':
return 'LONGTEXT';
default:
return this.key;
} }
}; toSql() {
TEXT.prototype.validate = function validate(value) { switch (this._length.toLowerCase()) {
if (typeof value !== 'string') { case 'tiny':
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value)); return 'TINYTEXT';
case 'medium':
return 'MEDIUMTEXT';
case 'long':
return 'LONGTEXT';
default:
return this.key;
}
} }
validate(value) {
return true; if (typeof value !== 'string') {
}; throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value));
}
return true;
}
}
/** /**
* An unlimited length case-insensitive text column. * An unlimited length case-insensitive text column.
...@@ -147,23 +145,17 @@ TEXT.prototype.validate = function validate(value) { ...@@ -147,23 +145,17 @@ TEXT.prototype.validate = function validate(value) {
* *
* @namespace DataTypes.CITEXT * @namespace DataTypes.CITEXT
*/ */
function CITEXT() { class CITEXT extends ABSTRACT {
if (!(this instanceof CITEXT)) return new CITEXT(); toSql() {
} return 'CITEXT';
inherits(CITEXT, ABSTRACT);
CITEXT.prototype.key = CITEXT.key = 'CITEXT';
CITEXT.prototype.toSql = function toSql() {
return 'CITEXT';
};
CITEXT.prototype.validate = function validate(value) {
if (typeof value !== 'string') {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value));
} }
validate(value) {
return true; if (typeof value !== 'string') {
}; throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value));
}
return true;
}
}
/** /**
* Base number type which is used to build other types * Base number type which is used to build other types
...@@ -178,67 +170,75 @@ CITEXT.prototype.validate = function validate(value) { ...@@ -178,67 +170,75 @@ CITEXT.prototype.validate = function validate(value) {
* *
* @namespace DataTypes.NUMBER * @namespace DataTypes.NUMBER
*/ */
function NUMBER(options) { class NUMBER extends ABSTRACT {
this.options = options; constructor(options = {}) {
this._length = options.length; super();
this._zerofill = options.zerofill; if (typeof options === 'number') {
this._decimals = options.decimals; options = {
this._precision = options.precision; length: options
this._scale = options.scale; };
this._unsigned = options.unsigned;
}
inherits(NUMBER, ABSTRACT);
NUMBER.prototype.key = NUMBER.key = 'NUMBER';
NUMBER.prototype.toSql = function toSql() {
let result = this.key;
if (this._length) {
result += `(${this._length}`;
if (typeof this._decimals === 'number') {
result += `,${this._decimals}`;
} }
result += ')'; this.options = options;
} this._length = options.length;
if (this._unsigned) { this._zerofill = options.zerofill;
result += ' UNSIGNED'; this._decimals = options.decimals;
this._precision = options.precision;
this._scale = options.scale;
this._unsigned = options.unsigned;
} }
if (this._zerofill) { toSql() {
result += ' ZEROFILL'; let result = this.key;
if (this._length) {
result += `(${this._length}`;
if (typeof this._decimals === 'number') {
result += `,${this._decimals}`;
}
result += ')';
}
if (this._unsigned) {
result += ' UNSIGNED';
}
if (this._zerofill) {
result += ' ZEROFILL';
}
return result;
} }
return result; validate(value) {
}; if (!Validator.isFloat(String(value))) {
NUMBER.prototype.validate = function(value) { throw new sequelizeErrors.ValidationError(util.format(`%j is not a valid ${this.key.toLowerCase()}`, value));
if (!Validator.isFloat(String(value))) { }
throw new sequelizeErrors.ValidationError(util.format(`%j is not a valid ${this.key.toLowerCase()}`, value)); return true;
} }
_stringify(number) {
return true; if (typeof number === 'number' || number === null || number === undefined) {
}; return number;
NUMBER.prototype._stringify = function _stringify(number) { }
if (typeof number === 'number' || number === null || number === undefined) { if (typeof number.toString === 'function') {
return number.toString();
}
return number; return number;
} }
if (typeof number.toString === 'function') { get UNSIGNED() {
return number.toString();
}
return number;
};
Object.defineProperty(NUMBER.prototype, 'UNSIGNED', {
get() {
this._unsigned = true; this._unsigned = true;
this.options.unsigned = true; this.options.unsigned = true;
return this; return this;
} }
});
Object.defineProperty(NUMBER.prototype, 'ZEROFILL', { get ZEROFILL() {
get() {
this._zerofill = true; this._zerofill = true;
this.options.zerofill = true; this.options.zerofill = true;
return this; return this;
} }
});
static get UNSIGNED() {
return new this().UNSIGNED;
}
static get ZEROFILL() {
return new this().ZEROFILL;
}
}
/** /**
* A 32 bit integer * A 32 bit integer
...@@ -247,21 +247,14 @@ Object.defineProperty(NUMBER.prototype, 'ZEROFILL', { ...@@ -247,21 +247,14 @@ Object.defineProperty(NUMBER.prototype, 'ZEROFILL', {
* *
* @namespace DataTypes.INTEGER * @namespace DataTypes.INTEGER
*/ */
function INTEGER(length) { class INTEGER extends NUMBER {
const options = typeof length === 'object' && length || { length }; validate(value) {
if (!(this instanceof INTEGER)) return new INTEGER(options); if (!Validator.isInt(String(value))) {
NUMBER.call(this, options); throw new sequelizeErrors.ValidationError(util.format(`%j is not a valid ${this.key.toLowerCase()}`, value));
} }
inherits(INTEGER, NUMBER); return true;
INTEGER.prototype.key = INTEGER.key = 'INTEGER';
INTEGER.prototype.validate = function validate(value) {
if (!Validator.isInt(String(value))) {
throw new sequelizeErrors.ValidationError(util.format(`%j is not a valid ${this.key.toLowerCase()}`, value));
} }
}
return true;
};
/** /**
* A 8 bit integer * A 8 bit integer
...@@ -270,14 +263,8 @@ INTEGER.prototype.validate = function validate(value) { ...@@ -270,14 +263,8 @@ INTEGER.prototype.validate = function validate(value) {
* *
* @namespace DataTypes.TINYINT * @namespace DataTypes.TINYINT
*/ */
function TINYINT(length) { class TINYINT extends INTEGER {
const options = typeof length === 'object' && length || { length };
if (!(this instanceof TINYINT)) return new TINYINT(options);
NUMBER.call(this, options);
} }
inherits(TINYINT, INTEGER);
TINYINT.prototype.key = TINYINT.key = 'TINYINT';
/** /**
* A 16 bit integer * A 16 bit integer
...@@ -286,14 +273,8 @@ TINYINT.prototype.key = TINYINT.key = 'TINYINT'; ...@@ -286,14 +273,8 @@ TINYINT.prototype.key = TINYINT.key = 'TINYINT';
* *
* @namespace DataTypes.SMALLINT * @namespace DataTypes.SMALLINT
*/ */
function SMALLINT(length) { class SMALLINT extends INTEGER {
const options = typeof length === 'object' && length || { length };
if (!(this instanceof SMALLINT)) return new SMALLINT(options);
NUMBER.call(this, options);
} }
inherits(SMALLINT, INTEGER);
SMALLINT.prototype.key = SMALLINT.key = 'SMALLINT';
/** /**
* A 24 bit integer * A 24 bit integer
...@@ -302,14 +283,8 @@ SMALLINT.prototype.key = SMALLINT.key = 'SMALLINT'; ...@@ -302,14 +283,8 @@ SMALLINT.prototype.key = SMALLINT.key = 'SMALLINT';
* *
* @namespace DataTypes.MEDIUMINT * @namespace DataTypes.MEDIUMINT
*/ */
function MEDIUMINT(length) { class MEDIUMINT extends INTEGER {
const options = typeof length === 'object' && length || { length };
if (!(this instanceof MEDIUMINT)) return new MEDIUMINT(options);
NUMBER.call(this, options);
} }
inherits(MEDIUMINT, INTEGER);
MEDIUMINT.prototype.key = MEDIUMINT.key = 'MEDIUMINT';
/** /**
* A 64 bit integer * A 64 bit integer
...@@ -318,14 +293,8 @@ MEDIUMINT.prototype.key = MEDIUMINT.key = 'MEDIUMINT'; ...@@ -318,14 +293,8 @@ MEDIUMINT.prototype.key = MEDIUMINT.key = 'MEDIUMINT';
* *
* @namespace DataTypes.BIGINT * @namespace DataTypes.BIGINT
*/ */
function BIGINT(length) { class BIGINT extends INTEGER {
const options = typeof length === 'object' && length || { length };
if (!(this instanceof BIGINT)) return new BIGINT(options);
NUMBER.call(this, options);
} }
inherits(BIGINT, INTEGER);
BIGINT.prototype.key = BIGINT.key = 'BIGINT';
/** /**
* Floating point number (4-byte precision). * Floating point number (4-byte precision).
...@@ -335,21 +304,17 @@ BIGINT.prototype.key = BIGINT.key = 'BIGINT'; ...@@ -335,21 +304,17 @@ BIGINT.prototype.key = BIGINT.key = 'BIGINT';
* *
* @namespace DataTypes.FLOAT * @namespace DataTypes.FLOAT
*/ */
function FLOAT(length, decimals) { class FLOAT extends NUMBER {
const options = typeof length === 'object' && length || { length, decimals }; constructor(length, decimals) {
if (!(this instanceof FLOAT)) return new FLOAT(options); super(typeof length === 'object' && length || { length, decimals });
NUMBER.call(this, options);
}
inherits(FLOAT, NUMBER);
FLOAT.prototype.key = FLOAT.key = 'FLOAT';
FLOAT.prototype.validate = function validate(value) {
if (!Validator.isFloat(String(value))) {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid float', value));
} }
validate(value) {
return true; if (!Validator.isFloat(String(value))) {
}; throw new sequelizeErrors.ValidationError(util.format('%j is not a valid float', value));
}
return true;
}
}
/** /**
* Floating point number (4-byte precision). * Floating point number (4-byte precision).
...@@ -359,14 +324,11 @@ FLOAT.prototype.validate = function validate(value) { ...@@ -359,14 +324,11 @@ FLOAT.prototype.validate = function validate(value) {
* *
* @namespace DataTypes.REAL * @namespace DataTypes.REAL
*/ */
function REAL(length, decimals) { class REAL extends NUMBER {
const options = typeof length === 'object' && length || { length, decimals }; constructor(length, decimals) {
if (!(this instanceof REAL)) return new REAL(options); super(typeof length === 'object' && length || { length, decimals });
NUMBER.call(this, options); }
} }
inherits(REAL, NUMBER);
REAL.prototype.key = REAL.key = 'REAL';
/** /**
* Floating point number (8-byte precision). * Floating point number (8-byte precision).
...@@ -376,14 +338,11 @@ REAL.prototype.key = REAL.key = 'REAL'; ...@@ -376,14 +338,11 @@ REAL.prototype.key = REAL.key = 'REAL';
* *
* @namespace DataTypes.DOUBLE * @namespace DataTypes.DOUBLE
*/ */
function DOUBLE(length, decimals) { class DOUBLE extends NUMBER {
const options = typeof length === 'object' && length || { length, decimals }; constructor(length, decimals) {
if (!(this instanceof DOUBLE)) return new DOUBLE(options); super(typeof length === 'object' && length || { length, decimals });
NUMBER.call(this, options); }
} }
inherits(DOUBLE, NUMBER);
DOUBLE.prototype.key = DOUBLE.key = 'DOUBLE PRECISION';
/** /**
* Decimal type, variable precision, take length as specified by user * Decimal type, variable precision, take length as specified by user
...@@ -393,30 +352,25 @@ DOUBLE.prototype.key = DOUBLE.key = 'DOUBLE PRECISION'; ...@@ -393,30 +352,25 @@ DOUBLE.prototype.key = DOUBLE.key = 'DOUBLE PRECISION';
* *
* @namespace DataTypes.DECIMAL * @namespace DataTypes.DECIMAL
*/ */
function DECIMAL(precision, scale) { class DECIMAL extends NUMBER {
const options = typeof precision === 'object' && precision || { precision, scale }; constructor(precision, scale) {
if (!(this instanceof DECIMAL)) return new DECIMAL(options); super(typeof precision === 'object' && precision || { precision, scale });
NUMBER.call(this, options);
}
inherits(DECIMAL, NUMBER);
DECIMAL.prototype.key = DECIMAL.key = 'DECIMAL';
DECIMAL.prototype.toSql = function toSql() {
if (this._precision || this._scale) {
return `DECIMAL(${[this._precision, this._scale].filter(_.identity).join(',')})`;
} }
toSql() {
return 'DECIMAL'; if (this._precision || this._scale) {
}; return `DECIMAL(${[this._precision, this._scale].filter(_.identity).join(',')})`;
DECIMAL.prototype.validate = function validate(value) { }
if (!Validator.isDecimal(String(value))) { return 'DECIMAL';
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid decimal', value));
} }
validate(value) {
if (!Validator.isDecimal(String(value))) {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid decimal', value));
}
return true;
}
}
return true; // TODO: Create intermediate class
};
const protoExtensions = { const protoExtensions = {
escape: false, escape: false,
_value(value) { _value(value) {
...@@ -447,43 +401,37 @@ for (const floating of [FLOAT, DOUBLE, REAL]) { ...@@ -447,43 +401,37 @@ for (const floating of [FLOAT, DOUBLE, REAL]) {
* *
* @namespace DataTypes.BOOLEAN * @namespace DataTypes.BOOLEAN
*/ */
function BOOLEAN() { class BOOLEAN extends ABSTRACT {
if (!(this instanceof BOOLEAN)) return new BOOLEAN(); toSql() {
} return 'TINYINT(1)';
inherits(BOOLEAN, ABSTRACT);
BOOLEAN.prototype.key = BOOLEAN.key = 'BOOLEAN';
BOOLEAN.prototype.toSql = function toSql() {
return 'TINYINT(1)';
};
BOOLEAN.prototype.validate = function validate(value) {
if (!Validator.isBoolean(String(value))) {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid boolean', value));
} }
validate(value) {
return true; if (!Validator.isBoolean(String(value))) {
}; throw new sequelizeErrors.ValidationError(util.format('%j is not a valid boolean', value));
BOOLEAN.prototype._sanitize = function _sanitize(value) {
if (value !== null && value !== undefined) {
if (Buffer.isBuffer(value) && value.length === 1) {
// Bit fields are returned as buffers
value = value[0];
}
const type = typeof value;
if (type === 'string') {
// Only take action on valid boolean strings.
return value === 'true' ? true : value === 'false' ? false : value;
} }
if (type === 'number') { return true;
// Only take action on valid boolean integers. }
return value === 1 ? true : value === 0 ? false : value; _sanitize(value) {
if (value !== null && value !== undefined) {
if (Buffer.isBuffer(value) && value.length === 1) {
// Bit fields are returned as buffers
value = value[0];
}
const type = typeof value;
if (type === 'string') {
// Only take action on valid boolean strings.
return value === 'true' ? true : value === 'false' ? false : value;
}
if (type === 'number') {
// Only take action on valid boolean integers.
return value === 1 ? true : value === 0 ? false : value;
}
} }
return value;
} }
}
return value;
};
BOOLEAN.parse = BOOLEAN.prototype._sanitize; BOOLEAN.parse = BOOLEAN.prototype._sanitize;
/** /**
...@@ -491,15 +439,11 @@ BOOLEAN.parse = BOOLEAN.prototype._sanitize; ...@@ -491,15 +439,11 @@ BOOLEAN.parse = BOOLEAN.prototype._sanitize;
* *
* @namespace DataTypes.TIME * @namespace DataTypes.TIME
*/ */
function TIME() { class TIME extends ABSTRACT {
if (!(this instanceof TIME)) return new TIME(); toSql() {
return 'TIME';
}
} }
inherits(TIME, ABSTRACT);
TIME.prototype.key = TIME.key = 'TIME';
TIME.prototype.toSql = function toSql() {
return 'TIME';
};
/** /**
* Date column with timezone, default is UTC * Date column with timezone, default is UTC
...@@ -508,174 +452,129 @@ TIME.prototype.toSql = function toSql() { ...@@ -508,174 +452,129 @@ TIME.prototype.toSql = function toSql() {
* *
* @namespace DataTypes.DATE * @namespace DataTypes.DATE
*/ */
function DATE(length) { class DATE extends ABSTRACT {
const options = typeof length === 'object' && length || { length }; constructor(length) {
super();
if (!(this instanceof DATE)) return new DATE(options); const options = typeof length === 'object' && length || { length };
this.options = options;
this.options = options; this._length = options.length || '';
this._length = options.length || '';
}
inherits(DATE, ABSTRACT);
DATE.prototype.key = DATE.key = 'DATE';
DATE.prototype.toSql = function toSql() {
return 'DATETIME';
};
DATE.prototype.validate = function validate(value) {
if (!Validator.isDate(String(value))) {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid date', value));
} }
toSql() {
return true; return 'DATETIME';
};
DATE.prototype._sanitize = function _sanitize(value, options) {
if ((!options || options && !options.raw) && !(value instanceof Date) && !!value) {
return new Date(value);
} }
validate(value) {
return value; if (!Validator.isDate(String(value))) {
}; throw new sequelizeErrors.ValidationError(util.format('%j is not a valid date', value));
}
DATE.prototype._isChanged = function _isChanged(value, originalValue) { return true;
if (
originalValue && !!value &&
(
value === originalValue ||
value instanceof Date && originalValue instanceof Date && value.getTime() === originalValue.getTime()
)
) {
return false;
} }
_sanitize(value, options) {
// not changed when set to same empty value if ((!options || options && !options.raw) && !(value instanceof Date) && !!value) {
if (!originalValue && !value && originalValue === value) { return new Date(value);
return false; }
return value;
} }
_isChanged(value, originalValue) {
return true; if (originalValue && !!value &&
}; (value === originalValue ||
value instanceof Date && originalValue instanceof Date && value.getTime() === originalValue.getTime())) {
DATE.prototype._applyTimezone = function _applyTimezone(date, options) { return false;
if (options.timezone) { }
if (momentTz.tz.zone(options.timezone)) { // not changed when set to same empty value
return momentTz(date).tz(options.timezone); if (!originalValue && !value && originalValue === value) {
return false;
} }
return date = moment(date).utcOffset(options.timezone); return true;
} }
return momentTz(date); _applyTimezone(date, options) {
}; if (options.timezone) {
if (momentTz.tz.zone(options.timezone)) {
DATE.prototype._stringify = function _stringify(date, options) { return momentTz(date).tz(options.timezone);
date = this._applyTimezone(date, options); }
return date = moment(date).utcOffset(options.timezone);
// Z here means current timezone, _not_ UTC }
return date.format('YYYY-MM-DD HH:mm:ss.SSS Z'); return momentTz(date);
}; }
_stringify(date, options) {
date = this._applyTimezone(date, options);
// Z here means current timezone, _not_ UTC
return date.format('YYYY-MM-DD HH:mm:ss.SSS Z');
}
}
/** /**
* A date only column (no timestamp) * A date only column (no timestamp)
* *
* @namespace DataTypes.DATEONLY * @namespace DataTypes.DATEONLY
*/ */
function DATEONLY() { class DATEONLY extends ABSTRACT {
if (!(this instanceof DATEONLY)) return new DATEONLY(); toSql() {
} return 'DATE';
util.inherits(DATEONLY, ABSTRACT);
DATEONLY.prototype.key = DATEONLY.key = 'DATEONLY';
DATEONLY.prototype.toSql = function() {
return 'DATE';
};
DATEONLY.prototype._stringify = function _stringify(date) {
return moment(date).format('YYYY-MM-DD');
};
DATEONLY.prototype._sanitize = function _sanitize(value, options) {
if ((!options || options && !options.raw) && !!value) {
return moment(value).format('YYYY-MM-DD');
} }
_stringify(date) {
return value; return moment(date).format('YYYY-MM-DD');
};
DATEONLY.prototype._isChanged = function _isChanged(value, originalValue) {
if (originalValue && !!value && originalValue === value) {
return false;
} }
_sanitize(value, options) {
// not changed when set to same empty value if ((!options || options && !options.raw) && !!value) {
if (!originalValue && !value && originalValue === value) { return moment(value).format('YYYY-MM-DD');
return false; }
return value;
} }
_isChanged(value, originalValue) {
return true; if (originalValue && !!value && originalValue === value) {
}; return false;
}
// not changed when set to same empty value
if (!originalValue && !value && originalValue === value) {
return false;
}
return true;
}
}
/** /**
* A key / value store column. Only available in Postgres. * A key / value store column. Only available in Postgres.
* *
* @namespace DataTypes.HSTORE * @namespace DataTypes.HSTORE
*/ */
function HSTORE() { class HSTORE extends ABSTRACT {
if (!(this instanceof HSTORE)) return new HSTORE(); validate(value) {
} if (!_.isPlainObject(value)) {
inherits(HSTORE, ABSTRACT); throw new sequelizeErrors.ValidationError(util.format('%j is not a valid hstore', value));
}
HSTORE.prototype.key = HSTORE.key = 'HSTORE'; return true;
HSTORE.prototype.validate = function validate(value) {
if (!_.isPlainObject(value)) {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid hstore', value));
} }
}
return true;
};
/** /**
* A JSON string column. Available in MySQL, Postgres and SQLite * A JSON string column. Available in MySQL, Postgres and SQLite
* *
* @namespace DataTypes.JSON * @namespace DataTypes.JSON
*/ */
function JSONTYPE() { class JSONTYPE extends ABSTRACT {
if (!(this instanceof JSONTYPE)) return new JSONTYPE(); validate() {
return true;
}
_stringify(value) {
return JSON.stringify(value);
}
} }
inherits(JSONTYPE, ABSTRACT);
JSONTYPE.prototype.key = JSONTYPE.key = 'JSON';
JSONTYPE.prototype.validate = function validate() {
return true;
};
JSONTYPE.prototype._stringify = function _stringify(value) {
return JSON.stringify(value);
};
/** /**
* A binary storage JSON column. Only available in Postgres. * A binary storage JSON column. Only available in Postgres.
* *
* @namespace DataTypes.JSONB * @namespace DataTypes.JSONB
*/ */
function JSONB() { class JSONB extends JSONTYPE {
if (!(this instanceof JSONB)) return new JSONB();
JSONTYPE.call(this);
} }
inherits(JSONB, JSONTYPE);
JSONB.prototype.key = JSONB.key = 'JSONB';
/** /**
* A default value of the current timestamp * A default value of the current timestamp
* *
* @namespace DataTypes.NOW * @namespace DataTypes.NOW
*/ */
function NOW() { class NOW extends ABSTRACT {
if (!(this instanceof NOW)) return new NOW();
} }
inherits(NOW, ABSTRACT);
NOW.prototype.key = NOW.key = 'NOW';
/** /**
* Binary storage * Binary storage
...@@ -685,63 +584,61 @@ NOW.prototype.key = NOW.key = 'NOW'; ...@@ -685,63 +584,61 @@ NOW.prototype.key = NOW.key = 'NOW';
* @namespace DataTypes.BLOB * @namespace DataTypes.BLOB
* *
*/ */
function BLOB(length) { class BLOB extends ABSTRACT {
const options = typeof length === 'object' && length || { length }; constructor(length) {
if (!(this instanceof BLOB)) return new BLOB(options); super();
this.options = options; const options = typeof length === 'object' && length || { length };
this._length = options.length || ''; this.options = options;
} this._length = options.length || '';
inherits(BLOB, ABSTRACT);
BLOB.prototype.key = BLOB.key = 'BLOB';
BLOB.prototype.toSql = function toSql() {
switch (this._length.toLowerCase()) {
case 'tiny':
return 'TINYBLOB';
case 'medium':
return 'MEDIUMBLOB';
case 'long':
return 'LONGBLOB';
default:
return this.key;
} }
}; toSql() {
BLOB.prototype.validate = function validate(value) { switch (this._length.toLowerCase()) {
if (typeof value !== 'string' && !Buffer.isBuffer(value)) { case 'tiny':
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid blob', value)); return 'TINYBLOB';
case 'medium':
return 'MEDIUMBLOB';
case 'long':
return 'LONGBLOB';
default:
return this.key;
}
} }
validate(value) {
return true; if (typeof value !== 'string' && !Buffer.isBuffer(value)) {
}; throw new sequelizeErrors.ValidationError(util.format('%j is not a valid blob', value));
BLOB.prototype.escape = false;
BLOB.prototype._stringify = function _stringify(value) {
if (!Buffer.isBuffer(value)) {
if (Array.isArray(value)) {
value = Buffer.from(value);
} else {
value = Buffer.from(value.toString());
} }
return true;
} }
const hex = value.toString('hex'); _stringify(value) {
if (!Buffer.isBuffer(value)) {
return this._hexify(hex); if (Array.isArray(value)) {
}; value = Buffer.from(value);
}
BLOB.prototype._hexify = function _hexify(hex) { else {
return `X'${hex}'`; value = Buffer.from(value.toString());
}; }
BLOB.prototype._bindParam = function _bindParam(value, options) {
if (!Buffer.isBuffer(value)) {
if (Array.isArray(value)) {
value = Buffer.from(value);
} else {
value = Buffer.from(value.toString());
} }
const hex = value.toString('hex');
return this._hexify(hex);
} }
return options.bindParam(value); _hexify(hex) {
}; return `X'${hex}'`;
}
_bindParam(value, options) {
if (!Buffer.isBuffer(value)) {
if (Array.isArray(value)) {
value = Buffer.from(value);
}
else {
value = Buffer.from(value.toString());
}
}
return options.bindParam(value);
}
}
BLOB.prototype.escape = false;
/** /**
* Range types are data types representing a range of values of some element type (called the range's subtype). * Range types are data types representing a range of values of some element type (called the range's subtype).
...@@ -751,34 +648,28 @@ BLOB.prototype._bindParam = function _bindParam(value, options) { ...@@ -751,34 +648,28 @@ BLOB.prototype._bindParam = function _bindParam(value, options) {
* *
* @namespace DataTypes.RANGE * @namespace DataTypes.RANGE
*/ */
function RANGE(subtype) { class RANGE extends ABSTRACT {
const options = _.isPlainObject(subtype) ? subtype : { subtype }; constructor(subtype) {
super();
if (!options.subtype) options.subtype = new INTEGER(); const options = _.isPlainObject(subtype) ? subtype : { subtype };
if (!options.subtype)
if (typeof options.subtype === 'function') { options.subtype = new INTEGER();
options.subtype = new options.subtype(); if (typeof options.subtype === 'function') {
} options.subtype = new options.subtype();
}
if (!(this instanceof RANGE)) return new RANGE(options); this._subtype = options.subtype.key;
this.options = options;
this._subtype = options.subtype.key;
this.options = options;
}
inherits(RANGE, ABSTRACT);
RANGE.prototype.key = RANGE.key = 'RANGE';
RANGE.prototype.validate = function validate(value) {
if (!Array.isArray(value)) {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid range', value));
} }
validate(value) {
if (value.length !== 2) { if (!Array.isArray(value)) {
throw new sequelizeErrors.ValidationError('A range must be an array with two elements'); throw new sequelizeErrors.ValidationError(util.format('%j is not a valid range', value));
}
if (value.length !== 2) {
throw new sequelizeErrors.ValidationError('A range must be an array with two elements');
}
return true;
} }
}
return true;
};
/** /**
* A column storing a unique universal identifier. * A column storing a unique universal identifier.
...@@ -786,57 +677,42 @@ RANGE.prototype.validate = function validate(value) { ...@@ -786,57 +677,42 @@ RANGE.prototype.validate = function validate(value) {
* *
* @namespace DataTypes.UUID * @namespace DataTypes.UUID
*/ */
function UUID() { class UUID extends ABSTRACT {
if (!(this instanceof UUID)) return new UUID(); validate(value, options) {
} if (typeof value !== 'string' || !Validator.isUUID(value) && (!options || !options.acceptStrings)) {
inherits(UUID, ABSTRACT); throw new sequelizeErrors.ValidationError(util.format('%j is not a valid uuid', value));
}
UUID.prototype.key = UUID.key = 'UUID'; return true;
UUID.prototype.validate = function validate(value, options) {
if (typeof value !== 'string' || !Validator.isUUID(value) && (!options || !options.acceptStrings)) {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid uuid', value));
} }
}
return true;
};
/** /**
* A default unique universal identifier generated following the UUID v1 standard * A default unique universal identifier generated following the UUID v1 standard
* *
* @namespace DataTypes.UUIDV1 * @namespace DataTypes.UUIDV1
*/ */
function UUIDV1() { class UUIDV1 extends ABSTRACT {
if (!(this instanceof UUIDV1)) return new UUIDV1(); validate(value, options) {
} if (typeof value !== 'string' || !Validator.isUUID(value) && (!options || !options.acceptStrings)) {
inherits(UUIDV1, ABSTRACT); throw new sequelizeErrors.ValidationError(util.format('%j is not a valid uuid', value));
}
UUIDV1.prototype.key = UUIDV1.key = 'UUIDV1'; return true;
UUIDV1.prototype.validate = function validate(value, options) {
if (typeof value !== 'string' || !Validator.isUUID(value) && (!options || !options.acceptStrings)) {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid uuid', value));
} }
}
return true;
};
/** /**
* A default unique universal identifier generated following the UUID v4 standard * A default unique universal identifier generated following the UUID v4 standard
* *
* @namespace DataTypes.UUIDV4 * @namespace DataTypes.UUIDV4
*/ */
function UUIDV4() { class UUIDV4 extends ABSTRACT {
if (!(this instanceof UUIDV4)) return new UUIDV4(); validate(value, options) {
} if (typeof value !== 'string' || !Validator.isUUID(value, 4) && (!options || !options.acceptStrings)) {
inherits(UUIDV4, ABSTRACT); throw new sequelizeErrors.ValidationError(util.format('%j is not a valid uuidv4', value));
}
UUIDV4.prototype.key = UUIDV4.key = 'UUIDV4'; return true;
UUIDV4.prototype.validate = function validate(value, options) {
if (typeof value !== 'string' || !Validator.isUUID(value, 4) && (!options || !options.acceptStrings)) {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid uuidv4', value));
} }
}
return true;
};
/** /**
* A virtual value that is not stored in the DB. This could for example be useful if you want to provide a default value in your model that is returned to the user but not stored in the DB. * A virtual value that is not stored in the DB. This could for example be useful if you want to provide a default value in your model that is returned to the user but not stored in the DB.
...@@ -883,16 +759,15 @@ UUIDV4.prototype.validate = function validate(value, options) { ...@@ -883,16 +759,15 @@ UUIDV4.prototype.validate = function validate(value, options) {
* @namespace DataTypes.VIRTUAL * @namespace DataTypes.VIRTUAL
* *
*/ */
function VIRTUAL(ReturnType, fields) { class VIRTUAL extends ABSTRACT {
if (!(this instanceof VIRTUAL)) return new VIRTUAL(ReturnType, fields); constructor(ReturnType, fields) {
if (typeof ReturnType === 'function') ReturnType = new ReturnType(); super();
if (typeof ReturnType === 'function')
this.returnType = ReturnType; ReturnType = new ReturnType();
this.fields = fields; this.returnType = ReturnType;
this.fields = fields;
}
} }
inherits(VIRTUAL, ABSTRACT);
VIRTUAL.prototype.key = VIRTUAL.key = 'VIRTUAL';
/** /**
* An enumeration, Postgres Only * An enumeration, Postgres Only
...@@ -909,27 +784,25 @@ VIRTUAL.prototype.key = VIRTUAL.key = 'VIRTUAL'; ...@@ -909,27 +784,25 @@ VIRTUAL.prototype.key = VIRTUAL.key = 'VIRTUAL';
* @namespace DataTypes.ENUM * @namespace DataTypes.ENUM
* *
*/ */
function ENUM(...args) { class ENUM extends ABSTRACT {
const value = args[0]; constructor(...args) {
const options = typeof value === 'object' && !Array.isArray(value) && value || { super();
values: args.reduce((result, element) => { const value = args[0];
return result.concat(Array.isArray(element) ? element : [element]); const options = typeof value === 'object' && !Array.isArray(value) && value || {
}, []) values: args.reduce((result, element) => {
}; return result.concat(Array.isArray(element) ? element : [element]);
if (!(this instanceof ENUM)) return new ENUM(options); }, [])
this.values = options.values; };
this.options = options; this.values = options.values;
} this.options = options;
inherits(ENUM, ABSTRACT);
ENUM.prototype.key = ENUM.key = 'ENUM';
ENUM.prototype.validate = function validate(value) {
if (!this.values.includes(value)) {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid choice in %j', value, this.values));
} }
validate(value) {
return true; if (!this.values.includes(value)) {
}; throw new sequelizeErrors.ValidationError(util.format('%j is not a valid choice in %j', value, this.values));
}
return true;
}
}
/** /**
* An array of `type`. Only available in Postgres. * An array of `type`. Only available in Postgres.
...@@ -941,27 +814,25 @@ ENUM.prototype.validate = function validate(value) { ...@@ -941,27 +814,25 @@ ENUM.prototype.validate = function validate(value) {
* *
* @namespace DataTypes.ARRAY * @namespace DataTypes.ARRAY
*/ */
function ARRAY(type) { class ARRAY extends ABSTRACT {
const options = _.isPlainObject(type) ? type : { type }; constructor(type) {
if (!(this instanceof ARRAY)) return new ARRAY(options); super();
this.type = typeof options.type === 'function' ? new options.type() : options.type; const options = _.isPlainObject(type) ? type : { type };
} this.type = typeof options.type === 'function' ? new options.type() : options.type;
inherits(ARRAY, ABSTRACT);
ARRAY.prototype.key = ARRAY.key = 'ARRAY';
ARRAY.prototype.toSql = function toSql() {
return `${this.type.toSql()}[]`;
};
ARRAY.prototype.validate = function validate(value) {
if (!Array.isArray(value)) {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid array', value));
} }
toSql() {
return true; return `${this.type.toSql()}[]`;
}; }
ARRAY.is = function is(obj, type) { validate(value) {
return obj instanceof ARRAY && obj.type instanceof type; if (!Array.isArray(value)) {
}; throw new sequelizeErrors.ValidationError(util.format('%j is not a valid array', value));
}
return true;
}
static is(obj, type) {
return obj instanceof ARRAY && obj.type instanceof type;
}
}
/** /**
* A column storing Geometry information. * A column storing Geometry information.
...@@ -1012,26 +883,23 @@ ARRAY.is = function is(obj, type) { ...@@ -1012,26 +883,23 @@ ARRAY.is = function is(obj, type) {
* @see {@link DataTypes.GEOGRAPHY} * @see {@link DataTypes.GEOGRAPHY}
* @namespace DataTypes.GEOMETRY * @namespace DataTypes.GEOMETRY
*/ */
function GEOMETRY(type, srid) { class GEOMETRY extends ABSTRACT {
const options = _.isPlainObject(type) ? type : { type, srid }; constructor(type, srid) {
super();
if (!(this instanceof GEOMETRY)) return new GEOMETRY(options); const options = _.isPlainObject(type) ? type : { type, srid };
this.options = options;
this.options = options; this.type = options.type;
this.type = options.type; this.srid = options.srid;
this.srid = options.srid; }
_stringify(value, options) {
return `GeomFromText(${options.escape(wkx.Geometry.parseGeoJSON(value).toWkt())})`;
}
_bindParam(value, options) {
return `GeomFromText(${options.bindParam(wkx.Geometry.parseGeoJSON(value).toWkt())})`;
}
} }
inherits(GEOMETRY, ABSTRACT);
GEOMETRY.prototype.key = GEOMETRY.key = 'GEOMETRY';
GEOMETRY.prototype.escape = false; GEOMETRY.prototype.escape = false;
GEOMETRY.prototype._stringify = function _stringify(value, options) {
return `GeomFromText(${options.escape(wkx.Geometry.parseGeoJSON(value).toWkt())})`;
};
GEOMETRY.prototype._bindParam = function _bindParam(value, options) {
return `GeomFromText(${options.bindParam(wkx.Geometry.parseGeoJSON(value).toWkt())})`;
};
/** /**
* A geography datatype represents two dimensional spacial objects in an elliptic coord system. * A geography datatype represents two dimensional spacial objects in an elliptic coord system.
...@@ -1059,26 +927,24 @@ GEOMETRY.prototype._bindParam = function _bindParam(value, options) { ...@@ -1059,26 +927,24 @@ GEOMETRY.prototype._bindParam = function _bindParam(value, options) {
* *
* @namespace DataTypes.GEOGRAPHY * @namespace DataTypes.GEOGRAPHY
*/ */
function GEOGRAPHY(type, srid) { class GEOGRAPHY extends ABSTRACT {
const options = _.isPlainObject(type) ? type : { type, srid }; constructor(type, srid) {
super();
if (!(this instanceof GEOGRAPHY)) return new GEOGRAPHY(options); const options = _.isPlainObject(type) ? type : { type, srid };
this.options = options;
this.options = options; this.type = options.type;
this.type = options.type; this.srid = options.srid;
this.srid = options.srid; }
_stringify(value, options) {
return `GeomFromText(${options.escape(wkx.Geometry.parseGeoJSON(value).toWkt())})`;
}
_bindParam(value, options) {
return `GeomFromText(${options.bindParam(wkx.Geometry.parseGeoJSON(value).toWkt())})`;
}
} }
inherits(GEOGRAPHY, ABSTRACT);
GEOGRAPHY.prototype.key = GEOGRAPHY.key = 'GEOGRAPHY';
GEOGRAPHY.prototype.escape = false; GEOGRAPHY.prototype.escape = false;
GEOGRAPHY.prototype._stringify = function _stringify(value, options) {
return `GeomFromText(${options.escape(wkx.Geometry.parseGeoJSON(value).toWkt())})`;
};
GEOGRAPHY.prototype._bindParam = function _bindParam(value, options) {
return `GeomFromText(${options.bindParam(wkx.Geometry.parseGeoJSON(value).toWkt())})`;
};
/** /**
* The cidr type holds an IPv4 or IPv6 network specification. Takes 7 or 19 bytes. * The cidr type holds an IPv4 or IPv6 network specification. Takes 7 or 19 bytes.
...@@ -1087,20 +953,14 @@ GEOGRAPHY.prototype._bindParam = function _bindParam(value, options) { ...@@ -1087,20 +953,14 @@ GEOGRAPHY.prototype._bindParam = function _bindParam(value, options) {
* *
* @namespace DataTypes.CIDR * @namespace DataTypes.CIDR
*/ */
function CIDR() { class CIDR extends ABSTRACT {
if (!(this instanceof CIDR)) return new CIDR(); validate(value) {
} if (typeof value !== 'string' || !Validator.isIPRange(value)) {
inherits(CIDR, ABSTRACT); throw new sequelizeErrors.ValidationError(util.format('%j is not a valid CIDR', value));
}
CIDR.prototype.key = CIDR.key = 'CIDR'; return true;
CIDR.prototype.validate = function validate(value) {
if (typeof value !== 'string' || !Validator.isIPRange(value)) {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid CIDR', value));
} }
}
return true;
};
/** /**
* The INET type holds an IPv4 or IPv6 host address, and optionally its subnet. Takes 7 or 19 bytes * The INET type holds an IPv4 or IPv6 host address, and optionally its subnet. Takes 7 or 19 bytes
...@@ -1109,20 +969,14 @@ CIDR.prototype.validate = function validate(value) { ...@@ -1109,20 +969,14 @@ CIDR.prototype.validate = function validate(value) {
* *
* @namespace DataTypes.INET * @namespace DataTypes.INET
*/ */
function INET() { class INET extends ABSTRACT {
if (!(this instanceof INET)) return new INET(); validate(value) {
} if (typeof value !== 'string' || !Validator.isIP(value)) {
inherits(INET, ABSTRACT); throw new sequelizeErrors.ValidationError(util.format('%j is not a valid INET', value));
}
INET.prototype.key = INET.key = 'INET'; return true;
INET.prototype.validate = function validate(value) {
if (typeof value !== 'string' || !Validator.isIP(value)) {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid INET', value));
} }
}
return true;
};
/** /**
* The MACADDR type stores MAC addresses. Takes 6 bytes * The MACADDR type stores MAC addresses. Takes 6 bytes
...@@ -1131,42 +985,12 @@ INET.prototype.validate = function validate(value) { ...@@ -1131,42 +985,12 @@ INET.prototype.validate = function validate(value) {
* *
* @namespace DataTypes.MACADDR * @namespace DataTypes.MACADDR
*/ */
function MACADDR() { class MACADDR extends ABSTRACT {
if (!(this instanceof MACADDR)) return new MACADDR(); validate(value) {
} if (typeof value !== 'string' || !Validator.isMACAddress(value)) {
inherits(MACADDR, ABSTRACT); throw new sequelizeErrors.ValidationError(util.format('%j is not a valid MACADDR', value));
MACADDR.prototype.key = MACADDR.key = 'MACADDR';
MACADDR.prototype.validate = function validate(value) {
if (typeof value !== 'string' || !Validator.isMACAddress(value)) {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid MACADDR', value));
}
return true;
};
const helpers = {
BINARY: [STRING, CHAR],
UNSIGNED: [NUMBER, TINYINT, SMALLINT, MEDIUMINT, INTEGER, BIGINT, FLOAT, DOUBLE, REAL, DECIMAL],
ZEROFILL: [NUMBER, TINYINT, SMALLINT, MEDIUMINT, INTEGER, BIGINT, FLOAT, DOUBLE, REAL, DECIMAL],
PRECISION: [DECIMAL],
SCALE: [DECIMAL]
};
for (const helper of Object.keys(helpers)) {
for (const DataType of helpers[helper]) {
if (!DataType[helper]) {
Object.defineProperty(DataType, helper, {
get() {
const dataType = new DataType();
if (typeof dataType[helper] === 'object') {
return dataType;
}
return dataType[helper].apply(dataType, arguments);
}
});
} }
return true;
} }
} }
...@@ -1248,8 +1072,8 @@ const DataTypes = module.exports = { ...@@ -1248,8 +1072,8 @@ const DataTypes = module.exports = {
ENUM, ENUM,
RANGE, RANGE,
REAL, REAL,
DOUBLE,
'DOUBLE PRECISION': DOUBLE, 'DOUBLE PRECISION': DOUBLE,
DOUBLE,
GEOMETRY, GEOMETRY,
GEOGRAPHY, GEOGRAPHY,
CIDR, CIDR,
...@@ -1258,14 +1082,35 @@ const DataTypes = module.exports = { ...@@ -1258,14 +1082,35 @@ const DataTypes = module.exports = {
CITEXT CITEXT
}; };
_.each(DataTypes, dataType => {
dataType.types = {}; _.each(DataTypes, (dataType, name) => {
// guard for aliases
if (!dataType.hasOwnProperty('key')) {
dataType.types = {};
dataType.key = dataType.prototype.key = name;
}
}); });
DataTypes.postgres = require('./dialects/postgres/data-types')(DataTypes); const dialectNames = ['postgres', 'mysql', 'mariadb', 'sqlite', 'mssql'];
DataTypes.mysql = require('./dialects/mysql/data-types')(DataTypes); const dialectMap = {};
DataTypes.mariadb = require('./dialects/mariadb/data-types')(DataTypes); for (const d of dialectNames) {
DataTypes.sqlite = require('./dialects/sqlite/data-types')(DataTypes); dialectMap[d] = require(`./dialects/${d}/data-types`)(DataTypes);
DataTypes.mssql = require('./dialects/mssql/data-types')(DataTypes); }
const dialectList = _.values(dialectMap);
for (const dataTypes of dialectList) {
_.each(dataTypes, (DataType, key) => {
if (!DataType.key) {
DataType.key = DataType.prototype.key = key;
}
});
}
// Wrap all data types to not require `new`
for (const dataTypes of [DataTypes, ...dialectList]) {
_.each(dataTypes, (DataType, key) => {
dataTypes[key] = classToInvokable(DataType);
});
}
module.exports = DataTypes; Object.assign(DataTypes, dialectMap);
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
const _ = require('lodash'); const _ = require('lodash');
const moment = require('moment-timezone'); const moment = require('moment-timezone');
const inherits = require('../../utils/inherits');
module.exports = BaseTypes => { module.exports = BaseTypes => {
BaseTypes.ABSTRACT.prototype.dialectTypes = 'https://mariadb.com/kb/en/library/resultset/#field-types'; BaseTypes.ABSTRACT.prototype.dialectTypes = 'https://mariadb.com/kb/en/library/resultset/#field-types';
...@@ -35,138 +34,83 @@ module.exports = BaseTypes => { ...@@ -35,138 +34,83 @@ module.exports = BaseTypes => {
BaseTypes.GEOMETRY.types.mariadb = ['GEOMETRY']; BaseTypes.GEOMETRY.types.mariadb = ['GEOMETRY'];
BaseTypes.JSON.types.mariadb = ['JSON']; BaseTypes.JSON.types.mariadb = ['JSON'];
function DECIMAL(precision, scale) { class DECIMAL extends BaseTypes.DECIMAL {
if (!(this instanceof DECIMAL)) { toSql() {
return new DECIMAL(precision, scale); let definition = super.toSql();
if (this._unsigned) {
definition += ' UNSIGNED';
}
if (this._zerofill) {
definition += ' ZEROFILL';
}
return definition;
} }
BaseTypes.DECIMAL.apply(this, arguments);
} }
inherits(DECIMAL, BaseTypes.DECIMAL); class DATE extends BaseTypes.DATE {
toSql() {
DECIMAL.prototype.toSql = function toSql() { return `DATETIME${this._length ? `(${this._length})` : ''}`;
let definition = BaseTypes.DECIMAL.prototype.toSql.apply(this);
if (this._unsigned) {
definition += ' UNSIGNED';
} }
_stringify(date, options) {
if (this._zerofill) { date = this._applyTimezone(date, options);
definition += ' ZEROFILL'; return date.format('YYYY-MM-DD HH:mm:ss.SSS');
} }
static parse(value, options) {
return definition; value = value.string();
}; if (value === null) {
return value;
function DATE(length) { }
if (!(this instanceof DATE)) { if (moment.tz.zone(options.timezone)) {
return new DATE(length); value = moment.tz(value, options.timezone).toDate();
} }
BaseTypes.DATE.apply(this, arguments); else {
} value = new Date(`${value} ${options.timezone}`);
}
inherits(DATE, BaseTypes.DATE);
DATE.prototype.toSql = function toSql() {
return `DATETIME${this._length ? `(${this._length})` : ''}`;
};
DATE.prototype._stringify = function _stringify(date, options) {
date = BaseTypes.DATE.prototype._applyTimezone(date, options);
return date.format('YYYY-MM-DD HH:mm:ss.SSS');
};
DATE.parse = function parse(value, options) {
value = value.string();
if (value === null) {
return value; return value;
} }
if (moment.tz.zone(options.timezone)) {
value = moment.tz(value, options.timezone).toDate();
} else {
value = new Date(`${value} ${options.timezone}`);
}
return value;
};
function DATEONLY() {
if (!(this instanceof DATEONLY)) {
return new DATEONLY();
}
BaseTypes.DATEONLY.call(this);
} }
inherits(DATEONLY, BaseTypes.DATEONLY); class DATEONLY extends BaseTypes.DATEONLY {
static parse(value) {
DATEONLY.parse = function parse(value) { return value.string();
return value.string(); };
}; }
function UUID() { class UUID extends BaseTypes.UUID {
if (!(this instanceof UUID)) { toSql() {
return new UUID(); return 'CHAR(36) BINARY';
} }
BaseTypes.UUID.call(this);
} }
inherits(UUID, BaseTypes.UUID); class GEOMETRY extends BaseTypes.GEOMETRY {
constructor(type, srid) {
UUID.prototype.toSql = function toSql() { super(type, srid);
return 'CHAR(36) BINARY'; if (_.isEmpty(this.type)) {
}; this.sqlType = this.key;
}
function GEOMETRY(type, srid) { else {
if (!(this instanceof GEOMETRY)) { this.sqlType = this.type;
return new GEOMETRY(type, srid); }
} }
BaseTypes.GEOMETRY.apply(this, arguments); toSql() {
return this.sqlType;
if (_.isEmpty(this.type)) {
this.sqlType = this.key;
} else {
this.sqlType = this.type;
} }
} }
inherits(GEOMETRY, BaseTypes.GEOMETRY); class ENUM extends BaseTypes.ENUM {
toSql(options) {
GEOMETRY.prototype.toSql = function toSql() { return `ENUM(${this.values.map(value => options.escape(value)).join(', ')})`;
return this.sqlType;
};
function ENUM() {
if (!(this instanceof ENUM)) {
const obj = Object.create(ENUM.prototype);
ENUM.apply(obj, arguments);
return obj;
} }
BaseTypes.ENUM.apply(this, arguments);
} }
inherits(ENUM, BaseTypes.ENUM); class JSONTYPE extends BaseTypes.JSON {
_stringify(value, options) {
ENUM.prototype.toSql = function toSql(options) { return options.operation === 'where' && typeof value === 'string' ? value
return `ENUM(${this.values.map(value => options.escape(value)).join(', ')})`; : JSON.stringify(value);
};
function JSONTYPE() {
if (!(this instanceof JSONTYPE)) {
return new JSONTYPE();
} }
BaseTypes.JSON.call(this);
} }
inherits(JSONTYPE, BaseTypes.JSON); return {
JSONTYPE.prototype._stringify = function _stringify(value, options) {
return options.operation === 'where' && typeof value === 'string' ? value
: JSON.stringify(value);
};
const exports = {
ENUM, ENUM,
DATE, DATE,
DATEONLY, DATEONLY,
...@@ -175,17 +119,4 @@ module.exports = BaseTypes => { ...@@ -175,17 +119,4 @@ module.exports = BaseTypes => {
DECIMAL, DECIMAL,
JSON: JSONTYPE JSON: JSONTYPE
}; };
_.forIn(exports, (DataType, key) => {
if (!DataType.key) {
DataType.key = key;
}
if (!DataType.extend) {
DataType.extend = function extend(oldType) {
return new DataType(oldType.options);
};
}
});
return exports;
}; };
'use strict'; 'use strict';
const _ = require('lodash');
const moment = require('moment'); const moment = require('moment');
const inherits = require('../../utils/inherits');
module.exports = BaseTypes => { module.exports = BaseTypes => {
const warn = BaseTypes.ABSTRACT.warn.bind(undefined, 'https://msdn.microsoft.com/en-us/library/ms187752%28v=sql.110%29.aspx'); const warn = BaseTypes.ABSTRACT.warn.bind(undefined, 'https://msdn.microsoft.com/en-us/library/ms187752%28v=sql.110%29.aspx');
...@@ -50,205 +48,154 @@ module.exports = BaseTypes => { ...@@ -50,205 +48,154 @@ module.exports = BaseTypes => {
// BaseTypes.GEOMETRY.types.mssql = [240]; // not yet supported // BaseTypes.GEOMETRY.types.mssql = [240]; // not yet supported
BaseTypes.GEOMETRY.types.mssql = false; BaseTypes.GEOMETRY.types.mssql = false;
function BLOB(length) { class BLOB extends BaseTypes.BLOB {
if (!(this instanceof BLOB)) return new BLOB(length); toSql() {
BaseTypes.BLOB.apply(this, arguments); if (this._length) {
} if (this._length.toLowerCase() === 'tiny') { // tiny = 2^8
inherits(BLOB, BaseTypes.BLOB); warn('MSSQL does not support BLOB with the `length` = `tiny` option. `VARBINARY(256)` will be used instead.');
return 'VARBINARY(256)';
BLOB.prototype.toSql = function toSql() { }
if (this._length) { warn('MSSQL does not support BLOB with the `length` option. `VARBINARY(MAX)` will be used instead.');
if (this._length.toLowerCase() === 'tiny') { // tiny = 2^8
warn('MSSQL does not support BLOB with the `length` = `tiny` option. `VARBINARY(256)` will be used instead.');
return 'VARBINARY(256)';
} }
warn('MSSQL does not support BLOB with the `length` option. `VARBINARY(MAX)` will be used instead.'); return 'VARBINARY(MAX)';
}
_hexify(hex) {
return `0x${hex}`;
} }
return 'VARBINARY(MAX)';
};
BLOB.prototype._hexify = function _hexify(hex) {
return `0x${hex}`;
};
function STRING(length, binary) {
if (!(this instanceof STRING)) return new STRING(length, binary);
BaseTypes.STRING.apply(this, arguments);
} }
inherits(STRING, BaseTypes.STRING);
STRING.prototype.toSql = function toSql() {
if (!this._binary) {
return `NVARCHAR(${this._length})`;
}
return `BINARY(${this._length})`;
};
STRING.prototype.escape = false; class STRING extends BaseTypes.STRING {
STRING.prototype._stringify = function _stringify(value, options) { toSql() {
if (this._binary) { if (!this._binary) {
return BLOB.prototype._stringify(value); return `NVARCHAR(${this._length})`;
}
return `BINARY(${this._length})`;
} }
return options.escape(value); _stringify(value, options) {
}; if (this._binary) {
STRING.prototype._bindParam = function _bindParam(value, options) { return BLOB.prototype._stringify(value);
return options.bindParam(this._binary ? Buffer.from(value) : value);
};
function TEXT(length) {
if (!(this instanceof TEXT)) return new TEXT(length);
BaseTypes.TEXT.apply(this, arguments);
}
inherits(TEXT, BaseTypes.TEXT);
TEXT.prototype.toSql = function toSql() {
// TEXT is deprecated in mssql and it would normally be saved as a non-unicode string.
// Using unicode is just future proof
if (this._length) {
if (this._length.toLowerCase() === 'tiny') { // tiny = 2^8
warn('MSSQL does not support TEXT with the `length` = `tiny` option. `NVARCHAR(256)` will be used instead.');
return 'NVARCHAR(256)';
} }
warn('MSSQL does not support TEXT with the `length` option. `NVARCHAR(MAX)` will be used instead.'); return options.escape(value);
}
_bindParam(value, options) {
return options.bindParam(this._binary ? Buffer.from(value) : value);
} }
return 'NVARCHAR(MAX)';
};
function BOOLEAN() {
if (!(this instanceof BOOLEAN)) return new BOOLEAN();
BaseTypes.BOOLEAN.call(this);
} }
inherits(BOOLEAN, BaseTypes.BOOLEAN);
BOOLEAN.prototype.toSql = function toSql() { STRING.prototype.escape = false;
return 'BIT';
};
function UUID() { class TEXT extends BaseTypes.TEXT {
if (!(this instanceof UUID)) return new UUID(); toSql() {
BaseTypes.UUID.call(this); // TEXT is deprecated in mssql and it would normally be saved as a non-unicode string.
// Using unicode is just future proof
if (this._length) {
if (this._length.toLowerCase() === 'tiny') { // tiny = 2^8
warn('MSSQL does not support TEXT with the `length` = `tiny` option. `NVARCHAR(256)` will be used instead.');
return 'NVARCHAR(256)';
}
warn('MSSQL does not support TEXT with the `length` option. `NVARCHAR(MAX)` will be used instead.');
}
return 'NVARCHAR(MAX)';
}
} }
inherits(UUID, BaseTypes.UUID);
UUID.prototype.toSql = function toSql() {
return 'CHAR(36)';
};
function NOW() { class BOOLEAN extends BaseTypes.BOOLEAN {
if (!(this instanceof NOW)) return new NOW(); toSql() {
BaseTypes.NOW.call(this); return 'BIT';
}
} }
inherits(NOW, BaseTypes.NOW);
NOW.prototype.toSql = function toSql() { class UUID extends BaseTypes.UUID {
return 'GETDATE()'; toSql() {
}; return 'CHAR(36)';
}
function DATE(length) {
if (!(this instanceof DATE)) return new DATE(length);
BaseTypes.DATE.apply(this, arguments);
} }
inherits(DATE, BaseTypes.DATE);
DATE.prototype.toSql = function toSql() { class NOW extends BaseTypes.NOW {
return 'DATETIMEOFFSET'; toSql() {
}; return 'GETDATE()';
}
function DATEONLY() {
if (!(this instanceof DATEONLY)) return new DATEONLY();
BaseTypes.DATEONLY.call(this);
} }
inherits(DATEONLY, BaseTypes.DATEONLY);
DATEONLY.parse = function(value) { class DATE extends BaseTypes.DATE {
return moment(value).format('YYYY-MM-DD'); toSql() {
}; return 'DATETIMEOFFSET';
}
function INTEGER(length) {
if (!(this instanceof INTEGER)) return new INTEGER(length);
BaseTypes.INTEGER.apply(this, arguments);
removeUnsupportedIntegerOptions(this);
} }
inherits(INTEGER, BaseTypes.INTEGER);
function TINYINT(length) { class DATEONLY extends BaseTypes.DATEONLY {
if (!(this instanceof TINYINT)) return new TINYINT(length); static parse(value) {
BaseTypes.TINYINT.apply(this, arguments); return moment(value).format('YYYY-MM-DD');
};
removeUnsupportedIntegerOptions(this);
} }
inherits(TINYINT, BaseTypes.TINYINT);
function SMALLINT(length) { class INTEGER extends BaseTypes.INTEGER {
if (!(this instanceof SMALLINT)) return new SMALLINT(length); constructor(length) {
BaseTypes.SMALLINT.apply(this, arguments); super(length);
removeUnsupportedIntegerOptions(this); removeUnsupportedIntegerOptions(this);
}
} }
inherits(SMALLINT, BaseTypes.SMALLINT); class TINYINT extends BaseTypes.TINYINT {
constructor(length) {
function BIGINT(length) { super(length);
if (!(this instanceof BIGINT)) return new BIGINT(length); removeUnsupportedIntegerOptions(this);
BaseTypes.BIGINT.apply(this, arguments); }
removeUnsupportedIntegerOptions(this);
} }
inherits(BIGINT, BaseTypes.BIGINT); class SMALLINT extends BaseTypes.SMALLINT {
constructor(length) {
function REAL(length, decimals) { super(length);
if (!(this instanceof REAL)) return new REAL(length, decimals); removeUnsupportedIntegerOptions(this);
BaseTypes.REAL.apply(this, arguments);
// MSSQL does not support any options for real
if (this._length || this.options.length || this._unsigned || this._zerofill) {
warn('MSSQL does not support REAL with options. Plain `REAL` will be used instead.');
this._length = undefined;
this.options.length = undefined;
this._unsigned = undefined;
this._zerofill = undefined;
} }
} }
inherits(REAL, BaseTypes.REAL); class BIGINT extends BaseTypes.BIGINT {
constructor(length) {
function FLOAT(length, decimals) { super(length);
if (!(this instanceof FLOAT)) return new FLOAT(length, decimals); removeUnsupportedIntegerOptions(this);
BaseTypes.FLOAT.apply(this, arguments);
// MSSQL does only support lengths as option.
// Values between 1-24 result in 7 digits precision (4 bytes storage size)
// Values between 25-53 result in 15 digits precision (8 bytes storage size)
// If decimals are provided remove these and print a warning
if (this._decimals) {
warn('MSSQL does not support Float with decimals. Plain `FLOAT` will be used instead.');
this._length = undefined;
this.options.length = undefined;
} }
if (this._unsigned) { }
warn('MSSQL does not support Float unsigned. `UNSIGNED` was removed.'); class REAL extends BaseTypes.REAL {
this._unsigned = undefined; constructor(length, decimals) {
super(length, decimals);
// MSSQL does not support any options for real
if (this._length || this.options.length || this._unsigned || this._zerofill) {
warn('MSSQL does not support REAL with options. Plain `REAL` will be used instead.');
this._length = undefined;
this.options.length = undefined;
this._unsigned = undefined;
this._zerofill = undefined;
}
} }
if (this._zerofill) { }
warn('MSSQL does not support Float zerofill. `ZEROFILL` was removed.'); class FLOAT extends BaseTypes.FLOAT {
this._zerofill = undefined; constructor(length, decimals) {
super(length, decimals);
// MSSQL does only support lengths as option.
// Values between 1-24 result in 7 digits precision (4 bytes storage size)
// Values between 25-53 result in 15 digits precision (8 bytes storage size)
// If decimals are provided remove these and print a warning
if (this._decimals) {
warn('MSSQL does not support Float with decimals. Plain `FLOAT` will be used instead.');
this._length = undefined;
this.options.length = undefined;
}
if (this._unsigned) {
warn('MSSQL does not support Float unsigned. `UNSIGNED` was removed.');
this._unsigned = undefined;
}
if (this._zerofill) {
warn('MSSQL does not support Float zerofill. `ZEROFILL` was removed.');
this._zerofill = undefined;
}
} }
} }
inherits(FLOAT, BaseTypes.FLOAT); class ENUM extends BaseTypes.ENUM {
toSql() {
function ENUM() { return 'VARCHAR(255)';
if (!(this instanceof ENUM)) {
const obj = Object.create(ENUM.prototype);
ENUM.apply(obj, arguments);
return obj;
} }
BaseTypes.ENUM.apply(this, arguments);
} }
inherits(ENUM, BaseTypes.ENUM);
ENUM.prototype.toSql = function toSql() {
return 'VARCHAR(255)';
};
const exports = { return {
BLOB, BLOB,
BOOLEAN, BOOLEAN,
ENUM, ENUM,
...@@ -265,15 +212,4 @@ module.exports = BaseTypes => { ...@@ -265,15 +212,4 @@ module.exports = BaseTypes => {
FLOAT, FLOAT,
TEXT TEXT
}; };
_.forIn(exports, (DataType, key) => {
if (!DataType.key) DataType.key = key;
if (!DataType.extend) {
DataType.extend = function extend(oldType) {
return new DataType(oldType.options);
};
}
});
return exports;
}; };
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
const wkx = require('wkx'); const wkx = require('wkx');
const _ = require('lodash'); const _ = require('lodash');
const moment = require('moment-timezone'); const moment = require('moment-timezone');
const inherits = require('../../utils/inherits');
module.exports = BaseTypes => { module.exports = BaseTypes => {
BaseTypes.ABSTRACT.prototype.dialectTypes = 'https://dev.mysql.com/doc/refman/5.7/en/data-types.html'; BaseTypes.ABSTRACT.prototype.dialectTypes = 'https://dev.mysql.com/doc/refman/5.7/en/data-types.html';
...@@ -36,143 +34,101 @@ module.exports = BaseTypes => { ...@@ -36,143 +34,101 @@ module.exports = BaseTypes => {
BaseTypes.GEOMETRY.types.mysql = ['GEOMETRY']; BaseTypes.GEOMETRY.types.mysql = ['GEOMETRY'];
BaseTypes.JSON.types.mysql = ['JSON']; BaseTypes.JSON.types.mysql = ['JSON'];
function DECIMAL(precision, scale) { class DECIMAL extends BaseTypes.DECIMAL {
if (!(this instanceof DECIMAL)) return new DECIMAL(precision, scale); toSql() {
BaseTypes.DECIMAL.apply(this, arguments); let definition = super.toSql();
} if (this._unsigned) {
inherits(DECIMAL, BaseTypes.DECIMAL); definition += ' UNSIGNED';
}
DECIMAL.prototype.toSql = function toSql() { if (this._zerofill) {
let definition = BaseTypes.DECIMAL.prototype.toSql.apply(this); definition += ' ZEROFILL';
}
if (this._unsigned) { return definition;
definition += ' UNSIGNED';
}
if (this._zerofill) {
definition += ' ZEROFILL';
} }
return definition;
};
function DATE(length) {
if (!(this instanceof DATE)) return new DATE(length);
BaseTypes.DATE.apply(this, arguments);
} }
inherits(DATE, BaseTypes.DATE);
DATE.prototype.toSql = function toSql() { class DATE extends BaseTypes.DATE {
return `DATETIME${this._length ? `(${this._length})` : ''}`; toSql() {
}; return `DATETIME${this._length ? `(${this._length})` : ''}`;
DATE.prototype._stringify = function _stringify(date, options) {
date = BaseTypes.DATE.prototype._applyTimezone(date, options);
// Fractional DATETIMEs only supported on MySQL 5.6.4+
if (this._length) {
return date.format('YYYY-MM-DD HH:mm:ss.SSS');
} }
_stringify(date, options) {
return date.format('YYYY-MM-DD HH:mm:ss'); date = this._applyTimezone(date, options);
}; // Fractional DATETIMEs only supported on MySQL 5.6.4+
if (this._length) {
DATE.parse = function parse(value, options) { return date.format('YYYY-MM-DD HH:mm:ss.SSS');
value = value.string(); }
return date.format('YYYY-MM-DD HH:mm:ss');
if (value === null) { }
static parse(value, options) {
value = value.string();
if (value === null) {
return value;
}
if (moment.tz.zone(options.timezone)) {
value = moment.tz(value, options.timezone).toDate();
}
else {
value = new Date(`${value} ${options.timezone}`);
}
return value; return value;
} }
}
if (moment.tz.zone(options.timezone)) { class DATEONLY extends BaseTypes.DATEONLY {
value = moment.tz(value, options.timezone).toDate(); static parse(value) {
} else { return value.string();
value = new Date(`${value} ${options.timezone}`);
} }
return value;
};
function DATEONLY() {
if (!(this instanceof DATEONLY)) return new DATEONLY();
BaseTypes.DATEONLY.call(this);
} }
inherits(DATEONLY, BaseTypes.DATEONLY); class UUID extends BaseTypes.UUID {
toSql() {
DATEONLY.parse = function parse(value) { return 'CHAR(36) BINARY';
return value.string(); }
};
function UUID() {
if (!(this instanceof UUID)) return new UUID();
BaseTypes.UUID.call(this);
} }
inherits(UUID, BaseTypes.UUID);
UUID.prototype.toSql = function toSql() {
return 'CHAR(36) BINARY';
};
const SUPPORTED_GEOMETRY_TYPES = ['POINT', 'LINESTRING', 'POLYGON']; const SUPPORTED_GEOMETRY_TYPES = ['POINT', 'LINESTRING', 'POLYGON'];
function GEOMETRY(type, srid) { class GEOMETRY extends BaseTypes.GEOMETRY {
if (!(this instanceof GEOMETRY)) return new GEOMETRY(type, srid); constructor(type, srid) {
BaseTypes.GEOMETRY.apply(this, arguments); super(type, srid);
if (_.isEmpty(this.type)) {
if (_.isEmpty(this.type)) { this.sqlType = this.key;
this.sqlType = this.key; return;
} else if (SUPPORTED_GEOMETRY_TYPES.includes(this.type)) { }
this.sqlType = this.type; if (SUPPORTED_GEOMETRY_TYPES.includes(this.type)) {
} else { this.sqlType = this.type;
return;
}
throw new Error(`Supported geometry types are: ${SUPPORTED_GEOMETRY_TYPES.join(', ')}`); throw new Error(`Supported geometry types are: ${SUPPORTED_GEOMETRY_TYPES.join(', ')}`);
} }
} static parse(value) {
inherits(GEOMETRY, BaseTypes.GEOMETRY); value = value.buffer();
// Empty buffer, MySQL doesn't support POINT EMPTY
GEOMETRY.parse = GEOMETRY.prototype.parse = function parse(value) { // check, https://dev.mysql.com/worklog/task/?id=2381
value = value.buffer(); if (!value || value.length === 0) {
return null;
// Empty buffer, MySQL doesn't support POINT EMPTY }
// check, https://dev.mysql.com/worklog/task/?id=2381 // For some reason, discard the first 4 bytes
if (!value || value.length === 0) { value = value.slice(4);
return null; return wkx.Geometry.parse(value).toGeoJSON();
} }
toSql() {
// For some reason, discard the first 4 bytes return this.sqlType;
value = value.slice(4);
return wkx.Geometry.parse(value).toGeoJSON();
};
GEOMETRY.prototype.toSql = function toSql() {
return this.sqlType;
};
function ENUM() {
if (!(this instanceof ENUM)) {
const obj = Object.create(ENUM.prototype);
ENUM.apply(obj, arguments);
return obj;
} }
BaseTypes.ENUM.apply(this, arguments);
} }
inherits(ENUM, BaseTypes.ENUM);
ENUM.prototype.toSql = function toSql(options) {
return `ENUM(${this.values.map(value => options.escape(value)).join(', ')})`;
};
function JSONTYPE() { class ENUM extends BaseTypes.ENUM {
if (!(this instanceof JSONTYPE)) return new JSONTYPE(); toSql(options) {
BaseTypes.JSON.call(this); return `ENUM(${this.values.map(value => options.escape(value)).join(', ')})`;
}
} }
inherits(JSONTYPE, BaseTypes.JSON);
JSONTYPE.prototype._stringify = function _stringify(value, options) { class JSONTYPE extends BaseTypes.JSON {
return options.operation === 'where' && typeof value === 'string' ? value : JSON.stringify(value); _stringify(value, options) {
}; return options.operation === 'where' && typeof value === 'string' ? value : JSON.stringify(value);
}
}
const exports = { return {
ENUM, ENUM,
DATE, DATE,
DATEONLY, DATEONLY,
...@@ -181,15 +137,4 @@ module.exports = BaseTypes => { ...@@ -181,15 +137,4 @@ module.exports = BaseTypes => {
DECIMAL, DECIMAL,
JSON: JSONTYPE JSON: JSONTYPE
}; };
_.forIn(exports, (DataType, key) => {
if (!DataType.key) DataType.key = key;
if (!DataType.extend) {
DataType.extend = function extend(oldType) {
return new DataType(oldType.options);
};
}
});
return exports;
}; };
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
const _ = require('lodash'); const _ = require('lodash');
const wkx = require('wkx'); const wkx = require('wkx');
const inherits = require('../../utils/inherits');
module.exports = BaseTypes => { module.exports = BaseTypes => {
const warn = BaseTypes.ABSTRACT.warn.bind(undefined, 'http://www.postgresql.org/docs/9.4/static/datatype.html'); const warn = BaseTypes.ABSTRACT.warn.bind(undefined, 'http://www.postgresql.org/docs/9.4/static/datatype.html');
...@@ -39,236 +38,184 @@ module.exports = BaseTypes => { ...@@ -39,236 +38,184 @@ module.exports = BaseTypes => {
BaseTypes.JSONB.types.postgres = ['jsonb']; BaseTypes.JSONB.types.postgres = ['jsonb'];
BaseTypes.TIME.types.postgres = ['time']; BaseTypes.TIME.types.postgres = ['time'];
function DATEONLY() { class DATEONLY extends BaseTypes.DATEONLY {
if (!(this instanceof DATEONLY)) return new DATEONLY(); _stringify(value, options) {
BaseTypes.DATEONLY.call(this); if (value === Infinity) {
} return 'Infinity';
}
inherits(DATEONLY, BaseTypes.DATEONLY); if (value === -Infinity) {
return '-Infinity';
DATEONLY.parse = function parse(value) { }
if (value === 'infinity') { return super._stringify(value, options);
return Infinity;
}
if (value === '-infinity') {
return -Infinity;
}
return value;
};
DATEONLY.prototype._stringify = function _stringify(value, options) {
if (value === Infinity) {
return 'Infinity';
}
if (value === -Infinity) {
return '-Infinity';
} }
_sanitize(value, options) {
return BaseTypes.DATEONLY.prototype._stringify.call(this, value, options); if ((!options || options && !options.raw) && value !== Infinity && value !== -Infinity) {
}; if (typeof value === 'string') {
const lower = value.toLowerCase();
DATEONLY.prototype._sanitize = function _sanitize(value, options) { if (lower === 'infinity') {
if ((!options || options && !options.raw) && value !== Infinity && value !== -Infinity) { return Infinity;
if (typeof value === 'string') { }
if (value.toLowerCase() === 'infinity') { if (lower === '-infinity') {
return Infinity; return -Infinity;
} }
if (value.toLowerCase() === '-infinity') {
return -Infinity;
} }
return super._sanitize(value);
} }
return value;
return BaseTypes.DATEONLY.prototype._sanitize.call(this, value);
} }
static parse(value) {
return value; if (value === 'infinity') {
}; return Infinity;
}
if (value === '-infinity') {
return -Infinity;
}
return value;
}
}
BaseTypes.DATEONLY.types.postgres = ['date']; BaseTypes.DATEONLY.types.postgres = ['date'];
function DECIMAL(precision, scale) { class DECIMAL extends BaseTypes.DECIMAL {
if (!(this instanceof DECIMAL)) return new DECIMAL(precision, scale); static parse(value) {
BaseTypes.DECIMAL.apply(this, arguments); return value;
}
} }
inherits(DECIMAL, BaseTypes.DECIMAL);
DECIMAL.parse = function parse(value) {
return value;
};
// numeric // numeric
BaseTypes.DECIMAL.types.postgres = ['numeric']; BaseTypes.DECIMAL.types.postgres = ['numeric'];
function STRING(length, binary) { class STRING extends BaseTypes.STRING {
if (!(this instanceof STRING)) return new STRING(length, binary); toSql() {
BaseTypes.STRING.apply(this, arguments); if (this._binary) {
} return 'BYTEA';
inherits(STRING, BaseTypes.STRING); }
return super.toSql();
STRING.prototype.toSql = function toSql() {
if (this._binary) {
return 'BYTEA';
} }
return BaseTypes.STRING.prototype.toSql.call(this); }
};
BaseTypes.STRING.types.postgres = ['varchar']; BaseTypes.STRING.types.postgres = ['varchar'];
function TEXT(length) { class TEXT extends BaseTypes.TEXT {
if (!(this instanceof TEXT)) return new TEXT(length); toSql() {
BaseTypes.TEXT.apply(this, arguments); if (this._length) {
} warn('PostgreSQL does not support TEXT with options. Plain `TEXT` will be used instead.');
inherits(TEXT, BaseTypes.TEXT); this._length = undefined;
}
TEXT.prototype.toSql = function toSql() { return 'TEXT';
if (this._length) {
warn('PostgreSQL does not support TEXT with options. Plain `TEXT` will be used instead.');
this._length = undefined;
} }
return 'TEXT'; }
};
BaseTypes.TEXT.types.postgres = ['text']; BaseTypes.TEXT.types.postgres = ['text'];
function CITEXT() { class CITEXT extends BaseTypes.CITEXT {
if (!(this instanceof CITEXT)) return new CITEXT(); static parse(value) {
BaseTypes.CITEXT.call(this); return value;
}
} }
inherits(CITEXT, BaseTypes.CITEXT);
CITEXT.parse = value => value;
BaseTypes.CITEXT.types.postgres = ['citext']; BaseTypes.CITEXT.types.postgres = ['citext'];
function CHAR(length, binary) { class CHAR extends BaseTypes.CHAR {
if (!(this instanceof CHAR)) return new CHAR(length, binary); toSql() {
BaseTypes.CHAR.apply(this, arguments); if (this._binary) {
} return 'BYTEA';
inherits(CHAR, BaseTypes.CHAR); }
return super.toSql();
CHAR.prototype.toSql = function toSql() {
if (this._binary) {
return 'BYTEA';
} }
return BaseTypes.CHAR.prototype.toSql.call(this);
};
BaseTypes.CHAR.types.postgres = ['char', 'bpchar'];
function BOOLEAN() {
if (!(this instanceof BOOLEAN)) return new BOOLEAN();
BaseTypes.BOOLEAN.call(this);
} }
inherits(BOOLEAN, BaseTypes.BOOLEAN);
BOOLEAN.prototype.toSql = function toSql() {
return 'BOOLEAN';
};
BOOLEAN.prototype._sanitize = function _sanitize(value) { BaseTypes.CHAR.types.postgres = ['char', 'bpchar'];
if (value !== null && value !== undefined) {
if (Buffer.isBuffer(value) && value.length === 1) {
// Bit fields are returned as buffers
value = value[0];
}
if (typeof value === 'string') {
// Only take action on valid boolean strings.
return value === 'true' || value === 't' ? true : value === 'false' || value === 'f' ? false : value;
class BOOLEAN extends BaseTypes.BOOLEAN {
toSql() {
return 'BOOLEAN';
}
_sanitize(value) {
if (value !== null && value !== undefined) {
if (Buffer.isBuffer(value) && value.length === 1) {
// Bit fields are returned as buffers
value = value[0];
}
if (typeof value === 'string') {
// Only take action on valid boolean strings.
return value === 'true' || value === 't' ? true : value === 'false' || value === 'f' ? false : value;
}
if (typeof value === 'number') {
// Only take action on valid boolean integers.
return value === 1 ? true : value === 0 ? false : value;
}
} }
if (typeof value === 'number') { return value;
// Only take action on valid boolean integers.
return value === 1 ? true : value === 0 ? false : value;
}
} }
}
return value;
};
BOOLEAN.parse = BOOLEAN.prototype._sanitize; BOOLEAN.parse = BOOLEAN.prototype._sanitize;
BaseTypes.BOOLEAN.types.postgres = ['bool']; BaseTypes.BOOLEAN.types.postgres = ['bool'];
function DATE(length) { class DATE extends BaseTypes.DATE {
if (!(this instanceof DATE)) return new DATE(length); toSql() {
BaseTypes.DATE.apply(this, arguments); return 'TIMESTAMP WITH TIME ZONE';
}
inherits(DATE, BaseTypes.DATE);
DATE.prototype.toSql = function toSql() {
return 'TIMESTAMP WITH TIME ZONE';
};
DATE.prototype.validate = function validate(value) {
if (value !== Infinity && value !== -Infinity) {
return BaseTypes.DATE.prototype.validate.call(this, value);
} }
validate(value) {
return true; if (value !== Infinity && value !== -Infinity) {
}; return super.validate(value);
}
DATE.prototype._stringify = function _stringify(value, options) { return true;
if (value === Infinity) {
return 'Infinity';
} }
if (value === -Infinity) { _stringify(value, options) {
return '-Infinity'; if (value === Infinity) {
return 'Infinity';
}
if (value === -Infinity) {
return '-Infinity';
}
return super._stringify(value, options);
} }
_sanitize(value, options) {
return BaseTypes.DATE.prototype._stringify.call(this, value, options); if ((!options || options && !options.raw) && !(value instanceof Date) && !!value && value !== Infinity && value !== -Infinity) {
}; if (typeof value === 'string') {
const lower = value.toLowerCase();
DATE.prototype._sanitize = function _sanitize(value, options) { if (lower === 'infinity') {
if ((!options || options && !options.raw) && !(value instanceof Date) && !!value && value !== Infinity && value !== -Infinity) { return Infinity;
if (typeof value === 'string') { }
if (value.toLowerCase() === 'infinity') { if (lower === '-infinity') {
return Infinity; return -Infinity;
} }
if (value.toLowerCase() === '-infinity') {
return -Infinity;
} }
return new Date(value);
} }
return value;
return new Date(value);
} }
}
return value;
};
BaseTypes.DATE.types.postgres = ['timestamptz']; BaseTypes.DATE.types.postgres = ['timestamptz'];
function TINYINT(length) { class TINYINT extends BaseTypes.TINYINT {
if (!(this instanceof TINYINT)) return new TINYINT(length); constructor(length) {
BaseTypes.TINYINT.apply(this, arguments); super(length);
removeUnsupportedIntegerOptions(this);
removeUnsupportedIntegerOptions(this); }
} }
inherits(TINYINT, BaseTypes.TINYINT);
// int2 // int2
BaseTypes.TINYINT.types.postgres = ['int2']; BaseTypes.TINYINT.types.postgres = ['int2'];
function SMALLINT(length) { class SMALLINT extends BaseTypes.SMALLINT {
if (!(this instanceof SMALLINT)) return new SMALLINT(length); constructor(length) {
BaseTypes.SMALLINT.apply(this, arguments); super(length);
removeUnsupportedIntegerOptions(this);
removeUnsupportedIntegerOptions(this); }
} }
inherits(SMALLINT, BaseTypes.SMALLINT);
// int2 // int2
BaseTypes.SMALLINT.types.postgres = ['int2']; BaseTypes.SMALLINT.types.postgres = ['int2'];
function INTEGER(length) { class INTEGER extends BaseTypes.INTEGER {
if (!(this instanceof INTEGER)) return new INTEGER(length); constructor(length) {
BaseTypes.INTEGER.apply(this, arguments); super(length);
removeUnsupportedIntegerOptions(this);
removeUnsupportedIntegerOptions(this); }
} }
inherits(INTEGER, BaseTypes.INTEGER);
INTEGER.parse = function parse(value) { INTEGER.parse = function parse(value) {
return parseInt(value, 10); return parseInt(value, 10);
}; };
...@@ -276,250 +223,216 @@ module.exports = BaseTypes => { ...@@ -276,250 +223,216 @@ module.exports = BaseTypes => {
// int4 // int4
BaseTypes.INTEGER.types.postgres = ['int4']; BaseTypes.INTEGER.types.postgres = ['int4'];
function BIGINT(length) { class BIGINT extends BaseTypes.BIGINT {
if (!(this instanceof BIGINT)) return new BIGINT(length); constructor(length) {
BaseTypes.BIGINT.apply(this, arguments); super(length);
removeUnsupportedIntegerOptions(this);
removeUnsupportedIntegerOptions(this); }
} }
inherits(BIGINT, BaseTypes.BIGINT);
// int8 // int8
BaseTypes.BIGINT.types.postgres = ['int8']; BaseTypes.BIGINT.types.postgres = ['int8'];
function REAL(length, decimals) { class REAL extends BaseTypes.REAL {
if (!(this instanceof REAL)) return new REAL(length, decimals); constructor(length) {
BaseTypes.REAL.apply(this, arguments); super(length);
removeUnsupportedIntegerOptions(this);
removeUnsupportedIntegerOptions(this); }
} }
inherits(REAL, BaseTypes.REAL);
// float4 // float4
BaseTypes.REAL.types.postgres = ['float4']; BaseTypes.REAL.types.postgres = ['float4'];
function DOUBLE(length, decimals) { class DOUBLE extends BaseTypes.DOUBLE {
if (!(this instanceof DOUBLE)) return new DOUBLE(length, decimals); constructor(length) {
BaseTypes.DOUBLE.apply(this, arguments); super(length);
removeUnsupportedIntegerOptions(this);
removeUnsupportedIntegerOptions(this); }
} }
inherits(DOUBLE, BaseTypes.DOUBLE);
// float8 // float8
BaseTypes.DOUBLE.types.postgres = ['float8']; BaseTypes.DOUBLE.types.postgres = ['float8'];
function FLOAT(length, decimals) { class FLOAT extends BaseTypes.FLOAT {
if (!(this instanceof FLOAT)) return new FLOAT(length, decimals); constructor(length, decimals) {
BaseTypes.FLOAT.apply(this, arguments); super(length, decimals);
// POSTGRES does only support lengths as parameter.
// POSTGRES does only support lengths as parameter. // Values between 1-24 result in REAL
// Values between 1-24 result in REAL // Values between 25-53 result in DOUBLE PRECISION
// Values between 25-53 result in DOUBLE PRECISION // If decimals are provided remove these and print a warning
// If decimals are provided remove these and print a warning if (this._decimals) {
if (this._decimals) { warn('PostgreSQL does not support FLOAT with decimals. Plain `FLOAT` will be used instead.');
warn('PostgreSQL does not support FLOAT with decimals. Plain `FLOAT` will be used instead.'); this._length = undefined;
this._length = undefined; this.options.length = undefined;
this.options.length = undefined; this._decimals = undefined;
this._decimals = undefined; }
} if (this._unsigned) {
if (this._unsigned) { warn('PostgreSQL does not support FLOAT unsigned. `UNSIGNED` was removed.');
warn('PostgreSQL does not support FLOAT unsigned. `UNSIGNED` was removed.'); this._unsigned = undefined;
this._unsigned = undefined; }
} if (this._zerofill) {
if (this._zerofill) { warn('PostgreSQL does not support FLOAT zerofill. `ZEROFILL` was removed.');
warn('PostgreSQL does not support FLOAT zerofill. `ZEROFILL` was removed.'); this._zerofill = undefined;
this._zerofill = undefined; }
} }
} }
inherits(FLOAT, BaseTypes.FLOAT);
delete FLOAT.parse; // Float has no separate type in PG delete FLOAT.parse; // Float has no separate type in PG
function BLOB(length) { class BLOB extends BaseTypes.BLOB {
if (!(this instanceof BLOB)) return new BLOB(length); toSql() {
BaseTypes.BLOB.apply(this, arguments); if (this._length) {
} warn('PostgreSQL does not support BLOB (BYTEA) with options. Plain `BYTEA` will be used instead.');
inherits(BLOB, BaseTypes.BLOB); this._length = undefined;
}
BLOB.prototype.toSql = function toSql() { return 'BYTEA';
if (this._length) { }
warn('PostgreSQL does not support BLOB (BYTEA) with options. Plain `BYTEA` will be used instead.'); _hexify(hex) {
this._length = undefined; // bytea hex format http://www.postgresql.org/docs/current/static/datatype-binary.html
return `E'\\\\x${hex}'`;
} }
return 'BYTEA';
};
BLOB.prototype._hexify = function _hexify(hex) {
// bytea hex format http://www.postgresql.org/docs/current/static/datatype-binary.html
return `E'\\\\x${hex}'`;
};
BaseTypes.BLOB.types.postgres = ['bytea'];
function GEOMETRY(type, srid) {
if (!(this instanceof GEOMETRY)) return new GEOMETRY(type, srid);
BaseTypes.GEOMETRY.apply(this, arguments);
} }
inherits(GEOMETRY, BaseTypes.GEOMETRY);
GEOMETRY.prototype.toSql = function toSql() {
let result = this.key;
if (this.type) { BaseTypes.BLOB.types.postgres = ['bytea'];
result += `(${this.type}`;
if (this.srid) { class GEOMETRY extends BaseTypes.GEOMETRY {
result += `,${this.srid}`; toSql() {
let result = this.key;
if (this.type) {
result += `(${this.type}`;
if (this.srid) {
result += `,${this.srid}`;
}
result += ')';
} }
return result;
result += ')'; }
static parse(value) {
const b = Buffer.from(value, 'hex');
return wkx.Geometry.parse(b).toGeoJSON();
}
_stringify(value, options) {
return `ST_GeomFromGeoJSON(${options.escape(JSON.stringify(value))})`;
}
_bindParam(value, options) {
return `ST_GeomFromGeoJSON(${options.bindParam(value)})`;
} }
return result;
};
BaseTypes.GEOMETRY.types.postgres = ['geometry'];
GEOMETRY.parse = GEOMETRY.prototype.parse = function parse(value) {
const b = Buffer.from(value, 'hex');
return wkx.Geometry.parse(b).toGeoJSON();
};
GEOMETRY.prototype._stringify = function _stringify(value, options) {
return `ST_GeomFromGeoJSON(${options.escape(JSON.stringify(value))})`;
};
GEOMETRY.prototype._bindParam = function _bindParam(value, options) {
return `ST_GeomFromGeoJSON(${options.bindParam(value)})`;
};
function GEOGRAPHY(type, srid) {
if (!(this instanceof GEOGRAPHY)) return new GEOGRAPHY(type, srid);
BaseTypes.GEOGRAPHY.apply(this, arguments);
} }
inherits(GEOGRAPHY, BaseTypes.GEOGRAPHY);
GEOGRAPHY.prototype.toSql = function toSql() { BaseTypes.GEOMETRY.types.postgres = ['geometry'];
let result = 'GEOGRAPHY';
if (this.type) {
result += `(${this.type}`;
if (this.srid) { class GEOGRAPHY extends BaseTypes.GEOGRAPHY {
result += `,${this.srid}`; toSql() {
let result = 'GEOGRAPHY';
if (this.type) {
result += `(${this.type}`;
if (this.srid) {
result += `,${this.srid}`;
}
result += ')';
} }
return result;
result += ')';
} }
static parse(value) {
return result; const b = Buffer.from(value, 'hex');
}; return wkx.Geometry.parse(b).toGeoJSON();
}
_stringify(value, options) {
return `ST_GeomFromGeoJSON(${options.escape(JSON.stringify(value))})`;
}
bindParam(value, options) {
return `ST_GeomFromGeoJSON(${options.bindParam(value)})`;
}
}
BaseTypes.GEOGRAPHY.types.postgres = ['geography']; BaseTypes.GEOGRAPHY.types.postgres = ['geography'];
GEOGRAPHY.parse = GEOGRAPHY.prototype.parse = function parse(value) {
const b = Buffer.from(value, 'hex');
return wkx.Geometry.parse(b).toGeoJSON();
};
GEOGRAPHY.prototype._stringify = function _stringify(value, options) {
return `ST_GeomFromGeoJSON(${options.escape(JSON.stringify(value))})`;
};
GEOGRAPHY.prototype.bindParam = function bindParam(value, options) {
return `ST_GeomFromGeoJSON(${options.bindParam(value)})`;
};
let hstore; let hstore;
function HSTORE() { class HSTORE extends BaseTypes.HSTORE {
if (!(this instanceof HSTORE)) return new HSTORE(); constructor() {
BaseTypes.HSTORE.apply(this, arguments); super();
if (!hstore) {
if (!hstore) { // All datatype files are loaded at import - make sure we don't load the hstore parser before a hstore is instantiated
// All datatype files are loaded at import - make sure we don't load the hstore parser before a hstore is instantiated hstore = require('./hstore');
hstore = require('./hstore'); }
} }
} _value(value) {
inherits(HSTORE, BaseTypes.HSTORE); if (!hstore) {
// All datatype files are loaded at import - make sure we don't load the hstore parser before a hstore is instantiated
HSTORE.parse = function parse(value) { hstore = require('./hstore');
if (!hstore) { }
// All datatype files are loaded at import - make sure we don't load the hstore parser before a hstore is instantiated return hstore.stringify(value);
hstore = require('./hstore');
} }
return hstore.parse(value); _stringify(value) {
}; return `'${this._value(value)}'`;
}
HSTORE.prototype.escape = false; _bindParam(value, options) {
HSTORE.prototype._value = function _value(value) { return options.bindParam(this._value(value));
if (!hstore) { }
// All datatype files are loaded at import - make sure we don't load the hstore parser before a hstore is instantiated static parse(value) {
hstore = require('./hstore'); if (!hstore) {
// All datatype files are loaded at import - make sure we don't load the hstore parser before a hstore is instantiated
hstore = require('./hstore');
}
return hstore.parse(value);
} }
return hstore.stringify(value);
};
HSTORE.prototype._stringify = function _stringify(value) {
return `'${this._value(value)}'`;
};
HSTORE.prototype._bindParam = function _bindParam(value, options) {
return options.bindParam(this._value(value));
};
BaseTypes.HSTORE.types.postgres = ['hstore'];
function RANGE(subtype) {
if (!(this instanceof RANGE)) return new RANGE(subtype);
BaseTypes.RANGE.apply(this, arguments);
} }
inherits(RANGE, BaseTypes.RANGE);
const range = require('./range'); HSTORE.prototype.escape = false;
RANGE.parse = function parse(value, options = { parser: val => val }) {
return range.parse(value, options.parser);
};
RANGE.prototype.escape = false; BaseTypes.HSTORE.types.postgres = ['hstore'];
RANGE.prototype._value = function _value(values, options) {
if (!Array.isArray(values)) {
return this.options.subtype.stringify(values, options);
}
const valueInclusivity = [true, false]; class RANGE extends BaseTypes.RANGE {
const valuesStringified = values.map((value, index) => { _value(values, options) {
if (_.isObject(value) && value.hasOwnProperty('value')) { if (!Array.isArray(values)) {
if (value.hasOwnProperty('inclusive')) { return this.options.subtype.stringify(values, options);
valueInclusivity[index] = value.inclusive;
}
value = value.value;
} }
if (value === null || value === -Infinity || value === Infinity) { const valueInclusivity = [true, false];
// Pass through "unbounded" bounds unchanged const valuesStringified = values.map((value, index) => {
return value; if (_.isObject(value) && value.hasOwnProperty('value')) {
if (value.hasOwnProperty('inclusive')) {
valueInclusivity[index] = value.inclusive;
}
value = value.value;
}
if (value === null || value === -Infinity || value === Infinity) {
// Pass through "unbounded" bounds unchanged
return value;
}
if (this.options.subtype.stringify) {
return this.options.subtype.stringify(value, options);
}
return options.escape(value);
});
// Array.map does not preserve extra array properties
valuesStringified.inclusive = valueInclusivity;
return range.stringify(valuesStringified);
}
_stringify(values, options) {
const value = this._value(values, options);
if (!Array.isArray(values)) {
return `'${value}'::${this.toCastType()}`;
} }
if (this.options.subtype.stringify) { return `'${value}'`;
return this.options.subtype.stringify(value, options); }
_bindParam(values, options) {
const value = this._value(values, options);
if (!Array.isArray(values)) {
return `${options.bindParam(value)}::${this.toCastType()}`;
} }
return options.escape(value); return options.bindParam(value);
});
// Array.map does not preserve extra array properties
valuesStringified.inclusive = valueInclusivity;
return range.stringify(valuesStringified);
};
RANGE.prototype._stringify = function _stringify(values, options) {
const value = this._value(values, options);
if (!Array.isArray(values)) {
return `'${value}'::${this.toCastType()}`;
} }
return `'${value}'`; toSql() {
}; return BaseTypes.RANGE.types.postgres.subtypes[this._subtype.toLowerCase()];
RANGE.prototype._bindParam = function _bindParam(values, options) {
const value = this._value(values, options);
if (!Array.isArray(values)) {
return `${options.bindParam(value)}::${this.toCastType()}`;
} }
return options.bindParam(value); toCastType() {
}; return BaseTypes.RANGE.types.postgres.castTypes[this._subtype.toLowerCase()];
}
static parse(value, options = { parser: val => val }) {
return range.parse(value, options.parser);
}
}
const range = require('./range');
RANGE.prototype.escape = false;
BaseTypes.RANGE.types.postgres = { BaseTypes.RANGE.types.postgres = {
subtypes: { subtypes: {
...@@ -538,13 +451,7 @@ module.exports = BaseTypes => { ...@@ -538,13 +451,7 @@ module.exports = BaseTypes => {
} }
}; };
RANGE.prototype.toSql = function toSql() { // TODO: Why are base types being manipulated??
return BaseTypes.RANGE.types.postgres.subtypes[this._subtype.toLowerCase()];
};
RANGE.prototype.toCastType = function toCastType() {
return BaseTypes.RANGE.types.postgres.castTypes[this._subtype.toLowerCase()];
};
BaseTypes.ARRAY.prototype.escape = false; BaseTypes.ARRAY.prototype.escape = false;
BaseTypes.ARRAY.prototype._value = function _value(values, options) { BaseTypes.ARRAY.prototype._value = function _value(values, options) {
return values.map(value => { return values.map(value => {
...@@ -584,19 +491,15 @@ module.exports = BaseTypes => { ...@@ -584,19 +491,15 @@ module.exports = BaseTypes => {
return options.bindParam(this._value(values, options)); return options.bindParam(this._value(values, options));
}; };
function ENUM(options) { class ENUM extends BaseTypes.ENUM {
if (!(this instanceof ENUM)) return new ENUM(options); static parse(value) {
BaseTypes.ENUM.apply(this, arguments); return value;
};
} }
inherits(ENUM, BaseTypes.ENUM);
ENUM.parse = function(value) {
return value;
};
BaseTypes.ENUM.types.postgres = [null]; BaseTypes.ENUM.types.postgres = [null];
const exports = { return {
DECIMAL, DECIMAL,
BLOB, BLOB,
STRING, STRING,
...@@ -619,13 +522,4 @@ module.exports = BaseTypes => { ...@@ -619,13 +522,4 @@ module.exports = BaseTypes => {
RANGE, RANGE,
ENUM ENUM
}; };
_.forIn(exports, (DataType, key) => {
if (!DataType.key) DataType.key = key;
if (!DataType.extend) {
DataType.extend = oldType => new DataType(oldType.options);
}
});
return exports;
}; };
'use strict'; 'use strict';
const _ = require('lodash');
const inherits = require('../../utils/inherits');
module.exports = BaseTypes => { module.exports = BaseTypes => {
const warn = BaseTypes.ABSTRACT.warn.bind(undefined, 'https://www.sqlite.org/datatype3.html'); const warn = BaseTypes.ABSTRACT.warn.bind(undefined, 'https://www.sqlite.org/datatype3.html');
...@@ -45,235 +42,155 @@ module.exports = BaseTypes => { ...@@ -45,235 +42,155 @@ module.exports = BaseTypes => {
BaseTypes.GEOMETRY.types.sqlite = false; BaseTypes.GEOMETRY.types.sqlite = false;
BaseTypes.JSON.types.sqlite = ['JSON', 'JSONB']; BaseTypes.JSON.types.sqlite = ['JSON', 'JSONB'];
function JSONTYPE() { class JSONTYPE extends BaseTypes.JSON {
if (!(this instanceof JSONTYPE)) return new JSONTYPE(); static parse(data) {
BaseTypes.JSON.call(this); return JSON.parse(data);
}
} }
inherits(JSONTYPE, BaseTypes.JSON);
JSONTYPE.parse = function parse(data) { class DATE extends BaseTypes.DATE {
return JSON.parse(data); static parse(date, options) {
}; if (!date.includes('+')) {
// For backwards compat. Dates inserted by sequelize < 2.0dev12 will not have a timestamp set
function DATE(length) { return new Date(date + options.timezone);
if (!(this instanceof DATE)) return new DATE(length); }
BaseTypes.DATE.apply(this, arguments); return new Date(date); // We already have a timezone stored in the string
}
} }
inherits(DATE, BaseTypes.DATE);
DATE.parse = function parse(date, options) { class DATEONLY extends BaseTypes.DATEONLY {
if (!date.includes('+')) { static parse(date) {
// For backwards compat. Dates inserted by sequelize < 2.0dev12 will not have a timestamp set return date;
return new Date(date + options.timezone);
} }
return new Date(date); // We already have a timezone stored in the string
};
function DATEONLY() {
if (!(this instanceof DATEONLY)) return new DATEONLY();
BaseTypes.DATEONLY.call(this);
} }
inherits(DATEONLY, BaseTypes.DATEONLY);
DATEONLY.parse = function parse(date) {
return date;
};
function STRING(length, binary) { class STRING extends BaseTypes.STRING {
if (!(this instanceof STRING)) return new STRING(length, binary); toSql() {
BaseTypes.STRING.apply(this, arguments); if (this._binary) {
return `VARCHAR BINARY(${this._length})`;
}
return super.toSql(this);
}
} }
inherits(STRING, BaseTypes.STRING);
STRING.prototype.toSql = function toSql() { class TEXT extends BaseTypes.TEXT {
if (this._binary) { toSql() {
return `VARCHAR BINARY(${this._length})`; if (this._length) {
warn('SQLite does not support TEXT with options. Plain `TEXT` will be used instead.');
this._length = undefined;
}
return 'TEXT';
} }
return BaseTypes.STRING.prototype.toSql.call(this);
};
function TEXT(length) {
if (!(this instanceof TEXT)) return new TEXT(length);
BaseTypes.TEXT.apply(this, arguments);
} }
inherits(TEXT, BaseTypes.TEXT);
TEXT.prototype.toSql = function toSql() { class CITEXT extends BaseTypes.CITEXT {
if (this._length) { toSql() {
warn('SQLite does not support TEXT with options. Plain `TEXT` will be used instead.'); return 'TEXT COLLATE NOCASE';
this._length = undefined;
} }
return 'TEXT';
};
function CITEXT() {
if (!(this instanceof CITEXT)) return new CITEXT();
BaseTypes.CITEXT.call(this);
} }
inherits(CITEXT, BaseTypes.CITEXT);
CITEXT.prototype.toSql = function toSql() { class CHAR extends BaseTypes.CHAR {
return 'TEXT COLLATE NOCASE'; toSql() {
}; if (this._binary) {
return `CHAR BINARY(${this._length})`;
function CHAR(length, binary) { }
if (!(this instanceof CHAR)) return new CHAR(length, binary); return super.toSql();
BaseTypes.CHAR.apply(this, arguments); }
} }
inherits(CHAR, BaseTypes.CHAR);
CHAR.prototype.toSql = function toSql() { class NUMBER extends BaseTypes.NUMBER {
if (this._binary) { toSql() {
return `CHAR BINARY(${this._length})`; let result = this.key;
if (this._unsigned) {
result += ' UNSIGNED';
}
if (this._zerofill) {
result += ' ZEROFILL';
}
if (this._length) {
result += `(${this._length}`;
if (typeof this._decimals === 'number') {
result += `,${this._decimals}`;
}
result += ')';
}
return result;
} }
return BaseTypes.CHAR.prototype.toSql.call(this);
};
function NUMBER(options) {
if (!(this instanceof NUMBER)) return new NUMBER(options);
BaseTypes.NUMBER.apply(this, arguments);
} }
inherits(NUMBER, BaseTypes.NUMBER);
NUMBER.prototype.toSql = function toSql() { class TINYINT extends BaseTypes.TINYINT {
let result = this.key; constructor(length) {
super(length);
if (this._unsigned) { removeUnsupportedIntegerOptions(this);
result += ' UNSIGNED';
}
if (this._zerofill) {
result += ' ZEROFILL';
} }
}
if (this._length) { class SMALLINT extends BaseTypes.SMALLINT {
result += `(${this._length}`; constructor(length) {
if (typeof this._decimals === 'number') { super(length);
result += `,${this._decimals}`; removeUnsupportedIntegerOptions(this);
}
result += ')';
} }
return result;
};
function TINYINT(length) {
if (!(this instanceof TINYINT)) return new TINYINT(length);
BaseTypes.TINYINT.apply(this, arguments);
removeUnsupportedIntegerOptions(this);
} }
inherits(TINYINT, BaseTypes.TINYINT);
TINYINT.prototype.toSql = function toSql() {
return NUMBER.prototype.toSql.call(this);
};
function SMALLINT(length) {
if (!(this instanceof SMALLINT)) return new SMALLINT(length);
BaseTypes.SMALLINT.apply(this, arguments);
removeUnsupportedIntegerOptions(this); class MEDIUMINT extends BaseTypes.MEDIUMINT {
constructor(length) {
super(length);
removeUnsupportedIntegerOptions(this);
}
} }
inherits(SMALLINT, BaseTypes.SMALLINT);
SMALLINT.prototype.toSql = function toSql() {
return NUMBER.prototype.toSql.call(this);
};
function MEDIUMINT(length) { class INTEGER extends BaseTypes.INTEGER {
if (!(this instanceof MEDIUMINT)) return new MEDIUMINT(length); constructor(length) {
BaseTypes.MEDIUMINT.apply(this, arguments); super(length);
removeUnsupportedIntegerOptions(this);
removeUnsupportedIntegerOptions(this); }
} }
inherits(MEDIUMINT, BaseTypes.MEDIUMINT);
MEDIUMINT.prototype.toSql = function toSql() {
return NUMBER.prototype.toSql.call(this);
};
function INTEGER(length) { class BIGINT extends BaseTypes.BIGINT {
if (!(this instanceof INTEGER)) return new INTEGER(length); constructor(length) {
BaseTypes.INTEGER.apply(this, arguments); super(length);
removeUnsupportedIntegerOptions(this);
removeUnsupportedIntegerOptions(this); }
} }
inherits(INTEGER, BaseTypes.INTEGER);
INTEGER.prototype.toSql = function toSql() { class FLOAT extends BaseTypes.FLOAT {
return NUMBER.prototype.toSql.call(this); }
};
function BIGINT(length) {
if (!(this instanceof BIGINT)) return new BIGINT(length);
BaseTypes.BIGINT.apply(this, arguments);
removeUnsupportedIntegerOptions(this); class DOUBLE extends BaseTypes.DOUBLE {
} }
inherits(BIGINT, BaseTypes.BIGINT);
BIGINT.prototype.toSql = function toSql() { class REAL extends BaseTypes.REAL { }
return NUMBER.prototype.toSql.call(this);
};
function FLOAT(length, decimals) { function parseFloating(value) {
if (!(this instanceof FLOAT)) return new FLOAT(length, decimals); if (typeof value !== 'string') {
BaseTypes.FLOAT.apply(this, arguments); return value;
}
if (value === 'NaN') {
return NaN;
}
if (value === 'Infinity') {
return Infinity;
}
if (value === '-Infinity') {
return -Infinity;
}
} }
inherits(FLOAT, BaseTypes.FLOAT); for (const floating of [FLOAT, DOUBLE, REAL]) {
FLOAT.prototype.toSql = function toSql() { floating.parse = parseFloating;
return NUMBER.prototype.toSql.call(this);
}; };
function DOUBLE(length, decimals) {
if (!(this instanceof DOUBLE)) return new DOUBLE(length, decimals);
BaseTypes.DOUBLE.apply(this, arguments);
}
inherits(DOUBLE, BaseTypes.DOUBLE);
DOUBLE.prototype.toSql = function toSql() {
return NUMBER.prototype.toSql.call(this);
};
function REAL(length, decimals) { for (const num of [FLOAT, DOUBLE, REAL, TINYINT, SMALLINT, MEDIUMINT, INTEGER, BIGINT]) {
if (!(this instanceof REAL)) return new REAL(length, decimals); num.prototype.toSql = NUMBER.prototype.toSql;
BaseTypes.REAL.apply(this, arguments);
}
inherits(REAL, BaseTypes.REAL);
REAL.prototype.toSql = function toSql() {
return NUMBER.prototype.toSql.call(this);
}; };
[FLOAT, DOUBLE, REAL].forEach(floating => { class ENUM extends BaseTypes.ENUM {
floating.parse = function parse(value) { toSql() {
if (typeof value === 'string') { return 'TEXT';
if (value === 'NaN') {
return NaN;
}
if (value === 'Infinity') {
return Infinity;
}
if (value === '-Infinity') {
return -Infinity;
}
}
return value;
};
});
function ENUM() {
if (!(this instanceof ENUM)) {
const obj = Object.create(ENUM.prototype);
ENUM.apply(obj, arguments);
return obj;
} }
BaseTypes.ENUM.apply(this, arguments);
} }
inherits(ENUM, BaseTypes.ENUM);
ENUM.prototype.toSql = function toSql() {
return 'TEXT';
};
const exports = { return {
DATE, DATE,
DATEONLY, DATEONLY,
STRING, STRING,
...@@ -292,16 +209,4 @@ module.exports = BaseTypes => { ...@@ -292,16 +209,4 @@ module.exports = BaseTypes => {
JSON: JSONTYPE, JSON: JSONTYPE,
CITEXT CITEXT
}; };
_.forIn(exports, (DataType, key) => {
if (!DataType.key) DataType.key = key;
if (!DataType.extend) {
DataType.extend = oldType => {
return new DataType(oldType.options);
};
}
});
return exports;
}; };
...@@ -11,6 +11,8 @@ const operatorsArray = _.values(operators); ...@@ -11,6 +11,8 @@ const operatorsArray = _.values(operators);
let inflection = require('inflection'); let inflection = require('inflection');
exports.classToInvokable = require('./utils/classToInvokable').classToInvokable;
exports.Promise = Promise; exports.Promise = Promise;
function useInflection(_inflection) { function useInflection(_inflection) {
...@@ -595,15 +597,6 @@ function defaults(object) { ...@@ -595,15 +597,6 @@ function defaults(object) {
} }
exports.defaults = defaults; exports.defaults = defaults;
function classToInvokable(Class) {
return new Proxy(Class, {
apply(Target, thisArg, args) {
return new Target(...args);
}
});
}
exports.classToInvokable = classToInvokable;
/** /**
* *
* @param {Object} index * @param {Object} index
......
'use strict';
/**
* Wraps a constructor to not need the `new` keyword using a proxy.
* Only used for data types.
* @param {Function} ctor
* @return {Proxy}
*/
function classToInvokable(Class) {
return new Proxy(Class, {
apply(Target, thisArg, args) {
return new Target(...args);
}
});
}
exports.classToInvokable = classToInvokable;
'use strict';
const util = require('util');
/**
* like util.inherits, but also copies over static properties. Inherit child constructor
* to have properties from super constructor
*
* @param {Function} constructor the child constructor
* @param {Function} superConstructor the super constructor
*
* @private
*/
function inherits(constructor, superConstructor) {
util.inherits(constructor, superConstructor); // Instance (prototype) methods
Object.assign(constructor, superConstructor); // Static methods
}
module.exports = inherits;
module.exports.inherits = inherits;
module.exports.default = inherits;
...@@ -890,7 +890,6 @@ describe(Support.getTestDialectTeaser('BelongsTo'), () => { ...@@ -890,7 +890,6 @@ describe(Support.getTestDialectTeaser('BelongsTo'), () => {
dataTypes.forEach(dataType => { dataTypes.forEach(dataType => {
const tableName = `TaskXYZ_${dataType.key}`; const tableName = `TaskXYZ_${dataType.key}`;
Tasks[dataType] = this.sequelize.define(tableName, { title: DataTypes.STRING }); Tasks[dataType] = this.sequelize.define(tableName, { title: DataTypes.STRING });
Tasks[dataType].belongsTo(User, { foreignKey: 'userId', keyType: dataType, constraints: false }); Tasks[dataType].belongsTo(User, { foreignKey: 'userId', keyType: dataType, constraints: false });
}); });
......
...@@ -64,6 +64,14 @@ export const TICK_CHAR: string; ...@@ -64,6 +64,14 @@ export const TICK_CHAR: string;
export function addTicks(s: string, tickChar?: string): string; export function addTicks(s: string, tickChar?: string): string;
export function removeTicks(s: string, tickChar?: string): string; export function removeTicks(s: string, tickChar?: string): string;
/**
* Wraps a constructor to not need the `new` keyword using a proxy.
* Only used for data types.
*/
export function classToInvokable<T extends new (...args: any[]) => any>(ctor: T): T & {
(...args: ConstructorParameters<T>): T;
}
export class SequelizeMethod { export class SequelizeMethod {
} }
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!