/*-------------------------------------------------------------------------
 *
 * 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.15 1996/11/08 05:59:31 momjian Exp $
 *
 * NOTES
 *    this is the "main" module of the postgres backend and
 *    hence the main module of the "traffic cop".
 *
 *-------------------------------------------------------------------------
 */
#include "libpq/pqsignal.h"	/* substitute for <signal.h> */

#if defined(linux)
#ifndef __USE_POSIX
#define __USE_POSIX
#endif
#endif /* defined(linux) */

#include <unistd.h>
#include <stdio.h>
#include <string.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
#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/tcopdebug.h"

#include "executor/execdebug.h"
#include "executor/executor.h"
#include "nodes/relation.h"

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

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

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

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

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

extern int	lockingOff;
extern int	NBuffers;

int	fsyncOff = 0;

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

Relation	reldesc;		/* current relation descritor */
char		relname[80];		/* current relation name */

#if defined(WIN32) || defined(next)
jmp_buf    Warn_restart;
#define sigsetjmp(x,y)  setjmp(x)
#define siglongjmp longjmp
#else
sigjmp_buf Warn_restart;
#endif /*defined(WIN32) || defined(next) */
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, int multiplexedBackend);
static char ReadCommand(char *inBuf, int 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, int 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, int 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 ( DebugPrintParse == true ) {
	    printf("\ninput string is \"%s\"\n",query_string);
	    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=================\n");
	printf("  After Rewriting\n");
	printf("=================\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("\nPlan 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("\nPlan 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 */
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, "    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");
    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	   flag;
    
    char   *DBName = NULL; 
    int    errs = 0;
    
    char   firstchar;
    char   parser_input[MAX_PARSE_BUFFER];
    char *userName;
    
    int    multiplexedBackend = 0;
    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;

#ifdef WIN32
    WSADATA WSAData;
#endif /* WIN32 */

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

#ifndef WIN32
    signal(SIGHUP, die);
    signal(SIGTERM, die);
    signal(SIGPIPE, die);
    signal(SIGUSR1, quickdie);
    signal(SIGUSR2, Async_NotifyHandler);
    signal(SIGFPE, FloatExceptionHandler);
#endif /* WIN32 */
    
    /* --------------------
     *	initialize globals 
     * -------------------
     */
    
    InitGlobals();

    /* ----------------
     *	parse command line arguments
     * ----------------
     */
    flagC = flagQ = flagS = flagE = ShowStats = 0;
    ShowParserStats = ShowPlannerStats = ShowExecutorStats = 0;
    
    /* 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;
    }

    while ((flag = getopt(argc, argv, "B:bCd:Ef:iLm: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;
	    
	    /* ----------------
	     *	-debug mode
	     * ----------------
	     */
	case 'd':
	    /* DebugMode = true;  */
	    flagQ = 0;
	    DebugPrintPlan = true;
	    DebugPrintParse = true;
	    DebugPrintRewrittenParsetree = true;
	    DebugLvl = (short)atoi(optarg);
	    break;
	    
	case 'E':
	    /* ----------------
	     *	E - echo the query the user entered
	     * ----------------
	     */
	    flagE = 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 '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 = 1;
	    serverPortnum = atoi(optarg);
#ifdef WIN32
           /* There was no postmaster started so the shared memory
           ** for the shared memory table hasn't been allocated so
           ** do it now.
           */
           _nt_init();
#endif /* WIN32 */
	    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 - assume stable main memory
	     *	    (don't flush all pages at end transaction)
	     * ----------------
	     */
	    flagS = 1;
	    SetTransactionFlushEnabled(false);
	    break;
	    
	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);
    }
    
    Noversion = flagC;
    Quiet = flagQ;
    EchoQuery = flagE;
    
    /* ----------------
     * 	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("\tbufsize   =    %d\n", NBuffers);
	
	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);
    }

#ifdef WIN32
    if ((status = WSAStartup(MAKEWORD(1,1), &WSAData)) == 0)
	(void) printf("%s\nInitializing WinSock: %s\n", WSAData.szDescription, WSAData.szSystemStatus);
    else {
	fprintf(stderr, "Error initializing WinSock: %d is the err", status);
	exit(1);
    }
#endif /* WIN32 */
    
    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()..");
    }
 
#if WIN32
     _nt_attach();
#endif /* WIN32 */

    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.
     * ----------------
     */

#ifndef WIN32    
    signal(SIGHUP, handle_warn);

    if (sigsetjmp(Warn_restart, 1) != 0) {
#else
    if (setjmp(Warn_restart) != 0) {
#endif /* WIN32 */
	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.15 $ $Date: 1996/11/08 05:59:31 $");
    }
    
    /* ----------------
     * 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 ( parser_input[0] ==  ' ' && parser_input[1] == '\0' ) {
		/* ----------------
		 *  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 WIN32
#ifdef NEED_RUSAGE
#include "rusagestub.h"
#else /* NEED_RUSAGE */
#include <sys/resource.h>
#endif /* NEED_RUSAGE */

struct rusage Save_r;
struct timeval Save_t;

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

void
ShowUsage()
{
    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",
	    elapse_t.tv_sec - Save_t.tv_sec,
	    elapse_t.tv_usec - Save_t.tv_usec,
	    r.ru_utime.tv_sec - Save_r.ru_utime.tv_sec,
	    r.ru_utime.tv_usec - Save_r.ru_utime.tv_usec,
	    r.ru_stime.tv_sec - Save_r.ru_stime.tv_sec,
	    r.ru_stime.tv_usec - Save_r.ru_stime.tv_usec);
    fprintf(StatFp,
	    "!\t[%ld.%06ld user %ld.%06ld sys total]\n",
	    user.tv_sec, user.tv_usec, sys.tv_sec, sys.tv_usec);
#ifndef NEED_RUSAGE
    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 /* NEED_RUSAGE */
    fprintf(StatFp, "! postgres usage stats:\n");
    PrintBufferUsage(StatFp);
/*     DisplayTupleCount(StatFp); */
}
#else
void
ShowUsage()
{}

void
ResetUsage()
{}
#endif /* WIN32 */
