| 1 | /* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. |
| 2 | |
| 3 | This program is free software; you can redistribute it and/or modify |
| 4 | it under the terms of the GNU General Public License as published by |
| 5 | the Free Software Foundation; version 2 of the License. |
| 6 | |
| 7 | This program is distributed in the hope that it will be useful, |
| 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | GNU General Public License for more details. |
| 11 | |
| 12 | You should have received a copy of the GNU General Public License |
| 13 | along with this program; if not, write to the Free Software |
| 14 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ |
| 15 | |
| 16 | /* remove current record in heap-database */ |
| 17 | |
| 18 | #include "heapdef.h" |
| 19 | |
| 20 | int heap_delete(HP_INFO *info, const uchar *record) |
| 21 | { |
| 22 | uchar *pos; |
| 23 | HP_SHARE *share=info->s; |
| 24 | HP_KEYDEF *keydef, *end, *p_lastinx; |
| 25 | DBUG_ENTER("heap_delete" ); |
| 26 | DBUG_PRINT("enter" ,("info: %p record: %p" , info, record)); |
| 27 | |
| 28 | test_active(info); |
| 29 | |
| 30 | if (info->opt_flag & READ_CHECK_USED && hp_rectest(info,record)) |
| 31 | DBUG_RETURN(my_errno); /* Record changed */ |
| 32 | share->changed=1; |
| 33 | |
| 34 | if ( --(share->records) < share->blength >> 1) share->blength>>=1; |
| 35 | pos=info->current_ptr; |
| 36 | |
| 37 | p_lastinx = share->keydef + info->lastinx; |
| 38 | for (keydef = share->keydef, end = keydef + share->keys; keydef < end; |
| 39 | keydef++) |
| 40 | { |
| 41 | if ((*keydef->delete_key)(info, keydef, record, pos, keydef == p_lastinx)) |
| 42 | goto err; |
| 43 | } |
| 44 | |
| 45 | info->update=HA_STATE_DELETED; |
| 46 | *((uchar**) pos)=share->del_link; |
| 47 | share->del_link=pos; |
| 48 | pos[share->visible]=0; /* Record deleted */ |
| 49 | share->deleted++; |
| 50 | share->key_version++; |
| 51 | #if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG) |
| 52 | DBUG_EXECUTE("check_heap" ,heap_check_heap(info, 0);); |
| 53 | #endif |
| 54 | |
| 55 | DBUG_RETURN(0); |
| 56 | err: |
| 57 | if (++(share->records) == share->blength) |
| 58 | share->blength+= share->blength; |
| 59 | DBUG_RETURN(my_errno); |
| 60 | } |
| 61 | |
| 62 | |
| 63 | /* |
| 64 | Remove one key from rb-tree |
| 65 | */ |
| 66 | |
| 67 | int hp_rb_delete_key(HP_INFO *info, register HP_KEYDEF *keyinfo, |
| 68 | const uchar *record, uchar *recpos, int flag) |
| 69 | { |
| 70 | heap_rb_param custom_arg; |
| 71 | size_t old_allocated; |
| 72 | int res; |
| 73 | |
| 74 | if (flag) |
| 75 | info->last_pos= NULL; /* For heap_rnext/heap_rprev */ |
| 76 | |
| 77 | custom_arg.keyseg= keyinfo->seg; |
| 78 | custom_arg.key_length= hp_rb_make_key(keyinfo, info->recbuf, record, recpos); |
| 79 | custom_arg.search_flag= SEARCH_SAME; |
| 80 | old_allocated= keyinfo->rb_tree.allocated; |
| 81 | res= tree_delete(&keyinfo->rb_tree, info->recbuf, custom_arg.key_length, |
| 82 | &custom_arg); |
| 83 | info->s->index_length-= (old_allocated - keyinfo->rb_tree.allocated); |
| 84 | return res; |
| 85 | } |
| 86 | |
| 87 | |
| 88 | /* |
| 89 | Remove one key from hash-table |
| 90 | |
| 91 | SYNPOSIS |
| 92 | hp_delete_key() |
| 93 | info Hash handler |
| 94 | keyinfo key definition of key that we want to delete |
| 95 | record row data to be deleted |
| 96 | recpos Pointer to heap record in memory |
| 97 | flag Is set if we want's to correct info->current_ptr |
| 98 | |
| 99 | RETURN |
| 100 | 0 Ok |
| 101 | other Error code |
| 102 | */ |
| 103 | |
| 104 | int hp_delete_key(HP_INFO *info, register HP_KEYDEF *keyinfo, |
| 105 | const uchar *record, uchar *recpos, int flag) |
| 106 | { |
| 107 | ulong blength, pos2, pos_hashnr, lastpos_hashnr, key_pos; |
| 108 | HASH_INFO *lastpos,*gpos,*pos,*pos3,*empty,*last_ptr; |
| 109 | HP_SHARE *share=info->s; |
| 110 | DBUG_ENTER("hp_delete_key" ); |
| 111 | |
| 112 | blength=share->blength; |
| 113 | if (share->records+1 == blength) |
| 114 | blength+= blength; |
| 115 | lastpos=hp_find_hash(&keyinfo->block,share->records); |
| 116 | last_ptr=0; |
| 117 | |
| 118 | /* Search after record with key */ |
| 119 | key_pos= hp_mask(hp_rec_hashnr(keyinfo, record), blength, share->records + 1); |
| 120 | pos= hp_find_hash(&keyinfo->block, key_pos); |
| 121 | |
| 122 | gpos = pos3 = 0; |
| 123 | |
| 124 | while (pos->ptr_to_rec != recpos) |
| 125 | { |
| 126 | if (flag && !hp_rec_key_cmp(keyinfo, record, pos->ptr_to_rec)) |
| 127 | last_ptr=pos; /* Previous same key */ |
| 128 | gpos=pos; |
| 129 | if (!(pos=pos->next_key)) |
| 130 | { |
| 131 | DBUG_RETURN(my_errno=HA_ERR_CRASHED); /* This shouldn't happend */ |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | /* Remove link to record */ |
| 136 | |
| 137 | if (flag) |
| 138 | { |
| 139 | /* Save for heap_rnext/heap_rprev */ |
| 140 | info->current_hash_ptr=last_ptr; |
| 141 | info->current_ptr = last_ptr ? last_ptr->ptr_to_rec : 0; |
| 142 | DBUG_PRINT("info" ,("Corrected current_ptr to point at: %p" , |
| 143 | info->current_ptr)); |
| 144 | } |
| 145 | empty=pos; |
| 146 | if (gpos) |
| 147 | gpos->next_key=pos->next_key; /* unlink current ptr */ |
| 148 | else if (pos->next_key) |
| 149 | { |
| 150 | empty=pos->next_key; |
| 151 | pos->ptr_to_rec= empty->ptr_to_rec; |
| 152 | pos->next_key= empty->next_key; |
| 153 | pos->hash_of_key= empty->hash_of_key; |
| 154 | } |
| 155 | else |
| 156 | keyinfo->hash_buckets--; |
| 157 | |
| 158 | if (empty == lastpos) /* deleted last hash key */ |
| 159 | DBUG_RETURN (0); |
| 160 | |
| 161 | /* Move the last key (lastpos) */ |
| 162 | lastpos_hashnr= lastpos->hash_of_key; |
| 163 | /* pos is where lastpos should be */ |
| 164 | pos=hp_find_hash(&keyinfo->block, hp_mask(lastpos_hashnr, share->blength, |
| 165 | share->records)); |
| 166 | if (pos == empty) /* Move to empty position. */ |
| 167 | { |
| 168 | empty[0]=lastpos[0]; |
| 169 | DBUG_RETURN(0); |
| 170 | } |
| 171 | pos_hashnr= pos->hash_of_key; |
| 172 | /* pos3 is where the pos should be */ |
| 173 | pos3= hp_find_hash(&keyinfo->block, |
| 174 | hp_mask(pos_hashnr, share->blength, share->records)); |
| 175 | if (pos != pos3) |
| 176 | { /* pos is on wrong posit */ |
| 177 | empty[0]=pos[0]; /* Save it here */ |
| 178 | pos[0]=lastpos[0]; /* This shold be here */ |
| 179 | hp_movelink(pos, pos3, empty); /* Fix link to pos */ |
| 180 | DBUG_RETURN(0); |
| 181 | } |
| 182 | pos2= hp_mask(lastpos_hashnr, blength, share->records + 1); |
| 183 | if (pos2 == hp_mask(pos_hashnr, blength, share->records + 1)) |
| 184 | { |
| 185 | /* lastpos and the row in the main bucket entry (pos) has the same hash */ |
| 186 | if (pos2 != share->records) |
| 187 | { |
| 188 | /* |
| 189 | The bucket entry was not deleted. Copy lastpos over the |
| 190 | deleted entry and update previous link to point to it. |
| 191 | */ |
| 192 | empty[0]= lastpos[0]; |
| 193 | hp_movelink(lastpos, pos, empty); |
| 194 | if (last_ptr == lastpos) |
| 195 | { |
| 196 | /* |
| 197 | We moved the row that info->current_hash_ptr points to. |
| 198 | Update info->current_hash_ptr to point to the new position. |
| 199 | */ |
| 200 | info->current_hash_ptr= empty; |
| 201 | } |
| 202 | DBUG_RETURN(0); |
| 203 | } |
| 204 | /* |
| 205 | Shrinking the hash table deleted the main bucket entry for this hash. |
| 206 | In this case the last entry was the first key in the key chain. |
| 207 | We move things around so that we keep the original key order to ensure |
| 208 | that heap_rnext() works. |
| 209 | |
| 210 | - Move the row at the main bucket entry to the empty spot. |
| 211 | - Move the last entry first in the new chain. |
| 212 | - Link in the first element of the hash. |
| 213 | */ |
| 214 | empty[0]= pos[0]; |
| 215 | pos[0]= lastpos[0]; |
| 216 | hp_movelink(pos, pos, empty); |
| 217 | |
| 218 | /* Update current_hash_ptr if the entry moved */ |
| 219 | if (last_ptr == lastpos) |
| 220 | info->current_hash_ptr= pos; |
| 221 | else if (last_ptr == pos) |
| 222 | info->current_hash_ptr= empty; |
| 223 | DBUG_RETURN(0); |
| 224 | } |
| 225 | |
| 226 | pos3= 0; /* Different positions merge */ |
| 227 | keyinfo->hash_buckets--; |
| 228 | empty[0]=lastpos[0]; |
| 229 | hp_movelink(pos3, empty, pos->next_key); |
| 230 | pos->next_key=empty; |
| 231 | DBUG_RETURN(0); |
| 232 | } |
| 233 | |