You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

345 lines
10 KiB

  1. /*
  2. Copyright (c) 2014, SkySQL Ab
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; version 2 of the License.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program; if not, write to the Free Software
  12. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
  13. #ifdef USE_PRAGMA_IMPLEMENTATION
  14. #pragma implementation // gcc: Class implementation
  15. #endif
  16. #include <my_config.h>
  17. /* This C++ files header file */
  18. #include "./rdb_cf_options.h"
  19. /* C++ system header files */
  20. #include <string>
  21. /* MySQL header files */
  22. #include "./log.h"
  23. /* RocksDB header files */
  24. #include "rocksdb/utilities/convenience.h"
  25. /* MyRocks header files */
  26. #include "./ha_rocksdb.h"
  27. #include "./rdb_cf_manager.h"
  28. #include "./rdb_compact_filter.h"
  29. namespace myrocks {
  30. Rdb_pk_comparator Rdb_cf_options::s_pk_comparator;
  31. Rdb_rev_comparator Rdb_cf_options::s_rev_pk_comparator;
  32. bool Rdb_cf_options::init(
  33. const rocksdb::BlockBasedTableOptions &table_options,
  34. std::shared_ptr<rocksdb::TablePropertiesCollectorFactory> prop_coll_factory,
  35. const char *const default_cf_options,
  36. const char *const override_cf_options) {
  37. DBUG_ASSERT(default_cf_options != nullptr);
  38. DBUG_ASSERT(override_cf_options != nullptr);
  39. m_default_cf_opts.comparator = &s_pk_comparator;
  40. m_default_cf_opts.compaction_filter_factory.reset(
  41. new Rdb_compact_filter_factory);
  42. m_default_cf_opts.table_factory.reset(
  43. rocksdb::NewBlockBasedTableFactory(table_options));
  44. if (prop_coll_factory) {
  45. m_default_cf_opts.table_properties_collector_factories.push_back(
  46. prop_coll_factory);
  47. }
  48. if (!set_default(std::string(default_cf_options)) ||
  49. !set_override(std::string(override_cf_options))) {
  50. return false;
  51. }
  52. return true;
  53. }
  54. void Rdb_cf_options::get(const std::string &cf_name,
  55. rocksdb::ColumnFamilyOptions *const opts) {
  56. DBUG_ASSERT(opts != nullptr);
  57. // Get defaults.
  58. rocksdb::GetColumnFamilyOptionsFromString(*opts, m_default_config, opts);
  59. // Get a custom confguration if we have one.
  60. Name_to_config_t::iterator it = m_name_map.find(cf_name);
  61. if (it != m_name_map.end()) {
  62. rocksdb::GetColumnFamilyOptionsFromString(*opts, it->second, opts);
  63. }
  64. }
  65. void Rdb_cf_options::update(const std::string &cf_name,
  66. const std::string &cf_options) {
  67. DBUG_ASSERT(!cf_name.empty());
  68. DBUG_ASSERT(!cf_options.empty());
  69. // Always update. If we didn't have an entry before then add it.
  70. m_name_map[cf_name] = cf_options;
  71. DBUG_ASSERT(!m_name_map.empty());
  72. }
  73. bool Rdb_cf_options::set_default(const std::string &default_config) {
  74. rocksdb::ColumnFamilyOptions options;
  75. if (!default_config.empty() &&
  76. !rocksdb::GetColumnFamilyOptionsFromString(options, default_config,
  77. &options)
  78. .ok()) {
  79. fprintf(stderr, "Invalid default column family config: %s\n",
  80. default_config.c_str());
  81. return false;
  82. }
  83. m_default_config = default_config;
  84. return true;
  85. }
  86. // Skip over any spaces in the input string.
  87. void Rdb_cf_options::skip_spaces(const std::string &input, size_t *const pos) {
  88. DBUG_ASSERT(pos != nullptr);
  89. while (*pos < input.size() && isspace(input[*pos]))
  90. ++(*pos);
  91. }
  92. // Find a valid column family name. Note that all characters except a
  93. // semicolon are valid (should this change?) and all spaces are trimmed from
  94. // the beginning and end but are not removed between other characters.
  95. bool Rdb_cf_options::find_column_family(const std::string &input,
  96. size_t *const pos,
  97. std::string *const key) {
  98. DBUG_ASSERT(pos != nullptr);
  99. DBUG_ASSERT(key != nullptr);
  100. const size_t beg_pos = *pos;
  101. size_t end_pos = *pos - 1;
  102. // Loop through the characters in the string until we see a '='.
  103. for (; *pos < input.size() && input[*pos] != '='; ++(*pos)) {
  104. // If this is not a space, move the end position to the current position.
  105. if (input[*pos] != ' ')
  106. end_pos = *pos;
  107. }
  108. if (end_pos == beg_pos - 1) {
  109. // NO_LINT_DEBUG
  110. sql_print_warning("No column family found (options: %s)", input.c_str());
  111. return false;
  112. }
  113. *key = input.substr(beg_pos, end_pos - beg_pos + 1);
  114. return true;
  115. }
  116. // Find a valid options portion. Everything is deemed valid within the options
  117. // portion until we hit as many close curly braces as we have seen open curly
  118. // braces.
  119. bool Rdb_cf_options::find_options(const std::string &input, size_t *const pos,
  120. std::string *const options) {
  121. DBUG_ASSERT(pos != nullptr);
  122. DBUG_ASSERT(options != nullptr);
  123. // Make sure we have an open curly brace at the current position.
  124. if (*pos < input.size() && input[*pos] != '{') {
  125. // NO_LINT_DEBUG
  126. sql_print_warning("Invalid cf options, '{' expected (options: %s)",
  127. input.c_str());
  128. return false;
  129. }
  130. // Skip the open curly brace and any spaces.
  131. ++(*pos);
  132. skip_spaces(input, pos);
  133. // Set up our brace_count, the begin position and current end position.
  134. size_t brace_count = 1;
  135. const size_t beg_pos = *pos;
  136. // Loop through the characters in the string until we find the appropriate
  137. // number of closing curly braces.
  138. while (*pos < input.size()) {
  139. switch (input[*pos]) {
  140. case '}':
  141. // If this is a closing curly brace and we bring the count down to zero
  142. // we can exit the loop with a valid options string.
  143. if (--brace_count == 0) {
  144. *options = input.substr(beg_pos, *pos - beg_pos);
  145. ++(*pos); // Move past the last closing curly brace
  146. return true;
  147. }
  148. break;
  149. case '{':
  150. // If this is an open curly brace increment the count.
  151. ++brace_count;
  152. break;
  153. default:
  154. break;
  155. }
  156. // Move to the next character.
  157. ++(*pos);
  158. }
  159. // We never found the correct number of closing curly braces.
  160. // Generate an error.
  161. // NO_LINT_DEBUG
  162. sql_print_warning("Mismatched cf options, '}' expected (options: %s)",
  163. input.c_str());
  164. return false;
  165. }
  166. bool Rdb_cf_options::find_cf_options_pair(const std::string &input,
  167. size_t *const pos,
  168. std::string *const cf,
  169. std::string *const opt_str) {
  170. DBUG_ASSERT(pos != nullptr);
  171. DBUG_ASSERT(cf != nullptr);
  172. DBUG_ASSERT(opt_str != nullptr);
  173. // Skip any spaces.
  174. skip_spaces(input, pos);
  175. // We should now have a column family name.
  176. if (!find_column_family(input, pos, cf))
  177. return false;
  178. // If we are at the end of the input then we generate an error.
  179. if (*pos == input.size()) {
  180. // NO_LINT_DEBUG
  181. sql_print_warning("Invalid cf options, '=' expected (options: %s)",
  182. input.c_str());
  183. return false;
  184. }
  185. // Skip equal sign and any spaces after it
  186. ++(*pos);
  187. skip_spaces(input, pos);
  188. // Find the options for this column family. This should be in the format
  189. // {<options>} where <options> may contain embedded pairs of curly braces.
  190. if (!find_options(input, pos, opt_str))
  191. return false;
  192. // Skip any trailing spaces after the option string.
  193. skip_spaces(input, pos);
  194. // We should either be at the end of the input string or at a semicolon.
  195. if (*pos < input.size()) {
  196. if (input[*pos] != ';') {
  197. // NO_LINT_DEBUG
  198. sql_print_warning("Invalid cf options, ';' expected (options: %s)",
  199. input.c_str());
  200. return false;
  201. }
  202. ++(*pos);
  203. }
  204. return true;
  205. }
  206. bool Rdb_cf_options::parse_cf_options(const std::string &cf_options,
  207. Name_to_config_t *option_map) {
  208. std::string cf;
  209. std::string opt_str;
  210. rocksdb::ColumnFamilyOptions options;
  211. DBUG_ASSERT(option_map != nullptr);
  212. DBUG_ASSERT(option_map->empty());
  213. // Loop through the characters of the string until we reach the end.
  214. size_t pos = 0;
  215. while (pos < cf_options.size()) {
  216. // Attempt to find <cf>={<opt_str>}.
  217. if (!find_cf_options_pair(cf_options, &pos, &cf, &opt_str)) {
  218. return false;
  219. }
  220. // Generate an error if we have already seen this column family.
  221. if (option_map->find(cf) != option_map->end()) {
  222. // NO_LINT_DEBUG
  223. sql_print_warning(
  224. "Duplicate entry for %s in override options (options: %s)",
  225. cf.c_str(), cf_options.c_str());
  226. return false;
  227. }
  228. // Generate an error if the <opt_str> is not valid according to RocksDB.
  229. if (!rocksdb::GetColumnFamilyOptionsFromString(options, opt_str, &options)
  230. .ok()) {
  231. // NO_LINT_DEBUG
  232. sql_print_warning(
  233. "Invalid cf config for %s in override options (options: %s)",
  234. cf.c_str(), cf_options.c_str());
  235. return false;
  236. }
  237. // If everything is good, add this cf/opt_str pair to the map.
  238. (*option_map)[cf] = opt_str;
  239. }
  240. return true;
  241. }
  242. bool Rdb_cf_options::set_override(const std::string &override_config) {
  243. Name_to_config_t configs;
  244. if (!parse_cf_options(override_config, &configs)) {
  245. return false;
  246. }
  247. // Everything checked out - make the map live
  248. m_name_map = configs;
  249. return true;
  250. }
  251. const rocksdb::Comparator *
  252. Rdb_cf_options::get_cf_comparator(const std::string &cf_name) {
  253. if (Rdb_cf_manager::is_cf_name_reverse(cf_name.c_str())) {
  254. return &s_rev_pk_comparator;
  255. } else {
  256. return &s_pk_comparator;
  257. }
  258. }
  259. std::shared_ptr<rocksdb::MergeOperator>
  260. Rdb_cf_options::get_cf_merge_operator(const std::string &cf_name) {
  261. return (cf_name == DEFAULT_SYSTEM_CF_NAME)
  262. ? std::make_shared<Rdb_system_merge_op>()
  263. : nullptr;
  264. }
  265. void Rdb_cf_options::get_cf_options(const std::string &cf_name,
  266. rocksdb::ColumnFamilyOptions *const opts) {
  267. *opts = m_default_cf_opts;
  268. get(cf_name, opts);
  269. // Set the comparator according to 'rev:'
  270. opts->comparator = get_cf_comparator(cf_name);
  271. opts->merge_operator = get_cf_merge_operator(cf_name);
  272. }
  273. } // namespace myrocks