|
7 | 7 | * Portions Copyright (c) 1994, Regents of the University of California
|
8 | 8 | *
|
9 | 9 | * 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 $ |
11 | 11 | *
|
12 | 12 | *-------------------------------------------------------------------------
|
13 | 13 | */
|
@@ -287,52 +287,91 @@ static void
|
287 | 287 | rewriteTargetList(Query*parsetree,Relationtarget_relation)
|
288 | 288 | {
|
289 | 289 | CmdTypecommandType=parsetree->commandType;
|
290 |
| -List*tlist=parsetree->targetList; |
| 290 | +TargetEntry**new_tles; |
291 | 291 | List*new_tlist=NIL;
|
| 292 | +List*junk_tlist=NIL; |
| 293 | +Form_pg_attributeatt_tup; |
292 | 294 | intattrno,
|
| 295 | +next_junk_attrno, |
293 | 296 | numattrs;
|
294 | 297 | ListCell*temp;
|
295 | 298 |
|
296 | 299 | /*
|
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. |
299 | 307 | */
|
300 | 308 | numattrs=RelationGetNumberOfAttributes(target_relation);
|
| 309 | +new_tles= (TargetEntry**)palloc0(numattrs*sizeof(TargetEntry*)); |
| 310 | +next_junk_attrno=numattrs+1; |
301 | 311 |
|
302 |
| -for (attrno=1;attrno <=numattrs;attrno++) |
| 312 | +foreach(temp,parsetree->targetList) |
303 | 313 | {
|
304 |
| -Form_pg_attributeatt_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; |
306 | 316 |
|
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; |
310 | 328 |
|
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 |
317 | 336 | {
|
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 | + */ |
320 | 345 |
|
321 |
| -if (!resdom->resjunk&&resdom->resno==attrno) |
| 346 | +/* Get the resno right, but don't copy unnecessarily */ |
| 347 | +if (resdom->resno!=next_junk_attrno) |
322 | 348 | {
|
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); |
326 | 352 | }
|
| 353 | +junk_tlist=lappend(junk_tlist,old_tle); |
| 354 | +next_junk_attrno++; |
327 | 355 | }
|
| 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; |
328 | 367 |
|
329 | 368 | /*
|
330 | 369 | * Handle the two cases where we need to insert a default
|
331 | 370 | * expression: it's an INSERT and there's no tlist entry for the
|
332 | 371 | * column, or the tlist entry is a DEFAULT placeholder node.
|
333 | 372 | */
|
334 | 373 | 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))) |
336 | 375 | {
|
337 | 376 | Node*new_expr;
|
338 | 377 |
|
@@ -380,40 +419,9 @@ rewriteTargetList(Query *parsetree, Relation target_relation)
|
380 | 419 | new_tlist=lappend(new_tlist,new_tle);
|
381 | 420 | }
|
382 | 421 |
|
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); |
415 | 423 |
|
416 |
| -parsetree->targetList=new_tlist; |
| 424 | +parsetree->targetList=list_concat(new_tlist,junk_tlist); |
417 | 425 | }
|
418 | 426 |
|
419 | 427 |
|
|