Back to index

openldap  2.4.31
Modules | Classes | Typedefs | Enumerations | Functions
Public API
Collaboration diagram for Public API:

Modules

 Version Macros
 Environment Flags
 Database Flags
 Write Flags
 Return Codes
 
   BerkeleyDB uses -30800 to -30999, we'll go under them

Classes

struct  MDB_val
 Generic structure used for passing keys and data in and out of the database. More...
struct  MDB_stat
 Statistics for a database in the environment. More...

Typedefs

typedef struct MDB_env
 Opaque structure for a database environment.
typedef struct MDB_txn
 Opaque structure for a transaction handle.
typedef unsigned int MDB_dbi
 A handle for an individual database in the DB environment.
typedef struct MDB_cursor
 Opaque structure for navigating through a database.
typedef struct MDB_val MDB_val
 Generic structure used for passing keys and data in and out of the database.
typedef intMDB_cmp_func )(const MDB_val *a, const MDB_val *b)
 A callback function used to compare two keys in a database.
typedef voidMDB_rel_func )(MDB_val *item, void *oldptr, void *newptr, void *relctx)
 A callback function used to relocate a position-dependent data item in a fixed-address database.
typedef enum MDB_cursor_op MDB_cursor_op
 Cursor Get operations.
typedef struct MDB_stat MDB_stat
 Statistics for a database in the environment.

Enumerations

enum  MDB_cursor_op {
  MDB_FIRST, MDB_FIRST_DUP, MDB_GET_BOTH, MDB_GET_BOTH_RANGE,
  MDB_GET_MULTIPLE, MDB_LAST, MDB_LAST_DUP, MDB_NEXT,
  MDB_NEXT_DUP, MDB_NEXT_MULTIPLE, MDB_NEXT_NODUP, MDB_PREV,
  MDB_PREV_DUP, MDB_PREV_NODUP, MDB_SET, MDB_SET_RANGE
}
 Cursor Get operations. More...

Functions

char * mdb_version (int *major, int *minor, int *patch)
 Return the mdb library version information.
char * mdb_strerror (int err)
 Return a string describing a given error code.
int mdb_env_create (MDB_env **env)
 Create an MDB environment handle.
int mdb_env_open (MDB_env *env, const char *path, unsigned int flags, mode_t mode)
 Open an environment handle.
int mdb_env_stat (MDB_env *env, MDB_stat *stat)
 Return statistics about the MDB environment.
int mdb_env_sync (MDB_env *env, int force)
 Flush the data buffers to disk.
void mdb_env_close (MDB_env *env)
 Close the environment and release the memory map.
int mdb_env_set_flags (MDB_env *env, unsigned int flags, int onoff)
 Set environment flags.
int mdb_env_get_flags (MDB_env *env, unsigned int *flags)
 Get environment flags.
int mdb_env_get_path (MDB_env *env, const char **path)
 Return the path that was used in mdb_env_open().
int mdb_env_set_mapsize (MDB_env *env, size_t size)
 Set the size of the memory map to use for this environment.
int mdb_env_set_maxreaders (MDB_env *env, unsigned int readers)
 Set the maximum number of threads for the environment.
int mdb_env_get_maxreaders (MDB_env *env, unsigned int *readers)
 Get the maximum number of threads for the environment.
int mdb_env_set_maxdbs (MDB_env *env, MDB_dbi dbs)
 Set the maximum number of databases for the environment.
int mdb_txn_begin (MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn)
 Create a transaction for use with the environment.
int mdb_txn_commit (MDB_txn *txn)
 Commit all the operations of a transaction into the database.
void mdb_txn_abort (MDB_txn *txn)
 Abandon all the operations of the transaction instead of saving them.
void mdb_txn_reset (MDB_txn *txn)
 Reset a read-only transaction.
int mdb_txn_renew (MDB_txn *txn)
 Renew a read-only transaction.
int mdb_open (MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *dbi)
 Open a database in the environment.
int mdb_stat (MDB_txn *txn, MDB_dbi dbi, MDB_stat *stat)
 Retrieve statistics for a database.
void mdb_close (MDB_env *env, MDB_dbi dbi)
 Close a database handle.
int mdb_drop (MDB_txn *txn, MDB_dbi dbi, int del)
 Delete a database and/or free all its pages.
int mdb_set_compare (MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp)
 Set a custom key comparison function for a database.
int mdb_set_dupsort (MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp)
 Set a custom data comparison function for a MDB_DUPSORT database.
int mdb_set_relfunc (MDB_txn *txn, MDB_dbi dbi, MDB_rel_func *rel)
 Set a relocation function for a MDB_FIXEDMAP database.
int mdb_set_relctx (MDB_txn *txn, MDB_dbi dbi, void *ctx)
 Set a context pointer for a MDB_FIXEDMAP database's relocation function.
int mdb_get (MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data)
 Get items from a database.
int mdb_put (MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data, unsigned int flags)
 Store items into a database.
int mdb_del (MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data)
 Delete items from a database.
int mdb_cursor_open (MDB_txn *txn, MDB_dbi dbi, MDB_cursor **cursor)
 Create a cursor handle.
void mdb_cursor_close (MDB_cursor *cursor)
 Close a cursor handle.
MDB_txnmdb_cursor_txn (MDB_cursor *cursor)
 Return the cursor's transaction handle.
MDB_dbi mdb_cursor_dbi (MDB_cursor *cursor)
 Return the cursor's database handle.
int mdb_cursor_get (MDB_cursor *cursor, MDB_val *key, MDB_val *data, MDB_cursor_op op)
 Retrieve by cursor.
int mdb_cursor_put (MDB_cursor *cursor, MDB_val *key, MDB_val *data, unsigned int flags)
 Store by cursor.
int mdb_cursor_del (MDB_cursor *cursor, unsigned int flags)
 Delete current key/data pair.
int mdb_cursor_count (MDB_cursor *cursor, size_t *countp)
 Return count of duplicates for current key.
int mdb_cmp (MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b)
 Compare two data items according to a particular database.
int mdb_dcmp (MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b)
 Compare two data items according to a particular database.

Class Documentation

struct MDB_val

Generic structure used for passing keys and data in and out of the database.

Definition at line 123 of file mdb.h.

Class Members
void * mv_data address of the data item
size_t mv_size size of the data item
struct MDB_stat

Statistics for a database in the environment.

Definition at line 256 of file mdb.h.

Class Members
size_t ms_branch_pages Number of internal (non-leaf) pages.
unsigned int ms_depth Depth (height) of the B-tree.
size_t ms_entries Number of data items.
size_t ms_leaf_pages Number of leaf pages.
size_t ms_overflow_pages Number of overflow pages.
unsigned int ms_psize Size of a database page. This is currently the same for all databases.

Typedef Documentation

A callback function used to compare two keys in a database.

Definition at line 129 of file mdb.h.

typedef struct MDB_cursor

Opaque structure for navigating through a database.

Definition at line 120 of file mdb.h.

Cursor Get operations.

This is the set of all operations for retrieving data using a cursor.

typedef unsigned int MDB_dbi

A handle for an individual database in the DB environment.

Definition at line 117 of file mdb.h.

typedef struct MDB_env

Opaque structure for a database environment.

A DB environment supports multiple databases, all residing in the same shared-memory map.

Definition at line 107 of file mdb.h.

typedef void( MDB_rel_func)(MDB_val *item, void *oldptr, void *newptr, void *relctx)

A callback function used to relocate a position-dependent data item in a fixed-address database.

The newptr gives the item's desired address in the memory map, and oldptr gives its previous address. The item's actual data resides at the address in item. This callback is expected to walk through the fields of the record in item and modify any values based at the oldptr address to be relative to the newptr address.

Parameters:
[in,out]itemThe item that is to be relocated.
[in]oldptrThe previous address.
[in]newptrThe new address to relocate to.
[in]relctxAn application-provided context, set by mdb_set_relctx().
Todo:
This feature is currently unimplemented.

Definition at line 145 of file mdb.h.

typedef struct MDB_stat MDB_stat

Statistics for a database in the environment.

typedef struct MDB_txn

Opaque structure for a transaction handle.

All database operations require a transaction handle. Transactions may be read-only or read-write.

Definition at line 114 of file mdb.h.

typedef struct MDB_val MDB_val

Generic structure used for passing keys and data in and out of the database.


Enumeration Type Documentation

Cursor Get operations.

This is the set of all operations for retrieving data using a cursor.

Enumerator:
MDB_FIRST 

Position at first key/data item.

MDB_FIRST_DUP 

Position at first data item of current key.

Only for MDB_DUPSORT

MDB_GET_BOTH 

Position at key/data pair.

Only for MDB_DUPSORT

MDB_GET_BOTH_RANGE 

position at key, nearest data.

Only for MDB_DUPSORT

MDB_GET_MULTIPLE 

Return all the duplicate data items at the current cursor position.

Only for MDB_DUPFIXED

MDB_LAST 

Position at last key/data item.

MDB_LAST_DUP 

Position at last data item of current key.

Only for MDB_DUPSORT

MDB_NEXT 

Position at next data item.

MDB_NEXT_DUP 

Position at next data item of current key.

Only for MDB_DUPSORT

MDB_NEXT_MULTIPLE 

Return all duplicate data items at the next cursor position.

Only for MDB_DUPFIXED

MDB_NEXT_NODUP 

Position at first data item of next key.

Only for MDB_DUPSORT

MDB_PREV 

Position at previous data item.

MDB_PREV_DUP 

Position at previous data item of current key.

Only for MDB_DUPSORT

MDB_PREV_NODUP 

Position at last data item of previous key.

Only for MDB_DUPSORT

MDB_SET 

Position at specified key.

MDB_SET_RANGE 

Position at first key greater than or equal to specified key.

Definition at line 207 of file mdb.h.


Function Documentation

void mdb_close ( MDB_env env,
MDB_dbi  dbi 
)

Close a database handle.

This call is not mutex protected. Handles should only be closed by a single thread, and only if no other threads are going to reference the database handle any further.

Parameters:
[in]envAn environment handle returned by mdb_env_create()
[in]dbiA database handle returned by mdb_open()

Definition at line 6211 of file mdb.c.

{
       char *ptr;
       if (dbi <= MAIN_DBI || dbi >= env->me_numdbs)
              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;
       free(ptr);
}

Here is the caller graph for this function:

int mdb_cmp ( MDB_txn txn,
MDB_dbi  dbi,
const MDB_val a,
const MDB_val b 
)

Compare two data items according to a particular database.

This returns a comparison as if the two data items were keys in the specified database.

Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()
[in]dbiA database handle returned by mdb_open()
[in]aThe first item to compare
[in]bThe second item to compare
Returns:
< 0 if a < b, 0 if a == b, > 0 if a > b

Definition at line 1164 of file mdb.c.

{
       return txn->mt_dbxs[dbi].md_cmp(a, b);
}

Close a cursor handle.

The cursor handle will be freed and must not be used again after this call.

Parameters:
[in]cursorA cursor handle returned by mdb_cursor_open()

Definition at line 5032 of file mdb.c.

{
       if (mc != NULL) {
              /* remove from txn, if tracked */
              if (mc->mc_txn->mt_cursors) {
                     MDB_cursor **prev = &mc->mc_txn->mt_cursors[mc->mc_dbi];
                     while (*prev && *prev != mc) prev = &(*prev)->mc_next;
                     if (*prev == mc)
                            *prev = mc->mc_next;
              }
              if (mc->mc_flags & C_ALLOCD)
                     free(mc);
       }
}

Here is the caller graph for this function:

int mdb_cursor_count ( MDB_cursor cursor,
size_t *  countp 
)

Return count of duplicates for current key.

This call is only valid on databases that support sorted duplicate data items MDB_DUPSORT.

Parameters:
[in]cursorA cursor handle returned by mdb_cursor_open()
[out]countpAddress where the count will be stored
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EINVAL - cursor is not initialized, or an invalid parameter was specified.

Definition at line 5009 of file mdb.c.

{
       MDB_node      *leaf;

       if (mc == NULL || countp == NULL)
              return EINVAL;

       if (!(mc->mc_db->md_flags & MDB_DUPSORT))
              return EINVAL;

       leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
       if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) {
              *countp = 1;
       } else {
              if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED))
                     return EINVAL;

              *countp = mc->mc_xcursor->mx_db.md_entries;
       }
       return MDB_SUCCESS;
}

Here is the caller graph for this function:

Return the cursor's database handle.

Parameters:
[in]cursorA cursor handle returned by mdb_cursor_open()

Definition at line 5055 of file mdb.c.

{
       if (!mc) return 0;
       return mc->mc_dbi;
}
int mdb_cursor_del ( MDB_cursor cursor,
unsigned int  flags 
)

Delete current key/data pair.

This function deletes the key/data pair to which the cursor refers.

Parameters:
[in]cursorA cursor handle returned by mdb_cursor_open()
[in]flagsOptions for this operation. This parameter must be set to 0 or one of the values described here.
  • MDB_NODUPDATA - delete all of the data items for the current key. This flag may only be specified if the database was opened with MDB_DUPSORT.
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EACCES - an attempt was made to modify a read-only database.
  • EINVAL - an invalid parameter was specified.

Definition at line 4488 of file mdb.c.

{
       MDB_node      *leaf;
       int rc;

       if (F_ISSET(mc->mc_txn->mt_flags, MDB_TXN_RDONLY))
              return EACCES;

       if (!mc->mc_flags & C_INITIALIZED)
              return EINVAL;

       rc = mdb_cursor_touch(mc);
       if (rc)
              return rc;

       leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);

       if (!IS_LEAF2(mc->mc_pg[mc->mc_top]) && F_ISSET(leaf->mn_flags, F_DUPDATA)) {
              if (flags != MDB_NODUPDATA) {
                     if (!F_ISSET(leaf->mn_flags, F_SUBDATA)) {
                            mc->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(leaf);
                     }
                     rc = mdb_cursor_del(&mc->mc_xcursor->mx_cursor, 0);
                     /* If sub-DB still has entries, we're done */
                     if (mc->mc_xcursor->mx_db.md_entries) {
                            if (leaf->mn_flags & F_SUBDATA) {
                                   /* update subDB info */
                                   void *db = NODEDATA(leaf);
                                   memcpy(db, &mc->mc_xcursor->mx_db, sizeof(MDB_db));
                            } else {
                                   /* shrink fake page */
                                   mdb_node_shrink(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
                            }
                            mc->mc_db->md_entries--;
                            return rc;
                     }
                     /* otherwise fall thru and delete the sub-DB */
              }

              if (leaf->mn_flags & F_SUBDATA) {
                     /* add all the child DB's pages to the free list */
                     rc = mdb_drop0(&mc->mc_xcursor->mx_cursor, 0);
                     if (rc == MDB_SUCCESS) {
                            mc->mc_db->md_entries -=
                                   mc->mc_xcursor->mx_db.md_entries;
                     }
              }
       }

       return mdb_cursor_del0(mc, leaf);
}

Here is the call graph for this function:

Here is the caller graph for this function:

int mdb_cursor_get ( MDB_cursor cursor,
MDB_val key,
MDB_val data,
MDB_cursor_op  op 
)

Retrieve by cursor.

This function retrieves key/data pairs from the database. The address and length of the key are returned in the object to which key refers (except for the case of the MDB_SET option, in which the key object is unchanged), and the address and length of the data are returned in the object to which data refers.

Parameters:
[in]cursorA cursor handle returned by mdb_cursor_open()
[in,out]keyThe key for a retrieved item
[in,out]dataThe data of a retrieved item
[in]opA cursor operation MDB_cursor_op
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • MDB_NOTFOUND - no matching key found.
  • EINVAL - an invalid parameter was specified.

Definition at line 3999 of file mdb.c.

{
       int            rc;
       int            exact = 0;

       assert(mc);

       switch (op) {
       case MDB_GET_BOTH:
       case MDB_GET_BOTH_RANGE:
              if (data == NULL || mc->mc_xcursor == NULL) {
                     rc = EINVAL;
                     break;
              }
              /* FALLTHRU */
       case MDB_SET:
       case MDB_SET_RANGE:
              if (key == NULL || key->mv_size == 0 || key->mv_size > MAXKEYSIZE) {
                     rc = EINVAL;
              } else if (op == MDB_SET_RANGE)
                     rc = mdb_cursor_set(mc, key, data, op, NULL);
              else
                     rc = mdb_cursor_set(mc, key, data, op, &exact);
              break;
       case MDB_GET_MULTIPLE:
              if (data == NULL ||
                     !(mc->mc_db->md_flags & MDB_DUPFIXED) ||
                     !(mc->mc_flags & C_INITIALIZED)) {
                     rc = EINVAL;
                     break;
              }
              rc = MDB_SUCCESS;
              if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) ||
                     (mc->mc_xcursor->mx_cursor.mc_flags & C_EOF))
                     break;
              goto fetchm;
       case MDB_NEXT_MULTIPLE:
              if (data == NULL ||
                     !(mc->mc_db->md_flags & MDB_DUPFIXED)) {
                     rc = EINVAL;
                     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);
              if (rc == MDB_SUCCESS) {
                     if (mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) {
                            MDB_cursor *mx;
fetchm:
                            mx = &mc->mc_xcursor->mx_cursor;
                            data->mv_size = NUMKEYS(mx->mc_pg[mx->mc_top]) *
                                   mx->mc_db->md_pad;
                            data->mv_data = METADATA(mx->mc_pg[mx->mc_top]);
                            mx->mc_ki[mx->mc_top] = NUMKEYS(mx->mc_pg[mx->mc_top])-1;
                     } 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);
              break;
       case MDB_PREV:
       case MDB_PREV_DUP:
       case MDB_PREV_NODUP:
              if (!(mc->mc_flags & C_INITIALIZED) || (mc->mc_flags & C_EOF))
                     rc = mdb_cursor_last(mc, key, data);
              else
                     rc = mdb_cursor_prev(mc, key, data, op);
              break;
       case MDB_FIRST:
              rc = mdb_cursor_first(mc, key, data);
              break;
       case MDB_FIRST_DUP:
              if (data == NULL ||
                     !(mc->mc_db->md_flags & MDB_DUPSORT) ||
                     !(mc->mc_flags & C_INITIALIZED) ||
                     !(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) {
                     rc = EINVAL;
                     break;
              }
              rc = mdb_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL);
              break;
       case MDB_LAST:
              rc = mdb_cursor_last(mc, key, data);
              break;
       case MDB_LAST_DUP:
              if (data == NULL ||
                     !(mc->mc_db->md_flags & MDB_DUPSORT) ||
                     !(mc->mc_flags & C_INITIALIZED) ||
                     !(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) {
                     rc = EINVAL;
                     break;
              }
              rc = mdb_cursor_last(&mc->mc_xcursor->mx_cursor, data, NULL);
              break;
       default:
              DPRINTF("unhandled/unimplemented cursor operation %u", op);
              rc = EINVAL;
              break;
       }

       return rc;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int mdb_cursor_open ( MDB_txn txn,
MDB_dbi  dbi,
MDB_cursor **  cursor 
)

Create a cursor handle.

Cursors are associated with a specific transaction and database and may not span threads.

Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()
[in]dbiA database handle returned by mdb_open()
[out]cursorAddress where the new MDB_cursor handle will be stored
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EINVAL - an invalid parameter was specified.

Definition at line 4972 of file mdb.c.

{
       MDB_cursor    *mc;
       MDB_xcursor   *mx = NULL;
       size_t size = sizeof(MDB_cursor);

       if (txn == NULL || ret == NULL || dbi >= txn->mt_numdbs)
              return EINVAL;

       /* Allow read access to the freelist */
       if (!dbi && !F_ISSET(txn->mt_flags, MDB_TXN_RDONLY))
              return EINVAL;

       if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT)
              size += sizeof(MDB_xcursor);

       if ((mc = malloc(size)) != NULL) {
              if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT) {
                     mx = (MDB_xcursor *)(mc + 1);
              }
              mdb_cursor_init(mc, txn, dbi, mx);
              if (txn->mt_cursors) {
                     mc->mc_next = txn->mt_cursors[dbi];
                     txn->mt_cursors[dbi] = mc;
              }
              mc->mc_flags |= C_ALLOCD;
       } else {
              return ENOMEM;
       }

       *ret = mc;

       return MDB_SUCCESS;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int mdb_cursor_put ( MDB_cursor cursor,
MDB_val key,
MDB_val data,
unsigned int  flags 
)

Store by cursor.

This function stores key/data pairs into the database. If the function fails for any reason, the state of the cursor will be unchanged. If the function succeeds and an item is inserted into the database, the cursor is always positioned to refer to the newly inserted item.

Parameters:
[in]cursorA cursor handle returned by mdb_cursor_open()
[in]keyThe key operated on.
[in]dataThe data operated on.
[in]flagsOptions for this operation. This parameter must be set to 0 or one of the values described here.
  • MDB_CURRENT - overwrite the data of the key/data pair to which the cursor refers with the specified data item. The key parameter is ignored.
  • MDB_NODUPDATA - enter the new key/data pair only if it does not already appear in the database. This flag may only be specified if the database was opened with MDB_DUPSORT. The function will return MDB_KEYEXIST if the key/data pair already appears in the database.
  • MDB_NOOVERWRITE - enter the new key/data pair only if the key does not already appear in the database. The function will return

    MDB_KEYEXIST if the key already appears in the database, even if

    the database supports duplicates (MDB_DUPSORT).

Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EACCES - an attempt was made to modify a read-only database.
  • EINVAL - an invalid parameter was specified.

Definition at line 4138 of file mdb.c.

