/* Copyright (c) 2005 PrimeBase Technologies GmbH
 *
 * PrimeBase XT
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * 2005-02-08	Paul McCullagh
 *
 * H&G2JCtL
 */
#ifndef __xt_table_h__
#define __xt_table_h__

#include <time.h>

#include "datalog_xt.h"
#include "filesys_xt.h"
#include "hashtab_xt.h"
#include "index_xt.h"
#include "cache_xt.h"
#include "util_xt.h"
#include "heap_xt.h"
#include "tabcache_xt.h"
#include "xactlog_xt.h"
#include "lock_xt.h"

struct XTDatabase;
struct XTThread;
struct XTCache;
struct XTOpenTable;
struct XTTablePath;

#define XT_TAB_INCOMPATIBLE_VERSION	4
#define XT_TAB_CURRENT_VERSION		5

#define XT_IND_CURRENT_VERSION		3

#define XT_HEAD_BUFFER_SIZE			1024

#ifdef DEBUG
//#define XT_TRACK_INDEX_UPDATES
//#define XT_TRACK_RETURNED_ROWS
#endif

/*
 * NOTE: Records may only be freed (placed on the free list), after
 * all currently running transactions have ended.
 * The reason is, running transactions may have references in memory
 * to these records (a sequential scan has a large buffer).
 * If the records are freed they may be re-used. This will
 * cause problems because the references will then refer to
 * new data.
 *
 * As a result, deleted records are first placed in the
 * REMOVED state. Later, when transactions have quit, they
 * are freed.
 */
#define XT_TAB_STATUS_FREED			0x00			/* On the free list. */
#define XT_TAB_STATUS_DELETE		0x01			/* A transactional delete record (an "update" that indicates a delete). */
#define XT_TAB_STATUS_FIXED			0x02
#define XT_TAB_STATUS_VARIABLE		0x03			/* Uses one block, but has the variable format. */
#define XT_TAB_STATUS_EXT_DLOG		0x04			/* Variable format, and the trailing part of the record in the data log. */
#define XT_TAB_STATUS_EXT_HDATA		0x05			/* Variable format, and the trailing part of the record in the handle data file. */
#define XT_TAB_STATUS_DATA			0x06			/* A block of data with a next pointer (5 bytes overhead). */
#define XT_TAB_STATUS_END_DATA		0x07			/* An block of data without an end pointer (1 byte overhead). */
#define XT_TAB_STATUS_MASK			0x0F

#define XT_TAB_STATUS_DEL_CLEAN		(XT_TAB_STATUS_DELETE | XT_TAB_STATUS_CLEANED_BIT)
#define XT_TAB_STATUS_FIX_CLEAN		(XT_TAB_STATUS_FIXED | XT_TAB_STATUS_CLEANED_BIT)
#define XT_TAB_STATUS_VAR_CLEAN		(XT_TAB_STATUS_VARIABLE | XT_TAB_STATUS_CLEANED_BIT)
#define XT_TAB_STATUS_EXT_CLEAN		(XT_TAB_STATUS_EXT_DLOG | XT_TAB_STATUS_CLEANED_BIT)

#define XT_TAB_STATUS_CLEANED_BIT	0x80			/* This bit is set when the record is cleaned and committed. */

#define XT_REC_IS_CLEAN(x)			((x) & XT_TAB_STATUS_CLEANED_BIT)
#define XT_REC_IS_FREE(x)			(((x) & XT_TAB_STATUS_MASK) == XT_TAB_STATUS_FREED)
#define XT_REC_IS_DELETE(x)			(((x) & XT_TAB_STATUS_MASK) == XT_TAB_STATUS_DELETE)
#define XT_REC_IS_FIXED(x)			(((x) & XT_TAB_STATUS_MASK) == XT_TAB_STATUS_FIXED)
#define XT_REC_IS_VARIABLE(x)		(((x) & XT_TAB_STATUS_MASK) == XT_TAB_STATUS_VARIABLE)
#define XT_REC_IS_EXT_DLOG(x)		(((x) & XT_TAB_STATUS_MASK) == XT_TAB_STATUS_EXT_DLOG)
#define XT_REC_IS_EXT_HDATA(x)		(((x) & XT_TAB_STATUS_MASK) == XT_TAB_STATUS_EXT_HDATA)
#define XT_REC_NOT_VALID(x)			(XT_REC_IS_FREE(x) || XT_REC_IS_DELETE(x))

/* Results for xt_use_table_by_id(): */
#define XT_TAB_OK					0
#define XT_TAB_NOT_FOUND			1
#define XT_TAB_NO_DICTIONARY		2
#define XT_TAB_POOL_CLOSED			3				/* Cannot open table at the moment, the pool is closed. */
#define XT_TAB_FAILED				4

#define XT_TAB_ROW_USE_RW_MUTEX

#ifdef XT_TAB_ROW_USE_FASTWRLOCK
#define XT_TAB_ROW_LOCK_TYPE			XTFastRWLockRec
#define XT_TAB_ROW_INIT_LOCK(s, i)		xt_fastrwlock_init(s, i)
#define XT_TAB_ROW_FREE_LOCK(s, i)		xt_fastrwlock_free(s, i)	
#define XT_TAB_ROW_READ_LOCK(i, s)		xt_fastrwlock_slock(i, s)
#define XT_TAB_ROW_WRITE_LOCK(i, s)		xt_fastrwlock_xlock(i, s)
#define XT_TAB_ROW_UNLOCK(i, s)			xt_fastrwlock_unlock(i, s)
#elif defined(XT_TAB_ROW_USE_PTHREAD_RW)
#define XT_TAB_ROW_LOCK_TYPE			xt_rwlock_type
#define XT_TAB_ROW_INIT_LOCK(s, i)		xt_init_rwlock(s, i)
#define XT_TAB_ROW_FREE_LOCK(s, i)		xt_free_rwlock(i)	
#define XT_TAB_ROW_READ_LOCK(i, s)		xt_slock_rwlock_ns(i)
#define XT_TAB_ROW_WRITE_LOCK(i, s)		xt_xlock_rwlock_ns(i)
#define XT_TAB_ROW_UNLOCK(i, s)			xt_unlock_rwlock_ns(i)
#elif defined(XT_TAB_ROW_USE_RW_MUTEX)
#define XT_TAB_ROW_LOCK_TYPE			XTRWMutexRec
#define XT_TAB_ROW_INIT_LOCK(s, i)		xt_rwmutex_init_with_autoname(s, i)
#define XT_TAB_ROW_FREE_LOCK(s, i)		xt_rwmutex_free(s, i)	
#define XT_TAB_ROW_READ_LOCK(i, s)		xt_rwmutex_slock(i, (s)->t_id)
#define XT_TAB_ROW_WRITE_LOCK(i, s)		xt_rwmutex_xlock(i, (s)->t_id)
#define XT_TAB_ROW_UNLOCK(i, s)			xt_rwmutex_unlock(i, (s)->t_id)
#else
#define XT_TAB_ROW_LOCK_TYPE			XTSpinLockRec
#define XT_TAB_ROW_INIT_LOCK(s, i)		xt_spinlock_init(s, i)
#define XT_TAB_ROW_FREE_LOCK(s, i)		xt_spinlock_free(s, i)	
#define XT_TAB_ROW_READ_LOCK(i, s)		xt_spinlock_lock(i)
#define XT_TAB_ROW_WRITE_LOCK(i, s)		xt_spinlock_lock(i)
#define XT_TAB_ROW_UNLOCK(i, s)			xt_spinlock_unlock(i)
#endif

/* ------- TABLE DATA FILE ------- */

#define XT_TAB_DATA_MAGIC		0x1234ABCD

#define XT_FORMAT_DEF_SPACE		512

#define XT_TAB_FLAGS_TEMP_TAB	1

/*
 * This header ensures that no record in the data file has the offset 0.
 */
typedef struct XTTableHead {
	XTDiskValue4			th_head_size_4;							/* The size of the table header. */
	XTDiskValue4			th_op_seq_4;
	XTDiskValue6			th_row_free_6;
	XTDiskValue6			th_row_eof_6;
	XTDiskValue6			th_row_fnum_6;
	XTDiskValue6			th_rec_free_6;
	XTDiskValue6			th_rec_eof_6;
	XTDiskValue6			th_rec_fnum_6;
} XTTableHeadDRec, *XTTableHeadDPtr;

typedef struct XTTableFormat {
	XTDiskValue4			tf_format_size_4;						/* The size of this structure (table format). */
	XTDiskValue4			tf_tab_head_size_4;						/* The offset of the first record in the data handle file. */
	XTDiskValue2			tf_tab_version_2;						/* The table version number. */
	XTDiskValue2			tf_tab_flags_2;							/* Table flags XT_TAB_FLAGS_* */
	XTDiskValue4			tf_rec_size_4;							/* The maximum size of records in the table. */
	XTDiskValue1			tf_rec_fixed_1;							/* Set to 1 if this table contains fixed length records. */
	XTDiskValue1			tf_reserved_1;							/* - */
	XTDiskValue8			tf_min_auto_inc_8;						/* This is the minimum auto-increment value. */
	xtWord1					tf_reserved[64];						/* Reserved, set to 0. */
	char					tf_definition[XT_VAR_LENGTH];			/* A cstring, currently it only contains the foreign key information. */
} XTTableFormatDRec, *XTTableFormatDPtr;

