//==============	DAE: OS/2 Database Access Engine	===================
//==============	   fcb.h: File Control Block		===================

#ifdef	FCB_INCLUDED
#error fcb.h already included
#endif	/* FCB_INCLUDED */
#define FCB_INCLUDED

// Database Key
typedef ULONG DBK;

// Flags for FCB
#define fFCBTemporaryTable		(1<<0)  	// This is a temporary file
#define fFCBClusteredIndex		(1<<1)  	// This FCB is for data records.
#define fFCBDenyRead			(1<<2)  	// no other session can read domain
// #define fFCBDenyWrite  		(1<<3)  	// no other session can write domain
#define fFCBSentinel			(1<<4)  	// FCB is only flag holder
// #define fFCBDenyDDL	  		(1<<5)		// no other transaction can update/delete/replace domain
#define fFCBWait				(1<<6)		// wait flag
#define fFCBOLCStatsAvail		(1<<7)		// are OLC Stats available?
#define fFCBOLCStatsChange		(1<<8)		// have OLC Stats changed since last open?
#define fFCBDeletePending		(1<<9)		// is a delete pending on this table/index?
#define fFCBDomainOperation		(1<<10)		// is used to synchronize bm cleanup
											// index creation, index deletion and table deletion

#define FFCBDomainOperation( pfcb )			( (pfcb)->wFlags & fFCBDomainOperation )
#define FCBSetDomainOperation( pfcb )	   	( (pfcb)->wFlags |= fFCBDomainOperation )
#define FCBResetDomainOperation( pfcb )		( (pfcb)->wFlags &= ~(fFCBDomainOperation) )

#define FFCBDeletePending( pfcb )		  	( (pfcb)->wFlags & fFCBDeletePending )
#define FCBSetDeletePending( pfcb )	 	  	( (pfcb)->wFlags |= fFCBDeletePending )
#define FCBResetDeletePending( pfcb )	  	( (pfcb)->wFlags &= ~(fFCBDeletePending) )

#define FFCBOLCStatsAvail( pfcb )		  	( (pfcb)->wFlags & fFCBOLCStatsAvail )
#define FCBSetOLCStatsAvail( pfcb )	 	  	( (pfcb)->wFlags |= fFCBOLCStatsAvail )
#define FCBResetOLCStatsAvail( pfcb )	  	( (pfcb)->wFlags &= ~(fFCBOLCStatsAvail) )

#define FFCBOLCStatsChange( pfcb )		  	( (pfcb)->wFlags & fFCBOLCStatsChange )
#define FCBSetOLCStatsChange( pfcb )	  	( (pfcb)->wFlags |= fFCBOLCStatsChange )
#define FCBResetOLCStatsChange( pfcb )	  	( (pfcb)->wFlags &= ~(fFCBOLCStatsChange) )

#define FFCBTemporaryTable( pfcb )		  	( (pfcb)->wFlags & fFCBTemporaryTable )
#define FCBSetTemporaryTable( pfcb )	  	( (pfcb)->wFlags |= fFCBTemporaryTable )
#define FCBResetTemporaryTable( pfcb )	  	( (pfcb)->wFlags &= ~(fFCBTemporaryTable) )

#define FFCBClusteredIndex( pfcb )		  	( (pfcb)->wFlags & fFCBClusteredIndex )
#define FCBSetClusteredIndex( pfcb )	  	( (pfcb)->wFlags |= fFCBClusteredIndex )
#define FCBResetClusteredIndex( pfcb )	  	( (pfcb)->wFlags &= ~(fFCBClusteredIndex) )

#define FFCBDenyWrite( pfcb )			  	( (pfcb)->crefDenyWrite > 0 )
#define FCBSetDenyWrite( pfcb )			  	( (pfcb)->crefDenyWrite++ )

#define FCBResetDenyWrite( pfcb )		  	\
	{									  	\
	Assert( (pfcb)->crefDenyWrite > 0 );  	\
	--(pfcb)->crefDenyWrite;			  	\
	}

#define FFCBDenyRead( pfcb, ppib )			( (pfcb)->wFlags & fFCBDenyRead && (ppib) != (pfcb)->ppibDenyRead )

#define FCBSetDenyRead( pfcb, ppib )		 		\
	{										 		\
	if ( (pfcb)->crefDenyRead++ == 0 )		 		\
		{									 		\
		Assert( (pfcb)->ppibDenyRead == ppibNil );	\
		(pfcb)->ppibDenyRead = (ppib);		 		\
		(pfcb)->wFlags |= fFCBDenyRead;		 		\
		}									 		\
	}

#define FCBResetDenyRead( pfcb )			 		\
	{										 		\
	Assert( (pfcb)->crefDenyRead > 0 );		 		\
	Assert( (pfcb)->ppibDenyRead != ppibNil );		\
	if ( --(pfcb)->crefDenyRead == 0 )		 		\
		{									 		\
		(pfcb)->wFlags &= ~(fFCBDenyRead);	 		\
		(pfcb)->ppibDenyRead = ppibNil;	   			\
		}								   			\
	}

#define FFCBDenyReadByUs( pfcb, ppib )	 	( (pfcb)->wFlags & fFCBDenyRead && (ppib) == (pfcb)->ppibDenyRead )

#define FFCBSentinel( pfcb )			   	( (pfcb)->wFlags & fFCBSentinel )
#define FCBSetSentinel( pfcb )				( (pfcb)->wFlags |= fFCBSentinel )
#define FCBResetSentinel( pfcb )		   	( (pfcb)->wFlags &= ~(fFCBSentinel) )

#define FFCBDenyDDL( pfcb, ppib )			( (pfcb)->crefDenyDDL > 0 && (ppib) != (pfcb)->ppibDDL )
#define FFCBDenyDDLByUs( pfcb, ppib )		( (pfcb)->crefDenyDDL > 0 && (ppib) == (pfcb)->ppibDDL )

#define FCBSetDenyDDL( pfcb, ppib )					\
	{												\
	if ( (pfcb)->crefDenyDDL++ == 0 )				\
		{											\
		Assert( (pfcb)->ppibDDL == ppibNil );		\
		(pfcb)->ppibDDL = (ppib);					\
		}											\
	}

#define FCBResetDenyDDL( pfcb )		  				\
	{												\
	Assert( (pfcb)->crefDenyDDL > 0 );	 			\
	Assert( (pfcb)->ppibDDL != ppibNil ); 			\
	if ( --(pfcb)->crefDenyDDL == 0 )	  			\
		{											\
		(pfcb)->ppibDDL = ppibNil;			  		\
		}											\
	}

#define FFCBWait( pfcb )							( (pfcb)->wFlags & fFCBWait )

#define FCBSetWait( pfcb )							\
	{												\
	Assert( !FFCBWait( pfcb ) );					\
	(pfcb)->wFlags |= fFCBWait;						\
	}

#define FCBResetWait( pfcb )						\
	{										   		\
	Assert( FFCBWait( pfcb ) );						\
	(pfcb)->wFlags &= ~(fFCBWait);					\
	}

#define FCBVersionIncrement( pfcb )			(pfcb)->cVersion++;
#define FCBVersionDecrement( pfcb )					\
	{												\
	Assert( (pfcb)->cVersion > 0 );					\
	(pfcb)->cVersion--;								\
	}
#define CVersionFCB( pfcb )					(pfcb)->cVersion

/* hash table for FCB's -- only FCB's for tables and db's are hashed
/**/
#define	cFCBBuckets	256
FCB*	pfcbHash[cFCBBuckets];

#define FCBHashInit()  								\
	{ 												\
	Assert( pfcbNil == (FCB *) 0 ); 				\
	memset( pfcbHash, '\0', sizeof( pfcbHash ) );	\
	}


#define	FFCBAvail( pfcb, ppib )							\
	(	pfcb->wRefCnt == 0 && 							\
		pfcb->pgnoFDP != 1 &&							\
		!FFCBSentinel( pfcb ) &&						\
		!FFCBDenyRead( pfcb, ppib ) &&					\
		!FFCBWait( pfcb ) &&							\
		( pfcb->dbid == dbidTemp || FFCBINoVersion( pfcb ) ) )


// File Control Block
//
struct _fcb
	{
	//--------------------USED BY DATA & INDEX FCB---------------------
	struct _fcb 	*pfcbNextIndex;  	// chain of indexes for this file
	struct _fcb		*pfcbNextInHashBucket;
	struct _fdb 	volatile *pfdb; 	// field descriptors
	struct _idb 	*pidb;			  	// index info (NULL if "seq." file)
	FUCB			*pfucb;				// chain of FUCBs open on this file
	PIB  			*ppibDDL;			// ppib of process updating index/adding column
	PIB  			*ppibDenyRead;		// ppib of process holding exclusive lock
	CRIT			critSplit;			//	per domain split MUTEX
	PGNO			pgnoFDP;			// FDP of this file/index
	PGNO			pgnoRoot;			// pgno of the root of the domain
	SRID			bmRoot;				// bm of root of the domain
										// -- useful if Root is movable, e.g, DATA
	
	DBID			dbid;				// which database
	INT				itagRoot;			// itag of the root of the domain
	INT				cbDensityFree;		// loading density parameter:
										// # of bytes free w/o using new page
	INT				wFlags;			 	// flags for this FCB
	INT				wRefCnt;			// # of FUCBs for this file/index
	INT				volatile cVersion;	// # of RCEs for this file/index
	INT				crefDenyRead;	 	// # of FUCBs with deny read flag
	INT				crefDenyWrite;	 	// # of FUCBs with deny write flag
	INT				crefDenyDDL;	 	// # of FUCBs with deny DDL flag

	ULONG		   	cpgCompactFreed;
	PERS_OLCSTAT	olcStat;
		
	//--------------------USED ONLY BY FCB OF DATA---------------------
	CHAR		   	*szFileName;			// name of file (for GetTableInfo)
	struct _fcb		*pfcbNext;		 		// Next data FCB in global list
	DBK	  			dbkMost;				// Greatest DBK in use
											// (if "sequential" file)
	ULONG		   	ulLongIdMax;			// max long field id
	BYTE		   	rgbitAllIndex[32];		//	used for clustered index FCB only
	BOOL		   	fAllIndexTagged;		//	used for clustered index FCB only
	};

#define FCBInit( pfcb )							\
	{											\
	memset( pfcb, '\0', sizeof( FCB ) );		\
	}

#define PfcbMEMAlloc()			(FCB*)PbMEMAlloc(iresFCB)

#ifdef DEBUG /*  Debug check for illegal use of freed fcb  */ 
#define MEMReleasePfcb(pfcb)										\
	{																\
	Assert( PfcbFCBGet( pfcb->dbid, pfcb->pgnoFDP ) != pfcb );		\
	MEMRelease( iresFCB, (BYTE*)(pfcb) );							\
	pfcb = pfcbNil;													\
	}
#else
#define MEMReleasePfcb(pfcb)										\
	{																\
	Assert( PfcbFCBGet( pfcb->dbid, pfcb->pgnoFDP ) != pfcb );		\
	MEMRelease( iresFCB, (BYTE*)(pfcb) );							\
	}
#endif

/*	if opening domain for read, write or read write, and not with
/*	deny read or deny write, and domain does not have deny read or
/*	deny write set, then return JET_errSuccess, else call
/*	ErrFCBISetMode to determine if lock is by other session or to
/*	put lock on domain.			 
/**/
#define	ErrFCBSetMode( ppib, pfcb, grbit )													\
( ( ( ( grbit & ( JET_bitTableDenyRead | JET_bitTableDenyWrite ) ) == 0 ) &&		\
	( ( FFCBDenyDDL( pfcb, ppib ) || FFCBDenyRead( pfcb, ppib ) || FFCBDenyWrite( pfcb ) ) == fFalse ) ) ?				\
	JET_errSuccess : ErrFCBISetMode( ppib, pfcb, grbit ) )

/*	reset DDL is same as reset Delete.  Both use deny read flags
/*	or sentinel.
/**/
#define	FCBResetRenameTable	FCBResetDeleteTable

extern BYTE * __near rgfcb;
extern FCB * __near pfcbGlobalList;
extern SEM __near semGlobalFCBList;
extern SEM __near semLinkUnlink;

VOID FCBLink( FUCB *pfucb, FCB *pfcb );
VOID FCBRegister( FCB *pfcb );
VOID FCBDiscard( FCB *pfcb );
VOID FCBUnlink( FUCB *pfucb );
FCB *PfcbFCBGet( DBID dbid, PGNO pgnoFDP );
ERR ErrFCBAlloc( PIB *ppib, FCB **ppfcb );
VOID FCBPurgeDatabase( DBID dbid );
VOID FCBPurgeTable( DBID dbid, PGNO pgnoFDP );
ERR ErrFCBNew( PIB *ppib, DBID dbid, PGNO pgno, FCB **ppfcb );
ERR ErrFCBISetMode( PIB *ppib, FCB *pfcb, JET_GRBIT grbit );
VOID FCBResetMode( PIB *ppib, FCB *pfcb, JET_GRBIT grbit );
ERR ErrFCBSetDeleteTable( PIB *ppib, DBID dbid, PGNO pgnoFDP );
VOID FCBResetDeleteTable( DBID dbid, PGNO pgnoFDP );
ERR ErrFCBSetRenameTable( PIB *ppib, DBID dbid, PGNO pgno );
FCB *FCBResetAfterRedo( void );
BOOL FFCBTableOpen ( DBID dbid, PGNO pgno );

VOID FCBLinkIndex( FCB *pfcbTable, FCB *pfcbIndex );
VOID FCBUnlinkIndex( FCB *pfcbTable, FCB *pfcbIndex );
BOOL FFCBUnlinkIndexIfFound( FCB *pfcbTable, FCB *pfcbIndex );
FCB *PfcbFCBUnlinkIndexByName( FCB *pfcb, CHAR *szIndex );
ERR ErrFCBSetDeleteIndex( PIB *ppib, FCB *pfcbTable, CHAR *szIndex );
VOID FCBResetDeleteIndex( FCB *pfcbIndex );