Avoid an Assert failure in deconstruct_array() by making get_attstatsslot()

use the actual element type of the array it's disassembling, rather than
trusting the type OID passed in by its caller.  This is needed because
sometimes the planner passes in a type OID that's only binary-compatible
with the target column's type, rather than being an exact match.  Per an
example from Bernd Helmle.

Possibly we should refactor get_attstatsslot/free_attstatsslot to not expect
the caller to supply type ID data at all, but for now I'll just do the
minimum-change fix.

Back-patch to 7.4.  Bernd's test case only crashes back to 8.0, but since
these subroutines are the same in 7.4, I suspect there may be variant
cases that would crash 7.4 as well.
This commit is contained in:
Tom Lane 2010-07-09 22:58:07 +00:00
parent 473c29e6ae
commit 371a14255b

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.138.2.2 2007/10/13 15:55:49 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.138.2.3 2010/07/09 22:58:07 tgl Exp $
* *
* NOTES * NOTES
* Eventually, the index information should go through here, too. * Eventually, the index information should go through here, too.
@ -2058,6 +2058,10 @@ get_attavgwidth(Oid relid, AttrNumber attnum)
* If the attribute type is pass-by-reference, the values referenced by * If the attribute type is pass-by-reference, the values referenced by
* the values array are themselves palloc'd. The palloc'd stuff can be * the values array are themselves palloc'd. The palloc'd stuff can be
* freed by calling free_attstatsslot. * freed by calling free_attstatsslot.
*
* Note: at present, atttype/atttypmod aren't actually used here at all.
* But the caller must have the correct (or at least binary-compatible)
* type ID to pass to free_attstatsslot later.
*/ */
bool bool
get_attstatsslot(HeapTuple statstuple, get_attstatsslot(HeapTuple statstuple,
@ -2072,6 +2076,7 @@ get_attstatsslot(HeapTuple statstuple,
Datum val; Datum val;
bool isnull; bool isnull;
ArrayType *statarray; ArrayType *statarray;
Oid arrayelemtype;
int narrayelem; int narrayelem;
HeapTuple typeTuple; HeapTuple typeTuple;
Form_pg_type typeForm; Form_pg_type typeForm;
@ -2094,17 +2099,24 @@ get_attstatsslot(HeapTuple statstuple,
elog(ERROR, "stavalues is null"); elog(ERROR, "stavalues is null");
statarray = DatumGetArrayTypeP(val); statarray = DatumGetArrayTypeP(val);
/* Need to get info about the array element type */ /*
* Need to get info about the array element type. We look at the
* actual element type embedded in the array, which might be only
* binary-compatible with the passed-in atttype. The info we
* extract here should be the same either way, but deconstruct_array
* is picky about having an exact type OID match.
*/
arrayelemtype = ARR_ELEMTYPE(statarray);
typeTuple = SearchSysCache(TYPEOID, typeTuple = SearchSysCache(TYPEOID,
ObjectIdGetDatum(atttype), ObjectIdGetDatum(arrayelemtype),
0, 0, 0); 0, 0, 0);
if (!HeapTupleIsValid(typeTuple)) if (!HeapTupleIsValid(typeTuple))
elog(ERROR, "cache lookup failed for type %u", atttype); elog(ERROR, "cache lookup failed for type %u", arrayelemtype);
typeForm = (Form_pg_type) GETSTRUCT(typeTuple); typeForm = (Form_pg_type) GETSTRUCT(typeTuple);
/* Deconstruct array into Datum elements; NULLs not expected */ /* Deconstruct array into Datum elements; NULLs not expected */
deconstruct_array(statarray, deconstruct_array(statarray,
atttype, arrayelemtype,
typeForm->typlen, typeForm->typlen,
typeForm->typbyval, typeForm->typbyval,
typeForm->typalign, typeForm->typalign,