1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-30 11:03:19 +03:00

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
This commit is contained in:
Tom Lane
2008-07-18 03:32:53 +00:00
parent a8fb90cf2d
commit 69a785b8bf
23 changed files with 626 additions and 85 deletions

View File

@ -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,

View File

@ -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:

View File

@ -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++;
}

View File

@ -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
@ -4118,6 +4120,19 @@ CreateFunctionStmt:
n->withClause = $9;
$$ = (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
{
@ -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;
@ -9861,6 +9897,51 @@ parser_init(void)
QueryIsRule = FALSE;
}
/*
* 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.

View File

@ -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,
@ -1395,6 +1397,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
*

View File

@ -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]);

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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);

View File

@ -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 */

View File

@ -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]);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);