#define XT_STAT_ID_MASK(x)	((x) & (u_int) 0x000000FF)

/* A record that fits completely in the data file record */
typedef struct XTTabRecHead {
	xtWord1					tr_rec_type_1;
	xtWord1					tr_stat_id_1;
	xtDiskRecordID4			tr_prev_rec_id_4;		/* The previous variation of this record. */
	XTDiskValue4			tr_xact_id_4;			/* The transaction ID. */
	XTDiskValue4			tr_row_id_4;			/* The row ID of this record. */
} XTTabRecHeadDRec, *XTTabRecHeadDPtr;

typedef struct XTTabRecFix {
	xtWord1					tr_rec_type_1;			/* XT_TAB_STATUS_FREED, XT_TAB_STATUS_DELETE,
													 * XT_TAB_STATUS_FIXED, XT_TAB_STATUS_VARIABLE */
	xtWord1					tr_stat_id_1;
	xtDiskRecordID4			tr_prev_rec_id_4;		/* The previous variation of this record. */
	XTDiskValue4			tr_xact_id_4;			/* The transaction ID. */
	XTDiskValue4			tr_row_id_4;			/* The row ID of this record. */
	xtWord1					rf_data[XT_VAR_LENGTH];	/* NOTE: This data is in RAW MySQL format. */
} XTTabRecFixDRec, *XTTabRecFixDPtr;

/* An extended record that overflows into the log file: */
typedef struct XTTabRecExt {
	xtWord1					tr_rec_type_1;			/* XT_TAB_STATUS_EXT_DLOG */
	xtWord1					tr_stat_id_1;
	xtDiskRecordID4			tr_prev_rec_id_4;		/* The previous variation of this record. */
	XTDiskValue4			tr_xact_id_4;			/* The transaction ID. */
	XTDiskValue4			tr_row_id_4;			/* The row ID of this record. */
	XTDiskValue2			re_log_id_2;			/* Reference to overflow area, log ID */
	XTDiskValue6			re_log_offs_6;			/* Reference to the overflow area, log offset */
	XTDiskValue4			re_log_dat_siz_4;		/* Size of the overflow data. */
	xtWord1					re_data[XT_VAR_LENGTH];	/* This data is in packed PBXT format. */
} XTTabRecExtDRec, *XTTabRecExtDPtr;

typedef struct XTTabRecExtHdat {
	xtWord1					tr_rec_type_1;			/* XT_TAB_STATUS_EXT_HDATA */
	xtWord1					tr_stat_id_1;
	xtDiskRecordID4			tr_prev_rec_id_4;		/* The previous variation of this record. */
	XTDiskValue4			tr_xact_id_4;			/* The transaction ID. */
	XTDiskValue4			tr_row_id_4;			/* The row ID of this record. */
	XTDiskValue4			eh_blk_rec_id_4;		/* The record ID of the next block. */
	XTDiskValue2			eh_blk_siz_2;			/* The total size of the data in the trailing blocks */
	xtWord1					eh_data[XT_VAR_LENGTH];	/* This data is in packed PBXT format. */
} XTTabRecExtHdatDRec, *XTTabRecExtHdatDPtr;

typedef struct XTTabRecData {
	xtWord1					tr_rec_type_1;			/* XT_TAB_STATUS_DATA */
	XTDiskValue4			rd_blk_rec_id_4;		/* The record ID of the next block. */
	xtWord1					rd_data[XT_VAR_LENGTH];	/* This data is in packed PBXT format. */
} XTTabRecDataDRec, *XTTabRecDataDPtr;

typedef struct XTTabRecEndDat {
	xtWord1					tr_rec_type_1;			/* XT_TAB_STATUS_END_DATA */
	xtWord1					ed_data[XT_VAR_LENGTH];	/* This data is in packed PBXT format. */
} XTTabRecEndDatDRec, *XTTabRecEndDatDPtr;

#define XT_REC_FIX_HEADER_SIZE		sizeof(XTTabRecHeadDRec)
#define XT_REC_EXT_HEADER_SIZE		offsetof(XTTabRecExtDRec, re_data)
#define XT_REC_FIX_EXT_HEADER_DIFF	(XT_REC_EXT_HEADER_SIZE - XT_REC_FIX_HEADER_SIZE)

typedef struct XTTabRecFree {
	xtWord1					rf_rec_type_1;
	xtWord1					rf_not_used_1;
	xtDiskRecordID4			rf_next_rec_id_4;		/* The next block on the free list. */
} XTTabRecFreeDRec, *XTTabRecFreeDPtr;

typedef struct XTTabRecInfo {
	XTTabRecFixDPtr			ri_fix_rec_buf;			/* This references the start of the buffer (set for all types of records) */
	XTTabRecExtDPtr			ri_ext_rec;				/* This is only set for extended records. */
	xtWord4					ri_rec_buf_size;
	XTactExtRecEntryDPtr	ri_log_buf;
	xtWord4					ri_log_data_size;		/* This size of the data in the log record. */
	xtRecordID				ri_rec_id;				/* The record ID. */
} XTTabRecInfoRec, *XTTabRecInfoPtr;

/* ------- TABLE ROW FILE ------- */

#define XT_TAB_ROW_SHIFTS		2
#define XT_TAB_ROW_MAGIC		0x4567CDEF
//#define XT_TAB_ROW_FREE			0
//#define XT_TAB_ROW_IN_USE		1

/*
 * NOTE: The shift count assumes the size of a table row
 * reference is 8 bytes (XT_TAB_ROW_SHIFTS)
 */
typedef struct XTTabRowRef {
	XTDiskValue4			rr_ref_id_4;			/* 4-byte reference, could be a RowID or a RecordID
													 * If this row is free, then it is a RowID, which
													 * references the next free row.
													 * If it is in use, then it is a RecordID which
													 * points to the first record in the variation
													 * list for the row.
													 */
} XTTabRowRefDRec, *XTTabRowRefDPtr;

/*
 * This is the header for the row file. The size MUST be a
 * the same size as sizeof(XTTabRowRefDRec)
 */
typedef struct XTTabRowHead {
	XTDiskValue4			rh_magic_4;
} XTTabRowHeadDRec, *XTTabRowHeadDPtr;

/* ------- TABLE & OPEN TABLES & TABLE LISTING ------- */

/* {TEMP-TABLES}
 * Temporary tables do not need to be flused,
 * and they also do not need to be recovered!
 * Currently this is determined by the name of the
 * table!
 */
typedef struct XTTable : public XTHeap {
	struct XTDatabase		*tab_db;			/* Heap pointer */
	XTPathStrPtr			tab_name;
	xtBool					tab_free_locks;
	xtTableID				tab_id;

	xtWord8					tab_auto_inc;							/* The last value returned as an auto-increment value {PRE-INC}. */
	XTSpinLockRec			tab_ainc_lock;							/* Lock for the auto-increment counter. */

	size_t					tab_index_format_offset;
	size_t					tab_index_header_size;
	size_t					tab_index_page_size;
	u_int					tab_index_block_shifts;
	XTIndexHeadDPtr			tab_index_head;
	size_t					tab_table_format_offset;
	size_t					tab_table_head_size;
	XTDictionaryRec			tab_dic;
	xt_mutex_type			tab_dic_field_lock;						/* Lock for setting field->ptr!. */

	XTRowLocksRec			tab_locks;								/* The locks held on this table. */

	XTTableSeqRec			tab_seq;								/* The table operation sequence. */
	XTTabCacheRec			tab_rows;
	XTTabCacheRec			tab_recs;

	/* Used to apply operations to the database in order. */
	XTSortedListPtr			tab_op_list;							/* The operation list. Operations to be applied. */

	/* Values that belong in the header when flushed! */
	xtBool					tab_flush_pending;						/* TRUE if the table needs to be flushed */
	xtBool					tab_recovery_done;						/* TRUE if the table has been recovered */
	xtBool					tab_temporary;							/* TRUE if this is a temporary table {TEMP-TABLES}. */
	off_t					tab_bytes_to_flush;						/* Number of bytes of the record/row files to flush. */

	xtOpSeqNo				tab_head_op_seq;						/* The number of the operation last applied to the database. */
	xtRowID					tab_head_row_free_id;
	xtRowID					tab_head_row_eof_id;
	xtWord4					tab_head_row_fnum;
	xtRecordID				tab_head_rec_free_id;
	xtRecordID				tab_head_rec_eof_id;
	xtWord4					tab_head_rec_fnum;

	xtOpSeqNo				tab_co_op_seq;							/* The operation last applied by the compactor. */

	xtBool					tab_wr_wake_freeer;						/* Set to TRUE if the writer must wake the freeer. */
	xtOpSeqNo				tab_wake_freeer_op;						/* Set to the sequence number the freeer is waiting for. */

	XTFilePtr				tab_row_file;
	xtRowID					tab_row_eof_id;							/* Indicates the EOF of the table row file. */
	xtRowID					tab_row_free_id;						/* The start of the free list in the table row file. */
	xtWord4					tab_row_fnum;							/* The count of the number of free rows on the free list. */
	xt_mutex_type			tab_row_lock;							/* Lock for updating the EOF and free list. */
	XT_TAB_ROW_LOCK_TYPE	tab_row_rwlock[XT_ROW_RWLOCKS];			/* Used to lock a row during update. */

	xt_mutex_type			tab_rec_flush_lock;						/* Required while the record/row files are being flushed. */
	XTFilePtr				tab_rec_file;
	xtRecordID				tab_rec_eof_id;							/* This value can only grow. */
	xtRecordID				tab_rec_free_id;
	xtWord4					tab_rec_fnum;							/* The count of the number of free rows on the free list. */
	xt_mutex_type			tab_rec_lock;							/* Lock for the free list. */

	xt_mutex_type			tab_ind_flush_lock;						/* Required while the index file is being flushed. */
	xtLogID					tab_ind_rec_log_id;						/* The point before which index entries have been written. */
	xtLogOffset				tab_ind_rec_log_offset;					/* The log offset of the write point. */
	XTFilePtr				tab_ind_file;
	xtIndexNodeID			tab_ind_eof;							/* This value can only grow. */
	xtIndexNodeID			tab_ind_free;							/* The start of the free page list of the index. */
	XTIndFreeListPtr		tab_ind_free_list;						/* A cache of the free list (if exists, don't go to disk!) */
	xt_mutex_type			tab_ind_lock;							/* Lock for reading and writing the index free list. */
	xtWord2					tab_ind_flush_seq;
} XTTableHRec, *XTTableHPtr;		/* Heap pointer */

/* Used for an in-memory list of the tables, ordered by ID. */
typedef struct XTTableEntry {
	xtTableID				te_tab_id;
	char					*te_tab_name;
	struct XTTablePath		*te_tab_path;
	XTTableHPtr				te_table;
} XTTableEntryRec, *XTTableEntryPtr;

typedef struct XTOpenTable {
	struct XTThread			*ot_thread;								/* The thread currently using this open table. */
	XTTableHPtr				ot_table;								/* PBXT table information. */

	struct XTOpenTable		*ot_otp_next_free;						/* Next free open table in the open table pool. */
	struct XTOpenTable		*ot_otp_mr_used;
	struct XTOpenTable		*ot_otp_lr_used;
	time_t					ot_otp_free_time;						/* The time this table was place on the free list. */

	//struct XTOpenTable	*ot_pool_next;							/* Next pointer for open table pool. */

	XT_ROW_REC_FILE_PTR		ot_rec_file;
	XT_ROW_REC_FILE_PTR		ot_row_file;
	XTOpenFilePtr			ot_ind_file;
	u_int					ot_err_index_no;						/* The number of the index on which the last error occurred */

	xtBool					ot_rec_fixed;							/* Cached from table for quick access. */
	size_t					ot_rec_size;							/* Cached from table for quick access. */
	
	char					ot_error_key[XT_IDENTIFIER_NAME_SIZE];
	xtBool					ot_for_update;							/* True if reading FOR UPDATE. */
	xtBool					ot_is_modify;							/* True if UPDATE or DELETE. */
	xtRowID					ot_temp_row_lock;						/* The temporary row lock set on this table. */
	u_int					ot_cols_req;							/* The number of columns required from the table. */

	/* GOTCHA: Separate buffers for reading and writing rows because
	 * of blob references, to this buffer, as in this test:
	 *
	 * drop table if exists t1;
	 * CREATE TABLE t1 (id MEDIUMINT NOT NULL, b1 BIT(8), vc TEXT, 
	 *                  bc CHAR(255), d DECIMAL(10,4) DEFAULT 0, 
	 *                  f FLOAT DEFAULT 0, total BIGINT UNSIGNED, 
	 *                  y YEAR, t DATE)
	 *                  PARTITION BY RANGE (YEAR(t)) 
	 *                 (PARTITION p1 VALUES LESS THAN (2005), 
	 *                  PARTITION p2 VALUES LESS THAN MAXVALUE);
	 *                
	 * INSERT INTO t1 VALUES(412,1,'eTesting MySQL databases is a cool ',
	 *                       'EEEMust make it bug free for the customer',
	 *                        654321.4321,15.21,0,1965,"2005-11-14");
	 * 
	 * UPDATE t1 SET b1 = 0, t="2006-02-22" WHERE id = 412;
	 * 
	 */
	size_t					ot_row_rbuf_size;						/* The current size of the read row buffer (resized dynamically). */
	xtWord1					*ot_row_rbuffer;						/* The row buffer for reading rows. */
	size_t					ot_row_wbuf_size;						/* The current size of the write row buffer (resized dynamically). */
	xtWord1					*ot_row_wbuffer;						/* The row buffer for writing rows. */

	/* Details of the current record: */
	xtRecordID				ot_curr_rec_id;							/* The offset of the current record. */
	xtRowID					ot_curr_row_id;							/* The row ID of the current record. */
	xtBool					ot_curr_updated;						/* TRUE if the current record was updated by the current transaction. */

	XTIndBlockPtr			ot_ind_res_bufs;						/* A list of reserved index buffers. */
	u_int					ot_ind_res_count;						/* The number of reserved buffers. */
#ifdef XT_TRACK_INDEX_UPDATES
	u_int					ot_ind_changed;
	u_int					ot_ind_reserved;
	u_int					ot_ind_reads;
#endif
#ifdef XT_TRACK_RETURNED_ROWS
	u_int					ot_rows_ret_max;
	u_int					ot_rows_ret_curr;
	xtRecordID				*ot_rows_returned;
#endif
	/* GOTCHA: Separate buffers for reading and writing the index are required
	 * because MySQL sometimes scans and updates an index with the same
	 * table handler.
	 */
	XTIdxItemRec			ot_ind_state;							/* Decribes the state of the index buffer. */
	XTIndHandlePtr			ot_ind_rhandle;							/* This handle references a block which is being used in a sequential scan. */
	//XTIdxBranchDRec			ot_ind_rbuf;							/* The index read buffer. */
	XTIdxBranchDRec			ot_ind_wbuf;							/* Buffer for the current index node for writing. */
	xtWord1					ot_ind_wbuf2[XT_INDEX_PAGE_SIZE];		/* Overflow for the write buffer when a node is too big. */

	/* Note: the fields below ot_ind_rbuf are not zero'ed out on creation
	 * of this structure!
	 */
	xtRecordID				ot_seq_rec_id;							/* Current position of a sequential scan. */
	xtRecordID				ot_seq_eof_id;							/* The EOF at the start of the sequential scan. */
	XTTabCachePagePtr		ot_seq_page;							/* If ot_seq_buffer is non-NULL, then a page has been locked! */
	xtBool					ot_on_page;
	size_t					ot_seq_offset;							/* Offset on the current page. */
} XTOpenTableRec, *XTOpenTablePtr;

#define XT_DATABASE_NAME_SIZE		XT_IDENTIFIER_NAME_SIZE

typedef struct XTTableDesc {
	char					td_tab_name[XT_TABLE_NAME_SIZE+4];	// 4 extra for DEL# (tables being deleted)
	xtTableID				td_tab_id;
	char					*td_file_name;

	struct XTDatabase		*td_db;
	struct XTTablePath		*td_tab_path;						// The path of the table.
	u_int					td_path_idx;
	XTOpenDirPtr			td_open_dir;
} XTTableDescRec, *XTTableDescPtr;


typedef struct XTFilesOfTable {
	int						ft_state;
	XTPathStrPtr			ft_tab_name;
	xtTableID				ft_tab_id;
	char					ft_file_path[PATH_MAX];
} XTFilesOfTableRec, *XTFilesOfTablePtr;

typedef struct XTRestrictItem {
	xtTableID				ri_tab_id;
	xtRecordID				ri_rec_id;
} XTRestrictItemRec, *XTRestrictItemPtr;

int					xt_tab_compare_names(const char *n1, const char *n2);
int					xt_tab_compare_paths(char *n1, char *n2);
void				xt_tab_init_db(struct XTThread *self, struct XTDatabase *db);
void				xt_tab_exit_db(struct XTThread *self, struct XTDatabase *db);
void				xt_check_tables(struct XTThread *self);

char				*xt_tab_file_to_name(size_t size, char *tab_name, char *file_name);

void				xt_create_table(struct XTThread *self, XTPathStrPtr name, XTDictionaryPtr dic);
XTTableHPtr			xt_use_table(struct XTThread *self, XTPathStrPtr name, xtBool no_load, xtBool missing_ok, xtBool *opened);
void				xt_sync_flush_table(struct XTThread *self, XTOpenTablePtr ot);
xtBool				xt_flush_record_row(XTOpenTablePtr ot, off_t *bytes_flushed, xtBool have_table_loc);
void				xt_flush_table(struct XTThread *self, XTOpenTablePtr ot);
XTTableHPtr			xt_use_table_no_lock(XTThreadPtr self, struct XTDatabase *db, XTPathStrPtr name, xtBool no_load, xtBool missing_ok, XTDictionaryPtr dic, xtBool *opened);
int					xt_use_table_by_id(struct XTThread *self, XTTableHPtr *tab, struct XTDatabase *db, xtTableID tab_id);
XTOpenTablePtr		xt_open_table(XTTableHPtr tab);
void				xt_close_table(XTOpenTablePtr ot, xtBool flush, xtBool have_table_lock);
void				xt_drop_table(struct XTThread *self, XTPathStrPtr name);
void				xt_check_table(XTThreadPtr self, XTOpenTablePtr tab);
void				xt_rename_table(struct XTThread *self, XTPathStrPtr old_name, XTPathStrPtr new_name);

void				xt_describe_tables_init(struct XTThread *self, struct XTDatabase *db, XTTableDescPtr td);
xtBool				xt_describe_tables_next(struct XTThread *self, XTTableDescPtr td);
void				xt_describe_tables_exit(struct XTThread *self, XTTableDescPtr td);

xtBool				xt_table_exists(struct XTDatabase *db);

void				xt_enum_tables_init(u_int *edx);
XTTableEntryPtr		xt_enum_tables_next(struct XTThread *self, struct XTDatabase *db, u_int *edx);

void				xt_enum_files_of_tables_init(struct XTDatabase *db, char *tab_name, xtTableID tab_id, XTFilesOfTablePtr ft);
xtBool				xt_enum_files_of_tables_next(XTFilesOfTablePtr ft);

xtBool				xt_tab_seq_init(XTOpenTablePtr ot);
void				xt_tab_seq_reset(XTOpenTablePtr ot);
void				xt_tab_seq_exit(XTOpenTablePtr ot);
xtBool				xt_tab_seq_next(XTOpenTablePtr ot, xtWord1 *buffer, xtBool *eof);

xtBool				xt_tab_new_record(XTOpenTablePtr ot, xtWord1 *buffer);
xtBool				xt_tab_delete_record(XTOpenTablePtr ot, xtWord1 *buffer);
xtBool				xt_tab_restrict_rows(XTBasicListPtr list, struct XTThread *thread);
xtBool				xt_tab_update_record(XTOpenTablePtr ot, xtWord1 *before_buf, xtWord1 *after_buf);
int					xt_tab_visible(XTOpenTablePtr ot);
int					xt_tab_read_record(register XTOpenTablePtr ot, xtWord1 *buffer);
int					xt_tab_dirty_read_record(register XTOpenTablePtr ot, xtWord1 *buffer);
void				xt_tab_load_row_pointers(XTThreadPtr self, XTOpenTablePtr ot);
void				xt_tab_load_table(struct XTThread *self, XTOpenTablePtr ot);
xtBool				xt_tab_load_record(register XTOpenTablePtr ot, xtRecordID rec_id, XTInfoBufferPtr rec_buf);
int					xt_tab_remove_record(XTOpenTablePtr ot, xtRecordID rec_id, xtWord1 *rec_data, xtRecordID *prev_var_rec_id, xtBool clean_delete, xtRowID row_id, xtXactID xn_id);
int					xt_tab_maybe_committed(XTOpenTablePtr ot, xtRecordID rec_id, xtXactID *xn_id, xtRowID *out_rowid, xtBool *out_updated);
xtBool				xt_tab_free_record(XTOpenTablePtr ot, u_int status, xtRecordID rec_id, xtBool clean_delete);
void				xt_tab_store_header(XTOpenTablePtr ot, XTTableHeadDPtr rec_head);
xtBool				xt_tab_write_header(XTOpenTablePtr ot, XTTableHeadDPtr rec_head, struct XTThread *thread);
xtBool				xt_tab_write_min_auto_inc(XTOpenTablePtr ot);

xtBool				xt_tab_get_row(register XTOpenTablePtr ot, xtRowID row_id, xtRecordID *var_rec_id);
xtBool				xt_tab_set_row(XTOpenTablePtr ot, u_int status, xtRowID row_id, xtRecordID var_rec_id);
xtBool				xt_tab_free_row(XTOpenTablePtr ot, XTTableHPtr tab, xtRowID row_id);

xtBool				xt_tab_load_ext_data(XTOpenTablePtr ot, xtRecordID load_rec_id, xtWord1 *buffer, u_int cols_req);
xtBool				xt_tab_put_rec_data(XTOpenTablePtr ot, xtRecordID rec_id, size_t size, xtWord1 *buffer, xtOpSeqNo *op_seq);
xtBool				xt_tab_put_eof_rec_data(XTOpenTablePtr ot, xtRecordID rec_id, size_t size, xtWord1 *buffer, xtOpSeqNo *op_seq);
xtBool				xt_tab_put_log_op_rec_data(XTOpenTablePtr ot, u_int status, xtRecordID free_rec_id, xtRecordID rec_id, size_t size, xtWord1 *buffer);
xtBool				xt_tab_put_log_rec_data(XTOpenTablePtr ot, u_int status, xtRecordID free_rec_id, xtRecordID rec_id, size_t size, xtWord1 *buffer, xtOpSeqNo *op_seq);
xtBool				xt_tab_get_rec_data(register XTOpenTablePtr ot, xtRecordID rec_id, size_t size, xtWord1 *buffer);
void				xt_tab_set_index_error(XTTableHPtr tab);

inline off_t		xt_row_id_to_row_offset(register XTTableHPtr tab, xtRowID row_id)
{
	return (off_t) tab->tab_rows.tci_header_size + (off_t) (row_id - 1) * (off_t) tab->tab_rows.tci_rec_size;
}

inline  xtRowID		xt_row_offset_row_id(register XTTableHPtr tab, off_t rec_offs)
{
#ifdef DEBUG
	if (((rec_offs - (off_t) tab->tab_rows.tci_header_size) % (off_t) tab->tab_rows.tci_rec_size) != 0) {
		printf("ERROR! Not a valid record offset!\n");
	}
#endif
	return (xtRowID) ((rec_offs - (off_t) tab->tab_rows.tci_header_size) / (off_t) tab->tab_rows.tci_rec_size) + 1;
}

inline off_t		xt_rec_id_to_rec_offset(register XTTableHPtr tab, xtRefID ref_id)
{
	if (!ref_id)
		return (off_t) 0;
	return (off_t) tab->tab_recs.tci_header_size + (off_t) (ref_id-1) * (off_t) tab->tab_recs.tci_rec_size;
}

inline  xtRefID		xt_rec_offset_rec_id(register XTTableHPtr tab, off_t ref_offs)
{
	if (!ref_offs)
		return (xtRefID) 0;
#ifdef DEBUG
	if (((ref_offs - (off_t) tab->tab_recs.tci_header_size) % (off_t) tab->tab_recs.tci_rec_size) != 0) {
		printf("ERROR! Not a valid record offset!\n");
	}
#endif
		
	return (xtRefID) ((ref_offs - (off_t) tab->tab_recs.tci_header_size) / (off_t) tab->tab_recs.tci_rec_size)+1;
}

inline off_t		xt_ind_node_to_offset(register XTTableHPtr tab, xtIndexNodeID node_id)
{
	if (!XT_NODE_ID(node_id))
		return (off_t) 0;
	return (off_t) tab->tab_index_header_size + (off_t) (XT_NODE_ID(node_id)-1) * (off_t) tab->tab_index_page_size;
}

inline xtIndexNodeID xt_ind_offset_to_node(register XTTableHPtr tab, off_t ind_offs)
{
	XT_NODE_TEMP;

	if (!ind_offs)
		return XT_RET_NODE_ID(0);
#ifdef DEBUG
	if (((ind_offs - (off_t) tab->tab_index_header_size) % (off_t) tab->tab_index_page_size) != 0) {
		printf("ERROR! Not a valid index offset!\n");
	}
#endif
		
	return XT_RET_NODE_ID(((ind_offs - (off_t) tab->tab_index_header_size) / (off_t) tab->tab_index_page_size)+1);
}

#define XT_RESIZE_ROW_BUFFER(thr, rb, size) \
	do { \
		if (rb->rb_size < size) { \
			xt_realloc(thr, (void **) &rb->x.rb_buffer, size); \
			rb->rb_size = size; \
		} \
	} \
	while (0)

#endif