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-1335  USA */
15 
16 /* remove current record in heap-database */
17 
18 #include "heapdef.h"
19 
heap_delete(HP_INFO * info,const uchar * record)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 
hp_rb_delete_key(HP_INFO * info,register HP_KEYDEF * keyinfo,const uchar * record,uchar * recpos,int flag)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 
hp_delete_key(HP_INFO * info,register HP_KEYDEF * keyinfo,const uchar * record,uchar * recpos,int flag)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