/*
 * 	writeset.c - writeset processing (wb)
 *
 * 	This module contains all the functions pertaining to 
 *	write set processing.
 *
 *
 */

#include "postgres.h"

#include "access/genam.h"
#include "access/heapam.h"
#include "access/printtup.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "catalog/pg_index.h"
#include "catalog/pg_operator.h"
#include "nodes/execnodes.h"
#include "nodes/parsenodes.h"
#include "utils/relcache.h"
#include "utils/fmgroids.h"
#include "miscadmin.h"
#include "storage/proc.h"
#include "parser/parse.h"

#include "replication/replication.h"

#ifdef REPL_UPDATE_EVERYWHERE
#include "catalog/pg_transactionid.h"
#include "utils/builtins.h"
#endif

/* DAR HACK these globals used to be in global.c and
    one day maybe they'll go back*/
WriteSet    MyWriteSet;
/* (mc) */
WriteSet    MyPeerWriteSet;
/**/

bool 		xact_started = false; /* (mc) */
TransactionType txn_type = NOT_REPLICATED;

static bool _execCmdList(RelInfo *rel, int currSect, int Granted, bool doTxnDelim);

static void _execQuery(CmdInfo *cmd_info);
static void _execUtility(CmdInfo *cmd);
static RelInfo* _findrel(WriteSetPtr ws, const char *relname);
static void _freeCmdInfo(CmdInfo *cmd);
static QueryInfo* _initQueryInfo(void);
static void _freeQueryInfo(QueryInfo *qry);
static bool _getindexinfo(RelInfo *rel_info);
static void _serializeQueryInfo(QueryInfo *qry, ebufptr extbuf);
static void _serializeCmdInfo(CmdInfo *cmd_info, ebufptr extbuf);
static CmdInfo* _unserializeQueryInfo(CmdType cmd, RelInfo *rel_info, bufsockptr bsock);
static CmdInfo* _unserializeCmdInfo(CmdType cmd, bufsockptr bsock);
static void _getEqualProcs(RelInfo *rel);
static regproc _equalProcLookup(Oid attr_typeid);
static void printPkeyValues(char *values, int numKeys, AttrNumber *attrNums, TupleDesc tdesc);
/*static bool WriteSetAtomicLockSeq(char **relNames, int granted[], RelInfo **relinfos, int numNames);*/
static char* printattribute(char *data, AttrNumber attnum, TupleDesc tdesc, bool isNull);
void WriteSetCollectTuple(ItemPointer origPtr, TupleTableSlot *slot, CmdInfo *currQuery);
/* (mc) */
extern IndexInfo *BuildIndexInfo(HeapTuple indexTuple);
extern char * get_username(Oid userId, bool *isSuper);
/**/

/*
*	WriteSetAtomicLockSeq (remote & local backend)
* 
*	Request locks on several relations in an atomic step. Atomic means that
*	from the point of the lock manager, no other locks can be set until
*	all the requests from the current set are granted or queued.
*
*	param:
*		relNames - names of the relations to lock
*		granted - array indicating which locks have 
*			been granted (on return)
*		numNames - number of relation names
*	return:
*		bool - true for success, false for failure

static bool
WriteSetAtomicLockSeq(char **relNames, int granted[], RelInfo **relinfos, int numNames)
{

	int			i;
	Relation 	rd;
	
	LockInfoData	lockinfos[numNames];
	LOCKMODE lockmodes[numNames];

	for(i = 0; i < numNames; i++)
	{	
		rd = RelationNameGetRelation(relNames[i]);

		granted[i] = 0;
		.
		lockinfos[i].lockRelId.relId = ((LockInfo) rd->rd_lockInfo)->lockRelId.relId;
		lockinfos[i].lockRelId.dbId = ((LockInfo) rd->rd_lockInfo)->lockRelId.dbId;	
		lockmodes[i] = WRITE_LOCK;
		
	}
	return TRUE;
}
*/

/*
*	WriteSetWaitOnock (remote & local backend)
*
*	Waits on locks that could not be granted immediately
*
*	param:
*		relNames - names of the relations modified by the 
*				write set
*		granted - array indicating which locks have 
*			been granted (on return)
*		numNames - number of relation names
*	return:
*		int - number of locks granted during last call

int
WriteSetWaitOnLock(char **relNames, int granted[], int numNames)
{
	
	int 		numWoken;
	
	Oid			wokenRelIds[numNames];	
	
	MemSet(&wokenRelIds, 0, numNames*sizeof(Oid));
	
	numWoken = 0;
	return numWoken;
}
*/

/*
*	WriteSetAddQuery (local backend)
*
*	Adds query data to the write set structure. Target tuples
*	are also collected during the execution of this procedure.
*
*	param:
*		ws - pointer to the write set to add the query to
*		query - the query to add
*	return:
*		void
*/

void
WriteSetAddQuery(WriteSetPtr ws, char *query, CmdType cmd, char* relname)
{
	Relation 		rel;
	RelInfo			*rel_info = NULL;
	void			*info_ptr = NULL;
	TupleCollection *tcoll;
	TupleDesc		tdesc;
	Dlelem			*tmp;
	
	/*
	*	Look for an existing relation information struct in the
	*	write set. It exists whenever a preceding query also
	*	targeted this relation. If no struct is found, create 
	*	a new one and gather all necessary info (primary key index...)
	*/
	if((rel_info = _findrel(ws, relname)) == NULL)
	{	
#ifdef WS_DEBUG_LOCAL
		elog(DEBUG, "[%d] Creating new RelInfo struct for relation %s", MyProc->pid, relname);
#endif
		rel_info = (RelInfo *) malloc(sizeof(RelInfo));
		strncpy(rel_info->name, relname, NAMEDATALEN + 1);
		rel = RelationNameGetRelation(relname);
		if(!rel)
			elog(ERROR, "%s: Table doesn't exist", relname);
		rel_info->relOid = RelationGetRelid(rel);
		/*elog(DEBUG, "%s has oid=%d", relname, (int)rel_info->relOid );  (mc) */
		if(!_getindexinfo(rel_info))
		{
			elog(DEBUG, "exiting");
			proc_exit(600);
		}
#ifdef WS_DEBUG_LOCAL
	/* swu (rem) */
	elog(DEBUG, "[%d] WriteSetAddQuery: after _getindexinfo", MyProc->pid);
#endif
		rel_info->querylist.list = DLNewList();
		rel_info->querylist.size = 0;
		tmp = DLNewElem(rel_info);
#ifdef WS_DEBUG_LOCAL
	/* swu (rem) */
	elog(DEBUG, "[%d] WriteSetAddQuery: before DLAddTail", MyProc->pid);
	elog(DEBUG, "WriteSetAddQuery: targetrels size %d", ws->targetrels.size);
#endif
		DLAddTail(ws->targetrels.list, tmp );
#ifdef WS_DEBUG_LOCAL
	/* swu (rem) */
	elog(DEBUG, "[%d] WriteSetAddQuery: after DLAddTail", MyProc->pid);
#endif
		ws->targetrels.size++;
	}
	else
	{
		rel = RelationIdGetRelation(rel_info->relOid);
	}

#ifdef WS_DEBUG_LOCAL
	/* swu (rem) */
	elog(DEBUG, "[%d] WriteSetAddQuery: before switch cmd", MyProc->pid);
#endif
	
	/*
	*	Generate a new query info struct. Process the query in order to
	*	find all target tuples.
	*/
	switch(cmd)
	{	
		/* (mc) the following checks are redundant, if we are here
		 * then we our query is either insert, delete or update.
		 * they are left to be a smooth transition from old code
		*/	
		case CMD_INSERT:
		case CMD_DELETE:
		case CMD_UPDATE:
			info_ptr = (void *) _initQueryInfo();
			((QueryInfo *)info_ptr)->info.cmd = cmd;
			if (query != NULL)
				((QueryInfo *)info_ptr)->info.stmt = strdup(query);
			
			tcoll = &((QueryInfo *)info_ptr)->tcoll;
			tcoll->rel = rel_info;
			tdesc = RelationGetDescr(rel);
			tcoll->numAttrs = tdesc->natts;
			tcoll->changeArray = (char *) malloc(tcoll->numAttrs * sizeof(char));
			MemSet(tcoll->changeArray, ' ', tcoll->numAttrs * sizeof(char));
			if(cmd == CMD_DELETE || cmd == CMD_UPDATE)
			{
				tcoll->pkey_values = (char **) malloc(TUPLE_LIMIT * sizeof(char *));
				MemSet(tcoll->pkey_values, 0, TUPLE_LIMIT * sizeof(char *));
			}
			if(cmd == CMD_INSERT || cmd == CMD_UPDATE)
			{
				tcoll->newtup_data = (char **) malloc(TUPLE_LIMIT * sizeof(char *));
				MemSet(tcoll->newtup_data, 0, TUPLE_LIMIT * sizeof(char *));
			}


			ws->currQuery = (CmdInfo *) info_ptr; //this should be passed to the executor through 
												   //EState struct instead of through global context...
			
			/*DAR HACK to do pg_exec_query is not what we want here*/
			/*pg_exec_query(query);*/
			
			/*
			*	Assign the appropriate execution section number, which is used
			*	at remote backends to preserve correct execution order
			*	of the queries when replicating on statement-level
			*/
			
			/* (mc) moved after tuple collection 
			if(tcoll->numTuples == 0)
			{
				 _freeQueryInfo((QueryInfo *) info_ptr); 
				 info_ptr = NULL; 
			}
			else if(tcoll->numTuples > tcoll->maxTuples)
			{
				ws->currQuery->sect = ++ws->critSect;
				ws->critSect++;
				//tcoll->numTuples = 0;
			}
			else
			{
				ws->currQuery->sect = ws->critSect;
			}
			
			ws->currQuery = NULL;
			*/
			break;
		default:
			elog(DEBUG, "WriteSetAddQuery: (error) unexpected command (%d)", (int)cmd);
			break;
	}
	
	if(info_ptr)
	{
		DLAddTail(rel_info->querylist.list, DLNewElem(info_ptr));
		rel_info->querylist.size++;
		
		/* (mc)
		addBkndDistRecoveryInfo();
		*/
	}
}

/* (mc) */
bool
WriteSetAllowUtility(bool isMaster, Node *utilityStmt, bool *replicate)
{
	DropStmt	*drop_stmt;
	CopyStmt   	*copy_stmt;
	bool		allow = false;
	
	switch (nodeTag(utilityStmt))
	{
		case T_TransactionStmt: 	/* begin, rollback, commit 	-*/
		case T_ClosePortalStmt: 	/* close 				-*/
		case T_FetchStmt: 			/* move, fetch 			-*/
		case T_ExplainStmt:			/* explain 				-*/
		case T_VariableSetStmt:		/* set variable 		-*/
		case T_VariableShowStmt:	/* show variable 		-*/
		case T_VariableResetStmt:	/* reset variable 		-*/
		case T_IndexStmt:			/* create index 		-*/
			allow = true;	
			*replicate = false;
			break;
		
		case T_CreateStmt:			/* create table			-*/
		case T_AlterTableStmt:		/* alter table: add/alter/drop cloumn, add/drop constraint, alter owner -*/
		case T_TruncateStmt:		/* truncate 			-*/
		case T_CommentStmt:			/* comment 				-*/
		case T_RenameStmt:			/* alter table: rename table or column	-*/
		case T_GrantStmt:			/* grant, revoke		-*/
		case T_CreateUserStmt:		/* create user 			-*/
		case T_AlterUserStmt:		/* alter user 			-*/
		case T_DropUserStmt:		/* drop user 			-*/
		case T_CreateGroupStmt:		/* create group 		-*/
		case T_AlterGroupStmt:		/* alter group 			-*/
		case T_DropGroupStmt:		/* drop group 			-*/
		case T_ViewStmt:			/* create view 			-*/	
		case T_ConstraintsSetStmt:	/* set constraint 		-*/
		case T_ClusterStmt:			/* cluster 				-*/
		case T_VacuumStmt:			/* vacuum, analyze 		-*/
			allow = isMaster? true:false;		
			*replicate = true;
			break;

		case T_LockStmt:			/* lock 				-*/
			allow = isMaster? true:false;		
			*replicate = false;
			break;
			
		case T_DropStmt:		/* drop table, sequence, view, index, rule, type */
			drop_stmt = (DropStmt *) utilityStmt;
			switch (drop_stmt->removeType)
			{
				case DROP_INDEX:	/*-*/	 
					allow = true;
					*replicate = false;
					break;
				case DROP_VIEW:		/*-*/
				case DROP_TABLE:	/*-*/
					allow = isMaster? true:false;		
					*replicate = true;
					break;
				case DROP_SEQUENCE: /*-*/
				case DROP_TYPE_P:	/*-*/
				case DROP_RULE:		/*-*/
					allow = false;		
					*replicate = false;
					break;
			}
			break;
			
		case T_CreatePLangStmt:		/* create language 		-*/
		case T_DropPLangStmt:		/* drop language 		-*/
		case T_VersionStmt:			/* create version 		-*/
		case T_ReindexStmt:			/* reindex 				-*/
		case T_ListenStmt:			/* listen 				-*/
		case T_UnlistenStmt:		/* unlisten 			-*/
		case T_LoadStmt:			/* load 				-*/
		/*
		case T_RecipeStmt:		not supported
		*/
		
		case T_DefineStmt:			/* create operator, type, aggregate -*/

		case T_CreatedbStmt:		/* create database 		-*/
		case T_DropdbStmt:			/* drop database 		-*/
		case T_RuleStmt:			/* create rule 			-*/
		/* drop rule is handled above 		*/
		case T_CreateSeqStmt:		/* create sequence 		-*/
		/* drop sequence is handled above 	*/
		case T_ProcedureStmt:		/* create function		-*/
		case T_RemoveFuncStmt:		/* drop function 		-*/
		case T_CreateTrigStmt:		/* create trigger 		-*/
		case T_DropTrigStmt:		/* drop trigger 		-*/
		/* create aggregate is handled above */
		case T_RemoveAggrStmt:		/* drop aggregate 		-*/
		/* create operator is handled above	 */
		case T_RemoveOperStmt:		/* drop operator 		-*/
	
		case T_NotifyStmt:			/* notify				-*/
		case T_CheckPointStmt:		/* checkpoint 			-*/
			allow = false;		
			*replicate = false;
			break;
		
		case T_CopyStmt:			/* copy 				*/
			copy_stmt = (CopyStmt *) utilityStmt;
			allow = copy_stmt->direction == TO ? true:false;
			*replicate = false;
			break;
		
		default:
			allow = false;
			elog(ERROR, "ProcessUtility: command #%d unsupported",
				 nodeTag(utilityStmt));
			break;
	}
	
	return allow;
	

}


bool
sendComplementaryWS(Node *utilityStmt, int *type )
{
	bool send = false;
	if (nodeTag(utilityStmt) == T_ViewStmt)
	{
		*type = T_ViewStmt;
		send = true;
	}
	if (nodeTag(utilityStmt) == T_CreateStmt)
	{
		*type = T_CreateStmt;
		send = true;
	}
	return send;
	
}


void
WriteSetAddUtility(WriteSetPtr ws, char *query)
{
	CmdInfo			*info_ptr;
	RelInfo			*rel_info;

	#ifdef WS_PRINT
		printWriteSet(ws, "WriteSetFormUtility: Before adding the utility command to the WS");
	#endif
	
	info_ptr = malloc(sizeof(CmdInfo));
	info_ptr->cmd = CMD_UTILITY;
	info_ptr->stmt = strdup(query);
	
	rel_info = (RelInfo *) malloc(sizeof(RelInfo));

	/*MemSet(rel_info->name, '\0',  (NAMEDATALEN+1)*sizeof(char)); */
	rel_info->relOid = (Oid) NULL;
	strcpy(rel_info->name, " ");
	
	/*MemSet(rel_info->indexinfo.indexName, '\0',  (NAMEDATALEN)*sizeof(char));*/
	rel_info->indexinfo.indexOid = (Oid) NULL;
	rel_info->indexinfo.numKeys = 0;
	strcpy(rel_info->indexinfo.indexName, " ");
	
	rel_info->querylist.list = DLNewList();
	rel_info->querylist.size = 0;
	
	DLAddTail(rel_info->querylist.list, DLNewElem(info_ptr));
	rel_info->querylist.size++;
		
	DLAddTail(ws->targetrels.list, DLNewElem(rel_info));
	ws->targetrels.size++;

	ws->currQuery = info_ptr;			
	
	#ifdef WS_PRINT
		printWriteSet(ws, "WriteSetFormUtility: After adding the utility command to the WS");
	#endif
	
	/* (mc) 
	addBkndDistRecoveryInfo();
	*/

}

char *
WriteSetFormCompUtility(Node *utilityStmt, int createType)
{
	bool		isSuper;
	char		*stmt;
	char		* user, 
				* relName; 
	int			size = 256;		/* initial default buffer size */
	CreateStmt 	* create_stmt;
	ViewStmt 	* view_stmt;
	
	stmt = (char *) malloc(size * sizeof(char));
	MemSet(stmt, '\0', size*sizeof(char));
	
	user = (char *) get_username(GetUserId(), &isSuper);

	if (createType == T_CreateStmt)
	{
		create_stmt = (CreateStmt *) utilityStmt;
		relName = strdup(create_stmt->relname);
	}
	else if (createType == T_ViewStmt)
	{
		view_stmt = (ViewStmt *) utilityStmt;
		relName = strdup(view_stmt->viewname);
	}
	else
	{
		/*elog....*/
	}

	strcpy(stmt, "GRANT ALL ON ");
	strcat(stmt, relName);
	strcat(stmt, " TO ");
	strcat(stmt, user);

	#ifdef WS_DEBUG_LOCAL
		elog(DEBUG, "WriteSetFormCompUtility: compl. stmt=%s", stmt);
	#endif
	
	return stmt;
}
/**/


/*
*	WriteSetCollectTuple (local backend)
*
*	Adds tuple information to the write set
*
*	param:
*		origPtr - tuple id of the original (unmodified) tuple, used to 
			get primary key values
		slot - tupletable slot containing the modified tuple (copy of the
			original one)
		currQuery - pointer to the query info struct in the write set to which
			the tuple has to be added to.
*	return:
*		void
*/
void
WriteSetCollectTuple(ItemPointer origPtr, TupleTableSlot *slot, CmdInfo *currQuery)
{
	Relation			rel;
	HeapTupleData		tuple; /* (mc) */
	TupleDesc			tdesc;
	IdxInfo				*idx_info;
	CmdType				cmd;
	int 				i,
						natts,
						maxTuples,
						numTuples,
						length,
						attnum,
						attindex;
	Oid					typoutput;
	char				*tup_data,
						*tup_head,
						**outputstr;
	TupleCollection 	*tcoll;
	bool				isNull;
	Buffer				dummy;
	Datum				curr_attr;
	Form_pg_attribute 	*att;
	//HeapTupleData		htuple;
	//DAR HACK new param for typtoout parameter.
	Oid					typElm;
	bool				typisvarlena;
	
	if(!currQuery || currQuery->cmd == CMD_UTILITY)
		return;

	cmd = currQuery->cmd;
	
#ifdef WS_DEBUG_LOCAL
	elog(DEBUG, "Collecting tuple");
#endif

	tcoll = &((QueryInfo *) currQuery)->tcoll;
	numTuples = tcoll->numTuples;
	maxTuples = tcoll->maxTuples;
	idx_info = &tcoll->rel->indexinfo;
	rel = RelationIdGetRelation(tcoll->rel->relOid);
	
	/*
	*	If the maximum number of tuples (TUPLE_LIMIT) has been collected,
	*	do not collect any more tuples.
	*/
	if(numTuples >= maxTuples)
	{	
		tcoll->numTuples++;
		return; 
	}
	
	if(slot->ttc_descIsNew)
	{
		tdesc = RelationGetDescr(rel);
	}
	else
	{
		tdesc = slot->ttc_tupleDescriptor;
	}
	att	= tdesc->attrs;
	natts = tdesc->natts;
	Assert(natts == tcoll->numAttrs);

	if(cmd == CMD_DELETE || cmd == CMD_UPDATE)
	{
#ifdef WS_DEBUG_LOCAL
		elog(DEBUG, "[%d] Collecting primary key info", MyProc->pid);
#endif	

		outputstr = (char **) malloc(idx_info->numKeys * sizeof(char *));
		
		/* DAR HACK - heap_fetch has some different parameters
					so here is a todo item.  It used to return
					a HeapTuple structure now it populates the struct
					We probably need a copy here (heap_copytuple) here.
		tuple = heap_fetch(rel, SnapshotNow, origPtr, &dummy);
		*/
		/* BAP - This is still broken.  The new 5th value seems to be a ref
					to a doing sequential scans.  This should work,
					but won't take advantage of indexes
		*/
		/* (mc) */
		tuple.t_datamcxt = CurrentMemoryContext;
		tuple.t_data = NULL;
		tuple.t_self = *origPtr; 
		/**/ 

		heap_fetch(rel, SnapshotNow, &tuple, &dummy, NULL); /* (mc) */

		/*  swu 
		 * cannot fetch the tuple */
		if(!tuple.t_data)
			elog(ERROR, "tuple concurrently updated");

		length = VARHDRSZ;
		for(i=0; i < idx_info->numKeys && i < INDEX_MAX_KEYS; i++)
		{
			attnum = idx_info->attrNums[i]; //remember that arrays are zero-based
			attindex = attnum - 1;
			curr_attr = heap_getattr(&tuple, attnum, /* (mc) */
					 tdesc, &isNull); //pkey_attrs cannot be NULL!!
						  
			/* DAR HACK - we need to use getTypeOutputInfo here which
						has two new parameters, but it checks the 
						validity so we can combine the next two
						lines.  I think.
			typoutput = typtoout((Oid) att[attindex]->atttypid);
			if (OidIsValid(typoutput))
			*/
			if (getTypeOutputInfo((Oid) att[attindex]->atttypid, &typoutput, 
									&typElm, &typisvarlena))
			{
				/*
				*	Convert attribute value to string (->network format)
				*/
				outputstr[i] = fmgr(typoutput, curr_attr,
							 /* DAR HACK I think we can use
							 	typElm from the last cache search
							 gettypelem(att[attindex]->atttypid), */
							 typElm,
							 att[attindex]->atttypmod);
				length += strlen(outputstr[i]) + 1;
			}
			else
			{
				elog(DEBUG, "tuple attribute unprintable");
				proc_exit(700);
			}
		}
		
		tup_data = tcoll->pkey_values[numTuples] = (char *) malloc(length * sizeof(char));
		/* DAR HACK Use VARATT_SIZEP?
		VARSIZE(tup_data) = length;
		*/
		VARATT_SIZEP(tup_data) = length;
		tup_data = VARDATA(tup_data);
		for(i=0; i < idx_info->numKeys && i < INDEX_MAX_KEYS; i++)
		{
			/*
			* Paste all attributes into one char array. ATTN: null chars
			* are also copied!
			*/
			int len = strlen(outputstr[i]) + 1;
			strncpy(tup_data, outputstr[i], len);
			tup_data += len;
			pfree(outputstr[i]);
		}
		ReleaseBuffer(dummy);
		free(outputstr);
	} /*if(cmd == CMD_DELETE || cmd == CMD_UPDATE)*/
	
	
	if(cmd == CMD_INSERT || cmd == CMD_UPDATE)
	{
#ifdef WS_DEBUG_LOCAL
		elog(DEBUG, "Collecting tuple data info");
#endif	
		tuple = *(slot->val); /* (mc) */
		outputstr = (char **) malloc(natts * sizeof(char *));
		length = natts + VARHDRSZ; // one change state byte for each attribute;
		for(i = 0; i < natts ; i++)
		{
			/*
			*	Do the same as above with the new tuples. Only modified
			*	tuple values are added to the ws. Which tuple atts have been 
			*	modified can be found in the <changeArray> var.
			*/
			if(tcoll->changeArray[i] == 'r')
			{
				curr_attr = heap_getattr(&tuple, i+1, /* (mc) */
								  tdesc, &isNull);
			}
			else
			{
				isNull = TRUE;
			}
			
			if(isNull)
			{
				outputstr[i] = NULL;
			}
			else
			{
				/* DAR HACK - we need to use getTypeOutputInfo here which
						has two new parameters, but it checks the 
						validity so we can combine the next two
						lines.  I think.
				typoutput = typtoout((Oid) att[i]->atttypid);
				if (OidIsValid(typoutput))
				*/
				/*
				typoutput = 0;
				typElm = 0;
				typisvarlena = TRUE;
				*/
				if (getTypeOutputInfo((Oid) att[i]->atttypid, 
								&typoutput, &typElm, &typisvarlena))
				{
					outputstr[i] = fmgr(typoutput, curr_attr,
								 /* DAR HACK I think we can use
								 	typElm from the last cache search
								 gettypelem(att[i]->atttypid),*/
								 typElm,
								 att[i]->atttypmod);
					length += strlen(outputstr[i]) + 1;
				}
				else
				{
					elog(DEBUG, "tuple attribute unprintable");
					proc_exit(700);
				}
			}
		}
		
		tup_head = tcoll->newtup_data[numTuples] = (char *) malloc(length * sizeof(char));
		/* DAR HACK Use VARATT_SIZEP?
		VARSIZE(tup_head) = length;
		*/
		VARATT_SIZEP(tup_head) = length;
		tup_head = VARDATA(tup_head);
		tup_data = tup_head + natts;
		
		strncpy(tup_head, tcoll->changeArray, natts);
		
		for(i=0; i < natts ; i++)
		{
			char *str = outputstr[i];
			
			if(!str && *tup_head == 'r')
			{
				/*
				* mark all attributes that have been changed and that have a null
				* value as new value.
				*/
				*tup_head = 'n';
			}
			else if(*tup_head == 'r')
			{
				int len = strlen(str) + 1;
				
				strncpy(tup_data, str, len);
				tup_data += len;
				pfree(str);
			}
			tup_head++; 
		}
		free(outputstr);
	}
	tcoll->numTuples++;
}

