Skip to content
Snippets Groups Projects
postgres.c 47.29 KiB
/*-------------------------------------------------------------------------
 *
 * postgres.c--
 *    POSTGRES C Backend Interface
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.39 1997/08/06 05:38:35 momjian Exp $
 *
 * NOTES
 *    this is the "main" module of the postgres backend and
 *    hence the main module of the "traffic cop".
 *
 *-------------------------------------------------------------------------
 */

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <setjmp.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/param.h>          /* for MAXHOSTNAMELEN on most */
#ifndef MAXHOSTNAMELEN
#include <netdb.h>              /* for MAXHOSTNAMELEN on some */
#endif
#ifndef MAXHOSTNAMELEN		/* for MAXHOSTNAMELEN under sco3.2v5.0.2 */
#include <sys/socket.h>
#endif
#include <errno.h>
#ifdef aix
#include <sys/select.h>
#endif /* aix */


#include "postgres.h"
#include "miscadmin.h"
#include "catalog/catname.h"
#include "access/xact.h"

#include "lib/dllist.h"

#include "parser/catalog_utils.h"
#include "parser/parse_query.h"     /* for MakeTimeRange() */
#include "commands/async.h"
#include "tcop/tcopprot.h"          /* where declarations for this file go */
#include "optimizer/planner.h"

#include "tcop/tcopprot.h"
#include "tcop/tcopdebug.h"

#include "executor/execdebug.h"
#include "executor/executor.h"
#if FALSE
#include "nodes/relation.h"
#endif
#include "nodes/print.h"

#include "optimizer/cost.h"
#include "optimizer/planner.h"
#if 0
#include "optimizer/xfunc.h"
#endif
#include "optimizer/prep.h"
#if FALSE
#include "nodes/plannodes.h"
#endif

#include "storage/bufmgr.h"
#include "fmgr.h"
#include "utils/palloc.h"
#include "utils/rel.h"

#include "nodes/pg_list.h"
#include "tcop/dest.h"
#if FALSE
#include "nodes/memnodes.h"
#endif
#include "utils/mcxt.h"
#include "tcop/pquery.h"
#include "tcop/utility.h"
#include "tcop/fastpath.h"

#include "libpq/libpq.h"
#include "libpq/pqsignal.h"
#include "rewrite/rewriteHandler.h" /* for QueryRewrite() */

/* ----------------
 *      global variables
 * ----------------
 */
static bool     DebugPrintQuery = false;
static bool     DebugPrintPlan = false;
static bool     DebugPrintParse = false;
static bool     DebugPrintRewrittenParsetree = false;
/*static bool   EnableRewrite = true; , never changes why have it*/
CommandDest whereToSendOutput;

#ifdef LOCK_MGR_DEBUG
extern int      lockDebug;
#endif
extern int      lockingOff;
extern int      NBuffers;

int     fsyncOff = 0;
int	SortMem = 512;

int     dontExecute = 0;
static int      ShowStats;
static bool     IsEmptyQuery = false;

char            relname[80];            /* current relation name */

#if defined(nextstep)
jmp_buf    Warn_restart;
#define sigsetjmp(x,y)  setjmp(x)
#define siglongjmp longjmp
#else
sigjmp_buf Warn_restart;
#endif /* defined(nextstep) */
int InWarn;

extern int      NBuffers;

static int      EchoQuery = 0;          /* default don't echo */
time_t          tim;
char            pg_pathname[256];
static int      ShowParserStats;
static int      ShowPlannerStats;
int             ShowExecutorStats;
FILE            *StatFp;

typedef struct frontend {
  bool  fn_connected;
  Port  fn_port;
  FILE  *fn_Pfin;  /* the input fd */
  FILE  *fn_Pfout; /* the output fd */
  bool  fn_done; /* set after the frontend closes its connection */
} FrontEnd;

static Dllist* frontendList;

/* ----------------
 *      people who want to use EOF should #define DONTUSENEWLINE in
 *      tcop/tcopdebug.h
 * ----------------
 */
#ifndef TCOP_DONTUSENEWLINE
int UseNewLine = 1;  /* Use newlines query delimiters (the default) */
#else
int UseNewLine = 0;  /* Use EOF as query delimiters */
#endif /* TCOP_DONTUSENEWLINE */

/* ----------------
 *      bushy tree plan flag: if true planner will generate bushy-tree
 *      plans
 * ----------------
 */
int BushyPlanFlag = 0; /* default to false -- consider only left-deep trees */

/*
** Flags for expensive function optimization -- JMH 3/9/92
*/
int XfuncMode = 0;

/*
 * ----------------
 *   Note: _exec_repeat_ defaults to 1 but may be changed
 *         by a DEBUG command.   If you set this to a large
 *         number N, run a single query, and then set it
 *         back to 1 and run N queries, you can get an idea
 *         of how much time is being spent in the parser and
 *         planner b/c in the first case this overhead only
 *         happens once.  -cim 6/9/91
 * ----------------
*/
int _exec_repeat_ = 1;

/* ----------------------------------------------------------------
 *      decls for routines only used in this file
 * ----------------------------------------------------------------
 */
static char InteractiveBackend(char *inBuf);
static char SocketBackend(char *inBuf, bool multiplexedBackend);
static char ReadCommand(char *inBuf, bool multiplexedBackend);


/* ----------------------------------------------------------------
 *      routines to obtain user input
 * ----------------------------------------------------------------
 */

/* ----------------
 *  InteractiveBackend() is called for user interactive connections
 *  the string entered by the user is placed in its parameter inBuf.
 * ----------------
 */

