Skip to content
Snippets Groups Projects
  • Bruce Momjian's avatar
    15cb32d9
    This is the final state of the rule system for 6.4 after the · 15cb32d9
    Bruce Momjian authored
        patch is applied:
    
    	Rewrite rules on relation level work fine now.
    
    	Event qualifications on insert/update/delete  rules  work
    	fine now.
    
    	I  added  the  new  keyword  OLD to reference the CURRENT
    	tuple. CURRENT will be removed in 6.5.
    
    	Update rules can  reference  NEW  and  OLD  in  the  rule
    	qualification and the actions.
    
    	Insert/update/delete rules on views can be established to
    	let them behave like real tables.
    
    	For  insert/update/delete  rules  multiple  actions   are
    	supported  now.   The  actions  can also be surrounded by
    	parantheses to make psql  happy.   Multiple  actions  are
    	required if update to a view requires updates to multiple
    	tables.
    
    	Regular users  are  permitted  to  create/drop  rules  on
    	tables     they     have     RULE     permissions     for
    	(DefineQueryRewrite() is  now  able  to  get  around  the
    	access  restrictions  on  pg_rewrite).  This enables view
    	creation for regular users too. This  required  an  extra
    	boolean  parameter  to  pg_parse_and_plan() that tells to
    	set skipAcl on all rangetable entries  of  the  resulting
    	queries.       There      is      a      new     function
    	pg_exec_query_acl_override()  that  could  be   used   by
    	backend utilities to use this facility.
    
    	All rule actions (not only views) inherit the permissions
    	of the event relations  owner.  Sample:  User  A  creates
    	tables    T1    and    T2,   creates   rules   that   log
    	INSERT/UPDATE/DELETE on T1 in T2 (like in the  regression
    	tests  for rules I created) and grants ALL but RULE on T1
    	to user B.  User B  can  now  fully  access  T1  and  the
    	logging  happens  in  T2.  But user B cannot access T2 at
    	all, only the rule actions can. And due to  missing  RULE
    	permissions on T1, user B cannot disable logging.
    
    	Rules  on  the  attribute  level are disabled (they don't
    	work properly and since regular users are  now  permitted
    	to create rules I decided to disable them).
    
    	Rules  on  select  must have exactly one action that is a
    	select (so select rules must be a view definition).
    
    	UPDATE NEW/OLD rules  are  disabled  (still  broken,  but
    	triggers can do it).
    
    	There are two new system views (pg_rule and pg_view) that
    	show the definition of the rules or views so the db admin
    	can  see  what  the  users do. They use two new functions
    	pg_get_ruledef() and pg_get_viewdef() that are  builtins.
    
    	The functions pg_get_ruledef() and pg_get_viewdef() could
    	be used to implement rule and view support in pg_dump.
    
    	PostgreSQL is now the only database system I  know,  that
    	has rewrite rules on the query level. All others (where I
    	found a  rule  statement  at  all)  use  stored  database
    	procedures  or  the  like  (triggers as we call them) for
    	active rules (as some call them).
    
        Future of the rule system:
    
    	The now disabled parts  of  the  rule  system  (attribute
    	level,  multiple  actions on select and update new stuff)
    	require a complete new rewrite handler from scratch.  The
    	old one is too badly wired up.
    
    	After  6.4  I'll  start to work on a new rewrite handler,
    	that fully supports the attribute level  rules,  multiple
    	actions on select and update new.  This will be available
    	for 6.5 so we get full rewrite rule capabilities.
    
    Jan
    15cb32d9
    History
    This is the final state of the rule system for 6.4 after the
    Bruce Momjian authored
        patch is applied:
    
    	Rewrite rules on relation level work fine now.
    
    	Event qualifications on insert/update/delete  rules  work
    	fine now.
    
    	I  added  the  new  keyword  OLD to reference the CURRENT
    	tuple. CURRENT will be removed in 6.5.
    
    	Update rules can  reference  NEW  and  OLD  in  the  rule
    	qualification and the actions.
    
    	Insert/update/delete rules on views can be established to
    	let them behave like real tables.
    
    	For  insert/update/delete  rules  multiple  actions   are
    	supported  now.   The  actions  can also be surrounded by
    	parantheses to make psql  happy.   Multiple  actions  are
    	required if update to a view requires updates to multiple
    	tables.
    
    	Regular users  are  permitted  to  create/drop  rules  on
    	tables     they     have     RULE     permissions     for
    	(DefineQueryRewrite() is  now  able  to  get  around  the
    	access  restrictions  on  pg_rewrite).  This enables view
    	creation for regular users too. This  required  an  extra
    	boolean  parameter  to  pg_parse_and_plan() that tells to
    	set skipAcl on all rangetable entries  of  the  resulting
    	queries.       There      is      a      new     function
    	pg_exec_query_acl_override()  that  could  be   used   by
    	backend utilities to use this facility.
    
    	All rule actions (not only views) inherit the permissions
    	of the event relations  owner.  Sample:  User  A  creates
    	tables    T1    and    T2,   creates   rules   that   log
    	INSERT/UPDATE/DELETE on T1 in T2 (like in the  regression
    	tests  for rules I created) and grants ALL but RULE on T1
    	to user B.  User B  can  now  fully  access  T1  and  the
    	logging  happens  in  T2.  But user B cannot access T2 at
    	all, only the rule actions can. And due to  missing  RULE
    	permissions on T1, user B cannot disable logging.
    
    	Rules  on  the  attribute  level are disabled (they don't
    	work properly and since regular users are  now  permitted
    	to create rules I decided to disable them).
    
    	Rules  on  select  must have exactly one action that is a
    	select (so select rules must be a view definition).
    
    	UPDATE NEW/OLD rules  are  disabled  (still  broken,  but
    	triggers can do it).
    
    	There are two new system views (pg_rule and pg_view) that
    	show the definition of the rules or views so the db admin
    	can  see  what  the  users do. They use two new functions
    	pg_get_ruledef() and pg_get_viewdef() that are  builtins.
    
    	The functions pg_get_ruledef() and pg_get_viewdef() could
    	be used to implement rule and view support in pg_dump.
    
    	PostgreSQL is now the only database system I  know,  that
    	has rewrite rules on the query level. All others (where I
    	found a  rule  statement  at  all)  use  stored  database
    	procedures  or  the  like  (triggers as we call them) for
    	active rules (as some call them).
    
        Future of the rule system:
    
    	The now disabled parts  of  the  rule  system  (attribute
    	level,  multiple  actions on select and update new stuff)
    	require a complete new rewrite handler from scratch.  The
    	old one is too badly wired up.
    
    	After  6.4  I'll  start to work on a new rewrite handler,
    	that fully supports the attribute level  rules,  multiple
    	actions on select and update new.  This will be available
    	for 6.5 so we get full rewrite rule capabilities.
    
    Jan
