UNPKG

29.7 kBJavaScriptView Raw
1'use strict';
2
3const util = require('util');
4const _ = require('lodash');
5const wkx = require('wkx');
6const sequelizeErrors = require('./errors');
7const Validator = require('./utils/validator-extras').validator;
8const momentTz = require('moment-timezone');
9const moment = require('moment');
10const { logger } = require('./utils/logger');
11const warnings = {};
12const { classToInvokable } = require('./utils/class-to-invokable');
13const { joinSQLFragments } = require('./utils/join-sql-fragments');
14
15class ABSTRACT {
16 toString(options) {
17 return this.toSql(options);
18 }
19 toSql() {
20 return this.key;
21 }
22 stringify(value, options) {
23 if (this._stringify) {
24 return this._stringify(value, options);
25 }
26 return value;
27 }
28 bindParam(value, options) {
29 if (this._bindParam) {
30 return this._bindParam(value, options);
31 }
32 return options.bindParam(this.stringify(value, options));
33 }
34 static toString() {
35 return this.name;
36 }
37 static warn(link, text) {
38 if (!warnings[text]) {
39 warnings[text] = true;
40 logger.warn(`${text} \n>> Check: ${link}`);
41 }
42 }
43 static extend(oldType) {
44 return new this(oldType.options);
45 }
46}
47
48ABSTRACT.prototype.dialectTypes = '';
49
50/**
51 * STRING A variable length string
52 */
53class STRING extends ABSTRACT {
54 /**
55 * @param {number} [length=255] length of string
56 * @param {boolean} [binary=false] Is this binary?
57 */
58 constructor(length, binary) {
59 super();
60 const options = typeof length === 'object' && length || { length, binary };
61 this.options = options;
62 this._binary = options.binary;
63 this._length = options.length || 255;
64 }
65 toSql() {
66 return joinSQLFragments([
67 `VARCHAR(${this._length})`,
68 this._binary && 'BINARY'
69 ]);
70 }
71 validate(value) {
72 if (Object.prototype.toString.call(value) !== '[object String]') {
73 if (this.options.binary && Buffer.isBuffer(value) || typeof value === 'number') {
74 return true;
75 }
76 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value));
77 }
78 return true;
79 }
80
81 get BINARY() {
82 this._binary = true;
83 this.options.binary = true;
84 return this;
85 }
86
87 static get BINARY() {
88 return new this().BINARY;
89 }
90}
91
92/**
93 * CHAR A fixed length string
94 */
95class CHAR extends STRING {
96 /**
97 * @param {number} [length=255] length of string
98 * @param {boolean} [binary=false] Is this binary?
99 */
100 constructor(length, binary) {
101 super(typeof length === 'object' && length || { length, binary });
102 }
103 toSql() {
104 return joinSQLFragments([
105 `CHAR(${this._length})`,
106 this._binary && 'BINARY'
107 ]);
108 }
109}
110
111/**
112 * Unlimited length TEXT column
113 */
114class TEXT extends ABSTRACT {
115 /**
116 * @param {string} [length=''] could be tiny, medium, long.
117 */
118 constructor(length) {
119 super();
120 const options = typeof length === 'object' && length || { length };
121 this.options = options;
122 this._length = options.length || '';
123 }
124 toSql() {
125 switch (this._length.toLowerCase()) {
126 case 'tiny':
127 return 'TINYTEXT';
128 case 'medium':
129 return 'MEDIUMTEXT';
130 case 'long':
131 return 'LONGTEXT';
132 default:
133 return this.key;
134 }
135 }
136 validate(value) {
137 if (typeof value !== 'string') {
138 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value));
139 }
140 return true;
141 }
142}
143
144/**
145 * An unlimited length case-insensitive text column.
146 * Original case is preserved but acts case-insensitive when comparing values (such as when finding or unique constraints).
147 * Only available in Postgres and SQLite.
148 *
149 */
150class CITEXT extends ABSTRACT {
151 toSql() {
152 return 'CITEXT';
153 }
154 validate(value) {
155 if (typeof value !== 'string') {
156 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value));
157 }
158 return true;
159 }
160}
161
162/**
163 * Base number type which is used to build other types
164 */
165class NUMBER extends ABSTRACT {
166 /**
167 * @param {object} options type options
168 * @param {string|number} [options.length] length of type, like `INT(4)`
169 * @param {boolean} [options.zerofill] Is zero filled?
170 * @param {boolean} [options.unsigned] Is unsigned?
171 * @param {string|number} [options.decimals] number of decimal points, used with length `FLOAT(5, 4)`
172 * @param {string|number} [options.precision] defines precision for decimal type
173 * @param {string|number} [options.scale] defines scale for decimal type
174 */
175 constructor(options = {}) {
176 super();
177 if (typeof options === 'number') {
178 options = {
179 length: options
180 };
181 }
182 this.options = options;
183 this._length = options.length;
184 this._zerofill = options.zerofill;
185 this._decimals = options.decimals;
186 this._precision = options.precision;
187 this._scale = options.scale;
188 this._unsigned = options.unsigned;
189 }
190 toSql() {
191 let result = this.key;
192 if (this._length) {
193 result += `(${this._length}`;
194 if (typeof this._decimals === 'number') {
195 result += `,${this._decimals}`;
196 }
197 result += ')';
198 }
199 if (this._unsigned) {
200 result += ' UNSIGNED';
201 }
202 if (this._zerofill) {
203 result += ' ZEROFILL';
204 }
205 return result;
206 }
207 validate(value) {
208 if (!Validator.isFloat(String(value))) {
209 throw new sequelizeErrors.ValidationError(util.format(`%j is not a valid ${this.key.toLowerCase()}`, value));
210 }
211 return true;
212 }
213 _stringify(number) {
214 if (typeof number === 'number' || typeof number === 'boolean' || number === null || number === undefined) {
215 return number;
216 }
217 if (typeof number.toString === 'function') {
218 return number.toString();
219 }
220 return number;
221 }
222
223 get UNSIGNED() {
224 this._unsigned = true;
225 this.options.unsigned = true;
226 return this;
227 }
228
229 get ZEROFILL() {
230 this._zerofill = true;
231 this.options.zerofill = true;
232 return this;
233 }
234
235 static get UNSIGNED() {
236 return new this().UNSIGNED;
237 }
238
239 static get ZEROFILL() {
240 return new this().ZEROFILL;
241 }
242}
243
244/**
245 * A 32 bit integer
246 */
247class INTEGER extends NUMBER {
248 validate(value) {
249 if (!Validator.isInt(String(value))) {
250 throw new sequelizeErrors.ValidationError(util.format(`%j is not a valid ${this.key.toLowerCase()}`, value));
251 }
252 return true;
253 }
254}
255
256/**
257 * A 8 bit integer
258 */
259class TINYINT extends INTEGER {
260}
261
262/**
263 * A 16 bit integer
264 */
265class SMALLINT extends INTEGER {
266}
267
268/**
269 * A 24 bit integer
270 */
271class MEDIUMINT extends INTEGER {
272}
273
274/**
275 * A 64 bit integer
276 */
277class BIGINT extends INTEGER {
278}
279
280/**
281 * Floating point number (4-byte precision).
282 */
283class FLOAT extends NUMBER {
284 /**
285 * @param {string|number} [length] length of type, like `FLOAT(4)`
286 * @param {string|number} [decimals] number of decimal points, used with length `FLOAT(5, 4)`
287 */
288 constructor(length, decimals) {
289 super(typeof length === 'object' && length || { length, decimals });
290 }
291 validate(value) {
292 if (!Validator.isFloat(String(value))) {
293 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid float', value));
294 }
295 return true;
296 }
297}
298
299/**
300 * Floating point number (4-byte precision).
301 */
302class REAL extends NUMBER {
303 /**
304 * @param {string|number} [length] length of type, like `REAL(4)`
305 * @param {string|number} [decimals] number of decimal points, used with length `REAL(5, 4)`
306 */
307 constructor(length, decimals) {
308 super(typeof length === 'object' && length || { length, decimals });
309 }
310}
311
312/**
313 * Floating point number (8-byte precision).
314 */
315class DOUBLE extends NUMBER {
316 /**
317 * @param {string|number} [length] length of type, like `DOUBLE PRECISION(25)`
318 * @param {string|number} [decimals] number of decimal points, used with length `DOUBLE PRECISION(25, 10)`
319 */
320 constructor(length, decimals) {
321 super(typeof length === 'object' && length || { length, decimals });
322 }
323}
324
325/**
326 * Decimal type, variable precision, take length as specified by user
327 */
328class DECIMAL extends NUMBER {
329 /**
330 * @param {string|number} [precision] defines precision
331 * @param {string|number} [scale] defines scale
332 */
333 constructor(precision, scale) {
334 super(typeof precision === 'object' && precision || { precision, scale });
335 }
336 toSql() {
337 if (this._precision || this._scale) {
338 return `DECIMAL(${[this._precision, this._scale].filter(_.identity).join(',')})`;
339 }
340 return 'DECIMAL';
341 }
342 validate(value) {
343 if (!Validator.isDecimal(String(value))) {
344 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid decimal', value));
345 }
346 return true;
347 }
348}
349
350// TODO: Create intermediate class
351const protoExtensions = {
352 escape: false,
353 _value(value) {
354 if (isNaN(value)) {
355 return 'NaN';
356 }
357 if (!isFinite(value)) {
358 const sign = value < 0 ? '-' : '';
359 return `${sign}Infinity`;
360 }
361
362 return value;
363 },
364 _stringify(value) {
365 return `'${this._value(value)}'`;
366 },
367 _bindParam(value, options) {
368 return options.bindParam(this._value(value));
369 }
370};
371
372for (const floating of [FLOAT, DOUBLE, REAL]) {
373 Object.assign(floating.prototype, protoExtensions);
374}
375
376/**
377 * A boolean / tinyint column, depending on dialect
378 */
379class BOOLEAN extends ABSTRACT {
380 toSql() {
381 return 'TINYINT(1)';
382 }
383 validate(value) {
384 if (!Validator.isBoolean(String(value))) {
385 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid boolean', value));
386 }
387 return true;
388 }
389 _sanitize(value) {
390 if (value !== null && value !== undefined) {
391 if (Buffer.isBuffer(value) && value.length === 1) {
392 // Bit fields are returned as buffers
393 value = value[0];
394 }
395 const type = typeof value;
396 if (type === 'string') {
397 // Only take action on valid boolean strings.
398 return value === 'true' ? true : value === 'false' ? false : value;
399 }
400 if (type === 'number') {
401 // Only take action on valid boolean integers.
402 return value === 1 ? true : value === 0 ? false : value;
403 }
404 }
405 return value;
406 }
407}
408
409
410BOOLEAN.parse = BOOLEAN.prototype._sanitize;
411
412/**
413 * A time column
414 *
415 */
416class TIME extends ABSTRACT {
417 toSql() {
418 return 'TIME';
419 }
420}
421
422/**
423 * Date column with timezone, default is UTC
424 */
425class DATE extends ABSTRACT {
426 /**
427 * @param {string|number} [length] precision to allow storing milliseconds
428 */
429 constructor(length) {
430 super();
431 const options = typeof length === 'object' && length || { length };
432 this.options = options;
433 this._length = options.length || '';
434 }
435 toSql() {
436 return 'DATETIME';
437 }
438 validate(value) {
439 if (!Validator.isDate(String(value))) {
440 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid date', value));
441 }
442 return true;
443 }
444 _sanitize(value, options) {
445 if ((!options || options && !options.raw) && !(value instanceof Date) && !!value) {
446 return new Date(value);
447 }
448 return value;
449 }
450 _isChanged(value, originalValue) {
451 if (originalValue && !!value &&
452 (value === originalValue ||
453 value instanceof Date && originalValue instanceof Date && value.getTime() === originalValue.getTime())) {
454 return false;
455 }
456 // not changed when set to same empty value
457 if (!originalValue && !value && originalValue === value) {
458 return false;
459 }
460 return true;
461 }
462 _applyTimezone(date, options) {
463 if (options.timezone) {
464 if (momentTz.tz.zone(options.timezone)) {
465 return momentTz(date).tz(options.timezone);
466 }
467 return date = moment(date).utcOffset(options.timezone);
468 }
469 return momentTz(date);
470 }
471 _stringify(date, options) {
472 date = this._applyTimezone(date, options);
473 // Z here means current timezone, _not_ UTC
474 return date.format('YYYY-MM-DD HH:mm:ss.SSS Z');
475 }
476}
477
478/**
479 * A date only column (no timestamp)
480 */
481class DATEONLY extends ABSTRACT {
482 toSql() {
483 return 'DATE';
484 }
485 _stringify(date) {
486 return moment(date).format('YYYY-MM-DD');
487 }
488 _sanitize(value, options) {
489 if ((!options || options && !options.raw) && !!value) {
490 return moment(value).format('YYYY-MM-DD');
491 }
492 return value;
493 }
494 _isChanged(value, originalValue) {
495 if (originalValue && !!value && originalValue === value) {
496 return false;
497 }
498 // not changed when set to same empty value
499 if (!originalValue && !value && originalValue === value) {
500 return false;
501 }
502 return true;
503 }
504}
505
506/**
507 * A key / value store column. Only available in Postgres.
508 */
509class HSTORE extends ABSTRACT {
510 validate(value) {
511 if (!_.isPlainObject(value)) {
512 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid hstore', value));
513 }
514 return true;
515 }
516}
517
518/**
519 * A JSON string column. Available in MySQL, Postgres and SQLite
520 */
521class JSONTYPE extends ABSTRACT {
522 validate() {
523 return true;
524 }
525 _stringify(value) {
526 return JSON.stringify(value);
527 }
528}
529
530/**
531 * A binary storage JSON column. Only available in Postgres.
532 */
533class JSONB extends JSONTYPE {
534}
535
536/**
537 * A default value of the current timestamp
538 */
539class NOW extends ABSTRACT {
540}
541
542/**
543 * Binary storage
544 */
545class BLOB extends ABSTRACT {
546 /**
547 * @param {string} [length=''] could be tiny, medium, long.
548 */
549 constructor(length) {
550 super();
551 const options = typeof length === 'object' && length || { length };
552 this.options = options;
553 this._length = options.length || '';
554 }
555 toSql() {
556 switch (this._length.toLowerCase()) {
557 case 'tiny':
558 return 'TINYBLOB';
559 case 'medium':
560 return 'MEDIUMBLOB';
561 case 'long':
562 return 'LONGBLOB';
563 default:
564 return this.key;
565 }
566 }
567 validate(value) {
568 if (typeof value !== 'string' && !Buffer.isBuffer(value)) {
569 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid blob', value));
570 }
571 return true;
572 }
573 _stringify(value) {
574 if (!Buffer.isBuffer(value)) {
575 if (Array.isArray(value)) {
576 value = Buffer.from(value);
577 }
578 else {
579 value = Buffer.from(value.toString());
580 }
581 }
582 const hex = value.toString('hex');
583 return this._hexify(hex);
584 }
585 _hexify(hex) {
586 return `X'${hex}'`;
587 }
588 _bindParam(value, options) {
589 if (!Buffer.isBuffer(value)) {
590 if (Array.isArray(value)) {
591 value = Buffer.from(value);
592 }
593 else {
594 value = Buffer.from(value.toString());
595 }
596 }
597 return options.bindParam(value);
598 }
599}
600
601
602BLOB.prototype.escape = false;
603
604/**
605 * Range types are data types representing a range of values of some element type (called the range's subtype).
606 * Only available in Postgres. See [the Postgres documentation](http://www.postgresql.org/docs/9.4/static/rangetypes.html) for more details
607 */
608class RANGE extends ABSTRACT {
609 /**
610 * @param {ABSTRACT} subtype A subtype for range, like RANGE(DATE)
611 */
612 constructor(subtype) {
613 super();
614 const options = _.isPlainObject(subtype) ? subtype : { subtype };
615 if (!options.subtype)
616 options.subtype = new INTEGER();
617 if (typeof options.subtype === 'function') {
618 options.subtype = new options.subtype();
619 }
620 this._subtype = options.subtype.key;
621 this.options = options;
622 }
623 validate(value) {
624 if (!Array.isArray(value)) {
625 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid range', value));
626 }
627 if (value.length !== 2) {
628 throw new sequelizeErrors.ValidationError('A range must be an array with two elements');
629 }
630 return true;
631 }
632}
633
634/**
635 * A column storing a unique universal identifier.
636 * Use with `UUIDV1` or `UUIDV4` for default values.
637 */
638class UUID extends ABSTRACT {
639 validate(value, options) {
640 if (typeof value !== 'string' || !Validator.isUUID(value) && (!options || !options.acceptStrings)) {
641 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid uuid', value));
642 }
643 return true;
644 }
645}
646
647/**
648 * A default unique universal identifier generated following the UUID v1 standard
649 */
650class UUIDV1 extends ABSTRACT {
651 validate(value, options) {
652 if (typeof value !== 'string' || !Validator.isUUID(value) && (!options || !options.acceptStrings)) {
653 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid uuid', value));
654 }
655 return true;
656 }
657}
658
659/**
660 * A default unique universal identifier generated following the UUID v4 standard
661 */
662class UUIDV4 extends ABSTRACT {
663 validate(value, options) {
664 if (typeof value !== 'string' || !Validator.isUUID(value, 4) && (!options || !options.acceptStrings)) {
665 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid uuidv4', value));
666 }
667 return true;
668 }
669}
670
671/**
672 * 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.
673 *
674 * You could also use it to validate a value before permuting and storing it. VIRTUAL also takes a return type and dependency fields as arguments
675 * If a virtual attribute is present in `attributes` it will automatically pull in the extra fields as well.
676 * Return type is mostly useful for setups that rely on types like GraphQL.
677 *
678 * @example <caption>Checking password length before hashing it</caption>
679 * sequelize.define('user', {
680 * password_hash: DataTypes.STRING,
681 * password: {
682 * type: DataTypes.VIRTUAL,
683 * set: function (val) {
684 * // Remember to set the data value, otherwise it won't be validated
685 * this.setDataValue('password', val);
686 * this.setDataValue('password_hash', this.salt + val);
687 * },
688 * validate: {
689 * isLongEnough: function (val) {
690 * if (val.length < 7) {
691 * throw new Error("Please choose a longer password")
692 * }
693 * }
694 * }
695 * }
696 * })
697 *
698 * # In the above code the password is stored plainly in the password field so it can be validated, but is never stored in the DB.
699 *
700 * @example <caption>Virtual with dependency fields</caption>
701 * {
702 * active: {
703 * type: new DataTypes.VIRTUAL(DataTypes.BOOLEAN, ['createdAt']),
704 * get: function() {
705 * return this.get('createdAt') > Date.now() - (7 * 24 * 60 * 60 * 1000)
706 * }
707 * }
708 * }
709 *
710 */
711class VIRTUAL extends ABSTRACT {
712 /**
713 * @param {ABSTRACT} [ReturnType] return type for virtual type
714 * @param {Array} [fields] array of fields this virtual type is dependent on
715 */
716 constructor(ReturnType, fields) {
717 super();
718 if (typeof ReturnType === 'function')
719 ReturnType = new ReturnType();
720 this.returnType = ReturnType;
721 this.fields = fields;
722 }
723}
724
725/**
726 * An enumeration, Postgres Only
727 *
728 * @example
729 * DataTypes.ENUM('value', 'another value')
730 * DataTypes.ENUM(['value', 'another value'])
731 * DataTypes.ENUM({
732 * values: ['value', 'another value']
733 * })
734 */
735class ENUM extends ABSTRACT {
736 /**
737 * @param {...any|{ values: any[] }|any[]} args either array of values or options object with values array. It also supports variadic values
738 */
739 constructor(...args) {
740 super();
741 const value = args[0];
742 const options = typeof value === 'object' && !Array.isArray(value) && value || {
743 values: args.reduce((result, element) => {
744 return result.concat(Array.isArray(element) ? element : [element]);
745 }, [])
746 };
747 this.values = options.values;
748 this.options = options;
749 }
750 validate(value) {
751 if (!this.values.includes(value)) {
752 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid choice in %j', value, this.values));
753 }
754 return true;
755 }
756}
757
758/**
759 * An array of `type`. Only available in Postgres.
760 *
761 * @example
762 * DataTypes.ARRAY(DataTypes.DECIMAL)
763 */
764class ARRAY extends ABSTRACT {
765 /**
766 * @param {ABSTRACT} type type of array values
767 */
768 constructor(type) {
769 super();
770 const options = _.isPlainObject(type) ? type : { type };
771 this.options = options;
772 this.type = typeof options.type === 'function' ? new options.type() : options.type;
773 }
774 toSql() {
775 return `${this.type.toSql()}[]`;
776 }
777 validate(value) {
778 if (!Array.isArray(value)) {
779 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid array', value));
780 }
781 return true;
782 }
783 static is(obj, type) {
784 return obj instanceof ARRAY && obj.type instanceof type;
785 }
786}
787
788/**
789 * A column storing Geometry information.
790 * It is only available in PostgreSQL (with PostGIS), MariaDB or MySQL.
791 *
792 * GeoJSON is accepted as input and returned as output.
793 *
794 * In PostGIS, the GeoJSON is parsed using the PostGIS function `ST_GeomFromGeoJSON`.
795 * In MySQL it is parsed using the function `GeomFromText`.
796 *
797 * Therefore, one can just follow the [GeoJSON spec](http://geojson.org/geojson-spec.html) for handling geometry objects. See the following examples:
798 *
799 * @example <caption>Defining a Geometry type attribute</caption>
800 * DataTypes.GEOMETRY
801 * DataTypes.GEOMETRY('POINT')
802 * DataTypes.GEOMETRY('POINT', 4326)
803 *
804 * @example <caption>Create a new point</caption>
805 * const point = { type: 'Point', coordinates: [39.807222,-76.984722]};
806 *
807 * User.create({username: 'username', geometry: point });
808 *
809 * @example <caption>Create a new linestring</caption>
810 * const line = { type: 'LineString', 'coordinates': [ [100.0, 0.0], [101.0, 1.0] ] };
811 *
812 * User.create({username: 'username', geometry: line });
813 *
814 * @example <caption>Create a new polygon</caption>
815 * const polygon = { type: 'Polygon', coordinates: [
816 * [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
817 * [100.0, 1.0], [100.0, 0.0] ]
818 * ]};
819 *
820 * User.create({username: 'username', geometry: polygon });
821 *
822 * @example <caption>Create a new point with a custom SRID</caption>
823 * const point = {
824 * type: 'Point',
825 * coordinates: [39.807222,-76.984722],
826 * crs: { type: 'name', properties: { name: 'EPSG:4326'} }
827 * };
828 *
829 * User.create({username: 'username', geometry: point })
830 *
831 *
832 * @see {@link DataTypes.GEOGRAPHY}
833 */
834class GEOMETRY extends ABSTRACT {
835 /**
836 * @param {string} [type] Type of geometry data
837 * @param {string} [srid] SRID of type
838 */
839 constructor(type, srid) {
840 super();
841 const options = _.isPlainObject(type) ? type : { type, srid };
842 this.options = options;
843 this.type = options.type;
844 this.srid = options.srid;
845 }
846 _stringify(value, options) {
847 return `GeomFromText(${options.escape(wkx.Geometry.parseGeoJSON(value).toWkt())})`;
848 }
849 _bindParam(value, options) {
850 return `GeomFromText(${options.bindParam(wkx.Geometry.parseGeoJSON(value).toWkt())})`;
851 }
852}
853
854GEOMETRY.prototype.escape = false;
855
856/**
857 * A geography datatype represents two dimensional spacial objects in an elliptic coord system.
858 *
859 * __The difference from geometry and geography type:__
860 *
861 * PostGIS 1.5 introduced a new spatial type called geography, which uses geodetic measurement instead of Cartesian measurement.
862 * Coordinate points in the geography type are always represented in WGS 84 lon lat degrees (SRID 4326),
863 * but measurement functions and relationships ST_Distance, ST_DWithin, ST_Length, and ST_Area always return answers in meters or assume inputs in meters.
864 *
865 * __What is best to use? It depends:__
866 *
867 * When choosing between the geometry and geography type for data storage, you should consider what you’ll be using it for.
868 * If all you do are simple measurements and relationship checks on your data, and your data covers a fairly large area, then most likely you’ll be better off storing your data using the new geography type.
869 * Although the new geography data type can cover the globe, the geometry type is far from obsolete.
870 * The geometry type has a much richer set of functions than geography, relationship checks are generally faster, and it has wider support currently across desktop and web-mapping tools
871 *
872 * @example <caption>Defining a Geography type attribute</caption>
873 * DataTypes.GEOGRAPHY
874 * DataTypes.GEOGRAPHY('POINT')
875 * DataTypes.GEOGRAPHY('POINT', 4326)
876 */
877class GEOGRAPHY extends ABSTRACT {
878 /**
879 * @param {string} [type] Type of geography data
880 * @param {string} [srid] SRID of type
881 */
882 constructor(type, srid) {
883 super();
884 const options = _.isPlainObject(type) ? type : { type, srid };
885 this.options = options;
886 this.type = options.type;
887 this.srid = options.srid;
888 }
889 _stringify(value, options) {
890 return `GeomFromText(${options.escape(wkx.Geometry.parseGeoJSON(value).toWkt())})`;
891 }
892 _bindParam(value, options) {
893 return `GeomFromText(${options.bindParam(wkx.Geometry.parseGeoJSON(value).toWkt())})`;
894 }
895}
896
897
898GEOGRAPHY.prototype.escape = false;
899
900/**
901 * The cidr type holds an IPv4 or IPv6 network specification. Takes 7 or 19 bytes.
902 *
903 * Only available for Postgres
904 */
905class CIDR extends ABSTRACT {
906 validate(value) {
907 if (typeof value !== 'string' || !Validator.isIPRange(value)) {
908 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid CIDR', value));
909 }
910 return true;
911 }
912}
913
914/**
915 * The INET type holds an IPv4 or IPv6 host address, and optionally its subnet. Takes 7 or 19 bytes
916 *
917 * Only available for Postgres
918 */
919class INET extends ABSTRACT {
920 validate(value) {
921 if (typeof value !== 'string' || !Validator.isIP(value)) {
922 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid INET', value));
923 }
924 return true;
925 }
926}
927
928/**
929 * The MACADDR type stores MAC addresses. Takes 6 bytes
930 *
931 * Only available for Postgres
932 *
933 */
934class MACADDR extends ABSTRACT {
935 validate(value) {
936 if (typeof value !== 'string' || !Validator.isMACAddress(value)) {
937 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid MACADDR', value));
938 }
939 return true;
940 }
941}
942
943/**
944 * The TSVECTOR type stores text search vectors.
945 *
946 * Only available for Postgres
947 *
948 */
949class TSVECTOR extends ABSTRACT {
950 validate(value) {
951 if (typeof value !== 'string') {
952 throw new sequelizeErrors.ValidationError(util.format('%j is not a valid string', value));
953 }
954 return true;
955 }
956}
957
958/**
959 * A convenience class holding commonly used data types. The data types are used when defining a new model using `Sequelize.define`, like this:
960 * ```js
961 * sequelize.define('model', {
962 * column: DataTypes.INTEGER
963 * })
964 * ```
965 * When defining a model you can just as easily pass a string as type, but often using the types defined here is beneficial. For example, using `DataTypes.BLOB`, mean
966 * that that column will be returned as an instance of `Buffer` when being fetched by sequelize.
967 *
968 * To provide a length for the data type, you can invoke it like a function: `INTEGER(2)`
969 *
970 * Some data types have special properties that can be accessed in order to change the data type.
971 * For example, to get an unsigned integer with zerofill you can do `DataTypes.INTEGER.UNSIGNED.ZEROFILL`.
972 * The order you access the properties in do not matter, so `DataTypes.INTEGER.ZEROFILL.UNSIGNED` is fine as well.
973 *
974 * * All number types (`INTEGER`, `BIGINT`, `FLOAT`, `DOUBLE`, `REAL`, `DECIMAL`) expose the properties `UNSIGNED` and `ZEROFILL`
975 * * The `CHAR` and `STRING` types expose the `BINARY` property
976 *
977 * Three of the values provided here (`NOW`, `UUIDV1` and `UUIDV4`) are special default values, that should not be used to define types. Instead they are used as shorthands for
978 * defining default values. For example, to get a uuid field with a default value generated following v1 of the UUID standard:
979 * ```js
980 * sequelize.define('model', {
981 * uuid: {
982 * type: DataTypes.UUID,
983 * defaultValue: DataTypes.UUIDV1,
984 * primaryKey: true
985 * }
986 * })
987 * ```
988 * There may be times when you want to generate your own UUID conforming to some other algorithm. This is accomplished
989 * using the defaultValue property as well, but instead of specifying one of the supplied UUID types, you return a value
990 * from a function.
991 * ```js
992 * sequelize.define('model', {
993 * uuid: {
994 * type: DataTypes.UUID,
995 * defaultValue: function() {
996 * return generateMyId()
997 * },
998 * primaryKey: true
999 * }
1000 * })
1001 * ```
1002 */
1003const DataTypes = module.exports = {
1004 ABSTRACT,
1005 STRING,
1006 CHAR,
1007 TEXT,
1008 NUMBER,
1009 TINYINT,
1010 SMALLINT,
1011 MEDIUMINT,
1012 INTEGER,
1013 BIGINT,
1014 FLOAT,
1015 TIME,
1016 DATE,
1017 DATEONLY,
1018 BOOLEAN,
1019 NOW,
1020 BLOB,
1021 DECIMAL,
1022 NUMERIC: DECIMAL,
1023 UUID,
1024 UUIDV1,
1025 UUIDV4,
1026 HSTORE,
1027 JSON: JSONTYPE,
1028 JSONB,
1029 VIRTUAL,
1030 ARRAY,
1031 ENUM,
1032 RANGE,
1033 REAL,
1034 'DOUBLE PRECISION': DOUBLE,
1035 DOUBLE,
1036 GEOMETRY,
1037 GEOGRAPHY,
1038 CIDR,
1039 INET,
1040 MACADDR,
1041 CITEXT,
1042 TSVECTOR
1043};
1044
1045_.each(DataTypes, (dataType, name) => {
1046 // guard for aliases
1047 if (!Object.prototype.hasOwnProperty.call(dataType, 'key')) {
1048 dataType.types = {};
1049 dataType.key = dataType.prototype.key = name;
1050 }
1051});
1052
1053const dialectMap = {};
1054dialectMap.postgres = require('./dialects/postgres/data-types')(DataTypes);
1055dialectMap.mysql = require('./dialects/mysql/data-types')(DataTypes);
1056dialectMap.mariadb = require('./dialects/mariadb/data-types')(DataTypes);
1057dialectMap.sqlite = require('./dialects/sqlite/data-types')(DataTypes);
1058dialectMap.mssql = require('./dialects/mssql/data-types')(DataTypes);
1059
1060const dialectList = Object.values(dialectMap);
1061
1062for (const dataTypes of dialectList) {
1063 _.each(dataTypes, (DataType, key) => {
1064 if (!DataType.key) {
1065 DataType.key = DataType.prototype.key = key;
1066 }
1067 });
1068}
1069
1070// Wrap all data types to not require `new`
1071for (const dataTypes of [DataTypes, ...dialectList]) {
1072 _.each(dataTypes, (DataType, key) => {
1073 dataTypes[key] = classToInvokable(DataType);
1074 });
1075}
1076
1077Object.assign(DataTypes, dialectMap);