mirror of
https://github.com/postgres/postgres.git
synced 2025-06-16 06:01:02 +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:
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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,
|
* 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
|
* returned or stored on disk. Their only purpose in life is to
|
||||||
* store some information useful only to the executor, mainly the values
|
* 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
|
* The general idea is the following: A target list consists of a list of
|
||||||
* TargetEntry nodes containing expressions. Each TargetEntry has a field
|
* TargetEntry nodes containing expressions. Each TargetEntry has a field
|
||||||
* called 'resjunk'. If the value of this field is true then the
|
* called 'resjunk'. If the value of this field is true then the
|
||||||
* corresponding attribute is a "junk" attribute.
|
* corresponding attribute is a "junk" attribute.
|
||||||
*
|
*
|
||||||
* When we initialize a plan we call 'ExecInitJunkFilter' to create
|
* When we initialize a plan we call ExecInitJunkFilter to create
|
||||||
* and store the appropriate information in the 'es_junkFilter' attribute of
|
* and store the appropriate information in the es_junkFilter attribute of
|
||||||
* EState.
|
* 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
|
* Finally, when at the top level we get back a tuple, we can call
|
||||||
* ExecGetJunkAttribute to retrieve the value of the junk attributes we
|
* ExecFindJunkAttribute/ExecGetJunkAttribute to retrieve the values of the
|
||||||
* are interested in, and ExecFilterJunk or ExecRemoveJunk to remove all
|
* junk attributes we are interested in, and ExecFilterJunk or ExecRemoveJunk
|
||||||
* the junk attributes from a tuple. This new "clean" tuple is then printed,
|
* to remove all the junk attributes from a tuple. This new "clean" tuple is
|
||||||
* replaced, deleted or inserted.
|
* 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,
|
* Locate the specified junk attribute in the junk filter's targetlist,
|
||||||
* extract & return the value and isNull flag of this attribute.
|
* and return its resno. Returns InvalidAttrNumber if not found.
|
||||||
*
|
|
||||||
* It returns false iff no junk attribute with such name was found.
|
|
||||||
*/
|
*/
|
||||||
bool
|
AttrNumber
|
||||||
ExecGetJunkAttribute(JunkFilter *junkfilter,
|
ExecFindJunkAttribute(JunkFilter *junkfilter, const char *attrName)
|
||||||
TupleTableSlot *slot,
|
|
||||||
char *attrName,
|
|
||||||
Datum *value,
|
|
||||||
bool *isNull)
|
|
||||||
{
|
{
|
||||||
ListCell *t;
|
ListCell *t;
|
||||||
|
|
||||||
/*
|
|
||||||
* Look in the junkfilter's target list for an attribute with the given
|
|
||||||
* name
|
|
||||||
*/
|
|
||||||
foreach(t, junkfilter->jf_targetList)
|
foreach(t, junkfilter->jf_targetList)
|
||||||
{
|
{
|
||||||
TargetEntry *tle = lfirst(t);
|
TargetEntry *tle = lfirst(t);
|
||||||
@ -229,13 +220,27 @@ ExecGetJunkAttribute(JunkFilter *junkfilter,
|
|||||||
(strcmp(tle->resname, attrName) == 0))
|
(strcmp(tle->resname, attrName) == 0))
|
||||||
{
|
{
|
||||||
/* We found it ! */
|
/* We found it ! */
|
||||||
*value = slot_getattr(slot, tle->resno, isNull);
|
return tle->resno;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ooops! We couldn't find this attribute... */
|
return InvalidAttrNumber;
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
estate->es_rowMarks = NIL;
|
||||||
foreach(l, parseTree->rowMarks)
|
foreach(l, parseTree->rowMarks)
|
||||||
@ -583,7 +585,8 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|||||||
erm->rti = rc->rti;
|
erm->rti = rc->rti;
|
||||||
erm->forUpdate = rc->forUpdate;
|
erm->forUpdate = rc->forUpdate;
|
||||||
erm->noWait = rc->noWait;
|
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);
|
estate->es_rowMarks = lappend(estate->es_rowMarks, erm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -703,6 +706,16 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|||||||
j = ExecInitJunkFilter(subplan->plan->targetlist,
|
j = ExecInitJunkFilter(subplan->plan->targetlist,
|
||||||
resultRelInfo->ri_RelationDesc->rd_att->tdhasoid,
|
resultRelInfo->ri_RelationDesc->rd_att->tdhasoid,
|
||||||
ExecAllocTableSlot(estate->es_tupleTable));
|
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->ri_junkFilter = j;
|
||||||
resultRelInfo++;
|
resultRelInfo++;
|
||||||
}
|
}
|
||||||
@ -726,9 +739,30 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|||||||
if (estate->es_result_relation_info)
|
if (estate->es_result_relation_info)
|
||||||
estate->es_result_relation_info->ri_junkFilter = j;
|
estate->es_result_relation_info->ri_junkFilter = j;
|
||||||
|
|
||||||
/* For SELECT, want to return the cleaned tuple type */
|
|
||||||
if (operation == CMD_SELECT)
|
if (operation == CMD_SELECT)
|
||||||
|
{
|
||||||
|
/* For SELECT, want to return the cleaned tuple type */
|
||||||
tupType = j->jf_cleanTupType;
|
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
|
else
|
||||||
@ -1111,13 +1145,8 @@ lnext: ;
|
|||||||
*/
|
*/
|
||||||
if (operation == CMD_UPDATE || operation == CMD_DELETE)
|
if (operation == CMD_UPDATE || operation == CMD_DELETE)
|
||||||
{
|
{
|
||||||
if (!ExecGetJunkAttribute(junkfilter,
|
datum = ExecGetJunkAttribute(slot, junkfilter->jf_junkAttNo,
|
||||||
slot,
|
&isNull);
|
||||||
"ctid",
|
|
||||||
&datum,
|
|
||||||
&isNull))
|
|
||||||
elog(ERROR, "could not find junk ctid column");
|
|
||||||
|
|
||||||
/* shouldn't ever get a null result... */
|
/* shouldn't ever get a null result... */
|
||||||
if (isNull)
|
if (isNull)
|
||||||
elog(ERROR, "ctid is NULL");
|
elog(ERROR, "ctid is NULL");
|
||||||
@ -1146,17 +1175,12 @@ lnext: ;
|
|||||||
LockTupleMode lockmode;
|
LockTupleMode lockmode;
|
||||||
HTSU_Result test;
|
HTSU_Result test;
|
||||||
|
|
||||||
if (!ExecGetJunkAttribute(junkfilter,
|
datum = ExecGetJunkAttribute(slot,
|
||||||
slot,
|
erm->ctidAttNo,
|
||||||
erm->resname,
|
&isNull);
|
||||||
&datum,
|
|
||||||
&isNull))
|
|
||||||
elog(ERROR, "could not find junk \"%s\" column",
|
|
||||||
erm->resname);
|
|
||||||
|
|
||||||
/* shouldn't ever get a null result... */
|
/* shouldn't ever get a null result... */
|
||||||
if (isNull)
|
if (isNull)
|
||||||
elog(ERROR, "\"%s\" is NULL", erm->resname);
|
elog(ERROR, "ctid is NULL");
|
||||||
|
|
||||||
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
|
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.130 2006/10/04 00:30:08 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.131 2006/12/04 02:06:55 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -110,8 +110,10 @@ extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid,
|
|||||||
extern JunkFilter *ExecInitJunkFilterConversion(List *targetList,
|
extern JunkFilter *ExecInitJunkFilterConversion(List *targetList,
|
||||||
TupleDesc cleanTupType,
|
TupleDesc cleanTupType,
|
||||||
TupleTableSlot *slot);
|
TupleTableSlot *slot);
|
||||||
extern bool ExecGetJunkAttribute(JunkFilter *junkfilter, TupleTableSlot *slot,
|
extern AttrNumber ExecFindJunkAttribute(JunkFilter *junkfilter,
|
||||||
char *attrName, Datum *value, bool *isNull);
|
const char *attrName);
|
||||||
|
extern Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno,
|
||||||
|
bool *isNull);
|
||||||
extern TupleTableSlot *ExecFilterJunk(JunkFilter *junkfilter,
|
extern TupleTableSlot *ExecFilterJunk(JunkFilter *junkfilter,
|
||||||
TupleTableSlot *slot);
|
TupleTableSlot *slot);
|
||||||
extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot);
|
extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.161 2006/09/28 20:51:42 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.162 2006/12/04 02:06:55 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -226,7 +226,8 @@ typedef struct ProjectionInfo
|
|||||||
* the tuple to be updated. This is needed to do the update, but we
|
* the tuple to be updated. This is needed to do the update, but we
|
||||||
* don't want the ctid to be part of the stored new tuple! So, we
|
* don't want the ctid to be part of the stored new tuple! So, we
|
||||||
* apply a "junk filter" to remove the junk attributes and form the
|
* apply a "junk filter" to remove the junk attributes and form the
|
||||||
* real output tuple.
|
* real output tuple. The junkfilter code also provides routines to
|
||||||
|
* extract the values of the junk attribute(s) from the input tuple.
|
||||||
*
|
*
|
||||||
* targetList: the original target list (including junk attributes).
|
* targetList: the original target list (including junk attributes).
|
||||||
* cleanTupType: the tuple descriptor for the "clean" tuple (with
|
* cleanTupType: the tuple descriptor for the "clean" tuple (with
|
||||||
@ -235,6 +236,9 @@ typedef struct ProjectionInfo
|
|||||||
* attribute numbers of the "original" tuple and the
|
* attribute numbers of the "original" tuple and the
|
||||||
* attribute numbers of the "clean" tuple.
|
* attribute numbers of the "clean" tuple.
|
||||||
* resultSlot: tuple slot used to hold cleaned tuple.
|
* resultSlot: tuple slot used to hold cleaned tuple.
|
||||||
|
* junkAttNo: not used by junkfilter code. Can be used by caller
|
||||||
|
* to remember the attno of a specific junk attribute
|
||||||
|
* (execMain.c stores the "ctid" attno here).
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
typedef struct JunkFilter
|
typedef struct JunkFilter
|
||||||
@ -244,6 +248,7 @@ typedef struct JunkFilter
|
|||||||
TupleDesc jf_cleanTupType;
|
TupleDesc jf_cleanTupType;
|
||||||
AttrNumber *jf_cleanMap;
|
AttrNumber *jf_cleanMap;
|
||||||
TupleTableSlot *jf_resultSlot;
|
TupleTableSlot *jf_resultSlot;
|
||||||
|
AttrNumber jf_junkAttNo;
|
||||||
} JunkFilter;
|
} JunkFilter;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
@ -352,7 +357,7 @@ typedef struct ExecRowMark
|
|||||||
Index rti; /* its range table index */
|
Index rti; /* its range table index */
|
||||||
bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */
|
bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */
|
||||||
bool noWait; /* NOWAIT option */
|
bool noWait; /* NOWAIT option */
|
||||||
char resname[32]; /* name for its ctid junk attribute */
|
AttrNumber ctidAttNo; /* resno of its ctid junk attribute */
|
||||||
} ExecRowMark;
|
} ExecRowMark;
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user