1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-27 07:42:10 +03:00

Prevent CREATE TABLE LIKE/INHERITS from (mis) copying whole-row Vars.

If a CHECK constraint or index definition contained a whole-row Var (that
is, "table.*"), an attempt to copy that definition via CREATE TABLE LIKE or
table inheritance produced incorrect results: the copied Var still claimed
to have the rowtype of the source table, rather than the created table.

For the LIKE case, it seems reasonable to just throw error for this
situation, since the point of LIKE is that the new table is not permanently
coupled to the old, so there's no reason to assume its rowtype will stay
compatible.  In the inheritance case, we should ideally allow such
constraints, but doing so will require nontrivial refactoring of CREATE
TABLE processing (because we'd need to know the OID of the new table's
rowtype before we adjust inherited CHECK constraints).  In view of the lack
of previous complaints, that doesn't seem worth the risk in a back-patched
bug fix, so just make it throw error for the inheritance case as well.

Along the way, replace change_varattnos_of_a_node() with a more robust
function map_variable_attnos(), which is capable of being extended to
handle insertion of ConvertRowtypeExpr whenever we get around to fixing
the inheritance case nicely, and in the meantime it returns a failure
indication to the caller so that a helpful message with some context can be
thrown.  Also, this code will do the right thing with subselects (if we
ever allow them in CHECK or indexes), and it range-checks varattnos before
using them to index into the map array.

Per report from Sergey Konoplev.  Back-patch to all supported branches.
This commit is contained in:
Tom Lane
2012-06-30 16:43:50 -04:00
parent e4ffa86b57
commit 541ffa65c3
5 changed files with 215 additions and 132 deletions

View File

@@ -1217,6 +1217,119 @@ replace_rte_variables_mutator(Node *node,
}
/*
* map_variable_attnos() finds all user-column Vars in an expression tree
* that reference a particular RTE, and adjusts their varattnos according
* to the given mapping array (varattno n is replaced by attno_map[n-1]).
* Vars for system columns are not modified.
*
* A zero in the mapping array represents a dropped column, which should not
* appear in the expression.
*
* If the expression tree contains a whole-row Var for the target RTE,
* the Var is not changed but *found_whole_row is returned as TRUE.
* For most callers this is an error condition, but we leave it to the caller
* to report the error so that useful context can be provided. (In some
* usages it would be appropriate to modify the Var's vartype and insert a
* ConvertRowtypeExpr node to map back to the original vartype. We might
* someday extend this function's API to support that. For now, the only
* concession to that future need is that this function is a tree mutator
* not just a walker.)
*
* This could be built using replace_rte_variables and a callback function,
* but since we don't ever need to insert sublinks, replace_rte_variables is
* overly complicated.
*/
typedef struct
{
int target_varno; /* RTE index to search for */
int sublevels_up; /* (current) nesting depth */
const AttrNumber *attno_map; /* map array for user attnos */
int map_length; /* number of entries in attno_map[] */
bool *found_whole_row; /* output flag */
} map_variable_attnos_context;
static Node *
map_variable_attnos_mutator(Node *node,
map_variable_attnos_context *context)
{
if (node == NULL)
return NULL;
if (IsA(node, Var))
{
Var *var = (Var *) node;
if (var->varno == context->target_varno &&
var->varlevelsup == context->sublevels_up)
{
/* Found a matching variable, make the substitution */
Var *newvar = (Var *) palloc(sizeof(Var));
int attno = var->varattno;
*newvar = *var;
if (attno > 0)
{
/* user-defined column, replace attno */
if (attno > context->map_length ||
context->attno_map[attno - 1] == 0)
elog(ERROR, "unexpected varattno %d in expression to be mapped",
attno);
newvar->varattno = newvar->varoattno = context->attno_map[attno - 1];
}
else if (attno == 0)
{
/* whole-row variable, warn caller */
*(context->found_whole_row) = true;
}
return (Node *) newvar;
}
/* otherwise fall through to copy the var normally */
}
else if (IsA(node, Query))
{
/* Recurse into RTE subquery or not-yet-planned sublink subquery */
Query *newnode;
context->sublevels_up++;
newnode = query_tree_mutator((Query *) node,
map_variable_attnos_mutator,
(void *) context,
0);
context->sublevels_up--;
return (Node *) newnode;
}
return expression_tree_mutator(node, map_variable_attnos_mutator,
(void *) context);
}
Node *
map_variable_attnos(Node *node,
int target_varno, int sublevels_up,
const AttrNumber *attno_map, int map_length,
bool *found_whole_row)
{
map_variable_attnos_context context;
context.target_varno = target_varno;
context.sublevels_up = sublevels_up;
context.attno_map = attno_map;
context.map_length = map_length;
context.found_whole_row = found_whole_row;
*found_whole_row = false;
/*
* Must be prepared to start with a Query or a bare expression tree; if
* it's a Query, we don't want to increment sublevels_up.
*/
return query_or_expression_tree_mutator(node,
map_variable_attnos_mutator,
(void *) &context,
0);
}
/*
* ResolveNew - replace Vars with corresponding items from a targetlist
*