/** * 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); }
/** * 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); }
/** * 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); }
/** Close (and free) all cursors related to this database table. @note This is a B+-tree 'backend' method. */ static ham_status_t my_fun_close_cursors(ham_btree_t *be, ham_u32_t flags) { ham_db_t *db=be_get_db(be); ham_assert(db, (0)); return (btree_close_cursors(db, flags)); }
/** * 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); }
ham_status_t btree_insert_cursor(ham_btree_t *be, ham_key_t *key, ham_record_t *record, ham_bt_cursor_t *cursor, ham_u32_t flags) { ham_status_t st; ham_db_t *db=be_get_db(be); insert_hints_t hints = {flags, flags, (ham_cursor_t *)cursor, 0, HAM_FALSE, HAM_FALSE, HAM_FALSE, 0, NULL, -1}; btree_insert_get_hints(&hints, db, key); /* * append the key? __append_key() will try to append the key; if it * fails because the key is NOT the highest key in the database or * because the current page is already full, it will remove the * HINT_APPEND flag and call btree_insert_cursor() again */ if (hints.force_append || hints.force_prepend) { ham_assert(hints.try_fast_track, (0)); st = __append_key(be, key, record, cursor, &hints); } else { /*ham_assert(!hints.try_fast_track, (0)); */ hints.force_append = HAM_FALSE; hints.force_prepend = HAM_FALSE; st = __insert_cursor(be, key, record, cursor, &hints); } if (st) { stats_update_insert_fail(db, &hints); } else { stats_update_insert(db, hints.processed_leaf_page, &hints); stats_update_any_bound(HAM_OPERATION_STATS_INSERT, db, hints.processed_leaf_page, key, hints.flags, hints.processed_slot); } return (st); }
ham_status_t btree_find_cursor(ham_btree_t *be, ham_bt_cursor_t *cursor, ham_key_t *key, ham_record_t *record, ham_u32_t flags) { ham_status_t st; ham_page_t *page = NULL; btree_node_t *node = NULL; int_key_t *entry; ham_s32_t idx = -1; ham_db_t *db=be_get_db(be); find_hints_t hints = {flags, flags, 0, HAM_FALSE, HAM_FALSE, 1}; btree_find_get_hints(&hints, db, key); if (hints.key_is_out_of_bounds) { stats_update_find_fail_oob(db, &hints); return HAM_KEY_NOT_FOUND; } if (hints.try_fast_track) { /* * see if we get a sure hit within this btree leaf; if not, revert to * regular scan * * As this is a speed-improvement hint re-using recent material, the * page should still sit in the cache, or we're using old info, which * should be discarded. */ st = db_fetch_page(&page, db, hints.leaf_page_addr, DB_ONLY_FROM_CACHE); ham_assert(st ? !page : 1, (0)); if (st) return st; if (page) { node=ham_page_get_btree_node(page); ham_assert(btree_node_is_leaf(node), (0)); ham_assert(btree_node_get_count(node) >= 3, (0)); /* edges + middle match */ idx = btree_node_search_by_key(db, page, key, hints.flags); /* * if we didn't hit a match OR a match at either edge, FAIL. * A match at one of the edges is very risky, as this can also * signal a match far away from the current node, so we need * the full tree traversal then. */ if (idx <= 0 || idx >= btree_node_get_count(node) - 1) { idx = -1; } /* * else: we landed in the middle of the node, so we don't need to * traverse the entire tree now. */ } /* Reset any errors which may have been collected during the hinting * phase -- this is done by setting 'idx = -1' above as that effectively * clears the possible error code stored in there when (idx < -1) */ } if (idx == -1) { /* get the address of the root page */ if (!btree_get_rootpage(be)) { stats_update_find_fail(db, &hints); return HAM_KEY_NOT_FOUND; } /* load the root page */ st=db_fetch_page(&page, db, btree_get_rootpage(be), 0); ham_assert(st ? !page : 1, (0)); if (!page) { ham_assert(st, (0)); stats_update_find_fail(db, &hints); return st ? st : HAM_INTERNAL_ERROR; } /* now traverse the root to the leaf nodes, till we find a leaf */ node=ham_page_get_btree_node(page); if (!btree_node_is_leaf(node)) { /* signal 'don't care' when we have multiple pages; we resolve this once we've got a hit further down */ if (hints.flags & (HAM_FIND_LT_MATCH | HAM_FIND_GT_MATCH)) hints.flags |= (HAM_FIND_LT_MATCH | HAM_FIND_GT_MATCH); for (;;) { hints.cost++; st=btree_traverse_tree(&page, 0, db, page, key); if (!page) { stats_update_find_fail(db, &hints); return st ? st : HAM_KEY_NOT_FOUND; } node=ham_page_get_btree_node(page); if (btree_node_is_leaf(node)) break; } } /* check the leaf page for the key */ idx=btree_node_search_by_key(db, page, key, hints.flags); if (idx < -1) { stats_update_find_fail(db, &hints); return (ham_status_t)idx; } } /* end of regular search */ /* * When we are performing an approximate match, the worst case * scenario is where we've picked the wrong side of the fence * while sitting at a page/node boundary: that's what this * next piece of code resolves: * * essentially it moves one record forwards or backward when * the flags tell us this is mandatory and we're not yet in the proper * position yet. * * The whole trick works, because the code above detects when * we need to traverse a multi-page btree -- where this worst-case * scenario can happen -- and adjusted the flags to accept * both LT and GT approximate matches so that btree_node_search_by_key() * will be hard pressed to return a 'key not found' signal (idx==-1), * instead delivering the nearest LT or GT match; all we need to * do now is ensure we've got the right one and if not, * shift by one. */ if (idx >= 0) { if ((ham_key_get_intflags(key) & KEY_IS_APPROXIMATE) && (hints.original_flags & (HAM_FIND_LT_MATCH | HAM_FIND_GT_MATCH)) != (HAM_FIND_LT_MATCH | HAM_FIND_GT_MATCH)) { if ((ham_key_get_intflags(key) & KEY_IS_GT) && (hints.original_flags & HAM_FIND_LT_MATCH)) { /* * if the index-1 is still in the page, just decrement the * index */ if (idx > 0) { idx--; } else { /* * otherwise load the left sibling page */ if (!btree_node_get_left(node)) { stats_update_find_fail(db, &hints); ham_assert(node == ham_page_get_btree_node(page), (0)); stats_update_any_bound(db, page, key, hints.original_flags, -1); return HAM_KEY_NOT_FOUND; } hints.cost++; st = db_fetch_page(&page, db, btree_node_get_left(node), 0); ham_assert(st ? !page : 1, (0)); if (!page) { ham_assert(st, (0)); stats_update_find_fail(db, &hints); return st ? st : HAM_INTERNAL_ERROR; } node = ham_page_get_btree_node(page); idx = btree_node_get_count(node) - 1; } ham_key_set_intflags(key, (ham_key_get_intflags(key) & ~KEY_IS_APPROXIMATE) | KEY_IS_LT); } else if ((ham_key_get_intflags(key) & KEY_IS_LT) && (hints.original_flags & HAM_FIND_GT_MATCH)) { /* * if the index+1 is still in the page, just increment the * index */ if (idx + 1 < btree_node_get_count(node)) { idx++; } else { /* * otherwise load the right sibling page */ if (!btree_node_get_right(node)) { stats_update_find_fail(db, &hints); ham_assert(node == ham_page_get_btree_node(page), (0)); stats_update_any_bound(db, page, key, hints.original_flags, -1); return HAM_KEY_NOT_FOUND; } hints.cost++; st = db_fetch_page(&page, db, btree_node_get_right(node), 0); if (!page) { ham_assert(st, (0)); stats_update_find_fail(db, &hints); return st ? st : HAM_INTERNAL_ERROR; } node = ham_page_get_btree_node(page); idx = 0; } ham_key_set_intflags(key, (ham_key_get_intflags(key) & ~KEY_IS_APPROXIMATE) | KEY_IS_GT); } } else if (!(ham_key_get_intflags(key) & KEY_IS_APPROXIMATE) && !(hints.original_flags & HAM_FIND_EXACT_MATCH) && (hints.original_flags != 0)) { /* * 'true GT/LT' has been added @ 2009/07/18 to complete * the EQ/LEQ/GEQ/LT/GT functionality; * * 'true LT/GT' is simply an extension upon the already existing * LEQ/GEQ logic just above; all we do here is move one record * up/down as it just happens that we get an exact ('equal') * match here. * * The fact that the LT/GT constants share their bits with the * LEQ/GEQ flags so that LEQ==(LT|EXACT) and GEQ==(GT|EXACT) * ensures that we can restrict our work to a simple adjustment * right here; everything else has already been taken of by the * LEQ/GEQ logic in the section above when the key has been * flagged with the KEY_IS_APPROXIMATE flag. */ if (hints.original_flags & HAM_FIND_LT_MATCH) { /* * if the index-1 is still in the page, just decrement the * index */ if (idx > 0) { idx--; ham_key_set_intflags(key, (ham_key_get_intflags(key) & ~KEY_IS_APPROXIMATE) | KEY_IS_LT); } else { /* * otherwise load the left sibling page */ if (!btree_node_get_left(node)) { /* when an error is otherwise unavoidable, see if we have an escape route through GT? */ if (hints.original_flags & HAM_FIND_GT_MATCH) { /* * if the index+1 is still in the page, just * increment the index */ if (idx + 1 < btree_node_get_count(node)) { idx++; } else { /* * otherwise load the right sibling page */ if (!btree_node_get_right(node)) { stats_update_find_fail(db, &hints); ham_assert(node == ham_page_get_btree_node(page), (0)); stats_update_any_bound(db, page, key, hints.original_flags, -1); return HAM_KEY_NOT_FOUND; } hints.cost++; st = db_fetch_page(&page, db, btree_node_get_right(node), 0); if (!page) { ham_assert(st, (0)); stats_update_find_fail(db, &hints); return st ? st : HAM_INTERNAL_ERROR; } node = ham_page_get_btree_node(page); idx = 0; } ham_key_set_intflags(key, (ham_key_get_intflags(key) & ~KEY_IS_APPROXIMATE) | KEY_IS_GT); } else { stats_update_find_fail(db, &hints); ham_assert(node == ham_page_get_btree_node(page), (0)); stats_update_any_bound(db, page, key, hints.original_flags, -1); return HAM_KEY_NOT_FOUND; } } else { hints.cost++; st = db_fetch_page(&page, db, btree_node_get_left(node), 0); if (!page) { ham_assert(st, (0)); stats_update_find_fail(db, &hints); return st ? st : HAM_INTERNAL_ERROR; } node = ham_page_get_btree_node(page); idx = btree_node_get_count(node) - 1; ham_key_set_intflags(key, (ham_key_get_intflags(key) & ~KEY_IS_APPROXIMATE) | KEY_IS_LT); } } } else if (hints.original_flags & HAM_FIND_GT_MATCH) { /* * if the index+1 is still in the page, just increment the * index */ if (idx + 1 < btree_node_get_count(node)) { idx++; } else { /* * otherwise load the right sibling page */ if (!btree_node_get_right(node)) { stats_update_find_fail(db, &hints); ham_assert(node == ham_page_get_btree_node(page), (0)); stats_update_any_bound(db, page, key, hints.original_flags, -1); return HAM_KEY_NOT_FOUND; } hints.cost++; st = db_fetch_page(&page, db, btree_node_get_right(node), 0); if (!page) { ham_assert(st, (0)); stats_update_find_fail(db, &hints); return st ? st : HAM_INTERNAL_ERROR; } node = ham_page_get_btree_node(page); idx = 0; } ham_key_set_intflags(key, (ham_key_get_intflags(key) & ~KEY_IS_APPROXIMATE) | KEY_IS_GT); } } } if (idx<0) { stats_update_find_fail(db, &hints); ham_assert(node, (0)); ham_assert(page, (0)); ham_assert(node == ham_page_get_btree_node(page), (0)); stats_update_any_bound(db, page, key, hints.original_flags, -1); return HAM_KEY_NOT_FOUND; } /* load the entry, and store record ID and key flags */ entry=btree_node_get_key(db, node, idx); /* set the cursor-position to this key */ if (cursor) { ham_assert(!(bt_cursor_get_flags(cursor)&BT_CURSOR_FLAG_UNCOUPLED), ("coupling an uncoupled cursor, but need a nil-cursor")); ham_assert(!(bt_cursor_get_flags(cursor)&BT_CURSOR_FLAG_COUPLED), ("coupling a coupled cursor, but need a nil-cursor")); page_add_cursor(page, (ham_cursor_t *)cursor); bt_cursor_set_flags(cursor, bt_cursor_get_flags(cursor)|BT_CURSOR_FLAG_COUPLED); bt_cursor_set_coupled_page(cursor, page); bt_cursor_set_coupled_index(cursor, idx); } /* * during util_read_key and util_read_record, new pages might be needed, * and the page at which we're pointing could be moved out of memory; * that would mean that the cursor would be uncoupled, and we're losing * the 'entry'-pointer. therefore we 'lock' the page by incrementing * the reference counter */ page_add_ref(page); ham_assert(btree_node_is_leaf(node), ("iterator points to internal node")); /* no need to load the key if we have an exact match: */ if (key && (ham_key_get_intflags(key) & KEY_IS_APPROXIMATE)) { ham_status_t st=util_read_key(db, entry, key); if (st) { page_release_ref(page); stats_update_find_fail(db, &hints); return (st); } } if (record) { ham_status_t st; record->_intflags=key_get_flags(entry); record->_rid=key_get_ptr(entry); st=util_read_record(db, record, flags); if (st) { page_release_ref(page); stats_update_find_fail(db, &hints); return (st); } } page_release_ref(page); stats_update_find(db, page, &hints); ham_assert(node == ham_page_get_btree_node(page), (0)); stats_update_any_bound(db, page, key, hints.original_flags, idx); return (0); }
static ham_status_t __insert_cursor(ham_btree_t *be, ham_key_t *key, ham_record_t *record, ham_bt_cursor_t *cursor, insert_hints_t *hints) { ham_status_t st; ham_page_t *root; ham_db_t *db=be_get_db(be); ham_env_t *env = db_get_env(db); insert_scratchpad_t scratchpad; ham_assert(hints->force_append == HAM_FALSE, (0)); ham_assert(hints->force_prepend == HAM_FALSE, (0)); /* * initialize the scratchpad */ memset(&scratchpad, 0, sizeof(scratchpad)); scratchpad.be=be; scratchpad.record=record; scratchpad.cursor=cursor; /* * get the root-page... */ ham_assert(btree_get_rootpage(be)!=0, ("btree has no root page")); st=db_fetch_page(&root, db, btree_get_rootpage(be), 0); ham_assert(st ? root == NULL : 1, (0)); if (st) return st; /* * ... and start the recursion */ st=__insert_recursive(root, key, 0, &scratchpad, hints); /* * if the root page was split, we have to create a new * root page. */ if (st==SPLIT) { ham_page_t *newroot; btree_node_t *node; /* * the root-page will be changed... */ st=ham_log_add_page_before(root); if (st) return (st); /* * allocate a new root page */ st=db_alloc_page(&newroot, db, PAGE_TYPE_B_ROOT, 0); ham_assert(st ? newroot == NULL : 1, (0)); if (st) return (st); ham_assert(page_get_owner(newroot), ("")); /* clear the node header */ memset(page_get_payload(newroot), 0, sizeof(btree_node_t)); stats_page_is_nuked(db, root, HAM_TRUE); /* * insert the pivot element and the ptr_left */ node=ham_page_get_btree_node(newroot); btree_node_set_ptr_left(node, btree_get_rootpage(be)); st=__insert_nosplit(newroot, &scratchpad.key, scratchpad.rid, scratchpad.record, scratchpad.cursor, hints); ham_assert(!(scratchpad.key.flags & HAM_KEY_USER_ALLOC), (0)); scratchpad.cursor=0; /* don't overwrite cursor if __insert_nosplit is called again */ if (st) { ham_assert(!(scratchpad.key.flags & HAM_KEY_USER_ALLOC), (0)); if (scratchpad.key.data) allocator_free(env_get_allocator(env), scratchpad.key.data); return (st); } /* * set the new root page * * !! * do NOT delete the old root page - it's still in use! * * also don't forget to flush the backend - otherwise the header * page of the database will not contain the updated information. * The backend is flushed when the database is closed, but if * recovery is enabled then the flush here is critical. */ btree_set_rootpage(be, page_get_self(newroot)); be_set_dirty(be, HAM_TRUE); be->_fun_flush(be); /* * As we re-purpose a page, we will reset its pagecounter * as well to signal its first use as the new type assigned * here. */ if (env_get_cache(env) && (page_get_type(root)!=PAGE_TYPE_B_INDEX)) cache_update_page_access_counter(root, env_get_cache(env), 0); page_set_type(root, PAGE_TYPE_B_INDEX); page_set_dirty(root, env); page_set_dirty(newroot, env); /* the root page was modified (btree_set_rootpage) - make sure that * it's logged */ if (env_get_rt_flags(env)&HAM_ENABLE_RECOVERY) { st=txn_add_page(env_get_txn(env), env_get_header_page(env), HAM_TRUE); if (st) return (st); } } /* * release the scratchpad-memory and return to caller */ ham_assert(!(scratchpad.key.flags & HAM_KEY_USER_ALLOC), (0)); if (scratchpad.key.data) allocator_free(env_get_allocator(env), scratchpad.key.data); return (st); }
static ham_status_t __append_key(ham_btree_t *be, ham_key_t *key, ham_record_t *record, ham_bt_cursor_t *cursor, insert_hints_t *hints) { ham_status_t st=0; ham_page_t *page; btree_node_t *node; ham_db_t *db; #ifdef HAM_DEBUG if (cursor && !bt_cursor_is_nil(cursor)) { ham_assert(be_get_db(be) == bt_cursor_get_db(cursor), (0)); } #endif db = be_get_db(be); /* * see if we get this btree leaf; if not, revert to regular scan * * As this is a speed-improvement hint re-using recent material, the page * should still sit in the cache, or we're using old info, which should be * discarded. */ st = db_fetch_page(&page, db, hints->leaf_page_addr, DB_ONLY_FROM_CACHE); if (st) return st; if (!page) { hints->force_append = HAM_FALSE; hints->force_prepend = HAM_FALSE; return (__insert_cursor(be, key, record, cursor, hints)); } page_add_ref(page); node=ham_page_get_btree_node(page); ham_assert(btree_node_is_leaf(node), ("iterator points to internal node")); /* * if the page is already full OR this page is not the right-most page * when we APPEND or the left-most node when we PREPEND * OR the new key is not the highest key: perform a normal insert */ if ((hints->force_append && btree_node_get_right(node)) || (hints->force_prepend && btree_node_get_left(node)) || btree_node_get_count(node) >= btree_get_maxkeys(be)) { page_release_ref(page); hints->force_append = HAM_FALSE; hints->force_prepend = HAM_FALSE; return (__insert_cursor(be, key, record, cursor, hints)); } /* * if the page is not empty: check if we append the key at the end / start * (depending on force_append/force_prepend), * or if it's actually inserted in the middle (when neither force_append * or force_prepend is specified: that'd be SEQUENTIAL insertion * hinting somewhere in the middle of the total key range. */ if (btree_node_get_count(node)!=0) { int cmp_hi; int cmp_lo; hints->cost++; if (!hints->force_prepend) { cmp_hi = key_compare_pub_to_int(db, page, key, btree_node_get_count(node)-1); /* key is in the middle */ if (cmp_hi < -1) { page_release_ref(page); return (ham_status_t)cmp_hi; } /* key is at the end */ if (cmp_hi > 0) { if (btree_node_get_right(node)) { /* not at top end of the btree, so we can't do the * fast track */ page_release_ref(page); //hints->flags &= ~HAM_HINT_APPEND; hints->force_append = HAM_FALSE; hints->force_prepend = HAM_FALSE; return (__insert_cursor(be, key, record, cursor, hints)); } hints->force_append = HAM_TRUE; hints->force_prepend = HAM_FALSE; } } else { /* hints->force_prepend is true */ /* not bigger than the right-most node while we * were trying to APPEND */ cmp_hi = -1; } if (!hints->force_append) { cmp_lo = key_compare_pub_to_int(db, page, key, 0); /* in the middle range */ if (cmp_lo < -1) { page_release_ref(page); return ((ham_status_t)cmp_lo); } /* key is at the start of page */ if (cmp_lo < 0) { if (btree_node_get_left(node)) { /* not at bottom end of the btree, so we can't * do the fast track */ page_release_ref(page); //hints->flags &= ~HAM_HINT_PREPEND; hints->force_append = HAM_FALSE; hints->force_prepend = HAM_FALSE; return (__insert_cursor(be, key, record, cursor, hints)); } hints->force_append = HAM_FALSE; hints->force_prepend = HAM_TRUE; } } else { /* hints->force_prepend is true */ /* not smaller than the left-most node while we were * trying to PREPEND */ cmp_lo = +1; } /* handle inserts in the middle range */ if (cmp_lo >= 0 && cmp_hi <= 0) { /* * Depending on where we are in the btree, the current key either * is going to end up in the middle of the given node/page, * OR the given key is out of range of the given leaf node. */ if (hints->force_append || hints->force_prepend) { /* * when prepend or append is FORCED, we are expected to * add keys ONLY at the beginning or end of the btree * key range. Clearly the current key does not fit that * criterium. */ page_release_ref(page); //hints->flags &= ~HAM_HINT_PREPEND; hints->force_append = HAM_FALSE; hints->force_prepend = HAM_FALSE; return (__insert_cursor(be, key, record, cursor, hints)); } /* * we discovered that the key must be inserted in the middle * of the current leaf. * * It does not matter whether the current leaf is at the start or * end of the btree range; as we need to add the key in the middle * of the current leaf, that info alone is enough to continue with * the fast track insert operation. */ ham_assert(!hints->force_prepend && !hints->force_append, (0)); } ham_assert((hints->force_prepend + hints->force_append) < 2, ("Either APPEND or PREPEND flag MAY be set, but not both")); } else { /* empty page: force insertion in slot 0 */ hints->force_append = HAM_FALSE; hints->force_prepend = HAM_TRUE; } /* * the page will be changed - write it to the log (if a log exists) */ st=ham_log_add_page_before(page); if (st) { page_release_ref(page); return (st); } /* * OK - we're really appending/prepending the new key. */ ham_assert(hints->force_append || hints->force_prepend, (0)); st=__insert_nosplit(page, key, 0, record, cursor, hints); page_release_ref(page); return (st); }
/** * create and initialize a new backend * * @remark this function is called after the @a ham_db_t structure * and the file were created * * the @a flags are stored in the database; only transfer * the persistent flags! * * @note This is a B+-tree 'backend' method. */ static ham_status_t my_fun_create(ham_btree_t *be, ham_u16_t keysize, ham_u32_t flags) { ham_status_t st; ham_page_t *root; ham_size_t maxkeys; 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)); if (be_is_active(be)) { ham_trace(("backend has alread been initialized before!")); /* HAM_INTERNAL_ERROR -- not really, when keeping custom * backends in mind */ return HAM_ALREADY_INITIALIZED; } /* * 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; } /* * allocate a new root page */ st=db_alloc_page(&root, db, PAGE_TYPE_B_ROOT, PAGE_IGNORE_FREELIST); ham_assert(st ? root == NULL : 1, (0)); ham_assert(!st ? root != NULL : 1, (0)); if (!root) return st ? st : HAM_INTERNAL_ERROR; memset(page_get_raw_payload(root), 0, sizeof(btree_node_t)+sizeof(ham_perm_page_union_t)); /* * calculate the maximum number of keys for this page, * and make sure that this number is even */ btree_set_maxkeys(be, (ham_u16_t)maxkeys); be_set_dirty(be, HAM_TRUE); be_set_keysize(be, keysize); be_set_flags(be, flags); btree_set_rootpage(be, page_get_self(root)); index_clear_reserved(indexdata); index_set_max_keys(indexdata, (ham_u16_t)maxkeys); index_set_keysize(indexdata, keysize); index_set_self(indexdata, page_get_self(root)); index_set_flags(indexdata, flags); index_set_recno(indexdata, 0); index_clear_reserved(indexdata); env_set_dirty(db_get_env(db)); be_set_active(be, HAM_TRUE); return (0); }