postgres.c 36.48 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.83 1998/08/24 01:38:02 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>
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif							/* aix */


#include "postgres.h"
#include "miscadmin.h"
#include "fmgr.h"

#include "access/xact.h"
#include "catalog/catname.h"
#include "commands/async.h"
#include "executor/execdebug.h"
#include "executor/executor.h"
#include "libpq/libpq.h"
#include "libpq/libpq-be.h"
#include "libpq/pqsignal.h"
#include "nodes/pg_list.h"
#include "nodes/print.h"
#include "optimizer/cost.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
#include "parser/parser.h"
#include "rewrite/rewriteHandler.h"		/* for QueryRewrite() */
#include "storage/bufmgr.h"
#include "tcop/dest.h"
#include "tcop/fastpath.h"
#include "tcop/pquery.h"
#include "tcop/tcopdebug.h"
#include "tcop/tcopprot.h"		/* where declarations for this file go */
#include "tcop/utility.h"
#include "utils/mcxt.h"
#include "utils/rel.h"

#if FALSE
#include "nodes/relation.h"
#endif

#if 0
#include "optimizer/xfunc.h"
#endif

#if FALSE
#include "nodes/plannodes.h"
#endif

#if FALSE
#include "nodes/memnodes.h"
#endif

#ifdef MULTIBYTE
#include "mb/pg_wchar.h"
#endif

/* ----------------
 *		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;
const char **ps_status;	/* this is our 'ps' status, argv[3] */

#ifdef LOCK_MGR_DEBUG
extern int	lockDebug;

#endif
extern int	lockingOff;
extern int	NBuffers;

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) */
bool			InError;

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;

