summaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
authorPeter Eisentraut2017-11-30 13:46:13 +0000
committerPeter Eisentraut2017-11-30 16:03:20 +0000
commite4128ee767df3c8c715eb08f8977647ae49dfb59 (patch)
tree6513b824fd69b982057f5fe2742039597ce6cea4 /src/backend/parser
parent1761653bbb17447906c812c347b3fe284ce699cf (diff)
SQL procedures
This adds a new object type "procedure" that is similar to a function but does not have a return type and is invoked by the new CALL statement instead of SELECT or similar. This implementation is aligned with the SQL standard and compatible with or similar to other SQL implementations. This commit adds new commands CALL, CREATE/ALTER/DROP PROCEDURE, as well as ALTER/DROP ROUTINE that can refer to either a function or a procedure (or an aggregate function, as an extension to SQL). There is also support for procedures in various utility commands such as COMMENT and GRANT, as well as support in pg_dump and psql. Support for defining procedures is available in all the languages supplied by the core distribution. While this commit is mainly syntax sugar around existing functionality, future features will rely on having procedures as a separate object type. Reviewed-by: Andrew Dunstan <andrew.dunstan@2ndquadrant.com>
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/gram.y255
-rw-r--r--src/backend/parser/parse_agg.c11
-rw-r--r--src/backend/parser/parse_expr.c8
-rw-r--r--src/backend/parser/parse_func.c201
4 files changed, 393 insertions, 82 deletions
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c301ca465d4..ebfc94f8969 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -253,7 +253,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
AlterCompositeTypeStmt AlterUserMappingStmt
AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt
AlterDefaultPrivilegesStmt DefACLAction
- AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
+ AnalyzeStmt CallStmt ClosePortalStmt ClusterStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
@@ -611,7 +611,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
BOOLEAN_P BOTH BY
- CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
+ CACHE CALL CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT
COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
@@ -660,14 +660,14 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
PARALLEL PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POLICY
POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
- PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROGRAM PUBLICATION
+ PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
QUOTE
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
- ROW ROWS RULE
+ ROUTINE ROUTINES ROW ROWS RULE
SAVEPOINT SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW
@@ -845,6 +845,7 @@ stmt :
| AlterTSDictionaryStmt
| AlterUserMappingStmt
| AnalyzeStmt
+ | CallStmt
| CheckPointStmt
| ClosePortalStmt
| ClusterStmt
@@ -942,6 +943,20 @@ stmt :
/*****************************************************************************
*
+ * CALL statement
+ *
+ *****************************************************************************/
+
+CallStmt: CALL func_application
+ {
+ CallStmt *n = makeNode(CallStmt);
+ n->funccall = castNode(FuncCall, $2);
+ $$ = (Node *)n;
+ }
+ ;
+
+/*****************************************************************************
+ *
* Create a new Postgres DBMS role
*
*****************************************************************************/
@@ -4554,6 +4569,24 @@ AlterExtensionContentsStmt:
n->object = (Node *) lcons(makeString($9), $7);
$$ = (Node *)n;
}
+ | ALTER EXTENSION name add_drop PROCEDURE function_with_argtypes
+ {
+ AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
+ n->extname = $3;
+ n->action = $4;
+ n->objtype = OBJECT_PROCEDURE;
+ n->object = (Node *) $6;
+ $$ = (Node *)n;
+ }
+ | ALTER EXTENSION name add_drop ROUTINE function_with_argtypes
+ {
+ AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
+ n->extname = $3;
+ n->action = $4;
+ n->objtype = OBJECT_ROUTINE;
+ n->object = (Node *) $6;
+ $$ = (Node *)n;
+ }
| ALTER EXTENSION name add_drop SCHEMA name
{
AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
@@ -6436,6 +6469,22 @@ CommentStmt:
n->comment = $8;
$$ = (Node *) n;
}
+ | COMMENT ON PROCEDURE function_with_argtypes IS comment_text
+ {
+ CommentStmt *n = makeNode(CommentStmt);
+ n->objtype = OBJECT_PROCEDURE;
+ n->object = (Node *) $4;
+ n->comment = $6;
+ $$ = (Node *) n;
+ }
+ | COMMENT ON ROUTINE function_with_argtypes IS comment_text
+ {
+ CommentStmt *n = makeNode(CommentStmt);
+ n->objtype = OBJECT_ROUTINE;
+ n->object = (Node *) $4;
+ n->comment = $6;
+ $$ = (Node *) n;
+ }
| COMMENT ON RULE name ON any_name IS comment_text
{
CommentStmt *n = makeNode(CommentStmt);
@@ -6614,6 +6663,26 @@ SecLabelStmt:
n->label = $9;
$$ = (Node *) n;
}
+ | SECURITY LABEL opt_provider ON PROCEDURE function_with_argtypes
+ IS security_label
+ {
+ SecLabelStmt *n = makeNode(SecLabelStmt);
+ n->provider = $3;
+ n->objtype = OBJECT_PROCEDURE;
+ n->object = (Node *) $6;
+ n->label = $8;
+ $$ = (Node *) n;
+ }
+ | SECURITY LABEL opt_provider ON ROUTINE function_with_argtypes
+ IS security_label
+ {
+ SecLabelStmt *n = makeNode(SecLabelStmt);
+ n->provider = $3;
+ n->objtype = OBJECT_ROUTINE;
+ n->object = (Node *) $6;
+ n->label = $8;
+ $$ = (Node *) n;
+ }
;
opt_provider: FOR NonReservedWord_or_Sconst { $$ = $2; }
@@ -6977,6 +7046,22 @@ privilege_target:
n->objs = $2;
$$ = n;
}
+ | PROCEDURE function_with_argtypes_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = ACL_OBJECT_PROCEDURE;
+ n->objs = $2;
+ $$ = n;
+ }
+ | ROUTINE function_with_argtypes_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = ACL_OBJECT_ROUTINE;
+ n->objs = $2;
+ $$ = n;
+ }
| DATABASE name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -7057,6 +7142,22 @@ privilege_target:
n->objs = $5;
$$ = n;
}
+ | ALL PROCEDURES IN_P SCHEMA name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_ALL_IN_SCHEMA;
+ n->objtype = ACL_OBJECT_PROCEDURE;
+ n->objs = $5;
+ $$ = n;
+ }
+ | ALL ROUTINES IN_P SCHEMA name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_ALL_IN_SCHEMA;
+ n->objtype = ACL_OBJECT_ROUTINE;
+ n->objs = $5;
+ $$ = n;
+ }
;
@@ -7213,6 +7314,7 @@ DefACLAction:
defacl_privilege_target:
TABLES { $$ = ACL_OBJECT_RELATION; }
| FUNCTIONS { $$ = ACL_OBJECT_FUNCTION; }
+ | ROUTINES { $$ = ACL_OBJECT_FUNCTION; }
| SEQUENCES { $$ = ACL_OBJECT_SEQUENCE; }
| TYPES_P { $$ = ACL_OBJECT_TYPE; }
| SCHEMAS { $$ = ACL_OBJECT_NAMESPACE; }
@@ -7413,6 +7515,18 @@ CreateFunctionStmt:
n->withClause = $7;
$$ = (Node *)n;
}
+ | CREATE opt_or_replace PROCEDURE func_name func_args_with_defaults
+ createfunc_opt_list
+ {
+ CreateFunctionStmt *n = makeNode(CreateFunctionStmt);
+ n->replace = $2;
+ n->funcname = $4;
+ n->parameters = $5;
+ n->returnType = NULL;
+ n->is_procedure = true;
+ n->options = $6;
+ $$ = (Node *)n;
+ }
;
opt_or_replace:
@@ -7830,7 +7944,7 @@ table_func_column_list:
;
/*****************************************************************************
- * ALTER FUNCTION
+ * ALTER FUNCTION / ALTER PROCEDURE / ALTER ROUTINE
*
* RENAME and OWNER subcommands are already provided by the generic
* ALTER infrastructure, here we just specify alterations that can
@@ -7841,6 +7955,23 @@ AlterFunctionStmt:
ALTER FUNCTION function_with_argtypes alterfunc_opt_list opt_restrict
{
AlterFunctionStmt *n = makeNode(AlterFunctionStmt);
+ n->objtype = OBJECT_FUNCTION;
+ n->func = $3;
+ n->actions = $4;
+ $$ = (Node *) n;
+ }
+ | ALTER PROCEDURE function_with_argtypes alterfunc_opt_list opt_restrict
+ {
+ AlterFunctionStmt *n = makeNode(AlterFunctionStmt);
+ n->objtype = OBJECT_PROCEDURE;
+ n->func = $3;
+ n->actions = $4;
+ $$ = (Node *) n;
+ }
+ | ALTER ROUTINE function_with_argtypes alterfunc_opt_list opt_restrict
+ {
+ AlterFunctionStmt *n = makeNode(AlterFunctionStmt);
+ n->objtype = OBJECT_ROUTINE;
n->func = $3;
n->actions = $4;
$$ = (Node *) n;
@@ -7865,6 +7996,8 @@ opt_restrict:
* QUERY:
*
* DROP FUNCTION funcname (arg1, arg2, ...) [ RESTRICT | CASCADE ]
+ * DROP PROCEDURE procname (arg1, arg2, ...) [ RESTRICT | CASCADE ]
+ * DROP ROUTINE routname (arg1, arg2, ...) [ RESTRICT | CASCADE ]
* DROP AGGREGATE aggname (arg1, ...) [ RESTRICT | CASCADE ]
* DROP OPERATOR opname (leftoperand_typ, rightoperand_typ) [ RESTRICT | CASCADE ]
*
@@ -7891,6 +8024,46 @@ RemoveFuncStmt:
n->concurrent = false;
$$ = (Node *)n;
}
+ | DROP PROCEDURE function_with_argtypes_list opt_drop_behavior
+ {
+ DropStmt *n = makeNode(DropStmt);
+ n->removeType = OBJECT_PROCEDURE;
+ n->objects = $3;
+ n->behavior = $4;
+ n->missing_ok = false;
+ n->concurrent = false;
+ $$ = (Node *)n;
+ }
+ | DROP PROCEDURE IF_P EXISTS function_with_argtypes_list opt_drop_behavior
+ {
+ DropStmt *n = makeNode(DropStmt);
+ n->removeType = OBJECT_PROCEDURE;
+ n->objects = $5;
+ n->behavior = $6;
+ n->missing_ok = true;
+ n->concurrent = false;
+ $$ = (Node *)n;
+ }
+ | DROP ROUTINE function_with_argtypes_list opt_drop_behavior
+ {
+ DropStmt *n = makeNode(DropStmt);
+ n->removeType = OBJECT_ROUTINE;
+ n->objects = $3;
+ n->behavior = $4;
+ n->missing_ok = false;
+ n->concurrent = false;
+ $$ = (Node *)n;
+ }
+ | DROP ROUTINE IF_P EXISTS function_with_argtypes_list opt_drop_behavior
+ {
+ DropStmt *n = makeNode(DropStmt);
+ n->removeType = OBJECT_ROUTINE;
+ n->objects = $5;
+ n->behavior = $6;
+ n->missing_ok = true;
+ n->concurrent = false;
+ $$ = (Node *)n;
+ }
;
RemoveAggrStmt:
@@ -8348,6 +8521,15 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
n->missing_ok = true;
$$ = (Node *)n;
}
+ | ALTER PROCEDURE function_with_argtypes RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_PROCEDURE;
+ n->object = (Node *) $3;
+ n->newname = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
| ALTER PUBLICATION name RENAME TO name
{
RenameStmt *n = makeNode(RenameStmt);
@@ -8357,6 +8539,15 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER ROUTINE function_with_argtypes RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_ROUTINE;
+ n->object = (Node *) $3;
+ n->newname = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
| ALTER SCHEMA name RENAME TO name
{
RenameStmt *n = makeNode(RenameStmt);
@@ -8736,6 +8927,22 @@ AlterObjectDependsStmt:
n->extname = makeString($7);
$$ = (Node *)n;
}
+ | ALTER PROCEDURE function_with_argtypes DEPENDS ON EXTENSION name
+ {
+ AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
+ n->objectType = OBJECT_PROCEDURE;
+ n->object = (Node *) $3;
+ n->extname = makeString($7);
+ $$ = (Node *)n;
+ }
+ | ALTER ROUTINE function_with_argtypes DEPENDS ON EXTENSION name
+ {
+ AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
+ n->objectType = OBJECT_ROUTINE;
+ n->object = (Node *) $3;
+ n->extname = makeString($7);
+ $$ = (Node *)n;
+ }
| ALTER TRIGGER name ON qualified_name DEPENDS ON EXTENSION name
{
AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
@@ -8851,6 +9058,24 @@ AlterObjectSchemaStmt:
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER PROCEDURE function_with_argtypes SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_PROCEDURE;
+ n->object = (Node *) $3;
+ n->newschema = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER ROUTINE function_with_argtypes SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_ROUTINE;
+ n->object = (Node *) $3;
+ n->newschema = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
| ALTER TABLE relation_expr SET SCHEMA name
{
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
@@ -9126,6 +9351,22 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
n->newowner = $9;
$$ = (Node *)n;
}
+ | ALTER PROCEDURE function_with_argtypes OWNER TO RoleSpec
+ {
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+ n->objectType = OBJECT_PROCEDURE;
+ n->object = (Node *) $3;
+ n->newowner = $6;
+ $$ = (Node *)n;
+ }
+ | ALTER ROUTINE function_with_argtypes OWNER TO RoleSpec
+ {
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+ n->objectType = OBJECT_ROUTINE;
+ n->object = (Node *) $3;
+ n->newowner = $6;
+ $$ = (Node *)n;
+ }
| ALTER SCHEMA name OWNER TO RoleSpec
{
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
@@ -14689,6 +14930,7 @@ unreserved_keyword:
| BEGIN_P
| BY
| CACHE
+ | CALL
| CALLED
| CASCADE
| CASCADED
@@ -14848,6 +15090,7 @@ unreserved_keyword:
| PRIVILEGES
| PROCEDURAL
| PROCEDURE
+ | PROCEDURES
| PROGRAM
| PUBLICATION
| QUOTE
@@ -14874,6 +15117,8 @@ unreserved_keyword:
| ROLE
| ROLLBACK
| ROLLUP
+ | ROUTINE
+ | ROUTINES
| ROWS
| RULE
| SAVEPOINT
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 64111f315e3..4c4f4cdc3d7 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -508,6 +508,14 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
break;
+ case EXPR_KIND_CALL:
+ if (isAgg)
+ err = _("aggregate functions are not allowed in CALL arguments");
+ else
+ err = _("grouping operations are not allowed in CALL arguments");
+
+ break;
+
/*
* There is intentionally no default: case here, so that the
* compiler will warn if we add a new ParseExprKind without
@@ -883,6 +891,9 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
case EXPR_KIND_PARTITION_EXPRESSION:
err = _("window functions are not allowed in partition key expression");
break;
+ case EXPR_KIND_CALL:
+ err = _("window functions are not allowed in CALL arguments");
+ break;
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 86d1da06775..29f9da796fc 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -480,6 +480,7 @@ transformIndirection(ParseState *pstate, A_Indirection *ind)
list_make1(result),
last_srf,
NULL,
+ false,
location);
if (newresult == NULL)
unknown_attribute(pstate, result, strVal(n), location);
@@ -629,6 +630,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
list_make1(node),
pstate->p_last_srf,
NULL,
+ false,
cref->location);
}
break;
@@ -676,6 +678,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
list_make1(node),
pstate->p_last_srf,
NULL,
+ false,
cref->location);
}
break;
@@ -736,6 +739,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
list_make1(node),
pstate->p_last_srf,
NULL,
+ false,
cref->location);
}
break;
@@ -1477,6 +1481,7 @@ transformFuncCall(ParseState *pstate, FuncCall *fn)
targs,
last_srf,
fn,
+ false,
fn->location);
}
@@ -1812,6 +1817,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
case EXPR_KIND_RETURNING:
case EXPR_KIND_VALUES:
case EXPR_KIND_VALUES_SINGLE:
+ case EXPR_KIND_CALL:
/* okay */
break;
case EXPR_KIND_CHECK_CONSTRAINT:
@@ -3462,6 +3468,8 @@ ParseExprKindName(ParseExprKind exprKind)
return "WHEN";
case EXPR_KIND_PARTITION_EXPRESSION:
return "PARTITION BY";
+ case EXPR_KIND_CALL:
+ return "CALL";
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index a11843332b0..2f20516e766 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -71,7 +71,7 @@ static Node *ParseComplexProjection(ParseState *pstate, const char *funcname,
*/
Node *
ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
- Node *last_srf, FuncCall *fn, int location)
+ Node *last_srf, FuncCall *fn, bool proc_call, int location)
{
bool is_column = (fn == NULL);
List *agg_order = (fn ? fn->agg_order : NIL);
@@ -263,7 +263,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
actual_arg_types[0], rettype, -1,
COERCION_EXPLICIT, COERCE_EXPLICIT_CALL, location);
}
- else if (fdresult == FUNCDETAIL_NORMAL)
+ else if (fdresult == FUNCDETAIL_NORMAL || fdresult == FUNCDETAIL_PROCEDURE)
{
/*
* Normal function found; was there anything indicating it must be an
@@ -306,6 +306,26 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
errmsg("OVER specified, but %s is not a window function nor an aggregate function",
NameListToString(funcname)),
parser_errposition(pstate, location)));
+
+ if (fdresult == FUNCDETAIL_NORMAL && proc_call)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("%s is not a procedure",
+ func_signature_string(funcname, nargs,
+ argnames,
+ actual_arg_types)),
+ errhint("To call a function, use SELECT."),
+ parser_errposition(pstate, location)));
+
+ if (fdresult == FUNCDETAIL_PROCEDURE && !proc_call)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("%s is a procedure",
+ func_signature_string(funcname, nargs,
+ argnames,
+ actual_arg_types)),
+ errhint("To call a procedure, use CALL."),
+ parser_errposition(pstate, location)));
}
else if (fdresult == FUNCDETAIL_AGGREGATE)
{
@@ -635,7 +655,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
check_srf_call_placement(pstate, last_srf, location);
/* build the appropriate output structure */
- if (fdresult == FUNCDETAIL_NORMAL)
+ if (fdresult == FUNCDETAIL_NORMAL || fdresult == FUNCDETAIL_PROCEDURE)
{
FuncExpr *funcexpr = makeNode(FuncExpr);
@@ -1589,6 +1609,8 @@ func_get_detail(List *funcname,
result = FUNCDETAIL_AGGREGATE;
else if (pform->proiswindow)
result = FUNCDETAIL_WINDOWFUNC;
+ else if (pform->prorettype == InvalidOid)
+ result = FUNCDETAIL_PROCEDURE;
else
result = FUNCDETAIL_NORMAL;
ReleaseSysCache(ftup);
@@ -1984,16 +2006,28 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
/*
* LookupFuncWithArgs
- * Like LookupFuncName, but the argument types are specified by a
- * ObjectWithArgs node.
+ *
+ * Like LookupFuncName, but the argument types are specified by a
+ * ObjectWithArgs node. Also, this function can check whether the result is a
+ * function, procedure, or aggregate, based on the objtype argument. Pass
+ * OBJECT_ROUTINE to accept any of them.
+ *
+ * For historical reasons, we also accept aggregates when looking for a
+ * function.
*/
Oid
-LookupFuncWithArgs(ObjectWithArgs *func, bool noError)
+LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError)
{
Oid argoids[FUNC_MAX_ARGS];
int argcount;
int i;
ListCell *args_item;
+ Oid oid;
+
+ Assert(objtype == OBJECT_AGGREGATE ||
+ objtype == OBJECT_FUNCTION ||
+ objtype == OBJECT_PROCEDURE ||
+ objtype == OBJECT_ROUTINE);
argcount = list_length(func->objargs);
if (argcount > FUNC_MAX_ARGS)
@@ -2013,90 +2047,100 @@ LookupFuncWithArgs(ObjectWithArgs *func, bool noError)
args_item = lnext(args_item);
}
- return LookupFuncName(func->objname, func->args_unspecified ? -1 : argcount, argoids, noError);
-}
-
-/*
- * LookupAggWithArgs
- * Find an aggregate function from a given ObjectWithArgs node.
- *
- * This is almost like LookupFuncWithArgs, but the error messages refer
- * to aggregates rather than plain functions, and we verify that the found
- * function really is an aggregate.
- */
-Oid
-LookupAggWithArgs(ObjectWithArgs *agg, bool noError)
-{
- Oid argoids[FUNC_MAX_ARGS];
- int argcount;
- int i;
- ListCell *lc;
- Oid oid;
- HeapTuple ftup;
- Form_pg_proc pform;
-
- argcount = list_length(agg->objargs);
- if (argcount > FUNC_MAX_ARGS)
- ereport(ERROR,
- (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
- errmsg_plural("functions cannot have more than %d argument",
- "functions cannot have more than %d arguments",
- FUNC_MAX_ARGS,
- FUNC_MAX_ARGS)));
+ /*
+ * When looking for a function or routine, we pass noError through to
+ * LookupFuncName and let it make any error messages. Otherwise, we make
+ * our own errors for the aggregate and procedure cases.
+ */
+ oid = LookupFuncName(func->objname, func->args_unspecified ? -1 : argcount, argoids,
+ (objtype == OBJECT_FUNCTION || objtype == OBJECT_ROUTINE) ? noError : true);
- i = 0;
- foreach(lc, agg->objargs)
+ if (objtype == OBJECT_FUNCTION)
{
- TypeName *t = (TypeName *) lfirst(lc);
-
- argoids[i] = LookupTypeNameOid(NULL, t, noError);
- i++;
+ /* Make sure it's a function, not a procedure */
+ if (oid && get_func_rettype(oid) == InvalidOid)
+ {
+ if (noError)
+ return InvalidOid;
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("%s is not a function",
+ func_signature_string(func->objname, argcount,
+ NIL, argoids))));
+ }
}
-
- oid = LookupFuncName(agg->objname, argcount, argoids, true);
-
- if (!OidIsValid(oid))
+ else if (objtype == OBJECT_PROCEDURE)
{
- if (noError)
- return InvalidOid;
- if (argcount == 0)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("aggregate %s(*) does not exist",
- NameListToString(agg->objname))));
- else
+ if (!OidIsValid(oid))
+ {
+ if (noError)
+ return InvalidOid;
+ else if (func->args_unspecified)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not find a procedure named \"%s\"",
+ NameListToString(func->objname))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("procedure %s does not exist",
+ func_signature_string(func->objname, argcount,
+ NIL, argoids))));
+ }
+
+ /* Make sure it's a procedure */
+ if (get_func_rettype(oid) != InvalidOid)
+ {
+ if (noError)
+ return InvalidOid;
ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("aggregate %s does not exist",
- func_signature_string(agg->objname, argcount,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("%s is not a procedure",
+ func_signature_string(func->objname, argcount,
NIL, argoids))));
+ }
}
-
- /* Make sure it's an aggregate */
- ftup = SearchSysCache1(PROCOID, ObjectIdGetDatum(oid));
- if (!HeapTupleIsValid(ftup)) /* should not happen */
- elog(ERROR, "cache lookup failed for function %u", oid);
- pform = (Form_pg_proc) GETSTRUCT(ftup);
-
- if (!pform->proisagg)
+ else if (objtype == OBJECT_AGGREGATE)
{
- ReleaseSysCache(ftup);
- if (noError)
- return InvalidOid;
- /* we do not use the (*) notation for functions... */
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("function %s is not an aggregate",
- func_signature_string(agg->objname, argcount,
- NIL, argoids))));
- }
+ if (!OidIsValid(oid))
+ {
+ if (noError)
+ return InvalidOid;
+ else if (func->args_unspecified)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not find a aggregate named \"%s\"",
+ NameListToString(func->objname))));
+ else if (argcount == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("aggregate %s(*) does not exist",
+ NameListToString(func->objname))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("aggregate %s does not exist",
+ func_signature_string(func->objname, argcount,
+ NIL, argoids))));
+ }
- ReleaseSysCache(ftup);
+ /* Make sure it's an aggregate */
+ if (!get_func_isagg(oid))
+ {
+ if (noError)
+ return InvalidOid;
+ /* we do not use the (*) notation for functions... */
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s is not an aggregate",
+ func_signature_string(func->objname, argcount,
+ NIL, argoids))));
+ }
+ }
return oid;
}
-
/*
* check_srf_call_placement
* Verify that a set-returning function is called in a valid place,
@@ -2236,6 +2280,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
case EXPR_KIND_PARTITION_EXPRESSION:
err = _("set-returning functions are not allowed in partition key expressions");
break;
+ case EXPR_KIND_CALL:
+ err = _("set-returning functions are not allowed in CALL arguments");
+ break;
/*
* There is intentionally no default: case here, so that the