/*-------------------------------------------------------------------------
 *
 * predicate.h
 *	  POSTGRES predicate locking definitions.
 *
 *
 * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * $PostgreSQL$
 *
 *-------------------------------------------------------------------------
 */
#ifndef PREDICATE_H
#define PREDICATE_H

#include "access/htup.h"
#include "storage/lock.h"
#include "utils/relcache.h"
#include "utils/snapshot.h"

/* GUC variables */
extern int	max_predicate_locks_per_xact;

/*
 * The SERIALIZABLEXACTTAG struct identifies a serializable transaction.
 */
typedef struct SERIALIZABLEXACTTAG
{
	VirtualTransactionId vxid;	/* We always have one of these. */
} SERIALIZABLEXACTTAG;

/*
 * Information needed for each serializable database transaction to support SSI techniques.
 * TODO SSI: Should inConflict and outConflict be lists?  That would allow us to reduce
 *			 false positives, *and* would allow us to guarantee that an immediate retry
 *			 of a transaction would never fail on the exact same conflicts.
 *			 The RAM doesn't look like it would be the limiting factor, but CPU time might
 *			 be -- we should have baseline benchmarks before attempting this.
 */
typedef struct SERIALIZABLEXACT
{
	/* hash key */
	SERIALIZABLEXACTTAG tag;

	/* data */
	struct SERIALIZABLEXACT *outConflict;		/* ptr to write transaction
												 * whose data we couldn't
												 * read. invalid means no
												 * conflict; self-reference
												 * means multiple or
												 * committed. */
	struct SERIALIZABLEXACT *inConflict;		/* ptr to read transaction
												 * which couldn't see our
												 * write. invalid means no
												 * conflict; self-reference
												 * means multiple or
												 * committed. */
	TransactionId topXid;		/* top level xid for the transaction, if one
								 * exists */
	TransactionId finishedBefore;		/* invalid means still running; else
										 * the struct expires when no tags <
										 * this. */
	TransactionId xmin;			/* the transaction's snapshot xmin */
	SHM_QUEUE	predicateLocks; /* list of associated PREDICATELOCK objects */
	SHM_QUEUE	xids;			/* list of associated SERIALIZABLEXID objects */
	SHM_QUEUE	finishedLink;	/* list link in
								 * FinishedSerializableTransactions */
	bool		rolledBack;		/* ignore conflicts when true; allows deferred
								 * cleanup */
} SERIALIZABLEXACT;


typedef enum PredicateLockTargetType
{
	PREDLOCKTAG_RELATION,
	PREDLOCKTAG_PAGE,
	PREDLOCKTAG_TUPLE
	/* TODO Other types may be needed for index locking */
}	PredicateLockTargetType;

/*
 * The PREDICATELOCKTARGETTAG struct is defined to fit into 16
 * bytes with no padding.  Note that this would need adjustment if we were
 * to widen Oid or BlockNumber to more than 32 bits.
 */
typedef struct PREDICATELOCKTARGETTAG
{
	uint32		locktag_field1; /* a 32-bit ID field */
	uint32		locktag_field2; /* a 32-bit ID field */
	uint32		locktag_field3; /* a 32-bit ID field */
	uint16		locktag_field4; /* a 16-bit ID field */
	uint16		locktag_field5; /* a 16-bit ID field */
} PREDICATELOCKTARGETTAG;

/*
 * These macros define how we map logical IDs of lockable objects into
 * the physical fields of PREDICATELOCKTARGETTAG.	Use these to set up values,
 * rather than accessing the fields directly.  Note multiple eval of target!
 *
 * TODO SSI: If we always use the same fields for the same type of value,
 * we should rename these.	Holding off until it's clear there are no exceptions.
 * Since indexes are relations with blocks and tuples, it's looking likely that
 * the rename will be possible.  If not, we may need to divide the last field
 * and use part of it for a target type, so that we know how to interpret the
 * data..
 */
#define SET_PREDICATELOCKTARGETTAG_RELATION(locktag,dboid,reloid) \
	((locktag).locktag_field1 = (dboid), \
	 (locktag).locktag_field2 = (reloid), \
	 (locktag).locktag_field3 = InvalidBlockNumber, \
	 (locktag).locktag_field4 = InvalidOffsetNumber, \
	 (locktag).locktag_field5 = 0)

#define SET_PREDICATELOCKTARGETTAG_PAGE(locktag,dboid,reloid,blocknum) \
	((locktag).locktag_field1 = (dboid), \
	 (locktag).locktag_field2 = (reloid), \
	 (locktag).locktag_field3 = (blocknum), \
	 (locktag).locktag_field4 = InvalidOffsetNumber, \
	 (locktag).locktag_field5 = 0)

#define SET_PREDICATELOCKTARGETTAG_TUPLE(locktag,dboid,reloid,blocknum,offnum) \
	((locktag).locktag_field1 = (dboid), \
	 (locktag).locktag_field2 = (reloid), \
	 (locktag).locktag_field3 = (blocknum), \
	 (locktag).locktag_field4 = (offnum), \
	 (locktag).locktag_field5 = 0)

#define GET_PREDICATELOCKTARGETTAG_DB(locktag) \
	((locktag).locktag_field1)
#define GET_PREDICATELOCKTARGETTAG_RELATION(locktag) \
	((locktag).locktag_field2)
#define GET_PREDICATELOCKTARGETTAG_PAGE(locktag) \
	((locktag).locktag_field3)
#define GET_PREDICATELOCKTARGETTAG_OFFSET(locktag) \
	((locktag).locktag_field4)
#define GET_PREDICATELOCKTARGETTAG_TYPE(locktag)							 \
	(((locktag).locktag_field4 != InvalidOffsetNumber) ? PREDLOCKTAG_TUPLE : \
	 (((locktag).locktag_field3 != InvalidBlockNumber) ? PREDLOCKTAG_PAGE :   \
	  PREDLOCKTAG_RELATION))

typedef struct PredicateLockData
{
	int			nelements;
	PREDICATELOCKTARGETTAG *locktags;
	SERIALIZABLEXACT *xacts;
} PredicateLockData;

/*
 * function prototypes
 */

/* housekeeping for shared memory predicate lock structures */
extern void InitPredicateLocks(void);
extern Size PredicateLockShmemSize(void);

/* predicate lock reporting */
extern PredicateLockData *GetPredicateLockStatusData(void);
extern bool PageIsPredicateLocked(const Relation relation, const BlockNumber blkno);

/* predicate lock maintenance */
extern void RegisterSerializableTransaction(const Snapshot snapshot);
extern void PredicateLockRelation(const Relation relation);
extern void PredicateLockPage(const Relation relation, const BlockNumber blkno);
extern void PredicateLockTuple(const Relation relation, const HeapTuple tuple);
extern void PredicateLockPageSplit(const Relation relation, const BlockNumber oldblkno, const BlockNumber newblkno);
extern void PredicateLockPageCombine(const Relation relation, const BlockNumber oldblkno, const BlockNumber newblkno);
extern void ReleasePredicateLocks(const bool isCommit);

/* conflict detection (may also trigger rollback) */
extern void CheckForSerializableConflictOut(const bool valid, const Relation relation, const HeapTuple tuple, const Buffer buffer);
extern void CheckForSerializableConflictIn(const Relation relation, const HeapTuple tuple, const Buffer buffer);

/* final rollback checking */
extern void PreCommit_CheckForSerializationFailure(void);

#endif   /* PREDICATE_H */
