/*-------------------------------------------------------------------------
 *
 * bootstrap.c--
 *    "backend" program support routines.
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    $Header: /usr/local/devel/pglite/cvs/src/backend/bootstrap/bootstrap.c,v 1.13 1995/03/10 19:56:05 andrew Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <string.h>
#include "libpq/pqsignal.h"	/* substitute for <signal.h> */

#define BOOTSTRAP_INCLUDE	/* mask out stuff in tcop/tcopprot.h */

#include "bootstrap/bootstrap.h"
#include "postgres.h"
#include "miscadmin.h"
#include "tcop/tcopprot.h"

#include "access/heapam.h"
#include "access/genam.h"
#include "utils/builtins.h"
#include "utils/tqual.h"
#include "utils/lsyscache.h"
#include "access/xact.h"
#include "utils/exc.h"	/* for ExcAbort and <setjmp.h> */
#include "fmgr.h"
#include "utils/palloc.h"
#include "utils/mcxt.h"
#include "storage/smgr.h"
#include "commands/defrem.h"
#include "access/transam.h"

#include "catalog/pg_type.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "catalog/index.h"

#undef BOOTSTRAP

/* #include "bootstrap/bootstrap.h" */

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

/*-
 * Basic information associated with each type.  This is used before
 * pg_type is created.
 *
 *	XXX several of these input/output functions do catalog scans
 *	    (e.g., F_REGPROCIN scans pg_proc).  this obviously creates some 
 *	    order dependencies in the catalog creation process.
 */
struct typinfo {
    char	name[16];
    Oid		oid;
    Oid		elem;
    int16	len;
    Oid		inproc;
    Oid		outproc;
};

static struct typinfo Procid[] = {
    { "bool",       16,    0,  1, F_BOOLIN,     F_BOOLOUT },
    { "bytea",      17,    0, -1, F_BYTEAIN,    F_BYTEAOUT },
    { "char",       18,    0,  1, F_CHARIN,     F_CHAROUT },
    { "char16",     19,    0, 16, F_CHAR16IN,   F_CHAR16OUT },
    { "dt",         20,    0,  4, F_DTIN,	    F_DTOUT},
    { "int2",       21,    0,  2, F_INT2IN,     F_INT2OUT },
    { "int28",      22,    0, 16, F_INT28IN,    F_INT28OUT },
    { "int4",       23,    0,  4, F_INT4IN,     F_INT4OUT },
    { "regproc",    24,    0,  4, F_REGPROCIN,  F_REGPROCOUT },
    { "text",       25,    0, -1, F_TEXTIN,     F_TEXTOUT },
    { "oid",        26,    0,  4, F_INT4IN,     F_INT4OUT },
    { "tid",        27,    0,  6, F_TIDIN,      F_TIDOUT },
    { "xid",        28,    0,  5, F_XIDIN,      F_XIDOUT },
    { "iid",        29,    0,  1, F_CIDIN,      F_CIDOUT },
    { "oid8",       30,    0, 32, F_OID8IN,     F_OID8OUT },
    { "smgr",      210,    0,  2, F_SMGRIN,     F_SMGROUT },
    { "_int2",    1005,   21, -1, F_ARRAY_IN,   F_ARRAY_OUT },
    { "_aclitem", 1034, 1033, -1, F_ARRAY_IN,   F_ARRAY_OUT }
};

static int n_types = sizeof(Procid) / sizeof(struct typinfo);

struct	typmap {			/* a hack */
    Oid	am_oid;
    TypeTupleFormData	am_typ;
};

#ifdef	EBUG
static	int	ShowLock = 0;
#endif

static	struct	typmap	**Typ = (struct typmap **)NULL;
static	struct	typmap	*Ap = (struct typmap *)NULL;
     
static	int		Warnings = 0;
static	int		ShowTime = 0;
static	char		Blanks[MAXATTR];
     
Relation	reldesc;		/* current relation descriptor */
/* static char	relname[80];	 */	/* current relation name */
static char16	relnameData;	 	
static Name     relname = &relnameData;  /* current relation name */

AttributeTupleForm attrtypes[MAXATTR];  /* points to attribute info */
static char	*values[MAXATTR];	/* cooresponding attribute values */
int		numattr;		/* number of attributes for cur. rel */

#if defined(PORTNAME_linux) || defined(WIN32)
static jmp_buf    Warn_restart;
#define sigsetjmp(x,y)  setjmp(x)
#else
static sigjmp_buf Warn_restart;
#endif

