| |
| |
| #include "lsmtest.h" |
| #include <stdlib.h> |
| |
| #ifdef HAVE_KYOTOCABINET |
| #include "kcpolydb.h" |
| extern "C" { |
| struct KcDb { |
| TestDb base; |
| kyotocabinet::TreeDB* db; |
| char *pVal; |
| }; |
| } |
| |
| int test_kc_open(const char *zFilename, int bClear, TestDb **ppDb){ |
| KcDb *pKcDb; |
| int ok; |
| int rc = 0; |
| |
| if( bClear ){ |
| char *zCmd = sqlite3_mprintf("rm -rf %s\n", zFilename); |
| system(zCmd); |
| sqlite3_free(zCmd); |
| } |
| |
| pKcDb = (KcDb *)malloc(sizeof(KcDb)); |
| memset(pKcDb, 0, sizeof(KcDb)); |
| |
| |
| pKcDb->db = new kyotocabinet::TreeDB(); |
| pKcDb->db->tune_page(TESTDB_DEFAULT_PAGE_SIZE); |
| pKcDb->db->tune_page_cache( |
| TESTDB_DEFAULT_PAGE_SIZE * TESTDB_DEFAULT_CACHE_SIZE |
| ); |
| ok = pKcDb->db->open(zFilename, |
| kyotocabinet::PolyDB::OWRITER | kyotocabinet::PolyDB::OCREATE |
| ); |
| if( ok==0 ){ |
| free(pKcDb); |
| pKcDb = 0; |
| rc = 1; |
| } |
| |
| *ppDb = (TestDb *)pKcDb; |
| return rc; |
| } |
| |
| int test_kc_close(TestDb *pDb){ |
| KcDb *pKcDb = (KcDb *)pDb; |
| if( pKcDb->pVal ){ |
| delete [] pKcDb->pVal; |
| } |
| pKcDb->db->close(); |
| delete pKcDb->db; |
| free(pKcDb); |
| return 0; |
| } |
| |
| int test_kc_write(TestDb *pDb, void *pKey, int nKey, void *pVal, int nVal){ |
| KcDb *pKcDb = (KcDb *)pDb; |
| int ok; |
| |
| ok = pKcDb->db->set((const char *)pKey, nKey, (const char *)pVal, nVal); |
| return (ok ? 0 : 1); |
| } |
| |
| int test_kc_delete(TestDb *pDb, void *pKey, int nKey){ |
| KcDb *pKcDb = (KcDb *)pDb; |
| int ok; |
| |
| ok = pKcDb->db->remove((const char *)pKey, nKey); |
| return (ok ? 0 : 1); |
| } |
| |
| int test_kc_delete_range( |
| TestDb *pDb, |
| void *pKey1, int nKey1, |
| void *pKey2, int nKey2 |
| ){ |
| int res; |
| KcDb *pKcDb = (KcDb *)pDb; |
| kyotocabinet::DB::Cursor* pCur = pKcDb->db->cursor(); |
| |
| if( pKey1 ){ |
| res = pCur->jump((const char *)pKey1, nKey1); |
| }else{ |
| res = pCur->jump(); |
| } |
| |
| while( 1 ){ |
| const char *pKey; size_t nKey; |
| const char *pVal; size_t nVal; |
| |
| pKey = pCur->get(&nKey, &pVal, &nVal); |
| if( pKey==0 ) break; |
| |
| #ifndef NDEBUG |
| if( pKey1 ){ |
| res = memcmp(pKey, pKey1, MIN((size_t)nKey1, nKey)); |
| assert( res>0 || (res==0 && nKey>nKey1) ); |
| } |
| #endif |
| |
| if( pKey2 ){ |
| res = memcmp(pKey, pKey2, MIN((size_t)nKey2, nKey)); |
| if( res>0 || (res==0 && (size_t)nKey2<nKey) ){ |
| delete [] pKey; |
| break; |
| } |
| } |
| pCur->remove(); |
| delete [] pKey; |
| } |
| |
| delete pCur; |
| return 0; |
| } |
| |
| int test_kc_fetch( |
| TestDb *pDb, |
| void *pKey, |
| int nKey, |
| void **ppVal, |
| int *pnVal |
| ){ |
| KcDb *pKcDb = (KcDb *)pDb; |
| size_t nVal; |
| |
| if( pKcDb->pVal ){ |
| delete [] pKcDb->pVal; |
| pKcDb->pVal = 0; |
| } |
| |
| pKcDb->pVal = pKcDb->db->get((const char *)pKey, nKey, &nVal); |
| if( pKcDb->pVal ){ |
| *ppVal = pKcDb->pVal; |
| *pnVal = nVal; |
| }else{ |
| *ppVal = 0; |
| *pnVal = -1; |
| } |
| |
| return 0; |
| } |
| |
| int test_kc_scan( |
| TestDb *pDb, /* Database handle */ |
| void *pCtx, /* Context pointer to pass to xCallback */ |
| int bReverse, /* True for a reverse order scan */ |
| void *pKey1, int nKey1, /* Start of search */ |
| void *pKey2, int nKey2, /* End of search */ |
| void (*xCallback)(void *pCtx, void *pKey, int nKey, void *pVal, int nVal) |
| ){ |
| KcDb *pKcDb = (KcDb *)pDb; |
| kyotocabinet::DB::Cursor* pCur = pKcDb->db->cursor(); |
| int res; |
| |
| if( bReverse==0 ){ |
| if( pKey1 ){ |
| res = pCur->jump((const char *)pKey1, nKey1); |
| }else{ |
| res = pCur->jump(); |
| } |
| }else{ |
| if( pKey2 ){ |
| res = pCur->jump_back((const char *)pKey2, nKey2); |
| }else{ |
| res = pCur->jump_back(); |
| } |
| } |
| |
| while( res ){ |
| const char *pKey; size_t nKey; |
| const char *pVal; size_t nVal; |
| pKey = pCur->get(&nKey, &pVal, &nVal); |
| |
| if( bReverse==0 && pKey2 ){ |
| res = memcmp(pKey, pKey2, MIN((size_t)nKey2, nKey)); |
| if( res>0 || (res==0 && (size_t)nKey2<nKey) ){ |
| delete [] pKey; |
| break; |
| } |
| }else if( bReverse!=0 && pKey1 ){ |
| res = memcmp(pKey, pKey1, MIN((size_t)nKey1, nKey)); |
| if( res<0 || (res==0 && (size_t)nKey1>nKey) ){ |
| delete [] pKey; |
| break; |
| } |
| } |
| |
| xCallback(pCtx, (void *)pKey, (int)nKey, (void *)pVal, (int)nVal); |
| delete [] pKey; |
| |
| if( bReverse ){ |
| res = pCur->step_back(); |
| }else{ |
| res = pCur->step(); |
| } |
| } |
| |
| delete pCur; |
| return 0; |
| } |
| #endif /* HAVE_KYOTOCABINET */ |
| |
| #ifdef HAVE_MDB |
| #include "lmdb.h" |
| |
| extern "C" { |
| struct MdbDb { |
| TestDb base; |
| MDB_env *env; |
| MDB_dbi dbi; |
| }; |
| } |
| |
| int test_mdb_open( |
| const char *zSpec, |
| const char *zFilename, |
| int bClear, |
| TestDb **ppDb |
| ){ |
| MDB_txn *txn; |
| MdbDb *pMdb; |
| int rc; |
| |
| if( bClear ){ |
| char *zCmd = sqlite3_mprintf("rm -rf %s\n", zFilename); |
| system(zCmd); |
| sqlite3_free(zCmd); |
| } |
| |
| pMdb = (MdbDb *)malloc(sizeof(MdbDb)); |
| memset(pMdb, 0, sizeof(MdbDb)); |
| |
| rc = mdb_env_create(&pMdb->env); |
| if( rc==0 ) rc = mdb_env_set_mapsize(pMdb->env, 1*1024*1024*1024); |
| if( rc==0 ) rc = mdb_env_open(pMdb->env, zFilename, MDB_NOSYNC|MDB_NOSUBDIR, 0600); |
| if( rc==0 ) rc = mdb_txn_begin(pMdb->env, NULL, 0, &txn); |
| if( rc==0 ){ |
| rc = mdb_open(txn, NULL, 0, &pMdb->dbi); |
| mdb_txn_commit(txn); |
| } |
| |
| *ppDb = (TestDb *)pMdb; |
| return rc; |
| } |
| |
| int test_mdb_close(TestDb *pDb){ |
| MdbDb *pMdb = (MdbDb *)pDb; |
| |
| mdb_close(pMdb->env, pMdb->dbi); |
| mdb_env_close(pMdb->env); |
| free(pMdb); |
| return 0; |
| } |
| |
| int test_mdb_write(TestDb *pDb, void *pKey, int nKey, void *pVal, int nVal){ |
| int rc; |
| MdbDb *pMdb = (MdbDb *)pDb; |
| MDB_val val; |
| MDB_val key; |
| MDB_txn *txn; |
| |
| val.mv_size = nVal; |
| val.mv_data = pVal; |
| key.mv_size = nKey; |
| key.mv_data = pKey; |
| |
| rc = mdb_txn_begin(pMdb->env, NULL, 0, &txn); |
| if( rc==0 ){ |
| rc = mdb_put(txn, pMdb->dbi, &key, &val, 0); |
| if( rc==0 ){ |
| rc = mdb_txn_commit(txn); |
| }else{ |
| mdb_txn_abort(txn); |
| } |
| } |
| |
| return rc; |
| } |
| |
| int test_mdb_delete(TestDb *pDb, void *pKey, int nKey){ |
| int rc; |
| MdbDb *pMdb = (MdbDb *)pDb; |
| MDB_val key; |
| MDB_txn *txn; |
| |
| key.mv_size = nKey; |
| key.mv_data = pKey; |
| rc = mdb_txn_begin(pMdb->env, NULL, 0, &txn); |
| if( rc==0 ){ |
| rc = mdb_del(txn, pMdb->dbi, &key, 0); |
| if( rc==0 ){ |
| rc = mdb_txn_commit(txn); |
| }else{ |
| mdb_txn_abort(txn); |
| } |
| } |
| |
| return rc; |
| } |
| |
| int test_mdb_fetch( |
| TestDb *pDb, |
| void *pKey, |
| int nKey, |
| void **ppVal, |
| int *pnVal |
| ){ |
| int rc; |
| MdbDb *pMdb = (MdbDb *)pDb; |
| MDB_val key; |
| MDB_txn *txn; |
| |
| key.mv_size = nKey; |
| key.mv_data = pKey; |
| |
| rc = mdb_txn_begin(pMdb->env, NULL, MDB_RDONLY, &txn); |
| if( rc==0 ){ |
| MDB_val val = {0, 0}; |
| rc = mdb_get(txn, pMdb->dbi, &key, &val); |
| if( rc==MDB_NOTFOUND ){ |
| rc = 0; |
| *ppVal = 0; |
| *pnVal = -1; |
| }else{ |
| *ppVal = val.mv_data; |
| *pnVal = val.mv_size; |
| } |
| mdb_txn_commit(txn); |
| } |
| |
| return rc; |
| } |
| |
| int test_mdb_scan( |
| TestDb *pDb, /* Database handle */ |
| void *pCtx, /* Context pointer to pass to xCallback */ |
| int bReverse, /* True for a reverse order scan */ |
| void *pKey1, int nKey1, /* Start of search */ |
| void *pKey2, int nKey2, /* End of search */ |
| void (*xCallback)(void *pCtx, void *pKey, int nKey, void *pVal, int nVal) |
| ){ |
| MdbDb *pMdb = (MdbDb *)pDb; |
| int rc; |
| MDB_cursor_op op = bReverse ? MDB_PREV : MDB_NEXT; |
| MDB_txn *txn; |
| |
| rc = mdb_txn_begin(pMdb->env, NULL, MDB_RDONLY, &txn); |
| if( rc==0 ){ |
| MDB_cursor *csr; |
| MDB_val key = {0, 0}; |
| MDB_val val = {0, 0}; |
| |
| rc = mdb_cursor_open(txn, pMdb->dbi, &csr); |
| if( rc==0 ){ |
| while( mdb_cursor_get(csr, &key, &val, op)==0 ){ |
| xCallback(pCtx, key.mv_data, key.mv_size, val.mv_data, val.mv_size); |
| } |
| mdb_cursor_close(csr); |
| } |
| } |
| |
| return rc; |
| } |
| |
| #endif /* HAVE_MDB */ |
| |