{
       MDB_node      *leaf = NULL;
       MDB_val       xdata, *rdata, dkey;
       MDB_page      *fp;
       MDB_db dummy;
       int do_sub = 0;
       unsigned int mcount = 0;
       size_t nsize;
       int rc, rc2;
       MDB_pagebuf pbuf;
       char dbuf[MAXKEYSIZE+1];
       unsigned int nflags;
       DKBUF;

       if (F_ISSET(mc->mc_txn->mt_flags, MDB_TXN_RDONLY))
              return EACCES;

       DPRINTF("==> put db %u key [%s], size %zu, data size %zu",
              mc->mc_dbi, DKEY(key), key ? key->mv_size:0, data->mv_size);

       dkey.mv_size = 0;

       if (flags == MDB_CURRENT) {
              if (!(mc->mc_flags & C_INITIALIZED))
                     return EINVAL;
              rc = MDB_SUCCESS;
       } else if (mc->mc_db->md_root == P_INVALID) {
              MDB_page *np;
              /* new database, write a root leaf page */
              DPUTS("allocating new root leaf page");
              if ((np = mdb_page_new(mc, P_LEAF, 1)) == NULL) {
                     return ENOMEM;
              }
              mc->mc_snum = 0;
              mdb_cursor_push(mc, np);
              mc->mc_db->md_root = np->mp_pgno;
              mc->mc_db->md_depth++;
              *mc->mc_dbflag = DB_DIRTY;
              if ((mc->mc_db->md_flags & (MDB_DUPSORT|MDB_DUPFIXED))
                     == MDB_DUPFIXED)
                     np->mp_flags |= P_LEAF2;
              mc->mc_flags |= C_INITIALIZED;
              rc = MDB_NOTFOUND;
              goto top;
       } else {
              int exact = 0;
              MDB_val d2;
              rc = mdb_cursor_set(mc, key, &d2, MDB_SET, &exact);
              if ((flags & MDB_NOOVERWRITE) && rc == 0) {
                     DPRINTF("duplicate key [%s]", DKEY(key));
                     *data = d2;
                     return MDB_KEYEXIST;
              }
              if (rc && rc != MDB_NOTFOUND)
                     return rc;
       }

       /* Cursor is positioned, now make sure all pages are writable */
       rc2 = mdb_cursor_touch(mc);
       if (rc2)
              return rc2;

top:
       /* The key already exists */
       if (rc == MDB_SUCCESS) {
              /* there's only a key anyway, so this is a no-op */
              if (IS_LEAF2(mc->mc_pg[mc->mc_top])) {
                     unsigned int ksize = mc->mc_db->md_pad;
                     if (key->mv_size != ksize)
                            return EINVAL;
                     if (flags == MDB_CURRENT) {
                            char *ptr = LEAF2KEY(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], ksize);
                            memcpy(ptr, key->mv_data, ksize);
                     }
                     return MDB_SUCCESS;
              }

              leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);

              /* DB has dups? */
              if (F_ISSET(mc->mc_db->md_flags, MDB_DUPSORT)) {
                     /* Was a single item before, must convert now */
more:
                     if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) {
                            /* Just overwrite the current item */
                            if (flags == MDB_CURRENT)
                                   goto current;

                            dkey.mv_size = NODEDSZ(leaf);
                            dkey.mv_data = NODEDATA(leaf);
#if UINT_MAX < SIZE_MAX
                            if (mc->mc_dbx->md_dcmp == mdb_cmp_int && dkey.mv_size == sizeof(size_t))
#ifdef MISALIGNED_OK
                                   mc->mc_dbx->md_dcmp = mdb_cmp_long;
#else
                                   mc->mc_dbx->md_dcmp = mdb_cmp_cint;
#endif
#endif
                            /* if data matches, ignore it */
                            if (!mc->mc_dbx->md_dcmp(data, &dkey))
                                   return (flags == MDB_NODUPDATA) ? MDB_KEYEXIST : MDB_SUCCESS;

                            /* create a fake page for the dup items */
                            memcpy(dbuf, dkey.mv_data, dkey.mv_size);
                            dkey.mv_data = dbuf;
                            fp = (MDB_page *)&pbuf;
                            fp->mp_pgno = mc->mc_pg[mc->mc_top]->mp_pgno;
                            fp->mp_flags = P_LEAF|P_DIRTY|P_SUBP;
                            fp->mp_lower = PAGEHDRSZ;
                            fp->mp_upper = PAGEHDRSZ + dkey.mv_size + data->mv_size;
                            if (mc->mc_db->md_flags & MDB_DUPFIXED) {
                                   fp->mp_flags |= P_LEAF2;
                                   fp->mp_pad = data->mv_size;
                            } else {
                                   fp->mp_upper += 2 * sizeof(indx_t) + 2 * NODESIZE +
                                          (dkey.mv_size & 1) + (data->mv_size & 1);
                            }
                            mdb_node_del(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], 0);
                            do_sub = 1;
                            rdata = &xdata;
                            xdata.mv_size = fp->mp_upper;
                            xdata.mv_data = fp;
                            flags |= F_DUPDATA;
                            goto new_sub;
                     }
                     if (!F_ISSET(leaf->mn_flags, F_SUBDATA)) {
                            /* See if we need to convert from fake page to subDB */
                            MDB_page *mp;
                            unsigned int offset;
                            unsigned int i;

                            fp = NODEDATA(leaf);
                            if (flags == MDB_CURRENT) {
                                   fp->mp_flags |= P_DIRTY;
                                   COPY_PGNO(fp->mp_pgno, mc->mc_pg[mc->mc_top]->mp_pgno);
                                   mc->mc_xcursor->mx_cursor.mc_pg[0] = fp;
                                   flags |= F_DUPDATA;
                                   goto put_sub;
                            }
                            if (mc->mc_db->md_flags & MDB_DUPFIXED) {
                                   offset = fp->mp_pad;
                            } else {
                                   offset = NODESIZE + sizeof(indx_t) + data->mv_size;
                            }
                            offset += offset & 1;
                            if (NODESIZE + sizeof(indx_t) + NODEKSZ(leaf) + NODEDSZ(leaf) +
                                   offset >= (mc->mc_txn->mt_env->me_psize - PAGEHDRSZ) /
                                          MDB_MINKEYS) {
                                   /* yes, convert it */
                                   dummy.md_flags = 0;
                                   if (mc->mc_db->md_flags & MDB_DUPFIXED) {
                                          dummy.md_pad = fp->mp_pad;
                                          dummy.md_flags = MDB_DUPFIXED;
                                          if (mc->mc_db->md_flags & MDB_INTEGERDUP)
                                                 dummy.md_flags |= MDB_INTEGERKEY;
                                   }
                                   dummy.md_depth = 1;
                                   dummy.md_branch_pages = 0;
                                   dummy.md_leaf_pages = 1;
                                   dummy.md_overflow_pages = 0;
                                   dummy.md_entries = NUMKEYS(fp);
                                   rdata = &xdata;
                                   xdata.mv_size = sizeof(MDB_db);
                                   xdata.mv_data = &dummy;
                                   mp = mdb_page_alloc(mc, 1);
                                   if (!mp)
                                          return ENOMEM;
                                   offset = mc->mc_txn->mt_env->me_psize - NODEDSZ(leaf);
                                   flags |= F_DUPDATA|F_SUBDATA;
                                   dummy.md_root = mp->mp_pgno;
                            } else {
                                   /* no, just grow it */
                                   rdata = &xdata;
                                   xdata.mv_size = NODEDSZ(leaf) + offset;
                                   xdata.mv_data = &pbuf;
                                   mp = (MDB_page *)&pbuf;
                                   mp->mp_pgno = mc->mc_pg[mc->mc_top]->mp_pgno;
                                   flags |= F_DUPDATA;
                            }
                            mp->mp_flags = fp->mp_flags | P_DIRTY;
                            mp->mp_pad   = fp->mp_pad;
                            mp->mp_lower = fp->mp_lower;
                            mp->mp_upper = fp->mp_upper + offset;
                            if (IS_LEAF2(fp)) {
                                   memcpy(METADATA(mp), METADATA(fp), NUMKEYS(fp) * fp->mp_pad);
                            } else {
                                   nsize = NODEDSZ(leaf) - fp->mp_upper;
                                   memcpy((char *)mp + mp->mp_upper, (char *)fp + fp->mp_upper, nsize);
                                   for (i=0; i<NUMKEYS(fp); i++)
                                          mp->mp_ptrs[i] = fp->mp_ptrs[i] + offset;
                            }
                            mdb_node_del(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], 0);
                            do_sub = 1;
                            goto new_sub;
                     }
                     /* data is on sub-DB, just store it */
                     flags |= F_DUPDATA|F_SUBDATA;
                     goto put_sub;
              }
current:
              /* overflow page overwrites need special handling */
              if (F_ISSET(leaf->mn_flags, F_BIGDATA)) {
                     MDB_page *omp;
                     pgno_t pg;
                     int ovpages, dpages;

                     ovpages = OVPAGES(NODEDSZ(leaf), mc->mc_txn->mt_env->me_psize);
                     dpages = OVPAGES(data->mv_size, mc->mc_txn->mt_env->me_psize);
                     memcpy(&pg, NODEDATA(leaf), sizeof(pg));
                     mdb_page_get(mc->mc_txn, pg, &omp);
                     /* Is the ov page writable and large enough? */
                     if ((omp->mp_flags & P_DIRTY) && ovpages >= dpages) {
                            /* yes, overwrite it. Note in this case we don't
                             * bother to try shrinking the node if the new data
                             * is smaller than the overflow threshold.
                             */
                            if (F_ISSET(flags, MDB_RESERVE))
                                   data->mv_data = METADATA(omp);
                            else
                                   memcpy(METADATA(omp), data->mv_data, data->mv_size);
                            goto done;
                     } else {
                            /* no, free ovpages */
                            int i;
                            mc->mc_db->md_overflow_pages -= ovpages;
                            for (i=0; i<ovpages; i++) {
                                   DPRINTF("freed ov page %zu", pg);
                                   mdb_midl_append(&mc->mc_txn->mt_free_pgs, pg);
                                   pg++;
                            }
                     }
              } else if (NODEDSZ(leaf) == data->mv_size) {
                     /* same size, just replace it. Note that we could
                      * also reuse this node if the new data is smaller,
                      * but instead we opt to shrink the node in that case.
                      */
                     if (F_ISSET(flags, MDB_RESERVE))
                            data->mv_data = NODEDATA(leaf);
                     else
                            memcpy(NODEDATA(leaf), data->mv_data, data->mv_size);
                     goto done;
              }
              mdb_node_del(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], 0);
              mc->mc_db->md_entries--;
       } else {
              DPRINTF("inserting key at index %i", mc->mc_ki[mc->mc_top]);
       }

       rdata = data;

