1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-02 09:02:37 +03:00

Refactor ExecGetJunkAttribute to avoid searching for junk attributes

by name on each and every row processed.  Profiling suggests this may
buy a percent or two for simple UPDATE scenarios, which isn't huge,
but when it's so easy to get ...
This commit is contained in:
Tom Lane
2006-12-04 02:06:55 +00:00
parent 406d028a9b
commit 8dcc8e3761
4 changed files with 90 additions and 54 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.54 2006/07/14 14:52:18 momjian Exp $
* $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.55 2006/12/04 02:06:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -26,24 +26,25 @@
* never make it out of the executor, i.e. they are never printed,
* returned or stored on disk. Their only purpose in life is to
* store some information useful only to the executor, mainly the values
* of some system attributes like "ctid" or rule locks.
* of system attributes like "ctid", or sort key columns that are not to
* be output.
*
* The general idea is the following: A target list consists of a list of
* TargetEntry nodes containing expressions. Each TargetEntry has a field
* called 'resjunk'. If the value of this field is true then the
* corresponding attribute is a "junk" attribute.
*
* When we initialize a plan we call 'ExecInitJunkFilter' to create
* and store the appropriate information in the 'es_junkFilter' attribute of
* When we initialize a plan we call ExecInitJunkFilter to create
* and store the appropriate information in the es_junkFilter attribute of
* EState.
*
* We then execute the plan ignoring the "resjunk" attributes.
* We then execute the plan, treating the resjunk attributes like any others.
*
* Finally, when at the top level we get back a tuple, we can call
* ExecGetJunkAttribute to retrieve the value of the junk attributes we
* are interested in, and ExecFilterJunk or ExecRemoveJunk to remove all
* the junk attributes from a tuple. This new "clean" tuple is then printed,
* replaced, deleted or inserted.
* ExecFindJunkAttribute/ExecGetJunkAttribute to retrieve the values of the
* junk attributes we are interested in, and ExecFilterJunk or ExecRemoveJunk
* to remove all the junk attributes from a tuple. This new "clean" tuple is
* then printed, replaced, deleted or inserted.
*
*-------------------------------------------------------------------------
*/
@ -201,26 +202,16 @@ ExecInitJunkFilterConversion(List *targetList,
}
/*
* ExecGetJunkAttribute
* ExecFindJunkAttribute
*
* Given a tuple (slot), the junk filter and a junk attribute's name,
* extract & return the value and isNull flag of this attribute.
*
* It returns false iff no junk attribute with such name was found.
* Locate the specified junk attribute in the junk filter's targetlist,
* and return its resno. Returns InvalidAttrNumber if not found.
*/
bool
ExecGetJunkAttribute(JunkFilter *junkfilter,
TupleTableSlot *slot,
char *attrName,
Datum *value,
bool *isNull)
AttrNumber
ExecFindJunkAttribute(JunkFilter *junkfilter, const char *attrName)
{
ListCell *t;
/*
* Look in the junkfilter's target list for an attribute with the given
* name
*/
foreach(t, junkfilter->jf_targetList)
{
TargetEntry *tle = lfirst(t);
@ -229,13 +220,27 @@ ExecGetJunkAttribute(JunkFilter *junkfilter,
(strcmp(tle->resname, attrName) == 0))
{
/* We found it ! */
*value = slot_getattr(slot, tle->resno, isNull);
return true;
return tle->resno;
}
}
/* Ooops! We couldn't find this attribute... */
return false;
return InvalidAttrNumber;
}
/*
* ExecGetJunkAttribute
*
* Given a junk filter's input tuple (slot) and a junk attribute's number
* previously found by ExecFindJunkAttribute, extract & return the value and
* isNull flag of the attribute.
*/
Datum
ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno,
bool *isNull)
{
Assert(attno > 0);
return slot_getattr(slot, attno, isNull);
}
/*

View File

@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.280 2006/10/04 00:29:52 momjian Exp $
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.281 2006/12/04 02:06:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -567,7 +567,9 @@ InitPlan(QueryDesc *queryDesc, int eflags)
}
/*
* Have to lock relations selected FOR UPDATE/FOR SHARE
* Have to lock relations selected FOR UPDATE/FOR SHARE before we
* initialize the plan tree, else we'd be doing a lock upgrade.
* While we are at it, build the ExecRowMark list.
*/
estate->es_rowMarks = NIL;
foreach(l, parseTree->rowMarks)
@ -583,7 +585,8 @@ InitPlan(QueryDesc *queryDesc, int eflags)
erm->rti = rc->rti;
erm->forUpdate = rc->forUpdate;
erm->noWait = rc->noWait;
snprintf(erm->resname, sizeof(erm->resname), "ctid%u", rc->rti);
/* We'll set up ctidAttno below */
erm->ctidAttNo = InvalidAttrNumber;
estate->es_rowMarks = lappend(estate->es_rowMarks, erm);
}
@ -703,6 +706,16 @@ InitPlan(QueryDesc *queryDesc, int eflags)
j = ExecInitJunkFilter(subplan->plan->targetlist,
resultRelInfo->ri_RelationDesc->rd_att->tdhasoid,
ExecAllocTableSlot(estate->es_tupleTable));
/*
* Since it must be UPDATE/DELETE, there had better be
* a "ctid" junk attribute in the tlist ... but ctid could
* be at a different resno for each result relation.
* We look up the ctid resnos now and save them in the
* junkfilters.
*/
j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
if (!AttributeNumberIsValid(j->jf_junkAttNo))
elog(ERROR, "could not find junk ctid column");
resultRelInfo->ri_junkFilter = j;
resultRelInfo++;
}
@ -726,9 +739,30 @@ InitPlan(QueryDesc *queryDesc, int eflags)
if (estate->es_result_relation_info)
estate->es_result_relation_info->ri_junkFilter = j;
/* For SELECT, want to return the cleaned tuple type */
if (operation == CMD_SELECT)
{
/* For SELECT, want to return the cleaned tuple type */
tupType = j->jf_cleanTupType;
/* For SELECT FOR UPDATE/SHARE, find the ctid attrs now */
foreach(l, estate->es_rowMarks)
{
ExecRowMark *erm = (ExecRowMark *) lfirst(l);
char resname[32];
snprintf(resname, sizeof(resname), "ctid%u", erm->rti);
erm->ctidAttNo = ExecFindJunkAttribute(j, resname);
if (!AttributeNumberIsValid(erm->ctidAttNo))
elog(ERROR, "could not find junk \"%s\" column",
resname);
}
}
else if (operation == CMD_UPDATE || operation == CMD_DELETE)
{
/* For UPDATE/DELETE, find the ctid junk attr now */
j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
if (!AttributeNumberIsValid(j->jf_junkAttNo))
elog(ERROR, "could not find junk ctid column");
}
}
}
else
@ -1111,13 +1145,8 @@ lnext: ;
*/
if (operation == CMD_UPDATE || operation == CMD_DELETE)
{
if (!ExecGetJunkAttribute(junkfilter,
slot,
"ctid",
&datum,
&isNull))
elog(ERROR, "could not find junk ctid column");
datum = ExecGetJunkAttribute(slot, junkfilter->jf_junkAttNo,
&isNull);
/* shouldn't ever get a null result... */
if (isNull)
elog(ERROR, "ctid is NULL");
@ -1146,17 +1175,12 @@ lnext: ;
LockTupleMode lockmode;
HTSU_Result test;
if (!ExecGetJunkAttribute(junkfilter,
slot,
erm->resname,
&datum,
&isNull))
elog(ERROR, "could not find junk \"%s\" column",
erm->resname);
datum = ExecGetJunkAttribute(slot,
erm->ctidAttNo,
&isNull);
/* shouldn't ever get a null result... */
if (isNull)
elog(ERROR, "\"%s\" is NULL", erm->resname);
elog(ERROR, "ctid is NULL");
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));