diff options
Diffstat (limited to 'src/backend')
| -rw-r--r-- | src/backend/catalog/information_schema.sql | 3 | ||||
| -rw-r--r-- | src/backend/catalog/namespace.c | 184 | ||||
| -rw-r--r-- | src/backend/catalog/pg_aggregate.c | 7 | ||||
| -rw-r--r-- | src/backend/commands/functioncmds.c | 33 | ||||
| -rw-r--r-- | src/backend/nodes/copyfuncs.c | 3 | ||||
| -rw-r--r-- | src/backend/nodes/equalfuncs.c | 3 | ||||
| -rw-r--r-- | src/backend/nodes/outfuncs.c | 3 | ||||
| -rw-r--r-- | src/backend/parser/gram.y | 67 | ||||
| -rw-r--r-- | src/backend/parser/keywords.c | 3 | ||||
| -rw-r--r-- | src/backend/parser/parse_expr.c | 36 | ||||
| -rw-r--r-- | src/backend/parser/parse_func.c | 75 | ||||
| -rw-r--r-- | src/backend/parser/parse_utilcmd.c | 3 | ||||
| -rw-r--r-- | src/backend/utils/adt/regproc.c | 9 | ||||
| -rw-r--r-- | src/backend/utils/adt/ruleutils.c | 65 | ||||
| -rw-r--r-- | src/backend/utils/fmgr/funcapi.c | 8 |
15 files changed, 391 insertions, 111 deletions
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index a9566d9d3f..0e2452fa0f 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -4,7 +4,7 @@ * * Copyright (c) 2003-2008, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.43 2008/01/01 19:45:48 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.44 2008/07/16 01:30:21 tgl Exp $ */ /* @@ -1006,6 +1006,7 @@ CREATE VIEW parameters AS WHEN proargmodes[(ss.x).n] = 'i' THEN 'IN' WHEN proargmodes[(ss.x).n] = 'o' THEN 'OUT' WHEN proargmodes[(ss.x).n] = 'b' THEN 'INOUT' + WHEN proargmodes[(ss.x).n] = 'v' THEN 'IN' END AS character_data) AS parameter_mode, CAST('NO' AS character_data) AS is_result, CAST('NO' AS character_data) AS as_locator, diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index dfaea41cfe..48b8ee45e6 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -13,7 +13,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.107 2008/07/01 02:09:34 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.108 2008/07/16 01:30:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -38,6 +38,7 @@ #include "commands/dbcommands.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_func.h" #include "storage/backendid.h" #include "storage/ipc.h" #include "utils/acl.h" @@ -561,24 +562,36 @@ TypeIsVisible(Oid typid) * retrieve a list of the possible matches. * * If nargs is -1, we return all functions matching the given name, - * regardless of argument count. + * regardless of argument count. (expand_variadic must be false in this case.) + * + * If expand_variadic is true, then variadic functions having the same number + * or fewer arguments will be retrieved, with the variadic argument and any + * additional argument positions filled with the variadic element type. + * nvargs in the returned struct is set to the number of such arguments. + * If expand_variadic is false, variadic arguments are not treated specially, + * and the returned nvargs will always be zero. * * We search a single namespace if the function name is qualified, else * all namespaces in the search path. The return list will never contain * multiple entries with identical argument lists --- in the multiple- * namespace case, we arrange for entries in earlier namespaces to mask - * identical entries in later namespaces. + * identical entries in later namespaces. We also arrange for non-variadic + * functions to mask variadic ones if the expanded argument list is the same. */ FuncCandidateList -FuncnameGetCandidates(List *names, int nargs) +FuncnameGetCandidates(List *names, int nargs, bool expand_variadic) { FuncCandidateList resultList = NULL; + bool any_variadic = false; char *schemaname; char *funcname; Oid namespaceId; CatCList *catlist; int i; + /* check for caller error */ + Assert(nargs >= 0 || !expand_variadic); + /* deconstruct the name list */ DeconstructQualifiedName(names, &schemaname, &funcname); @@ -604,11 +617,57 @@ FuncnameGetCandidates(List *names, int nargs) HeapTuple proctup = &catlist->members[i]->tuple; Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup); int pronargs = procform->pronargs; + int effective_nargs; int pathpos = 0; + bool variadic = false; + Oid va_elem_type = InvalidOid; FuncCandidateList newResult; + /* + * Check if function is variadic, and get variadic element type if so. + * If expand_variadic is false, we can just ignore variadic-ness. + * + * XXX it's annoying to inject something as expensive as this even + * when there are no variadic functions involved. Find a better way. + */ + if (expand_variadic) + { + Datum proargmodes; + bool isnull; + + proargmodes = SysCacheGetAttr(PROCOID, proctup, + Anum_pg_proc_proargmodes, &isnull); + if (!isnull) + { + ArrayType *ar = DatumGetArrayTypeP(proargmodes); + char *argmodes; + int j; + + argmodes = ARR_DATA_PTR(ar); + j = ARR_DIMS(ar)[0] - 1; + if (j >= 0 && argmodes[j] == PROARGMODE_VARIADIC) + { + variadic = any_variadic = true; + switch (procform->proargtypes.values[j]) + { + case ANYOID: + va_elem_type = ANYOID; + break; + case ANYARRAYOID: + va_elem_type = ANYELEMENTOID; + break; + default: + va_elem_type = get_element_type(procform->proargtypes.values[j]); + Assert(OidIsValid(va_elem_type)); + break; + } + } + } + } + /* Ignore if it doesn't match requested argument count */ - if (nargs >= 0 && pronargs != nargs) + if (nargs >= 0 && + (variadic ? (pronargs > nargs) : (pronargs != nargs))) continue; if (OidIsValid(namespaceId)) @@ -616,7 +675,6 @@ FuncnameGetCandidates(List *names, int nargs) /* Consider only procs in specified namespace */ if (procform->pronamespace != namespaceId) continue; - /* No need to check args, they must all be different */ } else { @@ -635,28 +693,63 @@ FuncnameGetCandidates(List *names, int nargs) } if (nsp == NULL) continue; /* proc is not in search path */ + } + /* + * We must compute the effective argument list so that we can easily + * compare it to earlier results. We waste a palloc cycle if it gets + * masked by an earlier result, but really that's a pretty infrequent + * case so it's not worth worrying about. + */ + effective_nargs = Max(pronargs, nargs); + newResult = (FuncCandidateList) + palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid) + + effective_nargs * sizeof(Oid)); + newResult->pathpos = pathpos; + newResult->oid = HeapTupleGetOid(proctup); + newResult->nargs = effective_nargs; + memcpy(newResult->args, procform->proargtypes.values, + pronargs * sizeof(Oid)); + if (variadic) + { + int i; + + newResult->nvargs = effective_nargs - pronargs + 1; + /* Expand variadic argument into N copies of element type */ + for (i = pronargs - 1; i < effective_nargs; i++) + newResult->args[i] = va_elem_type; + } + else + newResult->nvargs = 0; + + /* + * Does it have the same arguments as something we already accepted? + * If so, decide which one to keep. We can skip this check for the + * single-namespace case if no variadic match has been made, since + * then the unique index on pg_proc guarantees all the matches have + * different argument lists. + */ + if (any_variadic || !OidIsValid(namespaceId)) + { /* - * Okay, it's in the search path, but does it have the same - * arguments as something we already accepted? If so, keep only - * the one that appears earlier in the search path. - * * If we have an ordered list from SearchSysCacheList (the normal * case), then any conflicting proc must immediately adjoin this * one in the list, so we only need to look at the newest result * item. If we have an unordered list, we have to scan the whole - * result list. + * result list. Also, if either the current candidate or any + * previous candidate is a variadic match, we can't assume that + * conflicts are adjacent. */ if (resultList) { FuncCandidateList prevResult; - if (catlist->ordered) + if (catlist->ordered && !any_variadic) { - if (pronargs == resultList->nargs && - memcmp(procform->proargtypes.values, + if (effective_nargs == resultList->nargs && + memcmp(newResult->args, resultList->args, - pronargs * sizeof(Oid)) == 0) + effective_nargs * sizeof(Oid)) == 0) prevResult = resultList; else prevResult = NULL; @@ -667,22 +760,58 @@ FuncnameGetCandidates(List *names, int nargs) prevResult; prevResult = prevResult->next) { - if (pronargs == prevResult->nargs && - memcmp(procform->proargtypes.values, + if (effective_nargs == prevResult->nargs && + memcmp(newResult->args, prevResult->args, - pronargs * sizeof(Oid)) == 0) + effective_nargs * sizeof(Oid)) == 0) break; } } if (prevResult) { - /* We have a match with a previous result */ - Assert(pathpos != prevResult->pathpos); + /* + * We have a match with a previous result. Prefer the + * one that's earlier in the search path. + */ if (pathpos > prevResult->pathpos) + { + pfree(newResult); continue; /* keep previous result */ + } + else if (pathpos == prevResult->pathpos) + { + /* + * With variadic functions we could have, for example, + * both foo(numeric) and foo(variadic numeric[]) in + * the same namespace; if so we prefer the + * non-variadic match on efficiency grounds. It's + * also possible to have conflicting variadic + * functions, such as foo(numeric, variadic numeric[]) + * and foo(variadic numeric[]). If you're silly + * enough to do that, we throw an error. (XXX It'd be + * better to detect such conflicts when the functions + * are created.) + */ + if (variadic) + { + if (prevResult->nvargs > 0) + ereport(ERROR, + (errcode(ERRCODE_AMBIGUOUS_FUNCTION), + errmsg("variadic function %s conflicts with another", + func_signature_string(names, pronargs, + procform->proargtypes.values)))); + /* else, previous result wasn't variadic */ + pfree(newResult); + continue; /* keep previous result */ + } + /* non-variadic can replace a previous variadic */ + Assert(prevResult->nvargs > 0); + } /* replace previous result */ prevResult->pathpos = pathpos; - prevResult->oid = HeapTupleGetOid(proctup); + prevResult->oid = newResult->oid; + prevResult->nvargs = newResult->nvargs; + pfree(newResult); continue; /* args are same, of course */ } } @@ -691,15 +820,6 @@ FuncnameGetCandidates(List *names, int nargs) /* * Okay to add it to result list */ - newResult = (FuncCandidateList) - palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid) - + pronargs * sizeof(Oid)); - newResult->pathpos = pathpos; - newResult->oid = HeapTupleGetOid(proctup); - newResult->nargs = pronargs; - memcpy(newResult->args, procform->proargtypes.values, - pronargs * sizeof(Oid)); - newResult->next = resultList; resultList = newResult; } @@ -755,7 +875,8 @@ FunctionIsVisible(Oid funcid) visible = false; - clist = FuncnameGetCandidates(list_make1(makeString(proname)), nargs); + clist = FuncnameGetCandidates(list_make1(makeString(proname)), + nargs, false); for (; clist; clist = clist->next) { @@ -1023,6 +1144,7 @@ OpernameGetCandidates(List *names, char oprkind) newResult->pathpos = pathpos; newResult->oid = HeapTupleGetOid(opertup); newResult->nargs = 2; + newResult->nvargs = 0; newResult->args[0] = operform->oprleft; newResult->args[1] = operform->oprright; newResult->next = resultList; diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 1ff7261877..e1c67ce5cd 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.93 2008/06/19 00:46:04 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.94 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -293,6 +293,7 @@ lookup_agg_function(List *fnName, { Oid fnOid; bool retset; + int nvargs; Oid *true_oid_array; FuncDetailCode fdresult; AclResult aclresult; @@ -305,8 +306,8 @@ lookup_agg_function(List *fnName, * function's return value. it also returns the true argument types to * the function. */ - fdresult = func_get_detail(fnName, NIL, nargs, input_types, - &fnOid, rettype, &retset, + fdresult = func_get_detail(fnName, NIL, nargs, input_types, false, + &fnOid, rettype, &retset, &nvargs, &true_oid_array); /* only valid case is a normal function not returning a set */ diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 9831692eea..cb249d9c7d 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.95 2008/07/12 10:44:56 petere Exp $ + * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.96 2008/07/16 01:30:22 tgl Exp $ * * DESCRIPTION * These routines take the parse tree and pick out the @@ -173,6 +173,7 @@ examine_parameter_list(List *parameters, Oid languageOid, Datum *paramModes; Datum *paramNames; int outCount = 0; + int varCount = 0; bool have_names = false; ListCell *x; int i; @@ -228,15 +229,41 @@ examine_parameter_list(List *parameters, Oid languageOid, errmsg("functions cannot accept set arguments"))); if (fp->mode != FUNC_PARAM_OUT) + { + /* only OUT parameters can follow a VARIADIC parameter */ + if (varCount > 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("VARIADIC parameter must be the last input parameter"))); inTypes[inCount++] = toid; + } - if (fp->mode != FUNC_PARAM_IN) + if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC) { if (outCount == 0) /* save first OUT param's type */ *requiredResultType = toid; outCount++; } + if (fp->mode == FUNC_PARAM_VARIADIC) + { + varCount++; + /* validate variadic parameter type */ + switch (toid) + { + case ANYARRAYOID: + case ANYOID: + /* okay */ + break; + default: + if (!OidIsValid(get_element_type(toid))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("VARIADIC parameter must be an array"))); + break; + } + } + allTypes[i] = ObjectIdGetDatum(toid); paramModes[i] = CharGetDatum(fp->mode); @@ -253,7 +280,7 @@ examine_parameter_list(List *parameters, Oid languageOid, /* Now construct the proper outputs as needed */ *parameterTypes = buildoidvector(inTypes, inCount); - if (outCount > 0) + if (outCount > 0 || varCount > 0) { *allParameterTypes = construct_array(allTypes, parameterCount, OIDOID, sizeof(Oid), true, 'i'); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index a42c40327f..2e1ce4cb0d 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.394 2008/05/16 23:36:05 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.395 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1651,6 +1651,7 @@ _copyFuncCall(FuncCall *from) COPY_NODE_FIELD(args); COPY_SCALAR_FIELD(agg_star); COPY_SCALAR_FIELD(agg_distinct); + COPY_SCALAR_FIELD(func_variadic); COPY_SCALAR_FIELD(location); return newnode; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 435ee6a6af..41999226b6 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.323 2008/05/16 23:36:05 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.324 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1705,6 +1705,7 @@ _equalFuncCall(FuncCall *a, FuncCall *b) COMPARE_NODE_FIELD(args); COMPARE_SCALAR_FIELD(agg_star); COMPARE_SCALAR_FIELD(agg_distinct); + COMPARE_SCALAR_FIELD(func_variadic); COMPARE_SCALAR_FIELD(location); return true; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 51b46a83ed..a03063ce1e 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.326 2008/04/29 14:59:16 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.327 2008/07/16 01:30:22 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -1610,6 +1610,7 @@ _outFuncCall(StringInfo str, FuncCall *node) WRITE_NODE_FIELD(args); WRITE_BOOL_FIELD(agg_star); WRITE_BOOL_FIELD(agg_distinct); + WRITE_BOOL_FIELD(func_variadic); WRITE_INT_FIELD(location); } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 4e59e37da9..70bbe940af 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.616 2008/06/15 01:25:54 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.617 2008/07/16 01:30:22 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -444,7 +444,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL UPDATE USER USING - VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARYING + VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING VERBOSE VERSION_P VIEW VOLATILE WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE @@ -4200,10 +4200,11 @@ func_arg: ; /* INOUT is SQL99 standard, IN OUT is for Oracle compatibility */ -arg_class: IN_P { $$ = FUNC_PARAM_IN; } - | OUT_P { $$ = FUNC_PARAM_OUT; } - | INOUT { $$ = FUNC_PARAM_INOUT; } - | IN_P OUT_P { $$ = FUNC_PARAM_INOUT; } +arg_class: IN_P { $$ = FUNC_PARAM_IN; } + | OUT_P { $$ = FUNC_PARAM_OUT; } + | INOUT { $$ = FUNC_PARAM_INOUT; } + | IN_P OUT_P { $$ = FUNC_PARAM_INOUT; } + | VARIADIC { $$ = FUNC_PARAM_VARIADIC; } ; /* @@ -7336,6 +7337,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($5, $1); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @2; $$ = (Node *) n; } @@ -7394,6 +7396,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($3, $5); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @4; $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~", $1, (Node *) n, @2); } @@ -7406,6 +7409,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($4, $6); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @5; $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~", $1, (Node *) n, @2); } @@ -7418,6 +7422,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($3, $5); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @4; $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~*", $1, (Node *) n, @2); } @@ -7430,6 +7435,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($4, $6); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @5; $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~*", $1, (Node *) n, @2); } @@ -7441,6 +7447,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($4, makeNullAConst()); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @2; $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n, @2); } @@ -7451,6 +7458,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($4, $6); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @5; $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n, @2); } @@ -7461,6 +7469,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($5, makeNullAConst()); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @5; $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n, @2); } @@ -7471,6 +7480,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($5, $7); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @6; $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n, @2); } @@ -7862,6 +7872,7 @@ func_expr: func_name '(' ')' n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -7872,6 +7883,29 @@ func_expr: func_name '(' ')' n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; + n->location = @1; + $$ = (Node *)n; + } + | func_name '(' VARIADIC a_expr ')' + { + FuncCall *n = makeNode(FuncCall); + n->funcname = $1; + n->args = list_make1($4); + n->agg_star = FALSE; + n->agg_distinct = FALSE; + n->func_variadic = TRUE; + n->location = @1; + $$ = (Node *)n; + } + | func_name '(' expr_list ',' VARIADIC a_expr ')' + { + FuncCall *n = makeNode(FuncCall); + n->funcname = $1; + n->args = lappend($3, $6); + n->agg_star = FALSE; + n->agg_distinct = FALSE; + n->func_variadic = TRUE; n->location = @1; $$ = (Node *)n; } @@ -7886,6 +7920,7 @@ func_expr: func_name '(' ')' * "must be an aggregate", but there's no provision * for that in FuncCall at the moment. */ + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -7896,6 +7931,7 @@ func_expr: func_name '(' ')' n->args = $4; n->agg_star = FALSE; n->agg_distinct = TRUE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -7916,6 +7952,7 @@ func_expr: func_name '(' ')' n->args = NIL; n->agg_star = TRUE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -7974,6 +8011,7 @@ func_expr: func_name '(' ')' n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8043,6 +8081,7 @@ func_expr: func_name '(' ')' n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8053,6 +8092,7 @@ func_expr: func_name '(' ')' n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8063,6 +8103,7 @@ func_expr: func_name '(' ')' n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8073,6 +8114,7 @@ func_expr: func_name '(' ')' n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8085,6 +8127,7 @@ func_expr: func_name '(' ')' n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8100,6 +8143,7 @@ func_expr: func_name '(' ')' n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8111,6 +8155,7 @@ func_expr: func_name '(' ')' n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8124,6 +8169,7 @@ func_expr: func_name '(' ')' n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8143,6 +8189,7 @@ func_expr: func_name '(' ')' n->args = list_make1($3); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8156,6 +8203,7 @@ func_expr: func_name '(' ')' n->args = $4; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8166,6 +8214,7 @@ func_expr: func_name '(' ')' n->args = $4; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8176,6 +8225,7 @@ func_expr: func_name '(' ')' n->args = $4; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8186,6 +8236,7 @@ func_expr: func_name '(' ')' n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -9362,6 +9413,7 @@ reserved_keyword: | UNIQUE | USER | USING + | VARIADIC | WHEN | WHERE ; @@ -9566,6 +9618,7 @@ makeOverlaps(List *largs, List *rargs, int location) n->args = list_concat(largs, rargs); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = location; return n; } @@ -9625,7 +9678,7 @@ extractArgTypes(List *parameters) { FunctionParameter *p = (FunctionParameter *) lfirst(i); - if (p->mode != FUNC_PARAM_OUT) /* keep if IN or INOUT */ + if (p->mode != FUNC_PARAM_OUT) /* keep if IN, INOUT, VARIADIC */ result = lappend(result, p->argType); } return result; diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index 43013e1e77..97fba9c956 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.198 2008/07/03 20:58:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.199 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -393,6 +393,7 @@ const ScanKeyword ScanKeywords[] = { {"value", VALUE_P, UNRESERVED_KEYWORD}, {"values", VALUES, COL_NAME_KEYWORD}, {"varchar", VARCHAR, COL_NAME_KEYWORD}, + {"variadic", VARIADIC, RESERVED_KEYWORD}, {"varying", VARYING, UNRESERVED_KEYWORD}, {"verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD}, {"version", VERSION_P, UNRESERVED_KEYWORD}, diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 362108ba3f..8addb53e51 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.228 2008/04/29 14:59:16 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.229 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -358,8 +358,8 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) result = ParseFuncOrColumn(pstate, list_make1(n), list_make1(result), - false, false, true, - -1); + false, false, false, + true, -1); } } /* process trailing subscripts, if any */ @@ -481,8 +481,8 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) node = ParseFuncOrColumn(pstate, list_make1(makeString(name2)), list_make1(node), - false, false, true, - cref->location); + false, false, false, + true, cref->location); } break; } @@ -511,8 +511,8 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) node = ParseFuncOrColumn(pstate, list_make1(makeString(name3)), list_make1(node), - false, false, true, - cref->location); + false, false, false, + true, cref->location); } break; } @@ -552,8 +552,8 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) node = ParseFuncOrColumn(pstate, list_make1(makeString(name4)), list_make1(node), - false, false, true, - cref->location); + false, false, false, + true, cref->location); } break; } @@ -1018,25 +1018,21 @@ transformFuncCall(ParseState *pstate, FuncCall *fn) List *targs; ListCell *args; - /* - * Transform the list of arguments. We use a shallow list copy and then - * transform-in-place to avoid O(N^2) behavior from repeated lappend's. - * - * XXX: repeated lappend() would no longer result in O(n^2) behavior; - * worth reconsidering this design? - */ - targs = list_copy(fn->args); - foreach(args, targs) + /* Transform the list of arguments ... */ + targs = NIL; + foreach(args, fn->args) { - lfirst(args) = transformExpr(pstate, - (Node *) lfirst(args)); + targs = lappend(targs, transformExpr(pstate, + (Node *) lfirst(args))); } + /* ... and hand off to ParseFuncOrColumn */ return ParseFuncOrColumn(pstate, fn->funcname, targs, fn->agg_star, fn->agg_distinct, + fn->func_variadic, false, fn->location); } diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 3d44c2520b..3bb5c452a8 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.202 2008/03/26 21:10:38 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.203 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -56,14 +56,14 @@ static void unknown_attribute(ParseState *pstate, Node *relref, char *attname, * intended to be used only to deliver an appropriate error message, * not to affect the semantics. When is_column is true, we should have * a single argument (the putative table), unqualified function name - * equal to the column name, and no aggregate decoration. + * equal to the column name, and no aggregate or variadic decoration. * * The argument expressions (in fargs) must have been transformed already. */ Node * ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, - bool agg_star, bool agg_distinct, bool is_column, - int location) + bool agg_star, bool agg_distinct, bool func_variadic, + bool is_column, int location) { Oid rettype; Oid funcid; @@ -75,6 +75,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, Oid *declared_arg_types; Node *retval; bool retset; + int nvargs; FuncDetailCode fdresult; /* @@ -126,9 +127,10 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, * Check for column projection: if function has one argument, and that * argument is of complex type, and function name is not qualified, then * the "function call" could be a projection. We also check that there - * wasn't any aggregate decoration. + * wasn't any aggregate or variadic decoration. */ - if (nargs == 1 && !agg_star && !agg_distinct && list_length(funcname) == 1) + if (nargs == 1 && !agg_star && !agg_distinct && !func_variadic && + list_length(funcname) == 1) { Oid argtype = actual_arg_types[0]; @@ -153,11 +155,15 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, * func_get_detail looks up the function in the catalogs, does * disambiguation for polymorphic functions, handles inheritance, and * returns the funcid and type and set or singleton status of the - * function's return value. it also returns the true argument types to - * the function. + * function's return value. It also returns the true argument types to + * the function. (In the case of a variadic function call, the reported + * "true" types aren't really what is in pg_proc: the variadic argument is + * replaced by a suitable number of copies of its element type. We'll fix + * it up below.) */ fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types, - &funcid, &rettype, &retset, + !func_variadic, + &funcid, &rettype, &retset, &nvargs, &declared_arg_types); if (fdresult == FUNCDETAIL_COERCION) { @@ -242,6 +248,34 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, /* perform the necessary typecasting of arguments */ make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types); + /* + * If it's a variadic function call, transform the last nvargs arguments + * into an array --- unless it's an "any" variadic. + */ + if (nvargs > 0 && declared_arg_types[nargs - 1] != ANYOID) + { + ArrayExpr *newa = makeNode(ArrayExpr); + int non_var_args = nargs - nvargs; + List *vargs; + + Assert(non_var_args >= 0); + vargs = list_copy_tail(fargs, non_var_args); + fargs = list_truncate(fargs, non_var_args); + + newa->elements = vargs; + /* assume all the variadic arguments were coerced to the same type */ + newa->element_typeid = exprType((Node *) linitial(vargs)); + newa->array_typeid = get_array_type(newa->element_typeid); + if (!OidIsValid(newa->array_typeid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find array type for data type %s", + format_type_be(newa->element_typeid)))); + newa->multidims = false; + + fargs = lappend(fargs, newa); + } + /* build the appropriate output structure */ if (fdresult == FUNCDETAIL_NORMAL) { @@ -668,21 +702,12 @@ func_select_candidate(int nargs, * Find the named function in the system catalogs. * * Attempt to find the named function in the system catalogs with - * arguments exactly as specified, so that the normal case - * (exact match) is as quick as possible. + * arguments exactly as specified, so that the normal case (exact match) + * is as quick as possible. * * If an exact match isn't found: * 1) check for possible interpretation as a type coercion request - * 2) get a vector of all possible input arg type arrays constructed - * from the superclasses of the original input arg types - * 3) get a list of all possible argument type arrays to the function - * with given name and number of arguments - * 4) for each input arg type array from vector #1: - * a) find how many of the function arg type arrays from list #2 - * it can be coerced to - * b) if the answer is one, we have our function - * c) if the answer is more than one, attempt to resolve the conflict - * d) if the answer is zero, try the next array from vector #1 + * 2) apply the ambiguous-function resolution rules * * Note: we rely primarily on nargs/argtypes as the argument description. * The actual expression node list is passed in fargs so that we can check @@ -694,16 +719,18 @@ func_get_detail(List *funcname, List *fargs, int nargs, Oid *argtypes, + bool expand_variadic, Oid *funcid, /* return value */ Oid *rettype, /* return value */ bool *retset, /* return value */ + int *nvargs, /* return value */ Oid **true_typeids) /* return value */ { FuncCandidateList raw_candidates; FuncCandidateList best_candidate; /* Get list of possible candidates from namespace search */ - raw_candidates = FuncnameGetCandidates(funcname, nargs); + raw_candidates = FuncnameGetCandidates(funcname, nargs, expand_variadic); /* * Quickly check if there is an exact match to the input datatypes (there @@ -786,6 +813,7 @@ func_get_detail(List *funcname, *funcid = InvalidOid; *rettype = targetType; *retset = false; + *nvargs = 0; *true_typeids = argtypes; return FUNCDETAIL_COERCION; } @@ -835,6 +863,7 @@ func_get_detail(List *funcname, FuncDetailCode result; *funcid = best_candidate->oid; + *nvargs = best_candidate->nvargs; *true_typeids = best_candidate->args; ftup = SearchSysCache(PROCOID, @@ -1189,7 +1218,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError) { FuncCandidateList clist; - clist = FuncnameGetCandidates(funcname, nargs); + clist = FuncnameGetCandidates(funcname, nargs, false); while (clist) { diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 33f5b8015f..bbdea4642c 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -19,7 +19,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.13 2008/04/29 14:59:17 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.14 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -388,6 +388,7 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt, funccallnode->args = list_make1(castnode); funccallnode->agg_star = false; funccallnode->agg_distinct = false; + funccallnode->func_variadic = false; funccallnode->location = -1; constraint = makeNode(Constraint); diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c index 986bac041d..d50dc23d77 100644 --- a/src/backend/utils/adt/regproc.c +++ b/src/backend/utils/adt/regproc.c @@ -13,7 +13,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.107 2008/06/19 00:46:05 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.108 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -131,7 +131,7 @@ regprocin(PG_FUNCTION_ARGS) * pg_proc entries in the current search path. */ names = stringToQualifiedNameList(pro_name_or_oid); - clist = FuncnameGetCandidates(names, -1); + clist = FuncnameGetCandidates(names, -1, false); if (clist == NULL) ereport(ERROR, @@ -189,7 +189,8 @@ regprocout(PG_FUNCTION_ARGS) * Would this proc be found (uniquely!) by regprocin? If not, * qualify it. */ - clist = FuncnameGetCandidates(list_make1(makeString(proname)), -1); + clist = FuncnameGetCandidates(list_make1(makeString(proname)), + -1, false); if (clist != NULL && clist->next == NULL && clist->oid == proid) nspname = NULL; @@ -276,7 +277,7 @@ regprocedurein(PG_FUNCTION_ARGS) */ parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes); - clist = FuncnameGetCandidates(names, nargs); + clist = FuncnameGetCandidates(names, nargs, false); for (; clist; clist = clist->next) { diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 3754b4981e..dc4a6cc4a8 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.275 2008/06/06 17:59:29 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.276 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -193,7 +193,8 @@ static Node *processIndirection(Node *node, deparse_context *context, bool printit); static void printSubscripts(ArrayRef *aref, deparse_context *context); static char *generate_relation_name(Oid relid); -static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes); +static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes, + bool *is_variadic); static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2); static text *string_to_text(char *str); static char *flatten_reloptions(Oid relid); @@ -531,7 +532,7 @@ pg_get_triggerdef(PG_FUNCTION_ARGS) appendStringInfo(&buf, "FOR EACH STATEMENT "); appendStringInfo(&buf, "EXECUTE PROCEDURE %s(", - generate_function_name(trigrec->tgfoid, 0, NULL)); + generate_function_name(trigrec->tgfoid, 0, NULL, NULL)); if (trigrec->tgnargs > 0) { @@ -4293,6 +4294,7 @@ get_func_expr(FuncExpr *expr, deparse_context *context, Oid funcoid = expr->funcid; Oid argtypes[FUNC_MAX_ARGS]; int nargs; + bool is_variadic; ListCell *l; /* @@ -4343,8 +4345,17 @@ get_func_expr(FuncExpr *expr, deparse_context *context, } appendStringInfo(buf, "%s(", - generate_function_name(funcoid, nargs, argtypes)); - get_rule_expr((Node *) expr->args, context, true); + generate_function_name(funcoid, nargs, argtypes, + &is_variadic)); + nargs = 0; + foreach(l, expr->args) + { + if (nargs++ > 0) + appendStringInfoString(buf, ", "); + if (is_variadic && lnext(l) == NULL) + appendStringInfoString(buf, "VARIADIC "); + get_rule_expr((Node *) lfirst(l), context, true); + } appendStringInfoChar(buf, ')'); } @@ -4371,7 +4382,8 @@ get_agg_expr(Aggref *aggref, deparse_context *context) } appendStringInfo(buf, "%s(%s", - generate_function_name(aggref->aggfnoid, nargs, argtypes), + generate_function_name(aggref->aggfnoid, + nargs, argtypes, NULL), aggref->aggdistinct ? "DISTINCT " : ""); /* aggstar can be set only in zero-argument aggregates */ if (aggref->aggstar) @@ -5329,10 +5341,12 @@ generate_relation_name(Oid relid) * given that it is being called with the specified actual arg types. * (Arg types matter because of ambiguous-function resolution rules.) * - * The result includes all necessary quoting and schema-prefixing. + * The result includes all necessary quoting and schema-prefixing. We can + * also pass back an indication of whether the function is variadic. */ static char * -generate_function_name(Oid funcid, int nargs, Oid *argtypes) +generate_function_name(Oid funcid, int nargs, Oid *argtypes, + bool *is_variadic) { HeapTuple proctup; Form_pg_proc procform; @@ -5343,6 +5357,7 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes) Oid p_funcid; Oid p_rettype; bool p_retset; + int p_nvargs; Oid *p_true_typeids; proctup = SearchSysCache(PROCOID, @@ -5352,7 +5367,7 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes) elog(ERROR, "cache lookup failed for function %u", funcid); procform = (Form_pg_proc) GETSTRUCT(proctup); proname = NameStr(procform->proname); - Assert(nargs == procform->pronargs); + Assert(nargs >= procform->pronargs); /* * The idea here is to schema-qualify only if the parser would fail to @@ -5360,9 +5375,9 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes) * specified argtypes. */ p_result = func_get_detail(list_make1(makeString(proname)), - NIL, nargs, argtypes, + NIL, nargs, argtypes, false, &p_funcid, &p_rettype, - &p_retset, &p_true_typeids); + &p_retset, &p_nvargs, &p_true_typeids); if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE) && p_funcid == funcid) nspname = NULL; @@ -5371,6 +5386,34 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes) result = quote_qualified_identifier(nspname, proname); + /* Check variadic-ness if caller cares */ + if (is_variadic) + { + /* XXX change this if we simplify code in FuncnameGetCandidates */ + Datum proargmodes; + bool isnull; + + *is_variadic = false; + + proargmodes = SysCacheGetAttr(PROCOID, proctup, + Anum_pg_proc_proargmodes, &isnull); + if (!isnull) + { + ArrayType *ar = DatumGetArrayTypeP(proargmodes); + char *argmodes; + int j; + + argmodes = ARR_DATA_PTR(ar); + j = ARR_DIMS(ar)[0] - 1; + if (j >= 0 && argmodes[j] == PROARGMODE_VARIADIC) + { + /* "any" variadics are not treated as variadics for listing */ + if (procform->proargtypes.values[j] != ANYOID) + *is_variadic = true; + } + } + } + ReleaseSysCache(proctup); return result; diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index 42586f13aa..7cba375ee0 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -7,7 +7,7 @@ * Copyright (c) 2002-2008, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.39 2008/03/25 22:42:45 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.40 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -844,7 +844,8 @@ get_func_result_name(Oid functionId) numoutargs = 0; for (i = 0; i < numargs; i++) { - if (argmodes[i] == PROARGMODE_IN) + if (argmodes[i] == PROARGMODE_IN || + argmodes[i] == PROARGMODE_VARIADIC) continue; Assert(argmodes[i] == PROARGMODE_OUT || argmodes[i] == PROARGMODE_INOUT); @@ -994,7 +995,8 @@ build_function_result_tupdesc_d(Datum proallargtypes, { char *pname; - if (argmodes[i] == PROARGMODE_IN) + if (argmodes[i] == PROARGMODE_IN || + argmodes[i] == PROARGMODE_VARIADIC) continue; Assert(argmodes[i] == PROARGMODE_OUT || argmodes[i] == PROARGMODE_INOUT); |
