/* pte.c
 * Parallel Transaction Execution for erServer replication.
 * (c) 2001 Vadim Mikheev, PostgreSQL Inc.
 */

#include "executor/spi.h"
#include "utils/tqual.h"		/* SnapshotData */

PG_FUNCTION_INFO_V1(_pte_get_snapshot_);
PG_FUNCTION_INFO_V1(_pte_set_snapshot_);
Datum		_pte_get_snapshot_(PG_FUNCTION_ARGS);
Datum		_pte_set_snapshot_(PG_FUNCTION_ARGS);

/*
 * Get SERIALIZABLE snapshot control data from "master* transaction
 */
Datum
_pte_get_snapshot_(PG_FUNCTION_ARGS)
{
	char		buf[8192] = {0};
	char	   *snext = buf;
	size_t		len;
	size_t		maxlen = sizeof(buf) - 16;
	uint32		xcnt;
	Datum		result;

	if (SerializableSnapshot == NULL)
		elog(ERROR, "_pte_get_snapshot_: SerializableSnapshot is NULL");

	if (XactIsoLevel != XACT_SERIALIZABLE)
		elog(ERROR, "_pte_get_snapshot_: NOT serializable isolation level");

	sprintf(snext, "%d,%u,%u", SerializableSnapshot->xcnt,
		SerializableSnapshot->xmin, SerializableSnapshot->xmax);
	for (xcnt = 0; xcnt < SerializableSnapshot->xcnt; xcnt++)
	{
		len = strlen(snext);
		if (len >= maxlen)
		{
			char   *newbuf;
			maxlen = (maxlen + 16) * 2;
			newbuf = (char*) palloc(maxlen);
			strcpy(newbuf, snext);
			if (snext != buf)
				pfree(snext);
			snext = newbuf;
			maxlen -= 16;
		}
		sprintf(snext + len, ",%u", SerializableSnapshot->xip[xcnt]);
	}

	result = DirectFunctionCall1(textin, CStringGetDatum(snext));

	if (snext != buf)
		pfree(snext);

	return (result);
}

/*
 * Set SERIALIZABLE snapshot control data in "child" transaction
 */
Datum
_pte_set_snapshot_(PG_FUNCTION_ARGS)
{
	char	   *snext = DatumGetCString(DirectFunctionCall1(textout,
					PointerGetDatum(PG_GETARG_TEXT_P(0))));
	char	   *xthis = snext;
	char	   *xnext;
	int			xcnt;
	Snapshot	snapshot;
	Snapshot	old = SerializableSnapshot;

	if (old == NULL)
		elog(ERROR, "_pte_set_snapshot_: SerializableSnapshot is NULL");

	if (XactIsoLevel != XACT_SERIALIZABLE)
		elog(ERROR, "_pte_set_snapshot_: NOT serializable isolation level");

	snapshot = (Snapshot) malloc(sizeof(SnapshotData));

	/* xid count */
	xnext = strchr(xthis, ',');
	if (xnext == NULL)
		elog(ERROR, "_pte_set_snapshot_: xmin not found");
	*xnext = 0;
	snapshot->xcnt = atoi(xthis);
	if (snapshot->xcnt < 0)
		elog(ERROR, "_pte_set_snapshot_: invalid count %d", snapshot->xcnt);
	snapshot->xip = (TransactionId *)malloc(
		((snapshot->xcnt > 0) ? snapshot->xcnt : 1) * sizeof(TransactionId));

	xthis = xnext + 1;			/* xmin */
	xnext = strchr(xthis, ',');
	if (xnext == NULL)
		elog(ERROR, "_pte_set_snapshot_: xmax not found");
	*xnext = 0;
	snapshot->xmin = (TransactionId)atoi(xthis);

	xthis = xnext + 1;			/* xmax */
	xnext = strchr(xthis, ',');
	if (xnext != NULL)
		*xnext = 0;
	else
	{
		if (*xthis == 0)
			elog(ERROR, "_pte_set_snapshot_: xmax unspecified");
		xnext = xthis + strlen(xthis) - 1;
	}
	snapshot->xmax = (TransactionId)atoi(xthis);

	for (xcnt = 0; ; )
	{
		xthis = xnext + 1;			/* next active xid */
		if (*xthis == 0)
			break;
		xnext = strchr(xthis, ',');
		if (xnext != NULL)
			*xnext = 0;
		else
			xnext = xthis + strlen(xthis) - 1;
		if (xcnt < snapshot->xcnt)
		{
			snapshot->xip[xcnt] = (TransactionId)atoi(xthis);
			if (snapshot->xip[xcnt] < snapshot->xmin ||
				snapshot->xip[xcnt] >= snapshot->xmax)
				elog(ERROR, "_pte_set_snapshot_: invalid xid: %u <= %u < %u is false",
					snapshot->xmin, snapshot->xip[xcnt], snapshot->xmax);
		}
		xcnt++;
	}

	if (xcnt != snapshot->xcnt)
		elog(ERROR, "_pte_set_snapshot_: invalid number of active xids: %d != %d",
		xcnt, snapshot->xcnt);

	QuerySnapshot = SerializableSnapshot = snapshot;
	free(old->xip);
	free(old);
	pfree(snext);

	return (Int32GetDatum(0));
}
