1 /* Copyright (c) 2000, 2002-2007 MySQL AB
2    Use is subject to license terms
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; version 2 of the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA */
16 
17 /* Check that heap-structure is ok */
18 
19 #include "heapdef.h"
20 
21 static int check_one_key(HP_KEYDEF *, uint, ulong, ulong, my_bool);
22 static int check_one_rb_key(const HP_INFO *, uint, ulong, my_bool);
23 
24 
25 /*
26   Check if keys and rows are ok in a heap table
27 
28   SYNOPSIS
29     heap_check_heap()
30     info		Table handler
31     print_status	Prints some extra status
32 
33   NOTES
34     Doesn't change the state of the table handler
35 
36   RETURN VALUES
37     0	ok
38     1 error
39 */
40 
heap_check_heap(const HP_INFO * info,my_bool print_status)41 int heap_check_heap(const HP_INFO *info, my_bool print_status)
42 {
43   int error;
44   uint key;
45   ulong records=0, deleted=0, pos, next_block;
46   HP_SHARE *share=info->s;
47   uchar *current_ptr= info->current_ptr;
48   DBUG_ENTER("heap_check_heap");
49 
50   for (error=key= 0 ; key < share->keys ; key++)
51   {
52     if (share->keydef[key].algorithm == HA_KEY_ALG_BTREE)
53       error|= check_one_rb_key(info, key, share->records, print_status);
54     else
55       error|= check_one_key(share->keydef + key, key, share->records,
56 			    share->blength, print_status);
57   }
58   /*
59     This is basicly the same code as in hp_scan, but we repeat it here to
60     get shorter DBUG log file.
61   */
62   for (pos=next_block= 0 ; ; pos++)
63   {
64     if (pos < next_block)
65     {
66       current_ptr+= share->block.recbuffer;
67     }
68     else
69     {
70       next_block+= share->block.records_in_block;
71       if (next_block >= share->records+share->deleted)
72       {
73 	next_block= share->records+share->deleted;
74 	if (pos >= next_block)
75 	  break;				/* End of file */
76       }
77     }
78     current_ptr= hp_find_block(&share->block, pos);
79 
80     if (!current_ptr[share->visible])
81       deleted++;
82     else
83       records++;
84   }
85 
86   if (records != share->records || deleted != share->deleted)
87   {
88     DBUG_PRINT("error",("Found rows: %lu (%lu)  deleted %lu (%lu)",
89 			records, (ulong) share->records,
90                         deleted, (ulong) share->deleted));
91     error= 1;
92   }
93   DBUG_RETURN(error);
94 }
95 
96 
check_one_key(HP_KEYDEF * keydef,uint keynr,ulong records,ulong blength,my_bool print_status)97 static int check_one_key(HP_KEYDEF *keydef, uint keynr, ulong records,
98 			 ulong blength, my_bool print_status)
99 {
100   int error;
101   ulong i,found,max_links,seek,links;
102   ulong rec_link;				/* Only used with debugging */
103   ulong hash_buckets_found;
104   HASH_INFO *hash_info;
105 
106   error=0;
107   hash_buckets_found= 0;
108   for (i=found=max_links=seek=0 ; i < records ; i++)
109   {
110     hash_info=hp_find_hash(&keydef->block,i);
111     if (hash_info->hash_of_key != hp_rec_hashnr(keydef, hash_info->ptr_to_rec))
112     {
113       DBUG_PRINT("error",
114                  ("Found row with wrong hash_of_key at position %lu", i));
115       error= 1;
116     }
117     if (hp_mask(hash_info->hash_of_key, blength, records) == i)
118     {
119       found++;
120       seek++;
121       links=1;
122       while ((hash_info=hash_info->next_key) && found < records + 1)
123       {
124 	seek+= ++links;
125 	if ((rec_link= hp_mask(hash_info->hash_of_key, blength, records)) != i)
126 	{
127 	  DBUG_PRINT("error",
128                      ("Record in wrong link: Link %lu  Record: %p  Record-link %lu",
129                       i, hash_info->ptr_to_rec, rec_link));
130 	  error=1;
131 	}
132 	else
133 	  found++;
134       }
135       if (links > max_links) max_links=links;
136       hash_buckets_found++;
137     }
138   }
139   if (found != records)
140   {
141     DBUG_PRINT("error",("Found %ld of %ld records", found, records));
142     error=1;
143   }
144   if (keydef->hash_buckets != hash_buckets_found)
145   {
146     DBUG_PRINT("error",("Found %ld buckets, stats shows %ld buckets",
147                         hash_buckets_found, (long) keydef->hash_buckets));
148     error=1;
149   }
150   DBUG_PRINT("info",
151 	     ("key: %u  records: %ld  seeks: %lu  max links: %lu  "
152               "hitrate: %.2f  buckets: %lu",
153 	      keynr, records,seek,max_links,
154 	      (float) seek / (float) (records ? records : 1),
155               hash_buckets_found));
156   if (print_status)
157     printf("Key: %u  records: %ld  seeks: %lu  max links: %lu  "
158            "hitrate: %.2f  buckets: %lu\n",
159 	   keynr, records, seek, max_links,
160 	   (float) seek / (float) (records ? records : 1),
161            hash_buckets_found);
162   return error;
163 }
164 
check_one_rb_key(const HP_INFO * info,uint keynr,ulong records,my_bool print_status)165 static int check_one_rb_key(const HP_INFO *info, uint keynr, ulong records,
166 			    my_bool print_status)
167 {
168   HP_KEYDEF *keydef= info->s->keydef + keynr;
169   int error= 0;
170   ulong found= 0;
171   uchar *key, *recpos;
172   uint key_length;
173   uint not_used[2];
174   TREE_ELEMENT **last_pos;
175   TREE_ELEMENT *parents[MAX_TREE_HEIGHT+1];
176 
177   if ((key= tree_search_edge(&keydef->rb_tree, parents,
178 			     &last_pos, offsetof(TREE_ELEMENT, left))))
179   {
180     do
181     {
182       memcpy(&recpos, key + (*keydef->get_key_length)(keydef,key), sizeof(uchar*));
183       key_length= hp_rb_make_key(keydef, info->recbuf, recpos, 0);
184       if (ha_key_cmp(keydef->seg, (uchar*) info->recbuf, (uchar*) key,
185 		     key_length, SEARCH_FIND | SEARCH_SAME, not_used))
186       {
187 	error= 1;
188 	DBUG_PRINT("error",("Record in wrong link:  key: %u  Record: %p\n",
189 			    keynr, recpos));
190       }
191       else
192 	found++;
193       key= tree_search_next(&keydef->rb_tree, &last_pos,
194 			    offsetof(TREE_ELEMENT, left),
195 			    offsetof(TREE_ELEMENT, right));
196     } while (key);
197   }
198   if (found != records)
199   {
200     DBUG_PRINT("error",("Found %lu of %lu records", found, records));
201     error= 1;
202   }
203   if (print_status)
204     printf("Key: %d  records: %ld\n", keynr, records);
205   return error;
206 }
207