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

Commit 3af05373 by javiertury Committed by Sushant

feat(postgres): dyanmic oids (#10077)

1 parent 567c0198
...@@ -754,31 +754,7 @@ function RANGE(subtype) { ...@@ -754,31 +754,7 @@ function RANGE(subtype) {
} }
inherits(RANGE, ABSTRACT); 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.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) { RANGE.prototype.validate = function validate(value) {
if (!Array.isArray(value)) { if (!Array.isArray(value)) {
throw new sequelizeErrors.ValidationError(util.format('%j is not a valid range', value)); throw new sequelizeErrors.ValidationError(util.format('%j is not a valid range', value));
......
...@@ -18,40 +18,67 @@ class ConnectionManager extends AbstractConnectionManager { ...@@ -18,40 +18,67 @@ class ConnectionManager extends AbstractConnectionManager {
const pgLib = this._loadDialectModule('pg'); const pgLib = this._loadDialectModule('pg');
this.lib = this.sequelize.config.native ? pgLib.native : pgLib; this.lib = this.sequelize.config.native ? pgLib.native : pgLib;
this._clearDynamicOIDs();
this._clearTypeParser(); this._clearTypeParser();
this.refreshTypeParser(dataTypes.postgres); this.refreshTypeParser(dataTypes.postgres);
} }
// Expose this as a method so that the parsing may be updated when the user has added additional, custom types // Expose this as a method so that the parsing may be updated when the user has added additional, custom types
_refreshTypeParser(dataType) { _refreshTypeParser(dataType) {
if (dataType.types.postgres.oids) { const arrayParserBuilder = parser => {
for (const oid of dataType.types.postgres.oids) { return value => this.lib.types.arrayParser.create(value, parser).parse();
this.oidMap[oid] = value => dataType.parse(value, oid, this.lib.types.getTypeParser); };
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) { // Create parsers for normal or enum data types
for (const oid of dataType.types.postgres.array_oids) { const parser = value => dataType.parse(value);
this.arrayOidMap[oid] = value => { const arrayParser = arrayParserBuilder(parser);
return this.lib.types.arrayParser.create(value, v =>
dataType.parse(v, oid, this.lib.types.getTypeParser) // Set enum parsers
).parse(); 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() { _clearTypeParser() {
this.oidMap = {}; this.oidParserMap = new Map();
this.arrayOidMap = {};
} }
getTypeParser(oid) { getTypeParser(oid) {
if (this.oidMap[oid]) { if (this.oidParserMap.get(oid)) return this.oidParserMap.get(oid);
return this.oidMap[oid];
} else if (this.arrayOidMap[oid]) {
return this.arrayOidMap[oid];
}
return this.lib.types.getTypeParser.apply(undefined, arguments); return this.lib.types.getTypeParser.apply(undefined, arguments);
} }
...@@ -163,13 +190,9 @@ class ConnectionManager extends AbstractConnectionManager { ...@@ -163,13 +190,9 @@ class ConnectionManager extends AbstractConnectionManager {
return connection.query(query); return connection.query(query);
} }
}).tap(connection => { }).tap(connection => {
if ( if (Object.keys(this.nameOidMap).length === 0 &&
dataTypes.GEOGRAPHY.types.postgres.oids.length === 0 && this.enumOids.oids.length === 0 &&
dataTypes.GEOMETRY.types.postgres.oids.length === 0 && this.enumOids.arrayOids.length === 0) {
dataTypes.HSTORE.types.postgres.oids.length === 0 &&
dataTypes.ENUM.types.postgres.oids.length === 0 &&
dataTypes.CITEXT.types.postgres.oids.length === 0
) {
return this._refreshDynamicOIDs(connection); return this._refreshDynamicOIDs(connection);
} }
}).tap(connection => { }).tap(connection => {
...@@ -205,9 +228,17 @@ class ConnectionManager extends AbstractConnectionManager { ...@@ -205,9 +228,17 @@ class ConnectionManager extends AbstractConnectionManager {
} }
// Refresh dynamic OIDs for some types // 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( 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 => { ).then(results => {
let result = Array.isArray(results) ? results.pop() : results; let result = Array.isArray(results) ? results.pop() : results;
...@@ -220,40 +251,40 @@ class ConnectionManager extends AbstractConnectionManager { ...@@ -220,40 +251,40 @@ class ConnectionManager extends AbstractConnectionManager {
} }
} }
// Reset OID mapping for dynamic type const newNameOidMap = {};
[ const newEnumOids = {oids: [], arrayOids: []};
dataTypes.GEOMETRY,
dataTypes.HSTORE,
dataTypes.GEOGRAPHY,
dataTypes.ENUM,
dataTypes.CITEXT
].forEach(type => {
type.types.postgres.oids = [];
type.types.postgres.array_oids = [];
});
for (const row of result.rows) { for (const row of result.rows) {
let type; // Mapping enums, handled separatedly
if (row.typtype === 'e') {
if (row.typname === 'geometry') { newEnumOids.oids.push(row.oid);
type = dataTypes.GEOMETRY; if (row.typarray) newEnumOids.arrayOids.push(row.typarray);
} else if (row.typname === 'hstore') { continue;
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;
} }
type.types.postgres.oids.push(row.oid); // Mapping base types and their arrays
type.types.postgres.array_oids.push(row.typarray); 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); this.refreshTypeParser(dataTypes.postgres);
}); });
} }
_clearDynamicOIDs() {
this.nameOidMap = {};
this.enumOids = {oids: [], arrayOids: []};
}
} }
module.exports = ConnectionManager; module.exports = ConnectionManager;
......
...@@ -31,40 +31,13 @@ module.exports = BaseTypes => { ...@@ -31,40 +31,13 @@ module.exports = BaseTypes => {
* @see oid here https://github.com/lib/pq/blob/master/oid/types.go * @see oid here https://github.com/lib/pq/blob/master/oid/types.go
*/ */
BaseTypes.UUID.types.postgres = { BaseTypes.UUID.types.postgres = ['uuid'];
oids: [2950], BaseTypes.CIDR.types.postgres = ['cidr'];
array_oids: [2951] BaseTypes.INET.types.postgres = ['inet'];
}; BaseTypes.MACADDR.types.postgres = ['macaddr'];
BaseTypes.JSON.types.postgres = ['json'];
BaseTypes.CIDR.types.postgres = { BaseTypes.JSONB.types.postgres = ['jsonb'];
oids: [650], BaseTypes.TIME.types.postgres = ['time'];
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]
};
function DATEONLY() { function DATEONLY() {
if (!(this instanceof DATEONLY)) return new DATEONLY(); if (!(this instanceof DATEONLY)) return new DATEONLY();
...@@ -109,10 +82,7 @@ module.exports = BaseTypes => { ...@@ -109,10 +82,7 @@ module.exports = BaseTypes => {
return value; return value;
}; };
BaseTypes.DATEONLY.types.postgres = { BaseTypes.DATEONLY.types.postgres = ['date'];
oids: [1082],
array_oids: [1182]
};
function DECIMAL(precision, scale) { function DECIMAL(precision, scale) {
if (!(this instanceof DECIMAL)) return new DECIMAL(precision, scale); if (!(this instanceof DECIMAL)) return new DECIMAL(precision, scale);
...@@ -126,10 +96,7 @@ module.exports = BaseTypes => { ...@@ -126,10 +96,7 @@ module.exports = BaseTypes => {
}; };
// numeric // numeric
BaseTypes.DECIMAL.types.postgres = { BaseTypes.DECIMAL.types.postgres = ['numeric'];
oids: [1700],
array_oids: [1231]
};
function STRING(length, binary) { function STRING(length, binary) {
if (!(this instanceof STRING)) return new STRING(length, binary); if (!(this instanceof STRING)) return new STRING(length, binary);
...@@ -144,10 +111,7 @@ module.exports = BaseTypes => { ...@@ -144,10 +111,7 @@ module.exports = BaseTypes => {
return BaseTypes.STRING.prototype.toSql.call(this); return BaseTypes.STRING.prototype.toSql.call(this);
}; };
BaseTypes.STRING.types.postgres = { BaseTypes.STRING.types.postgres = ['varchar'];
oids: [1043],
array_oids: [1015]
};
function TEXT(length) { function TEXT(length) {
if (!(this instanceof TEXT)) return new TEXT(length); if (!(this instanceof TEXT)) return new TEXT(length);
...@@ -163,15 +127,17 @@ module.exports = BaseTypes => { ...@@ -163,15 +127,17 @@ module.exports = BaseTypes => {
return 'TEXT'; return 'TEXT';
}; };
BaseTypes.TEXT.types.postgres = { BaseTypes.TEXT.types.postgres = ['text'];
oids: [25],
array_oids: [1009]
};
BaseTypes.CITEXT.types.postgres = { function CITEXT() {
oids: [], if (!(this instanceof CITEXT)) return new CITEXT();
array_oids: [] BaseTypes.CITEXT.apply(this, arguments);
}; }
inherits(CITEXT, BaseTypes.CITEXT);
CITEXT.parse = value => value;
BaseTypes.CITEXT.types.postgres = ['citext'];
function CHAR(length, binary) { function CHAR(length, binary) {
if (!(this instanceof CHAR)) return new CHAR(length, binary); if (!(this instanceof CHAR)) return new CHAR(length, binary);
...@@ -186,10 +152,7 @@ module.exports = BaseTypes => { ...@@ -186,10 +152,7 @@ module.exports = BaseTypes => {
return BaseTypes.CHAR.prototype.toSql.call(this); return BaseTypes.CHAR.prototype.toSql.call(this);
}; };
BaseTypes.CHAR.types.postgres = { BaseTypes.CHAR.types.postgres = ['char', 'bpchar'];
oids: [18, 1042],
array_oids: [1002, 1014]
};
function BOOLEAN() { function BOOLEAN() {
if (!(this instanceof BOOLEAN)) return new BOOLEAN(); if (!(this instanceof BOOLEAN)) return new BOOLEAN();
...@@ -222,10 +185,7 @@ module.exports = BaseTypes => { ...@@ -222,10 +185,7 @@ module.exports = BaseTypes => {
}; };
BOOLEAN.parse = BOOLEAN.prototype._sanitize; BOOLEAN.parse = BOOLEAN.prototype._sanitize;
BaseTypes.BOOLEAN.types.postgres = { BaseTypes.BOOLEAN.types.postgres = ['bool'];
oids: [16],
array_oids: [1000]
};
function DATE(length) { function DATE(length) {
if (!(this instanceof DATE)) return new DATE(length); if (!(this instanceof DATE)) return new DATE(length);
...@@ -271,10 +231,7 @@ module.exports = BaseTypes => { ...@@ -271,10 +231,7 @@ module.exports = BaseTypes => {
return value; return value;
}; };
BaseTypes.DATE.types.postgres = { BaseTypes.DATE.types.postgres = ['timestamptz'];
oids: [1184],
array_oids: [1185]
};
function TINYINT(length) { function TINYINT(length) {
if (!(this instanceof TINYINT)) return new TINYINT(length); if (!(this instanceof TINYINT)) return new TINYINT(length);
...@@ -285,10 +242,7 @@ module.exports = BaseTypes => { ...@@ -285,10 +242,7 @@ module.exports = BaseTypes => {
inherits(TINYINT, BaseTypes.TINYINT); inherits(TINYINT, BaseTypes.TINYINT);
// int2 // int2
BaseTypes.TINYINT.types.postgres = { BaseTypes.TINYINT.types.postgres = ['int2'];
oids: [21],
array_oids: [1005]
};
function SMALLINT(length) { function SMALLINT(length) {
if (!(this instanceof SMALLINT)) return new SMALLINT(length); if (!(this instanceof SMALLINT)) return new SMALLINT(length);
...@@ -299,10 +253,7 @@ module.exports = BaseTypes => { ...@@ -299,10 +253,7 @@ module.exports = BaseTypes => {
inherits(SMALLINT, BaseTypes.SMALLINT); inherits(SMALLINT, BaseTypes.SMALLINT);
// int2 // int2
BaseTypes.SMALLINT.types.postgres = { BaseTypes.SMALLINT.types.postgres = ['int2'];
oids: [21],
array_oids: [1005]
};
function INTEGER(length) { function INTEGER(length) {
if (!(this instanceof INTEGER)) return new INTEGER(length); if (!(this instanceof INTEGER)) return new INTEGER(length);
...@@ -317,10 +268,7 @@ module.exports = BaseTypes => { ...@@ -317,10 +268,7 @@ module.exports = BaseTypes => {
}; };
// int4 // int4
BaseTypes.INTEGER.types.postgres = { BaseTypes.INTEGER.types.postgres = ['int4'];
oids: [23],
array_oids: [1007]
};
function BIGINT(length) { function BIGINT(length) {
if (!(this instanceof BIGINT)) return new BIGINT(length); if (!(this instanceof BIGINT)) return new BIGINT(length);
...@@ -331,10 +279,7 @@ module.exports = BaseTypes => { ...@@ -331,10 +279,7 @@ module.exports = BaseTypes => {
inherits(BIGINT, BaseTypes.BIGINT); inherits(BIGINT, BaseTypes.BIGINT);
// int8 // int8
BaseTypes.BIGINT.types.postgres = { BaseTypes.BIGINT.types.postgres = ['int8'];
oids: [20],
array_oids: [1016]
};
function REAL(length, decimals) { function REAL(length, decimals) {
if (!(this instanceof REAL)) return new REAL(length, decimals); if (!(this instanceof REAL)) return new REAL(length, decimals);
...@@ -345,10 +290,7 @@ module.exports = BaseTypes => { ...@@ -345,10 +290,7 @@ module.exports = BaseTypes => {
inherits(REAL, BaseTypes.REAL); inherits(REAL, BaseTypes.REAL);
// float4 // float4
BaseTypes.REAL.types.postgres = { BaseTypes.REAL.types.postgres = ['float4'];
oids: [700],
array_oids: [1021]
};
function DOUBLE(length, decimals) { function DOUBLE(length, decimals) {
if (!(this instanceof DOUBLE)) return new DOUBLE(length, decimals); if (!(this instanceof DOUBLE)) return new DOUBLE(length, decimals);
...@@ -359,10 +301,7 @@ module.exports = BaseTypes => { ...@@ -359,10 +301,7 @@ module.exports = BaseTypes => {
inherits(DOUBLE, BaseTypes.DOUBLE); inherits(DOUBLE, BaseTypes.DOUBLE);
// float8 // float8
BaseTypes.DOUBLE.types.postgres = { BaseTypes.DOUBLE.types.postgres = ['float8'];
oids: [701],
array_oids: [1022]
};
function FLOAT(length, decimals) { function FLOAT(length, decimals) {
if (!(this instanceof FLOAT)) return new FLOAT(length, decimals); if (!(this instanceof FLOAT)) return new FLOAT(length, decimals);
...@@ -409,10 +348,7 @@ module.exports = BaseTypes => { ...@@ -409,10 +348,7 @@ module.exports = BaseTypes => {
return `E'\\\\x${hex}'`; return `E'\\\\x${hex}'`;
}; };
BaseTypes.BLOB.types.postgres = { BaseTypes.BLOB.types.postgres = ['bytea'];
oids: [17],
array_oids: [1001]
};
function GEOMETRY(type, srid) { function GEOMETRY(type, srid) {
if (!(this instanceof GEOMETRY)) return new GEOMETRY(type, srid); if (!(this instanceof GEOMETRY)) return new GEOMETRY(type, srid);
...@@ -436,10 +372,7 @@ module.exports = BaseTypes => { ...@@ -436,10 +372,7 @@ module.exports = BaseTypes => {
return result; return result;
}; };
BaseTypes.GEOMETRY.types.postgres = { BaseTypes.GEOMETRY.types.postgres = ['geometry'];
oids: [],
array_oids: []
};
GEOMETRY.parse = GEOMETRY.prototype.parse = function parse(value) { GEOMETRY.parse = GEOMETRY.prototype.parse = function parse(value) {
const b = Buffer.from(value, 'hex'); const b = Buffer.from(value, 'hex');
...@@ -475,10 +408,7 @@ module.exports = BaseTypes => { ...@@ -475,10 +408,7 @@ module.exports = BaseTypes => {
return result; return result;
}; };
BaseTypes.GEOGRAPHY.types.postgres = { BaseTypes.GEOGRAPHY.types.postgres = ['geography'];
oids: [],
array_oids: []
};
GEOGRAPHY.parse = GEOGRAPHY.prototype.parse = function parse(value) { GEOGRAPHY.parse = GEOGRAPHY.prototype.parse = function parse(value) {
const b = Buffer.from(value, 'hex'); const b = Buffer.from(value, 'hex');
...@@ -528,10 +458,7 @@ module.exports = BaseTypes => { ...@@ -528,10 +458,7 @@ module.exports = BaseTypes => {
return options.bindParam(this._value(value)); return options.bindParam(this._value(value));
}; };
BaseTypes.HSTORE.types.postgres = { BaseTypes.HSTORE.types.postgres = ['hstore'];
oids: [],
array_oids: []
};
function RANGE(subtype) { function RANGE(subtype) {
if (!(this instanceof RANGE)) return new RANGE(subtype); if (!(this instanceof RANGE)) return new RANGE(subtype);
...@@ -539,26 +466,9 @@ module.exports = BaseTypes => { ...@@ -539,26 +466,9 @@ module.exports = BaseTypes => {
} }
inherits(RANGE, BaseTypes.RANGE); 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'); const range = require('./range');
RANGE.parse = function parse(value, oid, getTypeParser) { RANGE.parse = function parse(value, options = {parser: val => val}) {
const parser = getTypeParser(RANGE.oid_map[oid]); return range.parse(value, options.parser);
return range.parse(value, parser);
}; };
RANGE.prototype.escape = false; RANGE.prototype.escape = false;
...@@ -606,8 +516,27 @@ module.exports = BaseTypes => { ...@@ -606,8 +516,27 @@ module.exports = BaseTypes => {
}; };
BaseTypes.RANGE.types.postgres = { BaseTypes.RANGE.types.postgres = {
oids: [3904, 3906, 3908, 3910, 3912, 3926], subtypes: {
array_oids: [3905, 3907, 3909, 3911, 3913, 3927] 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; BaseTypes.ARRAY.prototype.escape = false;
...@@ -659,10 +588,7 @@ module.exports = BaseTypes => { ...@@ -659,10 +588,7 @@ module.exports = BaseTypes => {
return value; return value;
}; };
BaseTypes.ENUM.types.postgres = { BaseTypes.ENUM.types.postgres = [null];
oids: [],
array_oids: []
};
const exports = { const exports = {
DECIMAL, DECIMAL,
...@@ -670,6 +596,7 @@ module.exports = BaseTypes => { ...@@ -670,6 +596,7 @@ module.exports = BaseTypes => {
STRING, STRING,
CHAR, CHAR,
TEXT, TEXT,
CITEXT,
TINYINT, TINYINT,
SMALLINT, SMALLINT,
INTEGER, INTEGER,
......
...@@ -20,14 +20,7 @@ if (dialect.match(/^postgres/)) { ...@@ -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() { 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); return checkTimezoneParsing(this.sequelize.options);
}); });
}); });
...@@ -37,29 +30,92 @@ if (dialect.match(/^postgres/)) { ...@@ -37,29 +30,92 @@ if (dialect.match(/^postgres/)) {
DataTypes.GEOMETRY, DataTypes.GEOMETRY,
DataTypes.HSTORE, DataTypes.HSTORE,
DataTypes.GEOGRAPHY, DataTypes.GEOGRAPHY,
DataTypes.ENUM,
DataTypes.CITEXT DataTypes.CITEXT
]; ];
it('should fetch dynamic oids from the database', () => { // Expect at least these
dynamicTypesToCheck.forEach(type => { const expCastTypes = {
type.types.postgres.oids = []; integer: 'int4',
type.types.postgres.array_oids = []; 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 // Force start of connection manager to reload dynamic OIDs
const User = Support.sequelize.define('User', { const User = sequelize.define('User', {
perms: DataTypes.ENUM([ perms: DataTypes.ENUM(['foo', 'bar'])
'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 => { dynamicTypesToCheck.forEach(type => {
expect(type.types.postgres.oids, `DataType.${type.key}`).to.not.be.empty; expect(type.types.postgres,
expect(type.types.postgres.array_oids, `DataType.${type.key}`).to.not.be.empty; `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/)) { ...@@ -60,12 +60,12 @@ if (dialect.match(/^postgres/)) {
it('should stringify integer values with appropriate casting', () => { it('should stringify integer values with appropriate casting', () => {
const Range = new DataTypes.postgres.RANGE(DataTypes.INTEGER); 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', () => { it('should stringify bigint values with appropriate casting', () => {
const Range = new DataTypes.postgres.RANGE(DataTypes.BIGINT); 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', () => { it('should stringify numeric values with appropriate casting', () => {
...@@ -185,9 +185,15 @@ if (dialect.match(/^postgres/)) { ...@@ -185,9 +185,15 @@ if (dialect.match(/^postgres/)) {
}); });
it('should handle native postgres timestamp format', () => { it('should handle native postgres timestamp format', () => {
const tsOid = DataTypes.postgres.DATE.types.postgres.oids[0], // Make sure nameOidMap is loaded
parser = pg.types.getTypeParser(tsOid); Support.sequelize.connectionManager.getConnection().then(connection => {
expect(range.parse('(2016-01-01 08:00:00-04,)', parser)[0].value.toISOString()).to.equal('2016-01-01T12:00:00.000Z'); 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/)) { ...@@ -199,7 +205,7 @@ if (dialect.match(/^postgres/)) {
let stringified = Range.stringify(testRange, {}); let stringified = Range.stringify(testRange, {});
stringified = stringified.substr(1, stringified.length - 2); // Remove the escaping ticks 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!