/** The version number for a database's lockfile format. */
#define MDB_LOCK_VERSION 1
- /** @brief The maximum size of a key we can write to a database.
+ /** @brief The max size of a key we can write, or 0 for dynamic max.
*
- * We require that keys all fit onto a regular page. This limit
- * could be raised a bit further if needed; to something just
- * under (page size / #MDB_MINKEYS / 3).
+ * Define this as 0 to compute the max from the page size. 511
+ * is default for backwards compat: liblmdb <= 0.9.10 can break
+ * when modifying a DB with keys/dupsort data bigger than its max.
*
- * Note that data items in an #MDB_DUPSORT database are actually keys
- * of a subDB, so they're also limited to this size.
+ * Data items in an #MDB_DUPSORT database are also limited to
+ * this size, since they're actually keys of a sub-DB. Keys and
+ * #MDB_DUPSORT data items must fit on a node in a regular page.
*/
#ifndef MDB_MAXKEYSIZE
#define MDB_MAXKEYSIZE 511
+#endif
+
+ /** The maximum size of a key we can write to the environment. */
+#if MDB_MAXKEYSIZE
+#define ENV_MAXKEY(env) (MDB_MAXKEYSIZE)
+#else
+#define ENV_MAXKEY(env) ((env)->me_maxkey)
#endif
/** @brief The maximum size of a data item.
#define MAXDATASIZE 0xffffffffUL
#if MDB_DEBUG
+ /** Key size which fits in a #DKBUF.
+ * @ingroup debug
+ */
+#define DKBUF_MAXKEYSIZE ((MDB_MAXKEYSIZE) > 0 ? (MDB_MAXKEYSIZE) : 511)
/** A key buffer.
* @ingroup debug
* This is used for printing a hex dump of a key's contents.
*/
-#define DKBUF char kbuf[(MDB_MAXKEYSIZE*2+1)]
+#define DKBUF char kbuf[DKBUF_MAXKEYSIZE*2+1]
/** Display a key in hex.
* @ingroup debug
* Invoke a function to display a key in hex.
int me_maxfree_1pg;
/** Max size of a node on a page */
unsigned int me_nodemax;
+#if !(MDB_MAXKEYSIZE)
+ unsigned int me_maxkey; /**< max size of a key */
+#endif
#ifdef _WIN32
int me_pidquery; /**< Used in OpenProcess */
HANDLE me_rmutex; /* Windows mutexes don't reside in shared mem */
if (!key)
return "";
- if (key->mv_size > MDB_MAXKEYSIZE)
+ if (key->mv_size > DKBUF_MAXKEYSIZE)
return "MDB_MAXKEYSIZE";
/* may want to make this a dynamic check: if the key is mostly
* printable characters, print it as-is instead of converting to hex.
return i;
}
}
+
env->me_maxfree_1pg = (env->me_psize - PAGEHDRSZ) / sizeof(pgno_t) - 1;
env->me_nodemax = (((env->me_psize - PAGEHDRSZ) / MDB_MINKEYS) & -2)
- sizeof(indx_t);
-
+#if !(MDB_MAXKEYSIZE)
+ env->me_maxkey = env->me_nodemax - (NODESIZE + sizeof(MDB_db));
+#endif
env->me_maxpg = env->me_mapsize / env->me_psize;
+
#if MDB_DEBUG
{
int toggle = mdb_env_pick_meta(env);
enum { MDB_NO_ROOT = MDB_LAST_ERRCODE+10 }; /* internal code */
MDB_env *env = mc->mc_txn->mt_env;
MDB_node *leaf = NULL;
- MDB_val xdata, *rdata, dkey;
+ MDB_page *fp, *mp;
+ uint16_t fp_flags;
+ MDB_val xdata, *rdata, dkey, olddata;
MDB_db dummy;
- int do_sub = 0, insert = 0;
+ int do_sub = 0, insert;
unsigned int mcount = 0, dcount = 0, nospill;
size_t nsize;
int rc, rc2;
if (mc->mc_txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_ERROR))
return (mc->mc_txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN;
- if (flags != MDB_CURRENT && (key->mv_size == 0 || key->mv_size > MDB_MAXKEYSIZE))
- return MDB_BAD_VALSIZE;
-
- if (F_ISSET(mc->mc_db->md_flags, MDB_DUPSORT) && data->mv_size > MDB_MAXKEYSIZE)
+ if (flags != MDB_CURRENT && key->mv_size-1 >= ENV_MAXKEY(env))
return MDB_BAD_VALSIZE;
#if SIZE_MAX > MAXDATASIZE
- if (data->mv_size > MAXDATASIZE)
+ if (data->mv_size > ((mc->mc_db->md_flags & MDB_DUPSORT) ? ENV_MAXKEY(env) : MAXDATASIZE))
+ return MDB_BAD_VALSIZE;
+#else
+ if ((mc->mc_db->md_flags & MDB_DUPSORT) && data->mv_size > ENV_MAXKEY(env))
return MDB_BAD_VALSIZE;
#endif
return rc2;
}
- /* The key already exists */
- if (rc == MDB_SUCCESS) {
- MDB_page *fp, *mp;
- MDB_val olddata;
-
+ insert = rc;
+ if (insert) {
+ /* The key does not exist */
+ DPRINTF(("inserting key at index %i", mc->mc_ki[mc->mc_top]));
+ if ((mc->mc_db->md_flags & MDB_DUPSORT) &&
+ LEAFSIZE(key, data) > env->me_nodemax)
+ {
+ /* Too big for a node, insert in sub-DB */
+ fp_flags = P_LEAF|P_DIRTY;
+ fp = env->me_pbuf;
+ fp->mp_pad = data->mv_size; /* used if MDB_DUPFIXED */
+ fp->mp_lower = fp->mp_upper = olddata.mv_size = PAGEHDRSZ;
+ goto prep_subDB;
+ }
+ } else {
/* 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;
/* DB has dups? */
if (F_ISSET(mc->mc_db->md_flags, MDB_DUPSORT)) {
+ /* Prepare (sub-)page/sub-DB to accept the new item,
+ * if needed. fp: old sub-page or a header faking
+ * it. mp: new (sub-)page. offset: growth in page
+ * size. xdata: node data with new page or DB.
+ */
+ unsigned i, offset = 0;
mp = fp = xdata.mv_data = env->me_pbuf;
mp->mp_pgno = mc->mc_pg[mc->mc_top]->mp_pgno;
(dkey.mv_size & 1) + (data->mv_size & 1);
}
fp->mp_upper = xdata.mv_size;
+ olddata.mv_size = fp->mp_upper; /* pretend olddata is fp */
} else if (leaf->mn_flags & F_SUBDATA) {
/* Data is on sub-DB, just store it */
flags |= F_DUPDATA|F_SUBDATA;
goto put_sub;
} else {
- /* See if we need to convert from fake page to subDB */
- unsigned int offset;
- unsigned int i;
- uint16_t fp_flags;
-
+ /* Data is on sub-page */
fp = olddata.mv_data;
switch (flags) {
default:
flags |= F_DUPDATA;
goto put_sub;
}
- fp_flags = fp->mp_flags;
xdata.mv_size = olddata.mv_size + offset;
- if (NODESIZE+NODEKSZ(leaf)+xdata.mv_size > env->me_nodemax) {
- /* yes, convert it */
+ }
+
+ fp_flags = fp->mp_flags;
+ if (NODESIZE + NODEKSZ(leaf) + xdata.mv_size > env->me_nodemax) {
+ /* Too big for a sub-page, convert to sub-DB */
+ fp_flags &= ~P_SUBP;
+prep_subDB:
if (mc->mc_db->md_flags & MDB_DUPFIXED) {
+ fp_flags |= P_LEAF2;
dummy.md_pad = fp->mp_pad;
dummy.md_flags = MDB_DUPFIXED;
if (mc->mc_db->md_flags & MDB_INTEGERDUP)
offset = env->me_psize - olddata.mv_size;
flags |= F_DUPDATA|F_SUBDATA;
dummy.md_root = mp->mp_pgno;
- fp_flags &= ~P_SUBP;
- }
+ }
+ if (mp != fp) {
mp->mp_flags = fp_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)) {
+ if (fp_flags & P_LEAF2) {
memcpy(METADATA(mp), METADATA(fp), NUMKEYS(fp) * fp->mp_pad);
} else {
memcpy((char *)mp + mp->mp_upper, (char *)fp + fp->mp_upper,
}
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]));
- insert = 1;
}
rdata = data;
#if MDB_DEBUG
{
MDB_val k2;
- char kbuf2[(MDB_MAXKEYSIZE*2+1)];
+ char kbuf2[DKBUF_MAXKEYSIZE*2+1];
k2.mv_data = NODEKEY(node);
k2.mv_size = node->mn_ksize;
DPRINTF(("update key %u (ofs %u) [%s] to [%s] on page %"Z"u",
int mdb_env_get_maxkeysize(MDB_env *env)
{
- return MDB_MAXKEYSIZE;
+ return ENV_MAXKEY(env);
}
int mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx)