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

Commit 5357f6d2 by Jan Aagaard Meier

Merge pull request #4192 from sequelize/databaseVersion

feat(version) Automatically determine the database version when creat…
2 parents fe301789 e249d5e4
...@@ -184,11 +184,36 @@ ConnectionManager.prototype.getConnection = function(options) { ...@@ -184,11 +184,36 @@ ConnectionManager.prototype.getConnection = function(options) {
var self = this; var self = this;
options = options || {}; options = options || {};
return new Promise(function (resolve, reject) { var promise;
self.pool.acquire(function(err, connection) { if (this.sequelize.options.databaseVersion === 0) {
if (err) return reject(err); if (this.versionPromise) {
resolve(connection); promise = this.versionPromise;
}, options.priority, options.type, options.useMaster); } else {
promise = this.versionPromise = self.$connect(self.config).then(function (connection) {
var _options = {};
_options.transaction = { connection: connection }; // Cheat .query to use our private connection
_options.logging = function () {};
_options.logging.__testLoggingFn = true;
return self.sequelize.databaseVersion(_options).then(function (version) {
self.sequelize.options.databaseVersion = version;
self.versionPromise = undefined;
self.$disconnect(connection);
});
});
}
} else {
promise = Promise.resolve();
}
return promise.then(function () {
return new Promise(function (resolve, reject) {
self.pool.acquire(function(err, connection) {
if (err) return reject(err);
resolve(connection);
}, options.priority, options.type, options.useMaster);
});
}); });
}; };
ConnectionManager.prototype.releaseConnection = function(connection) { ConnectionManager.prototype.releaseConnection = function(connection) {
......
...@@ -8,7 +8,8 @@ var Utils = require('../../utils') ...@@ -8,7 +8,8 @@ var Utils = require('../../utils')
, util = require('util') , util = require('util')
, Dottie = require('dottie') , Dottie = require('dottie')
, BelongsTo = require('../../associations/belongs-to') , BelongsTo = require('../../associations/belongs-to')
, uuid = require('node-uuid'); , uuid = require('node-uuid')
, semver = require('semver');
/* istanbul ignore next */ /* istanbul ignore next */
var throwMethodUndefined = function(methodName) { var throwMethodUndefined = function(methodName) {
...@@ -249,18 +250,18 @@ var QueryGenerator = { ...@@ -249,18 +250,18 @@ var QueryGenerator = {
if (this._dialect.supports.EXCEPTION && options.exception) { if (this._dialect.supports.EXCEPTION && options.exception) {
// Mostly for internal use, so we expect the user to know what he's doing! // Mostly for internal use, so we expect the user to know what he's doing!
// pg_temp functions are private per connection, so we never risk this function interfering with another one. // pg_temp functions are private per connection, so we never risk this function interfering with another one.
if (semver.gte(this.sequelize.options.databaseVersion, '9.2.0')) {
// <= 9.1 // >= 9.2 - Use a UUID but prefix with 'func_' (numbers first not allowed)
//options.exception = 'WHEN unique_violation THEN NULL;'; var delimiter = '$func_' + uuid.v4().replace(/-/g, '') + '$';
//valueQuery = 'CREATE OR REPLACE FUNCTION pg_temp.testfunc() RETURNS SETOF <%= table %> AS $body$ BEGIN RETURN QUERY ' + valueQuery + '; EXCEPTION ' + options.exception + ' END; $body$ LANGUAGE plpgsql; SELECT * FROM pg_temp.testfunc(); DROP FUNCTION IF EXISTS pg_temp.testfunc();';
options.exception = 'WHEN unique_violation THEN GET STACKED DIAGNOSTICS sequelize_caught_exception = PG_EXCEPTION_DETAIL;';
// >= 9.2 - Use a UUID but prefix with 'func_' (numbers first not allowed) valueQuery = 'CREATE OR REPLACE FUNCTION pg_temp.testfunc(OUT response <%= table %>, OUT sequelize_caught_exception text) RETURNS RECORD AS ' + delimiter +
var delimiter = '$func_' + uuid.v4().replace(/-/g, '') + '$'; ' BEGIN ' + valueQuery + ' INTO response; EXCEPTION ' + options.exception + ' END ' + delimiter +
' LANGUAGE plpgsql; SELECT (testfunc.response).*, testfunc.sequelize_caught_exception FROM pg_temp.testfunc(); DROP FUNCTION IF EXISTS pg_temp.testfunc()';
options.exception = 'WHEN unique_violation THEN GET STACKED DIAGNOSTICS sequelize_caught_exception = PG_EXCEPTION_DETAIL;'; } else {
valueQuery = 'CREATE OR REPLACE FUNCTION pg_temp.testfunc(OUT response <%= table %>, OUT sequelize_caught_exception text) RETURNS RECORD AS ' + delimiter + options.exception = 'WHEN unique_violation THEN NULL;';
' BEGIN ' + valueQuery + ' INTO response; EXCEPTION ' + options.exception + ' END ' + delimiter + valueQuery = 'CREATE OR REPLACE FUNCTION pg_temp.testfunc() RETURNS SETOF <%= table %> AS $body$ BEGIN RETURN QUERY ' + valueQuery + '; EXCEPTION ' + options.exception + ' END; $body$ LANGUAGE plpgsql; SELECT * FROM pg_temp.testfunc(); DROP FUNCTION IF EXISTS pg_temp.testfunc();';
' LANGUAGE plpgsql; SELECT (testfunc.response).*, testfunc.sequelize_caught_exception FROM pg_temp.testfunc(); DROP FUNCTION IF EXISTS pg_temp.testfunc()'; }
} }
if (this._dialect.supports['ON DUPLICATE KEY'] && options.onDuplicate) { if (this._dialect.supports['ON DUPLICATE KEY'] && options.onDuplicate) {
......
...@@ -4,7 +4,8 @@ var AbstractConnectionManager = require('../abstract/connection-manager') ...@@ -4,7 +4,8 @@ var AbstractConnectionManager = require('../abstract/connection-manager')
, ConnectionManager , ConnectionManager
, Utils = require('../../utils') , Utils = require('../../utils')
, Promise = require('../../promise') , Promise = require('../../promise')
, sequelizeErrors = require('../../errors'); , sequelizeErrors = require('../../errors')
, semver = require('semver');
ConnectionManager = function(dialect, sequelize) { ConnectionManager = function(dialect, sequelize) {
AbstractConnectionManager.call(this, dialect, sequelize); AbstractConnectionManager.call(this, dialect, sequelize);
...@@ -95,7 +96,11 @@ ConnectionManager.prototype.connect = function(config) { ...@@ -95,7 +96,11 @@ ConnectionManager.prototype.connect = function(config) {
}); });
}).tap(function (connection) { }).tap(function (connection) {
// Disable escape characters in strings, see https://github.com/sequelize/sequelize/issues/3545 // Disable escape characters in strings, see https://github.com/sequelize/sequelize/issues/3545
var query = 'SET standard_conforming_strings=on;'; var query = '';
if (self.sequelize.options.databaseVersion !== 0 && semver.gte(self.sequelize.options.databaseVersion, '8.2.0')) {
query += 'SET standard_conforming_strings=on;';
}
if (!self.sequelize.config.keepDefaultTimezone) { if (!self.sequelize.config.keepDefaultTimezone) {
query += 'SET client_min_messages TO warning; SET TIME ZONE INTERVAL \'' + self.sequelize.options.timezone + '\' HOUR TO MINUTE'; query += 'SET client_min_messages TO warning; SET TIME ZONE INTERVAL \'' + self.sequelize.options.timezone + '\' HOUR TO MINUTE';
......
...@@ -29,7 +29,7 @@ var QueryGenerator = { ...@@ -29,7 +29,7 @@ var QueryGenerator = {
}, },
versionQuery: function() { versionQuery: function() {
return 'SELECT VERSION() as "version"'; return 'SHOW SERVER_VERSION';
}, },
createTableQuery: function(tableName, attributes, options) { createTableQuery: function(tableName, attributes, options) {
......
...@@ -269,6 +269,8 @@ Query.prototype.run = function(sql) { ...@@ -269,6 +269,8 @@ Query.prototype.run = function(sql) {
}); });
return result; return result;
} else if (self.isVersionQuery()) {
return results[0].server_version;
} else if (self.isShowOrDescribeQuery()) { } else if (self.isShowOrDescribeQuery()) {
return results; return results;
} else if (QueryTypes.BULKUPDATE === self.options.type) { } else if (QueryTypes.BULKUPDATE === self.options.type) {
...@@ -331,8 +333,6 @@ Query.prototype.run = function(sql) { ...@@ -331,8 +333,6 @@ Query.prototype.run = function(sql) {
} }
return self.instance || (rows && ((self.options.plain && rows[0]) || rows)) || undefined; return self.instance || (rows && ((self.options.plain && rows[0]) || rows)) || undefined;
} else if (self.isVersionQuery()) {
return results[0].version;
} else if (self.isRawQuery()) { } else if (self.isRawQuery()) {
return [rows, result]; return [rows, result];
} else { } else {
......
...@@ -63,12 +63,9 @@ QueryInterface.prototype.showAllSchemas = function(options) { ...@@ -63,12 +63,9 @@ QueryInterface.prototype.showAllSchemas = function(options) {
}; };
QueryInterface.prototype.databaseVersion = function(options) { QueryInterface.prototype.databaseVersion = function(options) {
options = options || {}; return this.sequelize.query(this.QueryGenerator.versionQuery(), _.assign({
return this.sequelize.query(this.QueryGenerator.versionQuery(), { type: QueryTypes.VERSION
raw: true, }, options));
type: QueryTypes.VERSION,
logging: options.logging
});
}; };
QueryInterface.prototype.createTable = function(tableName, attributes, options) { QueryInterface.prototype.createTable = function(tableName, attributes, options) {
......
...@@ -141,7 +141,8 @@ var Sequelize = function(database, username, password, options) { ...@@ -141,7 +141,8 @@ var Sequelize = function(database, username, password, options) {
pool: {}, pool: {},
quoteIdentifiers: true, quoteIdentifiers: true,
hooks: {}, hooks: {},
isolationLevel: Transaction.ISOLATION_LEVELS.REPEATABLE_READ isolationLevel: Transaction.ISOLATION_LEVELS.REPEATABLE_READ,
databaseVersion: 0
}, options || {}); }, options || {});
if (this.options.dialect === 'postgresql') { if (this.options.dialect === 'postgresql') {
...@@ -936,7 +937,7 @@ Sequelize.prototype.authenticate = function(options) { ...@@ -936,7 +937,7 @@ Sequelize.prototype.authenticate = function(options) {
}; };
Sequelize.prototype.databaseVersion = function(options) { Sequelize.prototype.databaseVersion = function(options) {
return this.queryInterface.databaseVersion(options); return this.getQueryInterface().databaseVersion(options);
}; };
Sequelize.prototype.validate = Sequelize.prototype.authenticate; Sequelize.prototype.validate = Sequelize.prototype.authenticate;
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
"moment": "^2.9.0", "moment": "^2.9.0",
"moment-timezone": "^0.4.0", "moment-timezone": "^0.4.0",
"node-uuid": "~1.4.1", "node-uuid": "~1.4.1",
"semver": "^5.0.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",
......
...@@ -83,6 +83,8 @@ describe('Connction Manager', function() { ...@@ -83,6 +83,8 @@ describe('Connction Manager', function() {
}); });
var connectStub = sandbox.stub(connectionManager, '$connect').returns(resolvedPromise); var connectStub = sandbox.stub(connectionManager, '$connect').returns(resolvedPromise);
sandbox.stub(connectionManager, '$disconnect').returns(resolvedPromise);
sandbox.stub(sequelize, 'databaseVersion').returns(resolvedPromise);
connectionManager.initPools(); connectionManager.initPools();
var queryOptions = { var queryOptions = {
...@@ -97,11 +99,13 @@ describe('Connction Manager', function() { ...@@ -97,11 +99,13 @@ describe('Connction Manager', function() {
.then(_getConnection) .then(_getConnection)
.then(_getConnection) .then(_getConnection)
.then(function checkPoolHosts() { .then(function checkPoolHosts() {
chai.expect(connectStub.calledThrice).to.be.true; chai.expect(connectStub.callCount).to.equal(4);
// First call is the get connection for DB versions - ignore
var calls = connectStub.getCalls(); var calls = connectStub.getCalls();
chai.expect(calls[0].args[0].host).to.eql('slave1'); chai.expect(calls[1].args[0].host).to.eql('slave1');
chai.expect(calls[1].args[0].host).to.eql('slave2'); chai.expect(calls[2].args[0].host).to.eql('slave2');
chai.expect(calls[2].args[0].host).to.eql('slave1'); chai.expect(calls[3].args[0].host).to.eql('slave1');
}); });
}); });
...@@ -125,6 +129,8 @@ describe('Connction Manager', function() { ...@@ -125,6 +129,8 @@ describe('Connction Manager', function() {
}); });
var connectStub = sandbox.stub(connectionManager, '$connect').returns(resolvedPromise); var connectStub = sandbox.stub(connectionManager, '$connect').returns(resolvedPromise);
sandbox.stub(connectionManager, '$disconnect').returns(resolvedPromise);
sandbox.stub(sequelize, 'databaseVersion').returns(resolvedPromise);
connectionManager.initPools(); connectionManager.initPools();
var queryOptions = { var queryOptions = {
...@@ -135,9 +141,9 @@ describe('Connction Manager', function() { ...@@ -135,9 +141,9 @@ describe('Connction Manager', function() {
return connectionManager.getConnection(queryOptions) return connectionManager.getConnection(queryOptions)
.then(function checkPoolHosts() { .then(function checkPoolHosts() {
chai.expect(connectStub.calledOnce).to.be.true; chai.expect(connectStub).to.have.been.calledTwice; // Once to get DB version, and once to actually get the connection.
var calls = connectStub.getCalls(); var calls = connectStub.getCalls();
chai.expect(calls[0].args[0].host).to.eql('the-boss'); chai.expect(calls[1].args[0].host).to.eql('the-boss');
}); });
}); });
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!