[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