option to create new buckets / sub-buckets

This commit is contained in:
mappu 2017-05-21 17:08:15 +12:00
parent 4394c8a50a
commit 98b78ab1e6
8 changed files with 201 additions and 12 deletions

89
main.go
View File

@ -78,6 +78,79 @@ func withBrowse_ReadOnly(b_ref ObjectReference, browse []string, fn func(db *bol
}) })
} }
func err2triple(err error) (int64, *C.char, int) {
if err != nil {
msg := err.Error()
return ERROR_AND_STOP_CALLING, C.CString(msg), len(msg)
}
return FINISHED_OK, nil, 0
}
//export Bolt_CreateBucket
func Bolt_CreateBucket(b_ref ObjectReference, browse []string, newBucket string) (int64, *C.char, int) {
err := withBoltDBReference(b_ref, func(db *bolt.DB) error {
return db.Update(func(tx *bolt.Tx) error {
if len(browse) == 0 {
// Top-level bucket
_, err := tx.CreateBucket([]byte(newBucket))
return err
} else {
// Deeper bucket
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 create the new bucket
_, err := bucket.CreateBucket([]byte(newBucket))
return err
}
})
})
return err2triple(err)
}
//export Bolt_DeleteBucket
func Bolt_DeleteBucket(b_ref ObjectReference, browse []string, delBucket string) (int64, *C.char, int) {
err := withBoltDBReference(b_ref, func(db *bolt.DB) error {
return db.Update(func(tx *bolt.Tx) error {
if len(browse) == 0 {
// Top-level bucket
return tx.DeleteBucket([]byte(delBucket))
} else {
// Deeper bucket
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 delete the selected bucket
return bucket.DeleteBucket([]byte(delBucket))
}
})
})
return err2triple(err)
}
type CallResponse struct { type CallResponse struct {
s string s string
e error e error
@ -94,7 +167,7 @@ func Bolt_DBStats(b ObjectReference) (int64, *C.char, int) {
jBytes, err := json.Marshal(stats) jBytes, err := json.Marshal(stats)
if err != nil { if err != nil {
return ERROR_AND_STOP_CALLING, C.CString(err.Error()), len(err.Error()) return err2triple(err)
} }
return REAL_MESSAGE, C.CString(string(jBytes)), len(jBytes) return REAL_MESSAGE, C.CString(string(jBytes)), len(jBytes)
@ -110,12 +183,12 @@ func Bolt_BucketStats(b ObjectReference, browse []string) (int64, *C.char, int)
}) })
if err != nil { if err != nil {
return ERROR_AND_STOP_CALLING, C.CString(err.Error()), len(err.Error()) return err2triple(err)
} }
jBytes, err := json.Marshal(stats) jBytes, err := json.Marshal(stats)
if err != nil { if err != nil {
return ERROR_AND_STOP_CALLING, C.CString(err.Error()), len(err.Error()) return err2triple(err)
} }
return REAL_MESSAGE, C.CString(string(jBytes)), len(jBytes) return REAL_MESSAGE, C.CString(string(jBytes)), len(jBytes)
@ -224,7 +297,7 @@ func Bolt_GetItem(b ObjectReference, browse []string, key string) (int64, *C.cha
}) })
if err != nil { if err != nil {
return ERROR_AND_STOP_CALLING, C.CString(err.Error()), len(err.Error()) return err2triple(err)
} }
return REAL_MESSAGE, ret, ret_len return REAL_MESSAGE, ret, ret_len
@ -234,20 +307,18 @@ func Bolt_GetItem(b ObjectReference, browse []string, key string) (int64, *C.cha
func GetNext(oRef ObjectReference) (int64, *C.char, int) { func GetNext(oRef ObjectReference) (int64, *C.char, int) {
pNC_Iface, ok := gms.Get(oRef) pNC_Iface, ok := gms.Get(oRef)
if !ok { if !ok {
msg := NullObjectReference.Error() return err2triple(NullObjectReference)
return ERROR_AND_STOP_CALLING, C.CString(msg), len(msg)
} }
pNC, ok := pNC_Iface.(*NextCall) pNC, ok := pNC_Iface.(*NextCall)
if !ok { if !ok {
msg := NullObjectReference.Error() return err2triple(NullObjectReference)
return ERROR_AND_STOP_CALLING, C.CString(msg), len(msg)
} }
cr, ok := <-pNC.content cr, ok := <-pNC.content
if !ok { if !ok {
gms.Delete(oRef) gms.Delete(oRef)
return FINISHED_OK, nil, 0 return err2triple(nil)
} }
if cr.e != nil { if cr.e != nil {

View File

@ -29,6 +29,33 @@ static const int ERROR_AND_KEEP_CALLING = 101;
static const int FINISHED_OK = 102; static const int FINISHED_OK = 102;
static const int REAL_MESSAGE = 103; static const int REAL_MESSAGE = 103;
bool BoltDB::addBucket(QStringList bucketPath, QByteArray bucketName, QString& errorOut)
{
GoSliceManagedWrapper browse(&bucketPath);
GoString bucketNameGS = Interop::toGoString_WeakRef(&bucketName);
auto resp = ::Bolt_CreateBucket(this->gmsDbRef, browse.slice, bucketNameGS);
if (resp.r0 == ERROR_AND_STOP_CALLING) {
errorOut = QString::fromUtf8(resp.r1, resp.r2);
free(resp.r1);
return false;
} else if (resp.r0 == FINISHED_OK) {
return true;
} else {
// ?? unreachable
return false;
}
}
bool BoltDB::deleteBucket(QStringList bucketPath, QByteArray bucketName, QString& errorOut)
{
errorOut = "Not implemented";
return false; // not implemented
}
bool BoltDB::listBucketsAtRoot(QString& errorOut, NameReciever cb) bool BoltDB::listBucketsAtRoot(QString& errorOut, NameReciever cb)
{ {
auto listJob = ::Bolt_ListBucketsAtRoot(this->gmsDbRef); auto listJob = ::Bolt_ListBucketsAtRoot(this->gmsDbRef);

View File

@ -20,6 +20,10 @@ public:
bool listBuckets(QStringList bucketPath, QString& errorOut, NameReciever cb); bool listBuckets(QStringList bucketPath, QString& errorOut, NameReciever cb);
bool addBucket(QStringList bucketPath, QByteArray bucketName, QString& errorOut);
bool deleteBucket(QStringList bucketPath, QByteArray bucketName, QString& errorOut);
bool listKeys(QStringList bucketPath, QString& errorOut, std::function<void(QByteArray, int64_t)> cb); bool listKeys(QStringList bucketPath, QString& errorOut, std::function<void(QByteArray, int64_t)> cb);
bool getData(QStringList bucketPath, QByteArray key, std::function<void(QByteArray)> onSuccess, std::function<void(QString)> onError); bool getData(QStringList bucketPath, QByteArray key, std::function<void(QByteArray)> onSuccess, std::function<void(QString)> onError);

View File

@ -6,6 +6,7 @@
#include <QFileDialog> #include <QFileDialog>
#include <QMessageBox> #include <QMessageBox>
#include <QJsonDocument> #include <QJsonDocument>
#include <QInputDialog>
MainWindow::MainWindow(QWidget *parent) : MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
@ -17,10 +18,14 @@ MainWindow::MainWindow(QWidget *parent) :
databaseContext = new QMenu(); databaseContext = new QMenu();
databaseContext->addAction(ui->actionRefresh_buckets); databaseContext->addAction(ui->actionRefresh_buckets);
databaseContext->addAction(ui->actionAdd_bucket);
databaseContext->addSeparator();
databaseContext->addAction(ui->actionDisconnect); databaseContext->addAction(ui->actionDisconnect);
bucketContext = new QMenu(); bucketContext = new QMenu();
bucketContext->addAction(ui->actionRefresh_buckets); bucketContext->addAction(ui->actionRefresh_buckets);
bucketContext->addAction(ui->actionAdd_bucket);
bucketContext->addSeparator();
bucketContext->addAction(ui->actionDelete_bucket); bucketContext->addAction(ui->actionDelete_bucket);
} }
@ -302,3 +307,69 @@ void MainWindow::on_bucketData_doubleClicked(const QModelIndex &index)
); );
} }
void MainWindow::on_actionAdd_bucket_triggered()
{
QTreeWidgetItem* itm = lastContextSelection;
if (itm == nullptr) {
return;
}
QTreeWidgetItem* top = itm;
QStringList browse;
while(top->parent() != nullptr) {
browse.push_front(top->text(0));
top = top->parent();
}
// Get BDB
auto *bdb = GET_BDB(top);
// Prompt for bucket name
QString name = QInputDialog::getText(this, tr("New bucket"), tr("Enter a key for the new bucket:"));
if (name.length() == 0) {
return;
}
// Create
QString err;
if (! bdb->addBucket(browse, name.toUtf8(), err)) {
QMessageBox qmb;
qmb.setText(tr("Error creating bucket: %1").arg(err));
qmb.exec();
return;
}
// Refresh bucket list
refreshBucketTree(itm); // sub-tree only
ui->bucketTree->expandItem(itm);
}
void MainWindow::on_actionDelete_bucket_triggered()
{
QTreeWidgetItem* itm = lastContextSelection;
if (itm == nullptr) {
return;
}
QTreeWidgetItem* top = itm;
QStringList browse;
while(top->parent() != nullptr) {
browse.push_front(top->text(0));
top = top->parent();
}
// Get BDB
auto *bdb = GET_BDB(top);
// Prompt for confirmation
if (QMessageBox::question(this, tr("Delete bucket"), tr("Are you sure you want to remove the bucket '%s'?").arg(itm->text(0)), QMessageBox::Yes, QMessageBox::Cancel) != QMessageBox::Yes) {
return;
}
}

View File

@ -40,6 +40,10 @@ private slots:
void on_actionNew_database_triggered(); void on_actionNew_database_triggered();
void on_actionAdd_bucket_triggered();
void on_actionDelete_bucket_triggered();
protected: protected:
void openDatabase(QString file); void openDatabase(QString file);
void refreshBucketTree(QTreeWidgetItem* top); void refreshBucketTree(QTreeWidgetItem* top);

View File

@ -297,8 +297,9 @@
</property> </property>
</action> </action>
<action name="actionDelete_bucket"> <action name="actionDelete_bucket">
<property name="enabled"> <property name="icon">
<bool>false</bool> <iconset resource="resources.qrc">
<normaloff>:/rsrc/table_delete.png</normaloff>:/rsrc/table_delete.png</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>Delete bucket</string> <string>Delete bucket</string>
@ -320,7 +321,16 @@
<normaloff>:/rsrc/database_add.png</normaloff>:/rsrc/database_add.png</iconset> <normaloff>:/rsrc/database_add.png</normaloff>:/rsrc/database_add.png</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>New database...</string> <string>&amp;New database...</string>
</property>
</action>
<action name="actionAdd_bucket">
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/rsrc/table_add.png</normaloff>:/rsrc/table_add.png</iconset>
</property>
<property name="text">
<string>Add bucket...</string>
</property> </property>
</action> </action>
</widget> </widget>

View File

@ -5,5 +5,7 @@
<file>rsrc/information.png</file> <file>rsrc/information.png</file>
<file>rsrc/database_lightning.png</file> <file>rsrc/database_lightning.png</file>
<file>rsrc/database.png</file> <file>rsrc/database.png</file>
<file>rsrc/table_add.png</file>
<file>rsrc/table_delete.png</file>
</qresource> </qresource>
</RCC> </RCC>

BIN
sample.db

Binary file not shown.