/*
*	WriteSetFormTuple (remote bknd)
*
*	Forms a new tuple by modifying a given tuple according to the data
*	stored in the write set
*
*	param:
*		rel - the relation the tuple belongs to
*		oldTuple - the unmodified tuple
*		newtup_data - the modified attributes from the write set
*			consists of a character array indicating which attributes
*			have been altered, and the altered attribute values themselves
*   		Example: |r| | |n|5|6|3|7|\0|
*			Explanation: A 4-attribute relation is modified. The first
*				attribute value must be replaced by "5637" , the last one must
*				be set to NULL. All other attributes remain unchanged.
*		tdesc - the tuple descriptor
*	return:
*		HeapTuple - the new (modified) tuple
*/
HeapTuple
WriteSetFormTuple(Relation rel, HeapTuple oldTuple, char *newtup_data, TupleDesc tdesc)
{
	bool		isNull;
	int			i,
				natts = tdesc->natts,
				numTargets;
	char		*tup_data,
				*tup_head,
				replVal[tdesc->natts],
				replNull[tdesc->natts];
	AttrNumber	targetNums[tdesc->natts];
	Datum		newvalues[tdesc->natts],
				oldvalues[tdesc->natts];
	
	MemSet(replVal, ' ', natts * sizeof(char));
	MemSet(replNull, ' ', natts * sizeof(char));
	MemSet(oldvalues, 0, natts * sizeof(Datum));
	MemSet(newvalues, 0, natts * sizeof(Datum));
	 
	tup_head = newtup_data;
	tup_data = tup_head + natts;
	numTargets = 0;
	for(i = 0; i < natts; i++)
	{
		switch(*tup_head)
		{
			case ' ':
				/*default array elem value*/
				oldvalues[i] =
				heap_getattr(oldTuple,
							 AttrOffsetGetAttrNumber(i),
							 tdesc,
							 &isNull);
				replNull[i] = (isNull) ? 'n' : ' ';
				break;
			case 'n':
				replNull[i] = 'n';
				break;
			case 'r':
				targetNums[numTargets] = i + 1;
				numTargets++;
				replVal[i] = 'r';
				break;
			default:
				elog(ERROR, "WriteSetFormTuple: Read unknown change type (%c)", *tup_head);
				break;
		}
		tup_head++;
	}
	
	/*
	* decompose the newtup_data array into datum pointers, then replace the 
	* appropriate datum pointers in the newvalues array. Then, form the new tuple
	* using this array.
	*/
	WriteSetGetDatum(newvalues, tup_data, numTargets, targetNums, tdesc);
	
	for(i = 0; i < natts; i++)
	{
		if(replVal[i] == ' ' && replNull[i] != 'n')
			newvalues[i] = oldvalues[i];
	}
	
	return heap_formtuple(tdesc, newvalues, replNull);

}

/*
*	Write WriteSetGetDatum (remote bknd)
*
*	Decomposes a character array of tuple data into Datum pointers to
*	the different attribute values
*
*	param:
*		values - the array containing the datum pointers upon return. This 
*				array contains one slot for each attribute of a certain table
*		data - the char array. The array contains ONLY a subset of all attributes
*				of the above table
*		numTargets - the number of attributes to decompose
*		targetNums - the attribute numbers of the attributes to decompose
*			Example : We have modified a 4-attribute relation tuple. Only
*				the first and last attr have been altered. The values
*				array has 4 slots, the data array contains the values of
*				the modified atts. numTargets is 2, targetNums is |1|4|.
*				Upon return, the datum array will contain the datum pointers
*				to the new values in its first and fourth slot.
*	
*	return:
*		void
*/
void
WriteSetGetDatum(Datum *values, char *data, int numTargets, AttrNumber *TargetNums, TupleDesc tdesc)
{
	char				*tup_data,
						c;
	int					i,
						j,
						totalNatts,
						att_ind;
	int16				i16;
	int32				i32;
	bool				found;
	Oid 				typinput[numTargets],
						attTypes[numTargets];
	Form_pg_attribute 	*att;
	//DAR HACK new param for typtoout parameter.
	Oid					typoutput;
	Oid					typElm;
	bool				typisvarlena;
	
	att = tdesc->attrs;
	totalNatts = tdesc->natts;
	MemSet(values, 0, totalNatts * sizeof(Datum));
	tup_data = data;
	
	/*
	* For each modified attribute, get the oid of the procedure 
	* that converts this attribute value from character array to 
	* the appropriate attribute type.
	*/
	for(i = 0; i < numTargets; i++)
	{
		att_ind = TargetNums[i] - 1;
		attTypes[i] = att[att_ind]->atttypid;
		found = false;
		for(j = 0; j < i; j++)
		{
			if(attTypes[i] == attTypes[j])
			{
				typinput[i] = typinput[j];
				found = TRUE;
				break;
			}
		}
		
		/* DAR HACK intotyp is a function which was put into printtup.c,
					so a to do is called for here.*/
		if(!found)
			typinput[i] = intotyp(attTypes[i]);
		
	}
	
	/*
	* Get the appropriate datum pointer value for the new attribute value
	*/
	for(i = 0; i < numTargets; i++)
	{
		att_ind = TargetNums[i] - 1;
		if (OidIsValid(typinput[i]))
		{
			/* elog(DEBUG, "att[att_ind]->attlen=%d",att[att_ind]->attlen); */
			switch(att[att_ind]->attlen)
			{
				case -1:
					/* DAR HACK - we need to use getTypeOutputInfo here which
						has two new parameters, but it checks the 
						validity so we can combine the next two
						lines.  I think. */
					if (getTypeOutputInfo((Oid) att[att_ind]->atttypid, 
								&typoutput, &typElm, &typisvarlena))
					{
						values[att_ind] = PointerGetDatum(fmgr(typinput[i], 
							tup_data,
							/* DAR HACK I think we can use
								typElm from the last cache search
							gettypelem(att[att_ind]->atttypid), */
							typElm,
							att[att_ind]->atttypmod));
						tup_data += strlen(tup_data) + 1;
					}
					break;
				case sizeof(char):
					c = DatumGetChar(Int32GetDatum(fmgr(typinput[i], tup_data)));
					values[att_ind] =  att[att_ind]->attbyval ?
									CharGetDatum(c) :
									PointerGetDatum(&c);				
					tup_data += strlen(tup_data) + 1;
					break;
				case sizeof(int16):
					i16 = DatumGetInt16(Int32GetDatum(fmgr(typinput[i], tup_data)));
					values[att_ind] = att[att_ind]->attbyval ?
								Int16GetDatum(i16) :
								PointerGetDatum(&i16);
					tup_data += strlen(tup_data) + 1;
					break;
				case sizeof(int32):
					i32 = (int32) fmgr(typinput[i], tup_data);
					values[att_ind] = att[att_ind]->attbyval ? 
										Int32GetDatum(i32) :
										PointerGetDatum(&i32);
					tup_data += strlen(tup_data) + 1;
					break;
				default:
					/* DAR HACK - we need to use getTypeOutputInfo here which
						has two new parameters, but it checks the 
						validity so we can combine the next two
						lines.  I think. */
					if (getTypeOutputInfo((Oid) att[att_ind]->atttypid, 
								&typoutput, &typElm, &typisvarlena))
					{
						values[att_ind] = PointerGetDatum( fmgr(typinput[i], 
							tup_data,
							/* DAR HACK I think we can use
								typElm from the last cache search
							gettypelem(att[att_ind]->atttypid), */
							typElm,
							att[att_ind]->atttypmod));
						tup_data += strlen(tup_data) + 1;
					}
					break;
			}	
		}
		else
		{
			elog(DEBUG, "WriteSetGetDatum: Unreadable value in data array");
			proc_exit(700);
		}
	}

}

