/* ----------------------------------------------------------------
 *   FILE
 *	backendsup.c
 *	
 *   DESCRIPTION
 *	"backend" program support routines.
 *
 *   INTERFACE ROUTINES
 *
 *   NOTES
 *
 *   IDENTIFICATION
 *	$Header: /private/postgres/src/bootstrap/RCS/bootstrap.c,v 1.15 1992/08/24 23:12:50 mao Exp $
 * ----------------------------------------------------------------
 */

#include <signal.h>

#include "tmp/postgres.h"
#include "tcop/tcopprot.h"

RcsId("$Header: /private/postgres/src/bootstrap/RCS/bootstrap.c,v 1.15 1992/08/24 23:12:50 mao Exp $");

#include "access/heapam.h"
#include "access/tqual.h"
#include "tmp/miscadmin.h"
#include "utils/exc.h"	/* for ExcAbort and <setjmp.h> */
#include "utils/fmgr.h"
#include "utils/mcxt.h"
#include "storage/smgr.h"

#include "catalog/pg_type.h"

#undef BOOTSTRAP
#include "y.tab.h"

/* ----------------
 *	prototypes
 *
 *  	NOTE: see also bkint.h
 * ----------------
 */
#include "support/bkint.h"

#ifndef linux
extern int setjmp();
#endif
extern int chdir();
extern char *getenv();

void SocketBackend ARGS((char *, LispValue));
void InteractiveBackend ARGS((char *, LispValue));
void EvalLine ARGS((char *, LispValue));
bool BootstrapAlreadySeen ARGS(( ObjectId id ));

/* ----------------
 *	global variables
 * ----------------
 */
/*
 * In the lexical analyzer, we need to get the reference number quickly from
 * the string, and the string from the reference number.  Thus we have
 * as our data structure a hash table, where the hashing key taken from
 * the particular string.  The hash table is chained.  One of the fields
 * of the hash table node is an index into the array of character pointers.
 * The unique index number that every string is assigned is simply the
 * position of its string pointer in the array of string pointers.
 */

char pg_pathname[256];

macro		*strtable [STRTABLESIZE];
hashnode	*hashtable [HASHTABLESIZE];

FILE		*source = NULL;
static int	strtable_end = -1;    /* Tells us last occupied string space */
static int	num_of_errs = 0;      /* Total number of errors encountered */

static char *   (typestr[16]) = {
	"bool", "bytea", "char", "char16", "dt", "int2", "int28",
	"int4", "regproc", "text", "oid", "tid", "xid", "iid",
	"oid8", "smgr"
};

/* functions associated with each type */
static	long	Procid[16][7] = {
	{ 16, 1, 0, F_BOOLIN, F_BOOLOUT, F_BOOLEQ, 0 },
	{ 17, -1, 1, F_BYTEAIN, F_BYTEAOUT, 0, 0 },
	{ 18, 1, 0, F_CHARIN, F_CHAROUT, F_CHAREQ, 0 },
	{ 19, 16, 1, F_CHAR16IN, F_CHAR16OUT, F_CHAR16EQ, 0 },
	{ 20, 4, 0, F_DATETIMEIN, F_DATETIMEOUT, 0, 0 },
	{ 21, 2, 0, F_INT2IN, F_INT2OUT, F_INT2EQ, F_INT2LT },
	{ 22, 16, 1, F_INT28IN, F_INT28OUT, 0, 0 },
	{ 23, 4, 0, F_INT4IN, F_INT4OUT, F_INT4EQ, F_INT4LT },
	{ 24, 4, 0, F_REGPROCIN, F_REGPROCOUT, 0, 0 },
	{ 25, -1, 1, F_TEXTIN, F_TEXTOUT, F_TEXTEQ, 0 },
	{ 26, 4, 0, F_INT4IN, F_INT4OUT, F_INT4EQ, 0 },
	{ 27, 6, 1, F_TIDIN, F_TIDOUT, 0, 0 },
	{ 28, 5, 1, F_XIDIN, F_XIDOUT, 0, 0 },
	{ 29, 1, 0, F_CIDIN, F_CIDOUT, 0, 0 },
	{ 30, 32, 1, F_OID8IN, F_OID8OUT, 0, 0 },
	{210, 2, 0, F_SMGRIN, F_SMGROUT, F_SMGREQ, 0 }
};

struct	typmap {			/* a hack */
	ObjectId	am_oid;
	struct type	am_typ;
};

#ifdef	EBUG
static	int	ShowLock = 0;
#endif

static	struct	typmap	**Typ = (struct typmap **)NULL;
static	struct	typmap	*Ap = (struct typmap *)NULL;

extern int	Quiet;
static	int		Warnings = 0;
static	int		ShowTime = 0;
static	char		Blanks[MAXATTR];
static	char		Buf[MAXPGPATH];
static TimeQual		StandardTimeQual = NowTimeQual;
static TimeQualSpace	StandardTimeQualSpace;

int		Userid;
Relation	reldesc;		/* current relation descritor */
char		relname[80];		/* current relation name */
struct	attribute *attrtypes[MAXATTR];  /* points to attribute info */
char		*values[MAXATTR];	/* cooresponding attribute values */
int		numattr;		/* number of attributes for cur. rel */
jmp_buf		Warn_restart;
int		DebugMode;
static int UseBackendParseY = 0;

