Skip to content

Commit 9e52381

Browse files
committed
Rewrite rewriteTargetList() to avoid O(N^2) behavior on wide target lists.
1 parent 4377648 commit 9e52381

File tree

1 file changed

+64
-56
lines changed

1 file changed

+64
-56
lines changed

src/backend/rewrite/rewriteHandler.c

Lines changed: 64 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1994, Regents of the University of California
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.148 2005/03/10 23:21:24 tgl Exp $
10+
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.149 2005/03/26 05:53:01 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -287,52 +287,91 @@ static void
287287
rewriteTargetList(Query *parsetree, Relation target_relation)
288288
{
289289
CmdType commandType = parsetree->commandType;
290-
List *tlist = parsetree->targetList;
290+
TargetEntry **new_tles;
291291
List *new_tlist = NIL;
292+
List *junk_tlist = NIL;
293+
Form_pg_attribute att_tup;
292294
int attrno,
295+
next_junk_attrno,
293296
numattrs;
294297
ListCell *temp;
295298

296299
/*
297-
* Scan the tuple description in the relation's relcache entry to make
298-
* sure we have all the user attributes in the right order.
300+
* We process the normal (non-junk) attributes by scanning the input
301+
* tlist once and transferring TLEs into an array, then scanning the
302+
* array to build an output tlist. This avoids O(N^2) behavior for
303+
* large numbers of attributes.
304+
*
305+
* Junk attributes are tossed into a separate list during the same
306+
* tlist scan, then appended to the reconstructed tlist.
299307
*/
300308
numattrs = RelationGetNumberOfAttributes(target_relation);
309+
new_tles = (TargetEntry **) palloc0(numattrs * sizeof(TargetEntry *));
310+
next_junk_attrno = numattrs + 1;
301311

302-
for (attrno = 1; attrno <= numattrs; attrno++)
312+
foreach(temp, parsetree->targetList)
303313
{
304-
Form_pg_attribute att_tup = target_relation->rd_att->attrs[attrno - 1];
305-
TargetEntry *new_tle = NULL;
314+
TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
315+
Resdom *resdom = old_tle->resdom;
306316

307-
/* We can ignore deleted attributes */
308-
if (att_tup->attisdropped)
309-
continue;
317+
if (!resdom->resjunk)
318+
{
319+
/* Normal attr: stash it into new_tles[] */
320+
attrno = resdom->resno;
321+
if (attrno < 1 || attrno > numattrs)
322+
elog(ERROR, "bogus resno %d in targetlist", attrno);
323+
att_tup = target_relation->rd_att->attrs[attrno - 1];
324+
325+
/* We can (and must) ignore deleted attributes */
326+
if (att_tup->attisdropped)
327+
continue;
310328

311-
/*
312-
* Look for targetlist entries matching this attr.
313-
*
314-
* Junk attributes are not candidates to be matched.
315-
*/
316-
foreach(temp, tlist)
329+
/* Merge with any prior assignment to same attribute */
330+
new_tles[attrno - 1] =
331+
process_matched_tle(old_tle,
332+
new_tles[attrno - 1],
333+
NameStr(att_tup->attname));
334+
}
335+
else
317336
{
318-
TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
319-
Resdom *resdom = old_tle->resdom;
337+
/*
338+
* Copy all resjunk tlist entries to junk_tlist, and
339+
* assign them resnos above the last real resno.
340+
*
341+
* Typical junk entries include ORDER BY or GROUP BY expressions
342+
* (are these actually possible in an INSERT or UPDATE?), system
343+
* attribute references, etc.
344+
*/
320345

321-
if (!resdom->resjunk && resdom->resno == attrno)
346+
/* Get the resno right, but don't copy unnecessarily */
347+
if (resdom->resno != next_junk_attrno)
322348
{
323-
new_tle = process_matched_tle(old_tle, new_tle,
324-
NameStr(att_tup->attname));
325-
/* keep scanning to detect multiple assignments to attr */
349+
resdom = (Resdom *) copyObject((Node *) resdom);
350+
resdom->resno = next_junk_attrno;
351+
old_tle = makeTargetEntry(resdom, old_tle->expr);
326352
}
353+
junk_tlist = lappend(junk_tlist, old_tle);
354+
next_junk_attrno++;
327355
}
356+
}
357+
358+
for (attrno = 1; attrno <= numattrs; attrno++)
359+
{
360+
TargetEntry *new_tle = new_tles[attrno - 1];
361+
362+
att_tup = target_relation->rd_att->attrs[attrno - 1];
363+
364+
/* We can (and must) ignore deleted attributes */
365+
if (att_tup->attisdropped)
366+
continue;
328367

329368
/*
330369
* Handle the two cases where we need to insert a default
331370
* expression: it's an INSERT and there's no tlist entry for the
332371
* column, or the tlist entry is a DEFAULT placeholder node.
333372
*/
334373
if ((new_tle == NULL && commandType == CMD_INSERT) ||
335-
(new_tle && new_tle->expr && IsA(new_tle->expr, SetToDefault)))
374+
(new_tle && new_tle->expr && IsA(new_tle->expr, SetToDefault)))
336375
{
337376
Node *new_expr;
338377

@@ -380,40 +419,9 @@ rewriteTargetList(Query *parsetree, Relation target_relation)
380419
new_tlist = lappend(new_tlist, new_tle);
381420
}
382421

383-
/*
384-
* Copy all resjunk tlist entries to the end of the new tlist, and
385-
* assign them resnos above the last real resno.
386-
*
387-
* Typical junk entries include ORDER BY or GROUP BY expressions (are
388-
* these actually possible in an INSERT or UPDATE?), system attribute
389-
* references, etc.
390-
*/
391-
foreach(temp, tlist)
392-
{
393-
TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
394-
Resdom *resdom = old_tle->resdom;
395-
396-
if (resdom->resjunk)
397-
{
398-
/* Get the resno right, but don't copy unnecessarily */
399-
if (resdom->resno != attrno)
400-
{
401-
resdom = (Resdom *) copyObject((Node *) resdom);
402-
resdom->resno = attrno;
403-
old_tle = makeTargetEntry(resdom, old_tle->expr);
404-
}
405-
new_tlist = lappend(new_tlist, old_tle);
406-
attrno++;
407-
}
408-
else
409-
{
410-
/* Let's just make sure we processed all the non-junk items */
411-
if (resdom->resno < 1 || resdom->resno > numattrs)
412-
elog(ERROR, "bogus resno %d in targetlist", resdom->resno);
413-
}
414-
}
422+
pfree(new_tles);
415423

416-
parsetree->targetList = new_tlist;
424+
parsetree->targetList = list_concat(new_tlist, junk_tlist);
417425
}
418426

419427

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy