+ /* there's only a key anyway, so this is a no-op */
+ if (IS_LEAF2(mc->mc_pg[mc->mc_top])) {
+ unsigned int ksize = mc->mc_db->md_pad;
+ if (key->mv_size != ksize)
+ return EINVAL;
+ if (flags == MDB_CURRENT) {
+ char *ptr = LEAF2KEY(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], ksize);
+ memcpy(ptr, key->mv_data, ksize);
+ }
+ return MDB_SUCCESS;
+ }
+
+ leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
+
+ /* DB has dups? */
+ if (F_ISSET(mc->mc_db->md_flags, MDB_DUPSORT)) {
+ /* Was a single item before, must convert now */
+more:
+ if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+ /* Just overwrite the current item */
+ if (flags == MDB_CURRENT)
+ goto current;
+
+ dkey.mv_size = NODEDSZ(leaf);
+ dkey.mv_data = NODEDATA(leaf);
+#if UINT_MAX < SIZE_MAX
+ if (mc->mc_dbx->md_dcmp == mdb_cmp_int && dkey.mv_size == sizeof(size_t))
+#ifdef MISALIGNED_OK
+ mc->mc_dbx->md_dcmp = mdb_cmp_long;
+#else
+ mc->mc_dbx->md_dcmp = mdb_cmp_cint;
+#endif
+#endif
+ /* if data matches, ignore it */
+ if (!mc->mc_dbx->md_dcmp(data, &dkey))
+ return (flags == MDB_NODUPDATA) ? MDB_KEYEXIST : MDB_SUCCESS;
+
+ /* create a fake page for the dup items */
+ memcpy(dbuf, dkey.mv_data, dkey.mv_size);
+ dkey.mv_data = dbuf;
+ fp = (MDB_page *)&pbuf;
+ fp->mp_pgno = mc->mc_pg[mc->mc_top]->mp_pgno;
+ fp->mp_flags = P_LEAF|P_DIRTY|P_SUBP;
+ fp->mp_lower = PAGEHDRSZ;
+ fp->mp_upper = PAGEHDRSZ + dkey.mv_size + data->mv_size;
+ if (mc->mc_db->md_flags & MDB_DUPFIXED) {
+ fp->mp_flags |= P_LEAF2;
+ fp->mp_pad = data->mv_size;
+ } else {
+ fp->mp_upper += 2 * sizeof(indx_t) + 2 * NODESIZE +
+ (dkey.mv_size & 1) + (data->mv_size & 1);
+ }
+ mdb_node_del(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], 0);
+ do_sub = 1;
+ rdata = &xdata;
+ xdata.mv_size = fp->mp_upper;
+ xdata.mv_data = fp;
+ flags |= F_DUPDATA;
+ goto new_sub;
+ }
+ if (!F_ISSET(leaf->mn_flags, F_SUBDATA)) {
+ /* See if we need to convert from fake page to subDB */
+ MDB_page *mp;
+ unsigned int offset;
+ unsigned int i;
+
+ fp = NODEDATA(leaf);
+ if (flags == MDB_CURRENT) {
+ fp->mp_flags |= P_DIRTY;
+ COPY_PGNO(fp->mp_pgno, mc->mc_pg[mc->mc_top]->mp_pgno);
+ mc->mc_xcursor->mx_cursor.mc_pg[0] = fp;
+ flags |= F_DUPDATA;
+ goto put_sub;
+ }
+ if (mc->mc_db->md_flags & MDB_DUPFIXED) {
+ offset = fp->mp_pad;
+ } else {
+ offset = NODESIZE + sizeof(indx_t) + data->mv_size;
+ }
+ offset += offset & 1;
+ if (NODESIZE + sizeof(indx_t) + NODEKSZ(leaf) + NODEDSZ(leaf) +
+ offset >= (mc->mc_txn->mt_env->me_psize - PAGEHDRSZ) /
+ MDB_MINKEYS) {
+ /* yes, convert it */
+ dummy.md_flags = 0;
+ if (mc->mc_db->md_flags & MDB_DUPFIXED) {
+ dummy.md_pad = fp->mp_pad;
+ dummy.md_flags = MDB_DUPFIXED;
+ if (mc->mc_db->md_flags & MDB_INTEGERDUP)
+ dummy.md_flags |= MDB_INTEGERKEY;
+ }
+ dummy.md_depth = 1;
+ dummy.md_branch_pages = 0;
+ dummy.md_leaf_pages = 1;
+ dummy.md_overflow_pages = 0;
+ dummy.md_entries = NUMKEYS(fp);
+ rdata = &xdata;
+ xdata.mv_size = sizeof(MDB_db);
+ xdata.mv_data = &dummy;
+ mp = mdb_page_alloc(mc, 1);
+ if (!mp)
+ return ENOMEM;
+ offset = mc->mc_txn->mt_env->me_psize - NODEDSZ(leaf);
+ flags |= F_DUPDATA|F_SUBDATA;
+ dummy.md_root = mp->mp_pgno;
+ } else {
+ /* no, just grow it */
+ rdata = &xdata;
+ xdata.mv_size = NODEDSZ(leaf) + offset;
+ xdata.mv_data = &pbuf;
+ mp = (MDB_page *)&pbuf;
+ mp->mp_pgno = mc->mc_pg[mc->mc_top]->mp_pgno;
+ flags |= F_DUPDATA;
+ }
+ mp->mp_flags = fp->mp_flags | P_DIRTY;
+ mp->mp_pad = fp->mp_pad;
+ mp->mp_lower = fp->mp_lower;
+ mp->mp_upper = fp->mp_upper + offset;
+ if (IS_LEAF2(fp)) {
+ memcpy(METADATA(mp), METADATA(fp), NUMKEYS(fp) * fp->mp_pad);
+ } else {
+ nsize = NODEDSZ(leaf) - fp->mp_upper;
+ memcpy((char *)mp + mp->mp_upper, (char *)fp + fp->mp_upper, nsize);
+ for (i=0; i<NUMKEYS(fp); i++)
+ mp->mp_ptrs[i] = fp->mp_ptrs[i] + offset;
+ }
+ mdb_node_del(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], 0);
+ do_sub = 1;
+ goto new_sub;
+ }
+ /* data is on sub-DB, just store it */
+ flags |= F_DUPDATA|F_SUBDATA;
+ goto put_sub;
+ }
+current:
+ /* overflow page overwrites need special handling */
+ if (F_ISSET(leaf->mn_flags, F_BIGDATA)) {
+ MDB_page *omp;
+ pgno_t pg;
+ int ovpages, dpages;
+
+ ovpages = OVPAGES(NODEDSZ(leaf), mc->mc_txn->mt_env->me_psize);
+ dpages = OVPAGES(data->mv_size, mc->mc_txn->mt_env->me_psize);
+ memcpy(&pg, NODEDATA(leaf), sizeof(pg));
+ mdb_page_get(mc->mc_txn, pg, &omp);
+ /* Is the ov page writable and large enough? */
+ if ((omp->mp_flags & P_DIRTY) && ovpages >= dpages) {
+ /* yes, overwrite it. Note in this case we don't
+ * bother to try shrinking the node if the new data
+ * is smaller than the overflow threshold.
+ */
+ if (F_ISSET(flags, MDB_RESERVE))
+ data->mv_data = METADATA(omp);
+ else
+ memcpy(METADATA(omp), data->mv_data, data->mv_size);
+ goto done;
+ } else {
+ /* no, free ovpages */
+ int i;
+ mc->mc_db->md_overflow_pages -= ovpages;
+ for (i=0; i<ovpages; i++) {
+ DPRINTF("freed ov page %zu", pg);
+ mdb_midl_append(&mc->mc_txn->mt_free_pgs, pg);
+ pg++;
+ }
+ }
+ } else if (NODEDSZ(leaf) == data->mv_size) {
+ /* same size, just replace it. Note that we could
+ * also reuse this node if the new data is smaller,
+ * but instead we opt to shrink the node in that case.
+ */
+ if (F_ISSET(flags, MDB_RESERVE))
+ data->mv_data = NODEDATA(leaf);
+ else
+ memcpy(NODEDATA(leaf), data->mv_data, data->mv_size);
+ goto done;
+ }
+ mdb_node_del(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], 0);
+ mc->mc_db->md_entries--;
+ } else {
+ DPRINTF("inserting key at index %i", mc->mc_ki[mc->mc_top]);
+ }
+
+ rdata = data;
+
+new_sub:
+ nflags = flags & NODE_ADD_FLAGS;
+ nsize = IS_LEAF2(mc->mc_pg[mc->mc_top]) ? key->mv_size : mdb_leaf_size(mc->mc_txn->mt_env, key, rdata);
+ if (SIZELEFT(mc->mc_pg[mc->mc_top]) < nsize) {
+ if (( flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA )
+ nflags &= ~MDB_APPEND;
+ rc = mdb_page_split(mc, key, rdata, P_INVALID, nflags);
+ } else {
+ /* There is room already in this leaf page. */
+ rc = mdb_node_add(mc, mc->mc_ki[mc->mc_top], key, rdata, 0, nflags);
+ if (rc == 0 && !do_sub) {
+ /* Adjust other cursors pointing to mp */
+ MDB_cursor *m2, *m3;
+ MDB_dbi dbi = mc->mc_dbi;
+ unsigned i = mc->mc_top;
+ MDB_page *mp = mc->mc_pg[i];
+
+ if (mc->mc_flags & C_SUB)
+ dbi--;
+
+ for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
+ if (mc->mc_flags & C_SUB)
+ m3 = &m2->mc_xcursor->mx_cursor;
+ else
+ m3 = m2;
+ if (m3 == mc || m3->mc_snum < mc->mc_snum) continue;
+ if (m3->mc_pg[i] == mp && m3->mc_ki[i] >= mc->mc_ki[i]) {
+ m3->mc_ki[i]++;
+ }
+ }
+ }