extern bool override;

extern	int	optind;
extern	char	*optarg;


/* ----------------------------------------------------------------
 *			misc functions
 * ----------------------------------------------------------------
 */
/* ----------------
 *	hack so linker links 
 * ----------------
 */

/* ----------------
 *	error handling / abort routines
 * ----------------
 */
void
err()
{
    cleanup();
    exitpg(1);
}


/* ----------------------------------------------------------------
 *				BootstrapMain
 * ----------------------------------------------------------------
 */
void
BootstrapMain(ac, av)
int  ac;
char *av[];
{
    int	  i;
    int	  flagC, flagQ;
    int	  portFd = -1;
    char  *dat;
    char  pathbuf[128], *getwd();
    extern char *DBName;

    /* ----------------
     *	initialize signal handlers
     * ----------------
     */
    signal(SIGHUP, die);
    signal(SIGINT, die);
    signal(SIGTERM, die);

    /* ----------------
     *	process command arguments
     * ----------------
     */
	Quiet = 0;
    flagC = 0;
    flagQ = 0;
    dat = NULL;

    for (i = 2; i < ac; ++i) {
	char *ptr = av[i];
	if (*ptr != '-') {
	    if (dat != NULL) {
		fputs("DB Name specified twice!\n", stderr);
		goto usage;
	    }
	    DBName = dat = ptr;
	    continue;
	}

	while (*++ptr) {
	    switch(*ptr) {
	    case 'd':	/* -debug mode */
		DebugMode = 1;
		ptr = "\0";
		break;
	    case 'a':	/* -ami	*/
		UseBackendParseY = 1;
		ptr = "\0";	/* \0\0 */
		break;
	    case 'C':
	        flagC = 1;
		break;
	    case 'Q':
		flagQ = 1;
		break;
	    case 'O':
		override = true;
		break;
	    case 'p':	/* started by postmaster */
		IsUnderPostmaster = true;
		ptr = "\0";	/* \0\0 */
		break;
	    case 'P':
		portFd = atoi(ptr + 1);
		ptr = "\0";
		break;
	    default:
		fprintf(stderr, "Option %c is illegal\n", *ptr);
		goto usage;
	    }
	}
    }
    if (dat == NULL)
	DBName = dat = getenv("USER");
    if (dat == NULL) {
	fputs("backend: failed, no db name specified\n", stderr);
	fputs("         and no USER enviroment variable\n", stderr);
	exitpg(1);
    }

    Noversion = flagC;
    Quiet = flagQ;

    /* ----------------
     *	initialize input fd
     * ----------------
     */
    if (IsUnderPostmaster == true && portFd < 0) {
	fputs("backend: failed, no -P option with -postmaster opt.\n", stderr);
	exitpg(1);
    }
    if (portFd >= 0)
	SetPQSocket(portFd);

    /* ----------------
     *	backend initialization
     * ----------------
     */
    /* XXX the -C version flag should be removed and combined with -O */
    GetDataHome();
    SetProcessingMode((override) ? BootstrapProcessing : InitProcessing);
    InitPostgres((String)dat);
    LockDisable(true);
    dat = Blanks;
    
    for (i = MAXATTR - 1; i >= 0; --i) {
	attrtypes[i]=(struct attribute *)NULL;
	*dat++ = ' ';
    }
    for(i = 0; i < STRTABLESIZE; ++i)
	strtable[i] = NULL;                    
    for(i = 0; i < HASHTABLESIZE; ++i)
	hashtable[i] = NULL;                   

    /* ----------------
     *	abort processing resumes here
     * ----------------
     */
    signal(SIGHUP, handle_warn);
    if (setjmp(Warn_restart) != 0) {
	Warnings++;
	AbortCurrentTransaction();
    }

    /* ----------------
     *	process input.
     * ----------------
     */
    for (;;) {
	Int_yyparse();
    }

    /* ----------------
     *	usage
     * ----------------
     */
usage:
    fputs("Usage: backend [-C] [-Q] [datname]\n", stderr);
    exitpg(1);
}

/* ----------------------------------------------------------------
 *		MANUAL BACKEND INTERACTIVE INTERFACE COMMANDS
 * ----------------------------------------------------------------
 */

/* ----------------
 *  	createrel
 *
 *	takes the name of the relation to create, creates the file
 *	and sticks the relation into the syscat.  if the relation
 *	already exists in the syscat, then nothing is created
 *	
 *	Note : createrel creates, but does not open the named relation
 * ----------------
 */
void
createrel(name)
    char *name;
{
    ObjectId toid;

    if (strlen(name) > 79) 
	name[79] ='\000';
    
    bcopy(name,relname,strlen(name)+1);

    printf("Amcreatr: relation %s.\n", relname);

    if ((numattr == 0) || (attrtypes == NULL)) {
	elog(WARN,"Warning: must define attributes before creating rel.\n");
	elog(WARN,"         relation not created.\n");
    } else {
	reldesc = heap_creatr(relname, numattr, DEFAULT_SMGR, attrtypes);
	if (reldesc == (Relation)NULL) {
	    elog(WARN,"Warning: cannot create relation %s.\n", relname);
	    elog(WARN,"         Probably should delete old %s.\n", relname);
	}
	toid = TypeDefine(relname, reldesc->rd_id, -1, -1, 'c', ',',
			  "textin", "textout", "textin", "textout",
			  NULL, "-", (Boolean) 0);

    }
}

