Commit e2a29eb5 authored by Tom Lane's avatar Tom Lane

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.
parent 01523ce1
...@@ -3,40 +3,40 @@ ...@@ -3,40 +3,40 @@
* preptlist.c * preptlist.c
* Routines to preprocess the parse tree target list * Routines to preprocess the parse tree target list
* *
* Copyright (c) 1994, Regents of the University of California * 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 * 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 "postgres.h"
#include "access/heapam.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/prep.h" #include "optimizer/prep.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "utils/lsyscache.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 *expand_targetlist(List *tlist, int command_type,
static List *replace_matching_resname(List *new_tlist, Index result_relation, List *range_table);
List *old_tlist);
static List *new_relation_targetlist(Oid relid, Index rt_index,
NodeTag node_type);
/* /*
* preprocess_targetlist * preprocess_targetlist
* Driver for preprocessing the parse tree 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. * Returns the new targetlist.
*/ */
List * List *
...@@ -45,53 +45,49 @@ preprocess_targetlist(List *tlist, ...@@ -45,53 +45,49 @@ preprocess_targetlist(List *tlist,
Index result_relation, Index result_relation,
List *range_table) 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 * for heap_formtuple to work, the targetlist must match the exact
* order of the attributes. We also need to fill in the missing * order of the attributes. We also need to fill in any missing
* attributes here. -ay 10/94 * 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,
t_list = copyObject(expanded_tlist); result_relation, range_table);
/* ------------------ /*
* for "replace" or "delete" queries, add ctid of the result * for "update" and "delete" queries, add ctid of the result
* relation into the target list so that the ctid can get * relation into the target list so that the ctid will propagate
* propogate through the execution and in the end ExecReplace() * through execution and ExecutePlan() will be able to identify
* will find the right tuple to replace or delete. This * the right tuple to replace or delete. This extra field is
* extra field will be removed in ExecReplace(). * marked "junk" so that it is not stored back into the tuple.
* For convinient, we append this extra field to the end of
* the target list.
* ------------------
*/ */
if (command_type == CMD_UPDATE || command_type == CMD_DELETE) if (command_type == CMD_UPDATE || command_type == CMD_DELETE)
{ {
TargetEntry *ctid;
Resdom *resdom; Resdom *resdom;
Var *var; Var *var;
resdom = makeResdom(length(t_list) + 1, resdom = makeResdom(length(tlist) + 1,
TIDOID, TIDOID,
-1, -1,
"ctid", pstrdup("ctid"),
0, 0,
0, 0,
true); true);
var = makeVar(result_relation, -1, TIDOID, -1, 0); var = makeVar(result_relation, SelfItemPointerAttributeNumber,
TIDOID, -1, 0);
/* 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);
ctid = makeTargetEntry(resdom, (Node *) var); tlist = lappend(tlist, makeTargetEntry(resdom, (Node *) var));
t_list = lappend(t_list, ctid);
} }
return t_list; return tlist;
} }
/***************************************************************************** /*****************************************************************************
...@@ -103,201 +99,98 @@ preprocess_targetlist(List *tlist, ...@@ -103,201 +99,98 @@ preprocess_targetlist(List *tlist,
/* /*
* expand_targetlist * expand_targetlist
* Given a target list as generated by the parser and a result relation, * Given a target list as generated by the parser and a result relation,
* add targetlist entries for the attributes which have not been used. * add targetlist entries for any missing attributes, and order the
* * non-junk attributes in proper field order.
* 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.
*/ */
static List * static List *
expand_targetlist(List *tlist, expand_targetlist(List *tlist, int command_type,
Oid relid, Index result_relation, List *range_table)
int command_type,
Index result_relation)
{
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, int old_tlist_len = length(tlist);
*i; List *new_tlist = NIL;
List *t_list = NIL; bool *tlistentry_used;
Relation rel;
foreach(i, new_tlist) int attrno,
{ numattrs,
TargetEntry *new_tle = (TargetEntry *) lfirst(i); old_tlist_index;
TargetEntry *matching_old_tl = NULL; List *temp;
foreach(temp, old_tlist)
{
TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
if (!strcmp(old_tle->resdom->resname, /*
new_tle->resdom->resname)) * Keep a map of which tlist items we have transferred to new list.
{ * +1 here keeps palloc from complaining if old_tlist_len=0.
matching_old_tl = old_tle; */
break; tlistentry_used = (bool *) palloc((old_tlist_len+1) * sizeof(bool));
} memset(tlistentry_used, 0, (old_tlist_len+1) * sizeof(bool));
}
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);
}
/* /*
* It is possible that 'old_tlist' has some negative attributes (i.e. * Scan the tuple description in the relation's relcache entry to make
* negative resnos). This only happens if this is a replace/append * sure we have all the user attributes in the right order.
* 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!
*/ */
foreach(temp, old_tlist) rel = heap_open(getrelid(result_relation, range_table), AccessShareLock);
{
TargetEntry *old_tle,
*new_tl;
old_tle = lfirst(temp); numattrs = RelationGetNumberOfAttributes(rel);
if (old_tle->resdom->resno < 0)
{ for (attrno = 1; attrno <= numattrs; attrno++)
Resdom *newresdom; {
Form_pg_attribute att_tup = rel->rd_att->attrs[attrno-1];
char *attrname = att_tup->attname.data;
TargetEntry *new_tle = NULL;
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 * We match targetlist entries to attributes using the resname.
* 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.
*/ */
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;
/* if (! tlistentry_used[old_tlist_index] &&
* Check if the tle is already in the new list strcmp(resdom->resname, attrname) == 0 &&
*/ ! resdom->resjunk)
foreach(i, t_list)
{ {
TargetEntry *new_tle = (TargetEntry *) lfirst(i); /*
* We can recycle the old TLE+resdom if right resno; else
if (new_tle->resdom->ressortgroupref == * make a new one to avoid modifying the old tlist structure.
old_tle->resdom->ressortgroupref) * (Is preserving old tlist actually necessary?)
*/
if (resdom->resno == attrno)
{ {
already_there = true; new_tle = old_tle;
break;
} }
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 not, add it and make sure it is now a junk attribute
*/
if (!already_there)
{
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; if (new_tle == NULL)
}
/*
* 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); * Didn't find a matching tlist entry, so make one.
} *
att_tup = (Form_pg_attribute) GETSTRUCT(tp); * For INSERT, generate a constant of the default value for
attname = pstrdup(att_tup->attname.data); * the attribute type, or NULL if no default value.
atttype = att_tup->atttypid; *
atttypmod = att_tup->atttypmod; * For UPDATE, generate a Var reference to the existing value
attisset = att_tup->attisset; * of the attribute, so that it gets copied to the new tuple.
*/
Oid atttype = att_tup->atttypid;
int32 atttypmod = att_tup->atttypmod;
switch (node_type) switch (command_type)
{ {
case T_Const: /* INSERT command */ case CMD_INSERT:
{ {
Datum typedefault = get_typdefault(atttype); Datum typedefault = get_typdefault(atttype);
int typlen; int typlen;
Const *temp_const; Const *temp_const;
TargetEntry *temp_tle;
if (typedefault == PointerGetDatum(NULL)) if (typedefault == PointerGetDatum(NULL))
typlen = 0; typlen = 0;
...@@ -308,7 +201,7 @@ new_relation_targetlist(Oid relid, Index rt_index, NodeTag node_type) ...@@ -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 * any set attribute is the size of the OID used to
* represent it. * represent it.
*/ */
if (attisset) if (att_tup->attisset)
typlen = get_typlen(OIDOID); typlen = get_typlen(OIDOID);
else else
typlen = get_typlen(atttype); typlen = get_typlen(atttype);
...@@ -322,40 +215,69 @@ new_relation_targetlist(Oid relid, Index rt_index, NodeTag node_type) ...@@ -322,40 +215,69 @@ new_relation_targetlist(Oid relid, Index rt_index, NodeTag node_type)
false, /* not a set */ false, /* not a set */
false); false);
temp_tle = makeTargetEntry(makeResdom(attno, new_tle = makeTargetEntry(makeResdom(attrno,
atttype, atttype,
-1, -1,
attname, pstrdup(attrname),
0, 0,
(Oid) 0, (Oid) 0,
false), false),
(Node *) temp_const); (Node *) temp_const);
t_list = lappend(t_list, temp_tle);
break; break;
} }
case T_Var: /* UPDATE command */ case CMD_UPDATE:
{ {
Var *temp_var; Var *temp_var;
TargetEntry *temp_tle;
temp_var = makeVar(rt_index, attno, atttype, temp_var = makeVar(result_relation, attrno, atttype,
atttypmod, 0); atttypmod, 0);
temp_tle = makeTargetEntry(makeResdom(attno, new_tle = makeTargetEntry(makeResdom(attrno,
atttype, atttype,
atttypmod, atttypmod,
attname, pstrdup(attrname),
0, 0,
(Oid) 0, (Oid) 0,
false), false),
(Node *) temp_var); (Node *) temp_var);
t_list = lappend(t_list, temp_tle);
break; break;
} }
default: /* do nothing */ default:
break; elog(ERROR, "expand_targetlist: unexpected command_type");
break;
}
}
new_tlist = lappend(new_tlist, new_tle);
}
/*
* 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++;
} }
return t_list; heap_close(rel, AccessShareLock);
pfree(tlistentry_used);
return new_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