From 8dcc8e376149adcfba012a9e9b7b0692bcbff9a2 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 4 Dec 2006 02:06:55 +0000 Subject: [PATCH] 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 ... --- src/backend/executor/execJunk.c | 61 ++++++++++++++++--------------- src/backend/executor/execMain.c | 64 ++++++++++++++++++++++----------- src/include/executor/executor.h | 8 +++-- src/include/nodes/execnodes.h | 11 ++++-- 4 files changed, 90 insertions(+), 54 deletions(-) diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c index 25d62e749c..3e8e189aa1 100644 --- a/src/backend/executor/execJunk.c +++ b/src/backend/executor/execJunk.c @@ -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 - * - * Given a tuple (slot), the junk filter and a junk attribute's name, - * extract & return the value and isNull flag of this attribute. + * ExecFindJunkAttribute * - * 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); } /* diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 5e5ca085ba..6db354cd4f 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -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)); diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index d030b4deea..beb103c88e 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * 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, TupleDesc cleanTupType, TupleTableSlot *slot); -extern bool ExecGetJunkAttribute(JunkFilter *junkfilter, TupleTableSlot *slot, - char *attrName, Datum *value, bool *isNull); +extern AttrNumber ExecFindJunkAttribute(JunkFilter *junkfilter, + const char *attrName); +extern Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, + bool *isNull); extern TupleTableSlot *ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot); extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 5cbe5a5428..273d5c3326 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * 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 * 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 - * 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). * 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 "clean" 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 @@ -244,6 +248,7 @@ typedef struct JunkFilter TupleDesc jf_cleanTupType; AttrNumber *jf_cleanMap; TupleTableSlot *jf_resultSlot; + AttrNumber jf_junkAttNo; } JunkFilter; /* ---------------- @@ -352,7 +357,7 @@ typedef struct ExecRowMark Index rti; /* its range table index */ bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */ bool noWait; /* NOWAIT option */ - char resname[32]; /* name for its ctid junk attribute */ + AttrNumber ctidAttNo; /* resno of its ctid junk attribute */ } ExecRowMark; -- 2.40.0