/* ----------------
 *	boot_openrel
 * ----------------
 */
void
boot_openrel(name)
    char *name;
{
    int		i;
    struct	typmap	**app;
    Relation	rdesc;
    HeapScanDesc	sdesc;
    HeapTuple	tup;
    extern	char	TYPE_R[];
	
    if(strlen(name) > 79) 
	name[79] ='\000';
    bcopy(name, relname, strlen(name)+1);
	
    if (Typ == (struct typmap **)NULL) {
	StartPortalAllocMode(DefaultAllocMode, 0);
	rdesc = heap_openr(TYPE_R);
	sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, (ScanKey)NULL);
      for (i=0; PointerIsValid(tup=heap_getnext(sdesc,0,(Buffer *)NULL)); ++i);
	heap_endscan(sdesc);
	app = Typ = ALLOC(struct typmap *, i + 1);
	while (i-- > 0)
	    *app++ = ALLOC(struct typmap, 1);
	*app = (struct typmap *)NULL;
	sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, (ScanKey)NULL);
	app = Typ;
	while (PointerIsValid(tup = heap_getnext(sdesc, 0, (Buffer *)NULL))) {
	    (*app)->am_oid = tup->t_oid;
	    bcopy(
		(char *)GETSTRUCT(tup), 
		(char *)&(*app++)->am_typ, 
		sizeof ((*app)->am_typ)
	    );
	}
	heap_endscan(sdesc);
	heap_close(rdesc);
	EndPortalAllocMode();
    }
	
    if (reldesc != NULL) {
	closerel(NULL);
    }

    if (!Quiet)
    	printf("Amopen: relation %s. attrsize %d\n", relname, 
		sizeof(struct attribute));
    
    reldesc = heap_openr(relname);
    Assert(reldesc);
    numattr = reldesc->rd_rel->relnatts;
    for (i = 0; i < numattr; i++) {
	if (attrtypes[i] == NULL) {
	    attrtypes[i] = AllocateAttribute();
	}
	bcopy(
	    (char *)reldesc->rd_att.data[i], 
	    (char *)attrtypes[i],
	    sizeof (*attrtypes[0])
	);
	if (DebugMode) {
	    struct attribute *at = attrtypes[i];
	    printf("create attribute %d name %s len %d num %d type %d\n",
		i, at->attname, at->attlen, at->attnum, at->atttypid
	    );
	    fflush(stdout);
	}
    }
}

/* ----------------
 *	closerel
 * ----------------
 */
void
closerel(name)
    char *name;
{
    /* ### insert errorchecking to make sure closing same relation we
     *  have already open
     */

    if (reldesc == NULL) {
	elog(WARN,"Warning: no opened relation to close.\n");
    } else {
	if (!Quiet) printf("Amclose: relation %s.\n", relname);
	heap_close(reldesc);
	reldesc = (Relation)NULL;
    }
}

/* ----------------
 *	printrel
 * ----------------
 */
void
printrel()
{
    HeapTuple		tuple;
    HeapScanDesc	scandesc;
    int			i;
    Buffer		b;

    if (Quiet)
	return;
    if (reldesc == NULL) {
	elog(WARN,"Warning: need to open relation to print.\n");
	return;
    }
    /* print the name of the attributes in the relation */
    printf("Relation %s:  ", relname);
    printf("[ ");
    for (i=0; i < reldesc->rd_rel->relnatts; i++)
	printf("%s ", &reldesc->rd_att.data[i]->attname);
    printf("]\n\n");
    StartPortalAllocMode(DefaultAllocMode, 0);
    
    /* print the tuples in the relation */
    scandesc = heap_beginscan(reldesc, 
			      0,
			      NowTimeQual,
			      (unsigned) 0,
			      (ScanKey) NULL);
    while ((tuple = heap_getnext(scandesc, 0, &b)) != NULL) {
	showtup(tuple, b, reldesc);
    }
    heap_endscan(scandesc);
    EndPortalAllocMode();
    printf("\nEnd of relation\n");
}


#ifdef	EBUG
/* ----------------
 *	randomprintrel
 * ----------------
 */
