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

Commit 3af05373 by javiertury Committed by Sushant

feat(postgres): dyanmic oids (#10077)

1 parent 567c0198
......@@ -754,31 +754,7 @@ function RANGE(subtype) {
}
inherits(RANGE, ABSTRACT);
const pgRangeSubtypes = {
integer: 'int4range',
bigint: 'int8range',
decimal: 'numrange',
dateonly: 'daterange',
date: 'tstzrange',
datenotz: 'tsrange'
};
const pgRangeCastTypes = {
integer: 'integer',
bigint: 'bigint',
decimal: 'numeric',
dateonly: 'date',
date: 'timestamptz',
datenotz: 'timestamp'
};
RANGE.prototype.key = RANGE.key = 'RANGE';
RANGE.prototype.toSql = function toSql() {
return pgRangeSubtypes[this._subtype.toLowerCase()];
};
RANGE.prototype.toCastType = function toCastType() {
return pgRangeCastTypes[this._subtype.toLowerCase()];
};
RANGE.prototype.validate = function validate(value) {
if (!Array.isArray(value)) {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid range', value));
......
......@@ -18,40 +18,67 @@ class ConnectionManager extends AbstractConnectionManager {
const pgLib = this._loadDialectModule('pg');
this.lib = this.sequelize.config.native ? pgLib.native : pgLib;
this._clearDynamicOIDs();
this._clearTypeParser();
this.refreshTypeParser(dataTypes.postgres);
}
// Expose this as a method so that the parsing may be updated when the user has added additional, custom types
_refreshTypeParser(dataType) {
if (dataType.types.postgres.oids) {
for (const oid of dataType.types.postgres.oids) {
this.oidMap[oid] = value => dataType.parse(value, oid, this.lib.types.getTypeParser);
const arrayParserBuilder = parser => {
return value => this.lib.types.arrayParser.create(value, parser).parse();
};
const rangeParserBuilder = parser => {
return value => dataType.parse(value, {parser});
};
// Set range parsers
if (dataType.key.toLowerCase() === 'range') {
for (const name in this.nameOidMap) {
const entry = this.nameOidMap[name];
if (! entry.rangeOid) continue;
const rangeParser = rangeParserBuilder(this.getTypeParser(entry.oid));
const arrayRangeParser = arrayParserBuilder(rangeParser);
this.oidParserMap.set(entry.rangeOid, rangeParser);
if (! entry.arrayRangeOid) continue;
this.oidParserMap.set(entry.arrayRangeOid, arrayRangeParser);
}
return;
}
if (dataType.types.postgres.array_oids) {
for (const oid of dataType.types.postgres.array_oids) {
this.arrayOidMap[oid] = value => {
return this.lib.types.arrayParser.create(value, v =>
dataType.parse(v, oid, this.lib.types.getTypeParser)
).parse();
};
}
// Create parsers for normal or enum data types
const parser = value => dataType.parse(value);
const arrayParser = arrayParserBuilder(parser);
// Set enum parsers
if (dataType.key.toLowerCase() === 'enum') {
this.enumOids.oids.forEach(oid => {
this.oidParserMap.set(oid, parser);
});
this.enumOids.arrayOids.forEach(arrayOid => {
this.oidParserMap.set(arrayOid, arrayParser);
});
return;
}
// Set parsers for normal data types
dataType.types.postgres.forEach(name => {
if (! this.nameOidMap[name]) return;
this.oidParserMap.set(this.nameOidMap[name].oid, parser);
if (! this.nameOidMap[name].arrayOid) return;
this.oidParserMap.set(this.nameOidMap[name].arrayOid, arrayParser);
});
}
_clearTypeParser() {
this.oidMap = {};
this.arrayOidMap = {};
this.oidParserMap = new Map();
}
getTypeParser(oid) {
if (this.oidMap[oid]) {
return this.oidMap[oid];
} else if (this.arrayOidMap[oid]) {
return this.arrayOidMap[oid];
}
if (this.oidParserMap.get(oid)) return this.oidParserMap.get(oid);
return this.lib.types.getTypeParser.apply(undefined, arguments);
}
......@@ -163,13 +190,9 @@ class ConnectionManager extends AbstractConnectionManager {
return connection.query(query);
}
}).tap(connection => {
if (
dataTypes.GEOGRAPHY.types.postgres.oids.length === 0 &&
dataTypes.GEOMETRY.types.postgres.oids.length === 0 &&
dataTypes.HSTORE.types.postgres.oids.length === 0 &&
dataTypes.ENUM.types.postgres.oids.length === 0 &&
dataTypes.CITEXT.types.postgres.oids.length === 0
) {
if (Object.keys(this.nameOidMap).length === 0 &&
this.enumOids.oids.length === 0 &&
this.enumOids.arrayOids.length === 0) {
return this._refreshDynamicOIDs(connection);
}
}).tap(connection => {
......@@ -205,9 +228,17 @@ class ConnectionManager extends AbstractConnectionManager {
}
// Refresh dynamic OIDs for some types
// These include, Geometry / HStore / Enum / Citext
// These include Geometry / Geography / HStore / Enum / Citext / Range
return (connection || this.sequelize).query(
"SELECT typname, typtype, oid, typarray FROM pg_type WHERE (typtype = 'b' AND typname IN ('hstore', 'geometry', 'geography', 'citext')) OR (typtype = 'e')"
'WITH ranges AS (' +
' SELECT pg_range.rngtypid, pg_type.typname AS rngtypname,' +
' pg_type.typarray AS rngtyparray, pg_range.rngsubtype' +
' FROM pg_range LEFT OUTER JOIN pg_type ON pg_type.oid = pg_range.rngtypid' +
')' +
'SELECT pg_type.typname, pg_type.typtype, pg_type.oid, pg_type.typarray,' +
' ranges.rngtypname, ranges.rngtypid, ranges.rngtyparray' +
' FROM pg_type LEFT OUTER JOIN ranges ON pg_type.oid = ranges.rngsubtype' +
' WHERE (pg_type.typtype IN(\'b\', \'e\'));'
).then(results => {
let result = Array.isArray(results) ? results.pop() : results;
......@@ -220,40 +251,40 @@ class ConnectionManager extends AbstractConnectionManager {
}
}
// Reset OID mapping for dynamic type
[
dataTypes.GEOMETRY,
dataTypes.HSTORE,
dataTypes.GEOGRAPHY,
dataTypes.ENUM,
dataTypes.CITEXT
].forEach(type => {
type.types.postgres.oids = [];
type.types.postgres.array_oids = [];
});
const newNameOidMap = {};
const newEnumOids = {oids: [], arrayOids: []};
for (const row of result.rows) {
let type;
if (row.typname === 'geometry') {
type = dataTypes.GEOMETRY;
} else if (row.typname === 'hstore') {
type = dataTypes.HSTORE;
} else if (row.typname === 'geography') {
type = dataTypes.GEOGRAPHY;
} else if (row.typname === 'citext') {
type = dataTypes.CITEXT;
} else if (row.typtype === 'e') {
type = dataTypes.ENUM;
// Mapping enums, handled separatedly
if (row.typtype === 'e') {
newEnumOids.oids.push(row.oid);
if (row.typarray) newEnumOids.arrayOids.push(row.typarray);
continue;
}
type.types.postgres.oids.push(row.oid);
type.types.postgres.array_oids.push(row.typarray);
}
// Mapping base types and their arrays
newNameOidMap[row.typname] = {oid: row.oid};
if (row.typarray) newNameOidMap[row.typname].arrayOid = row.typarray;
// Mapping ranges(of base types) and their arrays
if (row.rngtypid) {
newNameOidMap[row.typname].rangeOid = row.rngtypid;
if (row.rngtyparray) newNameOidMap[row.typname].arrayRangeOid = row.rngtyparray;
};
};
// Replace all OID mappings. Avoids temporary empty OID mappings.
this.nameOidMap = newNameOidMap;
this.enumOids = newEnumOids;
this.refreshTypeParser(dataTypes.postgres);
});
}
_clearDynamicOIDs() {
this.nameOidMap = {};
this.enumOids = {oids: [], arrayOids: []};
}
}
module.exports = ConnectionManager;
......
......@@ -31,40 +31,13 @@ module.exports = BaseTypes => {
* @see oid here https://github.com/lib/pq/blob/master/oid/types.go
*/
BaseTypes.UUID.types.postgres = {
oids: [2950],
array_oids: [2951]
};
BaseTypes.CIDR.types.postgres = {
oids: [650],
array_oids: [651]
};
BaseTypes.INET.types.postgres = {
oids: [869],
array_oids: [1041]
};
BaseTypes.MACADDR.types.postgres = {
oids: [829],
array_oids: [1040]
};
BaseTypes.JSON.types.postgres = {
oids: [114],
array_oids: [199]
};
BaseTypes.JSONB.types.postgres = {
oids: [3802],
array_oids: [3807]
};
BaseTypes.TIME.types.postgres = {
oids: [1083],
array_oids: [1183]
};
BaseTypes.UUID.types.postgres = ['uuid'];
BaseTypes.CIDR.types.postgres = ['cidr'];
BaseTypes.INET.types.postgres = ['inet'];
BaseTypes.MACADDR.types.postgres = ['macaddr'];
BaseTypes.JSON.types.postgres = ['json'];
BaseTypes.JSONB.types.postgres = ['jsonb'];
BaseTypes.TIME.types.postgres = ['time'];
function DATEONLY() {
if (!(this instanceof DATEONLY)) return new DATEONLY();
......@@ -109,10 +82,7 @@ module.exports = BaseTypes => {
return value;
};
BaseTypes.DATEONLY.types.postgres = {
oids: [1082],
array_oids: [1182]
};
BaseTypes.DATEONLY.types.postgres = ['date'];
function DECIMAL(precision, scale) {
if (!(this instanceof DECIMAL)) return new DECIMAL(precision, scale);
......@@ -126,10 +96,7 @@ module.exports = BaseTypes => {
};
// numeric
BaseTypes.DECIMAL.types.postgres = {
oids: [1700],
array_oids: [1231]
};
BaseTypes.DECIMAL.types.postgres = ['numeric'];
function STRING(length, binary) {
if (!(this instanceof STRING)) return new STRING(length, binary);
......@@ -144,10 +111,7 @@ module.exports = BaseTypes => {
return BaseTypes.STRING.prototype.toSql.call(this);
};
BaseTypes.STRING.types.postgres = {
oids: [1043],
array_oids: [1015]
};
BaseTypes.STRING.types.postgres = ['varchar'];
function TEXT(length) {
if (!(this instanceof TEXT)) return new TEXT(length);
......@@ -163,15 +127,17 @@ module.exports = BaseTypes => {
return 'TEXT';
};
BaseTypes.TEXT.types.postgres = {
oids: [25],
array_oids: [1009]
};
BaseTypes.TEXT.types.postgres = ['text'];
BaseTypes.CITEXT.types.postgres = {
oids: [],
array_oids: []
};
function CITEXT() {
if (!(this instanceof CITEXT)) return new CITEXT();
BaseTypes.CITEXT.apply(this, arguments);
}
inherits(CITEXT, BaseTypes.CITEXT);
CITEXT.parse = value => value;
BaseTypes.CITEXT.types.postgres = ['citext'];
function CHAR(length, binary) {
if (!(this instanceof CHAR)) return new CHAR(length, binary);
......@@ -186,10 +152,7 @@ module.exports = BaseTypes => {
return BaseTypes.CHAR.prototype.toSql.call(this);
};
BaseTypes.CHAR.types.postgres = {
oids: [18, 1042],
array_oids: [1002, 1014]
};
BaseTypes.CHAR.types.postgres = ['char', 'bpchar'];
function BOOLEAN() {
if (!(this instanceof BOOLEAN)) return new BOOLEAN();
......@@ -222,10 +185,7 @@ module.exports = BaseTypes => {
};
BOOLEAN.parse = BOOLEAN.prototype._sanitize;
BaseTypes.BOOLEAN.types.postgres = {
oids: [16],
array_oids: [1000]
};
BaseTypes.BOOLEAN.types.postgres = ['bool'];
function DATE(length) {
if (!(this instanceof DATE)) return new DATE(length);
......@@ -271,10 +231,7 @@ module.exports = BaseTypes => {
return value;
};
BaseTypes.DATE.types.postgres = {
oids: [1184],
array_oids: [1185]
};
BaseTypes.DATE.types.postgres = ['timestamptz'];
function TINYINT(length) {
if (!(this instanceof TINYINT)) return new TINYINT(length);
......@@ -285,10 +242,7 @@ module.exports = BaseTypes => {
inherits(TINYINT, BaseTypes.TINYINT);
// int2
BaseTypes.TINYINT.types.postgres = {
oids: [21],
array_oids: [1005]
};
BaseTypes.TINYINT.types.postgres = ['int2'];
function SMALLINT(length) {
if (!(this instanceof SMALLINT)) return new SMALLINT(length);
......@@ -299,10 +253,7 @@ module.exports = BaseTypes => {
inherits(SMALLINT, BaseTypes.SMALLINT);
// int2
BaseTypes.SMALLINT.types.postgres = {
oids: [21],
array_oids: [1005]
};
BaseTypes.SMALLINT.types.postgres = ['int2'];
function INTEGER(length) {
if (!(this instanceof INTEGER)) return new INTEGER(length);
......@@ -317,10 +268,7 @@ module.exports = BaseTypes => {
};
// int4
BaseTypes.INTEGER.types.postgres = {
oids: [23],
array_oids: [1007]
};
BaseTypes.INTEGER.types.postgres = ['int4'];
function BIGINT(length) {
if (!(this instanceof BIGINT)) return new BIGINT(length);
......@@ -331,10 +279,7 @@ module.exports = BaseTypes => {
inherits(BIGINT, BaseTypes.BIGINT);
// int8
BaseTypes.BIGINT.types.postgres = {
oids: [20],
array_oids: [1016]
};
BaseTypes.BIGINT.types.postgres = ['int8'];
function REAL(length, decimals) {
if (!(this instanceof REAL)) return new REAL(length, decimals);
......@@ -345,10 +290,7 @@ module.exports = BaseTypes => {
inherits(REAL, BaseTypes.REAL);
// float4
BaseTypes.REAL.types.postgres = {
oids: [700],
array_oids: [1021]
};
BaseTypes.REAL.types.postgres = ['float4'];
function DOUBLE(length, decimals) {
if (!(this instanceof DOUBLE)) return new DOUBLE(length, decimals);
......@@ -359,10 +301,7 @@ module.exports = BaseTypes => {
inherits(DOUBLE, BaseTypes.DOUBLE);
// float8
BaseTypes.DOUBLE.types.postgres = {
oids: [701],
array_oids: [1022]
};
BaseTypes.DOUBLE.types.postgres = ['float8'];
function FLOAT(length, decimals) {
if (!(this instanceof FLOAT)) return new FLOAT(length, decimals);
......@@ -409,10 +348,7 @@ module.exports = BaseTypes => {
return `E'\\\\x${hex}'`;
};
BaseTypes.BLOB.types.postgres = {
oids: [17],
array_oids: [1001]
};
BaseTypes.BLOB.types.postgres = ['bytea'];
function GEOMETRY(type, srid) {
if (!(this instanceof GEOMETRY)) return new GEOMETRY(type, srid);
......@@ -436,10 +372,7 @@ module.exports = BaseTypes => {
return result;
};
BaseTypes.GEOMETRY.types.postgres = {
oids: [],
array_oids: []
};
BaseTypes.GEOMETRY.types.postgres = ['geometry'];
GEOMETRY.parse = GEOMETRY.prototype.parse = function parse(value) {
const b = Buffer.from(value, 'hex');
......@@ -475,10 +408,7 @@ module.exports = BaseTypes => {
return result;
};
BaseTypes.GEOGRAPHY.types.postgres = {
oids: [],
array_oids: []
};
BaseTypes.GEOGRAPHY.types.postgres = ['geography'];
GEOGRAPHY.parse = GEOGRAPHY.prototype.parse = function parse(value) {
const b = Buffer.from(value, 'hex');
......@@ -528,10 +458,7 @@ module.exports = BaseTypes => {
return options.bindParam(this._value(value));
};
BaseTypes.HSTORE.types.postgres = {
oids: [],
array_oids: []
};
BaseTypes.HSTORE.types.postgres = ['hstore'];
function RANGE(subtype) {
if (!(this instanceof RANGE)) return new RANGE(subtype);
......@@ -539,26 +466,9 @@ module.exports = BaseTypes => {
}
inherits(RANGE, BaseTypes.RANGE);
RANGE.oid_map = {
3904: 23, // int4
3905: 23,
3906: 1700, // Numeric
3907: 1700,
3908: 1114, // timestamp
3909: 1114,
3910: 1184, // timestamptz
3911: 1184,
3912: 1082, // date
3913: 1082,
3926: 20, // int8
3927: 20
};
const range = require('./range');
RANGE.parse = function parse(value, oid, getTypeParser) {
const parser = getTypeParser(RANGE.oid_map[oid]);
return range.parse(value, parser);
RANGE.parse = function parse(value, options = {parser: val => val}) {
return range.parse(value, options.parser);
};
RANGE.prototype.escape = false;
......@@ -606,8 +516,27 @@ module.exports = BaseTypes => {
};
BaseTypes.RANGE.types.postgres = {
oids: [3904, 3906, 3908, 3910, 3912, 3926],
array_oids: [3905, 3907, 3909, 3911, 3913, 3927]
subtypes: {
integer: 'int4range',
decimal: 'numrange',
date: 'tstzrange',
dateonly: 'daterange',
bigint: 'int8range'
},
castTypes: {
integer: 'int4',
decimal: 'numeric',
date: 'timestamptz',
dateonly: 'date',
bigint: 'int8'
}
};
RANGE.prototype.toSql = function toSql() {
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;
......@@ -659,10 +588,7 @@ module.exports = BaseTypes => {
return value;
};
BaseTypes.ENUM.types.postgres = {
oids: [],
array_oids: []
};
BaseTypes.ENUM.types.postgres = [null];
const exports = {
DECIMAL,
......@@ -670,6 +596,7 @@ module.exports = BaseTypes => {
STRING,
CHAR,
TEXT,
CITEXT,
TINYINT,
SMALLINT,
INTEGER,
......
......@@ -20,14 +20,7 @@ if (dialect.match(/^postgres/)) {
});
}
it('should correctly parse the moment based timezone', function() {
return checkTimezoneParsing(this.sequelize.options);
});
it('should correctly parse the moment based timezone while fetching hstore oids', function() {
// reset oids so we need to refetch them
DataTypes.HSTORE.types.postgres.oids = [];
DataTypes.HSTORE.types.postgres.array_oids = [];
return checkTimezoneParsing(this.sequelize.options);
});
});
......@@ -37,29 +30,92 @@ if (dialect.match(/^postgres/)) {
DataTypes.GEOMETRY,
DataTypes.HSTORE,
DataTypes.GEOGRAPHY,
DataTypes.ENUM,
DataTypes.CITEXT
];
it('should fetch dynamic oids from the database', () => {
dynamicTypesToCheck.forEach(type => {
type.types.postgres.oids = [];
type.types.postgres.array_oids = [];
});
// Expect at least these
const expCastTypes = {
integer: 'int4',
decimal: 'numeric',
date: 'timestamptz',
dateonly: 'date',
bigint: 'int8'
};
function reloadDynamicOIDs(sequelize) {
// Reset oids so we need to refetch them
sequelize.connectionManager._clearDynamicOIDs();
sequelize.connectionManager._clearTypeParser();
// Model is needed to test the ENUM dynamic OID
const User = Support.sequelize.define('User', {
perms: DataTypes.ENUM([
'foo', 'bar'
])
// Force start of connection manager to reload dynamic OIDs
const User = sequelize.define('User', {
perms: DataTypes.ENUM(['foo', 'bar'])
});
return User.sync({force: true}).then(() => {
return User.sync({force: true});
}
it('should fetch regular dynamic oids and create parsers', () => {
const sequelize = Support.sequelize;
return reloadDynamicOIDs(sequelize).then(() => {
dynamicTypesToCheck.forEach(type => {
expect(type.types.postgres.oids, `DataType.${type.key}`).to.not.be.empty;
expect(type.types.postgres.array_oids, `DataType.${type.key}`).to.not.be.empty;
expect(type.types.postgres,
`DataType.${type.key}.types.postgres`).to.not.be.empty;
for (const name of type.types.postgres) {
const entry = sequelize.connectionManager.nameOidMap[name];
const oidParserMap = sequelize.connectionManager.oidParserMap;
expect(entry.oid, `nameOidMap[${name}].oid`).to.be.a('number');
expect(entry.arrayOid, `nameOidMap[${name}].arrayOid`).to.be.a('number');
expect(oidParserMap.get(entry.oid),
`oidParserMap.get(nameOidMap[${name}].oid)`).to.be.a('function');
expect(oidParserMap.get(entry.arrayOid),
`oidParserMap.get(nameOidMap[${name}].arrayOid)`).to.be.a('function');
}
});
});
});
it('should fetch enum dynamic oids and create parsers', () => {
const sequelize = Support.sequelize;
return reloadDynamicOIDs(sequelize).then(() => {
const enumOids = sequelize.connectionManager.enumOids;
const oidParserMap = sequelize.connectionManager.oidParserMap;
expect(enumOids.oids, 'enumOids.oids').to.not.be.empty;
expect(enumOids.arrayOids, 'enumOids.arrayOids').to.not.be.empty;
for (const oid of enumOids.oids) {
expect(oidParserMap.get(oid), 'oidParserMap.get(enumOids.oids)').to.be.a('function');
}
for (const arrayOid of enumOids.arrayOids) {
expect(oidParserMap.get(arrayOid), 'oidParserMap.get(enumOids.arrayOids)').to.be.a('function');
}
});
});
it('should fetch range dynamic oids and create parsers', () => {
const sequelize = Support.sequelize;
return reloadDynamicOIDs(sequelize).then(() => {
for (const baseKey in expCastTypes) {
const name = expCastTypes[baseKey];
const entry = sequelize.connectionManager.nameOidMap[name];
const oidParserMap = sequelize.connectionManager.oidParserMap;
for (const key of ['rangeOid', 'arrayRangeOid']) {
expect(entry[key], `nameOidMap[${name}][${key}]`).to.be.a('number');
}
expect(oidParserMap.get(entry.rangeOid),
`oidParserMap.get(nameOidMap[${name}].rangeOid)`).to.be.a('function');
expect(oidParserMap.get(entry.arrayRangeOid),
`oidParserMap.get(nameOidMap[${name}].arrayRangeOid)`).to.be.a('function');
}
});
});
});
}
......@@ -60,12 +60,12 @@ if (dialect.match(/^postgres/)) {
it('should stringify integer values with appropriate casting', () => {
const Range = new DataTypes.postgres.RANGE(DataTypes.INTEGER);
expect(Range.stringify(1)).to.equal('\'1\'::integer');
expect(Range.stringify(1)).to.equal('\'1\'::int4');
});
it('should stringify bigint values with appropriate casting', () => {
const Range = new DataTypes.postgres.RANGE(DataTypes.BIGINT);
expect(Range.stringify(1)).to.equal('\'1\'::bigint');
expect(Range.stringify(1)).to.equal('\'1\'::int8');
});
it('should stringify numeric values with appropriate casting', () => {
......@@ -185,9 +185,15 @@ if (dialect.match(/^postgres/)) {
});
it('should handle native postgres timestamp format', () => {
const tsOid = DataTypes.postgres.DATE.types.postgres.oids[0],
parser = pg.types.getTypeParser(tsOid);
expect(range.parse('(2016-01-01 08:00:00-04,)', parser)[0].value.toISOString()).to.equal('2016-01-01T12:00:00.000Z');
// Make sure nameOidMap is loaded
Support.sequelize.connectionManager.getConnection().then(connection => {
Support.sequelize.connectionManager.releaseConnection(connection);
const tsName = DataTypes.postgres.DATE.types.postgres[0],
tsOid = Support.sequelize.connectionManager.nameOidMap[tsName].oid,
parser = pg.types.getTypeParser(tsOid);
expect(range.parse('(2016-01-01 08:00:00-04,)', parser)[0].value.toISOString()).to.equal('2016-01-01T12:00:00.000Z');
});
});
});
......@@ -199,7 +205,7 @@ if (dialect.match(/^postgres/)) {
let stringified = Range.stringify(testRange, {});
stringified = stringified.substr(1, stringified.length - 2); // Remove the escaping ticks
expect(DataTypes.postgres.RANGE.parse(stringified, 3904, () => { return DataTypes.postgres.INTEGER.parse; })).to.deep.equal(testRange);
expect(DataTypes.postgres.RANGE.parse(stringified, {parser: DataTypes.postgres.INTEGER.parse})).to.deep.equal(testRange);
});
});
});
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!