int		DebugMode;
static int UseBackendParseY = 0;
static GlobalMemory nogc = (GlobalMemory) NULL;	/* special no-gc mem context */

static int Reboot = 0;
     
extern	int	optind;
extern	char	*optarg;
     
/*
 *  At bootstrap time, we first declare all the indices to be built, and
 *  then build them.  The IndexList structure stores enough information
 *  to allow us to build the indices after they've been declared.
 */

typedef struct _IndexList {
    Name		il_heap;
    Name		il_ind;
    int			il_natts;
    AttrNumber		*il_attnos;
    uint16 		il_nparams;
    Datum *		il_params;
    FuncIndexInfo 	*il_finfo;
    PredInfo 		*il_predInfo;
    struct _IndexList 	*il_next;
} IndexList;

static IndexList *ILHead = (IndexList *) NULL;
     
typedef void (*sig_func)();

/* non-export function prototypes */

#ifdef	EBUG
static void randomprintrel(void);
#endif



/* ----------------------------------------------------------------
 *			misc functions
 * ----------------------------------------------------------------
 */

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

/* ----------------------------------------------------------------
 *				BootstrapMain
 * ----------------------------------------------------------------
 */
int
BootstrapMain(int ac, char *av[])
{
    int	  i;
    int	  flagC, flagQ;
    int	  portFd = -1;
    char  *dat;
    
    /* ----------------
     *	initialize signal handlers
     * ----------------
     */
/*    signal(SIGHUP, (sig_func) die); */
    signal(SIGINT, (sig_func) die);
    signal(SIGTERM, (sig_func) die);
    
    /* --------------------
     *	initialize globals 
     * -------------------
     */
    
    InitGlobals();

    /* ----------------
     *	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;
	    }
	    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 'r':	/* -reboot */
		Reboot = 1;
		ptr = "\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)
        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);
    }
    
    /* ----------------
     *	backend initialization
     * ----------------
     */
    SetProcessingMode((override) ? BootstrapProcessing : InitProcessing);
    InitPostgres((String)dat);
    LockDisable(true);
    dat = Blanks;
    
    for (i = MAXATTR - 1; i >= 0; --i) {
	attrtypes[i]=(AttributeTupleForm )NULL;
	*dat++ = ' ';
    }
    for(i = 0; i < STRTABLESIZE; ++i)
	strtable[i] = NULL;                    
    for(i = 0; i < HASHTABLESIZE; ++i)
	hashtable[i] = NULL;                   
    
    /* ----------------
     *	abort processing resumes here  - What to do in WIN32?
     * ----------------
     */
#ifndef WIN32
    signal(SIGHUP, (sig_func) handle_warn);
    if (sigsetjmp(Warn_restart, 1) != 0) {
	Warnings++;
	AbortCurrentTransaction();
    }
#endif /* WIN32 */
    
    /* ----------------
     *	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(char *name)
{
    Oid toid;
    static char16 inputProcNameData = { "textin" };
    static char16 outputProcNameData = { "textout" };
    static char16 sendProcNameData = { "textin" };
    static char16 receiveProcNameData = { "textout" };
    
    
    if (strlen(name) > 15) 
	name[15] ='\000';
    
    memmove(relname->data, name, strlen(name)+1);
    
    printf("Amcreatr: relation %s.\n", relname->data);
    
    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->data, numattr, DEFAULT_SMGR, attrtypes);
	if (reldesc == (Relation)NULL) {
	    elog(WARN,"Warning: cannot create relation %s.\n", relname->data);
	    elog(WARN,"         Probably should delete old %s.\n", relname->data);
	}
	
	toid = TypeDefine(relname,
			  reldesc->rd_id,
			  -1,-1,'c',',',
			  &inputProcNameData,
			  &outputProcNameData,
			  &sendProcNameData,
			  &receiveProcNameData,
			  NULL,
			  "-",
			  (bool) 0,
			  'i');
			  
    }
}

/* ----------------
 *	boot_openrel
 * ----------------
 */
