1 /*
2 Copyright (c) 2014, SkySQL Ab
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 02111-1301 USA */
16
17 #ifdef USE_PRAGMA_IMPLEMENTATION
18 #pragma implementation // gcc: Class implementation
19 #endif
20
21 /* For use of 'PRIu64': */
22 #define __STDC_FORMAT_MACROS
23
24 #include <my_global.h>
25
26 #include <inttypes.h>
27
28 /* This C++ files header file */
29 #include "./rdb_cf_manager.h"
30
31 /* MyRocks header files */
32 #include "./ha_rocksdb.h"
33 #include "./ha_rocksdb_proto.h"
34 #include "./rdb_datadic.h"
35 #include "./rdb_psi.h"
36
37 #include <string>
38
39 namespace myrocks {
40
41 /* Check if ColumnFamily name says it's a reverse-ordered CF */
is_cf_name_reverse(const char * const name)42 bool Rdb_cf_manager::is_cf_name_reverse(const char *const name) {
43 /* nullptr means the default CF is used.. (TODO: can the default CF be
44 * reverse?) */
45 return (name && !strncmp(name, "rev:", 4));
46 }
47
init(std::unique_ptr<Rdb_cf_options> && cf_options,std::vector<rocksdb::ColumnFamilyHandle * > * const handles)48 void Rdb_cf_manager::init(
49 std::unique_ptr<Rdb_cf_options> &&cf_options,
50 std::vector<rocksdb::ColumnFamilyHandle *> *const handles) {
51 mysql_mutex_init(rdb_cfm_mutex_key, &m_mutex, MY_MUTEX_INIT_FAST);
52
53 DBUG_ASSERT(cf_options != nullptr);
54 DBUG_ASSERT(handles != nullptr);
55 DBUG_ASSERT(handles->size() > 0);
56
57 m_cf_options = std::move(cf_options);
58
59 for (auto cfh : *handles) {
60 DBUG_ASSERT(cfh != nullptr);
61 m_cf_name_map[cfh->GetName()] = cfh;
62 m_cf_id_map[cfh->GetID()] = cfh;
63 }
64 }
65
cleanup()66 void Rdb_cf_manager::cleanup() {
67 for (auto it : m_cf_name_map) {
68 delete it.second;
69 }
70 mysql_mutex_destroy(&m_mutex);
71 m_cf_options = nullptr;
72 }
73
74 /*
75 @brief
76 Find column family by name. If it doesn't exist, create it
77
78 @detail
79 See Rdb_cf_manager::get_cf
80 */
get_or_create_cf(rocksdb::DB * const rdb,const std::string & cf_name_arg)81 rocksdb::ColumnFamilyHandle *Rdb_cf_manager::get_or_create_cf(
82 rocksdb::DB *const rdb, const std::string &cf_name_arg) {
83 DBUG_ASSERT(rdb != nullptr);
84
85 rocksdb::ColumnFamilyHandle *cf_handle = nullptr;
86
87 if (cf_name_arg == PER_INDEX_CF_NAME) {
88 // per-index column families is no longer supported.
89 my_error(ER_PER_INDEX_CF_DEPRECATED, MYF(0));
90 return nullptr;
91 }
92
93 const std::string &cf_name =
94 cf_name_arg.empty() ? DEFAULT_CF_NAME : cf_name_arg;
95
96 RDB_MUTEX_LOCK_CHECK(m_mutex);
97
98 const auto it = m_cf_name_map.find(cf_name);
99
100 if (it != m_cf_name_map.end()) {
101 cf_handle = it->second;
102 } else {
103 /* Create a Column Family. */
104 rocksdb::ColumnFamilyOptions opts;
105 m_cf_options->get_cf_options(cf_name, &opts);
106
107 // NO_LINT_DEBUG
108 sql_print_information("RocksDB: creating a column family %s",
109 cf_name.c_str());
110 // NO_LINT_DEBUG
111 sql_print_information(" write_buffer_size=%ld", opts.write_buffer_size);
112
113 // NO_LINT_DEBUG
114 sql_print_information(" target_file_size_base=%" PRIu64,
115 opts.target_file_size_base);
116
117 const rocksdb::Status s =
118 rdb->CreateColumnFamily(opts, cf_name, &cf_handle);
119
120 if (s.ok()) {
121 m_cf_name_map[cf_handle->GetName()] = cf_handle;
122 m_cf_id_map[cf_handle->GetID()] = cf_handle;
123 } else {
124 cf_handle = nullptr;
125 }
126 }
127
128 RDB_MUTEX_UNLOCK_CHECK(m_mutex);
129
130 return cf_handle;
131 }
132
133 /*
134 Find column family by its cf_name.
135 */
136
get_cf(const std::string & cf_name_arg,const bool lock_held_by_caller) const137 rocksdb::ColumnFamilyHandle *Rdb_cf_manager::get_cf(
138 const std::string &cf_name_arg, const bool lock_held_by_caller) const {
139 rocksdb::ColumnFamilyHandle *cf_handle;
140
141 if (!lock_held_by_caller) {
142 RDB_MUTEX_LOCK_CHECK(m_mutex);
143 }
144 std::string cf_name = cf_name_arg.empty() ? DEFAULT_CF_NAME : cf_name_arg;
145
146 const auto it = m_cf_name_map.find(cf_name);
147 cf_handle = (it != m_cf_name_map.end()) ? it->second : nullptr;
148
149 if (!cf_handle) {
150 // NO_LINT_DEBUG
151 sql_print_warning("Column family '%s' not found.", cf_name.c_str());
152 }
153
154 if (!lock_held_by_caller) {
155 RDB_MUTEX_UNLOCK_CHECK(m_mutex);
156 }
157
158 return cf_handle;
159 }
160
get_cf(const uint32_t id) const161 rocksdb::ColumnFamilyHandle *Rdb_cf_manager::get_cf(const uint32_t id) const {
162 rocksdb::ColumnFamilyHandle *cf_handle = nullptr;
163
164 RDB_MUTEX_LOCK_CHECK(m_mutex);
165 const auto it = m_cf_id_map.find(id);
166 if (it != m_cf_id_map.end()) cf_handle = it->second;
167 RDB_MUTEX_UNLOCK_CHECK(m_mutex);
168
169 return cf_handle;
170 }
171
get_cf_names(void) const172 std::vector<std::string> Rdb_cf_manager::get_cf_names(void) const {
173 std::vector<std::string> names;
174
175 RDB_MUTEX_LOCK_CHECK(m_mutex);
176 for (auto it : m_cf_name_map) {
177 names.push_back(it.first);
178 }
179 RDB_MUTEX_UNLOCK_CHECK(m_mutex);
180
181 return names;
182 }
183
get_all_cf(void) const184 std::vector<rocksdb::ColumnFamilyHandle *> Rdb_cf_manager::get_all_cf(
185 void) const {
186 std::vector<rocksdb::ColumnFamilyHandle *> list;
187
188 RDB_MUTEX_LOCK_CHECK(m_mutex);
189
190 for (auto it : m_cf_id_map) {
191 DBUG_ASSERT(it.second != nullptr);
192 list.push_back(it.second);
193 }
194
195 RDB_MUTEX_UNLOCK_CHECK(m_mutex);
196
197 return list;
198 }
199
200 struct Rdb_cf_scanner : public Rdb_tables_scanner {
201 uint32_t m_cf_id;
202 int m_is_cf_used;
203
Rdb_cf_scannermyrocks::Rdb_cf_scanner204 explicit Rdb_cf_scanner(uint32_t cf_id)
205 : m_cf_id(cf_id), m_is_cf_used(false) {}
206
add_tablemyrocks::Rdb_cf_scanner207 int add_table(Rdb_tbl_def *tdef) override {
208 DBUG_ASSERT(tdef != nullptr);
209
210 for (uint i = 0; i < tdef->m_key_count; i++) {
211 const Rdb_key_def &kd = *tdef->m_key_descr_arr[i];
212
213 if (kd.get_cf()->GetID() == m_cf_id) {
214 m_is_cf_used = true;
215 return HA_EXIT_SUCCESS;
216 }
217 }
218 return HA_EXIT_SUCCESS;
219 }
220 };
221
drop_cf(const std::string & cf_name)222 int Rdb_cf_manager::drop_cf(const std::string &cf_name) {
223 auto ddl_manager = rdb_get_ddl_manager();
224 uint32_t cf_id = 0;
225
226 if (cf_name == DEFAULT_SYSTEM_CF_NAME) {
227 return HA_EXIT_FAILURE;
228 }
229
230 RDB_MUTEX_LOCK_CHECK(m_mutex);
231 auto cf_handle = get_cf(cf_name, true /* lock_held_by_caller */);
232 if (cf_handle == nullptr) {
233 RDB_MUTEX_UNLOCK_CHECK(m_mutex);
234 return HA_EXIT_SUCCESS;
235 }
236
237 cf_id = cf_handle->GetID();
238 Rdb_cf_scanner scanner(cf_id);
239
240 auto ret = ddl_manager->scan_for_tables(&scanner);
241 if (ret) {
242 RDB_MUTEX_UNLOCK_CHECK(m_mutex);
243 return ret;
244 }
245
246 if (scanner.m_is_cf_used) {
247 // column family is used by existing key
248 RDB_MUTEX_UNLOCK_CHECK(m_mutex);
249 return HA_EXIT_FAILURE;
250 }
251
252 auto rdb = rdb_get_rocksdb_db();
253 auto status = rdb->DropColumnFamily(cf_handle);
254 if (!status.ok()) {
255 RDB_MUTEX_UNLOCK_CHECK(m_mutex);
256 return ha_rocksdb::rdb_error_to_mysql(status);
257 }
258
259 delete cf_handle;
260
261 auto id_iter = m_cf_id_map.find(cf_id);
262 DBUG_ASSERT(id_iter != m_cf_id_map.end());
263 m_cf_id_map.erase(id_iter);
264
265 auto name_iter = m_cf_name_map.find(cf_name);
266 DBUG_ASSERT(name_iter != m_cf_name_map.end());
267 m_cf_name_map.erase(name_iter);
268
269 RDB_MUTEX_UNLOCK_CHECK(m_mutex);
270
271 return HA_EXIT_SUCCESS;
272 }
273 } // namespace myrocks
274