* BerkeleyDB API, but much simplified.
*/
/*
- * Copyright 2011-2015 Howard Chu, Symas Corp.
+ * Copyright 2011-2016 Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
+#if defined(__WIN64__)
+#define _FILE_OFFSET_BITS 64
+#endif
#ifdef _WIN32
#include <malloc.h>
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
+
+#ifdef _MSC_VER
+#include <io.h>
+typedef SSIZE_T ssize_t;
+#else
#include <unistd.h>
+#endif
#if defined(__sun) || defined(ANDROID)
/* Most platforms have posix_memalign, older may only have memalign */
#define ESECT
#endif
-#ifdef _MSC_VER
+#ifdef _WIN32
#define CALL_CONV WINAPI
#else
#define CALL_CONV
#define MDB_OWNERDEAD EOWNERDEAD /**< #LOCK_MUTEX0() result if dead owner */
#endif
-#ifdef MDB_OWNERDEAD
+#ifdef __GLIBC__
+#define GLIBC_VER ((__GLIBC__ << 16 )| __GLIBC_MINOR__)
+#endif
+/** Some platforms define the EOWNERDEAD error code
+ * even though they don't support Robust Mutexes.
+ * Compile with -DMDB_USE_ROBUST=0, or use some other
+ * mechanism like -DMDB_USE_POSIX_SEM instead of
+ * -DMDB_USE_POSIX_MUTEX.
+ * (Posix semaphores are not robust.)
+ */
+#ifndef MDB_USE_ROBUST
+/* Android currently lacks Robust Mutex support. So does glibc < 2.4. */
+# if defined(MDB_USE_POSIX_MUTEX) && (defined(ANDROID) || \
+ (defined(__GLIBC__) && GLIBC_VER < 0x020004))
+# define MDB_USE_ROBUST 0
+# else
+# define MDB_USE_ROBUST 1
+/* glibc < 2.12 only provided _np API */
+# if (defined(__GLIBC__) && GLIBC_VER < 0x02000c) || \
+ (defined(PTHREAD_MUTEX_ROBUST_NP) && !defined(PTHREAD_MUTEX_ROBUST))
+# define PTHREAD_MUTEX_ROBUST PTHREAD_MUTEX_ROBUST_NP
+# define pthread_mutexattr_setrobust(attr, flag) pthread_mutexattr_setrobust_np(attr, flag)
+# define pthread_mutex_consistent(mutex) pthread_mutex_consistent_np(mutex)
+# endif
+# endif
+#endif /* MDB_USE_ROBUST */
+
+#if defined(MDB_OWNERDEAD) && MDB_USE_ROBUST
#define MDB_ROBUST_SUPPORTED 1
#endif
*
* @note If O_DSYNC is undefined but exists in /usr/include,
* preferably set some compiler flag to get the definition.
- * Otherwise compile with the less efficient -DMDB_DSYNC=O_SYNC.
*/
#ifndef MDB_DSYNC
+# ifdef O_DSYNC
# define MDB_DSYNC O_DSYNC
+# else
+# define MDB_DSYNC O_SYNC
+# endif
#endif
#endif
#define FREE_DBI 0
/** Handle for the default DB. */
#define MAIN_DBI 1
+ /** Number of DBs in metapage (free and main) - also hardcoded elsewhere */
+#define CORE_DBS 2
+
+ /** Number of meta pages - also hardcoded elsewhere */
+#define NUM_METAS 2
/** Meta page content.
* A meta page is the start point for accessing a database snapshot.
uint32_t mm_version;
void *mm_address; /**< address for fixed mapping */
size_t mm_mapsize; /**< size of mmap region */
- MDB_db mm_dbs[2]; /**< first is free space, 2nd is main db */
+ MDB_db mm_dbs[CORE_DBS]; /**< first is free space, 2nd is main db */
/** The size of pages used in this DB */
-#define mm_psize mm_dbs[0].md_pad
+#define mm_psize mm_dbs[FREE_DBI].md_pad
/** Any persistent environment flags. @ref mdb_env */
-#define mm_flags mm_dbs[0].md_flags
+#define mm_flags mm_dbs[FREE_DBI].md_flags
pgno_t mm_last_pg; /**< last used page in file */
volatile txnid_t mm_txnid; /**< txnid that committed this page */
} MDB_meta;
* @ingroup internal
* @{
*/
-#define DB_DIRTY 0x01 /**< DB was modified or is DUPSORT data */
+#define DB_DIRTY 0x01 /**< DB was written in this txn */
#define DB_STALE 0x02 /**< Named-DB record is older than txnID */
#define DB_NEW 0x04 /**< Named-DB handle opened in this txn */
#define DB_VALID 0x08 /**< DB handle is valid, see also #MDB_VALID */
#define DB_USRVALID 0x10 /**< As #DB_VALID, but not set for #FREE_DBI */
+#define DB_DUPDATA 0x20 /**< DB is #MDB_DUPSORT data */
/** @} */
/** In write txns, array of cursors for each DB */
MDB_cursor **mt_cursors;
#define C_EOF 0x02 /**< No more data */
#define C_SUB 0x04 /**< Cursor is a sub-cursor */
#define C_DEL 0x08 /**< last op was a cursor_del */
-#define C_SPLITTING 0x20 /**< Cursor is in page_split */
#define C_UNTRACK 0x40 /**< Un-track cursor when closing */
/** @} */
unsigned int mc_flags; /**< @ref mdb_cursor */
unsigned char mx_dbflag;
} MDB_xcursor;
+ /** Check if there is an inited xcursor, so #XCURSOR_REFRESH() is proper */
+#define XCURSOR_INITED(mc) \
+ ((mc)->mc_xcursor && ((mc)->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED))
+
+ /** Update sub-page pointer, if any, in \b mc->mc_xcursor. Needed
+ * when the node which contains the sub-page may have moved. Called
+ * with \b mp = mc->mc_pg[mc->mc_top], \b ki = mc->mc_ki[mc->mc_top].
+ */
+#define XCURSOR_REFRESH(mc, mp, ki) do { \
+ MDB_page *xr_pg = (mp); \
+ MDB_node *xr_node = NODEPTR(xr_pg, ki); \
+ if ((xr_node->mn_flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA) \
+ (mc)->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(xr_node); \
+} while (0)
+
/** State of FreeDB old pages, stored in the MDB_env */
typedef struct MDB_pgstate {
pgno_t *mf_pghead; /**< Reclaimed freeDB pages, or NULL before use */
char *me_path; /**< path to the DB files */
char *me_map; /**< the memory map of the data file */
MDB_txninfo *me_txns; /**< the memory map of the lock file or NULL */
- MDB_meta *me_metas[2]; /**< pointers to the two meta pages */
+ MDB_meta *me_metas[NUM_METAS]; /**< pointers to the two meta pages */
void *me_pbuf; /**< scratch area for DUPSORT put() */
MDB_txn *me_txn; /**< current write transaction */
MDB_txn *me_txn0; /**< prealloc'd write transaction */
#endif
/** max bytes to write in one call */
-#define MAX_WRITE (0x80000000U >> (sizeof(ssize_t) == 4))
+#define MAX_WRITE (0x40000000U >> (sizeof(ssize_t) == 4))
/** Check \b txn and \b dbi arguments to a function */
#define TXN_DBI_EXIST(txn, dbi, validity) \
#define MDB_END_SLOT MDB_NOTLS /**< release any reader slot if #MDB_NOTLS */
static void mdb_txn_end(MDB_txn *txn, unsigned mode);
-static int mdb_page_get(MDB_txn *txn, pgno_t pgno, MDB_page **mp, int *lvl);
+static int mdb_page_get(MDB_cursor *mc, pgno_t pgno, MDB_page **mp, int *lvl);
static int mdb_page_search_root(MDB_cursor *mc,
MDB_val *key, int modify);
#define MDB_PS_MODIFY 1
MDB_val *key, MDB_val *data, pgno_t pgno, unsigned int flags);
static void mdb_node_del(MDB_cursor *mc, int ksize);
static void mdb_node_shrink(MDB_page *mp, indx_t indx);
-static int mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst);
-static int mdb_node_read(MDB_txn *txn, MDB_node *leaf, MDB_val *data);
+static int mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft);
+static int mdb_node_read(MDB_cursor *mc, MDB_node *leaf, MDB_val *data);
static size_t mdb_leaf_size(MDB_env *env, MDB_val *key, MDB_val *data);
static size_t mdb_branch_size(MDB_env *env, MDB_val *key);
static SECURITY_DESCRIPTOR mdb_null_sd;
static SECURITY_ATTRIBUTES mdb_all_sa;
static int mdb_sec_inited;
+
+static int utf8_to_utf16(const char *src, int srcsize, wchar_t **dst, int *dstsize);
#endif
/** Return the library version info. */
* This works as long as no function between the call to mdb_strerror
* and the actual use of the message uses more than 4K of stack.
*/
- char pad[4096];
- char buf[1024], *ptr = buf;
+#define MSGSIZE 1024
+#define PADSIZE 4096
+ char buf[MSGSIZE+PADSIZE], *ptr = buf;
#endif
int i;
if (!err)
;
}
buf[0] = 0;
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, err, 0, ptr, sizeof(buf), (va_list *)pad);
+ NULL, err, 0, ptr, MSGSIZE, (va_list *)buf+MSGSIZE);
return ptr;
#else
return strerror(err);
/** assert(3) variant in cursor context */
#define mdb_cassert(mc, expr) mdb_assert0((mc)->mc_txn->mt_env, expr, #expr)
/** assert(3) variant in transaction context */
-#define mdb_tassert(mc, expr) mdb_assert0((txn)->mt_env, expr, #expr)
+#define mdb_tassert(txn, expr) mdb_assert0((txn)->mt_env, expr, #expr)
/** assert(3) variant in environment context */
#define mdb_eassert(env, expr) mdb_assert0(env, expr, #expr)
MDB_node *node;
MDB_page *mp;
- if (!mc->mc_snum && !(mc->mc_flags & C_INITIALIZED)) return;
+ if (!mc->mc_snum || !(mc->mc_flags & C_INITIALIZED)) return;
for (i=0; i<mc->mc_top; i++) {
mp = mc->mc_pg[i];
node = NODEPTR(mp, mc->mc_ki[i]);
}
if (mc->mc_ki[i] >= NUMKEYS(mc->mc_pg[i]))
printf("ack!\n");
+ if (XCURSOR_INITED(mc)) {
+ node = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
+ if (((node->mn_flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA) &&
+ mc->mc_xcursor->mx_cursor.mc_pg[0] != NODEDATA(node)) {
+ printf("blah!\n");
+ }
+ }
}
#endif
mdb_tassert(txn, rc == MDB_NOTFOUND);
}
}
- if (freecount + count + 2 /* metapages */ != txn->mt_next_pgno) {
+ if (freecount + count + NUM_METAS != txn->mt_next_pgno) {
fprintf(stderr, "audit: %lu freecount: %lu count: %lu total: %lu next_pgno: %lu\n",
- txn->mt_txnid, freecount, count+2, freecount+count+2, txn->mt_next_pgno);
+ txn->mt_txnid, freecount, count+NUM_METAS,
+ freecount+count+NUM_METAS, txn->mt_next_pgno);
}
}
#endif
{
enum { Mask = P_SUBP|P_DIRTY|P_LOOSE|P_KEEP };
MDB_txn *txn = mc->mc_txn;
- MDB_cursor *m3;
+ MDB_cursor *m3, *m0 = mc;
MDB_xcursor *mx;
MDB_page *dp, *mp;
MDB_node *leaf;
pgno_t pgno = txn->mt_dbs[i].md_root;
if (pgno == P_INVALID)
continue;
- if ((rc = mdb_page_get(txn, pgno, &dp, &level)) != MDB_SUCCESS)
+ if ((rc = mdb_page_get(m0, pgno, &dp, &level)) != MDB_SUCCESS)
break;
if ((dp->mp_flags & Mask) == pflags && level <= 1)
dp->mp_flags ^= P_KEEP;
/* Estimate how much space this op will take */
i = m0->mc_db->md_depth;
/* Named DBs also dirty the main DB */
- if (m0->mc_dbi > MAIN_DBI)
+ if (m0->mc_dbi >= CORE_DBS)
i += txn->mt_dbs[MAIN_DBI].md_depth;
/* For puts, roughly factor in the key+data size */
if (key)
}
np = m2.mc_pg[m2.mc_top];
leaf = NODEPTR(np, m2.mc_ki[m2.mc_top]);
- if ((rc = mdb_node_read(txn, leaf, &data)) != MDB_SUCCESS)
+ if ((rc = mdb_node_read(&m2, leaf, &data)) != MDB_SUCCESS)
return rc;
idl = (MDB_ID *) data.mv_data;
} else {
for (; m2; m2=m2->mc_next) {
if (m2->mc_snum < mc->mc_snum) continue;
+ if (m2 == mc) continue;
if (m2->mc_pg[mc->mc_top] == mp) {
m2->mc_pg[mc->mc_top] = np;
- if ((mc->mc_db->md_flags & MDB_DUPSORT) &&
- IS_LEAF(np) &&
- m2->mc_ki[mc->mc_top] == mc->mc_ki[mc->mc_top])
- {
- MDB_node *leaf = NODEPTR(np, mc->mc_ki[mc->mc_top]);
- if (!(leaf->mn_flags & F_SUBDATA))
- m2->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(leaf);
- }
+ if (XCURSOR_INITED(m2) && IS_LEAF(np))
+ XCURSOR_REFRESH(m2, np, m2->mc_ki[mc->mc_top]);
}
}
}
*bk = *mc;
mc->mc_backup = bk;
mc->mc_db = &dst->mt_dbs[i];
- /* Kill pointers into src - and dst to reduce abuse: The
- * user may not use mc until dst ends. Otherwise we'd...
+ /* Kill pointers into src to reduce abuse: The
+ * user may not use mc until dst ends. But we need a valid
+ * txn pointer here for cursor fixups to keep working.
*/
- mc->mc_txn = NULL; /* ...set this to dst */
- mc->mc_dbflag = NULL; /* ...and &dst->mt_dbflags[i] */
+ mc->mc_txn = dst;
+ mc->mc_dbflag = &dst->mt_dbflags[i];
if ((mx = mc->mc_xcursor) != NULL) {
*(MDB_xcursor *)(bk+1) = *mx;
- mx->mx_cursor.mc_txn = NULL; /* ...and dst. */
+ mx->mx_cursor.mc_txn = dst;
}
mc->mc_next = dst->mt_cursors[i];
dst->mt_cursors[i] = mc;
}
/* Copy the DB info and flags */
- memcpy(txn->mt_dbs, meta->mm_dbs, 2 * sizeof(MDB_db));
+ memcpy(txn->mt_dbs, meta->mm_dbs, CORE_DBS * sizeof(MDB_db));
/* Moved to here to avoid a data race in read TXNs */
txn->mt_next_pgno = meta->mm_last_pg+1;
/* Setup db info */
txn->mt_numdbs = env->me_numdbs;
- for (i=2; i<txn->mt_numdbs; i++) {
+ for (i=CORE_DBS; i<txn->mt_numdbs; i++) {
x = env->me_dbflags[i];
txn->mt_dbs[i].md_flags = x & PERSISTENT_FLAGS;
txn->mt_dbflags[i] = (x & MDB_VALID) ? DB_VALID|DB_USRVALID|DB_STALE : 0;
txn->mt_dbflags[MAIN_DBI] = DB_VALID|DB_USRVALID;
txn->mt_dbflags[FREE_DBI] = DB_VALID;
- if (env->me_maxpg < txn->mt_next_pgno) {
- mdb_txn_end(txn, new_notls /*0 or MDB_END_SLOT*/ | MDB_END_FAIL_BEGIN);
- return MDB_MAP_RESIZED;
+ if (env->me_flags & MDB_FATAL_ERROR) {
+ DPUTS("environment had fatal error, must shutdown!");
+ rc = MDB_PANIC;
+ } else if (env->me_maxpg < txn->mt_next_pgno) {
+ rc = MDB_MAP_RESIZED;
+ } else {
+ return MDB_SUCCESS;
}
-
- return MDB_SUCCESS;
+ mdb_txn_end(txn, new_notls /*0 or MDB_END_SLOT*/ | MDB_END_FAIL_BEGIN);
+ return rc;
}
int
if (!txn || !F_ISSET(txn->mt_flags, MDB_TXN_RDONLY|MDB_TXN_FINISHED))
return EINVAL;
- if (txn->mt_env->me_flags & MDB_FATAL_ERROR) {
- DPUTS("environment had fatal error, must shutdown!");
- return MDB_PANIC;
- }
-
rc = mdb_txn_renew0(txn);
if (rc == MDB_SUCCESS) {
DPRINTF(("renew txn %"Z"u%c %p on mdbenv %p, root page %"Z"u",
flags &= MDB_TXN_BEGIN_FLAGS;
flags |= env->me_flags & MDB_WRITEMAP;
- if (env->me_flags & MDB_FATAL_ERROR) {
- DPUTS("environment had fatal error, must shutdown!");
- return MDB_PANIC;
- }
if (env->me_flags & MDB_RDONLY & ~flags) /* write txn in RDONLY env */
return EACCES;
MDB_env *env = txn->mt_env;
unsigned char *tdbflags = txn->mt_dbflags;
- for (i = n; --i >= 2;) {
+ for (i = n; --i >= CORE_DBS;) {
if (tdbflags[i] & DB_NEW) {
if (keep) {
env->me_dbflags[i] = txn->mt_dbs[i].md_flags | MDB_VALID;
/* Update parent's DB table. */
memcpy(parent->mt_dbs, txn->mt_dbs, txn->mt_numdbs * sizeof(MDB_db));
parent->mt_numdbs = txn->mt_numdbs;
- parent->mt_dbflags[0] = txn->mt_dbflags[0];
- parent->mt_dbflags[1] = txn->mt_dbflags[1];
- for (i=2; i<txn->mt_numdbs; i++) {
+ parent->mt_dbflags[FREE_DBI] = txn->mt_dbflags[FREE_DBI];
+ parent->mt_dbflags[MAIN_DBI] = txn->mt_dbflags[MAIN_DBI];
+ for (i=CORE_DBS; i<txn->mt_numdbs; i++) {
/* preserve parent's DB_NEW status */
x = parent->mt_dbflags[i] & DB_NEW;
parent->mt_dbflags[i] = txn->mt_dbflags[i] | x;
pspill[0] = y;
}
+ /* Remove anything in our spill list from parent's dirty list */
+ if (txn->mt_spill_pgs && txn->mt_spill_pgs[0]) {
+ for (i=1; i<=txn->mt_spill_pgs[0]; i++) {
+ MDB_ID pn = txn->mt_spill_pgs[i];
+ if (pn & 1)
+ continue; /* deleted spillpg */
+ pn >>= 1;
+ y = mdb_mid2l_search(dst, pn);
+ if (y <= dst[0].mid && dst[y].mid == pn) {
+ free(dst[y].mptr);
+ while (y < dst[0].mid) {
+ dst[y] = dst[y+1];
+ y++;
+ }
+ dst[0].mid--;
+ }
+ }
+ }
+
/* Find len = length of merging our dirty list with parent's */
x = dst[0].mid;
dst[0].mid = 0; /* simplify loops */
}
/* Append our loose page list to parent's */
- for (lp = &parent->mt_loose_pgs; *lp; lp = &NEXT_LOOSE_PAGE(lp))
+ for (lp = &parent->mt_loose_pgs; *lp; lp = &NEXT_LOOSE_PAGE(*lp))
;
*lp = txn->mt_loose_pgs;
parent->mt_loose_count += txn->mt_loose_count;
txn->mt_txnid, (void*)txn, (void*)env, txn->mt_dbs[MAIN_DBI].md_root));
/* Update DB root pointers */
- if (txn->mt_numdbs > 2) {
+ if (txn->mt_numdbs > CORE_DBS) {
MDB_cursor mc;
MDB_dbi i;
MDB_val data;
data.mv_size = sizeof(MDB_db);
mdb_cursor_init(&mc, txn, MAIN_DBI, NULL);
- for (i = 2; i < txn->mt_numdbs; i++) {
+ for (i = CORE_DBS; i < txn->mt_numdbs; i++) {
if (txn->mt_dbflags[i] & DB_DIRTY) {
if (TXN_DBI_CHANGED(txn, i)) {
rc = MDB_BAD_DBI;
* Read both meta pages so we can use the latest one.
*/
- for (i=off=0; i<2; i++, off = meta->mm_psize) {
+ for (i=off=0; i<NUM_METAS; i++, off += meta->mm_psize) {
#ifdef _WIN32
DWORD len;
OVERLAPPED ov;
meta->mm_version = MDB_DATA_VERSION;
meta->mm_mapsize = env->me_mapsize;
meta->mm_psize = env->me_psize;
- meta->mm_last_pg = 1;
+ meta->mm_last_pg = NUM_METAS-1;
meta->mm_flags = env->me_flags & 0xffff;
- meta->mm_flags |= MDB_INTEGERKEY;
- meta->mm_dbs[0].md_root = P_INVALID;
- meta->mm_dbs[1].md_root = P_INVALID;
+ meta->mm_flags |= MDB_INTEGERKEY; /* this is mm_dbs[FREE_DBI].md_flags */
+ meta->mm_dbs[FREE_DBI].md_root = P_INVALID;
+ meta->mm_dbs[MAIN_DBI].md_root = P_INVALID;
}
/** Write the environment parameters of a freshly created DB environment.
psize = env->me_psize;
- p = calloc(2, psize);
+ p = calloc(NUM_METAS, psize);
if (!p)
return ENOMEM;
q->mp_flags = P_META;
*(MDB_meta *)METADATA(q) = *meta;
- DO_PWRITE(rc, env->me_fd, p, psize * 2, len, 0);
+ DO_PWRITE(rc, env->me_fd, p, psize * NUM_METAS, len, 0);
if (!rc)
rc = ErrCode();
- else if ((unsigned) len == psize * 2)
+ else if ((unsigned) len == psize * NUM_METAS)
rc = MDB_SUCCESS;
else
rc = ENOSPC;
if (flags & MDB_WRITEMAP) {
mp->mm_mapsize = mapsize;
- mp->mm_dbs[0] = txn->mt_dbs[0];
- mp->mm_dbs[1] = txn->mt_dbs[1];
+ mp->mm_dbs[FREE_DBI] = txn->mt_dbs[FREE_DBI];
+ mp->mm_dbs[MAIN_DBI] = txn->mt_dbs[MAIN_DBI];
mp->mm_last_pg = txn->mt_next_pgno - 1;
#if (__GNUC__ * 100 + __GNUC_MINOR__ >= 404) && /* TODO: portability */ \
!(defined(__i386__) || defined(__x86_64__))
if (!(flags & (MDB_NOMETASYNC|MDB_NOSYNC))) {
unsigned meta_size = env->me_psize;
rc = (env->me_flags & MDB_MAPASYNC) ? MS_ASYNC : MS_SYNC;
- ptr = env->me_map;
- if (toggle) {
+ ptr = (char *)mp - PAGEHDRSZ;
#ifndef _WIN32 /* POSIX msync() requires ptr = start of OS page */
- if (meta_size < env->me_os_psize)
- meta_size += meta_size;
- else
+ r2 = (ptr - env->me_map) & (env->me_os_psize - 1);
+ ptr -= r2;
+ meta_size += r2;
#endif
- ptr += meta_size;
- }
if (MDB_MSYNC(ptr, meta_size, rc)) {
rc = ErrCode();
goto fail;
}
goto done;
}
- metab.mm_txnid = env->me_metas[toggle]->mm_txnid;
- metab.mm_last_pg = env->me_metas[toggle]->mm_last_pg;
+ metab.mm_txnid = mp->mm_txnid;
+ metab.mm_last_pg = mp->mm_last_pg;
meta.mm_mapsize = mapsize;
- meta.mm_dbs[0] = txn->mt_dbs[0];
- meta.mm_dbs[1] = txn->mt_dbs[1];
+ meta.mm_dbs[FREE_DBI] = txn->mt_dbs[FREE_DBI];
+ meta.mm_dbs[MAIN_DBI] = txn->mt_dbs[MAIN_DBI];
meta.mm_last_pg = txn->mt_next_pgno - 1;
meta.mm_txnid = txn->mt_txnid;
off = offsetof(MDB_meta, mm_mapsize);
ptr = (char *)&meta + off;
len = sizeof(MDB_meta) - off;
- if (toggle)
- off += env->me_psize;
- off += PAGEHDRSZ;
+ off += (char *)mp - env->me_map;
/* Write to the SYNC fd */
mfd = (flags & (MDB_NOSYNC|MDB_NOMETASYNC)) ? env->me_fd : env->me_mfd;
return ENOMEM;
e->me_maxreaders = DEFAULT_READERS;
- e->me_maxdbs = e->me_numdbs = 2;
+ e->me_maxdbs = e->me_numdbs = CORE_DBS;
e->me_fd = INVALID_HANDLE_VALUE;
e->me_lfd = INVALID_HANDLE_VALUE;
e->me_mfd = INVALID_HANDLE_VALUE;
{
if (env->me_map)
return EINVAL;
- env->me_maxdbs = dbs + 2; /* Named databases + main and free DB */
+ env->me_maxdbs = dbs + CORE_DBS;
return MDB_SUCCESS;
}
off_t size, rsize;
#ifdef _WIN32
- env->me_lfd = CreateFile(lpath, GENERIC_READ|GENERIC_WRITE,
+ wchar_t *wlpath;
+ rc = utf8_to_utf16(lpath, -1, &wlpath, NULL);
+ if (rc)
+ return rc;
+ env->me_lfd = CreateFileW(wlpath, GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
+ free(wlpath);
#else
env->me_lfd = open(lpath, O_RDWR|O_CREAT|MDB_CLOEXEC, mode);
#endif
mdb_hash_enc(&val, encbuf);
sprintf(env->me_txns->mti_rmname, "Global\\MDBr%s", encbuf);
sprintf(env->me_txns->mti_wmname, "Global\\MDBw%s", encbuf);
- env->me_rmutex = CreateMutex(&mdb_all_sa, FALSE, env->me_txns->mti_rmname);
+ env->me_rmutex = CreateMutexA(&mdb_all_sa, FALSE, env->me_txns->mti_rmname);
if (!env->me_rmutex) goto fail_errno;
- env->me_wmutex = CreateMutex(&mdb_all_sa, FALSE, env->me_txns->mti_wmname);
+ env->me_wmutex = CreateMutexA(&mdb_all_sa, FALSE, env->me_txns->mti_wmname);
if (!env->me_wmutex) goto fail_errno;
#elif defined(MDB_USE_POSIX_SEM)
struct stat stbuf;
#else /* MDB_USE_POSIX_MUTEX: */
pthread_mutexattr_t mattr;
- if ((rc = pthread_mutexattr_init(&mattr))
- || (rc = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED))
+ /* Solaris needs this before initing a robust mutex. Otherwise
+ * it may skip the init and return EBUSY "seems someone already
+ * inited" or EINVAL "it was inited differently".
+ */
+ memset(env->me_txns->mti_rmutex, 0, sizeof(*env->me_txns->mti_rmutex));
+ memset(env->me_txns->mti_wmutex, 0, sizeof(*env->me_txns->mti_wmutex));
+
+ if ((rc = pthread_mutexattr_init(&mattr)))
+ goto fail;
+
+ rc = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
#ifdef MDB_ROBUST_SUPPORTED
- || (rc = pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST))
+ if (!rc) rc = pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST);
#endif
- || (rc = pthread_mutex_init(env->me_txns->mti_rmutex, &mattr))
- || (rc = pthread_mutex_init(env->me_txns->mti_wmutex, &mattr)))
- goto fail;
+ if (!rc) rc = pthread_mutex_init(env->me_txns->mti_rmutex, &mattr);
+ if (!rc) rc = pthread_mutex_init(env->me_txns->mti_wmutex, &mattr);
pthread_mutexattr_destroy(&mattr);
+ if (rc)
+ goto fail;
#endif /* _WIN32 || MDB_USE_POSIX_SEM */
env->me_txns->mti_magic = MDB_MAGIC;
goto fail;
}
#ifdef _WIN32
- env->me_rmutex = OpenMutex(SYNCHRONIZE, FALSE, env->me_txns->mti_rmname);
+ env->me_rmutex = OpenMutexA(SYNCHRONIZE, FALSE, env->me_txns->mti_rmname);
if (!env->me_rmutex) goto fail_errno;
- env->me_wmutex = OpenMutex(SYNCHRONIZE, FALSE, env->me_txns->mti_wmname);
+ env->me_wmutex = OpenMutexA(SYNCHRONIZE, FALSE, env->me_txns->mti_wmname);
if (!env->me_wmutex) goto fail_errno;
#elif defined(MDB_USE_POSIX_SEM)
env->me_rmutex = sem_open(env->me_txns->mti_rmname, 0);
{
int oflags, rc, len, excl = -1;
char *lpath, *dpath;
+#ifdef _WIN32
+ wchar_t *wpath;
+#endif
if (env->me_fd!=INVALID_HANDLE_VALUE || (flags & ~(CHANGEABLE|CHANGELESS)))
return EINVAL;
len = OPEN_ALWAYS;
}
mode = FILE_ATTRIBUTE_NORMAL;
- env->me_fd = CreateFile(dpath, oflags, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ rc = utf8_to_utf16(dpath, -1, &wpath, NULL);
+ if (rc)
+ goto leave;
+ env->me_fd = CreateFileW(wpath, oflags, FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL, len, mode, NULL);
+ free(wpath);
#else
if (F_ISSET(flags, MDB_RDONLY))
oflags = O_RDONLY;
*/
#ifdef _WIN32
len = OPEN_EXISTING;
- env->me_mfd = CreateFile(dpath, oflags,
+ rc = utf8_to_utf16(dpath, -1, &wpath, NULL);
+ if (rc)
+ goto leave;
+ env->me_mfd = CreateFileW(wpath, oflags,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, len,
mode | FILE_FLAG_WRITE_THROUGH, NULL);
+ free(wpath);
#else
oflags &= ~O_CREAT;
env->me_mfd = open(dpath, oflags | MDB_DSYNC, mode);
/* Doing this here since me_dbxs may not exist during mdb_env_close */
if (env->me_dbxs) {
- for (i = env->me_maxdbs; --i > MAIN_DBI; )
+ for (i = env->me_maxdbs; --i >= CORE_DBS; )
free(env->me_dbxs[i].md_name.mv_data);
free(env->me_dbxs);
}
mc->mc_pg[mc->mc_top]->mp_pgno, DDBI(mc), (void *) mc));
mc->mc_snum--;
- if (mc->mc_snum)
+ if (mc->mc_snum) {
mc->mc_top--;
+ } else {
+ mc->mc_flags &= ~C_INITIALIZED;
+ }
}
}
}
/** Find the address of the page corresponding to a given page number.
- * @param[in] txn the transaction for this access.
+ * @param[in] mc the cursor accessing the page.
* @param[in] pgno the page number for the page to retrieve.
* @param[out] ret address of a pointer where the page's address will be stored.
* @param[out] lvl dirty_list inheritance level of found page. 1=current txn, 0=mapped page.
* @return 0 on success, non-zero on failure.
*/
static int
-mdb_page_get(MDB_txn *txn, pgno_t pgno, MDB_page **ret, int *lvl)
+mdb_page_get(MDB_cursor *mc, pgno_t pgno, MDB_page **ret, int *lvl)
{
+ MDB_txn *txn = mc->mc_txn;
MDB_env *env = txn->mt_env;
MDB_page *p = NULL;
int level;
indx_t i;
DPRINTF(("branch page %"Z"u has %u keys", mp->mp_pgno, NUMKEYS(mp)));
- mdb_cassert(mc, NUMKEYS(mp) > 1);
+ /* Don't assert on branch pages in the FreeDB. We can get here
+ * while in the process of rebalancing a FreeDB branch page; we must
+ * let that proceed. ITS#8336
+ */
+ mdb_cassert(mc, !mc->mc_dbi || NUMKEYS(mp) > 1);
DPRINTF(("found index 0 to page %"Z"u", NODEPGNO(NODEPTR(mp, 0))));
if (flags & (MDB_PS_FIRST|MDB_PS_LAST)) {
mdb_cassert(mc, i < NUMKEYS(mp));
node = NODEPTR(mp, i);
- if ((rc = mdb_page_get(mc->mc_txn, NODEPGNO(node), &mp, NULL)) != 0)
+ if ((rc = mdb_page_get(mc, NODEPGNO(node), &mp, NULL)) != 0)
return rc;
mc->mc_ki[mc->mc_top] = i;
MDB_node *node = NODEPTR(mp, 0);
int rc;
- if ((rc = mdb_page_get(mc->mc_txn, NODEPGNO(node), &mp, NULL)) != 0)
+ if ((rc = mdb_page_get(mc, NODEPGNO(node), &mp, NULL)) != 0)
return rc;
mc->mc_ki[mc->mc_top] = 0;
return MDB_NOTFOUND;
if ((leaf->mn_flags & (F_DUPDATA|F_SUBDATA)) != F_SUBDATA)
return MDB_INCOMPATIBLE; /* not a named DB */
- rc = mdb_node_read(mc->mc_txn, leaf, &data);
+ rc = mdb_node_read(&mc2, leaf, &data);
if (rc)
return rc;
memcpy(&flags, ((char *) data.mv_data + offsetof(MDB_db, md_flags)),
mdb_cassert(mc, root > 1);
if (!mc->mc_pg[0] || mc->mc_pg[0]->mp_pgno != root)
- if ((rc = mdb_page_get(mc->mc_txn, root, &mc->mc_pg[0], NULL)) != 0)
+ if ((rc = mdb_page_get(mc, root, &mc->mc_pg[0], NULL)) != 0)
return rc;
mc->mc_snum = 1;
return MDB_CORRUPTED;
}
}
+ txn->mt_dirty_room++;
if (!(env->me_flags & MDB_WRITEMAP))
mdb_dpage_free(env, mp);
release:
}
/** Return the data associated with a given node.
- * @param[in] txn The transaction for this operation.
+ * @param[in] mc The cursor for this operation.
* @param[in] leaf The node being read.
* @param[out] data Updated to point to the node's data.
* @return 0 on success, non-zero on failure.
*/
static int
-mdb_node_read(MDB_txn *txn, MDB_node *leaf, MDB_val *data)
+mdb_node_read(MDB_cursor *mc, MDB_node *leaf, MDB_val *data)
{
MDB_page *omp; /* overflow page */
pgno_t pgno;
*/
data->mv_size = NODEDSZ(leaf);
memcpy(&pgno, NODEDATA(leaf), sizeof(pgno));
- if ((rc = mdb_page_get(txn, pgno, &omp, NULL)) != 0) {
+ if ((rc = mdb_page_get(mc, pgno, &omp, NULL)) != 0) {
DPRINTF(("read overflow page %"Z"u failed", pgno));
return rc;
}
mdb_cassert(mc, IS_BRANCH(mc->mc_pg[mc->mc_top]));
indx = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
- if ((rc = mdb_page_get(mc->mc_txn, NODEPGNO(indx), &mp, NULL)) != 0) {
+ if ((rc = mdb_page_get(mc, NODEPGNO(indx), &mp, NULL)) != 0) {
/* mc will be inconsistent if caller does mc_snum++ as above */
mc->mc_flags &= ~(C_INITIALIZED|C_EOF);
return rc;
MDB_node *leaf;
int rc;
- if (mc->mc_flags & C_EOF) {
+ if ((mc->mc_flags & C_EOF) ||
+ ((mc->mc_flags & C_DEL) && op == MDB_NEXT_DUP)) {
return MDB_NOTFOUND;
}
-
- mdb_cassert(mc, mc->mc_flags & C_INITIALIZED);
+ if (!(mc->mc_flags & C_INITIALIZED))
+ return mdb_cursor_first(mc, key, data);
mp = mc->mc_pg[mc->mc_top];
DPRINTF(("cursor_next: top page is %"Z"u in cursor %p",
mdb_dbg_pgno(mp), (void *) mc));
- if (mc->mc_flags & C_DEL)
+ if (mc->mc_flags & C_DEL) {
+ mc->mc_flags ^= C_DEL;
goto skip;
+ }
if (mc->mc_ki[mc->mc_top] + 1u >= NUMKEYS(mp)) {
DPUTS("=====> move to next sibling page");
mdb_xcursor_init1(mc, leaf);
}
if (data) {
- if ((rc = mdb_node_read(mc->mc_txn, leaf, data)) != MDB_SUCCESS)
+ if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS)
return rc;
if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
MDB_node *leaf;
int rc;
- mdb_cassert(mc, mc->mc_flags & C_INITIALIZED);
+ if (!(mc->mc_flags & C_INITIALIZED)) {
+ rc = mdb_cursor_last(mc, key, data);
+ if (rc)
+ return rc;
+ mc->mc_ki[mc->mc_top]++;
+ }
mp = mc->mc_pg[mc->mc_top];
DPRINTF(("cursor_prev: top page is %"Z"u in cursor %p",
mdb_dbg_pgno(mp), (void *) mc));
+ mc->mc_flags &= ~(C_EOF|C_DEL);
+
if (mc->mc_ki[mc->mc_top] == 0) {
DPUTS("=====> move to prev sibling page");
if ((rc = mdb_cursor_sibling(mc, 0)) != MDB_SUCCESS) {
} else
mc->mc_ki[mc->mc_top]--;
- mc->mc_flags &= ~C_EOF;
-
DPRINTF(("==> cursor points to page %"Z"u with %u keys, key index %u",
mdb_dbg_pgno(mp), NUMKEYS(mp), mc->mc_ki[mc->mc_top]));
mdb_xcursor_init1(mc, leaf);
}
if (data) {
- if ((rc = mdb_node_read(mc->mc_txn, leaf, data)) != MDB_SUCCESS)
+ if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS)
return rc;
if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
} else
return MDB_NOTFOUND;
}
+ } else {
+ mc->mc_pg[0] = 0;
}
rc = mdb_page_search(mc, key, 0);
} else if (op == MDB_GET_BOTH || op == MDB_GET_BOTH_RANGE) {
MDB_val olddata;
MDB_cmp_func *dcmp;
- if ((rc = mdb_node_read(mc->mc_txn, leaf, &olddata)) != MDB_SUCCESS)
+ if ((rc = mdb_node_read(mc, leaf, &olddata)) != MDB_SUCCESS)
return rc;
dcmp = mc->mc_dbx->md_dcmp;
#if UINT_MAX < SIZE_MAX
if (op == MDB_GET_BOTH || rc > 0)
return MDB_NOTFOUND;
rc = 0;
- *data = olddata;
}
+ *data = olddata;
} else {
if (mc->mc_xcursor)
mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF);
- if ((rc = mdb_node_read(mc->mc_txn, leaf, data)) != MDB_SUCCESS)
+ if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS)
return rc;
}
}
if (rc)
return rc;
} else {
- if ((rc = mdb_node_read(mc->mc_txn, leaf, data)) != MDB_SUCCESS)
+ if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS)
return rc;
}
}
if (rc)
return rc;
} else {
- if ((rc = mdb_node_read(mc->mc_txn, leaf, data)) != MDB_SUCCESS)
+ if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS)
return rc;
}
}
MDB_GET_KEY(leaf, key);
if (data) {
if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
- if (mc->mc_flags & C_DEL)
- mdb_xcursor_init1(mc, leaf);
rc = mdb_cursor_get(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_GET_CURRENT);
} else {
- rc = mdb_node_read(mc->mc_txn, leaf, data);
+ rc = mdb_node_read(mc, leaf, data);
}
}
}
rc = MDB_INCOMPATIBLE;
break;
}
- if (!(mc->mc_flags & C_INITIALIZED))
- rc = mdb_cursor_first(mc, key, data);
- else
- rc = mdb_cursor_next(mc, key, data, MDB_NEXT_DUP);
+ rc = mdb_cursor_next(mc, key, data, MDB_NEXT_DUP);
if (rc == MDB_SUCCESS) {
if (mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) {
MDB_cursor *mx;
}
}
break;
+ case MDB_PREV_MULTIPLE:
+ if (data == NULL) {
+ rc = EINVAL;
+ break;
+ }
+ if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) {
+ rc = MDB_INCOMPATIBLE;
+ break;
+ }
+ if (!(mc->mc_flags & C_INITIALIZED))
+ rc = mdb_cursor_last(mc, key, data);
+ else
+ rc = MDB_SUCCESS;
+ if (rc == MDB_SUCCESS) {
+ MDB_cursor *mx = &mc->mc_xcursor->mx_cursor;
+ if (mx->mc_flags & C_INITIALIZED) {
+ rc = mdb_cursor_sibling(mx, 0);
+ if (rc == MDB_SUCCESS)
+ goto fetchm;
+ } else {
+ rc = MDB_NOTFOUND;
+ }
+ }
+ break;
case MDB_NEXT:
case MDB_NEXT_DUP:
case MDB_NEXT_NODUP:
- if (!(mc->mc_flags & C_INITIALIZED))
- rc = mdb_cursor_first(mc, key, data);
- else
- rc = mdb_cursor_next(mc, key, data, op);
+ rc = mdb_cursor_next(mc, key, data, op);
break;
case MDB_PREV:
case MDB_PREV_DUP:
case MDB_PREV_NODUP:
- if (!(mc->mc_flags & C_INITIALIZED)) {
- rc = mdb_cursor_last(mc, key, data);
- if (rc)
- break;
- mc->mc_flags |= C_INITIALIZED;
- mc->mc_ki[mc->mc_top]++;
- }
rc = mdb_cursor_prev(mc, key, data, op);
break;
case MDB_FIRST:
MDB_node *leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) {
MDB_GET_KEY(leaf, key);
- rc = mdb_node_read(mc->mc_txn, leaf, data);
+ rc = mdb_node_read(mc, leaf, data);
break;
}
}
{
int rc = MDB_SUCCESS;
- if (mc->mc_dbi > MAIN_DBI && !(*mc->mc_dbflag & DB_DIRTY)) {
+ if (mc->mc_dbi >= CORE_DBS && !(*mc->mc_dbflag & (DB_DIRTY|DB_DUPDATA))) {
+ /* Touch DB record of named DB */
MDB_cursor mc2;
MDB_xcursor mcx;
if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi))
* update branch key if there is a parent page
*/
if (mc->mc_top && !mc->mc_ki[mc->mc_top]) {
- unsigned short top = mc->mc_top;
+ unsigned short dtop = 1;
mc->mc_top--;
/* slot 0 is always an empty key, find real slot */
- while (mc->mc_top && !mc->mc_ki[mc->mc_top])
+ while (mc->mc_top && !mc->mc_ki[mc->mc_top]) {
mc->mc_top--;
+ dtop++;
+ }
if (mc->mc_ki[mc->mc_top])
rc2 = mdb_update_key(mc, key);
else
rc2 = MDB_SUCCESS;
- mc->mc_top = top;
+ mc->mc_top += dtop;
if (rc2)
return rc2;
}
#endif
/* does data match? */
if (!dcmp(data, &olddata)) {
- if (flags & MDB_NODUPDATA)
+ if (flags & (MDB_NODUPDATA|MDB_APPENDDUP))
return MDB_KEYEXIST;
/* overwrite it */
goto current;
int level, ovpages, dpages = OVPAGES(data->mv_size, env->me_psize);
memcpy(&pg, olddata.mv_data, sizeof(pg));
- if ((rc2 = mdb_page_get(mc->mc_txn, pg, &omp, &level)) != 0)
+ if ((rc2 = mdb_page_get(mc, pg, &omp, &level)) != 0)
return rc2;
ovpages = omp->mp_pages;
return ENOMEM;
id2.mid = pg;
id2.mptr = np;
+ /* Note - this page is already counted in parent's dirty_room */
rc2 = mdb_mid2l_insert(mc->mc_txn->mt_u.dirty_list, &id2);
mdb_cassert(mc, rc2 == 0);
+ /* Currently we make the page look as with put() in the
+ * parent txn, in case the user peeks at MDB_RESERVEd
+ * or unused parts. Some users treat ovpages specially.
+ */
if (!(flags & MDB_RESERVE)) {
- /* Copy end of page, adjusting alignment so
+ /* Skip the part where LMDB will put *data.
+ * Copy end of page, adjusting alignment so
* compiler may copy words instead of bytes.
*/
off = (PAGEHDRSZ + data->mv_size) & -sizeof(size_t);
} 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 && insert_key) {
+ if (rc == 0) {
/* Adjust other cursors pointing to mp */
MDB_cursor *m2, *m3;
MDB_dbi dbi = mc->mc_dbi;
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]) {
+ if (m3 == mc || m3->mc_snum < mc->mc_snum || m3->mc_pg[i] != mp) continue;
+ if (m3->mc_ki[i] >= mc->mc_ki[i] && insert_key) {
m3->mc_ki[i]++;
}
+ if (XCURSOR_INITED(m3))
+ XCURSOR_REFRESH(m3, mp, m3->mc_ki[i]);
}
}
}
MDB_xcursor *mx = mc->mc_xcursor;
unsigned i = mc->mc_top;
MDB_page *mp = mc->mc_pg[i];
+ int nkeys = NUMKEYS(mp);
for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) {
if (m2 == mc || m2->mc_snum < mc->mc_snum) continue;
if (m2->mc_pg[i] == mp) {
if (m2->mc_ki[i] == mc->mc_ki[i]) {
mdb_xcursor_init2(m2, mx, new_dupdata);
- } else if (!insert_key) {
- MDB_node *n2 = NODEPTR(mp, m2->mc_ki[i]);
- if (!(n2->mn_flags & F_SUBDATA))
- m2->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(n2);
+ } else if (!insert_key && m2->mc_ki[i] < nkeys) {
+ XCURSOR_REFRESH(m2, mp, m2->mc_ki[i]);
}
}
}
if (flags & MDB_NODUPDATA) {
/* mdb_cursor_del0() will subtract the final entry */
mc->mc_db->md_entries -= mc->mc_xcursor->mx_db.md_entries - 1;
+ mc->mc_xcursor->mx_cursor.mc_flags &= ~C_INITIALIZED;
} else {
if (!F_ISSET(leaf->mn_flags, F_SUBDATA)) {
mc->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(leaf);
if (m2 == mc || m2->mc_snum < mc->mc_snum) continue;
if (!(m2->mc_flags & C_INITIALIZED)) continue;
if (m2->mc_pg[mc->mc_top] == mp) {
- if (m2->mc_ki[mc->mc_top] == mc->mc_ki[mc->mc_top]) {
- m2->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(leaf);
- } else {
- MDB_node *n2 = NODEPTR(mp, m2->mc_ki[mc->mc_top]);
- if (!(n2->mn_flags & F_SUBDATA))
- m2->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(n2);
+ MDB_node *n2 = leaf;
+ if (m2->mc_ki[mc->mc_top] != mc->mc_ki[mc->mc_top]) {
+ n2 = NODEPTR(mp, m2->mc_ki[mc->mc_top]);
+ if (n2->mn_flags & F_SUBDATA) continue;
}
+ m2->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(n2);
}
}
}
mc->mc_db->md_entries--;
- mc->mc_flags |= C_DEL;
return rc;
+ } else {
+ mc->mc_xcursor->mx_cursor.mc_flags &= ~C_INITIALIZED;
}
/* otherwise fall thru and delete the sub-DB */
}
pgno_t pg;
memcpy(&pg, NODEDATA(leaf), sizeof(pg));
- if ((rc = mdb_page_get(mc->mc_txn, pg, &omp, NULL)) ||
+ if ((rc = mdb_page_get(mc, pg, &omp, NULL)) ||
(rc = mdb_ovpage_free(mc, omp)))
goto fail;
}
}
DPRINTF(("Sub-db -%u root page %"Z"u", mx->mx_cursor.mc_dbi,
mx->mx_db.md_root));
- mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DIRTY; /* DB_DIRTY guides mdb_cursor_touch */
+ mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DUPDATA;
#if UINT_MAX < SIZE_MAX
if (mx->mx_dbx.md_cmp == mdb_cmp_int && mx->mx_db.md_pad == sizeof(size_t))
mx->mx_dbx.md_cmp = mdb_cmp_clong;
mx->mx_cursor.mc_top = 0;
mx->mx_cursor.mc_flags |= C_INITIALIZED;
mx->mx_cursor.mc_ki[0] = 0;
- mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DIRTY; /* DB_DIRTY guides mdb_cursor_touch */
+ mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DUPDATA;
#if UINT_MAX < SIZE_MAX
mx->mx_dbx.md_cmp = src_mx->mx_dbx.md_cmp;
#endif
if (txn->mt_flags & MDB_TXN_BLOCKED)
return MDB_BAD_TXN;
- /* Allow read access to the freelist */
- if (!dbi && !F_ISSET(txn->mt_flags, MDB_TXN_RDONLY))
+ if (dbi == FREE_DBI && !F_ISSET(txn->mt_flags, MDB_TXN_RDONLY))
return EINVAL;
if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT)
static void
mdb_cursor_copy(const MDB_cursor *csrc, MDB_cursor *cdst);
+/** Perform \b act while tracking temporary cursor \b mn */
+#define WITH_CURSOR_TRACKING(mn, act) do { \
+ MDB_cursor dummy, *tracked, **tp = &(mn).mc_txn->mt_cursors[mn.mc_dbi]; \
+ if ((mn).mc_flags & C_SUB) { \
+ dummy.mc_flags = C_INITIALIZED; \
+ dummy.mc_xcursor = (MDB_xcursor *)&(mn); \
+ tracked = &dummy; \
+ } else { \
+ tracked = &(mn); \
+ } \
+ tracked->mc_next = *tp; \
+ *tp = tracked; \
+ { act; } \
+ *tp = tracked->mc_next; \
+} while (0)
+
/** Move a node from csrc to cdst.
*/
static int
-mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst)
+mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft)
{
MDB_node *srcnode;
MDB_val key, data;
data.mv_size = NODEDSZ(srcnode);
data.mv_data = NODEDATA(srcnode);
}
+ mn.mc_xcursor = NULL;
if (IS_BRANCH(cdst->mc_pg[cdst->mc_top]) && cdst->mc_ki[cdst->mc_top] == 0) {
unsigned int snum = cdst->mc_snum;
MDB_node *s2;
/* Adjust other cursors pointing to mp */
MDB_cursor *m2, *m3;
MDB_dbi dbi = csrc->mc_dbi;
- MDB_page *mp;
-
- mp = cdst->mc_pg[csrc->mc_top];
- for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
- if (csrc->mc_flags & C_SUB)
- m3 = &m2->mc_xcursor->mx_cursor;
- else
- m3 = m2;
- if (m3 == cdst) continue;
- if (m3->mc_pg[csrc->mc_top] == mp && m3->mc_ki[csrc->mc_top] >=
- cdst->mc_ki[csrc->mc_top]) {
- m3->mc_ki[csrc->mc_top]++;
+ MDB_page *mpd, *mps;
+
+ mps = csrc->mc_pg[csrc->mc_top];
+ /* If we're adding on the left, bump others up */
+ if (fromleft) {
+ mpd = cdst->mc_pg[csrc->mc_top];
+ for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
+ if (csrc->mc_flags & C_SUB)
+ m3 = &m2->mc_xcursor->mx_cursor;
+ else
+ m3 = m2;
+ if (!(m3->mc_flags & C_INITIALIZED) || m3->mc_top < csrc->mc_top)
+ continue;
+ if (m3 != cdst &&
+ m3->mc_pg[csrc->mc_top] == mpd &&
+ m3->mc_ki[csrc->mc_top] >= cdst->mc_ki[csrc->mc_top]) {
+ m3->mc_ki[csrc->mc_top]++;
+ }
+ if (m3 !=csrc &&
+ m3->mc_pg[csrc->mc_top] == mps &&
+ m3->mc_ki[csrc->mc_top] == csrc->mc_ki[csrc->mc_top]) {
+ m3->mc_pg[csrc->mc_top] = cdst->mc_pg[cdst->mc_top];
+ m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top];
+ m3->mc_ki[csrc->mc_top-1]++;
+ }
+ if (XCURSOR_INITED(m3) && IS_LEAF(mps))
+ XCURSOR_REFRESH(m3, m3->mc_pg[csrc->mc_top], m3->mc_ki[csrc->mc_top]);
}
- }
-
- mp = csrc->mc_pg[csrc->mc_top];
- for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
- if (csrc->mc_flags & C_SUB)
- m3 = &m2->mc_xcursor->mx_cursor;
- else
- m3 = m2;
- if (m3 == csrc) continue;
- if (m3->mc_pg[csrc->mc_top] == mp && m3->mc_ki[csrc->mc_top] ==
- csrc->mc_ki[csrc->mc_top]) {
- m3->mc_pg[csrc->mc_top] = cdst->mc_pg[cdst->mc_top];
- m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top];
+ } else
+ /* Adding on the right, bump others down */
+ {
+ for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
+ if (csrc->mc_flags & C_SUB)
+ m3 = &m2->mc_xcursor->mx_cursor;
+ else
+ m3 = m2;
+ if (m3 == csrc) continue;
+ if (!(m3->mc_flags & C_INITIALIZED) || m3->mc_top < csrc->mc_top)
+ continue;
+ if (m3->mc_pg[csrc->mc_top] == mps) {
+ if (!m3->mc_ki[csrc->mc_top]) {
+ m3->mc_pg[csrc->mc_top] = cdst->mc_pg[cdst->mc_top];
+ m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top];
+ m3->mc_ki[csrc->mc_top-1]--;
+ } else {
+ m3->mc_ki[csrc->mc_top]--;
+ }
+ if (XCURSOR_INITED(m3) && IS_LEAF(mps))
+ XCURSOR_REFRESH(m3, m3->mc_pg[csrc->mc_top], m3->mc_ki[csrc->mc_top]);
+ }
}
}
}
mdb_cursor_copy(csrc, &mn);
mn.mc_snum--;
mn.mc_top--;
- if ((rc = mdb_update_key(&mn, &key)) != MDB_SUCCESS)
+ /* We want mdb_rebalance to find mn when doing fixups */
+ WITH_CURSOR_TRACKING(mn,
+ rc = mdb_update_key(&mn, &key));
+ if (rc)
return rc;
}
if (IS_BRANCH(csrc->mc_pg[csrc->mc_top])) {
mdb_cursor_copy(cdst, &mn);
mn.mc_snum--;
mn.mc_top--;
- if ((rc = mdb_update_key(&mn, &key)) != MDB_SUCCESS)
+ /* We want mdb_rebalance to find mn when doing fixups */
+ WITH_CURSOR_TRACKING(mn,
+ rc = mdb_update_key(&mn, &key));
+ if (rc)
return rc;
}
if (IS_BRANCH(cdst->mc_pg[cdst->mc_top])) {
if ((rc = mdb_page_touch(cdst)))
return rc;
+ /* get dst page again now that we've touched it. */
+ pdst = cdst->mc_pg[cdst->mc_top];
+
/* Move all nodes from src to dst.
*/
j = nkeys = NUMKEYS(pdst);
MDB_cursor mn;
MDB_node *s2;
mdb_cursor_copy(csrc, &mn);
+ mn.mc_xcursor = NULL;
/* must find the lowest key below src */
rc = mdb_page_search_lowest(&mn);
if (rc)
/* Adjust other cursors pointing to mp */
MDB_cursor *m2, *m3;
MDB_dbi dbi = csrc->mc_dbi;
+ unsigned int top = csrc->mc_top;
for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
if (csrc->mc_flags & C_SUB)
m3 = m2;
if (m3 == csrc) continue;
if (m3->mc_snum < csrc->mc_snum) continue;
- if (m3->mc_pg[csrc->mc_top] == psrc) {
- m3->mc_pg[csrc->mc_top] = pdst;
- m3->mc_ki[csrc->mc_top] += nkeys;
+ if (m3->mc_pg[top] == psrc) {
+ m3->mc_pg[top] = pdst;
+ m3->mc_ki[top] += nkeys;
+ m3->mc_ki[top-1] = cdst->mc_ki[top-1];
+ } else if (m3->mc_pg[top-1] == csrc->mc_pg[top-1] &&
+ m3->mc_ki[top-1] > csrc->mc_ki[top-1]) {
+ m3->mc_ki[top-1]--;
}
+ if (XCURSOR_INITED(m3) && IS_LEAF(psrc))
+ XCURSOR_REFRESH(m3, m3->mc_pg[top], m3->mc_ki[top]);
}
}
{
mdb_rebalance(MDB_cursor *mc)
{
MDB_node *node;
- int rc;
+ int rc, fromleft;
unsigned int ptop, minkeys, thresh;
MDB_cursor mn;
indx_t oldki;
m3 = &m2->mc_xcursor->mx_cursor;
else
m3 = m2;
- if (m3->mc_snum < mc->mc_snum) continue;
+ if (!(m3->mc_flags & C_INITIALIZED) || (m3->mc_snum < mc->mc_snum))
+ continue;
if (m3->mc_pg[0] == mp) {
m3->mc_snum = 0;
m3->mc_top = 0;
if (rc)
return rc;
mc->mc_db->md_root = NODEPGNO(NODEPTR(mp, 0));
- rc = mdb_page_get(mc->mc_txn,mc->mc_db->md_root,&mc->mc_pg[0],NULL);
+ rc = mdb_page_get(mc, mc->mc_db->md_root, &mc->mc_pg[0], NULL);
if (rc)
return rc;
mc->mc_db->md_depth--;
m3 = &m2->mc_xcursor->mx_cursor;
else
m3 = m2;
- if (m3 == mc || m3->mc_snum < mc->mc_snum) continue;
+ if (m3 == mc) continue;
+ if (!(m3->mc_flags & C_INITIALIZED))
+ continue;
if (m3->mc_pg[0] == mp) {
- for (i=0; i<m3->mc_snum; i++) {
+ for (i=0; i<mc->mc_db->md_depth; i++) {
m3->mc_pg[i] = m3->mc_pg[i+1];
m3->mc_ki[i] = m3->mc_ki[i+1];
}
DPUTS("reading right neighbor");
mn.mc_ki[ptop]++;
node = NODEPTR(mc->mc_pg[ptop], mn.mc_ki[ptop]);
- rc = mdb_page_get(mc->mc_txn,NODEPGNO(node),&mn.mc_pg[mn.mc_top],NULL);
+ rc = mdb_page_get(mc, NODEPGNO(node), &mn.mc_pg[mn.mc_top], NULL);
if (rc)
return rc;
mn.mc_ki[mn.mc_top] = 0;
mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]);
+ fromleft = 0;
} else {
/* There is at least one neighbor to the left.
*/
DPUTS("reading left neighbor");
mn.mc_ki[ptop]--;
node = NODEPTR(mc->mc_pg[ptop], mn.mc_ki[ptop]);
- rc = mdb_page_get(mc->mc_txn,NODEPGNO(node),&mn.mc_pg[mn.mc_top],NULL);
+ rc = mdb_page_get(mc, NODEPGNO(node), &mn.mc_pg[mn.mc_top], NULL);
if (rc)
return rc;
mn.mc_ki[mn.mc_top] = NUMKEYS(mn.mc_pg[mn.mc_top]) - 1;
mc->mc_ki[mc->mc_top] = 0;
+ fromleft = 1;
}
DPRINTF(("found neighbor page %"Z"u (%u keys, %.1f%% full)",
* (A branch page must never have less than 2 keys.)
*/
if (PAGEFILL(mc->mc_txn->mt_env, mn.mc_pg[mn.mc_top]) >= thresh && NUMKEYS(mn.mc_pg[mn.mc_top]) > minkeys) {
- rc = mdb_node_move(&mn, mc);
- if (mc->mc_ki[mc->mc_top-1]) {
+ rc = mdb_node_move(&mn, mc, fromleft);
+ if (fromleft) {
+ /* if we inserted on left, bump position up */
oldki++;
}
} else {
- if (mc->mc_ki[ptop] == 0) {
+ if (!fromleft) {
rc = mdb_page_merge(&mn, mc);
} else {
- MDB_cursor dummy;
oldki += NUMKEYS(mn.mc_pg[mn.mc_top]);
mn.mc_ki[mn.mc_top] += mc->mc_ki[mn.mc_top] + 1;
/* We want mdb_rebalance to find mn when doing fixups */
- if (mc->mc_flags & C_SUB) {
- dummy.mc_next = mc->mc_txn->mt_cursors[mc->mc_dbi];
- mc->mc_txn->mt_cursors[mc->mc_dbi] = &dummy;
- dummy.mc_xcursor = (MDB_xcursor *)&mn;
- } else {
- mn.mc_next = mc->mc_txn->mt_cursors[mc->mc_dbi];
- mc->mc_txn->mt_cursors[mc->mc_dbi] = &mn;
- }
- rc = mdb_page_merge(mc, &mn);
- if (mc->mc_flags & C_SUB)
- mc->mc_txn->mt_cursors[mc->mc_dbi] = dummy.mc_next;
- else
- mc->mc_txn->mt_cursors[mc->mc_dbi] = mn.mc_next;
+ WITH_CURSOR_TRACKING(mn,
+ rc = mdb_page_merge(mc, &mn));
mdb_cursor_copy(&mn, mc);
}
mc->mc_flags &= ~C_EOF;
if (m3 == mc || m3->mc_snum < mc->mc_snum)
continue;
if (m3->mc_pg[mc->mc_top] == mp) {
- if (m3->mc_ki[mc->mc_top] >= ki) {
+ if (m3->mc_ki[mc->mc_top] == ki) {
m3->mc_flags |= C_DEL;
- if (m3->mc_ki[mc->mc_top] > ki)
- m3->mc_ki[mc->mc_top]--;
- else if (mc->mc_db->md_flags & MDB_DUPSORT)
- m3->mc_xcursor->mx_cursor.mc_flags |= C_EOF;
+ if (mc->mc_db->md_flags & MDB_DUPSORT) {
+ /* Sub-cursor referred into dataset which is gone */
+ m3->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF);
+ }
+ continue;
+ } else if (m3->mc_ki[mc->mc_top] > ki) {
+ m3->mc_ki[mc->mc_top]--;
}
+ if (XCURSOR_INITED(m3))
+ XCURSOR_REFRESH(m3, m3->mc_pg[mc->mc_top], m3->mc_ki[mc->mc_top]);
}
}
}
continue;
if (m3->mc_pg[mc->mc_top] == mp) {
/* if m3 points past last node in page, find next sibling */
- if (m3->mc_ki[mc->mc_top] >= nkeys) {
- rc = mdb_cursor_sibling(m3, 1);
- if (rc == MDB_NOTFOUND) {
- m3->mc_flags |= C_EOF;
- rc = MDB_SUCCESS;
+ if (m3->mc_ki[mc->mc_top] >= mc->mc_ki[mc->mc_top]) {
+ if (m3->mc_ki[mc->mc_top] >= nkeys) {
+ rc = mdb_cursor_sibling(m3, 1);
+ if (rc == MDB_NOTFOUND) {
+ m3->mc_flags |= C_EOF;
+ rc = MDB_SUCCESS;
+ continue;
+ }
+ }
+ if (mc->mc_db->md_flags & MDB_DUPSORT) {
+ MDB_node *node = NODEPTR(m3->mc_pg[m3->mc_top], m3->mc_ki[m3->mc_top]);
+ /* If this node is a fake page, it needs to be reinited
+ * because its data has moved. But just reset mc_pg[0]
+ * if the xcursor is already live.
+ */
+ if ((node->mn_flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA) {
+ if (m3->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)
+ m3->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(node);
+ else
+ mdb_xcursor_init1(m3, node);
+ }
}
}
}
rp->mp_pad = mp->mp_pad;
DPRINTF(("new right sibling: page %"Z"u", rp->mp_pgno));
- if (mc->mc_snum < 2) {
+ /* Usually when splitting the root page, the cursor
+ * height is 1. But when called from mdb_update_key,
+ * the cursor height may be greater because it walks
+ * up the stack while finding the branch slot to update.
+ */
+ if (mc->mc_top < 1) {
if ((rc = mdb_page_new(mc, P_BRANCH, 1, &pp)))
goto done;
/* shift current top to make room for new parent */
- mc->mc_pg[1] = mc->mc_pg[0];
- mc->mc_ki[1] = mc->mc_ki[0];
+ for (i=mc->mc_snum; i>0; i--) {
+ mc->mc_pg[i] = mc->mc_pg[i-1];
+ mc->mc_ki[i] = mc->mc_ki[i-1];
+ }
mc->mc_pg[0] = pp;
mc->mc_ki[0] = 0;
mc->mc_db->md_root = pp->mp_pgno;
mc->mc_db->md_depth--;
goto done;
}
- mc->mc_snum = 2;
- mc->mc_top = 1;
+ mc->mc_snum++;
+ mc->mc_top++;
ptop = 0;
} else {
ptop = mc->mc_top-1;
DPRINTF(("parent branch page is %"Z"u", mc->mc_pg[ptop]->mp_pgno));
}
- mc->mc_flags |= C_SPLITTING;
mdb_cursor_copy(mc, &mn);
+ mn.mc_xcursor = NULL;
mn.mc_pg[mn.mc_top] = rp;
mn.mc_ki[ptop] = mc->mc_ki[ptop]+1;
rp->mp_lower += sizeof(indx_t);
rp->mp_upper -= ksize - sizeof(indx_t);
mc->mc_ki[mc->mc_top] = x;
- mc->mc_pg[mc->mc_top] = rp;
}
} else {
int psize, nsize, k;
/* Copy separator key to the parent.
*/
if (SIZELEFT(mn.mc_pg[ptop]) < mdb_branch_size(env, &sepkey)) {
+ int snum = mc->mc_snum;
mn.mc_snum--;
mn.mc_top--;
did_split = 1;
- rc = mdb_page_split(&mn, &sepkey, NULL, rp->mp_pgno, 0);
+ /* We want other splits to find mn when doing fixups */
+ WITH_CURSOR_TRACKING(mn,
+ rc = mdb_page_split(&mn, &sepkey, NULL, rp->mp_pgno, 0));
if (rc)
goto done;
/* root split? */
- if (mn.mc_snum == mc->mc_snum) {
- mc->mc_pg[mc->mc_snum] = mc->mc_pg[mc->mc_top];
- mc->mc_ki[mc->mc_snum] = mc->mc_ki[mc->mc_top];
- mc->mc_pg[mc->mc_top] = mc->mc_pg[ptop];
- mc->mc_ki[mc->mc_top] = mc->mc_ki[ptop];
- mc->mc_snum++;
- mc->mc_top++;
+ if (mc->mc_snum > snum) {
ptop++;
}
/* Right page might now have changed parent.
rc = mdb_node_add(&mn, mn.mc_ki[ptop], &sepkey, NULL, rp->mp_pgno, 0);
mn.mc_top++;
}
- mc->mc_flags ^= C_SPLITTING;
if (rc != MDB_SUCCESS) {
goto done;
}
/* reset back to original page */
if (newindx < split_indx) {
mc->mc_pg[mc->mc_top] = mp;
- if (nflags & MDB_RESERVE) {
- node = NODEPTR(mp, mc->mc_ki[mc->mc_top]);
- if (!(node->mn_flags & F_BIGDATA))
- newdata->mv_data = NODEDATA(node);
- }
} else {
mc->mc_pg[mc->mc_top] = rp;
mc->mc_ki[ptop]++;
}
}
}
+ if (nflags & MDB_RESERVE) {
+ node = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
+ if (!(node->mn_flags & F_BIGDATA))
+ newdata->mv_data = NODEDATA(node);
+ }
+ } else {
+ if (newindx >= split_indx) {
+ mc->mc_pg[mc->mc_top] = rp;
+ mc->mc_ki[ptop]++;
+ /* Make sure mc_ki is still valid.
+ */
+ if (mn.mc_pg[ptop] != mc->mc_pg[ptop] &&
+ mc->mc_ki[ptop] >= NUMKEYS(mc->mc_pg[ptop])) {
+ for (i=0; i<=ptop; i++) {
+ mc->mc_pg[i] = mn.mc_pg[i];
+ mc->mc_ki[i] = mn.mc_ki[i];
+ }
+ }
+ }
}
{
/* Adjust other cursors pointing to mp */
MDB_cursor *m2, *m3;
MDB_dbi dbi = mc->mc_dbi;
- int fixup = NUMKEYS(mp);
+ nkeys = NUMKEYS(mp);
for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
if (mc->mc_flags & C_SUB)
continue;
if (!(m2->mc_flags & m3->mc_flags & C_INITIALIZED))
continue;
- if (m3->mc_flags & C_SPLITTING)
- continue;
if (new_root) {
int k;
+ /* sub cursors may be on different DB */
+ if (m3->mc_pg[0] != mp)
+ continue;
/* root split */
for (k=new_root; k>=0; k--) {
m3->mc_ki[k+1] = m3->mc_ki[k];
m3->mc_pg[k+1] = m3->mc_pg[k];
}
- if (m3->mc_ki[0] >= split_indx) {
+ if (m3->mc_ki[0] >= nkeys) {
m3->mc_ki[0] = 1;
} else {
m3->mc_ki[0] = 0;
if (m3->mc_top >= mc->mc_top && m3->mc_pg[mc->mc_top] == mp) {
if (m3->mc_ki[mc->mc_top] >= newindx && !(nflags & MDB_SPLIT_REPLACE))
m3->mc_ki[mc->mc_top]++;
- if (m3->mc_ki[mc->mc_top] >= fixup) {
+ if (m3->mc_ki[mc->mc_top] >= nkeys) {
m3->mc_pg[mc->mc_top] = rp;
- m3->mc_ki[mc->mc_top] -= fixup;
- m3->mc_ki[ptop] = mn.mc_ki[ptop];
+ m3->mc_ki[mc->mc_top] -= nkeys;
+ for (i=0; i<mc->mc_top; i++) {
+ m3->mc_ki[i] = mn.mc_ki[i];
+ m3->mc_pg[i] = mn.mc_pg[i];
+ }
}
} else if (!did_split && m3->mc_top >= ptop && m3->mc_pg[ptop] == mc->mc_pg[ptop] &&
m3->mc_ki[ptop] >= mc->mc_ki[ptop]) {
m3->mc_ki[ptop]++;
}
+ if (XCURSOR_INITED(m3) && IS_LEAF(mp))
+ XCURSOR_REFRESH(m3, m3->mc_pg[mc->mc_top], m3->mc_ki[mc->mc_top]);
}
}
DPRINTF(("mp left: %d, rp left: %d", SIZELEFT(mp), SIZELEFT(rp)));
{
MDB_cursor mc;
MDB_xcursor mx;
+ int rc;
if (!key || !data || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID))
return EINVAL;
if (flags & ~(MDB_NOOVERWRITE|MDB_NODUPDATA|MDB_RESERVE|MDB_APPEND|MDB_APPENDDUP))
return EINVAL;
+ if (txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED))
+ return (txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN;
+
mdb_cursor_init(&mc, txn, dbi, &mx);
- return mdb_cursor_put(&mc, key, data, flags);
+ mc.mc_next = txn->mt_cursors[dbi];
+ txn->mt_cursors[dbi] = &mc;
+ rc = mdb_cursor_put(&mc, key, data, flags);
+ txn->mt_cursors[dbi] = mc.mc_next;
+ return rc;
}
#ifndef MDB_WBUF
static int ESECT
mdb_env_cwalk(mdb_copy *my, pgno_t *pg, int flags)
{
- MDB_cursor mc;
- MDB_txn *txn = my->mc_txn;
+ MDB_cursor mc = {0};
MDB_node *ni;
MDB_page *mo, *mp, *leaf;
char *buf, *ptr;
return MDB_SUCCESS;
mc.mc_snum = 1;
- mc.mc_top = 0;
- mc.mc_txn = txn;
+ mc.mc_txn = my->mc_txn;
- rc = mdb_page_get(my->mc_txn, *pg, &mc.mc_pg[0], NULL);
+ rc = mdb_page_get(&mc, *pg, &mc.mc_pg[0], NULL);
if (rc)
return rc;
rc = mdb_page_search_root(&mc, NULL, MDB_PS_FIRST);
}
memcpy(&pg, NODEDATA(ni), sizeof(pg));
- rc = mdb_page_get(txn, pg, &omp, NULL);
+ rc = mdb_page_get(&mc, pg, &omp, NULL);
if (rc)
goto done;
if (my->mc_wlen[toggle] >= MDB_WBUF) {
again:
ni = NODEPTR(mp, mc.mc_ki[mc.mc_top]);
pg = NODEPGNO(ni);
- rc = mdb_page_get(txn, pg, &mp, NULL);
+ rc = mdb_page_get(&mc, pg, &mp, NULL);
if (rc)
goto done;
mc.mc_top++;
my.mc_wlen[1] = 0;
my.mc_olen[0] = 0;
my.mc_olen[1] = 0;
- my.mc_next_pgno = 2;
+ my.mc_next_pgno = NUM_METAS;
my.mc_status = 0;
my.mc_new = 1;
my.mc_toggle = 0;
return rc;
mp = (MDB_page *)my.mc_wbuf[0];
- memset(mp, 0, 2*env->me_psize);
+ memset(mp, 0, NUM_METAS * env->me_psize);
mp->mp_pgno = 0;
mp->mp_flags = P_META;
mm = (MDB_meta *)METADATA(mp);
mdb_cursor_init(&mc, txn, FREE_DBI, NULL);
while ((rc = mdb_cursor_get(&mc, &key, &data, MDB_NEXT)) == 0)
freecount += *(MDB_ID *)data.mv_data;
- freecount += txn->mt_dbs[0].md_branch_pages +
- txn->mt_dbs[0].md_leaf_pages +
- txn->mt_dbs[0].md_overflow_pages;
+ freecount += txn->mt_dbs[FREE_DBI].md_branch_pages +
+ txn->mt_dbs[FREE_DBI].md_leaf_pages +
+ txn->mt_dbs[FREE_DBI].md_overflow_pages;
/* Set metapage 1 */
mm->mm_last_pg = txn->mt_next_pgno - freecount - 1;
- mm->mm_dbs[1] = txn->mt_dbs[1];
- if (mm->mm_last_pg > 1) {
- mm->mm_dbs[1].md_root = mm->mm_last_pg;
+ mm->mm_dbs[MAIN_DBI] = txn->mt_dbs[MAIN_DBI];
+ if (mm->mm_last_pg > NUM_METAS-1) {
+ mm->mm_dbs[MAIN_DBI].md_root = mm->mm_last_pg;
mm->mm_txnid = 1;
} else {
- mm->mm_dbs[1].md_root = P_INVALID;
+ mm->mm_dbs[MAIN_DBI].md_root = P_INVALID;
}
}
- my.mc_wlen[0] = env->me_psize * 2;
+ my.mc_wlen[0] = env->me_psize * NUM_METAS;
my.mc_txn = txn;
pthread_mutex_lock(&my.mc_mutex);
while(my.mc_new)
pthread_cond_wait(&my.mc_cond, &my.mc_mutex);
pthread_mutex_unlock(&my.mc_mutex);
- rc = mdb_env_cwalk(&my, &txn->mt_dbs[1].md_root, 0);
+ rc = mdb_env_cwalk(&my, &txn->mt_dbs[MAIN_DBI].md_root, 0);
if (rc == MDB_SUCCESS && my.mc_wlen[my.mc_toggle])
rc = mdb_env_cthr_toggle(&my, 1);
mdb_env_cthr_toggle(&my, -1);
MDB_txn *txn = NULL;
mdb_mutexref_t wmutex = NULL;
int rc;
- size_t wsize;
+ size_t wsize, w3;
char *ptr;
#ifdef _WIN32
DWORD len, w2;
}
}
- wsize = env->me_psize * 2;
+ wsize = env->me_psize * NUM_METAS;
ptr = env->me_map;
w2 = wsize;
while (w2 > 0) {
if (rc)
goto leave;
- w2 = txn->mt_next_pgno * env->me_psize;
+ w3 = txn->mt_next_pgno * env->me_psize;
{
size_t fsize = 0;
if ((rc = mdb_fsize(env->me_fd, &fsize)))
goto leave;
- if (w2 > fsize)
- w2 = fsize;
+ if (w3 > fsize)
+ w3 = fsize;
}
- wsize = w2 - wsize;
+ wsize = w3 - wsize;
while (wsize > 0) {
if (wsize > MAX_WRITE)
w2 = MAX_WRITE;
int rc, len;
char *lpath;
HANDLE newfd = INVALID_HANDLE_VALUE;
+#ifdef _WIN32
+ wchar_t *wpath;
+#endif
if (env->me_flags & MDB_NOSUBDIR) {
lpath = (char *)path;
* already in the OS cache.
*/
#ifdef _WIN32
- newfd = CreateFile(lpath, GENERIC_WRITE, 0, NULL, CREATE_NEW,
+ rc = utf8_to_utf16(lpath, -1, &wpath, NULL);
+ if (rc)
+ goto leave;
+ newfd = CreateFileW(wpath, GENERIC_WRITE, 0, NULL, CREATE_NEW,
FILE_FLAG_NO_BUFFERING|FILE_FLAG_WRITE_THROUGH, NULL);
+ free(wpath);
#else
newfd = open(lpath, O_WRONLY|O_CREAT|O_EXCL, 0666);
#endif
MDB_db dummy;
int rc, dbflag, exact;
unsigned int unused = 0, seq;
+ char *namedup;
size_t len;
if (flags & ~VALID_FLAGS)
/* Is the DB already open? */
len = strlen(name);
- for (i=2; i<txn->mt_numdbs; i++) {
+ for (i=CORE_DBS; i<txn->mt_numdbs; i++) {
if (!txn->mt_dbxs[i].md_name.mv_size) {
/* Remember this free slot */
if (!unused) unused = i;
MDB_node *node = NODEPTR(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]);
if ((node->mn_flags & (F_DUPDATA|F_SUBDATA)) != F_SUBDATA)
return MDB_INCOMPATIBLE;
- } else if (rc == MDB_NOTFOUND && (flags & MDB_CREATE)) {
- /* Create if requested */
+ } else if (! (rc == MDB_NOTFOUND && (flags & MDB_CREATE))) {
+ return rc;
+ }
+
+ /* Done here so we cannot fail after creating a new DB */
+ if ((namedup = strdup(name)) == NULL)
+ return ENOMEM;
+
+ if (rc) {
+ /* MDB_NOTFOUND and MDB_CREATE: Create new DB */
data.mv_size = sizeof(MDB_db);
data.mv_data = &dummy;
memset(&dummy, 0, sizeof(dummy));
dbflag |= DB_DIRTY;
}
- /* OK, got info, add to table */
- if (rc == MDB_SUCCESS) {
+ if (rc) {
+ free(namedup);
+ } else {
+ /* Got info, register DBI in this txn */
unsigned int slot = unused ? unused : txn->mt_numdbs;
- txn->mt_dbxs[slot].md_name.mv_data = strdup(name);
+ txn->mt_dbxs[slot].md_name.mv_data = namedup;
txn->mt_dbxs[slot].md_name.mv_size = len;
txn->mt_dbxs[slot].md_rel = NULL;
txn->mt_dbflags[slot] = dbflag;
void mdb_dbi_close(MDB_env *env, MDB_dbi dbi)
{
char *ptr;
- if (dbi <= MAIN_DBI || dbi >= env->me_maxdbs)
+ if (dbi < CORE_DBS || dbi >= env->me_maxdbs)
return;
ptr = env->me_dbxs[dbi].md_name.mv_data;
/* If there was no name, this was already closed */
/* DUPSORT sub-DBs have no ovpages/DBs. Omit scanning leaves.
* This also avoids any P_LEAF2 pages, which have no nodes.
+ * Also if the DB doesn't have sub-DBs and has no overflow
+ * pages, omit scanning leaves.
*/
- if (mc->mc_flags & C_SUB)
+ if ((mc->mc_flags & C_SUB) ||
+ (!subs && !mc->mc_db->md_overflow_pages))
mdb_cursor_pop(mc);
mdb_cursor_copy(mc, &mx);
MDB_page *omp;
pgno_t pg;
memcpy(&pg, NODEDATA(ni), sizeof(pg));
- rc = mdb_page_get(txn, pg, &omp, NULL);
+ rc = mdb_page_get(mc, pg, &omp, NULL);
if (rc != 0)
goto done;
mdb_cassert(mc, IS_OVERFLOW(omp));
pg, omp->mp_pages);
if (rc)
goto done;
+ mc->mc_db->md_overflow_pages -= omp->mp_pages;
+ if (!mc->mc_db->md_overflow_pages && !subs)
+ break;
} else if (subs && (ni->mn_flags & F_SUBDATA)) {
mdb_xcursor_init1(mc, ni);
rc = mdb_drop0(&mc->mc_xcursor->mx_cursor, 0);
goto done;
}
}
+ if (!subs && !mc->mc_db->md_overflow_pages)
+ goto pop;
} else {
if ((rc = mdb_midl_need(&txn->mt_free_pgs, n)) != 0)
goto done;
/* no more siblings, go back to beginning
* of previous level.
*/
+pop:
mdb_cursor_pop(mc);
mc->mc_ki[0] = 0;
for (i=1; i<mc->mc_snum; i++) {
} else if (rc == MDB_NOTFOUND) {
rc = MDB_SUCCESS;
}
+ mc->mc_flags &= ~C_INITIALIZED;
return rc;
}
goto leave;
/* Can't delete the main DB */
- if (del && dbi > MAIN_DBI) {
+ if (del && dbi >= CORE_DBS) {
rc = mdb_del0(txn, MAIN_DBI, &mc->mc_dbx->md_name, NULL, F_SUBDATA);
if (!rc) {
txn->mt_dbflags[dbi] = DB_STALE;
}
#endif /* MDB_ROBUST_SUPPORTED */
/** @} */
+
+#if defined(_WIN32)
+static int utf8_to_utf16(const char *src, int srcsize, wchar_t **dst, int *dstsize)
+{
+ int need;
+ wchar_t *result;
+ need = MultiByteToWideChar(CP_UTF8, 0, src, srcsize, NULL, 0);
+ if (need == 0xFFFD)
+ return EILSEQ;
+ if (need == 0)
+ return EINVAL;
+ result = malloc(sizeof(wchar_t) * need);
+ if (!result)
+ return ENOMEM;
+ MultiByteToWideChar(CP_UTF8, 0, src, srcsize, result, need);
+ if (dstsize)
+ *dstsize = need;
+ *dst = result;
+ return 0;
+}
+#endif /* defined(_WIN32) */