
52ac6cd2d0 added new field to ginxlogDeletePage and was backpatched to 9.4. That led to problems when patched postgres instance applies WAL records generated by non-patched one. WAL records generated by non-patched instance don't contain new field, which patched one is expecting to see. Thankfully, we can distinguish patched and non-patched WAL records by their data size. If we see that WAL record is generated by non-patched instance, we skip processing of new field. This commit comes with some assertions. In particular, if it appears that on some platform struct data size didn't change then static assertion will trigger. Reported-by: Simon Riggs Discussion: https://postgr.es/m/CANP8%2Bj%2BK4whxf7ET7%2BgO%2BG-baC3-WxqqH%3DnV4X2CgfEPA3Yu3g%40mail.gmail.com Author: Alexander Korotkov Reviewed-by: Simon Riggs, Alvaro Herrera Backpatch-through: 9.4
229 lines
6.3 KiB
C
229 lines
6.3 KiB
C
/*--------------------------------------------------------------------------
|
|
* ginxlog.h
|
|
* header file for postgres inverted index xlog implementation.
|
|
*
|
|
* Copyright (c) 2006-2018, PostgreSQL Global Development Group
|
|
*
|
|
* src/include/access/ginxlog.h
|
|
*--------------------------------------------------------------------------
|
|
*/
|
|
#ifndef GINXLOG_H
|
|
#define GINXLOG_H
|
|
|
|
#include "access/ginblock.h"
|
|
#include "access/itup.h"
|
|
#include "access/xlogreader.h"
|
|
#include "lib/stringinfo.h"
|
|
#include "storage/off.h"
|
|
|
|
#define XLOG_GIN_CREATE_INDEX 0x00
|
|
|
|
#define XLOG_GIN_CREATE_PTREE 0x10
|
|
|
|
typedef struct ginxlogCreatePostingTree
|
|
{
|
|
uint32 size;
|
|
/* A compressed posting list follows */
|
|
} ginxlogCreatePostingTree;
|
|
|
|
/*
|
|
* The format of the insertion record varies depending on the page type.
|
|
* ginxlogInsert is the common part between all variants.
|
|
*
|
|
* Backup Blk 0: target page
|
|
* Backup Blk 1: left child, if this insertion finishes an incomplete split
|
|
*/
|
|
|
|
#define XLOG_GIN_INSERT 0x20
|
|
|
|
typedef struct
|
|
{
|
|
uint16 flags; /* GIN_INSERT_ISLEAF and/or GIN_INSERT_ISDATA */
|
|
|
|
/*
|
|
* FOLLOWS:
|
|
*
|
|
* 1. if not leaf page, block numbers of the left and right child pages
|
|
* whose split this insertion finishes, as BlockIdData[2] (beware of
|
|
* adding fields in this struct that would make them not 16-bit aligned)
|
|
*
|
|
* 2. a ginxlogInsertEntry or ginxlogRecompressDataLeaf struct, depending
|
|
* on tree type.
|
|
*
|
|
* NB: the below structs are only 16-bit aligned when appended to a
|
|
* ginxlogInsert struct! Beware of adding fields to them that require
|
|
* stricter alignment.
|
|
*/
|
|
} ginxlogInsert;
|
|
|
|
typedef struct
|
|
{
|
|
OffsetNumber offset;
|
|
bool isDelete;
|
|
IndexTupleData tuple; /* variable length */
|
|
} ginxlogInsertEntry;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
uint16 nactions;
|
|
|
|
/* Variable number of 'actions' follow */
|
|
} ginxlogRecompressDataLeaf;
|
|
|
|
/*
|
|
* Note: this struct is currently not used in code, and only acts as
|
|
* documentation. The WAL record format is as specified here, but the code
|
|
* uses straight access through a Pointer and memcpy to read/write these.
|
|
*/
|
|
typedef struct
|
|
{
|
|
uint8 segno; /* segment this action applies to */
|
|
char type; /* action type (see below) */
|
|
|
|
/*
|
|
* Action-specific data follows. For INSERT and REPLACE actions that is a
|
|
* GinPostingList struct. For ADDITEMS, a uint16 for the number of items
|
|
* added, followed by the items themselves as ItemPointers. DELETE actions
|
|
* have no further data.
|
|
*/
|
|
} ginxlogSegmentAction;
|
|
|
|
/* Action types */
|
|
#define GIN_SEGMENT_UNMODIFIED 0 /* no action (not used in WAL records) */
|
|
#define GIN_SEGMENT_DELETE 1 /* a whole segment is removed */
|
|
#define GIN_SEGMENT_INSERT 2 /* a whole segment is added */
|
|
#define GIN_SEGMENT_REPLACE 3 /* a segment is replaced */
|
|
#define GIN_SEGMENT_ADDITEMS 4 /* items are added to existing segment */
|
|
|
|
typedef struct
|
|
{
|
|
OffsetNumber offset;
|
|
PostingItem newitem;
|
|
} ginxlogInsertDataInternal;
|
|
|
|
/*
|
|
* Backup Blk 0: new left page (= original page, if not root split)
|
|
* Backup Blk 1: new right page
|
|
* Backup Blk 2: original page / new root page, if root split
|
|
* Backup Blk 3: left child, if this insertion completes an earlier split
|
|
*/
|
|
#define XLOG_GIN_SPLIT 0x30
|
|
|
|
typedef struct ginxlogSplit
|
|
{
|
|
RelFileNode node;
|
|
BlockNumber rrlink; /* right link, or root's blocknumber if root
|
|
* split */
|
|
BlockNumber leftChildBlkno; /* valid on a non-leaf split */
|
|
BlockNumber rightChildBlkno;
|
|
uint16 flags; /* see below */
|
|
} ginxlogSplit;
|
|
|
|
/*
|
|
* Flags used in ginxlogInsert and ginxlogSplit records
|
|
*/
|
|
#define GIN_INSERT_ISDATA 0x01 /* for both insert and split records */
|
|
#define GIN_INSERT_ISLEAF 0x02 /* ditto */
|
|
#define GIN_SPLIT_ROOT 0x04 /* only for split records */
|
|
|
|
/*
|
|
* Vacuum simply WAL-logs the whole page, when anything is modified. This
|
|
* is functionally identical to heap_newpage records, but is kept separate for
|
|
* debugging purposes. (When inspecting the WAL stream, it's easier to see
|
|
* what's going on when GIN vacuum records are marked as such, not as heap
|
|
* records.) This is currently only used for entry tree leaf pages.
|
|
*/
|
|
#define XLOG_GIN_VACUUM_PAGE 0x40
|
|
|
|
/*
|
|
* Vacuuming posting tree leaf page is WAL-logged like recompression caused
|
|
* by insertion.
|
|
*/
|
|
#define XLOG_GIN_VACUUM_DATA_LEAF_PAGE 0x90
|
|
|
|
typedef struct ginxlogVacuumDataLeafPage
|
|
{
|
|
ginxlogRecompressDataLeaf data;
|
|
} ginxlogVacuumDataLeafPage;
|
|
|
|
/*
|
|
* Backup Blk 0: deleted page
|
|
* Backup Blk 1: parent
|
|
* Backup Blk 2: left sibling
|
|
*/
|
|
#define XLOG_GIN_DELETE_PAGE 0x50
|
|
|
|
typedef struct ginxlogDeletePage
|
|
{
|
|
OffsetNumber parentOffset;
|
|
BlockNumber rightLink;
|
|
TransactionId deleteXid; /* last Xid which could see this page in scan */
|
|
} ginxlogDeletePage;
|
|
|
|
/*
|
|
* Previous version of ginxlogDeletePage struct, which didn't have deleteXid
|
|
* field. Used for size comparison (see ginRedoDeletePage()).
|
|
*/
|
|
typedef struct ginxlogDeletePageOld
|
|
{
|
|
OffsetNumber parentOffset;
|
|
BlockNumber rightLink;
|
|
} ginxlogDeletePageOld;
|
|
|
|
#define XLOG_GIN_UPDATE_META_PAGE 0x60
|
|
|
|
/*
|
|
* Backup Blk 0: metapage
|
|
* Backup Blk 1: tail page
|
|
*/
|
|
typedef struct ginxlogUpdateMeta
|
|
{
|
|
RelFileNode node;
|
|
GinMetaPageData metadata;
|
|
BlockNumber prevTail;
|
|
BlockNumber newRightlink;
|
|
int32 ntuples; /* if ntuples > 0 then metadata.tail was
|
|
* updated with that many tuples; else new sub
|
|
* list was inserted */
|
|
/* array of inserted tuples follows */
|
|
} ginxlogUpdateMeta;
|
|
|
|
#define XLOG_GIN_INSERT_LISTPAGE 0x70
|
|
|
|
typedef struct ginxlogInsertListPage
|
|
{
|
|
BlockNumber rightlink;
|
|
int32 ntuples;
|
|
/* array of inserted tuples follows */
|
|
} ginxlogInsertListPage;
|
|
|
|
/*
|
|
* Backup Blk 0: metapage
|
|
* Backup Blk 1 to (ndeleted + 1): deleted pages
|
|
*/
|
|
|
|
#define XLOG_GIN_DELETE_LISTPAGE 0x80
|
|
|
|
/*
|
|
* The WAL record for deleting list pages must contain a block reference to
|
|
* all the deleted pages, so the number of pages that can be deleted in one
|
|
* record is limited by XLR_MAX_BLOCK_ID. (block_id 0 is used for the
|
|
* metapage.)
|
|
*/
|
|
#define GIN_NDELETE_AT_ONCE Min(16, XLR_MAX_BLOCK_ID - 1)
|
|
typedef struct ginxlogDeleteListPages
|
|
{
|
|
GinMetaPageData metadata;
|
|
int32 ndeleted;
|
|
} ginxlogDeleteListPages;
|
|
|
|
extern void gin_redo(XLogReaderState *record);
|
|
extern void gin_desc(StringInfo buf, XLogReaderState *record);
|
|
extern const char *gin_identify(uint8 info);
|
|
extern void gin_xlog_startup(void);
|
|
extern void gin_xlog_cleanup(void);
|
|
extern void gin_mask(char *pagedata, BlockNumber blkno);
|
|
|
|
#endif /* GINXLOG_H */
|