[ADD/WIP] Most DB operations for LMDB

Somewhat untested. I want to go on a test run soon.

Next up: aargh... listing... The one thing LMDB may suck at.
This commit is contained in:
LDA 2024-08-08 16:25:09 +02:00
parent 5cb51a4d58
commit 0743401955

View file

@ -68,6 +68,203 @@ LMDBKillKey(MDB_val key)
Free(key.mv_data);
}
static HashMap *
LMDBDecode(MDB_val val)
{
FILE *fakefile;
Stream *fakestream;
HashMap *ret;
if (!val.mv_data || !val.mv_size)
{
return NULL;
}
fakefile = fmemopen(val.mv_data, val.mv_size, "r");
fakestream = StreamFile(fakefile);
ret = JsonDecode(fakestream);
StreamClose(fakestream);
return ret;
}
static DbRef *
LMDBLock(Db *d, Array *k)
{
LMDB *db = (LMDB *) d;
MDB_txn *transaction;
LMDBRef *ret = NULL;
MDB_val key, empty_json;
MDB_dbi dbi;
int code;
if (!d || !k)
{
return NULL;
}
pthread_mutex_lock(&d->lock);
key = LMDBTranslateKey(k);
/* create a txn */
if ((code = mdb_txn_begin(db->environ, NULL, 0, &transaction)) != 0)
{
/* Very bad! */
Log(LOG_ERR,
"%s: could not begin transaction: %s",
__func__, mdb_strerror(code)
);
pthread_mutex_unlock(&d->lock);
goto end;
}
/* apparently you need to give it a dbi */
if ((code = mdb_dbi_open(transaction, "db", MDB_CREATE, &dbi)) != 0)
{
Log(LOG_ERR,
"%s: could not get transaction dbi: %s",
__func__, mdb_strerror(code)
);
pthread_mutex_unlock(&d->lock);
goto end;
}
empty_json.mv_size = 0;
empty_json.mv_data = NULL;
/* get data from it */
code = mdb_get(transaction, dbi, &key, &empty_json);
if (code == MDB_NOTFOUND)
{
Log(LOG_ERR,
"%s: mdb_get failure: %s",
__func__, mdb_strerror(code)
);
mdb_txn_abort(transaction);
mdb_dbi_close(db->environ, dbi);
goto end;
}
else if (code != 0)
{
Log(LOG_ERR,
"%s: mdb_get failure: %s",
__func__, mdb_strerror(code)
);
mdb_txn_abort(transaction);
mdb_dbi_close(db->environ, dbi);
goto end;
}
ret = Malloc(sizeof(*ret));
DbRefInit(d, (DbRef *) ret);
/* TODO: Timestamp */
{
size_t i;
ret->base.name = ArrayCreate();
for (i = 0; i < ArraySize(k); i++)
{
char *ent = ArrayGet(k, i);
StringArrayAppend(ret->base.name, ent);
}
}
ret->base.json = LMDBDecode(empty_json);
ret->transaction = transaction;
ret->dbi = dbi;
end:
LMDBKillKey(key);
return (DbRef *) ret;
}
static bool
LMDBExists(Db *d, Array *k)
{
MDB_val key, empty;
LMDB *db = (LMDB *) d;
MDB_txn *transaction;
MDB_dbi dbi;
int code;
bool ret = false;
if (!d || !k)
{
return false;
}
pthread_mutex_lock(&d->lock);
key = LMDBTranslateKey(k);
/* create a txn */
if ((code = mdb_txn_begin(db->environ, NULL, 0, &transaction)) != 0)
{
/* Very bad! */
Log(LOG_ERR,
"%s: could not begin transaction: %s",
__func__, mdb_strerror(code)
);
goto end;
}
/* apparently you need to give it a dbi */
if ((code = mdb_dbi_open(transaction, "db", MDB_CREATE, &dbi)) != 0)
{
Log(LOG_ERR,
"%s: could not get transaction dbi: %s",
__func__, mdb_strerror(code)
);
goto end;
}
ret = mdb_get(transaction, dbi, &key, &empty) == 0;
mdb_txn_abort(transaction);
mdb_dbi_close(db->environ, dbi);
end:
LMDBKillKey(key);
pthread_mutex_unlock(&d->lock);
return ret;
}
static bool
LMDBDelete(Db *d, Array *k)
{
MDB_val key, empty;
LMDB *db = (LMDB *) d;
MDB_txn *transaction;
MDB_dbi dbi;
int code;
bool ret = false;
if (!d || !k)
{
return false;
}
pthread_mutex_lock(&d->lock);
key = LMDBTranslateKey(k);
/* create a txn */
if ((code = mdb_txn_begin(db->environ, NULL, 0, &transaction)) != 0)
{
/* Very bad! */
Log(LOG_ERR,
"%s: could not begin transaction: %s",
__func__, mdb_strerror(code)
);
goto end;
}
/* apparently you need to give it a dbi */
if ((code = mdb_dbi_open(transaction, "db", MDB_CREATE, &dbi)) != 0)
{
Log(LOG_ERR,
"%s: could not get transaction dbi: %s",
__func__, mdb_strerror(code)
);
goto end;
}
ret = mdb_del(transaction, dbi, &key, &empty) == 0;
mdb_txn_commit(transaction);
mdb_dbi_close(db->environ, dbi);
end:
LMDBKillKey(key);
pthread_mutex_unlock(&d->lock);
return ret;
}
static bool
LMDBUnlock(Db *d, DbRef *r)
@ -256,13 +453,13 @@ DbOpenLMDB(char *dir, size_t size)
DbInit((Db *) db);
db->environ = env;
db->base.lockFunc = NULL;
db->base.lockFunc = LMDBLock;
db->base.create = LMDBCreate;
db->base.unlock = LMDBUnlock;
db->base.delete = NULL;
db->base.exists = NULL;
db->base.delete = LMDBDelete;
db->base.exists = LMDBExists;
db->base.close = LMDBClose;
db->base.list = NULL;
db->base.list = NULL; /* TODO: This one is gonna be Fun. */
return (Db *) db;
}