diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2008-07-18 03:32:53 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2008-07-18 03:32:53 +0000 |
commit | 69a785b8bfe076847f72317a41964821e85ccfd6 (patch) | |
tree | 8089a0c1e3b1075d81f49a82ab73b443dbe7d564 /src | |
parent | a8fb90cf2db614f3c1d4331bfaafd9a1953148e9 (diff) | |
download | postgresql-69a785b8bfe076847f72317a41964821e85ccfd6.tar.gz postgresql-69a785b8bfe076847f72317a41964821e85ccfd6.zip |
Implement SQL-spec RETURNS TABLE syntax for functions.
(Unlike the original submission, this patch treats TABLE output parameters
as being entirely equivalent to OUT parameters -- tgl)
Pavel Stehule
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/catalog/information_schema.sql | 3 | ||||
-rw-r--r-- | src/backend/catalog/pg_proc.c | 3 | ||||
-rw-r--r-- | src/backend/commands/functioncmds.c | 10 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 89 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 145 | ||||
-rw-r--r-- | src/backend/utils/fmgr/funcapi.c | 14 | ||||
-rw-r--r-- | src/bin/pg_dump/pg_dump.c | 96 | ||||
-rw-r--r-- | src/bin/psql/describe.c | 25 | ||||
-rw-r--r-- | src/include/catalog/catversion.h | 4 | ||||
-rw-r--r-- | src/include/catalog/pg_proc.h | 7 | ||||
-rw-r--r-- | src/include/nodes/parsenodes.h | 5 | ||||
-rw-r--r-- | src/include/utils/builtins.h | 4 | ||||
-rw-r--r-- | src/pl/plpgsql/src/pl_comp.c | 6 | ||||
-rw-r--r-- | src/pl/plpython/plpython.c | 9 | ||||
-rw-r--r-- | src/test/regress/expected/plpgsql.out | 32 | ||||
-rw-r--r-- | src/test/regress/expected/rangefuncs.out | 37 | ||||
-rw-r--r-- | src/test/regress/sql/plpgsql.sql | 23 | ||||
-rw-r--r-- | src/test/regress/sql/rangefuncs.sql | 18 |
18 files changed, 472 insertions, 58 deletions
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 0e2452fa0f6..b10a2e8ea69 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.44 2008/07/16 01:30:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.45 2008/07/18 03:32:52 tgl Exp $ */ /* @@ -1007,6 +1007,7 @@ CREATE VIEW parameters AS WHEN proargmodes[(ss.x).n] = 'o' THEN 'OUT' WHEN proargmodes[(ss.x).n] = 'b' THEN 'INOUT' WHEN proargmodes[(ss.x).n] = 'v' THEN 'IN' + WHEN proargmodes[(ss.x).n] = 't' THEN 'OUT' 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/pg_proc.c b/src/backend/catalog/pg_proc.c index 37e7ed4343e..e2513eb7dde 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.152 2008/07/16 16:55:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.153 2008/07/18 03:32:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -242,6 +242,7 @@ ProcedureCreate(const char *procedureName, elog(ERROR, "variadic parameter must be last"); break; case PROARGMODE_OUT: + case PROARGMODE_TABLE: /* okay */ break; case PROARGMODE_VARIADIC: diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index d03de8bff17..2b3723d6445 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.97 2008/07/16 16:55:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.98 2008/07/18 03:32:52 tgl Exp $ * * DESCRIPTION * These routines take the parse tree and pick out the @@ -228,9 +228,10 @@ examine_parameter_list(List *parameters, Oid languageOid, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("functions cannot accept set arguments"))); - if (fp->mode != FUNC_PARAM_OUT) + /* handle input parameters */ + if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE) { - /* only OUT parameters can follow a VARIADIC parameter */ + /* other input parameters can't follow a VARIADIC parameter */ if (varCount > 0) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), @@ -238,9 +239,10 @@ examine_parameter_list(List *parameters, Oid languageOid, inTypes[inCount++] = toid; } + /* handle output parameters */ if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC) { - if (outCount == 0) /* save first OUT param's type */ + if (outCount == 0) /* save first output param's type */ *requiredResultType = toid; outCount++; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 70bbe940afd..39e52099f43 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.617 2008/07/16 01:30:22 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.618 2008/07/18 03:32:52 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -112,6 +112,8 @@ static Node *doNegate(Node *n, int location); static void doNegateFloat(Value *v); static Node *makeAArrayExpr(List *elements); static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args); +static List *mergeTableFuncParameters(List *func_args, List *columns); +static TypeName *TableFuncTypeName(List *columns); %} @@ -253,13 +255,13 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) TableFuncElementList opt_type_modifiers prep_type_clause execute_param_clause using_clause returning_clause - enum_val_list + enum_val_list table_func_column_list %type <range> OptTempTableName %type <into> into_clause create_as_target %type <defelt> createfunc_opt_item common_func_opt_item -%type <fun_param> func_arg +%type <fun_param> func_arg table_func_column %type <fun_param_mode> arg_class %type <typnam> func_return func_type @@ -4119,6 +4121,19 @@ CreateFunctionStmt: $$ = (Node *)n; } | CREATE opt_or_replace FUNCTION func_name func_args + RETURNS TABLE '(' table_func_column_list ')' createfunc_opt_list opt_definition + { + CreateFunctionStmt *n = makeNode(CreateFunctionStmt); + n->replace = $2; + n->funcname = $4; + n->parameters = mergeTableFuncParameters($5, $9); + n->returnType = TableFuncTypeName($9); + n->returnType->location = @7; + n->options = $11; + n->withClause = $12; + $$ = (Node *)n; + } + | CREATE opt_or_replace FUNCTION func_name func_args createfunc_opt_list opt_definition { CreateFunctionStmt *n = makeNode(CreateFunctionStmt); @@ -4338,6 +4353,27 @@ opt_definition: | /*EMPTY*/ { $$ = NIL; } ; +table_func_column: param_name func_type + { + FunctionParameter *n = makeNode(FunctionParameter); + n->name = $1; + n->argType = $2; + n->mode = FUNC_PARAM_TABLE; + $$ = n; + } + ; + +table_func_column_list: + table_func_column + { + $$ = list_make1($1); + } + | table_func_column_list ',' table_func_column + { + $$ = lappend($1, $3); + } + ; + /***************************************************************************** * ALTER FUNCTION * @@ -9678,7 +9714,7 @@ extractArgTypes(List *parameters) { FunctionParameter *p = (FunctionParameter *) lfirst(i); - if (p->mode != FUNC_PARAM_OUT) /* keep if IN, INOUT, VARIADIC */ + if (p->mode != FUNC_PARAM_OUT && p->mode != FUNC_PARAM_TABLE) result = lappend(result, p->argType); } return result; @@ -9862,6 +9898,51 @@ parser_init(void) } /* + * Merge the input and output parameters of a table function. + */ +static List * +mergeTableFuncParameters(List *func_args, List *columns) +{ + ListCell *lc; + + /* Explicit OUT and INOUT parameters shouldn't be used in this syntax */ + foreach(lc, func_args) + { + FunctionParameter *p = (FunctionParameter *) lfirst(lc); + + if (p->mode != FUNC_PARAM_IN && p->mode != FUNC_PARAM_VARIADIC) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("OUT and INOUT arguments aren't allowed in TABLE functions"))); + } + + return list_concat(func_args, columns); +} + +/* + * Determine return type of a TABLE function. A single result column + * returns setof that column's type; otherwise return setof record. + */ +static TypeName * +TableFuncTypeName(List *columns) +{ + TypeName *result; + + if (list_length(columns) == 1) + { + FunctionParameter *p = (FunctionParameter *) linitial(columns); + + result = (TypeName *) copyObject(p->argType); + } + else + result = SystemTypeName("record"); + + result->setof = true; + + return result; +} + +/* * Must undefine base_yylex before including scan.c, since we want it * to create the function base_yylex not filtered_base_yylex. */ diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index c7f896c524a..b3603c53c16 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.277 2008/07/16 16:55:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.278 2008/07/18 03:32:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -135,6 +135,8 @@ static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags); static char *pg_get_expr_worker(text *expr, Oid relid, char *relname, int prettyFlags); +static int print_function_arguments(StringInfo buf, HeapTuple proctup, + bool print_table_args); static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, int prettyFlags); static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, @@ -1396,6 +1398,147 @@ pg_get_serial_sequence(PG_FUNCTION_ARGS) /* + * pg_get_function_arguments + * Get a nicely-formatted list of arguments for a function. + * This is everything that would go between the parentheses in + * CREATE FUNCTION. + */ +Datum +pg_get_function_arguments(PG_FUNCTION_ARGS) +{ + Oid funcid = PG_GETARG_OID(0); + StringInfoData buf; + HeapTuple proctup; + + initStringInfo(&buf); + + proctup = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); + if (!HeapTupleIsValid(proctup)) + elog(ERROR, "cache lookup failed for function %u", funcid); + + (void) print_function_arguments(&buf, proctup, false); + + ReleaseSysCache(proctup); + + PG_RETURN_TEXT_P(string_to_text(buf.data)); +} + +/* + * pg_get_function_result + * Get a nicely-formatted version of the result type of a function. + * This is what would appear after RETURNS in CREATE FUNCTION. + */ +Datum +pg_get_function_result(PG_FUNCTION_ARGS) +{ + Oid funcid = PG_GETARG_OID(0); + StringInfoData buf; + HeapTuple proctup; + Form_pg_proc procform; + int ntabargs = 0; + + initStringInfo(&buf); + + proctup = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); + if (!HeapTupleIsValid(proctup)) + elog(ERROR, "cache lookup failed for function %u", funcid); + procform = (Form_pg_proc) GETSTRUCT(proctup); + + if (procform->proretset) + { + /* It might be a table function; try to print the arguments */ + appendStringInfoString(&buf, "TABLE("); + ntabargs = print_function_arguments(&buf, proctup, true); + if (ntabargs > 0) + appendStringInfoString(&buf, ")"); + else + resetStringInfo(&buf); + } + + if (ntabargs == 0) + { + /* Not a table function, so do the normal thing */ + if (procform->proretset) + appendStringInfoString(&buf, "SETOF "); + appendStringInfoString(&buf, format_type_be(procform->prorettype)); + } + + ReleaseSysCache(proctup); + + PG_RETURN_TEXT_P(string_to_text(buf.data)); +} + +/* + * Common code for pg_get_function_arguments and pg_get_function_result: + * append the desired subset of arguments to buf. We print only TABLE + * arguments when print_table_args is true, and all the others when it's false. + * Function return value is the number of arguments printed. + */ +static int +print_function_arguments(StringInfo buf, HeapTuple proctup, + bool print_table_args) +{ + int numargs; + Oid *argtypes; + char **argnames; + char *argmodes; + int argsprinted; + int i; + + numargs = get_func_arg_info(proctup, + &argtypes, &argnames, &argmodes); + + argsprinted = 0; + for (i = 0; i < numargs; i++) + { + Oid argtype = argtypes[i]; + char *argname = argnames ? argnames[i] : NULL; + char argmode = argmodes ? argmodes[i] : PROARGMODE_IN; + const char *modename; + + if (print_table_args != (argmode == PROARGMODE_TABLE)) + continue; + + switch (argmode) + { + case PROARGMODE_IN: + modename = ""; + break; + case PROARGMODE_INOUT: + modename = "INOUT "; + break; + case PROARGMODE_OUT: + modename = "OUT "; + break; + case PROARGMODE_VARIADIC: + modename = "VARIADIC "; + break; + case PROARGMODE_TABLE: + modename = ""; + break; + default: + elog(ERROR, "invalid parameter mode '%c'", argmode); + modename = NULL; /* keep compiler quiet */ + break; + } + if (argsprinted) + appendStringInfoString(buf, ", "); + appendStringInfoString(buf, modename); + if (argname && argname[0]) + appendStringInfo(buf, "%s ", argname); + appendStringInfoString(buf, format_type_be(argtype)); + argsprinted++; + } + + return argsprinted; +} + + +/* * deparse_expression - General utility for deparsing expressions * * calls deparse_expression_pretty with all prettyPrinting disabled diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index 7cba375ee03..6ff1b90ffcd 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.40 2008/07/16 01:30:22 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.41 2008/07/18 03:32:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -550,7 +550,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: - if (argmode == PROARGMODE_OUT) + if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) have_anyelement_result = true; else { @@ -565,7 +565,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, } break; case ANYARRAYOID: - if (argmode == PROARGMODE_OUT) + if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) have_anyarray_result = true; else { @@ -582,7 +582,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, default: break; } - if (argmode != PROARGMODE_OUT) + if (argmode != PROARGMODE_OUT && argmode != PROARGMODE_TABLE) inargno++; } @@ -848,7 +848,8 @@ get_func_result_name(Oid functionId) argmodes[i] == PROARGMODE_VARIADIC) continue; Assert(argmodes[i] == PROARGMODE_OUT || - argmodes[i] == PROARGMODE_INOUT); + argmodes[i] == PROARGMODE_INOUT || + argmodes[i] == PROARGMODE_TABLE); if (++numoutargs > 1) { /* multiple out args, so forget it */ @@ -999,7 +1000,8 @@ build_function_result_tupdesc_d(Datum proallargtypes, argmodes[i] == PROARGMODE_VARIADIC) continue; Assert(argmodes[i] == PROARGMODE_OUT || - argmodes[i] == PROARGMODE_INOUT); + argmodes[i] == PROARGMODE_INOUT || + argmodes[i] == PROARGMODE_TABLE); outargtypes[numoutargs] = argtypes[i]; if (argnames) pname = TextDatumGetCString(argnames[i]); diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index c4602fd0bce..1f09df21099 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12,7 +12,7 @@ * by PostgreSQL * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.495 2008/07/16 16:55:23 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.496 2008/07/18 03:32:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -165,7 +165,8 @@ static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, static void getDependencies(void); static void getDomainConstraints(TypeInfo *tinfo); static void getTableData(TableInfo *tblinfo, int numTables, bool oids); -static char *format_function_arguments(FuncInfo *finfo, int nallargs, +static char *format_function_arguments(FuncInfo *finfo, char *funcargs); +static char *format_function_arguments_old(FuncInfo *finfo, int nallargs, char **allargtypes, char **argmodes, char **argnames); @@ -6405,16 +6406,34 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) /* * format_function_arguments: generate function name and argument list * + * This is used when we can rely on pg_get_function_arguments to format + * the argument list. + */ +static char *format_function_arguments(FuncInfo *finfo, char *funcargs) +{ + PQExpBufferData fn; + + initPQExpBuffer(&fn); + appendPQExpBuffer(&fn, "%s(%s)", fmtId(finfo->dobj.name), funcargs); + return fn.data; +} + +/* + * format_function_arguments_old: generate function name and argument list + * * The argument type names are qualified if needed. The function name * is never qualified. * + * This is used only with pre-8.4 servers, so we aren't expecting to see + * VARIADIC or TABLE arguments. + * * Any or all of allargtypes, argmodes, argnames may be NULL. */ static char * -format_function_arguments(FuncInfo *finfo, int nallargs, - char **allargtypes, - char **argmodes, - char **argnames) +format_function_arguments_old(FuncInfo *finfo, int nallargs, + char **allargtypes, + char **argmodes, + char **argnames) { PQExpBufferData fn; int j; @@ -6444,9 +6463,6 @@ format_function_arguments(FuncInfo *finfo, int nallargs, case PROARGMODE_INOUT: argmode = "INOUT "; break; - case PROARGMODE_VARIADIC: - argmode = "VARIADIC "; - break; default: write_msg(NULL, "WARNING: bogus value in proargmodes array\n"); argmode = ""; @@ -6475,7 +6491,7 @@ format_function_arguments(FuncInfo *finfo, int nallargs, /* * format_function_signature: generate function name and argument list * - * This is like format_function_arguments except that only a minimal + * This is like format_function_arguments_old except that only a minimal * list of input argument types is generated; this is sufficient to * reference the function, but not to define it. * @@ -6527,6 +6543,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) char *proretset; char *prosrc; char *probin; + char *funcargs; + char *funcresult; char *proallargtypes; char *proargmodes; char *proargnames; @@ -6559,7 +6577,24 @@ dumpFunc(Archive *fout, FuncInfo *finfo) selectSourceSchema(finfo->dobj.namespace->dobj.name); /* Fetch function-specific details */ - if (g_fout->remoteVersion >= 80300) + if (g_fout->remoteVersion >= 80400) + { + /* + * In 8.4 and up we rely on pg_get_function_arguments and + * pg_get_function_result instead of examining proallargtypes etc. + */ + appendPQExpBuffer(query, + "SELECT proretset, prosrc, probin, " + "pg_catalog.pg_get_function_arguments(oid) as funcargs, " + "pg_catalog.pg_get_function_result(oid) as funcresult, " + "provolatile, proisstrict, prosecdef, " + "proconfig, procost, prorows, " + "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 80300) { appendPQExpBuffer(query, "SELECT proretset, prosrc, probin, " @@ -6659,9 +6694,19 @@ dumpFunc(Archive *fout, FuncInfo *finfo) proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset")); prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc")); probin = PQgetvalue(res, 0, PQfnumber(res, "probin")); - proallargtypes = PQgetvalue(res, 0, PQfnumber(res, "proallargtypes")); - proargmodes = PQgetvalue(res, 0, PQfnumber(res, "proargmodes")); - proargnames = PQgetvalue(res, 0, PQfnumber(res, "proargnames")); + if (g_fout->remoteVersion >= 80400) + { + funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs")); + funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult")); + proallargtypes = proargmodes = proargnames = NULL; + } + else + { + proallargtypes = PQgetvalue(res, 0, PQfnumber(res, "proallargtypes")); + proargmodes = PQgetvalue(res, 0, PQfnumber(res, "proargmodes")); + proargnames = PQgetvalue(res, 0, PQfnumber(res, "proargnames")); + funcargs = funcresult = NULL; + } provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile")); proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict")); prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef")); @@ -6766,8 +6811,11 @@ dumpFunc(Archive *fout, FuncInfo *finfo) } } - funcsig = format_function_arguments(finfo, nallargs, allargtypes, - argmodes, argnames); + if (funcargs) + funcsig = format_function_arguments(finfo, funcargs); + else + funcsig = format_function_arguments_old(finfo, nallargs, allargtypes, + argmodes, argnames); funcsig_tag = format_function_signature(finfo, false); /* @@ -6777,13 +6825,17 @@ dumpFunc(Archive *fout, FuncInfo *finfo) fmtId(finfo->dobj.namespace->dobj.name), funcsig); - rettypename = getFormattedTypeName(finfo->prorettype, zeroAsOpaque); - appendPQExpBuffer(q, "CREATE FUNCTION %s ", funcsig); - appendPQExpBuffer(q, "RETURNS %s%s", - (proretset[0] == 't') ? "SETOF " : "", - rettypename); - free(rettypename); + if (funcresult) + appendPQExpBuffer(q, "RETURNS %s", funcresult); + else + { + rettypename = getFormattedTypeName(finfo->prorettype, zeroAsOpaque); + appendPQExpBuffer(q, "RETURNS %s%s", + (proretset[0] == 't') ? "SETOF " : "", + rettypename); + free(rettypename); + } appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname)); if (provolatile[0] != PROVOLATILE_VOLATILE) diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index f8f6a657d21..0a0819cc029 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -8,7 +8,7 @@ * * Copyright (c) 2000-2008, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.182 2008/07/16 01:30:23 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.183 2008/07/18 03:32:52 tgl Exp $ */ #include "postgres_fe.h" @@ -189,15 +189,20 @@ describeFunctions(const char *pattern, bool verbose) printfPQExpBuffer(&buf, "SELECT n.nspname as \"%s\",\n" - " p.proname as \"%s\",\n" - " CASE WHEN p.proretset THEN 'setof ' ELSE '' END ||\n" - " pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n", + " p.proname as \"%s\",\n", gettext_noop("Schema"), - gettext_noop("Name"), - gettext_noop("Result data type")); + gettext_noop("Name")); - if (pset.sversion >= 80100) + if (pset.sversion >= 80400) appendPQExpBuffer(&buf, + " pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n" + " pg_catalog.pg_get_function_arguments(p.oid) as \"%s\"", + gettext_noop("Result data type"), + gettext_noop("Argument data types")); + else if (pset.sversion >= 80100) + appendPQExpBuffer(&buf, + " CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n" + " pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n" " CASE WHEN proallargtypes IS NOT NULL THEN\n" " pg_catalog.array_to_string(ARRAY(\n" " SELECT\n" @@ -227,10 +232,14 @@ describeFunctions(const char *pattern, bool verbose) " pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n" " ), ', ')\n" " END AS \"%s\"", - gettext_noop("Argument data types")); + gettext_noop("Result data type"), + gettext_noop("Argument data types")); else appendPQExpBuffer(&buf, + " CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n" + " pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n" " pg_catalog.oidvectortypes(p.proargtypes) as \"%s\"", + gettext_noop("Result data type"), gettext_noop("Argument data types")); if (verbose) diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 7581562a480..6fadd8b33e5 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.470 2008/07/16 16:55:23 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.471 2008/07/18 03:32:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200807161 +#define CATALOG_VERSION_NO 200807171 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e6931eca861..c516af8a164 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.508 2008/07/16 16:55:24 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.509 2008/07/18 03:32:53 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -2291,6 +2291,10 @@ DATA(insert OID = 1716 ( pg_get_expr PGNSP PGUID 12 1 0 0 f f t f s 2 25 "2 DESCR("deparse an encoded expression"); DATA(insert OID = 1665 ( pg_get_serial_sequence PGNSP PGUID 12 1 0 0 f f t f s 2 25 "25 25" _null_ _null_ _null_ pg_get_serial_sequence _null_ _null_ _null_ )); DESCR("name of sequence for a serial column"); +DATA(insert OID = 2162 ( pg_get_function_arguments PGNSP PGUID 12 1 0 0 f f t f s 1 25 "26" _null_ _null_ _null_ pg_get_function_arguments _null_ _null_ _null_ )); +DESCR("argument list of a function"); +DATA(insert OID = 2165 ( pg_get_function_result PGNSP PGUID 12 1 0 0 f f t f s 1 25 "26" _null_ _null_ _null_ pg_get_function_result _null_ _null_ _null_ )); +DESCR("result type of a function"); DATA(insert OID = 1686 ( pg_get_keywords PGNSP PGUID 12 10 400 0 f f t t s 0 2249 "" "{25,18,25}" "{o,o,o}" "{word,catcode,catdesc}" pg_get_keywords _null_ _null_ _null_ )); DESCR("list of SQL keywords"); @@ -4477,5 +4481,6 @@ DESCR("is txid visible in snapshot?"); #define PROARGMODE_OUT 'o' #define PROARGMODE_INOUT 'b' #define PROARGMODE_VARIADIC 'v' +#define PROARGMODE_TABLE 't' #endif /* PG_PROC_H */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 561cc9129db..e6de4b49c85 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.367 2008/07/16 01:30:23 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.368 2008/07/18 03:32:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1570,7 +1570,8 @@ typedef enum FunctionParameterMode FUNC_PARAM_IN = 'i', /* input only */ FUNC_PARAM_OUT = 'o', /* output only */ FUNC_PARAM_INOUT = 'b', /* both */ - FUNC_PARAM_VARIADIC = 'v' /* variadic */ + FUNC_PARAM_VARIADIC = 'v', /* variadic */ + FUNC_PARAM_TABLE = 't' /* table function output column */ } FunctionParameterMode; typedef struct FunctionParameter diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2abbe667891..fbe5a119cb9 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.318 2008/07/03 20:58:47 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.319 2008/07/18 03:32:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -551,6 +551,8 @@ extern Datum pg_get_expr(PG_FUNCTION_ARGS); extern Datum pg_get_expr_ext(PG_FUNCTION_ARGS); extern Datum pg_get_userbyid(PG_FUNCTION_ARGS); extern Datum pg_get_serial_sequence(PG_FUNCTION_ARGS); +extern Datum pg_get_function_arguments(PG_FUNCTION_ARGS); +extern Datum pg_get_function_result(PG_FUNCTION_ARGS); extern char *deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit); extern List *deparse_context_for(const char *aliasname, Oid relid); diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 1b2cba38815..b7b4eed9705 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.127 2008/07/16 01:30:23 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.128 2008/07/18 03:32:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -441,7 +441,9 @@ do_compile(FunctionCallInfo fcinfo, argmode == PROARGMODE_INOUT || argmode == PROARGMODE_VARIADIC) in_arg_varnos[num_in_args++] = argvariable->dno; - if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_INOUT) + if (argmode == PROARGMODE_OUT || + argmode == PROARGMODE_INOUT || + argmode == PROARGMODE_TABLE) out_arg_variables[num_out_args++] = argvariable; /* Add to namespace under the $n name */ diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c index 8e85c6707e2..c4d6803fcf4 100644 --- a/src/pl/plpython/plpython.c +++ b/src/pl/plpython/plpython.c @@ -1,7 +1,7 @@ /********************************************************************** * plpython.c - python as a procedural language for PostgreSQL * - * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.111 2008/07/16 01:30:23 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.112 2008/07/18 03:32:53 tgl Exp $ * ********************************************************************* */ @@ -1271,7 +1271,8 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key) /* proc->nargs was initialized to 0 above */ for (i = 0; i < total; i++) { - if (modes[i] != PROARGMODE_OUT) + if (modes[i] != PROARGMODE_OUT && + modes[i] != PROARGMODE_TABLE) (proc->nargs)++; } } @@ -1282,7 +1283,9 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key) HeapTuple argTypeTup; Form_pg_type argTypeStruct; - if (modes && modes[i] == PROARGMODE_OUT) + if (modes && + (modes[i] == PROARGMODE_OUT || + modes[i] == PROARGMODE_TABLE)) continue; /* skip OUT arguments */ Assert(types[i] == procStruct->proargtypes.values[pos]); diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index 1e4ef2645fa..94a485f46b3 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -3634,3 +3634,35 @@ NOTICE: non-variadic function called drop function pleast(numeric[]); drop function pleast(numeric); +-- test table functions +create function tftest(int) returns table(a int, b int) as $$ +begin + return query select $1, $1+i from generate_series(1,5) g(i); +end; +$$ language plpgsql immutable strict; +select * from tftest(10); + a | b +----+---- + 10 | 11 + 10 | 12 + 10 | 13 + 10 | 14 + 10 | 15 +(5 rows) + +create or replace function tftest(a1 int) returns table(a int, b int) as $$ +begin + a := a1; b := a1 + 1; + return next; + a := a1 * 10; b := a1 * 10 + 1; + return next; +end; +$$ language plpgsql immutable strict; +select * from tftest(10); + a | b +-----+----- + 10 | 11 + 100 | 101 +(2 rows) + +drop function tftest(int); diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 957bd49f246..90111553d9b 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -528,3 +528,40 @@ CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray) AS 'select $1, array[$1,$1]' LANGUAGE sql; ERROR: cannot determine result data type DETAIL: A function returning a polymorphic type must have at least one polymorphic argument. +-- +-- table functions +-- +CREATE OR REPLACE FUNCTION foo() +RETURNS TABLE(a int) +AS $$ SELECT a FROM generate_series(1,5) a(a) $$ LANGUAGE sql; +SELECT * FROM foo(); + a +--- + 1 + 2 + 3 + 4 + 5 +(5 rows) + +DROP FUNCTION foo(); +CREATE OR REPLACE FUNCTION foo(int) +RETURNS TABLE(a int, b int) +AS $$ SELECT a, b + FROM generate_series(1,$1) a(a), + generate_series(1,$1) b(b) $$ LANGUAGE sql; +SELECT * FROM foo(3); + a | b +---+--- + 1 | 1 + 1 | 2 + 1 | 3 + 2 | 1 + 2 | 2 + 2 | 3 + 3 | 1 + 3 | 2 + 3 | 3 +(9 rows) + +DROP FUNCTION foo(int); diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index 4d45dba2efc..9ebe2b5f332 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -2925,3 +2925,26 @@ select pleast(10); drop function pleast(numeric[]); drop function pleast(numeric); + +-- test table functions + +create function tftest(int) returns table(a int, b int) as $$ +begin + return query select $1, $1+i from generate_series(1,5) g(i); +end; +$$ language plpgsql immutable strict; + +select * from tftest(10); + +create or replace function tftest(a1 int) returns table(a int, b int) as $$ +begin + a := a1; b := a1 + 1; + return next; + a := a1 * 10; b := a1 * 10 + 1; + return next; +end; +$$ language plpgsql immutable strict; + +select * from tftest(10); + +drop function tftest(int); diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql index c56e35ded4e..435a836c66d 100644 --- a/src/test/regress/sql/rangefuncs.sql +++ b/src/test/regress/sql/rangefuncs.sql @@ -261,3 +261,21 @@ DROP FUNCTION dup(anyelement); -- fails, no way to deduce outputs CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray) AS 'select $1, array[$1,$1]' LANGUAGE sql; + +-- +-- table functions +-- + +CREATE OR REPLACE FUNCTION foo() +RETURNS TABLE(a int) +AS $$ SELECT a FROM generate_series(1,5) a(a) $$ LANGUAGE sql; +SELECT * FROM foo(); +DROP FUNCTION foo(); + +CREATE OR REPLACE FUNCTION foo(int) +RETURNS TABLE(a int, b int) +AS $$ SELECT a, b + FROM generate_series(1,$1) a(a), + generate_series(1,$1) b(b) $$ LANGUAGE sql; +SELECT * FROM foo(3); +DROP FUNCTION foo(int); |