aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_utilcmd.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2012-06-30 16:43:50 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2012-06-30 16:45:14 -0400
commit541ffa65c32b278da8ab2d19433fa6d37bd15c8d (patch)
tree5a248ba1c4e33f44e6452261edf3241edcc90bb2 /src/backend/parser/parse_utilcmd.c
parente4ffa86b5739fdf85b1050c4b2e26ab14ef476e9 (diff)
downloadpostgresql-541ffa65c32b278da8ab2d19433fa6d37bd15c8d.tar.gz
postgresql-541ffa65c32b278da8ab2d19433fa6d37bd15c8d.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/parser/parse_utilcmd.c')
-rw-r--r--src/backend/parser/parse_utilcmd.c94
1 files changed, 75 insertions, 19 deletions
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index adc0d5b775e..55a74eeaf44 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -108,7 +108,8 @@ static void transformOfType(CreateStmtContext *cxt,
TypeName *ofTypename);
static char *chooseIndexName(const RangeVar *relation, IndexStmt *index_stmt);
static IndexStmt *generateClonedIndexStmt(CreateStmtContext *cxt,
- Relation parent_index, AttrNumber *attmap);
+ Relation source_idx,
+ const AttrNumber *attmap, int attmap_length);
static List *get_collation(Oid collation, Oid actual_datatype);
static List *get_opclass(Oid opclass, Oid actual_datatype);
static void transformIndexConstraints(CreateStmtContext *cxt);
@@ -634,6 +635,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
Relation relation;
TupleDesc tupleDesc;
TupleConstr *constr;
+ AttrNumber *attmap;
AclResult aclresult;
char *comment;
ParseCallbackState pcbstate;
@@ -642,14 +644,14 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
relation = relation_openrv(table_like_clause->relation, AccessShareLock);
- if (relation->rd_rel->relkind != RELKIND_RELATION
- && relation->rd_rel->relkind != RELKIND_VIEW
- && relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE
- && relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+ if (relation->rd_rel->relkind != RELKIND_RELATION &&
+ relation->rd_rel->relkind != RELKIND_VIEW &&
+ relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
+ relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table, view, composite type, or foreign table",
- table_like_clause->relation->relname)));
+ RelationGetRelationName(relation))));
cancel_parser_errposition_callback(&pcbstate);
@@ -677,6 +679,13 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
constr = tupleDesc->constr;
/*
+ * Initialize column number map for map_variable_attnos(). We need this
+ * since dropped columns in the source table aren't copied, so the new
+ * table can have different column numbers.
+ */
+ attmap = (AttrNumber *) palloc0(sizeof(AttrNumber) * tupleDesc->natts);
+
+ /*
* Insert the copied attributes into the cxt for the new table definition.
*/
for (parent_attno = 1; parent_attno <= tupleDesc->natts;
@@ -687,7 +696,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
ColumnDef *def;
/*
- * Ignore dropped columns in the parent.
+ * Ignore dropped columns in the parent. attmap entry is left zero.
*/
if (attribute->attisdropped)
continue;
@@ -718,6 +727,8 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
*/
cxt->columns = lappend(cxt->columns, def);
+ attmap[parent_attno - 1] = list_length(cxt->columns);
+
/*
* Copy default, if present and the default has been requested
*/
@@ -776,22 +787,39 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
/*
* Copy CHECK constraints if requested, being careful to adjust attribute
- * numbers
+ * numbers so they match the child.
*/
if ((table_like_clause->options & CREATE_TABLE_LIKE_CONSTRAINTS) &&
tupleDesc->constr)
{
- AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
int ccnum;
for (ccnum = 0; ccnum < tupleDesc->constr->num_check; ccnum++)
{
char *ccname = tupleDesc->constr->check[ccnum].ccname;
char *ccbin = tupleDesc->constr->check[ccnum].ccbin;
- Node *ccbin_node = stringToNode(ccbin);
Constraint *n = makeNode(Constraint);
+ Node *ccbin_node;
+ bool found_whole_row;
+
+ ccbin_node = map_variable_attnos(stringToNode(ccbin),
+ 1, 0,
+ attmap, tupleDesc->natts,
+ &found_whole_row);
- change_varattnos_of_a_node(ccbin_node, attmap);
+ /*
+ * We reject whole-row variables because the whole point of LIKE
+ * is that the new table's rowtype might later diverge from the
+ * parent's. So, while translation might be possible right now,
+ * it wouldn't be possible to guarantee it would work in future.
+ */
+ 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\".",
+ ccname,
+ RelationGetRelationName(relation))));
n->contype = CONSTR_CHECK;
n->location = -1;
@@ -827,7 +855,6 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
if ((table_like_clause->options & CREATE_TABLE_LIKE_INDEXES) &&
relation->rd_rel->relhasindex)
{
- AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
List *parent_indexes;
ListCell *l;
@@ -842,7 +869,8 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
parent_index = index_open(parent_index_oid, AccessShareLock);
/* Build CREATE INDEX statement to recreate the parent_index */
- index_stmt = generateClonedIndexStmt(cxt, parent_index, attmap);
+ index_stmt = generateClonedIndexStmt(cxt, parent_index,
+ attmap, tupleDesc->natts);
/* Copy comment on index */
if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS)
@@ -961,7 +989,7 @@ chooseIndexName(const RangeVar *relation, IndexStmt *index_stmt)
*/
static IndexStmt *
generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
- AttrNumber *attmap)
+ const AttrNumber *attmap, int attmap_length)
{
Oid source_relid = RelationGetRelid(source_idx);
Form_pg_attribute *attrs = RelationGetDescr(source_idx)->attrs;
@@ -1149,14 +1177,26 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
{
/* Expressional index */
Node *indexkey;
+ bool found_whole_row;
if (indexpr_item == NULL)
elog(ERROR, "too few entries in indexprs list");
indexkey = (Node *) lfirst(indexpr_item);
indexpr_item = lnext(indexpr_item);
- /* OK to modify indexkey since we are working on a private copy */
- change_varattnos_of_a_node(indexkey, attmap);
+ /* Adjust Vars to match new table's column numbering */
+ indexkey = map_variable_attnos(indexkey,
+ 1, 0,
+ attmap, attmap_length,
+ &found_whole_row);
+
+ /* As in transformTableLikeClause, reject whole-row variables */
+ if (found_whole_row)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert whole-row table reference"),
+ errdetail("Index \"%s\" contains a whole-row table reference.",
+ RelationGetRelationName(source_idx))));
iparam->name = NULL;
iparam->expr = indexkey;
@@ -1213,12 +1253,28 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
if (!isnull)
{
char *pred_str;
+ Node *pred_tree;
+ bool found_whole_row;
/* Convert text string to node tree */
pred_str = TextDatumGetCString(datum);
- index->whereClause = (Node *) stringToNode(pred_str);
- /* Adjust attribute numbers */
- change_varattnos_of_a_node(index->whereClause, attmap);
+ pred_tree = (Node *) stringToNode(pred_str);
+
+ /* Adjust Vars to match new table's column numbering */
+ pred_tree = map_variable_attnos(pred_tree,
+ 1, 0,
+ attmap, attmap_length,
+ &found_whole_row);
+
+ /* As in transformTableLikeClause, reject whole-row variables */
+ if (found_whole_row)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert whole-row table reference"),
+ errdetail("Index \"%s\" contains a whole-row table reference.",
+ RelationGetRelationName(source_idx))));
+
+ index->whereClause = pred_tree;
}
/* Clean up */