void
randomprintrel()
{
    HeapTuple		tuple;
    HeapScanDesc	scandesc;
    Buffer		buffer;
    int			i, j, numattr, typeindex;
    int			count;
    int			mark = 0;
    static bool		isInitialized = false;
    extern		srandom();
    long		random();
    long		time();

    StartPortalAllocMode(DefaultAllocMode, 0);
    if (reldesc == NULL) {
	elog(WARN,"Warning: need to open relation to (r) print.\n");
    } else {
	/* print the name of the attributes in the relation */
	printf("Relation %s:  ", relname);
	printf("[ ");
	for (i=0; i<reldesc->rd_rel->relnatts; i++) {
	    printf("%s ", &reldesc->rd_att.data[i]->attname);
	}
	printf(
		"]\nWill display %d tuples in a slightly random order\n\n",
		count = 64
	);
	
	/* print the tuples in the relation */
	if (! isInitialized) {
	    srandom((int)time(0));
	    isInitialized = true;
	}
	scandesc = heap_beginscan(reldesc, 
			       random()&1, 
			       NowTimeQual,
			       0, 
			       (ScanKey) NULL);
	
	numattr = reldesc->rd_rel->relnatts;
	while (count-- != 0) {
	    if (!(random() & 0xf)) {
		printf("\tRESTARTING SCAN\n");
		heap_rescan(scandesc, random()&01, (ScanKey)NULL);
		mark = 0;
	    }
	    if (!(random() & 0x3)) {
		if (mark) {
		    printf("\tRESTORING MARK\n");
		    heap_restrpos(scandesc);
		    mark &= random();
		} else {
		    printf("\tSET MARK\n");
		    heap_markpos(scandesc);
		    mark = 0x1;
		}
	    }
	    if (!PointerIsValid(tuple =
			heap_getnext(scandesc, !(random() & 0x1), &buffer))) {
		puts("*NULL*");
		continue;
	    }
	    showtup(tuple, buffer, reldesc);
	}
	puts("\nDone");
	heap_endscan(scandesc);
    }
    EndPortalAllocMode();
}
#endif


/* ----------------
 *	showtup
 * ----------------
 */
void
showtup(tuple, buffer, relation)
    HeapTuple	tuple;
    Buffer	buffer;
    Relation	relation;
{
    char	*value, *fmgr();
    Boolean	isnull;
    struct	typmap	**app;
    int		i, typeindex;

    value = (char *)
	heap_getattr(tuple, buffer, ObjectIdAttributeNumber,
		  (struct attribute **)NULL, &isnull);
    
    if (isnull) {
	printf("*NULL* < ");
    } else {
	printf("%ld < ", (long)value);
    }
    
    for (i = 0; i < numattr; i++) {
	value = (char *)
	    heap_getattr(tuple, buffer, i + 1,
		      &reldesc->rd_att.data[0], &isnull);
	if (isnull) {
	    printf("<NULL> ");
	} else if (Typ != (struct typmap **)NULL) {
	    app = Typ;
	    while (*app != NULL && (*app)->am_oid !=
		reldesc->rd_att.data[i]->atttypid) {
		app++;
	    }
	    if (*app == NULL) {
		printf("Unable to find atttypid in Typ list! %d\n",
		    reldesc->rd_att.data[i]->atttypid
		);
		Assert(0);
	    }
	    printf("%s ", value = fmgr((*app)->am_typ.typoutput, value));
	    pfree(value);
	} else {
	    typeindex = reldesc->rd_att.data[i]->atttypid - FIRST_TYPE_OID;
	    printf("%s ", value = fmgr(Procid[typeindex][(int) Q_OUT], value));
	    pfree(value);
	}
    }
    if (!Quiet) printf(">\n");
    if (ShowTime) {
 	printf("\t[");
	value = heap_getattr(tuple, buffer, MinAbsoluteTimeAttributeNumber,
			  &reldesc->rd_att.data[0], &isnull);
	if (isnull) {
	    printf("{*NULL*} ");
	} else if (TimeIsValid((Time)value)) {
	    showtime((Time)value);
	}

	value = heap_getattr(tuple, buffer, MinCommandIdAttributeNumber,
			  &reldesc->rd_att.data[0], &isnull);
	if (isnull) {
	    printf("(*NULL*,");
	} else {
	    printf("(%u,", (CommandId)value);
	}
	
	value = heap_getattr(tuple, buffer, MinTransactionIdAttributeNumber,
			  &reldesc->rd_att.data[0], &isnull);
	
	if (isnull) {
	    printf("*NULL*),");
	} else if (!TransactionIdIsValid((TransactionId)value)) {
	    printf("-),");
	} else {
	    printf("%s)", TransactionIdFormString((TransactionId)value));
	    if (!TransactionIdDidCommit((TransactionId)value)) {
		if (TransactionIdDidAbort((TransactionId)value)) {
		    printf("*A*");
		} else {
		    printf("*InP*");
		}
	    }
	    value = (char *)TransactionIdGetCommitTime((TransactionId)value);
	    if (!TimeIsValid((Time)value)) {
		printf("{-},");
	    } else {
		showtime((Time)value);
		printf(",");
	    }
	}
	
	value = heap_getattr(tuple, buffer, MaxAbsoluteTimeAttributeNumber,
			  &reldesc->rd_att.data[0], &isnull);
	
	if (isnull) {
	    printf("{*NULL*} ");
	} else if (TimeIsValid((Time)value)) {
	    showtime((Time)value);
	}

	value = heap_getattr(tuple, buffer, MaxCommandIdAttributeNumber,
			&reldesc->rd_att.data[0], &isnull);
	
	if (isnull) {
	    printf("(*NULL*,");
	} else {
	    printf("(%u,", (CommandId)value);
	}

	value = heap_getattr(tuple, buffer, MaxTransactionIdAttributeNumber,
			&reldesc->rd_att.data[0], &isnull);
	
	if (isnull) {
	    printf("*NULL*)]\n");
	} else if (!TransactionIdIsValid((TransactionId)value)) {
	    printf("-)]\n");
	} else {
	    printf("%s)", TransactionIdFormString((TransactionId)value));
	    value = (char *)TransactionIdGetCommitTime((TransactionId)value);
	    if (!TimeIsValid((Time)value)) {
		printf("{-}]\n");
	    } else {
		showtime((Time)value);
		printf("]\n");
	    }
	}
    }
}