/* ----------------
 *		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);
static char ReadCommand(char *inBuf);


/* ----------------------------------------------------------------
 *		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;
			proc_exit(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)
{
	char		qtype[2];
	char		result = '\0';

	/* ----------------
	 *	get input from the frontend
	 * ----------------
	 */
	strcpy(qtype, "?");
	if (pq_getnchar(qtype, 0, 1) == EOF)
	{
		/* ------------
		 *	when front-end applications quits/dies
		 * ------------
		 */
		proc_exit(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)
{
	if (IsUnderPostmaster)
		return SocketBackend(inBuf);
	else
		return InteractiveBackend(inBuf);
}

List *
pg_parse_and_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 */
				  bool aclOverride)
{
	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++)
	{
		List	   *union_result,
				   *union_list,
				   *rewritten_list;

		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 != NIL)
		{
			int			len,
						k;

			/*
			 * Rewrite the UNIONS.
			 */
			foreach(rewritten_list, rewritten)
			{
				Query	   *qry = (Query *) lfirst(rewritten_list);

				union_result = NIL;
				foreach(union_list, qry->unionClause)
					union_result = nconc(union_result, QueryRewrite((Query *) lfirst(union_list)));
				qry->unionClause = union_result;
			}

			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);
			}
		}
	}
	/* ----------
	 * Due to rewriting, the new list could also have been
	 * shrunk (do instead nothing). Forget obsolete queries
	 * at the end.
	 * ----------
	 */
	new_list->len = j;

	/* we're done with the original lists, free it */
	free(querytree_list->qtrees);
	free(querytree_list);

	querytree_list = new_list;

	/*
	 * Override ACL checking if requested
	 */
	if (aclOverride) {
		for (i = 0; i < querytree_list->len; i++) {
			RangeTblEntry	*rte;
			List		*l;

			querytree = querytree_list->qtrees[i];
			if (querytree->commandType == CMD_UTILITY)
				continue;

			foreach (l, querytree->rtable) {
				rte = (RangeTblEntry *)lfirst(l);

				rte->skipAcl = TRUE;
			}
		}
	}

	if (DebugPrintRewrittenParsetree == true)
	{
		printf("\n---- \tafter rewriting:\n");

		for (i = 0; i < querytree_list->len; i++)
		{
			nodeDisplay(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");

				free(querytree_list->qtrees);
				free(querytree_list);
				if (queryListP)
					*queryListP = (QueryTreeList *) NULL;
				return (List *) NULL;
			}

			if (ShowPlannerStats)
				ResetUsage();

			/* call that optimizer */
			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
	}

	/* ----------
	 * Check if the rewriting had thrown away anything
	 * ----------
	 */
	if (querytree_list->len == 0) {
		free(querytree_list->qtrees);
		free(querytree_list);
		querytree_list = NULL;
	}

	if (queryListP)
		*queryListP = querytree_list;

	return (plan_list);
}

