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参数,决定是否在当前事务中预留空间。如果需要预留空间,则会创建一个新的节点,并将键复制到新节点中。如果键是一个重复数据项,还会增加键的引用计数。如果不需要预留空间,则直接在当前节点中更新数据。如果键是一个重复数据项,则会在已有的重复数据项后追加新的键值对。

最后,函数返回插入或更新操作的结果

Lightning Memory-Mapped Database ManagerC语言版本mdb_cursor_put函数源码分析

原文地址: https://www.cveoy.top/t/topic/iugU 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录