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

Commit ff97d93c by Gregory McLean Committed by Sushant

feat(postgres): add function variables for postgres (#11277)

1 parent b445ec03
...@@ -608,11 +608,11 @@ class PostgresQueryGenerator extends AbstractQueryGenerator { ...@@ -608,11 +608,11 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
if (!functionName || !returnType || !language || !body) throw new Error('createFunction missing some parameters. Did you pass functionName, returnType, language and body?'); if (!functionName || !returnType || !language || !body) throw new Error('createFunction missing some parameters. Did you pass functionName, returnType, language and body?');
const paramList = this.expandFunctionParamList(params); const paramList = this.expandFunctionParamList(params);
const variableList = options && options.variables ? this.expandFunctionVariableList(options.variables) : '';
const expandedOptionsArray = this.expandOptions(optionsArray); const expandedOptionsArray = this.expandOptions(optionsArray);
const statement = options && options.force ? 'CREATE OR REPLACE FUNCTION' : 'CREATE FUNCTION'; const statement = options && options.force ? 'CREATE OR REPLACE FUNCTION' : 'CREATE FUNCTION';
return `${statement} ${functionName}(${paramList}) RETURNS ${returnType} AS $func$ BEGIN ${body} END; $func$ language '${language}'${expandedOptionsArray};`; return `${statement} ${functionName}(${paramList}) RETURNS ${returnType} AS $func$ ${variableList} BEGIN ${body} END; $func$ language '${language}'${expandedOptionsArray};`;
} }
dropFunction(functionName, params) { dropFunction(functionName, params) {
...@@ -667,6 +667,25 @@ class PostgresQueryGenerator extends AbstractQueryGenerator { ...@@ -667,6 +667,25 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
return paramList.join(', '); return paramList.join(', ');
} }
expandFunctionVariableList(variables) {
if (!Array.isArray(variables)) {
throw new Error('expandFunctionVariableList: function variables must be an array');
}
const variableDefinitions = [];
variables.forEach(variable => {
if (!variable.name || !variable.type) {
throw new Error('function variable must have a name and type');
}
let variableDefinition = `DECLARE ${variable.name} ${variable.type}`;
if (variable.default) {
variableDefinition += ` := ${variable.default}`;
}
variableDefinition += ';';
variableDefinitions.push(variableDefinition);
});
return variableDefinitions.join(' ');
}
expandOptions(options) { expandOptions(options) {
return options === undefined || _.isEmpty(options) ? return options === undefined || _.isEmpty(options) ?
'' : options.join(' '); '' : options.join(' ');
......
...@@ -1231,7 +1231,14 @@ class QueryInterface { ...@@ -1231,7 +1231,14 @@ class QueryInterface {
* [ * [
* 'IMMUTABLE', * 'IMMUTABLE',
* 'LEAKPROOF' * 'LEAKPROOF'
* ] * ],
* {
* variables:
* [
* {type: 'integer', name: 'myVar', default: 100}
* ],
* force: true
* };
* ); * );
* *
* @param {string} functionName Name of SQL function to create * @param {string} functionName Name of SQL function to create
...@@ -1242,6 +1249,7 @@ class QueryInterface { ...@@ -1242,6 +1249,7 @@ class QueryInterface {
* @param {Array} optionsArray Extra-options for creation * @param {Array} optionsArray Extra-options for creation
* @param {Object} [options] query options * @param {Object} [options] query options
* @param {boolean} options.force If force is true, any existing functions with the same parameters will be replaced. For postgres, this means using `CREATE OR REPLACE FUNCTION` instead of `CREATE FUNCTION`. Default is false * @param {boolean} options.force If force is true, any existing functions with the same parameters will be replaced. For postgres, this means using `CREATE OR REPLACE FUNCTION` instead of `CREATE FUNCTION`. Default is false
* @param {Array<Object>} options.variables List of declared variables. Each variable should be an object with string fields `type` and `name`, and optionally having a `default` field as well.
* *
* @returns {Promise} * @returns {Promise}
*/ */
......
...@@ -173,6 +173,34 @@ if (dialect.match(/^postgres/)) { ...@@ -173,6 +173,34 @@ if (dialect.match(/^postgres/)) {
expect(res[0].my_func).to.be.eql('second'); expect(res[0].my_func).to.be.eql('second');
}); });
}); });
it('produces an error when options.variables is missing expected parameters', function() {
const body = 'return 1;';
expect(() => {
const options = { variables: 100 };
return this.queryInterface.createFunction('test_func', [], 'integer', 'plpgsql', body, [], options);
}).to.throw(/expandFunctionVariableList: function variables must be an array/);
expect(() => {
const options = { variables: [{ name: 'myVar' }] };
return this.queryInterface.createFunction('test_func', [], 'integer', 'plpgsql', body, [], options);
}).to.throw(/function variable must have a name and type/);
expect(() => {
const options = { variables: [{ type: 'integer' }] };
return this.queryInterface.createFunction('test_func', [], 'integer', 'plpgsql', body, [], options);
}).to.throw(/function variable must have a name and type/);
});
it('uses declared variables', function() {
const body = 'RETURN myVar + 1;';
const options = { variables: [{ type: 'integer', name: 'myVar', default: 100 }] };
return this.queryInterface.createFunction('add_one', [], 'integer', 'plpgsql', body, [], options)
.then(() => this.sequelize.query('select add_one();', { type: this.sequelize.QueryTypes.SELECT }))
.then(res => {
expect(res[0].add_one).to.be.eql(101);
});
});
}); });
describe('dropFunction', () => { describe('dropFunction', () => {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!