1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-10 17:42:29 +03:00

Rewriter and planner should use only resno, not resname, to identify

target columns in INSERT and UPDATE targetlists.  Don't rely on resname
to be accurate in ruleutils, either.  This fixes bug reported by
Donald Fraser, in which renaming a column referenced in a rule did not
work very well.
This commit is contained in:
Tom Lane
2003-08-11 23:04:50 +00:00
parent 730b3a1502
commit 302f1a86dc
15 changed files with 144 additions and 115 deletions

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.98 2003/08/04 02:39:56 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.99 2003/08/11 23:04:49 tgl Exp $
*
* NOTES
* some of the executor utility code such as "ExecTypeFromTL" should be
@@ -357,7 +357,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
void
TupleDescInitEntry(TupleDesc desc,
AttrNumber attributeNumber,
char *attributeName,
const char *attributeName,
Oid oidtypeid,
int32 typmod,
int attdim,
@@ -373,13 +373,6 @@ TupleDescInitEntry(TupleDesc desc,
AssertArg(PointerIsValid(desc));
AssertArg(attributeNumber >= 1);
AssertArg(attributeNumber <= desc->natts);
/*
* attributeName's are sometimes NULL, from resdom's. I don't know
* why that is, though -- Jolly
*/
/* AssertArg(NameIsValid(attributeName));*/
AssertArg(!PointerIsValid(desc->attrs[attributeNumber - 1]));
/*
@@ -394,6 +387,11 @@ TupleDescInitEntry(TupleDesc desc,
*/
att->attrelid = 0; /* dummy value */
/*
* Note: attributeName can be NULL, because the planner doesn't always
* fill in valid resname values in targetlists, particularly for resjunk
* attributes.
*/
if (attributeName != NULL)
namestrcpy(&(att->attname), attributeName);
else

View File

@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.30 2003/08/04 02:39:58 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.31 2003/08/11 23:04:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1439,8 +1439,8 @@ getObjectDescription(const ObjectAddress *object)
getRelationDescription(&buffer, object->objectId);
if (object->objectSubId != 0)
appendStringInfo(&buffer, " column %s",
get_attname(object->objectId,
object->objectSubId));
get_relid_attribute_name(object->objectId,
object->objectSubId));
break;
case OCLASS_PROC:

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.63 2003/08/04 02:39:59 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.64 2003/08/11 23:04:49 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -448,7 +448,8 @@ print_tl(List *tlist, List *rtable)
{
TargetEntry *tle = lfirst(tl);
printf("\t%d %s\t", tle->resdom->resno, tle->resdom->resname);
printf("\t%d %s\t", tle->resdom->resno,
tle->resdom->resname ? tle->resdom->resname : "<null>");
if (tle->resdom->ressortgroupref != 0)
printf("(%u):\t", tle->resdom->ressortgroupref);
else

View File

@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.64 2003/08/04 02:40:01 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.65 2003/08/11 23:04:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -150,8 +150,6 @@ expand_targetlist(List *tlist, int command_type,
if (!resdom->resjunk && resdom->resno == attrno)
{
Assert(strcmp(resdom->resname,
NameStr(att_tup->attname)) == 0);
new_tle = old_tle;
tlist = lnext(tlist);
}

View File

@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.103 2003/08/04 02:40:01 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.104 2003/08/11 23:04:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -64,7 +64,7 @@ static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
static Node *adjust_inherited_attrs_mutator(Node *node,
adjust_inherited_attrs_context *context);
static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);
static List *adjust_inherited_tlist(List *tlist, Oid new_relid);
static List *adjust_inherited_tlist(List *tlist, Oid old_relid, Oid new_relid);
/*
@@ -787,6 +787,7 @@ adjust_inherited_attrs(Node *node,
if (newnode->commandType == CMD_UPDATE)
newnode->targetList =
adjust_inherited_tlist(newnode->targetList,
old_relid,
new_relid);
}
return (Node *) newnode;
@@ -812,9 +813,10 @@ adjust_inherited_attrs_mutator(Node *node,
var->varnoold = context->new_rt_index;
if (var->varattno > 0)
{
char *attname = get_attname(context->old_relid,
var->varattno);
char *attname;
attname = get_relid_attribute_name(context->old_relid,
var->varattno);
var->varattno = get_attnum(context->new_relid, attname);
if (var->varattno == InvalidAttrNumber)
elog(ERROR, "attribute \"%s\" of relation \"%s\" does not exist",
@@ -976,7 +978,7 @@ adjust_relid_set(Relids relids, Index oldrelid, Index newrelid)
* Note that this is not needed for INSERT because INSERT isn't inheritable.
*/
static List *
adjust_inherited_tlist(List *tlist, Oid new_relid)
adjust_inherited_tlist(List *tlist, Oid old_relid, Oid new_relid)
{
bool changed_it = false;
List *tl;
@@ -989,21 +991,26 @@ adjust_inherited_tlist(List *tlist, Oid new_relid)
{
TargetEntry *tle = (TargetEntry *) lfirst(tl);
Resdom *resdom = tle->resdom;
char *attname;
if (resdom->resjunk)
continue; /* ignore junk items */
attrno = get_attnum(new_relid, resdom->resname);
attname = get_relid_attribute_name(old_relid, resdom->resno);
attrno = get_attnum(new_relid, attname);
if (attrno == InvalidAttrNumber)
elog(ERROR, "attribute \"%s\" of relation \"%s\" does not exist",
resdom->resname, get_rel_name(new_relid));
attname, get_rel_name(new_relid));
if (resdom->resno != attrno)
{
resdom = (Resdom *) copyObject((Node *) resdom);
resdom->resno = attrno;
resdom->resname = attname;
tle->resdom = resdom;
changed_it = true;
}
else
pfree(attname);
}
/*

View File

@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.286 2003/08/08 21:41:55 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.287 2003/08/11 23:04:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2124,10 +2124,12 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
{
Oid colType = lfirsto(dtlist);
Resdom *leftResdom = ((TargetEntry *) lfirst(lefttl))->resdom;
char *colName = pstrdup(leftResdom->resname);
char *colName;
Resdom *resdom;
Expr *expr;
Assert(!leftResdom->resjunk);
colName = pstrdup(leftResdom->resname);
resdom = makeResdom((AttrNumber) pstate->p_next_resno++,
colType,
-1,
@@ -2501,11 +2503,12 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
{
/*
* Resjunk nodes need no additional processing, but be sure
* they have names and resnos that do not match any target
* columns; else rewriter or planner might get confused.
* they have resnos that do not match any target columns;
* else rewriter or planner might get confused. They don't
* need a resname either.
*/
resnode->resname = "?resjunk?";
resnode->resno = (AttrNumber) pstate->p_next_resno++;
resnode->resname = NULL;
continue;
}
if (origTargetList == NIL)

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.88 2003/08/11 20:46:46 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.89 2003/08/11 23:04:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1516,8 +1516,6 @@ expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
char *
get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
{
char *attname;
if (attnum == InvalidAttrNumber)
return "*";
@@ -1535,13 +1533,7 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
* built (which can easily happen for rules).
*/
if (rte->rtekind == RTE_RELATION)
{
attname = get_attname(rte->relid, attnum);
if (attname == NULL)
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
attnum, rte->relid);
return attname;
}
return get_relid_attribute_name(rte->relid, attnum);
/*
* Otherwise use the column name from eref. There should always be

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.111 2003/08/11 20:46:46 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.112 2003/08/11 23:04:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -71,7 +71,7 @@ transformTargetEntry(ParseState *pstate,
type_id = exprType(expr);
type_mod = exprTypmod(expr);
if (colname == NULL)
if (colname == NULL && !resjunk)
{
/*
* Generate a suitable column name for a column without any
@@ -428,14 +428,19 @@ updateTargetListEntry(ParseState *pstate,
/*
* The result of the target expression should now match the
* destination column's type. Also, reset the resname and resno to
* identify the destination column --- rewriter and planner depend on
* that!
* destination column's type.
*/
resnode->restype = attrtype;
resnode->restypmod = attrtypmod;
resnode->resname = colname;
/*
* Set the resno to identify the target column --- the rewriter and
* planner depend on this. We also set the resname to identify the
* target column, but this is only for debugging purposes; it should
* not be relied on. (In particular, it might be out of date in a
* stored rule.)
*/
resnode->resno = (AttrNumber) attrno;
resnode->resname = colname;
}

View File

@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.128 2003/08/08 21:41:56 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.129 2003/08/11 23:04:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -48,7 +48,8 @@ static Query *rewriteRuleAction(Query *parsetree,
static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);
static void rewriteTargetList(Query *parsetree, Relation target_relation);
static TargetEntry *process_matched_tle(TargetEntry *src_tle,
TargetEntry *prior_tle);
TargetEntry *prior_tle,
const char *attrName);
static void markQueryForUpdate(Query *qry, bool skipOldNew);
static List *matchLocks(CmdType event, RuleLock *rulelocks,
int varno, Query *parsetree);
@@ -312,8 +313,7 @@ rewriteTargetList(Query *parsetree, Relation target_relation)
continue;
/*
* Look for targetlist entries matching this attr. We match by
* resno, but the resname should match too.
* Look for targetlist entries matching this attr.
*
* Junk attributes are not candidates to be matched.
*/
@@ -324,9 +324,8 @@ rewriteTargetList(Query *parsetree, Relation target_relation)
if (!resdom->resjunk && resdom->resno == attrno)
{
Assert(strcmp(resdom->resname,
NameStr(att_tup->attname)) == 0);
new_tle = process_matched_tle(old_tle, new_tle);
new_tle = process_matched_tle(old_tle, new_tle,
NameStr(att_tup->attname));
/* keep scanning to detect multiple assignments to attr */
}
}
@@ -424,11 +423,12 @@ rewriteTargetList(Query *parsetree, Relation target_relation)
* Convert a matched TLE from the original tlist into a correct new TLE.
*
* This routine detects and handles multiple assignments to the same target
* attribute.
* attribute. (The attribute name is needed only for error messages.)
*/
static TargetEntry *
process_matched_tle(TargetEntry *src_tle,
TargetEntry *prior_tle)
TargetEntry *prior_tle,
const char *attrName)
{
Resdom *resdom = src_tle->resdom;
Node *priorbottom;
@@ -456,7 +456,7 @@ process_matched_tle(TargetEntry *src_tle,
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("multiple assignments to same attribute \"%s\"",
resdom->resname)));
attrName)));
/*
* Prior TLE could be a nest of ArrayRefs if we do this more than
@@ -470,7 +470,7 @@ process_matched_tle(TargetEntry *src_tle,
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("multiple assignments to same attribute \"%s\"",
resdom->resname)));
attrName)));
/*
* Looks OK to nest 'em.

View File

@@ -3,7 +3,7 @@
* back to source text
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.150 2003/08/08 21:42:09 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.151 2003/08/11 23:04:49 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -207,7 +207,6 @@ static char *generate_relation_name(Oid relid);
static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
static void print_operator_name(StringInfo buf, List *opname);
static char *get_relid_attribute_name(Oid relid, AttrNumber attnum);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
@@ -1140,7 +1139,7 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
{
char *colName;
colName = get_attname(relId, DatumGetInt16(keys[j]));
colName = get_relid_attribute_name(relId, DatumGetInt16(keys[j]));
if (j == 0)
appendStringInfo(buf, "%s",
@@ -1901,7 +1900,6 @@ get_basic_select_query(Query *query, deparse_context *context,
foreach(l, query->targetList)
{
TargetEntry *tle = (TargetEntry *) lfirst(l);
bool tell_as = false;
char *colname;
if (tle->resdom->resjunk)
@@ -1924,24 +1922,30 @@ get_basic_select_query(Query *query, deparse_context *context,
else
colname = tle->resdom->resname;
/* Check if we must say AS ... */
if (!IsA(tle->expr, Var))
tell_as = (strcmp(colname, "?column?") != 0);
else
if (colname) /* resname could be NULL */
{
Var *var = (Var *) (tle->expr);
char *schemaname;
char *refname;
char *attname;
/* Check if we must say AS ... */
bool tell_as;
get_names_for_var(var, context, &schemaname, &refname, &attname);
tell_as = (attname == NULL ||
strcmp(attname, colname) != 0);
if (!IsA(tle->expr, Var))
tell_as = (strcmp(colname, "?column?") != 0);
else
{
Var *var = (Var *) (tle->expr);
char *schemaname;
char *refname;
char *attname;
get_names_for_var(var, context,
&schemaname, &refname, &attname);
tell_as = (attname == NULL ||
strcmp(attname, colname) != 0);
}
/* and do if so */
if (tell_as)
appendStringInfo(buf, " AS %s", quote_identifier(colname));
}
/* and do if so */
if (tell_as)
appendStringInfo(buf, " AS %s", quote_identifier(colname));
}
/* Add the FROM clause if needed */
@@ -2151,7 +2155,9 @@ get_insert_query_def(Query *query, deparse_context *context)
appendStringInfo(buf, sep);
sep = ", ";
appendStringInfo(buf, "%s", quote_identifier(tle->resdom->resname));
appendStringInfo(buf, "%s",
quote_identifier(get_relid_attribute_name(rte->relid,
tle->resdom->resno)));
}
appendStringInfo(buf, ") ");
@@ -2225,7 +2231,8 @@ get_update_query_def(Query *query, deparse_context *context)
*/
if (!tleIsArrayAssign(tle))
appendStringInfo(buf, "%s = ",
quote_identifier(tle->resdom->resname));
quote_identifier(get_relid_attribute_name(rte->relid,
tle->resdom->resno)));
get_rule_expr((Node *) tle->expr, context, false);
}
@@ -4351,22 +4358,3 @@ print_operator_name(StringInfo buf, List *opname)
appendStringInfo(buf, "%s)", strVal(lfirst(opname)));
}
}
/*
* get_relid_attribute_name
* Get an attribute name by its relations Oid and its attnum
*
* Same as underlying syscache routine get_attname(), except that error
* is handled by elog() instead of returning NULL.
*/
static char *
get_relid_attribute_name(Oid relid, AttrNumber attnum)
{
char *attname;
attname = get_attname(relid, attnum);
if (attname == NULL)
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
attnum, relid);
return attname;
}

