/*-------------------------------------------------------------------------
 * cdbtm.h
 * 
 * DENTIFICATION
 * 	src/include/cdb/cdbtm.h
 *-------------------------------------------------------------------------
 */
#ifndef CDBTM_H
#define CDBTM_H

#include "lib/stringinfo.h"
#include "access/xlogdefs.h"
#include "cdb/cdbdistributedsnapshot.h"
#include "cdb/cdblocaldistribxact.h"
#include "cdb/cdbdtxcontextinfo.h"
#include "cdb/cdbpublic.h"
#ifndef FRONTEND
#include "storage/s_lock.h"
#include "port/atomics.h"
#endif
#include "nodes/plannodes.h"
#include "tcop/cmdtag.h"

struct Gang;

/**
 * DTX states, used to track the state of the distributed transaction
 *   from the QD's point of view.
 */
typedef enum
{
	/**
	 * Uninitialized transaction
	 */
	DTX_STATE_NONE = 0,

	/**
	 * The distributed transaction is active.
	 * For a primary, this state means the transaction requires distributed
	 * coordination (because it is explicit or an implicit writer transaction),
	 * and it will switch to other dtx states in different phases.
	 * For a hot standby, there is no coordination necessary so transaction 
	 * will stay in this state until the end of the commit.
	 */
	DTX_STATE_ACTIVE_DISTRIBUTED,

	/**
	 * For one-phase optimization commit, we haven't run the commit yet
	 */
	DTX_STATE_ONE_PHASE_COMMIT,
	DTX_STATE_NOTIFYING_ONE_PHASE_COMMIT,

	/**
	 * For two-phase commit, the first phase is about to run
	 */
	DTX_STATE_PREPARING,

	/**
	 * For two-phase commit, the first phase has completed
	 */
	DTX_STATE_PREPARED,
	DTX_STATE_INSERTING_COMMITTED,
	DTX_STATE_INSERTED_COMMITTED,
	DTX_STATE_NOTIFYING_COMMIT_PREPARED,
	DTX_STATE_INSERTING_FORGET_COMMITTED,
	DTX_STATE_INSERTED_FORGET_COMMITTED,

	/**
	 * Transaction rollback has been requested and QD is notifying all QD processes.
	 *
	 * _NO_PREPARED means that no QEs have started work on the first phase of two-phase commit.
	 */
	DTX_STATE_NOTIFYING_ABORT_NO_PREPARED,

	/**
	 * Transaction rollback has been requested and QD is notifying all QD processes.
	 *
	 * _SOME_PREPARED means that at least one QE has done the first phase of two-phase commit.
	 */
	DTX_STATE_NOTIFYING_ABORT_SOME_PREPARED,

	/**
	 * Transaction rollback has been requested and QD is notifying all QD processes.
	 *
	 * _PREPARED means that the QE processes have done the first phase of two-phase commit.
	 */
	DTX_STATE_NOTIFYING_ABORT_PREPARED,
	DTX_STATE_RETRY_COMMIT_PREPARED,
	DTX_STATE_RETRY_ABORT_PREPARED
}	DtxState;

/**
 * Transaction Management QD to QE protocol commands
 *
 */
typedef CommandTag DtxProtocolCommand;
#define DTX_PROTOCOL_COMMAND_NONE                                  CMDTAG_DTX_NONE
	/**
	 * Instruct the QE to go into implied writer state if not already.  This
	 *   is used when promoting a direct-dispatch transaction to a full-cluster
	 *   transaction
	 */
#define DTX_PROTOCOL_COMMAND_ABORT_NO_PREPARED                     CMDTAG_DTX_ABORT_NO_PREPARED
#define DTX_PROTOCOL_COMMAND_ABORT_SOME_PREPARED                   CMDTAG_DTX_ABORT_SOME_PREPARED
#define DTX_PROTOCOL_COMMAND_COMMIT_ONEPHASE                       CMDTAG_DTX_COMMIT_ONEPHASE
#define DTX_PROTOCOL_COMMAND_COMMIT_PREPARED                       CMDTAG_DTX_COMMIT_PREPARED
#define DTX_PROTOCOL_COMMAND_PREPARE                               CMDTAG_DTX_PREPARE
	/* for explicit transaction that doesn't write any xlog */
#define DTX_PROTOCOL_COMMAND_ABORT_PREPARED                        CMDTAG_DTX_ABORT_PREPARED
#define DTX_PROTOCOL_COMMAND_RETRY_ABORT_PREPARED                  CMDTAG_DTX_RETRY_ABORT_PREPARED
#define DTX_PROTOCOL_COMMAND_RETRY_COMMIT_PREPARED                 CMDTAG_DTX_RETRY_COMMIT_PREPARED
#define DTX_PROTOCOL_COMMAND_RECOVERY_ABORT_PREPARED               CMDTAG_DTX_RECOVERY_ABORT_PREPARED
#define DTX_PROTOCOL_COMMAND_RECOVERY_COMMIT_PREPARED              CMDTAG_DTX_RECOVERY_COMMIT_PREPARED

#define DTX_PROTOCOL_COMMAND_SUBTRANSACTION_BEGIN_INTERNAL         CMDTAG_DTX_BEGIN_INTERNAL_SUBTRANSACTION
#define DTX_PROTOCOL_COMMAND_SUBTRANSACTION_RELEASE_INTERNAL       CMDTAG_DTX_RELEASE_CURRENT_SUBTRANSACTION
#define DTX_PROTOCOL_COMMAND_SUBTRANSACTION_ROLLBACK_INTERNAL      CMDTAG_DTX_ROLLBACK_CURRENT_SUBTRANSACTION

/* DTX Context above xact.c */
typedef enum
{
	/**
	 * There is no distributed transaction.  This is the initial state and,
	 *   for utility mode connections or master-only queries, the only state.
	 *
	 * It is also the state to which the QD and QE return to between transactions.
	 */
	DTX_CONTEXT_LOCAL_ONLY,

	/**
	 * On QD: the process is currently part of a distributed
	 *    transaction.  Whether or not the transaction has been started on a QE
	 *    is not a part of the QD state -- that is tracked by assigning one of the
	 *    DTX_CONTEXT_QE* values on the QE process, and by updating the state field of the
	 *    MyTmGxactLocal.
	 */
	DTX_CONTEXT_QD_DISTRIBUTED_CAPABLE,

	/**
	 * On QD: indicates that the second-phase of the two-phase commit is being retried.
	 */
	DTX_CONTEXT_QD_RETRY_PHASE_2,

	/**
	 * TODO: how is something an Entry db?  fix this documentation
	 */
	DTX_CONTEXT_QE_ENTRY_DB_SINGLETON,

	/**
	 * On a QE that is the root of a query, this context means that the
	 *   distributed transaction was opened implicitly by a non-writing query
	 *   in an auto-commit transaction.
	 *
	 * This is essentially the same as _TWO_PHASE_IMPLICIT_WRITER except we know the
	 *   query won't dirty anything and so we don't need a two-phase commit.
	 */
	DTX_CONTEXT_QE_AUTO_COMMIT_IMPLICIT,

	/**
	 * On a QE, this context means that the distributed transaction was opened explicitly
	 *   by a BEGIN query statement
	 *
	 * Note that this state can happen even if there are no writer queries (because this
	 *   state is entered when the BEGIN statement is processed).
	 */
	DTX_CONTEXT_QE_TWO_PHASE_EXPLICIT_WRITER,

	/**
	 * On a QE that is the root of a query, this context means that the
	 *   distributed transaction was opened implicitly by a writing query
	 *   in an auto-commit transaction, and this is the writing QE process
	 *
	 * Same as _AUTO_COMMIT_IMPLICIT except that we need a two-phase commit because
	 *   of the dirty data.
	 */
	DTX_CONTEXT_QE_TWO_PHASE_IMPLICIT_WRITER,

	/**
	 * On a QE that is not the root of a query, this context is used.  This can happen
	 *   regardless of whether the overall transaction for the query is implicit or explicit,
	 *   and auto-commit vs two-phase
	 */
	DTX_CONTEXT_QE_READER,

	/**
	 * On the QE, the two-phase commit has been prepared (first phase complete).
	 */
	DTX_CONTEXT_QE_PREPARED,

	/**
	 * On the QE, the two-phase commit has been committed or aborted (second phase complete).
	 */
	DTX_CONTEXT_QE_FINISH_PREPARED
} DtxContext;



typedef struct TMGXACT_UTILITY_MODE_REDO
{
	bool		committed;
	DistributedTransactionId gxid;
}	TMGXACT_UTILITY_MODE_REDO;

typedef struct TMGXACT
{
	/*
	 * Like PGPROC->xid to local transaction, gxid is set if distributed
	 * transaction needs two-phase, and it's reset when distributed
	 * transaction ends, with ProcArrayLock held.
	 */
	DistributedTransactionId	gxid;

#ifndef FRONTEND
	/*
	* Atomic assign and fetch an uint64 DistributedTransactionId,
	* cooperate with gxid.
	* When first assigning and fetching gxid, using this to keep
	* atomic and then assign/fetch gxid with its value.
	* Only use this on QD when necessary as QE's gxid and
	* DistributedSnapshot is dispatched from QD.
	*/
	pg_atomic_uint64 			atomic_gxid;
#endif

	/*
	 * This is similar to xmin of PROC, stores lowest dxid on first snapshot
	 * by process with this as MyTmGxact.
	 */
	DistributedTransactionId	xminDistributedSnapshot;

	bool						includeInCkpt;
	int							sessionId;
}	TMGXACT;

typedef struct TMGXACTLOCAL
{
	/*
	 * Memory only fields.
	 * If we are in hot standby, only 'state' is relevant.
	 */
 	DtxState				state;
	
	bool						explicitBeginRemembered;

	/* Used on QE, indicates the transaction applies one-phase commit protocol */
	bool						isOnePhaseCommit;

	bool						writerGangLost;

	Bitmapset					*dtxSegmentsMap;
	List						*dtxSegments;
	List						*waitGxids;
}	TMGXACTLOCAL;

typedef struct TMGXACTSTATUS
{
	DistributedTransactionId	gxid;
 	DtxState					state;
	int							sessionId;
	DistributedTransactionId	xminDistributedSnapshot;
} TMGXACTSTATUS;

typedef struct TMGALLXACTSTATUS
{
	int		next;
	int		count;

	TMGXACTSTATUS		*statusArray;
} TMGALLXACTSTATUS;

typedef enum
{
	DTX_RECOVERY_EVENT_ABORT_PREPARED	= 1 << 0,
	DTX_RECOVERY_EVENT_BUMP_GXID			= 1 << 1
} DtxRecoveryEvent;

#define DTM_DEBUG3 (Debug_print_full_dtm ? LOG : DEBUG3)
#define DTM_DEBUG5 (Debug_print_full_dtm ? LOG : DEBUG5)

extern int max_tm_gxacts;
extern int gp_gxid_prefetch_num;

/* whether we need a distributed snapshot or not, updated before each
 * query been dispatched. */
extern bool needDistributedSnapshot;

extern DtxContext DistributedTransactionContext;

/* state variables for how much of the log file has been flushed */
extern volatile bool *shmDtmStarted;
extern volatile bool *shmCleanupBackends;
extern volatile pid_t *shmDtxRecoveryPid;
extern volatile DtxRecoveryEvent *shmDtxRecoveryEvents;
extern uint32 *shmNextSnapshotId;
#ifndef FRONTEND
extern slock_t *shmDtxRecoveryEventLock;
extern slock_t *shmGxidGenLock;
#endif
extern DistributedTransactionId *shmCommittedGxidArray;
extern volatile int *shmNumCommittedGxacts;

extern bool IsDtxRecoveryProcess(void);

extern const char *DtxStateToString(DtxState state);
extern const char *DtxProtocolCommandToString(DtxProtocolCommand command);
extern const char *DtxContextToString(DtxContext context);
extern void dtxDeformGid(const char	*gid,
							DistributedTransactionId		*distribXid);
extern void dtxFormGid(char *gid, DistributedTransactionId gxid);
extern DistributedTransactionId getDistributedTransactionId(void);
extern bool getDistributedTransactionIdentifier(char *id);

extern void setDistributedTransactionContext(DtxContext context);

extern void resetTmGxact(void);
extern void	prepareDtxTransaction(void);
extern bool isPreparedDtxTransaction(void);
extern bool notifyCommittedDtxTransactionIsNeeded(void);
extern void notifyCommittedDtxTransaction(void);
extern void	rollbackDtxTransaction(void);

extern void insertingDistributedCommitted(void);
extern void insertedDistributedCommitted(void);

extern void redoDtxCheckPoint(TMGXACT_CHECKPOINT *gxact_checkpoint);
extern void redoDistributedCommitRecord(DistributedTransactionId gxid);
extern void redoDistributedForgetCommitRecord(DistributedTransactionId gxid);

extern void setupDtxTransaction(void);
extern DtxState getCurrentDtxState(void);
extern void bumpGxid(void);
extern bool isCurrentDtxActivated(void);

extern void sendDtxExplicitBegin(void);
extern bool isDtxExplicitBegin(void);

extern bool dispatchDtxCommand(const char *cmd);

extern void tmShmemInit(void);
extern int	tmShmemSize(void);

extern void verify_shared_snapshot_ready(int cid);

int			mppTxnOptions(bool needDtx);
int			mppTxOptions_IsoLevel(int txnOptions);
bool		isMppTxOptions_ReadOnly(int txnOptions);
bool		isMppTxOptions_NeedDtx(int txnOptions);
bool		isMppTxOptions_ExplicitBegin(int txnOptions);

extern void getAllDistributedXactStatus(TMGALLXACTSTATUS **allDistributedXactStatus);
extern bool getNextDistributedXactStatus(TMGALLXACTSTATUS *allDistributedXactStatus, TMGXACTSTATUS **distributedXactStatus);
extern void setupRegularDtxContext (void);
extern void setupQEDtxContext (DtxContextInfo *dtxContextInfo);
extern void finishDistributedTransactionContext (char *debugCaller, bool aborted);
extern void performDtxProtocolCommand(DtxProtocolCommand dtxProtocolCommand,
									  const char *gid,
									  DtxContextInfo *contextInfo);

extern bool currentDtxDispatchProtocolCommand(DtxProtocolCommand dtxProtocolCommand, bool raiseError);
extern bool doDispatchSubtransactionInternalCmd(DtxProtocolCommand cmdType);
extern bool doDispatchDtxProtocolCommand(DtxProtocolCommand dtxProtocolCommand, char *gid,
							 bool raiseError, List *dtxSegments,
							 char *serializedDtxContextInfo, int serializedDtxContextInfoLen);

extern void markCurrentGxactWriterGangLost(void);

extern bool currentGxactWriterGangLost(void);

extern void addToGxactDtxSegments(struct Gang* gp);
extern bool CurrentDtxIsRollingback(void);

extern pid_t DtxRecoveryPID(void);
extern DtxRecoveryEvent GetDtxRecoveryEvent(void);
extern void SetDtxRecoveryEvent(DtxRecoveryEvent event);
extern void DtxRecoveryMain(Datum main_arg);
extern bool DtxRecoveryStartRule(Datum main_arg);

#endif   /* CDBTM_H */
