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

Commit 091e99dd by Matt Liszewski

add option for prepending postgres queries with search_path

1 parent bc9213f5
...@@ -46,7 +46,8 @@ PostgresDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.supp ...@@ -46,7 +46,8 @@ PostgresDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.supp
GEOMETRY: true, GEOMETRY: true,
JSON: true, JSON: true,
JSONB: true, JSONB: true,
deferrableConstraints: true deferrableConstraints: true,
searchPath : true
}); });
ConnectionManager.prototype.defaultVersion = '9.4.0'; ConnectionManager.prototype.defaultVersion = '9.4.0';
......
...@@ -15,6 +15,11 @@ var QueryGenerator = { ...@@ -15,6 +15,11 @@ var QueryGenerator = {
options: {}, options: {},
dialect: 'postgres', dialect: 'postgres',
setSearchPath: function(searchPath) {
var query = 'SET search_path to <%= searchPath%>;';
return Utils._.template(query)({searchPath: searchPath});
},
createSchema: function(schema) { createSchema: function(schema) {
var query = 'CREATE SCHEMA <%= schema%>;'; var query = 'CREATE SCHEMA <%= schema%>;';
return Utils._.template(query)({schema: schema}); return Utils._.template(query)({schema: schema});
......
...@@ -90,9 +90,13 @@ Query.prototype.parseDialectSpecificFields = parseDialectSpecificFields; ...@@ -90,9 +90,13 @@ Query.prototype.parseDialectSpecificFields = parseDialectSpecificFields;
Query.prototype.run = function(sql) { Query.prototype.run = function(sql) {
this.sql = sql; this.sql = sql;
if(!Utils._.isEmpty(this.options.searchPath)){
this.sql = this.sequelize.queryInterface.QueryGenerator.setSearchPath(this.options.searchPath) + sql;
}
var self = this var self = this
, receivedError = false , receivedError = false
, query = this.client.query(sql) , query = this.client.query(this.sql)
, rows = []; , rows = [];
this.sequelize.log('Executing (' + (this.client.uuid || 'default') + '): ' + this.sql, this.options); this.sequelize.log('Executing (' + (this.client.uuid || 'default') + '): ' + this.sql, this.options);
......
...@@ -478,6 +478,7 @@ Instance.prototype._setInclude = function(key, value, options) { ...@@ -478,6 +478,7 @@ Instance.prototype._setInclude = function(key, value, options) {
* @param {Boolean} [options.validate=true] If false, validations won't be run. * @param {Boolean} [options.validate=true] If false, validations won't be run.
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql. * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Transaction} [options.transaction] * @param {Transaction} [options.transaction]
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* *
* @return {Promise<this|Errors.ValidationError>} * @return {Promise<this|Errors.ValidationError>}
*/ */
...@@ -804,6 +805,7 @@ Instance.prototype.updateAttributes = Instance.prototype.update; ...@@ -804,6 +805,7 @@ Instance.prototype.updateAttributes = Instance.prototype.update;
* @param {Boolean} [options.force=false] If set to true, paranoid models will actually be deleted * @param {Boolean} [options.force=false] If set to true, paranoid models will actually be deleted
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql. * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Transaction} [options.transaction] * @param {Transaction} [options.transaction]
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* *
* @return {Promise<undefined>} * @return {Promise<undefined>}
*/ */
...@@ -900,6 +902,7 @@ Instance.prototype.restore = function(options) { ...@@ -900,6 +902,7 @@ Instance.prototype.restore = function(options) {
* @param {Integer} [options.by=1] The number to increment by * @param {Integer} [options.by=1] The number to increment by
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql. * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Transaction} [options.transaction] * @param {Transaction} [options.transaction]
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* *
* @return {Promise<this>} * @return {Promise<this>}
*/ */
...@@ -973,6 +976,7 @@ Instance.prototype.increment = function(fields, options) { ...@@ -973,6 +976,7 @@ Instance.prototype.increment = function(fields, options) {
* @param {Integer} [options.by=1] The number to decrement by * @param {Integer} [options.by=1] The number to decrement by
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql. * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Transaction} [options.transaction] * @param {Transaction} [options.transaction]
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* *
* @return {Promise} * @return {Promise}
*/ */
...@@ -1046,4 +1050,4 @@ Instance.prototype.toJSON = function() { ...@@ -1046,4 +1050,4 @@ Instance.prototype.toJSON = function() {
}); });
}; };
module.exports = Instance; module.exports = Instance;
\ No newline at end of file
...@@ -1291,6 +1291,7 @@ Model.prototype.all = function(options) { ...@@ -1291,6 +1291,7 @@ Model.prototype.all = function(options) {
* @param {Boolean} [options.raw] Return raw result. See sequelize.query for more information. * @param {Boolean} [options.raw] Return raw result. See sequelize.query for more information.
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql. * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Object} [options.having] * @param {Object} [options.having]
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* *
* @see {Sequelize#query} * @see {Sequelize#query}
* @return {Promise<Array<Instance>>} * @return {Promise<Array<Instance>>}
...@@ -1422,6 +1423,7 @@ Model.$findSeperate = function(results, options) { ...@@ -1422,6 +1423,7 @@ Model.$findSeperate = function(results, options) {
* @param {Number|String|Buffer} [options] A hash of options to describe the scope of the search, or a number to search by id. * @param {Number|String|Buffer} [options] A hash of options to describe the scope of the search, or a number to search by id.
* @param {Object} ' [options] * @param {Object} ' [options]
* @param {Transaction} [options.transaction] Transaction to run query under * @param {Transaction} [options.transaction] Transaction to run query under
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* *
* @see {Model#findAll} for an explanation of options * @see {Model#findAll} for an explanation of options
* @return {Promise<Instance>} * @return {Promise<Instance>}
...@@ -1454,6 +1456,7 @@ Model.prototype.findByPrimary = Model.prototype.findById; ...@@ -1454,6 +1456,7 @@ Model.prototype.findByPrimary = Model.prototype.findById;
* *
* @param {Object} [options] A hash of options to describe the scope of the search * @param {Object} [options] A hash of options to describe the scope of the search
* @param {Transaction} [options.transaction] Transaction to run query under * @param {Transaction} [options.transaction] Transaction to run query under
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* *
* @see {Model#findAll} for an explanation of options * @see {Model#findAll} for an explanation of options
* @return {Promise<Instance>} * @return {Promise<Instance>}
...@@ -1529,6 +1532,7 @@ Model.prototype.aggregate = function(field, aggregateFunction, options) { ...@@ -1529,6 +1532,7 @@ Model.prototype.aggregate = function(field, aggregateFunction, options) {
* @param {Object} [options.group] For creating complex counts. Will return multiple rows as needed. * @param {Object} [options.group] For creating complex counts. Will return multiple rows as needed.
* @param {Transaction} [options.transaction] Transaction to run query under * @param {Transaction} [options.transaction] Transaction to run query under
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql. * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* *
* @return {Promise<Integer>} * @return {Promise<Integer>}
*/ */
...@@ -1760,6 +1764,7 @@ Model.prototype.bulkBuild = function(valueSets, options) { // testhint options:n ...@@ -1760,6 +1764,7 @@ Model.prototype.bulkBuild = function(valueSets, options) { // testhint options:n
* @param {String} [options.onDuplicate] * @param {String} [options.onDuplicate]
* @param {Transaction} [options.transaction] Transaction to run query under * @param {Transaction} [options.transaction] Transaction to run query under
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql. * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* *
* @return {Promise<Instance>} * @return {Promise<Instance>}
*/ */
...@@ -1959,6 +1964,7 @@ Model.prototype.findCreateFind = function(options) { ...@@ -1959,6 +1964,7 @@ Model.prototype.findCreateFind = function(options) {
* @param {Array} [options.fields=Object.keys(this.attributes)] The fields to insert / update. Defaults to all fields * @param {Array} [options.fields=Object.keys(this.attributes)] The fields to insert / update. Defaults to all fields
* @param {Transaction} [options.transaction] Transaction to run query under * @param {Transaction} [options.transaction] Transaction to run query under
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql. * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* *
* @alias insertOrUpdate * @alias insertOrUpdate
* @return {Promise<created>} Returns a boolean indicating whether the row was created or updated. * @return {Promise<created>} Returns a boolean indicating whether the row was created or updated.
...@@ -2020,6 +2026,7 @@ Model.prototype.insertOrUpdate = Model.prototype.upsert; ...@@ -2020,6 +2026,7 @@ Model.prototype.insertOrUpdate = Model.prototype.upsert;
* @param {Array} [options.updateOnDuplicate] Fields to update if row key already exists (on duplicate key update)? (only supported by mysql & mariadb). By default, all fields are updated. * @param {Array} [options.updateOnDuplicate] Fields to update if row key already exists (on duplicate key update)? (only supported by mysql & mariadb). By default, all fields are updated.
* @param {Transaction} [options.transaction] Transaction to run query under * @param {Transaction} [options.transaction] Transaction to run query under
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql. * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* *
* @return {Promise<Array<Instance>>} * @return {Promise<Array<Instance>>}
*/ */
...@@ -2169,6 +2176,7 @@ Model.prototype.bulkCreate = function(records, options) { ...@@ -2169,6 +2176,7 @@ Model.prototype.bulkCreate = function(records, options) {
* @param {Boolean|function} [options.cascade = false] Only used in conjuction with TRUNCATE. Truncates all tables that have foreign-key references to the named table, or to any tables added to the group due to CASCADE. * @param {Boolean|function} [options.cascade = false] Only used in conjuction with TRUNCATE. Truncates all tables that have foreign-key references to the named table, or to any tables added to the group due to CASCADE.
* @param {Transaction} [options.transaction] Transaction to run query under * @param {Transaction} [options.transaction] Transaction to run query under
* @param {Boolean|function} [options.logging] A function that logs sql queries, or false for no logging * @param {Boolean|function} [options.logging] A function that logs sql queries, or false for no logging
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* @return {Promise} * @return {Promise}
* *
* @see {Model#destroy} for more information * @see {Model#destroy} for more information
......
...@@ -147,6 +147,7 @@ QueryInterface.prototype.createTable = function(tableName, attributes, options, ...@@ -147,6 +147,7 @@ QueryInterface.prototype.createTable = function(tableName, attributes, options,
else if (!!vals[idx - 1]) { else if (!!vals[idx - 1]) {
options.after = vals[idx - 1]; options.after = vals[idx - 1];
} }
options.supportsSearchPath = false;
promises.push(self.sequelize.query(self.QueryGenerator.pgEnumAdd(tableName, keys[i], value, options), options)); promises.push(self.sequelize.query(self.QueryGenerator.pgEnumAdd(tableName, keys[i], value, options), options));
} }
}); });
...@@ -214,6 +215,7 @@ QueryInterface.prototype.dropTable = function(tableName, options) { ...@@ -214,6 +215,7 @@ QueryInterface.prototype.dropTable = function(tableName, options) {
for (i = 0; i < keyLen; i++) { for (i = 0; i < keyLen; i++) {
if (instanceTable.rawAttributes[keys[i]].type instanceof DataTypes.ENUM) { if (instanceTable.rawAttributes[keys[i]].type instanceof DataTypes.ENUM) {
sql = self.QueryGenerator.pgEnumDrop(getTableName, keys[i]); sql = self.QueryGenerator.pgEnumDrop(getTableName, keys[i]);
options.supportsSearchPath = false;
promises.push(self.sequelize.query(sql, { logging: options.logging, raw: true, transaction : options.transaction })); promises.push(self.sequelize.query(sql, { logging: options.logging, raw: true, transaction : options.transaction }));
} }
} }
...@@ -442,7 +444,7 @@ QueryInterface.prototype.addIndex = function(tableName, attributes, options, raw ...@@ -442,7 +444,7 @@ QueryInterface.prototype.addIndex = function(tableName, attributes, options, raw
options = options || {}; options = options || {};
options.fields = attributes; options.fields = attributes;
var sql = this.QueryGenerator.addIndexQuery(tableName, options, rawTablename); var sql = this.QueryGenerator.addIndexQuery(tableName, options, rawTablename);
return this.sequelize.query(sql, { logging: options.logging }); return this.sequelize.query(sql, { logging: options.logging, supportsSearchPath: false });
}; };
QueryInterface.prototype.showIndex = function(tableName, options) { QueryInterface.prototype.showIndex = function(tableName, options) {
...@@ -901,7 +903,8 @@ QueryInterface.prototype.commitTransaction = function(transaction, options) { ...@@ -901,7 +903,8 @@ QueryInterface.prototype.commitTransaction = function(transaction, options) {
options = Utils._.extend({ options = Utils._.extend({
transaction: transaction, transaction: transaction,
parent: options.transaction parent: options.transaction,
supportsSearchPath: false
}, options || {}); }, options || {});
var sql = this.QueryGenerator.commitTransactionQuery(options); var sql = this.QueryGenerator.commitTransactionQuery(options);
...@@ -920,7 +923,8 @@ QueryInterface.prototype.rollbackTransaction = function(transaction, options) { ...@@ -920,7 +923,8 @@ QueryInterface.prototype.rollbackTransaction = function(transaction, options) {
options = Utils._.extend({ options = Utils._.extend({
transaction: transaction, transaction: transaction,
parent: options.transaction parent: options.transaction,
supportsSearchPath: false
}, options || {}); }, options || {});
var sql = this.QueryGenerator.rollbackTransactionQuery(transaction, options); var sql = this.QueryGenerator.rollbackTransactionQuery(transaction, options);
......
...@@ -667,6 +667,8 @@ Sequelize.prototype.import = function(path) { ...@@ -667,6 +667,8 @@ Sequelize.prototype.import = function(path) {
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql. * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Instance} [options.instance] A sequelize instance used to build the return instance * @param {Instance} [options.instance] A sequelize instance used to build the return instance
* @param {Model} [options.model] A sequelize model used to build the returned model instances (used to be called callee) * @param {Model} [options.model] A sequelize model used to build the returned model instances (used to be called callee)
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* @param {Boolean} [options.supportsSearchPath] If false do not prepend the query with the search_path (Postgres only)
* @param {Object} [options.mapToModel=false] Map returned fields to model's fields if `options.model` or `options.instance` is present. Mapping will occur before building the model instance. * @param {Object} [options.mapToModel=false] Map returned fields to model's fields if `options.model` or `options.instance` is present. Mapping will occur before building the model instance.
* @param {Object} [options.fieldMap] Map returned fields to arbitrary names for `SELECT` query type. * @param {Object} [options.fieldMap] Map returned fields to arbitrary names for `SELECT` query type.
* *
...@@ -724,7 +726,8 @@ Sequelize.prototype.query = function(sql, options) { ...@@ -724,7 +726,8 @@ Sequelize.prototype.query = function(sql, options) {
options = Utils._.extend(Utils._.clone(this.options.query), options); options = Utils._.extend(Utils._.clone(this.options.query), options);
options = Utils._.defaults(options, { options = Utils._.defaults(options, {
logging: this.options.hasOwnProperty('logging') ? this.options.logging : console.log logging: this.options.hasOwnProperty('logging') ? this.options.logging : console.log,
searchPath: this.options.hasOwnProperty('searchPath') ? this.options.searchPath : 'DEFAULT',
}); });
if (options.transaction === undefined && Sequelize.cls) { if (options.transaction === undefined && Sequelize.cls) {
...@@ -747,6 +750,17 @@ Sequelize.prototype.query = function(sql, options) { ...@@ -747,6 +750,17 @@ Sequelize.prototype.query = function(sql, options) {
this.test.$runningQueries++; this.test.$runningQueries++;
} }
//if dialect doesn't support search_path or dialect option
//to prepend searchPath is not true delete the searchPath option
if (!self.dialect.supports.searchPath || !this.options.dialectOptions || !this.options.dialectOptions.prependSearchPath ||
options.supportsSearchPath === false) {
delete options.searchPath;
} else if (!options.searchPath) {
//if user wants to always prepend searchPath (dialectOptions.preprendSearchPath = true)
//then set to DEFAULT if none is provided
options.searchPath = 'DEFAULT';
}
return Promise.resolve( return Promise.resolve(
options.transaction ? options.transaction.connection : self.connectionManager.getConnection(options) options.transaction ? options.transaction.connection : self.connectionManager.getConnection(options)
).then(function (connection) { ).then(function (connection) {
...@@ -875,6 +889,7 @@ Sequelize.prototype.dropAllSchemas = function(options) { ...@@ -875,6 +889,7 @@ Sequelize.prototype.dropAllSchemas = function(options) {
* @param {RegEx} [options.match] Match a regex against the database name before syncing, a safety check for cases where force: true is used in tests but not live code * @param {RegEx} [options.match] Match a regex against the database name before syncing, a safety check for cases where force: true is used in tests but not live code
* @param {Boolean|function} [options.logging=console.log] A function that logs sql queries, or false for no logging * @param {Boolean|function} [options.logging=console.log] A function that logs sql queries, or false for no logging
* @param {String} [options.schema='public'] The schema that the tables should be created in. This can be overriden for each table in sequelize.define * @param {String} [options.schema='public'] The schema that the tables should be created in. This can be overriden for each table in sequelize.define
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* @param {Boolean} [options.hooks=true] If hooks is true then beforeSync, afterSync, beforBulkSync, afterBulkSync hooks will be called * @param {Boolean} [options.hooks=true] If hooks is true then beforeSync, afterSync, beforBulkSync, afterBulkSync hooks will be called
* @return {Promise} * @return {Promise}
*/ */
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!