new_sub:
       nflags = flags & NODE_ADD_FLAGS;
       nsize = IS_LEAF2(mc->mc_pg[mc->mc_top]) ? key->mv_size : mdb_leaf_size(mc->mc_txn->mt_env, key, rdata);
       if (SIZELEFT(mc->mc_pg[mc->mc_top]) < nsize) {
              if (( flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA )
                     nflags &= ~MDB_APPEND;
              rc = mdb_page_split(mc, key, rdata, P_INVALID, nflags);
       } else {
              /* There is room already in this leaf page. */
              rc = mdb_node_add(mc, mc->mc_ki[mc->mc_top], key, rdata, 0, nflags);
              if (rc == 0 && !do_sub) {
                     /* Adjust other cursors pointing to mp */
                     MDB_cursor *m2, *m3;
                     MDB_dbi dbi = mc->mc_dbi;
                     unsigned i = mc->mc_top;
                     MDB_page *mp = mc->mc_pg[i];

                     if (mc->mc_flags & C_SUB)
                            dbi--;

                     for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
                            if (mc->mc_flags & C_SUB)
                                   m3 = &m2->mc_xcursor->mx_cursor;
                            else
                                   m3 = m2;
                            if (m3 == mc || m3->mc_snum < mc->mc_snum) continue;
                            if (m3->mc_pg[i] == mp && m3->mc_ki[i] >= mc->mc_ki[i]) {
                                   m3->mc_ki[i]++;
                            }
                     }
              }
       }

       if (rc != MDB_SUCCESS)
              mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
       else {
              /* Now store the actual data in the child DB. Note that we're
               * storing the user data in the keys field, so there are strict
               * size limits on dupdata. The actual data fields of the child
               * DB are all zero size.
               */
              if (do_sub) {
                     int xflags;
put_sub:
                     xdata.mv_size = 0;
                     xdata.mv_data = "";
                     leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
                     if (flags & MDB_CURRENT) {
                            xflags = MDB_CURRENT;
                     } else {
                            mdb_xcursor_init1(mc, leaf);
                            xflags = (flags & MDB_NODUPDATA) ? MDB_NOOVERWRITE : 0;
                     }
                     /* converted, write the original data first */
                     if (dkey.mv_size) {
                            rc = mdb_cursor_put(&mc->mc_xcursor->mx_cursor, &dkey, &xdata, xflags);
                            if (rc)
                                   return rc;
                            {
                                   /* Adjust other cursors pointing to mp */
                                   MDB_cursor *m2;
                                   unsigned i = mc->mc_top;
                                   MDB_page *mp = mc->mc_pg[i];

                                   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 && m2->mc_ki[i] == mc->mc_ki[i]) {
                                                 mdb_xcursor_init1(m2, leaf);
                                          }
                                   }
                            }
                     }
                     xflags |= (flags & MDB_APPEND);
                     rc = mdb_cursor_put(&mc->mc_xcursor->mx_cursor, data, &xdata, xflags);
                     if (flags & F_SUBDATA) {
                            void *db = NODEDATA(leaf);
                            memcpy(db, &mc->mc_xcursor->mx_db, sizeof(MDB_db));
                     }
              }
              /* sub-writes might have failed so check rc again.
               * Don't increment count if we just replaced an existing item.
               */
              if (!rc && !(flags & MDB_CURRENT))
                     mc->mc_db->md_entries++;
              if (flags & MDB_MULTIPLE) {
                     mcount++;
                     if (mcount < data[1].mv_size) {
                            data[0].mv_data = (char *)data[0].mv_data + data[0].mv_size;
                            leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
                            goto more;
                     }
              }
       }
done:
       return rc;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Return the cursor's transaction handle.

Parameters:
[in]cursorA cursor handle returned by mdb_cursor_open()

Definition at line 5048 of file mdb.c.

{
       if (!mc) return NULL;
       return mc->mc_txn;
}
int mdb_dcmp ( MDB_txn txn,
MDB_dbi  dbi,
const MDB_val a,
const MDB_val b 
)

Compare two data items according to a particular database.

This returns a comparison as if the two items were data items of a sorted duplicates MDB_DUPSORT database.

Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()
[in]dbiA database handle returned by mdb_open()
[in]aThe first item to compare
[in]bThe second item to compare
Returns:
< 0 if a < b, 0 if a == b, > 0 if a > b

Definition at line 1170 of file mdb.c.

{
       if (txn->mt_dbxs[dbi].md_dcmp)
              return txn->mt_dbxs[dbi].md_dcmp(a, b);
       else
              return EINVAL;       /* too bad you can't distinguish this from a valid result */
}
int mdb_del ( MDB_txn txn,
MDB_dbi  dbi,
MDB_val key,
MDB_val data 
)

Delete items from a database.

This function removes key/data pairs from the database. If the database does not support sorted duplicate data items (MDB_DUPSORT) the data parameter is ignored. If the database supports sorted duplicates and the data parameter is NULL, all of the duplicate data items for the key will be deleted. Otherwise, if the data parameter is non-NULL only the matching data item will be deleted. This function will return MDB_NOTFOUND if the specified key/data pair is not in the database.

Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()
[in]dbiA database handle returned by mdb_open()
[in]keyThe key to delete from the database
[in]dataThe data to delete
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EACCES - an attempt was made to write in a read-only transaction.
  • EINVAL - an invalid parameter was specified.

Definition at line 5598 of file mdb.c.

{
       MDB_cursor mc;
       MDB_xcursor mx;
       MDB_cursor_op op;
       MDB_val rdata, *xdata;
       int            rc, exact;
       DKBUF;

       assert(key != NULL);

       DPRINTF("====> delete db %u key [%s]", dbi, DKEY(key));

       if (txn == NULL || !dbi || dbi >= txn->mt_numdbs)
              return EINVAL;

       if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) {
              return EACCES;
       }

       if (key->mv_size == 0 || key->mv_size > MAXKEYSIZE) {
              return EINVAL;
       }

       mdb_cursor_init(&mc, txn, dbi, &mx);

       exact = 0;
       if (data) {
              op = MDB_GET_BOTH;
              rdata = *data;
              xdata = &rdata;
       } else {
              op = MDB_SET;
              xdata = NULL;
       }
       rc = mdb_cursor_set(&mc, key, xdata, op, &exact);
       if (rc == 0)
              rc = mdb_cursor_del(&mc, data ? 0 : MDB_NODUPDATA);
       return rc;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int mdb_drop ( MDB_txn txn,
MDB_dbi  dbi,
int  del 
)

Delete a database and/or free all its pages.

If the del parameter is non-zero the DB handle will be closed and the DB will be deleted.

Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()
[in]dbiA database handle returned by mdb_open()
[in]delnon-zero to delete the DB from the environment, otherwise just free its pages.
Returns:
A non-zero error value on failure and 0 on success.

Definition at line 6282 of file mdb.c.

{
       MDB_cursor *mc;
       int rc;

       if (!txn || !dbi || dbi >= txn->mt_numdbs)
              return EINVAL;

       if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY))
              return EACCES;

       rc = mdb_cursor_open(txn, dbi, &mc);
       if (rc)
              return rc;

       rc = mdb_drop0(mc, mc->mc_db->md_flags & MDB_DUPSORT);
       if (rc)
              goto leave;

       /* Can't delete the main DB */
       if (del && dbi > MAIN_DBI) {
              rc = mdb_del(txn, MAIN_DBI, &mc->mc_dbx->md_name, NULL);
              if (!rc)
                     mdb_close(txn->mt_env, dbi);
       } else {
              txn->mt_dbflags[dbi] |= DB_DIRTY;
              txn->mt_dbs[dbi].md_depth = 0;
              txn->mt_dbs[dbi].md_branch_pages = 0;
              txn->mt_dbs[dbi].md_leaf_pages = 0;
              txn->mt_dbs[dbi].md_overflow_pages = 0;
              txn->mt_dbs[dbi].md_entries = 0;
              txn->mt_dbs[dbi].md_root = P_INVALID;
       }
leave:
       mdb_cursor_close(mc);
       return rc;
}

Here is the call graph for this function:

Close the environment and release the memory map.

Only a single thread may call this function. All transactions, databases, and cursors must already be closed before calling this function. Attempts to use any such handles after calling this function will cause a SIGSEGV. The environment handle will be freed and must not be used again after this call.

Parameters:
[in]envAn environment handle returned by mdb_env_create()

Definition at line 3029 of file mdb.c.

{
       MDB_page *dp;

       if (env == NULL)
              return;

       VGMEMP_DESTROY(env);
       while (env->me_dpages) {
              dp = env->me_dpages;
              VGMEMP_DEFINED(&dp->mp_next, sizeof(dp->mp_next));
              env->me_dpages = dp->mp_next;
              free(dp);
       }

       free(env->me_dbs[1]);
       free(env->me_dbs[0]);
       free(env->me_dbxs);
       free(env->me_path);

       LAZY_RWLOCK_DESTROY(&env->me_dblock);
       pthread_key_delete(env->me_txkey);

       if (env->me_map) {
              munmap(env->me_map, env->me_mapsize);
       }
       close(env->me_mfd);
       close(env->me_fd);
       if (env->me_txns) {
              pid_t pid = getpid();
              unsigned int i;
              for (i=0; i<env->me_txns->mti_numreaders; i++)
                     if (env->me_txns->mti_readers[i].mr_pid == pid)
                            env->me_txns->mti_readers[i].mr_pid = 0;
              munmap((void *)env->me_txns, (env->me_maxreaders-1)*sizeof(MDB_reader)+sizeof(MDB_txninfo));
       }
       close(env->me_lfd);
       mdb_midl_free(env->me_free_pgs);
       free(env);
}

Here is the call graph for this function:

Here is the caller graph for this function:

int mdb_env_create ( MDB_env **  env)

Create an MDB environment handle.

This function allocates memory for a MDB_env structure. To release the allocated memory and discard the handle, call mdb_env_close(). Before the handle may be used, it must be opened using mdb_env_open(). Various other options may also need to be set before opening the handle, e.g. mdb_env_set_mapsize(), mdb_env_set_maxreaders(), mdb_env_set_maxdbs(), depending on usage requirements.

Parameters:
[out]envThe address where the new handle will be stored
Returns:
A non-zero error value on failure and 0 on success.

Definition at line 2407 of file mdb.c.

