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
20int 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);
56err:
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
67int 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
104int 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