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