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