Commit 1345cc67 authored by Tom Lane's avatar Tom Lane

Use standard casting mechanism to convert types in plpgsql, when possible.

plpgsql's historical method for converting datatypes during assignments was
to apply the source type's output function and then the destination type's
input function.  Aside from being miserably inefficient in most cases, this
method failed outright in many cases where a user might expect it to work;
an example is that "declare x int; ... x := 3.9;" would fail, not round the
value to 4.

Instead, let's convert by applying the appropriate assignment cast whenever
there is one.  To avoid breaking compatibility unnecessarily, fall back to
the I/O conversion method if there is no assignment cast.

So far as I can tell, there is just one case where this method produces a
different result than the old code in a case where the old code would not
have thrown an error.  That is assignment of a boolean value to a string
variable (type text, varchar, or bpchar); the old way gave boolean's output
representation, ie 't'/'f', while the new way follows the behavior of the
bool-to-text cast and so gives 'true' or 'false'.  This will need to be
called out as an incompatibility in the 9.5 release notes.

Aside from handling many conversion cases more sanely, this method is
often significantly faster than the old way.  In part that's because
of more effective caching of the conversion info.
parent b9896198
...@@ -881,13 +881,14 @@ PREPARE <replaceable>statement_name</>(integer, integer) AS SELECT $1 &lt; $2; ...@@ -881,13 +881,14 @@ PREPARE <replaceable>statement_name</>(integer, integer) AS SELECT $1 &lt; $2;
<para> <para>
If the expression's result data type doesn't match the variable's If the expression's result data type doesn't match the variable's
data type, or the variable has a specific size/precision data type, the value will be coerced as though by an assignment cast
(like <type>char(20)</type>), the result value will be implicitly (see <xref linkend="typeconv-query">). If no assignment cast is known
converted by the <application>PL/pgSQL</application> interpreter using for the pair of data types involved, the <application>PL/pgSQL</>
the result type's output-function and interpreter will attempt to convert the result value textually, that is
the variable type's input-function. Note that this could potentially by applying the result type's output function followed by the variable
result in run-time errors generated by the input function, if the type's input function. Note that this could result in run-time errors
string form of the result value is not acceptable to the input function. generated by the input function, if the string form of the result value
is not acceptable to the input function.
</para> </para>
<para> <para>
......
...@@ -844,9 +844,10 @@ Check for an exact match with the target. ...@@ -844,9 +844,10 @@ Check for an exact match with the target.
<step performance="required"> <step performance="required">
<para> <para>
Otherwise, try to convert the expression to the target type. This will succeed Otherwise, try to convert the expression to the target type. This is possible
if there is a registered cast between the two types. if an <firstterm>assignment cast</> between the two types is registered in the
If the expression is an unknown-type literal, the contents of <structname>pg_cast</> catalog (see <xref linkend="sql-createcast">).
Alternatively, if the expression is an unknown-type literal, the contents of
the literal string will be fed to the input conversion routine for the target the literal string will be fed to the input conversion routine for the target
type. type.
</para> </para>
......
...@@ -559,8 +559,6 @@ do_compile(FunctionCallInfo fcinfo, ...@@ -559,8 +559,6 @@ do_compile(FunctionCallInfo fcinfo,
{ {
function->fn_retbyval = typeStruct->typbyval; function->fn_retbyval = typeStruct->typbyval;
function->fn_rettyplen = typeStruct->typlen; function->fn_rettyplen = typeStruct->typlen;
function->fn_rettypioparam = getTypeIOParam(typeTup);
fmgr_info(typeStruct->typinput, &(function->fn_retinput));
/* /*
* install $0 reference, but only for polymorphic return * install $0 reference, but only for polymorphic return
...@@ -803,7 +801,6 @@ plpgsql_compile_inline(char *proc_source) ...@@ -803,7 +801,6 @@ plpgsql_compile_inline(char *proc_source)
char *func_name = "inline_code_block"; char *func_name = "inline_code_block";
PLpgSQL_function *function; PLpgSQL_function *function;
ErrorContextCallback plerrcontext; ErrorContextCallback plerrcontext;
Oid typinput;
PLpgSQL_variable *var; PLpgSQL_variable *var;
int parse_rc; int parse_rc;
MemoryContext func_cxt; MemoryContext func_cxt;
...@@ -876,8 +873,6 @@ plpgsql_compile_inline(char *proc_source) ...@@ -876,8 +873,6 @@ plpgsql_compile_inline(char *proc_source)
/* a bit of hardwired knowledge about type VOID here */ /* a bit of hardwired knowledge about type VOID here */
function->fn_retbyval = true; function->fn_retbyval = true;
function->fn_rettyplen = sizeof(int32); function->fn_rettyplen = sizeof(int32);
getTypeInputInfo(VOIDOID, &typinput, &function->fn_rettypioparam);
fmgr_info(typinput, &(function->fn_retinput));
/* /*
* Remember if function is STABLE/IMMUTABLE. XXX would it be better to * Remember if function is STABLE/IMMUTABLE. XXX would it be better to
...@@ -2200,12 +2195,11 @@ build_datatype(HeapTuple typeTup, int32 typmod, Oid collation) ...@@ -2200,12 +2195,11 @@ build_datatype(HeapTuple typeTup, int32 typmod, Oid collation)
} }
typ->typlen = typeStruct->typlen; typ->typlen = typeStruct->typlen;
typ->typbyval = typeStruct->typbyval; typ->typbyval = typeStruct->typbyval;
typ->typtype = typeStruct->typtype;
typ->typrelid = typeStruct->typrelid; typ->typrelid = typeStruct->typrelid;
typ->typioparam = getTypeIOParam(typeTup);
typ->collation = typeStruct->typcollation; typ->collation = typeStruct->typcollation;
if (OidIsValid(collation) && OidIsValid(typ->collation)) if (OidIsValid(collation) && OidIsValid(typ->collation))
typ->collation = collation; typ->collation = collation;
fmgr_info(typeStruct->typinput, &(typ->typinput));
typ->atttypmod = typmod; typ->atttypmod = typmod;
return typ; return typ;
......
This diff is collapsed.
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "commands/event_trigger.h" #include "commands/event_trigger.h"
#include "commands/trigger.h" #include "commands/trigger.h"
#include "executor/spi.h" #include "executor/spi.h"
#include "utils/hsearch.h"
/********************************************************************** /**********************************************************************
* Definitions * Definitions
...@@ -178,10 +179,9 @@ typedef struct ...@@ -178,10 +179,9 @@ typedef struct
int ttype; /* PLPGSQL_TTYPE_ code */ int ttype; /* PLPGSQL_TTYPE_ code */
int16 typlen; /* stuff copied from its pg_type entry */ int16 typlen; /* stuff copied from its pg_type entry */
bool typbyval; bool typbyval;
char typtype;
Oid typrelid; Oid typrelid;
Oid typioparam;
Oid collation; /* from pg_type, but can be overridden */ Oid collation; /* from pg_type, but can be overridden */
FmgrInfo typinput; /* lookup info for typinput function */
int32 atttypmod; /* typmod (taken from someplace else) */ int32 atttypmod; /* typmod (taken from someplace else) */
} PLpgSQL_type; } PLpgSQL_type;
...@@ -709,8 +709,6 @@ typedef struct PLpgSQL_function ...@@ -709,8 +709,6 @@ typedef struct PLpgSQL_function
Oid fn_rettype; Oid fn_rettype;
int fn_rettyplen; int fn_rettyplen;
bool fn_retbyval; bool fn_retbyval;
FmgrInfo fn_retinput;
Oid fn_rettypioparam;
bool fn_retistuple; bool fn_retistuple;
bool fn_retset; bool fn_retset;
bool fn_readonly; bool fn_readonly;
...@@ -748,6 +746,9 @@ typedef struct PLpgSQL_function ...@@ -748,6 +746,9 @@ typedef struct PLpgSQL_function
PLpgSQL_datum **datums; PLpgSQL_datum **datums;
PLpgSQL_stmt_block *action; PLpgSQL_stmt_block *action;
/* table for performing casts needed in this function */
HTAB *cast_hash;
/* these fields change when the function is used */ /* these fields change when the function is used */
struct PLpgSQL_execstate *cur_estate; struct PLpgSQL_execstate *cur_estate;
unsigned long use_count; unsigned long use_count;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment