db_t db_init(const size_t size, const char * const path, const int flags, const int mode, int mdb_flags, int ndbi, dbi_t *dbis, size_t padsize){ int fd = -1, err = 0xdeadbeef; struct stat st; db_t db; db = malloc(size < sizeof(*db) ? sizeof(*db) : size); assert(db); // always do this mdb_flags |= MDB_NOSUBDIR; int r; // ignore MDB_RDONLY - key off of OS flags // see docs for open() - O_RDONLY is not a bit! if((flags & 0x3) == O_RDONLY){ mdb_flags |= MDB_RDONLY; // disable NOSYNC/NOMETASYNC for readonly, as that burns another file descriptor mdb_flags &= ~(MDB_NOSYNC|MDB_NOMETASYNC); }else{ mdb_flags &= ~MDB_RDONLY; } // default to 100mb minimum pad db->padsize = padsize ? padsize : (1<<27); db->env = NULL; db->txns = 0; db->handles = NULL; db->updated = 0; r = pthread_mutex_init(&db->mutex, NULL); FAIL(r, err, errno, fail); r = pthread_cond_init(&db->cond, NULL); FAIL(r, err, errno, fail); // unless MDB_RDONLY is specified, lmdb will automatically create non-existant databases, // which is not what I want. Try to emulate standard unix open() flags: fd = open(path, flags, mode); FAIL(-1 == fd, err, errno, fail); r = mdb_env_create(&db->env); FAIL(r, err, r, fail); r = mdb_env_set_maxdbs(db->env, ndbi); FAIL(r, err, r, fail); size_t mapsize; do{ r = fstat(fd, &st); FAIL(r, err, errno, fail); if(!st.st_size &&( flags & O_CREAT)) db->updated = 1; // pad out such that we have at least 1gb of map overhead mapsize = (1 + st.st_size / db->padsize) * db->padsize; r = mdb_env_set_mapsize(db->env, mapsize); }while(MDB_SUCCESS != r); close(fd); fd = -1; r = mdb_env_open(db->env, path, mdb_flags, mode); // mdb_env_open can return EAGAIN somehow, but I think it really means: if(EAGAIN == r) r = EMFILE; FAIL(r, err, r, fail); r = mdb_env_get_fd(db->env, &db->fd); FAIL(r, err, r, fail); if(dbis && ndbi){ db->handles = malloc(sizeof(db->handles[0]) * ndbi); FAIL(!db->handles, err, errno, fail); // open the indexes - try read-only first unsigned int txn_flags = MDB_RDONLY; txn_t txn = db_txn_new(db, NULL, txn_flags); int i; for(i = 0; i < ndbi; i++){ int dbflags = dbis[i].flags; retry: dbflags = (txn_flags & MDB_RDONLY) ? (dbflags & ~MDB_CREATE) : (dbflags | MDB_CREATE); r = mdb_dbi_open(txn->txn, dbis[i].name, dbflags, &db->handles[i]); if(MDB_SUCCESS != r){ if(MDB_NOTFOUND == r && (txn_flags & MDB_RDONLY)){ // we were in read-only and a sub-db was missing // end txn txn_commit(txn); // switch to read-write txn_flags &= ~MDB_RDONLY; txn = db_txn_new(db, NULL, txn_flags); // and pick up where we left off goto retry; }else{ txn_abort(txn); } FAIL(r, err, r, fail); } } r = txn_commit(txn); FAIL(r, err, r, fail); } return db; fail: db_close(db); if(fd != -1){ if((flags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) unlink(path); close(fd); } errno = err; return NULL; }
int slmdb_open(SLMDB *slmdb, const char *path, int open_flags, int lmdb_flags, int slmdb_flags) { struct stat st; MDB_env *env; MDB_txn *txn; MDB_dbi dbi; int db_fd; int status; /* * Create LMDB environment. */ if ((status = mdb_env_create(&env)) != 0) return (status); /* * Make sure that the memory map has room to store and commit an initial * "drop" transaction as well as fixed database metadata. We have no way * to recover from errors before the first application-level I/O request. */ #define SLMDB_FUDGE 10240 if (slmdb->curr_limit < SLMDB_FUDGE) slmdb->curr_limit = SLMDB_FUDGE; if (stat(path, &st) == 0 && st.st_size > slmdb->curr_limit - SLMDB_FUDGE) { if (st.st_size > slmdb->hard_limit) slmdb->hard_limit = st.st_size; if (st.st_size < slmdb->hard_limit - SLMDB_FUDGE) slmdb->curr_limit = st.st_size + SLMDB_FUDGE; else slmdb->curr_limit = slmdb->hard_limit; } /* * mdb_open() requires a txn, but since the default DB always exists in * an LMDB environment, we usually don't need to do anything else with * the txn. It is currently used for truncate and for bulk transactions. */ if ((status = mdb_env_set_mapsize(env, slmdb->curr_limit)) != 0 || (status = mdb_env_open(env, path, lmdb_flags, 0644)) != 0 || (status = mdb_txn_begin(env, (MDB_txn *) 0, lmdb_flags & MDB_RDONLY, &txn)) != 0 || (status = mdb_open(txn, (const char *) 0, 0, &dbi)) != 0 || (status = mdb_env_get_fd(env, &db_fd)) != 0) { mdb_env_close(env); return (status); } /* * Bundle up. */ slmdb->open_flags = open_flags; slmdb->lmdb_flags = lmdb_flags; slmdb->slmdb_flags = slmdb_flags; slmdb->env = env; slmdb->dbi = dbi; slmdb->db_fd = db_fd; slmdb->cursor = 0; slmdb_saved_key_init(slmdb); slmdb->api_retry_count = 0; slmdb->bulk_retry_count = 0; slmdb->api_retry_limit = SLMDB_DEF_API_RETRY_LIMIT; slmdb->bulk_retry_limit = SLMDB_DEF_BULK_RETRY_LIMIT; slmdb->longjmp_fn = 0; slmdb->notify_fn = 0; slmdb->assert_fn = 0; slmdb->cb_context = 0; slmdb->txn = txn; if ((status = slmdb_prepare(slmdb)) != 0) mdb_env_close(env); return (status); }