void
boot_openrel(char *name)
{
    int		i;
    struct	typmap	**app;
    Relation	rdesc;
    HeapScanDesc	sdesc;
    HeapTuple	tup;
    
    if (strlen(name) > 15) 
	name[15] ='\000';
    
    memmove(relname->data, name, strlen(name)+1);

    if (Typ == (struct typmap **)NULL) {
	StartPortalAllocMode(DefaultAllocMode, 0);
	rdesc = heap_openr(TypeRelationName);
	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;
	    memmove((char *)&(*app++)->am_typ, 
		    (char *)GETSTRUCT(tup), 
		    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->data, 
	       sizeof(FormData_pg_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();
	}
	memmove((char *)attrtypes[i],
		(char *)reldesc->rd_att[i], 
		ATTRIBUTE_TUPLE_SIZE);
	
	/* Some old pg_attribute tuples might not have attisset. */
	/* If the attname is attisset, don't look for it - it may
	   not be defined yet.
	   */
	if (namestrcmp(&attrtypes[i]->attname, "attisset"))
	    attrtypes[i]->attisset = get_attisset(reldesc->rd_id,
						  &(attrtypes[i]->attname));
	else
	    attrtypes[i]->attisset = false;
	
	if (DebugMode) {
	    AttributeTupleForm at = attrtypes[i];
	    printf("create attribute %d name %.*s len %d num %d type %d\n",
		   i, NAMEDATALEN, at->attname.data, at->attlen, at->attnum, 
		   at->atttypid
		   );
	    fflush(stdout);
	}
    }
}

/* ----------------
 *	closerel
 * ----------------
 */
void
closerel(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->data);
	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->data);
    printf("[ ");
    for (i=0; i < reldesc->rd_rel->relnatts; i++)
	printf("%.*s ", NAMEDATALEN, reldesc->rd_att[i]->attname.data);
    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;
    
    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->data);
	printf("[ ");
	for (i=0; i<reldesc->rd_rel->relnatts; i++) {
	    printf("%.*s ", NAMEDATALEN, reldesc->rd_att[i]->attname.data);
	}
	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(HeapTuple tuple, Buffer buffer, Relation relation)
{
    char	*value;
    bool	isnull;
    struct	typmap	**app;
    int		i, typeindex;
    AbsoluteTime tabstime;
    TransactionId txid;
    CommandId tcid;
    
    value = (char *)
	heap_getattr(tuple, buffer, ObjectIdAttributeNumber,
		     (TupleDesc)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, &isnull);
	if (isnull) {
	    printf("<NULL> ");
	} else if (Typ != (struct typmap **)NULL) {
	    app = Typ;
	    while (*app != NULL && (*app)->am_oid !=
		   reldesc->rd_att[i]->atttypid) {
		app++;
	    }
	    if (*app == NULL) {
		printf("Unable to find atttypid in Typ list! %d\n",
		       reldesc->rd_att[i]->atttypid
		       );
		Assert(0);
	    }
	    printf("%s ", value = fmgr((*app)->am_typ.typoutput, value,
				       (*app)->am_typ.typelem));
	    pfree(value);
	} else {
	    typeindex = reldesc->rd_att[i]->atttypid - FIRST_TYPE_OID;
	    printf("%s ", value = fmgr(Procid[typeindex].outproc, value,
				       Procid[typeindex].elem));
	    pfree(value);
	}
    }
    if (!Quiet) printf(">\n");
    if (ShowTime) {
	
 	printf("\t[");
	value = heap_getattr(tuple, buffer, MinAbsoluteTimeAttributeNumber,
			     reldesc->rd_att, &isnull);
	tabstime = DatumGetUInt32(value);
	if (isnull) {
	    printf("{*NULL*} ");
	} else if (AbsoluteTimeIsBackwardCompatiblyValid(tabstime)) {
	    showtime(tabstime);
	}
	
	value = heap_getattr(tuple, buffer, MinCommandIdAttributeNumber,
			     reldesc->rd_att, &isnull);
	tcid = DatumGetUInt16(value);
	if (isnull) {
	    printf("(*NULL*,");
	} else {
	    printf("(%u,", tcid);
	}
	
	value = heap_getattr(tuple, buffer, MinTransactionIdAttributeNumber,
			     reldesc->rd_att, &isnull);
	txid = DatumGetUInt32(value);
	if (isnull) {
	    printf("*NULL*),");
	} else if (!TransactionIdIsValid(txid)) {
	    printf("-),");
	} else {
	    printf("%s)", TransactionIdFormString(txid));
	    if (!TransactionIdDidCommit(txid)) {
		if (TransactionIdDidAbort(txid)) {
		    printf("*A*");
		} else {
		    printf("*InP*");
		}
	    }
	    tabstime = (AbsoluteTime) TransactionIdGetCommitTime(txid);
	    if (!AbsoluteTimeIsBackwardCompatiblyValid(tabstime)) {
		printf("{-},");
	    } else {
		showtime(tabstime);
		printf(",");
	    }
	}
	
	value = heap_getattr(tuple, buffer, MaxAbsoluteTimeAttributeNumber,
			     reldesc->rd_att, &isnull);
	tabstime = DatumGetUInt32(value);
	if (isnull) {
	    printf("{*NULL*} ");
	} else if (AbsoluteTimeIsBackwardCompatiblyValid(tabstime)) {
	    showtime(tabstime);
	}
	
	value = heap_getattr(tuple, buffer, MaxCommandIdAttributeNumber,
			     reldesc->rd_att, &isnull);
	tcid = DatumGetUInt16(value);
	if (isnull) {
	    printf("(*NULL*,");
	} else {
	    printf("(%u,", tcid);
	}
	
	value = heap_getattr(tuple, buffer, MaxTransactionIdAttributeNumber,
			     reldesc->rd_att, &isnull);
	txid = DatumGetUInt32(value);
	if (isnull) {
	    printf("*NULL*)]\n");
	} else if (!TransactionIdIsValid(txid)) {
	    printf("-)]\n");
	} else {
	    printf("%s)", TransactionIdFormString(txid));
	    tabstime = TransactionIdGetCommitTime(txid);
	    if (!AbsoluteTimeIsBackwardCompatiblyValid(tabstime)) {
		printf("{-}]\n");
	    } else {
		showtime(tabstime);
		printf("]\n");
	    }
	}
    }
}

