aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/tablecmds.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2012-06-30 16:43:58 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2012-06-30 16:45:27 -0400
commit52336a47024e8212be619fcfe28d384777ed7b72 (patch)
treecdcbfdabab860e907ad11556f4fa9a08b4a9b01c /src/backend/commands/tablecmds.c
parent31fff64ef522abeda9c26c0be7ffee32d27a1b3c (diff)
downloadpostgresql-52336a47024e8212be619fcfe28d384777ed7b72.tar.gz
postgresql-52336a47024e8212be619fcfe28d384777ed7b72.zip
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.
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r--src/backend/commands/tablecmds.c131
1 files changed, 22 insertions, 109 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 70753e33e43..d69809a2f86 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -68,6 +68,7 @@
#include "parser/parser.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteHandler.h"
+#include "rewrite/rewriteManip.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/lock.h"
@@ -253,7 +254,6 @@ static void truncate_check_rel(Relation rel);
static List *MergeAttributes(List *schema, List *supers, char relpersistence,
List **supOids, List **supconstr, int *supOidCount);
static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
-static bool change_varattnos_walker(Node *node, const AttrNumber *newattno);
static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
static void StoreCatalogInheritance(Oid relationId, List *supers);
@@ -1496,7 +1496,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
* parents after the first one, nor if we have dropped columns.)
*/
newattno = (AttrNumber *)
- palloc(tupleDesc->natts * sizeof(AttrNumber));
+ palloc0(tupleDesc->natts * sizeof(AttrNumber));
for (parent_attno = 1; parent_attno <= tupleDesc->natts;
parent_attno++)
@@ -1510,15 +1510,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
* Ignore dropped columns in the parent.
*/
if (attribute->attisdropped)
- {
- /*
- * change_varattnos_of_a_node asserts that this is greater
- * than zero, so if anything tries to use it, we should find
- * out.
- */
- newattno[parent_attno - 1] = 0;
- continue;
- }
+ continue; /* leave newattno entry as zero */
/*
* Does it conflict with some previously inherited column?
@@ -1656,14 +1648,30 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
{
char *name = check[i].ccname;
Node *expr;
+ bool found_whole_row;
/* ignore if the constraint is non-inheritable */
if (check[i].ccnoinherit)
continue;
- /* adjust varattnos of ccbin here */
- expr = stringToNode(check[i].ccbin);
- change_varattnos_of_a_node(expr, newattno);
+ /* Adjust Vars to match new table's column numbering */
+ expr = map_variable_attnos(stringToNode(check[i].ccbin),
+ 1, 0,
+ newattno, tupleDesc->natts,
+ &found_whole_row);
+
+ /*
+ * For the moment we have to reject whole-row variables.
+ * We could convert them, if we knew the new table's rowtype
+ * OID, but that hasn't been assigned yet.
+ */
+ if (found_whole_row)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert whole-row table reference"),
+ errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
+ name,
+ RelationGetRelationName(relation))));
/* check for duplicate */
if (!MergeCheckConstraint(constraints, name, expr))
@@ -1867,101 +1875,6 @@ MergeCheckConstraint(List *constraints, char *name, Node *expr)
/*
- * Replace varattno values in an expression tree according to the given
- * map array, that is, varattno N is replaced by newattno[N-1]. It is
- * caller's responsibility to ensure that the array is long enough to
- * define values for all user varattnos present in the tree. System column
- * attnos remain unchanged.
- *
- * Note that the passed node tree is modified in-place!
- */
-void
-change_varattnos_of_a_node(Node *node, const AttrNumber *newattno)
-{
- /* no setup needed, so away we go */
- (void) change_varattnos_walker(node, newattno);
-}
-
-static bool
-change_varattnos_walker(Node *node, const AttrNumber *newattno)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Var))
- {
- Var *var = (Var *) node;
-
- if (var->varlevelsup == 0 && var->varno == 1 &&
- var->varattno > 0)
- {
- /*
- * ??? the following may be a problem when the node is multiply
- * referenced though stringToNode() doesn't create such a node
- * currently.
- */
- Assert(newattno[var->varattno - 1] > 0);
- var->varattno = var->varoattno = newattno[var->varattno - 1];
- }
- return false;
- }
- return expression_tree_walker(node, change_varattnos_walker,
- (void *) newattno);
-}
-
-/*
- * Generate a map for change_varattnos_of_a_node from old and new TupleDesc's,
- * matching according to column name.
- */
-AttrNumber *
-varattnos_map(TupleDesc olddesc, TupleDesc newdesc)
-{
- AttrNumber *attmap;
- int i,
- j;
-
- attmap = (AttrNumber *) palloc0(sizeof(AttrNumber) * olddesc->natts);
- for (i = 1; i <= olddesc->natts; i++)
- {
- if (olddesc->attrs[i - 1]->attisdropped)
- continue; /* leave the entry as zero */
-
- for (j = 1; j <= newdesc->natts; j++)
- {
- if (strcmp(NameStr(olddesc->attrs[i - 1]->attname),
- NameStr(newdesc->attrs[j - 1]->attname)) == 0)
- {
- attmap[i - 1] = j;
- break;
- }
- }
- }
- return attmap;
-}
-
-/*
- * Generate a map for change_varattnos_of_a_node from a TupleDesc and a list
- * of ColumnDefs
- */
-AttrNumber *
-varattnos_map_schema(TupleDesc old, List *schema)
-{
- AttrNumber *attmap;
- int i;
-
- attmap = (AttrNumber *) palloc0(sizeof(AttrNumber) * old->natts);
- for (i = 1; i <= old->natts; i++)
- {
- if (old->attrs[i - 1]->attisdropped)
- continue; /* leave the entry as zero */
-
- attmap[i - 1] = findAttrByName(NameStr(old->attrs[i - 1]->attname),
- schema);
- }
- return attmap;
-}
-
-
-/*
* StoreCatalogInheritance
* Updates the system catalogs with proper inheritance information.
*