1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3 #ident "$Id$"
4 /*======
5 This file is part of TokuDB
6 
7 
8 Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9 
10     TokuDBis is free software: you can redistribute it and/or modify
11     it under the terms of the GNU General Public License, version 2,
12     as published by the Free Software Foundation.
13 
14     TokuDB 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 for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with TokuDB.  If not, see <http://www.gnu.org/licenses/>.
21 
22 ======= */
23 
24 #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
25 
26 namespace tokudb {
compute_total_key_parts(TABLE_SHARE * table_share)27     uint compute_total_key_parts(TABLE_SHARE *table_share) {
28         uint total_key_parts = 0;
29         for (uint i = 0; i < table_share->keys; i++) {
30             total_key_parts += table_share->key_info[i].user_defined_key_parts;
31         }
32         return total_key_parts;
33     }
34 
35     // Put the cardinality counters into the status dictionary.
set_card_in_status(DB * status_db,DB_TXN * txn,uint rec_per_keys,const uint64_t rec_per_key[])36     int set_card_in_status(
37         DB* status_db,
38         DB_TXN* txn,
39         uint rec_per_keys,
40         const uint64_t rec_per_key[]) {
41 
42         // encode cardinality into the buffer
43         tokudb::buffer b;
44         size_t s;
45         s = b.append_ui<uint32_t>(rec_per_keys);
46         assert_always(s > 0);
47         for (uint i = 0; i < rec_per_keys; i++) {
48             s = b.append_ui<uint64_t>(rec_per_key[i]);
49             assert_always(s > 0);
50         }
51         // write cardinality to status
52         int error =
53             tokudb::metadata::write(
54                 status_db,
55                 hatoku_cardinality,
56                 b.data(),
57                 b.size(),
58                 txn);
59         return error;
60     }
61 
62     // Get the cardinality counters from the status dictionary.
get_card_from_status(DB * status_db,DB_TXN * txn,uint rec_per_keys,uint64_t rec_per_key[])63     int get_card_from_status(
64         DB* status_db,
65         DB_TXN* txn,
66         uint rec_per_keys,
67         uint64_t rec_per_key[]) {
68 
69         // read cardinality from status
70         void* buf = 0; size_t buf_size = 0;
71         int error =
72             tokudb::metadata::read_realloc(
73                 status_db,
74                 txn,
75                 hatoku_cardinality,
76                 &buf,
77                 &buf_size);
78         if (error == 0) {
79             // decode cardinality from the buffer
80             tokudb::buffer b(buf, 0, buf_size);
81             size_t s;
82             uint32_t num_parts;
83             s = b.consume_ui<uint32_t>(&num_parts);
84             if (s == 0 || num_parts != rec_per_keys)
85                 error = EINVAL;
86             if (error == 0) {
87                 for (uint i = 0; i < rec_per_keys; i++) {
88                     s = b.consume_ui<uint64_t>(&rec_per_key[i]);
89                     if (s == 0) {
90                         error = EINVAL;
91                         break;
92                     }
93                 }
94             }
95         }
96         // cleanup
97         free(buf);
98         return error;
99     }
100 
101     // Delete the cardinality counters from the status dictionary.
delete_card_from_status(DB * status_db,DB_TXN * txn)102     int delete_card_from_status(DB* status_db, DB_TXN* txn) {
103         int error =
104             tokudb::metadata::remove(status_db, hatoku_cardinality, txn);
105         return error;
106     }
107 
find_index_of_key(const char * key_name,TABLE_SHARE * table_share,uint * index_offset_ptr)108     bool find_index_of_key(
109         const char* key_name,
110         TABLE_SHARE* table_share,
111         uint* index_offset_ptr) {
112 
113         for (uint i = 0; i < table_share->keys; i++) {
114             if (strcmp(key_name, table_share->key_info[i].name.str) == 0) {
115                 *index_offset_ptr = i;
116                 return true;
117             }
118         }
119         return false;
120     }
121 
copy_card(uint64_t * dest,uint64_t * src,size_t n)122     static void copy_card(uint64_t *dest, uint64_t *src, size_t n) {
123         for (size_t i = 0; i < n; i++)
124             dest[i] = src[i];
125     }
126 
127     // Altered table cardinality = select cardinality data from current table
128     // cardinality for keys that exist
129     // in the altered table and the current table.
alter_card(DB * status_db,DB_TXN * txn,TABLE_SHARE * table_share,TABLE_SHARE * altered_table_share)130     int alter_card(
131         DB* status_db,
132         DB_TXN *txn,
133         TABLE_SHARE* table_share,
134         TABLE_SHARE* altered_table_share) {
135 
136         int error;
137         // read existing cardinality data from status
138         uint table_total_key_parts =
139             tokudb::compute_total_key_parts(table_share);
140 
141         uint64_t rec_per_key[table_total_key_parts];
142         error =
143             get_card_from_status(
144                 status_db,
145                 txn,
146                 table_total_key_parts,
147                 rec_per_key);
148         // set altered records per key to unknown
149         uint altered_table_total_key_parts =
150             tokudb::compute_total_key_parts(altered_table_share);
151         uint64_t altered_rec_per_key[altered_table_total_key_parts];
152         for (uint i = 0; i < altered_table_total_key_parts; i++)
153             altered_rec_per_key[i] = 0;
154         // compute the beginning of the key offsets in the original table
155         uint orig_key_offset[table_share->keys];
156         uint orig_key_parts = 0;
157         for (uint i = 0; i < table_share->keys; i++) {
158             orig_key_offset[i] = orig_key_parts;
159             orig_key_parts += table_share->key_info[i].user_defined_key_parts;
160         }
161         // if orig card data exists, then use it to compute new card data
162         if (error == 0) {
163             uint next_key_parts = 0;
164             for (uint i = 0; error == 0 && i < altered_table_share->keys; i++) {
165                 uint ith_key_parts =
166                     altered_table_share->key_info[i].user_defined_key_parts;
167                 uint orig_key_index;
168                 if (find_index_of_key(
169                         altered_table_share->key_info[i].name.str,
170                         table_share,
171                         &orig_key_index)) {
172                     copy_card(
173                         &altered_rec_per_key[next_key_parts],
174                         &rec_per_key[orig_key_offset[orig_key_index]],
175                         ith_key_parts);
176                 }
177                 next_key_parts += ith_key_parts;
178             }
179         }
180         if (error == 0) {
181             error =
182                 set_card_in_status(
183                     status_db,
184                     txn,
185                     altered_table_total_key_parts,
186                     altered_rec_per_key);
187         } else {
188             error = delete_card_from_status(status_db, txn);
189         }
190         return error;
191     }
192 }
193