/*
*	WriteSetProcess (remote and local backends)
*
*	processes a write set, from lock acquisition to query execution.
*	The abort/commit decision is not handled in here. Note that queries 
*	are only executed by the remote backend, since local backends 
*	do not defer their queries. If this procedure is called by a local
*	backend, only locks are acquired, without executing any queries.
*
*	param:
*		ws - write set to process
*		doTxnDelim - determines whether StartTransactionCommand/
*			CommitTransactionCommand (see xact.c) must be called
*	return:
*		void
*
*/
void
WriteSetProcess(WriteSetPtr ws, bool doTxnDelim)
{
	bool 	reqWaitForLocks;
	int		currSect,
			i;
	Dlelem 	*curr;
	
	struct {
		int		rel_num;
		int		granted[ws->targetrels.size];
		char	*name[ws->targetrels.size];
		RelInfo *rel_ptr[ws->targetrels.size];
	} rel_dict;
			
	rel_dict.rel_num = ws->targetrels.size;
	if(rel_dict.rel_num == 0)
		return;
	
	/*
	* Initialize dictionary structure for faster access to
	* the write set
	*/
	MemSet(&rel_dict.granted, 0, rel_dict.rel_num * sizeof(int));
	curr = DLGetHead(ws->targetrels.list);
	for(i=0; (i < rel_dict.rel_num) && curr; i++) 
	{
		rel_dict.rel_ptr[i] = (RelInfo *) DLE_VAL(curr);
		rel_dict.name[i] = rel_dict.rel_ptr[i]->name;
		curr = DLGetSucc(curr);
	}
	
	
	for(i = 0; i < rel_dict.rel_num; i++)
	{	
		if(txn_type == REPLICATED_REMOTE && rel_dict.rel_ptr[i]->name[0] != ' ')
		{
			/*
			*	get the procedure oids of the '=' operation
			*	for the index attributes of this relation
			*   (needed by the remote bknd to process the ws)
			*/
			#ifdef WS_DEBUG_REMOTE
				elog(DEBUG,"WriteSetProcess: getting EqualProcs");
			#endif
			_getEqualProcs(rel_dict.rel_ptr[i]);
		
		}
	}
	#ifdef WS_DEBUG_REMOTE
		elog(DEBUG,"WriteSetProcess: processing the ws");
	#endif
	
	if(txn_type == REPLICATED_REMOTE)
	{
		/*  swu  */
		/* set boundary for this transaction, BEGIN */
		hasAddedToSysTable = FALSE;
		/*StartTransactionCommand();*/
		/*BeginTransactionBlock();*/
		#ifdef WS_DEBUG_REMOTE
			elog(DEBUG,"[%d] WriteSetProcess: executing %d queries", MyProc->pid, rel_dict.rel_num);
		#endif

		i = 0;
		while(i < rel_dict.rel_num)
		{
			reqWaitForLocks = _execCmdList(rel_dict.rel_ptr[i], currSect, 
						/*rel_dict.granted[i] (mc) testing */ 1, doTxnDelim);

			i++;
		}
		#ifdef WS_DEBUG_REMOTE
			elog(DEBUG,"[%d] WriteSetProcess: after _execCmdList", MyProc->pid);
		#endif

		WriteSetInit(&MyWriteSet);

		/*  swu (rem) */
		MemoryContextResetAndDeleteChildren(CurrentMemoryContext);

		/*  swu */
		/* set boundary for this transaction, END */
		/*EndTransactionBlock();*/
		/*CommitTransactionCommand();*/
	}
}


/*
*	WriteSetInit (remote & local backend)
*
*	initializes a write set
*
*	param:
*		ws - the write set to initialize
*	return:
*		void
*/

void
WriteSetInit(WriteSetPtr ws)
{
#ifdef WS_DEBUG_LOCAL
	elog(DEBUG, "	WriteSetInit: Initializing write set struct");
#endif

	if( ws->targetrels.list != NULL && DLGetHead(ws->targetrels.list))
	{
		WriteSetFree(ws);
	}

	ws->targetrels.list = DLNewList();
	ws->targetrels.size = 0;
	ws->currQuery = NULL;
	ws->critSect = 0;

}

/*
*	WriteSetFree (remote & local backend)
*
*	frees a write set
*
*	param:
*		ws - the write set to free
*	return:
*		void
*/
void 
WriteSetFree(WriteSetPtr ws)
{
	Dlelem 	*curr_r = NULL,
			*curr_q = NULL;
	RelInfo *rel_info = NULL;
	CmdInfo	*cmd_info = NULL;

	if(!ws)
		return;

	if(!ws->targetrels.list)
		return;

#ifdef WS_DEBUG_LOCAL
	elog(DEBUG, "	Freeing write set struct");
#endif

	curr_r = DLGetHead(ws->targetrels.list);
	while(curr_r)
	{
		rel_info = (RelInfo *) DLE_VAL(curr_r);

		if(rel_info->querylist.list != NULL)
		{
			curr_q = DLGetHead(rel_info->querylist.list);
			while(curr_q)
			{
				cmd_info = (CmdInfo *) DLE_VAL(curr_q);
				switch(cmd_info->cmd)
				{
					case CMD_UTILITY:
						_freeCmdInfo(cmd_info);
						break;
					case CMD_INSERT:
					case CMD_DELETE:
					case CMD_UPDATE:
						_freeQueryInfo((QueryInfo *) cmd_info);
						break;
					default:
						break;
				}
				curr_q = DLGetSucc(curr_q);
			}
			DLFreeList(rel_info->querylist.list);
		}
		free(rel_info);
		curr_r = DLGetSucc(curr_r);
	}
	DLFreeList(ws->targetrels.list);
	ws->targetrels.list = NULL;
	ws->targetrels.size = 0;
	ws->currQuery = NULL;
}

/*
*	WriteSetOnExit (remote & local backend)
*
*	This procedure is registered as postgres exit callback
*
*	param:
*		dummy - required by postgres (on_proc_exit()), not used
*		ws - ptr to ptr to write set to destroy
*	return:
*		void
*/
void 
WriteSetOnExit(int dummy, WriteSetPtr ws)
{
	WriteSetFree(ws);
}

/*
*	_findrel (local backend)
*
*	finds a relation info struct in the write set <targetrels> list
*
*	param:
*		ws - the write set to be searched
*		relname - the relation name to be looked for
*	return:
*		RelInfo - the appropriate RelInfo struct, NULL if not found
*/
static RelInfo *
_findrel(WriteSetPtr ws, const char *relname)
{
	Dlelem *curr;
	RelInfo *rel_info;	

	if(!relname || !ws || !ws->targetrels.list)
		return NULL;

	curr = DLGetHead(ws->targetrels.list);
	while(curr)
	{
		rel_info = (RelInfo *) DLE_VAL(curr);
		if(strcmp(rel_info->name, relname) == 0)
		{
			return rel_info;
		}
		curr = DLGetSucc(curr);
	}
	return NULL;
}

/*
*	_getindexinfo (local backend)
*
*	Retrieves index information about a relation. Currently, the first 
*	UNIQUE index found is used. This index is then used by the remote
*	backend to find the appropriate tuples to modify. Ideally, we should
*	always use the primary key index, which is not the case at the 
*	moment.
*   The pg_index system relation is used to find index information.
*
*	param:
*		rel_info - the relation info struct containing the name of
*			the relation we information about
*	return:
*		bool - TRUE on success, FALSE otherwise
*/
static bool
_getindexinfo(RelInfo *rel_info)
{
	int					i;
	IdxInfo				*ind_info;
	IndexInfo			*index_info; /* (mc) */
	Relation 			index_rel,
						class_rel,
						classindex_rel;
	bool				found = FALSE;
	HeapTuple			iTuple;
	HeapTuple			tupleCopy = NULL; /* (mc) */
	HeapTupleData		cTuple; /* (mc) */ 
	HeapScanDesc 		scandesc;
	IndexScanDesc		iscandesc;
	ScanKeyData			key;
	Form_pg_index 		indexTuple;
	Form_pg_class		classTuple;
	/*DAR HACK this structure doesn't exist any more.  
	  I think FmgrInfo, but I'm not sure
	FuncIndexInfoPtr 	dummy;
	*/
	RetrieveIndexResult	result = NULL;
	Buffer				buffer;
	/*int					numKeys=0;
	AttrNumber			*keyAttr;*/
#ifdef WS_DEBUG_LOCAL
	char				debug_msg[128];
	char				*index;
#endif
	
	ind_info = &rel_info->indexinfo;
	rel_info->relOid = RelationGetRelid(RelationNameGetRelation(rel_info->name));
	
	/*
	* open "pg_index"
	*/
	//DAR HACK added AccessShareLock parameter
	index_rel = heap_openr(IndexRelationName, AccessShareLock);
	/*
	* initialize key for relation scan (in our case, the key is the relation oid
	*/
	ScanKeyEntryInitialize(&key, 0, Anum_pg_index_indrelid,
						   F_OIDEQ,
						   ObjectIdGetDatum(rel_info->relOid));

	/*
	* scan the relation until a unique index is found
	*/
	scandesc = heap_beginscan(index_rel, false, SnapshotNow, 1, &key);
	
	/* cTuple = (HeapTuple) palloc(sizeof(HeapTupleData));  (mc) */
	/* (mc) */
	cTuple.t_datamcxt = CurrentMemoryContext;
	cTuple.t_data = NULL;
	/**/
	while (HeapTupleIsValid(iTuple = heap_getnext(scandesc, 0)))
	{
		indexTuple = (Form_pg_index) GETSTRUCT(iTuple);
		if((indexTuple->indisunique) && 
		   (indexTuple->indproc == InvalidOid))
		{
			/*
			* we found an index, now use "pg_class" to find its name
			*/
			//DAR HACK added AccessShareLock parameter
			
			/*elog(DEBUG, "indexTuple->indexrelid=%d", indexTuple->indexrelid);*/
			
			class_rel = heap_openr(RelationRelationName, AccessShareLock);
			classindex_rel = index_openr(ClassOidIndex);
			ScanKeyEntryInitialize(&key, 0, (AttrNumber) 1,
						   F_OIDEQ,
						   ObjectIdGetDatum(indexTuple->indexrelid));
			iscandesc = index_beginscan(classindex_rel, false, 1, &key);
			/*result = index_getnext(iscandesc, 0);
			if(result)*/
			while ( (result = index_getnext(iscandesc, 0) ) != NULL )
			{
				/* DAR HACK - heap_fetch has some different parameters
					so here is a to so item.  It used to return
					a HeapTuple structure now it populates the struct
					We probably need a copy here (heap_copytuple) here.
				cTuple = heap_fetch(class_rel, SnapshotNow, 
										&result->heap_iptr, &buffer); 
				*/
				/* BAP - This is still broken.  The new 5th value seems to be a ref
					to a doing sequential scans.  This should work,
					but won't take advantage of indexes
				*/
							
				
				cTuple.t_self = result->heap_iptr;  
				
				heap_fetch(class_rel, SnapshotNow, &cTuple, &buffer, NULL);
				
				/* if((found = HeapTupleIsValid(&cTuple)) == TRUE) */ 
				if (cTuple.t_data != NULL)
				{
					found = TRUE;
					tupleCopy = heap_copytuple(&cTuple);
										
					index_info = (IndexInfo *)BuildIndexInfo(iTuple);  /* (mc) */
					
					/*elog(DEBUG, "-----index_info->ii_NumKeyAttrs=%d", index_info->ii_NumKeyAttrs);*/
					for (i = 0; i < index_info->ii_NumKeyAttrs; i++)
					{
						/*elog(DEBUG, "-----index_info->ii_KeyAttrNumbers[%d]=%d", i, index_info->ii_KeyAttrNumbers[i]);*/
						if (index_info->ii_KeyAttrNumbers[i] != InvalidAttrNumber)
						{
							ind_info->attrNums[i] = index_info->ii_KeyAttrNumbers[i];
						}
					}
					ind_info->numKeys = index_info->ii_NumKeyAttrs;
					/*for (i = 0; i < indexStruct->indkey[i] != InvalidAttrNumber; i++)
					{
						elog(DEBUG, "-----");
						numkeys++;
					}*/
					
					classTuple = (Form_pg_class) GETSTRUCT(tupleCopy);
					strncpy(ind_info->indexName, classTuple->relname.data, NAMEDATALEN);
					/*elog(DEBUG, "relname=%s", ind_info->indexName);*/
					ind_info->indexOid = RelationGetRelid(
									RelationNameGetRelation(ind_info->indexName));
					/* DAR HACK this is not consistent with the
					   7.2 method.
					dummy = (FuncIndexInfoPtr) palloc(sizeof(FuncIndexInfo));
					ExecGetIndexKeyInfo(indexTuple, 
										&numKeys,
										&keyAttr,
										dummy);
					pfree(dummy);
					*/
					/*  (mc)
					 * ExecGetIndexKeyInfo has been replaced by BuildIndexInfo(HeapTuple)
					
					elog(DEBUG, "NumKeyAttrs =%d", (int)index_info->ii_NumKeyAttrs);
					numKeys = index_info->ii_NumKeyAttrs;
					ind_info->numKeys = numKeys;
					for(i = 0; i<numKeys; i++)
					{
						ind_info->attrNums[i] = keyAttr[i]; 
						ind_info->attrNums[i] = index_info->ii_KeyAttrNumbers[i];
						elog(DEBUG, "attrNums[%i]= %d", i, ind_info->attrNums[i]);
					}
					ind_info->numKeys = 1;
					ind_info->attrNums[0] = 1; */
				}
			} /* while (result) */
			/*pfree(index_info);*/
			index_endscan(iscandesc);
			index_close(classindex_rel);
			
			heap_close(class_rel, AccessShareLock);

			heap_endscan(scandesc);
			heap_close(index_rel, AccessShareLock);
			//DAR HACK added AccessShareLock parameter
			/* DAR HACK added AccessShareLock parameter */
			ReleaseBuffer(buffer);
#ifdef WS_DEBUG_LOCAL
			index = debug_msg;
			strcpy(debug_msg, "Index : ");
			strcat(debug_msg, ind_info->indexName);
			strcat(debug_msg, ", base relation attr numbers: ");
			for(i = 0; i < ind_info->numKeys; i++)
			{
				/* elog(DEBUG, "ind_info->attrNums %d=%d", i,ind_info->attrNums[i]); */
				index = debug_msg + strlen(debug_msg);
				snprintf(index, 128 - strlen(debug_msg),"%d ",(int)ind_info->attrNums[i]);
			}
			elog(DEBUG, "%s", debug_msg);
#endif
			/* pfree(cTuple);  (mc) */
			return found ? TRUE : FALSE;
		}
	}
	/* pfree(cTuple);  (mc) */
	pfree(index_info);
	heap_endscan(scandesc);
	//DAR HACK added AccessShareLock parameter
	heap_close(index_rel, AccessShareLock);
	return FALSE;
}



/*
*	WriteSetSerialize (local backend)
*
*	Marshals a write set to be sent: the write set message 
*		is constructed
*
*	param:
*		ws - the write set to marshal
*		extbuf - an external socket buffer to construct the message in
*	return:
*		void
*/
void
WriteSetSerialize(WriteSetPtr ws, ebufptr extbuf)
{
	int		i,
			numKeys;
	Dlelem 	*curr_r = NULL,
			*curr_q = NULL;
	RelInfo *rel_info = NULL;
	IdxInfo	*idx_info = NULL;
	CmdInfo	*cmd_info = NULL;
	
	if(!ws || !ws->targetrels.list )
		return;

#ifdef WS_SER_DEBUG
	elog(DEBUG, "WriteSetSerialize: Beginning serialization");
#endif

#ifdef WS_SER_DEBUG
	elog(DEBUG, "WriteSetSerialize: numrels=%d", ws->targetrels.size);
#endif
	sockPutIntE(ws->targetrels.size, INT_SIZE, extbuf); /* numrels */
	curr_r = DLGetHead(ws->targetrels.list);
	while(curr_r)
	{
		rel_info = (RelInfo *) DLE_VAL(curr_r);
		idx_info = &rel_info->indexinfo;
#ifdef WS_SER_DEBUG
		elog(DEBUG, "WriteSetSerialize: putting relname=%s, indexname=%s, numkeys=%d",
			(char *)&(rel_info->name), (char *)&(idx_info->indexName), idx_info->numKeys);
#endif
		sockPutsE(rel_info->name, extbuf);
		sockPutsE(idx_info->indexName, extbuf);
		sockPutIntE(idx_info->numKeys, 2, extbuf);
		numKeys = idx_info->numKeys;
		for(i = 0; i <	numKeys; i++)
		{
			sockPutIntE(idx_info->attrNums[i], 2, extbuf);
		}
#ifdef WS_SER_DEBUG
		elog(DEBUG, "WriteSetSerialize: serialized relinfo and indexinfo");
#endif
		sockPutIntE(rel_info->querylist.size, 2, extbuf);
		if(rel_info->querylist.list != NULL)
		{
#ifdef WS_SER_DEBUG
			elog(DEBUG, "WriteSetSerialize: serializing query list");
#endif
			curr_q = DLGetHead(rel_info->querylist.list);
			while(curr_q)
			{
				cmd_info = (CmdInfo *) DLE_VAL(curr_q);
#ifdef WS_SER_DEBUG
				if (cmd_info->stmt != NULL)
					elog(DEBUG, "WriteSetSerialize: query is <%s>", cmd_info->stmt);
#endif
				sockPutIntE(cmd_info->cmd, 2, extbuf);
				switch(cmd_info->cmd)
				{
					case CMD_UTILITY:
						_serializeCmdInfo(cmd_info, extbuf);
						break;
					case CMD_INSERT:
					case CMD_DELETE:
					case CMD_UPDATE:
#ifdef WS_SER_DEBUG
						elog(DEBUG, "WriteSetSerialize: serializing Query_info");
#endif
						_serializeQueryInfo((QueryInfo *) cmd_info, extbuf);
						break;
					default:
						break;
				}
				curr_q = DLGetSucc(curr_q);
			}
		}
		/*}*/
		curr_r = DLGetSucc(curr_r);
	}

#ifdef WS_SER_DEBUG
	elog(DEBUG, "serialization ended"); /* (mc) NOTICE/DEBUG */
#endif
}

/*
*	WriteSetUnserialize (remote backend)
*
*	Unmarshals a write set: the write set structure 
*		is constructed
*
*	param:
*		ws - the write set to unmarshal
*		bsock - the buffered socket containing the write set message
*	return:
*		void
*/
void
WriteSetUnserialize(WriteSetPtr ws, bufsockptr bsock)
{
	int			i,
				j,
				numKeys,
				numQueries,
				numRels=0;
	CmdType		cmd;
	Relation	rel;
	CmdInfo		*cmd_info = NULL;
	RelInfo		*rel_info;
	IdxInfo		*idx_info;
	AttrNumber 	*idx_attrs;
	bool		isUtility = true;
	
#ifdef WS_SER_DEBUG
	elog(DEBUG,"[%d] There are %d bytes left in the buffer", MyProc->pid, sockNumLeft(bsock));
#endif
	
	while (numRels == 0)
	{
		sockGetInt(&numRels, INT_SIZE, bsock);
#ifdef WS_SER_DEBUG
		elog(DEBUG, "numrels=%d", numRels);
		elog(DEBUG,	"There are %d bytes left in the buffer", sockNumLeft(bsock));
#endif
	}
	
#ifdef WS_SER_DEBUG
	elog(DEBUG, "[%d] Begin deserialization (numRels=%d)", MyProc->pid, numRels); /* (mc) NOTICE/DEBUG */
#endif
	for(j = 0; j < numRels; j++)
	{
		rel_info = (RelInfo *) malloc(sizeof(RelInfo));
		
		/* (mc) */
		sockGets(rel_info->name, NAMEDATALEN, bsock);

		if ( rel_info->name[0] != ' ' ) /* query stmt, not utility */ 
		{
			isUtility = false;
			/*get rel_info data*/
			rel = RelationNameGetRelation(rel_info->name);
			if(!rel)
				elog(ERROR, "%s: Table doesn't exist", rel_info->name);
		
			rel_info->relOid = RelationGetRelid(rel);
#ifdef WS_SER_DEBUG
			elog(DEBUG, "Read relname %s", rel_info->name);
#endif
		}
		/*get idx_info data*/
		idx_info = &rel_info->indexinfo;
		sockGets(idx_info->indexName, NAMEDATALEN, bsock);
		
		if (!isUtility)
		{
			rel = RelationNameGetRelation(idx_info->indexName);
			idx_info->indexOid = RelationGetRelid(rel);
#ifdef WS_SER_DEBUG
			elog(DEBUG, "Read idxname %s", idx_info->indexName);
#endif
			if(!rel)
				elog(ERROR, "%s: Table doesn'ttt exist", idx_info->indexName);
		}
		sockGetInt(&numKeys, 2, bsock);
		idx_info->numKeys = numKeys;
		idx_attrs = idx_info->attrNums;
#ifdef WS_SER_DEBUG
		elog(DEBUG, "[%d] Read numKeys %d", MyProc->pid, numKeys);
#endif
		for(i = 0; i < numKeys; i++)
		{
			int res = -1;
			sockGetInt(&res, 2, bsock);
			idx_attrs[i] = (AttrNumber) res;
#ifdef WS_SER_DEBUG
			elog(DEBUG, "Read index atrr num %d", (int) idx_attrs[i]);
#endif
		}
		
		
		/*get query list*/
		rel_info->querylist.list = DLNewList();
		rel_info->querylist.size = 0;
		sockGetInt(&numQueries, 2, bsock);
		rel_info->querylist.size = numQueries;
#ifdef WS_SER_DEBUG
		elog(DEBUG, "[%d] Deserializing query list (%d queries)", MyProc->pid, numQueries);
#endif
		for(i = 0; i < numQueries; i++)
		{
			sockGetInt((int *) &cmd, 2, bsock);
			switch(cmd)
			{
				case CMD_UTILITY:
					cmd_info = _unserializeCmdInfo(cmd, bsock);
					break;
				case CMD_INSERT:
				case CMD_DELETE:
				case CMD_UPDATE:
#ifdef EXEC_DEBUG
		/*  swu (rem) */
		elog(DEBUG, "[%d] WriteSetUnserialize: before _unserializeQueryInfo", MyProc->pid);
#endif
					cmd_info = _unserializeQueryInfo(cmd, rel_info, bsock);
#ifdef EXEC_DEBUG
		/*  swu (rem) */
		elog(DEBUG, "[%d] WriteSetUnserialize: after _unserializeQueryInfo", MyProc->pid);
#endif
					break;
				default:
					elog(DEBUG, "Unserialize: unknown command type");
					proc_exit(700);
					break;
			}
			if(!cmd_info)
			{
				elog(DEBUG, "Unserialize: parse error occurred");
				proc_exit(700);
			}
#ifdef WS_SER_DEBUG
		/*  swu (rem) */
		elog(DEBUG, "[%d] WriteSetUnserialize: Unserialize is done", MyProc->pid);
		elog(DEBUG, "WriteSetUnserialize: Unserialize is done %d", ws->targetrels.size);
#endif
			
			if(ws->critSect < cmd_info->sect)
			{
				ws->critSect = cmd_info->sect;
				//after unserializing the write set, critSect will hold
				//the highest execution section number in this write set
			}
#ifdef WS_SER_DEBUG
	/*  swu (rem) */
	elog(DEBUG, "[%d] WriteSetUnserialize: before DLAddTail", MyProc->pid);
#endif

			DLAddTail(rel_info->querylist.list, DLNewElem(cmd_info));
			rel_info->querylist.size++;
		}
		DLAddTail(ws->targetrels.list, DLNewElem(rel_info));
		ws->targetrels.size++;
	}
#ifdef WS_SER_DEBUG
	elog(DEBUG, "Deserialization ended");
#endif
}