{
       MDB_env *e;

       e = calloc(1, sizeof(MDB_env));
       if (!e)
              return ENOMEM;

       e->me_free_pgs = mdb_midl_alloc();
       if (!e->me_free_pgs) {
              free(e);
              return ENOMEM;
       }
       e->me_maxreaders = DEFAULT_READERS;
       e->me_maxdbs = 2;
       e->me_fd = INVALID_HANDLE_VALUE;
       e->me_lfd = INVALID_HANDLE_VALUE;
       e->me_mfd = INVALID_HANDLE_VALUE;
       VGMEMP_CREATE(e,0,0);
       *env = e;
       return MDB_SUCCESS;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int mdb_env_get_flags ( MDB_env env,
unsigned int flags 
)

Get environment flags.

Parameters:
[in]envAn environment handle returned by mdb_env_create()
[out]flagsThe address of an integer to store the flags
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EINVAL - an invalid parameter was specified.

Definition at line 6041 of file mdb.c.

{
       if (!env || !arg)
              return EINVAL;

       *arg = env->me_flags;
       return MDB_SUCCESS;
}
int mdb_env_get_maxreaders ( MDB_env env,
unsigned int readers 
)

Get the maximum number of threads for the environment.

Parameters:
[in]envAn environment handle returned by mdb_env_create()
[out]readersAddress of an integer to store the number of readers
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EINVAL - an invalid parameter was specified.

Definition at line 2460 of file mdb.c.

{
       if (!env || !readers)
              return EINVAL;
       *readers = env->me_maxreaders;
       return MDB_SUCCESS;
}
int mdb_env_get_path ( MDB_env env,
const char **  path 
)

Return the path that was used in mdb_env_open().

Parameters:
[in]envAn environment handle returned by mdb_env_create()
[out]pathAddress of a string pointer to contain the path. This is the actual string in the environment, not a copy. It should not be altered in any way.
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EINVAL - an invalid parameter was specified.

Definition at line 6051 of file mdb.c.

{
       if (!env || !arg)
              return EINVAL;

       *arg = env->me_path;
       return MDB_SUCCESS;
}
int mdb_env_open ( MDB_env env,
const char *  path,
unsigned int  flags,
mode_t  mode 
)

Open an environment handle.

If this function fails, mdb_env_close() must be called to discard the MDB_env handle.

Parameters:
[in]envAn environment handle returned by mdb_env_create()
[in]pathThe directory in which the database files reside. This directory must already exist and be writable.
[in]flagsSpecial options for this environment. This parameter must be set to 0 or by bitwise OR'ing together one or more of the values described here.
  • MDB_FIXEDMAP use a fixed address for the mmap region. This flag must be specified when creating the environment, and is stored persistently in the environment. If successful, the memory map will always reside at the same virtual address and pointers used to reference data items in the database will be constant across multiple invocations. This option may not always work, depending on how the operating system has allocated memory to shared libraries and other uses. The feature is highly experimental.
  • MDB_NOSUBDIR By default, MDB creates its environment in a directory whose pathname is given in path, and creates its data and lock files under that directory. With this option, path is used as-is for the database main data file. The database lock file is the path with "-lock" appended.
  • MDB_NOSYNC Don't perform a synchronous flush after committing a transaction. This means transactions will exhibit the ACI (atomicity, consistency, and isolation) properties, but not D (durability); that is database integrity will be maintained but it is possible some number of the most recently committed transactions may be undone after a system crash. The number of transactions at risk is governed by how often the system flushes dirty buffers to disk and how often mdb_env_sync() is called. This flag may be changed at any time using mdb_env_set_flags().
  • MDB_RDONLY Open the environment in read-only mode. No write operations will be allowed.
[in]modeThe UNIX permissions to set on created files. This parameter is ignored on Windows.
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • MDB_VERSION_MISMATCH - the version of the MDB library doesn't match the version that created the database environment.
  • EINVAL - the environment file headers are corrupted.
  • ENOENT - the directory specified by the path parameter doesn't exist.
  • EACCES - the user didn't have permission to access the environment files.
  • EAGAIN - the environment was locked by another process.

Definition at line 2929 of file mdb.c.

{
       int           oflags, rc, len, excl;
       char *lpath, *dpath;

       len = strlen(path);
       if (flags & MDB_NOSUBDIR) {
              rc = len + sizeof(LOCKSUFF) + len + 1;
       } else {
              rc = len + sizeof(LOCKNAME) + len + sizeof(DATANAME);
       }
       lpath = malloc(rc);
       if (!lpath)
              return ENOMEM;
       if (flags & MDB_NOSUBDIR) {
              dpath = lpath + len + sizeof(LOCKSUFF);
              sprintf(lpath, "%s" LOCKSUFF, path);
              strcpy(dpath, path);
       } else {
              dpath = lpath + len + sizeof(LOCKNAME);
              sprintf(lpath, "%s" LOCKNAME, path);
              sprintf(dpath, "%s" DATANAME, path);
       }

       rc = mdb_env_setup_locks(env, lpath, mode, &excl);
       if (rc)
              goto leave;

#ifdef _WIN32
       if (F_ISSET(flags, MDB_RDONLY)) {
              oflags = GENERIC_READ;
              len = OPEN_EXISTING;
       } else {
              oflags = GENERIC_READ|GENERIC_WRITE;
              len = OPEN_ALWAYS;
       }
       mode = FILE_ATTRIBUTE_NORMAL;
       if ((env->me_fd = CreateFile(dpath, oflags, FILE_SHARE_READ|FILE_SHARE_WRITE,
                     NULL, len, mode, NULL)) == INVALID_HANDLE_VALUE) {
              rc = ErrCode();
              goto leave;
       }
#else
       if (F_ISSET(flags, MDB_RDONLY))
              oflags = O_RDONLY;
       else
              oflags = O_RDWR | O_CREAT;

       if ((env->me_fd = open(dpath, oflags, mode)) == -1) {
              rc = ErrCode();
              goto leave;
       }
#endif

       if ((rc = mdb_env_open2(env, flags)) == MDB_SUCCESS) {
              /* synchronous fd for meta writes */
#ifdef _WIN32
              if (!(flags & (MDB_RDONLY|MDB_NOSYNC)))
                     mode |= FILE_FLAG_WRITE_THROUGH;
              if ((env->me_mfd = CreateFile(dpath, oflags, FILE_SHARE_READ|FILE_SHARE_WRITE,
                     NULL, len, mode, NULL)) == INVALID_HANDLE_VALUE) {
                     rc = ErrCode();
                     goto leave;
              }
#else
              if (!(flags & (MDB_RDONLY|MDB_NOSYNC)))
                     oflags |= MDB_DSYNC;
              if ((env->me_mfd = open(dpath, oflags, mode)) == -1) {
                     rc = ErrCode();
                     goto leave;
              }
#endif
              env->me_path = strdup(path);
              DPRINTF("opened dbenv %p", (void *) env);
              pthread_key_create(&env->me_txkey, mdb_env_reader_dest);
              LAZY_RWLOCK_INIT(&env->me_dblock, NULL);
              if (excl)
                     mdb_env_share_locks(env);
              env->me_dbxs = calloc(env->me_maxdbs, sizeof(MDB_dbx));
              env->me_dbs[0] = calloc(env->me_maxdbs, sizeof(MDB_db));
              env->me_dbs[1] = calloc(env->me_maxdbs, sizeof(MDB_db));
              env->me_numdbs = 2;
       }

leave:
       if (rc) {
              if (env->me_fd != INVALID_HANDLE_VALUE) {
                     close(env->me_fd);
                     env->me_fd = INVALID_HANDLE_VALUE;
              }
              if (env->me_lfd != INVALID_HANDLE_VALUE) {
                     close(env->me_lfd);
                     env->me_lfd = INVALID_HANDLE_VALUE;
              }
       }
       free(lpath);
       return rc;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int mdb_env_set_flags ( MDB_env env,
unsigned int  flags,
int  onoff 
)

Set environment flags.

This may be used to set some flags that weren't already set during

mdb_env_open(), or to unset these flags. Currently only the

MDB_NOSYNC flag setting may be changed with this function.

Parameters:
[in]envAn environment handle returned by mdb_env_create()
[in]flagsThe flags to change, bitwise OR'ed together
[in]onoffA non-zero value sets the flags, zero clears them.
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EINVAL - an invalid parameter was specified.

Definition at line 6029 of file mdb.c.

{
       if ((flag & CHANGEABLE) != flag)
              return EINVAL;
       if (onoff)
              env->me_flags |= flag;
       else
              env->me_flags &= ~flag;
       return MDB_SUCCESS;
}

Here is the caller graph for this function:

int mdb_env_set_mapsize ( MDB_env env,
size_t  size 
)

Set the size of the memory map to use for this environment.

The size should be a multiple of the OS page size. The default is 10485760 bytes. The size of the memory map is also the maximum size of the database. The value should be chosen as large as possible, to accommodate future growth of the database. This function may only be called after mdb_env_create() and before mdb_env_open().

Parameters:
[in]envAn environment handle returned by mdb_env_create()
[in]sizeThe size in bytes
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EINVAL - an invalid parameter was specified, or the environment is already open.

Definition at line 2431 of file mdb.c.

{
       if (env->me_map)
              return EINVAL;
       env->me_mapsize = size;
       if (env->me_psize)
              env->me_maxpg = env->me_mapsize / env->me_psize;
       return MDB_SUCCESS;
}

Here is the caller graph for this function:

int mdb_env_set_maxdbs ( MDB_env env,
MDB_dbi  dbs 
)

Set the maximum number of databases for the environment.

This function is only needed if multiple databases will be used in the environment. Simpler applications that only use a single database can ignore this option. This function may only be called after mdb_env_create() and before mdb_env_open().

Parameters:
[in]envAn environment handle returned by mdb_env_create()
[in]dbsThe maximum number of databases
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EINVAL - an invalid parameter was specified, or the environment is already open.

Definition at line 2442 of file mdb.c.

{
       if (env->me_map)
              return EINVAL;
       env->me_maxdbs = dbs;
       return MDB_SUCCESS;
}

Here is the caller graph for this function:

int mdb_env_set_maxreaders ( MDB_env env,
unsigned int  readers 
)

Set the maximum number of threads for the environment.

This defines the number of slots in the lock table that is used to track readers in the the environment. The default is 126. This function may only be called after mdb_env_create() and before mdb_env_open().

Parameters:
[in]envAn environment handle returned by mdb_env_create()
[in]readersThe maximum number of threads
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EINVAL - an invalid parameter was specified, or the environment is already open.

Definition at line 2451 of file mdb.c.

{
       if (env->me_map || readers < 1)
              return EINVAL;
       env->me_maxreaders = readers;
       return MDB_SUCCESS;
}

Here is the caller graph for this function:

int mdb_env_stat ( MDB_env env,
MDB_stat stat 
)

Return statistics about the MDB environment.

Parameters:
[in]envAn environment handle returned by mdb_env_create()
[out]statThe address of an MDB_stat structure where the statistics will be copied

Definition at line 6079 of file mdb.c.

{
       int toggle;

       if (env == NULL || arg == NULL)
              return EINVAL;

       mdb_env_read_meta(env, &toggle);

       return mdb_stat0(env, &env->me_metas[toggle]->mm_dbs[MAIN_DBI], arg);
}

Here is the call graph for this function:

Here is the caller graph for this function:

int mdb_env_sync ( MDB_env env,
int  force 
)

Flush the data buffers to disk.

Data is always written to disk when mdb_txn_commit() is called, but the operating system may keep it buffered. MDB always flushes the OS buffers upon commit as well, unless the environment was opened with MDB_NOSYNC.

Parameters:
[in]envAn environment handle returned by mdb_env_create()
[in]forceIf non-zero, force the flush to occur. Otherwise if the environment has the MDB_NOSYNC flag set the flushes will be omitted.
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EINVAL - an invalid parameter was specified.
  • EIO - an error occurred during synchronization.

Definition at line 1431 of file mdb.c.

{
       int rc = 0;
       if (force || !F_ISSET(env->me_flags, MDB_NOSYNC)) {
              if (MDB_FDATASYNC(env->me_fd))
                     rc = ErrCode();
       }
       return rc;
}

Here is the caller graph for this function:

int mdb_get ( MDB_txn txn,
MDB_dbi  dbi,
MDB_val key,
MDB_val data 
)

Get items from a database.

This function retrieves key/data pairs from the database. The address and length of the data associated with the specified key are returned in the structure to which data refers. If the database supports duplicate keys (MDB_DUPSORT) then the first data item for the key will be returned. Retrieval of other items requires the use of mdb_cursor_get().

Note:
The memory pointed to by the returned values is owned by the database. The caller need not dispose of the memory, and may not modify it in any way. For values returned in a read-only transaction any modification attempts will cause a SIGSEGV.
Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()
[in]dbiA database handle returned by mdb_open()
[in]keyThe key to search for in the database
[out]dataThe data corresponding to the key
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • MDB_NOTFOUND - the key was not in the database.
  • EINVAL - an invalid parameter was specified.

Definition at line 3523 of file mdb.c.

{
       MDB_cursor    mc;
       MDB_xcursor   mx;
       int exact = 0;
       DKBUF;

       assert(key);
       assert(data);
       DPRINTF("===> get db %u key [%s]", dbi, DKEY(key));

       if (txn == NULL || !dbi || dbi >= txn->mt_numdbs)
              return EINVAL;

       if (key->mv_size == 0 || key->mv_size > MAXKEYSIZE) {
              return EINVAL;
       }

       mdb_cursor_init(&mc, txn, dbi, &mx);
       return mdb_cursor_set(&mc, key, data, MDB_SET, &exact);
}

Here is the call graph for this function:

int mdb_open ( MDB_txn txn,
const char *  name,
unsigned int  flags,
MDB_dbi dbi 
)

Open a database in the environment.

The database handle may be discarded by calling mdb_close(). Only one thread at a time may call this function; it is not mutex-protected in a read-only transaction.

Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()
[in]nameThe name of the database to open. If only a single database is needed in the environment, this value may be NULL.
[in]flagsSpecial options for this database. This parameter must be set to 0 or by bitwise OR'ing together one or more of the values described here.
  • MDB_REVERSEKEY Keys are strings to be compared in reverse order, from the end of the strings to the beginning. By default, Keys are treated as strings and compared from beginning to end.
  • MDB_DUPSORT Duplicate keys may be used in the database. (Or, from another perspective, keys may have multiple data items, stored in sorted order.) By default keys must be unique and may have only a single data item.
  • MDB_INTEGERKEY Keys are binary integers in native byte order. Setting this option requires all keys to be the same size, typically sizeof(int) or sizeof(size_t).
  • MDB_DUPFIXED This flag may only be used in combination with MDB_DUPSORT. This option tells the library that the data items for this database are all the same size, which allows further optimizations in storage and retrieval. When all data items are the same size, the MDB_GET_MULTIPLE and MDB_NEXT_MULTIPLE cursor operations may be used to retrieve multiple items at once.
  • MDB_INTEGERDUP This option specifies that duplicate data items are also integers, and should be sorted as such.
  • MDB_REVERSEDUP This option specifies that duplicate data items should be compared as strings in reverse order.
  • MDB_CREATE Create the named database if it doesn't exist. This option is not allowed in a read-only transaction or a read-only environment.
[out]dbiAddress where the new MDB_dbi handle will be stored
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:

Definition at line 6124 of file mdb.c.

{
       MDB_val key, data;
       MDB_dbi i;
       MDB_cursor mc;
       int rc, dbflag, exact;
       size_t len;

       if (txn->mt_dbxs[FREE_DBI].md_cmp == NULL) {
              mdb_default_cmp(txn, FREE_DBI);
       }

       /* main DB? */
       if (!name) {
              *dbi = MAIN_DBI;
              if (flags & (MDB_DUPSORT|MDB_REVERSEKEY|MDB_INTEGERKEY))
                     txn->mt_dbs[MAIN_DBI].md_flags |= (flags & (MDB_DUPSORT|MDB_REVERSEKEY|MDB_INTEGERKEY));
              mdb_default_cmp(txn, MAIN_DBI);
              return MDB_SUCCESS;
       }

       if (txn->mt_dbxs[MAIN_DBI].md_cmp == NULL) {
              mdb_default_cmp(txn, MAIN_DBI);
       }

       /* Is the DB already open? */
       len = strlen(name);
       for (i=2; i<txn->mt_numdbs; i++) {
              if (len == txn->mt_dbxs[i].md_name.mv_size &&
                     !strncmp(name, txn->mt_dbxs[i].md_name.mv_data, len)) {
                     *dbi = i;
                     return MDB_SUCCESS;
              }
       }

       if (txn->mt_numdbs >= txn->mt_env->me_maxdbs - 1)
              return ENFILE;

       /* Find the DB info */
       dbflag = 0;
       exact = 0;
       key.mv_size = len;
       key.mv_data = (void *)name;
       mdb_cursor_init(&mc, txn, MAIN_DBI, NULL);
       rc = mdb_cursor_set(&mc, &key, &data, MDB_SET, &exact);
       if (rc == MDB_SUCCESS) {
              /* make sure this is actually a DB */
              MDB_node *node = NODEPTR(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]);
              if (!(node->mn_flags & F_SUBDATA))
                     return EINVAL;
       } else if (rc == MDB_NOTFOUND && (flags & MDB_CREATE)) {
              /* Create if requested */
              MDB_db dummy;
              data.mv_size = sizeof(MDB_db);
              data.mv_data = &dummy;
              memset(&dummy, 0, sizeof(dummy));
              dummy.md_root = P_INVALID;
              dummy.md_flags = flags & 0xffff;
              rc = mdb_cursor_put(&mc, &key, &data, F_SUBDATA);
              dbflag = DB_DIRTY;
       }

       /* OK, got info, add to table */
       if (rc == MDB_SUCCESS) {
              txn->mt_dbxs[txn->mt_numdbs].md_name.mv_data = strdup(name);
              txn->mt_dbxs[txn->mt_numdbs].md_name.mv_size = len;
              txn->mt_dbxs[txn->mt_numdbs].md_rel = NULL;
              txn->mt_dbflags[txn->mt_numdbs] = dbflag;
              memcpy(&txn->mt_dbs[txn->mt_numdbs], data.mv_data, sizeof(MDB_db));
              *dbi = txn->mt_numdbs;
              txn->mt_env->me_dbs[0][txn->mt_numdbs] = txn->mt_dbs[txn->mt_numdbs];
              txn->mt_env->me_dbs[1][txn->mt_numdbs] = txn->mt_dbs[txn->mt_numdbs];
              mdb_default_cmp(txn, txn->mt_numdbs);
              txn->mt_numdbs++;
       }

       return rc;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int mdb_put ( MDB_txn txn,
MDB_dbi  dbi,
MDB_val key,
MDB_val data,
unsigned int  flags 
)

Store items into a database.

This function stores key/data pairs in the database. The default behavior is to enter the new key/data pair, replacing any previously existing key if duplicates are disallowed, or adding a duplicate data item if duplicates are allowed (MDB_DUPSORT).

Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()
[in]dbiA database handle returned by mdb_open()
[in]keyThe key to store in the database
[in,out]dataThe data to store
[in]flagsSpecial options for this operation. This parameter must be set to 0 or by bitwise OR'ing together one or more of the values described here.
  • MDB_NODUPDATA - enter the new key/data pair only if it does not already appear in the database. This flag may only be specified if the database was opened with MDB_DUPSORT. The function will return MDB_KEYEXIST if the key/data pair already appears in the database.
  • MDB_NOOVERWRITE - enter the new key/data pair only if the key does not already appear in the database. The function will return

    MDB_KEYEXIST if the key already appears in the database, even if

    the database supports duplicates (MDB_DUPSORT). The data parameter will be set to point to the existing item.

Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EACCES - an attempt was made to write in a read-only transaction.
  • EINVAL - an invalid parameter was specified.
  • ENOMEM - the database is full, see mdb_env_set_mapsize().

Definition at line 5996 of file mdb.c.

{
       MDB_cursor mc;
       MDB_xcursor mx;

       assert(key != NULL);
       assert(data != NULL);

       if (txn == NULL || !dbi || dbi >= txn->mt_numdbs)
              return EINVAL;

       if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) {
              return EACCES;
       }

       if (key->mv_size == 0 || key->mv_size > MAXKEYSIZE) {
              return EINVAL;
       }

       if ((flags & (MDB_NOOVERWRITE|MDB_NODUPDATA|MDB_RESERVE|MDB_APPEND)) != flags)
              return EINVAL;

       mdb_cursor_init(&mc, txn, dbi, &mx);
       return mdb_cursor_put(&mc, key, data, flags);
}

Here is the call graph for this function:

Here is the caller graph for this function:

int mdb_set_compare ( MDB_txn txn,
MDB_dbi  dbi,
MDB_cmp_func cmp 
)

Set a custom key comparison function for a database.

The comparison function is called whenever it is necessary to compare a key specified by the application with a key currently stored in the database. If no comparison function is specified, and no special key flags were specified with mdb_open(), the keys are compared lexically, with shorter keys collating before longer keys.

Warning:
This function must be called before any data access functions are used, otherwise data corruption may occur. The same comparison function must be used by every program accessing the database, every time the database is used.
Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()
[in]dbiA database handle returned by mdb_open()
[in]cmpA MDB_cmp_func function
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EINVAL - an invalid parameter was specified.

Definition at line 6320 of file mdb.c.

{
       if (txn == NULL || !dbi || dbi >= txn->mt_numdbs)
              return EINVAL;

       txn->mt_dbxs[dbi].md_cmp = cmp;
       return MDB_SUCCESS;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int mdb_set_dupsort ( MDB_txn txn,
MDB_dbi  dbi,
MDB_cmp_func cmp 
)

Set a custom data comparison function for a MDB_DUPSORT database.

This comparison function is called whenever it is necessary to compare a data item specified by the application with a data item currently stored in the database. This function only takes effect if the database was opened with the MDB_DUPSORT flag. If no comparison function is specified, and no special key flags were specified with mdb_open(), the data items are compared lexically, with shorter items collating before longer items.

Warning:
This function must be called before any data access functions are used, otherwise data corruption may occur. The same comparison function must be used by every program accessing the database, every time the database is used.
Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()
[in]dbiA database handle returned by mdb_open()
[in]cmpA MDB_cmp_func function
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EINVAL - an invalid parameter was specified.

Definition at line 6329 of file mdb.c.

{
       if (txn == NULL || !dbi || dbi >= txn->mt_numdbs)
              return EINVAL;

       txn->mt_dbxs[dbi].md_dcmp = cmp;
       return MDB_SUCCESS;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int mdb_set_relctx ( MDB_txn txn,
MDB_dbi  dbi,
void ctx 
)

Set a context pointer for a MDB_FIXEDMAP database's relocation function.

See mdb_set_relfunc and MDB_rel_func for more details.

Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()
[in]dbiA database handle returned by mdb_open()
[in]ctxAn arbitrary pointer for whatever the application needs. It will be passed to the callback function set by mdb_set_relfunc as its relctx parameter whenever the callback is invoked.
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EINVAL - an invalid parameter was specified.

Definition at line 6347 of file mdb.c.

{
       if (txn == NULL || !dbi || dbi >= txn->mt_numdbs)
              return EINVAL;

       txn->mt_dbxs[dbi].md_relctx = ctx;
       return MDB_SUCCESS;
}
int mdb_set_relfunc ( MDB_txn txn,
MDB_dbi  dbi,
MDB_rel_func rel 
)

Set a relocation function for a MDB_FIXEDMAP database.

Todo:
The relocation function is called whenever it is necessary to move the data of an item to a different position in the database (e.g. through tree balancing operations, shifts as a result of adds or deletes, etc.). It is intended to allow address/position-dependent data items to be stored in a database in an environment opened with the MDB_FIXEDMAP option. Currently the relocation feature is unimplemented and setting this function has no effect.
Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()
[in]dbiA database handle returned by mdb_open()
[in]relA MDB_rel_func function
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EINVAL - an invalid parameter was specified.

Definition at line 6338 of file mdb.c.

{
       if (txn == NULL || !dbi || dbi >= txn->mt_numdbs)
              return EINVAL;

       txn->mt_dbxs[dbi].md_rel = rel;
       return MDB_SUCCESS;
}
int mdb_stat ( MDB_txn txn,
MDB_dbi  dbi,
MDB_stat stat 
)

Retrieve statistics for a database.

Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()
[in]dbiA database handle returned by mdb_open()
[out]statThe address of an MDB_stat structure where the statistics will be copied
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EINVAL - an invalid parameter was specified.

Definition at line 6203 of file mdb.c.

{
       if (txn == NULL || arg == NULL || dbi >= txn->mt_numdbs)
              return EINVAL;

       return mdb_stat0(txn->mt_env, &txn->mt_dbs[dbi], arg);
}

Here is the call graph for this function:

Here is the caller graph for this function:

char* mdb_strerror ( int  err)

Return a string describing a given error code.

This function is a superset of the ANSI C X3.159-1989 (ANSI C) strerror(3) function. If the error code is greater than or equal to 0, then the string returned by the system function strerror(3) is returned. If the error code is less than 0, an error string corresponding to the MDB library error is returned. See Return Codes for a list of MDB-specific error codes.

Parameters:
[in]errThe error code
Return values:
error messageThe description of the error

Definition at line 1056 of file mdb.c.

{
       if (!err)
              return ("Successful return: 0");

       if (err >= MDB_KEYEXIST && err <= MDB_VERSION_MISMATCH)
              return mdb_errstr[err - MDB_KEYEXIST];

       return strerror(err);
}

Here is the caller graph for this function:

Abandon all the operations of the transaction instead of saving them.

All cursors opened within the transaction will be closed by this call. The cursors and transaction handle will be freed and must not be used again after this call.

Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()

Definition at line 1784 of file mdb.c.

{
       if (txn == NULL)
              return;

       DPRINTF("abort txn %zu%c %p on mdbenv %p, root page %zu",
              txn->mt_txnid, (txn->mt_flags & MDB_TXN_RDONLY) ? 'r' : 'w',
              (void *)txn, (void *)txn->mt_env, txn->mt_dbs[MAIN_DBI].md_root);

       if (txn->mt_child)
              mdb_txn_abort(txn->mt_child);

       mdb_txn_reset0(txn);
       free(txn);
}

Here is the call graph for this function:

Here is the caller graph for this function:

int mdb_txn_begin ( MDB_env env,
MDB_txn parent,
unsigned int  flags,
MDB_txn **  txn 
)

Create a transaction for use with the environment.

The transaction handle may be discarded using mdb_txn_abort() or mdb_txn_commit().

Note:
Transactions may not span threads; a transaction must only be used by a single thread. Also, a thread may only have a single transaction.
Cursors may not span transactions; each cursor must be opened and closed within a single transaction.
Parameters:
[in]envAn environment handle returned by mdb_env_create()
[in]parentIf this parameter is non-NULL, the new transaction will be a nested transaction, with the transaction indicated by parent as its parent. Transactions may be nested to any level. A parent transaction may not issue any other operations besides mdb_txn_begin, mdb_txn_abort, or mdb_txn_commit while it has active child transactions.
[in]flagsSpecial options for this transaction. This parameter must be set to 0 or by bitwise OR'ing together one or more of the values described here.
  • MDB_RDONLY This transaction will not perform any write operations.
[out]txnAddress where the new MDB_txn handle will be stored
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • MDB_PANIC - a fatal error occurred earlier and the environment must be shut down.
  • ENOMEM - out of memory, or a read-only transaction was requested and the reader lock table is full. See mdb_env_set_maxreaders().

Definition at line 1633 of file mdb.c.

{
       MDB_txn *txn;
       int rc, size;

       if (env->me_flags & MDB_FATAL_ERROR) {
              DPUTS("environment had fatal error, must shutdown!");
              return MDB_PANIC;
       }
       if (parent) {
              /* parent already has an active child txn */
              if (parent->mt_child) {
                     return EINVAL;
              }
       }
       size = sizeof(MDB_txn) + env->me_maxdbs * (sizeof(MDB_db)+1);
       if (!(flags & MDB_RDONLY))
              size += env->me_maxdbs * sizeof(MDB_cursor *);

       if ((txn = calloc(1, size)) == NULL) {
              DPRINTF("calloc: %s", strerror(ErrCode()));
              return ENOMEM;
       }
       txn->mt_dbs = (MDB_db *)(txn+1);
       if (flags & MDB_RDONLY) {
              txn->mt_flags |= MDB_TXN_RDONLY;
              txn->mt_dbflags = (unsigned char *)(txn->mt_dbs + env->me_maxdbs);
       } else {
              txn->mt_cursors = (MDB_cursor **)(txn->mt_dbs + env->me_maxdbs);
              txn->mt_dbflags = (unsigned char *)(txn->mt_cursors + env->me_maxdbs);
       }
       txn->mt_env = env;

       if (parent) {
              txn->mt_free_pgs = mdb_midl_alloc();
              if (!txn->mt_free_pgs) {
                     free(txn);
                     return ENOMEM;
              }
              txn->mt_u.dirty_list = malloc(sizeof(ID2)*MDB_IDL_UM_SIZE);
              if (!txn->mt_u.dirty_list) {
                     free(txn->mt_free_pgs);
                     free(txn);
                     return ENOMEM;
              }
              txn->mt_txnid = parent->mt_txnid;
              txn->mt_toggle = parent->mt_toggle;
              txn->mt_u.dirty_list[0].mid = 0;
              txn->mt_free_pgs[0] = 0;
              txn->mt_next_pgno = parent->mt_next_pgno;
              parent->mt_child = txn;
              txn->mt_parent = parent;
              txn->mt_numdbs = parent->mt_numdbs;
              txn->mt_dbxs = parent->mt_dbxs;
              memcpy(txn->mt_dbs, parent->mt_dbs, txn->mt_numdbs * sizeof(MDB_db));
              memcpy(txn->mt_dbflags, parent->mt_dbflags, txn->mt_numdbs);
              mdb_cursor_shadow(parent, txn);
              rc = 0;
       } else {
              rc = mdb_txn_renew0(txn);
       }
       if (rc)
              free(txn);
       else {
              *ret = txn;
              DPRINTF("begin txn %zu%c %p on mdbenv %p, root page %zu",
                     txn->mt_txnid, (txn->mt_flags & MDB_TXN_RDONLY) ? 'r' : 'w',
                     (void *) txn, (void *) env, txn->mt_dbs[MAIN_DBI].md_root);
       }

       return rc;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Commit all the operations of a transaction into the database.

All cursors opened within the transaction will be closed by this call. The cursors and transaction handle will be freed and must not be used again after this call.

Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • EINVAL - an invalid parameter was specified.
  • ENOSPC - no more disk space.
  • EIO - a low-level I/O error occurred while writing.
  • ENOMEM - the transaction is nested and could not be merged into its parent.

Definition at line 1801 of file mdb.c.

{
       int            n, done;
       unsigned int i;
       ssize_t               rc;
       off_t          size;
       MDB_page      *dp;
       MDB_env       *env;
       pgno_t next, freecnt;
       MDB_cursor mc;

       assert(txn != NULL);
       assert(txn->mt_env != NULL);

       if (txn->mt_child) {
              mdb_txn_commit(txn->mt_child);
              txn->mt_child = NULL;
       }

       env = txn->mt_env;

       if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) {
              if (txn->mt_numdbs > env->me_numdbs) {
                     /* update the DB tables */
                     int toggle = !env->me_db_toggle;
                     MDB_db *ip, *jp;
                     MDB_dbi i;

                     ip = &env->me_dbs[toggle][env->me_numdbs];
                     jp = &txn->mt_dbs[env->me_numdbs];
                     LAZY_RWLOCK_WRLOCK(&env->me_dblock);
                     for (i = env->me_numdbs; i < txn->mt_numdbs; i++) {
                            *ip++ = *jp++;
                     }

                     env->me_db_toggle = toggle;
                     env->me_numdbs = txn->mt_numdbs;
                     LAZY_RWLOCK_UNLOCK(&env->me_dblock);
              }
              mdb_txn_abort(txn);
              return MDB_SUCCESS;
       }

       if (F_ISSET(txn->mt_flags, MDB_TXN_ERROR)) {
              DPUTS("error flag is set, can't commit");
              if (txn->mt_parent)
                     txn->mt_parent->mt_flags |= MDB_TXN_ERROR;
              mdb_txn_abort(txn);
              return EINVAL;
       }

       /* Merge (and close) our cursors with parent's */
       mdb_cursor_merge(txn);

       if (txn->mt_parent) {
              MDB_db *ip, *jp;
              MDB_dbi i;
              unsigned x, y;
              ID2L dst, src;

              /* Update parent's DB table */
              ip = &txn->mt_parent->mt_dbs[2];
              jp = &txn->mt_dbs[2];
              for (i = 2; i < txn->mt_numdbs; i++) {
                     if (ip->md_root != jp->md_root)
                            *ip = *jp;
                     ip++; jp++;
              }
              txn->mt_parent->mt_numdbs = txn->mt_numdbs;

              /* Append our free list to parent's */
              mdb_midl_append_list(&txn->mt_parent->mt_free_pgs,
                     txn->mt_free_pgs);
              mdb_midl_free(txn->mt_free_pgs);

              /* Merge our dirty list with parent's */
              dst = txn->mt_parent->mt_u.dirty_list;
              src = txn->mt_u.dirty_list;
              x = mdb_mid2l_search(dst, src[1].mid);
              for (y=1; y<=src[0].mid; y++) {
                     while (x <= dst[0].mid && dst[x].mid != src[y].mid) x++;
                     if (x > dst[0].mid)
                            break;
                     free(dst[x].mptr);
                     dst[x].mptr = src[y].mptr;
              }
              x = dst[0].mid;
              for (; y<=src[0].mid; y++) {
                     if (++x >= MDB_IDL_UM_MAX) {
                            mdb_txn_abort(txn);
                            return ENOMEM;
                     }
                     dst[x] = src[y];
              }
              dst[0].mid = x;
              free(txn->mt_u.dirty_list);
              txn->mt_parent->mt_child = NULL;
              free(txn);
              return MDB_SUCCESS;
       }

       if (txn != env->me_txn) {
              DPUTS("attempt to commit unknown transaction");
              mdb_txn_abort(txn);
              return EINVAL;
       }

       if (!txn->mt_u.dirty_list[0].mid)
              goto done;

       DPRINTF("committing txn %zu %p on mdbenv %p, root page %zu",
           txn->mt_txnid, (void *)txn, (void *)env, txn->mt_dbs[MAIN_DBI].md_root);

       mdb_cursor_init(&mc, txn, FREE_DBI, NULL);

       /* should only be one record now */
       if (env->me_pghead) {
              /* make sure first page of freeDB is touched and on freelist */
              mdb_page_search(&mc, NULL, 1);
       }

       /* Delete IDLs we used from the free list */
       if (env->me_pgfirst) {
              txnid_t cur;
              MDB_val key;
              int exact = 0;

              key.mv_size = sizeof(cur);
              for (cur = env->me_pgfirst; cur <= env->me_pglast; cur++) {
                     key.mv_data = &cur;

                     mdb_cursor_set(&mc, &key, NULL, MDB_SET, &exact);
                     mdb_cursor_del(&mc, 0);
              }
              env->me_pgfirst = 0;
              env->me_pglast = 0;
       }

       /* save to free list */
free2:
       freecnt = txn->mt_free_pgs[0];
       if (!MDB_IDL_IS_ZERO(txn->mt_free_pgs)) {
              MDB_val key, data;

              /* make sure last page of freeDB is touched and on freelist */
              key.mv_size = MAXKEYSIZE+1;
              key.mv_data = NULL;
              mdb_page_search(&mc, &key, 1);

              mdb_midl_sort(txn->mt_free_pgs);
#if MDB_DEBUG > 1
              {
                     unsigned int i;
                     ID *idl = txn->mt_free_pgs;
                     DPRINTF("IDL write txn %zu root %zu num %zu",
                            txn->mt_txnid, txn->mt_dbs[FREE_DBI].md_root, idl[0]);
                     for (i=0; i<idl[0]; i++) {
                            DPRINTF("IDL %zu", idl[i+1]);
                     }
              }
#endif
              /* write to last page of freeDB */
              key.mv_size = sizeof(pgno_t);
              key.mv_data = &txn->mt_txnid;
              data.mv_data = txn->mt_free_pgs;
              /* The free list can still grow during this call,
               * despite the pre-emptive touches above. So check
               * and make sure the entire thing got written.
               */
              do {
                     freecnt = txn->mt_free_pgs[0];
                     data.mv_size = MDB_IDL_SIZEOF(txn->mt_free_pgs);
                     rc = mdb_cursor_put(&mc, &key, &data, 0);
                     if (rc) {
                            mdb_txn_abort(txn);
                            return rc;
                     }
              } while (freecnt != txn->mt_free_pgs[0]);
       }
       /* should only be one record now */
again:
       if (env->me_pghead) {
              MDB_val key, data;
              MDB_oldpages *mop;
              pgno_t orig;
              txnid_t id;

              mop = env->me_pghead;
              id = mop->mo_txnid;
              key.mv_size = sizeof(id);
              key.mv_data = &id;
              data.mv_size = MDB_IDL_SIZEOF(mop->mo_pages);
              data.mv_data = mop->mo_pages;
              orig = mop->mo_pages[0];
              /* These steps may grow the freelist again
               * due to freed overflow pages...
               */
              mdb_cursor_put(&mc, &key, &data, 0);
              if (mop == env->me_pghead && env->me_pghead->mo_txnid == id) {
                     /* could have been used again here */
                     if (mop->mo_pages[0] != orig) {
                            data.mv_size = MDB_IDL_SIZEOF(mop->mo_pages);
                            data.mv_data = mop->mo_pages;
                            id = mop->mo_txnid;
                            mdb_cursor_put(&mc, &key, &data, 0);
                     }
                     env->me_pghead = NULL;
                     free(mop);
              } else {
                     /* was completely used up */
                     mdb_cursor_del(&mc, 0);
                     if (env->me_pghead)
                            goto again;
              }
              env->me_pgfirst = 0;
              env->me_pglast = 0;
       }
       /* Check for growth of freelist again */
       if (freecnt != txn->mt_free_pgs[0])
              goto free2;

       if (!MDB_IDL_IS_ZERO(txn->mt_free_pgs)) {
              if (mdb_midl_shrink(&txn->mt_free_pgs))
                     env->me_free_pgs = txn->mt_free_pgs;
       }

       /* Update DB root pointers. Their pages have already been
        * touched so this is all in-place and cannot fail.
        */
       {
              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++) {
                     if (txn->mt_dbflags[i] & DB_DIRTY) {
                            data.mv_data = &txn->mt_dbs[i];
                            mdb_cursor_put(&mc, &txn->mt_dbxs[i].md_name, &data, 0);
                     }
              }
       }
#if MDB_DEBUG > 2
       mdb_audit(txn);
#endif

       /* Commit up to MDB_COMMIT_PAGES dirty pages to disk until done.
        */
       next = 0;
       i = 1;
       do {
#ifdef _WIN32
              /* Windows actually supports scatter/gather I/O, but only on
               * unbuffered file handles. Since we're relying on the OS page
               * cache for all our data, that's self-defeating. So we just
               * write pages one at a time. We use the ov structure to set
               * the write offset, to at least save the overhead of a Seek
               * system call.
               */
              OVERLAPPED ov;
              memset(&ov, 0, sizeof(ov));
              for (; i<=txn->mt_u.dirty_list[0].mid; i++) {
                     size_t wsize;
                     dp = txn->mt_u.dirty_list[i].mptr;
                     DPRINTF("committing page %zu", dp->mp_pgno);
                     size = dp->mp_pgno * env->me_psize;
                     ov.Offset = size & 0xffffffff;
                     ov.OffsetHigh = size >> 16;
                     ov.OffsetHigh >>= 16;
                     /* clear dirty flag */
                     dp->mp_flags &= ~P_DIRTY;
                     wsize = env->me_psize;
                     if (IS_OVERFLOW(dp)) wsize *= dp->mp_pages;
                     rc = WriteFile(env->me_fd, dp, wsize, NULL, &ov);
                     if (!rc) {
                            n = ErrCode();
                            DPRINTF("WriteFile: %d", n);
                            mdb_txn_abort(txn);
                            return n;
                     }
              }
              done = 1;
#else
              struct iovec   iov[MDB_COMMIT_PAGES];
              n = 0;
              done = 1;
              size = 0;
              for (; i<=txn->mt_u.dirty_list[0].mid; i++) {
                     dp = txn->mt_u.dirty_list[i].mptr;
                     if (dp->mp_pgno != next) {
                            if (n) {
                                   rc = writev(env->me_fd, iov, n);
                                   if (rc != size) {
                                          n = ErrCode();
                                          if (rc > 0)
                                                 DPUTS("short write, filesystem full?");
                                          else
                                                 DPRINTF("writev: %s", strerror(n));
                                          mdb_txn_abort(txn);
                                          return n;
                                   }
                                   n = 0;
                                   size = 0;
                            }
                            lseek(env->me_fd, dp->mp_pgno * env->me_psize, SEEK_SET);
                            next = dp->mp_pgno;
                     }
                     DPRINTF("committing page %zu", dp->mp_pgno);
                     iov[n].iov_len = env->me_psize;
                     if (IS_OVERFLOW(dp)) iov[n].iov_len *= dp->mp_pages;
                     iov[n].iov_base = (char *)dp;
                     size += iov[n].iov_len;
                     next = dp->mp_pgno + (IS_OVERFLOW(dp) ? dp->mp_pages : 1);
                     /* clear dirty flag */
                     dp->mp_flags &= ~P_DIRTY;
                     if (++n >= MDB_COMMIT_PAGES) {
                            done = 0;
                            i++;
                            break;
                     }
              }

              if (n == 0)
                     break;

              rc = writev(env->me_fd, iov, n);
              if (rc != size) {
                     n = ErrCode();
                     if (rc > 0)
                            DPUTS("short write, filesystem full?");
                     else
                            DPRINTF("writev: %s", strerror(n));
                     mdb_txn_abort(txn);
                     return n;
              }
#endif
       } while (!done);

       /* Drop the dirty pages.
        */
       for (i=1; i<=txn->mt_u.dirty_list[0].mid; i++) {
              dp = txn->mt_u.dirty_list[i].mptr;
              if (!IS_OVERFLOW(dp) || dp->mp_pages == 1) {
                     dp->mp_next = txn->mt_env->me_dpages;
                     VGMEMP_FREE(txn->mt_env, dp);
                     txn->mt_env->me_dpages = dp;
              } else {
                     VGMEMP_FREE(txn->mt_env, dp);
                     free(dp);
              }
              txn->mt_u.dirty_list[i].mid = 0;
       }
       txn->mt_u.dirty_list[0].mid = 0;

       if ((n = mdb_env_sync(env, 0)) != 0 ||
           (n = mdb_env_write_meta(txn)) != MDB_SUCCESS) {
              mdb_txn_abort(txn);
              return n;
       }
       env->me_wtxnid = txn->mt_txnid;

done:
       env->me_txn = NULL;
       /* update the DB tables */
       {
              int toggle = !env->me_db_toggle;
              MDB_db *ip, *jp;
              MDB_dbi i;

              ip = &env->me_dbs[toggle][2];
              jp = &txn->mt_dbs[2];
              LAZY_RWLOCK_WRLOCK(&env->me_dblock);
              for (i = 2; i < txn->mt_numdbs; i++) {
                     if (ip->md_root != jp->md_root)
                            *ip = *jp;
                     ip++; jp++;
              }

              env->me_db_toggle = toggle;
              env->me_numdbs = txn->mt_numdbs;
              LAZY_RWLOCK_UNLOCK(&env->me_dblock);
       }

       UNLOCK_MUTEX_W(env);
       free(txn);

       return MDB_SUCCESS;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Renew a read-only transaction.

This acquires a new reader lock for a transaction handle that had been released by mdb_txn_reset(). It must be called before a reset transaction may be used again.

Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()
Returns:
A non-zero error value on failure and 0 on success. Some possible errors are:
  • MDB_PANIC - a fatal error occurred earlier and the environment must be shut down.
  • EINVAL - an invalid parameter was specified.

Definition at line 1611 of file mdb.c.

{
       int rc;

       if (!txn)
              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 %zu%c %p on mdbenv %p, root page %zu",
                     txn->mt_txnid, (txn->mt_flags & MDB_TXN_RDONLY) ? 'r' : 'w',
                     (void *)txn, (void *)txn->mt_env, txn->mt_dbs[MAIN_DBI].md_root);
       }
       return rc;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Reset a read-only transaction.

This releases the current reader lock but doesn't free the transaction handle, allowing it to be used again later by mdb_txn_renew(). It otherwise has the same effect as mdb_txn_abort() but saves some memory allocation/deallocation overhead if a thread is going to start a new read-only transaction again soon. All cursors opened within the transaction must be closed before the transaction is reset. Reader locks generally don't interfere with writers, but they keep old versions of database pages allocated. Thus they prevent the old pages from being reused when writers commit new data, and so under heavy load the database size may grow much more rapidly than otherwise.

Parameters:
[in]txnA transaction handle returned by mdb_txn_begin()

Definition at line 1771 of file mdb.c.

{
       if (txn == NULL)
              return;

       DPRINTF("reset txn %zu%c %p on mdbenv %p, root page %zu",
              txn->mt_txnid, (txn->mt_flags & MDB_TXN_RDONLY) ? 'r' : 'w',
              (void *) txn, (void *)txn->mt_env, txn->mt_dbs[MAIN_DBI].md_root);

       mdb_txn_reset0(txn);
}

Here is the call graph for this function:

Here is the caller graph for this function:

char* mdb_version ( int major,
int minor,
int patch 
)

Return the mdb library version information.

Parameters:
[out]majorif non-NULL, the library major version number is copied here
[out]minorif non-NULL, the library minor version number is copied here
[out]patchif non-NULL, the library patch version number is copied here
Return values:
version stringThe library version as a string

Definition at line 1037 of file mdb.c.

{
       if (major) *major = MDB_VERSION_MAJOR;
       if (minor) *minor = MDB_VERSION_MINOR;
       if (patch) *patch = MDB_VERSION_PATCH;
       return MDB_VERSION_STRING;
}

Here is the caller graph for this function: