Full_Name: Max Bolingbroke Version: LMDB HEAD OS: OS X URL: ftp://ftp.openldap.org/incoming/ Submission from: (NULL) (149.254.58.67)
It is possible to insert a (key, value) pair into an MDB_DUPSORTed LMDB database twice using mdb_cursor_put(..., MDB_CURRENT). If you attempt to insert the same (key, value) pair twice with mdb_put then the second insert has no effect. This seems like a bug in mdb_cursor_put.
Furthermore, you can use mdb_cursor_put(..., MDB_CURRENT) to insert duplicates in the opposite of their correctly sorted order. This is certainly a bug.
The following C program demonstrates both issues:
#define _XOPEN_SOURCE 500 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <stdarg.h> #include "lmdb.h"
void panic(const char * restrict format, ...) { va_list argp; va_start(argp, format); vprintf%foformat, argp); va_end(argp);
exit(1); }
void mdb_check(char *context, int rc) { if (rc != 0) { panic("%s: %s", context, mdb_strerror(rc)); } }
void mdb_check_notfound(char *context, int rc) { if (rc != MDB_NOTFOUND) { panic("%s: %s", context, mdb_strerror(rc)); } }
int BE(int x) { return (x & 0x000000FF) << 24 | (x & 0x0000FF00) << 8 | (x & 0x00FF0000) >> 8 | (x & 0xFF000000) >> 24; }
int main(int argc, char **argv) { srand(1337);
MDB_e * *env; mdb_check("mdb_env_create", mdb_env_create(&env)); mdb_check("mdb_env_set_maxdbs", mdb_env_set_maxdbs(env, 1)); mdb_check("mdb_env_set_mapsize", mdb_env_set_mapsize(env, 1024 * 1024)); mdb_check("mdb_env_open", mdb_env_open(env, "testdb", MDB_NOSUBDIR, 0644));
MDB_txn *txn; mdb_check("mdb_txn_begin", mdb_txn_begin(env, NULL, 0, &txn));
MDB_dbi dbi; mdb_check("mdb_dbi_open", mdb_dbi_open(txn, "root", MDB_CREATE | MDB_DUPSORT, &dbi));
int key; int value;
MDB_val keyBuffer; keyBuffer.mv_size = sizeof(int); keyBuffer.mv_data = &key;
MDB_val valueBuffer; valueBuffer.mv_size = sizeof(int); valueBuffer.mv_data = &value;
{ key = BE(2); value = BE(200); mdb_check("mdb_put", mdb_put(txn, dbi, &keyBuffer, &valueBuffer, 0));
key = BE(2); value = BE(300); mdb_check("mdb_put", mdb_put(txn, dbi, &keyBuffer, &valueBuffer, 0));
// Inserting the same (key, value) pair twice: second insert is ignored key = BE(2); value = BE(300); mdb_check("mdb_put", mdb_put(txn, dbi, &keyBuffer, &valueBuffer, 0)); }
{ MDB_cursor *cursor; mdb_check("mdb_cursor_open", mdb_cursor_open(txn, dbi, &cursor));
size_t count;
mdb_check("mdb_cursor_get", mdb_cursor_get(cursor, &keyBuffer, &valueBuffer, MDB_FIRST))0D0D mdb_check("mdb_cursor_count", mdb_cursor_count(cursor, &count)); if (count != 2) panic("count != 2");
if (0) { // Inserting the same (key, value) pair twice: second insert is not ignored. // While this is different to mdb_put, I can't tell from the docs whether this is a bug or not. keyBuffer.mv_size = sizeof(int); valueBuffer.mv_size = sizeof(int); keyBuffer.mv_data = &key; valueBuffer.mv_data = &value; key = BE(2); value = BE(300); mdb_check("mdb_cursor_put2,2C mdb_cursor_put(cursor, &keyBuffer, &valueBuffer, MDB_CURRENT)); mdb_check("mdb_cursor_count", mdb_cursor_count(cursor, &count)); if (count != 1) panic("count != 1"); } else { // This on the other hand is definitely a bug: after doing this the duplicates are not sorted. keyBuffer.mv_size = sizeof(int); valueBuffer.mv_size = sizeof(int); keyBuffer.mv_data = &key; valueBuffer.mv_data = &value; key = BE(2); value = BE(400); mdb_check("mdb_cursor_put", mdb_cursor_p(c8cursor, &keyBuffer, &valueBuffer, MDB_CURRENT)); mdb_check("mdb_cursor_count", mdb_cursor_count(cursor, &count)); if (count != 2) panic("count != 2");
mdb_check("mdb_cursor_get", mdb_cursor_get(cursor, &keyBuffer, &valueBuffer, MDB_FIRST)); printf("%d\n", BE(*((int*)valueBuffer.mv_data)));
mdb_check("mdb_cursor_get", mdb_cursor_get(cursor, &keyBuffer, &valueBuffer, MDB_NEXT)); printf("%d\n", BE(*((int*)valueBuffer.mv_data)));
mdb_check_notfound("mdb_cursor_get", mdb_cursor_get(cursor, &keyBuffer, &valueBuffer, MDB_NEXT)); }
mdb_cursor_close(cursor); } mdb_check("mdb_txn_commit", mdb_txn_commit(txn));
return 0; }