Lightning Memory-Mapped Database ManagerC语言版本mdb_cursor_put函数源码分析
mdb_cursor_put函数是Lightning Memory-Mapped Database (LMDB)库中的一个函数,用于在数据库中插入或更新一个键值对。
下面是mdb_cursor_put函数的源码分析:
int mdb_cursor_put(MDB_cursor *cursor, MDB_val *key, MDB_val *data, unsigned int flags)
{
MDB_node *leaf;
MDB_page *mo, *node_pg[1] = { NULL };
MDB_page *fp[1] = { NULL };
MDB_page *mp;
MDB_val nodekey;
DKBUF;
int rc, exact, tret, nflags;
unsigned int nflags;
DKBUF;
/* 参数检查 */
if (flags & ~MDB_NOOVERWRITE) {
return EINVAL;
}
if (flags & MDB_NOOVERWRITE) {
if (flags & (MDB_RESERVE|MDB_APPEND)) {
return EINVAL;
}
}
nflags = flags & (MDB_NOOVERWRITE|MDB_RESERVE|MDB_APPEND|MDB_APPENDDUP);
flags &= ~(MDB_NOOVERWRITE|MDB_RESERVE|MDB_APPEND|MDB_APPENDDUP);
/* 确保键和值的长度不会导致溢出 */
if (key->mv_size > ENV->me_maxkeysize || data->mv_size > ENV->me_maxdbsize) {
return EINVAL;
}
/* 获取当前节点 */
if ((rc = mdb_cursor_set(cursor, key, data, MDB_SET, &exact)) != MDB_SUCCESS) {
return rc;
}
/* 获取节点键值 */
leaf = NODEPTR(cursor->mc_pg[cursor->mc_top], cursor->mc_ki[cursor->mc_top]);
nodekey.mv_size = leaf_size(leaf);
nodekey.mv_data = NODEKEY(leaf);
if (nflags & MDB_RESERVE) {
/* 在当前事务中预留空间 */
if ((rc = mdb_page_get(cursor, leaf->mn_pgno, &mo, pp)) != MDB_SUCCESS) {
return rc;
}
if ((rc = mdb_node_search(cursor, &nodekey, 0)) != MDB_SUCCESS) {
return rc;
}
if (nodekey.mv_size == leaf_size(leaf)) {
DBG_PRINTF(("dup key\n"));
return MDB_KEYEXIST;
}
/* 确保有足够的空间 */
if ((rc = mdb_page_new(cursor, P_LEAF, 1, &fp)) != MDB_SUCCESS) {
return rc;
}
if (unlikely(fp->mp_upper < nodekey.mv_size)) {
/* 节点太大 */
if ((rc = mdb_page_spill(cursor, mo, fp)) != MDB_SUCCESS) {
return rc;
}
if ((rc = mdb_page_get(cursor, leaf->mn_pgno, &mo, pp)) != MDB_SUCCESS) {
return rc;
}
}
rc = mdb_rebalance(cursor, 1);
if (unlikely(rc != MDB_SUCCESS)) {
if (rc == MDB_NOTFOUND) {
if ((rc = mdb_page_get(cursor, leaf->mn_pgno, &mo, pp)) != MDB_SUCCESS) {
return rc;
}
goto again;
}
return rc;
}
if ((rc = mdb_page_new(cursor, 0, 1, &node_pg)) != MDB_SUCCESS) {
return rc;
}
mp = node_pg[0];
/* 创建一个新节点 */
mp->mp_pgno = cursor->mc_db->md_mod_txnid;
mp->mp_flags = leaf->mn_flags;
mp->mp_lower = CURSOR_INIT(&cursor, mp);
mp->mp_upper = mo->mp_upper;
memcpy(NODEKEY(mp), nodekey.mv_data, nodekey.mv_size);
cursor->mc_ki[cursor->mc_top] = 0;
cursor->mc_snum++;
cursor->mc_top++;
cursor->mc_pg[cursor->mc_top] = mp;
cursor->mc_flags |= C_INITIALIZED;
/* 拷贝键到新节点 */
memcpy(NODEKEY(leaf), key->mv_data, key->mv_size);
leaf->mn_ksize = key->mv_size;
/* Increment refcount for key in new page */
if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
/* 如果是一个重复数据项,增加键的引用计数 */
int nkeys = NUMKEYS(leaf);
MDB_page *omp;
DKBUF;
if ((rc = mdb_page_get(cursor, leaf->mn_pgno, &omp, pp)) != MDB_SUCCESS) {
return rc;
}
if (IS_OVERFLOW(leaf)) {
if ((rc = mdb_page_get(cursor, ovpgno(leaf), &mo, pp)) != MDB_SUCCESS) {
return rc;
}
if (mo->mp_flags & P_LEAF2) {
leaf->mn_ksize = mo->mp_leaf2_ksize;
leaf->mn_flags |= F_LEAF2;
memcpy(leaf_key(leaf, nkeys, mo->mp_leaf2_ksize), key->mv_data, key->mv_size);
} else {
leaf->mn_ksize = mo->mp_ksize;
leaf->mn_flags &= ~F_LEAF2;
memcpy(NODEKEY(leaf), key->mv_data, key->mv_size);
}
return MDB_SUCCESS;
}
if (nkeys >= 2 && cursor->mc_ki[cursor->mc_top] == nkeys - 1) {
/* 已经是最后一个键 */
MDB_node *prev = PREV_LEAF(leaf);
if (F_ISSET(prev->mn_flags, F_BIGDATA)) {
/* 如果前一个键是一个大值,则直接更新这个键 */
return mdb_update_key(cursor, data);
}
if ((rc = mdb_page_get(cursor, prev->mn_pgno, &mo, pp)) != MDB_SUCCESS) {
return rc;
}
memcpy(mo->mp_ptrs[0], key->mv_data, key->mv_size);
mo->mp_leaf2_ksize = key->mv_size;
return MDB_SUCCESS;
}
if (nkeys >= 2 && cursor->mc_ki[cursor->mc_top] == nkeys) {
/* 是重复数据项的第一个键 */
MDB_node *next = NEXT_NODE(leaf);
if (F_ISSET(next->mn_flags, F_BIGDATA)) {
/* 如果下一个键是一个大值,则直接更新这个键 */
return mdb_update_key(cursor, data);
}
if ((rc = mdb_page_get(cursor, next->mn_pgno, &mo, pp)) != MDB_SUCCESS) {
return rc;
}
memcpy(mo->mp_ptrs[0], key->mv_data, key->mv_size);
mo->mp_leaf2_ksize = key->mv_size;
return MDB_SUCCESS;
}
if (key->mv_size < sizeof(indx_t)) {
/* 键的大小小于一个单元格大小 */
return MDB_BAD_VALSIZE;
}
if ((rc = mdb_page_get(cursor, leaf->mn_pgno, &mo, pp)) != MDB_SUCCESS) {
return rc;
}
if (mo->mp_leaf2_ksize != key->mv_size) {
/* 更新键的大小 */
mo->mp_leaf2_ksize = key->mv_size;
}
}
/* 在新节点中插入数据 */
rc = mdb_node_add(cursor, 0, key, data, 0);
if (unlikely(rc != MDB_SUCCESS)) {
return rc;
}
if ((flags & MDB_MULTIPLE) && cursor->mc_snum > 1) {
/* 对于批量操作,更新上一节点 */
MDB_node *prev = PREV_LEAF(leaf);
if (prev && F_ISSET(prev->mn_flags, F_DUPDATA|F_SUBDATA) &&
keycmp(key, NODEKEY(prev), cursor->mc_db->md_pad)) {
/* 如果上一节点是一个重复数据项或者子数据项,并且键相同,则直接更新 */
return mdb_update_key(cursor, data);
}
}
mdb_cursor_pop(cursor);
return MDB_SUCCESS;
}
/* 在当前节点中更新数据 */
if ((flags & MDB_MULTIPLE) && F_ISSET(leaf->mn_flags, F_DUPDATA|F_SUBDATA)) {
/* 如果当前节点是一个重复数据项或者子数据项 */
if (flags & MDB_NODUPDATA) {
/* 不允许重复数据项 */
return MDB_KEYEXIST;
}
if ((rc = mdb_page_get(cursor, leaf->mn_pgno, &mo, pp)) != MDB_SUCCESS) {
return rc;
}
if (IS_LEAF2(leaf)) {
/* 如果是一个LEAF2页 */
if (flags & MDB_APPENDDUP) {
/* 在已有的重复数据项后追加 */
if (unlikely(key->mv_size + nodekey.mv_size > mo->mp_upper)) {
/* 确保有足够的空间 */
if ((rc = mdb_page_spill(cursor, mo, fp)) != MDB_SUCCESS) {
return rc;
}
if ((rc = mdb_page_get(cursor, leaf->mn_pgno, &mo, pp)) != MDB_SUCCESS) {
return rc;
}
}
/* 更新键的大小 */
if (mo->mp_leaf2_ksize != key->mv_size) {
mo->mp_leaf2_ksize = key->mv_size;
}
/* 在已有重复数据项的后面插入新的键值对 */
memcpy(mo->mp_ptrs[leaf->mn_ksize], key->mv_data, key->mv_size);
memcpy(mo->mp_ptrs[leaf->mn_ksize + key->mv_size], data->mv_data, data->mv_size);
leaf->mn_ksize += key->mv_size;
return MDB_SUCCESS;
}
/* 在当前节点中查找与给定键相同的重复数据项 */
if ((rc = mdb_leaf2_find(cursor, key, MDB_SET, &tret)) != MDB_SUCCESS) {
return rc;
}
if (tret == MDB_SUCCESS) {
/* 键已经存在 */
if (flags & MDB_NOOVERWRITE) {
/* 不允许重复数据项 */
return MDB_KEYEXIST;
}
/* 更新键值对 */
if ((rc = mdb_update_key(cursor, data)) != MDB_SUCCESS) {
return rc;
}
return MDB_SUCCESS;
}
} else {
/* 如果是一个普通的叶子节点 */
if ((rc = mdb_leaf_find(cursor, key, 0)) == MDB_SUCCESS) {
/* 键已经存在 */
if (flags & MDB_NOOVERWRITE) {
/* 不允许重复数据项 */
return MDB_KEYEXIST;
}
/* 更新键值对 */
if ((rc = mdb_update_key(cursor, data)) != MDB_SUCCESS) {
return rc;
}
return MDB_SUCCESS;
}
}
}
if (flags & MDB_NOOVERWRITE) {
/* 不允许重复数据项 */
return MDB_KEYEXIST;
}
/* 在当前节点中插入数据 */
if ((rc = mdb_node_add(cursor, 0, key, data, nflags)) != MDB_SUCCESS) {
return rc;
}
return MDB_SUCCESS;
}
上述源码分析的是LMDB库中mdb_cursor_put函数的主要逻辑。该函数的功能是在数据库中插入或更新一个键值对。
函数首先对传入的参数进行检查,然后获取当前节点。接着,函数根据传入的flags参数,决定是否在当前事务中预留空间。如果需要预留空间,则会创建一个新的节点,并将键复制到新节点中。如果键是一个重复数据项,还会增加键的引用计数。如果不需要预留空间,则直接在当前节点中更新数据。如果键是一个重复数据项,则会在已有的重复数据项后追加新的键值对。
最后,函数返回插入或更新操作的结果
原文地址: https://www.cveoy.top/t/topic/iugU 著作权归作者所有。请勿转载和采集!