/*
*	WriteSetGetChangedAtts (local backend)
*
*	Extracts the changed attributes numbers from a query plan
*	Note that in some cases, the changed attributes cannot be
*	determined. In this case, all attributes are presumed to have changed.
*
*	param:
*		targetlist - the plan's target list
*		changeArray - upon return, contains an 'r' char for each changed attribute
*			an a ' ' char for each unchanged one.
*		numattrs - the total number of attributes of the current relation
*
*		Example: The first and last attr of a 4-attribute relation have been 
*			changed by a query. changeArray = |r| | |r|, numattrs = 4.
*	return:
*		void
*/
void
WriteSetGetChangedAtts(List *targetlist, char *changeArray, int numAttrs)
{
	int 		i,
				j;
	List 		*tl;
	TargetEntry	*tle;
	
	
	for(i = 0; i < numAttrs; i++)
	{
		changeArray[i] = ' ';
	}
	
	if(targetlist == NULL)
		return;
		
	foreach(tl, targetlist)
	{
		tle = lfirst(tl);
		if(tle->resdom != NULL)
		{
			if(tle->resdom->resjunk == 0)
			{
				i = tle->resdom->resno-1;
				if(i >= numAttrs)
				{
					elog(DEBUG, "ChangeArray: Inconsistency between numAttrs"
								"and index value");
					for(j = 0; j < numAttrs; j++)
					{
						changeArray[i] = 'r';
					}
					return;
				}
				changeArray[i] = 'r';
			}
		}
		else
		{
			elog(DEBUG, "ChangeArray: found resdom == NULL");
			for(j = 0; j < i; j++)
			{
				changeArray[i] = 'r';
			}
			return;
		}
	}
}

/*
*	_serializeQueryInfo (local backend)
*
*	Marshals a query information struct
*
*	param:
*		qry - the query info struct to marshal
*		extbuf - an external socket buffer to construct the message in
*	return:
*		void
*/
static void
_serializeQueryInfo(QueryInfo *qry, ebufptr extbuf)
{
	TupleCollection	*tcoll = &qry->tcoll;
	int				numTuples = tcoll->numTuples,
					maxTuples = tcoll->maxTuples,
					varraylen,
					i;
	char			*varray;
	CmdType			cmd = qry->info.cmd;
	
	#ifdef WS_SER_DEBUG
		elog(DEBUG, "_serializeQueryInfo: numTuples=%d, maxTuples=%d", numTuples, maxTuples);
	#endif
	if(numTuples > maxTuples)
	{
		_serializeCmdInfo((CmdInfo *)qry, extbuf);
		return;
	}

	sockPutncharE("t", 1, extbuf);
	sockPutIntE(qry->info.sect, 2, extbuf); 
	sockPutIntE(numTuples, 2, extbuf);
	if(cmd == CMD_DELETE || cmd == CMD_UPDATE)
	{
		for(i = 0; i < numTuples; i++)
		{
#ifdef WS_SER_DEBUG
			elog(DEBUG, "serializing pkey_values[%d]", i);
#endif
			varray = tcoll->pkey_values[i];
			varraylen = VARSIZE(varray);
			sockPutIntE(varraylen, VARHDRSZ, extbuf);
			sockPutncharE(VARDATA(varray), varraylen - VARHDRSZ, extbuf);
 		}
	}
	
	if(cmd == CMD_INSERT || cmd == CMD_UPDATE)
	{
		for(i = 0; i < numTuples; i++)
		{
#ifdef WS_SER_DEBUG
			elog(DEBUG, "serializing newtup_data[%d]", i);
#endif
			varray = tcoll->newtup_data[i];
			varraylen = VARSIZE(varray);
			sockPutIntE(varraylen, VARHDRSZ, extbuf);
			sockPutncharE(VARDATA(varray), varraylen - VARHDRSZ, extbuf);
		}
	}
}

/*
*	_serializeCmdInfo (local backend)
*
*	Marshals a command information struct.
*
*	param:
*		cmd_info - the query info struct to marshal
*		extbuf - an external socket buffer to construct the message in
*	return:
*		void
*/
static void
_serializeCmdInfo(CmdInfo *cmd_info, ebufptr extbuf)
{
	/*compute size on the fly*/
	#ifdef WS_SER_DEBUG
		elog(DEBUG, "_serializeCmdInfo: serializing cmd=%d, sect=%d, stmt=%s",
			cmd_info->cmd, cmd_info->sect, cmd_info->stmt);
	#endif
	
	sockPutncharE("c", 1, extbuf);
	sockPutIntE(cmd_info->sect, 2, extbuf); //(oi)
	sockPutsE(cmd_info->stmt, extbuf);
}

/*
*	_unserializeQueryInfo (remote backend)
*
*	Unmarshals a query information struct
*
*	param:
*		cmd - the query command type
*		rel - the relation info struct this query belongs to
*		bsock - the buffered socket containing the query info to be
*			unmarshaled
*	return:
*		CmdInfo - pointer to the query info object containing the unmarshaled
*			query data
*/
static CmdInfo*
_unserializeQueryInfo(CmdType cmd, RelInfo *rel_info, bufsockptr bsock)
{
	char			data_type,
					*varray;
	int				numTuples,
					i,
					varraylen,
					str_len;
	QueryInfo		*qry_info = NULL;
	TupleCollection *tcoll;
	
	sockGetnchar(&data_type, 1, bsock);
#ifdef WS_SER_DEBUG
	elog(DEBUG, "_unserializeQueryInfo: Read data type %s", &data_type);
#endif
	if(data_type == 'c')
	{
		qry_info = _initQueryInfo();
		qry_info->info.cmd = cmd;
		sockGetInt(&qry_info->info.sect, 2, bsock);
		str_len = sockGetSlen(bsock);
		qry_info->info.stmt = (char *) malloc((str_len + 1) * sizeof(char));
		MemSet(qry_info->info.stmt, '\0', (str_len + 1) * sizeof(char));
		sockGets(qry_info->info.stmt, (str_len+1) * sizeof(char), bsock);
#ifdef WS_SER_DEBUG
		elog(DEBUG, "_unserializeQueryInfo: read stmt=%s", qry_info->info.stmt);
#endif
	}
	else if(data_type == 't')
	{
		qry_info = _initQueryInfo();
		qry_info->info.cmd = cmd;
		tcoll = &qry_info->tcoll;
		tcoll->rel = rel_info;
		sockGetInt(&qry_info->info.sect, 2, bsock);
		sockGetInt(&numTuples, 2, bsock);
		tcoll->numTuples = numTuples;
#ifdef WS_SER_DEBUG
		elog(DEBUG, "_unserializeQueryInfo: read numTuples %d", numTuples);
#endif
		if(cmd == CMD_DELETE || cmd == CMD_UPDATE)
		{
			tcoll->pkey_values = (char **) malloc(numTuples * sizeof(char *));

			for(i = 0; i < numTuples; i++)
			{
#ifdef WS_SER_DEBUG
				elog(DEBUG, "_unserializeQueryInfo: Unserializing pkey_values[%d]", i);
#endif

				sockGetInt(&varraylen, VARHDRSZ, bsock);
				varray = tcoll->pkey_values[i] = (char *) malloc(varraylen * sizeof(char));
				/* DAR HACK Use VARATT_SIZEP?
				VARSIZE(varray) = varraylen;
				*/
				VARATT_SIZEP(varray) = varraylen;
				sockGetnchar(VARDATA(varray), varraylen - VARHDRSZ, bsock);
			}
		}
		
		if(cmd == CMD_INSERT || cmd == CMD_UPDATE)
		{
			tcoll->newtup_data = (char **) malloc(numTuples * sizeof(char *));
			for(i = 0; i < numTuples; i++)
			{	
#ifdef WS_SER_DEBUG
				elog(DEBUG, "_unserializeQueryInfo: Unserializing newtup_data[%d]", i);
				elog(DEBUG,"_unserializeQueryInfo: There are %d bytes left in the buffer", sockNumLeft(bsock)); /* (mc) NOTICE/DEBUG */
#endif
				sockGetInt(&varraylen, VARHDRSZ, bsock);
				varray = tcoll->newtup_data[i] = (char *) malloc(varraylen * sizeof(char));
				/* DAR HACK Use VARATT_SIZEP?
				VARSIZE(varray) = varraylen;
				*/
				VARATT_SIZEP(varray) = varraylen;
				sockGetnchar(VARDATA(varray), varraylen - VARHDRSZ, bsock);
			}
		}
	}
#ifdef WS_SER_DEBUG
	else
	{
		elog(DEBUG, "_unserializeQueryInfo: Read unknown data type");
	}
#endif

	return (CmdInfo *) qry_info;
}

/*
*	_unserializeCmdInfo (remote backend)
*
*	Unmarshals a command information struct
*
*	param:
*		cmd - the query command type
*		bsock - the buffered socket containing the query info to be
*			unmarshaled
*	return:
*		CmdInfo - pointer to the command info object containing the unmarshaled
*			command data
*/
static CmdInfo*
_unserializeCmdInfo(CmdType cmd, bufsockptr bsock)
{
	int		str_len;
	CmdInfo	*cmd_info = (CmdInfo *) malloc(sizeof(CmdInfo));
	char	type_id;
	
	cmd_info->cmd = cmd;
	sockGetnchar(&type_id, sizeof(char), bsock); 
	
	if (type_id == 'c')
	{
		sockGetInt(&cmd_info->sect, 2, bsock);//(oi)
		str_len = sockGetSlen(bsock);
		cmd_info->stmt = (char *) malloc((str_len + 1) * sizeof(char));
		MemSet(cmd_info->stmt, '\0', (str_len + 1) * sizeof(char));
		sockGets(cmd_info->stmt, (str_len+1) * sizeof(char), bsock);
		#ifdef WS_SER_DEBUG
			elog(DEBUG, "_unserializeCmdInfo: deserialized ws: cmd=%d, typeid=%c, sect=%d, stmt=%s (size=%d)",
				cmd_info->cmd, type_id, cmd_info->sect, cmd_info->stmt, (str_len+1));
			
		#endif
		return cmd_info;
	}
	else if (type_id == 't')
	{
		#ifdef WS_SER_DEBUG
			elog(DEBUG, "_unserializeCmdInfo: (error) a 't' typeid should not be received here" );
		#endif
	}
	else
	{
		#ifdef WS_SER_DEBUG
			elog(DEBUG, "_unserializeCmdInfo: (error) unknown typeid received");
		#endif	
	}
	return (CmdInfo *)NULL;
}

/*
*	_freeQueryInfo (remote & local backend)
*
*	frees a query info struct
*
*	param:
*		qry - the struct to free
*	return:
*		void
*/
static void
_freeQueryInfo(QueryInfo *qry)
{
	int				i,
					limit;
	bool			freeTuples, freePkeys;
	TupleCollection *tcoll;
#ifdef WS_DEBUG_LOCAL
	/*  swu (rem) */
	elog(DEBUG, "	[%d] Freeing QueryInfo: just enter", MyProc->pid);
#endif
	
#ifdef WS_DEBUG_LOCAL
	elog(DEBUG, "	[%d] Freeing QueryInfo <%s>", MyProc->pid, qry->info.stmt == NULL ? "stmt string is null" : qry->info.stmt );
#endif
	free(qry->info.stmt);
	tcoll = &qry->tcoll;
	freeTuples = (tcoll->newtup_data != NULL);
	freePkeys = (tcoll->pkey_values != NULL);

	free(tcoll->changeArray);
	limit = (tcoll->numTuples > tcoll->maxTuples) ? tcoll->maxTuples : tcoll->numTuples;
	for(i = 0; i < limit; i++)
	{
		if(freeTuples)
		{
			free(tcoll->newtup_data[i]);
		}
		if(freePkeys)
		{
			free(tcoll->pkey_values[i]);
		}
	}

	free(tcoll->changeArray);
	free(tcoll->pkey_values);
	free(tcoll->newtup_data);
	free(qry);
}

/*
*	_freeCmdInfo (remote & local backend)
*
*	frees a command info struct
*
*	param:
*		cmd_info - the struct to free
*	return:
*		void
*/
static void
_freeCmdInfo(CmdInfo *cmd_info)
{
#ifdef WS_DEBUG_LOCAL
	elog(DEBUG, "Freeing CmdInfo (%s)", cmd_info->stmt);
#endif
	free(cmd_info->stmt);
	free(cmd_info);
}


/*
*	_execCmdList (remote backend)
*
*	executes queries in a query taking into account the current 
*	execution section and if locks needed are granted or not.
*
*	param:
*		rel - the relation struct this query list is associated to
*		currSect - the current execution section
*		isGranted - is the lock on the relation granted?
*		doTxnDelim - TRUE if StartTransactionCommand and Commit-
*			TransactionCommand have to be called while executing the 
*			queries			
*	return:
*		bool - TRUE if queries belonging to the current execution section
*			could not be executed because the corresponding lock has
*			not yet been granted. This instructs the caller that the current
*			execution section is not yet processed entirely. The subsequent
*			execution section must thus not be executed until the locks 
*			have been granted and all queries of the current exec. sect.
*			have been executed.
*			    FALSE if the caller does not have to wait for locks
*			before processing the next exec. sect.
*/
static bool
_execCmdList(RelInfo *rel, int currSect, int Granted, bool doTxnDelim)
{
	CmdType 			cmd;
	CmdInfo 			*cmd_info;
	Dllist 				*list;
	Dlelem 				*curr,
						*next;
	
	list = rel->querylist.list;
	curr = DLGetHead(list);
	
	if(!curr)
	{
		return FALSE;
	}
	else if(Granted < 1)
	{
		cmd_info = (CmdInfo *) DLE_VAL(curr);

		/*  swu ??? what is currSect for? */
		if(cmd_info->sect == currSect)
		{
			/*elog(DEBUG,"not granted,, in right section");*/ /* (mc) NOTICE/DEBUG */
			return TRUE;
		}
		else
		{
			/*elog(DEBUG,"not granted in wrong section");*/ /* (mc) NOTICE/DEBUG */
			return FALSE;
		}
	}

	while(curr)
	{
		next = DLGetSucc(curr);
		cmd_info = (CmdInfo *) DLE_VAL(curr);

		#ifdef WS_DEBUG_REMOTE
			elog(DEBUG,"	[%d] _execCmdList: stmt=%s", MyProc->pid, cmd_info->stmt==NULL ? "NULL":cmd_info->stmt);
		#endif

			if(doTxnDelim)
			{
				/* (mc) StartTransactionCommand is called from pg_exec_query_string 
				#ifdef WS_DEBUG_REMOTE
					elog(DEBUG,"	_execCmdList: StartTransactoinCommand");
				#endif
				
				StartTransactionCommand();
				*/
				
			}
			cmd = cmd_info->cmd;
			switch(cmd_info->cmd)
			{
				case CMD_UTILITY:
					#ifdef WS_DEBUG_REMOTE
						elog(DEBUG,"	[%d] _execCmdList: execute utility operation", MyProc->pid); /* (mc) NOTICE/DEBUG */
					#endif
					_execUtility(cmd_info);

					/*  swu (rem) */
					#ifdef WS_DEBUG_REMOTE
						elog(DEBUG,"	[%d] _execCmdList: done execute utility", MyProc->pid); /* (mc) NOTICE/DEBUG */
					#endif
					break;
				case CMD_INSERT:
				case CMD_DELETE:
				case CMD_UPDATE:

					#ifdef WS_DEBUG_REMOTE
						elog(DEBUG,"	[%d] _execCmdList: execute query operation", MyProc->pid); /* (mc) NOTICE/DEBUG */
					#endif
					
					_execQuery(cmd_info);
					
					/*  swu (rem) */
					#ifdef WS_DEBUG_REMOTE
						elog(DEBUG,"	[%d] _execCmdList: done execute query operation", MyProc->pid); /* (mc) NOTICE/DEBUG */
					#endif
					break;
				default:
					break;
			}

			DLRemove(curr);
			DLFreeElem(curr);
			curr = next;
	}
	
	return FALSE;
}



/*
*	_execUtility (remote backend)
*
*	executes a utility command (not used at the moment)
*
*	param:
*		cmd - the cmd struct containing the cmd to execute
*	return:
*		void
*/
static void
_execUtility(CmdInfo *cmd)
{
	/*DAR HACK to do pg_exec_query is not what we want here*/
	pg_exec_query(cmd->stmt);
	_freeCmdInfo(cmd);
}

/*
*	_execQueryInfo (remote backend)
*
*	executes a query
*
*	param:
*		cmd_info - the query struct containing the query to execute
*	return:
*		void
*/
static void
_execQuery(CmdInfo *cmd_info)
{	
	TupleCollection *tcoll;

	
	if(cmd_info->stmt == NULL)
	{
		/*
		*	process the tuples in the query struct
		*/
		#ifdef WS_DEBUG_REMOTE
			elog(DEBUG, "	_execQuery: Executing query tuples");
		#endif
		tcoll = &((QueryInfo *)cmd_info)->tcoll;
		/*DAR HACK this function was defined in execMain.c so
			a to do is needed here. */
		ExecProcessCollection(tcoll, cmd_info->cmd);
		
		_freeQueryInfo((QueryInfo *) cmd_info);
	}
	else
	{
		/*
		* execute the query string
		*/

#ifdef WS_DEBUG_REMOTE
		elog(DEBUG, "	_execQuery: Executing query stmt <%s>", cmd_info->stmt);
#endif
		/*DAR HACK to do pg_exec_query is not what we want here*/
		pg_exec_query(cmd_info->stmt);
		
		_freeCmdInfo(cmd_info);
	}
	
}

/*
*	_initQueryInfo (remote & local backend)
*
*	allocates and initializes a query info struct
*
*	param:
*		void
*	return:
*		QueryInfo - the initialized struct
*/
static QueryInfo*
_initQueryInfo(void)
{
	TupleCollection	*tcoll;
	QueryInfo 		*qry_info;
	
	qry_info = (QueryInfo *) malloc(sizeof(QueryInfo));
	qry_info->info.stmt = NULL;
	qry_info->info.sect = -1; 
	tcoll = &qry_info->tcoll;
	tcoll->rel = NULL;
	tcoll->numAttrs = 0;
	tcoll->changeArray = NULL;
	tcoll->maxTuples = TUPLE_LIMIT;
	tcoll->numTuples = 0;

	tcoll->pkey_values = NULL;
	tcoll->newtup_data = NULL;
	return  qry_info;
}


/*
*	_getEqualProcs (remote backend)
*
*	gets the procedure oids for the '=' operation for
*	the primary key index attributes of a relation
*
*	param:
*		rel - the relation info struct containing the index info
*	return:
*		void
*/
static void
_getEqualProcs(RelInfo *rel)
{
	IdxInfo				*idx_info;
	int 				numKeys,
						i,
						j;
	Oid					attType[INDEX_MAX_KEYS];
	RegProcedure		*equalProcs;
	AttrNumber			attnum;
	TupleDesc			tdesc;
	Form_pg_attribute	*attrs;
	
	idx_info = &(rel->indexinfo);
	numKeys = idx_info->numKeys;
	tdesc = RelationGetDescr(RelationNameGetRelation(rel->name));
	attrs = tdesc->attrs;	
	equalProcs = idx_info->equalProcs;
	MemSet(equalProcs, 0, INDEX_MAX_KEYS * sizeof(RegProcedure));
	
#ifdef WS_DEBUG_LOCAL
	elog(DEBUG, "Getting procedure oids for '=' operation on rel '%s'", rel->name);
#endif
	for(i = 0; i < numKeys; i++)
	{
		bool found = FALSE;

		attnum = idx_info->attrNums[i];
		attType[i] = attrs[attnum-1]->atttypid;

		/*optimization to reduce number of 'pg_operator' relation 
		scans and cache accesses*/
		for(j = 0; j < i; j++)
		{
			if(attType[j] == attType[i])
			{
				found = TRUE;
				equalProcs[i] = equalProcs[j];
				break;
			}
		}

		if(!found)
		{
			/*
			* cache lookup
			*/
			equalProcs[i] = (RegProcedure) _equalProcLookup(attType[i]);
		}
#ifdef WS_DEBUG_LOCAL
		elog(DEBUG, "Procedure oid for '=' op (for type %d) is %d", attType[i], equalProcs[i]);
#endif
	}
}
 

/*
*	_equalProcLookup (remote backend)
*
*	cache lookup for procedure oid of '=' operator for a specified type
*
*	param:
*		attr_typeid - the type to find the oid for
*	return:
*		the OID of the operator
*/
static regproc
_equalProcLookup(Oid attr_typeid)
{
	Relation 			op_rel;
	ScanKeyData			skeys[3];
	NameData			opr_name;
	HeapTuple			tuple;
	HeapScanDesc		scandesc;
	Form_pg_operator	oprTuple = NULL;
	
	MemSet(opr_name.data, 0, NAMEDATALEN * sizeof(char));
	opr_name.data[0] = '=';
	opr_name.data[1] = '\0';
	
	//DAR HACK added AccessShareLock parameter
	op_rel = heap_openr(OperatorRelationName, AccessShareLock);
	
	ScanKeyEntryInitialize(&skeys[0], 0, Anum_pg_operator_oprname, 
									F_NAMEEQ, NameGetDatum(&opr_name));
	ScanKeyEntryInitialize(&skeys[1], 0, Anum_pg_operator_oprleft,
									F_OIDEQ, ObjectIdGetDatum(attr_typeid));
	ScanKeyEntryInitialize(&skeys[2], 0, Anum_pg_operator_oprright,
									F_OIDEQ, ObjectIdGetDatum(attr_typeid));
	scandesc = heap_beginscan(op_rel, false, SnapshotNow, 3, skeys);
	
	if(HeapTupleIsValid(tuple = heap_getnext(scandesc, 0)))
	{
		oprTuple = (Form_pg_operator) GETSTRUCT(tuple);
	}
	heap_endscan(scandesc);
	//DAR HACK added AccessShareLock parameter
	heap_close(op_rel, AccessShareLock);
	
	return oprTuple->oprcode;
}

