]> granicus.if.org Git - postgresql/commitdiff
Refactor ExecGetJunkAttribute to avoid searching for junk attributes
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 4 Dec 2006 02:06:55 +0000 (02:06 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 4 Dec 2006 02:06:55 +0000 (02:06 +0000)
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
src/backend/executor/execMain.c
src/include/executor/executor.h
src/include/nodes/execnodes.h

index 25d62e749ce1e3aebd3d00f8a12411b1124e2031..3e8e189aa1debeef1c6243fd78c6d25fe3a89f02 100644 (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 $
  *
  *-------------------------------------------------------------------------
  */
  * 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);
 }
 
 /*
index 5e5ca085ba4a47270ef49903ed531423f452737f..6db354cd4f957e00bb9889e7b4a7120d1791c182 100644 (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));
 
index d030b4deea4724a6df02af4739a8ea8a98605e70..beb103c88e3d4932f37fa118ea8691cd0a5cf717 100644 (file)
@@ -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);
index 5cbe5a542876620199ddc6d822b7f696f870a4a6..273d5c33261fc2fdf1b8c648f3fc5edffac6abde 100644 (file)
@@ -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;