# error "Two's complement, reasonably sized integer types, please"
#endif
+#ifdef __GNUC__
+/** Put infrequently used env functions in separate section */
+# ifdef __APPLE__
+# define ESECT __attribute__ ((section("__TEXT,text_env")))
+# else
+# define ESECT __attribute__ ((section("text_env")))
+# endif
+#else
+#define ESECT
+#endif
+
/** @defgroup internal LMDB Internals
* @{
*/
* @{
*/
+ /** Features under development */
+#ifndef MDB_DEVEL
+#define MDB_DEVEL 0
+#endif
+
/** Wrapper around __func__, which is a C99 feature */
#if __STDC_VERSION__ >= 199901L
# define mdb_func_ __func__
#define pthread_key_delete(x) TlsFree(x)
#define pthread_getspecific(x) TlsGetValue(x)
#define pthread_setspecific(x,y) (TlsSetValue(x,y) ? 0 : ErrCode())
-#define pthread_mutex_unlock(x) ReleaseMutex(x)
-#define pthread_mutex_lock(x) WaitForSingleObject(x, INFINITE)
+#define pthread_mutex_unlock(x) ReleaseMutex(*x)
+#define pthread_mutex_lock(x) WaitForSingleObject(*x, INFINITE)
#define pthread_cond_signal(x) SetEvent(*x)
-#define pthread_cond_wait(cond,mutex) SignalObjectAndWait(*mutex, *cond, INFINITE, FALSE); WaitForSingleObject(*mutex, INFINITE)
+#define pthread_cond_wait(cond,mutex) do{SignalObjectAndWait(*mutex, *cond, INFINITE, FALSE); WaitForSingleObject(*mutex, INFINITE);}while(0)
#define THREAD_CREATE(thr,start,arg) thr=CreateThread(NULL,0,start,arg,0,NULL)
#define THREAD_FINISH(thr) WaitForSingleObject(thr, INFINITE)
-#define LOCK_MUTEX_R(env) pthread_mutex_lock((env)->me_rmutex)
-#define UNLOCK_MUTEX_R(env) pthread_mutex_unlock((env)->me_rmutex)
-#define LOCK_MUTEX_W(env) pthread_mutex_lock((env)->me_wmutex)
-#define UNLOCK_MUTEX_W(env) pthread_mutex_unlock((env)->me_wmutex)
+#define LOCK_MUTEX_R(env) pthread_mutex_lock(&(env)->me_rmutex)
+#define UNLOCK_MUTEX_R(env) pthread_mutex_unlock(&(env)->me_rmutex)
+#define LOCK_MUTEX_W(env) pthread_mutex_lock(&(env)->me_wmutex)
+#define UNLOCK_MUTEX_W(env) pthread_mutex_unlock(&(env)->me_wmutex)
#define getpid() GetCurrentProcessId()
#define MDB_FDATASYNC(fd) (!FlushFileBuffers(fd))
#define MDB_MSYNC(addr,len,flags) (!FlushViewOfFile(addr,len))
/** @brief The maximum size of a database page.
*
- * This is 32k, since it must fit in #MDB_page.%mp_upper.
+ * It is 32k or 64k, since value-PAGEBASE must fit in
+ * #MDB_page.%mp_upper.
*
* LMDB will use database pages < OS pages if needed.
* That causes more I/O in write transactions: The OS must
* pressure from other processes is high. So until OSs have
* actual paging support for Huge pages, they're not viable.
*/
-#define MAX_PAGESIZE 0x8000
+#define MAX_PAGESIZE (PAGEBASE ? 0x10000 : 0x8000)
/** The minimum number of keys required in a database page.
* Setting this to a larger value will place a smaller bound on the
#define MDB_MAGIC 0xBEEFC0DE
/** The version number for a database's datafile format. */
-#define MDB_DATA_VERSION 1
+#define MDB_DATA_VERSION ((MDB_DEVEL) ? 999 : 1)
/** The version number for a database's lockfile format. */
#define MDB_LOCK_VERSION 1
* 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.
+ * #MDB_DEVEL sets the default to 0.
*
* 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
+#define MDB_MAXKEYSIZE ((MDB_DEVEL) ? 0 : 511)
#endif
/** The maximum size of a key we can write to the environment. */
#define mp_next mp_p.p_next
union {
pgno_t p_pgno; /**< page number */
- void * p_next; /**< for in-memory list of freed structs */
+ struct MDB_page *p_next; /**< for in-memory list of freed pages */
} mp_p;
uint16_t mp_pad;
/** @defgroup mdb_page Page Flags
/** Address of first usable data byte in a page, after the header */
#define METADATA(p) ((void *)((char *)(p) + PAGEHDRSZ))
+ /** ITS#7713, change PAGEBASE to handle 65536 byte pages */
+#define PAGEBASE ((MDB_DEVEL) ? PAGEHDRSZ : 0)
+
/** Number of nodes on a page */
-#define NUMKEYS(p) (((p)->mp_lower - PAGEHDRSZ) >> 1)
+#define NUMKEYS(p) (((p)->mp_lower - (PAGEHDRSZ-PAGEBASE)) >> 1)
/** The amount of space remaining in the page */
#define SIZELEFT(p) (indx_t)((p)->mp_upper - (p)->mp_lower)
#define OVPAGES(size, psize) ((PAGEHDRSZ-1 + (size)) / (psize) + 1)
/** Link in #MDB_txn.%mt_loose_pages list */
-#define NEXT_LOOSE_PAGE(p) (*(MDB_page **)METADATA(p))
+#define NEXT_LOOSE_PAGE(p) (*(MDB_page **)((p) + 2))
/** Header for a single key/data pair within a page.
* Used in pages of type #P_BRANCH and #P_LEAF without #P_LEAF2.
#define LEAFSIZE(k, d) (NODESIZE + (k)->mv_size + (d)->mv_size)
/** Address of node \b i in page \b p */
-#define NODEPTR(p, i) ((MDB_node *)((char *)(p) + (p)->mp_ptrs[i]))
+#define NODEPTR(p, i) ((MDB_node *)((char *)(p) + (p)->mp_ptrs[i] + PAGEBASE))
/** Address of the key for the node */
#define NODEKEY(node) (void *)((node)->mn_data)
MDB_dbx *mt_dbxs;
/** Array of MDB_db records for each known DB */
MDB_db *mt_dbs;
+ /** Array of sequence numbers for each DB handle */
+ unsigned int *mt_dbiseqs;
/** @defgroup mt_dbflag Transaction DB Flags
* @ingroup internal
* @{
pgno_t me_maxpg; /**< me_mapsize / me_psize */
MDB_dbx *me_dbxs; /**< array of static DB info */
uint16_t *me_dbflags; /**< array of flags from MDB_db.md_flags */
+ unsigned int *me_dbiseqs; /**< array of dbi sequence numbers */
pthread_key_t me_txkey; /**< thread-key for readers */
MDB_pgstate me_pgstate; /**< state of old pages from freeDB */
# define me_pglast me_pgstate.mf_pglast
#define TXN_DBI_EXIST(txn, dbi) \
((txn) && (dbi) < (txn)->mt_numdbs && ((txn)->mt_dbflags[dbi] & DB_VALID))
+ /** Check for misused \b dbi handles */
+#define TXN_DBI_CHANGED(txn, dbi) \
+ ((txn)->mt_dbiseqs[dbi] != (txn)->mt_env->me_dbiseqs[dbi])
+
static int mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp);
static int mdb_page_new(MDB_cursor *mc, uint32_t flags, int num, MDB_page **mp);
static int mdb_page_touch(MDB_cursor *mc);
"MDB_BAD_RSLOT: Invalid reuse of reader locktable slot",
"MDB_BAD_TXN: Transaction cannot recover - it must be aborted",
"MDB_BAD_VALSIZE: Unsupported size of key/DB name/data, or wrong DUPFIXED size",
+ "MDB_BAD_DBI: The specified DBI handle was closed/changed unexpectedly",
};
char *
mdb_strerror(int err)
{
+#ifdef _WIN32
+ /** HACK: pad 4KB on stack over the buf. Return system msgs in buf.
+ * 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;
+#endif
int i;
if (!err)
return ("Successful return: 0");
return mdb_errstr[i];
}
+#ifdef _WIN32
+ /* These are the C-runtime error codes we use. The comment indicates
+ * their numeric value, and the Win32 error they would correspond to
+ * if the error actually came from a Win32 API. A major mess, we should
+ * have used LMDB-specific error codes for everything.
+ */
+ switch(err) {
+ case ENOENT: /* 2, FILE_NOT_FOUND */
+ case EIO: /* 5, ACCESS_DENIED */
+ case ENOMEM: /* 12, INVALID_ACCESS */
+ case EACCES: /* 13, INVALID_DATA */
+ case EBUSY: /* 16, CURRENT_DIRECTORY */
+ case EINVAL: /* 22, BAD_COMMAND */
+ case ENOSPC: /* 28, OUT_OF_PAPER */
+ return strerror(err);
+ default:
+ ;
+ }
+ buf[0] = 0;
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, err, 0, ptr, sizeof(buf), pad);
+ return ptr;
+#else
return strerror(err);
+#endif
}
/** assert(3) variant in cursor context */
total = EVEN(total);
}
fprintf(stderr, "Total: header %d + contents %d + unused %d\n",
- IS_LEAF2(mp) ? PAGEHDRSZ : mp->mp_lower, total, SIZELEFT(mp));
+ IS_LEAF2(mp) ? PAGEHDRSZ : PAGEBASE + mp->mp_lower, total, SIZELEFT(mp));
}
void
}
}
if (loose) {
+ DPRINTF(("loosen db %d page %"Z"u", DDBI(mc),
+ mp->mp_pgno));
NEXT_LOOSE_PAGE(mp) = mc->mc_txn->mt_loose_pgs;
mc->mc_txn->mt_loose_pgs = mp;
mp->mp_flags |= P_LOOSE;
static int
mdb_pages_xkeep(MDB_cursor *mc, unsigned pflags, int all)
{
- enum { Mask = P_SUBP|P_DIRTY|P_KEEP };
+ enum { Mask = P_SUBP|P_DIRTY|P_LOOSE|P_KEEP };
MDB_txn *txn = mc->mc_txn;
MDB_cursor *m3;
MDB_xcursor *mx;
break;
}
- /* Loose pages shouldn't be spilled */
- for (dp = txn->mt_loose_pgs; dp; dp = NEXT_LOOSE_PAGE(dp)) {
- if ((dp->mp_flags & Mask) == pflags)
- dp->mp_flags ^= P_KEEP;
- }
-
if (all) {
/* Mark dirty root pages */
for (i=0; i<txn->mt_numdbs; i++) {
for (i=dl[0].mid; i && need; i--) {
MDB_ID pn = dl[i].mid << 1;
dp = dl[i].mptr;
- if (dp->mp_flags & P_KEEP)
+ if (dp->mp_flags & (P_LOOSE|P_KEEP))
continue;
/* Can't spill twice, make sure it's not already in a parent's
* spill list.
if (num == 1 && txn->mt_loose_pgs) {
np = txn->mt_loose_pgs;
txn->mt_loose_pgs = NEXT_LOOSE_PAGE(np);
+ DPRINTF(("db %d use loose page %"Z"u", DDBI(mc),
+ np->mp_pgno));
*mp = np;
return MDB_SUCCESS;
}
* alignment so memcpy may copy words instead of bytes.
*/
if ((unused &= -Align) && !IS_LEAF2(src)) {
- upper &= -Align;
- memcpy(dst, src, (lower + (Align-1)) & -Align);
+ upper = (upper + PAGEBASE) & -Align;
+ memcpy(dst, src, (lower + PAGEBASE + (Align-1)) & -Align);
memcpy((pgno_t *)((char *)dst+upper), (pgno_t *)((char *)src+upper),
psize - upper);
} else {
txn->mt_free_pgs[0] = 0;
txn->mt_spill_pgs = NULL;
env->me_txn = txn;
+ memcpy(txn->mt_dbiseqs, env->me_dbiseqs, env->me_maxdbs * sizeof(unsigned int));
}
/* Copy the DB info and flags */
tsize = sizeof(MDB_ntxn);
}
size = tsize + env->me_maxdbs * (sizeof(MDB_db)+1);
- if (!(flags & MDB_RDONLY))
+ if (!(flags & MDB_RDONLY)) {
size += env->me_maxdbs * sizeof(MDB_cursor *);
+ /* child txns use parent's dbiseqs */
+ if (!parent)
+ size += env->me_maxdbs * sizeof(unsigned int);
+ }
if ((txn = calloc(1, size)) == NULL) {
- DPRINTF(("calloc: %s", strerror(ErrCode())));
+ DPRINTF(("calloc: %s", strerror(errno)));
return ENOMEM;
}
txn->mt_dbs = (MDB_db *) ((char *)txn + tsize);
if (flags & MDB_RDONLY) {
txn->mt_flags |= MDB_TXN_RDONLY;
txn->mt_dbflags = (unsigned char *)(txn->mt_dbs + env->me_maxdbs);
+ txn->mt_dbiseqs = env->me_dbiseqs;
} else {
txn->mt_cursors = (MDB_cursor **)(txn->mt_dbs + env->me_maxdbs);
- txn->mt_dbflags = (unsigned char *)(txn->mt_cursors + env->me_maxdbs);
+ if (parent) {
+ txn->mt_dbiseqs = parent->mt_dbiseqs;
+ txn->mt_dbflags = (unsigned char *)(txn->mt_cursors + env->me_maxdbs);
+ } else {
+ txn->mt_dbiseqs = (unsigned int *)(txn->mt_cursors + env->me_maxdbs);
+ txn->mt_dbflags = (unsigned char *)(txn->mt_dbiseqs + env->me_maxdbs);
+ }
}
txn->mt_env = env;
env->me_dbflags[i] = txn->mt_dbs[i].md_flags | MDB_VALID;
} else {
char *ptr = env->me_dbxs[i].md_name.mv_data;
- env->me_dbxs[i].md_name.mv_data = NULL;
- env->me_dbxs[i].md_name.mv_size = 0;
- env->me_dbflags[i] = 0;
- free(ptr);
+ if (ptr) {
+ env->me_dbxs[i].md_name.mv_data = NULL;
+ env->me_dbxs[i].md_name.mv_size = 0;
+ env->me_dbflags[i] = 0;
+ env->me_dbiseqs[i]++;
+ free(ptr);
+ }
}
}
}
while (++i <= pagecount) {
dp = dl[i].mptr;
/* Don't flush this page yet */
- if (dp->mp_flags & P_KEEP) {
- dp->mp_flags ^= P_KEEP;
+ if (dp->mp_flags & (P_LOOSE|P_KEEP)) {
+ dp->mp_flags &= ~P_KEEP;
dl[++j] = dl[i];
continue;
}
if (++i <= pagecount) {
dp = dl[i].mptr;
/* Don't flush this page yet */
- if (dp->mp_flags & P_KEEP) {
- dp->mp_flags ^= P_KEEP;
+ if (dp->mp_flags & (P_LOOSE|P_KEEP)) {
+ dp->mp_flags &= ~P_KEEP;
dl[i].mid = 0;
continue;
}
if (txn->mt_parent) {
MDB_txn *parent = txn->mt_parent;
+ MDB_page **lp;
MDB_ID2L dst, src;
MDB_IDL pspill;
unsigned x, y, len, ps_len;
}
}
+ /* Append our loose page list to parent's */
+ for (lp = &parent->mt_loose_pgs; *lp; lp = &NEXT_LOOSE_PAGE(lp))
+ ;
+ *lp = txn->mt_loose_pgs;
+
parent->mt_child = NULL;
mdb_midl_free(((MDB_ntxn *)txn)->mnt_pgstate.mf_pghead);
free(txn);
mdb_cursor_init(&mc, txn, MAIN_DBI, NULL);
for (i = 2; i < txn->mt_numdbs; i++) {
if (txn->mt_dbflags[i] & DB_DIRTY) {
+ if (TXN_DBI_CHANGED(txn, i)) {
+ rc = MDB_BAD_DBI;
+ goto fail;
+ }
data.mv_data = &txn->mt_dbs[i];
rc = mdb_cursor_put(&mc, &txn->mt_dbxs[i].md_name, &data, 0);
if (rc)
* @param[out] meta address of where to store the meta information
* @return 0 on success, non-zero on failure.
*/
-static int
+static int ESECT
mdb_env_read_header(MDB_env *env, MDB_meta *meta)
{
MDB_metabuf pbuf;
return 0;
}
-static void
+static void ESECT
mdb_env_init_meta0(MDB_env *env, MDB_meta *meta)
{
meta->mm_magic = MDB_MAGIC;
* @param[out] meta address of where to store the meta information
* @return 0 on success, non-zero on failure.
*/
-static int
+static int ESECT
mdb_env_init_meta(MDB_env *env, MDB_meta *meta)
{
MDB_page *p, *q;
{
MDB_env *env;
MDB_meta meta, metab, *mp;
+ size_t mapsize;
off_t off;
int rc, len, toggle;
char *ptr;
env = txn->mt_env;
mp = env->me_metas[toggle];
+ mapsize = env->me_metas[toggle ^ 1]->mm_mapsize;
+ /* Persist any increases of mapsize config */
+ if (mapsize < env->me_mapsize)
+ mapsize = env->me_mapsize;
if (env->me_flags & MDB_WRITEMAP) {
- /* Persist any increases of mapsize config */
- if (env->me_mapsize > mp->mm_mapsize)
- mp->mm_mapsize = env->me_mapsize;
+ mp->mm_mapsize = mapsize;
mp->mm_dbs[0] = txn->mt_dbs[0];
mp->mm_dbs[1] = txn->mt_dbs[1];
mp->mm_last_pg = txn->mt_next_pgno - 1;
metab.mm_txnid = env->me_metas[toggle]->mm_txnid;
metab.mm_last_pg = env->me_metas[toggle]->mm_last_pg;
- ptr = (char *)&meta;
- if (env->me_mapsize > mp->mm_mapsize) {
- /* Persist any increases of mapsize config */
- meta.mm_mapsize = env->me_mapsize;
- off = offsetof(MDB_meta, mm_mapsize);
- } else {
- off = offsetof(MDB_meta, mm_dbs[0].md_depth);
- }
- len = sizeof(MDB_meta) - off;
-
- ptr += off;
+ meta.mm_mapsize = mapsize;
meta.mm_dbs[0] = txn->mt_dbs[0];
meta.mm_dbs[1] = txn->mt_dbs[1];
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;
return (env->me_metas[0]->mm_txnid < env->me_metas[1]->mm_txnid);
}
-int
+int ESECT
mdb_env_create(MDB_env **env)
{
MDB_env *e;
return MDB_SUCCESS;
}
-static int
-mdb_env_map(MDB_env *env, void *addr, int newsize)
+static int ESECT
+mdb_env_map(MDB_env *env, void *addr)
{
MDB_page *p;
unsigned int flags = env->me_flags;
size_t msize;
if (flags & MDB_RDONLY) {
+ /* Don't set explicit map size, use whatever exists */
msize = 0;
sizelo = 0;
sizehi = 0;
msize = env->me_mapsize;
sizelo = msize & 0xffffffff;
sizehi = msize >> 16 >> 16; /* only needed on Win64 */
- }
- /* Windows won't create mappings for zero length files.
- * Just allocate the maxsize right now.
- */
- if (newsize) {
+ /* Windows won't create mappings for zero length files.
+ * and won't map more than the file size.
+ * Just set the maxsize right now.
+ */
if (SetFilePointer(env->me_fd, sizelo, &sizehi, 0) != (DWORD)sizelo
|| !SetEndOfFile(env->me_fd)
|| SetFilePointer(env->me_fd, 0, NULL, 0) != 0)
return ErrCode();
}
+
mh = CreateFileMapping(env->me_fd, NULL, flags & MDB_WRITEMAP ?
PAGE_READWRITE : PAGE_READONLY,
sizehi, sizelo, NULL);
return MDB_SUCCESS;
}
-int
+int ESECT
mdb_env_set_mapsize(MDB_env *env, size_t size)
{
/* If env is already open, caller is responsible for making
munmap(env->me_map, env->me_mapsize);
env->me_mapsize = size;
old = (env->me_flags & MDB_FIXEDMAP) ? env->me_map : NULL;
- rc = mdb_env_map(env, old, 1);
+ rc = mdb_env_map(env, old);
if (rc)
return rc;
}
return MDB_SUCCESS;
}
-int
+int ESECT
mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs)
{
if (env->me_map)
return MDB_SUCCESS;
}
-int
+int ESECT
mdb_env_set_maxreaders(MDB_env *env, unsigned int readers)
{
if (env->me_map || readers < 1)
return MDB_SUCCESS;
}
-int
+int ESECT
mdb_env_get_maxreaders(MDB_env *env, unsigned int *readers)
{
if (!env || !readers)
/** Further setup required for opening an LMDB environment
*/
-static int
+static int ESECT
mdb_env_open2(MDB_env *env)
{
unsigned int flags = env->me_flags;
env->me_mapsize = minsize;
}
- rc = mdb_env_map(env, meta.mm_address, newenv || env->me_mapsize != meta.mm_mapsize);
+ rc = mdb_env_map(env, (flags & MDB_FIXEDMAP) ? meta.mm_address : NULL);
if (rc)
return rc;
#endif
/** Downgrade the exclusive lock on the region back to shared */
-static int
+static int ESECT
mdb_env_share_locks(MDB_env *env, int *excl)
{
int rc = 0, toggle = mdb_env_pick_meta(env);
/** Try to get exlusive lock, otherwise shared.
* Maintain *excl = -1: no/unknown lock, 0: shared, 1: exclusive.
*/
-static int
+static int ESECT
mdb_env_excl_lock(MDB_env *env, int *excl)
{
int rc = 0;
* @param[in,out] excl In -1, out lock type: -1 none, 0 shared, 1 exclusive
* @return 0 on success, non-zero on failure.
*/
-static int
+static int ESECT
mdb_env_setup_locks(MDB_env *env, char *lpath, int mode, int *excl)
{
#ifdef _WIN32
# error "Persistent DB flags & env flags overlap, but both go in mm_flags"
#endif
-int
+int ESECT
mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode)
{
int oflags, rc, len, excl = -1;
env->me_path = strdup(path);
env->me_dbxs = calloc(env->me_maxdbs, sizeof(MDB_dbx));
env->me_dbflags = calloc(env->me_maxdbs, sizeof(uint16_t));
- if (!(env->me_dbxs && env->me_path && env->me_dbflags)) {
+ env->me_dbiseqs = calloc(env->me_maxdbs, sizeof(unsigned int));
+ if (!(env->me_dbxs && env->me_path && env->me_dbflags && env->me_dbiseqs)) {
rc = ENOMEM;
goto leave;
}
}
/** Destroy resources from mdb_env_open(), clear our readers & DBIs */
-static void
+static void ESECT
mdb_env_close0(MDB_env *env, int excl)
{
int i;
free(env->me_dbxs[i].md_name.mv_data);
free(env->me_pbuf);
+ free(env->me_dbiseqs);
free(env->me_dbflags);
free(env->me_dbxs);
free(env->me_path);
}
-void
+void ESECT
mdb_env_close(MDB_env *env)
{
MDB_page *dp;
/* Make sure we're using an up-to-date root */
if (*mc->mc_dbflag & DB_STALE) {
MDB_cursor mc2;
+ if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi))
+ return MDB_BAD_DBI;
mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, NULL);
rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, 0);
if (rc)
if (mc->mc_dbi > MAIN_DBI && !(*mc->mc_dbflag & DB_DIRTY)) {
MDB_cursor mc2;
MDB_xcursor mcx;
+ if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi))
+ return MDB_BAD_DBI;
mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, &mcx);
rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, MDB_PS_MODIFY);
if (rc)
if ((mc->mc_db->md_flags & MDB_DUPSORT) &&
LEAFSIZE(key, data) > env->me_nodemax)
{
- /* Too big for a node, insert in sub-DB */
+ /* Too big for a node, insert in sub-DB. Set up an empty
+ * "old sub-page" for prep_subDB to expand to a full page.
+ */
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;
+ fp->mp_lower = fp->mp_upper = (PAGEHDRSZ-PAGEBASE);
+ olddata.mv_size = PAGEHDRSZ;
goto prep_subDB;
}
} else {
/* Make sub-page header for the dup items, with dummy body */
fp->mp_flags = P_LEAF|P_DIRTY|P_SUBP;
- fp->mp_lower = PAGEHDRSZ;
+ fp->mp_lower = (PAGEHDRSZ-PAGEBASE);
xdata.mv_size = PAGEHDRSZ + dkey.mv_size + data->mv_size;
if (mc->mc_db->md_flags & MDB_DUPFIXED) {
fp->mp_flags |= P_LEAF2;
xdata.mv_size += 2 * (sizeof(indx_t) + NODESIZE) +
(dkey.mv_size & 1) + (data->mv_size & 1);
}
- fp->mp_upper = xdata.mv_size;
- olddata.mv_size = fp->mp_upper; /* pretend olddata is fp */
+ fp->mp_upper = xdata.mv_size - PAGEBASE;
+ olddata.mv_size = xdata.mv_size; /* pretend olddata is fp */
} else if (leaf->mn_flags & F_SUBDATA) {
/* Data is on sub-DB, just store it */
flags |= F_DUPDATA|F_SUBDATA;
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,
- olddata.mv_size - fp->mp_upper);
+ memcpy((char *)mp + mp->mp_upper + PAGEBASE, (char *)fp + fp->mp_upper + PAGEBASE,
+ olddata.mv_size - fp->mp_upper - PAGEBASE);
for (i=0; i<NUMKEYS(fp); i++)
mp->mp_ptrs[i] = fp->mp_ptrs[i] + offset;
}
DPRINTF(("allocated new mpage %"Z"u, page size %u",
np->mp_pgno, mc->mc_txn->mt_env->me_psize));
np->mp_flags = flags | P_DIRTY;
- np->mp_lower = PAGEHDRSZ;
- np->mp_upper = mc->mc_txn->mt_env->me_psize;
+ np->mp_lower = (PAGEHDRSZ-PAGEBASE);
+ np->mp_upper = mc->mc_txn->mt_env->me_psize - PAGEBASE;
if (IS_BRANCH(np))
mc->mc_db->md_branch_pages++;
}
}
- base = (char *)mp + mp->mp_upper;
+ base = (char *)mp + mp->mp_upper + PAGEBASE;
memmove(base + sz, base, ptr - mp->mp_upper);
mp->mp_lower -= sizeof(indx_t);
mp->mp_ptrs[i] += delta;
}
- base = (char *)mp + mp->mp_upper;
+ base = (char *)mp + mp->mp_upper + PAGEBASE;
memmove(base + delta, base, ptr - mp->mp_upper + NODESIZE + NODEKSZ(node));
mp->mp_upper += delta;
}
if (mc->mc_txn->mt_flags & MDB_TXN_ERROR)
return MDB_BAD_TXN;
+ if (!(mc->mc_flags & C_INITIALIZED))
+ return EINVAL;
+
+ if (!mc->mc_snum || (mc->mc_flags & C_EOF))
+ return MDB_NOTFOUND;
+
leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) {
*countp = 1;
mp->mp_ptrs[i] -= delta;
}
- base = (char *)mp + mp->mp_upper;
+ base = (char *)mp + mp->mp_upper + PAGEBASE;
len = ptr - mp->mp_upper + NODESIZE;
memmove(base - delta, base, len);
mp->mp_upper -= delta;
/* if mc points past last node in page, find next sibling */
if (mc->mc_ki[mc->mc_top] >= nkeys) {
rc = mdb_cursor_sibling(mc, 1);
- if (rc == MDB_NOTFOUND)
+ if (rc == MDB_NOTFOUND) {
+ mc->mc_flags |= C_EOF;
rc = MDB_SUCCESS;
+ }
}
/* Adjust other cursors pointing to mp */
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 (m3->mc_ki[mc->mc_top] >= nkeys) {
rc = mdb_cursor_sibling(m3, 1);
- if (rc == MDB_NOTFOUND)
+ if (rc == MDB_NOTFOUND) {
+ m3->mc_flags |= C_EOF;
rc = MDB_SUCCESS;
+ }
}
}
}
}
copy->mp_pgno = mp->mp_pgno;
copy->mp_flags = mp->mp_flags;
- copy->mp_lower = PAGEHDRSZ;
- copy->mp_upper = env->me_psize;
+ copy->mp_lower = (PAGEHDRSZ-PAGEBASE);
+ copy->mp_upper = env->me_psize - PAGEBASE;
/* prepare to insert */
for (i=0, j=0; i<nkeys; i++) {
psize += nsize;
node = NULL;
} else {
- node = (MDB_node *)((char *)mp + copy->mp_ptrs[i]);
+ node = (MDB_node *)((char *)mp + copy->mp_ptrs[i] + PAGEBASE);
psize += NODESIZE + NODEKSZ(node) + sizeof(indx_t);
if (IS_LEAF(mp)) {
if (F_ISSET(node->mn_flags, F_BIGDATA))
sepkey.mv_size = newkey->mv_size;
sepkey.mv_data = newkey->mv_data;
} else {
- node = (MDB_node *)((char *)mp + copy->mp_ptrs[split_indx]);
+ node = (MDB_node *)((char *)mp + copy->mp_ptrs[split_indx] + PAGEBASE);
sepkey.mv_size = node->mn_ksize;
sepkey.mv_data = NODEKEY(node);
}
/* Update index for the new key. */
mc->mc_ki[mc->mc_top] = j;
} else {
- node = (MDB_node *)((char *)mp + copy->mp_ptrs[i]);
+ node = (MDB_node *)((char *)mp + copy->mp_ptrs[i] + PAGEBASE);
rkey.mv_data = NODEKEY(node);
rkey.mv_size = node->mn_ksize;
if (IS_LEAF(mp)) {
mp->mp_lower = copy->mp_lower;
mp->mp_upper = copy->mp_upper;
memcpy(NODEPTR(mp, nkeys-1), NODEPTR(copy, nkeys-1),
- env->me_psize - copy->mp_upper);
+ env->me_psize - copy->mp_upper - PAGEBASE);
/* reset back to original page */
if (newindx < split_indx) {
} mdb_copy;
/** Dedicated writer thread for compacting copy. */
-static THREAD_RET
+static THREAD_RET ESECT
mdb_env_copythr(void *arg)
{
mdb_copy *my = arg;
}
/** Tell the writer thread there's a buffer ready to write */
-static int
+static int ESECT
mdb_env_cthr_toggle(mdb_copy *my, int st)
{
int toggle = my->mc_toggle ^ 1;
}
/** Depth-first tree traversal for compacting copy. */
-static int
+static int ESECT
mdb_env_cwalk(mdb_copy *my, pgno_t *pg, int flags)
{
MDB_cursor mc;
}
/** Copy environment with compaction. */
-static int
+static int ESECT
mdb_env_copyfd1(MDB_env *env, HANDLE fd)
{
MDB_meta *mm;
#ifdef _WIN32
my.mc_mutex = CreateMutex(NULL, FALSE, NULL);
my.mc_cond = CreateEvent(NULL, FALSE, FALSE, NULL);
- my.mc_wbuf[0] = _aligned_malloc(MDB_WBUF*2, env->me_psize);
+ my.mc_wbuf[0] = _aligned_malloc(MDB_WBUF*2, env->me_os_psize);
if (my.mc_wbuf[0] == NULL)
return errno;
#else
pthread_mutex_init(&my.mc_mutex, NULL);
pthread_cond_init(&my.mc_cond, NULL);
- rc = posix_memalign((void **)&my.mc_wbuf[0], env->me_psize, MDB_WBUF*2);
+ rc = posix_memalign((void **)&my.mc_wbuf[0], env->me_os_psize, MDB_WBUF*2);
if (rc)
return rc;
#endif
+ memset(my.mc_wbuf[0], 0, MDB_WBUF*2);
my.mc_wbuf[1] = my.mc_wbuf[0] + MDB_WBUF;
my.mc_wlen[0] = 0;
my.mc_wlen[1] = 0;
my.mc_toggle = 0;
my.mc_env = env;
my.mc_fd = fd;
+ THREAD_CREATE(thr, mdb_env_copythr, &my);
- /* Do the lock/unlock of the reader mutex before starting the
- * write txn. Otherwise other read txns could block writers.
- */
rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
if (rc)
return rc;
- if (env->me_txns) {
- /* We must start the actual read txn after blocking writers */
- mdb_txn_reset0(txn, "reset-stage1");
-
- /* Temporarily block writers until we snapshot the meta pages */
- LOCK_MUTEX_W(env);
-
- rc = mdb_txn_renew0(txn);
- if (rc) {
- UNLOCK_MUTEX_W(env);
- goto leave;
- }
- }
-
- THREAD_CREATE(thr, mdb_env_copythr, &my);
mp = (MDB_page *)my.mc_wbuf[0];
memset(mp, 0, 2*env->me_psize);
mp->mp_pgno = 0;
pthread_cond_wait(&my.mc_cond, &my.mc_mutex);
pthread_mutex_unlock(&my.mc_mutex);
THREAD_FINISH(thr);
-leave:
+
mdb_txn_abort(txn);
#ifdef _WIN32
CloseHandle(my.mc_cond);
}
/** Copy environment as-is. */
-static int
+static int ESECT
mdb_env_copyfd0(MDB_env *env, HANDLE fd)
{
MDB_txn *txn = NULL;
return rc;
}
-int
+int ESECT
mdb_env_copyfd2(MDB_env *env, HANDLE fd, unsigned int flags)
{
if (flags & MDB_CP_COMPACT)
return mdb_env_copyfd0(env, fd);
}
-int
+int ESECT
mdb_env_copyfd(MDB_env *env, HANDLE fd)
{
return mdb_env_copyfd2(env, fd, 0);
}
-int
+int ESECT
mdb_env_copy2(MDB_env *env, const char *path, unsigned int flags)
{
int rc, len;
goto leave;
}
+ if (env->me_psize >= env->me_os_psize) {
#ifdef O_DIRECT
/* Set O_DIRECT if the file system supports it */
if ((rc = fcntl(newfd, F_GETFL)) != -1)
goto leave;
}
#endif
+ }
rc = mdb_env_copyfd2(env, newfd, flags);
return rc;
}
-int
+int ESECT
mdb_env_copy(MDB_env *env, const char *path)
{
return mdb_env_copy2(env, path, 0);
}
-int
+int ESECT
mdb_env_set_flags(MDB_env *env, unsigned int flag, int onoff)
{
if ((flag & CHANGEABLE) != flag)
return MDB_SUCCESS;
}
-int
+int ESECT
mdb_env_get_flags(MDB_env *env, unsigned int *arg)
{
if (!env || !arg)
return MDB_SUCCESS;
}
-int
+int ESECT
mdb_env_set_userctx(MDB_env *env, void *ctx)
{
if (!env)
return MDB_SUCCESS;
}
-void *
+void * ESECT
mdb_env_get_userctx(MDB_env *env)
{
return env ? env->me_userctx : NULL;
}
-int
+int ESECT
mdb_env_set_assert(MDB_env *env, MDB_assert_func *func)
{
if (!env)
return MDB_SUCCESS;
}
-int
+int ESECT
mdb_env_get_path(MDB_env *env, const char **arg)
{
if (!env || !arg)
return MDB_SUCCESS;
}
-int
+int ESECT
mdb_env_get_fd(MDB_env *env, mdb_filehandle_t *arg)
{
if (!env || !arg)
* @param[out] arg the address of an #MDB_stat structure to receive the stats.
* @return 0, this function always succeeds.
*/
-static int
+static int ESECT
mdb_stat0(MDB_env *env, MDB_db *db, MDB_stat *arg)
{
arg->ms_psize = env->me_psize;
return MDB_SUCCESS;
}
-int
+
+int ESECT
mdb_env_stat(MDB_env *env, MDB_stat *arg)
{
int toggle;
return mdb_stat0(env, &env->me_metas[toggle]->mm_dbs[MAIN_DBI], arg);
}
-int
+int ESECT
mdb_env_info(MDB_env *env, MDB_envinfo *arg)
{
int toggle;
MDB_dbi i;
MDB_cursor mc;
int rc, dbflag, exact;
- unsigned int unused = 0;
+ unsigned int unused = 0, seq;
size_t len;
if (txn->mt_dbxs[FREE_DBI].md_cmp == NULL) {
txn->mt_dbxs[slot].md_name.mv_size = len;
txn->mt_dbxs[slot].md_rel = NULL;
txn->mt_dbflags[slot] = dbflag;
+ /* txn-> and env-> are the same in read txns, use
+ * tmp variable to avoid undefined assignment
+ */
+ seq = ++txn->mt_env->me_dbiseqs[slot];
+ txn->mt_dbiseqs[slot] = seq;
+
memcpy(&txn->mt_dbs[slot], data.mv_data, sizeof(MDB_db));
*dbi = slot;
mdb_default_cmp(txn, slot);
if (dbi <= MAIN_DBI || dbi >= env->me_maxdbs)
return;
ptr = env->me_dbxs[dbi].md_name.mv_data;
- env->me_dbxs[dbi].md_name.mv_data = NULL;
- env->me_dbxs[dbi].md_name.mv_size = 0;
- env->me_dbflags[dbi] = 0;
- free(ptr);
+ /* If there was no name, this was already closed */
+ if (ptr) {
+ env->me_dbxs[dbi].md_name.mv_data = NULL;
+ env->me_dbxs[dbi].md_name.mv_size = 0;
+ env->me_dbflags[dbi] = 0;
+ env->me_dbiseqs[dbi]++;
+ free(ptr);
+ }
}
int mdb_dbi_flags(MDB_txn *txn, MDB_dbi dbi, unsigned int *flags)
if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY))
return EACCES;
+ if (dbi > MAIN_DBI && TXN_DBI_CHANGED(txn, dbi))
+ return MDB_BAD_DBI;
+
rc = mdb_cursor_open(txn, dbi, &mc);
if (rc)
return rc;
return MDB_SUCCESS;
}
-int mdb_env_get_maxkeysize(MDB_env *env)
+int ESECT
+mdb_env_get_maxkeysize(MDB_env *env)
{
return ENV_MAXKEY(env);
}
-int mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx)
+int ESECT
+mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx)
{
unsigned int i, rdrs;
MDB_reader *mr;
/** Insert pid into list if not already present.
* return -1 if already present.
*/
-static int mdb_pid_insert(MDB_PID_T *ids, MDB_PID_T pid)
+static int ESECT
+mdb_pid_insert(MDB_PID_T *ids, MDB_PID_T pid)
{
/* binary search of pid in list */
unsigned base = 0;
return 0;
}
-int mdb_reader_check(MDB_env *env, int *dead)
+int ESECT
+mdb_reader_check(MDB_env *env, int *dead)
{
unsigned int i, j, rdrs;
MDB_reader *mr;