mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
Fix ALTER TABLE / SET TYPE for irregular inheritance
If inherited tables don't have exactly the same schema, the USING clause
in an ALTER TABLE / SET DATA TYPE misbehaves when applied to the
children tables since commit 9550e8348b. Starting with that commit,
the attribute numbers in the USING expression are fixed during parse
analysis. This can lead to bogus errors being reported during
execution, such as:
ERROR: attribute 2 has wrong type
DETAIL: Table has type smallint, but query expects integer.
Since it wouldn't do to revert to the original coding, we now apply a
transformation to map the attribute numbers to the correct ones for each
child.
Reported by Justin Pryzby
Analysis by Tom Lane; patch by me.
Discussion: https://postgr.es/m/20170102225618.GA10071@telsasoft.com
This commit is contained in:
@@ -206,55 +206,12 @@ convert_tuples_by_name(TupleDesc indesc,
|
||||
{
|
||||
TupleConversionMap *map;
|
||||
AttrNumber *attrMap;
|
||||
int n;
|
||||
int n = outdesc->natts;
|
||||
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))));
|
||||
}
|
||||
attrMap = convert_tuples_by_name_map(indesc, outdesc, msg);
|
||||
|
||||
/*
|
||||
* Check to see if the map is one-to-one and the tuple types are the same.
|
||||
@@ -312,6 +269,69 @@ convert_tuples_by_name(TupleDesc indesc,
|
||||
return map;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a palloc'd bare attribute map for tuple conversion, matching input
|
||||
* and output columns by name. (Dropped columns are ignored in both input and
|
||||
* output.) This is normally a subroutine for convert_tuples_by_name, but can
|
||||
* be used standalone.
|
||||
*/
|
||||
AttrNumber *
|
||||
convert_tuples_by_name_map(TupleDesc indesc,
|
||||
TupleDesc outdesc,
|
||||
const char *msg)
|
||||
{
|
||||
AttrNumber *attrMap;
|
||||
int n;
|
||||
int i;
|
||||
|
||||
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))));
|
||||
}
|
||||
|
||||
return attrMap;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform conversion of a tuple according to the map.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user