/* ----------------
 *	showtime
 * ----------------
 */
void
showtime(time)
    Time time;
{
    Assert(TimeIsValid(time));
    printf("{%d=%s}", time, nabstimeout(time));
}


/* ----------------
 * DEFINEATTR()
 *
 * define a <field,type> pair
 * if there are n fields in a relation to be created, this routine
 * will be called n times
 * ----------------
 */
void
DefineAttr(name, type, attnum)
    char *name, *type;
    int  attnum;
{
    int     attlen;
    TYPE    t;

    if (reldesc != NULL) {
	fputs("Warning: no open relations allowed with 't' command.\n",stderr);
	closerel(relname);
    }

    t = (TYPE)gettype(type);
    if (attrtypes[attnum] == (struct attribute *)NULL) 
	attrtypes[attnum] = AllocateAttribute();
    if (Typ != (struct typmap **)NULL) {
	attrtypes[attnum]->atttypid = Ap->am_oid;
	strncpy(attrtypes[attnum]->attname, name, 16);
	if (!Quiet) printf("<%s %s> ", attrtypes[attnum]->attname, type);
	attrtypes[attnum]->attnum = 1 + attnum; /* fillatt */
	attlen = attrtypes[attnum]->attlen = Ap->am_typ.typlen;
	attrtypes[attnum]->attbyval = Ap->am_typ.typbyval;
    } else {
	attrtypes[attnum]->atttypid = Procid[(int)t][(int)Q_OID];
	strncpy(attrtypes[attnum]->attname,name, 16);
	if (!Quiet) printf("<%s %s> ", attrtypes[attnum]->attname, type);
	attrtypes[attnum]->attnum = 1 + attnum; /* fillatt */
	attlen = attrtypes[attnum]->attlen = Procid[(int)t][(int)Q_LEN];
	attrtypes[attnum]->attbyval = (attlen==1) || (attlen==2)||(attlen==4);
    }
}


/* ----------------
 *	InsertOneTuple
 *	assumes that 'oid' will not be zero.
 * ----------------
 */
void
InsertOneTuple(objectid)
    ObjectId  objectid;
{
    HeapTuple tuple;
    int i;

    if (DebugMode) {
	printf("InsertOneTuple oid %d, %d attrs\n", objectid, numattr);
	fflush(stdout);
    }

    tuple = heap_formtuple(numattr,attrtypes,values,Blanks);
    if(objectid !=(OID)0) {
	tuple->t_oid=objectid;
    }
    RelationInsertHeapTuple(reldesc,(HeapTuple)tuple,(double *)NULL);
    pfree(tuple);
    if (DebugMode) {
	printf("End InsertOneTuple\n", objectid);
	fflush(stdout);
    }
    /*
     * Reset blanks for next tuple
     */
    for (i = 0; i<numattr; i++)
	Blanks[i] = ' ';
}

/* ----------------
 *	InsertOneValue
 * ----------------
 */
void
InsertOneValue(objectid, value, i)
    ObjectId  objectid;
    int	      i;
    char     *value;
{
    int		typeindex;
    char	*prt;
    struct typmap **app;
    HeapTuple	tuple;
    char	*fmgr();

    if (DebugMode)
	printf("Inserting value: '%s'\n", value);
    if (i < 0 || i >= MAXATTR) {
	printf("i out of range: %d\n", i);
	Assert(0);
    }

    if (Typ != (struct typmap **)NULL) {
	struct typmap *ap;
	if (DebugMode)
	    puts("Typ != NULL");
	app = Typ;
	while (*app && (*app)->am_oid != reldesc->rd_att.data[i]->atttypid)
	   ++app;
	ap = *app;
	if (ap == NULL) {
	    printf("Unable to find atttypid in Typ list! %d\n",
	        reldesc->rd_att.data[i]->atttypid
	    );
	    Assert(0);
	}
	if (!Quiet) printf("FMGR-IN/OUT %d, %d\n", ap->am_typ.typinput, 
	    ap->am_typ.typoutput);
	values[i] = fmgr(ap->am_typ.typinput, value);
	prt = fmgr(ap->am_typ.typoutput, values[i]);
	if (!Quiet) printf("%s ", prt);
	pfree(prt);
    } else {
	typeindex = attrtypes[i]->atttypid - FIRST_TYPE_OID;
	if (DebugMode)
	    printf("Typ == NULL, typeindex = %d idx = %d\n", typeindex, i);
	values[i] = fmgr(Procid[typeindex][(int)Q_IN], value);
	prt = fmgr(Procid[typeindex][(int) Q_OUT], values[i]);
	if (!Quiet) printf("%s ", prt);
	pfree(prt);
    }
    if (DebugMode) {
	puts("End InsertValue");
	fflush(stdout);
    }
}

