1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-06 19:59:18 +03:00

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

View File

@ -3,40 +3,40 @@
* preptlist.c * preptlist.c
* Routines to preprocess the parse tree target list * 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 * 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,
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,
result_relation, range_table);
t_list = copyObject(expanded_tlist); /*
* for "update" and "delete" queries, add ctid of the result
/* ------------------ * relation into the target list so that the ctid will propagate
* for "replace" or "delete" queries, add ctid of the result * through execution and ExecutePlan() will be able to identify
* relation into the target list so that the ctid can get * the right tuple to replace or delete. This extra field is
* propogate through the execution and in the end ExecReplace() * marked "junk" so that it is not stored back into the tuple.
* 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.
* ------------------
*/ */
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);
ctid = makeTargetEntry(resdom, (Node *) var); /* For an UPDATE, expand_targetlist already created a fresh tlist.
t_list = lappend(t_list, ctid); * 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 * 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; int old_tlist_len = length(tlist);
List *new_tlist = NIL;
switch (command_type) bool *tlistentry_used;
{ Relation rel;
case CMD_INSERT: int attrno,
node_type = (NodeTag) T_Const; numattrs,
break; old_tlist_index;
case CMD_UPDATE: List *temp;
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);
}
/* /*
* It is possible that 'old_tlist' has some negative attributes (i.e. * Keep a map of which tlist items we have transferred to new list.
* negative resnos). This only happens if this is a replace/append * +1 here keeps palloc from complaining if old_tlist_len=0.
* 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) 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, Form_pg_attribute att_tup = rel->rd_att->attrs[attrno-1];
*new_tl; 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 * 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 (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; case CMD_INSERT:
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 */
{ {
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)
* 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)
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);
} }
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;
} }