mirror of
https://github.com/MariaDB/server.git
synced 2025-01-18 04:53:01 +01:00
21fd2a5a36
Added key_nr to st_maria_keydef for faster keyinfo->keynr conversion For transactional tables, shift record number in keys up with 1 bit to have place to indicate if transid follows Checksum for MyISAM now ignores NULL and not used part of VARCHAR Renamed some variables that caused shadow compiler warnings Moved extra() call when waiting for tables to not be used to after tables are removed from cache. Fixed crashing bugs when using Maria TEMPORARY tables with TRUNCATE. Removed 'hack' code in sql directory to go around this bug. pagecache_unlock_by_ulink() now has extra argument to say if page was changed. Give error message if we fail to open control file Mark page cache variables as not flushable include/maria.h: Made min page cache larger (needed for pinning key page) Added key_nr to st_maria_keydef for faster keyinfo->keynr conversion Added write_comp_flag to move some runtime code to maria_open() include/my_base.h: Added new error message to be used when handler initialization failed include/my_global.h: Renamed dummy to swap_dummy to avoid conflicts with local 'dummy' variables include/my_handler.h: Added const to some parameters mysys/array.c: More DBUG mysys/my_error.c: Fixed indentation mysys/my_handler.c: Added const to some parameters Added missing error messages sql/field.h: Renamed variables to avoid variable shadowing sql/handler.h: Renamed parameter to avoid variable name conflict sql/item.h: Renamed variables to avoid variable shadowing sql/log_event_old.h: Renamed variables to avoid variable shadowing sql/set_var.h: Renamed variables to avoid variable shadowing sql/sql_delete.cc: Removed maria hack for temporary tables Fixed indentation sql/sql_table.cc: Moved extra() call when waiting for tables to not be used to after tables are removed from cache. This was needed to ensure we don't do a PREPARE_FOR_DROP or similar call while the table is still in use. sql/table.cc: Copy page_checksum from share Removed Maria hack storage/maria/Makefile.am: Added new files storage/maria/ha_maria.cc: Renamed records -> record_count and info -> create_info to avoid variable name conflicts Mark page cache variables as not flushable storage/maria/ma_blockrec.c: Moved _ma_unpin_all_pages() to ma_key_recover.c Moved init of info->pinned_pages to ma_open.c Moved _ma_finalize_row() to maria_key_recover.h Renamed some variables to avoid variable name conflicts Mark page_link.changed for blocks we change directly Simplify handling of undo link when writing LOGREC_UNDO_ROW_INSERT (old code crashed when having redo for index) storage/maria/ma_blockrec.h: Removed extra empty line storage/maria/ma_checkpoint.c: Remove not needed trnman.h storage/maria/ma_close.c: Free pinned pages (which are now always allocated) storage/maria/ma_control_file.c: Give error message if we fail to open control file storage/maria/ma_delete.c: Changes for redo logging (first part, logging of underflow not yet done) - Log undo-key-delete - Log delete of key - Updated arguments to _ma_fetch_keypage(), _ma_dispose(), _ma_write_keypage(), _ma_insert() - Added new arguments to some functions to be able to write redo information - Mark key pages as changed when we write with PAGECACHE_LOCK_LEFT_WRITELOCKED Remove one not needed _ma_write_keypage() in d_search() when upper level will do the write anyway Changed 2 bmove_upp() to bmove() as this made code easer to understand More function comments Indentation fixes storage/maria/ma_ft_update.c: New arguments to _ma_write_keypage() storage/maria/ma_loghandler.c: Fixed some DBUG_PRINT messages Simplify code Added new log entrys for key page redo Renamed some variables to avoid variable name shadowing storage/maria/ma_loghandler.h: Moved some defines here Added define for storing key number on key pages Added new translog record types Added enum for type of operations in LOGREC_REDO_INDEX storage/maria/ma_open.c: Always allocate info.pinned_pages (we need now also for normal key page usage) Update keyinfo->key_nr Added virtual functions to convert record position o number to be stored on key pages Update keyinfo->write_comp_flag to value of search flag to be used when writing key storage/maria/ma_page.c: Added redo for key pages - Extended _ma_fetch_keypage() with type of lock to put on page and address to used MARIA_PINNED_PAGE - _ma_fetch_keypage() now pin's pages if needed - Extended _ma_write_keypage() with type of locks to be used - ma_dispose() now locks info->s->state.key_del from other threads - ma_dispose() writes redo log record - ma_new() locks info->s->state.key_del from other threads if it was used - ma_new() now pins read page Other things: - Removed some not needed arguments from _ma_new() and _ma_dispose) - Added some new variables to simplify code - If EXTRA_DEBUG is used, do crc on full page to catch not unitialized bytes storage/maria/ma_pagecache.h: Applied patch from Sanja to add extra argument to pagecache_unlock_by_ulink() to mark if page was changed Added some defines for pagecache priority levels that one can use storage/maria/ma_range.c: Added new arguments for call to _ma_fetch_keypage() storage/maria/ma_recovery.c: - Added hooks for new translog types: REDO_INDEX, REDO_INDEX_NEW_PAGE, REDO_INDEX_FREE_PAGE, UNDO_KEY_INSERT, UNDO_KEY_DELETE and UNDO_KEY_DELETE_WITH_ROOT. - Moved variable declarations to start of function (portability fixes) - Removed some not needed initializations - Set only relevant state changes for each redo/undo entry storage/maria/lockman.c: Removed end space storage/maria/ma_check.c: Removed end space storage/maria/ma_create.c: Removed end space storage/maria/ma_locking.c: Removed end space storage/maria/ma_packrec.c: Removed end space storage/maria/ma_pagecache.c: Removed end space storage/maria/ma_panic.c: Removed end space storage/maria/ma_rt_index.c: Added new arguments for call to _ma_fetch_keypage(), _ma_write_keypage(), _ma_dispose() and _ma_new() Fixed indentation storage/maria/ma_rt_key.c: Added new arguments for call to _ma_fetch_keypage() storage/maria/ma_rt_split.c: Added new arguments for call to _ma_new() Use new keypage header Added new arguments for call to _ma_write_keypage() storage/maria/ma_search.c: Updated comments & indentation Added new arguments for call to _ma_fetch_keypage() Made some variables and arguments const Added virtual functions for converting row position to number to be stored in key use MARIA_RECORD_POS of record position instead of my_off_t Record in MARIA_KEY_PARAM how page was changed one key insert (needed for REDO) storage/maria/ma_sort.c: Removed end space storage/maria/ma_statrec.c: Updated arguments for call to _ma_rec_pos() storage/maria/ma_test1.c: Fixed too small buffer to init_pagecache() Fixed bug when using insert_count and test_flag storage/maria/ma_test2.c: Use more resonable pagecache size Remove not used code Reset blob_length to fix wrong output message storage/maria/ma_test_all.sh: Fixed wrong test storage/maria/ma_write.c: Lots of new code to handle REDO of key pages No logic changes because of REDO code, mostly adding new arguments and adding new code for logging Added new arguments for calls to _ma_fetch_keypage(), _ma_write_keypage() and similar functions Move setting of comp_flag in ma_ck_wrte_btree() from runtime to maria_open() Zerofill new used pages for: - To remove possible sensitive data left in buffer - To get idenitical data on pages after running redo - Better compression of pages if archived storage/maria/maria_chk.c: Added information if table is crash safe storage/maria/maria_def.h: New virtual function to convert between record position on key and normal record position Aded mutex and extra variables to handle locking of share->state.key_del Moved some structure variables to get things more aligned Added extra arguments to MARIA_KEY_PARAM to be able to remember what was changed on key page on key insert Added argument to MARIA_PINNED_PAGE to indicate if page was changed Updated prototypes for functions Added some structures for signaling changes in REDO handling storage/maria/unittest/ma_pagecache_single.c: Updated arguments for changed function calls storage/myisam/mi_check.c: Made calc_check_checksum virtual storage/myisam/mi_checksum.c: Update checksums to ignore null columns storage/myisam/mi_create.c: Mark if table has null column (to know when we have to use mi_checksum()) storage/myisam/mi_open.c: Added virtual function for calculating checksum to be able to easily ignore NULL fields storage/myisam/mi_test2.c: Fixed bug storage/myisam/myisamdef.h: Added virtual function for calculating checksum during check table Removed ha_key_cmp() as this is in handler.h storage/maria/ma_key_recover.c: New BitKeeper file ``storage/maria/ma_key_recover.c'' storage/maria/ma_key_recover.h: New BitKeeper file ``storage/maria/ma_key_recover.h'' storage/maria/ma_key_redo.c: New BitKeeper file ``storage/maria/ma_key_redo.c''
1117 lines
38 KiB
C
1117 lines
38 KiB
C
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
|
|
|
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 */
|
|
|
|
/* Remove a row from a MARIA table */
|
|
|
|
#include "ma_fulltext.h"
|
|
#include "ma_rt_index.h"
|
|
#include "trnman.h"
|
|
#include "ma_key_recover.h"
|
|
|
|
static int d_search(MARIA_HA *info,MARIA_KEYDEF *keyinfo,uint comp_flag,
|
|
uchar *key, uint key_length,
|
|
my_off_t page, uchar *anc_buff,
|
|
MARIA_PINNED_PAGE *anc_page_link);
|
|
static int del(MARIA_HA *info,MARIA_KEYDEF *keyinfo, uchar *key,
|
|
uchar *anc_buff, my_off_t leaf_page, uchar *leaf_buff,
|
|
MARIA_PINNED_PAGE *leaf_page_link, uchar *keypos,
|
|
my_off_t next_block, uchar *ret_key);
|
|
static int underflow(MARIA_HA *info,MARIA_KEYDEF *keyinfo,uchar *anc_buff,
|
|
my_off_t leaf_page,uchar *leaf_buff,
|
|
MARIA_PINNED_PAGE *leaf_page_link, uchar *keypos);
|
|
static uint remove_key(MARIA_KEYDEF *keyinfo,uint nod_flag,uchar *keypos,
|
|
uchar *lastkey,uchar *page_end,
|
|
my_off_t *next_block, MARIA_KEY_PARAM *s_temp);
|
|
static my_bool _ma_log_delete(MARIA_HA *info, my_off_t page, uchar *buff,
|
|
uchar *key_pos, uint move_length,
|
|
uint change_length);
|
|
|
|
|
|
int maria_delete(MARIA_HA *info,const uchar *record)
|
|
{
|
|
uint i;
|
|
uchar *old_key;
|
|
int save_errno;
|
|
char lastpos[8];
|
|
MARIA_SHARE *share=info->s;
|
|
DBUG_ENTER("maria_delete");
|
|
|
|
/* Test if record is in datafile */
|
|
|
|
DBUG_EXECUTE_IF("maria_pretend_crashed_table_on_usage",
|
|
maria_print_error(share, HA_ERR_CRASHED);
|
|
DBUG_RETURN(my_errno= HA_ERR_CRASHED););
|
|
DBUG_EXECUTE_IF("my_error_test_undefined_error",
|
|
maria_print_error(share, INT_MAX);
|
|
DBUG_RETURN(my_errno= INT_MAX););
|
|
if (!(info->update & HA_STATE_AKTIV))
|
|
{
|
|
DBUG_RETURN(my_errno=HA_ERR_KEY_NOT_FOUND); /* No database read */
|
|
}
|
|
if (share->options & HA_OPTION_READ_ONLY_DATA)
|
|
{
|
|
DBUG_RETURN(my_errno=EACCES);
|
|
}
|
|
if (_ma_readinfo(info,F_WRLCK,1))
|
|
DBUG_RETURN(my_errno);
|
|
if ((*share->compare_record)(info,record))
|
|
goto err; /* Error on read-check */
|
|
|
|
if (_ma_mark_file_changed(info))
|
|
goto err;
|
|
|
|
/* Remove all keys from the index file */
|
|
|
|
old_key= info->lastkey2;
|
|
for (i=0 ; i < share->base.keys ; i++ )
|
|
{
|
|
if (maria_is_key_active(share->state.key_map, i))
|
|
{
|
|
share->keyinfo[i].version++;
|
|
if (share->keyinfo[i].flag & HA_FULLTEXT )
|
|
{
|
|
if (_ma_ft_del(info, i, old_key, record, info->cur_row.lastpos))
|
|
goto err;
|
|
}
|
|
else
|
|
{
|
|
if (share->keyinfo[i].ck_delete(info,i,old_key,
|
|
_ma_make_key(info, i, old_key,
|
|
record,
|
|
info->cur_row.lastpos)))
|
|
goto err;
|
|
}
|
|
/* The above changed info->lastkey2. Inform maria_rnext_same(). */
|
|
info->update&= ~HA_STATE_RNEXT_SAME;
|
|
}
|
|
}
|
|
|
|
if (share->calc_checksum)
|
|
{
|
|
/*
|
|
We can't use the row based checksum as this doesn't have enough
|
|
precision.
|
|
*/
|
|
info->cur_row.checksum= (*share->calc_checksum)(info, record);
|
|
}
|
|
|
|
if ((*share->delete_record)(info, record))
|
|
goto err; /* Remove record from database */
|
|
|
|
info->state->checksum+= - !share->now_transactional *
|
|
info->cur_row.checksum;
|
|
info->update= HA_STATE_CHANGED+HA_STATE_DELETED+HA_STATE_ROW_CHANGED;
|
|
info->state->records-= !share->now_transactional;
|
|
share->state.changed|= STATE_NOT_OPTIMIZED_ROWS;
|
|
|
|
mi_sizestore(lastpos, info->cur_row.lastpos);
|
|
VOID(_ma_writeinfo(info,WRITEINFO_UPDATE_KEYFILE));
|
|
allow_break(); /* Allow SIGHUP & SIGINT */
|
|
if (info->invalidator != 0)
|
|
{
|
|
DBUG_PRINT("info", ("invalidator... '%s' (delete)", share->open_file_name));
|
|
(*info->invalidator)(share->open_file_name);
|
|
info->invalidator=0;
|
|
}
|
|
DBUG_RETURN(0);
|
|
|
|
err:
|
|
save_errno=my_errno;
|
|
mi_sizestore(lastpos, info->cur_row.lastpos);
|
|
if (save_errno != HA_ERR_RECORD_CHANGED)
|
|
{
|
|
maria_print_error(share, HA_ERR_CRASHED);
|
|
maria_mark_crashed(info); /* mark table crashed */
|
|
}
|
|
VOID(_ma_writeinfo(info,WRITEINFO_UPDATE_KEYFILE));
|
|
info->update|=HA_STATE_WRITTEN; /* Buffer changed */
|
|
allow_break(); /* Allow SIGHUP & SIGINT */
|
|
my_errno=save_errno;
|
|
if (save_errno == HA_ERR_KEY_NOT_FOUND)
|
|
{
|
|
maria_print_error(share, HA_ERR_CRASHED);
|
|
my_errno=HA_ERR_CRASHED;
|
|
}
|
|
|
|
DBUG_RETURN(my_errno);
|
|
} /* maria_delete */
|
|
|
|
|
|
/* Remove a key from the btree index */
|
|
|
|
int _ma_ck_delete(register MARIA_HA *info, uint keynr, uchar *key,
|
|
uint key_length)
|
|
{
|
|
int res;
|
|
LSN lsn= LSN_IMPOSSIBLE;
|
|
my_off_t new_root= info->s->state.key_root[keynr];
|
|
DBUG_ENTER("_ma_ck_delete");
|
|
|
|
res= _ma_ck_real_delete(info, info->s->keyinfo+keynr, key, key_length,
|
|
&new_root);
|
|
|
|
if (!res && info->s->now_transactional)
|
|
{
|
|
uchar log_data[LSN_STORE_SIZE + FILEID_STORE_SIZE +
|
|
KEY_NR_STORE_SIZE + PAGE_STORE_SIZE], *log_pos;
|
|
LEX_STRING log_array[TRANSLOG_INTERNAL_PARTS + 2];
|
|
struct st_msg_to_write_hook_for_undo_key msg;
|
|
enum translog_record_type log_type= LOGREC_UNDO_KEY_DELETE;
|
|
|
|
lsn_store(log_data, info->trn->undo_lsn);
|
|
key_nr_store(log_data + LSN_STORE_SIZE + FILEID_STORE_SIZE, keynr);
|
|
log_pos= log_data + LSN_STORE_SIZE + FILEID_STORE_SIZE + KEY_NR_STORE_SIZE;
|
|
|
|
if (new_root != info->s->state.key_root[keynr])
|
|
{
|
|
my_off_t page;
|
|
page= ((new_root == HA_OFFSET_ERROR) ? IMPOSSIBLE_PAGE_NO :
|
|
new_root / info->s->block_size);
|
|
page_store(log_pos, page);
|
|
log_pos+= PAGE_STORE_SIZE;
|
|
log_type= LOGREC_UNDO_KEY_DELETE_WITH_ROOT;
|
|
}
|
|
|
|
log_array[TRANSLOG_INTERNAL_PARTS + 0].str= (char*) log_data;
|
|
log_array[TRANSLOG_INTERNAL_PARTS + 0].length= (uint) (log_pos - log_data);
|
|
log_array[TRANSLOG_INTERNAL_PARTS + 1].str= (char*) key;
|
|
log_array[TRANSLOG_INTERNAL_PARTS + 1].length= key_length;
|
|
|
|
msg.root= &info->s->state.key_root[keynr];
|
|
msg.value= new_root;
|
|
|
|
if (translog_write_record(&lsn, log_type,
|
|
info->trn, info,
|
|
log_array[TRANSLOG_INTERNAL_PARTS + 0].length +
|
|
key_length,
|
|
TRANSLOG_INTERNAL_PARTS + 2, log_array,
|
|
log_data + LSN_STORE_SIZE, &msg))
|
|
res= -1;
|
|
}
|
|
else
|
|
{
|
|
info->s->state.key_root[keynr]= new_root;
|
|
_ma_fast_unlock_key_del(info);
|
|
}
|
|
_ma_unpin_all_pages_and_finalize_row(info, lsn);
|
|
DBUG_RETURN(res);
|
|
} /* _ma_ck_delete */
|
|
|
|
|
|
int _ma_ck_real_delete(register MARIA_HA *info, MARIA_KEYDEF *keyinfo,
|
|
uchar *key, uint key_length, my_off_t *root)
|
|
{
|
|
int error;
|
|
uint nod_flag;
|
|
my_off_t old_root;
|
|
uchar *root_buff;
|
|
MARIA_PINNED_PAGE *page_link;
|
|
DBUG_ENTER("_ma_ck_real_delete");
|
|
|
|
if ((old_root=*root) == HA_OFFSET_ERROR)
|
|
{
|
|
maria_print_error(info->s, HA_ERR_CRASHED);
|
|
DBUG_RETURN(my_errno=HA_ERR_CRASHED);
|
|
}
|
|
if (!(root_buff= (uchar*) my_alloca((uint) keyinfo->block_length+
|
|
HA_MAX_KEY_BUFF*2)))
|
|
{
|
|
DBUG_PRINT("error",("Couldn't allocate memory"));
|
|
DBUG_RETURN(my_errno=ENOMEM);
|
|
}
|
|
DBUG_PRINT("info",("root_page: %ld", (long) old_root));
|
|
if (!_ma_fetch_keypage(info, keyinfo, old_root,
|
|
PAGECACHE_LOCK_WRITE, DFLT_INIT_HITS, root_buff, 0,
|
|
&page_link))
|
|
{
|
|
error= -1;
|
|
goto err;
|
|
}
|
|
if ((error=d_search(info,keyinfo,
|
|
(keyinfo->flag & HA_FULLTEXT ?
|
|
SEARCH_FIND | SEARCH_UPDATE : SEARCH_SAME),
|
|
key, key_length, old_root, root_buff, page_link)) >0)
|
|
{
|
|
if (error == 2)
|
|
{
|
|
DBUG_PRINT("test",("Enlarging of root when deleting"));
|
|
error= _ma_enlarge_root(info,keyinfo,key,root);
|
|
}
|
|
else /* error == 1 */
|
|
{
|
|
uint used_length;
|
|
_ma_get_used_and_nod(info, root_buff, used_length, nod_flag);
|
|
page_link->changed= 1;
|
|
if (used_length <= nod_flag + info->s->keypage_header + 1)
|
|
{
|
|
error=0;
|
|
if (nod_flag)
|
|
*root= _ma_kpos(nod_flag, root_buff +info->s->keypage_header +
|
|
nod_flag);
|
|
else
|
|
*root=HA_OFFSET_ERROR;
|
|
if (_ma_dispose(info, old_root, 0))
|
|
error= -1;
|
|
}
|
|
else
|
|
error= _ma_write_keypage(info,keyinfo, old_root,
|
|
PAGECACHE_LOCK_LEFT_WRITELOCKED,
|
|
DFLT_INIT_HITS, root_buff);
|
|
}
|
|
}
|
|
err:
|
|
my_afree((uchar*) root_buff);
|
|
DBUG_PRINT("exit",("Return: %d",error));
|
|
DBUG_RETURN(error);
|
|
} /* _ma_ck_real_delete */
|
|
|
|
|
|
/*
|
|
@brief Remove key below key root
|
|
|
|
@param key Key to delete. Will contain new key if block was enlarged
|
|
|
|
@return
|
|
@retval 0 ok (anc_page is not changed)
|
|
@retval 1 If data on page is too small; In this case anc_buff is not saved
|
|
@retval 2 If data on page is too big
|
|
@retval -1 On errors
|
|
*/
|
|
|
|
static int d_search(register MARIA_HA *info, register MARIA_KEYDEF *keyinfo,
|
|
uint comp_flag, uchar *key, uint key_length,
|
|
my_off_t anc_page, uchar *anc_buff,
|
|
MARIA_PINNED_PAGE *anc_page_link)
|
|
{
|
|
int flag,ret_value,save_flag;
|
|
uint length,nod_flag,search_key_length;
|
|
my_bool last_key;
|
|
uchar *leaf_buff,*keypos;
|
|
my_off_t leaf_page,next_block;
|
|
uchar lastkey[HA_MAX_KEY_BUFF];
|
|
MARIA_PINNED_PAGE *leaf_page_link;
|
|
MARIA_KEY_PARAM s_temp;
|
|
DBUG_ENTER("d_search");
|
|
DBUG_DUMP("page",anc_buff,_ma_get_page_used(info, anc_buff));
|
|
|
|
search_key_length= (comp_flag & SEARCH_FIND) ? key_length : USE_WHOLE_KEY;
|
|
flag=(*keyinfo->bin_search)(info,keyinfo,anc_buff,key, search_key_length,
|
|
comp_flag, &keypos, lastkey, &last_key);
|
|
if (flag == MARIA_FOUND_WRONG_KEY)
|
|
{
|
|
DBUG_PRINT("error",("Found wrong key"));
|
|
DBUG_RETURN(-1);
|
|
}
|
|
nod_flag=_ma_test_if_nod(info, anc_buff);
|
|
|
|
if (!flag && keyinfo->flag & HA_FULLTEXT)
|
|
{
|
|
uint off;
|
|
int subkeys;
|
|
|
|
get_key_full_length_rdonly(off, lastkey);
|
|
subkeys=ft_sintXkorr(lastkey+off);
|
|
DBUG_ASSERT(info->ft1_to_ft2==0 || subkeys >=0);
|
|
comp_flag=SEARCH_SAME;
|
|
if (subkeys >= 0)
|
|
{
|
|
/* normal word, one-level tree structure */
|
|
if (info->ft1_to_ft2)
|
|
{
|
|
/* we're in ft1->ft2 conversion mode. Saving key data */
|
|
insert_dynamic(info->ft1_to_ft2, (lastkey+off));
|
|
}
|
|
else
|
|
{
|
|
/* we need exact match only if not in ft1->ft2 conversion mode */
|
|
flag=(*keyinfo->bin_search)(info,keyinfo,anc_buff,key,USE_WHOLE_KEY,
|
|
comp_flag, &keypos, lastkey, &last_key);
|
|
}
|
|
/* fall through to normal delete */
|
|
}
|
|
else
|
|
{
|
|
/* popular word. two-level tree. going down */
|
|
uint tmp_key_length;
|
|
my_off_t root;
|
|
uchar *kpos=keypos;
|
|
|
|
if (!(tmp_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&kpos,
|
|
lastkey)))
|
|
{
|
|
maria_print_error(info->s, HA_ERR_CRASHED);
|
|
my_errno= HA_ERR_CRASHED;
|
|
DBUG_RETURN(-1);
|
|
}
|
|
root= _ma_dpos(info,nod_flag,kpos);
|
|
if (subkeys == -1)
|
|
{
|
|
/* the last entry in sub-tree */
|
|
if (_ma_dispose(info, root, 1))
|
|
DBUG_RETURN(-1);
|
|
/* fall through to normal delete */
|
|
}
|
|
else
|
|
{
|
|
keyinfo=&info->s->ft2_keyinfo;
|
|
/* we'll modify key entry 'in vivo' */
|
|
kpos-=keyinfo->keylength+nod_flag;
|
|
get_key_full_length_rdonly(off, key);
|
|
key+=off;
|
|
ret_value= _ma_ck_real_delete(info, &info->s->ft2_keyinfo,
|
|
key, HA_FT_WLEN, &root);
|
|
_ma_dpointer(info, kpos+HA_FT_WLEN, root);
|
|
subkeys++;
|
|
ft_intXstore(kpos, subkeys);
|
|
if (!ret_value)
|
|
{
|
|
anc_page_link->changed= 1;
|
|
ret_value= _ma_write_keypage(info, keyinfo, anc_page,
|
|
PAGECACHE_LOCK_LEFT_WRITELOCKED,
|
|
DFLT_INIT_HITS, anc_buff);
|
|
}
|
|
DBUG_PRINT("exit",("Return: %d",ret_value));
|
|
DBUG_RETURN(ret_value);
|
|
}
|
|
}
|
|
}
|
|
leaf_buff=0;
|
|
LINT_INIT(leaf_page);
|
|
if (nod_flag)
|
|
{
|
|
leaf_page= _ma_kpos(nod_flag,keypos);
|
|
if (!(leaf_buff= (uchar*) my_alloca((uint) keyinfo->block_length+
|
|
HA_MAX_KEY_BUFF*2)))
|
|
{
|
|
DBUG_PRINT("error", ("Couldn't allocate memory"));
|
|
my_errno=ENOMEM;
|
|
DBUG_RETURN(-1);
|
|
}
|
|
if (!_ma_fetch_keypage(info,keyinfo,leaf_page,
|
|
PAGECACHE_LOCK_WRITE, DFLT_INIT_HITS, leaf_buff,
|
|
0, &leaf_page_link))
|
|
goto err;
|
|
}
|
|
|
|
if (flag != 0)
|
|
{
|
|
if (!nod_flag)
|
|
{
|
|
DBUG_PRINT("error",("Didn't find key"));
|
|
maria_print_error(info->s, HA_ERR_CRASHED);
|
|
my_errno=HA_ERR_CRASHED; /* This should newer happend */
|
|
goto err;
|
|
}
|
|
save_flag=0;
|
|
ret_value=d_search(info, keyinfo, comp_flag, key, key_length,
|
|
leaf_page, leaf_buff, leaf_page_link);
|
|
}
|
|
else
|
|
{ /* Found key */
|
|
uint tmp;
|
|
length= _ma_get_page_used(info, anc_buff);
|
|
if (!(tmp= remove_key(keyinfo,nod_flag,keypos,lastkey,anc_buff+length,
|
|
&next_block, &s_temp)))
|
|
goto err;
|
|
|
|
anc_page_link->changed= 1;
|
|
length-= tmp;
|
|
_ma_store_page_used(info, anc_buff, length, nod_flag);
|
|
|
|
/*
|
|
Log initial changes on pages
|
|
If there is an underflow, there will be more changes logged to the
|
|
page
|
|
*/
|
|
if (info->s->now_transactional &&
|
|
_ma_log_delete(info, anc_page, anc_buff, s_temp.key_pos,
|
|
s_temp.move_length, s_temp.changed_length))
|
|
DBUG_RETURN(-1);
|
|
|
|
if (!nod_flag)
|
|
{ /* On leaf page */
|
|
if (test(length <= (info->quick_mode ?
|
|
MARIA_MIN_KEYBLOCK_LENGTH :
|
|
(uint) keyinfo->underflow_block_length)))
|
|
{
|
|
/* Page will be written by caller if we return 1 */
|
|
DBUG_RETURN(1);
|
|
}
|
|
if (_ma_write_keypage(info, keyinfo, anc_page,
|
|
PAGECACHE_LOCK_LEFT_WRITELOCKED, DFLT_INIT_HITS,
|
|
anc_buff))
|
|
DBUG_RETURN(-1);
|
|
DBUG_RETURN(0);
|
|
}
|
|
save_flag=1; /* Mark that anc_buff is changed */
|
|
ret_value= del(info, keyinfo, key, anc_buff, leaf_page, leaf_buff,
|
|
leaf_page_link, keypos, next_block, lastkey);
|
|
}
|
|
if (ret_value >0)
|
|
{
|
|
save_flag=1;
|
|
if (ret_value == 1)
|
|
ret_value= underflow(info, keyinfo, anc_buff, leaf_page, leaf_buff,
|
|
leaf_page_link, keypos);
|
|
else
|
|
{ /* This happens only with packed keys */
|
|
DBUG_PRINT("test",("Enlarging of key when deleting"));
|
|
if (!_ma_get_last_key(info,keyinfo,anc_buff,lastkey,keypos,&length))
|
|
goto err;
|
|
ret_value= _ma_insert(info, keyinfo, key, anc_buff, keypos, anc_page,
|
|
lastkey, (my_off_t) 0, (uchar*) 0,
|
|
(MARIA_PINNED_PAGE*) 0, (uchar*) 0, (my_bool) 0);
|
|
}
|
|
}
|
|
if (ret_value == 0 && _ma_get_page_used(info, anc_buff) >
|
|
(uint) (keyinfo->block_length - KEYPAGE_CHECKSUM_SIZE))
|
|
{
|
|
save_flag=1;
|
|
ret_value= _ma_split_page(info,keyinfo,key,anc_buff,lastkey,0) | 2;
|
|
}
|
|
if (save_flag && ret_value != 1)
|
|
{
|
|
anc_page_link->changed= 1;
|
|
ret_value|= _ma_write_keypage(info, keyinfo, anc_page,
|
|
PAGECACHE_LOCK_LEFT_WRITELOCKED,
|
|
DFLT_INIT_HITS, anc_buff);
|
|
}
|
|
else
|
|
{
|
|
DBUG_DUMP("page", anc_buff, _ma_get_page_used(info, anc_buff));
|
|
}
|
|
my_afree(leaf_buff);
|
|
DBUG_PRINT("exit",("Return: %d",ret_value));
|
|
DBUG_RETURN(ret_value);
|
|
|
|
err:
|
|
my_afree(leaf_buff);
|
|
DBUG_PRINT("exit",("Error: %d",my_errno));
|
|
DBUG_RETURN (-1);
|
|
} /* d_search */
|
|
|
|
|
|
/* Remove a key that has a page-reference */
|
|
|
|
static int del(register MARIA_HA *info, register MARIA_KEYDEF *keyinfo,
|
|
uchar *key, uchar *anc_buff, my_off_t leaf_page,
|
|
uchar *leaf_buff, MARIA_PINNED_PAGE *leaf_page_link,
|
|
uchar *keypos, /* Pos to where deleted key was */
|
|
my_off_t next_block,
|
|
uchar *ret_key) /* key before keypos in anc_buff */
|
|
{
|
|
int ret_value,length;
|
|
uint a_length, nod_flag, used_length, tmp;
|
|
my_off_t next_page;
|
|
uchar keybuff[HA_MAX_KEY_BUFF],*endpos,*next_buff,*key_start, *prev_key;
|
|
MARIA_SHARE *share=info->s;
|
|
MARIA_KEY_PARAM s_temp;
|
|
MARIA_PINNED_PAGE *next_page_link;
|
|
DBUG_ENTER("del");
|
|
DBUG_PRINT("enter",("leaf_page: %ld keypos: 0x%lx", (long) leaf_page,
|
|
(ulong) keypos));
|
|
|
|
_ma_get_used_and_nod(info, leaf_buff, used_length, nod_flag);
|
|
DBUG_DUMP("leaf_buff", leaf_buff, used_length);
|
|
|
|
endpos= leaf_buff + used_length;
|
|
if (!(key_start= _ma_get_last_key(info,keyinfo,leaf_buff,keybuff,endpos,
|
|
&tmp)))
|
|
DBUG_RETURN(-1);
|
|
|
|
if (nod_flag)
|
|
{
|
|
next_page= _ma_kpos(nod_flag,endpos);
|
|
if (!(next_buff= (uchar*) my_alloca((uint) keyinfo->block_length+
|
|
HA_MAX_KEY_BUFF*2)))
|
|
DBUG_RETURN(-1);
|
|
if (!_ma_fetch_keypage(info, keyinfo, next_page, PAGECACHE_LOCK_WRITE,
|
|
DFLT_INIT_HITS, next_buff, 0, &next_page_link))
|
|
ret_value= -1;
|
|
else
|
|
{
|
|
DBUG_DUMP("next_page", next_buff, _ma_get_page_used(info, next_buff));
|
|
if ((ret_value= del(info,keyinfo,key,anc_buff, next_page, next_buff,
|
|
next_page_link, keypos, next_block, ret_key)) >0)
|
|
{
|
|
/* Get new length after key was deleted */
|
|
endpos=leaf_buff+_ma_get_page_used(info, leaf_buff);
|
|
if (ret_value == 1)
|
|
{
|
|
ret_value= underflow(info, keyinfo, leaf_buff, next_page,
|
|
next_buff, next_page_link, endpos);
|
|
if (ret_value == 0 &&
|
|
_ma_get_page_used(info, leaf_buff) >
|
|
(uint) (keyinfo->block_length - KEYPAGE_CHECKSUM_SIZE))
|
|
{
|
|
ret_value= (_ma_split_page(info,keyinfo,key,leaf_buff,ret_key,0) |
|
|
2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBUG_PRINT("test",("Inserting of key when deleting"));
|
|
if (!_ma_get_last_key(info,keyinfo,leaf_buff,keybuff,endpos,
|
|
&tmp))
|
|
goto err;
|
|
ret_value= _ma_insert(info, keyinfo, key, leaf_buff, endpos,
|
|
leaf_page, keybuff, (my_off_t) 0, (uchar*) 0,
|
|
(MARIA_PINNED_PAGE *) 0, (uchar*) 0, 0);
|
|
}
|
|
}
|
|
leaf_page_link->changed= 1;
|
|
if (_ma_write_keypage(info, keyinfo, leaf_page,
|
|
PAGECACHE_LOCK_LEFT_WRITELOCKED,
|
|
DFLT_INIT_HITS, leaf_buff))
|
|
goto err;
|
|
}
|
|
my_afree(next_buff);
|
|
DBUG_RETURN(ret_value);
|
|
}
|
|
|
|
/* Remove last key from leaf page */
|
|
leaf_page_link->changed= 1;
|
|
_ma_store_page_used(info, leaf_buff, key_start-leaf_buff, nod_flag);
|
|
if (_ma_write_keypage(info, keyinfo, leaf_page,
|
|
PAGECACHE_LOCK_LEFT_WRITELOCKED, DFLT_INIT_HITS,
|
|
leaf_buff))
|
|
goto err;
|
|
|
|
/* Place last key in ancestor page on deleted key position */
|
|
|
|
a_length= _ma_get_page_used(info, anc_buff);
|
|
endpos=anc_buff+a_length;
|
|
if (keypos != anc_buff+info->s->keypage_header + share->base.key_reflength &&
|
|
!_ma_get_last_key(info,keyinfo,anc_buff,ret_key,keypos,&tmp))
|
|
goto err;
|
|
prev_key= (keypos == anc_buff + info->s->keypage_header +
|
|
share->base.key_reflength ? 0 : ret_key);
|
|
length=(*keyinfo->pack_key)(keyinfo,share->base.key_reflength,
|
|
keypos == endpos ? (uchar*) 0 : keypos,
|
|
prev_key, prev_key,
|
|
keybuff,&s_temp);
|
|
if (length > 0)
|
|
bmove_upp(endpos+length,endpos,(uint) (endpos-keypos));
|
|
else
|
|
bmove(keypos,keypos-length, (int) (endpos-keypos)+length);
|
|
(*keyinfo->store_key)(keyinfo,keypos,&s_temp);
|
|
/* Save pointer to next leaf */
|
|
if (!(*keyinfo->get_key)(keyinfo,share->base.key_reflength,&keypos,ret_key))
|
|
goto err;
|
|
_ma_kpointer(info,keypos - share->base.key_reflength,next_block);
|
|
_ma_store_page_used(info, anc_buff, a_length + length,
|
|
share->base.key_reflength);
|
|
|
|
DBUG_RETURN(_ma_get_page_used(info, leaf_buff) <=
|
|
(info->quick_mode ? MARIA_MIN_KEYBLOCK_LENGTH :
|
|
(uint) keyinfo->underflow_block_length));
|
|
err:
|
|
DBUG_RETURN(-1);
|
|
} /* del */
|
|
|
|
|
|
/* Balances adjacent pages if underflow occours */
|
|
|
|
static int underflow(register MARIA_HA *info, register MARIA_KEYDEF *keyinfo,
|
|
uchar *anc_buff,
|
|
my_off_t leaf_page,/* Ancestor page and underflow page */
|
|
uchar *leaf_buff,
|
|
MARIA_PINNED_PAGE *leaf_page_link,
|
|
uchar *keypos) /* Position to pos after key */
|
|
{
|
|
int t_length;
|
|
uint length,anc_length,buff_length,leaf_length,p_length,s_length,nod_flag,
|
|
key_reflength,key_length;
|
|
my_off_t next_page;
|
|
uchar anc_key[HA_MAX_KEY_BUFF],leaf_key[HA_MAX_KEY_BUFF];
|
|
uchar *buff,*endpos,*next_keypos,*anc_pos,*half_pos,*temp_pos,*prev_key;
|
|
uchar *after_key;
|
|
MARIA_KEY_PARAM s_temp;
|
|
MARIA_SHARE *share=info->s;
|
|
MARIA_PINNED_PAGE *next_page_link;
|
|
DBUG_ENTER("underflow");
|
|
DBUG_PRINT("enter",("leaf_page: %ld keypos: 0x%lx",(long) leaf_page,
|
|
(ulong) keypos));
|
|
DBUG_DUMP("anc_buff", anc_buff, _ma_get_page_used(info, anc_buff));
|
|
DBUG_DUMP("leaf_buff", leaf_buff, _ma_get_page_used(info, leaf_buff));
|
|
|
|
buff=info->buff;
|
|
info->keyread_buff_used=1;
|
|
next_keypos=keypos;
|
|
nod_flag= _ma_test_if_nod(info, leaf_buff);
|
|
p_length= nod_flag+info->s->keypage_header;
|
|
anc_length= _ma_get_page_used(info, anc_buff);
|
|
leaf_length= _ma_get_page_used(info, leaf_buff);
|
|
key_reflength=share->base.key_reflength;
|
|
if (info->s->keyinfo+info->lastinx == keyinfo)
|
|
info->page_changed=1;
|
|
|
|
if ((keypos < anc_buff + anc_length && (info->state->records & 1)) ||
|
|
keypos == anc_buff + info->s->keypage_header + key_reflength)
|
|
{ /* Use page right of anc-page */
|
|
DBUG_PRINT("test",("use right page"));
|
|
|
|
if (keyinfo->flag & HA_BINARY_PACK_KEY)
|
|
{
|
|
if (!(next_keypos= _ma_get_key(info, keyinfo,
|
|
anc_buff, buff, keypos, &length)))
|
|
goto err;
|
|
}
|
|
else
|
|
{
|
|
/* Got to end of found key */
|
|
buff[0]=buff[1]=0; /* Avoid length error check if packed key */
|
|
if (!(*keyinfo->get_key)(keyinfo,key_reflength,&next_keypos,
|
|
buff))
|
|
goto err;
|
|
}
|
|
next_page= _ma_kpos(key_reflength,next_keypos);
|
|
if (!_ma_fetch_keypage(info,keyinfo, next_page, PAGECACHE_LOCK_WRITE,
|
|
DFLT_INIT_HITS, buff, 0, &next_page_link))
|
|
goto err;
|
|
buff_length= _ma_get_page_used(info, buff);
|
|
DBUG_DUMP("next",buff,buff_length);
|
|
|
|
/* find keys to make a big key-page */
|
|
bmove(next_keypos-key_reflength, buff + info->s->keypage_header,
|
|
key_reflength);
|
|
if (!_ma_get_last_key(info,keyinfo,anc_buff,anc_key,next_keypos,&length) ||
|
|
!_ma_get_last_key(info,keyinfo,leaf_buff,leaf_key,
|
|
leaf_buff+leaf_length,&length))
|
|
goto err;
|
|
|
|
/* merge pages and put parting key from anc_buff between */
|
|
prev_key=(leaf_length == p_length ? (uchar*) 0 : leaf_key);
|
|
t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,buff+p_length,
|
|
prev_key, prev_key,
|
|
anc_key, &s_temp);
|
|
length=buff_length-p_length;
|
|
endpos=buff+length+leaf_length+t_length;
|
|
/* buff will always be larger than before !*/
|
|
bmove_upp(endpos, buff+buff_length,length);
|
|
memcpy(buff, leaf_buff,(size_t) leaf_length);
|
|
(*keyinfo->store_key)(keyinfo,buff+leaf_length,&s_temp);
|
|
buff_length=(uint) (endpos-buff);
|
|
_ma_store_page_used(info, buff, buff_length, nod_flag);
|
|
|
|
/* remove key from anc_buff */
|
|
|
|
if (!(s_length=remove_key(keyinfo,key_reflength,keypos,anc_key,
|
|
anc_buff+anc_length,(my_off_t *) 0, &s_temp)))
|
|
goto err;
|
|
|
|
anc_length-=s_length;
|
|
_ma_store_page_used(info, anc_buff, anc_length, key_reflength);
|
|
|
|
if (buff_length <= (uint) (keyinfo->block_length - KEYPAGE_CHECKSUM_SIZE))
|
|
{ /* Keys in one page */
|
|
memcpy(leaf_buff,buff,(size_t) buff_length);
|
|
next_page_link->changed= 1;
|
|
if (_ma_dispose(info, next_page, 0))
|
|
goto err;
|
|
}
|
|
else
|
|
{ /* Page is full */
|
|
endpos=anc_buff+anc_length;
|
|
DBUG_PRINT("test",("anc_buff: 0x%lx endpos: 0x%lx",
|
|
(long) anc_buff, (long) endpos));
|
|
if (keypos != anc_buff + info->s->keypage_header + key_reflength &&
|
|
!_ma_get_last_key(info,keyinfo,anc_buff,anc_key,keypos,&length))
|
|
goto err;
|
|
if (!(half_pos= _ma_find_half_pos(info, nod_flag, keyinfo, buff,
|
|
leaf_key, &key_length, &after_key)))
|
|
goto err;
|
|
length=(uint) (half_pos-buff);
|
|
memcpy(leaf_buff,buff,(size_t) length);
|
|
_ma_store_page_used(info, leaf_buff, length, nod_flag);
|
|
|
|
/* Correct new keypointer to leaf_page */
|
|
half_pos=after_key;
|
|
_ma_kpointer(info,leaf_key+key_length,next_page);
|
|
/* Save key in anc_buff */
|
|
prev_key=(keypos == anc_buff + info->s->keypage_header + key_reflength ?
|
|
(uchar*) 0 : anc_key),
|
|
t_length=(*keyinfo->pack_key)(keyinfo,key_reflength,
|
|
(keypos == endpos ? (uchar*) 0 :
|
|
keypos),
|
|
prev_key, prev_key,
|
|
leaf_key, &s_temp);
|
|
if (t_length >= 0)
|
|
bmove_upp(endpos+t_length, endpos, (uint) (endpos-keypos));
|
|
else
|
|
bmove(keypos,keypos-t_length,(uint) (endpos-keypos)+t_length);
|
|
(*keyinfo->store_key)(keyinfo,keypos,&s_temp);
|
|
anc_length+= t_length;
|
|
_ma_store_page_used(info, anc_buff, anc_length, key_reflength);
|
|
|
|
/* Store key first in new page */
|
|
if (nod_flag)
|
|
bmove(buff+info->s->keypage_header, half_pos-nod_flag,
|
|
(size_t) nod_flag);
|
|
if (!(*keyinfo->get_key)(keyinfo,nod_flag,&half_pos,leaf_key))
|
|
goto err;
|
|
t_length=(int) (*keyinfo->pack_key)(keyinfo, nod_flag, (uchar*) 0,
|
|
(uchar*) 0, (uchar*) 0,
|
|
leaf_key, &s_temp);
|
|
/* t_length will always be > 0 for a new page !*/
|
|
length=(uint) ((buff+_ma_get_page_used(info, buff))-half_pos);
|
|
bmove(buff+p_length+t_length, half_pos, (size_t) length);
|
|
(*keyinfo->store_key)(keyinfo,buff+p_length,&s_temp);
|
|
_ma_store_page_used(info, buff, length + t_length + p_length, nod_flag);
|
|
|
|
next_page_link->changed= 1;
|
|
if (_ma_write_keypage(info, keyinfo, next_page,
|
|
PAGECACHE_LOCK_LEFT_WRITELOCKED, DFLT_INIT_HITS,
|
|
buff))
|
|
goto err;
|
|
}
|
|
leaf_page_link->changed= 1;
|
|
if (_ma_write_keypage(info, keyinfo, leaf_page,
|
|
PAGECACHE_LOCK_LEFT_WRITELOCKED, DFLT_INIT_HITS,
|
|
leaf_buff))
|
|
goto err;
|
|
DBUG_RETURN(anc_length <= ((info->quick_mode ? MARIA_MIN_BLOCK_LENGTH :
|
|
(uint) keyinfo->underflow_block_length)));
|
|
}
|
|
|
|
DBUG_PRINT("test",("use left page"));
|
|
|
|
keypos= _ma_get_last_key(info,keyinfo,anc_buff,anc_key,keypos,&length);
|
|
if (!keypos)
|
|
goto err;
|
|
next_page= _ma_kpos(key_reflength,keypos);
|
|
if (!_ma_fetch_keypage(info, keyinfo, next_page, PAGECACHE_LOCK_WRITE,
|
|
DFLT_INIT_HITS, buff, 0, &next_page_link))
|
|
goto err;
|
|
buff_length= _ma_get_page_used(info, buff);
|
|
endpos=buff+buff_length;
|
|
DBUG_DUMP("prev",buff,buff_length);
|
|
|
|
/* find keys to make a big key-page */
|
|
bmove(next_keypos - key_reflength, leaf_buff + info->s->keypage_header,
|
|
key_reflength);
|
|
next_keypos=keypos;
|
|
if (!(*keyinfo->get_key)(keyinfo,key_reflength,&next_keypos,
|
|
anc_key))
|
|
goto err;
|
|
if (!_ma_get_last_key(info,keyinfo,buff,leaf_key,endpos,&length))
|
|
goto err;
|
|
|
|
/* merge pages and put parting key from anc_buff between */
|
|
prev_key=(leaf_length == p_length ? (uchar*) 0 : leaf_key);
|
|
t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,
|
|
(leaf_length == p_length ?
|
|
(uchar*) 0 : leaf_buff+p_length),
|
|
prev_key, prev_key,
|
|
anc_key, &s_temp);
|
|
if (t_length >= 0)
|
|
bmove(endpos+t_length, leaf_buff+p_length,
|
|
(size_t) (leaf_length-p_length));
|
|
else /* We gained space */
|
|
bmove(endpos,leaf_buff+((int) p_length-t_length),
|
|
(size_t) (leaf_length-p_length+t_length));
|
|
|
|
(*keyinfo->store_key)(keyinfo,endpos,&s_temp);
|
|
buff_length=buff_length+leaf_length-p_length+t_length;
|
|
_ma_store_page_used(info, buff, buff_length, nod_flag);
|
|
|
|
/* remove key from anc_buff */
|
|
if (!(s_length= remove_key(keyinfo,key_reflength,keypos,anc_key,
|
|
anc_buff+anc_length,(my_off_t *) 0, &s_temp)))
|
|
goto err;
|
|
|
|
anc_length-=s_length;
|
|
_ma_store_page_used(info, anc_buff, anc_length, key_reflength);
|
|
|
|
if (buff_length <= (uint) (keyinfo->block_length - KEYPAGE_CHECKSUM_SIZE))
|
|
{ /* Keys in one page */
|
|
leaf_page_link->changed= 1;
|
|
if (_ma_dispose(info, leaf_page, 0))
|
|
goto err;
|
|
}
|
|
else
|
|
{ /* Page is full */
|
|
if (keypos == anc_buff+ info->s->keypage_header + key_reflength)
|
|
anc_pos=0; /* First key */
|
|
else if (!_ma_get_last_key(info,keyinfo,anc_buff,anc_pos=anc_key,keypos,
|
|
&length))
|
|
goto err;
|
|
endpos= _ma_find_half_pos(info, nod_flag, keyinfo, buff, leaf_key,
|
|
&key_length, &half_pos);
|
|
if (!endpos)
|
|
goto err;
|
|
_ma_kpointer(info,leaf_key+key_length,leaf_page);
|
|
/* Save key in anc_buff */
|
|
DBUG_DUMP("anc_buff",anc_buff,anc_length);
|
|
DBUG_DUMP("key_to_anc",leaf_key,key_length);
|
|
|
|
temp_pos=anc_buff+anc_length;
|
|
t_length=(*keyinfo->pack_key)(keyinfo,key_reflength,
|
|
keypos == temp_pos ? (uchar*) 0
|
|
: keypos,
|
|
anc_pos, anc_pos,
|
|
leaf_key,&s_temp);
|
|
if (t_length > 0)
|
|
bmove_upp(temp_pos+t_length, temp_pos, (uint) (temp_pos-keypos));
|
|
else
|
|
bmove(keypos,keypos-t_length,(uint) (temp_pos-keypos)+t_length);
|
|
(*keyinfo->store_key)(keyinfo,keypos,&s_temp);
|
|
anc_length+= t_length;
|
|
_ma_store_page_used(info, anc_buff, anc_length, key_reflength);
|
|
|
|
/* Store first key on new page */
|
|
if (nod_flag)
|
|
bmove(leaf_buff + info->s->keypage_header, half_pos-nod_flag,
|
|
(size_t) nod_flag);
|
|
if (!(length=(*keyinfo->get_key)(keyinfo,nod_flag,&half_pos,leaf_key)))
|
|
goto err;
|
|
DBUG_DUMP("key_to_leaf",leaf_key,length);
|
|
t_length=(*keyinfo->pack_key)(keyinfo,nod_flag, (uchar*) 0,
|
|
(uchar*) 0, (uchar*) 0, leaf_key, &s_temp);
|
|
length=(uint) ((buff+buff_length)-half_pos);
|
|
DBUG_PRINT("info",("t_length: %d length: %d",t_length,(int) length));
|
|
bmove(leaf_buff+p_length+t_length,half_pos,
|
|
(size_t) length);
|
|
(*keyinfo->store_key)(keyinfo,leaf_buff+p_length,&s_temp);
|
|
_ma_store_page_used(info, leaf_buff, length + t_length + p_length,
|
|
nod_flag);
|
|
leaf_page_link->changed= 1;
|
|
if (_ma_write_keypage(info, keyinfo, leaf_page,
|
|
PAGECACHE_LOCK_LEFT_WRITELOCKED, DFLT_INIT_HITS,
|
|
leaf_buff))
|
|
goto err;
|
|
_ma_store_page_used(info, buff, (uint) (endpos - buff),nod_flag);
|
|
}
|
|
next_page_link->changed= 1;
|
|
if (_ma_write_keypage(info, keyinfo, next_page,
|
|
PAGECACHE_LOCK_LEFT_WRITELOCKED, DFLT_INIT_HITS, buff))
|
|
goto err;
|
|
DBUG_RETURN(anc_length <= (uint)
|
|
(keyinfo->block_length - KEYPAGE_CHECKSUM_SIZE)/2);
|
|
|
|
err:
|
|
DBUG_RETURN(-1);
|
|
} /* underflow */
|
|
|
|
|
|
/*
|
|
@brief Remove a key from page
|
|
|
|
@fn remove_key()
|
|
keyinfo Key handle
|
|
keypos Where on page key starts
|
|
lastkey Unpacked version of key to be removed
|
|
page_end Pointer to end of page
|
|
next_block If <> 0 and node-page, this is set to address of
|
|
next page
|
|
s_temp Information about what changes was done one the page:
|
|
s_temp.key_pos Start of key
|
|
s_temp.move_length Number of bytes removed at keypos
|
|
s_temp.changed_length Number of bytes changed at keypos
|
|
|
|
@todo
|
|
The current code doesn't handle the case that the next key may be
|
|
packed better against the previous key if there is a case difference
|
|
|
|
@return
|
|
@retval 0 error
|
|
@retval # How many chars was removed
|
|
*/
|
|
|
|
static uint remove_key(MARIA_KEYDEF *keyinfo, uint nod_flag,
|
|
uchar *keypos, uchar *lastkey,
|
|
uchar *page_end, my_off_t *next_block,
|
|
MARIA_KEY_PARAM *s_temp)
|
|
{
|
|
int s_length;
|
|
uchar *start;
|
|
DBUG_ENTER("remove_key");
|
|
DBUG_PRINT("enter", ("keypos: 0x%lx page_end: 0x%lx",
|
|
(long) keypos, (long) page_end));
|
|
|
|
start= s_temp->key_pos= keypos;
|
|
s_temp->changed_length= 0;
|
|
if (!(keyinfo->flag &
|
|
(HA_PACK_KEY | HA_SPACE_PACK_USED | HA_VAR_LENGTH_KEY |
|
|
HA_BINARY_PACK_KEY)))
|
|
{
|
|
s_length=(int) (keyinfo->keylength+nod_flag);
|
|
if (next_block && nod_flag)
|
|
*next_block= _ma_kpos(nod_flag,keypos+s_length);
|
|
}
|
|
else
|
|
{ /* Let keypos point at next key */
|
|
/* Calculate length of key */
|
|
if (!(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,lastkey))
|
|
DBUG_RETURN(0); /* Error */
|
|
|
|
if (next_block && nod_flag)
|
|
*next_block= _ma_kpos(nod_flag,keypos);
|
|
s_length=(int) (keypos-start);
|
|
if (keypos != page_end)
|
|
{
|
|
if (keyinfo->flag & HA_BINARY_PACK_KEY)
|
|
{
|
|
uchar *old_key= start;
|
|
uint next_length,prev_length,prev_pack_length;
|
|
|
|
/* keypos points here on start of next key */
|
|
get_key_length(next_length,keypos);
|
|
get_key_pack_length(prev_length,prev_pack_length,old_key);
|
|
if (next_length > prev_length)
|
|
{
|
|
uint diff= (next_length-prev_length);
|
|
/* We have to copy data from the current key to the next key */
|
|
keypos-= diff + prev_pack_length;
|
|
store_key_length(keypos, prev_length);
|
|
bmove(keypos + prev_pack_length, lastkey + prev_length, diff);
|
|
s_length=(int) (keypos-start);
|
|
s_temp->changed_length= diff + prev_pack_length;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Check if a variable length first key part */
|
|
if ((keyinfo->seg->flag & HA_PACK_KEY) && *keypos & 128)
|
|
{
|
|
/* Next key is packed against the current one */
|
|
uint next_length,prev_length,prev_pack_length,lastkey_length,
|
|
rest_length;
|
|
if (keyinfo->seg[0].length >= 127)
|
|
{
|
|
if (!(prev_length=mi_uint2korr(start) & 32767))
|
|
goto end;
|
|
next_length=mi_uint2korr(keypos) & 32767;
|
|
keypos+=2;
|
|
prev_pack_length=2;
|
|
}
|
|
else
|
|
{
|
|
if (!(prev_length= *start & 127))
|
|
goto end; /* Same key as previous*/
|
|
next_length= *keypos & 127;
|
|
keypos++;
|
|
prev_pack_length=1;
|
|
}
|
|
if (!(*start & 128))
|
|
prev_length=0; /* prev key not packed */
|
|
if (keyinfo->seg[0].flag & HA_NULL_PART)
|
|
lastkey++; /* Skip null marker */
|
|
get_key_length(lastkey_length,lastkey);
|
|
if (!next_length) /* Same key after */
|
|
{
|
|
next_length=lastkey_length;
|
|
rest_length=0;
|
|
}
|
|
else
|
|
get_key_length(rest_length,keypos);
|
|
|
|
if (next_length >= prev_length)
|
|
{
|
|
/* Next key is based on deleted key */
|
|
uint pack_length;
|
|
uint diff= (next_length-prev_length);
|
|
|
|
bmove(keypos - diff, lastkey + prev_length, diff);
|
|
rest_length+= diff;
|
|
pack_length= prev_length ? get_pack_length(rest_length): 0;
|
|
keypos-= diff + pack_length + prev_pack_length;
|
|
s_length=(int) (keypos-start);
|
|
if (prev_length) /* Pack against prev key */
|
|
{
|
|
*keypos++= start[0];
|
|
if (prev_pack_length == 2)
|
|
*keypos++= start[1];
|
|
store_key_length(keypos,rest_length);
|
|
}
|
|
else
|
|
{
|
|
/* Next key is not packed anymore */
|
|
if (keyinfo->seg[0].flag & HA_NULL_PART)
|
|
{
|
|
rest_length++; /* Mark not null */
|
|
}
|
|
if (prev_pack_length == 2)
|
|
{
|
|
mi_int2store(keypos,rest_length);
|
|
}
|
|
else
|
|
*keypos= rest_length;
|
|
}
|
|
s_temp->changed_length= diff + pack_length + prev_pack_length;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
end:
|
|
bmove(start, start+s_length, (uint) (page_end-start-s_length));
|
|
s_temp->move_length= s_length;
|
|
DBUG_RETURN((uint) s_length);
|
|
} /* remove_key */
|
|
|
|
|
|
/****************************************************************************
|
|
Logging of redos
|
|
****************************************************************************/
|
|
|
|
/*
|
|
@brief log entry where some parts are deleted and some things are changed
|
|
*/
|
|
|
|
static my_bool _ma_log_delete(MARIA_HA *info, my_off_t page, uchar *buff,
|
|
uchar *key_pos, uint move_length,
|
|
uint change_length)
|
|
{
|
|
LSN lsn;
|
|
uchar log_data[FILEID_STORE_SIZE + PAGE_STORE_SIZE + 9], *log_pos;
|
|
LEX_STRING log_array[TRANSLOG_INTERNAL_PARTS + 2];
|
|
uint translog_parts;
|
|
uint offset= (uint) (key_pos - buff);
|
|
DBUG_ENTER("_ma_log_delete");
|
|
DBUG_ASSERT(info->s->now_transactional && move_length);
|
|
|
|
/* Store address of new root page */
|
|
page/= info->s->block_size;
|
|
page_store(log_data + FILEID_STORE_SIZE, page);
|
|
log_pos= log_data+ FILEID_STORE_SIZE + PAGE_STORE_SIZE;
|
|
log_pos[0]= KEY_OP_OFFSET;
|
|
int2store(log_pos+1, offset);
|
|
log_pos[3]= KEY_OP_SHIFT;
|
|
int2store(log_pos+4, -(int) move_length);
|
|
log_pos+= 6;
|
|
translog_parts= 1;
|
|
if (change_length)
|
|
{
|
|
log_pos[0]= KEY_OP_CHANGE;
|
|
int2store(log_pos+1, change_length);
|
|
log_pos+= 3;
|
|
translog_parts= 2;
|
|
log_array[TRANSLOG_INTERNAL_PARTS + 1].str= buff + offset;
|
|
log_array[TRANSLOG_INTERNAL_PARTS + 1].length= change_length;
|
|
}
|
|
|
|
log_array[TRANSLOG_INTERNAL_PARTS + 0].str= (char*) log_data;
|
|
log_array[TRANSLOG_INTERNAL_PARTS + 0].length= (uint) (log_pos - log_data);
|
|
|
|
if (translog_write_record(&lsn, LOGREC_REDO_INDEX,
|
|
info->trn, info,
|
|
log_array[TRANSLOG_INTERNAL_PARTS + 0].length +
|
|
change_length,
|
|
TRANSLOG_INTERNAL_PARTS + translog_parts,
|
|
log_array, log_data, NULL))
|
|
DBUG_RETURN(1);
|
|
DBUG_RETURN(0);
|
|
}
|