Rewrite preprocess_targetlist() to reduce overhead for simple INSERTs.

In particular, don't bother to look up type information for attributes
where we're not actually going to use it, and avoid copying entire tlist
structure when it's not necessary.
This commit is contained in:
Tom Lane 1999-10-30 23:06:32 +00:00
parent 01523ce14a
commit e2a29eb52c
1 changed files with 160 additions and 238 deletions

View File

@ -3,40 +3,40 @@
* preptlist.c
* Routines to preprocess the parse tree target list
*
* This module takes care of altering the query targetlist as needed for
* INSERT, UPDATE, and DELETE queries. For INSERT and UPDATE queries,
* the targetlist must contain an entry for each attribute of the target
* relation in the correct order. For both UPDATE and DELETE queries,
* we need a junk targetlist entry holding the CTID attribute --- the
* executor relies on this to find the tuple to be replaced/deleted.
*
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.31 1999/08/22 20:14:51 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.32 1999/10/30 23:06:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/prep.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
static List *expand_targetlist(List *tlist, Oid relid, int command_type,
Index result_relation);
static List *replace_matching_resname(List *new_tlist,
List *old_tlist);
static List *new_relation_targetlist(Oid relid, Index rt_index,
NodeTag node_type);
static List *expand_targetlist(List *tlist, int command_type,
Index result_relation, List *range_table);
/*
* preprocess_targetlist
* Driver for preprocessing the parse tree targetlist.
*
* 1. Deal with appends and replaces by filling missing attributes
* in the target list.
* 2. Reset operator OIDs to the appropriate regproc ids.
*
* Returns the new targetlist.
*/
List *
@ -45,53 +45,49 @@ preprocess_targetlist(List *tlist,
Index result_relation,
List *range_table)
{
Oid relid = InvalidOid;
List *expanded_tlist;
List *t_list;
if (result_relation >= 1 && command_type != CMD_SELECT)
relid = getrelid(result_relation, range_table);
/*
* for heap_formtuple to work, the targetlist must match the exact
* order of the attributes. We also need to fill in the missing
* attributes here. -ay 10/94
* order of the attributes. We also need to fill in any missing
* attributes. -ay 10/94
*/
expanded_tlist = expand_targetlist(tlist, relid, command_type, result_relation);
if (command_type == CMD_INSERT || command_type == CMD_UPDATE)
tlist = expand_targetlist(tlist, command_type,
result_relation, range_table);
t_list = copyObject(expanded_tlist);
/* ------------------
* for "replace" or "delete" queries, add ctid of the result
* relation into the target list so that the ctid can get
* propogate through the execution and in the end ExecReplace()
* will find the right tuple to replace or delete. This
* extra field will be removed in ExecReplace().
* For convinient, we append this extra field to the end of
* the target list.
* ------------------
/*
* for "update" and "delete" queries, add ctid of the result
* relation into the target list so that the ctid will propagate
* through execution and ExecutePlan() will be able to identify
* the right tuple to replace or delete. This extra field is
* marked "junk" so that it is not stored back into the tuple.
*/
if (command_type == CMD_UPDATE || command_type == CMD_DELETE)
{
TargetEntry *ctid;
Resdom *resdom;
Var *var;
resdom = makeResdom(length(t_list) + 1,
resdom = makeResdom(length(tlist) + 1,
TIDOID,
-1,
"ctid",
pstrdup("ctid"),
0,
0,
true);
var = makeVar(result_relation, -1, TIDOID, -1, 0);
var = makeVar(result_relation, SelfItemPointerAttributeNumber,
TIDOID, -1, 0);
ctid = makeTargetEntry(resdom, (Node *) var);
t_list = lappend(t_list, ctid);
/* For an UPDATE, expand_targetlist already created a fresh tlist.
* For DELETE, better do a listCopy so that we don't destructively
* modify the original tlist (is this really necessary?).
*/
if (command_type == CMD_DELETE)
tlist = listCopy(tlist);
tlist = lappend(tlist, makeTargetEntry(resdom, (Node *) var));
}
return t_list;
return tlist;
}
/*****************************************************************************
@ -103,201 +99,98 @@ preprocess_targetlist(List *tlist,
/*
* expand_targetlist
* Given a target list as generated by the parser and a result relation,
* add targetlist entries for the attributes which have not been used.
*
* XXX This code is only supposed to work with unnested relations.
*
* 'tlist' is the original target list
* 'relid' is the relid of the result relation
* 'command' is the update command
*
* Returns the expanded target list, sorted in resno order.
* add targetlist entries for any missing attributes, and order the
* non-junk attributes in proper field order.
*/
static List *
expand_targetlist(List *tlist,
Oid relid,
int command_type,
Index result_relation)
expand_targetlist(List *tlist, int command_type,
Index result_relation, List *range_table)
{
NodeTag node_type = T_Invalid;
switch (command_type)
{
case CMD_INSERT:
node_type = (NodeTag) T_Const;
break;
case CMD_UPDATE:
node_type = (NodeTag) T_Var;
break;
}
if (node_type != T_Invalid)
{
List *ntlist = new_relation_targetlist(relid,
result_relation,
node_type);
return replace_matching_resname(ntlist, tlist);
}
else
return tlist;
}
static List *
replace_matching_resname(List *new_tlist, List *old_tlist)
{
List *temp,
*i;
List *t_list = NIL;
foreach(i, new_tlist)
{
TargetEntry *new_tle = (TargetEntry *) lfirst(i);
TargetEntry *matching_old_tl = NULL;
foreach(temp, old_tlist)
{
TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
if (!strcmp(old_tle->resdom->resname,
new_tle->resdom->resname))
{
matching_old_tl = old_tle;
break;
}
}
if (matching_old_tl)
{
/* XXX safe to modify old resdom? */
matching_old_tl->resdom->resno = new_tle->resdom->resno;
t_list = lappend(t_list, matching_old_tl);
}
else
t_list = lappend(t_list, new_tle);
}
int old_tlist_len = length(tlist);
List *new_tlist = NIL;
bool *tlistentry_used;
Relation rel;
int attrno,
numattrs,
old_tlist_index;
List *temp;
/*
* It is possible that 'old_tlist' has some negative attributes (i.e.
* negative resnos). This only happens if this is a replace/append
* command and we explicitly specify a system attribute. Of course
* this is not a very good idea if this is a user query, but on the
* other hand the rule manager uses this mechanism to replace rule
* locks.
*
* So, copy all these entries to the end of the target list and set their
* 'resjunk' value to true to show that these are special attributes
* and have to be treated specially by the executor!
* Keep a map of which tlist items we have transferred to new list.
* +1 here keeps palloc from complaining if old_tlist_len=0.
*/
foreach(temp, old_tlist)
tlistentry_used = (bool *) palloc((old_tlist_len+1) * sizeof(bool));
memset(tlistentry_used, 0, (old_tlist_len+1) * sizeof(bool));
/*
* Scan the tuple description in the relation's relcache entry to make
* sure we have all the user attributes in the right order.
*/
rel = heap_open(getrelid(result_relation, range_table), AccessShareLock);
numattrs = RelationGetNumberOfAttributes(rel);
for (attrno = 1; attrno <= numattrs; attrno++)
{
TargetEntry *old_tle,
*new_tl;
Form_pg_attribute att_tup = rel->rd_att->attrs[attrno-1];
char *attrname = att_tup->attname.data;
TargetEntry *new_tle = NULL;
old_tle = lfirst(temp);
if (old_tle->resdom->resno < 0)
{
Resdom *newresdom;
newresdom = (Resdom *) copyObject((Node *) old_tle->resdom);
newresdom->resno = length(t_list) + 1;
newresdom->resjunk = true;
new_tl = makeTargetEntry(newresdom, old_tle->expr);
t_list = lappend(t_list, new_tl);
}
/*
* Also it is possible that the parser or rewriter added some junk
* attributes to hold ORDER/GROUP BY expressions which are not part of
* the result attributes. We can simply identify them by looking
* at the ressortgroupref in the TLE's resdom, which is a unique
* number telling which TLE belongs to which Sort/GroupClause.
* We match targetlist entries to attributes using the resname.
*/
else if (old_tle->resdom->ressortgroupref > 0)
old_tlist_index = 0;
foreach(temp, tlist)
{
bool already_there = false;
TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
Resdom *resdom = old_tle->resdom;
/*
* Check if the tle is already in the new list
*/
foreach(i, t_list)
if (! tlistentry_used[old_tlist_index] &&
strcmp(resdom->resname, attrname) == 0 &&
! resdom->resjunk)
{
TargetEntry *new_tle = (TargetEntry *) lfirst(i);
if (new_tle->resdom->ressortgroupref ==
old_tle->resdom->ressortgroupref)
/*
* We can recycle the old TLE+resdom if right resno; else
* make a new one to avoid modifying the old tlist structure.
* (Is preserving old tlist actually necessary?)
*/
if (resdom->resno == attrno)
{
already_there = true;
break;
new_tle = old_tle;
}
else
{
resdom = (Resdom *) copyObject((Node *) resdom);
resdom->resno = attrno;
new_tle = makeTargetEntry(resdom, old_tle->expr);
}
tlistentry_used[old_tlist_index] = true;
break;
}
old_tlist_index++;
}
if (new_tle == NULL)
{
/*
* If not, add it and make sure it is now a junk attribute
* Didn't find a matching tlist entry, so make one.
*
* For INSERT, generate a constant of the default value for
* the attribute type, or NULL if no default value.
*
* For UPDATE, generate a Var reference to the existing value
* of the attribute, so that it gets copied to the new tuple.
*/
if (!already_there)
Oid atttype = att_tup->atttypid;
int32 atttypmod = att_tup->atttypmod;
switch (command_type)
{
Resdom *newresdom;
newresdom = (Resdom *) copyObject((Node *) old_tle->resdom);
newresdom->resno = length(t_list) + 1;
newresdom->resjunk = true;
new_tl = makeTargetEntry(newresdom, old_tle->expr);
t_list = lappend(t_list, new_tl);
}
}
}
return t_list;
}
/*
* new_relation_targetlist
* Generate a targetlist for the relation with relation OID 'relid'
* and rangetable index 'rt_index'.
*
* Returns the new targetlist.
*/
static List *
new_relation_targetlist(Oid relid, Index rt_index, NodeTag node_type)
{
List *t_list = NIL;
int natts = get_relnatts(relid);
AttrNumber attno;
for (attno = 1; attno <= natts; attno++)
{
HeapTuple tp;
Form_pg_attribute att_tup;
char *attname;
Oid atttype;
int32 atttypmod;
bool attisset;
tp = SearchSysCacheTuple(ATTNUM,
ObjectIdGetDatum(relid),
UInt16GetDatum(attno),
0, 0);
if (! HeapTupleIsValid(tp))
{
elog(ERROR, "new_relation_targetlist: no attribute tuple %u %d",
relid, attno);
}
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
attname = pstrdup(att_tup->attname.data);
atttype = att_tup->atttypid;
atttypmod = att_tup->atttypmod;
attisset = att_tup->attisset;
switch (node_type)
{
case T_Const: /* INSERT command */
case CMD_INSERT:
{
Datum typedefault = get_typdefault(atttype);
int typlen;
Const *temp_const;
TargetEntry *temp_tle;
if (typedefault == PointerGetDatum(NULL))
typlen = 0;
@ -308,7 +201,7 @@ new_relation_targetlist(Oid relid, Index rt_index, NodeTag node_type)
* any set attribute is the size of the OID used to
* represent it.
*/
if (attisset)
if (att_tup->attisset)
typlen = get_typlen(OIDOID);
else
typlen = get_typlen(atttype);
@ -322,40 +215,69 @@ new_relation_targetlist(Oid relid, Index rt_index, NodeTag node_type)
false, /* not a set */
false);
temp_tle = makeTargetEntry(makeResdom(attno,
atttype,
-1,
attname,
0,
(Oid) 0,
false),
(Node *) temp_const);
t_list = lappend(t_list, temp_tle);
new_tle = makeTargetEntry(makeResdom(attrno,
atttype,
-1,
pstrdup(attrname),
0,
(Oid) 0,
false),
(Node *) temp_const);
break;
}
case T_Var: /* UPDATE command */
case CMD_UPDATE:
{
Var *temp_var;
TargetEntry *temp_tle;
temp_var = makeVar(rt_index, attno, atttype,
temp_var = makeVar(result_relation, attrno, atttype,
atttypmod, 0);
temp_tle = makeTargetEntry(makeResdom(attno,
atttype,
atttypmod,
attname,
0,
(Oid) 0,
false),
(Node *) temp_var);
t_list = lappend(t_list, temp_tle);
new_tle = makeTargetEntry(makeResdom(attrno,
atttype,
atttypmod,
pstrdup(attrname),
0,
(Oid) 0,
false),
(Node *) temp_var);
break;
}
default: /* do nothing */
break;
default:
elog(ERROR, "expand_targetlist: unexpected command_type");
break;
}
}
new_tlist = lappend(new_tlist, new_tle);
}
return t_list;
/*
* Copy all unprocessed tlist entries to the end of the new tlist,
* making sure they are marked resjunk = true. Typical junk entries
* include ORDER BY or GROUP BY expressions (are these actually possible
* in an INSERT or UPDATE?), system attribute references, etc.
*/
old_tlist_index = 0;
foreach(temp, tlist)
{
TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
if (! tlistentry_used[old_tlist_index])
{
Resdom *resdom;
resdom = (Resdom *) copyObject((Node *) old_tle->resdom);
resdom->resno = attrno++;
resdom->resjunk = true;
new_tlist = lappend(new_tlist,
makeTargetEntry(resdom, old_tle->expr));
}
old_tlist_index++;
}
heap_close(rel, AccessShareLock);
pfree(tlistentry_used);
return new_tlist;
}