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