/* ----------------
 *	showtime
 * ----------------
 */
void
showtime(AbsoluteTime time)
{
    Assert(AbsoluteTimeIsBackwardCompatiblyValid(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(char *name, char *type, int attnum)
{
    int     attlen;
    int     t;
    
    if (reldesc != NULL) {
	fputs("Warning: no open relations allowed with 't' command.\n",stderr);
	closerel(relname->data);
    }
    
    t = gettype(type);
    if (attrtypes[attnum] == (AttributeTupleForm )NULL) 
	attrtypes[attnum] = AllocateAttribute();
    if (Typ != (struct typmap **)NULL) {
	attrtypes[attnum]->atttypid = Ap->am_oid;
	namestrcpy(&attrtypes[attnum]->attname, name);
	if (!Quiet) printf("<%.*s %s> ", NAMEDATALEN, 
			   attrtypes[attnum]->attname.data, 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[t].oid;
	namestrcpy(&attrtypes[attnum]->attname,name);
	if (!Quiet) printf("<%.*s %s> ", NAMEDATALEN,
			   attrtypes[attnum]->attname.data, type);
	attrtypes[attnum]->attnum = 1 + attnum; /* fillatt */
	attlen = attrtypes[attnum]->attlen = Procid[t].len;
	attrtypes[attnum]->attbyval = (attlen==1) || (attlen==2)||(attlen==4);
    }
}


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

    int i;
    
    if (DebugMode) {
	printf("InsertOneTuple oid %d, %d attrs\n", objectid, numattr);
	fflush(stdout);
    }
    
    tuple = heap_formtuple(numattr,(TupleDesc)attrtypes,(Datum*)values,Blanks);     
    if(objectid !=(Oid)0) {
	tuple->t_oid=objectid;
    }
    heap_insert(reldesc, tuple);
    if (Reboot) {
	Relation *idescs;
	int num = 0;
	char **name;
	
#define LOOKING_AT(x) (!namecmp(x, &(reldesc->rd_rel->relname)))
    
	if (LOOKING_AT(AttributeRelationName)) {
	    num = Num_pg_attr_indices; name = Name_pg_attr_indices;
	} else if (LOOKING_AT(ProcedureRelationName)) {
	    num = Num_pg_proc_indices; name = Name_pg_proc_indices;
	} else if (LOOKING_AT(RelationRelationName)) {
	    num = Num_pg_class_indices; name = Name_pg_class_indices;
	} else if (LOOKING_AT(TypeRelationName)) {
	    num = Num_pg_type_indices; name = Name_pg_type_indices;
	}

	if (num) {
	    idescs = (Relation *) palloc(num * sizeof(Relation));
	    CatalogOpenIndices(num, name, idescs);
	    CatalogIndexInsert(idescs, num, reldesc, tuple);
	    CatalogCloseIndices(num, idescs);
	    pfree(idescs);
	}
    }
    pfree(tuple);
    if (DebugMode) {
	printf("End InsertOneTuple, objectid=%d\n", objectid);
	fflush(stdout);
    }
    /*
     * Reset blanks for next tuple
     */
    for (i = 0; i<numattr; i++)
	Blanks[i] = ' ';
}

/* ----------------
 *	InsertOneValue
 * ----------------
 */
void
InsertOneValue(Oid objectid, char *value, int i)
{
    int		typeindex;
    char	*prt;
    struct typmap **app;
    
    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[i]->atttypid)
	    ++app;
	ap = *app;
	if (ap == NULL) {
	    printf("Unable to find atttypid in Typ list! %d\n",
		   reldesc->rd_att[i]->atttypid
		   );
	    Assert(0);
	}
	values[i] = fmgr(ap->am_typ.typinput, value,
			 ap->am_typ.typelem);
	prt = fmgr(ap->am_typ.typoutput, values[i],
		   ap->am_typ.typelem);
	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].inproc, value,
			 Procid[typeindex].elem);
	prt = fmgr(Procid[typeindex].outproc, values[i],
		   Procid[typeindex].elem);
	if (!Quiet) printf("%s ", prt);
	pfree(prt);
    }
    if (DebugMode) {
	puts("End InsertValue");
	fflush(stdout);
    }
}

/* ----------------
 *	InsertOneNull
 * ----------------
 */
void
InsertOneNull(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 specified heap relation.  The index
 *	is not yet populated; that happens at the end of bootstrapping, in
 *	response to a 'build indices' command.
 * ----------------
 */
void
defineindex(char *heapName,
	    char *indexName,
	    char *accessMethodName,
	    List *attributeList)
{
    NameData heapNameData;
    NameData indexNameData;
    NameData amNameData;

    namestrcpy(&heapNameData,heapName);
    namestrcpy(&indexNameData,indexName);
    namestrcpy(&amNameData, accessMethodName);
    
    DefineIndex(&heapNameData,
		&indexNameData,
		&amNameData,
		attributeList,
		NIL, NULL, NIL);
}

#define MORE_THAN_THE_NUMBER_OF_CATALOGS 256

bool
BootstrapAlreadySeen(Oid id)
{
    static Oid 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);
}

/* ----------------
 *	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(char *type)
{
    int		i;
    Relation	rdesc;
    HeapScanDesc	sdesc;
    HeapTuple	tup;
    struct	typmap	**app;
    
    if (Typ != (struct typmap **)NULL) {
	for (app = Typ; *app != (struct typmap *)NULL; app++) {
	    if (strncmp((*app)->am_typ.typname.data, type, NAMEDATALEN) == 0) {
		Ap = *app;
		return((*app)->am_oid);
	    }
	}
    } else {
	for (i = 0; i <= n_types; i++) {
	    if (strncmp(type, Procid[i].name, NAMEDATALEN) == 0) {
		return(i);
	    }
	}
	if (DebugMode)
	    printf("backendsup.c: External Type: %.*s\n", NAMEDATALEN, type);
        rdesc = heap_openr(TypeRelationName);
        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;
	    memmove((char *)&(*app++)->am_typ,
		    (char *)GETSTRUCT(tup), 
		    sizeof ((*app)->am_typ));
        }
        heap_endscan(sdesc);
        heap_close(rdesc);
        return(gettype(type));
    }
    elog(WARN, "Error: unknown type '%s'.\n", type);
    err();
}

/* ----------------
 *	AllocateAttribute
 * ----------------
 */
AttributeTupleForm  /* XXX */
AllocateAttribute()
{
    AttributeTupleForm attribute =
	(AttributeTupleForm)malloc(sizeof(FormData_pg_attribute)*1); 
    
    if (!PointerIsValid(attribute)) {
	elog(FATAL, "AllocateAttribute: malloc failed");
    }
    memset(attribute, 0, sizeof *attribute);
    
    return (attribute);
}

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

