Rewrite preprocess_targetlist() to reduce overhead for simple INSERTs.
authorTom Lane
Sat, 30 Oct 1999 23:06:32 +0000 (23:06 +0000)
committerTom Lane
Sat, 30 Oct 1999 23:06:32 +0000 (23:06 +0000)
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.

src/backend/optimizer/prep/preptlist.c

index 95e5ddbc9db923277571ed8a09aceabd7763c9b9..f3158050d9f924872856a92ea008222cdc9eb29b 100644 (file)
@@ -3,40 +3,40 @@
  * preptlist.c
  *   Routines to preprocess the parse tree target list
  *
- * Copyright (c) 1994, Regents of the University of California
+ * 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
  *
  * 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 "access/heapam.h"
 #include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
-#include "optimizer/clauses.h"
 #include "optimizer/prep.h"
 #include "parser/parsetree.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 *replace_matching_resname(List *new_tlist,
-                        List *old_tlist);
-static List *new_relation_targetlist(Oid relid, Index rt_index,
-                       NodeTag node_type);
+
+static List *expand_targetlist(List *tlist, int command_type,
+                              Index result_relation, List *range_table);
 
 
 /*
  * preprocess_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.
  */
 List *
@@ -45,53 +45,49 @@ preprocess_targetlist(List *tlist,
                      Index result_relation,
                      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
-    * order of the attributes. We also need to fill in the missing
-    * attributes here.                         -ay 10/94
+    * order of the attributes. We also need to fill in any missing
+    * attributes                         -ay 10/94
     */
-   expanded_tlist = expand_targetlist(tlist, relid, command_type, result_relation);
-
-   t_list = copyObject(expanded_tlist);
-
-   /* ------------------
-    *  for "replace" or "delete" queries, add ctid of the result
-    *  relation into the target list so that the ctid can get
-    *  propogate through the execution and in the end ExecReplace()
-    *  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_INSERT || command_type == CMD_UPDATE)
+       tlist = expand_targetlist(tlist, command_type,
+                                 result_relation, range_table);
+
+   /*
+    *  for "update" and "delete" queries, add ctid of the result
+    *  relation into the target list so that the ctid will propagate
+    *  through execution and ExecutePlan() will be able to identify
+    *  the right tuple to replace or delete.  This extra field is
+    *  marked "junk" so that it is not stored back into the tuple.
     */
    if (command_type == CMD_UPDATE || command_type == CMD_DELETE)
    {
-       TargetEntry *ctid;
        Resdom     *resdom;
        Var        *var;
 
-       resdom = makeResdom(length(t_list) + 1,
+       resdom = makeResdom(length(tlist) + 1,
                            TIDOID,
                            -1,
-                           "ctid",
+                           pstrdup("ctid"),
                            0,
                            0,
                            true);
 
-       var = makeVar(result_relation, -1, TIDOID, -1, 0);
+       var = makeVar(result_relation, SelfItemPointerAttributeNumber,
+                     TIDOID, -1, 0);
+
+       /* For an UPDATE, expand_targetlist already created a fresh tlist.
+        * 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);
 
-       ctid = makeTargetEntry(resdom, (Node *) var);
-       t_list = lappend(t_list, ctid);
+       tlist = lappend(tlist, makeTargetEntry(resdom, (Node *) var));
    }
 
-   return t_list;
+   return tlist;
 }
 
 /*****************************************************************************
@@ -103,201 +99,98 @@ preprocess_targetlist(List *tlist,
 /*
  * expand_targetlist
  *   Given a target list as generated by the parser and a result relation,
- *   add targetlist entries for the attributes which have not been used.
- *
- *   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.
+ *   add targetlist entries for any missing attributes, and order the
+ *   non-junk attributes in proper field order.
  */
 static List *
-expand_targetlist(List *tlist,
-                 Oid relid,
-                 int command_type,
-                 Index result_relation)
-{
-   NodeTag     node_type = T_Invalid;
-
-   switch (command_type)
-   {
-       case CMD_INSERT:
-           node_type = (NodeTag) T_Const;
-           break;
-       case CMD_UPDATE:
-           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)
+expand_targetlist(List *tlist, int command_type,
+                 Index result_relation, List *range_table)
 {
-   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);
+   int         old_tlist_len = length(tlist);
+   List       *new_tlist = NIL;
+   bool       *tlistentry_used;
+   Relation    rel;
+   int         attrno,
+               numattrs,
+               old_tlist_index;
+   List       *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);
-   }
+   /*
+    * Keep a map of which tlist items we have transferred to new list.
+    * +1 here keeps palloc from complaining if old_tlist_len=0.
+    */
+   tlistentry_used = (bool *) palloc((old_tlist_len+1) * sizeof(bool));
+   memset(tlistentry_used, 0, (old_tlist_len+1) * sizeof(bool));
 
    /*
-    * It is possible that 'old_tlist' has some negative attributes (i.e.
-    * negative resnos). This only happens if this is a replace/append
-    * 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!
+    * Scan the tuple description in the relation's relcache entry to make
+    * sure we have all the user attributes in the right order.
     */
-   foreach(temp, old_tlist)
-   {
-       TargetEntry *old_tle,
-                  *new_tl;
+   rel = heap_open(getrelid(result_relation, range_table), AccessShareLock);
 
-       old_tle = lfirst(temp);
-       if (old_tle->resdom->resno < 0)
-       {
-           Resdom     *newresdom;
+   numattrs = RelationGetNumberOfAttributes(rel);
+
+   for (attrno = 1; attrno <= numattrs; attrno++)
+   {
+       Form_pg_attribute att_tup = rel->rd_att->attrs[attrno-1];
+       char           *attrname = att_tup->attname.data;
+       TargetEntry    *new_tle = NULL;
 
-           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
-        * 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.
+        * We match targetlist entries to attributes using the resname.
         */
-       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;
 
-           /*
-            * Check if the tle is already in the new list
-            */
-           foreach(i, t_list)
+           if (! tlistentry_used[old_tlist_index] &&
+               strcmp(resdom->resname, attrname) == 0 &&
+               ! resdom->resjunk)
            {
-               TargetEntry *new_tle = (TargetEntry *) lfirst(i);
-
-               if (new_tle->resdom->ressortgroupref ==
-                   old_tle->resdom->ressortgroupref)
+               /*
+                * We can recycle the old TLE+resdom if right resno; else
+                * make a new one to avoid modifying the old tlist structure.
+                * (Is preserving old tlist actually necessary?)
+                */
+               if (resdom->resno == attrno)
                {
-                   already_there = true;
-                   break;
+                   new_tle = old_tle;
                }
+               else
+               {
+                   resdom = (Resdom *) copyObject((Node *) resdom);
+                   resdom->resno = attrno;
+                   new_tle = makeTargetEntry(resdom, old_tle->expr);
+               }
+               tlistentry_used[old_tlist_index] = true;
+               break;
            }
-
-           /*
-            * If not, add it and make sure it is now a junk attribute
-            */
-           if (!already_there)
-           {
-               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);
-           }
+           old_tlist_index++;
        }
-   }
 
-   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))
+       if (new_tle == NULL)
        {
-           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;
+           /*
+            * 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.
+            */
+           Oid         atttype = att_tup->atttypid;
+           int32       atttypmod = att_tup->atttypmod;
 
-       switch (node_type)
-       {
-           case T_Const:       /* INSERT command */
+           switch (command_type)
+           {
+               case CMD_INSERT:
                {
                    Datum       typedefault = get_typdefault(atttype);
                    int         typlen;
                    Const      *temp_const;
-                   TargetEntry *temp_tle;
 
                    if (typedefault == PointerGetDatum(NULL))
                        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
                         * represent it.
                         */
-                       if (attisset)
+                       if (att_tup->attisset)
                            typlen = get_typlen(OIDOID);
                        else
                            typlen = get_typlen(atttype);
@@ -322,40 +215,69 @@ new_relation_targetlist(Oid relid, Index rt_index, NodeTag node_type)
                                           false, /* not a set */
                                           false);
 
-                   temp_tle = makeTargetEntry(makeResdom(attno,
-                                                         atttype,
-                                                         -1,
-                                                         attname,
-                                                         0,
-                                                         (Oid) 0,
-                                                         false),
-                                              (Node *) temp_const);
-                   t_list = lappend(t_list, temp_tle);
+                   new_tle = makeTargetEntry(makeResdom(attrno,
+                                                        atttype,
+                                                        -1,
+                                                        pstrdup(attrname),
+                                                        0,
+                                                        (Oid) 0,
+                                                        false),
+                                             (Node *) temp_const);
                    break;
                }
-           case T_Var:         /* UPDATE command */
+               case CMD_UPDATE:
                {
                    Var        *temp_var;
-                   TargetEntry *temp_tle;
 
-                   temp_var = makeVar(rt_index, attno, atttype,
+                   temp_var = makeVar(result_relation, attrno, atttype,
                                       atttypmod, 0);
 
-                   temp_tle = makeTargetEntry(makeResdom(attno,
-                                                         atttype,
-                                                         atttypmod,
-                                                         attname,
-                                                         0,
-                                                         (Oid) 0,
-                                                         false),
-                                              (Node *) temp_var);
-                   t_list = lappend(t_list, temp_tle);
+                   new_tle = makeTargetEntry(makeResdom(attrno,
+                                                        atttype,
+                                                        atttypmod,
+                                                        pstrdup(attrname),
+                                                        0,
+                                                        (Oid) 0,
+                                                        false),
+                                             (Node *) temp_var);
                    break;
                }
-           default:            /* do nothing */
-               break;
+               default:
+                   elog(ERROR, "expand_targetlist: unexpected command_type");
+                   break;
+           }
+       }
+
+       new_tlist = lappend(new_tlist, new_tle);
+   }
+
+   /*
+    * 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++;
    }
 
-   return t_list;
+   heap_close(rel, AccessShareLock);
+
+   pfree(tlistentry_used);
+
+   return new_tlist;
 }