View File

@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.105 2003/08/04 02:40:06 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.106 2003/08/11 23:04:49 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@@ -180,11 +180,10 @@ get_op_hash_function(Oid opno)
/*
* get_attname
*
* Given the relation id and the attribute number,
* return the "attname" field from the attribute relation.
*
* Note: returns a palloc'd copy of the string, or NULL if no such operator.
* Note: returns a palloc'd copy of the string, or NULL if no such attribute.
*/
char *
get_attname(Oid relid, AttrNumber attnum)
@@ -208,6 +207,24 @@ get_attname(Oid relid, AttrNumber attnum)
return NULL;
}
/*
* get_relid_attribute_name
*
* Same as above routine get_attname(), except that error
* is handled by elog() instead of returning NULL.
*/
char *
get_relid_attribute_name(Oid relid, AttrNumber attnum)
{
char *attname;
attname = get_attname(relid, attnum);
if (attname == NULL)
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
attnum, relid);
return attname;
}
/*
* get_attnum
*
@@ -1443,7 +1460,7 @@ get_typtype(Oid typid)
* get_typname
* Returns the name of a given type.
*
* Returns a palloc'd copy of the string, or NULL if no such relation.
* Returns a palloc'd copy of the string, or NULL if no such type.
*
* NOTE: since type name is not unique, be wary of code that uses this
* for anything except preparing error messages.

View File

@@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.148 2003/08/04 23:59:39 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.149 2003/08/11 23:04:49 tgl Exp $
*
*--------------------------------------------------------------------
*/
@@ -3293,7 +3293,7 @@ GetPGVariableResultDesc(const char *name)
/* need a tuple descriptor representing a single TEXT column */
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, (char *) varname,
TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
TEXTOID, -1, 0, false);
}
return tupdesc;
@@ -3333,7 +3333,7 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest)
/* need a tuple descriptor representing a single TEXT column */
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, (char *) varname,
TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
TEXTOID, -1, 0, false);
/* prepare for projection of tuples */