/* ----------------
 *	InsertOneNull
 * ----------------
 */
void
InsertOneNull(i)
    int	      i;
{
    if (DebugMode)
	printf("Inserting null\n");
    if (i < 0 || i >= MAXATTR) {
	elog(FATAL, "i out of range (too many attrs): %d\n", i);
    }
    values[i] = (char *)NULL;
    Blanks[i] = 'n';
}

/* ----------------
 *	defineindex
 *
 *	This defines an index on the oid attribute of the specified
 *	heap relation.
 * ----------------
 */
void
defineindex(heapName, indexName, accessMethodName, attributeList)
    char  *heapName;
    char  *indexName;
    char  *accessMethodName;
    List  attributeList;
{
    Relation    cur_relation;

    DefineIndex(heapName,
		indexName,
		accessMethodName,
		attributeList,
		LispNil,
		LispNil);
    /*
     * All of the rest of this routine is needed only because in bootstrap
     * processing we don't increment xact id's.  The normal DefineIndex
     * code replaces a pg_class tuple with updated info including the
     * relhasindex flag (which we need to have updated).  Unfortunately, 
     * there are always two indices defined on each catalog causing us to 
     * update the same pg_class tuple twice for each catalog getting an 
     * index during bootstrap resulting in the ghost tuple problem (see 
     * heap_replace).  To get around this we change the relhasindex 
     * field ourselves in this routine keeping track of what catalogs we 
     * already changed so that we don't modify those tuples twice.  The 
     * normal mechanism for updating pg_class is disabled during bootstrap.
     *
     *		-mer 
     */
    cur_relation = heap_openr(heapName);
    if (! RelationIsValid(cur_relation))
            elog(WARN, "defineindex: could not open %s relation", heapName);

    if (!BootstrapAlreadySeen(cur_relation->rd_id))
	UpdateStats(cur_relation->rd_id, 0, true);
}

#define MORE_THAN_THE_NUMBER_OF_CATALOGS 256

bool
BootstrapAlreadySeen(id)
    ObjectId id;
{
    static ObjectId seenArray[MORE_THAN_THE_NUMBER_OF_CATALOGS];
    static int nseen = 0;
    bool seenthis;
    int i;

    seenthis = false;

    for (i=0; i < nseen; i++)
    {
	if (seenArray[i] == id)
	{
	    seenthis = true;
	    break;
	}
    }
    if (!seenthis)
    {
	seenArray[nseen] = id;
	nseen++;
    }
    return (seenthis);
}


/* ----------------
 *	handletime
 * ----------------
 */
void
handletime()
{
    int     numberOfTimes;
    char    firstTime[80];
    char    secondTime[80];
    Time    time1;
    Time    time2;

    if (scanf("%d", &numberOfTimes) != 1) {
	fputs("Error: failed to read number of time fields\n", stderr);
	err();
    }
    if (numberOfTimes < -1 || numberOfTimes > 2) {
	elog(
	    WARN, 
	    "number of time fields (%d) is not -1, 0, 1, or 2\n",
	    numberOfTimes
	);
    } else if (numberOfTimes == -1) {
	ShowTime = !ShowTime;
	return;
    } else if (numberOfTimes == 0) {
	StandardTimeQual = NowTimeQual;
	puts("Time range reset to \"now\"");
    } else if (numberOfTimes == 1) {
	if (scanf("%[^\n]", firstTime) != 1) {
	    fputs("Error: failed to read time\n", stderr);
	    err();
	}
	time1 = nabstimein(firstTime);
	if (!Quiet) printf("*** You entered \"%s\"", firstTime);
	if (time1 == INVALID_ABSTIME) {
	    if (!Quiet) 
			printf(" which is INVALID (time range unchanged).\n");
	} else {
	    if (!Quiet) 
	    	printf(" with representation %d.\n", time1);
	    bcopy(
		(char *)TimeFormSnapshotTimeQual(time1),
		(char *)&StandardTimeQualSpace,
		sizeof StandardTimeQualSpace
	    );
	    StandardTimeQual = (TimeQual)&StandardTimeQualSpace;
	}
    } else {	/* numberOfTimes == 2 */
	if (scanf("%[^,],%[^\n]", firstTime, secondTime) != 2) {
	    fputs("Error: failed to read both times\n", stderr);
	    err();
	}
	if (!Quiet) 
		printf("*** You entered \"%s\" and \"%s\"\n", firstTime, secondTime);
	time1 = nabstimein(firstTime);
	time2 = nabstimein(secondTime);
	if (time1 == INVALID_ABSTIME) {
	    time1 = InvalidTime;
	    if (time2 == INVALID_ABSTIME) {
		time2 = InvalidTime;
		if (!Quiet) printf("*** range is [,].\n");
	    } else {
		if (!Quiet) printf("*** range is [,%d].\n", time2);
	    }
	} else {
	    if (time2 == INVALID_ABSTIME) {
		time2 = InvalidTime;
		if (!Quiet) printf("*** range is [%d,].\n", time1);
	    } else {
		if (!Quiet) printf("*** range is [%d,%d].\n", time1, time2);
	    }
	}
	bcopy(
	    (char *)TimeFormRangedTimeQual(time1, time2),
	    (char *)&StandardTimeQualSpace,
	    sizeof StandardTimeQualSpace
	);
	StandardTimeQual = (TimeQual)&StandardTimeQualSpace;
    }
}

