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