aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2008-07-18 03:32:53 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2008-07-18 03:32:53 +0000
commit69a785b8bfe076847f72317a41964821e85ccfd6 (patch)
tree8089a0c1e3b1075d81f49a82ab73b443dbe7d564 /src
parenta8fb90cf2db614f3c1d4331bfaafd9a1953148e9 (diff)
downloadpostgresql-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.sql3
-rw-r--r--src/backend/catalog/pg_proc.c3
-rw-r--r--src/backend/commands/functioncmds.c10
-rw-r--r--src/backend/parser/gram.y89
-rw-r--r--src/backend/utils/adt/ruleutils.c145
-rw-r--r--src/backend/utils/fmgr/funcapi.c14
-rw-r--r--src/bin/pg_dump/pg_dump.c96
-rw-r--r--src/bin/psql/describe.c25
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/catalog/pg_proc.h7
-rw-r--r--src/include/nodes/parsenodes.h5
-rw-r--r--src/include/utils/builtins.h4
-rw-r--r--src/pl/plpgsql/src/pl_comp.c6
-rw-r--r--src/pl/plpython/plpython.c9
-rw-r--r--src/test/regress/expected/plpgsql.out32
-rw-r--r--src/test/regress/expected/rangefuncs.out37
-rw-r--r--src/test/regress/sql/plpgsql.sql23
-rw-r--r--src/test/regress/sql/rangefuncs.sql18
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);