/* ----------------
 *	cleanup
 * ----------------
 */
void
cleanup()
{
    static	int	beenhere = 0;
     
    if (!beenhere)
	beenhere = 1;
    else {
	elog(FATAL,"Memory manager fault: cleanup called twice.\n", stderr);
	exitpg(1);
    }
    if (reldesc != (Relation)NULL) {
	heap_close(reldesc);
    }
    CommitTransactionCommand();
    exitpg(Warnings);
}

/* ----------------
 *	gettype
 * ----------------
 */
int
gettype(type)
    char *type;
{
    int		i;
    Relation	rdesc;
    HeapScanDesc	sdesc;
    HeapTuple	tup;
    struct	typmap	**app;
    extern	char	TYPE_R[];

    if (Typ != (struct typmap **)NULL) {
	for (app = Typ; *app != (struct typmap *)NULL; app++) {
	    if (strcmp((*app)->am_typ.typname, type) == 0) {
		Ap = *app;
		return((*app)->am_oid);
	    }
	}
    } else {
	for (i = 0; i <= (int)TY_LAST; i++) {
	    if (strcmp(type, typestr[i]) == 0) {
		return(i);
	    }
	}
	if (DebugMode)
	    printf("backendsup.c: External Type: %s\n", type);
        rdesc = heap_openr(TYPE_R);
        sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, (ScanKey)NULL);
	i = 0;
	while (PointerIsValid(tup = heap_getnext(sdesc, 0, (Buffer *)NULL)))
	    ++i;
	heap_endscan(sdesc);
	app = Typ = ALLOC(struct typmap *, i + 1);
	while (i-- > 0)
	    *app++ = ALLOC(struct typmap, 1);
	*app = (struct typmap *)NULL;
	sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, (ScanKey)NULL);
	app = Typ;
	while (PointerIsValid(tup = heap_getnext(sdesc, 0, (Buffer *)NULL))) {
	    (*app)->am_oid = tup->t_oid;
	    bcopy(
		(char *)GETSTRUCT(tup), 
		(char *)&(*app++)->am_typ,
	        sizeof ((*app)->am_typ)
	    );
        }
        heap_endscan(sdesc);
        heap_close(rdesc);
        return(gettype(type));
    }
    elog(WARN, "Error: unknown type '%s'.\n", type);
    err();
    /* NOTREACHED */
}

/* ----------------
 *	AllocateAttribute
 * ----------------
 */
struct attribute * /* XXX */
AllocateAttribute()
{
    struct attribute	*attribute = new(struct attribute); /* XXX */

    if (!PointerIsValid(attribute)) {
	elog(FATAL, "AllocateAttribute: malloc failed");
    }
    bzero((Pointer)attribute, sizeof *attribute);

    return (attribute);
}

/* ----------------
 *	MapEscape
 * ----------------
 */
#define isoctal(c) ((c)>='0' && (c) <= '7')

unsigned char 
MapEscape(s)
    char **s;
{
    int i;
    char octal[4];
    switch(*(*s)++) {
    case '\\':
	return '\\';
    case 'b':
	return '\b';
    case 'f':
 	return '\f';
    case 'r':
	return '\r';
    case 't':
	return '\t';
    case '"':
	return '"';
    case '\'':
	return '\'';
    case '0': 
    case '1': 
    case '2': 
    case '3': 
    case '4': 
    case '5':
    case '6': 
    case '7':
	for (--*s,i=0;isoctal((*s)[i]) && (i<3); ++i)
	    octal[i]=(*s)[i];
	*s += i;
	octal[i] = '\0';
	if ((i = strtol(octal,0,8)) <= 0377) 
	    return ((char)i);
    default:
	break;
    }
    elog(WARN,"Bad Escape sequence");
    return *((*s)++); /* ignore */
}

/* ----------------
 *	EnterString
 *	returns the string table position of the identifier
 *	passed to it.  We add it to the table if we can't find it.
 * ----------------
 */
int 
EnterString (str)
    char	*str;
{
    hashnode	*node;
    int        len;
	
    len= strlen(str);
	
    if ( (node = FindStr(str, len, 0)) != NULL)
	return (node->strnum);
    else {
	node = AddStr(str, len, 0);
	return (node->strnum);
    }
}

/* ----------------
 *	LexIDStr
 *	when given an idnum into the 'string-table' return the string
 *	associated with the idnum
 * ----------------
 */
char *
LexIDStr(ident_num) 
    int ident_num;
{
    return(strtable[ident_num]->s);
}    


/* ----------------
 *	CompHash
 *
 * 	Compute a hash function for a given string.  We look at the first,
 * 	the last, and the middle character of a string to try to get spread
 * 	the strings out.  The function is rather arbitrary, except that we
 * 	are mod'ing by a prime number.
 * ----------------
 */
int 
CompHash (str, len)
    char *str;
    int	len;
{
    register int result;
	
    result =(NUM * str[0] + NUMSQR * str[len-1] + NUMCUBE * str[(len-1)/2]);

    return (result % HASHTABLESIZE);

}

