1 /* Copyright (c) 2000, 2019, 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 St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 /* remove current record in heap-database */
24 
25 #include <stddef.h>
26 #include <sys/types.h>
27 
28 #include "my_dbug.h"
29 #include "my_inttypes.h"
30 #include "storage/heap/heapdef.h"
31 
heap_delete(HP_INFO * info,const uchar * record)32 int heap_delete(HP_INFO *info, const uchar *record) {
33   uchar *pos;
34   HP_SHARE *share = info->s;
35   HP_KEYDEF *keydef, *end, *p_lastinx;
36   DBUG_TRACE;
37   DBUG_PRINT("enter", ("info: %p  record: %p", info, record));
38 
39   test_active(info);
40 
41   if (info->opt_flag & READ_CHECK_USED && hp_rectest(info, record))
42     return my_errno(); /* Record changed */
43   share->changed = 1;
44 
45   if (--(share->records) < share->blength >> 1) share->blength >>= 1;
46   pos = info->current_ptr;
47 
48   p_lastinx = share->keydef + info->lastinx;
49   for (keydef = share->keydef, end = keydef + share->keys; keydef < end;
50        keydef++) {
51     if ((*keydef->delete_key)(info, keydef, record, pos, keydef == p_lastinx))
52       goto err;
53   }
54 
55   info->update = HA_STATE_DELETED;
56   *((uchar **)pos) = share->del_link;
57   share->del_link = pos;
58   pos[share->reclength] = 0; /* Record deleted */
59   share->deleted++;
60   info->current_hash_ptr = nullptr;
61 #if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG)
62   DBUG_EXECUTE("check_heap", heap_check_heap(info, 0););
63 #endif
64 
65   return 0;
66 err:
67   if (++(share->records) == share->blength) share->blength += share->blength;
68   return my_errno();
69 }
70 
71 /*
72   Remove one key from rb-tree
73 */
74 
hp_rb_delete_key(HP_INFO * info,HP_KEYDEF * keyinfo,const uchar * record,uchar * recpos,int flag)75 int hp_rb_delete_key(HP_INFO *info, HP_KEYDEF *keyinfo, const uchar *record,
76                      uchar *recpos, int flag) {
77   heap_rb_param custom_arg;
78   uint old_allocated;
79   int res;
80 
81   if (flag) info->last_pos = nullptr; /* For heap_rnext/heap_rprev */
82 
83   custom_arg.keyseg = keyinfo->seg;
84   custom_arg.key_length = hp_rb_make_key(keyinfo, info->recbuf, record, recpos);
85   custom_arg.search_flag = SEARCH_SAME;
86   old_allocated = keyinfo->rb_tree.allocated;
87   res = tree_delete(&keyinfo->rb_tree, info->recbuf, custom_arg.key_length,
88                     &custom_arg);
89   info->s->index_length -= (old_allocated - keyinfo->rb_tree.allocated);
90   return res;
91 }
92 
93 /*
94   Remove one key from hash-table
95 
96   SYNPOSIS
97     hp_delete_key()
98     info		Hash handler
99     keyinfo		key definition of key that we want to delete
100     record		row data to be deleted
101     recpos		Pointer to heap record in memory
102     flag		Is set if we want's to correct info->current_ptr
103 
104   RETURN
105     0      Ok
106     other  Error code
107 */
108 
hp_delete_key(HP_INFO * info,HP_KEYDEF * keyinfo,const uchar * record,uchar * recpos,int flag)109 int hp_delete_key(HP_INFO *info, HP_KEYDEF *keyinfo, const uchar *record,
110                   uchar *recpos, int flag) {
111   ulong blength, pos2, pos_hashnr, lastpos_hashnr, key_pos;
112   HASH_INFO *lastpos, *gpos, *pos, *pos3, *empty, *last_ptr;
113   HP_SHARE *share = info->s;
114   DBUG_TRACE;
115 
116   blength = share->blength;
117   if (share->records + 1 == blength) blength += blength;
118   lastpos = hp_find_hash(&keyinfo->block, share->records);
119   last_ptr = nullptr;
120 
121   /* Search after record with key */
122   key_pos =
123       hp_mask(hp_rec_hashnr(keyinfo, record), blength, share->records + 1);
124   pos = hp_find_hash(&keyinfo->block, key_pos);
125 
126   gpos = pos3 = nullptr;
127 
128   while (pos->ptr_to_rec != recpos) {
129     if (flag && !hp_rec_key_cmp(keyinfo, record, pos->ptr_to_rec))
130       last_ptr = pos; /* Previous same key */
131     gpos = pos;
132     if (!(pos = pos->next_key)) {
133       set_my_errno(HA_ERR_CRASHED);
134       return HA_ERR_CRASHED; /* This shouldn't happend */
135     }
136   }
137 
138   /* Remove link to record */
139 
140   if (flag) {
141     /* Save for heap_rnext/heap_rprev */
142     info->current_hash_ptr = last_ptr;
143     info->current_ptr = last_ptr ? last_ptr->ptr_to_rec : nullptr;
144     DBUG_PRINT("info",
145                ("Corrected current_ptr to point at: %p", info->current_ptr));
146   }
147   empty = pos;
148   if (gpos)
149     gpos->next_key = pos->next_key; /* unlink current ptr */
150   else if (pos->next_key) {
151     empty = pos->next_key;
152     *pos = *empty;
153   } else
154     keyinfo->hash_buckets--;
155 
156   if (empty == lastpos) /* deleted last hash key */
157     return 0;
158 
159   /* Move the last key (lastpos) */
160   lastpos_hashnr = lastpos->hash;
161   /* pos is where lastpos should be */
162   pos = hp_find_hash(&keyinfo->block,
163                      hp_mask(lastpos_hashnr, share->blength, share->records));
164   if (pos == empty) /* Move to empty position. */
165   {
166     empty[0] = lastpos[0];
167     return 0;
168   }
169   pos_hashnr = pos->hash;
170   /* pos3 is where the pos should be */
171   pos3 = hp_find_hash(&keyinfo->block,
172                       hp_mask(pos_hashnr, share->blength, share->records));
173   if (pos != pos3) {               /* pos is on wrong posit */
174     empty[0] = pos[0];             /* Save it here */
175     pos[0] = lastpos[0];           /* This shold be here */
176     hp_movelink(pos, pos3, empty); /* Fix link to pos */
177     return 0;
178   }
179   pos2 = hp_mask(lastpos_hashnr, blength, share->records + 1);
180   if (pos2 == hp_mask(pos_hashnr, blength,
181                       share->records + 1)) { /* Identical key-positions */
182     if (pos2 != share->records) {
183       empty[0] = lastpos[0];
184       hp_movelink(lastpos, pos, empty);
185       return 0;
186     }
187     pos3 = pos; /* Link pos->next after lastpos */
188     /*
189       One of elements from the bucket we're scanning is moved to the
190       beginning of the list. Reset search since this element may not have
191       been processed yet.
192     */
193     if (flag && pos2 == key_pos) {
194       info->current_ptr = nullptr;
195       info->current_hash_ptr = nullptr;
196     }
197   } else {
198     pos3 = nullptr; /* Different positions merge */
199     keyinfo->hash_buckets--;
200   }
201 
202   empty[0] = lastpos[0];
203   hp_movelink(pos3, empty, pos->next_key);
204   pos->next_key = empty;
205   return 0;
206 }
207