1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-31 17:02:12 +03:00

SQL-standard function body

This adds support for writing CREATE FUNCTION and CREATE PROCEDURE
statements for language SQL with a function body that conforms to the
SQL standard and is portable to other implementations.

Instead of the PostgreSQL-specific AS $$ string literal $$ syntax,
this allows writing out the SQL statements making up the body
unquoted, either as a single statement:

    CREATE FUNCTION add(a integer, b integer) RETURNS integer
        LANGUAGE SQL
        RETURN a + b;

or as a block

    CREATE PROCEDURE insert_data(a integer, b integer)
    LANGUAGE SQL
    BEGIN ATOMIC
      INSERT INTO tbl VALUES (a);
      INSERT INTO tbl VALUES (b);
    END;

The function body is parsed at function definition time and stored as
expression nodes in a new pg_proc column prosqlbody.  So at run time,
no further parsing is required.

However, this form does not support polymorphic arguments, because
there is no more parse analysis done at call time.

Dependencies between the function and the objects it uses are fully
tracked.

A new RETURN statement is introduced.  This can only be used inside
function bodies.  Internally, it is treated much like a SELECT
statement.

psql needs some new intelligence to keep track of function body
boundaries so that it doesn't send off statements when it sees
semicolons that are inside a function body.

Tested-by: Jaime Casanova <jcasanov@systemguards.com.ec>
Reviewed-by: Julien Rouhaud <rjuju123@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/1c11f1eb-f00c-43b7-799d-2d44132c02d7@2ndquadrant.com
This commit is contained in:
Peter Eisentraut
2021-04-07 21:30:08 +02:00
parent 1e55e7d175
commit e717a9a18b
37 changed files with 1415 additions and 216 deletions

View File

