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

Commit e29b4783 by Aleksander Barszczewski Committed by Timshel Knoll-Miller

pg range handling improved

1 parent f1289159
# Future # Future
- [ADDED] Improve support for pg range type to handle unbound ranges, +/-infinity bounds and empty ranges
- [FIXED] Add support for Babel/ES6 imports [#4881](https://github.com/sequelize/sequelize/issues/4881) - [FIXED] Add support for Babel/ES6 imports [#4881](https://github.com/sequelize/sequelize/issues/4881)
# 3.14.2 # 3.14.2
- [FIXED] Model.aggregate methods now support attributes and where conditions with fields. [#4935](https://github.com/sequelize/sequelize/issues/4935) - [FIXED] Model.aggregate methods now support attributes and where conditions with fields. [#4935](https://github.com/sequelize/sequelize/issues/4935)
...@@ -43,7 +44,7 @@ ...@@ -43,7 +44,7 @@
- [FIXED] Include all with scopes [#4584](https://github.com/sequelize/sequelize/issues/4584) - [FIXED] Include all with scopes [#4584](https://github.com/sequelize/sequelize/issues/4584)
- [INTERNALS] Corrected spelling seperate -> separate - [INTERNALS] Corrected spelling seperate -> separate
- [ADDED] Added `include` and `exclude` to `options.attributes`. [#4074](https://github.com/sequelize/sequelize/issues/4074) - [ADDED] Added `include` and `exclude` to `options.attributes`. [#4074](https://github.com/sequelize/sequelize/issues/4074)
- [FIXED/INTERNALS] Only recurse on plain objects in `mapOptionFieldNames`. [#4596](https://github.com/sequelize/sequelize/issues/4596) - [FIXED/INTERNALS] Only recurse on plain objects in `mapOptionFieldNames`. [#4596](https://github.com/sequelize/sequelize/issues/4596)
# 3.10.0 # 3.10.0
- [ADDED] support `search_path` for postgres with lots of schemas [#4534](https://github.com/sequelize/sequelize/pull/4534) - [ADDED] support `search_path` for postgres with lots of schemas [#4534](https://github.com/sequelize/sequelize/pull/4534)
......
...@@ -2,12 +2,34 @@ ...@@ -2,12 +2,34 @@
var _ = require('lodash'); var _ = require('lodash');
function stringifyRangeBound (bound) {
if (bound === null) {
return '' ;
} else if (bound === Infinity || bound === -Infinity) {
return bound.toString().toLowerCase();
} else {
return JSON.stringify(bound);
}
}
function parseRangeBound (bound, parseType) {
if (!bound) {
return null;
} else if (bound === 'infinity') {
return Infinity;
} else if (bound === '-infinity') {
return -Infinity;
} else {
return parseType(bound);
}
}
function stringify (data) { function stringify (data) {
if (data === null) return null; if (data === null) return null;
if (!_.isArray(data) || data.length !== 2) return ''; if (!_.isArray(data)) throw new Error('range must be an array');
if (!data.length) return 'empty';
if (_.any(data, _.isNull)) return ''; if (data.length !== 2) throw new Error('range array length must be 0 (empty) or 2 (lower and upper bounds)');
if (data.hasOwnProperty('inclusive')) { if (data.hasOwnProperty('inclusive')) {
if (!data.inclusive) data.inclusive = [false, false]; if (!data.inclusive) data.inclusive = [false, false];
...@@ -23,11 +45,19 @@ function stringify (data) { ...@@ -23,11 +45,19 @@ function stringify (data) {
} }
}); });
return (data.inclusive[0] ? '[' : '(') + JSON.stringify(data[0]) + ',' + JSON.stringify(data[1]) + (data.inclusive[1] ? ']' : ')'); var lowerBound = stringifyRangeBound(data[0]);
var upperBound = stringifyRangeBound(data[1]);
return (data.inclusive[0] ? '[' : '(') + lowerBound + ',' + upperBound + (data.inclusive[1] ? ']' : ')');
} }
function parse (value, parser) { function parse (value, parser) {
if (value === null) return null; if (value === null) return null;
if (value === 'empty') {
var empty = [];
empty.inclusive = [];
return empty;
}
var result = value var result = value
.substring(1, value.length - 1) .substring(1, value.length - 1)
...@@ -37,7 +67,7 @@ function parse (value, parser) { ...@@ -37,7 +67,7 @@ function parse (value, parser) {
result = result result = result
.map(function (value) { .map(function (value) {
return parser(value); return parseRangeBound(value, parser);
}); });
result.inclusive = [(value[0] === '['), (value[value.length - 1] === ']')]; result.inclusive = [(value[0] === '['), (value[value.length - 1] === ']')];
......
...@@ -5,29 +5,42 @@ var chai = require('chai') ...@@ -5,29 +5,42 @@ var chai = require('chai')
, Support = require(__dirname + '/../../support') , Support = require(__dirname + '/../../support')
, DataTypes = require(__dirname + '/../../../../lib/data-types') , DataTypes = require(__dirname + '/../../../../lib/data-types')
, dialect = Support.getTestDialect() , dialect = Support.getTestDialect()
, range = require('../../../../lib/dialects/postgres/range'); , range = require('../../../../lib/dialects/postgres/range')
, _ = require('lodash');
if (dialect.match(/^postgres/)) { if (dialect.match(/^postgres/)) {
// Don't try to load pg until we know we're running on postgres.
var pg = require('pg');
describe('[POSTGRES Specific] range datatype', function () { describe('[POSTGRES Specific] range datatype', function () {
describe('stringify', function () { describe('stringify', function () {
it('should handle empty objects correctly', function () { it('should handle empty objects correctly', function () {
expect(range.stringify([])).to.equal(''); expect(range.stringify([])).to.equal('empty');
});
it('should handle null as empty bound', function () {
expect(range.stringify([null, 1])).to.equal('(,1)');
expect(range.stringify([1, null])).to.equal('(1,)');
expect(range.stringify([null, null])).to.equal('(,)');
}); });
it('should return empty string when either of boundaries is null', function () { it('should handle Infinity/-Infinity as infinity/-infinity bounds', function () {
expect(range.stringify([null, 'test'])).to.equal(''); expect(range.stringify([Infinity, 1])).to.equal('(infinity,1)');
expect(range.stringify([123, null])).to.equal(''); expect(range.stringify([1, Infinity])).to.equal('(1,infinity)');
expect(range.stringify([-Infinity, 1])).to.equal('(-infinity,1)');
expect(range.stringify([1, -Infinity])).to.equal('(1,-infinity)');
expect(range.stringify([-Infinity, Infinity])).to.equal('(-infinity,infinity)');
}); });
it('should return empty string when boundaries array of invalid size', function () { it('should throw error when array length is no 0 or 2', function () {
expect(range.stringify([1])).to.equal(''); expect(function () { range.stringify([1]); }).to.throw();
expect(range.stringify([1, 2, 3])).to.equal(''); expect(function () { range.stringify([1, 2, 3]); }).to.throw();
}); });
it('should return empty string when non-array parameter is passed', function () { it('should throw error when non-array parameter is passed', function () {
expect(range.stringify({})).to.equal(''); expect(function () { range.stringify({}); }).to.throw();
expect(range.stringify('test')).to.equal(''); expect(function () { range.stringify('test'); }).to.throw();
expect(range.stringify(undefined)).to.equal(''); expect(function () { range.stringify(undefined); }).to.throw();
}); });
it('should handle array of objects with `inclusive` and `value` properties', function () { it('should handle array of objects with `inclusive` and `value` properties', function () {
...@@ -68,13 +81,34 @@ if (dialect.match(/^postgres/)) { ...@@ -68,13 +81,34 @@ if (dialect.match(/^postgres/)) {
expect(range.parse(null)).to.equal(null); expect(range.parse(null)).to.equal(null);
}); });
it('should handle empty string correctly', function () { it('should handle empty range string correctly', function () {
expect(range.parse('')).to.deep.equal(''); expect(range.parse('empty')).to.deep.equal(_.extend([], { inclusive: [] }));
});
it('should handle empty bounds correctly', function () {
expect(range.parse('(1,)', DataTypes.postgres.INTEGER.parse)).to.deep.equal(_.extend([1, null], { inclusive: [false, false] }));
expect(range.parse('(,1)', DataTypes.postgres.INTEGER.parse)).to.deep.equal(_.extend([null, 1], { inclusive: [false, false] }));
expect(range.parse('(,)', DataTypes.postgres.INTEGER.parse)).to.deep.equal(_.extend([null, null], { inclusive: [false, false] }));
});
it('should handle infinity/-infinity bounds correctly', function () {
expect(range.parse('(infinity,1)', DataTypes.postgres.INTEGER.parse)).to.deep.equal(_.extend([Infinity, 1], { inclusive: [false, false] }));
expect(range.parse('(1,infinity)', DataTypes.postgres.INTEGER.parse)).to.deep.equal(_.extend([1, Infinity], { inclusive: [false, false] }));
expect(range.parse('(-infinity,1)', DataTypes.postgres.INTEGER.parse)).to.deep.equal(_.extend([-Infinity, 1], { inclusive: [false, false] }));
expect(range.parse('(1,-infinity)', DataTypes.postgres.INTEGER.parse)).to.deep.equal(_.extend([1, -Infinity], { inclusive: [false, false] }));
expect(range.parse('(-infinity,infinity)', DataTypes.postgres.INTEGER.parse)).to.deep.equal(_.extend([-Infinity, Infinity], { inclusive: [false, false] }));
}); });
it('should return raw value if not range is returned', function () { it('should return raw value if not range is returned', function () {
expect(range.parse('some_non_array')).to.deep.equal('some_non_array'); expect(range.parse('some_non_array')).to.deep.equal('some_non_array');
}); });
it('should handle native postgres timestamp format', function () {
var 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].toISOString()).to.equal('2016-01-01T12:00:00.000Z');
});
}); });
describe('stringify and parse', function () { describe('stringify and parse', function () {
it('should stringify then parse back the same structure', function () { it('should stringify then parse back the same structure', function () {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!