mirror of
https://github.com/MariaDB/server.git
synced 2025-01-31 02:51:44 +01:00
d0b9387b88
* Recovery of the table's live checksum (CREATE TABLE ... CHECKSUM=1) is achieved in this patch. The table's live checksum (info->s->state.state.checksum) is updated in inwrite_rec_hook's under the log mutex when writing UNDO_ROW_INSERT|UPDATE|DELETE and REDO_DELETE_ALL. The checksum variation caused by the operation is stored in these UNDOs, so that the REDO phase, when it sees such UNDOs, can update the live checksum if it is older (state.is_of_lsn is lower) than the record. It is also used, as a nice add-on with no cost, to do less row checksum computation during the UNDO phase (as we have it in the record already). Doing this work, it became pressing to move in-write hooks (write_hook_for_redo() et al) to ma_blockrec.c. The 'parts' argument of inwrite_rec_hook is unpredictable (it comes mangled at this stage, for example by LSN compression) so it is replaced by a 'void* hook_arg', which is used to pass down information, currently only to write_hook_for_clr_end() (previous undo_lsn and type of undone record). * If from ha_maria, we print to stderr how many seconds (with one fractional digit) the REDO phase took, same for UNDO phase and for final table close. Just to give an indication for debugging and maybe also for Support. storage/maria/ha_maria.cc: question for Monty storage/maria/ma_blockrec.c: * log in-write hooks (write_hook_for_redo() etc) move from ma_loghandler.c to here; this is natural: the hooks are coupled to their callers (functions in ma_blockrec.c). * translog_write_record() now has a new argument "hook_arg"; using it to pass down to write_hook_for_clr_end() the transaction's previous_undo_lsn and the type of the being undone record, and also to pass down to all UNDOs the live checksum variation caused by the operation. * If table has live checksum, store in UNDO_ROW_INSERT|UPDATE|DELETE and in CLR_END the checksum variation ("delta") caused by the operation. For example if a DELETE caused the table's live checksum to change from 123 to 456, we store in the UNDO_ROW_DELETE, in 4 bytes, the value 333 (456-123). * Instead of hard-coded "1" as length of the place where we store the undone record's type in CLR_END, use a symbol CLR_TYPE_STORE_SIZE; use macros clr_type_store and clr_type_korr. * write_block_record() has a new parameter 'old_record_checksum' which is the pre-computed checksum of old_record; that value is used to update the table's live checksum when writing UNDO_ROW_UPDATE|CLR_END. * In allocate_write_block_record(), if we are executing UNDO_ROW_DELETE the row's checksum is already computed. * _ma_update_block_record2() now expect the new row's checksum into cur_row.checksum (was already true) and the old row's checksum into new_row.checksum (that's new). Its two callers, maria_update() and _ma_apply_undo_row_update(), honour this. * When executing an UNDO_ROW_INSERT|UPDATE|DELETE in UNDO phase, pick up the checksum delta from the log record. It is then used to update the table's live checksum when writing CLR_END, and saves us a computation of record. storage/maria/ma_blockrec.h: in-write hooks move from ma_loghandler.c storage/maria/ma_check.c: more straightforward size of buffer storage/maria/ma_checkpoint.c: <= is enough storage/maria/ma_commit.c: new prototype of translog_write_record() storage/maria/ma_create.c: new prototype of translog_write_record() storage/maria/ma_delete.c: The row's checksum must be computed before calling(*delete_record)(), not after, because it must be known inside _ma_delete_block_record() (to update the table's live checksum when writing UNDO_ROW_DELETE). If deleting from a transactional table, live checksum was already updated when writing UNDO_ROW_DELETE. storage/maria/ma_delete_all.c: @todo is now done (in ma_loghandler.c) storage/maria/ma_delete_table.c: new prototype of translog_write_record() storage/maria/ma_loghandler.c: * in-write hooks move to ma_blockrec.c. * translog_write_record() gets a new argument 'hook_arg' which is passed down to pre|inwrite_rec_hook. It is more useful that 'parts' for those hooks, because when those hooks are called, 'parts' has possibly been mangled (like with LSN compression) and is so unpredictable. * fix for compiler warning (unused buffer_start when compiling without debug support) * Because checksum delta is stored into UNDO_ROW_INSERT|UPDATE|DELETE and CLR_END, but only if the table has live checksum, these records are not PSEUDOFIXEDLENGTH anymore, they are now VARIABLE_LENGTH (their length is X if no live checksum and X+4 otherwise). * add an inwrite_rec_hook for UNDO_ROW_UPDATE, which updates the table's live checksum. Update it also in hooks of UNDO_ROW_INSERT| DELETE and REDO_DELETE_ALL and CLR_END. * Bugfix: when reading a record in translog_read_record(), it happened that "length" became negative, because the function assumed that the record extended beyond the page's end, whereas it may be shorter. storage/maria/ma_loghandler.h: * Instead of hard-coded "1" and "4", use symbols and macros to store/retrieve the type of record which the CLR_END corresponds to, and the checksum variation caused by the operation which logs the record * translog_write_record() gets a new argument 'hook_arg' which is passed down to pre|inwrite_rec_hook. It is more useful that 'parts' for those hooks, because when those hooks are called, 'parts' has possibly been mangled (like with LSN compression) and is so unpredictable. storage/maria/ma_open.c: fix for "empty body in if() statement" (when compiling without safemutex) storage/maria/ma_pagecache.c: <= is enough storage/maria/ma_recovery.c: * print the time that each recovery phase (REDO/UNDO/flush) took; this is enabled only when recovering from ha_maria. Is it printed n seconds with a fractional part of one digit (like 123.4 seconds). * In the REDO phase, update the table's live checksum by using the checksum delta stored in UNDO_ROW_INSERT|DELETE|UPDATE and CLR_END. Update it too when seeing REDO_DELETE_ALL. * In the UNDO phase, when executing UNDO_ROW_INSERT, if the table does not have live checksum then reading the record's header (as done by the master loop of run_undo_phase()) is enough; otherwise we do a translog_read_record() to have the checksum delta ready for _ma_apply_undo_row_insert(). * When at the end of the REDO phase we notice that there is an unfinished group of REDOs, don't assert in debug binaries, as I verified that it can happen in real life (with kill -9) * removing ' in #error as it confuses gcc3 storage/maria/ma_rename.c: new prototype of translog_write_record() storage/maria/ma_test_recovery.expected: Change in output of ma_test_recovery: now all live checksums of original tables equal those of tables recreated by the REDO phase and those of tables fixed by the UNDO phase. I.e. recovery of the live checksum looks like working (which was after all the only goal of this changeset). I checked by hand that it's not just all live checksums which are now 0 and that's why they match. They are the old values like 3757530372. maria.test has hard-coded checksum values in its result file so checks this too. storage/maria/ma_update.c: * It's useless to put up HA_STATE_CHANGED in 'key_changed', as we put up HA_STATE_CHANGED in info->update anyway. * We need to compute the old and new rows' checksum before calling (*update_record)(), as checksum delta must be known when logging UNDO_ROW_UPDATE which is done by _ma_update_block_record(). Note that some functions change the 'newrec' record (at least _ma_check_unique() does) so we cannot move the checksum computation too early in the function. storage/maria/ma_write.c: If inserting into a transactional table, live's checksum was already updated when writing UNDO_ROW_INSERT. The multiplication is a trick to save an if(). storage/maria/unittest/ma_test_loghandler-t.c: new prototype of translog_write_record() storage/maria/unittest/ma_test_loghandler_first_lsn-t.c: new prototype of translog_write_record() storage/maria/unittest/ma_test_loghandler_max_lsn-t.c: new prototype of translog_write_record() storage/maria/unittest/ma_test_loghandler_multigroup-t.c: new prototype of translog_write_record() storage/maria/unittest/ma_test_loghandler_multithread-t.c: new prototype of translog_write_record() storage/maria/unittest/ma_test_loghandler_noflush-t.c: new prototype of translog_write_record() storage/maria/unittest/ma_test_loghandler_pagecache-t.c: new prototype of translog_write_record() storage/maria/unittest/ma_test_loghandler_purge-t.c: new prototype of translog_write_record() storage/myisam/sort.c: fix for compiler warnings in pushbuild (write_merge_key* functions didn't have their declaration match MARIA_HA::write_key).
220 lines
9.9 KiB
C
220 lines
9.9 KiB
C
/* Copyright (C) 2007 Michael Widenius
|
|
|
|
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; version 2 of the License.
|
|
|
|
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 */
|
|
|
|
/*
|
|
Storage of records in block
|
|
*/
|
|
|
|
#define LSN_SIZE 7
|
|
#define DIR_COUNT_SIZE 1 /* Stores number of rows on page */
|
|
#define EMPTY_SPACE_SIZE 2 /* Stores empty space on page */
|
|
#define PAGE_TYPE_SIZE 1
|
|
#define PAGE_SUFFIX_SIZE 0 /* Bytes for page suffix */
|
|
#define PAGE_HEADER_SIZE (LSN_SIZE + DIR_COUNT_SIZE + EMPTY_SPACE_SIZE +\
|
|
PAGE_TYPE_SIZE)
|
|
#define PAGE_OVERHEAD_SIZE (PAGE_HEADER_SIZE + DIR_ENTRY_SIZE + \
|
|
PAGE_SUFFIX_SIZE)
|
|
#define BLOCK_RECORD_POINTER_SIZE 6
|
|
|
|
#define FULL_PAGE_SIZE(block_size) ((block_size) - LSN_SIZE - PAGE_TYPE_SIZE)
|
|
|
|
#define ROW_EXTENT_PAGE_SIZE 5
|
|
#define ROW_EXTENT_COUNT_SIZE 2
|
|
#define ROW_EXTENT_SIZE (ROW_EXTENT_PAGE_SIZE + ROW_EXTENT_COUNT_SIZE)
|
|
#define TAIL_BIT 0x8000 /* Bit in page_count to signify tail */
|
|
/* Number of extents reserved MARIA_BITMAP_BLOCKS to store head part */
|
|
#define ELEMENTS_RESERVED_FOR_MAIN_PART 4
|
|
/* Fields before 'row->null_field_lengths' used by find_where_to_split_row */
|
|
#define EXTRA_LENGTH_FIELDS 3
|
|
|
|
/* Size for the different parts in the row header (and head page) */
|
|
|
|
#define FLAG_SIZE 1
|
|
#define TRANSID_SIZE 6
|
|
#define VERPTR_SIZE 7
|
|
#define DIR_ENTRY_SIZE 4
|
|
#define FIELD_OFFSET_SIZE 2 /* size of pointers to field starts */
|
|
|
|
/* Minimum header size needed for a new row */
|
|
#define BASE_ROW_HEADER_SIZE FLAG_SIZE
|
|
#define TRANS_ROW_EXTRA_HEADER_SIZE TRANSID_SIZE
|
|
|
|
#define PAGE_TYPE_MASK 127
|
|
enum en_page_type { UNALLOCATED_PAGE, HEAD_PAGE, TAIL_PAGE, BLOB_PAGE, MAX_PAGE_TYPE };
|
|
|
|
#define PAGE_TYPE_OFFSET LSN_SIZE
|
|
#define DIR_COUNT_OFFSET LSN_SIZE+PAGE_TYPE_SIZE
|
|
#define EMPTY_SPACE_OFFSET (DIR_COUNT_OFFSET + DIR_COUNT_SIZE)
|
|
|
|
#define PAGE_CAN_BE_COMPACTED 128 /* Bit in PAGE_TYPE */
|
|
|
|
/* Bits used for flag uchar (one byte, first in record) */
|
|
#define ROW_FLAG_TRANSID 1
|
|
#define ROW_FLAG_VER_PTR 2
|
|
#define ROW_FLAG_DELETE_TRANSID 4
|
|
#define ROW_FLAG_NULLS_EXTENDED 8
|
|
#define ROW_FLAG_EXTENTS 128
|
|
#define ROW_FLAG_ALL (1+2+4+8+128)
|
|
|
|
/******** Variables that affects how data pages are utilized ********/
|
|
|
|
/* Minium size of tail segment */
|
|
#define MIN_TAIL_SIZE 32
|
|
|
|
/*
|
|
Fixed length part of Max possible header size; See row data structure
|
|
table in ma_blockrec.c.
|
|
*/
|
|
#define MAX_FIXED_HEADER_SIZE (FLAG_SIZE + 3 + ROW_EXTENT_SIZE + 3)
|
|
#define TRANS_MAX_FIXED_HEADER_SIZE (MAX_FIXED_HEADER_SIZE + \
|
|
TRANSID_SIZE + VERPTR_SIZE + \
|
|
TRANSID_SIZE)
|
|
|
|
/* We use 1 uchar in record header to store number of directory entries */
|
|
#define MAX_ROWS_PER_PAGE 255
|
|
|
|
/* Bits for MARIA_BITMAP_BLOCKS->used */
|
|
/* We stored data on disk in the block */
|
|
#define BLOCKUSED_USED 1
|
|
/* Bitmap on disk is block->org_bitmap_value ; Happens only on update */
|
|
#define BLOCKUSED_USE_ORG_BITMAP 2
|
|
/* We stored tail data on disk for the block */
|
|
#define BLOCKUSED_TAIL 4
|
|
|
|
/******* defines that affects allocation (density) of data *******/
|
|
|
|
/*
|
|
If the tail part (from the main block or a blob) would use more than 75 % of
|
|
the size of page, store the tail on a full page instead of a shared
|
|
tail page.
|
|
*/
|
|
#define MAX_TAIL_SIZE(block_size) ((block_size) *3 / 4)
|
|
|
|
/* Don't allocate memory for too many row extents on the stack */
|
|
#define ROW_EXTENTS_ON_STACK 32
|
|
|
|
/* Functions to convert MARIA_RECORD_POS to/from page:offset */
|
|
|
|
static inline MARIA_RECORD_POS ma_recordpos(ulonglong page, uint dir_entry)
|
|
{
|
|
DBUG_ASSERT(dir_entry <= 255);
|
|
return (MARIA_RECORD_POS) ((page << 8) | dir_entry);
|
|
}
|
|
|
|
static inline my_off_t ma_recordpos_to_page(MARIA_RECORD_POS record_pos)
|
|
{
|
|
return record_pos >> 8;
|
|
}
|
|
|
|
static inline uint ma_recordpos_to_dir_entry(MARIA_RECORD_POS record_pos)
|
|
{
|
|
return (uint) (record_pos & 255);
|
|
}
|
|
|
|
/* ma_blockrec.c */
|
|
void _ma_init_block_record_data(void);
|
|
my_bool _ma_once_init_block_record(MARIA_SHARE *share, File dfile);
|
|
my_bool _ma_once_end_block_record(MARIA_SHARE *share);
|
|
my_bool _ma_init_block_record(MARIA_HA *info);
|
|
void _ma_end_block_record(MARIA_HA *info);
|
|
|
|
my_bool _ma_update_block_record(MARIA_HA *info, MARIA_RECORD_POS pos,
|
|
const uchar *oldrec, const uchar *newrec);
|
|
my_bool _ma_delete_block_record(MARIA_HA *info, const uchar *record);
|
|
int _ma_read_block_record(MARIA_HA *info, uchar *record,
|
|
MARIA_RECORD_POS record_pos);
|
|
int _ma_read_block_record2(MARIA_HA *info, uchar *record,
|
|
uchar *data, uchar *end_of_data);
|
|
int _ma_scan_block_record(MARIA_HA *info, uchar *record,
|
|
MARIA_RECORD_POS, my_bool);
|
|
my_bool _ma_cmp_block_unique(MARIA_HA *info, MARIA_UNIQUEDEF *def,
|
|
const uchar *record, MARIA_RECORD_POS pos);
|
|
my_bool _ma_scan_init_block_record(MARIA_HA *info);
|
|
void _ma_scan_end_block_record(MARIA_HA *info);
|
|
|
|
MARIA_RECORD_POS _ma_write_init_block_record(MARIA_HA *info,
|
|
const uchar *record);
|
|
my_bool _ma_write_block_record(MARIA_HA *info, const uchar *record);
|
|
my_bool _ma_write_abort_block_record(MARIA_HA *info);
|
|
my_bool _ma_compare_block_record(register MARIA_HA *info,
|
|
register const uchar *record);
|
|
|
|
/* ma_bitmap.c */
|
|
my_bool _ma_bitmap_init(MARIA_SHARE *share, File file);
|
|
my_bool _ma_bitmap_end(MARIA_SHARE *share);
|
|
my_bool _ma_flush_bitmap(MARIA_SHARE *share);
|
|
my_bool _ma_bitmap_find_place(MARIA_HA *info, MARIA_ROW *row,
|
|
MARIA_BITMAP_BLOCKS *result_blocks);
|
|
my_bool _ma_bitmap_release_unused(MARIA_HA *info, MARIA_BITMAP_BLOCKS *blocks);
|
|
my_bool _ma_bitmap_free_full_pages(MARIA_HA *info, const uchar *extents,
|
|
uint count);
|
|
my_bool _ma_bitmap_set(MARIA_HA *info, ulonglong pos, my_bool head,
|
|
uint empty_space);
|
|
my_bool _ma_reset_full_page_bits(MARIA_HA *info, MARIA_FILE_BITMAP *bitmap,
|
|
ulonglong page, uint page_count);
|
|
uint _ma_free_size_to_head_pattern(MARIA_FILE_BITMAP *bitmap, uint size);
|
|
my_bool _ma_bitmap_find_new_place(MARIA_HA *info, MARIA_ROW *new_row,
|
|
ulonglong page, uint free_size,
|
|
MARIA_BITMAP_BLOCKS *result_blocks);
|
|
my_bool _ma_check_bitmap_data(MARIA_HA *info,
|
|
enum en_page_type page_type, ulonglong page,
|
|
uint empty_space, uint *bitmap_pattern);
|
|
my_bool _ma_check_if_right_bitmap_type(MARIA_HA *info,
|
|
enum en_page_type page_type,
|
|
ulonglong page,
|
|
uint *bitmap_pattern);
|
|
void _ma_bitmap_delete_all(MARIA_SHARE *share);
|
|
int _ma_bitmap_create_first(MARIA_SHARE *share);
|
|
uint _ma_apply_redo_insert_row_head_or_tail(MARIA_HA *info, LSN lsn,
|
|
uint page_type,
|
|
const uchar *header,
|
|
const uchar *data,
|
|
size_t data_length);
|
|
uint _ma_apply_redo_purge_row_head_or_tail(MARIA_HA *info, LSN lsn,
|
|
uint page_type,
|
|
const uchar *header);
|
|
uint _ma_apply_redo_purge_blocks(MARIA_HA *info, LSN lsn,
|
|
const uchar *header);
|
|
my_bool _ma_apply_undo_row_insert(MARIA_HA *info, LSN undo_lsn,
|
|
const uchar *header);
|
|
my_bool _ma_apply_undo_row_delete(MARIA_HA *info, LSN undo_lsn,
|
|
const uchar *header, size_t length);
|
|
my_bool _ma_apply_undo_row_update(MARIA_HA *info, LSN undo_lsn,
|
|
const uchar *header, size_t length);
|
|
|
|
my_bool write_hook_for_redo(enum translog_record_type type,
|
|
TRN *trn, MARIA_HA *tbl_info, LSN *lsn,
|
|
void *hook_arg);
|
|
my_bool write_hook_for_undo(enum translog_record_type type,
|
|
TRN *trn, MARIA_HA *tbl_info, LSN *lsn,
|
|
void *hook_arg);
|
|
my_bool write_hook_for_redo_delete_all(enum translog_record_type type,
|
|
TRN *trn, MARIA_HA *tbl_info,
|
|
LSN *lsn, void *hook_arg);
|
|
my_bool write_hook_for_undo_row_insert(enum translog_record_type type,
|
|
TRN *trn, MARIA_HA *tbl_info,
|
|
LSN *lsn, void *hook_arg);
|
|
my_bool write_hook_for_undo_row_delete(enum translog_record_type type,
|
|
TRN *trn, MARIA_HA *tbl_info,
|
|
LSN *lsn, void *hook_arg);
|
|
my_bool write_hook_for_undo_row_update(enum translog_record_type type,
|
|
TRN *trn, MARIA_HA *tbl_info,
|
|
LSN *lsn, void *hook_arg);
|
|
my_bool write_hook_for_clr_end(enum translog_record_type type,
|
|
TRN *trn, MARIA_HA *tbl_info, LSN *lsn,
|
|
void *hook_arg);
|
|
my_bool write_hook_for_file_id(enum translog_record_type type,
|
|
TRN *trn, MARIA_HA *tbl_info, LSN *lsn,
|
|
void *hook_arg);
|