Skip to content

Commit 1345cc6

Browse files
committed
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.
1 parent b989619 commit 1345cc6

File tree

5 files changed

+241
-131
lines changed

5 files changed

+241
-131
lines changed

doc/src/sgml/plpgsql.sgml

+8-7
Original file line numberDiff line numberDiff line change
@@ -881,13 +881,14 @@ PREPARE <replaceable>statement_name</>(integer, integer) AS SELECT $1 &lt; $2;
881881

882882
<para>
883883
If the expression's result data type doesn't match the variable's
884-
data type, or the variable has a specific size/precision
885-
(like <type>char(20)</type>), the result value will be implicitly
886-
converted by the <application>PL/pgSQL</application> interpreter using
887-
the result type's output-function and
888-
the variable type's input-function. Note that this could potentially
889-
result in run-time errors generated by the input function, if the
890-
string form of the result value is not acceptable to the input function.
884+
data type, the value will be coerced as though by an assignment cast
885+
(see <xref linkend="typeconv-query">). If no assignment cast is known
886+
for the pair of data types involved, the <application>PL/pgSQL</>
887+
interpreter will attempt to convert the result value textually, that is
888+
by applying the result type's output function followed by the variable
889+
type's input function. Note that this could result in run-time errors
890+
generated by the input function, if the string form of the result value
891+
is not acceptable to the input function.
891892
</para>
892893

893894
<para>

doc/src/sgml/typeconv.sgml

+4-3
Original file line numberDiff line numberDiff line change
@@ -844,9 +844,10 @@ Check for an exact match with the target.
844844

845845
<step performance="required">
846846
<para>
847-
Otherwise, try to convert the expression to the target type. This will succeed
848-
if there is a registered cast between the two types.
849-
If the expression is an unknown-type literal, the contents of
847+
Otherwise, try to convert the expression to the target type. This is possible
848+
if an <firstterm>assignment cast</> between the two types is registered in the
849+
<structname>pg_cast</> catalog (see <xref linkend="sql-createcast">).
850+
Alternatively, if the expression is an unknown-type literal, the contents of
850851
the literal string will be fed to the input conversion routine for the target
851852
type.
852853
</para>

src/pl/plpgsql/src/pl_comp.c

+1-7
Original file line numberDiff line numberDiff line change
@@ -559,8 +559,6 @@ do_compile(FunctionCallInfo fcinfo,
559559
{
560560
function->fn_retbyval = typeStruct->typbyval;
561561
function->fn_rettyplen = typeStruct->typlen;
562-
function->fn_rettypioparam = getTypeIOParam(typeTup);
563-
fmgr_info(typeStruct->typinput, &(function->fn_retinput));
564562

565563
/*
566564
* install $0 reference, but only for polymorphic return
@@ -803,7 +801,6 @@ plpgsql_compile_inline(char *proc_source)
803801
char *func_name = "inline_code_block";
804802
PLpgSQL_function *function;
805803
ErrorContextCallback plerrcontext;
806-
Oid typinput;
807804
PLpgSQL_variable *var;
808805
int parse_rc;
809806
MemoryContext func_cxt;
@@ -876,8 +873,6 @@ plpgsql_compile_inline(char *proc_source)
876873
/* a bit of hardwired knowledge about type VOID here */
877874
function->fn_retbyval = true;
878875
function->fn_rettyplen = sizeof(int32);
879-
getTypeInputInfo(VOIDOID, &typinput, &function->fn_rettypioparam);
880-
fmgr_info(typinput, &(function->fn_retinput));
881876

882877
/*
883878
* Remember if function is STABLE/IMMUTABLE. XXX would it be better to
@@ -2200,12 +2195,11 @@ build_datatype(HeapTuple typeTup, int32 typmod, Oid collation)
22002195
}
22012196
typ->typlen = typeStruct->typlen;
22022197
typ->typbyval = typeStruct->typbyval;
2198+
typ->typtype = typeStruct->typtype;
22032199
typ->typrelid = typeStruct->typrelid;
2204-
typ->typioparam = getTypeIOParam(typeTup);
22052200
typ->collation = typeStruct->typcollation;
22062201
if (OidIsValid(collation) && OidIsValid(typ->collation))
22072202
typ->collation = collation;
2208-
fmgr_info(typeStruct->typinput, &(typ->typinput));
22092203
typ->atttypmod = typmod;
22102204

22112205
return typ;

0 commit comments

Comments
 (0)