static char
InteractiveBackend(char *inBuf)
{
    char *stuff = inBuf;                /* current place in input buffer */
    int c;                              /* character read from getc() */
    bool end = false;                   /* end-of-input flag */
    bool backslashSeen = false;         /* have we seen a \ ? */
    
    /* ----------------
     *  display a prompt and obtain input from the user
     * ----------------
     */
    printf("> ");
    
    for (;;) {
        if (UseNewLine) {
            /* ----------------
             *  if we are using \n as a delimiter, then read
             *  characters until the \n.
             * ----------------
             */
            while ( (c = getc(stdin)) != EOF) {
                if (c == '\n') {
                    if (backslashSeen) {
                        stuff--;
                        continue;
                    } else {
                        /* keep the newline character */
                        *stuff++ = '\n';
                        *stuff++ = '\0';
                        break;
                    }
                } else if (c == '\\')
                    backslashSeen = true;
                else
                    backslashSeen = false;
                
                *stuff++ = (char)c;
            }
            
            if (c == EOF)
                end = true;
        } else {
            /* ----------------
             *  otherwise read characters until EOF.
             * ----------------
             */
            while ( (c = getc(stdin)) != EOF )
                *stuff++ = (char)c;
            
            if ( stuff == inBuf )
                end = true;
        }
        
        if (end) {
            if (!Quiet) puts("EOF");
            IsEmptyQuery = true;
            exitpg(0);
        }
        
        /* ----------------
         *  otherwise we have a user query so process it.
         * ----------------
         */
        break;
    }
    
    /* ----------------
     *  if the query echo flag was given, print the query..
     * ----------------
     */
    if (EchoQuery)
        printf("query is: %s\n", inBuf);
    
    return('Q');
}
/* ----------------
 *  SocketBackend()     Is called for frontend-backend connections
 *
 *  If the input is a query (case 'Q') then the string entered by
 *  the user is placed in its parameter inBuf.
 *
 *  If the input is a fastpath function call (case 'F') then
 *  the function call is processed in HandleFunctionRequest().
 *  (now called from PostgresMain())
 * ----------------
 */

static char
SocketBackend(char *inBuf, bool multiplexedBackend)
{
    char qtype[2];
    char result = '\0';
    
    /* ----------------
     *  get input from the frontend
     * ----------------
     */
    (void) strcpy(qtype, "?");
    if (pq_getnchar(qtype,0,1) == EOF) {
        /* ------------
         *  when front-end applications quits/dies
         * ------------
         */
        if (multiplexedBackend) {
            return 'X';
        }
        else
            exitpg(0);
    }
    
    switch(*qtype) {
        /* ----------------
         *  'Q': user entered a query
         * ----------------
         */
    case 'Q':
        pq_getstr(inBuf, MAX_PARSE_BUFFER);
        result = 'Q';
        break;
        
        /* ----------------
         *  'F':  calling user/system functions
         * ----------------
         */
    case 'F':   
        pq_getstr(inBuf, MAX_PARSE_BUFFER);/* ignore the rest of the line */
        result = 'F';
        break;
        
        /* ----------------
         *  'X':  frontend is exiting
         * ----------------
         */
    case 'X':
        result = 'X';
        break;
        
        /* ----------------
         *  otherwise we got garbage from the frontend.
         *
         *  XXX are we certain that we want to do an elog(FATAL) here?
         *      -cim 1/24/90
         * ----------------
         */
    default:
        elog(FATAL, "Socket command type %c unknown\n", *qtype);
        break;
    }
    return result;
}

/* ----------------
 *      ReadCommand reads a command from either the frontend or
 *      standard input, places it in inBuf, and returns a char
 *      representing whether the string is a 'Q'uery or a 'F'astpath
 *      call.
 * ----------------
 */
static char
ReadCommand(char *inBuf, bool multiplexedBackend)
{
    if (IsUnderPostmaster || multiplexedBackend)
        return SocketBackend(inBuf, multiplexedBackend);
    else
        return InteractiveBackend(inBuf);
}

