Skip to content

Commit

Permalink
feat(postgres): add function variables for postgres (#11277)
Browse files Browse the repository at this point in the history
  • Loading branch information
GregDevProjects authored and sushantdhiman committed Aug 9, 2019
1 parent b445ec0 commit ff97d93
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 3 deletions.
23 changes: 21 additions & 2 deletions lib/dialects/postgres/query-generator.js
Expand Up @@ -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?');

const paramList = this.expandFunctionParamList(params);
const variableList = options && options.variables ? this.expandFunctionVariableList(options.variables) : '';
const expandedOptionsArray = this.expandOptions(optionsArray);

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) {
Expand Down Expand Up @@ -667,6 +667,25 @@ class PostgresQueryGenerator extends AbstractQueryGenerator {
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) {
return options === undefined || _.isEmpty(options) ?
'' : options.join(' ');
Expand Down
10 changes: 9 additions & 1 deletion lib/query-interface.js
Expand Up @@ -1231,7 +1231,14 @@ class QueryInterface {
* [
* 'IMMUTABLE',
* 'LEAKPROOF'
* ]
* ],
* {
* variables:
* [
* {type: 'integer', name: 'myVar', default: 100}
* ],
* force: true
* };
* );
*
* @param {string} functionName Name of SQL function to create
Expand All @@ -1242,6 +1249,7 @@ class QueryInterface {
* @param {Array} optionsArray Extra-options for creation
* @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 {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}
*/
Expand Down
28 changes: 28 additions & 0 deletions test/integration/dialects/postgres/query-interface.test.js
Expand Up @@ -173,6 +173,34 @@ if (dialect.match(/^postgres/)) {
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', () => {
Expand Down

0 comments on commit ff97d93

Please sign in to comment.