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

Commit 56dbddde by Jan Aagaard Meier

Merge pull request #4013 from mikeringrose/master

Adding GEOMETRY Support
2 parents c38aa5f7 b81e559e
...@@ -26,6 +26,7 @@ addons: ...@@ -26,6 +26,7 @@ addons:
before_script: before_script:
- "mysql -e 'create database sequelize_test;'" - "mysql -e 'create database sequelize_test;'"
- "psql -c 'create database sequelize_test;' -U postgres" - "psql -c 'create database sequelize_test;' -U postgres"
- "psql sequelize_test -c 'create extension postgis;' -U postgres"
script: script:
- "make test" - "make test"
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
var util = require('util') var util = require('util')
, _ = require('lodash') , _ = require('lodash')
, Wkt = require('wellknown')
, warnings = {}; , warnings = {};
/** /**
...@@ -594,6 +595,26 @@ var helpers = { ...@@ -594,6 +595,26 @@ var helpers = {
SCALE: [DECIMAL] SCALE: [DECIMAL]
}; };
var GEOMETRY = function(type, srid) {
var options = _.isPlainObject(type) ? type : {
type: type,
srid: srid
};
if (!(this instanceof GEOMETRY)) return new GEOMETRY(options);
this.options = options;
this.type = options.type;
this.srid = options.srid;
};
util.inherits(GEOMETRY, ABSTRACT);
GEOMETRY.prototype.key = GEOMETRY.key = 'GEOMETRY';
GEOMETRY.prototype.parse = function(value) {
return Wkt.parse(value);
};
Object.keys(helpers).forEach(function (helper) { Object.keys(helpers).forEach(function (helper) {
helpers[helper].forEach(function (DataType) { helpers[helper].forEach(function (DataType) {
if (!DataType[helper]) { if (!DataType[helper]) {
...@@ -640,5 +661,6 @@ module.exports = { ...@@ -640,5 +661,6 @@ module.exports = {
RANGE: RANGE, RANGE: RANGE,
REAL: REAL, REAL: REAL,
DOUBLE: DOUBLE, DOUBLE: DOUBLE,
'DOUBLE PRECISION': DOUBLE 'DOUBLE PRECISION': DOUBLE,
GEOMETRY: GEOMETRY
}; };
...@@ -996,7 +996,10 @@ var QueryGenerator = { ...@@ -996,7 +996,10 @@ var QueryGenerator = {
attr[0] = self.handleSequelizeMethod(attr[0]); attr[0] = self.handleSequelizeMethod(attr[0]);
addTable = false; addTable = false;
} else { } else {
if (attr[0].indexOf('(') === -1 && attr[0].indexOf(')') === -1) { if (mainModel && mainModel._isGeometryAttribute(attr[1])) {
attr[0] = this.geometrySelect(attr[0]);
}
else if (attr[0].indexOf('(') === -1 && attr[0].indexOf(')') === -1) {
attr[0] = self.quoteIdentifier(attr[0]); attr[0] = self.quoteIdentifier(attr[0]);
} }
} }
...@@ -2143,6 +2146,10 @@ var QueryGenerator = { ...@@ -2143,6 +2146,10 @@ var QueryGenerator = {
booleanValue: function(value) { booleanValue: function(value) {
return value; return value;
},
geometrySelect: function(column) {
return this.quoteIdentifiers(column);
} }
}; };
......
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
var BaseTypes = require('../../data-types') var BaseTypes = require('../../data-types')
, util = require('util') , util = require('util')
, _ = require('lodash'); , _ = require('lodash')
, wkx = require('wkx')
, wkt = require('wellknown');
BaseTypes.ABSTRACT.prototype.dialectTypes = 'http://www.postgresql.org/docs/9.4/static/datatype.html'; BaseTypes.ABSTRACT.prototype.dialectTypes = 'http://www.postgresql.org/docs/9.4/static/datatype.html';
...@@ -153,6 +155,33 @@ BaseTypes.BLOB.prototype.toSql = function() { ...@@ -153,6 +155,33 @@ BaseTypes.BLOB.prototype.toSql = function() {
return 'BYTEA'; return 'BYTEA';
}; };
var GEOMETRY = function() {
if (!(this instanceof GEOMETRY)) return new GEOMETRY();
BaseTypes.GEOMETRY.apply(this, arguments);
};
util.inherits(GEOMETRY, BaseTypes.GEOMETRY);
GEOMETRY.prototype.toSql = function() {
var result = this.key;
if (this.type){
result += '(' + this.type;
if (this.srid){
result += ',' + this.srid;
}
result += ')';
}
return result;
};
GEOMETRY.prototype.parse = function(value) {
var b = new Buffer(value, 'hex');
return wkt.parse(wkx.Geometry.parse(b).toWkt());
};
module.exports = { module.exports = {
STRING: STRING, STRING: STRING,
CHAR: CHAR, CHAR: CHAR,
...@@ -162,7 +191,8 @@ module.exports = { ...@@ -162,7 +191,8 @@ module.exports = {
BIGINT: BIGINT, BIGINT: BIGINT,
REAL: REAL, REAL: REAL,
'DOUBLE PRECISION': DOUBLE, 'DOUBLE PRECISION': DOUBLE,
FLOAT: FLOAT FLOAT: FLOAT,
GEOMETRY: GEOMETRY
}; };
_.forIn(module.exports, function (DataType, key) { _.forIn(module.exports, function (DataType, key) {
...@@ -172,4 +202,4 @@ _.forIn(module.exports, function (DataType, key) { ...@@ -172,4 +202,4 @@ _.forIn(module.exports, function (DataType, key) {
return new DataType(oldType.options); return new DataType(oldType.options);
}; };
} }
}); });
\ No newline at end of file
...@@ -95,7 +95,7 @@ var QueryGenerator = { ...@@ -95,7 +95,7 @@ var QueryGenerator = {
}, },
showTablesQuery: function() { showTablesQuery: function() {
return "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';"; return "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type LIKE '%TABLE' AND table_name != 'spatial_ref_sys';";
}, },
describeTableQuery: function(tableName, schema) { describeTableQuery: function(tableName, schema) {
...@@ -897,6 +897,13 @@ var QueryGenerator = { ...@@ -897,6 +897,13 @@ var QueryGenerator = {
return 'ARRAY[' + value.map(function (v) { return 'ARRAY[' + value.map(function (v) {
return SqlString.escape(JSON.stringify(v), false, this.options.timezone, this.dialect, field); return SqlString.escape(JSON.stringify(v), false, this.options.timezone, this.dialect, field);
}, this).join(',') + ']::' + jsonType.key + '[]'; }, this).join(',') + ']::' + jsonType.key + '[]';
} else if (value && field && field.type instanceof DataTypes.GEOMETRY) {
var s = "ST_GeomFromGeoJSON('" + JSON.stringify(value) + "'";
if (field.type.srid) {
s += + ", " + field.type.srid;
}
s += ")";
return s;
} }
return SqlString.escape(value, false, this.options.timezone, this.dialect, field); return SqlString.escape(value, false, this.options.timezone, this.dialect, field);
......
...@@ -28,6 +28,10 @@ parseDialectSpecificFields = { ...@@ -28,6 +28,10 @@ parseDialectSpecificFields = {
this.lib.types.arrayParser.create(value, function (v) { this.lib.types.arrayParser.create(value, function (v) {
return range.parse(v, options.dataType.type); return range.parse(v, options.dataType.type);
}).parse() : range.parse(value, options.dataType); }).parse() : range.parse(value, options.dataType);
},
geometry: function (value, options) {
if (value === null) return null;
return options.dataType.parse(value);
} }
}; };
......
...@@ -360,6 +360,11 @@ Instance.prototype.set = function(key, value, options) { ...@@ -360,6 +360,11 @@ Instance.prototype.set = function(key, value, options) {
value = (value === 1) ? true : (value === 0) ? false : value; value = (value === 1) ? true : (value === 0) ? false : value;
} }
} }
if (this.Model._hasGeometryAttributes && this.Model._isGeometryAttribute(key) && _.isString(value)) {
value = this.dialect.DatatTypes.GEOMETRY.parse(value);
}
if (!options.raw && ((primitives.indexOf(typeof value) === -1 && value !== null) || value !== originalValue)) { if (!options.raw && ((primitives.indexOf(typeof value) === -1 && value !== null) || value !== originalValue)) {
this._previousDataValues[key] = originalValue; this._previousDataValues[key] = originalValue;
this.changed(key, true); this.changed(key, true);
...@@ -978,7 +983,7 @@ Instance.prototype.decrement = function(fields, options) { ...@@ -978,7 +983,7 @@ Instance.prototype.decrement = function(fields, options) {
*/ */
Instance.prototype.equals = function(other) { Instance.prototype.equals = function(other) {
var result = true; var result = true;
if (!other || !other.dataValues) { if (!other || !other.dataValues) {
return false; return false;
} }
......
...@@ -736,6 +736,7 @@ Model.prototype.refreshAttributes = function() { ...@@ -736,6 +736,7 @@ Model.prototype.refreshAttributes = function() {
this._hstoreAttributes = []; this._hstoreAttributes = [];
this._rangeAttributes = []; this._rangeAttributes = [];
this._jsonAttributes = []; this._jsonAttributes = [];
this._geometryAttributes = [];
this._virtualAttributes = []; this._virtualAttributes = [];
this._defaultValues = {}; this._defaultValues = {};
this.Instance.prototype.validators = {}; this.Instance.prototype.validators = {};
...@@ -767,6 +768,8 @@ Model.prototype.refreshAttributes = function() { ...@@ -767,6 +768,8 @@ Model.prototype.refreshAttributes = function() {
self._jsonAttributes.push(name); self._jsonAttributes.push(name);
} else if (definition.type instanceof DataTypes.VIRTUAL) { } else if (definition.type instanceof DataTypes.VIRTUAL) {
self._virtualAttributes.push(name); self._virtualAttributes.push(name);
} else if (definition.type instanceof DataTypes.GEOMETRY) {
self._geometryAttributes.push(name);
} }
if (definition.hasOwnProperty('defaultValue')) { if (definition.hasOwnProperty('defaultValue')) {
...@@ -825,6 +828,11 @@ Model.prototype.refreshAttributes = function() { ...@@ -825,6 +828,11 @@ Model.prototype.refreshAttributes = function() {
return self._virtualAttributes.indexOf(key) !== -1; return self._virtualAttributes.indexOf(key) !== -1;
}); });
this._hasGeometryAttributes = !!this._geometryAttributes.length;
this._isGeometryAttribute = Utils._.memoize(function(key) {
return self._geometryAttributes.indexOf(key) !== -1;
});
this._hasDefaultValues = !Utils._.isEmpty(this._defaultValues); this._hasDefaultValues = !Utils._.isEmpty(this._defaultValues);
this.attributes = this.rawAttributes; this.attributes = this.rawAttributes;
......
...@@ -43,7 +43,9 @@ ...@@ -43,7 +43,9 @@
"node-uuid": "~1.4.1", "node-uuid": "~1.4.1",
"shimmer": "1.0.0", "shimmer": "1.0.0",
"toposort-class": "~0.3.0", "toposort-class": "~0.3.0",
"validator": "^3.34.0" "validator": "^3.34.0",
"wellknown": "^0.3.1",
"wkx": "0.0.7"
}, },
"devDependencies": { "devDependencies": {
"chai": "^2.1.2", "chai": "^2.1.2",
......
...@@ -31,7 +31,8 @@ if (dialect.match(/^postgres/)) { ...@@ -31,7 +31,8 @@ if (dialect.match(/^postgres/)) {
course_period: DataTypes.RANGE(DataTypes.DATE), course_period: DataTypes.RANGE(DataTypes.DATE),
acceptable_marks: { type: DataTypes.RANGE(DataTypes.DECIMAL), defaultValue: [0.65, 1] }, acceptable_marks: { type: DataTypes.RANGE(DataTypes.DECIMAL), defaultValue: [0.65, 1] },
available_amount: DataTypes.RANGE, available_amount: DataTypes.RANGE,
holidays: DataTypes.ARRAY(DataTypes.RANGE(DataTypes.DATE)) holidays: DataTypes.ARRAY(DataTypes.RANGE(DataTypes.DATE)),
location: DataTypes.GEOMETRY()
}); });
return this.User.sync({ force: true }); return this.User.sync({ force: true });
}); });
...@@ -875,6 +876,35 @@ if (dialect.match(/^postgres/)) { ...@@ -875,6 +876,35 @@ if (dialect.match(/^postgres/)) {
}); });
}); });
it('should save geometry correctly', function() {
var point = { type: 'Point', coordinates: [39.807222,-76.984722] };
return this.User.create({ username: 'user', email: ['foo@bar.com'], location: point}).then(function(newUser) {
expect(newUser.location).to.deep.eql(point);
});
});
it('should update geometry correctly', function() {
var User = this.User;
var point1 = { type: 'Point', coordinates: [39.807222,-76.984722] }
, point2 = { type: 'Point', coordinates: [39.828333,-77.232222] };
return User.create({ username: 'user', email: ['foo@bar.com'], location: point1}).then(function(oldUser) {
return User.update({ location: point2 }, { where: { username: oldUser.username }, returning: true }).spread(function(count, updatedUsers) {
expect(updatedUsers[0].location).to.deep.eql(point2);
});
});
});
it('should read geometry correctly', function() {
var User = this.User;
var point = { type: 'Point', coordinates: [39.807222,-76.984722] };
return User.create({ username: 'user', email: ['foo@bar.com'], location: point}).then(function(user) {
return User.find({ where: { username: user.username }});
}).then(function(user) {
expect(user.location).to.deep.eql(point);
});
});
describe('[POSTGRES] Unquoted identifiers', function() { describe('[POSTGRES] Unquoted identifiers', function() {
it('can insert and select', function() { it('can insert and select', function() {
var self = this; var self = this;
......
...@@ -666,5 +666,29 @@ suite(Support.getTestDialectTeaser('SQL'), function() { ...@@ -666,5 +666,29 @@ suite(Support.getTestDialectTeaser('SQL'), function() {
} }
}); });
} }
if (current.dialect.supports.ARRAY) {
suite('GEOMETRY', function () {
testsql('GEOMETRY', DataTypes.GEOMETRY, {
postgres: 'GEOMETRY'
});
testsql('GEOMETRY(\'POINT\')', DataTypes.GEOMETRY('POINT'), {
postgres: 'GEOMETRY(POINT)'
});
testsql('GEOMETRY(\'LINESTRING\')', DataTypes.GEOMETRY('LINESTRING'), {
postgres: 'GEOMETRY(LINESTRING)',
});
testsql('GEOMETRY(\'POLYGON\')', DataTypes.GEOMETRY('POLYGON'), {
postgres: 'GEOMETRY(POLYGON)'
});
testsql('GEOMETRY(\'POINT\',4326)', DataTypes.GEOMETRY('POINT', 4326), {
postgres: 'GEOMETRY(POINT,4326)'
});
});
}
}); });
}); });
\ No newline at end of file
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!