List *
pg_plan(char *query_string,     /* string to execute */
        Oid *typev,             /* argument types */
        int nargs,              /* number of arguments */
        QueryTreeList **queryListP,  /* pointer to the parse trees */
        CommandDest dest)       /* where results should go */
{
    QueryTreeList *querytree_list;
    int i;
    List *plan_list = NIL;
    Plan *plan;
    int j;
    QueryTreeList *new_list; 
    List *rewritten = NIL;
    Query* querytree;

    /* ----------------
     *  (1) parse the request string into a list of parse trees
     * ----------------
     */
    if (ShowParserStats)
        ResetUsage();
    
    querytree_list = parser(query_string, typev, nargs);
    
    if (ShowParserStats) {
        fprintf(stderr, "! Parser Stats:\n");
        ShowUsage();
    }

    /* new_list holds the rewritten queries */
    new_list = (QueryTreeList*)malloc(sizeof(QueryTreeList));
    new_list->len = querytree_list->len;
    new_list->qtrees = (Query**)malloc(new_list->len * sizeof(Query*));

    /* ----------------
     *  (2) rewrite the queries, as necessary     
     * ----------------
     */
    j = 0; /* counter for the new_list, new_list can be longer than
              old list as a result of rewrites */
    for (i=0;i<querytree_list->len;i++) {
        querytree = querytree_list->qtrees[i];
        

        /* don't rewrite utilites */
        if (querytree->commandType == CMD_UTILITY) {
            new_list->qtrees[j++] = querytree;
            continue;
        }
        
        if ( DebugPrintQuery == true ) {
            printf("\n---- \tquery is:\n%s\n",query_string);
            printf("\n");
	    fflush(stdout);
        }
        
        if ( DebugPrintParse == true ) {
            printf("\n---- \tparser outputs :\n");
            nodeDisplay(querytree);
            printf("\n");
        }
        
        /* rewrite queries (retrieve, append, delete, replace) */
        rewritten = QueryRewrite(querytree);
        if (rewritten != NULL) {
          int len, k;
          len = length(rewritten);
          if (len == 1)
            new_list->qtrees[j++] = (Query*)lfirst(rewritten); 
          else {
            /* rewritten queries are longer than original query */
            /* grow the new_list to accommodate */
            new_list->len += len - 1; /* - 1 because originally we 
                                         allocated one space for the query */
            new_list->qtrees = realloc(new_list->qtrees, 
                                       new_list->len * sizeof(Query*));
            for (k=0;k<len;k++)
              new_list->qtrees[j++] = (Query*)nth(k, rewritten);
          }
        }
    }
    
    /* we're done with the original lists, free it */
    free(querytree_list->qtrees);
    free(querytree_list);

    querytree_list = new_list;

    /* ----------------
     * Fix time range quals
     * this _must_ go here, because it must take place after rewrites
     * ( if they take place ) so that time quals are usable by the executor
     *
     * Also, need to frob the range table entries here to plan union
     * queries for archived relations.
     * ----------------
     */
    for (i=0;i<querytree_list->len;i++) {
        List *l;
        List *rt = NULL;

        querytree = querytree_list->qtrees[i];

        /* ----------------
         *  utilities don't have time ranges
         * ----------------
         */
        if (querytree->commandType == CMD_UTILITY)
            continue;
        
        rt = querytree->rtable;
        
        foreach (l, rt) {
            RangeTblEntry *rte = lfirst(l);
            TimeRange *timequal = rte->timeRange;

            if (timequal) {
                int timecode = (rte->timeRange->endDate == NULL)? 0 : 1;

                rte->timeQual = makeTimeRange(rte->timeRange->startDate,
                                              rte->timeRange->endDate,
                                              timecode);
            }else {
                rte->timeQual = NULL;
            }
        }
        
        /* check for archived relations */
        plan_archive(rt);
    }
    
    if (DebugPrintRewrittenParsetree == true) {
        printf("\n---- \tafter rewriting:\n");

        for (i=0; i<querytree_list->len; i++) {
            print(querytree_list->qtrees[i]);
            printf("\n");
        }
    }
    
    for (i=0; i<querytree_list->len;i++) {
        querytree = querytree_list->qtrees[i];
        
        /*
         *  For each query that isn't a utility invocation,
         *  generate a plan.
         */
        
        if (querytree->commandType != CMD_UTILITY) {
            
            if (IsAbortedTransactionBlockState()) {
                /* ----------------
                 *   the EndCommand() stuff is to tell the frontend
                 *   that the command ended. -cim 6/1/90
                 * ----------------
                 */
                char *tag = "*ABORT STATE*";
                EndCommand(tag, dest);
                
                elog(NOTICE, "(transaction aborted): %s",
                     "queries ignored until END");
                
                *queryListP = (QueryTreeList*)NULL;
                return (List*)NULL;
            }
            
            if (ShowPlannerStats) ResetUsage();
            plan = planner(querytree);
            if (ShowPlannerStats) {
                fprintf(stderr, "! Planner Stats:\n");
                ShowUsage();
            }
            plan_list = lappend(plan_list, plan);
#ifdef INDEXSCAN_PATCH
            /* ----------------
             *  Print plan if debugging.
             *  This has been moved here to get debugging output
             *  also for queries in functions.  DZ - 27-8-1996
             * ----------------
             */
            if ( DebugPrintPlan == true ) {
                printf("\n---- \tplan is :\n");
                nodeDisplay(plan);
                printf("\n");
            }
#endif
        }
#ifdef FUNC_UTIL_PATCH
        /*
         * If the command is an utility append a null plan. This is
         * needed to keep the plan_list aligned with the querytree_list
         * or the function executor will crash.  DZ - 30-8-1996
         */
        else {
            plan_list = lappend(plan_list, NULL);
        }
#endif
    }
    
    if (queryListP)
        *queryListP = querytree_list;
    
    return (plan_list);
}

/* ----------------------------------------------------------------
 *      pg_eval()
 *      
 *      Takes a querystring, runs the parser/utilities or
 *      parser/planner/executor over it as necessary
 *      Begin Transaction Should have been called before this
 *      and CommitTransaction After this is called
 *      This is strictly because we do not allow for nested xactions.
 *
 *      NON-OBVIOUS-RESTRICTIONS
 *      this function _MUST_ allocate a new "parsetree" each time, 
 *      since it may be stored in a named portal and should not 
 *      change its value.
 *
 * ----------------------------------------------------------------
 */

void
pg_eval(char *query_string, char **argv, Oid *typev, int nargs)
{
    pg_eval_dest(query_string, argv, typev, nargs, whereToSendOutput);
}

