fis-gtm/sr_port/gds_blk_upgrade.h

131 lines
8.1 KiB
C

/****************************************************************
* *
* Copyright 2005, 2012 Fidelity Information Services, Inc *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
#ifndef GDS_BLK_UPGRADE_INCLUDED
#define GDS_BLK_UPGRADE_INCLUDED
#define UPGRADE_IF_NEEDED 0 /* default */
#define UPGRADE_NEVER 1
#define UPGRADE_ALWAYS 2
int4 gds_blk_upgrade(sm_uc_ptr_t gds_blk_src, sm_uc_ptr_t gds_blk_trg, int4 bsiz, enum db_ver *ondsk_blkver);
GBLREF uint4 gtm_blkupgrade_flag; /* control whether dynamic upgrade is attempted or not */
GBLREF boolean_t dse_running;
/* See if block needs to be converted to current version. Assume buffer is at least short aligned.
* Note: csd->fully_upgraded is not derived within the macro but instead passed in as a parameter to ensure whichever
* function (dsk_read currently) references this does that once and copies the value into a local variable that is used
* in all further usages. This way multiple usages are guaranteed to see the same value. Using csd->fully_upgraded in
* each of those cases could cause different values to be seen (since csd can be concurrently updated).
*/
#define GDS_BLK_UPGRADE_IF_NEEDED(blknum, srcbuffptr, trgbuffptr, curcsd, ondskblkver, upgrdstatus, fully_upgraded) \
{ \
/* In order to detect if a block needs to be upgraded or not, we do the following series of tests. \
* If DSE, the variable "gtm_blkupgrade_flag" controls whether upgrade is attempted or not. \
* If it is UPGRADE_NEVER, we never attempt upgrade. \
* Likewise, if it is UPGRADE_ALWAYS, we unconditionally upgrade. \
* If it is UPGRADE_IF_NEEDED, then the following checks are done. \
* 1) If the file-header has "fully_upgraded" set to TRUE, we know for sure no block needs to be upgraded. \
* This check is performed in this macro itself as it is a quick check and avoids a function call. \
* 2) Else, the block might or might not need an upgrade. To decide, we do some more checks. \
* V5 onwards, the first 2 bytes of the block header is the version indicator. It is 1 in V5. \
* In V4, the first 2 bytes of the block header was the block-size which is guaranteed to be \
* at least SIZEOF(v15_blk_hdr) (the size of the V4 blk_hdr structure) which is 8 bytes in Unix \
* and 7 bytes in VMS. We use the first 2 bytes in the block header as a first level check. \
* If they are >= SIZEOF(v15_blk_hdr), we decide it is a V4 format block and try to upgrade. \
* This check is performed in this macro itself as it is a quick check and avoids a function call. \
* 3) It is quite possible that we might conclude the format incorrectly based on just the above checks. \
* This can be due to any one of the following. \
* => A V4 format block might have a corrupt block-header where the first 2 bytes are exactly 1. \
* => A V5 format block might have a corrupt block-header where the first 2 bytes are not 1. \
* => We might be reading an unused block (marked free/recycled in the bitmap) from disk. \
* This is possible due to a variety of reasons including concurrency issues while \
* traversing the B-tree that cause us to end up in a restartable situation. \
* If we read such a block, then the block contents (including the header) is not valid. \
* In VMS it contains uninitialized data. In Unix it contains 0s. \
* In all the above cases, we have no definitive way of determining exactly which format the block is. \
* For the case where we incorrectly conclude it is V5 format, we dont do much. \
* But for the other case, we do better by checking if the V4 block has enough room to accommodate \
* the extra space needed for the upgrade. \
* If yes, we go ahead with the upgrade. \
* If not, we check if the V4 block size is less than the database block size. \
* If yes, then we believe it is a valid V4 block that is just too big to be upgraded. \
* We therefore issue a DYNUPGRDFAIL error. \
* If no, this is a corrupt block and we decide not to upgrade. \
* Something to consider for the future is to invoke block certification on this block. \
* This check is involved and hence is done in the function "gds_blk_upgrade" (invoked from this macro). \
* This is not a theoretically foolproof solution but for all practical purposes should be good enough. \
* Note that for a database that has been completely upgraded to V5 format, we do not have any inconclusiveness. \
* \
* Note the clearing of srcbuffptr is done as a flag that gds_blk_upgrd was run (used by dsk_read). \
*/ \
if (!dse_running || (UPGRADE_IF_NEEDED == gtm_blkupgrade_flag)) \
{ \
if ((fully_upgraded) || (SIZEOF(v15_blk_hdr) > ((v15_blk_hdr_ptr_t)(srcbuffptr))->bsiz)) \
{ \
upgrdstatus = SS_NORMAL; \
if (NULL != (ondskblkver)) \
*(ondskblkver) = GDSV6; \
} else \
{ \
upgrdstatus = gds_blk_upgrade((sm_uc_ptr_t)(srcbuffptr), (sm_uc_ptr_t)(trgbuffptr), \
(curcsd)->blk_size, (ondskblkver)); \
if (srcbuffptr != trgbuffptr) \
srcbuffptr = NULL; \
} \
} else if (UPGRADE_NEVER == gtm_blkupgrade_flag) \
{ \
upgrdstatus = SS_NORMAL; \
if (NULL != (ondskblkver)) \
*(ondskblkver) = GDSV6; \
} else if (UPGRADE_ALWAYS == gtm_blkupgrade_flag) \
{ \
upgrdstatus = gds_blk_upgrade((sm_uc_ptr_t)(srcbuffptr), (sm_uc_ptr_t)(trgbuffptr), \
(curcsd)->blk_size, (ondskblkver)); \
if (NULL != (ondskblkver)) \
*(ondskblkver) = GDSV4; \
if (srcbuffptr != trgbuffptr) \
srcbuffptr = NULL; \
} \
}
/* This macro is invoked by dsk_read.c when we know for sure we are reading a valid block. This checks that the block
* we read from disk contains a valid block header. It also checks that we CANNOT have read a V4 format reused block
* if the database has been fully upgraded. It uses checks similar to those used in the GDS_BLK_UPGRADE_IF_NEEDED
* macro to determine if it is a V4 or V5 format block header.
* Note: csd->fully_upgraded is not derived within the macro but instead passed in as a parameter to ensure whichever
* function (dsk_read currently) references this does that once and copies the value into a local variable that is used
* in all further usages. This way multiple usages are guaranteed to see the same value. Using csd->fully_upgraded in
* each of those cases could cause different values to be seen (since csd can be concurrently updated).
*/
#define GDS_BLK_HDR_CHECK(csd, v5_blk_hdr, fully_upgraded) \
{ \
v15_blk_hdr_ptr_t v4_blk_hdr; \
\
v4_blk_hdr = (v15_blk_hdr_ptr_t)v5_blk_hdr; \
if (!(fully_upgraded) || (SIZEOF(v15_blk_hdr) > v4_blk_hdr->bsiz)) \
{ /* V5 formatted buffer in shared memory (even though might be V4 format in disk) */ \
assert((unsigned)GDSVLAST > (unsigned)v5_blk_hdr->bver); \
assert((LCL_MAP_LEVL == v5_blk_hdr->levl) || ((unsigned)MAX_BT_DEPTH > (unsigned)v5_blk_hdr->levl)); \
assert((unsigned)size >= (unsigned)v5_blk_hdr->bsiz); \
assert(csd->trans_hist.curr_tn >= v5_blk_hdr->tn); \
} else \
{ /* V4 formatted buffer in shared memory (not converted because fully_upgraded is TRUE). \
* Possible if we are reading a recycled block that is in V4 format from a fully upgraded database. \
* But all recycled blocks are now upgraded by MUPIP REORG UPGRADE so this should be impossible. \
*/ \
assert(FALSE); \
} \
}
#endif