Re: Fix issuing of multiple command completions per command

From: Bruce Momjian <pgman(at)candle(dot)pha(dot)pa(dot)us>
To: Fernando Nasser <fnasser(at)redhat(dot)com>
Cc: pgsql-patches(at)postgresql(dot)org
Subject: Re: Fix issuing of multiple command completions per command
Date: 2002-02-26 04:11:59
Message-ID: 200202260411.g1Q4BxZ09040@candle.pha.pa.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-patches


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(at)redhat(dot)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(at)candle(dot)pha(dot)pa(dot)us | (610) 853-3000
+ If your life is a hard drive, | 830 Blythe Avenue
+ Christ can be your backup. | Drexel Hill, Pennsylvania 19026

In response to

Browse pgsql-patches by date

  From Date Subject
Next Message Janardhana Reddy 2002-02-26 07:03:54 Re: WAL Performance Improvements
Previous Message Rod Taylor 2002-02-26 03:08:49 Re: Basic DOMAIN Support