/* ----------------
 *	FindStr
 *
 * 	This routine looks for the specified string in the hash
 * 	table.  It returns a pointer to the hash node found,
 * 	or NULL if the string is not in the table.
 * ----------------
 */
hashnode *
FindStr (str, length, mderef)
    char	*str;
    int		length;
    hashnode   *mderef;
{
    hashnode	*node;
    node = hashtable [CompHash (str, length)];
    while (node != NULL) {
	/*
	 * We must differentiate between string constants that
	 * might have the same value as a identifier
	 * and the identifier itself.
	 */
	if (!strcmp(str, strtable[node->strnum]->s)) {
	    return(node);  /* no need to check */
	} else {
	    node = node->next;
	}
    }
    /* Couldn't find it in the list */
    return (NULL);
}

/* ----------------
 *	AddStr
 *
 * 	This function adds the specified string, along with its associated
 * 	data, to the hash table and the string table.  We return the node
 * 	so that the calling routine can find out the unique id that AddStr
 * 	has assigned to this string.
 * ----------------
 */
hashnode *
AddStr(str, strlength, mderef)
    char *str;
    int	 strlength;
    int  mderef;
{
    hashnode	*temp, *trail, *newnode;
    int		hashresult;
    char	*stroverflowmesg =
"There are too many string constants and identifiers for\
 the compiler to handle.";

    if (++strtable_end == STRTABLESIZE) {
	/* Error, string table overflow, so we Punt */
	elog(FATAL, stroverflowmesg);
    }

    strtable[strtable_end] = new(macro);
    strtable [strtable_end]->s = malloc((unsigned) strlength + 1);
    strtable[strtable_end]->mderef = 0;
    strcpy (strtable[strtable_end]->s, str);

    /* Now put a node in the hash table */

    newnode = new(hashnode);
    newnode->strnum = strtable_end;
    newnode->next = NULL;

    /* Find out where it goes */

    hashresult = CompHash (str, strlength);
    if (hashtable [hashresult] == NULL) {
	hashtable [hashresult] = newnode;
    } else {			/* There is something in the list */
	trail = hashtable [hashresult];
	temp = trail->next;
	while (temp != NULL) {
	    trail = temp;
	    temp = temp->next;
	}
	trail->next = newnode;
    }
    return (newnode);
}

/* ----------------
 *	printhashtable
 *
 * 	This routine dumps out the hash table for inspection.
 * ----------------
 */
void
printhashtable()
{
    register int i;
    hashnode *hashptr;

    if (Quiet) return;

    fprintf (stderr, "\n\nHash Table:\n");
    for (i = 0; i < HASHTABLESIZE; i++) {
        if (hashtable[i] != NULL) {
	    fprintf (stderr, "Position: %d\n", i);
	    hashptr = hashtable[i];
	    do {
	        fprintf (stderr, "\tString #%d", hashptr->strnum);
		hashptr = hashptr->next;
	    } while (hashptr != NULL);
	}
    }
}

/* ----------------
 *	printstrtable
 *
 *	This routine dumps out the string table for inspection.
 * ----------------
 */
void
printstrtable ()
{
    register int i;

    if (Quiet) return;

    fprintf (stderr, "\nString Table:\n");
    for (i = 0; i <= strtable_end; i++) {
	fprintf (stderr, "#%d: \"%s\";\t   ", i, strtable[i]->s);
	if (i % 3 == 0 && i > 0)
	    fprintf (stderr, "\n");
    }
    fprintf (stderr, "\n");
}

/* ----------------
 *	emalloc
 *
 *	Error checked version of malloc.
 * ----------------
 */
char *
emalloc (size)
    unsigned size;
{
    char *p;

    if ((p = malloc (size)) == NULL)
	elog (FATAL,"Memory Allocation Error\n");
    return (p);
}

/* ----------------
 *	LookUpMacro()
 *
 *  	takes an index into the string table and converts it into the
 *  	appropriate "string"
 * ----------------
 */
int
LookUpMacro(xmacro)
    char *xmacro;
{
    hashnode *node;
    int macro_indx;

    if (node = FindStr(xmacro,strlen(xmacro),0))
	macro_indx = node->strnum;
  
    if (strtable[macro_indx]->mderef == 0)
	elog(WARN,"Dereferencing non-existent macro");
    
    return(strtable[macro_indx]->mderef);
}

/* ----------------
 *	DefineMacro
 *
 *	takes two identifiers and links them up
 *	by putting a pointer between id1 and id2
 *	(the identifiers pointed to by indx1 and indx2
 * ----------------
 */
void
DefineMacro(indx1, indx2)
    int indx1, indx2;
{
    strtable[indx1]->mderef = indx2;
    if (strtable[indx1]->mderef != 0)
	elog(WARN,"Warning :redefinition of macro\n");
}

/* ----------------
 *	printmacros
 * ----------------
 */
void
printmacros()
{
    register int i;

    fprintf(stdout,"Macro Table:\n");
    for( i = 0;i < strtable_end; ++i) {
	if (strtable[i]->mderef != 0)
	    printf("\"%s\" = \"%s\"\n",strtable[i]->s,
				strtable[strtable[i]->mderef]->s);
    }
}

