[postgresql/f13/master] Add fix to make plpgsql cope with dropped columns in rowtypes

Tom Lane tgl at fedoraproject.org
Thu Apr 7 21:00:23 UTC 2011


commit 3052b0b191727bdc1329a32448806dd6e4602af4
Author: Tom Lane <tgl at redhat.com>
Date:   Thu Apr 7 16:57:03 2011 -0400

    Add fix to make plpgsql cope with dropped columns in rowtypes

 postgresql-rowtypes.patch |  764 +++++++++++++++++++++++++++++++++++++++++++++
 postgresql.spec           |   37 ++-
 2 files changed, 788 insertions(+), 13 deletions(-)
---
diff --git a/postgresql-rowtypes.patch b/postgresql-rowtypes.patch
new file mode 100644
index 0000000..42ca1b9
--- /dev/null
+++ b/postgresql-rowtypes.patch
@@ -0,0 +1,764 @@
+Upstream patch that fixes bug #694249 in the 8.4.x branch.
+This patch file will not be needed with 8.4.8 and later.
+
+
+commit 5d3853a7fa40b28b44b14084863fd83a188c9a9e
+Author: Tom Lane <tgl at sss.pgh.pa.us>
+Date:   Thu Apr 7 13:55:28 2011 -0400
+
+    Fix plpgsql's issues with dropped columns in rowtypes in 8.4 branch.
+    
+    This is a back-patch of commit dcb2bda9b7042dbf43f876c94ebf35d951de10e9 of
+    Aug 6 2009, which fixed assorted cases in which plpgsql would fail to cope
+    with composite types that contain any dropped columns.  Per discussion,
+    this fix has been out in 9.0 for long enough to make it improbable that it
+    creates any new bugs, so this is a low-risk fix.  To make it even lower
+    risk, I did not back-patch the changes in execQual.c, but just accepted
+    the duplication of code between there and tupconvert.c.  The added files
+    tupconvert.h and tupconvert.c match their current states in HEAD.
+
+diff --git a/src/backend/access/common/Makefile b/src/backend/access/common/Makefile
+index 9e05a6a..a80ee38 100644
+--- a/src/backend/access/common/Makefile
++++ b/src/backend/access/common/Makefile
+@@ -12,6 +12,7 @@ subdir = src/backend/access/common
+ top_builddir = ../../../..
+ include $(top_builddir)/src/Makefile.global
+ 
+-OBJS = heaptuple.o indextuple.o printtup.o reloptions.o scankey.o tupdesc.o  
++OBJS = heaptuple.o indextuple.o printtup.o reloptions.o scankey.o \
++	tupconvert.o tupdesc.o
+ 
+ include $(top_srcdir)/src/backend/common.mk
+diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c
+new file mode 100644
+index 0000000..34e5f11
+--- /dev/null
++++ b/src/backend/access/common/tupconvert.c
+@@ -0,0 +1,365 @@
++/*-------------------------------------------------------------------------
++ *
++ * tupconvert.c
++ *	  Tuple conversion support.
++ *
++ * These functions provide conversion between rowtypes that are logically
++ * equivalent but might have columns in a different order or different sets
++ * of dropped columns.	There is some overlap of functionality with the
++ * executor's "junkfilter" routines, but these functions work on bare
++ * HeapTuples rather than TupleTableSlots.
++ *
++ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
++ * Portions Copyright (c) 1994, Regents of the University of California
++ *
++ *
++ * IDENTIFICATION
++ *	  src/backend/access/common/tupconvert.c
++ *
++ *-------------------------------------------------------------------------
++ */
++#include "postgres.h"
++
++#include "access/tupconvert.h"
++#include "utils/builtins.h"
++
++
++/*
++ * The conversion setup routines have the following common API:
++ *
++ * The setup routine checks whether the given source and destination tuple
++ * descriptors are logically compatible.  If not, it throws an error.
++ * If so, it returns NULL if they are physically compatible (ie, no conversion
++ * is needed), else a TupleConversionMap that can be used by do_convert_tuple
++ * to perform the conversion.
++ *
++ * The TupleConversionMap, if needed, is palloc'd in the caller's memory
++ * context.  Also, the given tuple descriptors are referenced by the map,
++ * so they must survive as long as the map is needed.
++ *
++ * The caller must supply a suitable primary error message to be used if
++ * a compatibility error is thrown.  Recommended coding practice is to use
++ * gettext_noop() on this string, so that it is translatable but won't
++ * actually be translated unless the error gets thrown.
++ *
++ *
++ * Implementation notes:
++ *
++ * The key component of a TupleConversionMap is an attrMap[] array with
++ * one entry per output column.  This entry contains the 1-based index of
++ * the corresponding input column, or zero to force a NULL value (for
++ * a dropped output column).  The TupleConversionMap also contains workspace
++ * arrays.
++ */
++
++
++/*
++ * Set up for tuple conversion, matching input and output columns by
++ * position.  (Dropped columns are ignored in both input and output.)
++ *
++ * Note: the errdetail messages speak of indesc as the "returned" rowtype,
++ * outdesc as the "expected" rowtype.  This is okay for current uses but
++ * might need generalization in future.
++ */
++TupleConversionMap *
++convert_tuples_by_position(TupleDesc indesc,
++						   TupleDesc outdesc,
++						   const char *msg)
++{
++	TupleConversionMap *map;
++	AttrNumber *attrMap;
++	int			nincols;
++	int			noutcols;
++	int			n;
++	int			i;
++	int			j;
++	bool		same;
++
++	/* Verify compatibility and prepare attribute-number map */
++	n = outdesc->natts;
++	attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
++	j = 0;						/* j is next physical input attribute */
++	nincols = noutcols = 0;		/* these count non-dropped attributes */
++	same = true;
++	for (i = 0; i < n; i++)
++	{
++		Form_pg_attribute att = outdesc->attrs[i];
++		Oid			atttypid;
++		int32		atttypmod;
++
++		if (att->attisdropped)
++			continue;			/* attrMap[i] is already 0 */
++		noutcols++;
++		atttypid = att->atttypid;
++		atttypmod = att->atttypmod;
++		for (; j < indesc->natts; j++)
++		{
++			att = indesc->attrs[j];
++			if (att->attisdropped)
++				continue;
++			nincols++;
++			/* Found matching column, check type */
++			if (atttypid != att->atttypid ||
++				(atttypmod != att->atttypmod && atttypmod >= 0))
++				ereport(ERROR,
++						(errcode(ERRCODE_DATATYPE_MISMATCH),
++						 errmsg_internal("%s", _(msg)),
++						 errdetail("Returned type %s does not match expected type %s in column %d.",
++								   format_type_with_typemod(att->atttypid,
++															att->atttypmod),
++								   format_type_with_typemod(atttypid,
++															atttypmod),
++								   noutcols)));
++			attrMap[i] = (AttrNumber) (j + 1);
++			j++;
++			break;
++		}
++		if (attrMap[i] == 0)
++			same = false;		/* we'll complain below */
++	}
++
++	/* Check for unused input columns */
++	for (; j < indesc->natts; j++)
++	{
++		if (indesc->attrs[j]->attisdropped)
++			continue;
++		nincols++;
++		same = false;			/* we'll complain below */
++	}
++
++	/* Report column count mismatch using the non-dropped-column counts */
++	if (!same)
++		ereport(ERROR,
++				(errcode(ERRCODE_DATATYPE_MISMATCH),
++				 errmsg_internal("%s", _(msg)),
++				 errdetail("Number of returned columns (%d) does not match "
++						   "expected column count (%d).",
++						   nincols, noutcols)));
++
++	/*
++	 * Check to see if the map is one-to-one and the tuple types are the same.
++	 * (We check the latter because if they're not, we want to do conversion
++	 * to inject the right OID into the tuple datum.)
++	 */
++	if (indesc->natts == outdesc->natts &&
++		indesc->tdtypeid == outdesc->tdtypeid)
++	{
++		for (i = 0; i < n; i++)
++		{
++			if (attrMap[i] == (i + 1))
++				continue;
++
++			/*
++			 * If it's a dropped column and the corresponding input column is
++			 * also dropped, we needn't convert.  However, attlen and attalign
++			 * must agree.
++			 */
++			if (attrMap[i] == 0 &&
++				indesc->attrs[i]->attisdropped &&
++				indesc->attrs[i]->attlen == outdesc->attrs[i]->attlen &&
++				indesc->attrs[i]->attalign == outdesc->attrs[i]->attalign)
++				continue;
++
++			same = false;
++			break;
++		}
++	}
++	else
++		same = false;
++
++	if (same)
++	{
++		/* Runtime conversion is not needed */
++		pfree(attrMap);
++		return NULL;
++	}
++
++	/* Prepare the map structure */
++	map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
++	map->indesc = indesc;
++	map->outdesc = outdesc;
++	map->attrMap = attrMap;
++	/* preallocate workspace for Datum arrays */
++	map->outvalues = (Datum *) palloc(n * sizeof(Datum));
++	map->outisnull = (bool *) palloc(n * sizeof(bool));
++	n = indesc->natts + 1;		/* +1 for NULL */
++	map->invalues = (Datum *) palloc(n * sizeof(Datum));
++	map->inisnull = (bool *) palloc(n * sizeof(bool));
++	map->invalues[0] = (Datum) 0;		/* set up the NULL entry */
++	map->inisnull[0] = true;
++
++	return map;
++}
++
++/*
++ * Set up for tuple conversion, matching input and output columns by name.
++ * (Dropped columns are ignored in both input and output.)	This is intended
++ * for use when the rowtypes are related by inheritance, so we expect an exact
++ * match of both type and typmod.  The error messages will be a bit unhelpful
++ * unless both rowtypes are named composite types.
++ */
++TupleConversionMap *
++convert_tuples_by_name(TupleDesc indesc,
++					   TupleDesc outdesc,
++					   const char *msg)
++{
++	TupleConversionMap *map;
++	AttrNumber *attrMap;
++	int			n;
++	int			i;
++	bool		same;
++
++	/* Verify compatibility and prepare attribute-number map */
++	n = outdesc->natts;
++	attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
++	for (i = 0; i < n; i++)
++	{
++		Form_pg_attribute att = outdesc->attrs[i];
++		char	   *attname;
++		Oid			atttypid;
++		int32		atttypmod;
++		int			j;
++
++		if (att->attisdropped)
++			continue;			/* attrMap[i] is already 0 */
++		attname = NameStr(att->attname);
++		atttypid = att->atttypid;
++		atttypmod = att->atttypmod;
++		for (j = 0; j < indesc->natts; j++)
++		{
++			att = indesc->attrs[j];
++			if (att->attisdropped)
++				continue;
++			if (strcmp(attname, NameStr(att->attname)) == 0)
++			{
++				/* Found it, check type */
++				if (atttypid != att->atttypid || atttypmod != att->atttypmod)
++					ereport(ERROR,
++							(errcode(ERRCODE_DATATYPE_MISMATCH),
++							 errmsg_internal("%s", _(msg)),
++							 errdetail("Attribute \"%s\" of type %s does not match corresponding attribute of type %s.",
++									   attname,
++									   format_type_be(outdesc->tdtypeid),
++									   format_type_be(indesc->tdtypeid))));
++				attrMap[i] = (AttrNumber) (j + 1);
++				break;
++			}
++		}
++		if (attrMap[i] == 0)
++			ereport(ERROR,
++					(errcode(ERRCODE_DATATYPE_MISMATCH),
++					 errmsg_internal("%s", _(msg)),
++					 errdetail("Attribute \"%s\" of type %s does not exist in type %s.",
++							   attname,
++							   format_type_be(outdesc->tdtypeid),
++							   format_type_be(indesc->tdtypeid))));
++	}
++
++	/*
++	 * Check to see if the map is one-to-one and the tuple types are the same.
++	 * (We check the latter because if they're not, we want to do conversion
++	 * to inject the right OID into the tuple datum.)
++	 */
++	if (indesc->natts == outdesc->natts &&
++		indesc->tdtypeid == outdesc->tdtypeid)
++	{
++		same = true;
++		for (i = 0; i < n; i++)
++		{
++			if (attrMap[i] == (i + 1))
++				continue;
++
++			/*
++			 * If it's a dropped column and the corresponding input column is
++			 * also dropped, we needn't convert.  However, attlen and attalign
++			 * must agree.
++			 */
++			if (attrMap[i] == 0 &&
++				indesc->attrs[i]->attisdropped &&
++				indesc->attrs[i]->attlen == outdesc->attrs[i]->attlen &&
++				indesc->attrs[i]->attalign == outdesc->attrs[i]->attalign)
++				continue;
++
++			same = false;
++			break;
++		}
++	}
++	else
++		same = false;
++
++	if (same)
++	{
++		/* Runtime conversion is not needed */
++		pfree(attrMap);
++		return NULL;
++	}
++
++	/* Prepare the map structure */
++	map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
++	map->indesc = indesc;
++	map->outdesc = outdesc;
++	map->attrMap = attrMap;
++	/* preallocate workspace for Datum arrays */
++	map->outvalues = (Datum *) palloc(n * sizeof(Datum));
++	map->outisnull = (bool *) palloc(n * sizeof(bool));
++	n = indesc->natts + 1;		/* +1 for NULL */
++	map->invalues = (Datum *) palloc(n * sizeof(Datum));
++	map->inisnull = (bool *) palloc(n * sizeof(bool));
++	map->invalues[0] = (Datum) 0;		/* set up the NULL entry */
++	map->inisnull[0] = true;
++
++	return map;
++}
++
++/*
++ * Perform conversion of a tuple according to the map.
++ */
++HeapTuple
++do_convert_tuple(HeapTuple tuple, TupleConversionMap *map)
++{
++	AttrNumber *attrMap = map->attrMap;
++	Datum	   *invalues = map->invalues;
++	bool	   *inisnull = map->inisnull;
++	Datum	   *outvalues = map->outvalues;
++	bool	   *outisnull = map->outisnull;
++	int			outnatts = map->outdesc->natts;
++	int			i;
++
++	/*
++	 * Extract all the values of the old tuple, offsetting the arrays so that
++	 * invalues[0] is left NULL and invalues[1] is the first source attribute;
++	 * this exactly matches the numbering convention in attrMap.
++	 */
++	heap_deform_tuple(tuple, map->indesc, invalues + 1, inisnull + 1);
++
++	/*
++	 * Transpose into proper fields of the new tuple.
++	 */
++	for (i = 0; i < outnatts; i++)
++	{
++		int			j = attrMap[i];
++
++		outvalues[i] = invalues[j];
++		outisnull[i] = inisnull[j];
++	}
++
++	/*
++	 * Now form the new tuple.
++	 */
++	return heap_form_tuple(map->outdesc, outvalues, outisnull);
++}
++
++/*
++ * Free a TupleConversionMap structure.
++ */
++void
++free_conversion_map(TupleConversionMap *map)
++{
++	/* indesc and outdesc are not ours to free */
++	pfree(map->attrMap);
++	pfree(map->invalues);
++	pfree(map->inisnull);
++	pfree(map->outvalues);
++	pfree(map->outisnull);
++	pfree(map);
++}
+diff --git a/src/include/access/tupconvert.h b/src/include/access/tupconvert.h
+new file mode 100644
+index 0000000..ab79f09
+--- /dev/null
++++ b/src/include/access/tupconvert.h
+@@ -0,0 +1,44 @@
++/*-------------------------------------------------------------------------
++ *
++ * tupconvert.h
++ *	  Tuple conversion support.
++ *
++ *
++ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
++ * Portions Copyright (c) 1994, Regents of the University of California
++ *
++ * src/include/access/tupconvert.h
++ *
++ *-------------------------------------------------------------------------
++ */
++#ifndef TUPCONVERT_H
++#define TUPCONVERT_H
++
++#include "access/htup.h"
++
++
++typedef struct TupleConversionMap
++{
++	TupleDesc	indesc;			/* tupdesc for source rowtype */
++	TupleDesc	outdesc;		/* tupdesc for result rowtype */
++	AttrNumber *attrMap;		/* indexes of input fields, or 0 for null */
++	Datum	   *invalues;		/* workspace for deconstructing source */
++	bool	   *inisnull;
++	Datum	   *outvalues;		/* workspace for constructing result */
++	bool	   *outisnull;
++} TupleConversionMap;
++
++
++extern TupleConversionMap *convert_tuples_by_position(TupleDesc indesc,
++						   TupleDesc outdesc,
++						   const char *msg);
++
++extern TupleConversionMap *convert_tuples_by_name(TupleDesc indesc,
++					   TupleDesc outdesc,
++					   const char *msg);
++
++extern HeapTuple do_convert_tuple(HeapTuple tuple, TupleConversionMap *map);
++
++extern void free_conversion_map(TupleConversionMap *map);
++
++#endif   /* TUPCONVERT_H */
+diff --git a/src/pl/plpgsql/src/nls.mk b/src/pl/plpgsql/src/nls.mk
+index c1195c3..de0d6c4 100644
+--- a/src/pl/plpgsql/src/nls.mk
++++ b/src/pl/plpgsql/src/nls.mk
+@@ -2,7 +2,7 @@
+ CATALOG_NAME	:= plpgsql
+ AVAIL_LANGUAGES	:= de es fr it ja ko ro pt_BR zh_CN zh_TW
+ GETTEXT_FILES	:= pl_comp.c pl_exec.c pl_gram.c pl_funcs.c pl_handler.c pl_scan.c
+-GETTEXT_TRIGGERS:= _ errmsg errmsg_plural:1,2 errdetail errdetail_log errdetail_plural:1,2 errhint errcontext validate_tupdesc_compat:3 yyerror plpgsql_yyerror
++GETTEXT_TRIGGERS:= _ errmsg errmsg_plural:1,2 errdetail errdetail_log errdetail_plural:1,2 errhint errcontext yyerror plpgsql_yyerror
+ 
+ .PHONY: gettext-files
+ gettext-files: distprep
+diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
+index 104765b..1374090 100644
+--- a/src/pl/plpgsql/src/pl_exec.c
++++ b/src/pl/plpgsql/src/pl_exec.c
+@@ -18,6 +18,7 @@
+ #include <ctype.h>
+ 
+ #include "access/transam.h"
++#include "access/tupconvert.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_type.h"
+ #include "executor/spi_priv.h"
+@@ -189,8 +190,6 @@ static Datum exec_simple_cast_value(Datum value, Oid valtype,
+ 					   Oid reqtype, int32 reqtypmod,
+ 					   bool isnull);
+ static void exec_init_tuple_store(PLpgSQL_execstate *estate);
+-static void validate_tupdesc_compat(TupleDesc expected, TupleDesc returned,
+-						const char *msg);
+ static void exec_set_found(PLpgSQL_execstate *estate, bool state);
+ static void plpgsql_create_econtext(PLpgSQL_execstate *estate);
+ static void plpgsql_destroy_econtext(PLpgSQL_execstate *estate);
+@@ -381,14 +380,21 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
+ 			 * expected result type.  XXX would be better to cache the tupdesc
+ 			 * instead of repeating get_call_result_type()
+ 			 */
++			HeapTuple	rettup = (HeapTuple) DatumGetPointer(estate.retval);
+ 			TupleDesc	tupdesc;
++			TupleConversionMap *tupmap;
+ 
+ 			switch (get_call_result_type(fcinfo, NULL, &tupdesc))
+ 			{
+ 				case TYPEFUNC_COMPOSITE:
+ 					/* got the expected result rowtype, now check it */
+-					validate_tupdesc_compat(tupdesc, estate.rettupdesc,
+-											"returned record type does not match expected record type");
++					tupmap = convert_tuples_by_position(estate.rettupdesc,
++														tupdesc,
++														gettext_noop("returned record type does not match expected record type"));
++					/* it might need conversion */
++					if (tupmap)
++						rettup = do_convert_tuple(rettup, tupmap);
++					/* no need to free map, we're about to return anyway */
+ 					break;
+ 				case TYPEFUNC_RECORD:
+ 
+@@ -413,9 +419,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
+ 			 * Copy tuple to upper executor memory, as a tuple Datum. Make
+ 			 * sure it is labeled with the caller-supplied tuple type.
+ 			 */
+-			estate.retval =
+-				PointerGetDatum(SPI_returntuple((HeapTuple) DatumGetPointer(estate.retval),
+-												tupdesc));
++			estate.retval = PointerGetDatum(SPI_returntuple(rettup, tupdesc));
+ 		}
+ 		else
+ 		{
+@@ -704,11 +708,20 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
+ 		rettup = NULL;
+ 	else
+ 	{
+-		validate_tupdesc_compat(trigdata->tg_relation->rd_att,
+-								estate.rettupdesc,
+-								"returned row structure does not match the structure of the triggering table");
++		TupleConversionMap *tupmap;
++
++		rettup = (HeapTuple) DatumGetPointer(estate.retval);
++		/* check rowtype compatibility */
++		tupmap = convert_tuples_by_position(estate.rettupdesc,
++											trigdata->tg_relation->rd_att,
++											gettext_noop("returned row structure does not match the structure of the triggering table"));
++		/* it might need conversion */
++		if (tupmap)
++			rettup = do_convert_tuple(rettup, tupmap);
++		/* no need to free map, we're about to return anyway */
++
+ 		/* Copy tuple to upper executor memory */
+-		rettup = SPI_copytuple((HeapTuple) DatumGetPointer(estate.retval));
++		rettup = SPI_copytuple(rettup);
+ 	}
+ 
+ 	/*
+@@ -2190,6 +2203,7 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
+ 			case PLPGSQL_DTYPE_REC:
+ 				{
+ 					PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar;
++					TupleConversionMap *tupmap;
+ 
+ 					if (!HeapTupleIsValid(rec->tup))
+ 						ereport(ERROR,
+@@ -2198,9 +2212,16 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
+ 								  rec->refname),
+ 						errdetail("The tuple structure of a not-yet-assigned"
+ 								  " record is indeterminate.")));
+-					validate_tupdesc_compat(tupdesc, rec->tupdesc,
+-								"wrong record type supplied in RETURN NEXT");
++					tupmap = convert_tuples_by_position(rec->tupdesc,
++														tupdesc,
++														gettext_noop("wrong record type supplied in RETURN NEXT"));
+ 					tuple = rec->tup;
++					/* it might need conversion */
++					if (tupmap)
++					{
++						tuple = do_convert_tuple(tuple, tupmap);
++						free_conversion_map(tupmap);
++					}
+ 				}
+ 				break;
+ 
+@@ -2280,6 +2301,7 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
+ {
+ 	Portal		portal;
+ 	uint32		processed = 0;
++	TupleConversionMap *tupmap;
+ 
+ 	if (!estate->retisset)
+ 		ereport(ERROR,
+@@ -2302,8 +2324,9 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
+ 										   stmt->params);
+ 	}
+ 
+-	validate_tupdesc_compat(estate->rettupdesc, portal->tupDesc,
+-				   "structure of query does not match function result type");
++	tupmap = convert_tuples_by_position(portal->tupDesc,
++										estate->rettupdesc,
++										gettext_noop("structure of query does not match function result type"));
+ 
+ 	while (true)
+ 	{
+@@ -2317,13 +2340,20 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
+ 		{
+ 			HeapTuple	tuple = SPI_tuptable->vals[i];
+ 
++			if (tupmap)
++				tuple = do_convert_tuple(tuple, tupmap);
+ 			tuplestore_puttuple(estate->tuple_store, tuple);
++			if (tupmap)
++				heap_freetuple(tuple);
+ 			processed++;
+ 		}
+ 
+ 		SPI_freetuptable(SPI_tuptable);
+ 	}
+ 
++	if (tupmap)
++		free_conversion_map(tupmap);
++
+ 	SPI_freetuptable(SPI_tuptable);
+ 	SPI_cursor_close(portal);
+ 
+@@ -5217,45 +5247,6 @@ exec_simple_check_plan(PLpgSQL_expr *expr)
+ 	expr->expr_simple_type = exprType((Node *) tle->expr);
+ }
+ 
+-/*
+- * Validates compatibility of supplied TupleDesc pair by checking number and type
+- * of attributes.
+- */
+-static void
+-validate_tupdesc_compat(TupleDesc expected, TupleDesc returned, const char *msg)
+-{
+-	int			i;
+-	const char *dropped_column_type = gettext_noop("N/A (dropped column)");
+-
+-	if (!expected || !returned)
+-		ereport(ERROR,
+-				(errcode(ERRCODE_DATATYPE_MISMATCH),
+-				 errmsg("%s", _(msg))));
+-
+-	if (expected->natts != returned->natts)
+-		ereport(ERROR,
+-				(errcode(ERRCODE_DATATYPE_MISMATCH),
+-				 errmsg("%s", _(msg)),
+-				 errdetail("Number of returned columns (%d) does not match "
+-						   "expected column count (%d).",
+-						   returned->natts, expected->natts)));
+-
+-	for (i = 0; i < expected->natts; i++)
+-		if (expected->attrs[i]->atttypid != returned->attrs[i]->atttypid)
+-			ereport(ERROR,
+-					(errcode(ERRCODE_DATATYPE_MISMATCH),
+-					 errmsg("%s", _(msg)),
+-				   errdetail("Returned type %s does not match expected type "
+-							 "%s in column \"%s\".",
+-							 OidIsValid(returned->attrs[i]->atttypid) ?
+-							 format_type_be(returned->attrs[i]->atttypid) :
+-							 _(dropped_column_type),
+-							 OidIsValid(expected->attrs[i]->atttypid) ?
+-							 format_type_be(expected->attrs[i]->atttypid) :
+-							 _(dropped_column_type),
+-							 NameStr(expected->attrs[i]->attname))));
+-}
+-
+ /* ----------
+  * exec_set_found			Set the global found variable
+  *					to true/false
+diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
+index 163268c..bbcdf12 100644
+--- a/src/test/regress/expected/plpgsql.out
++++ b/src/test/regress/expected/plpgsql.out
+@@ -3285,6 +3285,57 @@ select * from return_dquery();
+ (4 rows)
+ 
+ drop function return_dquery();
++-- test RETURN QUERY with dropped columns
++create table tabwithcols(a int, b int, c int, d int);
++insert into tabwithcols values(10,20,30,40),(50,60,70,80);
++create or replace function returnqueryf()
++returns setof tabwithcols as $$
++begin
++  return query select * from tabwithcols;
++  return query execute 'select * from tabwithcols';
++end;
++$$ language plpgsql;
++select * from returnqueryf();
++ a  | b  | c  | d  
++----+----+----+----
++ 10 | 20 | 30 | 40
++ 50 | 60 | 70 | 80
++ 10 | 20 | 30 | 40
++ 50 | 60 | 70 | 80
++(4 rows)
++
++alter table tabwithcols drop column b;
++select * from returnqueryf();
++ a  | c  | d  
++----+----+----
++ 10 | 30 | 40
++ 50 | 70 | 80
++ 10 | 30 | 40
++ 50 | 70 | 80
++(4 rows)
++
++alter table tabwithcols drop column d;
++select * from returnqueryf();
++ a  | c  
++----+----
++ 10 | 30
++ 50 | 70
++ 10 | 30
++ 50 | 70
++(4 rows)
++
++alter table tabwithcols add column d int;
++select * from returnqueryf();
++ a  | c  | d 
++----+----+---
++ 10 | 30 |  
++ 50 | 70 |  
++ 10 | 30 |  
++ 50 | 70 |  
++(4 rows)
++
++drop function returnqueryf();
++drop table tabwithcols;
+ -- Tests for 8.4's new RAISE features
+ create or replace function raise_test() returns void as $$
+ begin
+diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
+index 48bb538..2241c1e 100644
+--- a/src/test/regress/sql/plpgsql.sql
++++ b/src/test/regress/sql/plpgsql.sql
+@@ -2684,6 +2684,36 @@ select * from return_dquery();
+ 
+ drop function return_dquery();
+ 
++-- test RETURN QUERY with dropped columns
++
++create table tabwithcols(a int, b int, c int, d int);
++insert into tabwithcols values(10,20,30,40),(50,60,70,80);
++
++create or replace function returnqueryf()
++returns setof tabwithcols as $$
++begin
++  return query select * from tabwithcols;
++  return query execute 'select * from tabwithcols';
++end;
++$$ language plpgsql;
++
++select * from returnqueryf();
++
++alter table tabwithcols drop column b;
++
++select * from returnqueryf();
++
++alter table tabwithcols drop column d;
++
++select * from returnqueryf();
++
++alter table tabwithcols add column d int;
++
++select * from returnqueryf();
++
++drop function returnqueryf();
++drop table tabwithcols;
++
+ -- Tests for 8.4's new RAISE features
+ 
+ create or replace function raise_test() returns void as $$
diff --git a/postgresql.spec b/postgresql.spec
index 3ef383c..fafdf1c 100755
--- a/postgresql.spec
+++ b/postgresql.spec
@@ -53,12 +53,12 @@ Summary: PostgreSQL client programs
 Name: postgresql
 %global majorversion 8.4
 Version: 8.4.7
-Release: 1%{?dist}
+Release: 2%{?dist}
 # The PostgreSQL license is very similar to other MIT licenses, but the OSI
 # recognizes it as an independent license, so we do as well.
 License: PostgreSQL
 Group: Applications/Databases
-Url: http://www.postgresql.org/ 
+Url: http://www.postgresql.org/
 
 Source0: ftp://ftp.postgresql.org/pub/source/v%{version}/postgresql-%{version}.tar.bz2
 # The PDF file is generated by generate-pdf.sh, which see for comments
@@ -80,6 +80,7 @@ Patch2: postgresql-ac-version.patch
 Patch3: postgresql-logging.patch
 Patch4: postgresql-oom-adj.patch
 Patch6: postgresql-perl-rpath.patch
+Patch7: postgresql-rowtypes.patch
 
 BuildRequires: perl(ExtUtils::MakeMaker) glibc-devel bison flex autoconf gawk
 BuildRequires: perl(ExtUtils::Embed), perl-devel
@@ -134,7 +135,7 @@ BuildRequires: systemtap-sdt-devel
 %endif
 
 # main package requires -libs subpackage
-Requires: postgresql-libs = %{version}-%{release}
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
 
 Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root
 
@@ -170,7 +171,8 @@ PostgreSQL server.
 %package server
 Summary: The programs needed to create and run a PostgreSQL server
 Group: Applications/Databases
-Requires: %{name} = %{version}-%{release}
+Requires: %{name}%{?_isa} = %{version}-%{release}
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
 Requires(pre): /usr/sbin/useradd
 Requires(post): chkconfig
 Requires(preun): chkconfig
@@ -193,7 +195,7 @@ to install the postgresql package.
 %package docs
 Summary: Extra documentation for PostgreSQL
 Group: Applications/Databases
-Requires: postgresql = %{version}-%{release}
+Requires: %{name}%{?_isa} = %{version}-%{release}
 
 %description docs
 The postgresql-docs package includes some additional documentation for
@@ -204,7 +206,8 @@ and source files for the PostgreSQL tutorial.
 %package contrib
 Summary: Contributed modules distributed with PostgreSQL
 Group: Applications/Databases
-Requires: postgresql = %{version}-%{release}
+Requires: %{name}%{?_isa} = %{version}-%{release}
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
 
 %description contrib
 The postgresql-contrib package contains contributed packages that are
@@ -214,7 +217,8 @@ included in the PostgreSQL distribution.
 %package devel
 Summary: PostgreSQL development header files and libraries
 Group: Development/Libraries
-Requires: postgresql = %{version}-%{release}
+Requires: %{name}%{?_isa} = %{version}-%{release}
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
 
 %description devel
 The postgresql-devel package contains the header files and libraries
@@ -227,7 +231,7 @@ develop applications which will interact with a PostgreSQL server.
 %package plperl
 Summary: The Perl procedural language for PostgreSQL
 Group: Applications/Databases
-Requires: postgresql-server = %{version}-%{release}
+Requires: %{name}-server%{?_isa} = %{version}-%{release}
 Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
 
 %description plperl
@@ -240,7 +244,7 @@ procedural language for the backend.
 %package plpython
 Summary: The Python procedural language for PostgreSQL
 Group: Applications/Databases
-Requires: postgresql-server = %{version}-%{release}
+Requires: %{name}-server%{?_isa} = %{version}-%{release}
 
 %description plpython
 PostgreSQL is an advanced Object-Relational database management
@@ -252,7 +256,7 @@ procedural language for the backend.
 %package pltcl
 Summary: The Tcl procedural language for PostgreSQL
 Group: Applications/Databases
-Requires: postgresql-server = %{version}-%{release}
+Requires: %{name}-server%{?_isa} = %{version}-%{release}
 
 %description pltcl
 PostgreSQL is an advanced Object-Relational database management
@@ -264,7 +268,7 @@ procedural language for the backend.
 %package test
 Summary: The test suite distributed with PostgreSQL
 Group: Applications/Databases
-Requires: postgresql-server = %{version}-%{release}
+Requires: %{name}-server%{?_isa} = %{version}-%{release}
 
 %description test
 PostgreSQL is an advanced Object-Relational database management
@@ -282,6 +286,7 @@ system, including regression tests and benchmarks.
 %patch3 -p1
 %patch4 -p1
 %patch6 -p1
+%patch7 -p1
 
 autoconf
 
@@ -310,9 +315,9 @@ CFLAGS=`echo $CFLAGS|xargs -n 1|grep -v ffast-math|xargs -n 100`
 CFLAGS="$CFLAGS -DLINUX_OOM_ADJ=0"
 # let's try removing this kluge, it may just be a workaround for bz#520916
 # # use -O1 on sparc64 and alpha
-# %ifarch sparc64 alpha
+# %%ifarch sparc64 alpha
 # CFLAGS=`echo $CFLAGS| sed -e "s|-O2|-O1|g" `
-# %endif
+# %%endif
 
 %configure --disable-rpath \
 %if %beta
@@ -716,6 +721,12 @@ rm -rf $RPM_BUILD_ROOT
 %endif
 
 %changelog
+* Thu Apr  7 2011 Tom Lane <tgl at redhat.com> 8.4.7-2
+- Add fix to make plpgsql cope with dropped columns in rowtypes
+Related: #694249
+- Add %%{?_isa} to cross-subpackage Requires, per latest packaging guidelines,
+  and add explicit dependencies on the -libs subpackage to pacify rpmdiff
+
 * Tue Feb  1 2011 Tom Lane <tgl at redhat.com> 8.4.7-1
 - Update to PostgreSQL 8.4.7, for various fixes described at
   http://www.postgresql.org/docs/8.4/static/release-8-4-7.html


More information about the scm-commits mailing list