From 031938147d1fb7c9016fd54fa5ed5e1d65856ee2 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Wed, 10 Sep 2014 18:56:38 +0400 Subject: [PATCH] #11: Column families for RocksDB-SE: support setting options for column families --- mysql-test/suite/rocksdb/show_engine.result | 9 + storage/rocksdb/CMakeLists.txt | 4 +- storage/rocksdb/ha_rocksdb.cc | 303 +++++++++++--------- storage/rocksdb/ha_rocksdb.h | 2 + storage/rocksdb/rdb_cf_manager.cc | 107 +++++++ storage/rocksdb/rdb_cf_manager.h | 66 +++++ storage/rocksdb/rdb_cf_option.cc | 143 +++++++++ storage/rocksdb/rdb_cf_option.h | 59 ++++ 8 files changed, 558 insertions(+), 135 deletions(-) create mode 100644 storage/rocksdb/rdb_cf_manager.cc create mode 100644 storage/rocksdb/rdb_cf_manager.h create mode 100644 storage/rocksdb/rdb_cf_option.cc create mode 100644 storage/rocksdb/rdb_cf_option.h diff --git a/mysql-test/suite/rocksdb/show_engine.result b/mysql-test/suite/rocksdb/show_engine.result index 3908a82ede94..eebdc65659c0 100644 --- a/mysql-test/suite/rocksdb/show_engine.result +++ b/mysql-test/suite/rocksdb/show_engine.result @@ -1,5 +1,14 @@ SHOW ENGINE rocksdb STATUS; Type Name Status +ROCKSDB live_files cf=default name=/000020.sst size=236629 +cf=default name=/000016.sst size=329126 +cf=default name=/000012.sst size=237072 +cf=default name=/000008.sst size=330647 +cf=default name=/000010.sst size=209635 +cf=default name=/000014.sst size=209635 +cf=default name=/000018.sst size=209635 +cf=default name=/000022.sst size=209633 + SHOW ENGINE rocksdb MUTEX; Type Name Status SHOW ENGINE ALL MUTEX; diff --git a/storage/rocksdb/CMakeLists.txt b/storage/rocksdb/CMakeLists.txt index 4dc48d114332..3a756a0901bf 100644 --- a/storage/rocksdb/CMakeLists.txt +++ b/storage/rocksdb/CMakeLists.txt @@ -48,7 +48,9 @@ IF(ROCKSDB_OK) rdb_datadic.cc rdb_datadic.h rdb_locks.cc rdb_locks.h rdb_rowmods.cc rdb_rowmods.h - rdb_applyiter.cc rdb_applyiter.h) + rdb_applyiter.cc rdb_applyiter.h + rdb_cf_option.cc rdb_cf_option.h + rdb_cf_manager.cc rdb_cf_manager.h) MYSQL_ADD_PLUGIN(rocksdb_se ${ROCKSDB_SOURCES} STORAGE_ENGINE STATIC_ONLY MANDATORY LINK_LIBRARIES ${LINK_DIR} snappy bz2) diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc index c703025e539d..040a03a5e76b 100644 --- a/storage/rocksdb/ha_rocksdb.cc +++ b/storage/rocksdb/ha_rocksdb.cc @@ -25,162 +25,164 @@ #include "my_bit.h" +#include + #include "rdb_datadic.h" #include "rdb_locks.h" #include "rdb_rowmods.h" +#include "rdb_cf_option.h" +#include "rdb_cf_manager.h" + #include "rocksdb/table.h" -#include -#include void dbug_dump_database(rocksdb::DB *db); - static handler *rocksdb_create_handler(handlerton *hton, TABLE_SHARE *table, MEM_ROOT *mem_root); - void key_copy(uchar *to_key, uchar *from_record, KEY *key_info, uint key_length); +/////////////////////////////////////////////////////////// +// Parameters and settings +/////////////////////////////////////////////////////////// +extern const longlong ROCKSDB_WRITE_BUFFER_SIZE_DEFAULT; +extern const int ROCKSDB_TARGET_FILE_SIZE_BASE_DEFAULT; +static char * rocksdb_write_buffer_size_str; +Numeric_cf_option write_buffer_size_map; + +static char * rocksdb_target_file_size_base_str; +Numeric_cf_option target_file_size_base_map; + + +/////////////////////////////////////////////////////////// +// Globals +/////////////////////////////////////////////////////////// handlerton *rocksdb_hton; rocksdb::DB *rdb= NULL; -rocksdb::ColumnFamilyOptions default_cf_opts; - +static std::shared_ptr rocksdb_stats; -/* - We need a column family manager. Its functions: - - create column families (synchronized, don't create the same twice) - - keep count in each column family. - = the count is kept on-disk. - = there are no empty CFs. initially count=1. - = then, when doing DDL, we increase or decrease it. - (atomicity is maintained by being in the same WriteBatch with DDLs) - = if DROP discovers that now count=0, it removes the CF. - - Current state is: - - CFs are created in a synchronized way. We can't remove them, yet. -*/ +rocksdb::ColumnFamilyOptions default_cf_opts; -class Column_family_manager -{ - typedef std::map ColumnFamilyHandleMap; - ColumnFamilyHandleMap cf_map; +Column_family_manager cf_manager; +Table_ddl_manager ddl_manager; +LockTable row_locks; - rocksdb::ColumnFamilyHandle *default_cf; +/* + Hash used to track the number of open tables; variable for example share + methods +*/ +static HASH rocksdb_open_tables; - mysql_mutex_t cfm_mutex; -public: - /* - This is called right after the DB::Open() call. The parameters describe column - families that are present in the database. The first CF is the default CF. - */ - void init(std::vector *names, - std::vector *handles); - void cleanup(); +/* The mutex used to init the hash; variable for example share methods */ +mysql_mutex_t rocksdb_mutex; - /* Used by CREATE TABLE. name=NULL means use default column family */ - rocksdb::ColumnFamilyHandle* get_or_create_cf(const char *name); - /* Used by table open */ - rocksdb::ColumnFamilyHandle* get_cf(const char *name); +////////////////////////////////////////////////////////////////////////////// +// Options parse support functions +////////////////////////////////////////////////////////////////////////////// - void drop_cf(); -}; +static int +rocksdb_write_buffer_size_validate(THD* thd, + struct st_mysql_sys_var* var, + void* save, + struct st_mysql_value* value) +{ + /* The option is read-only, it should never be updated */ + DBUG_ASSERT(0); + return 1; +#if 0 + const char* param_str; + char buff[STRING_BUFFER_USUAL_SIZE]; + int len= sizeof(buff); -Column_family_manager cf_manager; + param_str= value->val_str(value, buff, &len); + if (param_str != NULL) + { + if (parse_multi_number(param_str, &write_buffer_size_map)) + { + save= (void*)1; + return 1; + } + } + save= NULL; + return 0; +#endif +} -void Column_family_manager::init(std::vector *names, - std::vector *handles) +static void +rocksdb_write_buffer_size_update(THD* thd, + struct st_mysql_sys_var* var, + void* var_ptr, + const void* save) { - mysql_mutex_init(NULL, &cfm_mutex, MY_MUTEX_INIT_FAST); - DBUG_ASSERT(names->size() == handles->size()); - DBUG_ASSERT(names->size() > 0); - - default_cf= (*handles)[0]; - for (size_t i = 0; i < names->size(); ++i) - cf_map[(*names)[i]]= (*handles)[i]; + /* The option is read-only, it should never be updated */ + DBUG_ASSERT(0); } -void Column_family_manager::cleanup() +static bool rocksdb_parse_write_buffer_size_arg() { - ColumnFamilyHandleMap::iterator it; - for (it= cf_map.begin(); it!=cf_map.end(); it++) + write_buffer_size_map.default_val= ROCKSDB_WRITE_BUFFER_SIZE_DEFAULT; + if (parse_per_cf_numeric(rocksdb_write_buffer_size_str, &write_buffer_size_map)) { - delete it->second; + sql_print_error("RocksDB: Invalid value for rocksdb_write_buffer_size: %s", + rocksdb_write_buffer_size_str); + return true; } - mysql_mutex_destroy(&cfm_mutex); + else + return false; } -rocksdb::ColumnFamilyHandle* -Column_family_manager::get_or_create_cf(const char *name) +static int +rocksdb_target_file_size_base_validate(THD* thd, + struct st_mysql_sys_var* var, + void* save, + struct st_mysql_value* value) { - rocksdb::ColumnFamilyHandle* cf_handle; - ColumnFamilyHandleMap::iterator it; + /* The option is read-only, it should never be updated */ + DBUG_ASSERT(0); + return 1; +} - mysql_mutex_lock(&cfm_mutex); - if (name == NULL) - { - cf_handle= default_cf; - } - else if ((it= cf_map.find(name)) != cf_map.end()) - cf_handle= it->second; - else - { - /* Create a Column Family. */ - std::string cf_name(name); - rocksdb::Status s= rdb->CreateColumnFamily(default_cf_opts, name, - &cf_handle); - if (s.ok()) - cf_map[cf_name]= cf_handle; - else - cf_handle= NULL; - } - mysql_mutex_unlock(&cfm_mutex); - return cf_handle; +static void +rocksdb_target_file_size_base_update(THD* thd, + struct st_mysql_sys_var* var, + void* var_ptr, + const void* save) +{ + /* The option is read-only, it should never be updated */ + DBUG_ASSERT(0); } -rocksdb::ColumnFamilyHandle* -Column_family_manager::get_cf(const char *name) +static bool rocksdb_parse_target_file_size_base_arg() { - rocksdb::ColumnFamilyHandle* cf_handle; - ColumnFamilyHandleMap::iterator it; - - mysql_mutex_lock(&cfm_mutex); - if (name == NULL) + target_file_size_base_map.default_val= ROCKSDB_TARGET_FILE_SIZE_BASE_DEFAULT; + if (parse_per_cf_numeric(rocksdb_target_file_size_base_str, + &target_file_size_base_map)) { - cf_handle= default_cf; + sql_print_error("RocksDB: Invalid value for rocksdb_target_file_size_base: %s", + rocksdb_target_file_size_base_str); + return true; } - else if ((it= cf_map.find(name)) != cf_map.end()) - cf_handle= it->second; else - cf_handle= NULL; - mysql_mutex_unlock(&cfm_mutex); - - return cf_handle; + return false; } +////////////////////////////////////////////////////////////////////////////// +// Options definitions +////////////////////////////////////////////////////////////////////////////// +static long long rocksdb_block_cache_size; -Table_ddl_manager ddl_manager; - -LockTable row_locks; - -/* - Hash used to track the number of open tables; variable for example share - methods -*/ -static HASH rocksdb_open_tables; - -/* The mutex used to init the hash; variable for example share methods */ -mysql_mutex_t rocksdb_mutex; - +//static long long rocksdb_write_buffer_size; +//static int rocksdb_target_file_size_base; //TODO: 0 means don't wait at all, and we don't support it yet? static MYSQL_THDVAR_ULONG(lock_wait_timeout, PLUGIN_VAR_RQCMDARG, @@ -198,28 +200,36 @@ static MYSQL_THDVAR_ULONG(bulk_load_size, PLUGIN_VAR_RQCMDARG, "Max #records in a batch for bulk-load mode", NULL, NULL, /*default*/ 1000, /*min*/ 1, /*max*/ 1024*1024*1024, 0); -static long long rocksdb_block_cache_size; -static long long rocksdb_write_buffer_size; -static int rocksdb_target_file_size_base; - static MYSQL_SYSVAR_LONGLONG(block_cache_size, rocksdb_block_cache_size, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, "block_cache size for RocksDB", NULL, NULL, /* RocksDB's default is 8 MB: */ 8*1024*1024L, /* min */ 1024L, /* max */ LONGLONG_MAX, /* Block size */1024L); -static MYSQL_SYSVAR_LONGLONG(write_buffer_size, rocksdb_write_buffer_size, +static MYSQL_SYSVAR_STR(write_buffer_size, rocksdb_write_buffer_size_str, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, - "options.write_buffer_size for RocksDB", - NULL, NULL, /* RocksDB's default is 4 MB: */ 4*1024*1024L, - /* min */ 1024L, /* max */ LONGLONG_MAX, /* Block size */1024L); + "options.write_buffer_size for RocksDB (Can also be set per-column family)", + rocksdb_write_buffer_size_validate, + rocksdb_write_buffer_size_update, "4194304" /* default is 4 MB for default CF */); +const longlong ROCKSDB_WRITE_BUFFER_SIZE_DEFAULT=4194304; + +static MYSQL_SYSVAR_STR(target_file_size_base, + rocksdb_target_file_size_base_str, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "options.target_file_size_base for RocksDB (Can also be set per-column family)", + rocksdb_target_file_size_base_validate, + rocksdb_target_file_size_base_update, + "2097152" /* default is 2 MB for default CF */); +const int ROCKSDB_TARGET_FILE_SIZE_BASE_DEFAULT=2097152; +#if 0 static MYSQL_SYSVAR_INT(target_file_size_base, rocksdb_target_file_size_base, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, "options.target_file_size_base for RocksDB", NULL, NULL, /* RocksDB's default is 2 MB: */ 2*1024*1024L, /* min */ 1024L, /* max */ INT_MAX, /* Block size */1024L); +#endif static struct st_mysql_sys_var* rocksdb_system_variables[]= { MYSQL_SYSVAR(lock_wait_timeout), @@ -327,13 +337,13 @@ class Primary_key_comparator : public rocksdb::Comparator void FindShortSuccessor(std::string* key) const {} }; -Primary_key_comparator primary_key_comparator; +Primary_key_comparator rocksdb_pk_comparator; int compare_mem_comparable_keys(const uchar *a, size_t a_len, const uchar *b, size_t b_len) { rocksdb::Slice a_slice((char*)a, a_len); rocksdb::Slice b_slice((char*)b, b_len); - return primary_key_comparator.Compare(a_slice, b_slice); + return rocksdb_pk_comparator.Compare(a_slice, b_slice); } @@ -617,7 +627,20 @@ static bool rocksdb_show_status(handlerton* hton, return res; } -static std::shared_ptr rocksdb_stats; + +void get_cf_options(const std::string &cf_name, rocksdb::ColumnFamilyOptions *opts) +{ + *opts= default_cf_opts; + int tfsb= target_file_size_base_map.get_val(cf_name.c_str()); + size_t wbs= write_buffer_size_map.get_val(cf_name.c_str()); + + opts->write_buffer_size= wbs; + opts->target_file_size_base= tfsb; +} + +/* + Engine initialization function +*/ static int rocksdb_init_func(void *p) { @@ -626,6 +649,13 @@ static int rocksdb_init_func(void *p) #ifdef HAVE_PSI_INTERFACE init_rocksdb_psi_keys(); #endif + + /* Parse command-line option values */ + if (rocksdb_parse_write_buffer_size_arg() || + rocksdb_parse_target_file_size_base_arg()) + { + DBUG_RETURN(1); + } rocksdb_hton= (handlerton *)p; mysql_mutex_init(ex_key_mutex_example, &rocksdb_mutex, MY_MUTEX_INIT_FAST); @@ -648,10 +678,6 @@ static int rocksdb_init_func(void *p) rocksdb_hton->flags= HTON_TEMPORARY_NOT_SUPPORTED | HTON_SUPPORTS_EXTENDED_KEYS; - /* - As for the datadir, innobase_init() uses mysql_real_data_home for - embedded server, and current directory for the "full server". - */ DBUG_ASSERT(!mysqld_embedded); row_locks.init(compare_mem_comparable_keys, @@ -696,9 +722,10 @@ static int rocksdb_init_func(void *p) std::vector cf_descr; std::vector cf_handles; - default_cf_opts.comparator= &primary_key_comparator; - default_cf_opts.write_buffer_size= rocksdb_write_buffer_size; - default_cf_opts.target_file_size_base= rocksdb_target_file_size_base; + default_cf_opts.comparator= &rocksdb_pk_comparator; + + default_cf_opts.write_buffer_size= write_buffer_size_map.get_default_val(); + default_cf_opts.target_file_size_base= target_file_size_base_map.get_default_val(); rocksdb::BlockBasedTableOptions table_options; table_options.block_cache = rocksdb::NewLRUCache(rocksdb_block_cache_size); @@ -709,12 +736,22 @@ static int rocksdb_init_func(void *p) Create one column family named "default". */ if (cf_names.size() == 0) - cf_names.push_back("default"); + cf_names.push_back(DEFAULT_CF_NAME); + sql_print_information("RocksDB: Column Families at start:"); for (size_t i = 0; i < cf_names.size(); ++i) { - cf_descr.push_back(rocksdb::ColumnFamilyDescriptor(cf_names[i], - default_cf_opts)); + int tfsb= target_file_size_base_map.get_val(cf_names[i].c_str()); + size_t wbs= write_buffer_size_map.get_val(cf_names[i].c_str()); + + sql_print_information(" cf=%s", cf_names[i].c_str()); + sql_print_information(" write_buffer_size=%ld", wbs); + sql_print_information(" target_file_size_base=%d", tfsb); + + rocksdb::ColumnFamilyOptions opts(default_cf_opts); + opts.write_buffer_size= wbs; + opts.target_file_size_base= tfsb; + cf_descr.push_back(rocksdb::ColumnFamilyDescriptor(cf_names[i], opts)); } rocksdb::Options main_opts(db_opts, default_cf_opts); @@ -734,10 +771,6 @@ static int rocksdb_init_func(void *p) DBUG_RETURN(1); sql_print_information("RocksDB instance opened"); - sql_print_information(" write_buffer_size=%lu", - rdb->GetOptions().write_buffer_size); - sql_print_information(" target_file_size_base=%d", - rdb->GetOptions().target_file_size_base); DBUG_RETURN(0); } @@ -1311,16 +1344,18 @@ int ha_rocksdb::create_key_defs(TABLE *table_arg, const char *db_table, uint len for (i= 0; i < table_arg->s->keys; i++) { + rocksdb::ColumnFamilyHandle* cf_handle; /* index comment has Column Family name. If there was no comment, we get - NULL, and it means use default column family. + NULL, and it means use the default column family. */ const char *comment= table_arg->key_info[i].comment.str; - rocksdb::ColumnFamilyHandle* cf_handle= cf_manager.get_or_create_cf(comment); + cf_handle= cf_manager.get_or_create_cf(rdb, comment); if (!cf_handle) goto error; - if (!(key_descr[i]= new RDBSE_KEYDEF(ddl_manager.get_next_number(), i, cf_handle))) + if (!(key_descr[i]= new RDBSE_KEYDEF(ddl_manager.get_next_number(), i, + cf_handle))) goto error; } pk_descr= key_descr[table_arg->s->primary_key]; @@ -2502,7 +2537,7 @@ int ha_rocksdb::update_row(const uchar *old_data, uchar *new_data) /* Compare rowkey for the new record with rowkey for the old one */ rocksdb::Slice old_key_slice(last_rowkey.ptr(), last_rowkey.length()); - if (primary_key_comparator.Compare(key_slice, old_key_slice)) + if (rocksdb_pk_comparator.Compare(key_slice, old_key_slice)) { /* Ok, they are different. We're doing DELETE+PUT */ diff --git a/storage/rocksdb/ha_rocksdb.h b/storage/rocksdb/ha_rocksdb.h index 34423479bca0..e4cd7aef701f 100644 --- a/storage/rocksdb/ha_rocksdb.h +++ b/storage/rocksdb/ha_rocksdb.h @@ -33,6 +33,8 @@ #include "rdb_applyiter.h" +const char * const DEFAULT_CF_NAME= "default"; + //#ifdef HAVE_PSI_INTERFACE extern PSI_stage_info stage_waiting_on_row_lock; diff --git a/storage/rocksdb/rdb_cf_manager.cc b/storage/rocksdb/rdb_cf_manager.cc new file mode 100644 index 000000000000..d3ff4bcec73f --- /dev/null +++ b/storage/rocksdb/rdb_cf_manager.cc @@ -0,0 +1,107 @@ +/* + Copyright (c) 2014, SkySQL Ab + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef USE_PRAGMA_IMPLEMENTATION +#pragma implementation // gcc: Class implementation +#endif + +#include +#include "sql_class.h" +#include "ha_rocksdb.h" + +#include "rdb_cf_manager.h" + + +void Column_family_manager::init(std::vector *names, + std::vector *handles) +{ + mysql_mutex_init(NULL, &cfm_mutex, MY_MUTEX_INIT_FAST); + DBUG_ASSERT(names->size() == handles->size()); + DBUG_ASSERT(names->size() > 0); + + default_cf= (*handles)[0]; + for (size_t i = 0; i < names->size(); ++i) + cf_map[(*names)[i]]= (*handles)[i]; +} + + +void Column_family_manager::cleanup() +{ + ColumnFamilyHandleMap::iterator it; + for (it= cf_map.begin(); it!=cf_map.end(); it++) + { + delete it->second; + } + mysql_mutex_destroy(&cfm_mutex); +} + + +rocksdb::ColumnFamilyHandle* +Column_family_manager::get_or_create_cf(rocksdb::DB *rdb, const char *name) +{ + rocksdb::ColumnFamilyHandle* cf_handle; + ColumnFamilyHandleMap::iterator it; + + mysql_mutex_lock(&cfm_mutex); + if (name == NULL) + { + cf_handle= default_cf; + } + else if ((it= cf_map.find(name)) != cf_map.end()) + cf_handle= it->second; + else + { + /* Create a Column Family. */ + std::string cf_name(name); + rocksdb::ColumnFamilyOptions opts; + get_cf_options(cf_name, &opts); + + sql_print_information("RocksDB: creating column family %s", cf_name.c_str()); + sql_print_information(" write_buffer_size=%ld", opts.write_buffer_size); + sql_print_information(" target_file_size_base=%d", opts.target_file_size_base); + + rocksdb::Status s= rdb->CreateColumnFamily(opts, name, &cf_handle); + if (s.ok()) + cf_map[cf_name]= cf_handle; + else + cf_handle= NULL; + } + mysql_mutex_unlock(&cfm_mutex); + + return cf_handle; +} + + +rocksdb::ColumnFamilyHandle* +Column_family_manager::get_cf(const char *name) +{ + rocksdb::ColumnFamilyHandle* cf_handle; + ColumnFamilyHandleMap::iterator it; + + mysql_mutex_lock(&cfm_mutex); + if (name == NULL) + cf_handle= default_cf; + else if ((it= cf_map.find(name)) != cf_map.end()) + cf_handle= it->second; + else + cf_handle= NULL; + mysql_mutex_unlock(&cfm_mutex); + + return cf_handle; +} + + + diff --git a/storage/rocksdb/rdb_cf_manager.h b/storage/rocksdb/rdb_cf_manager.h new file mode 100644 index 000000000000..e6d08e58725c --- /dev/null +++ b/storage/rocksdb/rdb_cf_manager.h @@ -0,0 +1,66 @@ +/* + Copyright (c) 2014, SkySQL Ab + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include + + +/* + Expected from outside: a function that fills CF options for a given name. +*/ +void get_cf_options(const std::string &cf_name, rocksdb::ColumnFamilyOptions *opts); + +/* + We need a column family manager. Its functions: + - create column families (synchronized, don't create the same twice) + - keep count in each column family. + = the count is kept on-disk. + = there are no empty CFs. initially count=1. + = then, when doing DDL, we increase or decrease it. + (atomicity is maintained by being in the same WriteBatch with DDLs) + = if DROP discovers that now count=0, it removes the CF. + + Current state is: + - CFs are created in a synchronized way. We can't remove them, yet. +*/ + +class Column_family_manager +{ + typedef std::map ColumnFamilyHandleMap; + + ColumnFamilyHandleMap cf_map; + + rocksdb::ColumnFamilyHandle *default_cf; + + mysql_mutex_t cfm_mutex; +public: + /* + This is called right after the DB::Open() call. The parameters describe column + families that are present in the database. The first CF is the default CF. + */ + void init(std::vector *names, + std::vector *handles); + void cleanup(); + + /* Used by CREATE TABLE. name=NULL means use default column family */ + rocksdb::ColumnFamilyHandle* get_or_create_cf(rocksdb::DB *rdb, + const char *name); + + /* Used by table open */ + rocksdb::ColumnFamilyHandle* get_cf(const char *name); + + // void drop_cf(); -- not implemented so far. +}; + diff --git a/storage/rocksdb/rdb_cf_option.cc b/storage/rocksdb/rdb_cf_option.cc new file mode 100644 index 000000000000..74ff11b1bad1 --- /dev/null +++ b/storage/rocksdb/rdb_cf_option.cc @@ -0,0 +1,143 @@ +/* + Copyright (c) 2014, SkySQL Ab + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef USE_PRAGMA_IMPLEMENTATION +#pragma implementation // gcc: Class implementation +#endif + +#include +#include "ha_rocksdb.h" + +#include "rdb_cf_option.h" + + +/* + Read a number from a string. The number may have a suffix Kk/Mm/Gg. + The code is copied from eval_num_suffix(). + + @param str INOUT string pointer + @param number OUT number + + @return 0 - OK + 1 - Error +*/ + +bool read_number_and_suffix(const char **str, longlong *number) +{ + char *endchar; + longlong num; + + errno= 0; + num= strtoll((char*)*str, &endchar, 10); + if (errno == ERANGE) + return true; + + int suffix_chars= 1; + if (*endchar == 'k' || *endchar == 'K') + num*= 1024L; + else if (*endchar == 'm' || *endchar == 'M') + num*= 1024L * 1024L; + else if (*endchar == 'g' || *endchar == 'G') + num*= 1024L * 1024L * 1024L; + else + suffix_chars= 0; + + endchar += suffix_chars; + *str= endchar; + *number= num; + return 0; +} + + +/* + Read column family name, followed by semicolon ':' + + @param str INOUT string pointer. On return points to right after + the semicolon. + @return 0 - OK + 1 - Error +*/ + +bool read_cf_name(const char **str_arg) +{ + const char *str= *str_arg; + while (str[0] && str[0] !=':') str++; + + if (str[0] == ':') + { + *str_arg= str + 1; + return 0; /* OK */ + } + return 1; /* Error */ +} + + +/* + Parse a string value that maybe either + - a single number-with-suffix + - a line in form + cfname:value,cfname2:value2,... + + @param str String with option value + @param out OUT Parsed option value + + @return false OK + @return true Parse error +*/ + +bool parse_per_cf_numeric(const char *str, Numeric_cf_option *out) +{ + longlong num; + const char *p= str; + if (!read_number_and_suffix(&p, &num) && p[0]==0) + { + /* The whole string is one number. Assign it as default and exit */ + out->default_val= num; + return false; + } + + /* Try reading it as a comma-separated list of cfname:value pairs*/ + while (1) + { + const char *cf_name= str; + if (read_cf_name(&str)) + return true; + /* -1 is to omit the ':' from cf name string: */ + std::string cf_name_str(cf_name, str - cf_name - 1); + if (read_number_and_suffix(&str, &num)) + return true; + + out->name_map[cf_name_str]= num; + + /* + However, if there is column family named "DEFAULT" it is take in as + default. + */ + if (!strcmp(cf_name_str.c_str(), DEFAULT_CF_NAME)) + out->default_val= num; + + /* Skip the comma */ + if (*str == ',') + str++; + else if (*str == 0) + break; + else + return true; + } + return false; +} + + diff --git a/storage/rocksdb/rdb_cf_option.h b/storage/rocksdb/rdb_cf_option.h new file mode 100644 index 000000000000..c86e98c1f706 --- /dev/null +++ b/storage/rocksdb/rdb_cf_option.h @@ -0,0 +1,59 @@ +/* + Copyright (c) 2014, SkySQL Ab + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include + +/* + Numeric per-column family option value. + + Per-column family option can be set + - Globally (the same value applies to all column families) + - Per column family: there is a {cf_name -> value} map, + and also there is a default value which applies to column + families not found in the map. +*/ +class Numeric_cf_option +{ +public: + typedef std::map NameToLonglong; + + /* cf_name -> value map*/ + NameToLonglong name_map; + + /* The default value (if there is only one value, it is stored here) */ + longlong default_val; + + /* Get option value for column family with name cf_name */ + longlong get_val(const char *cf_name) + { + NameToLonglong::iterator it= name_map.find(cf_name); + if (it != name_map.end()) + { + return it->second; + } + else + return default_val; + } + + longlong get_default_val() { return default_val; } +}; + +/* + Parse string representation of per-column family option and store it in *out +*/ +bool parse_per_cf_numeric(const char *str, Numeric_cf_option *out); + +