Thread: Fix issuing of multiple command completions per command

Fix issuing of multiple command completions per command

From
Fernando Nasser
Date:
(Please note that "[PATCHES] Allow arbitrary levels of
analyze/rewriting"
must be applied before this patch)

This patch prevents the issue of multiple command completions
when queries are rewritten into a sequence queries by analyze/rewrite.
Currently the front ends are receiving many;  libpq just uses the last
one received so that is why we see only one response in psql, for
instance.

It fixes a bug where the user receives a bogus completion message
for queries that have been turned into a sequence of queries where
the last one has a different completion tag.

It also prevents command-completions to be generated by utility commands
that are inside SQL-functions.

It also fixes some misplaced calls to set ps_status.

Furthermore, these changes made possible to reduce the number of
times ps_status is set (a request from Tom Lane). This is done by
default but it is still possible to set a compiler flag and have
it generate more calls as it does today (one for each query produced
by the rewriting of the original query).


Here is basically what is done:

The completion is sent by EndCommand().  This call is used OK by
plannable queries and is necessary there for telling that the data
sent to a backend has come to an end.  Utilities, however, don't
send any data (except FETCH) and may be rewritten into a series of
other utility queries.  The changes consolidate the calls to
EndCommand() for utility queries and let the plannable ones
handle it pairwise with BeginCommand() (which is not used by
utility queries).


ChangeLog:

        * src/backend/commands/command.c (PerformPortalFetch): Send
        command completion so that fe knows we are done with sending
data.
        * src/backend/commands/explain.c (ExplainOneQuery): Set
ps_status
        to EXPLAIN or SELECT (for the EXPLAIN ANALYZE case).
        * src/backend/executor/functions.c (postquel_getnext): Do not
send
        command completion messages for utilities inside SQL-functions
        (one will be sent for the command that invoked the function).
        * src/backend/executor/spi.c (_SPI_execute, _SPI_execute_plan):
Add
        comments.
        * src/backend/tcop/postgres.c (pg_exec_query_string):
        Do not send a command completion message or set the ps_status
for
        each utility query that results for the rewriting of a command;
        just send one for the original command.  Let plannable queries
take
        care of their own completion.
        (CreateCommandTag): New function.  Create a command completion
tag
        based on the parse tree node (rather than on an already/analyzed
        query node).
        * src/backend/tcop/utility.c (ProcessUtility): Do
        not meddle with ps_status or command completion in this
function.
        * src/backend/tcop/pquery.c (ProcessQuery): Let the caller set
        ps_status.





--
Fernando Nasser
Red Hat Canada Ltd.                     E-Mail:  fnasser@redhat.com
2323 Yonge Street, Suite #300
Toronto, Ontario   M4P 2C9Index: src/backend/commands/command.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/commands/command.c,v
retrieving revision 1.152
diff -c -p -r1.152 command.c
*** src/backend/commands/command.c    2002/01/03 23:19:30    1.152
--- src/backend/commands/command.c    2002/02/14 10:49:18
*************** PerformPortalFetch(char *name,
*** 215,231 ****
      }

      /*
       * Clean up and switch back to old context.
       */
      if (temp_desc)
          pfree(queryDesc);

      MemoryContextSwitchTo(oldcontext);
-
-     /*
-      * Note: the "end-of-command" tag is returned by higher-level utility
-      * code
-      */
  }

  /* --------------------------------
--- 215,231 ----
      }

      /*
+      * tell fe/be or whatever that we're done sending the data
+      */
+     EndCommand(tag, queryDesc->dest);
+
+     /*
       * Clean up and switch back to old context.
       */
      if (temp_desc)
          pfree(queryDesc);

      MemoryContextSwitchTo(oldcontext);
  }

  /* --------------------------------
Index: src/backend/commands/explain.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/commands/explain.c,v
retrieving revision 1.67
diff -c -p -r1.67 explain.c
*** src/backend/commands/explain.c    2001/10/25 05:49:25    1.67
--- src/backend/commands/explain.c    2002/02/14 10:49:18
***************
*** 19,24 ****
--- 19,25 ----
  #include "parser/parsetree.h"
  #include "rewrite/rewriteHandler.h"
  #include "tcop/pquery.h"
+ #include "utils/ps_status.h"
  #include "utils/relcache.h"

  typedef struct ExplainState
*************** ExplainOneQuery(Query *query, bool verbo
*** 113,118 ****
--- 114,121 ----
          struct timeval starttime;
          struct timeval endtime;

+         set_ps_display("SELECT");
+
          /*
           * Set up the instrumentation for the top node. This will cascade
           * during plan initialisation
*************** ExplainOneQuery(Query *query, bool verbo
*** 133,138 ****
--- 136,143 ----
          }
          totaltime = (double) endtime.tv_sec +
              (double) endtime.tv_usec / 1000000.0;
+
+         set_ps_display("EXPLAIN");
      }

      es = (ExplainState *) palloc(sizeof(ExplainState));
Index: src/backend/executor/functions.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/executor/functions.c,v
retrieving revision 1.47
diff -c -p -r1.47 functions.c
*** src/backend/executor/functions.c    2001/10/28 06:25:43    1.47
--- src/backend/executor/functions.c    2002/02/14 10:49:18
*************** postquel_getnext(execution_state *es)
*** 276,281 ****
--- 276,283 ----
           * Process a utility command. (create, destroy...)    DZ - 30-8-1996
           */
          ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest);
+         /* We should not call EndCommand() here as we are inside a function
+          * call and command-complete reports should not be issued. */
          if (!LAST_POSTQUEL_COMMAND(es))
              CommandCounterIncrement();
          return (TupleTableSlot *) NULL;
Index: src/backend/executor/spi.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/executor/spi.c,v
retrieving revision 1.64
diff -c -p -r1.64 spi.c
*** src/backend/executor/spi.c    2002/01/03 20:30:47    1.64
--- src/backend/executor/spi.c    2002/02/14 10:49:18
*************** _SPI_execute(char *src, int tcount, _SPI
*** 1010,1015 ****
--- 1010,1017 ----
              if (plan == NULL)
              {
                  ProcessUtility(queryTree->utilityStmt, None);
+                 /* We don't need to call EndCommand() as we don't want to
+                  * send anything (dest = None) */
                  if (!islastquery)
                      CommandCounterIncrement();
                  else
*************** _SPI_execute_plan(_SPI_plan *plan, Datum
*** 1084,1089 ****
--- 1086,1093 ----
          if (queryTree->commandType == CMD_UTILITY)
          {
              ProcessUtility(queryTree->utilityStmt, None);
+             /* We don't need to call EndCommand() as we don't want to
+              * send anything (dest = None) */
              if (!islastquery)
                  CommandCounterIncrement();
              else
Index: src/backend/parser/analyze.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/parser/analyze.c,v
retrieving revision 1.213
diff -c -p -r1.213 analyze.c
*** src/backend/parser/analyze.c    2002/01/03 23:21:31    1.213
--- src/backend/parser/analyze.c    2002/02/14 10:49:18
*************** typedef struct
*** 64,80 ****
  } CreateStmtContext;


! static Query *transformStmt(ParseState *pstate, Node *stmt);
  static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
! static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
  static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
! static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt);
  static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
  static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
  static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
  static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
! static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
! static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt);
  static void transformColumnDefinition(ParseState *pstate,
                            CreateStmtContext *cxt,
                            ColumnDef *column);
--- 64,85 ----
  } CreateStmtContext;


! static Query *transformStmt(ParseState *pstate, Node *stmt,
!                         List **extras_before, List **extras_after);
  static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
! static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
!                         List **extras_before, List **extras_after);
  static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
! static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt,
!                         List **extras_before, List **extras_after);
  static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
  static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
  static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
  static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
! static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
!                         List **extras_before, List **extras_after);
! static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
!                         List **extras_before, List **extras_after);
  static void transformColumnDefinition(ParseState *pstate,
                            CreateStmtContext *cxt,
                            ColumnDef *column);
*************** static Oid    transformFkeyGetColType(Creat
*** 101,109 ****
  static void release_pstate_resources(ParseState *pstate);
  static FromExpr *makeFromExpr(List *fromlist, Node *quals);

- /* kluge to return extra info from transformCreateStmt() */
- static List *extras_before;
- static List *extras_after;


  /*
--- 106,111 ----
*************** parse_analyze(Node *parseTree, ParseStat
*** 121,137 ****
      List       *result = NIL;
      ParseState *pstate = make_parsestate(parentParseState);
      Query       *query;

!     extras_before = extras_after = NIL;
!
!     query = transformStmt(pstate, parseTree);
      release_pstate_resources(pstate);
!
      while (extras_before != NIL)
      {
!         result = lappend(result,
!                          transformStmt(pstate, lfirst(extras_before)));
!         release_pstate_resources(pstate);
          extras_before = lnext(extras_before);
      }

--- 123,138 ----
      List       *result = NIL;
      ParseState *pstate = make_parsestate(parentParseState);
      Query       *query;
+     /* Lists to return extra commands from transformation */
+     List *extras_before = NIL;
+     List *extras_after = NIL;

!     query = transformStmt(pstate, parseTree, &extras_before, &extras_after);
      release_pstate_resources(pstate);
!
      while (extras_before != NIL)
      {
!         result = nconc(result, parse_analyze(lfirst(extras_before), pstate));
          extras_before = lnext(extras_before);
      }

*************** parse_analyze(Node *parseTree, ParseStat
*** 139,147 ****

      while (extras_after != NIL)
      {
!         result = lappend(result,
!                          transformStmt(pstate, lfirst(extras_after)));
!         release_pstate_resources(pstate);
          extras_after = lnext(extras_after);
      }

--- 140,146 ----

      while (extras_after != NIL)
      {
!         result = nconc(result, parse_analyze(lfirst(extras_after), pstate));
          extras_after = lnext(extras_after);
      }

*************** release_pstate_resources(ParseState *pst
*** 164,170 ****
   *      transform a Parse tree into a Query tree.
   */
  static Query *
! transformStmt(ParseState *pstate, Node *parseTree)
  {
      Query       *result = NULL;

--- 163,170 ----
   *      transform a Parse tree into a Query tree.
   */
  static Query *
! transformStmt(ParseState *pstate, Node *parseTree,
!               List **extras_before,    List **extras_after)
  {
      Query       *result = NULL;

*************** transformStmt(ParseState *pstate, Node *
*** 174,180 ****
               * Non-optimizable statements
               */
          case T_CreateStmt:
!             result = transformCreateStmt(pstate, (CreateStmt *) parseTree);
              break;

          case T_IndexStmt:
--- 174,181 ----
               * Non-optimizable statements
               */
          case T_CreateStmt:
!             result = transformCreateStmt(pstate, (CreateStmt *) parseTree,
!                                     extras_before, extras_after);
              break;

          case T_IndexStmt:
*************** transformStmt(ParseState *pstate, Node *
*** 182,195 ****
              break;

          case T_RuleStmt:
!             result = transformRuleStmt(pstate, (RuleStmt *) parseTree);
              break;

          case T_ViewStmt:
              {
                  ViewStmt   *n = (ViewStmt *) parseTree;

!                 n->query = transformStmt(pstate, (Node *) n->query);

                  /*
                   * If a list of column names was given, run through and
--- 183,198 ----
              break;

          case T_RuleStmt:
!             result = transformRuleStmt(pstate, (RuleStmt *) parseTree,
!                                     extras_before, extras_after);
              break;

          case T_ViewStmt:
              {
                  ViewStmt   *n = (ViewStmt *) parseTree;

!                 n->query = transformStmt(pstate, (Node *) n->query,
!                                     extras_before, extras_after);

                  /*
                   * If a list of column names was given, run through and
*************** transformStmt(ParseState *pstate, Node *
*** 239,258 ****

                  result = makeNode(Query);
                  result->commandType = CMD_UTILITY;
!                 n->query = transformStmt(pstate, (Node *) n->query);
                  result->utilityStmt = (Node *) parseTree;
              }
              break;

          case T_AlterTableStmt:
!             result = transformAlterTableStmt(pstate, (AlterTableStmt *) parseTree);
              break;

              /*
               * Optimizable statements
               */
          case T_InsertStmt:
!             result = transformInsertStmt(pstate, (InsertStmt *) parseTree);
              break;

          case T_DeleteStmt:
--- 242,264 ----

                  result = makeNode(Query);
                  result->commandType = CMD_UTILITY;
!                 n->query = transformStmt(pstate, (Node *) n->query,
!                                 extras_before, extras_after);
                  result->utilityStmt = (Node *) parseTree;
              }
              break;

          case T_AlterTableStmt:
!             result = transformAlterTableStmt(pstate, (AlterTableStmt *) parseTree,
!                                         extras_before, extras_after);
              break;

              /*
               * Optimizable statements
               */
          case T_InsertStmt:
!             result = transformInsertStmt(pstate, (InsertStmt *) parseTree,
!                                         extras_before, extras_after);
              break;

          case T_DeleteStmt:
*************** transformDeleteStmt(ParseState *pstate,
*** 337,343 ****
   *      transform an Insert Statement
   */
  static Query *
! transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
  {
      Query       *qry = makeNode(Query);
      List       *sub_rtable;
--- 343,350 ----
   *      transform an Insert Statement
   */
  static Query *
! transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
!                     List **extras_before, List **extras_after)
  {
      Query       *qry = makeNode(Query);
      List       *sub_rtable;
*************** transformInsertStmt(ParseState *pstate,
*** 402,408 ****
          sub_pstate->p_rtable = sub_rtable;
          sub_pstate->p_namespace = sub_namespace;

!         selectQuery = transformStmt(sub_pstate, stmt->selectStmt);

          release_pstate_resources(sub_pstate);
          pfree(sub_pstate);
--- 409,420 ----
          sub_pstate->p_rtable = sub_rtable;
          sub_pstate->p_namespace = sub_namespace;

!         /*
!          * Note: we are not expecting that extras_before and extras_after
!          * are going to be used by the transformation of the SELECT statement.
!           */
!         selectQuery = transformStmt(sub_pstate, stmt->selectStmt,
!                                 extras_before, extras_after);

          release_pstate_resources(sub_pstate);
          pfree(sub_pstate);
*************** CreateIndexName(char *table_name, char *
*** 658,664 ****
   *      - thomas 1997-12-02
   */
  static Query *
! transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
  {
      CreateStmtContext cxt;
      Query       *q;
--- 670,677 ----
   *      - thomas 1997-12-02
   */
  static Query *
! transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
!                     List **extras_before, List **extras_after)
  {
      CreateStmtContext cxt;
      Query       *q;
*************** transformCreateStmt(ParseState *pstate,
*** 728,735 ****
      q->utilityStmt = (Node *) stmt;
      stmt->tableElts = cxt.columns;
      stmt->constraints = cxt.ckconstraints;
!     extras_before = cxt.blist;
!     extras_after = cxt.alist;

      return q;
  }
--- 741,748 ----
      q->utilityStmt = (Node *) stmt;
      stmt->tableElts = cxt.columns;
      stmt->constraints = cxt.ckconstraints;
!     *extras_before = nconc (*extras_before, cxt.blist);
!     *extras_after = nconc (cxt.alist, *extras_after);

      return q;
  }
*************** transformIndexStmt(ParseState *pstate, I
*** 1668,1674 ****
   *      trees which is transformed into a list of query trees.
   */
  static Query *
! transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
  {
      Query       *qry;
      RangeTblEntry *oldrte;
--- 1681,1688 ----
   *      trees which is transformed into a list of query trees.
   */
  static Query *
! transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
!                 List **extras_before, List **extras_after)
  {
      Query       *qry;
      RangeTblEntry *oldrte;
*************** transformRuleStmt(ParseState *pstate, Ru
*** 1797,1803 ****
              addRTEtoQuery(sub_pstate, newrte, false, true);

              /* Transform the rule action statement */
!             top_subqry = transformStmt(sub_pstate, action);

              /*
               * We cannot support utility-statement actions (eg NOTIFY)
--- 1811,1818 ----
              addRTEtoQuery(sub_pstate, newrte, false, true);

              /* Transform the rule action statement */
!             top_subqry = transformStmt(sub_pstate, action,
!                                 extras_before, extras_after);

              /*
               * We cannot support utility-statement actions (eg NOTIFY)
*************** transformUpdateStmt(ParseState *pstate,
*** 2494,2500 ****
   *    transform an Alter Table Statement
   */
  static Query *
! transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt)
  {
      CreateStmtContext cxt;
      Query       *qry;
--- 2509,2516 ----
   *    transform an Alter Table Statement
   */
  static Query *
! transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
!                         List **extras_before, List **extras_after)
  {
      CreateStmtContext cxt;
      Query       *qry;
*************** transformAlterTableStmt(ParseState *psta
*** 2534,2541 ****
              transformFKConstraints(pstate, &cxt);

              ((ColumnDef *) stmt->def)->constraints = cxt.ckconstraints;
!             extras_before = cxt.blist;
!             extras_after = cxt.alist;
              break;

          case 'C':
--- 2550,2557 ----
              transformFKConstraints(pstate, &cxt);

              ((ColumnDef *) stmt->def)->constraints = cxt.ckconstraints;
!             *extras_before = nconc(*extras_before, cxt.blist);
!             *extras_after = nconc(cxt.alist, *extras_after);
              break;

          case 'C':
*************** transformAlterTableStmt(ParseState *psta
*** 2571,2578 ****

              Assert(cxt.columns == NIL);
              stmt->def = (Node *) nconc(cxt.ckconstraints, cxt.fkconstraints);
!             extras_before = cxt.blist;
!             extras_after = cxt.alist;
              break;

          default:
--- 2587,2594 ----

              Assert(cxt.columns == NIL);
              stmt->def = (Node *) nconc(cxt.ckconstraints, cxt.fkconstraints);
!             *extras_before = nconc(*extras_before, cxt.blist);
!             *extras_after = nconc(cxt.alist, *extras_after);
              break;

          default:
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/tcop/postgres.c,v
retrieving revision 1.245
diff -c -p -r1.245 postgres.c
*** src/backend/tcop/postgres.c    2002/01/10 01:11:45    1.245
--- src/backend/tcop/postgres.c    2002/02/14 10:49:19
***************
*** 64,69 ****
--- 64,78 ----

  #include "pgstat.h"

+ static char *CreateCommandTag(Node *parsetree);
+
+ /* Uncomment the following line if you want ps_status
+  * to be set for each individual query that may result from
+  * a rewrite as oposed to just the main (original) one
+  */
+ /* #define FINE_GRAIN_PROFILE 1 */
+
+
  /* ----------------
   *        global variables
   * ----------------
*************** pg_exec_query_string(char *query_string,
*** 634,639 ****
--- 643,650 ----
      foreach(parsetree_item, parsetree_list)
      {
          Node       *parsetree = (Node *) lfirst(parsetree_item);
+         char       *commandTag = NULL;
+         bool        isQueryStmt = false;
          bool        isTransactionStmt;
          List       *querytree_list,
                     *querytree_item;
*************** pg_exec_query_string(char *query_string,
*** 675,680 ****
--- 686,693 ----
                   * command ended. -cim 6/1/90
                   */
                  char       *tag = "*ABORT STATE*";
+
+                 set_ps_display(tag);

                  elog(NOTICE, "current transaction is aborted, "
                       "queries ignored until end of transaction block");
*************** pg_exec_query_string(char *query_string,
*** 702,708 ****

          /*
           * OK to analyze and rewrite this query.
!          *
           * Switch to appropriate context for constructing querytrees (again,
           * these must outlive the execution context).
           */
--- 715,737 ----

          /*
           * OK to analyze and rewrite this query.
!          */
!
!         /*
!          * First we set the command-completion tag to the main query
!          * (as opposed to each of the others that may have
!          * been generated by analyze and rewrite)
!          */
!         commandTag = CreateCommandTag(parsetree);
!
! #ifndef FINE_GRAIN_PROFILE
!         /*
!          * Set ps_status to the main query tag
!          */
!         set_ps_display(commandTag);
! #endif
!
!         /*
           * Switch to appropriate context for constructing querytrees (again,
           * these must outlive the execution context).
           */
*************** pg_exec_query_string(char *query_string,
*** 746,752 ****
--- 775,796 ----
                  else if (DebugLvl > 1)
                      elog(DEBUG, "ProcessUtility");

+ #ifdef FINE_GRAIN_PROFILE
+                 set_ps_display(CreateUtilityTag(querytree->utilityStmt));
+ #endif
                  ProcessUtility(querytree->utilityStmt, dest);
+
+                 /*
+                  * FETCHes, as plannable queries, take care of completion
+                  * themselves but we still have to do it for MOVEs as they
+                  * do not send data (and thus, do not send a completion).
+                  */
+                 if (nodeTag(parsetree) == T_FetchStmt)
+                 {
+                     FetchStmt  *stmt = (FetchStmt *) parsetree;
+                     if (!(stmt->ismove))
+                         isQueryStmt = true;  /* Don't do it twice */
+                 }
              }
              else
              {
*************** pg_exec_query_string(char *query_string,
*** 754,759 ****
--- 798,805 ----
                   * process a plannable query.
                   */
                  Plan       *plan;
+
+                 isQueryStmt = true;

                  plan = pg_plan_query(querytree);

*************** pg_exec_query_string(char *query_string,
*** 818,823 ****
--- 864,876 ----

          }                        /* end loop over queries generated from a
                                   * parsetree */
+
+         /*
+          * tell fe/be or whatever that we're done if we only processed
+          * utilities (plannable queries take care of completion themselves)
+          */
+         if (!isQueryStmt)
+             EndCommand(commandTag, dest);
      }                            /* end loop over parsetrees */

      /*
*************** assertTest(int val)
*** 2037,2039 ****
--- 2090,2354 ----
  #endif

  #endif
+
+
+ /* ----------------------------------------------------------------
+  *        CreateCommandTag
+  *
+  *        utility to get a string representation of the
+  *        command operation.
+  * ----------------------------------------------------------------
+  */
+ static char *
+ CreateCommandTag(Node *parsetree)
+ {
+     char       *tag = NULL;
+
+     switch (nodeTag(parsetree))
+     {
+         case T_InsertStmt:
+             tag = "INSERT";
+             break;
+
+         case T_DeleteStmt:
+             tag = "DELETE";
+             break;
+
+         case T_UpdateStmt:
+             tag = "UPDATE";
+             break;
+
+         case T_SelectStmt:
+             tag = "SELECT";
+             break;
+
+         case T_TransactionStmt:
+             {
+                 TransactionStmt *stmt = (TransactionStmt *) parsetree;
+
+                 switch (stmt->command)
+                 {
+                     case BEGIN_TRANS:
+                         tag = "BEGIN";
+                         break;
+
+                     case COMMIT:
+                         tag = "COMMIT";
+                         break;
+
+                     case ROLLBACK:
+                         tag = "ROLLBACK";
+                         break;
+                 }
+             }
+             break;
+
+         case T_ClosePortalStmt:
+             tag = "CLOSE";
+             break;
+
+         case T_FetchStmt:
+             {
+                 FetchStmt  *stmt = (FetchStmt *) parsetree;
+                 tag = (stmt->ismove) ? "MOVE" : "FETCH";
+             }
+             break;
+
+         case T_CreateStmt:
+             tag = "CREATE";
+             break;
+
+         case T_DropStmt:
+             tag = "DROP";
+             break;
+
+         case T_TruncateStmt:
+             tag = "TRUNCATE";
+             break;
+
+         case T_CommentStmt:
+             tag = "COMMENT";
+             break;
+
+         case T_CopyStmt:
+             tag = "COPY";
+             break;
+
+         case T_RenameStmt:
+             tag = "ALTER";
+             break;
+
+         case T_AlterTableStmt:
+             tag = "ALTER";
+             break;
+
+         case T_GrantStmt:
+             {
+                 GrantStmt  *stmt = (GrantStmt *) parsetree;
+                 tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
+             }
+             break;
+
+         case T_DefineStmt:
+             tag = "CREATE";
+             break;
+
+         case T_ViewStmt:        /* CREATE VIEW */
+             tag = "CREATE";
+             break;
+
+         case T_ProcedureStmt:    /* CREATE FUNCTION */
+             tag = "CREATE";
+             break;
+
+         case T_IndexStmt:        /* CREATE INDEX */
+             tag = "CREATE";
+             break;
+
+         case T_RuleStmt:        /* CREATE RULE */
+             tag = "CREATE";
+             break;
+
+         case T_CreateSeqStmt:
+             tag = "CREATE";
+             break;
+
+         case T_RemoveAggrStmt:
+             tag = "DROP";
+             break;
+
+         case T_RemoveFuncStmt:
+             tag = "DROP";
+             break;
+
+         case T_RemoveOperStmt:
+             tag = "DROP";
+             break;
+
+         case T_VersionStmt:
+             tag = "CREATE VERSION";
+             break;
+
+         case T_CreatedbStmt:
+             tag = "CREATE DATABASE";
+             break;
+
+         case T_DropdbStmt:
+             tag = "DROP DATABASE";
+             break;
+
+         case T_NotifyStmt:
+             tag = "NOTIFY";
+             break;
+
+         case T_ListenStmt:
+             tag = "LISTEN";
+             break;
+
+         case T_UnlistenStmt:
+             tag = "UNLISTEN";
+             break;
+
+         case T_LoadStmt:
+             tag = "LOAD";
+             break;
+
+         case T_ClusterStmt:
+             tag = "CLUSTER";
+             break;
+
+         case T_VacuumStmt:
+             if (((VacuumStmt *) parsetree)->vacuum)
+                 tag = "VACUUM";
+             else
+                 tag = "ANALYZE";
+             break;
+
+         case T_ExplainStmt:
+             tag = "EXPLAIN";
+             break;
+
+ #ifdef NOT_USED
+         case T_RecipeStmt:
+             tag = "EXECUTE RECIPE";
+             break;
+ #endif
+
+         case T_VariableSetStmt:
+             tag = "SET VARIABLE";
+             break;
+
+         case T_VariableShowStmt:
+             tag = "SHOW VARIABLE";
+             break;
+
+         case T_VariableResetStmt:
+             tag = "RESET VARIABLE";
+             break;
+
+         case T_CreateTrigStmt:
+             tag = "CREATE";
+             break;
+
+         case T_DropTrigStmt:
+             tag = "DROP";
+             break;
+
+         case T_CreatePLangStmt:
+             tag = "CREATE";
+             break;
+
+         case T_DropPLangStmt:
+             tag = "DROP";
+             break;
+
+         case T_CreateUserStmt:
+             tag = "CREATE USER";
+             break;
+
+         case T_AlterUserStmt:
+             tag = "ALTER USER";
+             break;
+
+         case T_DropUserStmt:
+             tag = "DROP USER";
+             break;
+
+         case T_LockStmt:
+             tag = "LOCK TABLE";
+             break;
+
+         case T_ConstraintsSetStmt:
+             tag = "SET CONSTRAINTS";
+             break;
+
+         case T_CreateGroupStmt:
+             tag = "CREATE GROUP";
+             break;
+
+         case T_AlterGroupStmt:
+             tag = "ALTER GROUP";
+             break;
+
+         case T_DropGroupStmt:
+             tag = "DROP GROUP";
+             break;
+
+         case T_CheckPointStmt:
+             tag = "CHECKPOINT";
+             break;
+
+         case T_ReindexStmt:
+             tag = "REINDEX";
+             break;
+
+         default:
+             elog(DEBUG, "CreateUtilityTag: unknown utility operation type %d",
+                  nodeTag(parsetree));
+             tag = "???";
+             break;
+     }
+
+     return tag;
+ }
+
Index: src/backend/tcop/pquery.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/tcop/pquery.c,v
retrieving revision 1.46
diff -c -p -r1.46 pquery.c
*** src/backend/tcop/pquery.c    2001/10/25 05:49:43    1.46
--- src/backend/tcop/pquery.c    2002/02/14 10:49:19
*************** ProcessQuery(Query *parsetree,
*** 180,186 ****
      EState       *state;
      TupleDesc    attinfo;

!     set_ps_display(tag = CreateOperationTag(operation));

      /*
       * initialize portal/into relation status
--- 180,186 ----
      EState       *state;
      TupleDesc    attinfo;

!     tag = CreateOperationTag(operation);

      /*
       * initialize portal/into relation status
Index: src/backend/tcop/utility.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/tcop/utility.c,v
retrieving revision 1.125
diff -c -p -r1.125 utility.c
*** src/backend/tcop/utility.c    2002/02/07 00:27:30    1.125
--- src/backend/tcop/utility.c    2002/02/14 10:49:19
***************
*** 44,54 ****
  #include "rewrite/rewriteRemove.h"
  #include "tcop/utility.h"
  #include "utils/acl.h"
- #include "utils/ps_status.h"
  #include "utils/syscache.h"
  #include "utils/temprel.h"
  #include "access/xlog.h"

  /*
   * Error-checking support for DROP commands
   */
--- 44,54 ----
  #include "rewrite/rewriteRemove.h"
  #include "tcop/utility.h"
  #include "utils/acl.h"
  #include "utils/syscache.h"
  #include "utils/temprel.h"
  #include "access/xlog.h"

+
  /*
   * Error-checking support for DROP commands
   */
*************** CheckDropPermissions(char *name, char ri
*** 132,144 ****

  /* ----------------
   *        general utility function invoker
   * ----------------
   */
  void
  ProcessUtility(Node *parsetree,
                 CommandDest dest)
  {
-     char       *commandTag = NULL;
      char       *relname;
      char       *relationName;

--- 132,145 ----

  /* ----------------
   *        general utility function invoker
+  *
+  *    Returns the command-complete tag appropriate for the function
   * ----------------
   */
  void
  ProcessUtility(Node *parsetree,
                 CommandDest dest)
  {
      char       *relname;
      char       *relationName;

*************** ProcessUtility(Node *parsetree,
*** 155,171 ****
                  switch (stmt->command)
                  {
                      case BEGIN_TRANS:
-                         set_ps_display(commandTag = "BEGIN");
                          BeginTransactionBlock();
                          break;

                      case COMMIT:
-                         set_ps_display(commandTag = "COMMIT");
                          EndTransactionBlock();
                          break;

                      case ROLLBACK:
-                         set_ps_display(commandTag = "ROLLBACK");
                          UserAbortTransactionBlock();
                          break;
                  }
--- 156,169 ----
*************** ProcessUtility(Node *parsetree,
*** 180,187 ****
              {
                  ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;

-                 set_ps_display(commandTag = "CLOSE");
-
                  PerformPortalClose(stmt->portalname, dest);
              }
              break;
--- 178,183 ----
*************** ProcessUtility(Node *parsetree,
*** 193,200 ****
                  bool        forward;
                  int            count;

-                 set_ps_display(commandTag = (stmt->ismove) ? "MOVE" : "FETCH");
-
                  SetQuerySnapshot();

                  forward = (bool) (stmt->direction == FORWARD);
--- 189,194 ----
*************** ProcessUtility(Node *parsetree,
*** 204,210 ****
                   */

                  count = stmt->howMany;
!                 PerformPortalFetch(portalName, forward, count, commandTag,
                                     (stmt->ismove) ? None : dest);        /* /dev/null for MOVE */
              }
              break;
--- 198,204 ----
                   */

                  count = stmt->howMany;
!                 PerformPortalFetch(portalName, forward, count,(stmt->ismove) ? "MOVE" : "FETCH",
                                     (stmt->ismove) ? None : dest);        /* /dev/null for MOVE */
              }
              break;
*************** ProcessUtility(Node *parsetree,
*** 215,222 ****
               *
               */
          case T_CreateStmt:
-             set_ps_display(commandTag = "CREATE");
-
              DefineRelation((CreateStmt *) parsetree, RELKIND_RELATION);

              /*
--- 209,214 ----
*************** ProcessUtility(Node *parsetree,
*** 234,241 ****
                  List       *args = stmt->names;
                  List       *arg;

-                 set_ps_display(commandTag = "DROP");
-
                  foreach(arg, args)
                  {
                      relname = strVal(lfirst(arg));
--- 226,231 ----
*************** ProcessUtility(Node *parsetree,
*** 296,303 ****
              {
                  Relation    rel;

-                 set_ps_display(commandTag = "TRUNCATE");
-
                  relname = ((TruncateStmt *) parsetree)->relName;
                  if (!allowSystemTableMods && IsSystemRelationName(relname))
                      elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
--- 286,291 ----
*************** ProcessUtility(Node *parsetree,
*** 325,332 ****

                  statement = ((CommentStmt *) parsetree);

-                 set_ps_display(commandTag = "COMMENT");
-
                  CommentObject(statement->objtype, statement->objname,
                                statement->objproperty, statement->objlist,
                                statement->comment);
--- 313,318 ----
*************** ProcessUtility(Node *parsetree,
*** 337,344 ****
              {
                  CopyStmt   *stmt = (CopyStmt *) parsetree;

-                 set_ps_display(commandTag = "COPY");
-
                  if (stmt->direction != FROM)
                      SetQuerySnapshot();

--- 323,328 ----
*************** ProcessUtility(Node *parsetree,
*** 365,372 ****
              {
                  RenameStmt *stmt = (RenameStmt *) parsetree;

-                 set_ps_display(commandTag = "ALTER");
-
                  relname = stmt->relname;
                  if (!allowSystemTableMods && IsSystemRelationName(relname))
                      elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
--- 349,354 ----
*************** ProcessUtility(Node *parsetree,
*** 413,420 ****
              {
                  AlterTableStmt *stmt = (AlterTableStmt *) parsetree;

-                 set_ps_display(commandTag = "ALTER");
-
                  /*
                   * Some or all of these functions are recursive to cover
                   * inherited things, so permission checks are done there.
--- 395,400 ----
*************** ProcessUtility(Node *parsetree,
*** 475,483 ****
              {
                  GrantStmt  *stmt = (GrantStmt *) parsetree;

-                 commandTag = stmt->is_grant ? "GRANT" : "REVOKE";
-                 set_ps_display(commandTag);
-
                  ExecuteGrantStmt(stmt);
              }
              break;
--- 455,460 ----
*************** ProcessUtility(Node *parsetree,
*** 491,498 ****
              {
                  DefineStmt *stmt = (DefineStmt *) parsetree;

-                 set_ps_display(commandTag = "CREATE");
-
                  switch (stmt->defType)
                  {
                      case OPERATOR:
--- 468,473 ----
*************** ProcessUtility(Node *parsetree,
*** 514,528 ****
              {
                  ViewStmt   *stmt = (ViewStmt *) parsetree;

-                 set_ps_display(commandTag = "CREATE");
-
                  DefineView(stmt->viewname, stmt->query);        /* retrieve parsetree */
              }
              break;

          case T_ProcedureStmt:    /* CREATE FUNCTION */
-             set_ps_display(commandTag = "CREATE");
-
              CreateFunction((ProcedureStmt *) parsetree);
              break;

--- 489,499 ----
*************** ProcessUtility(Node *parsetree,
*** 530,537 ****
              {
                  IndexStmt  *stmt = (IndexStmt *) parsetree;

-                 set_ps_display(commandTag = "CREATE");
-
                  relname = stmt->relname;
                  if (!allowSystemTableMods && IsSystemRelationName(relname))
                      elog(ERROR, "CREATE INDEX: relation \"%s\" is a system catalog",
--- 501,506 ----
*************** ProcessUtility(Node *parsetree,
*** 559,573 ****
                  aclcheck_result = pg_aclcheck(relname, GetUserId(), ACL_RULE);
                  if (aclcheck_result != ACLCHECK_OK)
                      elog(ERROR, "%s: %s", relname, aclcheck_error_strings[aclcheck_result]);
-                 set_ps_display(commandTag = "CREATE");

                  DefineQueryRewrite(stmt);
              }
              break;

          case T_CreateSeqStmt:
-             set_ps_display(commandTag = "CREATE");
-
              DefineSequence((CreateSeqStmt *) parsetree);
              break;

--- 528,539 ----
*************** ProcessUtility(Node *parsetree,
*** 576,583 ****
                  RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
                  char       *typename = (char *) NULL;

-                 set_ps_display(commandTag = "DROP");
-
                  if (stmt->aggtype != NULL)
                      typename = TypeNameToInternalName((TypeName *) stmt->aggtype);

--- 542,547 ----
*************** ProcessUtility(Node *parsetree,
*** 589,596 ****
              {
                  RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;

-                 set_ps_display(commandTag = "DROP");
-
                  RemoveFunction(stmt->funcname, stmt->args);
              }
              break;
--- 553,558 ----
*************** ProcessUtility(Node *parsetree,
*** 603,610 ****
                  char       *typename1 = (char *) NULL;
                  char       *typename2 = (char *) NULL;

-                 set_ps_display(commandTag = "DROP");
-
                  if (typenode1 != NULL)
                      typename1 = TypeNameToInternalName(typenode1);
                  if (typenode2 != NULL)
--- 565,570 ----
*************** ProcessUtility(Node *parsetree,
*** 622,629 ****
              {
                  CreatedbStmt *stmt = (CreatedbStmt *) parsetree;

-                 set_ps_display(commandTag = "CREATE DATABASE");
-
                  createdb(stmt->dbname, stmt->dbpath,
                           stmt->dbtemplate, stmt->encoding);
              }
--- 582,587 ----
*************** ProcessUtility(Node *parsetree,
*** 633,640 ****
              {
                  DropdbStmt *stmt = (DropdbStmt *) parsetree;

-                 set_ps_display(commandTag = "DROP DATABASE");
-
                  dropdb(stmt->dbname);
              }
              break;
--- 591,596 ----
*************** ProcessUtility(Node *parsetree,
*** 644,651 ****
              {
                  NotifyStmt *stmt = (NotifyStmt *) parsetree;

-                 set_ps_display(commandTag = "NOTIFY");
-
                  Async_Notify(stmt->relname);
              }
              break;
--- 600,605 ----
*************** ProcessUtility(Node *parsetree,
*** 654,661 ****
              {
                  ListenStmt *stmt = (ListenStmt *) parsetree;

-                 set_ps_display(commandTag = "LISTEN");
-
                  Async_Listen(stmt->relname, MyProcPid);
              }
              break;
--- 608,613 ----
*************** ProcessUtility(Node *parsetree,
*** 664,671 ****
              {
                  UnlistenStmt *stmt = (UnlistenStmt *) parsetree;

-                 set_ps_display(commandTag = "UNLISTEN");
-
                  Async_Unlisten(stmt->relname, MyProcPid);
              }
              break;
--- 616,621 ----
*************** ProcessUtility(Node *parsetree,
*** 678,685 ****
              {
                  LoadStmt   *stmt = (LoadStmt *) parsetree;

-                 set_ps_display(commandTag = "LOAD");
-
                  closeAllVfds(); /* probably not necessary... */
                  load_file(stmt->filename);
              }
--- 628,633 ----
*************** ProcessUtility(Node *parsetree,
*** 689,696 ****
              {
                  ClusterStmt *stmt = (ClusterStmt *) parsetree;

-                 set_ps_display(commandTag = "CLUSTER");
-
                  relname = stmt->relname;
                  if (IsSystemRelationName(relname))
                      elog(ERROR, "CLUSTER: relation \"%s\" is a system catalog",
--- 637,642 ----
*************** ProcessUtility(Node *parsetree,
*** 703,714 ****
              break;

          case T_VacuumStmt:
-             if (((VacuumStmt *) parsetree)->vacuum)
-                 commandTag = "VACUUM";
-             else
-                 commandTag = "ANALYZE";
-             set_ps_display(commandTag);
-
              vacuum((VacuumStmt *) parsetree);
              break;

--- 649,654 ----
*************** ProcessUtility(Node *parsetree,
*** 716,723 ****
              {
                  ExplainStmt *stmt = (ExplainStmt *) parsetree;

-                 set_ps_display(commandTag = "EXPLAIN");
-
                  ExplainQuery(stmt->query, stmt->verbose, stmt->analyze, dest);
              }
              break;
--- 656,661 ----
*************** ProcessUtility(Node *parsetree,
*** 731,738 ****
              {
                  RecipeStmt *stmt = (RecipeStmt *) parsetree;

-                 set_ps_display(commandTag = "EXECUTE RECIPE");
-
                  beginRecipe(stmt);
              }
              break;
--- 669,674 ----
*************** ProcessUtility(Node *parsetree,
*** 746,752 ****
                  VariableSetStmt *n = (VariableSetStmt *) parsetree;

                  SetPGVariable(n->name, n->args);
-                 set_ps_display(commandTag = "SET VARIABLE");
              }
              break;

--- 682,687 ----
*************** ProcessUtility(Node *parsetree,
*** 755,761 ****
                  VariableShowStmt *n = (VariableShowStmt *) parsetree;

                  GetPGVariable(n->name);
-                 set_ps_display(commandTag = "SHOW VARIABLE");
              }
              break;

--- 690,695 ----
*************** ProcessUtility(Node *parsetree,
*** 764,770 ****
                  VariableResetStmt *n = (VariableResetStmt *) parsetree;

                  ResetPGVariable(n->name);
-                 set_ps_display(commandTag = "RESET VARIABLE");
              }
              break;

--- 698,703 ----
*************** ProcessUtility(Node *parsetree,
*** 772,785 ****
               * ******************************** TRIGGER statements *******************************
               */
          case T_CreateTrigStmt:
-             set_ps_display(commandTag = "CREATE");
-
              CreateTrigger((CreateTrigStmt *) parsetree);
              break;

          case T_DropTrigStmt:
-             set_ps_display(commandTag = "DROP");
-
              DropTrigger((DropTrigStmt *) parsetree);
              break;

--- 705,714 ----
*************** ProcessUtility(Node *parsetree,
*** 787,800 ****
               * ************* PROCEDURAL LANGUAGE statements *****************
               */
          case T_CreatePLangStmt:
-             set_ps_display(commandTag = "CREATE");
-
              CreateProceduralLanguage((CreatePLangStmt *) parsetree);
              break;

          case T_DropPLangStmt:
-             set_ps_display(commandTag = "DROP");
-
              DropProceduralLanguage((DropPLangStmt *) parsetree);
              break;

--- 716,725 ----
*************** ProcessUtility(Node *parsetree,
*** 803,859 ****
               *
               */
          case T_CreateUserStmt:
-             set_ps_display(commandTag = "CREATE USER");
-
              CreateUser((CreateUserStmt *) parsetree);
              break;

          case T_AlterUserStmt:
-             set_ps_display(commandTag = "ALTER USER");
-
              AlterUser((AlterUserStmt *) parsetree);
              break;

          case T_DropUserStmt:
-             set_ps_display(commandTag = "DROP USER");
-
              DropUser((DropUserStmt *) parsetree);
              break;

          case T_LockStmt:
-             set_ps_display(commandTag = "LOCK TABLE");
-
              LockTableCommand((LockStmt *) parsetree);
              break;

          case T_ConstraintsSetStmt:
-             set_ps_display(commandTag = "SET CONSTRAINTS");
-
              DeferredTriggerSetState((ConstraintsSetStmt *) parsetree);
              break;

          case T_CreateGroupStmt:
-             set_ps_display(commandTag = "CREATE GROUP");
-
              CreateGroup((CreateGroupStmt *) parsetree);
              break;

          case T_AlterGroupStmt:
-             set_ps_display(commandTag = "ALTER GROUP");
-
              AlterGroup((AlterGroupStmt *) parsetree, "ALTER GROUP");
              break;

          case T_DropGroupStmt:
-             set_ps_display(commandTag = "DROP GROUP");
-
              DropGroup((DropGroupStmt *) parsetree);
              break;

          case T_CheckPointStmt:
              {
-                 set_ps_display(commandTag = "CHECKPOINT");
-
                  if (!superuser())
                      elog(ERROR, "permission denied");
                  CreateCheckPoint(false);
--- 728,766 ----
*************** ProcessUtility(Node *parsetree,
*** 864,871 ****
              {
                  ReindexStmt *stmt = (ReindexStmt *) parsetree;

-                 set_ps_display(commandTag = "REINDEX");
-
                  switch (stmt->reindexType)
                  {
                      case INDEX:
--- 771,776 ----
*************** ProcessUtility(Node *parsetree,
*** 913,919 ****
      }

      /*
!      * tell fe/be or whatever that we're done.
       */
!     EndCommand(commandTag, dest);
  }
--- 818,826 ----
      }

      /*
!      * Note: the "end-of-command" tag is to be sent by the caller,
!      * if appropriate.
       */
!     return;
  }
+

Re: Fix issuing of multiple command completions per command

From
Bruce Momjian
Date:
Your patch has been added to the PostgreSQL unapplied patches list at:

    http://candle.pha.pa.us/cgi-bin/pgpatches

I will try to apply it within the next 48 hours.

---------------------------------------------------------------------------


Fernando Nasser wrote:
> (Please note that "[PATCHES] Allow arbitrary levels of
> analyze/rewriting"
> must be applied before this patch)
>
> This patch prevents the issue of multiple command completions
> when queries are rewritten into a sequence queries by analyze/rewrite.
> Currently the front ends are receiving many;  libpq just uses the last
> one received so that is why we see only one response in psql, for
> instance.
>
> It fixes a bug where the user receives a bogus completion message
> for queries that have been turned into a sequence of queries where
> the last one has a different completion tag.
>
> It also prevents command-completions to be generated by utility commands
> that are inside SQL-functions.
>
> It also fixes some misplaced calls to set ps_status.
>
> Furthermore, these changes made possible to reduce the number of
> times ps_status is set (a request from Tom Lane). This is done by
> default but it is still possible to set a compiler flag and have
> it generate more calls as it does today (one for each query produced
> by the rewriting of the original query).
>
>
> Here is basically what is done:
>
> The completion is sent by EndCommand().  This call is used OK by
> plannable queries and is necessary there for telling that the data
> sent to a backend has come to an end.  Utilities, however, don't
> send any data (except FETCH) and may be rewritten into a series of
> other utility queries.  The changes consolidate the calls to
> EndCommand() for utility queries and let the plannable ones
> handle it pairwise with BeginCommand() (which is not used by
> utility queries).
>
>
> ChangeLog:
>
>         * src/backend/commands/command.c (PerformPortalFetch): Send
>         command completion so that fe knows we are done with sending
> data.
>         * src/backend/commands/explain.c (ExplainOneQuery): Set
> ps_status
>         to EXPLAIN or SELECT (for the EXPLAIN ANALYZE case).
>         * src/backend/executor/functions.c (postquel_getnext): Do not
> send
>         command completion messages for utilities inside SQL-functions
>         (one will be sent for the command that invoked the function).
>         * src/backend/executor/spi.c (_SPI_execute, _SPI_execute_plan):
> Add
>         comments.
>         * src/backend/tcop/postgres.c (pg_exec_query_string):
>         Do not send a command completion message or set the ps_status
> for
>         each utility query that results for the rewriting of a command;
>         just send one for the original command.  Let plannable queries
> take
>         care of their own completion.
>         (CreateCommandTag): New function.  Create a command completion
> tag
>         based on the parse tree node (rather than on an already/analyzed
>         query node).
>         * src/backend/tcop/utility.c (ProcessUtility): Do
>         not meddle with ps_status or command completion in this
> function.
>         * src/backend/tcop/pquery.c (ProcessQuery): Let the caller set
>         ps_status.
>
>
>
>
>
> --
> Fernando Nasser
> Red Hat Canada Ltd.                     E-Mail:  fnasser@redhat.com
> 2323 Yonge Street, Suite #300
> Toronto, Ontario   M4P 2C9

> Index: src/backend/commands/command.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/commands/command.c,v
> retrieving revision 1.152
> diff -c -p -r1.152 command.c
> *** src/backend/commands/command.c    2002/01/03 23:19:30    1.152
> --- src/backend/commands/command.c    2002/02/14 10:49:18
> *************** PerformPortalFetch(char *name,
> *** 215,231 ****
>       }
>
>       /*
>        * Clean up and switch back to old context.
>        */
>       if (temp_desc)
>           pfree(queryDesc);
>
>       MemoryContextSwitchTo(oldcontext);
> -
> -     /*
> -      * Note: the "end-of-command" tag is returned by higher-level utility
> -      * code
> -      */
>   }
>
>   /* --------------------------------
> --- 215,231 ----
>       }
>
>       /*
> +      * tell fe/be or whatever that we're done sending the data
> +      */
> +     EndCommand(tag, queryDesc->dest);
> +
> +     /*
>        * Clean up and switch back to old context.
>        */
>       if (temp_desc)
>           pfree(queryDesc);
>
>       MemoryContextSwitchTo(oldcontext);
>   }
>
>   /* --------------------------------
> Index: src/backend/commands/explain.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/commands/explain.c,v
> retrieving revision 1.67
> diff -c -p -r1.67 explain.c
> *** src/backend/commands/explain.c    2001/10/25 05:49:25    1.67
> --- src/backend/commands/explain.c    2002/02/14 10:49:18
> ***************
> *** 19,24 ****
> --- 19,25 ----
>   #include "parser/parsetree.h"
>   #include "rewrite/rewriteHandler.h"
>   #include "tcop/pquery.h"
> + #include "utils/ps_status.h"
>   #include "utils/relcache.h"
>
>   typedef struct ExplainState
> *************** ExplainOneQuery(Query *query, bool verbo
> *** 113,118 ****
> --- 114,121 ----
>           struct timeval starttime;
>           struct timeval endtime;
>
> +         set_ps_display("SELECT");
> +
>           /*
>            * Set up the instrumentation for the top node. This will cascade
>            * during plan initialisation
> *************** ExplainOneQuery(Query *query, bool verbo
> *** 133,138 ****
> --- 136,143 ----
>           }
>           totaltime = (double) endtime.tv_sec +
>               (double) endtime.tv_usec / 1000000.0;
> +
> +         set_ps_display("EXPLAIN");
>       }
>
>       es = (ExplainState *) palloc(sizeof(ExplainState));
> Index: src/backend/executor/functions.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/executor/functions.c,v
> retrieving revision 1.47
> diff -c -p -r1.47 functions.c
> *** src/backend/executor/functions.c    2001/10/28 06:25:43    1.47
> --- src/backend/executor/functions.c    2002/02/14 10:49:18
> *************** postquel_getnext(execution_state *es)
> *** 276,281 ****
> --- 276,283 ----
>            * Process a utility command. (create, destroy...)    DZ - 30-8-1996
>            */
>           ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest);
> +         /* We should not call EndCommand() here as we are inside a function
> +          * call and command-complete reports should not be issued. */
>           if (!LAST_POSTQUEL_COMMAND(es))
>               CommandCounterIncrement();
>           return (TupleTableSlot *) NULL;
> Index: src/backend/executor/spi.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/executor/spi.c,v
> retrieving revision 1.64
> diff -c -p -r1.64 spi.c
> *** src/backend/executor/spi.c    2002/01/03 20:30:47    1.64
> --- src/backend/executor/spi.c    2002/02/14 10:49:18
> *************** _SPI_execute(char *src, int tcount, _SPI
> *** 1010,1015 ****
> --- 1010,1017 ----
>               if (plan == NULL)
>               {
>                   ProcessUtility(queryTree->utilityStmt, None);
> +                 /* We don't need to call EndCommand() as we don't want to
> +                  * send anything (dest = None) */
>                   if (!islastquery)
>                       CommandCounterIncrement();
>                   else
> *************** _SPI_execute_plan(_SPI_plan *plan, Datum
> *** 1084,1089 ****
> --- 1086,1093 ----
>           if (queryTree->commandType == CMD_UTILITY)
>           {
>               ProcessUtility(queryTree->utilityStmt, None);
> +             /* We don't need to call EndCommand() as we don't want to
> +              * send anything (dest = None) */
>               if (!islastquery)
>                   CommandCounterIncrement();
>               else
> Index: src/backend/parser/analyze.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/parser/analyze.c,v
> retrieving revision 1.213
> diff -c -p -r1.213 analyze.c
> *** src/backend/parser/analyze.c    2002/01/03 23:21:31    1.213
> --- src/backend/parser/analyze.c    2002/02/14 10:49:18
> *************** typedef struct
> *** 64,80 ****
>   } CreateStmtContext;
>
>
> ! static Query *transformStmt(ParseState *pstate, Node *stmt);
>   static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
> ! static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
>   static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
> ! static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt);
>   static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
>   static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
>   static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
>   static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
> ! static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
> ! static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt);
>   static void transformColumnDefinition(ParseState *pstate,
>                             CreateStmtContext *cxt,
>                             ColumnDef *column);
> --- 64,85 ----
>   } CreateStmtContext;
>
>
> ! static Query *transformStmt(ParseState *pstate, Node *stmt,
> !                         List **extras_before, List **extras_after);
>   static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
> ! static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
> !                         List **extras_before, List **extras_after);
>   static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
> ! static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt,
> !                         List **extras_before, List **extras_after);
>   static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
>   static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
>   static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
>   static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
> ! static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
> !                         List **extras_before, List **extras_after);
> ! static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
> !                         List **extras_before, List **extras_after);
>   static void transformColumnDefinition(ParseState *pstate,
>                             CreateStmtContext *cxt,
>                             ColumnDef *column);
> *************** static Oid    transformFkeyGetColType(Creat
> *** 101,109 ****
>   static void release_pstate_resources(ParseState *pstate);
>   static FromExpr *makeFromExpr(List *fromlist, Node *quals);
>
> - /* kluge to return extra info from transformCreateStmt() */
> - static List *extras_before;
> - static List *extras_after;
>
>
>   /*
> --- 106,111 ----
> *************** parse_analyze(Node *parseTree, ParseStat
> *** 121,137 ****
>       List       *result = NIL;
>       ParseState *pstate = make_parsestate(parentParseState);
>       Query       *query;
>
> !     extras_before = extras_after = NIL;
> !
> !     query = transformStmt(pstate, parseTree);
>       release_pstate_resources(pstate);
> !
>       while (extras_before != NIL)
>       {
> !         result = lappend(result,
> !                          transformStmt(pstate, lfirst(extras_before)));
> !         release_pstate_resources(pstate);
>           extras_before = lnext(extras_before);
>       }
>
> --- 123,138 ----
>       List       *result = NIL;
>       ParseState *pstate = make_parsestate(parentParseState);
>       Query       *query;
> +     /* Lists to return extra commands from transformation */
> +     List *extras_before = NIL;
> +     List *extras_after = NIL;
>
> !     query = transformStmt(pstate, parseTree, &extras_before, &extras_after);
>       release_pstate_resources(pstate);
> !
>       while (extras_before != NIL)
>       {
> !         result = nconc(result, parse_analyze(lfirst(extras_before), pstate));
>           extras_before = lnext(extras_before);
>       }
>
> *************** parse_analyze(Node *parseTree, ParseStat
> *** 139,147 ****
>
>       while (extras_after != NIL)
>       {
> !         result = lappend(result,
> !                          transformStmt(pstate, lfirst(extras_after)));
> !         release_pstate_resources(pstate);
>           extras_after = lnext(extras_after);
>       }
>
> --- 140,146 ----
>
>       while (extras_after != NIL)
>       {
> !         result = nconc(result, parse_analyze(lfirst(extras_after), pstate));
>           extras_after = lnext(extras_after);
>       }
>
> *************** release_pstate_resources(ParseState *pst
> *** 164,170 ****
>    *      transform a Parse tree into a Query tree.
>    */
>   static Query *
> ! transformStmt(ParseState *pstate, Node *parseTree)
>   {
>       Query       *result = NULL;
>
> --- 163,170 ----
>    *      transform a Parse tree into a Query tree.
>    */
>   static Query *
> ! transformStmt(ParseState *pstate, Node *parseTree,
> !               List **extras_before,    List **extras_after)
>   {
>       Query       *result = NULL;
>
> *************** transformStmt(ParseState *pstate, Node *
> *** 174,180 ****
>                * Non-optimizable statements
>                */
>           case T_CreateStmt:
> !             result = transformCreateStmt(pstate, (CreateStmt *) parseTree);
>               break;
>
>           case T_IndexStmt:
> --- 174,181 ----
>                * Non-optimizable statements
>                */
>           case T_CreateStmt:
> !             result = transformCreateStmt(pstate, (CreateStmt *) parseTree,
> !                                     extras_before, extras_after);
>               break;
>
>           case T_IndexStmt:
> *************** transformStmt(ParseState *pstate, Node *
> *** 182,195 ****
>               break;
>
>           case T_RuleStmt:
> !             result = transformRuleStmt(pstate, (RuleStmt *) parseTree);
>               break;
>
>           case T_ViewStmt:
>               {
>                   ViewStmt   *n = (ViewStmt *) parseTree;
>
> !                 n->query = transformStmt(pstate, (Node *) n->query);
>
>                   /*
>                    * If a list of column names was given, run through and
> --- 183,198 ----
>               break;
>
>           case T_RuleStmt:
> !             result = transformRuleStmt(pstate, (RuleStmt *) parseTree,
> !                                     extras_before, extras_after);
>               break;
>
>           case T_ViewStmt:
>               {
>                   ViewStmt   *n = (ViewStmt *) parseTree;
>
> !                 n->query = transformStmt(pstate, (Node *) n->query,
> !                                     extras_before, extras_after);
>
>                   /*
>                    * If a list of column names was given, run through and
> *************** transformStmt(ParseState *pstate, Node *
> *** 239,258 ****
>
>                   result = makeNode(Query);
>                   result->commandType = CMD_UTILITY;
> !                 n->query = transformStmt(pstate, (Node *) n->query);
>                   result->utilityStmt = (Node *) parseTree;
>               }
>               break;
>
>           case T_AlterTableStmt:
> !             result = transformAlterTableStmt(pstate, (AlterTableStmt *) parseTree);
>               break;
>
>               /*
>                * Optimizable statements
>                */
>           case T_InsertStmt:
> !             result = transformInsertStmt(pstate, (InsertStmt *) parseTree);
>               break;
>
>           case T_DeleteStmt:
> --- 242,264 ----
>
>                   result = makeNode(Query);
>                   result->commandType = CMD_UTILITY;
> !                 n->query = transformStmt(pstate, (Node *) n->query,
> !                                 extras_before, extras_after);
>                   result->utilityStmt = (Node *) parseTree;
>               }
>               break;
>
>           case T_AlterTableStmt:
> !             result = transformAlterTableStmt(pstate, (AlterTableStmt *) parseTree,
> !                                         extras_before, extras_after);
>               break;
>
>               /*
>                * Optimizable statements
>                */
>           case T_InsertStmt:
> !             result = transformInsertStmt(pstate, (InsertStmt *) parseTree,
> !                                         extras_before, extras_after);
>               break;
>
>           case T_DeleteStmt:
> *************** transformDeleteStmt(ParseState *pstate,
> *** 337,343 ****
>    *      transform an Insert Statement
>    */
>   static Query *
> ! transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
>   {
>       Query       *qry = makeNode(Query);
>       List       *sub_rtable;
> --- 343,350 ----
>    *      transform an Insert Statement
>    */
>   static Query *
> ! transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
> !                     List **extras_before, List **extras_after)
>   {
>       Query       *qry = makeNode(Query);
>       List       *sub_rtable;
> *************** transformInsertStmt(ParseState *pstate,
> *** 402,408 ****
>           sub_pstate->p_rtable = sub_rtable;
>           sub_pstate->p_namespace = sub_namespace;
>
> !         selectQuery = transformStmt(sub_pstate, stmt->selectStmt);
>
>           release_pstate_resources(sub_pstate);
>           pfree(sub_pstate);
> --- 409,420 ----
>           sub_pstate->p_rtable = sub_rtable;
>           sub_pstate->p_namespace = sub_namespace;
>
> !         /*
> !          * Note: we are not expecting that extras_before and extras_after
> !          * are going to be used by the transformation of the SELECT statement.
> !           */
> !         selectQuery = transformStmt(sub_pstate, stmt->selectStmt,
> !                                 extras_before, extras_after);
>
>           release_pstate_resources(sub_pstate);
>           pfree(sub_pstate);
> *************** CreateIndexName(char *table_name, char *
> *** 658,664 ****
>    *      - thomas 1997-12-02
>    */
>   static Query *
> ! transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
>   {
>       CreateStmtContext cxt;
>       Query       *q;
> --- 670,677 ----
>    *      - thomas 1997-12-02
>    */
>   static Query *
> ! transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
> !                     List **extras_before, List **extras_after)
>   {
>       CreateStmtContext cxt;
>       Query       *q;
> *************** transformCreateStmt(ParseState *pstate,
> *** 728,735 ****
>       q->utilityStmt = (Node *) stmt;
>       stmt->tableElts = cxt.columns;
>       stmt->constraints = cxt.ckconstraints;
> !     extras_before = cxt.blist;
> !     extras_after = cxt.alist;
>
>       return q;
>   }
> --- 741,748 ----
>       q->utilityStmt = (Node *) stmt;
>       stmt->tableElts = cxt.columns;
>       stmt->constraints = cxt.ckconstraints;
> !     *extras_before = nconc (*extras_before, cxt.blist);
> !     *extras_after = nconc (cxt.alist, *extras_after);
>
>       return q;
>   }
> *************** transformIndexStmt(ParseState *pstate, I
> *** 1668,1674 ****
>    *      trees which is transformed into a list of query trees.
>    */
>   static Query *
> ! transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
>   {
>       Query       *qry;
>       RangeTblEntry *oldrte;
> --- 1681,1688 ----
>    *      trees which is transformed into a list of query trees.
>    */
>   static Query *
> ! transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
> !                 List **extras_before, List **extras_after)
>   {
>       Query       *qry;
>       RangeTblEntry *oldrte;
> *************** transformRuleStmt(ParseState *pstate, Ru
> *** 1797,1803 ****
>               addRTEtoQuery(sub_pstate, newrte, false, true);
>
>               /* Transform the rule action statement */
> !             top_subqry = transformStmt(sub_pstate, action);
>
>               /*
>                * We cannot support utility-statement actions (eg NOTIFY)
> --- 1811,1818 ----
>               addRTEtoQuery(sub_pstate, newrte, false, true);
>
>               /* Transform the rule action statement */
> !             top_subqry = transformStmt(sub_pstate, action,
> !                                 extras_before, extras_after);
>
>               /*
>                * We cannot support utility-statement actions (eg NOTIFY)
> *************** transformUpdateStmt(ParseState *pstate,
> *** 2494,2500 ****
>    *    transform an Alter Table Statement
>    */
>   static Query *
> ! transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt)
>   {
>       CreateStmtContext cxt;
>       Query       *qry;
> --- 2509,2516 ----
>    *    transform an Alter Table Statement
>    */
>   static Query *
> ! transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
> !                         List **extras_before, List **extras_after)
>   {
>       CreateStmtContext cxt;
>       Query       *qry;
> *************** transformAlterTableStmt(ParseState *psta
> *** 2534,2541 ****
>               transformFKConstraints(pstate, &cxt);
>
>               ((ColumnDef *) stmt->def)->constraints = cxt.ckconstraints;
> !             extras_before = cxt.blist;
> !             extras_after = cxt.alist;
>               break;
>
>           case 'C':
> --- 2550,2557 ----
>               transformFKConstraints(pstate, &cxt);
>
>               ((ColumnDef *) stmt->def)->constraints = cxt.ckconstraints;
> !             *extras_before = nconc(*extras_before, cxt.blist);
> !             *extras_after = nconc(cxt.alist, *extras_after);
>               break;
>
>           case 'C':
> *************** transformAlterTableStmt(ParseState *psta
> *** 2571,2578 ****
>
>               Assert(cxt.columns == NIL);
>               stmt->def = (Node *) nconc(cxt.ckconstraints, cxt.fkconstraints);
> !             extras_before = cxt.blist;
> !             extras_after = cxt.alist;
>               break;
>
>           default:
> --- 2587,2594 ----
>
>               Assert(cxt.columns == NIL);
>               stmt->def = (Node *) nconc(cxt.ckconstraints, cxt.fkconstraints);
> !             *extras_before = nconc(*extras_before, cxt.blist);
> !             *extras_after = nconc(cxt.alist, *extras_after);
>               break;
>
>           default:
> Index: src/backend/tcop/postgres.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/tcop/postgres.c,v
> retrieving revision 1.245
> diff -c -p -r1.245 postgres.c
> *** src/backend/tcop/postgres.c    2002/01/10 01:11:45    1.245
> --- src/backend/tcop/postgres.c    2002/02/14 10:49:19
> ***************
> *** 64,69 ****
> --- 64,78 ----
>
>   #include "pgstat.h"
>
> + static char *CreateCommandTag(Node *parsetree);
> +
> + /* Uncomment the following line if you want ps_status
> +  * to be set for each individual query that may result from
> +  * a rewrite as oposed to just the main (original) one
> +  */
> + /* #define FINE_GRAIN_PROFILE 1 */
> +
> +
>   /* ----------------
>    *        global variables
>    * ----------------
> *************** pg_exec_query_string(char *query_string,
> *** 634,639 ****
> --- 643,650 ----
>       foreach(parsetree_item, parsetree_list)
>       {
>           Node       *parsetree = (Node *) lfirst(parsetree_item);
> +         char       *commandTag = NULL;
> +         bool        isQueryStmt = false;
>           bool        isTransactionStmt;
>           List       *querytree_list,
>                      *querytree_item;
> *************** pg_exec_query_string(char *query_string,
> *** 675,680 ****
> --- 686,693 ----
>                    * command ended. -cim 6/1/90
>                    */
>                   char       *tag = "*ABORT STATE*";
> +
> +                 set_ps_display(tag);
>
>                   elog(NOTICE, "current transaction is aborted, "
>                        "queries ignored until end of transaction block");
> *************** pg_exec_query_string(char *query_string,
> *** 702,708 ****
>
>           /*
>            * OK to analyze and rewrite this query.
> !          *
>            * Switch to appropriate context for constructing querytrees (again,
>            * these must outlive the execution context).
>            */
> --- 715,737 ----
>
>           /*
>            * OK to analyze and rewrite this query.
> !          */
> !
> !         /*
> !          * First we set the command-completion tag to the main query
> !          * (as opposed to each of the others that may have
> !          * been generated by analyze and rewrite)
> !          */
> !         commandTag = CreateCommandTag(parsetree);
> !
> ! #ifndef FINE_GRAIN_PROFILE
> !         /*
> !          * Set ps_status to the main query tag
> !          */
> !         set_ps_display(commandTag);
> ! #endif
> !
> !         /*
>            * Switch to appropriate context for constructing querytrees (again,
>            * these must outlive the execution context).
>            */
> *************** pg_exec_query_string(char *query_string,
> *** 746,752 ****
> --- 775,796 ----
>                   else if (DebugLvl > 1)
>                       elog(DEBUG, "ProcessUtility");
>
> + #ifdef FINE_GRAIN_PROFILE
> +                 set_ps_display(CreateUtilityTag(querytree->utilityStmt));
> + #endif
>                   ProcessUtility(querytree->utilityStmt, dest);
> +
> +                 /*
> +                  * FETCHes, as plannable queries, take care of completion
> +                  * themselves but we still have to do it for MOVEs as they
> +                  * do not send data (and thus, do not send a completion).
> +                  */
> +                 if (nodeTag(parsetree) == T_FetchStmt)
> +                 {
> +                     FetchStmt  *stmt = (FetchStmt *) parsetree;
> +                     if (!(stmt->ismove))
> +                         isQueryStmt = true;  /* Don't do it twice */
> +                 }
>               }
>               else
>               {
> *************** pg_exec_query_string(char *query_string,
> *** 754,759 ****
> --- 798,805 ----
>                    * process a plannable query.
>                    */
>                   Plan       *plan;
> +
> +                 isQueryStmt = true;
>
>                   plan = pg_plan_query(querytree);
>
> *************** pg_exec_query_string(char *query_string,
> *** 818,823 ****
> --- 864,876 ----
>
>           }                        /* end loop over queries generated from a
>                                    * parsetree */
> +
> +         /*
> +          * tell fe/be or whatever that we're done if we only processed
> +          * utilities (plannable queries take care of completion themselves)
> +          */
> +         if (!isQueryStmt)
> +             EndCommand(commandTag, dest);
>       }                            /* end loop over parsetrees */
>
>       /*
> *************** assertTest(int val)
> *** 2037,2039 ****
> --- 2090,2354 ----
>   #endif
>
>   #endif
> +
> +
> + /* ----------------------------------------------------------------
> +  *        CreateCommandTag
> +  *
> +  *        utility to get a string representation of the
> +  *        command operation.
> +  * ----------------------------------------------------------------
> +  */
> + static char *
> + CreateCommandTag(Node *parsetree)
> + {
> +     char       *tag = NULL;
> +
> +     switch (nodeTag(parsetree))
> +     {
> +         case T_InsertStmt:
> +             tag = "INSERT";
> +             break;
> +
> +         case T_DeleteStmt:
> +             tag = "DELETE";
> +             break;
> +
> +         case T_UpdateStmt:
> +             tag = "UPDATE";
> +             break;
> +
> +         case T_SelectStmt:
> +             tag = "SELECT";
> +             break;
> +
> +         case T_TransactionStmt:
> +             {
> +                 TransactionStmt *stmt = (TransactionStmt *) parsetree;
> +
> +                 switch (stmt->command)
> +                 {
> +                     case BEGIN_TRANS:
> +                         tag = "BEGIN";
> +                         break;
> +
> +                     case COMMIT:
> +                         tag = "COMMIT";
> +                         break;
> +
> +                     case ROLLBACK:
> +                         tag = "ROLLBACK";
> +                         break;
> +                 }
> +             }
> +             break;
> +
> +         case T_ClosePortalStmt:
> +             tag = "CLOSE";
> +             break;
> +
> +         case T_FetchStmt:
> +             {
> +                 FetchStmt  *stmt = (FetchStmt *) parsetree;
> +                 tag = (stmt->ismove) ? "MOVE" : "FETCH";
> +             }
> +             break;
> +
> +         case T_CreateStmt:
> +             tag = "CREATE";
> +             break;
> +
> +         case T_DropStmt:
> +             tag = "DROP";
> +             break;
> +
> +         case T_TruncateStmt:
> +             tag = "TRUNCATE";
> +             break;
> +
> +         case T_CommentStmt:
> +             tag = "COMMENT";
> +             break;
> +
> +         case T_CopyStmt:
> +             tag = "COPY";
> +             break;
> +
> +         case T_RenameStmt:
> +             tag = "ALTER";
> +             break;
> +
> +         case T_AlterTableStmt:
> +             tag = "ALTER";
> +             break;
> +
> +         case T_GrantStmt:
> +             {
> +                 GrantStmt  *stmt = (GrantStmt *) parsetree;
> +                 tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
> +             }
> +             break;
> +
> +         case T_DefineStmt:
> +             tag = "CREATE";
> +             break;
> +
> +         case T_ViewStmt:        /* CREATE VIEW */
> +             tag = "CREATE";
> +             break;
> +
> +         case T_ProcedureStmt:    /* CREATE FUNCTION */
> +             tag = "CREATE";
> +             break;
> +
> +         case T_IndexStmt:        /* CREATE INDEX */
> +             tag = "CREATE";
> +             break;
> +
> +         case T_RuleStmt:        /* CREATE RULE */
> +             tag = "CREATE";
> +             break;
> +
> +         case T_CreateSeqStmt:
> +             tag = "CREATE";
> +             break;
> +
> +         case T_RemoveAggrStmt:
> +             tag = "DROP";
> +             break;
> +
> +         case T_RemoveFuncStmt:
> +             tag = "DROP";
> +             break;
> +
> +         case T_RemoveOperStmt:
> +             tag = "DROP";
> +             break;
> +
> +         case T_VersionStmt:
> +             tag = "CREATE VERSION";
> +             break;
> +
> +         case T_CreatedbStmt:
> +             tag = "CREATE DATABASE";
> +             break;
> +
> +         case T_DropdbStmt:
> +             tag = "DROP DATABASE";
> +             break;
> +
> +         case T_NotifyStmt:
> +             tag = "NOTIFY";
> +             break;
> +
> +         case T_ListenStmt:
> +             tag = "LISTEN";
> +             break;
> +
> +         case T_UnlistenStmt:
> +             tag = "UNLISTEN";
> +             break;
> +
> +         case T_LoadStmt:
> +             tag = "LOAD";
> +             break;
> +
> +         case T_ClusterStmt:
> +             tag = "CLUSTER";
> +             break;
> +
> +         case T_VacuumStmt:
> +             if (((VacuumStmt *) parsetree)->vacuum)
> +                 tag = "VACUUM";
> +             else
> +                 tag = "ANALYZE";
> +             break;
> +
> +         case T_ExplainStmt:
> +             tag = "EXPLAIN";
> +             break;
> +
> + #ifdef NOT_USED
> +         case T_RecipeStmt:
> +             tag = "EXECUTE RECIPE";
> +             break;
> + #endif
> +
> +         case T_VariableSetStmt:
> +             tag = "SET VARIABLE";
> +             break;
> +
> +         case T_VariableShowStmt:
> +             tag = "SHOW VARIABLE";
> +             break;
> +
> +         case T_VariableResetStmt:
> +             tag = "RESET VARIABLE";
> +             break;
> +
> +         case T_CreateTrigStmt:
> +             tag = "CREATE";
> +             break;
> +
> +         case T_DropTrigStmt:
> +             tag = "DROP";
> +             break;
> +
> +         case T_CreatePLangStmt:
> +             tag = "CREATE";
> +             break;
> +
> +         case T_DropPLangStmt:
> +             tag = "DROP";
> +             break;
> +
> +         case T_CreateUserStmt:
> +             tag = "CREATE USER";
> +             break;
> +
> +         case T_AlterUserStmt:
> +             tag = "ALTER USER";
> +             break;
> +
> +         case T_DropUserStmt:
> +             tag = "DROP USER";
> +             break;
> +
> +         case T_LockStmt:
> +             tag = "LOCK TABLE";
> +             break;
> +
> +         case T_ConstraintsSetStmt:
> +             tag = "SET CONSTRAINTS";
> +             break;
> +
> +         case T_CreateGroupStmt:
> +             tag = "CREATE GROUP";
> +             break;
> +
> +         case T_AlterGroupStmt:
> +             tag = "ALTER GROUP";
> +             break;
> +
> +         case T_DropGroupStmt:
> +             tag = "DROP GROUP";
> +             break;
> +
> +         case T_CheckPointStmt:
> +             tag = "CHECKPOINT";
> +             break;
> +
> +         case T_ReindexStmt:
> +             tag = "REINDEX";
> +             break;
> +
> +         default:
> +             elog(DEBUG, "CreateUtilityTag: unknown utility operation type %d",
> +                  nodeTag(parsetree));
> +             tag = "???";
> +             break;
> +     }
> +
> +     return tag;
> + }
> +
> Index: src/backend/tcop/pquery.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/tcop/pquery.c,v
> retrieving revision 1.46
> diff -c -p -r1.46 pquery.c
> *** src/backend/tcop/pquery.c    2001/10/25 05:49:43    1.46
> --- src/backend/tcop/pquery.c    2002/02/14 10:49:19
> *************** ProcessQuery(Query *parsetree,
> *** 180,186 ****
>       EState       *state;
>       TupleDesc    attinfo;
>
> !     set_ps_display(tag = CreateOperationTag(operation));
>
>       /*
>        * initialize portal/into relation status
> --- 180,186 ----
>       EState       *state;
>       TupleDesc    attinfo;
>
> !     tag = CreateOperationTag(operation);
>
>       /*
>        * initialize portal/into relation status
> Index: src/backend/tcop/utility.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/tcop/utility.c,v
> retrieving revision 1.125
> diff -c -p -r1.125 utility.c
> *** src/backend/tcop/utility.c    2002/02/07 00:27:30    1.125
> --- src/backend/tcop/utility.c    2002/02/14 10:49:19
> ***************
> *** 44,54 ****
>   #include "rewrite/rewriteRemove.h"
>   #include "tcop/utility.h"
>   #include "utils/acl.h"
> - #include "utils/ps_status.h"
>   #include "utils/syscache.h"
>   #include "utils/temprel.h"
>   #include "access/xlog.h"
>
>   /*
>    * Error-checking support for DROP commands
>    */
> --- 44,54 ----
>   #include "rewrite/rewriteRemove.h"
>   #include "tcop/utility.h"
>   #include "utils/acl.h"
>   #include "utils/syscache.h"
>   #include "utils/temprel.h"
>   #include "access/xlog.h"
>
> +
>   /*
>    * Error-checking support for DROP commands
>    */
> *************** CheckDropPermissions(char *name, char ri
> *** 132,144 ****
>
>   /* ----------------
>    *        general utility function invoker
>    * ----------------
>    */
>   void
>   ProcessUtility(Node *parsetree,
>                  CommandDest dest)
>   {
> -     char       *commandTag = NULL;
>       char       *relname;
>       char       *relationName;
>
> --- 132,145 ----
>
>   /* ----------------
>    *        general utility function invoker
> +  *
> +  *    Returns the command-complete tag appropriate for the function
>    * ----------------
>    */
>   void
>   ProcessUtility(Node *parsetree,
>                  CommandDest dest)
>   {
>       char       *relname;
>       char       *relationName;
>
> *************** ProcessUtility(Node *parsetree,
> *** 155,171 ****
>                   switch (stmt->command)
>                   {
>                       case BEGIN_TRANS:
> -                         set_ps_display(commandTag = "BEGIN");
>                           BeginTransactionBlock();
>                           break;
>
>                       case COMMIT:
> -                         set_ps_display(commandTag = "COMMIT");
>                           EndTransactionBlock();
>                           break;
>
>                       case ROLLBACK:
> -                         set_ps_display(commandTag = "ROLLBACK");
>                           UserAbortTransactionBlock();
>                           break;
>                   }
> --- 156,169 ----
> *************** ProcessUtility(Node *parsetree,
> *** 180,187 ****
>               {
>                   ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "CLOSE");
> -
>                   PerformPortalClose(stmt->portalname, dest);
>               }
>               break;
> --- 178,183 ----
> *************** ProcessUtility(Node *parsetree,
> *** 193,200 ****
>                   bool        forward;
>                   int            count;
>
> -                 set_ps_display(commandTag = (stmt->ismove) ? "MOVE" : "FETCH");
> -
>                   SetQuerySnapshot();
>
>                   forward = (bool) (stmt->direction == FORWARD);
> --- 189,194 ----
> *************** ProcessUtility(Node *parsetree,
> *** 204,210 ****
>                    */
>
>                   count = stmt->howMany;
> !                 PerformPortalFetch(portalName, forward, count, commandTag,
>                                      (stmt->ismove) ? None : dest);        /* /dev/null for MOVE */
>               }
>               break;
> --- 198,204 ----
>                    */
>
>                   count = stmt->howMany;
> !                 PerformPortalFetch(portalName, forward, count,(stmt->ismove) ? "MOVE" : "FETCH",
>                                      (stmt->ismove) ? None : dest);        /* /dev/null for MOVE */
>               }
>               break;
> *************** ProcessUtility(Node *parsetree,
> *** 215,222 ****
>                *
>                */
>           case T_CreateStmt:
> -             set_ps_display(commandTag = "CREATE");
> -
>               DefineRelation((CreateStmt *) parsetree, RELKIND_RELATION);
>
>               /*
> --- 209,214 ----
> *************** ProcessUtility(Node *parsetree,
> *** 234,241 ****
>                   List       *args = stmt->names;
>                   List       *arg;
>
> -                 set_ps_display(commandTag = "DROP");
> -
>                   foreach(arg, args)
>                   {
>                       relname = strVal(lfirst(arg));
> --- 226,231 ----
> *************** ProcessUtility(Node *parsetree,
> *** 296,303 ****
>               {
>                   Relation    rel;
>
> -                 set_ps_display(commandTag = "TRUNCATE");
> -
>                   relname = ((TruncateStmt *) parsetree)->relName;
>                   if (!allowSystemTableMods && IsSystemRelationName(relname))
>                       elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
> --- 286,291 ----
> *************** ProcessUtility(Node *parsetree,
> *** 325,332 ****
>
>                   statement = ((CommentStmt *) parsetree);
>
> -                 set_ps_display(commandTag = "COMMENT");
> -
>                   CommentObject(statement->objtype, statement->objname,
>                                 statement->objproperty, statement->objlist,
>                                 statement->comment);
> --- 313,318 ----
> *************** ProcessUtility(Node *parsetree,
> *** 337,344 ****
>               {
>                   CopyStmt   *stmt = (CopyStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "COPY");
> -
>                   if (stmt->direction != FROM)
>                       SetQuerySnapshot();
>
> --- 323,328 ----
> *************** ProcessUtility(Node *parsetree,
> *** 365,372 ****
>               {
>                   RenameStmt *stmt = (RenameStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "ALTER");
> -
>                   relname = stmt->relname;
>                   if (!allowSystemTableMods && IsSystemRelationName(relname))
>                       elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
> --- 349,354 ----
> *************** ProcessUtility(Node *parsetree,
> *** 413,420 ****
>               {
>                   AlterTableStmt *stmt = (AlterTableStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "ALTER");
> -
>                   /*
>                    * Some or all of these functions are recursive to cover
>                    * inherited things, so permission checks are done there.
> --- 395,400 ----
> *************** ProcessUtility(Node *parsetree,
> *** 475,483 ****
>               {
>                   GrantStmt  *stmt = (GrantStmt *) parsetree;
>
> -                 commandTag = stmt->is_grant ? "GRANT" : "REVOKE";
> -                 set_ps_display(commandTag);
> -
>                   ExecuteGrantStmt(stmt);
>               }
>               break;
> --- 455,460 ----
> *************** ProcessUtility(Node *parsetree,
> *** 491,498 ****
>               {
>                   DefineStmt *stmt = (DefineStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "CREATE");
> -
>                   switch (stmt->defType)
>                   {
>                       case OPERATOR:
> --- 468,473 ----
> *************** ProcessUtility(Node *parsetree,
> *** 514,528 ****
>               {
>                   ViewStmt   *stmt = (ViewStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "CREATE");
> -
>                   DefineView(stmt->viewname, stmt->query);        /* retrieve parsetree */
>               }
>               break;
>
>           case T_ProcedureStmt:    /* CREATE FUNCTION */
> -             set_ps_display(commandTag = "CREATE");
> -
>               CreateFunction((ProcedureStmt *) parsetree);
>               break;
>
> --- 489,499 ----
> *************** ProcessUtility(Node *parsetree,
> *** 530,537 ****
>               {
>                   IndexStmt  *stmt = (IndexStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "CREATE");
> -
>                   relname = stmt->relname;
>                   if (!allowSystemTableMods && IsSystemRelationName(relname))
>                       elog(ERROR, "CREATE INDEX: relation \"%s\" is a system catalog",
> --- 501,506 ----
> *************** ProcessUtility(Node *parsetree,
> *** 559,573 ****
>                   aclcheck_result = pg_aclcheck(relname, GetUserId(), ACL_RULE);
>                   if (aclcheck_result != ACLCHECK_OK)
>                       elog(ERROR, "%s: %s", relname, aclcheck_error_strings[aclcheck_result]);
> -                 set_ps_display(commandTag = "CREATE");
>
>                   DefineQueryRewrite(stmt);
>               }
>               break;
>
>           case T_CreateSeqStmt:
> -             set_ps_display(commandTag = "CREATE");
> -
>               DefineSequence((CreateSeqStmt *) parsetree);
>               break;
>
> --- 528,539 ----
> *************** ProcessUtility(Node *parsetree,
> *** 576,583 ****
>                   RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
>                   char       *typename = (char *) NULL;
>
> -                 set_ps_display(commandTag = "DROP");
> -
>                   if (stmt->aggtype != NULL)
>                       typename = TypeNameToInternalName((TypeName *) stmt->aggtype);
>
> --- 542,547 ----
> *************** ProcessUtility(Node *parsetree,
> *** 589,596 ****
>               {
>                   RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "DROP");
> -
>                   RemoveFunction(stmt->funcname, stmt->args);
>               }
>               break;
> --- 553,558 ----
> *************** ProcessUtility(Node *parsetree,
> *** 603,610 ****
>                   char       *typename1 = (char *) NULL;
>                   char       *typename2 = (char *) NULL;
>
> -                 set_ps_display(commandTag = "DROP");
> -
>                   if (typenode1 != NULL)
>                       typename1 = TypeNameToInternalName(typenode1);
>                   if (typenode2 != NULL)
> --- 565,570 ----
> *************** ProcessUtility(Node *parsetree,
> *** 622,629 ****
>               {
>                   CreatedbStmt *stmt = (CreatedbStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "CREATE DATABASE");
> -
>                   createdb(stmt->dbname, stmt->dbpath,
>                            stmt->dbtemplate, stmt->encoding);
>               }
> --- 582,587 ----
> *************** ProcessUtility(Node *parsetree,
> *** 633,640 ****
>               {
>                   DropdbStmt *stmt = (DropdbStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "DROP DATABASE");
> -
>                   dropdb(stmt->dbname);
>               }
>               break;
> --- 591,596 ----
> *************** ProcessUtility(Node *parsetree,
> *** 644,651 ****
>               {
>                   NotifyStmt *stmt = (NotifyStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "NOTIFY");
> -
>                   Async_Notify(stmt->relname);
>               }
>               break;
> --- 600,605 ----
> *************** ProcessUtility(Node *parsetree,
> *** 654,661 ****
>               {
>                   ListenStmt *stmt = (ListenStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "LISTEN");
> -
>                   Async_Listen(stmt->relname, MyProcPid);
>               }
>               break;
> --- 608,613 ----
> *************** ProcessUtility(Node *parsetree,
> *** 664,671 ****
>               {
>                   UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "UNLISTEN");
> -
>                   Async_Unlisten(stmt->relname, MyProcPid);
>               }
>               break;
> --- 616,621 ----
> *************** ProcessUtility(Node *parsetree,
> *** 678,685 ****
>               {
>                   LoadStmt   *stmt = (LoadStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "LOAD");
> -
>                   closeAllVfds(); /* probably not necessary... */
>                   load_file(stmt->filename);
>               }
> --- 628,633 ----
> *************** ProcessUtility(Node *parsetree,
> *** 689,696 ****
>               {
>                   ClusterStmt *stmt = (ClusterStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "CLUSTER");
> -
>                   relname = stmt->relname;
>                   if (IsSystemRelationName(relname))
>                       elog(ERROR, "CLUSTER: relation \"%s\" is a system catalog",
> --- 637,642 ----
> *************** ProcessUtility(Node *parsetree,
> *** 703,714 ****
>               break;
>
>           case T_VacuumStmt:
> -             if (((VacuumStmt *) parsetree)->vacuum)
> -                 commandTag = "VACUUM";
> -             else
> -                 commandTag = "ANALYZE";
> -             set_ps_display(commandTag);
> -
>               vacuum((VacuumStmt *) parsetree);
>               break;
>
> --- 649,654 ----
> *************** ProcessUtility(Node *parsetree,
> *** 716,723 ****
>               {
>                   ExplainStmt *stmt = (ExplainStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "EXPLAIN");
> -
>                   ExplainQuery(stmt->query, stmt->verbose, stmt->analyze, dest);
>               }
>               break;
> --- 656,661 ----
> *************** ProcessUtility(Node *parsetree,
> *** 731,738 ****
>               {
>                   RecipeStmt *stmt = (RecipeStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "EXECUTE RECIPE");
> -
>                   beginRecipe(stmt);
>               }
>               break;
> --- 669,674 ----
> *************** ProcessUtility(Node *parsetree,
> *** 746,752 ****
>                   VariableSetStmt *n = (VariableSetStmt *) parsetree;
>
>                   SetPGVariable(n->name, n->args);
> -                 set_ps_display(commandTag = "SET VARIABLE");
>               }
>               break;
>
> --- 682,687 ----
> *************** ProcessUtility(Node *parsetree,
> *** 755,761 ****
>                   VariableShowStmt *n = (VariableShowStmt *) parsetree;
>
>                   GetPGVariable(n->name);
> -                 set_ps_display(commandTag = "SHOW VARIABLE");
>               }
>               break;
>
> --- 690,695 ----
> *************** ProcessUtility(Node *parsetree,
> *** 764,770 ****
>                   VariableResetStmt *n = (VariableResetStmt *) parsetree;
>
>                   ResetPGVariable(n->name);
> -                 set_ps_display(commandTag = "RESET VARIABLE");
>               }
>               break;
>
> --- 698,703 ----
> *************** ProcessUtility(Node *parsetree,
> *** 772,785 ****
>                * ******************************** TRIGGER statements *******************************
>                */
>           case T_CreateTrigStmt:
> -             set_ps_display(commandTag = "CREATE");
> -
>               CreateTrigger((CreateTrigStmt *) parsetree);
>               break;
>
>           case T_DropTrigStmt:
> -             set_ps_display(commandTag = "DROP");
> -
>               DropTrigger((DropTrigStmt *) parsetree);
>               break;
>
> --- 705,714 ----
> *************** ProcessUtility(Node *parsetree,
> *** 787,800 ****
>                * ************* PROCEDURAL LANGUAGE statements *****************
>                */
>           case T_CreatePLangStmt:
> -             set_ps_display(commandTag = "CREATE");
> -
>               CreateProceduralLanguage((CreatePLangStmt *) parsetree);
>               break;
>
>           case T_DropPLangStmt:
> -             set_ps_display(commandTag = "DROP");
> -
>               DropProceduralLanguage((DropPLangStmt *) parsetree);
>               break;
>
> --- 716,725 ----
> *************** ProcessUtility(Node *parsetree,
> *** 803,859 ****
>                *
>                */
>           case T_CreateUserStmt:
> -             set_ps_display(commandTag = "CREATE USER");
> -
>               CreateUser((CreateUserStmt *) parsetree);
>               break;
>
>           case T_AlterUserStmt:
> -             set_ps_display(commandTag = "ALTER USER");
> -
>               AlterUser((AlterUserStmt *) parsetree);
>               break;
>
>           case T_DropUserStmt:
> -             set_ps_display(commandTag = "DROP USER");
> -
>               DropUser((DropUserStmt *) parsetree);
>               break;
>
>           case T_LockStmt:
> -             set_ps_display(commandTag = "LOCK TABLE");
> -
>               LockTableCommand((LockStmt *) parsetree);
>               break;
>
>           case T_ConstraintsSetStmt:
> -             set_ps_display(commandTag = "SET CONSTRAINTS");
> -
>               DeferredTriggerSetState((ConstraintsSetStmt *) parsetree);
>               break;
>
>           case T_CreateGroupStmt:
> -             set_ps_display(commandTag = "CREATE GROUP");
> -
>               CreateGroup((CreateGroupStmt *) parsetree);
>               break;
>
>           case T_AlterGroupStmt:
> -             set_ps_display(commandTag = "ALTER GROUP");
> -
>               AlterGroup((AlterGroupStmt *) parsetree, "ALTER GROUP");
>               break;
>
>           case T_DropGroupStmt:
> -             set_ps_display(commandTag = "DROP GROUP");
> -
>               DropGroup((DropGroupStmt *) parsetree);
>               break;
>
>           case T_CheckPointStmt:
>               {
> -                 set_ps_display(commandTag = "CHECKPOINT");
> -
>                   if (!superuser())
>                       elog(ERROR, "permission denied");
>                   CreateCheckPoint(false);
> --- 728,766 ----
> *************** ProcessUtility(Node *parsetree,
> *** 864,871 ****
>               {
>                   ReindexStmt *stmt = (ReindexStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "REINDEX");
> -
>                   switch (stmt->reindexType)
>                   {
>                       case INDEX:
> --- 771,776 ----
> *************** ProcessUtility(Node *parsetree,
> *** 913,919 ****
>       }
>
>       /*
> !      * tell fe/be or whatever that we're done.
>        */
> !     EndCommand(commandTag, dest);
>   }
> --- 818,826 ----
>       }
>
>       /*
> !      * Note: the "end-of-command" tag is to be sent by the caller,
> !      * if appropriate.
>        */
> !     return;
>   }
> +

>
> ---------------------------(end of broadcast)---------------------------
> TIP 5: Have you checked our extensive FAQ?
>
> http://www.postgresql.org/users-lounge/docs/faq.html

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026

Re: Fix issuing of multiple command completions per command

From
Tom Lane
Date:
Fernando Nasser <fnasser@redhat.com> writes:
> (Please note that "[PATCHES] Allow arbitrary levels of
> analyze/rewriting" must be applied before this patch)

Actually, it looks like you accidentally included that patch as part of
this one.  Since Bruce already applied that patch, this one will not
apply cleanly.


While this looks good as far as it goes, I don't think it
resolves the problems exposed by Coax's recent complaint:
http://archives.postgresql.org/pgsql-bugs/2002-02/msg00155.php

There are two remaining bogosities:

1. We need to get rid of the static variable CommandInfo that's
manipulated by UpdateCommandInfo; static state is certainly going
to be a bad idea in this context.

2. EndCommand should *not* be issued until we have completed all the
actions of the (rewritten) query.  Moving EndCommand into
PerformPortalFetch, as you did here, is exactly the wrong direction.
What we need is for EndCommand to be executed from pg_exec_query_string.
That means that the tag auxiliary info (currently set up by
UpdateCommandInfo) has to be passed out from ProcessQuery to
pg_exec_query_string, which is the only place that can know when to send
the command completion notice back to the client.

As the comments in dest.h and dest.c point out, BeginCommand/EndCommand
are fairly confused in purpose and usage right now.  Maybe rather than
patching, we ought to decide what functionality we really need and do a
major reconstruction job.

One rather nasty issue that arises here is that I don't think
pg_exec_query_string has enough knowledge to understand which of the
queries produced by query rewriting corresponds to the "original" query.
This is critical in order to figure out which command's auxiliary info
is the right stuff to pass back to the client.  I suspect we will need
to expand QueryRewrite's API to identify the "main" query in the
returned query list.

Seems like we've opened a real can of worms here...

            regards, tom lane

Re: Fix issuing of multiple command completions per command

From
Bruce Momjian
Date:
This email has been reported to have problems.  Please modify and
resubmit.  Thanks.

---------------------------------------------------------------------------

Fernando Nasser wrote:
> (Please note that "[PATCHES] Allow arbitrary levels of
> analyze/rewriting"
> must be applied before this patch)
>
> This patch prevents the issue of multiple command completions
> when queries are rewritten into a sequence queries by analyze/rewrite.
> Currently the front ends are receiving many;  libpq just uses the last
> one received so that is why we see only one response in psql, for
> instance.
>
> It fixes a bug where the user receives a bogus completion message
> for queries that have been turned into a sequence of queries where
> the last one has a different completion tag.
>
> It also prevents command-completions to be generated by utility commands
> that are inside SQL-functions.
>
> It also fixes some misplaced calls to set ps_status.
>
> Furthermore, these changes made possible to reduce the number of
> times ps_status is set (a request from Tom Lane). This is done by
> default but it is still possible to set a compiler flag and have
> it generate more calls as it does today (one for each query produced
> by the rewriting of the original query).
>
>
> Here is basically what is done:
>
> The completion is sent by EndCommand().  This call is used OK by
> plannable queries and is necessary there for telling that the data
> sent to a backend has come to an end.  Utilities, however, don't
> send any data (except FETCH) and may be rewritten into a series of
> other utility queries.  The changes consolidate the calls to
> EndCommand() for utility queries and let the plannable ones
> handle it pairwise with BeginCommand() (which is not used by
> utility queries).
>
>
> ChangeLog:
>
>         * src/backend/commands/command.c (PerformPortalFetch): Send
>         command completion so that fe knows we are done with sending
> data.
>         * src/backend/commands/explain.c (ExplainOneQuery): Set
> ps_status
>         to EXPLAIN or SELECT (for the EXPLAIN ANALYZE case).
>         * src/backend/executor/functions.c (postquel_getnext): Do not
> send
>         command completion messages for utilities inside SQL-functions
>         (one will be sent for the command that invoked the function).
>         * src/backend/executor/spi.c (_SPI_execute, _SPI_execute_plan):
> Add
>         comments.
>         * src/backend/tcop/postgres.c (pg_exec_query_string):
>         Do not send a command completion message or set the ps_status
> for
>         each utility query that results for the rewriting of a command;
>         just send one for the original command.  Let plannable queries
> take
>         care of their own completion.
>         (CreateCommandTag): New function.  Create a command completion
> tag
>         based on the parse tree node (rather than on an already/analyzed
>         query node).
>         * src/backend/tcop/utility.c (ProcessUtility): Do
>         not meddle with ps_status or command completion in this
> function.
>         * src/backend/tcop/pquery.c (ProcessQuery): Let the caller set
>         ps_status.
>
>
>
>
>
> --
> Fernando Nasser
> Red Hat Canada Ltd.                     E-Mail:  fnasser@redhat.com
> 2323 Yonge Street, Suite #300
> Toronto, Ontario   M4P 2C9

> Index: src/backend/commands/command.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/commands/command.c,v
> retrieving revision 1.152
> diff -c -p -r1.152 command.c
> *** src/backend/commands/command.c    2002/01/03 23:19:30    1.152
> --- src/backend/commands/command.c    2002/02/14 10:49:18
> *************** PerformPortalFetch(char *name,
> *** 215,231 ****
>       }
>
>       /*
>        * Clean up and switch back to old context.
>        */
>       if (temp_desc)
>           pfree(queryDesc);
>
>       MemoryContextSwitchTo(oldcontext);
> -
> -     /*
> -      * Note: the "end-of-command" tag is returned by higher-level utility
> -      * code
> -      */
>   }
>
>   /* --------------------------------
> --- 215,231 ----
>       }
>
>       /*
> +      * tell fe/be or whatever that we're done sending the data
> +      */
> +     EndCommand(tag, queryDesc->dest);
> +
> +     /*
>        * Clean up and switch back to old context.
>        */
>       if (temp_desc)
>           pfree(queryDesc);
>
>       MemoryContextSwitchTo(oldcontext);
>   }
>
>   /* --------------------------------
> Index: src/backend/commands/explain.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/commands/explain.c,v
> retrieving revision 1.67
> diff -c -p -r1.67 explain.c
> *** src/backend/commands/explain.c    2001/10/25 05:49:25    1.67
> --- src/backend/commands/explain.c    2002/02/14 10:49:18
> ***************
> *** 19,24 ****
> --- 19,25 ----
>   #include "parser/parsetree.h"
>   #include "rewrite/rewriteHandler.h"
>   #include "tcop/pquery.h"
> + #include "utils/ps_status.h"
>   #include "utils/relcache.h"
>
>   typedef struct ExplainState
> *************** ExplainOneQuery(Query *query, bool verbo
> *** 113,118 ****
> --- 114,121 ----
>           struct timeval starttime;
>           struct timeval endtime;
>
> +         set_ps_display("SELECT");
> +
>           /*
>            * Set up the instrumentation for the top node. This will cascade
>            * during plan initialisation
> *************** ExplainOneQuery(Query *query, bool verbo
> *** 133,138 ****
> --- 136,143 ----
>           }
>           totaltime = (double) endtime.tv_sec +
>               (double) endtime.tv_usec / 1000000.0;
> +
> +         set_ps_display("EXPLAIN");
>       }
>
>       es = (ExplainState *) palloc(sizeof(ExplainState));
> Index: src/backend/executor/functions.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/executor/functions.c,v
> retrieving revision 1.47
> diff -c -p -r1.47 functions.c
> *** src/backend/executor/functions.c    2001/10/28 06:25:43    1.47
> --- src/backend/executor/functions.c    2002/02/14 10:49:18
> *************** postquel_getnext(execution_state *es)
> *** 276,281 ****
> --- 276,283 ----
>            * Process a utility command. (create, destroy...)    DZ - 30-8-1996
>            */
>           ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest);
> +         /* We should not call EndCommand() here as we are inside a function
> +          * call and command-complete reports should not be issued. */
>           if (!LAST_POSTQUEL_COMMAND(es))
>               CommandCounterIncrement();
>           return (TupleTableSlot *) NULL;
> Index: src/backend/executor/spi.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/executor/spi.c,v
> retrieving revision 1.64
> diff -c -p -r1.64 spi.c
> *** src/backend/executor/spi.c    2002/01/03 20:30:47    1.64
> --- src/backend/executor/spi.c    2002/02/14 10:49:18
> *************** _SPI_execute(char *src, int tcount, _SPI
> *** 1010,1015 ****
> --- 1010,1017 ----
>               if (plan == NULL)
>               {
>                   ProcessUtility(queryTree->utilityStmt, None);
> +                 /* We don't need to call EndCommand() as we don't want to
> +                  * send anything (dest = None) */
>                   if (!islastquery)
>                       CommandCounterIncrement();
>                   else
> *************** _SPI_execute_plan(_SPI_plan *plan, Datum
> *** 1084,1089 ****
> --- 1086,1093 ----
>           if (queryTree->commandType == CMD_UTILITY)
>           {
>               ProcessUtility(queryTree->utilityStmt, None);
> +             /* We don't need to call EndCommand() as we don't want to
> +              * send anything (dest = None) */
>               if (!islastquery)
>                   CommandCounterIncrement();
>               else
> Index: src/backend/parser/analyze.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/parser/analyze.c,v
> retrieving revision 1.213
> diff -c -p -r1.213 analyze.c
> *** src/backend/parser/analyze.c    2002/01/03 23:21:31    1.213
> --- src/backend/parser/analyze.c    2002/02/14 10:49:18
> *************** typedef struct
> *** 64,80 ****
>   } CreateStmtContext;
>
>
> ! static Query *transformStmt(ParseState *pstate, Node *stmt);
>   static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
> ! static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
>   static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
> ! static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt);
>   static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
>   static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
>   static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
>   static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
> ! static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
> ! static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt);
>   static void transformColumnDefinition(ParseState *pstate,
>                             CreateStmtContext *cxt,
>                             ColumnDef *column);
> --- 64,85 ----
>   } CreateStmtContext;
>
>
> ! static Query *transformStmt(ParseState *pstate, Node *stmt,
> !                         List **extras_before, List **extras_after);
>   static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
> ! static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
> !                         List **extras_before, List **extras_after);
>   static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
> ! static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt,
> !                         List **extras_before, List **extras_after);
>   static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
>   static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
>   static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
>   static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
> ! static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
> !                         List **extras_before, List **extras_after);
> ! static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
> !                         List **extras_before, List **extras_after);
>   static void transformColumnDefinition(ParseState *pstate,
>                             CreateStmtContext *cxt,
>                             ColumnDef *column);
> *************** static Oid    transformFkeyGetColType(Creat
> *** 101,109 ****
>   static void release_pstate_resources(ParseState *pstate);
>   static FromExpr *makeFromExpr(List *fromlist, Node *quals);
>
> - /* kluge to return extra info from transformCreateStmt() */
> - static List *extras_before;
> - static List *extras_after;
>
>
>   /*
> --- 106,111 ----
> *************** parse_analyze(Node *parseTree, ParseStat
> *** 121,137 ****
>       List       *result = NIL;
>       ParseState *pstate = make_parsestate(parentParseState);
>       Query       *query;
>
> !     extras_before = extras_after = NIL;
> !
> !     query = transformStmt(pstate, parseTree);
>       release_pstate_resources(pstate);
> !
>       while (extras_before != NIL)
>       {
> !         result = lappend(result,
> !                          transformStmt(pstate, lfirst(extras_before)));
> !         release_pstate_resources(pstate);
>           extras_before = lnext(extras_before);
>       }
>
> --- 123,138 ----
>       List       *result = NIL;
>       ParseState *pstate = make_parsestate(parentParseState);
>       Query       *query;
> +     /* Lists to return extra commands from transformation */
> +     List *extras_before = NIL;
> +     List *extras_after = NIL;
>
> !     query = transformStmt(pstate, parseTree, &extras_before, &extras_after);
>       release_pstate_resources(pstate);
> !
>       while (extras_before != NIL)
>       {
> !         result = nconc(result, parse_analyze(lfirst(extras_before), pstate));
>           extras_before = lnext(extras_before);
>       }
>
> *************** parse_analyze(Node *parseTree, ParseStat
> *** 139,147 ****
>
>       while (extras_after != NIL)
>       {
> !         result = lappend(result,
> !                          transformStmt(pstate, lfirst(extras_after)));
> !         release_pstate_resources(pstate);
>           extras_after = lnext(extras_after);
>       }
>
> --- 140,146 ----
>
>       while (extras_after != NIL)
>       {
> !         result = nconc(result, parse_analyze(lfirst(extras_after), pstate));
>           extras_after = lnext(extras_after);
>       }
>
> *************** release_pstate_resources(ParseState *pst
> *** 164,170 ****
>    *      transform a Parse tree into a Query tree.
>    */
>   static Query *
> ! transformStmt(ParseState *pstate, Node *parseTree)
>   {
>       Query       *result = NULL;
>
> --- 163,170 ----
>    *      transform a Parse tree into a Query tree.
>    */
>   static Query *
> ! transformStmt(ParseState *pstate, Node *parseTree,
> !               List **extras_before,    List **extras_after)
>   {
>       Query       *result = NULL;
>
> *************** transformStmt(ParseState *pstate, Node *
> *** 174,180 ****
>                * Non-optimizable statements
>                */
>           case T_CreateStmt:
> !             result = transformCreateStmt(pstate, (CreateStmt *) parseTree);
>               break;
>
>           case T_IndexStmt:
> --- 174,181 ----
>                * Non-optimizable statements
>                */
>           case T_CreateStmt:
> !             result = transformCreateStmt(pstate, (CreateStmt *) parseTree,
> !                                     extras_before, extras_after);
>               break;
>
>           case T_IndexStmt:
> *************** transformStmt(ParseState *pstate, Node *
> *** 182,195 ****
>               break;
>
>           case T_RuleStmt:
> !             result = transformRuleStmt(pstate, (RuleStmt *) parseTree);
>               break;
>
>           case T_ViewStmt:
>               {
>                   ViewStmt   *n = (ViewStmt *) parseTree;
>
> !                 n->query = transformStmt(pstate, (Node *) n->query);
>
>                   /*
>                    * If a list of column names was given, run through and
> --- 183,198 ----
>               break;
>
>           case T_RuleStmt:
> !             result = transformRuleStmt(pstate, (RuleStmt *) parseTree,
> !                                     extras_before, extras_after);
>               break;
>
>           case T_ViewStmt:
>               {
>                   ViewStmt   *n = (ViewStmt *) parseTree;
>
> !                 n->query = transformStmt(pstate, (Node *) n->query,
> !                                     extras_before, extras_after);
>
>                   /*
>                    * If a list of column names was given, run through and
> *************** transformStmt(ParseState *pstate, Node *
> *** 239,258 ****
>
>                   result = makeNode(Query);
>                   result->commandType = CMD_UTILITY;
> !                 n->query = transformStmt(pstate, (Node *) n->query);
>                   result->utilityStmt = (Node *) parseTree;
>               }
>               break;
>
>           case T_AlterTableStmt:
> !             result = transformAlterTableStmt(pstate, (AlterTableStmt *) parseTree);
>               break;
>
>               /*
>                * Optimizable statements
>                */
>           case T_InsertStmt:
> !             result = transformInsertStmt(pstate, (InsertStmt *) parseTree);
>               break;
>
>           case T_DeleteStmt:
> --- 242,264 ----
>
>                   result = makeNode(Query);
>                   result->commandType = CMD_UTILITY;
> !                 n->query = transformStmt(pstate, (Node *) n->query,
> !                                 extras_before, extras_after);
>                   result->utilityStmt = (Node *) parseTree;
>               }
>               break;
>
>           case T_AlterTableStmt:
> !             result = transformAlterTableStmt(pstate, (AlterTableStmt *) parseTree,
> !                                         extras_before, extras_after);
>               break;
>
>               /*
>                * Optimizable statements
>                */
>           case T_InsertStmt:
> !             result = transformInsertStmt(pstate, (InsertStmt *) parseTree,
> !                                         extras_before, extras_after);
>               break;
>
>           case T_DeleteStmt:
> *************** transformDeleteStmt(ParseState *pstate,
> *** 337,343 ****
>    *      transform an Insert Statement
>    */
>   static Query *
> ! transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
>   {
>       Query       *qry = makeNode(Query);
>       List       *sub_rtable;
> --- 343,350 ----
>    *      transform an Insert Statement
>    */
>   static Query *
> ! transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
> !                     List **extras_before, List **extras_after)
>   {
>       Query       *qry = makeNode(Query);
>       List       *sub_rtable;
> *************** transformInsertStmt(ParseState *pstate,
> *** 402,408 ****
>           sub_pstate->p_rtable = sub_rtable;
>           sub_pstate->p_namespace = sub_namespace;
>
> !         selectQuery = transformStmt(sub_pstate, stmt->selectStmt);
>
>           release_pstate_resources(sub_pstate);
>           pfree(sub_pstate);
> --- 409,420 ----
>           sub_pstate->p_rtable = sub_rtable;
>           sub_pstate->p_namespace = sub_namespace;
>
> !         /*
> !          * Note: we are not expecting that extras_before and extras_after
> !          * are going to be used by the transformation of the SELECT statement.
> !           */
> !         selectQuery = transformStmt(sub_pstate, stmt->selectStmt,
> !                                 extras_before, extras_after);
>
>           release_pstate_resources(sub_pstate);
>           pfree(sub_pstate);
> *************** CreateIndexName(char *table_name, char *
> *** 658,664 ****
>    *      - thomas 1997-12-02
>    */
>   static Query *
> ! transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
>   {
>       CreateStmtContext cxt;
>       Query       *q;
> --- 670,677 ----
>    *      - thomas 1997-12-02
>    */
>   static Query *
> ! transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
> !                     List **extras_before, List **extras_after)
>   {
>       CreateStmtContext cxt;
>       Query       *q;
> *************** transformCreateStmt(ParseState *pstate,
> *** 728,735 ****
>       q->utilityStmt = (Node *) stmt;
>       stmt->tableElts = cxt.columns;
>       stmt->constraints = cxt.ckconstraints;
> !     extras_before = cxt.blist;
> !     extras_after = cxt.alist;
>
>       return q;
>   }
> --- 741,748 ----
>       q->utilityStmt = (Node *) stmt;
>       stmt->tableElts = cxt.columns;
>       stmt->constraints = cxt.ckconstraints;
> !     *extras_before = nconc (*extras_before, cxt.blist);
> !     *extras_after = nconc (cxt.alist, *extras_after);
>
>       return q;
>   }
> *************** transformIndexStmt(ParseState *pstate, I
> *** 1668,1674 ****
>    *      trees which is transformed into a list of query trees.
>    */
>   static Query *
> ! transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
>   {
>       Query       *qry;
>       RangeTblEntry *oldrte;
> --- 1681,1688 ----
>    *      trees which is transformed into a list of query trees.
>    */
>   static Query *
> ! transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
> !                 List **extras_before, List **extras_after)
>   {
>       Query       *qry;
>       RangeTblEntry *oldrte;
> *************** transformRuleStmt(ParseState *pstate, Ru
> *** 1797,1803 ****
>               addRTEtoQuery(sub_pstate, newrte, false, true);
>
>               /* Transform the rule action statement */
> !             top_subqry = transformStmt(sub_pstate, action);
>
>               /*
>                * We cannot support utility-statement actions (eg NOTIFY)
> --- 1811,1818 ----
>               addRTEtoQuery(sub_pstate, newrte, false, true);
>
>               /* Transform the rule action statement */
> !             top_subqry = transformStmt(sub_pstate, action,
> !                                 extras_before, extras_after);
>
>               /*
>                * We cannot support utility-statement actions (eg NOTIFY)
> *************** transformUpdateStmt(ParseState *pstate,
> *** 2494,2500 ****
>    *    transform an Alter Table Statement
>    */
>   static Query *
> ! transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt)
>   {
>       CreateStmtContext cxt;
>       Query       *qry;
> --- 2509,2516 ----
>    *    transform an Alter Table Statement
>    */
>   static Query *
> ! transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
> !                         List **extras_before, List **extras_after)
>   {
>       CreateStmtContext cxt;
>       Query       *qry;
> *************** transformAlterTableStmt(ParseState *psta
> *** 2534,2541 ****
>               transformFKConstraints(pstate, &cxt);
>
>               ((ColumnDef *) stmt->def)->constraints = cxt.ckconstraints;
> !             extras_before = cxt.blist;
> !             extras_after = cxt.alist;
>               break;
>
>           case 'C':
> --- 2550,2557 ----
>               transformFKConstraints(pstate, &cxt);
>
>               ((ColumnDef *) stmt->def)->constraints = cxt.ckconstraints;
> !             *extras_before = nconc(*extras_before, cxt.blist);
> !             *extras_after = nconc(cxt.alist, *extras_after);
>               break;
>
>           case 'C':
> *************** transformAlterTableStmt(ParseState *psta
> *** 2571,2578 ****
>
>               Assert(cxt.columns == NIL);
>               stmt->def = (Node *) nconc(cxt.ckconstraints, cxt.fkconstraints);
> !             extras_before = cxt.blist;
> !             extras_after = cxt.alist;
>               break;
>
>           default:
> --- 2587,2594 ----
>
>               Assert(cxt.columns == NIL);
>               stmt->def = (Node *) nconc(cxt.ckconstraints, cxt.fkconstraints);
> !             *extras_before = nconc(*extras_before, cxt.blist);
> !             *extras_after = nconc(cxt.alist, *extras_after);
>               break;
>
>           default:
> Index: src/backend/tcop/postgres.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/tcop/postgres.c,v
> retrieving revision 1.245
> diff -c -p -r1.245 postgres.c
> *** src/backend/tcop/postgres.c    2002/01/10 01:11:45    1.245
> --- src/backend/tcop/postgres.c    2002/02/14 10:49:19
> ***************
> *** 64,69 ****
> --- 64,78 ----
>
>   #include "pgstat.h"
>
> + static char *CreateCommandTag(Node *parsetree);
> +
> + /* Uncomment the following line if you want ps_status
> +  * to be set for each individual query that may result from
> +  * a rewrite as oposed to just the main (original) one
> +  */
> + /* #define FINE_GRAIN_PROFILE 1 */
> +
> +
>   /* ----------------
>    *        global variables
>    * ----------------
> *************** pg_exec_query_string(char *query_string,
> *** 634,639 ****
> --- 643,650 ----
>       foreach(parsetree_item, parsetree_list)
>       {
>           Node       *parsetree = (Node *) lfirst(parsetree_item);
> +         char       *commandTag = NULL;
> +         bool        isQueryStmt = false;
>           bool        isTransactionStmt;
>           List       *querytree_list,
>                      *querytree_item;
> *************** pg_exec_query_string(char *query_string,
> *** 675,680 ****
> --- 686,693 ----
>                    * command ended. -cim 6/1/90
>                    */
>                   char       *tag = "*ABORT STATE*";
> +
> +                 set_ps_display(tag);
>
>                   elog(NOTICE, "current transaction is aborted, "
>                        "queries ignored until end of transaction block");
> *************** pg_exec_query_string(char *query_string,
> *** 702,708 ****
>
>           /*
>            * OK to analyze and rewrite this query.
> !          *
>            * Switch to appropriate context for constructing querytrees (again,
>            * these must outlive the execution context).
>            */
> --- 715,737 ----
>
>           /*
>            * OK to analyze and rewrite this query.
> !          */
> !
> !         /*
> !          * First we set the command-completion tag to the main query
> !          * (as opposed to each of the others that may have
> !          * been generated by analyze and rewrite)
> !          */
> !         commandTag = CreateCommandTag(parsetree);
> !
> ! #ifndef FINE_GRAIN_PROFILE
> !         /*
> !          * Set ps_status to the main query tag
> !          */
> !         set_ps_display(commandTag);
> ! #endif
> !
> !         /*
>            * Switch to appropriate context for constructing querytrees (again,
>            * these must outlive the execution context).
>            */
> *************** pg_exec_query_string(char *query_string,
> *** 746,752 ****
> --- 775,796 ----
>                   else if (DebugLvl > 1)
>                       elog(DEBUG, "ProcessUtility");
>
> + #ifdef FINE_GRAIN_PROFILE
> +                 set_ps_display(CreateUtilityTag(querytree->utilityStmt));
> + #endif
>                   ProcessUtility(querytree->utilityStmt, dest);
> +
> +                 /*
> +                  * FETCHes, as plannable queries, take care of completion
> +                  * themselves but we still have to do it for MOVEs as they
> +                  * do not send data (and thus, do not send a completion).
> +                  */
> +                 if (nodeTag(parsetree) == T_FetchStmt)
> +                 {
> +                     FetchStmt  *stmt = (FetchStmt *) parsetree;
> +                     if (!(stmt->ismove))
> +                         isQueryStmt = true;  /* Don't do it twice */
> +                 }
>               }
>               else
>               {
> *************** pg_exec_query_string(char *query_string,
> *** 754,759 ****
> --- 798,805 ----
>                    * process a plannable query.
>                    */
>                   Plan       *plan;
> +
> +                 isQueryStmt = true;
>
>                   plan = pg_plan_query(querytree);
>
> *************** pg_exec_query_string(char *query_string,
> *** 818,823 ****
> --- 864,876 ----
>
>           }                        /* end loop over queries generated from a
>                                    * parsetree */
> +
> +         /*
> +          * tell fe/be or whatever that we're done if we only processed
> +          * utilities (plannable queries take care of completion themselves)
> +          */
> +         if (!isQueryStmt)
> +             EndCommand(commandTag, dest);
>       }                            /* end loop over parsetrees */
>
>       /*
> *************** assertTest(int val)
> *** 2037,2039 ****
> --- 2090,2354 ----
>   #endif
>
>   #endif
> +
> +
> + /* ----------------------------------------------------------------
> +  *        CreateCommandTag
> +  *
> +  *        utility to get a string representation of the
> +  *        command operation.
> +  * ----------------------------------------------------------------
> +  */
> + static char *
> + CreateCommandTag(Node *parsetree)
> + {
> +     char       *tag = NULL;
> +
> +     switch (nodeTag(parsetree))
> +     {
> +         case T_InsertStmt:
> +             tag = "INSERT";
> +             break;
> +
> +         case T_DeleteStmt:
> +             tag = "DELETE";
> +             break;
> +
> +         case T_UpdateStmt:
> +             tag = "UPDATE";
> +             break;
> +
> +         case T_SelectStmt:
> +             tag = "SELECT";
> +             break;
> +
> +         case T_TransactionStmt:
> +             {
> +                 TransactionStmt *stmt = (TransactionStmt *) parsetree;
> +
> +                 switch (stmt->command)
> +                 {
> +                     case BEGIN_TRANS:
> +                         tag = "BEGIN";
> +                         break;
> +
> +                     case COMMIT:
> +                         tag = "COMMIT";
> +                         break;
> +
> +                     case ROLLBACK:
> +                         tag = "ROLLBACK";
> +                         break;
> +                 }
> +             }
> +             break;
> +
> +         case T_ClosePortalStmt:
> +             tag = "CLOSE";
> +             break;
> +
> +         case T_FetchStmt:
> +             {
> +                 FetchStmt  *stmt = (FetchStmt *) parsetree;
> +                 tag = (stmt->ismove) ? "MOVE" : "FETCH";
> +             }
> +             break;
> +
> +         case T_CreateStmt:
> +             tag = "CREATE";
> +             break;
> +
> +         case T_DropStmt:
> +             tag = "DROP";
> +             break;
> +
> +         case T_TruncateStmt:
> +             tag = "TRUNCATE";
> +             break;
> +
> +         case T_CommentStmt:
> +             tag = "COMMENT";
> +             break;
> +
> +         case T_CopyStmt:
> +             tag = "COPY";
> +             break;
> +
> +         case T_RenameStmt:
> +             tag = "ALTER";
> +             break;
> +
> +         case T_AlterTableStmt:
> +             tag = "ALTER";
> +             break;
> +
> +         case T_GrantStmt:
> +             {
> +                 GrantStmt  *stmt = (GrantStmt *) parsetree;
> +                 tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
> +             }
> +             break;
> +
> +         case T_DefineStmt:
> +             tag = "CREATE";
> +             break;
> +
> +         case T_ViewStmt:        /* CREATE VIEW */
> +             tag = "CREATE";
> +             break;
> +
> +         case T_ProcedureStmt:    /* CREATE FUNCTION */
> +             tag = "CREATE";
> +             break;
> +
> +         case T_IndexStmt:        /* CREATE INDEX */
> +             tag = "CREATE";
> +             break;
> +
> +         case T_RuleStmt:        /* CREATE RULE */
> +             tag = "CREATE";
> +             break;
> +
> +         case T_CreateSeqStmt:
> +             tag = "CREATE";
> +             break;
> +
> +         case T_RemoveAggrStmt:
> +             tag = "DROP";
> +             break;
> +
> +         case T_RemoveFuncStmt:
> +             tag = "DROP";
> +             break;
> +
> +         case T_RemoveOperStmt:
> +             tag = "DROP";
> +             break;
> +
> +         case T_VersionStmt:
> +             tag = "CREATE VERSION";
> +             break;
> +
> +         case T_CreatedbStmt:
> +             tag = "CREATE DATABASE";
> +             break;
> +
> +         case T_DropdbStmt:
> +             tag = "DROP DATABASE";
> +             break;
> +
> +         case T_NotifyStmt:
> +             tag = "NOTIFY";
> +             break;
> +
> +         case T_ListenStmt:
> +             tag = "LISTEN";
> +             break;
> +
> +         case T_UnlistenStmt:
> +             tag = "UNLISTEN";
> +             break;
> +
> +         case T_LoadStmt:
> +             tag = "LOAD";
> +             break;
> +
> +         case T_ClusterStmt:
> +             tag = "CLUSTER";
> +             break;
> +
> +         case T_VacuumStmt:
> +             if (((VacuumStmt *) parsetree)->vacuum)
> +                 tag = "VACUUM";
> +             else
> +                 tag = "ANALYZE";
> +             break;
> +
> +         case T_ExplainStmt:
> +             tag = "EXPLAIN";
> +             break;
> +
> + #ifdef NOT_USED
> +         case T_RecipeStmt:
> +             tag = "EXECUTE RECIPE";
> +             break;
> + #endif
> +
> +         case T_VariableSetStmt:
> +             tag = "SET VARIABLE";
> +             break;
> +
> +         case T_VariableShowStmt:
> +             tag = "SHOW VARIABLE";
> +             break;
> +
> +         case T_VariableResetStmt:
> +             tag = "RESET VARIABLE";
> +             break;
> +
> +         case T_CreateTrigStmt:
> +             tag = "CREATE";
> +             break;
> +
> +         case T_DropTrigStmt:
> +             tag = "DROP";
> +             break;
> +
> +         case T_CreatePLangStmt:
> +             tag = "CREATE";
> +             break;
> +
> +         case T_DropPLangStmt:
> +             tag = "DROP";
> +             break;
> +
> +         case T_CreateUserStmt:
> +             tag = "CREATE USER";
> +             break;
> +
> +         case T_AlterUserStmt:
> +             tag = "ALTER USER";
> +             break;
> +
> +         case T_DropUserStmt:
> +             tag = "DROP USER";
> +             break;
> +
> +         case T_LockStmt:
> +             tag = "LOCK TABLE";
> +             break;
> +
> +         case T_ConstraintsSetStmt:
> +             tag = "SET CONSTRAINTS";
> +             break;
> +
> +         case T_CreateGroupStmt:
> +             tag = "CREATE GROUP";
> +             break;
> +
> +         case T_AlterGroupStmt:
> +             tag = "ALTER GROUP";
> +             break;
> +
> +         case T_DropGroupStmt:
> +             tag = "DROP GROUP";
> +             break;
> +
> +         case T_CheckPointStmt:
> +             tag = "CHECKPOINT";
> +             break;
> +
> +         case T_ReindexStmt:
> +             tag = "REINDEX";
> +             break;
> +
> +         default:
> +             elog(DEBUG, "CreateUtilityTag: unknown utility operation type %d",
> +                  nodeTag(parsetree));
> +             tag = "???";
> +             break;
> +     }
> +
> +     return tag;
> + }
> +
> Index: src/backend/tcop/pquery.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/tcop/pquery.c,v
> retrieving revision 1.46
> diff -c -p -r1.46 pquery.c
> *** src/backend/tcop/pquery.c    2001/10/25 05:49:43    1.46
> --- src/backend/tcop/pquery.c    2002/02/14 10:49:19
> *************** ProcessQuery(Query *parsetree,
> *** 180,186 ****
>       EState       *state;
>       TupleDesc    attinfo;
>
> !     set_ps_display(tag = CreateOperationTag(operation));
>
>       /*
>        * initialize portal/into relation status
> --- 180,186 ----
>       EState       *state;
>       TupleDesc    attinfo;
>
> !     tag = CreateOperationTag(operation);
>
>       /*
>        * initialize portal/into relation status
> Index: src/backend/tcop/utility.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/tcop/utility.c,v
> retrieving revision 1.125
> diff -c -p -r1.125 utility.c
> *** src/backend/tcop/utility.c    2002/02/07 00:27:30    1.125
> --- src/backend/tcop/utility.c    2002/02/14 10:49:19
> ***************
> *** 44,54 ****
>   #include "rewrite/rewriteRemove.h"
>   #include "tcop/utility.h"
>   #include "utils/acl.h"
> - #include "utils/ps_status.h"
>   #include "utils/syscache.h"
>   #include "utils/temprel.h"
>   #include "access/xlog.h"
>
>   /*
>    * Error-checking support for DROP commands
>    */
> --- 44,54 ----
>   #include "rewrite/rewriteRemove.h"
>   #include "tcop/utility.h"
>   #include "utils/acl.h"
>   #include "utils/syscache.h"
>   #include "utils/temprel.h"
>   #include "access/xlog.h"
>
> +
>   /*
>    * Error-checking support for DROP commands
>    */
> *************** CheckDropPermissions(char *name, char ri
> *** 132,144 ****
>
>   /* ----------------
>    *        general utility function invoker
>    * ----------------
>    */
>   void
>   ProcessUtility(Node *parsetree,
>                  CommandDest dest)
>   {
> -     char       *commandTag = NULL;
>       char       *relname;
>       char       *relationName;
>
> --- 132,145 ----
>
>   /* ----------------
>    *        general utility function invoker
> +  *
> +  *    Returns the command-complete tag appropriate for the function
>    * ----------------
>    */
>   void
>   ProcessUtility(Node *parsetree,
>                  CommandDest dest)
>   {
>       char       *relname;
>       char       *relationName;
>
> *************** ProcessUtility(Node *parsetree,
> *** 155,171 ****
>                   switch (stmt->command)
>                   {
>                       case BEGIN_TRANS:
> -                         set_ps_display(commandTag = "BEGIN");
>                           BeginTransactionBlock();
>                           break;
>
>                       case COMMIT:
> -                         set_ps_display(commandTag = "COMMIT");
>                           EndTransactionBlock();
>                           break;
>
>                       case ROLLBACK:
> -                         set_ps_display(commandTag = "ROLLBACK");
>                           UserAbortTransactionBlock();
>                           break;
>                   }
> --- 156,169 ----
> *************** ProcessUtility(Node *parsetree,
> *** 180,187 ****
>               {
>                   ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "CLOSE");
> -
>                   PerformPortalClose(stmt->portalname, dest);
>               }
>               break;
> --- 178,183 ----
> *************** ProcessUtility(Node *parsetree,
> *** 193,200 ****
>                   bool        forward;
>                   int            count;
>
> -                 set_ps_display(commandTag = (stmt->ismove) ? "MOVE" : "FETCH");
> -
>                   SetQuerySnapshot();
>
>                   forward = (bool) (stmt->direction == FORWARD);
> --- 189,194 ----
> *************** ProcessUtility(Node *parsetree,
> *** 204,210 ****
>                    */
>
>                   count = stmt->howMany;
> !                 PerformPortalFetch(portalName, forward, count, commandTag,
>                                      (stmt->ismove) ? None : dest);        /* /dev/null for MOVE */
>               }
>               break;
> --- 198,204 ----
>                    */
>
>                   count = stmt->howMany;
> !                 PerformPortalFetch(portalName, forward, count,(stmt->ismove) ? "MOVE" : "FETCH",
>                                      (stmt->ismove) ? None : dest);        /* /dev/null for MOVE */
>               }
>               break;
> *************** ProcessUtility(Node *parsetree,
> *** 215,222 ****
>                *
>                */
>           case T_CreateStmt:
> -             set_ps_display(commandTag = "CREATE");
> -
>               DefineRelation((CreateStmt *) parsetree, RELKIND_RELATION);
>
>               /*
> --- 209,214 ----
> *************** ProcessUtility(Node *parsetree,
> *** 234,241 ****
>                   List       *args = stmt->names;
>                   List       *arg;
>
> -                 set_ps_display(commandTag = "DROP");
> -
>                   foreach(arg, args)
>                   {
>                       relname = strVal(lfirst(arg));
> --- 226,231 ----
> *************** ProcessUtility(Node *parsetree,
> *** 296,303 ****
>               {
>                   Relation    rel;
>
> -                 set_ps_display(commandTag = "TRUNCATE");
> -
>                   relname = ((TruncateStmt *) parsetree)->relName;
>                   if (!allowSystemTableMods && IsSystemRelationName(relname))
>                       elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
> --- 286,291 ----
> *************** ProcessUtility(Node *parsetree,
> *** 325,332 ****
>
>                   statement = ((CommentStmt *) parsetree);
>
> -                 set_ps_display(commandTag = "COMMENT");
> -
>                   CommentObject(statement->objtype, statement->objname,
>                                 statement->objproperty, statement->objlist,
>                                 statement->comment);
> --- 313,318 ----
> *************** ProcessUtility(Node *parsetree,
> *** 337,344 ****
>               {
>                   CopyStmt   *stmt = (CopyStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "COPY");
> -
>                   if (stmt->direction != FROM)
>                       SetQuerySnapshot();
>
> --- 323,328 ----
> *************** ProcessUtility(Node *parsetree,
> *** 365,372 ****
>               {
>                   RenameStmt *stmt = (RenameStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "ALTER");
> -
>                   relname = stmt->relname;
>                   if (!allowSystemTableMods && IsSystemRelationName(relname))
>                       elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
> --- 349,354 ----
> *************** ProcessUtility(Node *parsetree,
> *** 413,420 ****
>               {
>                   AlterTableStmt *stmt = (AlterTableStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "ALTER");
> -
>                   /*
>                    * Some or all of these functions are recursive to cover
>                    * inherited things, so permission checks are done there.
> --- 395,400 ----
> *************** ProcessUtility(Node *parsetree,
> *** 475,483 ****
>               {
>                   GrantStmt  *stmt = (GrantStmt *) parsetree;
>
> -                 commandTag = stmt->is_grant ? "GRANT" : "REVOKE";
> -                 set_ps_display(commandTag);
> -
>                   ExecuteGrantStmt(stmt);
>               }
>               break;
> --- 455,460 ----
> *************** ProcessUtility(Node *parsetree,
> *** 491,498 ****
>               {
>                   DefineStmt *stmt = (DefineStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "CREATE");
> -
>                   switch (stmt->defType)
>                   {
>                       case OPERATOR:
> --- 468,473 ----
> *************** ProcessUtility(Node *parsetree,
> *** 514,528 ****
>               {
>                   ViewStmt   *stmt = (ViewStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "CREATE");
> -
>                   DefineView(stmt->viewname, stmt->query);        /* retrieve parsetree */
>               }
>               break;
>
>           case T_ProcedureStmt:    /* CREATE FUNCTION */
> -             set_ps_display(commandTag = "CREATE");
> -
>               CreateFunction((ProcedureStmt *) parsetree);
>               break;
>
> --- 489,499 ----
> *************** ProcessUtility(Node *parsetree,
> *** 530,537 ****
>               {
>                   IndexStmt  *stmt = (IndexStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "CREATE");
> -
>                   relname = stmt->relname;
>                   if (!allowSystemTableMods && IsSystemRelationName(relname))
>                       elog(ERROR, "CREATE INDEX: relation \"%s\" is a system catalog",
> --- 501,506 ----
> *************** ProcessUtility(Node *parsetree,
> *** 559,573 ****
>                   aclcheck_result = pg_aclcheck(relname, GetUserId(), ACL_RULE);
>                   if (aclcheck_result != ACLCHECK_OK)
>                       elog(ERROR, "%s: %s", relname, aclcheck_error_strings[aclcheck_result]);
> -                 set_ps_display(commandTag = "CREATE");
>
>                   DefineQueryRewrite(stmt);
>               }
>               break;
>
>           case T_CreateSeqStmt:
> -             set_ps_display(commandTag = "CREATE");
> -
>               DefineSequence((CreateSeqStmt *) parsetree);
>               break;
>
> --- 528,539 ----
> *************** ProcessUtility(Node *parsetree,
> *** 576,583 ****
>                   RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
>                   char       *typename = (char *) NULL;
>
> -                 set_ps_display(commandTag = "DROP");
> -
>                   if (stmt->aggtype != NULL)
>                       typename = TypeNameToInternalName((TypeName *) stmt->aggtype);
>
> --- 542,547 ----
> *************** ProcessUtility(Node *parsetree,
> *** 589,596 ****
>               {
>                   RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "DROP");
> -
>                   RemoveFunction(stmt->funcname, stmt->args);
>               }
>               break;
> --- 553,558 ----
> *************** ProcessUtility(Node *parsetree,
> *** 603,610 ****
>                   char       *typename1 = (char *) NULL;
>                   char       *typename2 = (char *) NULL;
>
> -                 set_ps_display(commandTag = "DROP");
> -
>                   if (typenode1 != NULL)
>                       typename1 = TypeNameToInternalName(typenode1);
>                   if (typenode2 != NULL)
> --- 565,570 ----
> *************** ProcessUtility(Node *parsetree,
> *** 622,629 ****
>               {
>                   CreatedbStmt *stmt = (CreatedbStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "CREATE DATABASE");
> -
>                   createdb(stmt->dbname, stmt->dbpath,
>                            stmt->dbtemplate, stmt->encoding);
>               }
> --- 582,587 ----
> *************** ProcessUtility(Node *parsetree,
> *** 633,640 ****
>               {
>                   DropdbStmt *stmt = (DropdbStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "DROP DATABASE");
> -
>                   dropdb(stmt->dbname);
>               }
>               break;
> --- 591,596 ----
> *************** ProcessUtility(Node *parsetree,
> *** 644,651 ****
>               {
>                   NotifyStmt *stmt = (NotifyStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "NOTIFY");
> -
>                   Async_Notify(stmt->relname);
>               }
>               break;
> --- 600,605 ----
> *************** ProcessUtility(Node *parsetree,
> *** 654,661 ****
>               {
>                   ListenStmt *stmt = (ListenStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "LISTEN");
> -
>                   Async_Listen(stmt->relname, MyProcPid);
>               }
>               break;
> --- 608,613 ----
> *************** ProcessUtility(Node *parsetree,
> *** 664,671 ****
>               {
>                   UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "UNLISTEN");
> -
>                   Async_Unlisten(stmt->relname, MyProcPid);
>               }
>               break;
> --- 616,621 ----
> *************** ProcessUtility(Node *parsetree,
> *** 678,685 ****
>               {
>                   LoadStmt   *stmt = (LoadStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "LOAD");
> -
>                   closeAllVfds(); /* probably not necessary... */
>                   load_file(stmt->filename);
>               }
> --- 628,633 ----
> *************** ProcessUtility(Node *parsetree,
> *** 689,696 ****
>               {
>                   ClusterStmt *stmt = (ClusterStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "CLUSTER");
> -
>                   relname = stmt->relname;
>                   if (IsSystemRelationName(relname))
>                       elog(ERROR, "CLUSTER: relation \"%s\" is a system catalog",
> --- 637,642 ----
> *************** ProcessUtility(Node *parsetree,
> *** 703,714 ****
>               break;
>
>           case T_VacuumStmt:
> -             if (((VacuumStmt *) parsetree)->vacuum)
> -                 commandTag = "VACUUM";
> -             else
> -                 commandTag = "ANALYZE";
> -             set_ps_display(commandTag);
> -
>               vacuum((VacuumStmt *) parsetree);
>               break;
>
> --- 649,654 ----
> *************** ProcessUtility(Node *parsetree,
> *** 716,723 ****
>               {
>                   ExplainStmt *stmt = (ExplainStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "EXPLAIN");
> -
>                   ExplainQuery(stmt->query, stmt->verbose, stmt->analyze, dest);
>               }
>               break;
> --- 656,661 ----
> *************** ProcessUtility(Node *parsetree,
> *** 731,738 ****
>               {
>                   RecipeStmt *stmt = (RecipeStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "EXECUTE RECIPE");
> -
>                   beginRecipe(stmt);
>               }
>               break;
> --- 669,674 ----
> *************** ProcessUtility(Node *parsetree,
> *** 746,752 ****
>                   VariableSetStmt *n = (VariableSetStmt *) parsetree;
>
>                   SetPGVariable(n->name, n->args);
> -                 set_ps_display(commandTag = "SET VARIABLE");
>               }
>               break;
>
> --- 682,687 ----
> *************** ProcessUtility(Node *parsetree,
> *** 755,761 ****
>                   VariableShowStmt *n = (VariableShowStmt *) parsetree;
>
>                   GetPGVariable(n->name);
> -                 set_ps_display(commandTag = "SHOW VARIABLE");
>               }
>               break;
>
> --- 690,695 ----
> *************** ProcessUtility(Node *parsetree,
> *** 764,770 ****
>                   VariableResetStmt *n = (VariableResetStmt *) parsetree;
>
>                   ResetPGVariable(n->name);
> -                 set_ps_display(commandTag = "RESET VARIABLE");
>               }
>               break;
>
> --- 698,703 ----
> *************** ProcessUtility(Node *parsetree,
> *** 772,785 ****
>                * ******************************** TRIGGER statements *******************************
>                */
>           case T_CreateTrigStmt:
> -             set_ps_display(commandTag = "CREATE");
> -
>               CreateTrigger((CreateTrigStmt *) parsetree);
>               break;
>
>           case T_DropTrigStmt:
> -             set_ps_display(commandTag = "DROP");
> -
>               DropTrigger((DropTrigStmt *) parsetree);
>               break;
>
> --- 705,714 ----
> *************** ProcessUtility(Node *parsetree,
> *** 787,800 ****
>                * ************* PROCEDURAL LANGUAGE statements *****************
>                */
>           case T_CreatePLangStmt:
> -             set_ps_display(commandTag = "CREATE");
> -
>               CreateProceduralLanguage((CreatePLangStmt *) parsetree);
>               break;
>
>           case T_DropPLangStmt:
> -             set_ps_display(commandTag = "DROP");
> -
>               DropProceduralLanguage((DropPLangStmt *) parsetree);
>               break;
>
> --- 716,725 ----
> *************** ProcessUtility(Node *parsetree,
> *** 803,859 ****
>                *
>                */
>           case T_CreateUserStmt:
> -             set_ps_display(commandTag = "CREATE USER");
> -
>               CreateUser((CreateUserStmt *) parsetree);
>               break;
>
>           case T_AlterUserStmt:
> -             set_ps_display(commandTag = "ALTER USER");
> -
>               AlterUser((AlterUserStmt *) parsetree);
>               break;
>
>           case T_DropUserStmt:
> -             set_ps_display(commandTag = "DROP USER");
> -
>               DropUser((DropUserStmt *) parsetree);
>               break;
>
>           case T_LockStmt:
> -             set_ps_display(commandTag = "LOCK TABLE");
> -
>               LockTableCommand((LockStmt *) parsetree);
>               break;
>
>           case T_ConstraintsSetStmt:
> -             set_ps_display(commandTag = "SET CONSTRAINTS");
> -
>               DeferredTriggerSetState((ConstraintsSetStmt *) parsetree);
>               break;
>
>           case T_CreateGroupStmt:
> -             set_ps_display(commandTag = "CREATE GROUP");
> -
>               CreateGroup((CreateGroupStmt *) parsetree);
>               break;
>
>           case T_AlterGroupStmt:
> -             set_ps_display(commandTag = "ALTER GROUP");
> -
>               AlterGroup((AlterGroupStmt *) parsetree, "ALTER GROUP");
>               break;
>
>           case T_DropGroupStmt:
> -             set_ps_display(commandTag = "DROP GROUP");
> -
>               DropGroup((DropGroupStmt *) parsetree);
>               break;
>
>           case T_CheckPointStmt:
>               {
> -                 set_ps_display(commandTag = "CHECKPOINT");
> -
>                   if (!superuser())
>                       elog(ERROR, "permission denied");
>                   CreateCheckPoint(false);
> --- 728,766 ----
> *************** ProcessUtility(Node *parsetree,
> *** 864,871 ****
>               {
>                   ReindexStmt *stmt = (ReindexStmt *) parsetree;
>
> -                 set_ps_display(commandTag = "REINDEX");
> -
>                   switch (stmt->reindexType)
>                   {
>                       case INDEX:
> --- 771,776 ----
> *************** ProcessUtility(Node *parsetree,
> *** 913,919 ****
>       }
>
>       /*
> !      * tell fe/be or whatever that we're done.
>        */
> !     EndCommand(commandTag, dest);
>   }
> --- 818,826 ----
>       }
>
>       /*
> !      * Note: the "end-of-command" tag is to be sent by the caller,
> !      * if appropriate.
>        */
> !     return;
>   }
> +

>
> ---------------------------(end of broadcast)---------------------------
> TIP 5: Have you checked our extensive FAQ?
>
> http://www.postgresql.org/users-lounge/docs/faq.html

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026

Re: Fix issuing of multiple command completions per command

From
Fernando Nasser
Date:
Tom Lane wrote:
>
> Fernando Nasser <fnasser@redhat.com> writes:
> > (Please note that "[PATCHES] Allow arbitrary levels of
> > analyze/rewriting" must be applied before this patch)
>
> Actually, it looks like you accidentally included that patch as part of
> this one.  Since Bruce already applied that patch, this one will not
> apply cleanly.
>

Oops!  I am attaching the corrected patch. Sorry.


> While this looks good as far as it goes, I don't think it
> resolves the problems exposed by Coax's recent complaint:
> http://archives.postgresql.org/pgsql-bugs/2002-02/msg00155.php
>

It would solve the completion tag part as it would send the INSERT.
Remember that you asked me to generate the tag based on the parse
tree (before the analyze/rewrite).

And I prevent a second command completion tag to clobber it.
We should ask him to try the patch.  Coax says he is not very
concerned with the CommandInfo -- the application needs the
right CommandTag.

But yes, this patch is to fix the tag part.  We can work together on
a follow-up patch to improve the CommandInfo part as well, but we
will have to change things around a bit, as you suggest.

> There are two remaining bogosities:
>
> 1. We need to get rid of the static variable CommandInfo that's
> manipulated by UpdateCommandInfo; static state is certainly going
> to be a bad idea in this context.
>

Humm, I believe it is only used from the Begin to the End calls.
It seems harmless to me, but it may indeed go away once
we rearrange things around to choose what is printed


> 2. EndCommand should *not* be issued until we have completed all the
> actions of the (rewritten) query.  Moving EndCommand into
> PerformPortalFetch, as you did here, is exactly the wrong direction.
> What we need is for EndCommand to be executed from pg_exec_query_string.
> That means that the tag auxiliary info (currently set up by
> UpdateCommandInfo) has to be passed out from ProcessQuery to
> pg_exec_query_string, which is the only place that can know when to send
> the command completion notice back to the client.
>

That was my first impression, but after I looked more deeply into it
I figured that this is the right thing to do (I explain it below).

And remember that there is still the 'Z'.  This completion will close
the
flow of data packets that may have been sent.  It is good for the
async processing as well as the application may process the data
while waiting for the 'Z' (otherwise it will not know that all the
data has arrived).


> As the comments in dest.h and dest.c point out, BeginCommand/EndCommand
> are fairly confused in purpose and usage right now.  Maybe rather than
> patching, we ought to decide what functionality we really need and do a
> major reconstruction job.
>

I think it was fine until rewriting of queries became more frequent.
It is actually still fine for plannable queries (with this patch
applied).

The way I see it is that this is a palnnable query thing where
Begin and End calls should open and close the flow of data.

For plannable queries we have:

BeginCommand()
<send data>
EndCommand()

It has to be properly adapted for utility queries and in that case
it has to be handled by pg_exec_query_string which is the one that
has a view of the utility processing as a whole (no sending of data
involved in this case either).



> One rather nasty issue that arises here is that I don't think
> pg_exec_query_string has enough knowledge to understand which of the
> queries produced by query rewriting corresponds to the "original" query.
> This is critical in order to figure out which command's auxiliary info
> is the right stuff to pass back to the client.  I suspect we will need
> to expand QueryRewrite's API to identify the "main" query in the
> returned query list.
>

For tags we avoided this issue when you asked me to use the parse tree
to
generate the completion tokens.  But I agree that we will probably have
to do exactly what you are saying if we want to control what is the
CommandInfo that will be printed with the CommandTag.

But consider also that the CommandInfo that corresponds to the data
stream was already sent.  And it is zero anyway if more than one row
is affected, isn't it?  Maybe it is not that broken after all
(with tis patch applied, I mean).


My suggestion is that we do this in stages.  Get the tags fixed (this
patch), as CREATE SCHEMAS gets very weird without it.  And that we
discuss
a little bit what to do with CommandInfo and create a patch for that.
Also, a single patch for both things will meddle with too much code.

--
Fernando Nasser
Red Hat Canada Ltd.                     E-Mail:  fnasser@redhat.com
2323 Yonge Street, Suite #300
Toronto, Ontario   M4P 2C9Index: src/backend/commands/command.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/commands/command.c,v
retrieving revision 1.154
diff -c -p -r1.154 command.c
*** src/backend/commands/command.c    2002/02/19 20:11:12    1.154
--- src/backend/commands/command.c    2002/02/26 03:33:46
*************** PerformPortalFetch(char *name,
*** 230,246 ****
      SetScanCommandId(savedId);

      /*
       * Clean up and switch back to old context.
       */
      if (temp_desc)
          pfree(queryDesc);

      MemoryContextSwitchTo(oldcontext);
-
-     /*
-      * Note: the "end-of-command" tag is returned by higher-level utility
-      * code
-      */
  }

  /* --------------------------------
--- 230,246 ----
      SetScanCommandId(savedId);

      /*
+      * tell fe/be or whatever that we're done sending the data
+      */
+     EndCommand(tag, queryDesc->dest);
+
+     /*
       * Clean up and switch back to old context.
       */
      if (temp_desc)
          pfree(queryDesc);

      MemoryContextSwitchTo(oldcontext);
  }

  /* --------------------------------
Index: src/backend/commands/explain.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/commands/explain.c,v
retrieving revision 1.67
diff -c -p -r1.67 explain.c
*** src/backend/commands/explain.c    2001/10/25 05:49:25    1.67
--- src/backend/commands/explain.c    2002/02/26 03:33:46
***************
*** 19,24 ****
--- 19,25 ----
  #include "parser/parsetree.h"
  #include "rewrite/rewriteHandler.h"
  #include "tcop/pquery.h"
+ #include "utils/ps_status.h"
  #include "utils/relcache.h"

  typedef struct ExplainState
*************** ExplainOneQuery(Query *query, bool verbo
*** 113,118 ****
--- 114,121 ----
          struct timeval starttime;
          struct timeval endtime;

+         set_ps_display("SELECT");
+
          /*
           * Set up the instrumentation for the top node. This will cascade
           * during plan initialisation
*************** ExplainOneQuery(Query *query, bool verbo
*** 133,138 ****
--- 136,143 ----
          }
          totaltime = (double) endtime.tv_sec +
              (double) endtime.tv_usec / 1000000.0;
+
+         set_ps_display("EXPLAIN");
      }

      es = (ExplainState *) palloc(sizeof(ExplainState));
Index: src/backend/executor/functions.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/executor/functions.c,v
retrieving revision 1.47
diff -c -p -r1.47 functions.c
*** src/backend/executor/functions.c    2001/10/28 06:25:43    1.47
--- src/backend/executor/functions.c    2002/02/26 03:33:46
*************** postquel_getnext(execution_state *es)
*** 276,281 ****
--- 276,283 ----
           * Process a utility command. (create, destroy...)    DZ - 30-8-1996
           */
          ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest);
+         /* We should not call EndCommand() here as we are inside a function
+          * call and command-complete reports should not be issued. */
          if (!LAST_POSTQUEL_COMMAND(es))
              CommandCounterIncrement();
          return (TupleTableSlot *) NULL;
Index: src/backend/executor/spi.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/executor/spi.c,v
retrieving revision 1.65
diff -c -p -r1.65 spi.c
*** src/backend/executor/spi.c    2002/02/14 15:24:08    1.65
--- src/backend/executor/spi.c    2002/02/26 03:33:46
*************** _SPI_execute(char *src, int tcount, _SPI
*** 1012,1017 ****
--- 1012,1019 ----
              if (plan == NULL)
              {
                  ProcessUtility(queryTree->utilityStmt, None);
+                 /* We don't need to call EndCommand() as we don't want to
+                  * send anything (dest = None) */
                  if (!islastquery)
                      CommandCounterIncrement();
                  else
*************** _SPI_execute_plan(_SPI_plan *plan, Datum
*** 1086,1091 ****
--- 1088,1095 ----
          if (queryTree->commandType == CMD_UTILITY)
          {
              ProcessUtility(queryTree->utilityStmt, None);
+             /* We don't need to call EndCommand() as we don't want to
+              * send anything (dest = None) */
              if (!islastquery)
                  CommandCounterIncrement();
              else
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/tcop/postgres.c,v
retrieving revision 1.247
diff -c -p -r1.247 postgres.c
*** src/backend/tcop/postgres.c    2002/02/23 01:31:36    1.247
--- src/backend/tcop/postgres.c    2002/02/26 03:33:47
***************
*** 64,69 ****
--- 64,78 ----

  #include "pgstat.h"

+ static char *CreateCommandTag(Node *parsetree);
+
+ /* Uncomment the following line if you want ps_status
+  * to be set for each individual query that may result from
+  * a rewrite as oposed to just the main (original) one
+  */
+ /* #define FINE_GRAIN_PROFILE 1 */
+
+
  /* ----------------
   *        global variables
   * ----------------
*************** pg_exec_query_string(char *query_string,
*** 634,639 ****
--- 643,650 ----
      foreach(parsetree_item, parsetree_list)
      {
          Node       *parsetree = (Node *) lfirst(parsetree_item);
+         char       *commandTag = NULL;
+         bool        isQueryStmt = false;
          bool        isTransactionStmt;
          List       *querytree_list,
                     *querytree_item;
*************** pg_exec_query_string(char *query_string,
*** 675,680 ****
--- 686,693 ----
                   * command ended. -cim 6/1/90
                   */
                  char       *tag = "*ABORT STATE*";
+
+                 set_ps_display(tag);

                  elog(NOTICE, "current transaction is aborted, "
                       "queries ignored until end of transaction block");
*************** pg_exec_query_string(char *query_string,
*** 702,708 ****

          /*
           * OK to analyze and rewrite this query.
!          *
           * Switch to appropriate context for constructing querytrees (again,
           * these must outlive the execution context).
           */
--- 715,737 ----

          /*
           * OK to analyze and rewrite this query.
!          */
!
!         /*
!          * First we set the command-completion tag to the main query
!          * (as opposed to each of the others that may have
!          * been generated by analyze and rewrite)
!          */
!         commandTag = CreateCommandTag(parsetree);
!
! #ifndef FINE_GRAIN_PROFILE
!         /*
!          * Set ps_status to the main query tag
!          */
!         set_ps_display(commandTag);
! #endif
!
!         /*
           * Switch to appropriate context for constructing querytrees (again,
           * these must outlive the execution context).
           */
*************** pg_exec_query_string(char *query_string,
*** 746,752 ****
--- 775,796 ----
                  else if (DebugLvl > 1)
                      elog(DEBUG, "ProcessUtility");

+ #ifdef FINE_GRAIN_PROFILE
+                 set_ps_display(CreateUtilityTag(querytree->utilityStmt));
+ #endif
                  ProcessUtility(querytree->utilityStmt, dest);
+
+                 /*
+                  * FETCHes, as plannable queries, take care of completion
+                  * themselves but we still have to do it for MOVEs as they
+                  * do not send data (and thus, do not send a completion).
+                  */
+                 if (nodeTag(parsetree) == T_FetchStmt)
+                 {
+                     FetchStmt  *stmt = (FetchStmt *) parsetree;
+                     if (!(stmt->ismove))
+                         isQueryStmt = true;  /* Don't do it twice */
+                 }
              }
              else
              {
*************** pg_exec_query_string(char *query_string,
*** 754,759 ****
--- 798,805 ----
                   * process a plannable query.
                   */
                  Plan       *plan;
+
+                 isQueryStmt = true;

                  plan = pg_plan_query(querytree);

*************** pg_exec_query_string(char *query_string,
*** 818,823 ****
--- 864,876 ----

          }                        /* end loop over queries generated from a
                                   * parsetree */
+
+         /*
+          * tell fe/be or whatever that we're done if we only processed
+          * utilities (plannable queries take care of completion themselves)
+          */
+         if (!isQueryStmt)
+             EndCommand(commandTag, dest);
      }                            /* end loop over parsetrees */

      /*
*************** assertTest(int val)
*** 2037,2039 ****
--- 2090,2354 ----
  #endif

  #endif
+
+
+ /* ----------------------------------------------------------------
+  *        CreateCommandTag
+  *
+  *        utility to get a string representation of the
+  *        command operation.
+  * ----------------------------------------------------------------
+  */
+ static char *
+ CreateCommandTag(Node *parsetree)
+ {
+     char       *tag = NULL;
+
+     switch (nodeTag(parsetree))
+     {
+         case T_InsertStmt:
+             tag = "INSERT";
+             break;
+
+         case T_DeleteStmt:
+             tag = "DELETE";
+             break;
+
+         case T_UpdateStmt:
+             tag = "UPDATE";
+             break;
+
+         case T_SelectStmt:
+             tag = "SELECT";
+             break;
+
+         case T_TransactionStmt:
+             {
+                 TransactionStmt *stmt = (TransactionStmt *) parsetree;
+
+                 switch (stmt->command)
+                 {
+                     case BEGIN_TRANS:
+                         tag = "BEGIN";
+                         break;
+
+                     case COMMIT:
+                         tag = "COMMIT";
+                         break;
+
+                     case ROLLBACK:
+                         tag = "ROLLBACK";
+                         break;
+                 }
+             }
+             break;
+
+         case T_ClosePortalStmt:
+             tag = "CLOSE";
+             break;
+
+         case T_FetchStmt:
+             {
+                 FetchStmt  *stmt = (FetchStmt *) parsetree;
+                 tag = (stmt->ismove) ? "MOVE" : "FETCH";
+             }
+             break;
+
+         case T_CreateStmt:
+             tag = "CREATE";
+             break;
+
+         case T_DropStmt:
+             tag = "DROP";
+             break;
+
+         case T_TruncateStmt:
+             tag = "TRUNCATE";
+             break;
+
+         case T_CommentStmt:
+             tag = "COMMENT";
+             break;
+
+         case T_CopyStmt:
+             tag = "COPY";
+             break;
+
+         case T_RenameStmt:
+             tag = "ALTER";
+             break;
+
+         case T_AlterTableStmt:
+             tag = "ALTER";
+             break;
+
+         case T_GrantStmt:
+             {
+                 GrantStmt  *stmt = (GrantStmt *) parsetree;
+                 tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
+             }
+             break;
+
+         case T_DefineStmt:
+             tag = "CREATE";
+             break;
+
+         case T_ViewStmt:        /* CREATE VIEW */
+             tag = "CREATE";
+             break;
+
+         case T_ProcedureStmt:    /* CREATE FUNCTION */
+             tag = "CREATE";
+             break;
+
+         case T_IndexStmt:        /* CREATE INDEX */
+             tag = "CREATE";
+             break;
+
+         case T_RuleStmt:        /* CREATE RULE */
+             tag = "CREATE";
+             break;
+
+         case T_CreateSeqStmt:
+             tag = "CREATE";
+             break;
+
+         case T_RemoveAggrStmt:
+             tag = "DROP";
+             break;
+
+         case T_RemoveFuncStmt:
+             tag = "DROP";
+             break;
+
+         case T_RemoveOperStmt:
+             tag = "DROP";
+             break;
+
+         case T_VersionStmt:
+             tag = "CREATE VERSION";
+             break;
+
+         case T_CreatedbStmt:
+             tag = "CREATE DATABASE";
+             break;
+
+         case T_DropdbStmt:
+             tag = "DROP DATABASE";
+             break;
+
+         case T_NotifyStmt:
+             tag = "NOTIFY";
+             break;
+
+         case T_ListenStmt:
+             tag = "LISTEN";
+             break;
+
+         case T_UnlistenStmt:
+             tag = "UNLISTEN";
+             break;
+
+         case T_LoadStmt:
+             tag = "LOAD";
+             break;
+
+         case T_ClusterStmt:
+             tag = "CLUSTER";
+             break;
+
+         case T_VacuumStmt:
+             if (((VacuumStmt *) parsetree)->vacuum)
+                 tag = "VACUUM";
+             else
+                 tag = "ANALYZE";
+             break;
+
+         case T_ExplainStmt:
+             tag = "EXPLAIN";
+             break;
+
+ #ifdef NOT_USED
+         case T_RecipeStmt:
+             tag = "EXECUTE RECIPE";
+             break;
+ #endif
+
+         case T_VariableSetStmt:
+             tag = "SET VARIABLE";
+             break;
+
+         case T_VariableShowStmt:
+             tag = "SHOW VARIABLE";
+             break;
+
+         case T_VariableResetStmt:
+             tag = "RESET VARIABLE";
+             break;
+
+         case T_CreateTrigStmt:
+             tag = "CREATE";
+             break;
+
+         case T_DropTrigStmt:
+             tag = "DROP";
+             break;
+
+         case T_CreatePLangStmt:
+             tag = "CREATE";
+             break;
+
+         case T_DropPLangStmt:
+             tag = "DROP";
+             break;
+
+         case T_CreateUserStmt:
+             tag = "CREATE USER";
+             break;
+
+         case T_AlterUserStmt:
+             tag = "ALTER USER";
+             break;
+
+         case T_DropUserStmt:
+             tag = "DROP USER";
+             break;
+
+         case T_LockStmt:
+             tag = "LOCK TABLE";
+             break;
+
+         case T_ConstraintsSetStmt:
+             tag = "SET CONSTRAINTS";
+             break;
+
+         case T_CreateGroupStmt:
+             tag = "CREATE GROUP";
+             break;
+
+         case T_AlterGroupStmt:
+             tag = "ALTER GROUP";
+             break;
+
+         case T_DropGroupStmt:
+             tag = "DROP GROUP";
+             break;
+
+         case T_CheckPointStmt:
+             tag = "CHECKPOINT";
+             break;
+
+         case T_ReindexStmt:
+             tag = "REINDEX";
+             break;
+
+         default:
+             elog(DEBUG, "CreateUtilityTag: unknown utility operation type %d",
+                  nodeTag(parsetree));
+             tag = "???";
+             break;
+     }
+
+     return tag;
+ }
+
Index: src/backend/tcop/pquery.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/tcop/pquery.c,v
retrieving revision 1.46
diff -c -p -r1.46 pquery.c
*** src/backend/tcop/pquery.c    2001/10/25 05:49:43    1.46
--- src/backend/tcop/pquery.c    2002/02/26 03:33:47
*************** ProcessQuery(Query *parsetree,
*** 180,186 ****
      EState       *state;
      TupleDesc    attinfo;

!     set_ps_display(tag = CreateOperationTag(operation));

      /*
       * initialize portal/into relation status
--- 180,186 ----
      EState       *state;
      TupleDesc    attinfo;

!     tag = CreateOperationTag(operation);

      /*
       * initialize portal/into relation status
Index: src/backend/tcop/utility.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/tcop/utility.c,v
retrieving revision 1.126
diff -c -p -r1.126 utility.c
*** src/backend/tcop/utility.c    2002/02/24 20:20:20    1.126
--- src/backend/tcop/utility.c    2002/02/26 03:33:47
***************
*** 44,50 ****
  #include "rewrite/rewriteRemove.h"
  #include "tcop/utility.h"
  #include "utils/acl.h"
- #include "utils/ps_status.h"
  #include "utils/syscache.h"
  #include "utils/temprel.h"
  #include "access/xlog.h"
--- 44,49 ----
*************** void
*** 138,144 ****
  ProcessUtility(Node *parsetree,
                 CommandDest dest)
  {
-     char       *commandTag = NULL;
      char       *relname;
      char       *relationName;

--- 137,142 ----
*************** ProcessUtility(Node *parsetree,
*** 155,171 ****
                  switch (stmt->command)
                  {
                      case BEGIN_TRANS:
-                         set_ps_display(commandTag = "BEGIN");
                          BeginTransactionBlock();
                          break;

                      case COMMIT:
-                         set_ps_display(commandTag = "COMMIT");
                          EndTransactionBlock();
                          break;

                      case ROLLBACK:
-                         set_ps_display(commandTag = "ROLLBACK");
                          UserAbortTransactionBlock();
                          break;
                  }
--- 153,166 ----
*************** ProcessUtility(Node *parsetree,
*** 180,187 ****
              {
                  ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;

-                 set_ps_display(commandTag = "CLOSE");
-
                  PerformPortalClose(stmt->portalname, dest);
              }
              break;
--- 175,180 ----
*************** ProcessUtility(Node *parsetree,
*** 193,200 ****
                  bool        forward;
                  int            count;

-                 set_ps_display(commandTag = (stmt->ismove) ? "MOVE" : "FETCH");
-
                  SetQuerySnapshot();

                  forward = (bool) (stmt->direction == FORWARD);
--- 186,191 ----
*************** ProcessUtility(Node *parsetree,
*** 204,210 ****
                   */

                  count = stmt->howMany;
!                 PerformPortalFetch(portalName, forward, count, commandTag,
                                     (stmt->ismove) ? None : dest);        /* /dev/null for MOVE */
              }
              break;
--- 195,201 ----
                   */

                  count = stmt->howMany;
!                 PerformPortalFetch(portalName, forward, count,(stmt->ismove) ? "MOVE" : "FETCH",
                                     (stmt->ismove) ? None : dest);        /* /dev/null for MOVE */
              }
              break;
*************** ProcessUtility(Node *parsetree,
*** 215,222 ****
               *
               */
          case T_CreateStmt:
-             set_ps_display(commandTag = "CREATE");
-
              DefineRelation((CreateStmt *) parsetree, RELKIND_RELATION);

              /*
--- 206,211 ----
*************** ProcessUtility(Node *parsetree,
*** 234,241 ****
                  List       *args = stmt->names;
                  List       *arg;

-                 set_ps_display(commandTag = "DROP");
-
                  foreach(arg, args)
                  {
                      relname = strVal(lfirst(arg));
--- 223,228 ----
*************** ProcessUtility(Node *parsetree,
*** 296,303 ****
              {
                  Relation    rel;

-                 set_ps_display(commandTag = "TRUNCATE");
-
                  relname = ((TruncateStmt *) parsetree)->relName;
                  if (!allowSystemTableMods && IsSystemRelationName(relname))
                      elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
--- 283,288 ----
*************** ProcessUtility(Node *parsetree,
*** 325,332 ****

                  statement = ((CommentStmt *) parsetree);

-                 set_ps_display(commandTag = "COMMENT");
-
                  CommentObject(statement->objtype, statement->objname,
                                statement->objproperty, statement->objlist,
                                statement->comment);
--- 310,315 ----
*************** ProcessUtility(Node *parsetree,
*** 337,344 ****
              {
                  CopyStmt   *stmt = (CopyStmt *) parsetree;

-                 set_ps_display(commandTag = "COPY");
-
                  if (stmt->direction != FROM)
                      SetQuerySnapshot();

--- 320,325 ----
*************** ProcessUtility(Node *parsetree,
*** 365,372 ****
              {
                  RenameStmt *stmt = (RenameStmt *) parsetree;

-                 set_ps_display(commandTag = "ALTER");
-
                  relname = stmt->relname;
                  if (!allowSystemTableMods && IsSystemRelationName(relname))
                      elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
--- 346,351 ----
*************** ProcessUtility(Node *parsetree,
*** 413,420 ****
              {
                  AlterTableStmt *stmt = (AlterTableStmt *) parsetree;

-                 set_ps_display(commandTag = "ALTER");
-
                  /*
                   * Some or all of these functions are recursive to cover
                   * inherited things, so permission checks are done there.
--- 392,397 ----
*************** ProcessUtility(Node *parsetree,
*** 475,483 ****
              {
                  GrantStmt  *stmt = (GrantStmt *) parsetree;

-                 commandTag = stmt->is_grant ? "GRANT" : "REVOKE";
-                 set_ps_display(commandTag);
-
                  ExecuteGrantStmt(stmt);
              }
              break;
--- 452,457 ----
*************** ProcessUtility(Node *parsetree,
*** 491,498 ****
              {
                  DefineStmt *stmt = (DefineStmt *) parsetree;

-                 set_ps_display(commandTag = "CREATE");
-
                  switch (stmt->defType)
                  {
                      case OPERATOR:
--- 465,470 ----
*************** ProcessUtility(Node *parsetree,
*** 514,528 ****
              {
                  ViewStmt   *stmt = (ViewStmt *) parsetree;

-                 set_ps_display(commandTag = "CREATE");
-
                  DefineView(stmt->viewname, stmt->query);        /* retrieve parsetree */
              }
              break;

          case T_ProcedureStmt:    /* CREATE FUNCTION */
-             set_ps_display(commandTag = "CREATE");
-
              CreateFunction((ProcedureStmt *) parsetree);
              break;

--- 486,496 ----
*************** ProcessUtility(Node *parsetree,
*** 530,537 ****
              {
                  IndexStmt  *stmt = (IndexStmt *) parsetree;

-                 set_ps_display(commandTag = "CREATE");
-
                  relname = stmt->relname;
                  if (!allowSystemTableMods && IsSystemRelationName(relname))
                      elog(ERROR, "CREATE INDEX: relation \"%s\" is a system catalog",
--- 498,503 ----
*************** ProcessUtility(Node *parsetree,
*** 559,573 ****
                  aclcheck_result = pg_aclcheck(relname, GetUserId(), ACL_RULE);
                  if (aclcheck_result != ACLCHECK_OK)
                      elog(ERROR, "%s: %s", relname, aclcheck_error_strings[aclcheck_result]);
-                 set_ps_display(commandTag = "CREATE");

                  DefineQueryRewrite(stmt);
              }
              break;

          case T_CreateSeqStmt:
-             set_ps_display(commandTag = "CREATE");
-
              DefineSequence((CreateSeqStmt *) parsetree);
              break;

--- 525,536 ----
*************** ProcessUtility(Node *parsetree,
*** 576,583 ****
                  RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
                  char       *typename = (char *) NULL;

-                 set_ps_display(commandTag = "DROP");
-
                  if (stmt->aggtype != NULL)
                      typename = TypeNameToInternalName((TypeName *) stmt->aggtype);

--- 539,544 ----
*************** ProcessUtility(Node *parsetree,
*** 589,596 ****
              {
                  RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;

-                 set_ps_display(commandTag = "DROP");
-
                  RemoveFunction(stmt->funcname, stmt->args);
              }
              break;
--- 550,555 ----
*************** ProcessUtility(Node *parsetree,
*** 603,610 ****
                  char       *typename1 = (char *) NULL;
                  char       *typename2 = (char *) NULL;

-                 set_ps_display(commandTag = "DROP");
-
                  if (typenode1 != NULL)
                      typename1 = TypeNameToInternalName(typenode1);
                  if (typenode2 != NULL)
--- 562,567 ----
*************** ProcessUtility(Node *parsetree,
*** 622,629 ****
              {
                  CreatedbStmt *stmt = (CreatedbStmt *) parsetree;

-                 set_ps_display(commandTag = "CREATE DATABASE");
-
                  createdb(stmt->dbname, stmt->dbowner,
                           stmt->dbpath, stmt->dbtemplate,
                           stmt->encoding);
--- 579,584 ----
*************** ProcessUtility(Node *parsetree,
*** 634,641 ****
              {
                  DropdbStmt *stmt = (DropdbStmt *) parsetree;

-                 set_ps_display(commandTag = "DROP DATABASE");
-
                  dropdb(stmt->dbname);
              }
              break;
--- 589,594 ----
*************** ProcessUtility(Node *parsetree,
*** 645,652 ****
              {
                  NotifyStmt *stmt = (NotifyStmt *) parsetree;

-                 set_ps_display(commandTag = "NOTIFY");
-
                  Async_Notify(stmt->relname);
              }
              break;
--- 598,603 ----
*************** ProcessUtility(Node *parsetree,
*** 655,662 ****
              {
                  ListenStmt *stmt = (ListenStmt *) parsetree;

-                 set_ps_display(commandTag = "LISTEN");
-
                  Async_Listen(stmt->relname, MyProcPid);
              }
              break;
--- 606,611 ----
*************** ProcessUtility(Node *parsetree,
*** 665,672 ****
              {
                  UnlistenStmt *stmt = (UnlistenStmt *) parsetree;

-                 set_ps_display(commandTag = "UNLISTEN");
-
                  Async_Unlisten(stmt->relname, MyProcPid);
              }
              break;
--- 614,619 ----
*************** ProcessUtility(Node *parsetree,
*** 679,686 ****
              {
                  LoadStmt   *stmt = (LoadStmt *) parsetree;

-                 set_ps_display(commandTag = "LOAD");
-
                  closeAllVfds(); /* probably not necessary... */
                  load_file(stmt->filename);
              }
--- 626,631 ----
*************** ProcessUtility(Node *parsetree,
*** 690,697 ****
              {
                  ClusterStmt *stmt = (ClusterStmt *) parsetree;

-                 set_ps_display(commandTag = "CLUSTER");
-
                  relname = stmt->relname;
                  if (IsSystemRelationName(relname))
                      elog(ERROR, "CLUSTER: relation \"%s\" is a system catalog",
--- 635,640 ----
*************** ProcessUtility(Node *parsetree,
*** 704,715 ****
              break;

          case T_VacuumStmt:
-             if (((VacuumStmt *) parsetree)->vacuum)
-                 commandTag = "VACUUM";
-             else
-                 commandTag = "ANALYZE";
-             set_ps_display(commandTag);
-
              vacuum((VacuumStmt *) parsetree);
              break;

--- 647,652 ----
*************** ProcessUtility(Node *parsetree,
*** 717,724 ****
              {
                  ExplainStmt *stmt = (ExplainStmt *) parsetree;

-                 set_ps_display(commandTag = "EXPLAIN");
-
                  ExplainQuery(stmt->query, stmt->verbose, stmt->analyze, dest);
              }
              break;
--- 654,659 ----
*************** ProcessUtility(Node *parsetree,
*** 732,739 ****
              {
                  RecipeStmt *stmt = (RecipeStmt *) parsetree;

-                 set_ps_display(commandTag = "EXECUTE RECIPE");
-
                  beginRecipe(stmt);
              }
              break;
--- 667,672 ----
*************** ProcessUtility(Node *parsetree,
*** 747,753 ****
                  VariableSetStmt *n = (VariableSetStmt *) parsetree;

                  SetPGVariable(n->name, n->args);
-                 set_ps_display(commandTag = "SET VARIABLE");
              }
              break;

--- 680,685 ----
*************** ProcessUtility(Node *parsetree,
*** 756,762 ****
                  VariableShowStmt *n = (VariableShowStmt *) parsetree;

                  GetPGVariable(n->name);
-                 set_ps_display(commandTag = "SHOW VARIABLE");
              }
              break;

--- 688,693 ----
*************** ProcessUtility(Node *parsetree,
*** 765,771 ****
                  VariableResetStmt *n = (VariableResetStmt *) parsetree;

                  ResetPGVariable(n->name);
-                 set_ps_display(commandTag = "RESET VARIABLE");
              }
              break;

--- 696,701 ----
*************** ProcessUtility(Node *parsetree,
*** 773,786 ****
               * ******************************** TRIGGER statements *******************************
               */
          case T_CreateTrigStmt:
-             set_ps_display(commandTag = "CREATE");
-
              CreateTrigger((CreateTrigStmt *) parsetree);
              break;

          case T_DropTrigStmt:
-             set_ps_display(commandTag = "DROP");
-
              DropTrigger((DropTrigStmt *) parsetree);
              break;

--- 703,712 ----
*************** ProcessUtility(Node *parsetree,
*** 788,801 ****
               * ************* PROCEDURAL LANGUAGE statements *****************
               */
          case T_CreatePLangStmt:
-             set_ps_display(commandTag = "CREATE");
-
              CreateProceduralLanguage((CreatePLangStmt *) parsetree);
              break;

          case T_DropPLangStmt:
-             set_ps_display(commandTag = "DROP");
-
              DropProceduralLanguage((DropPLangStmt *) parsetree);
              break;

--- 714,723 ----
*************** ProcessUtility(Node *parsetree,
*** 804,860 ****
               *
               */
          case T_CreateUserStmt:
-             set_ps_display(commandTag = "CREATE USER");
-
              CreateUser((CreateUserStmt *) parsetree);
              break;

          case T_AlterUserStmt:
-             set_ps_display(commandTag = "ALTER USER");
-
              AlterUser((AlterUserStmt *) parsetree);
              break;

          case T_DropUserStmt:
-             set_ps_display(commandTag = "DROP USER");
-
              DropUser((DropUserStmt *) parsetree);
              break;

          case T_LockStmt:
-             set_ps_display(commandTag = "LOCK TABLE");
-
              LockTableCommand((LockStmt *) parsetree);
              break;

          case T_ConstraintsSetStmt:
-             set_ps_display(commandTag = "SET CONSTRAINTS");
-
              DeferredTriggerSetState((ConstraintsSetStmt *) parsetree);
              break;

          case T_CreateGroupStmt:
-             set_ps_display(commandTag = "CREATE GROUP");
-
              CreateGroup((CreateGroupStmt *) parsetree);
              break;

          case T_AlterGroupStmt:
-             set_ps_display(commandTag = "ALTER GROUP");
-
              AlterGroup((AlterGroupStmt *) parsetree, "ALTER GROUP");
              break;

          case T_DropGroupStmt:
-             set_ps_display(commandTag = "DROP GROUP");
-
              DropGroup((DropGroupStmt *) parsetree);
              break;

          case T_CheckPointStmt:
              {
-                 set_ps_display(commandTag = "CHECKPOINT");
-
                  if (!superuser())
                      elog(ERROR, "permission denied");
                  CreateCheckPoint(false);
--- 726,764 ----
*************** ProcessUtility(Node *parsetree,
*** 865,872 ****
              {
                  ReindexStmt *stmt = (ReindexStmt *) parsetree;

-                 set_ps_display(commandTag = "REINDEX");
-
                  switch (stmt->reindexType)
                  {
                      case INDEX:
--- 769,774 ----
*************** ProcessUtility(Node *parsetree,
*** 914,920 ****
      }

      /*
!      * tell fe/be or whatever that we're done.
       */
!     EndCommand(commandTag, dest);
  }
--- 816,823 ----
      }

      /*
!      * Note: the "end-of-command" tag is to be sent by the caller,
!      * if appropriate.
       */
!     return;
  }

Re: Fix issuing of multiple command completions per command

From
Tom Lane
Date:
Fernando Nasser <fnasser@redhat.com> writes:
> Oops!  I am attaching the corrected patch. Sorry.

Thanks, that'll save me some work.

> We should ask him to try the patch.  Coax says he is not very
> concerned with the CommandInfo -- the application needs the
> right CommandTag.

No, he needs LASTOID to be right too --- and that's coming from
CommandInfo.

> And remember that there is still the 'Z'.  This completion will close
> the
> flow of data packets that may have been sent.  It is good for the
> async processing as well as the application may process the data
> while waiting for the 'Z' (otherwise it will not know that all the
> data has arrived).

But 'Z' only comes after we have processed all the queries in a query
string.  The 'C' message should come out when, and only when, we have
finished processing a particular query within the string --- and that
includes everything that stems from rewriting that query, IMHO.

> The way I see it is that this is a palnnable query thing where
> Begin and End calls should open and close the flow of data.

> For plannable queries we have:

> BeginCommand()
> <send data>
> EndCommand()

Actually, the destination receiver setup and teardown calls are the
things that open and close the flow of data.  BeginCommand/EndCommand
are vestigial things given the destination receiver mechanism --- which
was something that didn't exist originally; I added it a couple years
ago to clean up the interfaces to printtup.c.  It looks like it's now
time to take the next step in cleaning up that stuff.

What I am thinking of doing right now is applying your patch together
with a modification to remove EndCommand and UpdateCommandInfo.
Instead of those two routines, ProcessQuery should construct a
command tag string and return same to its caller.  Then
pg_exec_query_string can save the command tag and send it to the
client at the right time (namely, after it's processed the whole
querytree list).

As a second step I'd like to get rid of BeginCommand and fold its
functionality into the destination receiver setup routines.  However,
that's not a bug fix but just code beautification.

> But consider also that the CommandInfo that corresponds to the data
> stream was already sent.  And it is zero anyway if more than one row
> is affected, isn't it?  Maybe it is not that broken after all
> (with tis patch applied, I mean).

Yes, it's pretty broken.  Consider an INSERT command that itself inserts
only one row, but there are rules that add additional inserts or
updates.  We must return the CommandInfo from the specified insert
itself, not the other actions.

            regards, tom lane

Re: Fix issuing of multiple command completions per command

From
Fernando Nasser
Date:
Fernando Nasser wrote:
>
> Tom Lane wrote:
> >
> > 2. EndCommand should *not* be issued until we have completed all the
> > actions of the (rewritten) query.  Moving EndCommand into
> > PerformPortalFetch, as you did here, is exactly the wrong direction.
> > What we need is for EndCommand to be executed from pg_exec_query_string.
> > That means that the tag auxiliary info (currently set up by
> > UpdateCommandInfo) has to be passed out from ProcessQuery to
> > pg_exec_query_string, which is the only place that can know when to send
> > the command completion notice back to the client.
> >
>
> That was my first impression, but after I looked more deeply into it
> I figured that this is the right thing to do (I explain it below).
>
> And remember that there is still the 'Z'.  This completion will close
> the
> flow of data packets that may have been sent.  It is good for the
> async processing as well as the application may process the data
> while waiting for the 'Z' (otherwise it will not know that all the
> data has arrived).
>

OK, I take this back.  I see your point now.  We don't want async
processing to resume before all the extras_after are processed
because we want all these side effects to be perceived by whoever
is doing the processing.  So it doesn't matter that the data has
already arrived or not -- one has to wait.

It seems a small change though.  I believe I can safely let
pg_exec_query_string do all the EndCommand() calls.  I would just
have to add your suggested change to get the CommandInfo as well.
Let me know if you want me to try that.




--
Fernando Nasser
Red Hat Canada Ltd.                     E-Mail:  fnasser@redhat.com
2323 Yonge Street, Suite #300
Toronto, Ontario   M4P 2C9

Re: Fix issuing of multiple command completions per command

From
Tom Lane
Date:
Fernando Nasser <fnasser@redhat.com> writes:
> It seems a small change though.  I believe I can safely let
> pg_exec_query_string do all the EndCommand() calls.  I would just
> have to add your suggested change to get the CommandInfo as well.
> Let me know if you want me to try that.

I have already committed changes to call EndCommand from
pg_exec_query_string, and am currently working on cleaning up
BeginCommand as well.  By the end of the day, both BeginCommand and
EndCommand will be called once per received query (raw parse tree).

BeginCommand doesn't actually do anything in this formulation
(all its functions have been pushed into DestReceiver setup routines)
but it seems appropriate to have it for symmetry.  Somebody might want
it someday.

            regards, tom lane