@@ -71,6 +71,7 @@ static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
bool isTopLevel, List **targetlist);
static void determineRecursiveColTypes(ParseState *pstate,
Node *larg, List *nrtargetlist);
static Query *transformReturnStmt(ParseState *pstate, ReturnStmt *stmt);
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
static List *transformReturningList(ParseState *pstate, List *returningList);
static List *transformUpdateTargetList(ParseState *pstate,
@@ -323,6 +324,10 @@ transformStmt(ParseState *pstate, Node *parseTree)
}
break;
case T_ReturnStmt:
result = transformReturnStmt(pstate, (ReturnStmt *) parseTree);
break;
case T_PLAssignStmt:
result = transformPLAssignStmt(pstate,
(PLAssignStmt *) parseTree);
@@ -2244,6 +2249,36 @@ determineRecursiveColTypes(ParseState *pstate, Node *larg, List *nrtargetlist)
}
/*
* transformReturnStmt -
* transforms a return statement
*/
static Query *
transformReturnStmt(ParseState *pstate, ReturnStmt *stmt)
{
Query *qry = makeNode(Query);
qry->commandType = CMD_SELECT;
qry->isReturn = true;
qry->targetList = list_make1(makeTargetEntry((Expr *) transformExpr(pstate, stmt->returnval, EXPR_KIND_SELECT_TARGET),
1, NULL, false));
if (pstate->p_resolve_unknowns)
resolveTargetListUnknowns(pstate, qry->targetList);
qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
qry->hasAggs = pstate->p_hasAggs;
assign_query_collations(pstate, qry);
return qry;
}
/*
* transformUpdateStmt -
* transforms an update statement

View File

@@ -262,7 +262,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
struct GroupClause *groupclause;
}
%type <node> stmt schema_stmt
%type <node> stmt toplevel_stmt schema_stmt routine_body_stmt
AlterEventTrigStmt
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
@@ -289,9 +289,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt
ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
RemoveFuncStmt RemoveOperStmt RenameStmt ReturnStmt RevokeStmt RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
SecLabelStmt SelectStmt TransactionStmt TruncateStmt
SecLabelStmt SelectStmt TransactionStmt TransactionStmtLegacy TruncateStmt
UnlistenStmt UpdateStmt VacuumStmt
VariableResetStmt VariableSetStmt VariableShowStmt
ViewStmt CheckPointStmt CreateConversionStmt
@@ -395,14 +395,14 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> vacuum_relation
%type <selectlimit> opt_select_limit select_limit limit_clause
%type <list> parse_toplevel stmtmulti
%type <list> parse_toplevel stmtmulti routine_body_stmt_list
OptTableElementList TableElementList OptInherit definition
OptTypedTableElementList TypedTableElementList
reloptions opt_reloptions
OptWith opt_definition func_args func_args_list
func_args_with_defaults func_args_with_defaults_list
aggr_args aggr_args_list
func_as createfunc_opt_list alterfunc_opt_list
func_as createfunc_opt_list opt_createfunc_opt_list alterfunc_opt_list
old_aggr_definition old_aggr_list
oper_argtypes RuleActionList RuleActionMulti
opt_column_list columnList opt_name_list
@@ -428,6 +428,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
vacuum_relation_list opt_vacuum_relation_list
drop_option_list
%type <node> opt_routine_body
%type <groupclause> group_clause
%type <list> group_by_list
%type <node> group_by_item empty_grouping_set rollup_clause cube_clause
@@ -637,7 +638,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
/* ordinary key words in alphabetical order */
%token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
ASENSITIVE ASSERTION ASSIGNMENT ASYMMETRIC AT ATTACH ATTRIBUTE AUTHORIZATION
ASENSITIVE ASSERTION ASSIGNMENT ASYMMETRIC ATOMIC AT ATTACH ATTRIBUTE AUTHORIZATION
BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
BOOLEAN_P BOTH BREADTH BY
@@ -699,7 +700,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REFERENCING
REFRESH REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA
RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
RESET RESTART RESTRICT RETURN RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
ROUTINE ROUTINES ROW ROWS RULE
SAVEPOINT SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
@@ -869,7 +870,7 @@ parse_toplevel:
* we'd get -1 for the location in such cases.
* We also take care to discard empty statements entirely.
*/
stmtmulti: stmtmulti ';' stmt
stmtmulti: stmtmulti ';' toplevel_stmt
{
if ($1 != NIL)
{
@@ -881,7 +882,7 @@ stmtmulti: stmtmulti ';' stmt
else
$$ = $1;
}
| stmt
| toplevel_stmt
{
if ($1 != NULL)
$$ = list_make1(makeRawStmt($1, 0));
@@ -890,7 +891,16 @@ stmtmulti: stmtmulti ';' stmt
}
;
stmt :
/*
* toplevel_stmt includes BEGIN and END. stmt does not include them, because
* those words have different meanings in function bodys.
*/
toplevel_stmt:
stmt
| TransactionStmtLegacy
;
stmt:
AlterEventTrigStmt
| AlterDatabaseStmt
| AlterDatabaseSetStmt
@@ -7477,7 +7487,7 @@ opt_nulls_order: NULLS_LA FIRST_P { $$ = SORTBY_NULLS_FIRST; }
CreateFunctionStmt:
CREATE opt_or_replace FUNCTION func_name func_args_with_defaults
RETURNS func_return createfunc_opt_list
RETURNS func_return opt_createfunc_opt_list opt_routine_body
{
CreateFunctionStmt *n = makeNode(CreateFunctionStmt);
n->is_procedure = false;
@@ -7486,10 +7496,11 @@ CreateFunctionStmt:
n->parameters = $5;
n->returnType = $7;
n->options = $8;
n->sql_body = $9;
$$ = (Node *)n;
}
| CREATE opt_or_replace FUNCTION func_name func_args_with_defaults
RETURNS TABLE '(' table_func_column_list ')' createfunc_opt_list
RETURNS TABLE '(' table_func_column_list ')' opt_createfunc_opt_list opt_routine_body
{
CreateFunctionStmt *n = makeNode(CreateFunctionStmt);
n->is_procedure = false;
@@ -7499,10 +7510,11 @@ CreateFunctionStmt:
n->returnType = TableFuncTypeName($9);
n->returnType->location = @7;
n->options = $11;
n->sql_body = $12;
$$ = (Node *)n;
}
| CREATE opt_or_replace FUNCTION func_name func_args_with_defaults
createfunc_opt_list
opt_createfunc_opt_list opt_routine_body
{
CreateFunctionStmt *n = makeNode(CreateFunctionStmt);
n->is_procedure = false;
@@ -7511,10 +7523,11 @@ CreateFunctionStmt:
n->parameters = $5;
n->returnType = NULL;
n->options = $6;
n->sql_body = $7;
$$ = (Node *)n;
}
| CREATE opt_or_replace PROCEDURE func_name func_args_with_defaults
createfunc_opt_list
opt_createfunc_opt_list opt_routine_body
{
CreateFunctionStmt *n = makeNode(CreateFunctionStmt);
n->is_procedure = true;
@@ -7523,6 +7536,7 @@ CreateFunctionStmt:
n->parameters = $5;
n->returnType = NULL;
n->options = $6;
n->sql_body = $7;
$$ = (Node *)n;
}
;
@@ -7833,6 +7847,11 @@ aggregate_with_argtypes_list:
{ $$ = lappend($1, $3); }
;
opt_createfunc_opt_list:
createfunc_opt_list
| /*EMPTY*/ { $$ = NIL; }
;
createfunc_opt_list:
/* Must be at least one to prevent conflict */
createfunc_opt_item { $$ = list_make1($1); }
@@ -7944,6 +7963,51 @@ func_as: Sconst { $$ = list_make1(makeString($1)); }
}
;
ReturnStmt: RETURN a_expr
{
ReturnStmt *r = makeNode(ReturnStmt);
r->returnval = (Node *) $2;
$$ = (Node *) r;
}
;
opt_routine_body:
ReturnStmt
{
$$ = $1;
}
| BEGIN_P ATOMIC routine_body_stmt_list END_P
{
/*
* A compound statement is stored as a single-item list
* containing the list of statements as its member. That
* way, the parse analysis code can tell apart an empty
* body from no body at all.
*/
$$ = (Node *) list_make1($3);
}
| /*EMPTY*/
{
$$ = NULL;
}
;
routine_body_stmt_list:
routine_body_stmt_list routine_body_stmt ';'
{
$$ = lappend($1, $2);
}
| /*EMPTY*/
{
$$ = NIL;
}
;
routine_body_stmt:
stmt
| ReturnStmt
;
transform_type_list:
FOR TYPE_P Typename { $$ = list_make1($3); }
| transform_type_list ',' FOR TYPE_P Typename { $$ = lappend($1, $5); }
@@ -9897,13 +9961,6 @@ TransactionStmt:
n->chain = $3;
$$ = (Node *)n;
}
| BEGIN_P opt_transaction transaction_mode_list_or_empty
{
TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_BEGIN;
n->options = $3;
$$ = (Node *)n;
}
| START TRANSACTION transaction_mode_list_or_empty
{
TransactionStmt *n = makeNode(TransactionStmt);
@@ -9919,14 +9976,6 @@ TransactionStmt:
n->chain = $3;
$$ = (Node *)n;
}
| END_P opt_transaction opt_transaction_chain
{
TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_COMMIT;
n->options = NIL;
n->chain = $3;
$$ = (Node *)n;
}
| ROLLBACK opt_transaction opt_transaction_chain
{
TransactionStmt *n = makeNode(TransactionStmt);
@@ -9993,6 +10042,24 @@ TransactionStmt:
}
;
TransactionStmtLegacy:
BEGIN_P opt_transaction transaction_mode_list_or_empty
{
TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_BEGIN;
n->options = $3;
$$ = (Node *)n;
}
| END_P opt_transaction opt_transaction_chain
{
TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_COMMIT;
n->options = NIL;
n->chain = $3;
$$ = (Node *)n;
}
;
opt_transaction: WORK
| TRANSACTION
| /*EMPTY*/
@@ -15429,6 +15496,7 @@ unreserved_keyword:
| ASSERTION
| ASSIGNMENT
| AT
| ATOMIC
| ATTACH
| ATTRIBUTE
| BACKWARD
@@ -15631,6 +15699,7 @@ unreserved_keyword:
| RESET
| RESTART
| RESTRICT
| RETURN
| RETURNS
| REVOKE
| ROLE
@@ -15938,6 +16007,7 @@ bare_label_keyword:
| ASSIGNMENT
| ASYMMETRIC
| AT
| ATOMIC
| ATTACH
| ATTRIBUTE
| AUTHORIZATION
@@ -16212,6 +16282,7 @@ bare_label_keyword:
| RESET
| RESTART
| RESTRICT
| RETURN
| RETURNS
| REVOKE
| RIGHT