UNPKG

@secam/pgsql-ast-parser

Version:

Fork of pgsql-ast-parser Simple Postgres SQL parser/modifier for pg-mem

236 lines (202 loc) 6.7 kB
import 'mocha'; import 'chai'; import { checkInvalid, checkStatement } from './spec-utils'; import { CreateFunctionStatement } from './ast'; import { parse } from '../parser'; import { expect } from 'chai'; describe('Create function', () => { const integer = { name: 'integer' }; const int = { name: 'int' }; const text = { name: 'text' }; checkStatement(`CREATE FUNCTION add(integer, integer) RETURNS integer AS 'select $1 + $2;' LANGUAGE SQL IMMUTABLE RETURNS NULL ON NULL INPUT`, { type: 'create function', name: { name: 'add' }, arguments: [{ type: integer }, { type: integer }], code: 'select $1 + $2;', language: { name: 'sql' }, purity: 'immutable', onNullInput: 'null', returns: integer, }); // modifiers BEFORE code block checkStatement(`CREATE FUNCTION add(integer, integer) RETURNS integer stable LANGUAGE SQL RETURNS NULL ON NULL INPUT AS 'select $1 + $2;' `, { type: 'create function', name: { name: 'add' }, arguments: [{ type: integer }, { type: integer }], code: 'select $1 + $2;', language: { name: 'sql' }, purity: 'stable', onNullInput: 'null', returns: integer, }); // duplicate modifiers checkInvalid(`CREATE FUNCTION add(integer, integer) RETURNS integer stable LANGUAGE SQL STABLE AS 'select $1 + $2;' `); checkStatement(`CREATE FUNCTION fn(in integer) RETURNS INTeger AS 'code' LANGUAGE SQL`, { type: 'create function', name: { name: 'fn' }, arguments: [{ type: integer, mode: 'in' }], code: 'code', returns: integer, language: { name: 'sql' }, }); checkStatement(`CREATE FUNCTION fn(i integer = 2) AS 'code' LANGUAGE SQL returns integer`, { type: 'create function', name: { name: 'fn' }, arguments: [{ type: integer, name: { name: 'i' }, default: { type: 'integer', value: 2 } }], code: 'code', returns: integer, language: { name: 'sql' }, }); checkStatement(`CREATE FUNCTION fn(in out integer) returns integer AS 'code' LANGUAGE SQL`, { type: 'create function', name: { name: 'fn' }, arguments: [{ type: integer, mode: 'in', name: { name: 'out' } }], code: 'code', returns: integer, language: { name: 'sql' }, }); checkStatement(`CREATE OR REPLACE FUNCTION increment(i integer) RETURNS integer AS $$BEGIN RETURN i + 1; END;$$ VOLATILE LANGUAGE plpgsql`, { type: 'create function', name: { name: 'increment' }, orReplace: true, arguments: [{ type: integer, name: { name: 'i' } }], returns: integer, language: { name: 'plpgsql' }, purity: 'volatile', code: `BEGIN RETURN i + 1; END;`, }); checkStatement(`CREATE FUNCTION dup(in int, out f1 int, out f2 text) returns integer AS $$SELECT $1, CAST($1 AS text) || ' is text'$$ LANGUAGE SQL`, { type: 'create function', name: { name: 'dup' }, language: { name: 'sql' }, returns: integer, arguments: [{ type: int, mode: 'in', }, { type: int, name: { name: 'f1' }, mode: 'out', }, { type: text, name: { name: 'f2' }, mode: 'out', }], code: `SELECT $1, CAST($1 AS text) || ' is text'`, }); checkStatement(`CREATE FUNCTION public.dup(int) RETURNS TABLE(f1 int, f2 text) AS $$SELECT $1, CAST($1 AS text) || ' is text'$$ LANGUAGE SQL`, { type: 'create function', name: { name: 'dup', schema: 'public', }, language: { name: 'sql' }, arguments: [{ type: int }], code: `SELECT $1, CAST($1 AS text) || ' is text'`, returns: { kind: 'table', columns: [{ name: { name: 'f1' }, type: int }, { name: { name: 'f2' }, type: text, }], } }); const simple: CreateFunctionStatement = { type: 'create function', name: { name: 'fn' }, language: { name: 'sql' }, returns: integer, arguments: [], code: `body`, }; const simpleSt = (x: string) => [ `CREATE FUNCTION fn() AS 'body' returns integer LANGUAGE SQL ${x}`, ]; checkStatement(`CREATE FUNCTION fn() AS 'body' returns integer LEAKPROOF LANGUAGE SQL `, { ...simple, leakproof: true, }); checkStatement(simpleSt(`NOT LEAKPROOF`), { ...simple, leakproof: false, }); checkStatement(simpleSt(`CALLED ON NULL INPUT`), { ...simple, onNullInput: 'call', }); checkStatement(simpleSt(`RETURNS NULL ON NULL INPUT`), { ...simple, onNullInput: 'null', }); checkStatement(simpleSt(`STRICT`), { ...simple, onNullInput: 'strict', }); checkStatement(`do $$ something $$`, { type: 'do', code: ' something ', }); checkStatement(`do language js $$ something $$`, { type: 'do', language: { name: 'js' }, code: ' something ', }); it('is not greedy', () => { const parsed = parse(`create function foo() returns text as $$ select 'hi'; $$ language sql; create function bar() returns text as $$ select 'there'; $$ language sql;`); expect(parsed.length).to.equal(2); }); checkStatement('drop function my_function', { type: 'drop function', name: { name: 'my_function' }, }); checkStatement('drop function if exists my_function', { type: 'drop function', ifExists: true, name: { name: 'my_function' }, }); checkStatement('drop function my_function(text)', { type: 'drop function', name: { name: 'my_function' }, arguments: [{ type: { name: 'text' } }] }); checkStatement('drop function my_function(text, float)', { type: 'drop function', name: { name: 'my_function' }, arguments: [{ type: { name: 'text' }, }, { type: { name: 'float' }, }] }); checkStatement('drop function my_function(txt text, fl float)', { type: 'drop function', name: { name: 'my_function' }, arguments: [{ type: { name: 'text' }, name: { name: 'txt' }, }, { type: { name: 'float' }, name: { name: 'fl' }, }] }); });