void
pg_eval_dest(char *query_string, /* string to execute */
             char **argv,       /* arguments */
             Oid *typev,        /* argument types */
             int nargs,         /* number of arguments */
             CommandDest dest)  /* where results should go */
{
    List *plan_list; 
    Plan *plan;
    Query *querytree;
    int i,j;
    QueryTreeList *querytree_list;
    
    /* plan the queries */
    plan_list = pg_plan(query_string, typev, nargs, &querytree_list, dest);
    
    /* pg_plan could have failed */
    if (querytree_list == NULL)
        return;

    for (i=0;i<querytree_list->len;i++) {
        querytree = querytree_list->qtrees[i];
        
#ifdef FUNC_UTIL_PATCH
        /*
         * Advance on the plan_list in every case.  Now the plan_list
         * has the same length of the querytree_list.  DZ - 30-8-1996
         */
        plan = (Plan *) lfirst(plan_list);
        plan_list = lnext(plan_list);
#endif
        if (querytree->commandType == CMD_UTILITY) {
            /* ----------------
             *   process utility functions (create, destroy, etc..)
             *
             *   Note: we do not check for the transaction aborted state
             *   because that is done in ProcessUtility.
             * ----------------
             */
            if (! Quiet) {
                time(&tim);
                printf("\tProcessUtility() at %s\n", ctime(&tim));
            }
            
            ProcessUtility(querytree->utilityStmt, dest);
            
        } else {
#ifndef FUNC_UTIL_PATCH
            /*
             * Moved before the if.  DZ - 30-8-1996
             */
            plan = (Plan *) lfirst(plan_list);
            plan_list = lnext(plan_list);
#endif
            
#ifdef INDEXSCAN_PATCH
            /*
             *  Print moved in pg_plan.  DZ - 27-8-1996
             */
#else
            /* ----------------
             *  print plan if debugging
             * ----------------
             */
            if ( DebugPrintPlan == true ) {
                printf("\n---- plan is :\n");
                nodeDisplay(plan);
                printf("\n");
            }
#endif
            
            /* ----------------
             *   execute the plan
             *
             */
            if (ShowExecutorStats)
                ResetUsage();
            
            for (j = 0; j < _exec_repeat_; j++) {
                if (! Quiet) {
                    time(&tim);
                    printf("\tProcessQuery() at %s\n", ctime(&tim));
                }
                ProcessQuery(querytree, plan, argv, typev, nargs, dest);
            }
            
            if (ShowExecutorStats) {
                fprintf(stderr, "! Executor Stats:\n");
                ShowUsage();
            }
        }
        /*
         *  In a query block, we want to increment the command counter
         *  between queries so that the effects of early queries are
         *  visible to subsequent ones.
         */
        
        if (querytree_list)
            CommandCounterIncrement();
    }

    free(querytree_list->qtrees);
    free(querytree_list);
}

/* --------------------------------
 *      signal handler routines used in PostgresMain()
 *
 *      handle_warn() is used to catch kill(getpid(),1) which
 *      occurs when elog(WARN) is called.
 *
 *      quickdie() occurs when signalled by the postmaster, some backend
 *      has bought the farm we need to stop what we're doing and exit.
 *
 *      die() preforms an orderly cleanup via ExitPostgres()
 * --------------------------------
 */

void
handle_warn(SIGNAL_ARGS)
{
    siglongjmp(Warn_restart, 1);
}

void
quickdie(SIGNAL_ARGS)
{
    elog(NOTICE, "I have been signalled by the postmaster.");
    elog(NOTICE, "Some backend process has died unexpectedly and possibly");
    elog(NOTICE, "corrupted shared memory.  The current transaction was");
    elog(NOTICE, "aborted, and I am going to exit.  Please resend the");
    elog(NOTICE, "last query. -- The postgres backend");
    
    /*
     *  DO NOT ExitPostgres(0) -- we're here because shared memory may be
     *  corrupted, so we don't want to flush any shared state to stable
     *  storage.  Just nail the windows shut and get out of town.
     */
    
    exit (0);
}

void
die(SIGNAL_ARGS)
{
    ExitPostgres(0);
}

/* signal handler for floating point exception */
static void
FloatExceptionHandler(SIGNAL_ARGS)
{
   elog(WARN, "floating point exception! the last floating point operation eit\
her exceeded legal ranges or was a divide by zero");
}


static void usage(char* progname)
{
    fprintf(stderr, 
            "Usage: %s [-B nbufs] [-d lvl] ] [-f plantype] \t[-m portno] [\t -o filename]\n",
            progname);
    fprintf(stderr,"\t[-P portno] [-t tracetype] [-x opttype] [-bCEiLFNopQSs] [dbname]\n");
    fprintf(stderr, "    b: consider bushy plan trees during optimization\n");
    fprintf(stderr, "    B: set number of buffers in buffer pool\n");
    fprintf(stderr, "    C: supress version info\n");
    fprintf(stderr, "    d: set debug level\n");
    fprintf(stderr, "    E: echo query before execution\n");
    fprintf(stderr, "    e  turn on European date format\n");
    fprintf(stderr, "    F: turn off fsync\n");
    fprintf(stderr, "    f: forbid plantype generation\n");
    fprintf(stderr, "    i: don't execute the query, just show the plan tree\n");
#ifdef LOCK_MGR_DEBUG
    fprintf(stderr, "    K: set locking debug level [0|1|2]\n");
#endif
    fprintf(stderr, "    L: turn off locking\n");
    fprintf(stderr, "    m: set up a listening backend at portno to support multiple front-ends\n");
    fprintf(stderr, "    M: start as postmaster\n");
    fprintf(stderr, "    N: don't use newline as query delimiter\n");
    fprintf(stderr, "    o: send stdout and stderr to given filename \n");
    fprintf(stderr, "    p: backend started by postmaster\n");
    fprintf(stderr, "    P: set port file descriptor\n");
    fprintf(stderr, "    Q: suppress informational messages\n");
    fprintf(stderr, "    S: assume stable main memory\n");
    fprintf(stderr, "    s: show stats after each query\n");
    fprintf(stderr, "    t: trace component execution times\n");
    fprintf(stderr, "    T: execute all possible plans for each query\n");
    fprintf(stderr, "    x: control expensive function optimization\n");
}

/* ----------------------------------------------------------------
 *      PostgresMain
 *        postgres main loop
 *      all backends, interactive or otherwise start here
 * ----------------------------------------------------------------
 */
int
PostgresMain(int argc, char *argv[])
{
    int    flagC;
    int    flagQ;
    int    flagS;
    int    flagE;
    int    flagEu;
    int    flag;
    
    char   *DBName = NULL; 
    int    errs = 0;
    
    char   firstchar;
    char   parser_input[MAX_PARSE_BUFFER];
    char *userName;
    
    bool   multiplexedBackend;
    char*  hostName;                /* the host name of the backend server */
    char   hostbuf[MAXHOSTNAMELEN];
    int    serverSock;
    int    serverPortnum = 0;
    int    nSelected; /* number of descriptors ready from select(); */
    int    maxFd = 0; /* max file descriptor + 1 */
    fd_set rmask, basemask;
    FrontEnd *newFE, *currentFE = NULL;
    int    numFE = 0; /* keep track of number of active frontends */
    Port   *newPort;
    int    newFd;
    Dlelem *curr;
    int    status;

    extern int    optind;
    extern char   *optarg;
    extern short  DebugLvl;
    
    /* ----------------
     *  register signal handlers.
     * ----------------
     */
    pqsignal(SIGINT, die);

    pqsignal(SIGHUP, die);
    pqsignal(SIGTERM, die);
    pqsignal(SIGPIPE, die);
    pqsignal(SIGUSR1, quickdie);
    pqsignal(SIGUSR2, Async_NotifyHandler);
    pqsignal(SIGFPE, FloatExceptionHandler);
    
    /* --------------------
     *  initialize globals 
     * -------------------
     */
    
    MasterPid = getpid();

    /* ----------------
     *  parse command line arguments
     * ----------------
     */
    flagC = flagQ = flagS = flagE = flagEu = ShowStats = 0;
    ShowParserStats = ShowPlannerStats = ShowExecutorStats = 0;
#ifdef LOCK_MGR_DEBUG
    lockDebug = 0;
#endif

    /* get hostname is either the environment variable PGHOST
       or 'localhost' */
    if (!(hostName = getenv("PGHOST"))) {
        if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
            (void) strcpy(hostbuf, "localhost");
        hostName = hostbuf;
    }

    DataDir = getenv("PGDATA");   /* default */
    multiplexedBackend = false;   /* default */

    while ((flag = getopt(argc, argv, "B:bCD:d:Eef:iK:Lm:MNo:P:pQSst:x:F")) 
           != EOF)
        switch (flag) {
            
        case 'b':
            /* ----------------
             *  set BushyPlanFlag to true.
             * ----------------
             */
            BushyPlanFlag = 1;
            break;
        case 'B':
            /* ----------------
             *  specify the size of buffer pool
             * ----------------
             */
            NBuffers = atoi(optarg);
            break;
            
        case 'C':
            /* ----------------
             *  don't print version string (don't know why this is 'C' --mao)
             * ----------------
             */
            flagC = 1;
            break;
            
        case 'D':   /* PGDATA directory */
            DataDir = optarg;
            
        case 'd':   /* debug level */
            flagQ = 0;
            DebugLvl = (short)atoi(optarg);
            if (DebugLvl > 1)
	        DebugPrintQuery = true;
            if (DebugLvl > 2)
            {
	        DebugPrintParse = true;
		DebugPrintPlan = true;
            	DebugPrintRewrittenParsetree = true;
            }
            break;
            
        case 'E':
            /* ----------------
             *  E - echo the query the user entered
             * ----------------
             */
            flagE = 1;
            break;
            
        case 'e':
            /* --------------------------
             * Use european date formats.
             * --------------------------
             */
            flagEu = 1;
            break;

        case 'F':
            /* --------------------
             *  turn off fsync
             * --------------------
             */
            fsyncOff = 1;
            break;

        case 'f':
            /* -----------------
             *    f - forbid generation of certain plans
             * -----------------
             */
            switch (optarg[0]) {
            case 's': /* seqscan */
                _enable_seqscan_ = false;
                break;
            case 'i': /* indexscan */
                _enable_indexscan_ = false;
                break;
            case 'n': /* nestloop */
                _enable_nestloop_ = false;
                break;
            case 'm': /* mergejoin */
                _enable_mergesort_ = false;
                break;
            case 'h': /* hashjoin */
                _enable_hashjoin_ = false;
                break;
            default:
                errs++;
            }
            break;

        case 'i':
            dontExecute = 1;
            break;
            
        case 'K':
#ifdef LOCK_MGR_DEBUG
            lockDebug = atoi(optarg);
#else
	    fprintf(stderr, "Lock debug not compiled in\n");
#endif
            break;
            
        case 'L':
            /* --------------------
             *  turn off locking
             * --------------------
             */
            lockingOff = 1;
            break;
            
        case 'm':
            /* start up a listening backend that can respond to 
               multiple front-ends.  (Note:  all the front-end connections
               are still connected to a single-threaded backend.  Requests
               are FCFS.  Everything is in one transaction 
               */
            multiplexedBackend = true;
            serverPortnum = atoi(optarg);
            break;
        case 'M':
            exit(PostmasterMain(argc, argv));
            break;
        case 'N':
            /* ----------------
             *  N - Don't use newline as a query delimiter
             * ----------------
             */
            UseNewLine = 0;
            break;
            
        case 'o':
            /* ----------------
             *  o - send output (stdout and stderr) to the given file
             * ----------------
             */
            (void) strncpy(OutputFileName, optarg, MAXPGPATH);
            break;
            
        case 'p':       /* started by postmaster */
            /* ----------------
             *  p - special flag passed if backend was forked
             *      by a postmaster.
             * ----------------
             */
            IsUnderPostmaster = true;
            break;
            
        case 'P':
            /* ----------------
             *  P - Use the passed file descriptor number as the port
             *    on which to communicate with the user.  This is ONLY
             *    useful for debugging when fired up by the postmaster.
             * ----------------
             */
            Portfd = atoi(optarg);
            break;
            
        case 'Q':
            /* ----------------
             *  Q - set Quiet mode (reduce debugging output)
             * ----------------
             */
            flagQ = 1;
            break;

        case 'S':
            /* ----------------
             *  S - amount of sort memory to use in 1k bytes
             * ----------------
             */
            SortMem = atoi(optarg);
            break;
#ifdef NOT_USED
        case 'S':
            /* ----------------
             *  S - assume stable main memory
             *      (don't flush all pages at end transaction)
             * ----------------
             */
            flagS = 1;
            SetTransactionFlushEnabled(false);
            break;
#endif
            
        case 's':
            /* ----------------
             *    s - report usage statistics (timings) after each query
             * ----------------
             */
            ShowStats = 1;
            StatFp = stderr;
            break;
            
        case 't':
            /* ----------------
             *  tell postgres to report usage statistics (timings) for
             *  each query
             *
             *  -tpa[rser] = print stats for parser time of each query
             *  -tpl[anner] = print stats for planner time of each query
             *  -te[xecutor] = print stats for executor time of each query
             *  caution: -s can not be used together with -t.
             * ----------------
             */
            StatFp = stderr;
            switch (optarg[0]) {
            case 'p':  if (optarg[1] == 'a')
                ShowParserStats = 1;
            else if (optarg[1] == 'l')
                ShowPlannerStats = 1;
            else
                errs++;
                break;
            case 'e':  ShowExecutorStats = 1;   break;
            default:   errs++; break;
            } 
            break;
            
        case 'x':
#if 0 /* planner/xfunc.h */
            /* control joey hellerstein's expensive function optimization */
            if (XfuncMode != 0)
                {
                    fprintf(stderr, "only one -x flag is allowed\n");
                    errs++;
                    break;
                }
            if (strcmp(optarg, "off") == 0)
                XfuncMode = XFUNC_OFF;
            else if (strcmp(optarg, "nor") == 0)
                XfuncMode = XFUNC_NOR;
            else if (strcmp(optarg, "nopull") == 0)
                XfuncMode = XFUNC_NOPULL;
            else if (strcmp(optarg, "nopm") == 0)
                XfuncMode = XFUNC_NOPM;
            else if (strcmp(optarg, "pullall") == 0)
                XfuncMode = XFUNC_PULLALL;
            else if (strcmp(optarg, "wait") == 0)
                XfuncMode = XFUNC_WAIT;
            else {
                fprintf(stderr, "use -x {off,nor,nopull,nopm,pullall,wait}\n");
                errs++;
            }
#endif
            break;
            
        default:
            /* ----------------
             *  default: bad command line option
             * ----------------
             */
            errs++;
        }
    
    /* ----------------
     *  get user name and pathname and check command line validity
     * ----------------
     */
    SetPgUserName();
    userName = GetPgUserName();
    
    if (FindBackend(pg_pathname, argv[0]) < 0)
        elog(FATAL, "%s: could not locate executable, bailing out...",
             argv[0]);
    
    if (errs || argc - optind > 1) {
        usage (argv[0]);
        exitpg(1);
    } else if (argc - optind == 1) {
        DBName = argv[optind];
    } else if ((DBName = userName) == NULL) {
        fprintf(stderr, "%s: USER undefined and no database specified\n",
                argv[0]);
        exitpg(1);
    }
    
    if (ShowStats && 
        (ShowParserStats || ShowPlannerStats || ShowExecutorStats)) {
        fprintf(stderr, "-s can not be used together with -t.\n");
        exitpg(1);
    }

    if (!DataDir) {
        fprintf(stderr, "%s does not know where to find the database system "
                "data.  You must specify the directory that contains the "
                "database system either by specifying the -D invocation "
                "option or by setting the PGDATA environment variable.\n\n",
                argv[0]);
        exitpg(1);
    }
    
    Noversion = flagC;
    Quiet = flagQ;
    EchoQuery = flagE;
    EuroDates = flagEu;
    
    /* ----------------
     *  print flags
     * ----------------
     */
    if (! Quiet) {
        puts("\t---debug info---");
        printf("\tQuiet =        %c\n", Quiet     ? 't' : 'f');
        printf("\tNoversion =    %c\n", Noversion ? 't' : 'f');
        printf("\tstable    =    %c\n", flagS     ? 't' : 'f');
        printf("\ttimings   =    %c\n", ShowStats ? 't' : 'f');
        printf("\tdates     =    %s\n", EuroDates ? "European" : "Normal");
        printf("\tbufsize   =    %d\n", NBuffers);
        printf("\tsortmem   =    %d\n", SortMem);
        
        printf("\tquery echo =   %c\n", EchoQuery ? 't' : 'f');
        printf("\tmultiplexed backend? =  %c\n", multiplexedBackend ? 't' : 'f');
        printf("\tDatabaseName = [%s]\n", DBName);
        puts("\t----------------\n");
    }
    
    /* ----------------
     *  initialize portal file descriptors
     * ----------------
     */
    if (IsUnderPostmaster == true) {
        if (Portfd < 0) {
            fprintf(stderr,
                    "Postmaster flag set: no port number specified, use /dev/null\n");
            Portfd = open(NULL_DEV, O_RDWR, 0666);
        }
        pq_init(Portfd);
    }

    if (multiplexedBackend) {
      if (serverPortnum == 0 ||
          StreamServerPort(hostName, serverPortnum, &serverSock) != STATUS_OK)
        {
          fprintf(stderr, "Postgres: cannot create stream port %d\n", serverPortnum);
          exit(1);
        }
/*
{
    char buf[100];
    sprintf(buf, "stream port %d created, socket = %d\n", serverPortnum, serverSock);
    puts(buf);
}
*/
      FD_ZERO(&rmask);
      FD_ZERO(&basemask);
      FD_SET(serverSock, &basemask);  

      frontendList = DLNewList();
      /* add the original FrontEnd to the list */
      if (IsUnderPostmaster == true) {
        FrontEnd *fe = malloc(sizeof(FrontEnd));

        FD_SET(Portfd, &basemask);
        maxFd = Max(serverSock,Portfd) + 1;

        fe->fn_connected = true;
        fe->fn_Pfin = Pfin;
        fe->fn_Pfout = Pfout;
        fe->fn_done = false;
        (fe->fn_port).sock = Portfd;
        DLAddHead(frontendList, DLNewElem(fe));
        numFE++;
      } else {
          numFE = 1;
          maxFd = serverSock + 1;
      }
    }

    if (IsUnderPostmaster || multiplexedBackend)
        whereToSendOutput = Remote;
    else 
        whereToSendOutput = Debug;
    
    SetProcessingMode(InitProcessing);
    
    /* initialize */
    if (! Quiet) {
        puts("\tInitPostgres()..");
    }
 
    InitPostgres(DBName);
    /* ----------------
     *  if an exception is encountered, processing resumes here
     *  so we abort the current transaction and start a new one.
     *  This must be done after we initialize the slave backends
     *  so that the slaves signal the master to abort the transaction
     *  rather than calling AbortCurrentTransaction() themselves.
     *
     *  Note:  elog(WARN) causes a kill(getpid(),1) to occur sending
     *         us back here.
     * ----------------
     */

    pqsignal(SIGHUP, handle_warn);

    if (sigsetjmp(Warn_restart, 1) != 0) {
        InWarn = 1;

        time(&tim);
        
        if (! Quiet)
            printf("\tAbortCurrentTransaction() at %s\n", ctime(&tim));

        memset(parser_input, 0, MAX_PARSE_BUFFER);
        
        AbortCurrentTransaction();
    }
    InWarn = 0;
    
    /* ----------------
     *  POSTGRES main processing loop begins here
     * ----------------
     */
    if (IsUnderPostmaster == false) {
        puts("\nPOSTGRES backend interactive interface");
        puts("$Revision: 1.39 $ $Date: 1997/08/06 05:38:35 $");
    }
    
    /* ----------------
     * if stable main memory is assumed (-S flag is set), it is necessary
     * to flush all dirty shared buffers before exit
     * plai 8/7/90
     * ----------------
     */
    if (!TransactionFlushEnabled())
        on_exitpg(FlushBufferPool, (caddr_t) 0);
    
    for (;;) {
      
      if (multiplexedBackend) {
        if (numFE == 0) 
          break;

        memmove((char *) &rmask, (char *) &basemask, sizeof(fd_set));
        nSelected = select(maxFd, &rmask,0,0,0);

        if (nSelected < 0) {

          if (errno == EINTR) continue;
          fprintf(stderr,"postgres: multiplexed backend select failed\n");
          exitpg(1);
        }
        if (FD_ISSET(serverSock, &rmask)) {
        /* new connection pending on our well-known port's socket */
          newFE = (FrontEnd*) malloc (sizeof(FrontEnd));
          memset(newFE, 0, sizeof(FrontEnd));
          newFE->fn_connected = false;
          newFE->fn_done = false;
          newPort = &(newFE->fn_port);
          if (StreamConnection(serverSock,newPort) != STATUS_OK) {
            StreamClose(newPort->sock);
            newFd = -1;
          }
          else {
            DLAddHead(frontendList, DLNewElem(newFE));
            numFE++;
            newFd = newPort->sock;
            if (newFd >= maxFd) maxFd = newFd + 1;
            FD_SET(newFd, &rmask);
            FD_SET(newFd, &basemask);
            --nSelected;
            FD_CLR(serverSock, &rmask);
          }
          continue;
        } /* if FD_ISSET(serverSock) */

        /* if we get here, it means that the serverSocket was not the one
           selected.  Instead, one of the front ends was selected.
           find which one */
        curr = DLGetHead(frontendList);
        while (curr) {
          FrontEnd *fe = (FrontEnd*)DLE_VAL(curr);
          Port *port = &(fe->fn_port);

          /* this is lifted from postmaster.c */
          if (FD_ISSET(port->sock, &rmask)) {
            if (fe->fn_connected == false) {
                /* we have a message from a new frontEnd */
                status = PacketReceive(port, &port->buf, NON_BLOCKING);
                if (status == STATUS_OK) {
                  fe->fn_connected = true;
                  pq_init(port->sock);
                  fe->fn_Pfin = Pfin;
                  fe->fn_Pfout = Pfout;
                }
                else
                  fprintf(stderr,"Multiplexed backend: error in reading packets from %d\n", port->sock);
               }
            else  /* we have a query from an existing,  active FrontEnd */
              {
                Pfin = fe->fn_Pfin;
                Pfout = fe->fn_Pfout;
                currentFE = fe;
              }
            if (fe->fn_done)
                {
                    Dlelem *c = curr;
                    curr = DLGetSucc(curr);
                    DLRemove(c);
                }
             break;
              }
          else
            curr = DLGetSucc(curr);
        }
    }
        /* ----------------
         *   (1) read a command. 
         * ----------------
         */
        memset(parser_input, 0, MAX_PARSE_BUFFER);

        firstchar = ReadCommand(parser_input, multiplexedBackend);
        /* process the command */
        switch (firstchar) {
            /* ----------------
             *  'F' indicates a fastpath call.
             *      XXX HandleFunctionRequest
             * ----------------
             */
        case 'F':
            IsEmptyQuery = false;
            
            /* start an xact for this function invocation */
            if (! Quiet) {
                time(&tim);
                printf("\tStartTransactionCommand() at %s\n", ctime(&tim));
            }
            
            StartTransactionCommand();
            HandleFunctionRequest();
            break;
            
            /* ----------------
             *  'Q' indicates a user query
             * ----------------
             */
        case 'Q':
            fflush(stdout);
            
            if ( strspn(parser_input," \t\n") == strlen(parser_input)) {
                /* ----------------
                 *  if there is nothing in the input buffer, don't bother
                 *  trying to parse and execute anything..
                 * ----------------
                 */
                IsEmptyQuery = true;
            } else {
                /* ----------------
                 *  otherwise, process the input string.
                 * ----------------
                 */
                IsEmptyQuery = false;
                if (ShowStats)
                    ResetUsage();
                
                /* start an xact for this query */
                if (! Quiet) {
                    time(&tim);
                    printf("\tStartTransactionCommand() at %s\n", ctime(&tim));
                }
                StartTransactionCommand();
                
                pg_eval(parser_input, (char **) NULL, (Oid *) NULL, 0);
                
                if (ShowStats)
                    ShowUsage();
            }
            break;
            
            /* ----------------
             *  'X' means that the frontend is closing down the socket
             * ----------------
             */
        case 'X':
            IsEmptyQuery = true;
            if (multiplexedBackend) {
               FD_CLR(currentFE->fn_port.sock, &basemask);
               currentFE->fn_done = true;
               numFE--;
             }
            pq_close();
            break;
            
        default:
            elog(WARN,"unknown frontend message was recieved");
        }
        
        /* ----------------
         *   (3) commit the current transaction
         *
         *   Note: if we had an empty input buffer, then we didn't
         *   call pg_eval, so we don't bother to commit this transaction.
         * ----------------
         */
        if (! IsEmptyQuery) {
            if (! Quiet) {
                time(&tim);
                printf("\tCommitTransactionCommand() at %s\n", ctime(&tim));
            }
            CommitTransactionCommand();
            
        } else {
            if (IsUnderPostmaster || multiplexedBackend)
                NullCommand(Remote);
        }
        
} /* infinite for-loop */
  exitpg(0);
  return 1;
}

