Commit 9e523813 authored by Tom Lane's avatar Tom Lane

Rewrite rewriteTargetList() to avoid O(N^2) behavior on wide target lists.

parent 4377648b
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.148 2005/03/10 23:21:24 tgl Exp $
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.149 2005/03/26 05:53:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -287,44 +287,83 @@ static void
rewriteTargetList(Query *parsetree, Relation target_relation)
{
CmdType commandType = parsetree->commandType;
List *tlist = parsetree->targetList;
TargetEntry **new_tles;
List *new_tlist = NIL;
List *junk_tlist = NIL;
Form_pg_attribute att_tup;
int attrno,
next_junk_attrno,
numattrs;
ListCell *temp;
/*
* Scan the tuple description in the relation's relcache entry to make
* sure we have all the user attributes in the right order.
* We process the normal (non-junk) attributes by scanning the input
* tlist once and transferring TLEs into an array, then scanning the
* array to build an output tlist. This avoids O(N^2) behavior for
* large numbers of attributes.
*
* Junk attributes are tossed into a separate list during the same
* tlist scan, then appended to the reconstructed tlist.
*/
numattrs = RelationGetNumberOfAttributes(target_relation);
new_tles = (TargetEntry **) palloc0(numattrs * sizeof(TargetEntry *));
next_junk_attrno = numattrs + 1;
for (attrno = 1; attrno <= numattrs; attrno++)
foreach(temp, parsetree->targetList)
{
Form_pg_attribute att_tup = target_relation->rd_att->attrs[attrno - 1];
TargetEntry *new_tle = NULL;
TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
Resdom *resdom = old_tle->resdom;
/* We can ignore deleted attributes */
if (att_tup->attisdropped)
continue;
if (!resdom->resjunk)
{
/* Normal attr: stash it into new_tles[] */
attrno = resdom->resno;
if (attrno < 1 || attrno > numattrs)
elog(ERROR, "bogus resno %d in targetlist", attrno);
att_tup = target_relation->rd_att->attrs[attrno - 1];
/* We can (and must) ignore deleted attributes */
if (att_tup->attisdropped)
continue;
/*
* Look for targetlist entries matching this attr.
*
* Junk attributes are not candidates to be matched.
*/
foreach(temp, tlist)
/* Merge with any prior assignment to same attribute */
new_tles[attrno - 1] =
process_matched_tle(old_tle,
new_tles[attrno - 1],
NameStr(att_tup->attname));
}
else
{
TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
Resdom *resdom = old_tle->resdom;
/*
* Copy all resjunk tlist entries to junk_tlist, and
* assign them resnos above the last real resno.
*
* Typical junk entries include ORDER BY or GROUP BY expressions
* (are these actually possible in an INSERT or UPDATE?), system
* attribute references, etc.
*/
if (!resdom->resjunk && resdom->resno == attrno)
/* Get the resno right, but don't copy unnecessarily */
if (resdom->resno != next_junk_attrno)
{
new_tle = process_matched_tle(old_tle, new_tle,
NameStr(att_tup->attname));
/* keep scanning to detect multiple assignments to attr */
resdom = (Resdom *) copyObject((Node *) resdom);
resdom->resno = next_junk_attrno;
old_tle = makeTargetEntry(resdom, old_tle->expr);
}
junk_tlist = lappend(junk_tlist, old_tle);
next_junk_attrno++;
}
}
for (attrno = 1; attrno <= numattrs; attrno++)
{
TargetEntry *new_tle = new_tles[attrno - 1];
att_tup = target_relation->rd_att->attrs[attrno - 1];
/* We can (and must) ignore deleted attributes */
if (att_tup->attisdropped)
continue;
/*
* Handle the two cases where we need to insert a default
......@@ -332,7 +371,7 @@ rewriteTargetList(Query *parsetree, Relation target_relation)
* column, or the tlist entry is a DEFAULT placeholder node.
*/
if ((new_tle == NULL && commandType == CMD_INSERT) ||
(new_tle && new_tle->expr && IsA(new_tle->expr, SetToDefault)))
(new_tle && new_tle->expr && IsA(new_tle->expr, SetToDefault)))
{
Node *new_expr;
......@@ -380,40 +419,9 @@ rewriteTargetList(Query *parsetree, Relation target_relation)
new_tlist = lappend(new_tlist, new_tle);
}
/*
* Copy all resjunk tlist entries to the end of the new tlist, and
* assign them resnos above the last real resno.
*
* Typical junk entries include ORDER BY or GROUP BY expressions (are
* these actually possible in an INSERT or UPDATE?), system attribute
* references, etc.
*/
foreach(temp, tlist)
{
TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
Resdom *resdom = old_tle->resdom;
if (resdom->resjunk)
{
/* Get the resno right, but don't copy unnecessarily */
if (resdom->resno != attrno)
{
resdom = (Resdom *) copyObject((Node *) resdom);
resdom->resno = attrno;
old_tle = makeTargetEntry(resdom, old_tle->expr);
}
new_tlist = lappend(new_tlist, old_tle);
attrno++;
}
else
{
/* Let's just make sure we processed all the non-junk items */
if (resdom->resno < 1 || resdom->resno > numattrs)
elog(ERROR, "bogus resno %d in targetlist", resdom->resno);
}
}
pfree(new_tles);
parsetree->targetList = new_tlist;
parsetree->targetList = list_concat(new_tlist, junk_tlist);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment