/*-------------------------------------------------------------------------
 *
 * hash_xlog.h
 *	  header file for Postgres hash AM implementation
 *
 *
 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/access/hash_xlog.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef HASH_XLOG_H
#define HASH_XLOG_H

#include "access/xlogreader.h"
#include "lib/stringinfo.h"
#include "storage/off.h"

/* Number of buffers required for XLOG_HASH_SQUEEZE_PAGE operation */
#define HASH_XLOG_FREE_OVFL_BUFS	6

/*
 * XLOG records for hash operations
 */
#define XLOG_HASH_INIT_META_PAGE	0x00	/* initialize the meta page */
#define XLOG_HASH_INIT_BITMAP_PAGE	0x10	/* initialize the bitmap page */
#define XLOG_HASH_INSERT		0x20	/* add index tuple without split */
#define XLOG_HASH_ADD_OVFL_PAGE 0x30	/* add overflow page */
#define XLOG_HASH_SPLIT_ALLOCATE_PAGE	0x40	/* allocate new page for split */
#define XLOG_HASH_SPLIT_PAGE	0x50	/* split page */
#define XLOG_HASH_SPLIT_COMPLETE	0x60	/* completion of split operation */
#define XLOG_HASH_MOVE_PAGE_CONTENTS	0x70	/* remove tuples from one page
												 * and add to another page */
#define XLOG_HASH_SQUEEZE_PAGE	0x80	/* add tuples to one of the previous
										 * pages in chain and free the ovfl
										 * page */
#define XLOG_HASH_DELETE		0x90	/* delete index tuples from a page */
#define XLOG_HASH_SPLIT_CLEANUP 0xA0	/* clear split-cleanup flag in primary
										 * bucket page after deleting tuples
										 * that are moved due to split	*/
#define XLOG_HASH_UPDATE_META_PAGE	0xB0	/* update meta page after vacuum */

#define XLOG_HASH_VACUUM_ONE_PAGE	0xC0	/* remove dead tuples from index
											 * page */

/*
 * xl_hash_split_allocate_page flag values, 8 bits are available.
 */
#define XLH_SPLIT_META_UPDATE_MASKS		(1<<0)
#define XLH_SPLIT_META_UPDATE_SPLITPOINT		(1<<1)

/*
 * This is what we need to know about a HASH index create.
 *
 * Backup block 0: metapage
 */
typedef struct xl_hash_createidx
{
	double		num_tuples;
	RegProcedure procid;
	uint16		ffactor;
}			xl_hash_createidx;
#define SizeOfHashCreateIdx (offsetof(xl_hash_createidx, ffactor) + sizeof(uint16))

/*
 * This is what we need to know about simple (without split) insert.
 *
 * This data record is used for XLOG_HASH_INSERT
 *
 * Backup Blk 0: original page (data contains the inserted tuple)
 * Backup Blk 1: metapage (HashMetaPageData)
 */
typedef struct xl_hash_insert
{
	OffsetNumber offnum;
} xl_hash_insert;

#define SizeOfHashInsert	(offsetof(xl_hash_insert, offnum) + sizeof(OffsetNumber))

/*
 * This is what we need to know about addition of overflow page.
 *
 * This data record is used for XLOG_HASH_ADD_OVFL_PAGE
 *
 * Backup Blk 0: newly allocated overflow page
 * Backup Blk 1: page before new overflow page in the bucket chain
 * Backup Blk 2: bitmap page
 * Backup Blk 3: new bitmap page
 * Backup Blk 4: metapage
 */
typedef struct xl_hash_add_ovfl_page
{
	uint16		bmsize;
	bool		bmpage_found;
} xl_hash_add_ovfl_page;

#define SizeOfHashAddOvflPage	\
	(offsetof(xl_hash_add_ovfl_page, bmpage_found) + sizeof(bool))

/*
 * This is what we need to know about allocating a page for split.
 *
 * This data record is used for XLOG_HASH_SPLIT_ALLOCATE_PAGE
 *
 * Backup Blk 0: page for old bucket
 * Backup Blk 1: page for new bucket
 * Backup Blk 2: metapage
 */
typedef struct xl_hash_split_allocate_page
{
	uint32		new_bucket;
	uint16		old_bucket_flag;
	uint16		new_bucket_flag;
	uint8		flags;
} xl_hash_split_allocate_page;

#define SizeOfHashSplitAllocPage	\
	(offsetof(xl_hash_split_allocate_page, flags) + sizeof(uint8))

/*
 * This is what we need to know about completing the split operation.
 *
 * This data record is used for XLOG_HASH_SPLIT_COMPLETE
 *
 * Backup Blk 0: page for old bucket
 * Backup Blk 1: page for new bucket
 */
typedef struct xl_hash_split_complete
{
	uint16		old_bucket_flag;
	uint16		new_bucket_flag;
} xl_hash_split_complete;

#define SizeOfHashSplitComplete \
	(offsetof(xl_hash_split_complete, new_bucket_flag) + sizeof(uint16))

/*
 * This is what we need to know about move page contents required during
 * squeeze operation.
 *
 * This data record is used for XLOG_HASH_MOVE_PAGE_CONTENTS
 *
 * Backup Blk 0: bucket page
 * Backup Blk 1: page containing moved tuples
 * Backup Blk 2: page from which tuples will be removed
 */
typedef struct xl_hash_move_page_contents
{
	uint16		ntups;
	bool		is_prim_bucket_same_wrt;	/* TRUE if the page to which
											 * tuples are moved is same as
											 * primary bucket page */
} xl_hash_move_page_contents;

#define SizeOfHashMovePageContents	\
	(offsetof(xl_hash_move_page_contents, is_prim_bucket_same_wrt) + sizeof(bool))

/*
 * This is what we need to know about the squeeze page operation.
 *
 * This data record is used for XLOG_HASH_SQUEEZE_PAGE
 *
 * Backup Blk 0: page containing tuples moved from freed overflow page
 * Backup Blk 1: freed overflow page
 * Backup Blk 2: page previous to the freed overflow page
 * Backup Blk 3: page next to the freed overflow page
 * Backup Blk 4: bitmap page containing info of freed overflow page
 * Backup Blk 5: meta page
 */
typedef struct xl_hash_squeeze_page
{
	BlockNumber prevblkno;
	BlockNumber nextblkno;
	uint16		ntups;
	bool		is_prim_bucket_same_wrt;	/* TRUE if the page to which
											 * tuples are moved is same as
											 * primary bucket page */
	bool		is_prev_bucket_same_wrt;	/* TRUE if the page to which
											 * tuples are moved is the page
											 * previous to the freed overflow
											 * page */
} xl_hash_squeeze_page;

#define SizeOfHashSqueezePage	\
	(offsetof(xl_hash_squeeze_page, is_prev_bucket_same_wrt) + sizeof(bool))

/*
 * This is what we need to know about the deletion of index tuples from a page.
 *
 * This data record is used for XLOG_HASH_DELETE
 *
 * Backup Blk 0: primary bucket page
 * Backup Blk 1: page from which tuples are deleted
 */
typedef struct xl_hash_delete
{
	bool		clear_dead_marking; /* TRUE if this operation clears
									 * LH_PAGE_HAS_DEAD_TUPLES flag */
	bool		is_primary_bucket_page; /* TRUE if the operation is for
										 * primary bucket page */
} xl_hash_delete;

#define SizeOfHashDelete	(offsetof(xl_hash_delete, is_primary_bucket_page) + sizeof(bool))

/*
 * This is what we need for metapage update operation.
 *
 * This data record is used for XLOG_HASH_UPDATE_META_PAGE
 *
 * Backup Blk 0: meta page
 */
typedef struct xl_hash_update_meta_page
{
	double		ntuples;
} xl_hash_update_meta_page;

#define SizeOfHashUpdateMetaPage	\
	(offsetof(xl_hash_update_meta_page, ntuples) + sizeof(double))

/*
 * This is what we need to initialize metapage.
 *
 * This data record is used for XLOG_HASH_INIT_META_PAGE
 *
 * Backup Blk 0: meta page
 */
typedef struct xl_hash_init_meta_page
{
	double		num_tuples;
	RegProcedure procid;
	uint16		ffactor;
} xl_hash_init_meta_page;

#define SizeOfHashInitMetaPage		\
	(offsetof(xl_hash_init_meta_page, ffactor) + sizeof(uint16))

/*
 * This is what we need to initialize bitmap page.
 *
 * This data record is used for XLOG_HASH_INIT_BITMAP_PAGE
 *
 * Backup Blk 0: bitmap page
 * Backup Blk 1: meta page
 */
typedef struct xl_hash_init_bitmap_page
{
	uint16		bmsize;
} xl_hash_init_bitmap_page;

#define SizeOfHashInitBitmapPage	\
	(offsetof(xl_hash_init_bitmap_page, bmsize) + sizeof(uint16))

/*
 * This is what we need for index tuple deletion and to
 * update the meta page.
 *
 * This data record is used for XLOG_HASH_VACUUM_ONE_PAGE
 *
 * Backup Blk 0: bucket page
 * Backup Blk 1: meta page
 */
typedef struct xl_hash_vacuum_one_page
{
	RelFileNode hnode;
	int			ntuples;

	/* TARGET OFFSET NUMBERS FOLLOW AT THE END */
} xl_hash_vacuum_one_page;

#define SizeOfHashVacuumOnePage \
	(offsetof(xl_hash_vacuum_one_page, ntuples) + sizeof(int))

extern void hash_redo(XLogReaderState *record);
extern void hash_desc(StringInfo buf, XLogReaderState *record);
extern const char *hash_identify(uint8 info);
extern void hash_mask(char *pagedata, BlockNumber blkno);

#endif							/* HASH_XLOG_H */
