Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
Postgres FD Implementation
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Abuhujair Javed
Postgres FD Implementation
Commits
fd97cf4d
Commit
fd97cf4d
authored
Apr 05, 2005
by
Tom Lane
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
plpgsql does OUT parameters, as per my proposal a few weeks ago.
parent
2af664e7
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
958 additions
and
359 deletions
+958
-359
doc/src/sgml/plpgsql.sgml
doc/src/sgml/plpgsql.sgml
+119
-21
src/backend/utils/fmgr/funcapi.c
src/backend/utils/fmgr/funcapi.c
+103
-1
src/include/funcapi.h
src/include/funcapi.h
+5
-1
src/pl/plpgsql/src/gram.y
src/pl/plpgsql/src/gram.y
+39
-21
src/pl/plpgsql/src/pl_comp.c
src/pl/plpgsql/src/pl_comp.c
+321
-137
src/pl/plpgsql/src/pl_exec.c
src/pl/plpgsql/src/pl_exec.c
+154
-160
src/pl/plpgsql/src/pl_funcs.c
src/pl/plpgsql/src/pl_funcs.c
+10
-12
src/pl/plpgsql/src/plpgsql.h
src/pl/plpgsql/src/plpgsql.h
+4
-5
src/test/regress/expected/plpgsql.out
src/test/regress/expected/plpgsql.out
+119
-0
src/test/regress/sql/plpgsql.sql
src/test/regress/sql/plpgsql.sql
+84
-1
No files found.
doc/src/sgml/plpgsql.sgml
View file @
fd97cf4d
<!--
<!--
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.6
2 2005/03/13 09:36:30 neilc
Exp $
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.6
3 2005/04/05 06:22:14 tgl
Exp $
-->
-->
<chapter id="plpgsql">
<chapter id="plpgsql">
...
@@ -83,7 +83,7 @@ $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.62 2005/03/13 09:36:30 neilc Ex
...
@@ -83,7 +83,7 @@ $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.62 2005/03/13 09:36:30 neilc Ex
that contains many statements for which execution plans might be
that contains many statements for which execution plans might be
required will only prepare and save those plans that are really
required will only prepare and save those plans that are really
used during the lifetime of the database connection. This can
used during the lifetime of the database connection. This can
substantially reduce the total amount of time required to parse
,
substantially reduce the total amount of time required to parse
and generate execution plans for the statements in a
and generate execution plans for the statements in a
<application>PL/pgSQL</> function. A disadvantage is that errors
<application>PL/pgSQL</> function. A disadvantage is that errors
in a specific expression or command may not be detected until that
in a specific expression or command may not be detected until that
...
@@ -215,6 +215,7 @@ $$ LANGUAGE plpgsql;
...
@@ -215,6 +215,7 @@ $$ LANGUAGE plpgsql;
<type>void</> if it has no useful return value.
<type>void</> if it has no useful return value.
</para>
</para>
<note>
<para>
<para>
<application>PL/pgSQL</> does not currently have full support for
<application>PL/pgSQL</> does not currently have full support for
domain types: it treats a domain the same as the underlying scalar
domain types: it treats a domain the same as the underlying scalar
...
@@ -223,6 +224,20 @@ $$ LANGUAGE plpgsql;
...
@@ -223,6 +224,20 @@ $$ LANGUAGE plpgsql;
it is a hazard if you declare a <application>PL/pgSQL</> function
it is a hazard if you declare a <application>PL/pgSQL</> function
as returning a domain type.
as returning a domain type.
</para>
</para>
</note>
<para>
<application>PL/pgSQL</> functions can also be declared with output
parameters in place of an explicit specification of the return type.
This does not add any fundamental capability to the language, but
it is often convenient, especially for returning multiple values.
</para>
<para>
Specific examples appear in
<xref linkend="plpgsql-declaration-aliases"> and
<xref linkend="plpgsql-statements-returning">.
</para>
</sect2>
</sect2>
</sect1>
</sect1>
...
@@ -631,12 +646,12 @@ DECLARE
...
@@ -631,12 +646,12 @@ DECLARE
v_string ALIAS FOR $1;
v_string ALIAS FOR $1;
index ALIAS FOR $2;
index ALIAS FOR $2;
BEGIN
BEGIN
-- some computations here
-- some computations
using v_string and index
here
END;
END;
$$ LANGUAGE plpgsql;
$$ LANGUAGE plpgsql;
CREATE FUNCTION concat_selected_fields(in_t tablename) RETURNS text AS $$
CREATE FUNCTION concat_selected_fields(in_t
some
tablename) RETURNS text AS $$
BEGIN
BEGIN
RETURN in_t.f1 || in_t.f3 || in_t.f5 || in_t.f7;
RETURN in_t.f1 || in_t.f3 || in_t.f5 || in_t.f7;
END;
END;
...
@@ -644,6 +659,49 @@ $$ LANGUAGE plpgsql;
...
@@ -644,6 +659,49 @@ $$ LANGUAGE plpgsql;
</programlisting>
</programlisting>
</para>
</para>
<para>
When a <application>PL/pgSQL</application> function is declared
with output parameters, the output parameters are given
<literal>$<replaceable>n</replaceable></literal> names and optional
aliases in just the same way as the normal input parameters. An
output parameter is effectively a variable that starts out NULL;
it should be assigned to during the execution of the function.
The final value of the parameter is what is returned. For instance,
the sales-tax example could also be done this way:
<programlisting>
CREATE FUNCTION sales_tax(subtotal real, OUT tax real) AS $$
BEGIN
tax := subtotal * 0.06;
RETURN;
END;
$$ LANGUAGE plpgsql;
</programlisting>
Notice that we omitted <literal>RETURNS real</> — we could have
included it, but it would be redundant.
</para>
<para>
Output parameters are most useful when returning multiple values.
A trivial example is:
<programlisting>
CREATE FUNCTION sum_n_product(x int, y int, OUT sum int, OUT prod int) AS $$
BEGIN
sum := x + y;
prod := x * y;
RETURN;
END;
$$ LANGUAGE plpgsql;
</programlisting>
As discussed in <xref linkend="xfunc-output-parameters">, this
effectively creates an anonymous record type for the function's
results. If a <literal>RETURNS</> clause is given, it must say
<literal>RETURNS record</>.
</para>
<para>
<para>
When the return type of a <application>PL/pgSQL</application>
When the return type of a <application>PL/pgSQL</application>
function is declared as a polymorphic type (<type>anyelement</type>
function is declared as a polymorphic type (<type>anyelement</type>
...
@@ -658,6 +716,7 @@ $$ LANGUAGE plpgsql;
...
@@ -658,6 +716,7 @@ $$ LANGUAGE plpgsql;
though that is not required. <literal>$0</literal> can also be
though that is not required. <literal>$0</literal> can also be
given an alias. For example, this function works on any data type
given an alias. For example, this function works on any data type
that has a <literal>+</> operator:
that has a <literal>+</> operator:
<programlisting>
<programlisting>
CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement)
CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement)
RETURNS anyelement AS $$
RETURNS anyelement AS $$
...
@@ -668,6 +727,24 @@ BEGIN
...
@@ -668,6 +727,24 @@ BEGIN
RETURN result;
RETURN result;
END;
END;
$$ LANGUAGE plpgsql;
$$ LANGUAGE plpgsql;
</programlisting>
</para>
<para>
The same effect can be had by declaring one or more output parameters as
<type>anyelement</type> or <type>anyarray</type>. In this case the
special <literal>$0</literal> parameter is not used; the output
parameters themselves serve the same purpose. For example:
<programlisting>
CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement,
OUT sum anyelement)
AS $$
BEGIN
sum := v1 + v2 + v3;
RETURN;
END;
$$ LANGUAGE plpgsql;
</programlisting>
</programlisting>
</para>
</para>
</sect2>
</sect2>
...
@@ -756,18 +833,21 @@ user_id users.user_id%TYPE;
...
@@ -756,18 +833,21 @@ user_id users.user_id%TYPE;
</para>
</para>
<para>
<para>
Here is an example of using composite types:
Here is an example of using composite types. <structname>table1</>
and <structname>table2</> are existing tables having at least the
mentioned fields:
<programlisting>
<programlisting>
CREATE FUNCTION merge_fields(t_row table
name
) RETURNS text AS $$
CREATE FUNCTION merge_fields(t_row table
1
) RETURNS text AS $$
DECLARE
DECLARE
t2_row table2
name
%ROWTYPE;
t2_row table2%ROWTYPE;
BEGIN
BEGIN
SELECT * INTO t2_row FROM table2
name
WHERE ... ;
SELECT * INTO t2_row FROM table2 WHERE ... ;
RETURN t_row.f1 || t2_row.f3 || t_row.f5 || t2_row.f7;
RETURN t_row.f1 || t2_row.f3 || t_row.f5 || t2_row.f7;
END;
END;
$$ LANGUAGE plpgsql;
$$ LANGUAGE plpgsql;
SELECT merge_fields(t.*) FROM table
name
t WHERE ... ;
SELECT merge_fields(t.*) FROM table
1
t WHERE ... ;
</programlisting>
</programlisting>
</para>
</para>
</sect2>
</sect2>
...
@@ -1411,6 +1491,12 @@ RETURN <replaceable>expression</replaceable>;
...
@@ -1411,6 +1491,12 @@ RETURN <replaceable>expression</replaceable>;
as the <replaceable>expression</replaceable>.
as the <replaceable>expression</replaceable>.
</para>
</para>
<para>
If you declared the function with output parameters, write just
<command>RETURN</command> with no expression. The current values
of the output parameter variables will be returned.
</para>
<para>
<para>
The return value of a function cannot be left undefined. If
The return value of a function cannot be left undefined. If
control reaches the end of the top-level block of the function
control reaches the end of the top-level block of the function
...
@@ -1441,8 +1527,30 @@ RETURN NEXT <replaceable>expression</replaceable>;
...
@@ -1441,8 +1527,30 @@ RETURN NEXT <replaceable>expression</replaceable>;
commands, and then a final <command>RETURN</command> command
commands, and then a final <command>RETURN</command> command
with no argument is used to indicate that the function has
with no argument is used to indicate that the function has
finished executing. <command>RETURN NEXT</command> can be used
finished executing. <command>RETURN NEXT</command> can be used
with both scalar and composite data types; in the latter case, an
with both scalar and composite data types; with a composite result
entire <quote>table</quote> of results will be returned.
type, an entire <quote>table</quote> of results will be returned.
</para>
<para>
<command>RETURN NEXT</command> does not actually return from the
function — it simply saves away the value of the expression.
Execution then continues with the next statement in
the <application>PL/pgSQL</> function. As successive
<command>RETURN NEXT</command> commands are executed, the result
set is built up. A final <command>RETURN</command>, which should
have no argument, causes control to exit the function.
</para>
<para>
If you declared the function with output parameters, write just
<command>RETURN NEXT</command> with no expression. The current values
of the output parameter variable(s) will be saved for eventual return.
Note that you must declare the function as returning
<literal>SETOF record</literal> when there are
multiple output parameters, or
<literal>SETOF <replaceable>sometype</></literal> when there is
just one output parameter of type <replaceable>sometype</>, in
order to create a set-returning function with output parameters.
</para>
</para>
<para>
<para>
...
@@ -1457,16 +1565,6 @@ SELECT * FROM some_func();
...
@@ -1457,16 +1565,6 @@ SELECT * FROM some_func();
<literal>FROM</literal> clause.
<literal>FROM</literal> clause.
</para>
</para>
<para>
<command>RETURN NEXT</command> does not actually return from the
function; it simply saves away the value of the expression.
Execution then continues with the next statement in
the <application>PL/pgSQL</> function. As successive
<command>RETURN NEXT</command> commands are executed, the result
set is built up. A final <command>RETURN</command>, which should
have no argument, causes control to exit the function.
</para>
<note>
<note>
<para>
<para>
The current implementation of <command>RETURN NEXT</command>
The current implementation of <command>RETURN NEXT</command>
...
...
src/backend/utils/fmgr/funcapi.c
View file @
fd97cf4d
...
@@ -7,7 +7,7 @@
...
@@ -7,7 +7,7 @@
* Copyright (c) 2002-2005, PostgreSQL Global Development Group
* Copyright (c) 2002-2005, PostgreSQL Global Development Group
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.
19 2005/03/31 22:46:16
tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.
20 2005/04/05 06:22:14
tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -483,6 +483,108 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
...
@@ -483,6 +483,108 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return
true
;
return
true
;
}
}
/*
* Given the declared argument types and modes for a function,
* replace any polymorphic types (ANYELEMENT/ANYARRAY) with correct data
* types deduced from the input arguments. Returns TRUE if able to deduce
* all types, FALSE if not. This is the same logic as
* resolve_polymorphic_tupdesc, but with a different argument representation.
*
* argmodes may be NULL, in which case all arguments are assumed to be IN mode.
*/
bool
resolve_polymorphic_argtypes
(
int
numargs
,
Oid
*
argtypes
,
char
*
argmodes
,
Node
*
call_expr
)
{
bool
have_anyelement_result
=
false
;
bool
have_anyarray_result
=
false
;
Oid
anyelement_type
=
InvalidOid
;
Oid
anyarray_type
=
InvalidOid
;
int
inargno
;
int
i
;
/* First pass: resolve polymorphic inputs, check for outputs */
inargno
=
0
;
for
(
i
=
0
;
i
<
numargs
;
i
++
)
{
char
argmode
=
argmodes
?
argmodes
[
i
]
:
PROARGMODE_IN
;
switch
(
argtypes
[
i
])
{
case
ANYELEMENTOID
:
if
(
argmode
==
PROARGMODE_OUT
)
have_anyelement_result
=
true
;
else
{
if
(
!
OidIsValid
(
anyelement_type
))
{
anyelement_type
=
get_call_expr_argtype
(
call_expr
,
inargno
);
if
(
!
OidIsValid
(
anyelement_type
))
return
false
;
}
argtypes
[
i
]
=
anyelement_type
;
}
break
;
case
ANYARRAYOID
:
if
(
argmode
==
PROARGMODE_OUT
)
have_anyarray_result
=
true
;
else
{
if
(
!
OidIsValid
(
anyarray_type
))
{
anyarray_type
=
get_call_expr_argtype
(
call_expr
,
inargno
);
if
(
!
OidIsValid
(
anyarray_type
))
return
false
;
}
argtypes
[
i
]
=
anyarray_type
;
}
break
;
default:
break
;
}
if
(
argmode
!=
PROARGMODE_OUT
)
inargno
++
;
}
/* Done? */
if
(
!
have_anyelement_result
&&
!
have_anyarray_result
)
return
true
;
/* If no input polymorphics, parser messed up */
if
(
!
OidIsValid
(
anyelement_type
)
&&
!
OidIsValid
(
anyarray_type
))
return
false
;
/* If needed, deduce one polymorphic type from the other */
if
(
have_anyelement_result
&&
!
OidIsValid
(
anyelement_type
))
anyelement_type
=
resolve_generic_type
(
ANYELEMENTOID
,
anyarray_type
,
ANYARRAYOID
);
if
(
have_anyarray_result
&&
!
OidIsValid
(
anyarray_type
))
anyarray_type
=
resolve_generic_type
(
ANYARRAYOID
,
anyelement_type
,
ANYELEMENTOID
);
/* And finally replace the output column types as needed */
for
(
i
=
0
;
i
<
numargs
;
i
++
)
{
switch
(
argtypes
[
i
])
{
case
ANYELEMENTOID
:
argtypes
[
i
]
=
anyelement_type
;
break
;
case
ANYARRAYOID
:
argtypes
[
i
]
=
anyarray_type
;
break
;
default:
break
;
}
}
return
true
;
}
/*
/*
* get_type_func_class
* get_type_func_class
* Given the type OID, obtain its TYPEFUNC classification.
* Given the type OID, obtain its TYPEFUNC classification.
...
...
src/include/funcapi.h
View file @
fd97cf4d
...
@@ -9,7 +9,7 @@
...
@@ -9,7 +9,7 @@
*
*
* Copyright (c) 2002-2005, PostgreSQL Global Development Group
* Copyright (c) 2002-2005, PostgreSQL Global Development Group
*
*
* $PostgreSQL: pgsql/src/include/funcapi.h,v 1.1
6 2005/03/31 22:46:24
tgl Exp $
* $PostgreSQL: pgsql/src/include/funcapi.h,v 1.1
7 2005/04/05 06:22:15
tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -167,6 +167,10 @@ extern TypeFuncClass get_func_result_type(Oid functionId,
...
@@ -167,6 +167,10 @@ extern TypeFuncClass get_func_result_type(Oid functionId,
Oid
*
resultTypeId
,
Oid
*
resultTypeId
,
TupleDesc
*
resultTupleDesc
);
TupleDesc
*
resultTupleDesc
);
extern
bool
resolve_polymorphic_argtypes
(
int
numargs
,
Oid
*
argtypes
,
char
*
argmodes
,
Node
*
call_expr
);
extern
TupleDesc
build_function_result_tupdesc_d
(
Datum
proallargtypes
,
extern
TupleDesc
build_function_result_tupdesc_d
(
Datum
proallargtypes
,
Datum
proargmodes
,
Datum
proargmodes
,
Datum
proargnames
);
Datum
proargnames
);
...
...
src/pl/plpgsql/src/gram.y
View file @
fd97cf4d
...
@@ -4,7 +4,7 @@
...
@@ -4,7 +4,7 @@
* procedural language
* procedural language
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.6
6 2005/02/22 07:18:24 neilc
Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.6
7 2005/04/05 06:22:16 tgl
Exp $
*
*
* This software is copyrighted by Jan Wieck - Hamburg.
* This software is copyrighted by Jan Wieck - Hamburg.
*
*
...
@@ -1052,28 +1052,41 @@ stmt_return : K_RETURN lno
...
@@ -1052,28 +1052,41 @@ stmt_return : K_RETURN lno
PLpgSQL_stmt_return *new;
PLpgSQL_stmt_return *new;
new = palloc0(sizeof(PLpgSQL_stmt_return));
new = palloc0(sizeof(PLpgSQL_stmt_return));
new->cmd_type = PLPGSQL_STMT_RETURN;
new->lineno = $2;
new->expr = NULL;
new->expr = NULL;
new->retrecno = -1;
new->retvarno = -1;
new->retrowno = -1;
if (plpgsql_curr_compile->fn_retset)
if (plpgsql_curr_compile->fn_retset)
{
{
if (yylex() != ';')
if (yylex() != ';')
yyerror("RETURN cannot have a parameter in function returning set; use RETURN NEXT");
yyerror("RETURN cannot have a parameter in function returning set; use RETURN NEXT");
}
}
else if (plpgsql_curr_compile->out_param_varno >= 0)
{
if (yylex() != ';')
yyerror("RETURN cannot have a parameter in function with OUT parameters");
new->retvarno = plpgsql_curr_compile->out_param_varno;
}
else if (plpgsql_curr_compile->fn_rettype == VOIDOID)
{
if (yylex() != ';')
yyerror("function returning void cannot specify RETURN expression");
}
else if (plpgsql_curr_compile->fn_retistuple)
else if (plpgsql_curr_compile->fn_retistuple)
{
{
switch (yylex())
switch (yylex())
{
{
case K_NULL:
case K_NULL:
/* we allow this to support RETURN NULL in triggers */
break;
break;
case T_ROW:
case T_ROW:
new->ret
row
no = yylval.row->rowno;
new->ret
var
no = yylval.row->rowno;
break;
break;
case T_RECORD:
case T_RECORD:
new->ret
rec
no = yylval.rec->recno;
new->ret
var
no = yylval.rec->recno;
break;
break;
default:
default:
...
@@ -1083,11 +1096,6 @@ stmt_return : K_RETURN lno
...
@@ -1083,11 +1096,6 @@ stmt_return : K_RETURN lno
if (yylex() != ';')
if (yylex() != ';')
yyerror("RETURN must specify a record or row variable in function returning tuple");
yyerror("RETURN must specify a record or row variable in function returning tuple");
}
}
else if (plpgsql_curr_compile->fn_rettype == VOIDOID)
{
if (yylex() != ';')
yyerror("function returning void cannot specify RETURN expression");
}
else
else
{
{
/*
/*
...
@@ -1098,9 +1106,6 @@ stmt_return : K_RETURN lno
...
@@ -1098,9 +1106,6 @@ stmt_return : K_RETURN lno
new->expr = plpgsql_read_expression(';', ";");
new->expr = plpgsql_read_expression(';', ";");
}
}
new->cmd_type = PLPGSQL_STMT_RETURN;
new->lineno = $2;
$$ = (PLpgSQL_stmt *)new;
$$ = (PLpgSQL_stmt *)new;
}
}
;
;
...
@@ -1115,18 +1120,31 @@ stmt_return_next: K_RETURN_NEXT lno
...
@@ -1115,18 +1120,31 @@ stmt_return_next: K_RETURN_NEXT lno
new = palloc0(sizeof(PLpgSQL_stmt_return_next));
new = palloc0(sizeof(PLpgSQL_stmt_return_next));
new->cmd_type = PLPGSQL_STMT_RETURN_NEXT;
new->cmd_type = PLPGSQL_STMT_RETURN_NEXT;
new->lineno = $2;
new->lineno = $2;
new->expr = NULL;
new->retvarno = -1;
if (plpgsql_curr_compile->
fn_retistuple
)
if (plpgsql_curr_compile->
out_param_varno >= 0
)
{
{
int tok = yylex();
if (yylex() != ';')
yyerror("RETURN NEXT cannot have a parameter in function with OUT parameters");
new->retvarno = plpgsql_curr_compile->out_param_varno;
}
else if (plpgsql_curr_compile->fn_retistuple)
{
switch (yylex())
{
case T_ROW:
new->retvarno = yylval.row->rowno;
break;
if (tok == T_RECORD)
case T_RECORD:
new->rec = yylval.rec;
new->retvarno = yylval.rec->recno;
else if (tok == T_ROW)
break;
new->row = yylval.row;
else
yyerror("RETURN NEXT must specify a record or row variable in function returning tuple");
default:
yyerror("RETURN NEXT must specify a record or row variable in function returning tuple");
break;
}
if (yylex() != ';')
if (yylex() != ';')
yyerror("RETURN NEXT must specify a record or row variable in function returning tuple");
yyerror("RETURN NEXT must specify a record or row variable in function returning tuple");
}
}
...
...
src/pl/plpgsql/src/pl_comp.c
View file @
fd97cf4d
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
* procedural language
* procedural language
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.8
5 2005/03/29 00:17:23
tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.8
6 2005/04/05 06:22:16
tgl Exp $
*
*
* This software is copyrighted by Jan Wieck - Hamburg.
* This software is copyrighted by Jan Wieck - Hamburg.
*
*
...
@@ -49,6 +49,7 @@
...
@@ -49,6 +49,7 @@
#include "catalog/pg_class.h"
#include "catalog/pg_class.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "nodes/makefuncs.h"
#include "nodes/makefuncs.h"
#include "parser/gramparse.h"
#include "parser/gramparse.h"
#include "parser/parse_type.h"
#include "parser/parse_type.h"
...
@@ -122,13 +123,20 @@ static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,
...
@@ -122,13 +123,20 @@ static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,
HeapTuple
procTup
,
HeapTuple
procTup
,
PLpgSQL_func_hashkey
*
hashkey
,
PLpgSQL_func_hashkey
*
hashkey
,
bool
forValidator
);
bool
forValidator
);
static
char
**
fetchArgNames
(
HeapTuple
procTup
,
int
nargs
);
static
int
fetchArgInfo
(
HeapTuple
procTup
,
static
PLpgSQL_row
*
build_row_var
(
Oid
classOid
);
Oid
**
p_argtypes
,
char
***
p_argnames
,
char
**
p_argmodes
);
static
PLpgSQL_row
*
build_row_from_class
(
Oid
classOid
);
static
PLpgSQL_row
*
build_row_from_vars
(
PLpgSQL_variable
**
vars
,
int
numvars
);
static
PLpgSQL_type
*
build_datatype
(
HeapTuple
typeTup
,
int32
typmod
);
static
PLpgSQL_type
*
build_datatype
(
HeapTuple
typeTup
,
int32
typmod
);
static
void
compute_function_hashkey
(
FunctionCallInfo
fcinfo
,
static
void
compute_function_hashkey
(
FunctionCallInfo
fcinfo
,
Form_pg_proc
procStruct
,
Form_pg_proc
procStruct
,
PLpgSQL_func_hashkey
*
hashkey
,
PLpgSQL_func_hashkey
*
hashkey
,
bool
forValidator
);
bool
forValidator
);
static
void
plpgsql_resolve_polymorphic_argtypes
(
int
numargs
,
Oid
*
argtypes
,
char
*
argmodes
,
Node
*
call_expr
,
bool
forValidator
,
const
char
*
proname
);
static
PLpgSQL_function
*
plpgsql_HashTableLookup
(
PLpgSQL_func_hashkey
*
func_key
);
static
PLpgSQL_function
*
plpgsql_HashTableLookup
(
PLpgSQL_func_hashkey
*
func_key
);
static
void
plpgsql_HashTableInsert
(
PLpgSQL_function
*
function
,
static
void
plpgsql_HashTableInsert
(
PLpgSQL_function
*
function
,
PLpgSQL_func_hashkey
*
func_key
);
PLpgSQL_func_hashkey
*
func_key
);
...
@@ -259,11 +267,17 @@ do_compile(FunctionCallInfo fcinfo,
...
@@ -259,11 +267,17 @@ do_compile(FunctionCallInfo fcinfo,
PLpgSQL_variable
*
var
;
PLpgSQL_variable
*
var
;
PLpgSQL_rec
*
rec
;
PLpgSQL_rec
*
rec
;
int
i
;
int
i
;
int
arg_varnos
[
FUNC_MAX_ARGS
];
ErrorContextCallback
plerrcontext
;
ErrorContextCallback
plerrcontext
;
int
parse_rc
;
int
parse_rc
;
Oid
rettypeid
;
Oid
rettypeid
;
int
numargs
;
int
num_in_args
;
int
num_out_args
;
Oid
*
argtypes
;
char
**
argnames
;
char
**
argnames
;
char
*
argmodes
;
int
*
in_arg_varnos
=
NULL
;
PLpgSQL_variable
**
out_arg_variables
;
MemoryContext
func_cxt
;
MemoryContext
func_cxt
;
/*
/*
...
@@ -330,10 +344,110 @@ do_compile(FunctionCallInfo fcinfo,
...
@@ -330,10 +344,110 @@ do_compile(FunctionCallInfo fcinfo,
function
->
fn_cmin
=
HeapTupleHeaderGetCmin
(
procTup
->
t_data
);
function
->
fn_cmin
=
HeapTupleHeaderGetCmin
(
procTup
->
t_data
);
function
->
fn_functype
=
functype
;
function
->
fn_functype
=
functype
;
function
->
fn_cxt
=
func_cxt
;
function
->
fn_cxt
=
func_cxt
;
function
->
out_param_varno
=
-
1
;
/* set up for no OUT param */
switch
(
functype
)
switch
(
functype
)
{
{
case
T_FUNCTION
:
case
T_FUNCTION
:
/*
* Fetch info about the procedure's parameters. Allocations
* aren't needed permanently, so make them in tmp cxt.
*
* We also need to resolve any polymorphic input or output
* argument types. In validation mode we won't be able to,
* so we arbitrarily assume we are dealing with integers.
*/
MemoryContextSwitchTo
(
compile_tmp_cxt
);
numargs
=
fetchArgInfo
(
procTup
,
&
argtypes
,
&
argnames
,
&
argmodes
);
plpgsql_resolve_polymorphic_argtypes
(
numargs
,
argtypes
,
argmodes
,
fcinfo
->
flinfo
->
fn_expr
,
forValidator
,
plpgsql_error_funcname
);
in_arg_varnos
=
(
int
*
)
palloc
(
numargs
*
sizeof
(
int
));
out_arg_variables
=
(
PLpgSQL_variable
**
)
palloc
(
numargs
*
sizeof
(
PLpgSQL_variable
*
));
MemoryContextSwitchTo
(
func_cxt
);
/*
* Create the variables for the procedure's parameters.
*/
num_in_args
=
num_out_args
=
0
;
for
(
i
=
0
;
i
<
numargs
;
i
++
)
{
char
buf
[
32
];
Oid
argtypeid
=
argtypes
[
i
];
char
argmode
=
argmodes
?
argmodes
[
i
]
:
PROARGMODE_IN
;
PLpgSQL_type
*
argdtype
;
PLpgSQL_variable
*
argvariable
;
int
argitemtype
;
/* Create $n name for variable */
snprintf
(
buf
,
sizeof
(
buf
),
"$%d"
,
i
+
1
);
/* Create datatype info */
argdtype
=
plpgsql_build_datatype
(
argtypeid
,
-
1
);
/* Disallow pseudotype argument */
/* (note we already replaced ANYARRAY/ANYELEMENT) */
/* (build_variable would do this, but wrong message) */
if
(
argdtype
->
ttype
!=
PLPGSQL_TTYPE_SCALAR
&&
argdtype
->
ttype
!=
PLPGSQL_TTYPE_ROW
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_FEATURE_NOT_SUPPORTED
),
errmsg
(
"plpgsql functions cannot take type %s"
,
format_type_be
(
argtypeid
))));
/* Build variable and add to datum list */
argvariable
=
plpgsql_build_variable
(
buf
,
0
,
argdtype
,
false
);
if
(
argvariable
->
dtype
==
PLPGSQL_DTYPE_VAR
)
{
argitemtype
=
PLPGSQL_NSTYPE_VAR
;
/* input argument vars are forced to be CONSTANT */
if
(
argmode
==
PROARGMODE_IN
)
((
PLpgSQL_var
*
)
argvariable
)
->
isconst
=
true
;
}
else
{
Assert
(
argvariable
->
dtype
==
PLPGSQL_DTYPE_ROW
);
argitemtype
=
PLPGSQL_NSTYPE_ROW
;
}
/* Remember arguments in appropriate arrays */
if
(
argmode
==
PROARGMODE_IN
||
argmode
==
PROARGMODE_INOUT
)
in_arg_varnos
[
num_in_args
++
]
=
argvariable
->
dno
;
if
(
argmode
==
PROARGMODE_OUT
||
argmode
==
PROARGMODE_INOUT
)
out_arg_variables
[
num_out_args
++
]
=
argvariable
;
/* Add to namespace under the $n name */
plpgsql_ns_additem
(
argitemtype
,
argvariable
->
dno
,
buf
);
/* If there's a name for the argument, make an alias */
if
(
argnames
&&
argnames
[
i
][
0
]
!=
'\0'
)
plpgsql_ns_additem
(
argitemtype
,
argvariable
->
dno
,
argnames
[
i
]);
}
/*
* If there's just one OUT parameter, out_param_varno points
* directly to it. If there's more than one, build a row
* that holds all of them.
*/
if
(
num_out_args
==
1
)
function
->
out_param_varno
=
out_arg_variables
[
0
]
->
dno
;
else
if
(
num_out_args
>
1
)
{
PLpgSQL_row
*
row
=
build_row_from_vars
(
out_arg_variables
,
num_out_args
);
plpgsql_adddatum
((
PLpgSQL_datum
*
)
row
);
function
->
out_param_varno
=
row
->
rowno
;
}
/*
/*
* Check for a polymorphic returntype. If found, use the
* Check for a polymorphic returntype. If found, use the
* actual returntype type from the caller's FuncExpr node, if
* actual returntype type from the caller's FuncExpr node, if
...
@@ -355,6 +469,7 @@ do_compile(FunctionCallInfo fcinfo,
...
@@ -355,6 +469,7 @@ do_compile(FunctionCallInfo fcinfo,
rettypeid
=
INT4OID
;
rettypeid
=
INT4OID
;
}
}
else
else
{
rettypeid
=
get_fn_expr_rettype
(
fcinfo
->
flinfo
);
rettypeid
=
get_fn_expr_rettype
(
fcinfo
->
flinfo
);
if
(
!
OidIsValid
(
rettypeid
))
if
(
!
OidIsValid
(
rettypeid
))
ereport
(
ERROR
,
ereport
(
ERROR
,
...
@@ -363,6 +478,7 @@ do_compile(FunctionCallInfo fcinfo,
...
@@ -363,6 +478,7 @@ do_compile(FunctionCallInfo fcinfo,
"for polymorphic function
\"
%s
\"
"
,
"for polymorphic function
\"
%s
\"
"
,
plpgsql_error_funcname
)));
plpgsql_error_funcname
)));
}
}
}
/*
/*
* Normal function has a defined returntype
* Normal function has a defined returntype
...
@@ -410,10 +526,12 @@ do_compile(FunctionCallInfo fcinfo,
...
@@ -410,10 +526,12 @@ do_compile(FunctionCallInfo fcinfo,
/*
/*
* install $0 reference, but only for polymorphic return
* install $0 reference, but only for polymorphic return
* types
* types, and not when the return is specified through an
* output parameter.
*/
*/
if
(
procStruct
->
prorettype
==
ANYARRAYOID
||
if
((
procStruct
->
prorettype
==
ANYARRAYOID
||
procStruct
->
prorettype
==
ANYELEMENTOID
)
procStruct
->
prorettype
==
ANYELEMENTOID
)
&&
num_out_args
==
0
)
{
{
(
void
)
plpgsql_build_variable
(
"$0"
,
0
,
(
void
)
plpgsql_build_variable
(
"$0"
,
0
,
build_datatype
(
typeTup
,
-
1
),
build_datatype
(
typeTup
,
-
1
),
...
@@ -421,72 +539,6 @@ do_compile(FunctionCallInfo fcinfo,
...
@@ -421,72 +539,6 @@ do_compile(FunctionCallInfo fcinfo,
}
}
}
}
ReleaseSysCache
(
typeTup
);
ReleaseSysCache
(
typeTup
);
/*
* Create the variables for the procedure's
* parameters. Allocations aren't needed permanently, so
* make them in tmp cxt.
*/
MemoryContextSwitchTo
(
compile_tmp_cxt
);
argnames
=
fetchArgNames
(
procTup
,
procStruct
->
pronargs
);
MemoryContextSwitchTo
(
func_cxt
);
for
(
i
=
0
;
i
<
procStruct
->
pronargs
;
i
++
)
{
char
buf
[
32
];
Oid
argtypeid
;
PLpgSQL_type
*
argdtype
;
PLpgSQL_variable
*
argvariable
;
int
argitemtype
;
/* Create $n name for variable */
snprintf
(
buf
,
sizeof
(
buf
),
"$%d"
,
i
+
1
);
/*
* Since we already did the replacement of polymorphic
* argument types by actual argument types while computing
* the hashkey, we can just use those results.
*/
argtypeid
=
hashkey
->
argtypes
[
i
];
argdtype
=
plpgsql_build_datatype
(
argtypeid
,
-
1
);
/* Disallow pseudotype argument */
/* (note we already replaced ANYARRAY/ANYELEMENT) */
/* (build_variable would do this, but wrong message) */
if
(
argdtype
->
ttype
!=
PLPGSQL_TTYPE_SCALAR
&&
argdtype
->
ttype
!=
PLPGSQL_TTYPE_ROW
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_FEATURE_NOT_SUPPORTED
),
errmsg
(
"plpgsql functions cannot take type %s"
,
format_type_be
(
argtypeid
))));
/* Build variable and add to datum list */
argvariable
=
plpgsql_build_variable
(
buf
,
0
,
argdtype
,
false
);
if
(
argvariable
->
dtype
==
PLPGSQL_DTYPE_VAR
)
{
/* argument vars are forced to be CONSTANT (why?) */
((
PLpgSQL_var
*
)
argvariable
)
->
isconst
=
true
;
argitemtype
=
PLPGSQL_NSTYPE_VAR
;
}
else
{
Assert
(
argvariable
->
dtype
==
PLPGSQL_DTYPE_ROW
);
argitemtype
=
PLPGSQL_NSTYPE_ROW
;
}
/* Remember datum number */
arg_varnos
[
i
]
=
argvariable
->
dno
;
/* Add to namespace under the $n name */
plpgsql_ns_additem
(
argitemtype
,
argvariable
->
dno
,
buf
);
/* If there's a name for the argument, make an alias */
if
(
argnames
)
plpgsql_ns_additem
(
argitemtype
,
argvariable
->
dno
,
argnames
[
i
]);
}
break
;
break
;
case
T_TRIGGER
:
case
T_TRIGGER
:
...
@@ -598,7 +650,7 @@ do_compile(FunctionCallInfo fcinfo,
...
@@ -598,7 +650,7 @@ do_compile(FunctionCallInfo fcinfo,
*/
*/
function
->
fn_nargs
=
procStruct
->
pronargs
;
function
->
fn_nargs
=
procStruct
->
pronargs
;
for
(
i
=
0
;
i
<
function
->
fn_nargs
;
i
++
)
for
(
i
=
0
;
i
<
function
->
fn_nargs
;
i
++
)
function
->
fn_argvarnos
[
i
]
=
arg_varnos
[
i
];
function
->
fn_argvarnos
[
i
]
=
in_
arg_varnos
[
i
];
function
->
ndatums
=
plpgsql_nDatums
;
function
->
ndatums
=
plpgsql_nDatums
;
function
->
datums
=
palloc
(
sizeof
(
PLpgSQL_datum
*
)
*
plpgsql_nDatums
);
function
->
datums
=
palloc
(
sizeof
(
PLpgSQL_datum
*
)
*
plpgsql_nDatums
);
for
(
i
=
0
;
i
<
plpgsql_nDatums
;
i
++
)
for
(
i
=
0
;
i
<
plpgsql_nDatums
;
i
++
)
...
@@ -660,40 +712,96 @@ plpgsql_compile_error_callback(void *arg)
...
@@ -660,40 +712,96 @@ plpgsql_compile_error_callback(void *arg)
/*
/*
* Fetch the argument names, if any, from the proargnames field of the
* Fetch info about the argument types, names, and IN/OUT modes from the
* pg_proc tuple. Results are palloc'd.
* pg_proc tuple. Return value is the number of arguments.
* Other results are palloc'd.
*/
*/
static
char
**
static
int
fetchArgNames
(
HeapTuple
procTup
,
int
nargs
)
fetchArgInfo
(
HeapTuple
procTup
,
Oid
**
p_argtypes
,
char
***
p_argnames
,
char
**
p_argmodes
)
{
{
Datum
argnamesDatum
;
Form_pg_proc
procStruct
=
(
Form_pg_proc
)
GETSTRUCT
(
procTup
);
Datum
proallargtypes
;
Datum
proargmodes
;
Datum
proargnames
;
bool
isNull
;
bool
isNull
;
ArrayType
*
arr
;
int
numargs
;
Datum
*
elems
;
Datum
*
elems
;
int
nelems
;
int
nelems
;
char
**
result
;
int
i
;
int
i
;
if
(
nargs
==
0
)
/* First discover the total number of parameters and get their types */
return
NULL
;
proallargtypes
=
SysCacheGetAttr
(
PROCOID
,
procTup
,
Anum_pg_proc_proallargtypes
,
&
isNull
);
if
(
!
isNull
)
{
/*
* We expect the arrays to be 1-D arrays of the right types; verify
* that. For the OID and char arrays, we don't need to use
* deconstruct_array() since the array data is just going to look like
* a C array of values.
*/
arr
=
DatumGetArrayTypeP
(
proallargtypes
);
/* ensure not toasted */
numargs
=
ARR_DIMS
(
arr
)[
0
];
if
(
ARR_NDIM
(
arr
)
!=
1
||
numargs
<
0
||
ARR_ELEMTYPE
(
arr
)
!=
OIDOID
)
elog
(
ERROR
,
"proallargtypes is not a 1-D Oid array"
);
Assert
(
numargs
>=
procStruct
->
pronargs
);
*
p_argtypes
=
(
Oid
*
)
palloc
(
numargs
*
sizeof
(
Oid
));
memcpy
(
*
p_argtypes
,
ARR_DATA_PTR
(
arr
),
numargs
*
sizeof
(
Oid
));
}
else
{
/* If no proallargtypes, use proargtypes */
numargs
=
procStruct
->
proargtypes
.
dim1
;
Assert
(
numargs
==
procStruct
->
pronargs
);
*
p_argtypes
=
(
Oid
*
)
palloc
(
numargs
*
sizeof
(
Oid
));
memcpy
(
*
p_argtypes
,
procStruct
->
proargtypes
.
values
,
numargs
*
sizeof
(
Oid
));
}
argnamesDatum
=
SysCacheGetAttr
(
PROCOID
,
procTup
,
Anum_pg_proc_proargnames
,
/* Get argument names, if available */
proargnames
=
SysCacheGetAttr
(
PROCOID
,
procTup
,
Anum_pg_proc_proargnames
,
&
isNull
);
&
isNull
);
if
(
isNull
)
if
(
isNull
)
return
NULL
;
*
p_argnames
=
NULL
;
else
deconstruct_array
(
DatumGetArrayTypeP
(
argnamesDatum
),
{
deconstruct_array
(
DatumGetArrayTypeP
(
proargnames
),
TEXTOID
,
-
1
,
false
,
'i'
,
TEXTOID
,
-
1
,
false
,
'i'
,
&
elems
,
&
nelems
);
&
elems
,
&
nelems
);
if
(
nelems
!=
numargs
)
/* should not happen */
if
(
nelems
!=
nargs
)
/* should not happen */
elog
(
ERROR
,
"proargnames must have the same number of elements as the function has arguments"
);
elog
(
ERROR
,
"proargnames must have the same number of elements as the function has arguments"
);
*
p_argnames
=
(
char
**
)
palloc
(
sizeof
(
char
*
)
*
numargs
);
for
(
i
=
0
;
i
<
numargs
;
i
++
)
(
*
p_argnames
)[
i
]
=
DatumGetCString
(
DirectFunctionCall1
(
textout
,
elems
[
i
]));
}
result
=
(
char
**
)
palloc
(
sizeof
(
char
*
)
*
nargs
);
/* Get argument modes, if available */
proargmodes
=
SysCacheGetAttr
(
PROCOID
,
procTup
,
for
(
i
=
0
;
i
<
nargs
;
i
++
)
Anum_pg_proc_proargmodes
,
result
[
i
]
=
DatumGetCString
(
DirectFunctionCall1
(
textout
,
elems
[
i
]));
&
isNull
);
if
(
isNull
)
*
p_argmodes
=
NULL
;
else
{
arr
=
DatumGetArrayTypeP
(
proargmodes
);
/* ensure not toasted */
if
(
ARR_NDIM
(
arr
)
!=
1
||
ARR_DIMS
(
arr
)[
0
]
!=
numargs
||
ARR_ELEMTYPE
(
arr
)
!=
CHAROID
)
elog
(
ERROR
,
"proargmodes is not a 1-D char array"
);
*
p_argmodes
=
(
char
*
)
palloc
(
numargs
*
sizeof
(
char
));
memcpy
(
*
p_argmodes
,
ARR_DATA_PTR
(
arr
),
numargs
*
sizeof
(
char
));
}
return
result
;
return
numargs
;
}
}
...
@@ -1449,7 +1557,7 @@ plpgsql_build_variable(const char *refname, int lineno, PLpgSQL_type *dtype,
...
@@ -1449,7 +1557,7 @@ plpgsql_build_variable(const char *refname, int lineno, PLpgSQL_type *dtype,
/* Composite type -- build a row variable */
/* Composite type -- build a row variable */
PLpgSQL_row
*
row
;
PLpgSQL_row
*
row
;
row
=
build_row_
var
(
dtype
->
typrelid
);
row
=
build_row_
from_class
(
dtype
->
typrelid
);
row
->
dtype
=
PLPGSQL_DTYPE_ROW
;
row
->
dtype
=
PLPGSQL_DTYPE_ROW
;
row
->
refname
=
pstrdup
(
refname
);
row
->
refname
=
pstrdup
(
refname
);
...
@@ -1504,7 +1612,7 @@ plpgsql_build_variable(const char *refname, int lineno, PLpgSQL_type *dtype,
...
@@ -1504,7 +1612,7 @@ plpgsql_build_variable(const char *refname, int lineno, PLpgSQL_type *dtype,
* Build a row-variable data structure given the pg_class OID.
* Build a row-variable data structure given the pg_class OID.
*/
*/
static
PLpgSQL_row
*
static
PLpgSQL_row
*
build_row_
var
(
Oid
classOid
)
build_row_
from_class
(
Oid
classOid
)
{
{
PLpgSQL_row
*
row
;
PLpgSQL_row
*
row
;
Relation
rel
;
Relation
rel
;
...
@@ -1589,6 +1697,62 @@ build_row_var(Oid classOid)
...
@@ -1589,6 +1697,62 @@ build_row_var(Oid classOid)
return
row
;
return
row
;
}
}
/*
* Build a row-variable data structure given the component variables.
*/
static
PLpgSQL_row
*
build_row_from_vars
(
PLpgSQL_variable
**
vars
,
int
numvars
)
{
PLpgSQL_row
*
row
;
int
i
;
row
=
palloc0
(
sizeof
(
PLpgSQL_row
));
row
->
dtype
=
PLPGSQL_DTYPE_ROW
;
row
->
rowtupdesc
=
CreateTemplateTupleDesc
(
numvars
,
false
);
row
->
nfields
=
numvars
;
row
->
fieldnames
=
palloc
(
numvars
*
sizeof
(
char
*
));
row
->
varnos
=
palloc
(
numvars
*
sizeof
(
int
));
for
(
i
=
0
;
i
<
numvars
;
i
++
)
{
PLpgSQL_variable
*
var
=
vars
[
i
];
Oid
typoid
=
RECORDOID
;
int32
typmod
=
-
1
;
switch
(
var
->
dtype
)
{
case
PLPGSQL_DTYPE_VAR
:
typoid
=
((
PLpgSQL_var
*
)
var
)
->
datatype
->
typoid
;
typmod
=
((
PLpgSQL_var
*
)
var
)
->
datatype
->
atttypmod
;
break
;
case
PLPGSQL_DTYPE_REC
:
break
;
case
PLPGSQL_DTYPE_ROW
:
if
(((
PLpgSQL_row
*
)
var
)
->
rowtupdesc
)
{
typoid
=
((
PLpgSQL_row
*
)
var
)
->
rowtupdesc
->
tdtypeid
;
typmod
=
((
PLpgSQL_row
*
)
var
)
->
rowtupdesc
->
tdtypmod
;
}
break
;
default:
elog
(
ERROR
,
"unrecognized dtype: %d"
,
var
->
dtype
);
}
row
->
fieldnames
[
i
]
=
var
->
refname
;
row
->
varnos
[
i
]
=
var
->
dno
;
TupleDescInitEntry
(
row
->
rowtupdesc
,
i
+
1
,
var
->
refname
,
typoid
,
typmod
,
0
);
}
return
row
;
}
/* ----------
/* ----------
* plpgsql_parse_datatype Scanner found something that should
* plpgsql_parse_datatype Scanner found something that should
...
@@ -1820,8 +1984,6 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
...
@@ -1820,8 +1984,6 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
PLpgSQL_func_hashkey
*
hashkey
,
PLpgSQL_func_hashkey
*
hashkey
,
bool
forValidator
)
bool
forValidator
)
{
{
int
i
;
/* Make sure any unused bytes of the struct are zero */
/* Make sure any unused bytes of the struct are zero */
MemSet
(
hashkey
,
0
,
sizeof
(
PLpgSQL_func_hashkey
));
MemSet
(
hashkey
,
0
,
sizeof
(
PLpgSQL_func_hashkey
));
...
@@ -1840,42 +2002,64 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
...
@@ -1840,42 +2002,64 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
hashkey
->
trigrelOid
=
RelationGetRelid
(
trigdata
->
tg_relation
);
hashkey
->
trigrelOid
=
RelationGetRelid
(
trigdata
->
tg_relation
);
}
}
/* get the argument types */
if
(
procStruct
->
pronargs
>
0
)
for
(
i
=
0
;
i
<
procStruct
->
pronargs
;
i
++
)
{
{
Oid
argtypeid
=
procStruct
->
proargtypes
.
values
[
i
];
/* get the argument types */
memcpy
(
hashkey
->
argtypes
,
procStruct
->
proargtypes
.
values
,
procStruct
->
pronargs
*
sizeof
(
Oid
));
/*
/* resolve any polymorphic argument types */
* Check for polymorphic arguments. If found, use the actual
plpgsql_resolve_polymorphic_argtypes
(
procStruct
->
pronargs
,
* parameter type from the caller's FuncExpr node, if we have one.
hashkey
->
argtypes
,
* (In validation mode we arbitrarily assume we are dealing with
NULL
,
* integers. This lets us build a valid, if possibly useless,
fcinfo
->
flinfo
->
fn_expr
,
* function hashtable entry.)
forValidator
,
*
NameStr
(
procStruct
->
proname
));
* We can support arguments of type ANY the same way as normal
}
* polymorphic arguments.
}
/*
* This is the same as the standard resolve_polymorphic_argtypes() function,
* but with a special case for validation: assume that polymorphic arguments
* are integer or integer-array. Also, we go ahead and report the error
* if we can't resolve the types.
*/
*/
if
(
argtypeid
==
ANYARRAYOID
||
argtypeid
==
ANYELEMENTOID
||
static
void
argtypeid
==
ANYOID
)
plpgsql_resolve_polymorphic_argtypes
(
int
numargs
,
{
Oid
*
argtypes
,
char
*
argmodes
,
if
(
forValidator
)
Node
*
call_expr
,
bool
forValidator
,
const
char
*
proname
)
{
int
i
;
if
(
!
forValidator
)
{
{
if
(
argtypeid
==
ANYARRAYOID
)
/* normal case, pass to standard routine */
argtypeid
=
INT4ARRAYOID
;
if
(
!
resolve_polymorphic_argtypes
(
numargs
,
argtypes
,
argmodes
,
else
call_expr
))
argtypeid
=
INT4OID
;
}
else
argtypeid
=
get_fn_expr_argtype
(
fcinfo
->
flinfo
,
i
);
if
(
!
OidIsValid
(
argtypeid
))
ereport
(
ERROR
,
ereport
(
ERROR
,
(
errcode
(
ERRCODE_FEATURE_NOT_SUPPORTED
),
(
errcode
(
ERRCODE_FEATURE_NOT_SUPPORTED
),
errmsg
(
"could not determine actual argument "
errmsg
(
"could not determine actual argument "
"type for polymorphic function
\"
%s
\"
"
,
"type for polymorphic function
\"
%s
\"
"
,
NameStr
(
procStruct
->
proname
))));
proname
)));
}
else
{
/* special validation case */
for
(
i
=
0
;
i
<
numargs
;
i
++
)
{
switch
(
argtypes
[
i
])
{
case
ANYELEMENTOID
:
argtypes
[
i
]
=
INT4OID
;
break
;
case
ANYARRAYOID
:
argtypes
[
i
]
=
INT4ARRAYOID
;
break
;
default:
break
;
}
}
}
hashkey
->
argtypes
[
i
]
=
argtypeid
;
}
}
}
}
...
...
src/pl/plpgsql/src/pl_exec.c
View file @
fd97cf4d
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
* procedural language
* procedural language
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.13
3 2005/03/25 01:45:42
tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.13
4 2005/04/05 06:22:16
tgl Exp $
*
*
* This software is copyrighted by Jan Wieck - Hamburg.
* This software is copyrighted by Jan Wieck - Hamburg.
*
*
...
@@ -75,8 +75,6 @@ static PLpgSQL_expr *active_simple_exprs = NULL;
...
@@ -75,8 +75,6 @@ static PLpgSQL_expr *active_simple_exprs = NULL;
************************************************************/
************************************************************/
static
void
plpgsql_exec_error_callback
(
void
*
arg
);
static
void
plpgsql_exec_error_callback
(
void
*
arg
);
static
PLpgSQL_datum
*
copy_plpgsql_datum
(
PLpgSQL_datum
*
datum
);
static
PLpgSQL_datum
*
copy_plpgsql_datum
(
PLpgSQL_datum
*
datum
);
static
PLpgSQL_var
*
copy_var
(
PLpgSQL_var
*
var
);
static
PLpgSQL_rec
*
copy_rec
(
PLpgSQL_rec
*
rec
);
static
int
exec_stmt_block
(
PLpgSQL_execstate
*
estate
,
static
int
exec_stmt_block
(
PLpgSQL_execstate
*
estate
,
PLpgSQL_stmt_block
*
block
);
PLpgSQL_stmt_block
*
block
);
...
@@ -212,11 +210,11 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
...
@@ -212,11 +210,11 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
* Make local execution copies of all the datums
* Make local execution copies of all the datums
*/
*/
estate
.
err_text
=
gettext_noop
(
"during initialization of execution state"
);
estate
.
err_text
=
gettext_noop
(
"during initialization of execution state"
);
for
(
i
=
0
;
i
<
func
->
ndatums
;
i
++
)
for
(
i
=
0
;
i
<
estate
.
ndatums
;
i
++
)
estate
.
datums
[
i
]
=
copy_plpgsql_datum
(
func
->
datums
[
i
]);
estate
.
datums
[
i
]
=
copy_plpgsql_datum
(
func
->
datums
[
i
]);
/*
/*
* Store the actual call argument values into the variables
* Store the actual call argument values into the
appropriate
variables
*/
*/
estate
.
err_text
=
gettext_noop
(
"while storing call arguments into local variables"
);
estate
.
err_text
=
gettext_noop
(
"while storing call arguments into local variables"
);
for
(
i
=
0
;
i
<
func
->
fn_nargs
;
i
++
)
for
(
i
=
0
;
i
<
func
->
fn_nargs
;
i
++
)
...
@@ -272,36 +270,6 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
...
@@ -272,36 +270,6 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
}
}
}
}
/*
* Initialize the other variables to NULL values for now. The default
* values are set when the blocks are entered.
*/
estate
.
err_text
=
gettext_noop
(
"while initializing local variables to NULL"
);
for
(
i
=
estate
.
found_varno
;
i
<
estate
.
ndatums
;
i
++
)
{
switch
(
estate
.
datums
[
i
]
->
dtype
)
{
case
PLPGSQL_DTYPE_VAR
:
{
PLpgSQL_var
*
var
=
(
PLpgSQL_var
*
)
estate
.
datums
[
i
];
var
->
value
=
0
;
var
->
isnull
=
true
;
var
->
freeval
=
false
;
}
break
;
case
PLPGSQL_DTYPE_ROW
:
case
PLPGSQL_DTYPE_REC
:
case
PLPGSQL_DTYPE_RECFIELD
:
case
PLPGSQL_DTYPE_ARRAYELEM
:
break
;
default:
elog
(
ERROR
,
"unrecognized dtype: %d"
,
func
->
datums
[
i
]
->
dtype
);
}
}
/*
/*
* Set the magic variable FOUND to false
* Set the magic variable FOUND to false
*/
*/
...
@@ -445,7 +413,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
...
@@ -445,7 +413,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
* Make local execution copies of all the datums
* Make local execution copies of all the datums
*/
*/
estate
.
err_text
=
gettext_noop
(
"during initialization of execution state"
);
estate
.
err_text
=
gettext_noop
(
"during initialization of execution state"
);
for
(
i
=
0
;
i
<
func
->
ndatums
;
i
++
)
for
(
i
=
0
;
i
<
estate
.
ndatums
;
i
++
)
estate
.
datums
[
i
]
=
copy_plpgsql_datum
(
func
->
datums
[
i
]);
estate
.
datums
[
i
]
=
copy_plpgsql_datum
(
func
->
datums
[
i
]);
/*
/*
...
@@ -551,7 +519,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
...
@@ -551,7 +519,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
var
->
freeval
=
false
;
var
->
freeval
=
false
;
/*
/*
* Store the
actual call
argument values into the special execution
* Store the
trigger
argument values into the special execution
* state variables
* state variables
*/
*/
estate
.
err_text
=
gettext_noop
(
"while storing call arguments into local variables"
);
estate
.
err_text
=
gettext_noop
(
"while storing call arguments into local variables"
);
...
@@ -566,37 +534,6 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
...
@@ -566,37 +534,6 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
CStringGetDatum
(
trigdata
->
tg_trigger
->
tgargs
[
i
]));
CStringGetDatum
(
trigdata
->
tg_trigger
->
tgargs
[
i
]));
}
}
/*
* Initialize the other variables to NULL values for now. The default
* values are set when the blocks are entered.
*/
estate
.
err_text
=
gettext_noop
(
"while initializing local variables to NULL"
);
for
(
i
=
estate
.
found_varno
;
i
<
estate
.
ndatums
;
i
++
)
{
switch
(
estate
.
datums
[
i
]
->
dtype
)
{
case
PLPGSQL_DTYPE_VAR
:
{
PLpgSQL_var
*
var
=
(
PLpgSQL_var
*
)
estate
.
datums
[
i
];
var
->
value
=
0
;
var
->
isnull
=
true
;
var
->
freeval
=
false
;
}
break
;
case
PLPGSQL_DTYPE_ROW
:
case
PLPGSQL_DTYPE_REC
:
case
PLPGSQL_DTYPE_RECFIELD
:
case
PLPGSQL_DTYPE_ARRAYELEM
:
case
PLPGSQL_DTYPE_TRIGARG
:
break
;
default:
elog
(
ERROR
,
"unrecognized dtype: %d"
,
func
->
datums
[
i
]
->
dtype
);
}
}
/*
/*
* Set the magic variable FOUND to false
* Set the magic variable FOUND to false
*/
*/
...
@@ -710,67 +647,65 @@ plpgsql_exec_error_callback(void *arg)
...
@@ -710,67 +647,65 @@ plpgsql_exec_error_callback(void *arg)
/* ----------
/* ----------
* Support functions for copying local execution variables
* Support function for initializing local execution variables
*
* NB: this is not a generic copy operation because it assumes that any
* pass-by-ref original values will live as long as the copy is needed.
* ----------
* ----------
*/
*/
static
PLpgSQL_datum
*
static
PLpgSQL_datum
*
copy_plpgsql_datum
(
PLpgSQL_datum
*
datum
)
copy_plpgsql_datum
(
PLpgSQL_datum
*
datum
)
{
{
PLpgSQL_datum
*
result
=
NULL
;
PLpgSQL_datum
*
result
;
switch
(
datum
->
dtype
)
switch
(
datum
->
dtype
)
{
{
case
PLPGSQL_DTYPE_VAR
:
case
PLPGSQL_DTYPE_VAR
:
result
=
(
PLpgSQL_datum
*
)
copy_var
((
PLpgSQL_var
*
)
datum
);
{
PLpgSQL_var
*
new
=
palloc
(
sizeof
(
PLpgSQL_var
));
memcpy
(
new
,
datum
,
sizeof
(
PLpgSQL_var
));
/* Ensure the value is null (possibly not needed?) */
new
->
value
=
0
;
new
->
isnull
=
true
;
new
->
freeval
=
false
;
result
=
(
PLpgSQL_datum
*
)
new
;
}
break
;
break
;
case
PLPGSQL_DTYPE_REC
:
case
PLPGSQL_DTYPE_REC
:
result
=
(
PLpgSQL_datum
*
)
copy_rec
((
PLpgSQL_rec
*
)
datum
);
{
PLpgSQL_rec
*
new
=
palloc
(
sizeof
(
PLpgSQL_rec
));
memcpy
(
new
,
datum
,
sizeof
(
PLpgSQL_rec
));
/* Ensure the value is null (possibly not needed?) */
new
->
tup
=
NULL
;
new
->
tupdesc
=
NULL
;
new
->
freetup
=
false
;
new
->
freetupdesc
=
false
;
result
=
(
PLpgSQL_datum
*
)
new
;
}
break
;
break
;
case
PLPGSQL_DTYPE_ROW
:
case
PLPGSQL_DTYPE_ROW
:
case
PLPGSQL_DTYPE_RECFIELD
:
case
PLPGSQL_DTYPE_RECFIELD
:
case
PLPGSQL_DTYPE_ARRAYELEM
:
case
PLPGSQL_DTYPE_ARRAYELEM
:
case
PLPGSQL_DTYPE_TRIGARG
:
case
PLPGSQL_DTYPE_TRIGARG
:
/*
* These datum records are read-only at runtime, so no need
* to copy them
*/
result
=
datum
;
result
=
datum
;
break
;
break
;
default:
default:
elog
(
ERROR
,
"unrecognized dtype: %d"
,
datum
->
dtype
);
elog
(
ERROR
,
"unrecognized dtype: %d"
,
datum
->
dtype
);
result
=
NULL
;
/* keep compiler quiet */
break
;
}
}
return
result
;
return
result
;
}
}
static
PLpgSQL_var
*
copy_var
(
PLpgSQL_var
*
var
)
{
PLpgSQL_var
*
new
=
palloc
(
sizeof
(
PLpgSQL_var
));
memcpy
(
new
,
var
,
sizeof
(
PLpgSQL_var
));
new
->
freeval
=
false
;
return
new
;
}
static
PLpgSQL_rec
*
copy_rec
(
PLpgSQL_rec
*
rec
)
{
PLpgSQL_rec
*
new
=
palloc
(
sizeof
(
PLpgSQL_rec
));
memcpy
(
new
,
rec
,
sizeof
(
PLpgSQL_rec
));
new
->
tup
=
NULL
;
new
->
tupdesc
=
NULL
;
new
->
freetup
=
false
;
new
->
freetupdesc
=
false
;
return
new
;
}
static
bool
static
bool
exception_matches_conditions
(
ErrorData
*
edata
,
PLpgSQL_condition
*
cond
)
exception_matches_conditions
(
ErrorData
*
edata
,
PLpgSQL_condition
*
cond
)
...
@@ -1682,16 +1617,30 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
...
@@ -1682,16 +1617,30 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
if
(
estate
->
retisset
)
if
(
estate
->
retisset
)
return
PLPGSQL_RC_RETURN
;
return
PLPGSQL_RC_RETURN
;
if
(
estate
->
retistuple
)
/* initialize for null result (possibly a tuple) */
{
/* initialize for null result tuple */
estate
->
retval
=
(
Datum
)
0
;
estate
->
retval
=
(
Datum
)
0
;
estate
->
rettupdesc
=
NULL
;
estate
->
rettupdesc
=
NULL
;
estate
->
retisnull
=
true
;
estate
->
retisnull
=
true
;
if
(
stmt
->
retrecno
>=
0
)
if
(
stmt
->
retvarno
>=
0
)
{
PLpgSQL_datum
*
retvar
=
estate
->
datums
[
stmt
->
retvarno
];
switch
(
retvar
->
dtype
)
{
case
PLPGSQL_DTYPE_VAR
:
{
PLpgSQL_var
*
var
=
(
PLpgSQL_var
*
)
retvar
;
estate
->
retval
=
var
->
value
;
estate
->
retisnull
=
var
->
isnull
;
estate
->
rettype
=
var
->
datatype
->
typoid
;
}
break
;
case
PLPGSQL_DTYPE_REC
:
{
{
PLpgSQL_rec
*
rec
=
(
PLpgSQL_rec
*
)
(
estate
->
datums
[
stmt
->
retrecno
])
;
PLpgSQL_rec
*
rec
=
(
PLpgSQL_rec
*
)
retvar
;
if
(
HeapTupleIsValid
(
rec
->
tup
))
if
(
HeapTupleIsValid
(
rec
->
tup
))
{
{
...
@@ -1699,15 +1648,14 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
...
@@ -1699,15 +1648,14 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
estate
->
rettupdesc
=
rec
->
tupdesc
;
estate
->
rettupdesc
=
rec
->
tupdesc
;
estate
->
retisnull
=
false
;
estate
->
retisnull
=
false
;
}
}
return
PLPGSQL_RC_RETURN
;
}
}
break
;
if
(
stmt
->
retrowno
>=
0
)
case
PLPGSQL_DTYPE_ROW
:
{
{
PLpgSQL_row
*
row
=
(
PLpgSQL_row
*
)
(
estate
->
datums
[
stmt
->
retrowno
])
;
PLpgSQL_row
*
row
=
(
PLpgSQL_row
*
)
retvar
;
if
(
row
->
rowtupdesc
)
/* should always be true here */
Assert
(
row
->
rowtupdesc
);
{
estate
->
retval
=
(
Datum
)
make_tuple_from_row
(
estate
,
row
,
estate
->
retval
=
(
Datum
)
make_tuple_from_row
(
estate
,
row
,
row
->
rowtupdesc
);
row
->
rowtupdesc
);
if
(
estate
->
retval
==
(
Datum
)
NULL
)
/* should not happen */
if
(
estate
->
retval
==
(
Datum
)
NULL
)
/* should not happen */
...
@@ -1715,10 +1663,18 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
...
@@ -1715,10 +1663,18 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
estate
->
rettupdesc
=
row
->
rowtupdesc
;
estate
->
rettupdesc
=
row
->
rowtupdesc
;
estate
->
retisnull
=
false
;
estate
->
retisnull
=
false
;
}
}
break
;
default:
elog
(
ERROR
,
"unrecognized dtype: %d"
,
retvar
->
dtype
);
}
return
PLPGSQL_RC_RETURN
;
return
PLPGSQL_RC_RETURN
;
}
}
if
(
stmt
->
expr
!=
NULL
)
if
(
stmt
->
expr
!=
NULL
)
{
if
(
estate
->
retistuple
)
{
{
exec_run_select
(
estate
,
stmt
->
expr
,
1
,
NULL
);
exec_run_select
(
estate
,
stmt
->
expr
,
1
,
NULL
);
if
(
estate
->
eval_processed
>
0
)
if
(
estate
->
eval_processed
>
0
)
...
@@ -1728,24 +1684,23 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
...
@@ -1728,24 +1684,23 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
estate
->
retisnull
=
false
;
estate
->
retisnull
=
false
;
}
}
}
}
return
PLPGSQL_RC_RETURN
;
else
{
/* Normal case for scalar results */
estate
->
retval
=
exec_eval_expr
(
estate
,
stmt
->
expr
,
&
(
estate
->
retisnull
),
&
(
estate
->
rettype
));
}
}
}
if
(
estate
->
fn_rettype
==
VOIDOID
)
if
(
estate
->
fn_rettype
==
VOIDOID
)
{
{
/* Special hack for function returning VOID */
/* Special hack for function returning VOID */
Assert
(
stmt
->
expr
==
NULL
);
Assert
(
stmt
->
retvarno
<
0
&&
stmt
->
expr
==
NULL
);
estate
->
retval
=
(
Datum
)
0
;
estate
->
retval
=
(
Datum
)
0
;
estate
->
retisnull
=
false
;
estate
->
retisnull
=
false
;
estate
->
rettype
=
VOIDOID
;
estate
->
rettype
=
VOIDOID
;
}
}
else
{
/* Normal case for scalar results */
estate
->
retval
=
exec_eval_expr
(
estate
,
stmt
->
expr
,
&
(
estate
->
retisnull
),
&
(
estate
->
rettype
));
}
return
PLPGSQL_RC_RETURN
;
return
PLPGSQL_RC_RETURN
;
}
}
...
@@ -1777,9 +1732,39 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
...
@@ -1777,9 +1732,39 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
tupdesc
=
estate
->
rettupdesc
;
tupdesc
=
estate
->
rettupdesc
;
natts
=
tupdesc
->
natts
;
natts
=
tupdesc
->
natts
;
if
(
stmt
->
rec
)
if
(
stmt
->
retvarno
>=
0
)
{
PLpgSQL_datum
*
retvar
=
estate
->
datums
[
stmt
->
retvarno
];
switch
(
retvar
->
dtype
)
{
case
PLPGSQL_DTYPE_VAR
:
{
{
PLpgSQL_rec
*
rec
=
(
PLpgSQL_rec
*
)
(
estate
->
datums
[
stmt
->
rec
->
recno
]);
PLpgSQL_var
*
var
=
(
PLpgSQL_var
*
)
retvar
;
Datum
retval
=
var
->
value
;
bool
isNull
=
var
->
isnull
;
if
(
natts
!=
1
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_DATATYPE_MISMATCH
),
errmsg
(
"wrong result type supplied in RETURN NEXT"
)));
/* coerce type if needed */
retval
=
exec_simple_cast_value
(
retval
,
var
->
datatype
->
typoid
,
tupdesc
->
attrs
[
0
]
->
atttypid
,
tupdesc
->
attrs
[
0
]
->
atttypmod
,
&
isNull
);
tuple
=
heap_form_tuple
(
tupdesc
,
&
retval
,
&
isNull
);
free_tuple
=
true
;
}
break
;
case
PLPGSQL_DTYPE_REC
:
{
PLpgSQL_rec
*
rec
=
(
PLpgSQL_rec
*
)
retvar
;
if
(
!
HeapTupleIsValid
(
rec
->
tup
))
if
(
!
HeapTupleIsValid
(
rec
->
tup
))
ereport
(
ERROR
,
ereport
(
ERROR
,
...
@@ -1793,21 +1778,32 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
...
@@ -1793,21 +1778,32 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
errmsg
(
"wrong record type supplied in RETURN NEXT"
)));
errmsg
(
"wrong record type supplied in RETURN NEXT"
)));
tuple
=
rec
->
tup
;
tuple
=
rec
->
tup
;
}
}
else
if
(
stmt
->
row
)
break
;
case
PLPGSQL_DTYPE_ROW
:
{
{
tuple
=
make_tuple_from_row
(
estate
,
stmt
->
row
,
tupdesc
);
PLpgSQL_row
*
row
=
(
PLpgSQL_row
*
)
retvar
;
tuple
=
make_tuple_from_row
(
estate
,
row
,
tupdesc
);
if
(
tuple
==
NULL
)
if
(
tuple
==
NULL
)
ereport
(
ERROR
,
ereport
(
ERROR
,
(
errcode
(
ERRCODE_DATATYPE_MISMATCH
),
(
errcode
(
ERRCODE_DATATYPE_MISMATCH
),
errmsg
(
"wrong record type supplied in RETURN NEXT"
)));
errmsg
(
"wrong record type supplied in RETURN NEXT"
)));
free_tuple
=
true
;
free_tuple
=
true
;
}
}
break
;
default:
elog
(
ERROR
,
"unrecognized dtype: %d"
,
retvar
->
dtype
);
tuple
=
NULL
;
/* keep compiler quiet */
break
;
}
}
else
if
(
stmt
->
expr
)
else
if
(
stmt
->
expr
)
{
{
Datum
retval
;
Datum
retval
;
bool
isNull
;
bool
isNull
;
Oid
rettype
;
Oid
rettype
;
char
nullflag
;
if
(
natts
!=
1
)
if
(
natts
!=
1
)
ereport
(
ERROR
,
ereport
(
ERROR
,
...
@@ -1826,9 +1822,7 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
...
@@ -1826,9 +1822,7 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
tupdesc
->
attrs
[
0
]
->
atttypmod
,
tupdesc
->
attrs
[
0
]
->
atttypmod
,
&
isNull
);
&
isNull
);
nullflag
=
isNull
?
'n'
:
' '
;
tuple
=
heap_form_tuple
(
tupdesc
,
&
retval
,
&
isNull
);
tuple
=
heap_formtuple
(
tupdesc
,
&
retval
,
&
nullflag
);
free_tuple
=
true
;
free_tuple
=
true
;
...
...
src/pl/plpgsql/src/pl_funcs.c
View file @
fd97cf4d
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
* procedural language
* procedural language
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.
39 2005/02/22 07:18:24 neilc
Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.
40 2005/04/05 06:22:16 tgl
Exp $
*
*
* This software is copyrighted by Jan Wieck - Hamburg.
* This software is copyrighted by Jan Wieck - Hamburg.
*
*
...
@@ -857,14 +857,12 @@ dump_return(PLpgSQL_stmt_return *stmt)
...
@@ -857,14 +857,12 @@ dump_return(PLpgSQL_stmt_return *stmt)
{
{
dump_ind
();
dump_ind
();
printf
(
"RETURN "
);
printf
(
"RETURN "
);
if
(
stmt
->
retrecno
>=
0
)
if
(
stmt
->
retvarno
>=
0
)
printf
(
"record %d"
,
stmt
->
retrecno
);
printf
(
"variable %d"
,
stmt
->
retvarno
);
else
if
(
stmt
->
retrowno
>=
0
)
else
if
(
stmt
->
expr
!=
NULL
)
printf
(
"row %d"
,
stmt
->
retrowno
);
else
if
(
stmt
->
expr
==
NULL
)
printf
(
"NULL"
);
else
dump_expr
(
stmt
->
expr
);
dump_expr
(
stmt
->
expr
);
else
printf
(
"NULL"
);
printf
(
"
\n
"
);
printf
(
"
\n
"
);
}
}
...
@@ -873,12 +871,12 @@ dump_return_next(PLpgSQL_stmt_return_next *stmt)
...
@@ -873,12 +871,12 @@ dump_return_next(PLpgSQL_stmt_return_next *stmt)
{
{
dump_ind
();
dump_ind
();
printf
(
"RETURN NEXT "
);
printf
(
"RETURN NEXT "
);
if
(
stmt
->
rec
!=
NULL
)
if
(
stmt
->
retvarno
>=
0
)
printf
(
"target = %d %s
\n
"
,
stmt
->
rec
->
recno
,
stmt
->
rec
->
refname
);
printf
(
"variable %d"
,
stmt
->
retvarno
);
else
if
(
stmt
->
row
!=
NULL
)
printf
(
"target = %d %s
\n
"
,
stmt
->
row
->
rowno
,
stmt
->
row
->
refname
);
else
if
(
stmt
->
expr
!=
NULL
)
else
if
(
stmt
->
expr
!=
NULL
)
dump_expr
(
stmt
->
expr
);
dump_expr
(
stmt
->
expr
);
else
printf
(
"NULL"
);
printf
(
"
\n
"
);
printf
(
"
\n
"
);
}
}
...
...
src/pl/plpgsql/src/plpgsql.h
View file @
fd97cf4d
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
* procedural language
* procedural language
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.5
7 2005/02/22 07:18:24 neilc
Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.5
8 2005/04/05 06:22:16 tgl
Exp $
*
*
* This software is copyrighted by Jan Wieck - Hamburg.
* This software is copyrighted by Jan Wieck - Hamburg.
*
*
...
@@ -491,17 +491,15 @@ typedef struct
...
@@ -491,17 +491,15 @@ typedef struct
int
cmd_type
;
int
cmd_type
;
int
lineno
;
int
lineno
;
PLpgSQL_expr
*
expr
;
PLpgSQL_expr
*
expr
;
int
retrecno
;
int
retvarno
;
int
retrowno
;
}
PLpgSQL_stmt_return
;
}
PLpgSQL_stmt_return
;
typedef
struct
typedef
struct
{
/* RETURN NEXT statement */
{
/* RETURN NEXT statement */
int
cmd_type
;
int
cmd_type
;
int
lineno
;
int
lineno
;
PLpgSQL_rec
*
rec
;
PLpgSQL_row
*
row
;
PLpgSQL_expr
*
expr
;
PLpgSQL_expr
*
expr
;
int
retvarno
;
}
PLpgSQL_stmt_return_next
;
}
PLpgSQL_stmt_return_next
;
typedef
struct
typedef
struct
...
@@ -572,6 +570,7 @@ typedef struct PLpgSQL_function
...
@@ -572,6 +570,7 @@ typedef struct PLpgSQL_function
int
fn_nargs
;
int
fn_nargs
;
int
fn_argvarnos
[
FUNC_MAX_ARGS
];
int
fn_argvarnos
[
FUNC_MAX_ARGS
];
int
out_param_varno
;
int
found_varno
;
int
found_varno
;
int
new_varno
;
int
new_varno
;
int
old_varno
;
int
old_varno
;
...
...
src/test/regress/expected/plpgsql.out
View file @
fd97cf4d
...
@@ -1738,6 +1738,125 @@ SELECT * FROM test_ret_rec_dyn(5) AS (a int, b numeric, c text);
...
@@ -1738,6 +1738,125 @@ SELECT * FROM test_ret_rec_dyn(5) AS (a int, b numeric, c text);
50 | 5 | xxx
50 | 5 | xxx
(1 row)
(1 row)
--
-- Test handling of OUT parameters, including polymorphic cases
--
-- wrong way to do it:
create function f1(in i int, out j int) returns int as $$
begin
return i+1;
end$$ language plpgsql;
ERROR: RETURN cannot have a parameter in function with OUT parameters at or near "i" at character 74
LINE 3: return i+1;
^
create function f1(in i int, out j int) as $$
begin
j := i+1;
return;
end$$ language plpgsql;
select f1(42);
f1
----
43
(1 row)
select * from f1(42);
f1
----
43
(1 row)
create or replace function f1(inout i int) as $$
begin
i := i+1;
return;
end$$ language plpgsql;
select f1(42);
f1
----
43
(1 row)
select * from f1(42);
f1
----
43
(1 row)
drop function f1(int);
create function f1(in i int, out j int) returns setof int as $$
begin
j := i+1;
return next;
j := i+2;
return next;
return;
end$$ language plpgsql;
select * from f1(42);
f1
----
43
44
(2 rows)
drop function f1(int);
create function f1(in i int, out j int, out k text) as $$
begin
j := i;
j := j+1;
k := 'foo';
return;
end$$ language plpgsql;
select f1(42);
f1
----------
(43,foo)
(1 row)
select * from f1(42);
j | k
----+-----
43 | foo
(1 row)
drop function f1(int);
create function f1(in i int, out j int, out k text) returns setof record as $$
begin
j := i+1;
k := 'foo';
return next;
j := j+1;
k := 'foot';
return next;
return;
end$$ language plpgsql;
select * from f1(42);
j | k
----+------
43 | foo
44 | foot
(2 rows)
drop function f1(int);
create function dup(in i anyelement, out j anyelement, out k anyarray) as $$
begin
j := i;
k := array[j,j];
return;
end$$ language plpgsql;
select * from dup(42);
j | k
----+---------
42 | {42,42}
(1 row)
select * from dup('foo'::text);
j | k
-----+-----------
foo | {foo,foo}
(1 row)
drop function dup(anyelement);
--
--
-- test PERFORM
-- test PERFORM
--
--
...
...
src/test/regress/sql/plpgsql.sql
View file @
fd97cf4d
...
@@ -1560,6 +1560,89 @@ END;' language 'plpgsql';
...
@@ -1560,6 +1560,89 @@ END;' language 'plpgsql';
SELECT
*
FROM
test_ret_rec_dyn
(
1500
)
AS
(
a
int
,
b
int
,
c
int
);
SELECT
*
FROM
test_ret_rec_dyn
(
1500
)
AS
(
a
int
,
b
int
,
c
int
);
SELECT
*
FROM
test_ret_rec_dyn
(
5
)
AS
(
a
int
,
b
numeric
,
c
text
);
SELECT
*
FROM
test_ret_rec_dyn
(
5
)
AS
(
a
int
,
b
numeric
,
c
text
);
--
-- Test handling of OUT parameters, including polymorphic cases
--
-- wrong way to do it:
create
function
f1
(
in
i
int
,
out
j
int
)
returns
int
as
$$
begin
return
i
+
1
;
end
$$
language
plpgsql
;
create
function
f1
(
in
i
int
,
out
j
int
)
as
$$
begin
j
:
=
i
+
1
;
return
;
end
$$
language
plpgsql
;
select
f1
(
42
);
select
*
from
f1
(
42
);
create
or
replace
function
f1
(
inout
i
int
)
as
$$
begin
i
:
=
i
+
1
;
return
;
end
$$
language
plpgsql
;
select
f1
(
42
);
select
*
from
f1
(
42
);
drop
function
f1
(
int
);
create
function
f1
(
in
i
int
,
out
j
int
)
returns
setof
int
as
$$
begin
j
:
=
i
+
1
;
return
next
;
j
:
=
i
+
2
;
return
next
;
return
;
end
$$
language
plpgsql
;
select
*
from
f1
(
42
);
drop
function
f1
(
int
);
create
function
f1
(
in
i
int
,
out
j
int
,
out
k
text
)
as
$$
begin
j
:
=
i
;
j
:
=
j
+
1
;
k
:
=
'foo'
;
return
;
end
$$
language
plpgsql
;
select
f1
(
42
);
select
*
from
f1
(
42
);
drop
function
f1
(
int
);
create
function
f1
(
in
i
int
,
out
j
int
,
out
k
text
)
returns
setof
record
as
$$
begin
j
:
=
i
+
1
;
k
:
=
'foo'
;
return
next
;
j
:
=
j
+
1
;
k
:
=
'foot'
;
return
next
;
return
;
end
$$
language
plpgsql
;
select
*
from
f1
(
42
);
drop
function
f1
(
int
);
create
function
dup
(
in
i
anyelement
,
out
j
anyelement
,
out
k
anyarray
)
as
$$
begin
j
:
=
i
;
k
:
=
array
[
j
,
j
];
return
;
end
$$
language
plpgsql
;
select
*
from
dup
(
42
);
select
*
from
dup
(
'foo'
::
text
);
drop
function
dup
(
anyelement
);
--
--
-- test PERFORM
-- test PERFORM
--
--
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment