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 | |