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 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 ...@@ -287,44 +287,83 @@ static void
rewriteTargetList(Query *parsetree, Relation target_relation) rewriteTargetList(Query *parsetree, Relation target_relation)
{ {
CmdType commandType = parsetree->commandType; CmdType commandType = parsetree->commandType;
List *tlist = parsetree->targetList; TargetEntry **new_tles;
List *new_tlist = NIL; List *new_tlist = NIL;
List *junk_tlist = NIL;
Form_pg_attribute att_tup;
int attrno, int attrno,
next_junk_attrno,
numattrs; numattrs;
ListCell *temp; ListCell *temp;
/* /*
* Scan the tuple description in the relation's relcache entry to make * We process the normal (non-junk) attributes by scanning the input
* sure we have all the user attributes in the right order. * 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); 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 *old_tle = (TargetEntry *) lfirst(temp);
TargetEntry *new_tle = NULL; Resdom *resdom = old_tle->resdom;
/* We can ignore deleted attributes */ if (!resdom->resjunk)
if (att_tup->attisdropped) {
continue; /* 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;
/* /* Merge with any prior assignment to same attribute */
* Look for targetlist entries matching this attr. new_tles[attrno - 1] =
* process_matched_tle(old_tle,
* Junk attributes are not candidates to be matched. new_tles[attrno - 1],
*/ NameStr(att_tup->attname));
foreach(temp, tlist) }
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, resdom = (Resdom *) copyObject((Node *) resdom);
NameStr(att_tup->attname)); resdom->resno = next_junk_attrno;
/* keep scanning to detect multiple assignments to attr */ 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 * Handle the two cases where we need to insert a default
...@@ -332,7 +371,7 @@ rewriteTargetList(Query *parsetree, Relation target_relation) ...@@ -332,7 +371,7 @@ rewriteTargetList(Query *parsetree, Relation target_relation)
* column, or the tlist entry is a DEFAULT placeholder node. * column, or the tlist entry is a DEFAULT placeholder node.
*/ */
if ((new_tle == NULL && commandType == CMD_INSERT) || 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; Node *new_expr;
...@@ -380,40 +419,9 @@ rewriteTargetList(Query *parsetree, Relation target_relation) ...@@ -380,40 +419,9 @@ rewriteTargetList(Query *parsetree, Relation target_relation)
new_tlist = lappend(new_tlist, new_tle); new_tlist = lappend(new_tlist, new_tle);
} }
/* pfree(new_tles);
* 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);
}
}
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