unsigned char MapEscape(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 (char *str)
{
    hashnode	*node;
    int        len;
    
    len= strlen(str);

    node = FindStr(str, len, 0);
    if (node) {
	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(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(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(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(char *str, int strlength, int mderef)
{
    hashnode	*temp, *trail, *newnode;
    int		hashresult;
    int		len;
    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] = (macro*)malloc(sizeof(macro)*1);
    
    /*
     *  Some of the utilites (eg, define type, create relation) assume
     *  that the string they're passed is a char16.  We get array bound
     *  read violations from purify if we don't allocate at least 16
     *  bytes for strings of this sort.  Because we're lazy, we allocate
     *  at least sixteen bytes all the time.
     */
    
    if ((len = strlength + 1) < 16)
	len = 16;
    
    strtable [strtable_end]->s = malloc((unsigned) len);
    strtable[strtable_end]->mderef = 0;
    strcpy (strtable[strtable_end]->s, str);
    
    /* Now put a node in the hash table */
    
    newnode = (hashnode*)malloc(sizeof(hashnode)*1);
    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(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(char *xmacro)
{
    hashnode *node;
    int macro_indx;
    
    if ((node = FindStr(xmacro,strlen(xmacro),0))!=NULL)
	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(int indx1, int 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);
    }
}

/*
 *  index_register() -- record an index that has been set up for building
 *			later.
 *
 *	At bootstrap time, we define a bunch of indices on system catalogs.
 *	We postpone actually building the indices until just before we're
 *	finished with initialization, however.  This is because more classes
 *	and indices may be defined, and we want to be sure that all of them
 *	are present in the index.
 */
void
index_register(Name heap,
	       Name ind,
	       int natts,
	       AttrNumber *attnos,
	       uint16 nparams,
	       Datum *params,
	       FuncIndexInfo *finfo,
	       PredInfo *predInfo)
{
    Datum *v;
    IndexList *newind;
    int len;
    MemoryContext oldcxt;
    
    /*
     *  XXX mao 10/31/92 -- don't gc index reldescs, associated info
     *  at bootstrap time.  we'll declare the indices now, but want to
     *  create them later.
     */
    
    if (nogc == (GlobalMemory) NULL)
	nogc = CreateGlobalMemory("BootstrapNoGC");
    
    oldcxt = MemoryContextSwitchTo((MemoryContext) nogc);
    
    newind = (IndexList *) palloc(sizeof(IndexList));
    newind->il_heap = (Name) palloc(sizeof(char16));
    strncpy(&(newind->il_heap->data[0]), &(heap->data[0]), NAMEDATALEN);
    newind->il_ind = (Name) palloc(sizeof(char16));
    strncpy(&(newind->il_ind->data[0]), &(ind->data[0]), NAMEDATALEN);
    newind->il_natts = natts;
    
    if (PointerIsValid(finfo))
	len = FIgetnArgs(finfo) * sizeof(AttrNumber);
    else
	len = natts * sizeof(AttrNumber);
    
    newind->il_attnos = (AttrNumber *) palloc(len);
    memmove(newind->il_attnos, attnos, len); 
    
    if ((newind->il_nparams = nparams) > 0) {
	v = newind->il_params = (Datum *) palloc(2 * nparams * sizeof(Datum));
	nparams *= 2;
	while (nparams-- > 0) {
	    *v = (Datum) palloc(strlen((char *)(*params)) + 1);
	    strcpy((char *) *v++, (char *) *params++);
	}
    } else {
	newind->il_params = (Datum *) NULL;
    }
    
    if (finfo != (FuncIndexInfo *) NULL) {
	newind->il_finfo = (FuncIndexInfo *) palloc(sizeof(FuncIndexInfo));
 	memmove(newind->il_finfo, finfo, sizeof(FuncIndexInfo)); 
    } else {
	newind->il_finfo = (FuncIndexInfo *) NULL;
    }
    
    if (predInfo != NULL) {
	newind->il_predInfo = (PredInfo*)palloc(sizeof(PredInfo));
	newind->il_predInfo->pred = predInfo->pred;
	newind->il_predInfo->oldPred = predInfo->oldPred;
    } else {
	newind->il_predInfo = NULL;
    }
    
    newind->il_next = ILHead;
    
    ILHead = newind;
    
    (void) MemoryContextSwitchTo(oldcxt);
}

void
build_indices()
{
    Relation heap;
    Relation ind;
    
    for ( ; ILHead != (IndexList *) NULL; ILHead = ILHead->il_next) {
	heap = heap_openr(ILHead->il_heap);
	ind = index_openr(ILHead->il_ind);
	index_build(heap, ind, ILHead->il_natts, ILHead->il_attnos,
		    ILHead->il_nparams, ILHead->il_params, ILHead->il_finfo,
		    ILHead->il_predInfo);
	
	/*
	 * 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 
	 */
	heap = heap_openr(ILHead->il_heap);
	
	if (!BootstrapAlreadySeen(heap->rd_id))
	    UpdateStats(heap->rd_id, 0, true);
    }
}