/* ----------------------------------------------------------------
 *		pg_exec_query()
 *
 *		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_exec_query(char *query_string)
{
	pg_exec_query_dest(query_string, whereToSendOutput, FALSE);
}

void
pg_exec_query_acl_override(char *query_string)
{
	pg_exec_query_dest(query_string, whereToSendOutput, TRUE);
}

void
pg_exec_query_dest(char *query_string,	/* string to execute */
				   CommandDest dest,	/* where results should go */
				   bool aclOverride)	/* to give utility
				   			 * commands power of
							 * superusers
							 */
{
	List	   *plan_list;
	Plan	   *plan;
	Query	   *querytree;
	int			i,
				j;
	QueryTreeList *querytree_list;

	/* plan the queries */
	plan_list = pg_parse_and_plan(query_string, NULL, 0, &querytree_list, dest, aclOverride);

	if (QueryCancel)
		CancelQuery();
		
	/* pg_parse_and_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_parse_and_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, 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(), SIGHUP) which
 *		occurs when elog(ERROR) is called.
 *
 *		quickdie() occurs when signalled by the postmaster.
 *		Some backend has bought the farm,
 *		so 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, "Message from PostgreSQL backend:"
		 "\n\tThe Postmaster has informed me that some other backend"
		 " died abnormally and possibly corrupted shared memory."
		 "\n\tI have rolled back the current transaction and am"
		 " going to terminate your database system connection and exit."
	"\n\tPlease reconnect to the database system and repeat your query.");


	/*
	 * 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(ERROR, "floating point exception!"
		 " The last floating point operation either exceeded legal ranges"
		 " or was a divide by zero");
}


/* signal handler for query cancel signal from postmaster */
static void
QueryCancelHandler(SIGNAL_ARGS)
{
	QueryCancel = true;
}

void
CancelQuery(void)
{
	/* QueryCancel flag will be reset in main loop, which we reach by
	 * longjmp from elog().
	 */
	elog(ERROR, "Query was cancelled.");
}


static void
usage(char *progname)
{
	fprintf(stderr,
			"Usage: %s [options] [dbname]\n", progname);
	fprintf(stderr, "\t-B buffers\tset number of buffers in buffer pool\n");
	fprintf(stderr, "\t-C \t\tsupress version info\n");
	fprintf(stderr, "\t-D dir\t\tdata directory\n");
	fprintf(stderr, "\t-E \t\techo query before execution\n");
	fprintf(stderr, "\t-F \t\tturn off fsync\n");
#ifdef LOCK_MGR_DEBUG
	fprintf(stderr, "\t-K \t\tset locking debug level [0|1|2]\n");
#endif
	fprintf(stderr, "\t-P port\t\tset port file descriptor\n");
	fprintf(stderr, "\t-Q \t\tsuppress informational messages\n");
	fprintf(stderr, "\t-S buffers\tset amount of sort memory available\n");
	fprintf(stderr, "\t-d [1|2|3]\tset debug level\n");
	fprintf(stderr, "\t-e \t\tturn on European date format\n");
	fprintf(stderr, "\t-o file\t\tsend stdout and stderr to given filename \n");
	fprintf(stderr, "\t-s \t\tshow stats after each query\n");
	fprintf(stderr, "\t-v version\tset protocol version being used by frontend\n");
}

/* ----------------------------------------------------------------
 *		PostgresMain
 *		  postgres main loop
 *		all backends, interactive or otherwise start here
 * ----------------------------------------------------------------
 */
int
PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
{
	bool			flagC = false,
					flagQ = false,
					flagE = false,
					flagEu = false;
	int				flag;

	char	   *DBName = NULL;
	int			errs = 0;

	char		firstchar;
	char		parser_input[MAX_PARSE_BUFFER];
	char	   *userName;

	char	   *DBDate = NULL;
	extern int	optind;
	extern char *optarg;
	extern short DebugLvl;

	/* ----------------
	 *	parse command line arguments
	 * ----------------
	 */

	/*
	 * Set default values.
	 */
	ShowStats = 0;
	ShowParserStats = ShowPlannerStats = ShowExecutorStats = 0;
#ifdef LOCK_MGR_DEBUG
	lockDebug = 0;
#endif

	/*
	 * get hostname is either the environment variable PGHOST or NULL NULL
	 * means Unix-socket only
	 */
	DataDir = getenv("PGDATA");

	/*
	 * Try to get initial values for date styles and formats. Does not do
	 * a complete job, but should be good enough for backend. Cannot call
	 * parse_date() since palloc/pfree memory is not set up yet.
	 */
	DBDate = getenv("PGDATESTYLE");
	if (DBDate != NULL)
	{
		if (strcasecmp(DBDate, "ISO") == 0)
			DateStyle = USE_ISO_DATES;
		else if (strcasecmp(DBDate, "SQL") == 0)
			DateStyle = USE_SQL_DATES;
		else if (strcasecmp(DBDate, "POSTGRES") == 0)
			DateStyle = USE_POSTGRES_DATES;
		else if (strcasecmp(DBDate, "GERMAN") == 0)
		{
			DateStyle = USE_GERMAN_DATES;
			EuroDates = TRUE;
		}

		if (strcasecmp(DBDate, "NONEURO") == 0)
			EuroDates = FALSE;
		else if (strcasecmp(DBDate, "EURO") == 0)
			EuroDates = TRUE;
	}

    optind = 1; /* reset after postmaster usage */
	
	while ((flag = getopt(argc, argv, "B:bCD:d:Eef:iK:Lm:MNo:P:pQS:st:v: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 = true;
				break;

			case 'D':			/* PGDATA directory */
				DataDir = optarg;

			case 'd':			/* debug level */
				flagQ = false;
				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 = true;
				break;

			case 'e':
				/* --------------------------
				 * Use european date formats.
				 * --------------------------
				 */
				flagEu = true;
				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_mergejoin_ = 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':
				/* Multiplexed backends are no longer supported. */
				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
				 * ----------------
				 */
				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 = true;
				break;

			case 'S':
				/* ----------------
				 *	S - amount of sort memory to use in 1k bytes
				 * ----------------
				 */
				{
					int			S;

					S = atoi(optarg);
					if (S >= 4 * BLCKSZ / 1024)
						SortMem = S;
				}
				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 'v':
				FrontendProtocol = (ProtocolVersion) atoi(optarg);
				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();

#ifdef CYR_RECODE
	SetCharSet();
#endif

	if (FindExec(pg_pathname, argv[0], "postgres") < 0)
		elog(FATAL, "%s: could not locate executable, bailing out...",
			 argv[0]);

	if (errs || argc - optind > 1)
	{
		usage(argv[0]);
		proc_exit(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]);
		proc_exit(1);
	}

	if (ShowStats &&
		(ShowParserStats || ShowPlannerStats || ShowExecutorStats))
	{
		fprintf(stderr, "-s can not be used together with -t.\n");
		proc_exit(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]);
		proc_exit(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("\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("\tDatabaseName = [%s]\n", DBName);
		puts("\t----------------\n");
	}

	/* ----------------
	 *	set process params for ps
	 * ----------------
	 */
	if (IsUnderPostmaster)
	{
		int i;

		Assert(real_argc >= 4);
		real_argv[1] = userName;
		real_argv[2] = DBName;
		ps_status = (const char **)&real_argv[3];
		*ps_status = "idle";
		for (i = 4; i < real_argc; i++)
			real_argv[i] = "";  /* blank them */
	}
	/* we just put a dummy here so we don't have to test everywhere */
	else	ps_status = malloc(sizeof(char *));

	/* ----------------
	 *	initialize portal file descriptors
	 * ----------------
	 */
	if (IsUnderPostmaster)
	{
		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);
		whereToSendOutput = Remote;
	}
	else
		whereToSendOutput = Debug;

	SetProcessingMode(InitProcessing);

	/* initialize */
	if (!Quiet)
		puts("\tInitPostgres()..");

	InitPostgres(DBName);

#ifdef MULTIBYTE
	/* set default client encoding */
	if (!Quiet)
	{
		puts("\treset_client_encoding()..");
	}
	reset_client_encoding();
	if (!Quiet)
	{
		puts("\treset_client_encoding() done.");
	}
#endif

	/* ----------------
	 *  Set up handler for cancel-request signal, and
	 *	send this backend's cancellation info to the frontend.
	 *	This should not be done until we are sure startup is successful.
	 * ----------------
	 */

	pqsignal(SIGINT, QueryCancelHandler);

	if (whereToSendOutput == Remote &&
		PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
	{
		pq_putnchar("K", 1);
		pq_putint((int32) MyProcPid, sizeof(int32));
		pq_putint((int32) MyCancelKey, sizeof(int32));
		/* Need not flush since ReadyForQuery will do it. */
	}

	/* ----------------
	 *	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(ERROR) causes a kill(getpid(), SIGHUP) to occur sending
	 *		   us back here.
	 * ----------------
	 */

	pqsignal(SIGHUP, handle_warn);

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

		time(&tim);

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

		MemSet(parser_input, 0, MAX_PARSE_BUFFER);

		AbortCurrentTransaction();

	}

	InError = false;

	/* ----------------
	 *	POSTGRES main processing loop begins here
	 * ----------------
	 */
	if (!IsUnderPostmaster)
	{
		puts("\nPOSTGRES backend interactive interface");
		puts("$Revision: 1.83 $ $Date: 1998/08/24 01:38:02 $");
	}

	/* ----------------
	 * if stable main memory is assumed (-S(old) flag is set), it is necessary
	 * to flush all dirty shared buffers before exit
	 * plai 8/7/90
	 * ----------------
	 */
	if (!TransactionFlushEnabled())
		on_shmem_exit(FlushBufferPool, NULL);

	for (;;)
	{
		/* ----------------
		 *	 (0) tell the frontend we're ready for a new query.
		 * ----------------
		 */
		ReadyForQuery(whereToSendOutput);

		/* ----------------
		 *	 (1) read a command.
		 * ----------------
		 */
		MemSet(parser_input, 0, MAX_PARSE_BUFFER);

		firstchar = ReadCommand(parser_input);

		QueryCancel = false;

		/* 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();
				*ps_status = "idle";
				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_exec_query(parser_input);

					*ps_status = "idle";

					if (ShowStats)
						ShowUsage();
				}
				break;

				/* ----------------
				 *	'X' means that the frontend is closing down the socket
				 * ----------------
				 */
			case 'X':
				pq_close();
				proc_exit(0);
				break;

			default:
				elog(ERROR, "unknown frontend message was received");
		}

		/* ----------------
		 *	 (3) commit the current transaction
		 *
		 *	 Note: if we had an empty input buffer, then we didn't
		 *	 call pg_exec_query, 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)
				NullCommand(Remote);
		}

	}							/* infinite for-loop */
	proc_exit(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); */
}