Commit 2548a426 authored by John Esmet's avatar John Esmet

FT-307 Add a 'memcmp magic' API, where users can specify that a

particular value for the first byte of a key means it may be compared
with memcmp and a length check.

FT-50 Implement toku_builtin_cmp_fun with memcmp and a length check
parent 17c5b4d9
......@@ -545,6 +545,7 @@ static void print_db_struct (void) {
"int (*change_fanout)(DB *db, uint32_t fanout)",
"int (*get_fanout)(DB *db, uint32_t *fanout)",
"int (*set_fanout)(DB *db, uint32_t fanout)",
"int (*set_memcmp_magic)(DB *db, uint8_t magic)",
"int (*set_indexer)(DB*, DB_INDEXER*)",
"void (*get_indexer)(DB*, DB_INDEXER**)",
"int (*verify_with_progress)(DB *, int (*progress_callback)(void *progress_extra, float progress), void *progress_extra, int verbose, int keep_going)",
......
......@@ -110,12 +110,16 @@ namespace toku {
// that points may be positive or negative infinity.
class comparator {
public:
void create(ft_compare_func cmp, DESCRIPTOR desc) {
void init(ft_compare_func cmp, DESCRIPTOR desc, uint8_t memcmp_magic) {
_cmp = cmp;
XCALLOC(_fake_db);
_fake_db->cmp_descriptor = desc;
_builtin = _cmp == &toku_builtin_compare_fun;
_memcmp_magic = memcmp_magic;
}
public:
void create(ft_compare_func cmp, DESCRIPTOR desc, uint8_t memcmp_magic = 0) {
XCALLOC(_fake_db);
init(cmp, desc, memcmp_magic);
}
// inherit the attributes of another comparator, but keep our own
......@@ -124,9 +128,7 @@ namespace toku {
invariant_notnull(_fake_db);
invariant_notnull(cmp._cmp);
invariant_notnull(cmp._fake_db);
_cmp = cmp._cmp;
_fake_db->cmp_descriptor = cmp._fake_db->cmp_descriptor;
_builtin = cmp._builtin;
init(cmp._cmp, cmp._fake_db->cmp_descriptor, cmp._memcmp_magic);
}
// like inherit, but doesn't require that the this comparator
......@@ -148,14 +150,24 @@ namespace toku {
return _cmp;
}
uint8_t get_memcmp_magic() const {
return _memcmp_magic;
}
bool valid() const {
return _cmp != nullptr;
}
inline bool dbt_has_memcmp_magic(const DBT *dbt) const {
return *reinterpret_cast<const char *>(dbt->data) == _memcmp_magic;
}
int operator()(const DBT *a, const DBT *b) const {
if (__builtin_expect(!!(toku_dbt_is_infinite(a) || toku_dbt_is_infinite(b)), 0)) {
if (__builtin_expect(toku_dbt_is_infinite(a) || toku_dbt_is_infinite(b), 0)) {
return toku_dbt_infinite_compare(a, b);
} else if (_builtin) {
} else if (_memcmp_magic && dbt_has_memcmp_magic(a)
// At this point we expect b to also have the memcmp magic
&& __builtin_expect(dbt_has_memcmp_magic(b), 1)) {
return toku_builtin_compare_fun(nullptr, a, b);
} else {
// yikes, const sadness here
......@@ -166,7 +178,7 @@ namespace toku {
private:
DB *_fake_db;
ft_compare_func _cmp;
bool _builtin;
uint8_t _memcmp_magic;
};
} /* namespace toku */
......@@ -278,6 +278,7 @@ struct ft_options {
enum toku_compression_method compression_method;
unsigned int fanout;
unsigned int flags;
uint8_t memcmp_magic;
ft_compare_func compare_fun;
ft_update_func update_fun;
};
......
......@@ -2887,6 +2887,17 @@ toku_ft_handle_get_fanout(FT_HANDLE ft_handle, unsigned int *fanout)
*fanout = ft_handle->options.fanout;
}
}
void toku_ft_handle_set_memcmp_magic(FT_HANDLE ft_handle, uint8_t magic) {
invariant(magic != 0);
if (ft_handle->ft) {
// handle is already open, application bug if memcmp magic changes
invariant(ft_handle->ft->cmp.get_memcmp_magic() == magic);
} else {
ft_handle->options.memcmp_magic = magic;
}
}
static int
verify_builtin_comparisons_consistent(FT_HANDLE t, uint32_t flags) {
if ((flags & TOKU_DB_KEYCMP_BUILTIN) && (t->options.compare_fun != toku_builtin_compare_fun))
......@@ -2955,6 +2966,7 @@ toku_ft_handle_inherit_options(FT_HANDLE t, FT ft) {
.compression_method = ft->h->compression_method,
.fanout = ft->h->fanout,
.flags = ft->h->flags,
.memcmp_magic = ft->cmp.get_memcmp_magic(),
.compare_fun = ft->cmp.get_compare_func(),
.update_fun = ft->update_fun
};
......@@ -4721,32 +4733,23 @@ int toku_ft_strerror_r(int error, char *buf, size_t buflen)
}
}
// when a and b are chars, return a-b is safe here because return type is int. No over/underflow possible.
int toku_keycompare (const void *key1, uint32_t key1len, const void *key2, uint32_t key2len) {
int comparelen = key1len<key2len ? key1len : key2len;
const unsigned char *k1;
const unsigned char *k2;
for (CAST_FROM_VOIDP(k1, key1), CAST_FROM_VOIDP(k2, key2);
comparelen>4;
k1+=4, k2+=4, comparelen-=4) {
{ int v1=k1[0], v2=k2[0]; if (v1!=v2) return v1-v2; }
{ int v1=k1[1], v2=k2[1]; if (v1!=v2) return v1-v2; }
{ int v1=k1[2], v2=k2[2]; if (v1!=v2) return v1-v2; }
{ int v1=k1[3], v2=k2[3]; if (v1!=v2) return v1-v2; }
}
for (;
comparelen>0;
k1++, k2++, comparelen--) {
if (*k1 != *k2) {
return (int)*k1-(int)*k2;
}
}
if (key1len<key2len) return -1;
if (key1len>key2len) return 1;
return 0;
int toku_keycompare(const void *key1, uint32_t key1len, const void *key2, uint32_t key2len) {
int comparelen = key1len < key2len ? key1len : key2len;
int c = memcmp(key1, key2, comparelen);
if (__builtin_expect(c != 0, 1)) {
return c;
} else {
if (key1len < key2len) {
return -1;
} else if (key1len > key2len) {
return 1;
} else {
return 0;
}
}
}
int toku_builtin_compare_fun (DB *db __attribute__((__unused__)), const DBT *a, const DBT*b) {
int toku_builtin_compare_fun(DB *db __attribute__((__unused__)), const DBT *a, const DBT*b) {
return toku_keycompare(a->data, a->size, b->data, b->size);
}
......
......@@ -126,6 +126,7 @@ void toku_ft_handle_set_compression_method(FT_HANDLE, enum toku_compression_meth
void toku_ft_handle_get_compression_method(FT_HANDLE, enum toku_compression_method *);
void toku_ft_handle_set_fanout(FT_HANDLE, unsigned int fanout);
void toku_ft_handle_get_fanout(FT_HANDLE, unsigned int *fanout);
void toku_ft_handle_set_memcmp_magic(FT_HANDLE, uint8_t magic);
void toku_ft_set_bt_compare(FT_HANDLE ft_handle, ft_compare_func cmp_func);
const toku::comparator &toku_ft_get_comparator(FT_HANDLE ft_handle);
......
......@@ -384,7 +384,7 @@ static void ft_init(FT ft, FT_OPTIONS options, CACHEFILE cf) {
toku_list_init(&ft->live_ft_handles);
// intuitively, the comparator points to the FT's cmp descriptor
ft->cmp.create(options->compare_fun, &ft->cmp_descriptor);
ft->cmp.create(options->compare_fun, &ft->cmp_descriptor, options->memcmp_magic);
ft->update_fun = options->update_fun;
if (ft->cf != NULL) {
......@@ -486,7 +486,7 @@ int toku_read_ft_and_store_in_cachefile (FT_HANDLE ft_handle, CACHEFILE cf, LSN
invariant_notnull(ft);
// intuitively, the comparator points to the FT's cmp descriptor
ft->cmp.create(ft_handle->options.compare_fun, &ft->cmp_descriptor);
ft->cmp.create(ft_handle->options.compare_fun, &ft->cmp_descriptor, ft_handle->options.memcmp_magic);
ft->update_fun = ft_handle->options.update_fun;
ft->cf = cf;
toku_cachefile_set_userdata(cf,
......@@ -611,6 +611,7 @@ toku_ft_init(FT ft,
.compression_method = compression_method,
.fanout = fanout,
.flags = 0,
.memcmp_magic = 0,
.compare_fun = NULL,
.update_fun = NULL
};
......
......@@ -465,27 +465,6 @@ needs_recovery (DB_ENV *env) {
static int toku_env_txn_checkpoint(DB_ENV * env, uint32_t kbyte, uint32_t min, uint32_t flags);
// Instruct db to use the default (built-in) key comparison function
// by setting the flag bits in the db and ft structs
static int
db_use_builtin_key_cmp(DB *db) {
HANDLE_PANICKED_DB(db);
int r = 0;
if (db_opened(db))
r = toku_ydb_do_error(db->dbenv, EINVAL, "Comparison functions cannot be set after DB open.\n");
else if (db->i->key_compare_was_set)
r = toku_ydb_do_error(db->dbenv, EINVAL, "Key comparison function already set.\n");
else {
uint32_t tflags;
toku_ft_get_flags(db->i->ft_handle, &tflags);
tflags |= TOKU_DB_KEYCMP_BUILTIN;
toku_ft_set_flags(db->i->ft_handle, tflags);
db->i->key_compare_was_set = true;
}
return r;
}
// Keys used in persistent environment dictionary:
// Following keys added in version 12
static const char * orig_env_ver_key = "original_version";
......@@ -1025,7 +1004,7 @@ env_open(DB_ENV * env, const char *home, uint32_t flags, int mode) {
{
r = toku_db_create(&env->i->persistent_environment, env, 0);
assert_zero(r);
r = db_use_builtin_key_cmp(env->i->persistent_environment);
r = toku_db_use_builtin_key_cmp(env->i->persistent_environment);
assert_zero(r);
r = toku_db_open_iname(env->i->persistent_environment, txn, toku_product_name_strings.environmentdictionary, DB_CREATE, mode);
if (r != 0) {
......@@ -1063,7 +1042,7 @@ env_open(DB_ENV * env, const char *home, uint32_t flags, int mode) {
{
r = toku_db_create(&env->i->directory, env, 0);
assert_zero(r);
r = db_use_builtin_key_cmp(env->i->directory);
r = toku_db_use_builtin_key_cmp(env->i->directory);
assert_zero(r);
r = toku_db_open_iname(env->i->directory, txn, toku_product_name_strings.fileopsdirectory, DB_CREATE, mode);
if (r != 0) {
......
......@@ -431,8 +431,27 @@ void toku_db_lt_on_destroy_callback(toku::locktree *lt) {
toku_ft_handle_close(ft_handle);
}
int
toku_db_open_iname(DB * db, DB_TXN * txn, const char *iname_in_env, uint32_t flags, int mode) {
// Instruct db to use the default (built-in) key comparison function
// by setting the flag bits in the db and ft structs
int toku_db_use_builtin_key_cmp(DB *db) {
HANDLE_PANICKED_DB(db);
int r = 0;
if (db_opened(db)) {
r = toku_ydb_do_error(db->dbenv, EINVAL, "Comparison functions cannot be set after DB open.\n");
} else if (db->i->key_compare_was_set) {
r = toku_ydb_do_error(db->dbenv, EINVAL, "Key comparison function already set.\n");
} else {
uint32_t tflags;
toku_ft_get_flags(db->i->ft_handle, &tflags);
tflags |= TOKU_DB_KEYCMP_BUILTIN;
toku_ft_set_flags(db->i->ft_handle, tflags);
db->i->key_compare_was_set = true;
}
return r;
}
int toku_db_open_iname(DB * db, DB_TXN * txn, const char *iname_in_env, uint32_t flags, int mode) {
//Set comparison functions if not yet set.
HANDLE_READ_ONLY_TXN(txn);
if (!db->i->key_compare_was_set && db->dbenv->i->bt_compare) {
......@@ -704,6 +723,19 @@ toku_db_get_fanout(DB *db, unsigned int *fanout) {
return 0;
}
static int
toku_db_set_memcmp_magic(DB *db, uint8_t magic) {
HANDLE_PANICKED_DB(db);
if (db_opened(db)) {
return EINVAL;
}
if (magic == 0) {
return EINVAL;
}
toku_ft_handle_set_memcmp_magic(db->i->ft_handle, magic);
return 0;
}
static int
toku_db_get_fractal_tree_info64(DB *db, uint64_t *num_blocks_allocated, uint64_t *num_blocks_in_use, uint64_t *size_allocated, uint64_t *size_in_use) {
HANDLE_PANICKED_DB(db);
......@@ -1101,6 +1133,7 @@ toku_db_create(DB ** db, DB_ENV * env, uint32_t flags) {
USDB(change_compression_method);
USDB(set_fanout);
USDB(get_fanout);
USDB(set_memcmp_magic);
USDB(change_fanout);
USDB(set_flags);
USDB(get_flags);
......
......@@ -131,6 +131,7 @@ static inline const toku::comparator &toku_db_get_comparator(DB *db) {
return toku_ft_get_comparator(db->i->ft_handle);
}
int toku_db_use_builtin_key_cmp(DB *db);
int toku_db_pre_acquire_fileops_lock(DB *db, DB_TXN *txn);
int toku_db_open_iname(DB * db, DB_TXN * txn, const char *iname, uint32_t flags, int mode);
int toku_db_pre_acquire_table_lock(DB *db, DB_TXN *txn);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment