#include "boltdb.h" #include BoltDB::BoltDB() { } BoltDB* BoltDB::createFrom(QString filePath, bool readOnly, QString &errorOut) { QByteArray filePathBytes(filePath.toUtf8()); GoString filePathGS = Interop::toGoString_WeakRef(&filePathBytes); auto open_ret = ::Bolt_Open(readOnly, filePathGS); if (open_ret.r2 != 0) { errorOut = QString::fromUtf8(open_ret.r1, open_ret.r2); free(open_ret.r1); return nullptr; } BoltDB *ret = new BoltDB(); ret->gmsDbRef = open_ret.r0; return ret; } static const int ERROR_AND_STOP_CALLING = 100; static const int ERROR_AND_KEEP_CALLING = 101; static const int FINISHED_OK = 102; static const int REAL_MESSAGE = 103; static bool handleTriple(int r0, char* r1, int64_t r2, QString& errorOut) { if (r0 == ERROR_AND_STOP_CALLING) { errorOut = QString::fromUtf8(r1, r2); free(r1); return false; } else if (r0 == FINISHED_OK) { return true; } else { // ?? unreachable return false; } } bool BoltDB::addBucket(const QList& bucketPath, QByteArray bucketName, QString& errorOut) { GoSliceManagedWrapper browse(bucketPath); GoString bucketNameGS = Interop::toGoString_WeakRef(&bucketName); auto resp = ::Bolt_CreateBucket(this->gmsDbRef, browse.slice, bucketNameGS); return handleTriple(resp.r0, resp.r1, resp.r2, errorOut); } bool BoltDB::deleteBucket(const QList& bucketPath, QByteArray bucketName, QString& errorOut) { GoSliceManagedWrapper browse(bucketPath); GoString bucketNameGS = Interop::toGoString_WeakRef(&bucketName); auto resp = ::Bolt_DeleteBucket(this->gmsDbRef, browse.slice, bucketNameGS); return handleTriple(resp.r0, resp.r1, resp.r2, errorOut); } bool BoltDB::setItem(const QList& bucketPath, QByteArray keyName, QByteArray value, QString& errorOut) { GoSliceManagedWrapper browse(bucketPath); GoString keyNameGS = Interop::toGoString_WeakRef(&keyName); GoString valueGS = Interop::toGoString_WeakRef(&value); auto resp = ::Bolt_SetItem(this->gmsDbRef, browse.slice, keyNameGS, valueGS); return handleTriple(resp.r0, resp.r1, resp.r2, errorOut); } bool BoltDB::deleteItem(const QList& bucketPath, QByteArray keyName, QString& errorOut) { GoSliceManagedWrapper browse(bucketPath); GoString keyNameGS = Interop::toGoString_WeakRef(&keyName); auto resp = ::Bolt_DeleteItem(this->gmsDbRef, browse.slice, keyNameGS); return handleTriple(resp.r0, resp.r1, resp.r2, errorOut); } bool BoltDB::listBucketsAtRoot(QString& errorOut, NameReciever cb) { auto listJob = ::Bolt_ListBucketsAtRoot(this->gmsDbRef); return pumpNext(listJob, errorOut, cb); } bool BoltDB::listBuckets(const QList& bucketPath, QString& errorOut, NameReciever cb) { if (bucketPath.size() == 0) { return listBucketsAtRoot(errorOut, cb); } GoSliceManagedWrapper browse(bucketPath); auto listJob = ::Bolt_ListBuckets(this->gmsDbRef, browse.slice); return pumpNext(listJob, errorOut, cb); } bool BoltDB::listKeys(const QList& bucketPath, QString& errorOut, std::function cb) { GoSliceManagedWrapper browse(bucketPath); auto listJob = ::Bolt_ListItems(this->gmsDbRef, browse.slice); return pumpNext(listJob, errorOut, [=](QByteArray b) { // First 8 bytes are little-endian uint64 len int64_t dataLen = qFromLittleEndian(b.mid(0, 8)); cb(b.mid(8), dataLen); }); } bool BoltDB::getData(const QList& bucketPath, QByteArray key, std::function onSuccess, std::function onError) { GoSliceManagedWrapper browse(bucketPath); GoString keyGS = Interop::toGoString_WeakRef(&key); auto resp = ::Bolt_GetItem(this->gmsDbRef, browse.slice, keyGS); if (resp.r0 == ERROR_AND_STOP_CALLING) { onError(QString::fromUtf8(resp.r1, resp.r2)); free(resp.r1); return false; } else if (resp.r0 == REAL_MESSAGE) { onSuccess(QByteArray(resp.r1, resp.r2)); free(resp.r1); return true; } else { // ?? unreachable return false; } } bool BoltDB::pumpNext(GoInt64 jobRef, QString& errorOut, NameReciever cb) { errorOut.clear(); for(;;) { auto gnr = ::GetNext(jobRef); if (gnr.r0 == ERROR_AND_STOP_CALLING) { errorOut.append(QString::fromUtf8(gnr.r1, gnr.r2)); // log error free(gnr.r1); break; // done } else if (gnr.r0 == ERROR_AND_KEEP_CALLING) { errorOut.append(QString::fromUtf8(gnr.r1, gnr.r2)); // log error free(gnr.r1); continue; } else if (gnr.r0 == FINISHED_OK) { // Once we hit this, the go-side will clean up the channel / associated goroutines break; } else if (gnr.r0 == REAL_MESSAGE) { cb(QByteArray(gnr.r1, gnr.r2)); free(gnr.r1); continue; } } return (errorOut.length() == 0); } bool BoltDB::getStatsJSON(std::function onSuccess, std::function onError) { auto statresp = Bolt_DBStats(this->gmsDbRef); if (statresp.r0 == ERROR_AND_STOP_CALLING) { onError(QString::fromUtf8(statresp.r1, statresp.r2)); free(statresp.r1); return false; } else if (statresp.r0 == REAL_MESSAGE) { onSuccess(QByteArray(statresp.r1, statresp.r2)); free(statresp.r1); return true; } else { // ?? shouldn't be reachable return false; } } bool BoltDB::getBucketStatsJSON(const QList& bucketPath, std::function onSuccess, std::function onError) { GoSliceManagedWrapper sliceWrapper(bucketPath); auto statresp = Bolt_BucketStats(this->gmsDbRef, sliceWrapper.slice); if (statresp.r0 == ERROR_AND_STOP_CALLING) { QString err = QString::fromUtf8(statresp.r1, statresp.r2); free(statresp.r1); onError(err); return false; } else if (statresp.r0 == REAL_MESSAGE) { onSuccess(QByteArray(statresp.r1, statresp.r2)); free(statresp.r1); return true; } else { // ?? shouldn't be reachable return false; } } BoltDB::~BoltDB() { auto err = ::Bolt_Close(this->gmsDbRef); if (err.r1 != 0) { // Error closing database! // Need to display an alert... somewhere free(err.r0); } }