/** * flush the backend * * @remark this function is called during ham_flush @note This is a B+-tree 'backend' method. */ static ham_status_t my_fun_flush(ham_btree_t *be) { ham_db_t *db=be_get_db(be); db_indexdata_t *indexdata=env_get_indexdata_ptr(db_get_env(db), db_get_indexdata_offset(db)); /* * nothing to do if the backend was not touched */ if (!be_is_dirty(be)) return (0); index_set_max_keys(indexdata, btree_get_maxkeys(be)); index_set_keysize(indexdata, be_get_keysize(be)); index_set_self(indexdata, btree_get_rootpage(be)); index_set_flags(indexdata, be_get_flags(be)); index_set_recno(indexdata, be_get_recno(be)); index_clear_reserved(indexdata); env_set_dirty(db_get_env(db)); be_set_dirty(be, HAM_FALSE); return (0); }
ham_status_t blob_get_datasize(ham_db_t *db, ham_offset_t blobid, ham_offset_t *size) { ham_status_t st; ham_page_t *page; blob_t hdr; /* * in-memory-database: the blobid is actually a pointer to the memory * buffer, in which the blob is stored */ if (env_get_rt_flags(db_get_env(db))&HAM_IN_MEMORY_DB) { blob_t *hdr=(blob_t *)U64_TO_PTR(blobid); *size=blob_get_size(hdr); return (0); } ham_assert(blobid%DB_CHUNKSIZE==0, ("blobid is %llu", blobid)); /* read the blob header */ st=__read_chunk(db_get_env(db), 0, &page, blobid, (ham_u8_t *)&hdr, sizeof(hdr)); if (st) return (st); ham_assert(blob_get_alloc_size(&hdr)%DB_CHUNKSIZE==0, (0)); if (blob_get_self(&hdr)!=blobid) return (HAM_BLOB_NOT_FOUND); *size=blob_get_size(&hdr); return (0); }
/** * estimate the number of keys per page, given the keysize * * @remark this function is only available when * hamsterdb is compiled with HAM_ENABLE_INTERNAL turned on. * * @note This is a B+-tree 'backend' method. */ static ham_status_t my_fun_calc_keycount_per_page(ham_btree_t *be, ham_size_t *maxkeys, ham_u16_t keysize) { ham_db_t *db=be_get_db(be); if (keysize == 0) { *maxkeys=btree_get_maxkeys(be); } else { /* * prevent overflow - maxkeys only has 16 bit! */ *maxkeys=btree_calc_maxkeys(env_get_pagesize(db_get_env(db)), keysize); if (*maxkeys>MAX_KEYS_PER_NODE) { ham_trace(("keysize/pagesize ratio too high")); return HAM_INV_KEYSIZE; } else if (*maxkeys==0) { ham_trace(("keysize too large for the current pagesize")); return HAM_INV_KEYSIZE; } } return (0); }
static ham_status_t _remote_cursor_close(ham_cursor_t *cursor) { ham_status_t st; ham_env_t *env=db_get_env(cursor_get_db(cursor)); proto_wrapper_t *request, *reply; request=proto_init_cursor_close_request(cursor_get_remote_handle(cursor)); st=_perform_request(env, env_get_curl(env), request, &reply); proto_delete(request); if (st) { if (reply) proto_delete(reply); return (st); } ham_assert(reply!=0, ("")); ham_assert(proto_has_cursor_close_reply(reply)!=0, ("")); st=proto_cursor_close_reply_get_status(reply); proto_delete(reply); return (st); }
static ham_status_t _remote_cursor_move(ham_cursor_t *cursor, ham_key_t *key, ham_record_t *record, ham_u32_t flags) { ham_status_t st; ham_db_t *db=cursor_get_db(cursor); ham_env_t *env=db_get_env(db); proto_wrapper_t *request, *reply; request=proto_init_cursor_move_request(cursor_get_remote_handle(cursor), key, record, flags); st=_perform_request(env, env_get_curl(env), request, &reply); proto_delete(request); if (st) { if (reply) proto_delete(reply); return (st); } ham_assert(reply!=0, ("")); ham_assert(proto_has_cursor_move_reply(reply)!=0, ("")); st=proto_cursor_move_reply_get_status(reply); if (st) goto bail; /* modify key/record, but make sure that USER_ALLOC is respected! */ if (proto_cursor_move_reply_has_key(reply)) { ham_assert(key, ("")); key->_flags=proto_cursor_move_reply_get_key_intflags(reply); key->size=proto_cursor_move_reply_get_key_size(reply); if (!(key->flags&HAM_KEY_USER_ALLOC)) { st=db_resize_key_allocdata(db, key->size); if (st) goto bail; key->data=db_get_key_allocdata(db); } memcpy(key->data, proto_cursor_move_reply_get_key_data(reply), key->size); } /* same for the record */ if (proto_cursor_move_reply_has_record(reply)) { ham_assert(record, ("")); record->size=proto_cursor_move_reply_get_record_size(reply); if (!(record->flags&HAM_RECORD_USER_ALLOC)) { st=db_resize_record_allocdata(db, record->size); if (st) goto bail; record->data=db_get_record_allocdata(db); } memcpy(record->data, proto_cursor_move_reply_get_record_data(reply), record->size); } bail: proto_delete(reply); return (st); }
static ham_status_t _remote_cursor_overwrite(ham_cursor_t *cursor, ham_record_t *record, ham_u32_t flags) { ham_status_t st; ham_db_t *db=cursor_get_db(cursor); ham_env_t *env=db_get_env(db); proto_wrapper_t *request, *reply; request=proto_init_cursor_overwrite_request( cursor_get_remote_handle(cursor), record, flags); st=_perform_request(env, env_get_curl(env), request, &reply); proto_delete(request); if (st) { if (reply) proto_delete(reply); return (st); } ham_assert(reply!=0, ("")); ham_assert(proto_has_cursor_overwrite_reply(reply)!=0, ("")); st=proto_cursor_overwrite_reply_get_status(reply); proto_delete(reply); return (st); }
void extkey_cache_destroy(extkey_cache_t *cache) { ham_size_t i; extkey_t *e, *n; ham_db_t *db=extkey_cache_get_db(cache); ham_env_t *env = db_get_env(db); /* * make sure that all entries are empty */ for (i=0; i<extkey_cache_get_bucketsize(cache); i++) { e=extkey_cache_get_bucket(cache, i); while (e) { #if HAM_DEBUG /* * make sure that the extkey-cache is empty - only for in-memory * databases and DEBUG builds. */ if (env_get_rt_flags(env)&HAM_IN_MEMORY_DB) ham_assert(!"extkey-cache is not empty!", (0)); #endif n=extkey_get_next(e); allocator_free(env_get_allocator(env), e); e=n; } } allocator_free(env_get_allocator(env), cache); }
ham_status_t extkey_cache_insert(extkey_cache_t *cache, ham_offset_t blobid, ham_size_t size, const ham_u8_t *data) { ham_size_t h=my_calc_hash(cache, blobid); extkey_t *e; ham_db_t *db=extkey_cache_get_db(cache); ham_env_t *env = db_get_env(db); /* * DEBUG build: make sure that the item is not inserted twice! */ #ifdef HAM_DEBUG e=extkey_cache_get_bucket(cache, h); while (e) { ham_assert(extkey_get_blobid(e)!=blobid, (0)); e=extkey_get_next(e); } #endif e=(extkey_t *)allocator_alloc(env_get_allocator(env), SIZEOF_EXTKEY_T+size); if (!e) return HAM_OUT_OF_MEMORY; extkey_set_blobid(e, blobid); extkey_set_txn_id(e, env_get_txn_id(env)); extkey_set_next(e, extkey_cache_get_bucket(cache, h)); extkey_set_size(e, size); memcpy(extkey_get_data(e), data, size); extkey_cache_set_bucket(cache, h, e); extkey_cache_set_usedsize(cache, extkey_cache_get_usedsize(cache)+size); return (0); }
ham_status_t extkey_cache_purge(extkey_cache_t *cache) { ham_size_t i; extkey_t *e, *n; ham_env_t *env; ham_assert(extkey_cache_get_db(cache), (0)); env = db_get_env(extkey_cache_get_db(cache)); /* * delete all entries which are "too old" (were not * used in the last EXTKEY_MAX_AGE transactions) */ for (i=0; i<extkey_cache_get_bucketsize(cache); i++) { extkey_t *p=0; e=extkey_cache_get_bucket(cache, i); while (e) { n=extkey_get_next(e); if (env_get_txn_id(env)-extkey_get_txn_id(e)>EXTKEY_MAX_AGE) { /* deleted the head element of the list? */ if (!p) extkey_cache_set_bucket(cache, i, n); else extkey_set_next(p, n); allocator_free(env_get_allocator(env), e); } else p=e; e=n; } } return (0); }
static ham_status_t _remote_fun_check_integrity(ham_db_t *db, ham_txn_t *txn) { ham_status_t st; ham_env_t *env=db_get_env(db); proto_wrapper_t *request, *reply; request=proto_init_check_integrity_request(db_get_remote_handle(db), txn ? txn_get_remote_handle(txn) : 0); st=_perform_request(env, env_get_curl(env), request, &reply); proto_delete(request); if (st) { if (reply) proto_delete(reply); return (st); } ham_assert(reply!=0, ("")); ham_assert(proto_has_check_integrity_reply(reply), ("")); st=proto_check_integrity_reply_get_status(reply); proto_delete(reply); return (st); }
static ham_status_t _remote_fun_get_key_count(ham_db_t *db, ham_txn_t *txn, ham_u32_t flags, ham_offset_t *keycount) { ham_status_t st; ham_env_t *env=db_get_env(db); proto_wrapper_t *request, *reply; request=proto_init_db_get_key_count_request(db_get_remote_handle(db), txn ? txn_get_remote_handle(txn) : 0, flags); st=_perform_request(env, env_get_curl(env), request, &reply); proto_delete(request); if (st) { if (reply) proto_delete(reply); return (st); } ham_assert(reply!=0, ("")); ham_assert(proto_has_db_get_key_count_reply(reply), ("")); st=proto_db_get_key_count_reply_get_status(reply); if (!st) *keycount=proto_db_get_key_count_reply_get_key_count(reply); proto_delete(reply); return (st); }
ham_status_t extkey_cache_remove(extkey_cache_t *cache, ham_offset_t blobid) { ham_db_t *db=extkey_cache_get_db(cache); ham_env_t *env = db_get_env(db); ham_size_t h=my_calc_hash(cache, blobid); extkey_t *e, *prev=0; e=extkey_cache_get_bucket(cache, h); while (e) { if (extkey_get_blobid(e)==blobid) break; prev=e; e=extkey_get_next(e); } if (!e) return (HAM_KEY_NOT_FOUND); if (prev) extkey_set_next(prev, extkey_get_next(e)); else extkey_cache_set_bucket(cache, h, extkey_get_next(e)); extkey_cache_set_usedsize(cache, extkey_cache_get_usedsize(cache)-extkey_get_size(e)); allocator_free(env_get_allocator(env), e); return (0); }
/** * open and initialize a backend * * @remark this function is called after the ham_db_structure * was allocated and the file was opened @note This is a B+-tree 'backend' method. */ static ham_status_t my_fun_open(ham_btree_t *be, ham_u32_t flags) { ham_offset_t rootadd; ham_offset_t recno; ham_u16_t maxkeys; ham_u16_t keysize; ham_db_t *db=be_get_db(be); db_indexdata_t *indexdata=env_get_indexdata_ptr(db_get_env(db), db_get_indexdata_offset(db)); /* * load root address and maxkeys (first two bytes are the * database name) */ maxkeys = index_get_max_keys(indexdata); keysize = index_get_keysize(indexdata); rootadd = index_get_self(indexdata); flags = index_get_flags(indexdata); recno = index_get_recno(indexdata); btree_set_rootpage(be, rootadd); btree_set_maxkeys(be, maxkeys); be_set_keysize(be, keysize); be_set_flags(be, flags); be_set_recno(be, recno); be_set_active(be, HAM_TRUE); return (0); }
static ham_status_t _remote_fun_find(ham_db_t *db, ham_txn_t *txn, ham_key_t *key, ham_record_t *record, ham_u32_t flags) { ham_status_t st; ham_env_t *env=db_get_env(db); proto_wrapper_t *request, *reply; request=proto_init_db_find_request(db_get_remote_handle(db), txn ? txn_get_remote_handle(txn) : 0, key, record, flags); st=_perform_request(env, env_get_curl(env), request, &reply); proto_delete(request); if (st) { if (reply) proto_delete(reply); return (st); } ham_assert(reply!=0, ("")); ham_assert(proto_has_db_find_reply(reply)!=0, ("")); st=proto_db_find_reply_get_status(reply); if (st==0) { /* approx. matching: need to copy the _flags and the key data! */ if (proto_db_find_reply_has_key(reply)) { ham_assert(key, ("")); key->_flags=proto_db_find_reply_get_key_intflags(reply); key->size=proto_db_find_reply_get_key_size(reply); if (!(key->flags&HAM_KEY_USER_ALLOC)) { st=db_resize_key_allocdata(db, key->size); if (st) goto bail; key->data=db_get_key_allocdata(db); } memcpy(key->data, proto_db_find_reply_get_key_data(reply), key->size); } if (proto_db_find_reply_has_record(reply)) { record->size=proto_db_find_reply_get_record_size(reply); if (!(record->flags&HAM_RECORD_USER_ALLOC)) { st=db_resize_record_allocdata(db, record->size); if (st) goto bail; record->data=db_get_record_allocdata(db); } memcpy(record->data, proto_db_find_reply_get_record_data(reply), record->size); } } bail: proto_delete(reply); return (st); }
ham_status_t extkey_remove(ham_db_t *db, ham_offset_t blobid) { ham_status_t st; if (db_get_extkey_cache(db)) { st=extkey_cache_remove(db_get_extkey_cache(db), blobid); if (st && st!=HAM_KEY_NOT_FOUND) return (st); } return (blob_free(db_get_env(db), db, blobid, 0)); }
/** * Remove all extended keys for the given @a page from the * extended key cache. */ static ham_status_t my_fun_free_page_extkeys(ham_btree_t *be, ham_page_t *page, ham_u32_t flags) { ham_db_t *db=be_get_db(be); ham_assert(page_get_owner(page) == db, (0)); ham_assert(0 == (flags & ~DB_MOVE_TO_FREELIST), (0)); /* * if this page has a header, and it's either a B-Tree root page or * a B-Tree index page: remove all extended keys from the cache, * and/or free their blobs */ if (page_get_pers(page) && (!(page_get_npers_flags(page)&PAGE_NPERS_NO_HEADER)) && (page_get_type(page)==PAGE_TYPE_B_ROOT || page_get_type(page)==PAGE_TYPE_B_INDEX)) { ham_size_t i; ham_offset_t blobid; int_key_t *bte; btree_node_t *node=ham_page_get_btree_node(page); extkey_cache_t *c; ham_assert(db, ("Must be set as page owner when this is a Btree page")); ham_assert(db=page_get_owner(page), ("")); c=db_get_extkey_cache(db); for (i=0; i<btree_node_get_count(node); i++) { bte=btree_node_get_key(db, node, i); if (key_get_flags(bte)&KEY_IS_EXTENDED) { blobid=key_get_extended_rid(db, bte); if (env_get_rt_flags(db_get_env(db))&HAM_IN_MEMORY_DB) { /* delete the blobid to prevent that it's freed twice */ *(ham_offset_t *)(key_get_key(bte)+ (db_get_keysize(db)-sizeof(ham_offset_t)))=0; } //(void)key_erase_record(db, bte, 0, BLOB_FREE_ALL_DUPES); if (c) (void)extkey_cache_remove(c, blobid); } } } return (HAM_SUCCESS); }
static ham_status_t _remote_cursor_find(ham_cursor_t *cursor, ham_key_t *key, ham_record_t *record, ham_u32_t flags) { ham_status_t st; ham_db_t *db=cursor_get_db(cursor); ham_env_t *env=db_get_env(db); proto_wrapper_t *request, *reply; request=proto_init_cursor_find_request(cursor_get_remote_handle(cursor), key, record, flags); st=_perform_request(env, env_get_curl(env), request, &reply); proto_delete(request); if (st) { if (reply) proto_delete(reply); return (st); } ham_assert(reply!=0, ("")); ham_assert(proto_has_cursor_find_reply(reply)!=0, ("")); st=proto_cursor_find_reply_get_status(reply); if (st) goto bail; /* approx. matching: need to copy the _flags! */ if (proto_cursor_find_reply_has_key(reply)) { key->_flags=proto_cursor_find_reply_get_key_intflags(reply); } if (proto_cursor_find_reply_has_record(reply)) { ham_assert(record, ("")); record->size=proto_cursor_find_reply_get_record_size(reply); if (!(record->flags&HAM_RECORD_USER_ALLOC)) { st=db_resize_record_allocdata(db, record->size); if (st) goto bail; record->data=db_get_record_allocdata(db); } memcpy(record->data, proto_cursor_find_reply_get_record_data(reply), record->size); } bail: proto_delete(reply); return (st); }
/* * when the last hit leaf node is split or shrunk, blow it away for all operations! * * Also blow away a page when a transaction aborts which has modified this page. We'd rather * reconstruct our critical statistics then carry the wrong bounds, etc. around. * * This is done to prevent the hinter from hinting/pointing at an (by now) * INVALID btree node later on! */ void stats_page_is_nuked(ham_db_t *db, struct ham_page_t *page, ham_bool_t split) { ham_runtime_statistics_dbdata_t *dbdata = db_get_db_perf_data(db); ham_env_t *env = db_get_env(db); int i; for (i = 0; i <= 2; i++) { ham_runtime_statistics_opdbdata_t *opstats = db_get_op_perf_data(db, i); ham_assert(i == HAM_OPERATION_STATS_FIND || i == HAM_OPERATION_STATS_INSERT || i == HAM_OPERATION_STATS_ERASE, (0)); if (opstats->btree_last_page_addr == page_get_self(page)) { opstats->btree_last_page_addr = 0; opstats->btree_last_page_sq_hits = 0; } } if (dbdata->lower_bound_page_address == page_get_self(page)) { if (dbdata->lower_bound.data) { ham_assert(env_get_allocator(env) != 0, (0)); allocator_free(env_get_allocator(env), dbdata->lower_bound.data); } memset(&dbdata->lower_bound, 0, sizeof(dbdata->lower_bound)); dbdata->lower_bound_index = 0; dbdata->lower_bound_page_address = 0; dbdata->lower_bound_set = HAM_FALSE; } if (dbdata->upper_bound_page_address == page_get_self(page)) { if (dbdata->upper_bound.data) { ham_assert(env_get_allocator(env) != 0, (0)); allocator_free(env_get_allocator(env), dbdata->upper_bound.data); } memset(&dbdata->upper_bound, 0, sizeof(dbdata->upper_bound)); dbdata->upper_bound_index = 0; dbdata->upper_bound_page_address = 0; dbdata->upper_bound_set = HAM_FALSE; } }
void stats_trash_dbdata(ham_db_t *db, ham_runtime_statistics_dbdata_t *dbdata) { ham_env_t *env = db_get_env(db); /* trash the upper/lower bound keys, when set: */ if (dbdata->upper_bound.data) { ham_assert(env_get_allocator(env) != 0, (0)); allocator_free(env_get_allocator(env), dbdata->upper_bound.data); } if (dbdata->lower_bound.data) { ham_assert(env_get_allocator(env) != 0, (0)); allocator_free(env_get_allocator(env), dbdata->lower_bound.data); } memset(dbdata, 0, sizeof(*dbdata)); }
static ham_status_t _remote_cursor_insert(ham_cursor_t *cursor, ham_key_t *key, ham_record_t *record, ham_u32_t flags) { ham_status_t st; ham_db_t *db=cursor_get_db(cursor); ham_env_t *env=db_get_env(db); proto_wrapper_t *request, *reply; ham_bool_t send_key=HAM_TRUE; /* recno: do not send the key */ if (db_get_rt_flags(db)&HAM_RECORD_NUMBER) send_key=HAM_FALSE; request=proto_init_cursor_insert_request(cursor_get_remote_handle(cursor), send_key ? key : 0, record, flags); st=_perform_request(env, env_get_curl(env), request, &reply); proto_delete(request); if (st) { if (reply) proto_delete(reply); return (st); } ham_assert(reply!=0, ("")); ham_assert(proto_has_cursor_insert_reply(reply)!=0, ("")); st=proto_cursor_insert_reply_get_status(reply); /* recno: the key was modified! */ if (st==0 && proto_cursor_insert_reply_has_key(reply)) { if (proto_cursor_insert_reply_get_key_size(reply) ==sizeof(ham_offset_t)) { ham_assert(key->data!=0, ("")); ham_assert(key->size==sizeof(ham_offset_t), ("")); memcpy(key->data, proto_cursor_insert_reply_get_key_data(reply), sizeof(ham_offset_t)); } } proto_delete(reply); return (st); }
extkey_cache_t * extkey_cache_new(ham_db_t *db) { extkey_cache_t *c; int memsize; memsize=sizeof(extkey_cache_t)+EXTKEY_CACHE_BUCKETSIZE*sizeof(extkey_t *); c=(extkey_cache_t *)allocator_calloc(env_get_allocator(db_get_env(db)), memsize); if (!c) { // HAM_OUT_OF_MEMORY; return (0); } extkey_cache_set_db(c, db); extkey_cache_set_bucketsize(c, EXTKEY_CACHE_BUCKETSIZE); return (c); }
static ham_status_t _remote_fun_close(ham_db_t *db, ham_u32_t flags) { ham_status_t st; ham_env_t *env=db_get_env(db); proto_wrapper_t *request, *reply; /* * auto-cleanup cursors? */ if (flags&HAM_AUTO_CLEANUP) { ham_cursor_t *cursor=db_get_cursors(db); while ((cursor=db_get_cursors(db))) { (void)ham_cursor_close(cursor); } } else if (db_get_cursors(db)) { return (HAM_CURSOR_STILL_OPEN); } request=proto_init_db_close_request(db_get_remote_handle(db), flags); st=_perform_request(env, env_get_curl(env), request, &reply); proto_delete(request); if (st) { if (reply) proto_delete(reply); return (st); } ham_assert(reply!=0, ("")); ham_assert(proto_has_db_close_reply(reply), ("")); st=proto_db_close_reply_get_status(reply); proto_delete(reply); if (st==0) db_set_remote_handle(db, 0); return (st); }
void db_update_global_stats_erase_query(ham_db_t *db, ham_size_t key_size) { ham_env_t *env = db_get_env(db); if (!(env_get_rt_flags(env)&HAM_IN_MEMORY_DB)) { ham_runtime_statistics_globdata_t *globalstats = env_get_global_perf_data(env); ham_runtime_statistics_opdbdata_t *opstats = db_get_op_perf_data(db, HAM_OPERATION_STATS_ERASE); #ifdef HAM_DEBUG ham_u16_t bucket = ham_bitcount2bucket_index(key_size / DB_CHUNKSIZE); ham_assert(bucket < HAM_FREELIST_SLOT_SPREAD, (0)); //ham_assert(device_get_freelist_cache(dev), (0)); #endif globalstats->erase_query_count++; opstats->query_count++; } }
static ham_status_t _remote_cursor_create(ham_db_t *db, ham_txn_t *txn, ham_u32_t flags, ham_cursor_t **cursor) { ham_env_t *env=db_get_env(db); ham_status_t st; proto_wrapper_t *request, *reply; request=proto_init_cursor_create_request(db_get_remote_handle(db), txn ? txn_get_remote_handle(txn) : 0, flags); st=_perform_request(env, env_get_curl(env), request, &reply); proto_delete(request); if (st) { if (reply) proto_delete(reply); return (st); } ham_assert(reply!=0, ("")); ham_assert(proto_has_cursor_create_reply(reply)!=0, ("")); st=proto_cursor_create_reply_get_status(reply); if (st) { proto_delete(reply); return (st); } *cursor=(ham_cursor_t *)allocator_calloc(env_get_allocator(env), sizeof(ham_cursor_t)); if (!(*cursor)) return (HAM_OUT_OF_MEMORY); cursor_set_remote_handle(*cursor, proto_cursor_create_reply_get_cursor_handle(reply)); proto_delete(reply); return (st); }
ham_status_t btree_create(ham_backend_t **backend_ref, ham_db_t *db, ham_u32_t flags) { ham_btree_t *btree; *backend_ref = 0; btree = (ham_btree_t *)allocator_calloc(env_get_allocator(db_get_env(db)), sizeof(*btree)); if (!btree) { return HAM_OUT_OF_MEMORY; } /* initialize the backend */ btree->_db=db; btree->_fun_create=my_fun_create; btree->_fun_open=my_fun_open; btree->_fun_close=my_fun_close; btree->_fun_flush=my_fun_flush; btree->_fun_delete=my_fun_delete; btree->_fun_find=btree_find; btree->_fun_insert=btree_insert; btree->_fun_erase=btree_erase; btree->_fun_enumerate=btree_enumerate; #ifdef HAM_ENABLE_INTERNAL btree->_fun_check_integrity=btree_check_integrity; btree->_fun_calc_keycount_per_page=my_fun_calc_keycount_per_page; #else btree->_fun_check_integrity=0; btree->_fun_calc_keycount_per_page=0; #endif btree->_fun_cursor_create = my_fun_cursor_create; btree->_fun_close_cursors = my_fun_close_cursors; btree->_fun_uncouple_all_cursors=my_fun_uncouple_all_cursors; btree->_fun_free_page_extkeys=my_fun_free_page_extkeys; *backend_ref = (ham_backend_t *)btree; return HAM_SUCCESS; }
static ham_status_t _remote_cursor_clone(ham_cursor_t *src, ham_cursor_t **dest) { ham_env_t *env=db_get_env(cursor_get_db(src)); ham_status_t st; proto_wrapper_t *request, *reply; request=proto_init_cursor_clone_request(cursor_get_remote_handle(src)); st=_perform_request(env, env_get_curl(env), request, &reply); proto_delete(request); if (st) { if (reply) proto_delete(reply); return (st); } ham_assert(reply!=0, ("")); ham_assert(proto_has_cursor_clone_reply(reply)!=0, ("")); st=proto_cursor_clone_reply_get_status(reply); if (st) { proto_delete(reply); return (st); } *dest=(ham_cursor_t *)allocator_calloc(env_get_allocator(env), sizeof(ham_cursor_t)); if (!(*dest)) return (HAM_OUT_OF_MEMORY); cursor_set_remote_handle(*dest, proto_cursor_clone_reply_get_cursor_handle(reply)); proto_delete(reply); return (st); }
ham_status_t extkey_cache_fetch(extkey_cache_t *cache, ham_offset_t blobid, ham_size_t *size, ham_u8_t **data) { ham_size_t h=my_calc_hash(cache, blobid); extkey_t *e; e=extkey_cache_get_bucket(cache, h); while (e) { if (extkey_get_blobid(e)==blobid) break; e=extkey_get_next(e); } if (!e) return (HAM_KEY_NOT_FOUND); *size=extkey_get_size(e); *data=extkey_get_data(e); extkey_set_txn_id(e, env_get_txn_id(db_get_env(extkey_cache_get_db(cache)))); return (0); }
ham_status_t blob_read(ham_db_t *db, ham_offset_t blobid, ham_record_t *record, ham_u32_t flags) { ham_status_t st; ham_page_t *page; blob_t hdr; ham_size_t blobsize=0; /* * in-memory-database: the blobid is actually a pointer to the memory * buffer, in which the blob is stored */ if (env_get_rt_flags(db_get_env(db))&HAM_IN_MEMORY_DB) { blob_t *hdr=(blob_t *)U64_TO_PTR(blobid); ham_u8_t *data=(ham_u8_t *)(U64_TO_PTR(blobid))+sizeof(blob_t); /* when the database is closing, the header is already deleted */ if (!hdr) { record->size = 0; return (0); } blobsize = (ham_size_t)blob_get_size(hdr); if (flags&HAM_PARTIAL) { if (record->partial_offset>blobsize) { ham_trace(("partial offset is greater than the total " "record size")); return (HAM_INV_PARAMETER); } if (record->partial_offset+record->partial_size>blobsize) blobsize=blobsize-record->partial_offset; else blobsize=record->partial_size; } if (!blobsize) { /* empty blob? */ record->data = 0; record->size = 0; } else { ham_u8_t *d=data; if (flags&HAM_PARTIAL) d+=record->partial_offset; if ((flags&HAM_DIRECT_ACCESS) && !(record->flags&HAM_RECORD_USER_ALLOC)) { record->size=blobsize; record->data=d; } else { /* resize buffer, if necessary */ if (!(record->flags & HAM_RECORD_USER_ALLOC)) { st=db_resize_record_allocdata(db, blobsize); if (st) return (st); record->data = db_get_record_allocdata(db); } /* and copy the data */ memcpy(record->data, d, blobsize); record->size = blobsize; } } return (0); } ham_assert(blobid%DB_CHUNKSIZE==0, ("blobid is %llu", blobid)); /* * first step: read the blob header */ st=__read_chunk(db_get_env(db), 0, &page, blobid, (ham_u8_t *)&hdr, sizeof(hdr)); if (st) return (st); ham_assert(blob_get_alloc_size(&hdr)%DB_CHUNKSIZE==0, (0)); /* * sanity check */ if (blob_get_self(&hdr)!=blobid) return (HAM_BLOB_NOT_FOUND); blobsize = (ham_size_t)blob_get_size(&hdr); if (flags&HAM_PARTIAL) { if (record->partial_offset>blobsize) { ham_trace(("partial offset+size is greater than the total " "record size")); return (HAM_INV_PARAMETER); } if (record->partial_offset+record->partial_size>blobsize) blobsize=blobsize-record->partial_offset; else blobsize=record->partial_size; } /* * empty blob? */ if (!blobsize) { record->data = 0; record->size = 0; return (0); } /* * second step: resize the blob buffer */ if (!(record->flags & HAM_RECORD_USER_ALLOC)) { st=db_resize_record_allocdata(db, blobsize); if (st) return (st); record->data = db_get_record_allocdata(db); } /* * third step: read the blob data */ st=__read_chunk(db_get_env(db), page, 0, blobid+sizeof(blob_t)+(flags&HAM_PARTIAL ? record->partial_offset : 0), record->data, blobsize); if (st) return (st); record->size = blobsize; return (0); }
ham_status_t blob_duplicate_erase(ham_db_t *db, ham_offset_t table_id, ham_size_t position, ham_u32_t flags, ham_offset_t *new_table_id) { ham_status_t st; ham_record_t rec; ham_size_t i; dupe_table_t *table; ham_offset_t rid; ham_env_t *env = db_get_env(db); /* store the public record pointer, otherwise it's destroyed */ ham_size_t rs=db_get_record_allocsize(db); void *rp=db_get_record_allocdata(db); db_set_record_allocdata(db, 0); db_set_record_allocsize(db, 0); memset(&rec, 0, sizeof(rec)); if (new_table_id) *new_table_id=table_id; st=blob_read(db, table_id, &rec, 0); if (st) return (st); /* restore the public record pointer */ db_set_record_allocsize(db, rs); db_set_record_allocdata(db, rp); table=(dupe_table_t *)rec.data; /* * if BLOB_FREE_ALL_DUPES is set *OR* if the last duplicate is deleted: * free the whole duplicate table */ if (flags&BLOB_FREE_ALL_DUPES || (position==0 && dupe_table_get_count(table)==1)) { for (i=0; i<dupe_table_get_count(table); i++) { dupe_entry_t *e=dupe_table_get_entry(table, i); if (!(dupe_entry_get_flags(e)&(KEY_BLOB_SIZE_SMALL |KEY_BLOB_SIZE_TINY |KEY_BLOB_SIZE_EMPTY))) { st=blob_free(env, db, dupe_entry_get_rid(e), 0); if (st) { allocator_free(env_get_allocator(env), table); return (st); } } } st=blob_free(env, db, table_id, 0); /* [i_a] isn't this superfluous (& * dangerous), thanks to the * free_all_dupes loop above??? */ allocator_free(env_get_allocator(env), table); if (st) return (st); if (new_table_id) *new_table_id=0; return (0); } else { ham_record_t rec={0}; dupe_entry_t *e=dupe_table_get_entry(table, position); if (!(dupe_entry_get_flags(e)&(KEY_BLOB_SIZE_SMALL |KEY_BLOB_SIZE_TINY |KEY_BLOB_SIZE_EMPTY))) { st=blob_free(env, db, dupe_entry_get_rid(e), 0); if (st) { allocator_free(env_get_allocator(env), table); return (st); } } memmove(e, e+1, ((dupe_table_get_count(table)-position)-1)*sizeof(dupe_entry_t)); dupe_table_set_count(table, dupe_table_get_count(table)-1); rec.data=(ham_u8_t *)table; rec.size=sizeof(dupe_table_t) +(dupe_table_get_capacity(table)-1)*sizeof(dupe_entry_t); st=blob_overwrite(env, db, table_id, &rec, 0, &rid); if (st) { allocator_free(env_get_allocator(env), table); return (st); } if (new_table_id) *new_table_id=rid; } /* * return 0 as a rid if the table is empty */ if (dupe_table_get_count(table)==0) if (new_table_id) *new_table_id=0; allocator_free(env_get_allocator(env), table); return (0); }
ham_status_t blob_duplicate_insert(ham_db_t *db, ham_offset_t table_id, ham_record_t *record, ham_size_t position, ham_u32_t flags, dupe_entry_t *entries, ham_size_t num_entries, ham_offset_t *rid, ham_size_t *new_position) { ham_status_t st=0; dupe_table_t *table=0; ham_bool_t alloc_table=0; ham_bool_t resize=0; ham_page_t *page=0; ham_env_t *env=db_get_env(db); /* * create a new duplicate table if none existed, and insert * the first entry */ if (!table_id) { ham_assert(num_entries==2, ("")); /* allocates space for 8 (!) entries */ table=allocator_calloc(env_get_allocator(env), sizeof(dupe_table_t)+7*sizeof(dupe_entry_t)); if (!table) return HAM_OUT_OF_MEMORY; dupe_table_set_capacity(table, 8); dupe_table_set_count(table, 1); memcpy(dupe_table_get_entry(table, 0), &entries[0], sizeof(entries[0])); /* skip the first entry */ entries++; num_entries--; alloc_table=1; } else { /* * otherwise load the existing table */ st=__get_duplicate_table(&table, &page, env, table_id); ham_assert(st ? table == NULL : 1, (0)); ham_assert(st ? page == NULL : 1, (0)); if (!table) return st ? st : HAM_INTERNAL_ERROR; if (!page && !(env_get_rt_flags(env)&HAM_IN_MEMORY_DB)) alloc_table=1; } if (page) if ((st=ham_log_add_page_before(page))) return (st); ham_assert(num_entries==1, ("")); /* * resize the table, if necessary */ if (!(flags & HAM_OVERWRITE) && dupe_table_get_count(table)+1>=dupe_table_get_capacity(table)) { dupe_table_t *old=table; ham_size_t new_cap=dupe_table_get_capacity(table); if (new_cap < 3*8) new_cap += 8; else new_cap += new_cap/3; table=allocator_calloc(env_get_allocator(env), sizeof(dupe_table_t)+ (new_cap-1)*sizeof(dupe_entry_t)); if (!table) return (HAM_OUT_OF_MEMORY); dupe_table_set_capacity(table, new_cap); dupe_table_set_count(table, dupe_table_get_count(old)); memcpy(dupe_table_get_entry(table, 0), dupe_table_get_entry(old, 0), dupe_table_get_count(old)*sizeof(dupe_entry_t)); if (alloc_table) allocator_free(env_get_allocator(env), old); alloc_table=1; resize=1; } /* * insert sorted, unsorted or overwrite the entry at the requested position */ if (flags&HAM_OVERWRITE) { dupe_entry_t *e=dupe_table_get_entry(table, position); if (!(dupe_entry_get_flags(e)&(KEY_BLOB_SIZE_SMALL |KEY_BLOB_SIZE_TINY |KEY_BLOB_SIZE_EMPTY))) { (void)blob_free(env, db, dupe_entry_get_rid(e), 0); } memcpy(dupe_table_get_entry(table, position), &entries[0], sizeof(entries[0])); } else { if (db_get_rt_flags(db)&HAM_SORT_DUPLICATES) { if (page) page_add_ref(page); position=__get_sorted_position(db, table, record, flags); if (page) page_release_ref(page); if (position<0) return ((ham_status_t)position); } else if (flags&HAM_DUPLICATE_INSERT_BEFORE) { /* do nothing, insert at the current position */ } else if (flags&HAM_DUPLICATE_INSERT_AFTER) { position++; if (position > dupe_table_get_count(table)) position=dupe_table_get_count(table); } else if (flags&HAM_DUPLICATE_INSERT_FIRST) { position=0; } else if (flags&HAM_DUPLICATE_INSERT_LAST) { position=dupe_table_get_count(table); } else { position=dupe_table_get_count(table); } if (position != dupe_table_get_count(table)) { memmove(dupe_table_get_entry(table, position+1), dupe_table_get_entry(table, position), sizeof(entries[0])*(dupe_table_get_count(table)-position)); } memcpy(dupe_table_get_entry(table, position), &entries[0], sizeof(entries[0])); dupe_table_set_count(table, dupe_table_get_count(table)+1); } /* * write the table back to disk and return the blobid of the table */ if ((table_id && !page) || resize) { ham_record_t rec={0}; rec.data=(ham_u8_t *)table; rec.size=sizeof(dupe_table_t) +(dupe_table_get_capacity(table)-1)*sizeof(dupe_entry_t); st=blob_overwrite(env, db, table_id, &rec, 0, rid); } else if (!table_id) { ham_record_t rec={0}; rec.data=(ham_u8_t *)table; rec.size=sizeof(dupe_table_t) +(dupe_table_get_capacity(table)-1)*sizeof(dupe_entry_t); st=blob_allocate(env, db, &rec, 0, rid); } else if (table_id && page) { page_set_dirty(page, env); } else { ham_assert(!"shouldn't be here", (0)); } if (alloc_table) allocator_free(env_get_allocator(env), table); if (new_position) *new_position=position; return (st); }