mirror of
https://github.com/postgres/postgres.git
synced 2025-06-16 06:01:02 +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:
@ -20,6 +20,7 @@
|
||||
#include "access/reloptions.h"
|
||||
#include "access/relscan.h"
|
||||
#include "access/sysattr.h"
|
||||
#include "access/tupconvert.h"
|
||||
#include "access/xact.h"
|
||||
#include "access/xlog.h"
|
||||
#include "catalog/catalog.h"
|
||||
@ -8540,12 +8541,69 @@ ATPrepAlterColumnType(List **wqueue,
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
/*
|
||||
* The recursion case is handled by ATSimpleRecursion. However, if we are
|
||||
* told not to recurse, there had better not be any child tables; else the
|
||||
* alter would put them out of step.
|
||||
* Recurse manually by queueing a new command for each child, if
|
||||
* necessary. We cannot apply ATSimpleRecursion here because we need to
|
||||
* remap attribute numbers in the USING expression, if any.
|
||||
*
|
||||
* If we are told not to recurse, there had better not be any child
|
||||
* tables; else the alter would put them out of step.
|
||||
*/
|
||||
if (recurse)
|
||||
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
|
||||
{
|
||||
Oid relid = RelationGetRelid(rel);
|
||||
ListCell *child;
|
||||
List *children;
|
||||
|
||||
children = find_all_inheritors(relid, lockmode, NULL);
|
||||
|
||||
/*
|
||||
* find_all_inheritors does the recursive search of the inheritance
|
||||
* hierarchy, so all we have to do is process all of the relids in the
|
||||
* list that it returns.
|
||||
*/
|
||||
foreach(child, children)
|
||||
{
|
||||
Oid childrelid = lfirst_oid(child);
|
||||
Relation childrel;
|
||||
|
||||
if (childrelid == relid)
|
||||
continue;
|
||||
|
||||
/* find_all_inheritors already got lock */
|
||||
childrel = relation_open(childrelid, NoLock);
|
||||
CheckTableNotInUse(childrel, "ALTER TABLE");
|
||||
|
||||
/*
|
||||
* Remap the attribute numbers. If no USING expression was
|
||||
* specified, there is no need for this step.
|
||||
*/
|
||||
if (def->cooked_default)
|
||||
{
|
||||
AttrNumber *attmap;
|
||||
bool found_whole_row;
|
||||
|
||||
/* create a copy to scribble on */
|
||||
cmd = copyObject(cmd);
|
||||
|
||||
attmap = convert_tuples_by_name_map(RelationGetDescr(childrel),
|
||||
RelationGetDescr(rel),
|
||||
gettext_noop("could not convert row type"));
|
||||
((ColumnDef *) cmd->def)->cooked_default =
|
||||
map_variable_attnos(def->cooked_default,
|
||||
1, 0,
|
||||
attmap, RelationGetDescr(rel)->natts,
|
||||
&found_whole_row);
|
||||
if (found_whole_row)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot convert whole-row table reference"),
|
||||
errdetail("USING expression contains a whole-row table reference.")));
|
||||
pfree(attmap);
|
||||
}
|
||||
ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode);
|
||||
relation_close(childrel, NoLock);
|
||||
}
|
||||
}
|
||||
else if (!recursing &&
|
||||
find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
|
||||
ereport(ERROR,
|
||||
|
Reference in New Issue
Block a user