#ifndef HAVE_GETRUSAGE
#include "rusagestub.h"
#else /* HAVE_GETRUSAGE */
#include <sys/resource.h>
#endif /* HAVE_GETRUSAGE */

struct rusage Save_r;
struct timeval Save_t;

void
ResetUsage(void)
{
    struct timezone tz;
    getrusage(RUSAGE_SELF, &Save_r);
    gettimeofday(&Save_t, &tz);
    ResetBufferUsage();
/*    ResetTupleCount(); */
}

void
ShowUsage(void)
{
    struct timeval user, sys;
    struct timeval elapse_t;
    struct timezone tz;
    struct rusage r;
    
    getrusage(RUSAGE_SELF, &r);
    gettimeofday(&elapse_t, &tz);
    memmove((char *)&user, (char *)&r.ru_utime, sizeof(user)); 
    memmove((char *)&sys, (char *)&r.ru_stime,sizeof(sys)); 
    if (elapse_t.tv_usec < Save_t.tv_usec) {
        elapse_t.tv_sec--;
        elapse_t.tv_usec += 1000000;
    }
    if (r.ru_utime.tv_usec < Save_r.ru_utime.tv_usec) {
        r.ru_utime.tv_sec--;
        r.ru_utime.tv_usec += 1000000;
    }
    if (r.ru_stime.tv_usec < Save_r.ru_stime.tv_usec) {
        r.ru_stime.tv_sec--;
        r.ru_stime.tv_usec += 1000000;
    }
    
    /*
     *  the only stats we don't show here are for memory usage -- i can't
     *  figure out how to interpret the relevant fields in the rusage
     *  struct, and they change names across o/s platforms, anyway.
     *  if you can figure out what the entries mean, you can somehow
     *  extract resident set size, shared text size, and unshared data
     *  and stack sizes.
     */
    
    fprintf(StatFp, "! system usage stats:\n");
    fprintf(StatFp, 
            "!\t%ld.%06ld elapsed %ld.%06ld user %ld.%06ld system sec\n",
            (long int) elapse_t.tv_sec - Save_t.tv_sec,
            (long int) elapse_t.tv_usec - Save_t.tv_usec,
            (long int) r.ru_utime.tv_sec - Save_r.ru_utime.tv_sec,
            (long int) r.ru_utime.tv_usec - Save_r.ru_utime.tv_usec,
            (long int) r.ru_stime.tv_sec - Save_r.ru_stime.tv_sec,
            (long int) r.ru_stime.tv_usec - Save_r.ru_stime.tv_usec);
    fprintf(StatFp,
            "!\t[%ld.%06ld user %ld.%06ld sys total]\n",
            (long int) user.tv_sec, 
            (long int) user.tv_usec, 
            (long int) sys.tv_sec, 
            (long int) sys.tv_usec);
#ifdef HAVE_GETRUSAGE
    fprintf(StatFp, 
            "!\t%ld/%ld [%ld/%ld] filesystem blocks in/out\n",
            r.ru_inblock - Save_r.ru_inblock,
            /* they only drink coffee at dec */
            r.ru_oublock - Save_r.ru_oublock,
            r.ru_inblock, r.ru_oublock);
    fprintf(StatFp, 
            "!\t%ld/%ld [%ld/%ld] page faults/reclaims, %ld [%ld] swaps\n",
            r.ru_majflt - Save_r.ru_majflt,
            r.ru_minflt - Save_r.ru_minflt,
            r.ru_majflt, r.ru_minflt,
            r.ru_nswap - Save_r.ru_nswap,
            r.ru_nswap);
    fprintf(StatFp, 
            "!\t%ld [%ld] signals rcvd, %ld/%ld [%ld/%ld] messages rcvd/sent\n",
            r.ru_nsignals - Save_r.ru_nsignals,
            r.ru_nsignals,
            r.ru_msgrcv - Save_r.ru_msgrcv,
            r.ru_msgsnd - Save_r.ru_msgsnd,
            r.ru_msgrcv, r.ru_msgsnd);
    fprintf(StatFp, 
            "!\t%ld/%ld [%ld/%ld] voluntary/involuntary context switches\n",
            r.ru_nvcsw - Save_r.ru_nvcsw,
            r.ru_nivcsw - Save_r.ru_nivcsw,
            r.ru_nvcsw, r.ru_nivcsw);
#endif /* HAVE_GETRUSAGE */
    fprintf(StatFp, "! postgres usage stats:\n");
    PrintBufferUsage(StatFp);
/*     DisplayTupleCount(StatFp); */
}