1 /*
2    Copyright (c) 2012, Monty Program 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 #include <my_global.h>
18 
19 /* C++ standard header files */
20 #include <algorithm>
21 #include <cctype>
22 #include <map>
23 #include <string>
24 #include <vector>
25 
26 /* MySQL header files */
27 #include <sql_show.h>
28 
29 /* RocksDB header files */
30 #include "rocksdb/compaction_filter.h"
31 #include "rocksdb/convenience.h"
32 #include "rocksdb/filter_policy.h"
33 #include "rocksdb/memtablerep.h"
34 #include "rocksdb/merge_operator.h"
35 #include "rocksdb/slice_transform.h"
36 #include "rocksdb/utilities/transaction_db.h"
37 
38 /* MyRocks header files */
39 #include "./ha_rocksdb.h"
40 #include "./ha_rocksdb_proto.h"
41 #include "./rdb_cf_manager.h"
42 #include "./rdb_datadic.h"
43 #include "./rdb_utils.h"
44 #include "./rdb_mariadb_server_port.h"
45 
46 #include "./rdb_mariadb_port.h"
47 
48 namespace myrocks {
49 
50 /**
51   Define the INFORMATION_SCHEMA (I_S) structures needed by MyRocks storage
52   engine.
53 */
54 
55 #define ROCKSDB_FIELD_INFO(_name_, _len_, _type_, _flag_) \
56   { _name_, _len_, _type_, 0, _flag_, nullptr, 0 }
57 
58 #define ROCKSDB_FIELD_INFO_END \
59   ROCKSDB_FIELD_INFO(nullptr, 0, MYSQL_TYPE_NULL, 0)
60 
61 /*
62   Support for INFORMATION_SCHEMA.ROCKSDB_CFSTATS dynamic table
63  */
64 namespace RDB_CFSTATS_FIELD {
65 enum { CF_NAME = 0, STAT_TYPE, VALUE };
66 }  // namespace RDB_CFSTATS_FIELD
67 
68 static ST_FIELD_INFO rdb_i_s_cfstats_fields_info[] = {
69     ROCKSDB_FIELD_INFO("CF_NAME", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
70     ROCKSDB_FIELD_INFO("STAT_TYPE", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
71     ROCKSDB_FIELD_INFO("VALUE", sizeof(uint64_t), MYSQL_TYPE_LONGLONG, 0),
72     ROCKSDB_FIELD_INFO_END};
73 
rdb_i_s_cfstats_fill_table(my_core::THD * const thd,my_core::TABLE_LIST * const tables,my_core::Item * const cond MY_ATTRIBUTE ((__unused__)))74 static int rdb_i_s_cfstats_fill_table(
75     my_core::THD *const thd, my_core::TABLE_LIST *const tables,
76     my_core::Item *const cond MY_ATTRIBUTE((__unused__))) {
77   DBUG_ENTER_FUNC();
78 
79   DBUG_ASSERT(tables != nullptr);
80   DBUG_ASSERT(tables->table != nullptr);
81   DBUG_ASSERT(tables->table->field != nullptr);
82 
83   int ret = 0;
84   uint64_t val;
85 
86   const std::vector<std::pair<const std::string, std::string>> cf_properties = {
87       {rocksdb::DB::Properties::kNumImmutableMemTable,
88        "NUM_IMMUTABLE_MEM_TABLE"},
89       {rocksdb::DB::Properties::kMemTableFlushPending,
90        "MEM_TABLE_FLUSH_PENDING"},
91       {rocksdb::DB::Properties::kCompactionPending, "COMPACTION_PENDING"},
92       {rocksdb::DB::Properties::kCurSizeActiveMemTable,
93        "CUR_SIZE_ACTIVE_MEM_TABLE"},
94       {rocksdb::DB::Properties::kCurSizeAllMemTables,
95        "CUR_SIZE_ALL_MEM_TABLES"},
96       {rocksdb::DB::Properties::kNumEntriesActiveMemTable,
97        "NUM_ENTRIES_ACTIVE_MEM_TABLE"},
98       {rocksdb::DB::Properties::kNumEntriesImmMemTables,
99        "NUM_ENTRIES_IMM_MEM_TABLES"},
100       {rocksdb::DB::Properties::kEstimateTableReadersMem,
101        "NON_BLOCK_CACHE_SST_MEM_USAGE"},
102       {rocksdb::DB::Properties::kNumLiveVersions, "NUM_LIVE_VERSIONS"}};
103 
104   rocksdb::DB *const rdb = rdb_get_rocksdb_db();
105 
106   if (!rdb) {
107     DBUG_RETURN(ret);
108   }
109 
110   const Rdb_cf_manager &cf_manager = rdb_get_cf_manager();
111 
112   for (const auto &cf_name : cf_manager.get_cf_names()) {
113     DBUG_ASSERT(!cf_name.empty());
114     rocksdb::ColumnFamilyHandle *cfh = cf_manager.get_cf(cf_name);
115     if (cfh == nullptr) {
116       continue;
117     }
118 
119     for (const auto &property : cf_properties) {
120       if (!rdb->GetIntProperty(cfh, property.first, &val)) {
121         continue;
122       }
123 
124       tables->table->field[RDB_CFSTATS_FIELD::CF_NAME]->store(
125           cf_name.c_str(), cf_name.size(), system_charset_info);
126       tables->table->field[RDB_CFSTATS_FIELD::STAT_TYPE]->store(
127           property.second.c_str(), property.second.size(), system_charset_info);
128       tables->table->field[RDB_CFSTATS_FIELD::VALUE]->store(val, true);
129 
130       ret = static_cast<int>(
131           my_core::schema_table_store_record(thd, tables->table));
132 
133       if (ret) {
134         DBUG_RETURN(ret);
135       }
136     }
137   }
138 
139   DBUG_RETURN(0);
140 }
141 
rdb_i_s_cfstats_init(void * p)142 static int rdb_i_s_cfstats_init(void *p) {
143   DBUG_ENTER_FUNC();
144 
145   if (prevent_myrocks_loading)
146     DBUG_RETURN(1);
147 
148   DBUG_ASSERT(p != nullptr);
149 
150   my_core::ST_SCHEMA_TABLE *schema;
151 
152   schema = (my_core::ST_SCHEMA_TABLE *)p;
153 
154   schema->fields_info = rdb_i_s_cfstats_fields_info;
155   schema->fill_table = rdb_i_s_cfstats_fill_table;
156 
157   DBUG_RETURN(0);
158 }
159 
160 /*
161   Support for INFORMATION_SCHEMA.ROCKSDB_DBSTATS dynamic table
162  */
163 namespace RDB_DBSTATS_FIELD {
164 enum { STAT_TYPE = 0, VALUE };
165 }  // namespace RDB_DBSTATS_FIELD
166 
167 static ST_FIELD_INFO rdb_i_s_dbstats_fields_info[] = {
168     ROCKSDB_FIELD_INFO("STAT_TYPE", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
169     ROCKSDB_FIELD_INFO("VALUE", sizeof(uint64_t), MYSQL_TYPE_LONGLONG, 0),
170     ROCKSDB_FIELD_INFO_END};
171 
rdb_i_s_dbstats_fill_table(my_core::THD * const thd,my_core::TABLE_LIST * const tables,my_core::Item * const cond MY_ATTRIBUTE ((__unused__)))172 static int rdb_i_s_dbstats_fill_table(
173     my_core::THD *const thd, my_core::TABLE_LIST *const tables,
174     my_core::Item *const cond MY_ATTRIBUTE((__unused__))) {
175   DBUG_ENTER_FUNC();
176 
177   DBUG_ASSERT(tables != nullptr);
178   DBUG_ASSERT(tables->table != nullptr);
179   DBUG_ASSERT(tables->table->field != nullptr);
180 
181   int ret = 0;
182   uint64_t val;
183 
184   const std::vector<std::pair<std::string, std::string>> db_properties = {
185       {rocksdb::DB::Properties::kBackgroundErrors, "DB_BACKGROUND_ERRORS"},
186       {rocksdb::DB::Properties::kNumSnapshots, "DB_NUM_SNAPSHOTS"},
187       {rocksdb::DB::Properties::kOldestSnapshotTime,
188        "DB_OLDEST_SNAPSHOT_TIME"}};
189 
190   rocksdb::DB *const rdb = rdb_get_rocksdb_db();
191 
192   if (!rdb) {
193     DBUG_RETURN(ret);
194   }
195 
196   const rocksdb::BlockBasedTableOptions &table_options =
197       rdb_get_table_options();
198 
199   for (const auto &property : db_properties) {
200     if (!rdb->GetIntProperty(property.first, &val)) {
201       continue;
202     }
203 
204     tables->table->field[RDB_DBSTATS_FIELD::STAT_TYPE]->store(
205         property.second.c_str(), property.second.size(), system_charset_info);
206     tables->table->field[RDB_DBSTATS_FIELD::VALUE]->store(val, true);
207 
208     ret = static_cast<int>(
209         my_core::schema_table_store_record(thd, tables->table));
210 
211     if (ret) {
212       DBUG_RETURN(ret);
213     }
214   }
215 
216   /*
217     Currently, this can only show the usage of a block cache allocated
218     directly by the handlerton. If the column family config specifies a block
219     cache (i.e. the column family option has a parameter such as
220     block_based_table_factory={block_cache=1G}), then the block cache is
221     allocated within the rocksdb::GetColumnFamilyOptionsFromString().
222 
223     There is no interface to retrieve this block cache, nor fetch the usage
224     information from the column family.
225    */
226   val = (table_options.block_cache ? table_options.block_cache->GetUsage() : 0);
227 
228   tables->table->field[RDB_DBSTATS_FIELD::STAT_TYPE]->store(
229       STRING_WITH_LEN("DB_BLOCK_CACHE_USAGE"), system_charset_info);
230   tables->table->field[RDB_DBSTATS_FIELD::VALUE]->store(val, true);
231 
232   ret =
233       static_cast<int>(my_core::schema_table_store_record(thd, tables->table));
234 
235   DBUG_RETURN(ret);
236 }
237 
rdb_i_s_dbstats_init(void * const p)238 static int rdb_i_s_dbstats_init(void *const p) {
239   DBUG_ENTER_FUNC();
240 
241   if (prevent_myrocks_loading)
242     DBUG_RETURN(1);
243 
244   DBUG_ASSERT(p != nullptr);
245 
246   my_core::ST_SCHEMA_TABLE *schema;
247 
248   schema = (my_core::ST_SCHEMA_TABLE *)p;
249 
250   schema->fields_info = rdb_i_s_dbstats_fields_info;
251   schema->fill_table = rdb_i_s_dbstats_fill_table;
252 
253   DBUG_RETURN(0);
254 }
255 
256 /*
257   Support for INFORMATION_SCHEMA.ROCKSDB_PERF_CONTEXT dynamic table
258  */
259 namespace RDB_PERF_CONTEXT_FIELD {
260 enum { TABLE_SCHEMA = 0, TABLE_NAME, PARTITION_NAME, STAT_TYPE, VALUE };
261 }  // namespace RDB_PERF_CONTEXT_FIELD
262 
263 static ST_FIELD_INFO rdb_i_s_perf_context_fields_info[] = {
264     ROCKSDB_FIELD_INFO("TABLE_SCHEMA", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
265     ROCKSDB_FIELD_INFO("TABLE_NAME", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
266     ROCKSDB_FIELD_INFO("PARTITION_NAME", NAME_LEN + 1, MYSQL_TYPE_STRING,
267                        MY_I_S_MAYBE_NULL),
268     ROCKSDB_FIELD_INFO("STAT_TYPE", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
269     ROCKSDB_FIELD_INFO("VALUE", sizeof(uint64_t), MYSQL_TYPE_LONGLONG, 0),
270     ROCKSDB_FIELD_INFO_END};
271 
rdb_i_s_perf_context_fill_table(my_core::THD * const thd,my_core::TABLE_LIST * const tables,my_core::Item * const cond MY_ATTRIBUTE ((__unused__)))272 static int rdb_i_s_perf_context_fill_table(
273     my_core::THD *const thd, my_core::TABLE_LIST *const tables,
274     my_core::Item *const cond MY_ATTRIBUTE((__unused__))) {
275   DBUG_ENTER_FUNC();
276 
277   DBUG_ASSERT(thd != nullptr);
278   DBUG_ASSERT(tables != nullptr);
279   DBUG_ASSERT(tables->table != nullptr);
280 
281   int ret = 0;
282   Field **field = tables->table->field;
283   DBUG_ASSERT(field != nullptr);
284 
285   rocksdb::DB *const rdb = rdb_get_rocksdb_db();
286 
287   if (!rdb) {
288     DBUG_RETURN(ret);
289   }
290 
291   const std::vector<std::string> tablenames = rdb_get_open_table_names();
292 
293   for (const auto &it : tablenames) {
294     std::string str, dbname, tablename, partname;
295     Rdb_perf_counters counters;
296 
297     int rc = rdb_normalize_tablename(it, &str);
298 
299     if (rc != HA_EXIT_SUCCESS) {
300       DBUG_RETURN(rc);
301     }
302 
303     if (rdb_split_normalized_tablename(str, &dbname, &tablename, &partname)) {
304       continue;
305     }
306 
307     if (rdb_get_table_perf_counters(it.c_str(), &counters)) {
308       continue;
309     }
310 
311     field[RDB_PERF_CONTEXT_FIELD::TABLE_SCHEMA]->store(
312         dbname.c_str(), dbname.size(), system_charset_info);
313     field[RDB_PERF_CONTEXT_FIELD::TABLE_NAME]->store(
314         tablename.c_str(), tablename.size(), system_charset_info);
315 
316     if (partname.size() == 0) {
317       field[RDB_PERF_CONTEXT_FIELD::PARTITION_NAME]->set_null();
318     } else {
319       field[RDB_PERF_CONTEXT_FIELD::PARTITION_NAME]->set_notnull();
320       field[RDB_PERF_CONTEXT_FIELD::PARTITION_NAME]->store(
321           partname.c_str(), partname.size(), system_charset_info);
322     }
323 
324     for (int i = 0; i < PC_MAX_IDX; i++) {
325       field[RDB_PERF_CONTEXT_FIELD::STAT_TYPE]->store(
326           rdb_pc_stat_types[i].c_str(), rdb_pc_stat_types[i].size(),
327           system_charset_info);
328       field[RDB_PERF_CONTEXT_FIELD::VALUE]->store(counters.m_value[i], true);
329 
330       ret = static_cast<int>(
331           my_core::schema_table_store_record(thd, tables->table));
332 
333       if (ret) {
334         DBUG_RETURN(ret);
335       }
336     }
337   }
338 
339   DBUG_RETURN(0);
340 }
341 
rdb_i_s_perf_context_init(void * const p)342 static int rdb_i_s_perf_context_init(void *const p) {
343   DBUG_ENTER_FUNC();
344 
345   if (prevent_myrocks_loading)
346     DBUG_RETURN(1);
347   DBUG_ASSERT(p != nullptr);
348 
349   my_core::ST_SCHEMA_TABLE *schema;
350 
351   schema = (my_core::ST_SCHEMA_TABLE *)p;
352 
353   schema->fields_info = rdb_i_s_perf_context_fields_info;
354   schema->fill_table = rdb_i_s_perf_context_fill_table;
355 
356   DBUG_RETURN(0);
357 }
358 
359 /*
360   Support for INFORMATION_SCHEMA.ROCKSDB_PERF_CONTEXT_GLOBAL dynamic table
361  */
362 namespace RDB_PERF_CONTEXT_GLOBAL_FIELD {
363 enum { STAT_TYPE = 0, VALUE };
364 }  // namespace RDB_PERF_CONTEXT_GLOBAL_FIELD
365 
366 static ST_FIELD_INFO rdb_i_s_perf_context_global_fields_info[] = {
367     ROCKSDB_FIELD_INFO("STAT_TYPE", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
368     ROCKSDB_FIELD_INFO("VALUE", sizeof(uint64_t), MYSQL_TYPE_LONGLONG, 0),
369     ROCKSDB_FIELD_INFO_END};
370 
rdb_i_s_perf_context_global_fill_table(my_core::THD * const thd,my_core::TABLE_LIST * const tables,my_core::Item * const cond MY_ATTRIBUTE ((__unused__)))371 static int rdb_i_s_perf_context_global_fill_table(
372     my_core::THD *const thd, my_core::TABLE_LIST *const tables,
373     my_core::Item *const cond MY_ATTRIBUTE((__unused__))) {
374   DBUG_ENTER_FUNC();
375 
376   DBUG_ASSERT(thd != nullptr);
377   DBUG_ASSERT(tables != nullptr);
378   DBUG_ASSERT(tables->table != nullptr);
379   DBUG_ASSERT(tables->table->field != nullptr);
380 
381   int ret = 0;
382 
383   rocksdb::DB *const rdb = rdb_get_rocksdb_db();
384 
385   if (!rdb) {
386     DBUG_RETURN(ret);
387   }
388 
389   // Get a copy of the global perf counters.
390   Rdb_perf_counters global_counters;
391   rdb_get_global_perf_counters(&global_counters);
392 
393   for (int i = 0; i < PC_MAX_IDX; i++) {
394     tables->table->field[RDB_PERF_CONTEXT_GLOBAL_FIELD::STAT_TYPE]->store(
395         rdb_pc_stat_types[i].c_str(), rdb_pc_stat_types[i].size(),
396         system_charset_info);
397     tables->table->field[RDB_PERF_CONTEXT_GLOBAL_FIELD::VALUE]->store(
398         global_counters.m_value[i], true);
399 
400     ret = static_cast<int>(
401         my_core::schema_table_store_record(thd, tables->table));
402 
403     if (ret) {
404       DBUG_RETURN(ret);
405     }
406   }
407 
408   DBUG_RETURN(0);
409 }
410 
rdb_i_s_perf_context_global_init(void * const p)411 static int rdb_i_s_perf_context_global_init(void *const p) {
412   DBUG_ENTER_FUNC();
413 
414   if (prevent_myrocks_loading)
415     DBUG_RETURN(1);
416 
417   DBUG_ASSERT(p != nullptr);
418 
419   my_core::ST_SCHEMA_TABLE *schema;
420 
421   schema = (my_core::ST_SCHEMA_TABLE *)p;
422 
423   schema->fields_info = rdb_i_s_perf_context_global_fields_info;
424   schema->fill_table = rdb_i_s_perf_context_global_fill_table;
425 
426   DBUG_RETURN(0);
427 }
428 
429 /*
430   Support for INFORMATION_SCHEMA.ROCKSDB_CFOPTIONS dynamic table
431  */
432 namespace RDB_CFOPTIONS_FIELD {
433 enum { CF_NAME = 0, OPTION_TYPE, VALUE };
434 }  // namespace RDB_CFOPTIONS_FIELD
435 
436 static ST_FIELD_INFO rdb_i_s_cfoptions_fields_info[] = {
437     ROCKSDB_FIELD_INFO("CF_NAME", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
438     ROCKSDB_FIELD_INFO("OPTION_TYPE", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
439     ROCKSDB_FIELD_INFO("VALUE", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
440     ROCKSDB_FIELD_INFO_END};
441 
rdb_i_s_cfoptions_fill_table(my_core::THD * const thd,my_core::TABLE_LIST * const tables,my_core::Item * const cond MY_ATTRIBUTE ((__unused__)))442 static int rdb_i_s_cfoptions_fill_table(
443     my_core::THD *const thd, my_core::TABLE_LIST *const tables,
444     my_core::Item *const cond MY_ATTRIBUTE((__unused__))) {
445   DBUG_ENTER_FUNC();
446 
447   DBUG_ASSERT(thd != nullptr);
448   DBUG_ASSERT(tables != nullptr);
449 
450   int ret = 0;
451 
452   rocksdb::DB *const rdb = rdb_get_rocksdb_db();
453 
454   if (!rdb) {
455     DBUG_RETURN(ret);
456   }
457 
458   Rdb_cf_manager &cf_manager = rdb_get_cf_manager();
459 
460   for (const auto &cf_name : cf_manager.get_cf_names()) {
461     std::string val;
462     rocksdb::ColumnFamilyOptions opts;
463 
464     DBUG_ASSERT(!cf_name.empty());
465     cf_manager.get_cf_options(cf_name, &opts);
466 
467     std::vector<std::pair<std::string, std::string>> cf_option_types = {
468         {"COMPARATOR", opts.comparator == nullptr
469                            ? "NULL"
470                            : std::string(opts.comparator->Name())},
471         {"MERGE_OPERATOR", opts.merge_operator == nullptr
472                                ? "NULL"
473                                : std::string(opts.merge_operator->Name())},
474         {"COMPACTION_FILTER",
475          opts.compaction_filter == nullptr
476              ? "NULL"
477              : std::string(opts.compaction_filter->Name())},
478         {"COMPACTION_FILTER_FACTORY",
479          opts.compaction_filter_factory == nullptr
480              ? "NULL"
481              : std::string(opts.compaction_filter_factory->Name())},
482         {"WRITE_BUFFER_SIZE", std::to_string(opts.write_buffer_size)},
483         {"MAX_WRITE_BUFFER_NUMBER",
484          std::to_string(opts.max_write_buffer_number)},
485         {"MIN_WRITE_BUFFER_NUMBER_TO_MERGE",
486          std::to_string(opts.min_write_buffer_number_to_merge)},
487         {"NUM_LEVELS", std::to_string(opts.num_levels)},
488         {"LEVEL0_FILE_NUM_COMPACTION_TRIGGER",
489          std::to_string(opts.level0_file_num_compaction_trigger)},
490         {"LEVEL0_SLOWDOWN_WRITES_TRIGGER",
491          std::to_string(opts.level0_slowdown_writes_trigger)},
492         {"LEVEL0_STOP_WRITES_TRIGGER",
493          std::to_string(opts.level0_stop_writes_trigger)},
494         {"MAX_MEM_COMPACTION_LEVEL",
495          std::to_string(opts.max_mem_compaction_level)},
496         {"TARGET_FILE_SIZE_BASE", std::to_string(opts.target_file_size_base)},
497         {"TARGET_FILE_SIZE_MULTIPLIER",
498          std::to_string(opts.target_file_size_multiplier)},
499         {"MAX_BYTES_FOR_LEVEL_BASE",
500          std::to_string(opts.max_bytes_for_level_base)},
501         {"LEVEL_COMPACTION_DYNAMIC_LEVEL_BYTES",
502          opts.level_compaction_dynamic_level_bytes ? "ON" : "OFF"},
503         {"MAX_BYTES_FOR_LEVEL_MULTIPLIER",
504          std::to_string(opts.max_bytes_for_level_multiplier)},
505         {"SOFT_RATE_LIMIT", std::to_string(opts.soft_rate_limit)},
506         {"HARD_RATE_LIMIT", std::to_string(opts.hard_rate_limit)},
507         {"RATE_LIMIT_DELAY_MAX_MILLISECONDS",
508          std::to_string(opts.rate_limit_delay_max_milliseconds)},
509         {"ARENA_BLOCK_SIZE", std::to_string(opts.arena_block_size)},
510         {"DISABLE_AUTO_COMPACTIONS",
511          opts.disable_auto_compactions ? "ON" : "OFF"},
512         {"PURGE_REDUNDANT_KVS_WHILE_FLUSH",
513          opts.purge_redundant_kvs_while_flush ? "ON" : "OFF"},
514         {"MAX_SEQUENTIAL_SKIP_IN_ITERATIONS",
515          std::to_string(opts.max_sequential_skip_in_iterations)},
516         {"MEMTABLE_FACTORY", opts.memtable_factory == nullptr
517                                  ? "NULL"
518                                  : opts.memtable_factory->Name()},
519         {"INPLACE_UPDATE_SUPPORT", opts.inplace_update_support ? "ON" : "OFF"},
520         {"INPLACE_UPDATE_NUM_LOCKS",
521          opts.inplace_update_num_locks ? "ON" : "OFF"},
522         {"MEMTABLE_PREFIX_BLOOM_BITS_RATIO",
523          std::to_string(opts.memtable_prefix_bloom_size_ratio)},
524         {"MEMTABLE_PREFIX_BLOOM_HUGE_PAGE_TLB_SIZE",
525          std::to_string(opts.memtable_huge_page_size)},
526         {"BLOOM_LOCALITY", std::to_string(opts.bloom_locality)},
527         {"MAX_SUCCESSIVE_MERGES", std::to_string(opts.max_successive_merges)},
528         {"OPTIMIZE_FILTERS_FOR_HITS",
529          (opts.optimize_filters_for_hits ? "ON" : "OFF")},
530     };
531 
532     // get MAX_BYTES_FOR_LEVEL_MULTIPLIER_ADDITIONAL option value
533     val = opts.max_bytes_for_level_multiplier_additional.empty() ? "NULL" : "";
534 
535     for (const auto &level : opts.max_bytes_for_level_multiplier_additional) {
536       val.append(std::to_string(level) + ":");
537     }
538 
539     val.pop_back();
540     cf_option_types.push_back(
541         {"MAX_BYTES_FOR_LEVEL_MULTIPLIER_ADDITIONAL", val});
542 
543     // get COMPRESSION_TYPE option value
544     GetStringFromCompressionType(&val, opts.compression);
545 
546     if (val.empty()) {
547       val = "NULL";
548     }
549 
550     cf_option_types.push_back({"COMPRESSION_TYPE", val});
551 
552     // get COMPRESSION_PER_LEVEL option value
553     val = opts.compression_per_level.empty() ? "NULL" : "";
554 
555     for (const auto &compression_type : opts.compression_per_level) {
556       std::string res;
557 
558       GetStringFromCompressionType(&res, compression_type);
559 
560       if (!res.empty()) {
561         val.append(res + ":");
562       }
563     }
564 
565     val.pop_back();
566     cf_option_types.push_back({"COMPRESSION_PER_LEVEL", val});
567 
568     // get compression_opts value
569     val = std::to_string(opts.compression_opts.window_bits) + ":";
570     val.append(std::to_string(opts.compression_opts.level) + ":");
571     val.append(std::to_string(opts.compression_opts.strategy));
572 
573     cf_option_types.push_back({"COMPRESSION_OPTS", val});
574 
575     // bottommost_compression
576     if (opts.bottommost_compression) {
577       std::string res;
578 
579       GetStringFromCompressionType(&res, opts.bottommost_compression);
580 
581       if (!res.empty()) {
582         cf_option_types.push_back({"BOTTOMMOST_COMPRESSION", res});
583       }
584     }
585 
586     // get PREFIX_EXTRACTOR option
587     cf_option_types.push_back(
588         {"PREFIX_EXTRACTOR", opts.prefix_extractor == nullptr
589                                  ? "NULL"
590                                  : std::string(opts.prefix_extractor->Name())});
591 
592     // get COMPACTION_STYLE option
593     switch (opts.compaction_style) {
594       case rocksdb::kCompactionStyleLevel:
595         val = "kCompactionStyleLevel";
596         break;
597       case rocksdb::kCompactionStyleUniversal:
598         val = "kCompactionStyleUniversal";
599         break;
600       case rocksdb::kCompactionStyleFIFO:
601         val = "kCompactionStyleFIFO";
602         break;
603       case rocksdb::kCompactionStyleNone:
604         val = "kCompactionStyleNone";
605         break;
606       default:
607         val = "NULL";
608     }
609 
610     cf_option_types.push_back({"COMPACTION_STYLE", val});
611 
612     // get COMPACTION_OPTIONS_UNIVERSAL related options
613     const rocksdb::CompactionOptionsUniversal compac_opts =
614         opts.compaction_options_universal;
615 
616     val = "{SIZE_RATIO=";
617 
618     val.append(std::to_string(compac_opts.size_ratio));
619     val.append("; MIN_MERGE_WIDTH=");
620     val.append(std::to_string(compac_opts.min_merge_width));
621     val.append("; MAX_MERGE_WIDTH=");
622     val.append(std::to_string(compac_opts.max_merge_width));
623     val.append("; MAX_SIZE_AMPLIFICATION_PERCENT=");
624     val.append(std::to_string(compac_opts.max_size_amplification_percent));
625     val.append("; COMPRESSION_SIZE_PERCENT=");
626     val.append(std::to_string(compac_opts.compression_size_percent));
627     val.append("; STOP_STYLE=");
628 
629     switch (compac_opts.stop_style) {
630       case rocksdb::kCompactionStopStyleSimilarSize:
631         val.append("kCompactionStopStyleSimilarSize}");
632         break;
633       case rocksdb::kCompactionStopStyleTotalSize:
634         val.append("kCompactionStopStyleTotalSize}");
635         break;
636       default:
637         val.append("}");
638     }
639 
640     cf_option_types.push_back({"COMPACTION_OPTIONS_UNIVERSAL", val});
641 
642     // get COMPACTION_OPTION_FIFO option
643     cf_option_types.push_back(
644         {"COMPACTION_OPTION_FIFO::MAX_TABLE_FILES_SIZE",
645          std::to_string(opts.compaction_options_fifo.max_table_files_size)});
646 
647     // get table related options
648     std::vector<std::string> table_options =
649         split_into_vector(opts.table_factory->GetPrintableTableOptions(), '\n');
650 
651     for (auto option : table_options) {
652       option.erase(std::remove(option.begin(), option.end(), ' '),
653                    option.end());
654 
655       int pos = option.find(":");
656       std::string option_name = option.substr(0, pos);
657       std::string option_value = option.substr(pos + 1, option.length());
658       std::transform(option_name.begin(), option_name.end(),
659                      option_name.begin(),
660                      [](unsigned char c) { return std::toupper(c); });
661 
662       cf_option_types.push_back(
663           {"TABLE_FACTORY::" + option_name, option_value});
664     }
665 
666     for (const auto &cf_option_type : cf_option_types) {
667       DBUG_ASSERT(tables->table != nullptr);
668       DBUG_ASSERT(tables->table->field != nullptr);
669 
670       tables->table->field[RDB_CFOPTIONS_FIELD::CF_NAME]->store(
671           cf_name.c_str(), cf_name.size(), system_charset_info);
672       tables->table->field[RDB_CFOPTIONS_FIELD::OPTION_TYPE]->store(
673           cf_option_type.first.c_str(), cf_option_type.first.size(),
674           system_charset_info);
675       tables->table->field[RDB_CFOPTIONS_FIELD::VALUE]->store(
676           cf_option_type.second.c_str(), cf_option_type.second.size(),
677           system_charset_info);
678 
679       ret = static_cast<int>(
680           my_core::schema_table_store_record(thd, tables->table));
681 
682       if (ret) {
683         DBUG_RETURN(ret);
684       }
685     }
686   }
687 
688   DBUG_RETURN(0);
689 }
690 
691 /*
692   Support for INFORMATION_SCHEMA.ROCKSDB_GLOBAL_INFO dynamic table
693  */
694 namespace RDB_GLOBAL_INFO_FIELD {
695 enum { TYPE = 0, NAME, VALUE };
696 }
697 
698 static ST_FIELD_INFO rdb_i_s_global_info_fields_info[] = {
699     ROCKSDB_FIELD_INFO("TYPE", FN_REFLEN + 1, MYSQL_TYPE_STRING, 0),
700     ROCKSDB_FIELD_INFO("NAME", FN_REFLEN + 1, MYSQL_TYPE_STRING, 0),
701     ROCKSDB_FIELD_INFO("VALUE", FN_REFLEN + 1, MYSQL_TYPE_STRING, 0),
702     ROCKSDB_FIELD_INFO_END};
703 
704 /*
705  * helper function for rdb_i_s_global_info_fill_table
706  * to insert (TYPE, KEY, VALUE) rows into
707  * information_schema.rocksdb_global_info
708  */
rdb_global_info_fill_row(my_core::THD * const thd,my_core::TABLE_LIST * const tables,const char * const type,const char * const name,const char * const value)709 static int rdb_global_info_fill_row(my_core::THD *const thd,
710                                     my_core::TABLE_LIST *const tables,
711                                     const char *const type,
712                                     const char *const name,
713                                     const char *const value) {
714   DBUG_ASSERT(thd != nullptr);
715   DBUG_ASSERT(tables != nullptr);
716   DBUG_ASSERT(tables->table != nullptr);
717   DBUG_ASSERT(type != nullptr);
718   DBUG_ASSERT(name != nullptr);
719   DBUG_ASSERT(value != nullptr);
720 
721   Field **field = tables->table->field;
722   DBUG_ASSERT(field != nullptr);
723 
724   field[RDB_GLOBAL_INFO_FIELD::TYPE]->store(type, strlen(type),
725                                             system_charset_info);
726   field[RDB_GLOBAL_INFO_FIELD::NAME]->store(name, strlen(name),
727                                             system_charset_info);
728   field[RDB_GLOBAL_INFO_FIELD::VALUE]->store(value, strlen(value),
729                                              system_charset_info);
730 
731   return my_core::schema_table_store_record(thd, tables->table);
732 }
733 
rdb_i_s_global_info_fill_table(my_core::THD * const thd,my_core::TABLE_LIST * const tables,my_core::Item * const cond MY_ATTRIBUTE ((__unused__)))734 static int rdb_i_s_global_info_fill_table(
735     my_core::THD *const thd, my_core::TABLE_LIST *const tables,
736     my_core::Item *const cond MY_ATTRIBUTE((__unused__))) {
737   DBUG_ENTER_FUNC();
738 
739   DBUG_ASSERT(thd != nullptr);
740   DBUG_ASSERT(tables != nullptr);
741 
742   static const uint32_t INT_BUF_LEN = 21;
743   static const uint32_t CF_ID_INDEX_BUF_LEN = 60;
744 
745   int ret = 0;
746 
747   rocksdb::DB *const rdb = rdb_get_rocksdb_db();
748 
749   if (!rdb) {
750     DBUG_RETURN(ret);
751   }
752 
753   /* binlog info */
754   Rdb_binlog_manager *const blm = rdb_get_binlog_manager();
755   DBUG_ASSERT(blm != nullptr);
756 
757   char file_buf[FN_REFLEN + 1] = {0};
758   my_off_t pos = 0;
759   char pos_buf[INT_BUF_LEN] = {0};
760   char gtid_buf[GTID_BUF_LEN] = {0};
761 
762   if (blm->read(file_buf, &pos, gtid_buf)) {
763     snprintf(pos_buf, INT_BUF_LEN, "%llu", (ulonglong)pos);
764 
765     ret |= rdb_global_info_fill_row(thd, tables, "BINLOG", "FILE", file_buf);
766     ret |= rdb_global_info_fill_row(thd, tables, "BINLOG", "POS", pos_buf);
767     ret |= rdb_global_info_fill_row(thd, tables, "BINLOG", "GTID", gtid_buf);
768   }
769 
770   /* max index info */
771   const Rdb_dict_manager *const dict_manager = rdb_get_dict_manager();
772   DBUG_ASSERT(dict_manager != nullptr);
773 
774   uint32_t max_index_id;
775   char max_index_id_buf[INT_BUF_LEN] = {0};
776 
777   if (dict_manager->get_max_index_id(&max_index_id)) {
778     snprintf(max_index_id_buf, INT_BUF_LEN, "%u", max_index_id);
779 
780     ret |= rdb_global_info_fill_row(thd, tables, "MAX_INDEX_ID", "MAX_INDEX_ID",
781                                     max_index_id_buf);
782   }
783 
784   /* cf_id -> cf_flags */
785   char cf_id_buf[INT_BUF_LEN] = {0};
786   char cf_value_buf[FN_REFLEN + 1] = {0};
787   const Rdb_cf_manager &cf_manager = rdb_get_cf_manager();
788 
789   for (const auto &cf_handle : cf_manager.get_all_cf()) {
790     DBUG_ASSERT(cf_handle != nullptr);
791 
792     uint flags;
793 
794     if (!dict_manager->get_cf_flags(cf_handle->GetID(), &flags)) {
795       // NO_LINT_DEBUG
796       sql_print_error(
797           "RocksDB: Failed to get column family flags "
798           "from CF with id = %u. MyRocks data dictionary may "
799           "be corrupted.",
800           cf_handle->GetID());
801       abort();
802     }
803 
804     snprintf(cf_id_buf, INT_BUF_LEN, "%u", cf_handle->GetID());
805     snprintf(cf_value_buf, FN_REFLEN, "%s [%u]", cf_handle->GetName().c_str(),
806              flags);
807 
808     ret |= rdb_global_info_fill_row(thd, tables, "CF_FLAGS", cf_id_buf,
809                                     cf_value_buf);
810 
811     if (ret) {
812       break;
813     }
814   }
815 
816   /* DDL_DROP_INDEX_ONGOING */
817   std::unordered_set<GL_INDEX_ID> gl_index_ids;
818   dict_manager->get_ongoing_index_operation(
819       &gl_index_ids, Rdb_key_def::DDL_DROP_INDEX_ONGOING);
820   char cf_id_index_buf[CF_ID_INDEX_BUF_LEN] = {0};
821 
822   for (auto gl_index_id : gl_index_ids) {
823     snprintf(cf_id_index_buf, CF_ID_INDEX_BUF_LEN, "cf_id:%u,index_id:%u",
824              gl_index_id.cf_id, gl_index_id.index_id);
825 
826     ret |= rdb_global_info_fill_row(thd, tables, "DDL_DROP_INDEX_ONGOING",
827                                     cf_id_index_buf, "");
828 
829     if (ret) {
830       break;
831     }
832   }
833 
834   DBUG_RETURN(ret);
835 }
836 
837 /*
838   Support for INFORMATION_SCHEMA.ROCKSDB_COMPACTION_STATS dynamic table
839  */
rdb_i_s_compact_stats_fill_table(my_core::THD * thd,my_core::TABLE_LIST * tables,my_core::Item * cond MY_ATTRIBUTE ((__unused__)))840 static int rdb_i_s_compact_stats_fill_table(
841     my_core::THD *thd, my_core::TABLE_LIST *tables,
842     my_core::Item *cond MY_ATTRIBUTE((__unused__))) {
843   DBUG_ASSERT(thd != nullptr);
844   DBUG_ASSERT(tables != nullptr);
845 
846   DBUG_ENTER_FUNC();
847 
848   int ret = 0;
849   rocksdb::DB *rdb = rdb_get_rocksdb_db();
850 
851   if (!rdb) {
852     DBUG_RETURN(ret);
853   }
854 
855   Rdb_cf_manager &cf_manager = rdb_get_cf_manager();
856 
857   for (auto cf_name : cf_manager.get_cf_names()) {
858     rocksdb::ColumnFamilyHandle *cfh = cf_manager.get_cf(cf_name);
859 
860     if (cfh == nullptr) {
861       continue;
862     }
863 
864     std::map<std::string, std::string> props;
865     bool bool_ret MY_ATTRIBUTE((__unused__));
866     bool_ret = rdb->GetMapProperty(cfh, "rocksdb.cfstats", &props);
867     DBUG_ASSERT(bool_ret);
868 
869     const std::string prop_name_prefix = "compaction.";
870     for (auto const &prop_ent : props) {
871       std::string prop_name = prop_ent.first;
872       if (prop_name.find(prop_name_prefix) != 0) {
873         continue;
874       }
875       std::string value = prop_ent.second;
876       std::size_t del_pos = prop_name.find('.', prop_name_prefix.size());
877       DBUG_ASSERT(del_pos != std::string::npos);
878       std::string level_str = prop_name.substr(
879           prop_name_prefix.size(), del_pos - prop_name_prefix.size());
880       std::string type_str = prop_name.substr(del_pos + 1);
881 
882       Field **field = tables->table->field;
883       DBUG_ASSERT(field != nullptr);
884 
885       field[0]->store(cf_name.c_str(), cf_name.size(), system_charset_info);
886       field[1]->store(level_str.c_str(), level_str.size(), system_charset_info);
887       field[2]->store(type_str.c_str(), type_str.size(), system_charset_info);
888       field[3]->store(std::stod(value));
889 
890       ret |= static_cast<int>(
891           my_core::schema_table_store_record(thd, tables->table));
892 
893       if (ret != 0) {
894         DBUG_RETURN(ret);
895       }
896     }
897   }
898 
899   DBUG_RETURN(ret);
900 }
901 
902 static ST_FIELD_INFO rdb_i_s_compact_stats_fields_info[] = {
903     ROCKSDB_FIELD_INFO("CF_NAME", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
904     ROCKSDB_FIELD_INFO("LEVEL", FN_REFLEN + 1, MYSQL_TYPE_STRING, 0),
905     ROCKSDB_FIELD_INFO("TYPE", FN_REFLEN + 1, MYSQL_TYPE_STRING, 0),
906     ROCKSDB_FIELD_INFO("VALUE", sizeof(double), MYSQL_TYPE_DOUBLE, 0),
907     ROCKSDB_FIELD_INFO_END};
908 
909 namespace  // anonymous namespace = not visible outside this source file
910 {
911 struct Rdb_ddl_scanner : public Rdb_tables_scanner {
912   my_core::THD *m_thd;
913   my_core::TABLE *m_table;
914 
915   int add_table(Rdb_tbl_def *tdef) override;
916 };
917 }  // anonymous namespace
918 
919 /*
920   Support for INFORMATION_SCHEMA.ROCKSDB_DDL dynamic table
921  */
922 namespace RDB_DDL_FIELD {
923 enum {
924   TABLE_SCHEMA = 0,
925   TABLE_NAME,
926   PARTITION_NAME,
927   INDEX_NAME,
928   COLUMN_FAMILY,
929   INDEX_NUMBER,
930   INDEX_TYPE,
931   KV_FORMAT_VERSION,
932   TTL_DURATION,
933   INDEX_FLAGS,
934   CF,
935   AUTO_INCREMENT
936 };
937 }  // namespace RDB_DDL_FIELD
938 
939 static ST_FIELD_INFO rdb_i_s_ddl_fields_info[] = {
940     ROCKSDB_FIELD_INFO("TABLE_SCHEMA", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
941     ROCKSDB_FIELD_INFO("TABLE_NAME", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
942     ROCKSDB_FIELD_INFO("PARTITION_NAME", NAME_LEN + 1, MYSQL_TYPE_STRING,
943                        MY_I_S_MAYBE_NULL),
944     ROCKSDB_FIELD_INFO("INDEX_NAME", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
945     ROCKSDB_FIELD_INFO("COLUMN_FAMILY", sizeof(uint32_t), MYSQL_TYPE_LONG, 0),
946     ROCKSDB_FIELD_INFO("INDEX_NUMBER", sizeof(uint32_t), MYSQL_TYPE_LONG, 0),
947     ROCKSDB_FIELD_INFO("INDEX_TYPE", sizeof(uint16_t), MYSQL_TYPE_SHORT, 0),
948     ROCKSDB_FIELD_INFO("KV_FORMAT_VERSION", sizeof(uint16_t), MYSQL_TYPE_SHORT,
949                        0),
950     ROCKSDB_FIELD_INFO("TTL_DURATION", sizeof(uint64), MYSQL_TYPE_LONGLONG, 0),
951     ROCKSDB_FIELD_INFO("INDEX_FLAGS", sizeof(uint64), MYSQL_TYPE_LONGLONG, 0),
952     ROCKSDB_FIELD_INFO("CF", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
953     ROCKSDB_FIELD_INFO("AUTO_INCREMENT", sizeof(uint64_t), MYSQL_TYPE_LONGLONG,
954                        MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED),
955     ROCKSDB_FIELD_INFO_END};
956 
add_table(Rdb_tbl_def * tdef)957 int Rdb_ddl_scanner::add_table(Rdb_tbl_def *tdef) {
958   DBUG_ASSERT(tdef != nullptr);
959 
960   int ret = 0;
961 
962   DBUG_ASSERT(m_table != nullptr);
963   Field **field = m_table->field;
964   DBUG_ASSERT(field != nullptr);
965   const Rdb_dict_manager *dict_manager = rdb_get_dict_manager();
966 
967   const std::string &dbname = tdef->base_dbname();
968   field[RDB_DDL_FIELD::TABLE_SCHEMA]->store(dbname.c_str(), dbname.size(),
969                                             system_charset_info);
970 
971   const std::string &tablename = tdef->base_tablename();
972   field[RDB_DDL_FIELD::TABLE_NAME]->store(tablename.c_str(), tablename.size(),
973                                           system_charset_info);
974 
975   const std::string &partname = tdef->base_partition();
976   if (partname.length() == 0) {
977     field[RDB_DDL_FIELD::PARTITION_NAME]->set_null();
978   } else {
979     field[RDB_DDL_FIELD::PARTITION_NAME]->set_notnull();
980     field[RDB_DDL_FIELD::PARTITION_NAME]->store(
981         partname.c_str(), partname.size(), system_charset_info);
982   }
983 
984   for (uint i = 0; i < tdef->m_key_count; i++) {
985     const Rdb_key_def &kd = *tdef->m_key_descr_arr[i];
986 
987     field[RDB_DDL_FIELD::INDEX_NAME]->store(kd.m_name.c_str(), kd.m_name.size(),
988                                             system_charset_info);
989 
990     GL_INDEX_ID gl_index_id = kd.get_gl_index_id();
991     field[RDB_DDL_FIELD::COLUMN_FAMILY]->store(gl_index_id.cf_id, true);
992     field[RDB_DDL_FIELD::INDEX_NUMBER]->store(gl_index_id.index_id, true);
993     field[RDB_DDL_FIELD::INDEX_TYPE]->store(kd.m_index_type, true);
994     field[RDB_DDL_FIELD::KV_FORMAT_VERSION]->store(kd.m_kv_format_version,
995                                                    true);
996     field[RDB_DDL_FIELD::TTL_DURATION]->store(kd.m_ttl_duration, true);
997     field[RDB_DDL_FIELD::INDEX_FLAGS]->store(kd.m_index_flags_bitmap, true);
998 
999     std::string cf_name = kd.get_cf()->GetName();
1000     field[RDB_DDL_FIELD::CF]->store(cf_name.c_str(), cf_name.size(),
1001                                     system_charset_info);
1002     ulonglong auto_incr;
1003     if (dict_manager->get_auto_incr_val(tdef->get_autoincr_gl_index_id(),
1004                                         &auto_incr)) {
1005       field[RDB_DDL_FIELD::AUTO_INCREMENT]->set_notnull();
1006       field[RDB_DDL_FIELD::AUTO_INCREMENT]->store(auto_incr, true);
1007     } else {
1008       field[RDB_DDL_FIELD::AUTO_INCREMENT]->set_null();
1009     }
1010 
1011     ret = my_core::schema_table_store_record(m_thd, m_table);
1012     if (ret) return ret;
1013   }
1014   return HA_EXIT_SUCCESS;
1015 }
1016 
rdb_i_s_ddl_fill_table(my_core::THD * const thd,my_core::TABLE_LIST * const tables,my_core::Item * const cond)1017 static int rdb_i_s_ddl_fill_table(my_core::THD *const thd,
1018                                   my_core::TABLE_LIST *const tables,
1019                                   my_core::Item *const cond) {
1020   DBUG_ENTER_FUNC();
1021 
1022   DBUG_ASSERT(thd != nullptr);
1023   DBUG_ASSERT(tables != nullptr);
1024   DBUG_ASSERT(tables->table != nullptr);
1025 
1026   int ret = 0;
1027   rocksdb::DB *const rdb = rdb_get_rocksdb_db();
1028 
1029   if (!rdb) {
1030     DBUG_RETURN(ret);
1031   }
1032 
1033   Rdb_ddl_scanner ddl_arg;
1034 
1035   ddl_arg.m_thd = thd;
1036   ddl_arg.m_table = tables->table;
1037 
1038   Rdb_ddl_manager *ddl_manager = rdb_get_ddl_manager();
1039   DBUG_ASSERT(ddl_manager != nullptr);
1040 
1041   ret = ddl_manager->scan_for_tables(&ddl_arg);
1042 
1043   DBUG_RETURN(ret);
1044 }
1045 
rdb_i_s_ddl_init(void * const p)1046 static int rdb_i_s_ddl_init(void *const p) {
1047   DBUG_ENTER_FUNC();
1048 
1049   if (prevent_myrocks_loading)
1050     DBUG_RETURN(1);
1051 
1052   my_core::ST_SCHEMA_TABLE *schema;
1053 
1054   DBUG_ASSERT(p != nullptr);
1055 
1056   schema = (my_core::ST_SCHEMA_TABLE *)p;
1057 
1058   schema->fields_info = rdb_i_s_ddl_fields_info;
1059   schema->fill_table = rdb_i_s_ddl_fill_table;
1060 
1061   DBUG_RETURN(0);
1062 }
1063 
rdb_i_s_cfoptions_init(void * const p)1064 static int rdb_i_s_cfoptions_init(void *const p) {
1065   DBUG_ENTER_FUNC();
1066 
1067   if (prevent_myrocks_loading)
1068     DBUG_RETURN(1);
1069 
1070   DBUG_ASSERT(p != nullptr);
1071 
1072   my_core::ST_SCHEMA_TABLE *schema;
1073 
1074   schema = (my_core::ST_SCHEMA_TABLE *)p;
1075 
1076   schema->fields_info = rdb_i_s_cfoptions_fields_info;
1077   schema->fill_table = rdb_i_s_cfoptions_fill_table;
1078 
1079   DBUG_RETURN(0);
1080 }
1081 
rdb_i_s_global_info_init(void * const p)1082 static int rdb_i_s_global_info_init(void *const p) {
1083   DBUG_ENTER_FUNC();
1084 
1085   if (prevent_myrocks_loading)
1086     DBUG_RETURN(1);
1087 
1088   DBUG_ASSERT(p != nullptr);
1089 
1090   my_core::ST_SCHEMA_TABLE *schema;
1091 
1092   schema = reinterpret_cast<my_core::ST_SCHEMA_TABLE *>(p);
1093 
1094   schema->fields_info = rdb_i_s_global_info_fields_info;
1095   schema->fill_table = rdb_i_s_global_info_fill_table;
1096 
1097   DBUG_RETURN(0);
1098 }
1099 
rdb_i_s_compact_stats_init(void * p)1100 static int rdb_i_s_compact_stats_init(void *p) {
1101   my_core::ST_SCHEMA_TABLE *schema;
1102 
1103   DBUG_ENTER_FUNC();
1104 
1105   if (prevent_myrocks_loading)
1106     DBUG_RETURN(1);
1107 
1108   DBUG_ASSERT(p != nullptr);
1109 
1110   schema = reinterpret_cast<my_core::ST_SCHEMA_TABLE *>(p);
1111 
1112   schema->fields_info = rdb_i_s_compact_stats_fields_info;
1113   schema->fill_table = rdb_i_s_compact_stats_fill_table;
1114 
1115   DBUG_RETURN(0);
1116 }
1117 
1118 /* Given a path to a file return just the filename portion. */
rdb_filename_without_path(const std::string & path)1119 static std::string rdb_filename_without_path(const std::string &path) {
1120   /* Find last slash in path */
1121   const size_t pos = path.rfind('/');
1122 
1123   /* None found?  Just return the original string */
1124   if (pos == std::string::npos) {
1125     return std::string(path);
1126   }
1127 
1128   /* Return everything after the slash (or backslash) */
1129   return path.substr(pos + 1);
1130 }
1131 
1132 /*
1133   Support for INFORMATION_SCHEMA.ROCKSDB_SST_PROPS dynamic table
1134  */
1135 namespace RDB_SST_PROPS_FIELD {
1136 enum {
1137   SST_NAME = 0,
1138   COLUMN_FAMILY,
1139   DATA_BLOCKS,
1140   ENTRIES,
1141   RAW_KEY_SIZE,
1142   RAW_VALUE_SIZE,
1143   DATA_BLOCK_SIZE,
1144   INDEX_BLOCK_SIZE,
1145   INDEX_PARTITIONS,
1146   TOP_LEVEL_INDEX_SIZE,
1147   FILTER_BLOCK_SIZE,
1148   COMPRESSION_ALGO,
1149   CREATION_TIME,
1150   FILE_CREATION_TIME,
1151   OLDEST_KEY_TIME,
1152   FILTER_POLICY,
1153   COMPRESSION_OPTIONS,
1154 };
1155 }  // namespace RDB_SST_PROPS_FIELD
1156 
1157 static ST_FIELD_INFO rdb_i_s_sst_props_fields_info[] = {
1158     ROCKSDB_FIELD_INFO("SST_NAME", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
1159     ROCKSDB_FIELD_INFO("COLUMN_FAMILY", sizeof(uint32_t), MYSQL_TYPE_LONG, 0),
1160     ROCKSDB_FIELD_INFO("DATA_BLOCKS", sizeof(int64_t), MYSQL_TYPE_LONGLONG, 0),
1161     ROCKSDB_FIELD_INFO("ENTRIES", sizeof(int64_t), MYSQL_TYPE_LONGLONG, 0),
1162     ROCKSDB_FIELD_INFO("RAW_KEY_SIZE", sizeof(int64_t), MYSQL_TYPE_LONGLONG, 0),
1163     ROCKSDB_FIELD_INFO("RAW_VALUE_SIZE", sizeof(int64_t), MYSQL_TYPE_LONGLONG,
1164                        0),
1165     ROCKSDB_FIELD_INFO("DATA_BLOCK_SIZE", sizeof(int64_t), MYSQL_TYPE_LONGLONG,
1166                        0),
1167     ROCKSDB_FIELD_INFO("INDEX_BLOCK_SIZE", sizeof(int64_t), MYSQL_TYPE_LONGLONG,
1168                        0),
1169     ROCKSDB_FIELD_INFO("INDEX_PARTITIONS", sizeof(uint32_t), MYSQL_TYPE_LONG,
1170                        0),
1171     ROCKSDB_FIELD_INFO("TOP_LEVEL_INDEX_SIZE", sizeof(int64_t),
1172                        MYSQL_TYPE_LONGLONG, 0),
1173     ROCKSDB_FIELD_INFO("FILTER_BLOCK_SIZE", sizeof(int64_t),
1174                        MYSQL_TYPE_LONGLONG, 0),
1175     ROCKSDB_FIELD_INFO("COMPRESSION_ALGO", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
1176     ROCKSDB_FIELD_INFO("CREATION_TIME", sizeof(int64_t), MYSQL_TYPE_LONGLONG,
1177                        0),
1178     ROCKSDB_FIELD_INFO("FILE_CREATION_TIME", sizeof(int64_t),
1179                        MYSQL_TYPE_LONGLONG, 0),
1180     ROCKSDB_FIELD_INFO("OLDEST_KEY_TIME", sizeof(int64_t), MYSQL_TYPE_LONGLONG,
1181                        0),
1182     ROCKSDB_FIELD_INFO("FILTER_POLICY", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
1183     ROCKSDB_FIELD_INFO("COMPRESSION_OPTIONS", NAME_LEN + 1, MYSQL_TYPE_STRING,
1184                        0),
1185     ROCKSDB_FIELD_INFO_END};
1186 
rdb_i_s_sst_props_fill_table(my_core::THD * const thd,my_core::TABLE_LIST * const tables,my_core::Item * const cond MY_ATTRIBUTE ((__unused__)))1187 static int rdb_i_s_sst_props_fill_table(
1188     my_core::THD *const thd, my_core::TABLE_LIST *const tables,
1189     my_core::Item *const cond MY_ATTRIBUTE((__unused__))) {
1190   DBUG_ENTER_FUNC();
1191 
1192   DBUG_ASSERT(thd != nullptr);
1193   DBUG_ASSERT(tables != nullptr);
1194   DBUG_ASSERT(tables->table != nullptr);
1195 
1196   int ret = 0;
1197   Field **field = tables->table->field;
1198   DBUG_ASSERT(field != nullptr);
1199 
1200   /* Iterate over all the column families */
1201   rocksdb::DB *const rdb = rdb_get_rocksdb_db();
1202 
1203   if (!rdb) {
1204     DBUG_RETURN(ret);
1205   }
1206 
1207   const Rdb_cf_manager &cf_manager = rdb_get_cf_manager();
1208 
1209   for (const auto &cf_handle : cf_manager.get_all_cf()) {
1210     /* Grab the the properties of all the tables in the column family */
1211     rocksdb::TablePropertiesCollection table_props_collection;
1212     const rocksdb::Status s =
1213         rdb->GetPropertiesOfAllTables(cf_handle, &table_props_collection);
1214 
1215     if (!s.ok()) {
1216       continue;
1217     }
1218 
1219     /* Iterate over all the items in the collection, each of which contains a
1220      * name and the actual properties */
1221     for (const auto &props : table_props_collection) {
1222       /* Add the SST name into the output */
1223       const std::string sst_name = rdb_filename_without_path(props.first);
1224 
1225       field[RDB_SST_PROPS_FIELD::SST_NAME]->store(
1226           sst_name.data(), sst_name.size(), system_charset_info);
1227 
1228       field[RDB_SST_PROPS_FIELD::COLUMN_FAMILY]->store(
1229           props.second->column_family_id, true);
1230       field[RDB_SST_PROPS_FIELD::DATA_BLOCKS]->store(
1231           props.second->num_data_blocks, true);
1232       field[RDB_SST_PROPS_FIELD::ENTRIES]->store(props.second->num_entries,
1233                                                  true);
1234       field[RDB_SST_PROPS_FIELD::RAW_KEY_SIZE]->store(
1235           props.second->raw_key_size, true);
1236       field[RDB_SST_PROPS_FIELD::RAW_VALUE_SIZE]->store(
1237           props.second->raw_value_size, true);
1238       field[RDB_SST_PROPS_FIELD::DATA_BLOCK_SIZE]->store(
1239           props.second->data_size, true);
1240       field[RDB_SST_PROPS_FIELD::INDEX_BLOCK_SIZE]->store(
1241           props.second->index_size, true);
1242       field[RDB_SST_PROPS_FIELD::INDEX_PARTITIONS]->store(
1243           props.second->index_partitions, true);
1244       field[RDB_SST_PROPS_FIELD::TOP_LEVEL_INDEX_SIZE]->store(
1245           props.second->top_level_index_size, true);
1246       field[RDB_SST_PROPS_FIELD::FILTER_BLOCK_SIZE]->store(
1247           props.second->filter_size, true);
1248       if (props.second->compression_name.empty()) {
1249         field[RDB_SST_PROPS_FIELD::COMPRESSION_ALGO]->set_null();
1250       } else {
1251         field[RDB_SST_PROPS_FIELD::COMPRESSION_ALGO]->store(
1252             props.second->compression_name.c_str(),
1253             props.second->compression_name.size(), system_charset_info);
1254       }
1255       field[RDB_SST_PROPS_FIELD::CREATION_TIME]->store(
1256           props.second->creation_time, true);
1257       field[RDB_SST_PROPS_FIELD::FILE_CREATION_TIME]->store(
1258           props.second->file_creation_time, true);
1259       field[RDB_SST_PROPS_FIELD::OLDEST_KEY_TIME]->store(
1260           props.second->oldest_key_time, true);
1261       if (props.second->filter_policy_name.empty()) {
1262         field[RDB_SST_PROPS_FIELD::FILTER_POLICY]->set_null();
1263       } else {
1264         field[RDB_SST_PROPS_FIELD::FILTER_POLICY]->store(
1265             props.second->filter_policy_name.c_str(),
1266             props.second->filter_policy_name.size(), system_charset_info);
1267       }
1268       if (props.second->compression_options.empty()) {
1269         field[RDB_SST_PROPS_FIELD::COMPRESSION_OPTIONS]->set_null();
1270       } else {
1271         field[RDB_SST_PROPS_FIELD::COMPRESSION_OPTIONS]->store(
1272             props.second->compression_options.c_str(),
1273             props.second->compression_options.size(), system_charset_info);
1274       }
1275 
1276       /* Tell MySQL about this row in the virtual table */
1277       ret = static_cast<int>(
1278           my_core::schema_table_store_record(thd, tables->table));
1279 
1280       if (ret != 0) {
1281         DBUG_RETURN(ret);
1282       }
1283     }
1284   }
1285 
1286   DBUG_RETURN(ret);
1287 }
1288 
1289 /* Initialize the information_schema.rocksdb_sst_props virtual table */
rdb_i_s_sst_props_init(void * const p)1290 static int rdb_i_s_sst_props_init(void *const p) {
1291   DBUG_ENTER_FUNC();
1292 
1293   DBUG_ASSERT(p != nullptr);
1294 
1295   my_core::ST_SCHEMA_TABLE *schema;
1296 
1297   schema = (my_core::ST_SCHEMA_TABLE *)p;
1298 
1299   schema->fields_info = rdb_i_s_sst_props_fields_info;
1300   schema->fill_table = rdb_i_s_sst_props_fill_table;
1301 
1302   DBUG_RETURN(0);
1303 }
1304 
1305 /*
1306   Support for INFORMATION_SCHEMA.ROCKSDB_INDEX_FILE_MAP dynamic table
1307  */
1308 namespace RDB_INDEX_FILE_MAP_FIELD {
1309 enum {
1310   COLUMN_FAMILY = 0,
1311   INDEX_NUMBER,
1312   SST_NAME,
1313   NUM_ROWS,
1314   DATA_SIZE,
1315   ENTRY_DELETES,
1316   ENTRY_SINGLEDELETES,
1317   ENTRY_MERGES,
1318   ENTRY_OTHERS,
1319   DISTINCT_KEYS_PREFIX
1320 };
1321 }  // namespace RDB_INDEX_FILE_MAP_FIELD
1322 
1323 static ST_FIELD_INFO rdb_i_s_index_file_map_fields_info[] = {
1324     /* The information_schema.rocksdb_index_file_map virtual table has four
1325      * fields:
1326      *   COLUMN_FAMILY => the index's column family contained in the SST file
1327      *   INDEX_NUMBER => the index id contained in the SST file
1328      *   SST_NAME => the name of the SST file containing some indexes
1329      *   NUM_ROWS => the number of entries of this index id in this SST file
1330      *   DATA_SIZE => the data size stored in this SST file for this index id */
1331     ROCKSDB_FIELD_INFO("COLUMN_FAMILY", sizeof(uint32_t), MYSQL_TYPE_LONG, 0),
1332     ROCKSDB_FIELD_INFO("INDEX_NUMBER", sizeof(uint32_t), MYSQL_TYPE_LONG, 0),
1333     ROCKSDB_FIELD_INFO("SST_NAME", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
1334     ROCKSDB_FIELD_INFO("NUM_ROWS", sizeof(int64_t), MYSQL_TYPE_LONGLONG, 0),
1335     ROCKSDB_FIELD_INFO("DATA_SIZE", sizeof(int64_t), MYSQL_TYPE_LONGLONG, 0),
1336     ROCKSDB_FIELD_INFO("ENTRY_DELETES", sizeof(int64_t), MYSQL_TYPE_LONGLONG,
1337                        0),
1338     ROCKSDB_FIELD_INFO("ENTRY_SINGLEDELETES", sizeof(int64_t),
1339                        MYSQL_TYPE_LONGLONG, 0),
1340     ROCKSDB_FIELD_INFO("ENTRY_MERGES", sizeof(int64_t), MYSQL_TYPE_LONGLONG, 0),
1341     ROCKSDB_FIELD_INFO("ENTRY_OTHERS", sizeof(int64_t), MYSQL_TYPE_LONGLONG, 0),
1342     ROCKSDB_FIELD_INFO("DISTINCT_KEYS_PREFIX", MAX_REF_PARTS * 25,
1343                        MYSQL_TYPE_STRING, 0),
1344     ROCKSDB_FIELD_INFO_END};
1345 
1346 /* Fill the information_schema.rocksdb_index_file_map virtual table */
rdb_i_s_index_file_map_fill_table(my_core::THD * const thd,my_core::TABLE_LIST * const tables,my_core::Item * const cond MY_ATTRIBUTE ((__unused__)))1347 static int rdb_i_s_index_file_map_fill_table(
1348     my_core::THD *const thd, my_core::TABLE_LIST *const tables,
1349     my_core::Item *const cond MY_ATTRIBUTE((__unused__))) {
1350   DBUG_ENTER_FUNC();
1351 
1352   DBUG_ASSERT(thd != nullptr);
1353   DBUG_ASSERT(tables != nullptr);
1354   DBUG_ASSERT(tables->table != nullptr);
1355 
1356   int ret = 0;
1357   Field **field = tables->table->field;
1358   DBUG_ASSERT(field != nullptr);
1359 
1360   /* Iterate over all the column families */
1361   rocksdb::DB *const rdb = rdb_get_rocksdb_db();
1362 
1363   if (!rdb) {
1364     DBUG_RETURN(ret);
1365   }
1366 
1367   const Rdb_cf_manager &cf_manager = rdb_get_cf_manager();
1368 
1369   for (const auto &cf_handle : cf_manager.get_all_cf()) {
1370     /* Grab the the properties of all the tables in the column family */
1371     rocksdb::TablePropertiesCollection table_props_collection;
1372     const rocksdb::Status s =
1373         rdb->GetPropertiesOfAllTables(cf_handle, &table_props_collection);
1374 
1375     if (!s.ok()) {
1376       continue;
1377     }
1378 
1379     /* Iterate over all the items in the collection, each of which contains a
1380      * name and the actual properties */
1381     for (const auto &props : table_props_collection) {
1382       /* Add the SST name into the output */
1383       const std::string sst_name = rdb_filename_without_path(props.first);
1384 
1385       field[RDB_INDEX_FILE_MAP_FIELD::SST_NAME]->store(
1386           sst_name.data(), sst_name.size(), system_charset_info);
1387 
1388       /* Get the __indexstats__ data out of the table property */
1389       std::vector<Rdb_index_stats> stats;
1390       Rdb_tbl_prop_coll::read_stats_from_tbl_props(props.second, &stats);
1391 
1392       if (stats.empty()) {
1393         field[RDB_INDEX_FILE_MAP_FIELD::COLUMN_FAMILY]->store(-1, true);
1394         field[RDB_INDEX_FILE_MAP_FIELD::INDEX_NUMBER]->store(-1, true);
1395         field[RDB_INDEX_FILE_MAP_FIELD::NUM_ROWS]->store(-1, true);
1396         field[RDB_INDEX_FILE_MAP_FIELD::DATA_SIZE]->store(-1, true);
1397         field[RDB_INDEX_FILE_MAP_FIELD::ENTRY_DELETES]->store(-1, true);
1398         field[RDB_INDEX_FILE_MAP_FIELD::ENTRY_SINGLEDELETES]->store(-1, true);
1399         field[RDB_INDEX_FILE_MAP_FIELD::ENTRY_MERGES]->store(-1, true);
1400         field[RDB_INDEX_FILE_MAP_FIELD::ENTRY_OTHERS]->store(-1, true);
1401       } else {
1402         for (const auto &it : stats) {
1403           /* Add the index number, the number of rows, and data size to the
1404            * output */
1405           field[RDB_INDEX_FILE_MAP_FIELD::COLUMN_FAMILY]->store(
1406               it.m_gl_index_id.cf_id, true);
1407           field[RDB_INDEX_FILE_MAP_FIELD::INDEX_NUMBER]->store(
1408               it.m_gl_index_id.index_id, true);
1409           field[RDB_INDEX_FILE_MAP_FIELD::NUM_ROWS]->store(it.m_rows, true);
1410           field[RDB_INDEX_FILE_MAP_FIELD::DATA_SIZE]->store(it.m_data_size,
1411                                                             true);
1412           field[RDB_INDEX_FILE_MAP_FIELD::ENTRY_DELETES]->store(
1413               it.m_entry_deletes, true);
1414           field[RDB_INDEX_FILE_MAP_FIELD::ENTRY_SINGLEDELETES]->store(
1415               it.m_entry_single_deletes, true);
1416           field[RDB_INDEX_FILE_MAP_FIELD::ENTRY_MERGES]->store(
1417               it.m_entry_merges, true);
1418           field[RDB_INDEX_FILE_MAP_FIELD::ENTRY_OTHERS]->store(
1419               it.m_entry_others, true);
1420 
1421           std::string distinct_keys_prefix;
1422 
1423           for (size_t i = 0; i < it.m_distinct_keys_per_prefix.size(); i++) {
1424             if (i > 0) {
1425               distinct_keys_prefix += ",";
1426             }
1427 
1428             distinct_keys_prefix +=
1429                 std::to_string(it.m_distinct_keys_per_prefix[i]);
1430           }
1431 
1432           field[RDB_INDEX_FILE_MAP_FIELD::DISTINCT_KEYS_PREFIX]->store(
1433               distinct_keys_prefix.data(), distinct_keys_prefix.size(),
1434               system_charset_info);
1435 
1436           /* Tell MySQL about this row in the virtual table */
1437           ret = static_cast<int>(
1438               my_core::schema_table_store_record(thd, tables->table));
1439 
1440           if (ret != 0) {
1441             break;
1442           }
1443         }
1444       }
1445     }
1446   }
1447 
1448   DBUG_RETURN(ret);
1449 }
1450 
1451 /* Initialize the information_schema.rocksdb_index_file_map virtual table */
rdb_i_s_index_file_map_init(void * const p)1452 static int rdb_i_s_index_file_map_init(void *const p) {
1453   DBUG_ENTER_FUNC();
1454 
1455   if (prevent_myrocks_loading)
1456     DBUG_RETURN(1);
1457 
1458   DBUG_ASSERT(p != nullptr);
1459 
1460   my_core::ST_SCHEMA_TABLE *schema;
1461 
1462   schema = (my_core::ST_SCHEMA_TABLE *)p;
1463 
1464   schema->fields_info = rdb_i_s_index_file_map_fields_info;
1465   schema->fill_table = rdb_i_s_index_file_map_fill_table;
1466 
1467   DBUG_RETURN(0);
1468 }
1469 
1470 /*
1471   Support for INFORMATION_SCHEMA.ROCKSDB_LOCKS dynamic table
1472  */
1473 namespace RDB_LOCKS_FIELD {
1474 enum { COLUMN_FAMILY_ID = 0, TRANSACTION_ID, KEY, MODE };
1475 }  // namespace RDB_LOCKS_FIELD
1476 
1477 static ST_FIELD_INFO rdb_i_s_lock_info_fields_info[] = {
1478     ROCKSDB_FIELD_INFO("COLUMN_FAMILY_ID", sizeof(uint32_t), MYSQL_TYPE_LONG,
1479                        0),
1480     ROCKSDB_FIELD_INFO("TRANSACTION_ID", sizeof(uint32_t), MYSQL_TYPE_LONG, 0),
1481     ROCKSDB_FIELD_INFO("KEY", FN_REFLEN + 1, MYSQL_TYPE_STRING, 0),
1482     ROCKSDB_FIELD_INFO("MODE", 32, MYSQL_TYPE_STRING, 0),
1483     ROCKSDB_FIELD_INFO_END};
1484 
1485 /* Fill the information_schema.rocksdb_locks virtual table */
rdb_i_s_lock_info_fill_table(my_core::THD * const thd,my_core::TABLE_LIST * const tables,my_core::Item * const cond MY_ATTRIBUTE ((__unused__)))1486 static int rdb_i_s_lock_info_fill_table(
1487     my_core::THD *const thd, my_core::TABLE_LIST *const tables,
1488     my_core::Item *const cond MY_ATTRIBUTE((__unused__))) {
1489   DBUG_ENTER_FUNC();
1490 
1491   DBUG_ASSERT(thd != nullptr);
1492   DBUG_ASSERT(tables != nullptr);
1493   DBUG_ASSERT(tables->table != nullptr);
1494   DBUG_ASSERT(tables->table->field != nullptr);
1495 
1496   int ret = 0;
1497 
1498   rocksdb::TransactionDB *const rdb = rdb_get_rocksdb_db();
1499 
1500   if (!rdb) {
1501     DBUG_RETURN(ret);
1502   }
1503 
1504   /* cf id -> rocksdb::KeyLockInfo */
1505   std::unordered_multimap<uint32_t, rocksdb::KeyLockInfo> lock_info =
1506       rdb->GetLockStatusData();
1507 
1508   for (const auto &lock : lock_info) {
1509     const uint32_t cf_id = lock.first;
1510     const auto &key_lock_info = lock.second;
1511     const auto key_hexstr = rdb_hexdump(key_lock_info.key.c_str(),
1512                                         key_lock_info.key.length(), FN_REFLEN);
1513 
1514     for (const auto &id : key_lock_info.ids) {
1515       tables->table->field[RDB_LOCKS_FIELD::COLUMN_FAMILY_ID]->store(cf_id,
1516                                                                      true);
1517       tables->table->field[RDB_LOCKS_FIELD::TRANSACTION_ID]->store(id, true);
1518 
1519       tables->table->field[RDB_LOCKS_FIELD::KEY]->store(
1520           key_hexstr.c_str(), key_hexstr.size(), system_charset_info);
1521       tables->table->field[RDB_LOCKS_FIELD::MODE]->store(
1522           key_lock_info.exclusive ? "X" : "S", 1, system_charset_info);
1523 
1524       /* Tell MySQL about this row in the virtual table */
1525       ret = static_cast<int>(
1526           my_core::schema_table_store_record(thd, tables->table));
1527 
1528       if (ret != 0) {
1529         break;
1530       }
1531     }
1532   }
1533 
1534   DBUG_RETURN(ret);
1535 }
1536 
1537 /* Initialize the information_schema.rocksdb_lock_info virtual table */
rdb_i_s_lock_info_init(void * const p)1538 static int rdb_i_s_lock_info_init(void *const p) {
1539   DBUG_ENTER_FUNC();
1540 
1541   if (prevent_myrocks_loading)
1542     DBUG_RETURN(1);
1543 
1544   DBUG_ASSERT(p != nullptr);
1545 
1546   my_core::ST_SCHEMA_TABLE *schema;
1547 
1548   schema = (my_core::ST_SCHEMA_TABLE *)p;
1549 
1550   schema->fields_info = rdb_i_s_lock_info_fields_info;
1551   schema->fill_table = rdb_i_s_lock_info_fill_table;
1552 
1553   DBUG_RETURN(0);
1554 }
1555 
1556 /*
1557   Support for INFORMATION_SCHEMA.ROCKSDB_TRX dynamic table
1558  */
1559 namespace RDB_TRX_FIELD {
1560 enum {
1561   TRANSACTION_ID = 0,
1562   STATE,
1563   NAME,
1564   WRITE_COUNT,
1565   LOCK_COUNT,
1566   TIMEOUT_SEC,
1567   WAITING_KEY,
1568   WAITING_COLUMN_FAMILY_ID,
1569   IS_REPLICATION,
1570   SKIP_TRX_API,
1571   READ_ONLY,
1572   HAS_DEADLOCK_DETECTION,
1573   NUM_ONGOING_BULKLOAD,
1574   THREAD_ID,
1575   QUERY
1576 };
1577 }  // namespace RDB_TRX_FIELD
1578 
1579 static ST_FIELD_INFO rdb_i_s_trx_info_fields_info[] = {
1580     ROCKSDB_FIELD_INFO("TRANSACTION_ID", sizeof(ulonglong), MYSQL_TYPE_LONGLONG,
1581                        0),
1582     ROCKSDB_FIELD_INFO("STATE", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
1583     ROCKSDB_FIELD_INFO("NAME", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
1584     ROCKSDB_FIELD_INFO("WRITE_COUNT", sizeof(ulonglong), MYSQL_TYPE_LONGLONG,
1585                        0),
1586     ROCKSDB_FIELD_INFO("LOCK_COUNT", sizeof(ulonglong), MYSQL_TYPE_LONGLONG, 0),
1587     ROCKSDB_FIELD_INFO("TIMEOUT_SEC", sizeof(uint32_t), MYSQL_TYPE_LONG, 0),
1588     ROCKSDB_FIELD_INFO("WAITING_KEY", FN_REFLEN + 1, MYSQL_TYPE_STRING, 0),
1589     ROCKSDB_FIELD_INFO("WAITING_COLUMN_FAMILY_ID", sizeof(uint32_t),
1590                        MYSQL_TYPE_LONG, 0),
1591     ROCKSDB_FIELD_INFO("IS_REPLICATION", sizeof(uint32_t), MYSQL_TYPE_LONG, 0),
1592     ROCKSDB_FIELD_INFO("SKIP_TRX_API", sizeof(uint32_t), MYSQL_TYPE_LONG, 0),
1593     ROCKSDB_FIELD_INFO("READ_ONLY", sizeof(uint32_t), MYSQL_TYPE_LONG, 0),
1594     ROCKSDB_FIELD_INFO("HAS_DEADLOCK_DETECTION", sizeof(uint32_t),
1595                        MYSQL_TYPE_LONG, 0),
1596     ROCKSDB_FIELD_INFO("NUM_ONGOING_BULKLOAD", sizeof(uint32_t),
1597                        MYSQL_TYPE_LONG, 0),
1598     ROCKSDB_FIELD_INFO("THREAD_ID", sizeof(ulong), MYSQL_TYPE_LONG, 0),
1599     ROCKSDB_FIELD_INFO("QUERY", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
1600     ROCKSDB_FIELD_INFO_END};
1601 
1602 /* Fill the information_schema.rocksdb_trx virtual table */
rdb_i_s_trx_info_fill_table(my_core::THD * const thd,my_core::TABLE_LIST * const tables,my_core::Item * const cond MY_ATTRIBUTE ((__unused__)))1603 static int rdb_i_s_trx_info_fill_table(
1604     my_core::THD *const thd, my_core::TABLE_LIST *const tables,
1605     my_core::Item *const cond MY_ATTRIBUTE((__unused__))) {
1606   DBUG_ENTER_FUNC();
1607 
1608   DBUG_ASSERT(thd != nullptr);
1609   DBUG_ASSERT(tables != nullptr);
1610   DBUG_ASSERT(tables->table != nullptr);
1611   DBUG_ASSERT(tables->table->field != nullptr);
1612 
1613   int ret = 0;
1614   rocksdb::DB *const rdb = rdb_get_rocksdb_db();
1615 
1616   if (!rdb) {
1617     DBUG_RETURN(ret);
1618   }
1619 
1620   const std::vector<Rdb_trx_info> &all_trx_info = rdb_get_all_trx_info();
1621 
1622   for (const auto &info : all_trx_info) {
1623     auto name_hexstr =
1624         rdb_hexdump(info.name.c_str(), info.name.length(), NAME_LEN);
1625     auto key_hexstr = rdb_hexdump(info.waiting_key.c_str(),
1626                                   info.waiting_key.length(), FN_REFLEN);
1627 
1628     tables->table->field[RDB_TRX_FIELD::TRANSACTION_ID]->store(info.trx_id,
1629                                                                true);
1630     tables->table->field[RDB_TRX_FIELD::STATE]->store(
1631         info.state.c_str(), info.state.length(), system_charset_info);
1632     tables->table->field[RDB_TRX_FIELD::NAME]->store(
1633         name_hexstr.c_str(), name_hexstr.length(), system_charset_info);
1634     tables->table->field[RDB_TRX_FIELD::WRITE_COUNT]->store(info.write_count,
1635                                                             true);
1636     tables->table->field[RDB_TRX_FIELD::LOCK_COUNT]->store(info.lock_count,
1637                                                            true);
1638     tables->table->field[RDB_TRX_FIELD::TIMEOUT_SEC]->store(info.timeout_sec,
1639                                                             false);
1640     tables->table->field[RDB_TRX_FIELD::WAITING_KEY]->store(
1641         key_hexstr.c_str(), key_hexstr.length(), system_charset_info);
1642     tables->table->field[RDB_TRX_FIELD::WAITING_COLUMN_FAMILY_ID]->store(
1643         info.waiting_cf_id, true);
1644     tables->table->field[RDB_TRX_FIELD::IS_REPLICATION]->store(
1645         info.is_replication, false);
1646     tables->table->field[RDB_TRX_FIELD::SKIP_TRX_API]->store(info.skip_trx_api,
1647                                                              false);
1648     tables->table->field[RDB_TRX_FIELD::READ_ONLY]->store(info.read_only,
1649                                                           false);
1650     tables->table->field[RDB_TRX_FIELD::HAS_DEADLOCK_DETECTION]->store(
1651         info.deadlock_detect, false);
1652     tables->table->field[RDB_TRX_FIELD::NUM_ONGOING_BULKLOAD]->store(
1653         info.num_ongoing_bulk_load, false);
1654     tables->table->field[RDB_TRX_FIELD::THREAD_ID]->store(info.thread_id, true);
1655     tables->table->field[RDB_TRX_FIELD::QUERY]->store(
1656         info.query_str.c_str(), info.query_str.length(), system_charset_info);
1657 
1658     /* Tell MySQL about this row in the virtual table */
1659     ret = static_cast<int>(
1660         my_core::schema_table_store_record(thd, tables->table));
1661 
1662     if (ret != 0) {
1663       break;
1664     }
1665   }
1666 
1667   DBUG_RETURN(ret);
1668 }
1669 
1670 /* Initialize the information_schema.rocksdb_trx_info virtual table */
rdb_i_s_trx_info_init(void * const p)1671 static int rdb_i_s_trx_info_init(void *const p) {
1672   DBUG_ENTER_FUNC();
1673 
1674   if (prevent_myrocks_loading)
1675     DBUG_RETURN(1);
1676 
1677   DBUG_ASSERT(p != nullptr);
1678 
1679   my_core::ST_SCHEMA_TABLE *schema;
1680 
1681   schema = (my_core::ST_SCHEMA_TABLE *)p;
1682 
1683   schema->fields_info = rdb_i_s_trx_info_fields_info;
1684   schema->fill_table = rdb_i_s_trx_info_fill_table;
1685 
1686   DBUG_RETURN(0);
1687 }
1688 
1689 /*
1690   Support for INFORMATION_SCHEMA.ROCKSDB_DEADLOCK dynamic table
1691  */
1692 namespace RDB_DEADLOCK_FIELD {
1693 enum {
1694   DEADLOCK_ID = 0,
1695   TIMESTAMP,
1696   TRANSACTION_ID,
1697   CF_NAME,
1698   WAITING_KEY,
1699   LOCK_TYPE,
1700   INDEX_NAME,
1701   TABLE_NAME,
1702   ROLLED_BACK,
1703 };
1704 }  // namespace RDB_DEADLOCK_FIELD
1705 
1706 static ST_FIELD_INFO rdb_i_s_deadlock_info_fields_info[] = {
1707     ROCKSDB_FIELD_INFO("DEADLOCK_ID", sizeof(ulonglong), MYSQL_TYPE_LONGLONG,
1708                        0),
1709     ROCKSDB_FIELD_INFO("TIMESTAMP", sizeof(ulonglong), MYSQL_TYPE_LONGLONG, 0),
1710     ROCKSDB_FIELD_INFO("TRANSACTION_ID", sizeof(ulonglong), MYSQL_TYPE_LONGLONG,
1711                        0),
1712     ROCKSDB_FIELD_INFO("CF_NAME", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
1713     ROCKSDB_FIELD_INFO("WAITING_KEY", FN_REFLEN + 1, MYSQL_TYPE_STRING, 0),
1714     ROCKSDB_FIELD_INFO("LOCK_TYPE", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
1715     ROCKSDB_FIELD_INFO("INDEX_NAME", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
1716     ROCKSDB_FIELD_INFO("TABLE_NAME", NAME_LEN + 1, MYSQL_TYPE_STRING, 0),
1717     ROCKSDB_FIELD_INFO("ROLLED_BACK", sizeof(ulonglong), MYSQL_TYPE_LONGLONG,
1718                        0),
1719     ROCKSDB_FIELD_INFO_END};
1720 
1721 /* Fill the information_schema.rocksdb_trx virtual table */
rdb_i_s_deadlock_info_fill_table(my_core::THD * const thd,my_core::TABLE_LIST * const tables,my_core::Item * const cond MY_ATTRIBUTE ((__unused__)))1722 static int rdb_i_s_deadlock_info_fill_table(
1723     my_core::THD *const thd, my_core::TABLE_LIST *const tables,
1724     my_core::Item *const cond MY_ATTRIBUTE((__unused__))) {
1725   DBUG_ENTER_FUNC();
1726 
1727   DBUG_ASSERT(thd != nullptr);
1728   DBUG_ASSERT(tables != nullptr);
1729   DBUG_ASSERT(tables->table != nullptr);
1730   DBUG_ASSERT(tables->table->field != nullptr);
1731 
1732   static const std::string str_exclusive("EXCLUSIVE");
1733   static const std::string str_shared("SHARED");
1734 
1735   int ret = 0;
1736   rocksdb::DB *const rdb = rdb_get_rocksdb_db();
1737 
1738   if (!rdb) {
1739     DBUG_RETURN(ret);
1740   }
1741 
1742   const std::vector<Rdb_deadlock_info> &all_dl_info = rdb_get_deadlock_info();
1743 
1744   ulonglong id = 0;
1745   for (const auto &info : all_dl_info) {
1746     auto deadlock_time = info.deadlock_time;
1747     for (const auto &trx_info : info.path) {
1748       tables->table->field[RDB_DEADLOCK_FIELD::DEADLOCK_ID]->store(id, true);
1749       tables->table->field[RDB_DEADLOCK_FIELD::TIMESTAMP]->store(deadlock_time,
1750                                                                  true);
1751       tables->table->field[RDB_DEADLOCK_FIELD::TRANSACTION_ID]->store(
1752           trx_info.trx_id, true);
1753       tables->table->field[RDB_DEADLOCK_FIELD::CF_NAME]->store(
1754           trx_info.cf_name.c_str(), trx_info.cf_name.length(),
1755           system_charset_info);
1756       tables->table->field[RDB_DEADLOCK_FIELD::WAITING_KEY]->store(
1757           trx_info.waiting_key.c_str(), trx_info.waiting_key.length(),
1758           system_charset_info);
1759       if (trx_info.exclusive_lock) {
1760         tables->table->field[RDB_DEADLOCK_FIELD::LOCK_TYPE]->store(
1761             str_exclusive.c_str(), str_exclusive.length(), system_charset_info);
1762       } else {
1763         tables->table->field[RDB_DEADLOCK_FIELD::LOCK_TYPE]->store(
1764             str_shared.c_str(), str_shared.length(), system_charset_info);
1765       }
1766       tables->table->field[RDB_DEADLOCK_FIELD::INDEX_NAME]->store(
1767           trx_info.index_name.c_str(), trx_info.index_name.length(),
1768           system_charset_info);
1769       tables->table->field[RDB_DEADLOCK_FIELD::TABLE_NAME]->store(
1770           trx_info.table_name.c_str(), trx_info.table_name.length(),
1771           system_charset_info);
1772       tables->table->field[RDB_DEADLOCK_FIELD::ROLLED_BACK]->store(
1773           trx_info.trx_id == info.victim_trx_id, true);
1774 
1775       /* Tell MySQL about this row in the virtual table */
1776       ret = static_cast<int>(
1777           my_core::schema_table_store_record(thd, tables->table));
1778 
1779       if (ret != 0) {
1780         break;
1781       }
1782     }
1783     id++;
1784   }
1785 
1786   DBUG_RETURN(ret);
1787 }
1788 
1789 /* Initialize the information_schema.rocksdb_trx_info virtual table */
rdb_i_s_deadlock_info_init(void * const p)1790 static int rdb_i_s_deadlock_info_init(void *const p) {
1791   DBUG_ENTER_FUNC();
1792 
1793   DBUG_ASSERT(p != nullptr);
1794 
1795   my_core::ST_SCHEMA_TABLE *schema;
1796 
1797   schema = (my_core::ST_SCHEMA_TABLE *)p;
1798 
1799   schema->fields_info = rdb_i_s_deadlock_info_fields_info;
1800   schema->fill_table = rdb_i_s_deadlock_info_fill_table;
1801 
1802   DBUG_RETURN(0);
1803 }
1804 
rdb_i_s_deinit(void * p MY_ATTRIBUTE ((__unused__)))1805 static int rdb_i_s_deinit(void *p MY_ATTRIBUTE((__unused__))) {
1806   DBUG_ENTER_FUNC();
1807   DBUG_RETURN(0);
1808 }
1809 
1810 static struct st_mysql_information_schema rdb_i_s_info = {
1811     MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION};
1812 
1813 struct st_maria_plugin rdb_i_s_cfstats = {
1814     MYSQL_INFORMATION_SCHEMA_PLUGIN,
1815     &rdb_i_s_info,
1816     "ROCKSDB_CFSTATS",
1817     "Facebook",
1818     "RocksDB column family stats",
1819     PLUGIN_LICENSE_GPL,
1820     rdb_i_s_cfstats_init,
1821     rdb_i_s_deinit,
1822     0x0001,  /* version number (0.1) */
1823     nullptr, /* status variables */
1824     nullptr, /* system variables */
1825     nullptr, /* config options */
1826     MYROCKS_MARIADB_PLUGIN_MATURITY_LEVEL
1827 };
1828 
1829 struct st_maria_plugin rdb_i_s_dbstats = {
1830     MYSQL_INFORMATION_SCHEMA_PLUGIN,
1831     &rdb_i_s_info,
1832     "ROCKSDB_DBSTATS",
1833     "Facebook",
1834     "RocksDB database stats",
1835     PLUGIN_LICENSE_GPL,
1836     rdb_i_s_dbstats_init,
1837     rdb_i_s_deinit,
1838     0x0001,  /* version number (0.1) */
1839     nullptr, /* status variables */
1840     nullptr, /* system variables */
1841     nullptr, /* config options */
1842     MYROCKS_MARIADB_PLUGIN_MATURITY_LEVEL
1843 };
1844 
1845 struct st_maria_plugin rdb_i_s_perf_context = {
1846     MYSQL_INFORMATION_SCHEMA_PLUGIN,
1847     &rdb_i_s_info,
1848     "ROCKSDB_PERF_CONTEXT",
1849     "Facebook",
1850     "RocksDB perf context stats",
1851     PLUGIN_LICENSE_GPL,
1852     rdb_i_s_perf_context_init,
1853     rdb_i_s_deinit,
1854     0x0001,  /* version number (0.1) */
1855     nullptr, /* status variables */
1856     nullptr, /* system variables */
1857     nullptr, /* config options */
1858     MYROCKS_MARIADB_PLUGIN_MATURITY_LEVEL
1859 };
1860 
1861 struct st_maria_plugin rdb_i_s_perf_context_global = {
1862     MYSQL_INFORMATION_SCHEMA_PLUGIN,
1863     &rdb_i_s_info,
1864     "ROCKSDB_PERF_CONTEXT_GLOBAL",
1865     "Facebook",
1866     "RocksDB perf context stats (all)",
1867     PLUGIN_LICENSE_GPL,
1868     rdb_i_s_perf_context_global_init,
1869     rdb_i_s_deinit,
1870     0x0001,  /* version number (0.1) */
1871     nullptr, /* status variables */
1872     nullptr, /* system variables */
1873     nullptr, /* config options */
1874     MYROCKS_MARIADB_PLUGIN_MATURITY_LEVEL
1875 };
1876 
1877 struct st_maria_plugin rdb_i_s_cfoptions = {
1878     MYSQL_INFORMATION_SCHEMA_PLUGIN,
1879     &rdb_i_s_info,
1880     "ROCKSDB_CF_OPTIONS",
1881     "Facebook",
1882     "RocksDB column family options",
1883     PLUGIN_LICENSE_GPL,
1884     rdb_i_s_cfoptions_init,
1885     rdb_i_s_deinit,
1886     0x0001,  /* version number (0.1) */
1887     nullptr, /* status variables */
1888     nullptr, /* system variables */
1889     nullptr, /* config options */
1890     MYROCKS_MARIADB_PLUGIN_MATURITY_LEVEL
1891 };
1892 
1893 struct st_maria_plugin rdb_i_s_global_info = {
1894     MYSQL_INFORMATION_SCHEMA_PLUGIN,
1895     &rdb_i_s_info,
1896     "ROCKSDB_GLOBAL_INFO",
1897     "Facebook",
1898     "RocksDB global info",
1899     PLUGIN_LICENSE_GPL,
1900     rdb_i_s_global_info_init,
1901     rdb_i_s_deinit,
1902     0x0001,  /* version number (0.1) */
1903     nullptr, /* status variables */
1904     nullptr, /* system variables */
1905     nullptr, /* config options */
1906     MYROCKS_MARIADB_PLUGIN_MATURITY_LEVEL
1907 };
1908 
1909 struct st_maria_plugin rdb_i_s_compact_stats = {
1910     MYSQL_INFORMATION_SCHEMA_PLUGIN,
1911     &rdb_i_s_info,
1912     "ROCKSDB_COMPACTION_STATS",
1913     "Facebook",
1914     "RocksDB compaction stats",
1915     PLUGIN_LICENSE_GPL,
1916     rdb_i_s_compact_stats_init,
1917     rdb_i_s_deinit,
1918     0x0001,  /* version number (0.1) */
1919     nullptr, /* status variables */
1920     nullptr, /* system variables */
1921     nullptr, /* config options */
1922     MYROCKS_MARIADB_PLUGIN_MATURITY_LEVEL
1923 };
1924 
1925 struct st_maria_plugin rdb_i_s_ddl = {
1926     MYSQL_INFORMATION_SCHEMA_PLUGIN,
1927     &rdb_i_s_info,
1928     "ROCKSDB_DDL",
1929     "Facebook",
1930     "RocksDB Data Dictionary",
1931     PLUGIN_LICENSE_GPL,
1932     rdb_i_s_ddl_init,
1933     rdb_i_s_deinit,
1934     0x0001,  /* version number (0.1) */
1935     nullptr, /* status variables */
1936     nullptr, /* system variables */
1937     nullptr, /* config options */
1938     MYROCKS_MARIADB_PLUGIN_MATURITY_LEVEL
1939 };
1940 
1941 struct st_maria_plugin rdb_i_s_sst_props = {
1942     MYSQL_INFORMATION_SCHEMA_PLUGIN,
1943     &rdb_i_s_info,
1944     "ROCKSDB_SST_PROPS",
1945     "Facebook",
1946     "RocksDB SST Properties",
1947     PLUGIN_LICENSE_GPL,
1948     rdb_i_s_sst_props_init,
1949     rdb_i_s_deinit,
1950     0x0001,  /* version number (0.1) */
1951     nullptr, /* status variables */
1952     nullptr, /* system variables */
1953     nullptr, /* config options */
1954     MYROCKS_MARIADB_PLUGIN_MATURITY_LEVEL
1955 };
1956 
1957 struct st_maria_plugin rdb_i_s_index_file_map = {
1958     MYSQL_INFORMATION_SCHEMA_PLUGIN,
1959     &rdb_i_s_info,
1960     "ROCKSDB_INDEX_FILE_MAP",
1961     "Facebook",
1962     "RocksDB index file map",
1963     PLUGIN_LICENSE_GPL,
1964     rdb_i_s_index_file_map_init,
1965     rdb_i_s_deinit,
1966     0x0001,  /* version number (0.1) */
1967     nullptr, /* status variables */
1968     nullptr, /* system variables */
1969     nullptr, /* config options */
1970     MYROCKS_MARIADB_PLUGIN_MATURITY_LEVEL
1971 };
1972 
1973 struct st_maria_plugin rdb_i_s_lock_info = {
1974     MYSQL_INFORMATION_SCHEMA_PLUGIN,
1975     &rdb_i_s_info,
1976     "ROCKSDB_LOCKS",
1977     "Facebook",
1978     "RocksDB lock information",
1979     PLUGIN_LICENSE_GPL,
1980     rdb_i_s_lock_info_init,
1981     nullptr,
1982     0x0001,  /* version number (0.1) */
1983     nullptr, /* status variables */
1984     nullptr, /* system variables */
1985     nullptr, /* config options */
1986     MYROCKS_MARIADB_PLUGIN_MATURITY_LEVEL
1987 };
1988 
1989 struct st_maria_plugin rdb_i_s_trx_info = {
1990     MYSQL_INFORMATION_SCHEMA_PLUGIN,
1991     &rdb_i_s_info,
1992     "ROCKSDB_TRX",
1993     "Facebook",
1994     "RocksDB transaction information",
1995     PLUGIN_LICENSE_GPL,
1996     rdb_i_s_trx_info_init,
1997     nullptr,
1998     0x0001,  /* version number (0.1) */
1999     nullptr, /* status variables */
2000     nullptr, /* system variables */
2001     nullptr, /* config options */
2002     MYROCKS_MARIADB_PLUGIN_MATURITY_LEVEL
2003 };
2004 
2005 struct st_maria_plugin rdb_i_s_deadlock_info = {
2006     MYSQL_INFORMATION_SCHEMA_PLUGIN,
2007     &rdb_i_s_info,
2008     "ROCKSDB_DEADLOCK",
2009     "Facebook",
2010     "RocksDB transaction information",
2011     PLUGIN_LICENSE_GPL,
2012     rdb_i_s_deadlock_info_init,
2013     nullptr,
2014     0x0001,  /* version number (0.1) */
2015     nullptr, /* status variables */
2016     nullptr, /* system variables */
2017     nullptr, /* config options */
2018     MYROCKS_MARIADB_PLUGIN_MATURITY_LEVEL
2019 };
2020 }  // namespace myrocks
2021