]> granicus.if.org Git - postgresql/commitdiff
Rewrite preprocess_targetlist() to reduce overhead for simple INSERTs.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 30 Oct 1999 23:06:32 +0000 (23:06 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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;
 }