/*#ifdef WS_PRINT_LOW_LEVEL*/

/*
* debugging functions to print the contents of a write set
*/
static void
printNewTupleData(char *values, TupleDesc tdesc)
{
	char 				*tup_data,
						*tup_head;
	int					i,
						natts;
	
	natts = tdesc->natts;
	tup_head = values;
	tup_data = tup_head + natts;

	for(i = 0; i < natts; i++)
	{
		switch(*tup_head)
		{
			case ' ':
				break;
			case 'n':
				tup_data = printattribute(tup_data, i+1, tdesc, TRUE);
				break;
			case 'r':
				tup_data = printattribute(tup_data, i+1, tdesc, FALSE);
				break;
			default:
				elog(DEBUG, "Unknown change type read");
				break;
		}
		tup_head++;
	}
}

static void
printPkeyValues(char *values, int numKeys, AttrNumber *attrNums, TupleDesc tdesc)
{
	char 				*tup_data;
	int					i;
	
	tup_data = values;
	elog(DEBUG,"print pkey values"); /* (mc) NOTICE/DEBUG */
	for(i = 0; i < numKeys; i++)
	{
		elog(DEBUG,"print key values num key %d",i); /* (mc) NOTICE/DEBUG */
		tup_data = printattribute(tup_data, attrNums[i], tdesc, FALSE);
	}
}

static char *
printattribute(char *data, AttrNumber attnum, TupleDesc tdesc, bool isNull)
{
	char				line[80];
	Form_pg_attribute	att = tdesc->attrs[attnum-1];
	
	MemSet(line, 0, 80*sizeof(char));
	snprintf(line, 80, " %d: %s = \"%s\"\t(typeid = %d, len = %d, typmod = %d, byval = %s)",
						 attnum, NameStr(att->attname), isNull ? "NULL" : data, att->atttypid, att->attlen, 
								att->atttypmod, att->attbyval ? "t" : "f");
	if(!isNull)
	{
		data += strlen(data)+1;
	}
		
	printf("\t%s\n", line);
	fflush(stdout);
	return data;
}

/*#endif*/
/*#ifdef WS_PRINT*/


void
printWriteSet(WriteSetPtr ws, const char *comment)
{
	Dlelem			*curr_r,
					*curr_q;
	char			line[80],
					*index;
	int				i;
	RelInfo			*rel_info;
	IdxInfo			*idx_info;
	CmdInfo			*cmd_info;
	QueryInfo 		*qry_info;
	TupleCollection	*tcoll;
	TupleDesc		tdesc;

	if(!comment || !ws)
		return;

	curr_r = DLGetHead(ws->targetrels.list);
	if (curr_r)
		printf("\nPRINTWRITESET: %s\n", comment);
	while(curr_r)
	{
		rel_info = (RelInfo *) DLE_VAL(curr_r);
		if (rel_info->name[0] == ' ') /* utility */
		{
			printf("{ RELINFO (addr = %lx)\n", (unsigned long)rel_info);
			printf("utilitylist = \n");
			curr_q = DLGetHead(rel_info->querylist.list);
			if(!curr_q)
				printf("\t{ empty }\n");
			while(curr_q)
			{
				cmd_info = (CmdInfo *) DLE_VAL(curr_q);
				/*elog(DEBUG, "cmd is %d", (int) cmd_info->cmd);*/
				switch(cmd_info->cmd)
				{
					case CMD_UTILITY:
						printf("    { CMDINFO\n\tcmd = %d\n\tsect=%d\n\tstmt = %s\n    }\n", 
									cmd_info->cmd, cmd_info->sect, cmd_info->stmt);
						break;
					default:
						printf("    { should not be here \n}");
						break;
				}
				fflush(stdout);
				curr_q = DLGetSucc(curr_q);
			}
		}	
		else /* query */
		{
			idx_info = &rel_info->indexinfo;
			tdesc = RelationGetDescr(RelationIdGetRelation(rel_info->relOid));
			if(rel_info->querylist.list != NULL)
			{
				printf("{ RELINFO (addr = %lx)\n", (unsigned long)rel_info);
				printf("name = %s\nrelOid = %d\nindexinfo = \n    { INDEXINFO\n", 
								rel_info->name, rel_info->relOid);
				printf("\tindexName = %s\n\tindexOid = %d\n\tnumKeys = %d\n",
						idx_info->indexName, idx_info->indexOid, idx_info->numKeys);
				index = line;
				snprintf(line, 80, "attrNums = [ ");
				for(i = 0; i < idx_info->numKeys; i++)
				{
					index = line + strlen(line);
					snprintf(index, 80 - strlen(line),"%d ",(int)idx_info->attrNums[i]);
				}
				index = line + strlen(line);
				snprintf(index, 80 - strlen(line), "]\n    }\n");
				printf("\t%s\n", line);
				
				printf("querylist = \n");
				curr_q = DLGetHead(rel_info->querylist.list);
				if(!curr_q)
					printf("\t{ empty }\n");
				while(curr_q)
				{
					
					cmd_info = (CmdInfo *) DLE_VAL(curr_q);
					/*elog(DEBUG, "cmd is %d", (int) cmd_info->cmd);*/
					switch(cmd_info->cmd)
					{
						case CMD_UTILITY:
							printf("    { should not be here } ");
							break;
						case CMD_INSERT:
						case CMD_DELETE:
						case CMD_UPDATE:
							qry_info = (QueryInfo *) cmd_info;
							tcoll = &qry_info->tcoll;
							printf("    { QUERYINFO\n\tcmd = %d\n\tsect = %d\n\tstmt = %s\n", 
										qry_info->info.cmd, qry_info->info.sect, qry_info->info.stmt==NULL ?
																 "<NULL>" : qry_info->info.stmt);
							printf("\trel = %lx\n\tmaxTuples = %d\n\tnumTuples = %d\n", 
									(unsigned long) tcoll->rel, tcoll->maxTuples, tcoll->numTuples);
							/*#ifdef WS_PRINT_LOW_LEVEL*/
							printf("\tPrinting the first 10 tuples (at most)...\n");
							for(i = 0; i < tcoll->numTuples && i < tcoll->maxTuples && i < 10; i++)
							{
								printf("\n\ttuple no. %d:\n", i);
								if(tcoll->pkey_values != NULL)
								{
									printf("\tpkey_values:\n");
									printPkeyValues(VARDATA(tcoll->pkey_values[i]), idx_info->numKeys,
											idx_info->attrNums, tdesc);
								}
								if(tcoll->newtup_data != NULL)
								{
									printf("\tnew_tuple:\n");
									printNewTupleData(VARDATA(tcoll->newtup_data[i]), tdesc);
								}
							}
							/*#endif*/
							printf("    }\n");
							break;
						default:
							break;
					}
					fflush(stdout);
					curr_q = DLGetSucc(curr_q);
				}
			printf("}\n");
			}
		}
		fflush(stdout);
		curr_r = DLGetSucc(curr_r);
		
	}
}
/*#endif*/

/* (mc) */
void
WriteSetVerifyTuples(WriteSetPtr ws)
{
	CmdType			cmd;
	TupleCollection *tcoll;
	
	
	cmd = ws->currQuery->cmd;
	
	switch(cmd)
	{	
		/*
		case CMD_UTILITY:
			info_ptr = malloc(sizeof(CmdInfo));
			((CmdInfo *)info_ptr)->cmd = cmd;
			((CmdInfo *)info_ptr)->stmt = strdup(query);
			break;
		*/
		case CMD_INSERT:
		case CMD_DELETE:
		case CMD_UPDATE:
			/*  swu (rem) */
			/*#ifdef WS_DEBUG_LOCAL
				elog(DEBUG, "WriteSetVerifyTuples: numTuples=%d, maxTuples=%d", 
					tcoll->numTuples, tcoll->maxTuples);
				elog(DEBUG, "WriteSetVerifyTuples: currQuery->sect=%d, critSect=%d", 
					ws->currQuery->sect, ws->critSect);
			#endif*/
			tcoll = &(((QueryInfo *) ws->currQuery)->tcoll);
			if(tcoll->numTuples == 0)
			{
				/*_freeQueryInfo((QueryInfo *) info_ptr); 
				 info_ptr = NULL;*/
			}
			else if(tcoll->numTuples > tcoll->maxTuples)
			{
				ws->currQuery->sect = ++ws->critSect;
				ws->critSect++;
				//tcoll->numTuples = 0;
			}
			else
			{
				ws->currQuery->sect = ws->critSect;
			}
			#ifdef WS_DEBUG_LOCAL
				elog(DEBUG, "[%d] WriteSetVerifyTuples: currQuery->sect=%d, critSect=%d",
							MyProc->pid, ws->currQuery->sect, ws->critSect);
			#endif
			ws->currQuery = NULL;
			break;
		default:
			break;
	}
	

#ifdef WS_PRINT
	printWriteSet(ws, "WriteSetAddQuery: After tuple collection");
#endif
	
}


/*
 *	get_writeset (local backend)
 *
 *	return:
 *		link to the write set 
 */
Datum
get_writeset(PG_FUNCTION_ARGS){
	ebuf		extbuf;
	bytestream	*result;
	size_t		totallen;
	sockInitE(&extbuf, WS_BUF_SIZE);

#ifdef EXEC_DEBUG
	/*  swu (rem) */
	elog(DEBUG, "[%d] get_writeset: in get_writeset", MyProc->pid);
#endif
	/*printWriteSet(&MyWriteSet, "in get_writeset");*/
	WriteSetSerialize(&MyWriteSet, &extbuf);
	/*sprintf(extbuf.buf, "I have serialized");
	extbuf.currindex = strlen(extbuf.buf);*/

	totallen = extbuf.currindex + VARHDRSZ;
	result = palloc(totallen);
	VARATT_SIZEP(result) = totallen;

	memmove(VARDATA(result), extbuf.buf, extbuf.currindex);

	return PointerGetDatum(result);
}


/*
 *	put_writeset (remote backend)
 *
 *	return:
 *		link to the write set 
 */
Datum
put_writeset(PG_FUNCTION_ARGS){
#ifdef EXEC_DEBUG
	/*  swu (rem) */
	elog(DEBUG, "[%d] put_writeset: in put_writeset", MyProc->pid);
#endif

  	bufsockptr	bsock;
	bytestream	*arg = (bytestream *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));

	size_t len = VARSIZE(arg) - VARHDRSZ;

	txn_type = REPLICATED_REMOTE;

	bsock = sockInit(AF_UNIX_BUFSIZE);

	memmove(bsock->inBuffer, VARDATA(arg), len);
	bsock->inStart = 0;
	bsock->inEnd = len;
#ifdef EXEC_DEBUG
	/*  swu (rem) */
	elog(DEBUG, "[%d] put_writeset: before WriteSetUnserialize", MyProc->pid);
#endif
	/*elog(NOTICE," I will unserialize\n");*/
	WriteSetUnserialize(&MyWriteSet, bsock);

#ifdef EXEC_DEBUG
	/*  swu (rem) */
	elog(DEBUG, "[%d] put_writeset: before WriteSetProcess", MyProc->pid);
#endif
	/*printWriteSet(&MyWriteSet, "in put_writeset");*/
	WriteSetProcess(&MyWriteSet, true);

	/* remove all accumulated queries in the ws */
	/*WriteSetInit(&MyWriteSet);*/

	txn_type = REPLICATED_LOCAL;

	PG_RETURN_BOOL(TRUE);
}
