From 60d71104e8bdc15538da50e7fff8d3b4bb49404e Mon Sep 17 00:00:00 2001 From: mappu Date: Sun, 21 May 2017 12:39:55 +1200 Subject: [PATCH] retrieve bucket properties --- main.go | 49 ++++++++++++++++ qbolt.h | 9 +++ qbolt/boltdb.cpp | 23 ++++++++ qbolt/boltdb.h | 2 + qbolt/interop.cpp | 19 +++++++ qbolt/interop.h | 13 +++++ qbolt/mainwindow.cpp | 61 +++++++++++++++----- qbolt/mainwindow.ui | 132 +++++++++++++++++++++++++++---------------- 8 files changed, 246 insertions(+), 62 deletions(-) diff --git a/main.go b/main.go index 4d6222f..c3390b5 100644 --- a/main.go +++ b/main.go @@ -76,6 +76,34 @@ func withBoltDBReference(b ObjectReference, fn func(db *bolt.DB) error) error { return fn(ptrDB) } +func withBrowse_ReadOnly(b_ref ObjectReference, browse []string, fn func(db *bolt.DB, tx *bolt.Tx, bucket *bolt.Bucket) error) error { + if len(browse) == 0 { + // not a bucket + return errors.New("No bucket selected") + } + + return withBoltDBReference(b_ref, func(db *bolt.DB) error { + return db.View(func(tx *bolt.Tx) error { + + bucket := tx.Bucket([]byte(browse[0])) + if bucket == nil { + return errors.New("Unknown bucket") + } + + for i := 1; i < len(browse); i += 1 { + bucket = bucket.Bucket([]byte(browse[i])) + if bucket == nil { + return errors.New("Unknown bucket") + } + } + + // Walked the bucket chain, now run the user callback + return fn(db, tx, bucket) + + }) + }) +} + type CallResponse struct { s string e error @@ -98,6 +126,27 @@ func Bolt_DBStats(b ObjectReference) (int64, *C.char, int) { return REAL_MESSAGE, C.CString(string(jBytes)), len(jBytes) } +//export Bolt_BucketStats +func Bolt_BucketStats(b ObjectReference, browse []string) (int64, *C.char, int) { + var stats bolt.BucketStats + + err := withBrowse_ReadOnly(b, browse, func(db *bolt.DB, tx *bolt.Tx, bucket *bolt.Bucket) error { + stats = bucket.Stats() + return nil + }) + + if err != nil { + return ERROR_AND_STOP_CALLING, C.CString(err.Error()), len(err.Error()) + } + + jBytes, err := json.Marshal(stats) + if err != nil { + return ERROR_AND_STOP_CALLING, C.CString(err.Error()), len(err.Error()) + } + + return REAL_MESSAGE, C.CString(string(jBytes)), len(jBytes) +} + type NextCall struct { content chan CallResponse } diff --git a/qbolt.h b/qbolt.h index 2ef1c00..c4d6e1e 100644 --- a/qbolt.h +++ b/qbolt.h @@ -76,6 +76,15 @@ struct Bolt_DBStats_return { extern struct Bolt_DBStats_return Bolt_DBStats(GoInt64 p0); +/* Return type for Bolt_BucketStats */ +struct Bolt_BucketStats_return { + GoInt64 r0; + char* r1; + GoInt r2; +}; + +extern struct Bolt_BucketStats_return Bolt_BucketStats(GoInt64 p0, GoSlice p1); + extern GoInt64 Bolt_ListBuckets(GoInt64 p0, GoSlice p1); /* Return type for GetNext */ diff --git a/qbolt/boltdb.cpp b/qbolt/boltdb.cpp index 86386d6..a37cda8 100644 --- a/qbolt/boltdb.cpp +++ b/qbolt/boltdb.cpp @@ -84,6 +84,29 @@ bool BoltDB::getStatsJSON(std::function onSuccess, std::function< } } +bool BoltDB::getBucketStatsJSON(QStringList 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) { + QString json = QString::fromUtf8(statresp.r1, statresp.r2); + free(statresp.r1); + onSuccess(json); + return true; + + } else { + // ?? shouldn't be reachable + return false; + } +} + BoltDB::~BoltDB() { auto err = ::Bolt_Close(this->gmsDbRef); diff --git a/qbolt/boltdb.h b/qbolt/boltdb.h index 9c8de3d..379c4b6 100644 --- a/qbolt/boltdb.h +++ b/qbolt/boltdb.h @@ -20,6 +20,8 @@ public: bool getStatsJSON(std::function onSuccess, std::function onError); + bool getBucketStatsJSON(QStringList bucketPath, std::function onSuccess, std::function onError); + ~BoltDB(); }; diff --git a/qbolt/interop.cpp b/qbolt/interop.cpp index fe8da7c..bed064d 100644 --- a/qbolt/interop.cpp +++ b/qbolt/interop.cpp @@ -1,5 +1,7 @@ #include "interop.h" +#include + Interop::Interop() { @@ -12,3 +14,20 @@ GoString Interop::toGoString_WeakRef(QByteArray *qba) { int64_t Interop::GetMagic() { return ::GetMagic(); } + +// + +GoSliceManagedWrapper::GoSliceManagedWrapper(QStringList *qsl) : + rawStrings(), + strings(), + slice() +{ + for (int i = 0; i < qsl->size(); ++i) { + rawStrings.push_back( qsl->at(i).toUtf8() ); + strings.push_back(GoString{ rawStrings[i].data(), rawStrings[i].size() }); + } + + slice.data = static_cast( & strings.first() ); + slice.len = strings.size(); // * sizeof(GoString); + slice.cap = slice.len; +} diff --git a/qbolt/interop.h b/qbolt/interop.h index f637fbc..f34c04c 100644 --- a/qbolt/interop.h +++ b/qbolt/interop.h @@ -3,6 +3,19 @@ #include "../qbolt.h" #include +#include + +class GoSliceManagedWrapper { + Q_DISABLE_COPY(GoSliceManagedWrapper) + +public: + GoSliceManagedWrapper(QStringList *qsl); +protected: + QList rawStrings; + QList strings; +public: + GoSlice slice; +}; class Interop { diff --git a/qbolt/mainwindow.cpp b/qbolt/mainwindow.cpp index 20b011e..68add78 100644 --- a/qbolt/mainwindow.cpp +++ b/qbolt/mainwindow.cpp @@ -31,6 +31,15 @@ static const int BdbPointerRole = Qt::UserRole + 1; #define SET_BDB(top, bdb) top->setData(0, BdbPointerRole, QVariant::fromValue(static_cast(bdb))) #define GET_BDB(top) static_cast( top->data(0, BdbPointerRole).value() ) +/* +static BoltDB* GET_BDB(QTreeWidgetItem *top) { + while(top->parent() != nullptr) { + top = top->parent(); + } + + return static_cast( top->data(0, BdbPointerRole).value() ); +} +*/ void MainWindow::on_actionOpen_database_triggered() { @@ -152,32 +161,58 @@ void MainWindow::on_bucketTree_currentItemChanged(QTreeWidgetItem *current, QTre { Q_UNUSED(previous); if (current == nullptr) { - ui->tabWidget->setVisible(false); + ui->stackedWidget->setVisible(false); return; } - ui->tabWidget->setVisible(true); + ui->stackedWidget->setVisible(true); if (current->parent() == nullptr) { // Selected a database + ui->stackedWidget->setCurrentWidget(ui->databasePage); + auto *bdb = GET_BDB(current); - ui->propertiesArea->clear(); - bdb->getStatsJSON([=](QString j) { - ui->propertiesArea->setPlainText(j); - }, [=](QString error) { - QMessageBox qmb; - qmb.setText(tr("Error retrieving database statistics: %1").arg(error)); - qmb.exec(); - }); + bdb->getStatsJSON( + [=](QString j) { + ui->databasePropertiesArea->clear(); + ui->databasePropertiesArea->setPlainText(j); + }, + [=](QString error) { + QMessageBox qmb; + qmb.setText(tr("Error retrieving database statistics: %1").arg(error)); + qmb.exec(); + } + ); } else { // Selected a bucket + + ui->stackedWidget->setCurrentWidget(ui->bucketPage); + + QStringList browse; + QTreeWidgetItem *top = current; + while (top->parent() != nullptr) { + browse.push_front(top->text(0)); + top = top->parent(); + } + auto *bdb = GET_BDB(top); + + bdb->getBucketStatsJSON( + browse, + [=](QString j) { + ui->bucketPropertiesArea->clear(); + ui->bucketPropertiesArea->setPlainText(j); + }, + [=](QString error) { + QMessageBox qmb; + qmb.setText(tr("Error retrieving bucket statistics: %1").arg(error)); + qmb.exec(); + } + ); + // Load the data tab // TODO - - ui->propertiesArea->clear(); } - // refresh properties for the currently selected tree item... } void MainWindow::on_actionClear_selection_triggered() diff --git a/qbolt/mainwindow.ui b/qbolt/mainwindow.ui index 54c52d4..b5afda7 100644 --- a/qbolt/mainwindow.ui +++ b/qbolt/mainwindow.ui @@ -52,81 +52,115 @@ - - - 0 - - - - Properties - - + + + - 3 + 0 - 3 + 0 - 3 + 0 - 3 + 0 - - - QFrame::NoFrame - - - true - - - No selection + + + 0 + + + Database + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + QFrame::NoFrame + + + true + + + No selection + + + + + - - - Data - + - 3 + 0 - 3 + 0 - 3 + 0 - 3 + 0 - - - QFrame::NoFrame - - + + 0 - - false - - - true - - - false - - - - 1 - - + + + Bucket + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + QFrame::NoFrame + + + true + + + + + + + + Data + +