@ -42,6 +42,560 @@ Created 6/2/1994 Heikki Tuuri
# include "ibuf0ibuf.h"
# include "trx0trx.h"
# ifdef UNIV_BLOB_DEBUG
# include "srv0srv.h"
# include "ut0rbt.h"
/** TRUE when messages about index->blobs modification are enabled. */
static ibool btr_blob_dbg_msg ;
/** Issue a message about an operation on index->blobs.
@ param op operation
@ param b the entry being subjected to the operation
@ param ctx the context of the operation */
# define btr_blob_dbg_msg_issue(op, b, ctx) \
fprintf ( stderr , op " %u:%u:%u->%u %s(%u,%u,%u) \n " , \
( b ) - > ref_page_no , ( b ) - > ref_heap_no , \
( b ) - > ref_field_no , ( b ) - > blob_page_no , ctx , \
( b ) - > owner , ( b ) - > always_owner , ( b ) - > del )
/** Insert to index->blobs a reference to an off-page column.
@ param index the index tree
@ param b the reference
@ param ctx context ( for logging ) */
UNIV_INTERN
void
btr_blob_dbg_rbt_insert (
/*====================*/
dict_index_t * index , /*!< in/out: index tree */
const btr_blob_dbg_t * b , /*!< in: the reference */
const char * ctx ) /*!< in: context (for logging) */
{
if ( btr_blob_dbg_msg ) {
btr_blob_dbg_msg_issue ( " insert " , b , ctx ) ;
}
mutex_enter ( & index - > blobs_mutex ) ;
rbt_insert ( index - > blobs , b , b ) ;
mutex_exit ( & index - > blobs_mutex ) ;
}
/** Remove from index->blobs a reference to an off-page column.
@ param index the index tree
@ param b the reference
@ param ctx context ( for logging ) */
UNIV_INTERN
void
btr_blob_dbg_rbt_delete (
/*====================*/
dict_index_t * index , /*!< in/out: index tree */
const btr_blob_dbg_t * b , /*!< in: the reference */
const char * ctx ) /*!< in: context (for logging) */
{
if ( btr_blob_dbg_msg ) {
btr_blob_dbg_msg_issue ( " delete " , b , ctx ) ;
}
mutex_enter ( & index - > blobs_mutex ) ;
ut_a ( rbt_delete ( index - > blobs , b ) ) ;
mutex_exit ( & index - > blobs_mutex ) ;
}
/**************************************************************/ /**
Comparator for items ( btr_blob_dbg_t ) in index - > blobs .
The key in index - > blobs is ( ref_page_no , ref_heap_no , ref_field_no ) .
@ return negative , 0 or positive if * a < * b , * a = * b , * a > * b */
static
int
btr_blob_dbg_cmp (
/*=============*/
const void * a , /*!< in: first btr_blob_dbg_t to compare */
const void * b ) /*!< in: second btr_blob_dbg_t to compare */
{
const btr_blob_dbg_t * aa = a ;
const btr_blob_dbg_t * bb = b ;
ut_ad ( aa ! = NULL ) ;
ut_ad ( bb ! = NULL ) ;
if ( aa - > ref_page_no ! = bb - > ref_page_no ) {
return ( aa - > ref_page_no < bb - > ref_page_no ? - 1 : 1 ) ;
}
if ( aa - > ref_heap_no ! = bb - > ref_heap_no ) {
return ( aa - > ref_heap_no < bb - > ref_heap_no ? - 1 : 1 ) ;
}
if ( aa - > ref_field_no ! = bb - > ref_field_no ) {
return ( aa - > ref_field_no < bb - > ref_field_no ? - 1 : 1 ) ;
}
return ( 0 ) ;
}
/**************************************************************/ /**
Add a reference to an off - page column to the index - > blobs map . */
UNIV_INTERN
void
btr_blob_dbg_add_blob (
/*==================*/
const rec_t * rec , /*!< in: clustered index record */
ulint field_no , /*!< in: off-page column number */
ulint page_no , /*!< in: start page of the column */
dict_index_t * index , /*!< in/out: index tree */
const char * ctx ) /*!< in: context (for logging) */
{
btr_blob_dbg_t b ;
const page_t * page = page_align ( rec ) ;
ut_a ( index - > blobs ) ;
b . blob_page_no = page_no ;
b . ref_page_no = page_get_page_no ( page ) ;
b . ref_heap_no = page_rec_get_heap_no ( rec ) ;
b . ref_field_no = field_no ;
ut_a ( b . ref_field_no > = index - > n_uniq ) ;
b . always_owner = b . owner = TRUE ;
b . del = FALSE ;
ut_a ( ! rec_get_deleted_flag ( rec , page_is_comp ( page ) ) ) ;
btr_blob_dbg_rbt_insert ( index , & b , ctx ) ;
}
/**************************************************************/ /**
Add to index - > blobs any references to off - page columns from a record .
@ return number of references added */
UNIV_INTERN
ulint
btr_blob_dbg_add_rec (
/*=================*/
const rec_t * rec , /*!< in: record */
dict_index_t * index , /*!< in/out: index */
const ulint * offsets , /*!< in: offsets */
const char * ctx ) /*!< in: context (for logging) */
{
ulint count = 0 ;
ulint i ;
btr_blob_dbg_t b ;
ibool del ;
ut_ad ( rec_offs_validate ( rec , index , offsets ) ) ;
if ( ! rec_offs_any_extern ( offsets ) ) {
return ( 0 ) ;
}
b . ref_page_no = page_get_page_no ( page_align ( rec ) ) ;
b . ref_heap_no = page_rec_get_heap_no ( rec ) ;
del = ( rec_get_deleted_flag ( rec , rec_offs_comp ( offsets ) ) ! = 0 ) ;
for ( i = 0 ; i < rec_offs_n_fields ( offsets ) ; i + + ) {
if ( rec_offs_nth_extern ( offsets , i ) ) {
ulint len ;
const byte * field_ref = rec_get_nth_field (
rec , offsets , i , & len ) ;
ut_a ( len ! = UNIV_SQL_NULL ) ;
ut_a ( len > = BTR_EXTERN_FIELD_REF_SIZE ) ;
field_ref + = len - BTR_EXTERN_FIELD_REF_SIZE ;
if ( ! memcmp ( field_ref , field_ref_zero ,
BTR_EXTERN_FIELD_REF_SIZE ) ) {
/* the column has not been stored yet */
continue ;
}
b . ref_field_no = i ;
b . blob_page_no = mach_read_from_4 (
field_ref + BTR_EXTERN_PAGE_NO ) ;
ut_a ( b . ref_field_no > = index - > n_uniq ) ;
b . always_owner = b . owner
= ! ( field_ref [ BTR_EXTERN_LEN ]
& BTR_EXTERN_OWNER_FLAG ) ;
b . del = del ;
btr_blob_dbg_rbt_insert ( index , & b , ctx ) ;
count + + ;
}
}
return ( count ) ;
}
/**************************************************************/ /**
Display the references to off - page columns .
This function is to be called from a debugger ,
for example when a breakpoint on ut_dbg_assertion_failed is hit . */
UNIV_INTERN
void
btr_blob_dbg_print (
/*===============*/
const dict_index_t * index ) /*!< in: index tree */
{
const ib_rbt_node_t * node ;
if ( ! index - > blobs ) {
return ;
}
/* We intentionally do not acquire index->blobs_mutex here.
This function is to be called from a debugger , and the caller
should make sure that the index - > blobs_mutex is held . */
for ( node = rbt_first ( index - > blobs ) ;
node ! = NULL ; node = rbt_next ( index - > blobs , node ) ) {
const btr_blob_dbg_t * b
= rbt_value ( btr_blob_dbg_t , node ) ;
fprintf ( stderr , " %u:%u:%u->%u%s%s%s \n " ,
b - > ref_page_no , b - > ref_heap_no , b - > ref_field_no ,
b - > blob_page_no ,
b - > owner ? " " : " (disowned) " ,
b - > always_owner ? " " : " (has disowned) " ,
b - > del ? " (deleted) " : " " ) ;
}
}
/**************************************************************/ /**
Remove from index - > blobs any references to off - page columns from a record .
@ return number of references removed */
UNIV_INTERN
ulint
btr_blob_dbg_remove_rec (
/*====================*/
const rec_t * rec , /*!< in: record */
dict_index_t * index , /*!< in/out: index */
const ulint * offsets , /*!< in: offsets */
const char * ctx ) /*!< in: context (for logging) */
{
ulint i ;
ulint count = 0 ;
btr_blob_dbg_t b ;
ut_ad ( rec_offs_validate ( rec , index , offsets ) ) ;
if ( ! rec_offs_any_extern ( offsets ) ) {
return ( 0 ) ;
}
b . ref_page_no = page_get_page_no ( page_align ( rec ) ) ;
b . ref_heap_no = page_rec_get_heap_no ( rec ) ;
for ( i = 0 ; i < rec_offs_n_fields ( offsets ) ; i + + ) {
if ( rec_offs_nth_extern ( offsets , i ) ) {
ulint len ;
const byte * field_ref = rec_get_nth_field (
rec , offsets , i , & len ) ;
ut_a ( len ! = UNIV_SQL_NULL ) ;
ut_a ( len > = BTR_EXTERN_FIELD_REF_SIZE ) ;
field_ref + = len - BTR_EXTERN_FIELD_REF_SIZE ;
b . ref_field_no = i ;
b . blob_page_no = mach_read_from_4 (
field_ref + BTR_EXTERN_PAGE_NO ) ;
switch ( b . blob_page_no ) {
case 0 :
/* The column has not been stored yet.
The BLOB pointer must be all zero .
There cannot be a BLOB starting at
page 0 , because page 0 is reserved for
the tablespace header . */
ut_a ( ! memcmp ( field_ref , field_ref_zero ,
BTR_EXTERN_FIELD_REF_SIZE ) ) ;
/* fall through */
case FIL_NULL :
/* the column has been freed already */
continue ;
}
btr_blob_dbg_rbt_delete ( index , & b , ctx ) ;
count + + ;
}
}
return ( count ) ;
}
/**************************************************************/ /**
Check that there are no references to off - page columns from or to
the given page . Invoked when freeing or clearing a page .
@ return TRUE when no orphan references exist */
UNIV_INTERN
ibool
btr_blob_dbg_is_empty (
/*==================*/
dict_index_t * index , /*!< in: index */
ulint page_no ) /*!< in: page number */
{
const ib_rbt_node_t * node ;
ibool success = TRUE ;
if ( ! index - > blobs ) {
return ( success ) ;
}
mutex_enter ( & index - > blobs_mutex ) ;
for ( node = rbt_first ( index - > blobs ) ;
node ! = NULL ; node = rbt_next ( index - > blobs , node ) ) {
const btr_blob_dbg_t * b
= rbt_value ( btr_blob_dbg_t , node ) ;
if ( b - > ref_page_no ! = page_no & & b - > blob_page_no ! = page_no ) {
continue ;
}
fprintf ( stderr ,
" InnoDB: orphan BLOB ref%s%s%s %u:%u:%u->%u \n " ,
b - > owner ? " " : " (disowned) " ,
b - > always_owner ? " " : " (has disowned) " ,
b - > del ? " (deleted) " : " " ,
b - > ref_page_no , b - > ref_heap_no , b - > ref_field_no ,
b - > blob_page_no ) ;
if ( b - > blob_page_no ! = page_no | | b - > owner | | ! b - > del ) {
success = FALSE ;
}
}
mutex_exit ( & index - > blobs_mutex ) ;
return ( success ) ;
}
/**************************************************************/ /**
Count and process all references to off - page columns on a page .
@ return number of references processed */
UNIV_INTERN
ulint
btr_blob_dbg_op (
/*============*/
const page_t * page , /*!< in: B-tree leaf page */
const rec_t * rec , /*!< in: record to start from
( NULL to process the whole page ) */
dict_index_t * index , /*!< in/out: index */
const char * ctx , /*!< in: context (for logging) */
const btr_blob_dbg_op_f op ) /*!< in: operation on records */
{
ulint count = 0 ;
mem_heap_t * heap = NULL ;
ulint offsets_ [ REC_OFFS_NORMAL_SIZE ] ;
ulint * offsets = offsets_ ;
rec_offs_init ( offsets_ ) ;
ut_a ( fil_page_get_type ( page ) = = FIL_PAGE_INDEX ) ;
ut_a ( ! rec | | page_align ( rec ) = = page ) ;
if ( ! index - > blobs | | ! page_is_leaf ( page )
| | ! dict_index_is_clust ( index ) ) {
return ( 0 ) ;
}
if ( rec = = NULL ) {
rec = page_get_infimum_rec ( page ) ;
}
do {
offsets = rec_get_offsets ( rec , index , offsets ,
ULINT_UNDEFINED , & heap ) ;
count + = op ( rec , index , offsets , ctx ) ;
rec = page_rec_get_next_const ( rec ) ;
} while ( ! page_rec_is_supremum ( rec ) ) ;
if ( UNIV_LIKELY_NULL ( heap ) ) {
mem_heap_free ( heap ) ;
}
return ( count ) ;
}
/**************************************************************/ /**
Count and add to index - > blobs any references to off - page columns
from records on a page .
@ return number of references added */
UNIV_INTERN
ulint
btr_blob_dbg_add (
/*=============*/
const page_t * page , /*!< in: rewritten page */
dict_index_t * index , /*!< in/out: index */
const char * ctx ) /*!< in: context (for logging) */
{
btr_blob_dbg_assert_empty ( index , page_get_page_no ( page ) ) ;
return ( btr_blob_dbg_op ( page , NULL , index , ctx , btr_blob_dbg_add_rec ) ) ;
}
/**************************************************************/ /**
Count and remove from index - > blobs any references to off - page columns
from records on a page .
Used when reorganizing a page , before copying the records .
@ return number of references removed */
UNIV_INTERN
ulint
btr_blob_dbg_remove (
/*================*/
const page_t * page , /*!< in: b-tree page */
dict_index_t * index , /*!< in/out: index */
const char * ctx ) /*!< in: context (for logging) */
{
ulint count ;
count = btr_blob_dbg_op ( page , NULL , index , ctx ,
btr_blob_dbg_remove_rec ) ;
/* Check that no references exist. */
btr_blob_dbg_assert_empty ( index , page_get_page_no ( page ) ) ;
return ( count ) ;
}
/**************************************************************/ /**
Restore in index - > blobs any references to off - page columns
Used when page reorganize fails due to compressed page overflow . */
UNIV_INTERN
void
btr_blob_dbg_restore (
/*=================*/
const page_t * npage , /*!< in: page that failed to compress */
const page_t * page , /*!< in: copy of original page */
dict_index_t * index , /*!< in/out: index */
const char * ctx ) /*!< in: context (for logging) */
{
ulint removed ;
ulint added ;
ut_a ( page_get_page_no ( npage ) = = page_get_page_no ( page ) ) ;
ut_a ( page_get_space_id ( npage ) = = page_get_space_id ( page ) ) ;
removed = btr_blob_dbg_remove ( npage , index , ctx ) ;
added = btr_blob_dbg_add ( page , index , ctx ) ;
ut_a ( added = = removed ) ;
}
/**************************************************************/ /**
Modify the ' deleted ' flag of a record . */
UNIV_INTERN
void
btr_blob_dbg_set_deleted_flag (
/*==========================*/
const rec_t * rec , /*!< in: record */
dict_index_t * index , /*!< in/out: index */
const ulint * offsets , /*!< in: rec_get_offs(rec, index) */
ibool del ) /*!< in: TRUE=deleted, FALSE=exists */
{
const ib_rbt_node_t * node ;
btr_blob_dbg_t b ;
btr_blob_dbg_t * c ;
ulint i ;
ut_ad ( rec_offs_validate ( rec , index , offsets ) ) ;
ut_a ( dict_index_is_clust ( index ) ) ;
ut_a ( del = = ! ! del ) ; /* must be FALSE==0 or TRUE==1 */
if ( ! rec_offs_any_extern ( offsets ) | | ! index - > blobs ) {
return ;
}
b . ref_page_no = page_get_page_no ( page_align ( rec ) ) ;
b . ref_heap_no = page_rec_get_heap_no ( rec ) ;
for ( i = 0 ; i < rec_offs_n_fields ( offsets ) ; i + + ) {
if ( rec_offs_nth_extern ( offsets , i ) ) {
ulint len ;
const byte * field_ref = rec_get_nth_field (
rec , offsets , i , & len ) ;
ut_a ( len ! = UNIV_SQL_NULL ) ;
ut_a ( len > = BTR_EXTERN_FIELD_REF_SIZE ) ;
field_ref + = len - BTR_EXTERN_FIELD_REF_SIZE ;
b . ref_field_no = i ;
b . blob_page_no = mach_read_from_4 (
field_ref + BTR_EXTERN_PAGE_NO ) ;
switch ( b . blob_page_no ) {
case 0 :
ut_a ( memcmp ( field_ref , field_ref_zero ,
BTR_EXTERN_FIELD_REF_SIZE ) ) ;
/* page number 0 is for the
page allocation bitmap */
case FIL_NULL :
/* the column has been freed already */
ut_error ;
}
mutex_enter ( & index - > blobs_mutex ) ;
node = rbt_lookup ( index - > blobs , & b ) ;
ut_a ( node ) ;
c = rbt_value ( btr_blob_dbg_t , node ) ;
/* The flag should be modified. */
c - > del = del ;
if ( btr_blob_dbg_msg ) {
b = * c ;
mutex_exit ( & index - > blobs_mutex ) ;
btr_blob_dbg_msg_issue ( " del_mk " , & b , " " ) ;
} else {
mutex_exit ( & index - > blobs_mutex ) ;
}
}
}
}
/**************************************************************/ /**
Change the ownership of an off - page column . */
UNIV_INTERN
void
btr_blob_dbg_owner (
/*===============*/
const rec_t * rec , /*!< in: record */
dict_index_t * index , /*!< in/out: index */
const ulint * offsets , /*!< in: rec_get_offs(rec, index) */
ulint i , /*!< in: ith field in rec */
ibool own ) /*!< in: TRUE=owned, FALSE=disowned */
{
const ib_rbt_node_t * node ;
btr_blob_dbg_t b ;
const byte * field_ref ;
ulint len ;
ut_ad ( rec_offs_validate ( rec , index , offsets ) ) ;
ut_a ( rec_offs_nth_extern ( offsets , i ) ) ;
field_ref = rec_get_nth_field ( rec , offsets , i , & len ) ;
ut_a ( len ! = UNIV_SQL_NULL ) ;
ut_a ( len > = BTR_EXTERN_FIELD_REF_SIZE ) ;
field_ref + = len - BTR_EXTERN_FIELD_REF_SIZE ;
b . ref_page_no = page_get_page_no ( page_align ( rec ) ) ;
b . ref_heap_no = page_rec_get_heap_no ( rec ) ;
b . ref_field_no = i ;
b . owner = ! ( field_ref [ BTR_EXTERN_LEN ] & BTR_EXTERN_OWNER_FLAG ) ;
b . blob_page_no = mach_read_from_4 ( field_ref + BTR_EXTERN_PAGE_NO ) ;
ut_a ( b . owner = = own ) ;
mutex_enter ( & index - > blobs_mutex ) ;
node = rbt_lookup ( index - > blobs , & b ) ;
/* row_ins_clust_index_entry_by_modify() invokes
btr_cur_unmark_extern_fields ( ) also for the newly inserted
references , which are all zero bytes until the columns are stored .
The node lookup must fail if and only if that is the case . */
ut_a ( ! memcmp ( field_ref , field_ref_zero , BTR_EXTERN_FIELD_REF_SIZE )
= = ! node ) ;
if ( node ) {
btr_blob_dbg_t * c = rbt_value ( btr_blob_dbg_t , node ) ;
/* Some code sets ownership from TRUE to TRUE.
We do not allow changing ownership from FALSE to FALSE . */
ut_a ( own | | c - > owner ) ;
c - > owner = own ;
if ( ! own ) {
c - > always_owner = FALSE ;
}
}
mutex_exit ( & index - > blobs_mutex ) ;
}
# endif /* UNIV_BLOB_DEBUG */
/*
Latching strategy of the InnoDB B - tree
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -296,6 +850,7 @@ btr_page_create(
page_t * page = buf_block_get_frame ( block ) ;
ut_ad ( mtr_memo_contains ( mtr , block , MTR_MEMO_PAGE_X_FIX ) ) ;
btr_blob_dbg_assert_empty ( index , buf_block_get_page_no ( block ) ) ;
if ( UNIV_LIKELY_NULL ( page_zip ) ) {
page_create_zip ( block , index , level , mtr ) ;
@ -489,6 +1044,7 @@ btr_page_free_low(
modify clock */
buf_block_modify_clock_inc ( block ) ;
btr_blob_dbg_assert_empty ( index , buf_block_get_page_no ( block ) ) ;
if ( dict_index_is_ibuf ( index ) ) {
@ -773,6 +1329,13 @@ btr_create(
block = buf_page_get ( space , zip_size , page_no ,
RW_X_LATCH , mtr ) ;
} else {
# ifdef UNIV_BLOB_DEBUG
if ( ( type & DICT_CLUSTERED ) & & ! index - > blobs ) {
mutex_create ( & index - > blobs_mutex , SYNC_ANY_LATCH ) ;
index - > blobs = rbt_create ( sizeof ( btr_blob_dbg_t ) ,
btr_blob_dbg_cmp ) ;
}
# endif /* UNIV_BLOB_DEBUG */
block = fseg_create ( space , 0 ,
PAGE_HEADER + PAGE_BTR_SEG_TOP , mtr ) ;
}
@ -996,6 +1559,7 @@ btr_page_reorganize_low(
block - > check_index_page_at_flush = TRUE ;
# endif /* !UNIV_HOTBACKUP */
btr_blob_dbg_remove ( page , index , " btr_page_reorganize " ) ;
/* Recreate the page: note that global data on page (possible
segment headers , next page - field , etc . ) is preserved intact */
@ -1024,6 +1588,8 @@ btr_page_reorganize_low(
( ! page_zip_compress ( page_zip , page , index , NULL ) ) ) {
/* Restore the old page and exit. */
btr_blob_dbg_restore ( page , temp_page , index ,
" btr_page_reorganize_compress_fail " ) ;
# if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG
/* Check that the bytes that we skip are identical. */
@ -1157,6 +1723,7 @@ btr_page_empty(
# endif /* UNIV_ZIP_DEBUG */
btr_search_drop_page_hash_index ( block ) ;
btr_blob_dbg_remove ( page , index , " btr_page_empty " ) ;
/* Recreate the page: note that global data on page (possible
segment headers , next page - field , etc . ) is preserved intact */
@ -2497,6 +3064,7 @@ btr_lift_page_up(
index ) ;
}
btr_blob_dbg_remove ( page , index , " btr_lift_page_up " ) ;
lock_update_copy_and_discard ( father_block , block ) ;
/* Go upward to root page, decrementing levels by one. */
@ -2758,6 +3326,7 @@ err_exit:
lock_update_merge_right ( merge_block , orig_succ , block ) ;
}
btr_blob_dbg_remove ( page , index , " btr_compress " ) ;
mem_heap_free ( heap ) ;
if ( ! dict_index_is_clust ( index ) & & page_is_leaf ( merge_page ) ) {
@ -2988,6 +3557,8 @@ btr_discard_page(
block ) ;
}
btr_blob_dbg_remove ( page , index , " btr_discard_page " ) ;
/* Free the file page */
btr_page_free ( index , block , mtr ) ;