From 6c8712d804a48ddd6eb748a83ce1a4906f08ad5f Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Sun, 17 Mar 2013 01:38:48 +0100 Subject: [PATCH] Change the internal data type for cell contents to a binary type Store the data of a DB cell in a QByteArray, i.e. a binary data type, instead of putting it in a QString, thus converting it to a UTF8 string. Rewrite the reading and writing of DB cells to correctly handle binary data containing 0x00 bytes. Change the edit dialog to actually do all the data checks etc. on a currently invisible QHexEdit widget instead of a QTextEdit. All these changes combined make it possible to actually store binary data without it being corrupted. You can for example import pictures now, export them and actually open the exported file. So this is an improvement. --- src/EditDialog.cpp | 33 +++++++---- src/EditDialog.h | 6 +- src/ExportCsvDialog.cpp | 2 +- src/MainWindow.cpp | 19 +++--- src/MainWindow.h | 2 +- src/sqlitedb.cpp | 124 +++++++++++++++++++++------------------- src/sqlitedb.h | 4 +- 7 files changed, 104 insertions(+), 86 deletions(-) diff --git a/src/EditDialog.cpp b/src/EditDialog.cpp index ff4a0686..ec429a38 100644 --- a/src/EditDialog.cpp +++ b/src/EditDialog.cpp @@ -4,12 +4,17 @@ #include #include #include "sqlitedb.h" +#include EditDialog::EditDialog(QWidget* parent) : QDialog(parent), ui(new Ui::EditDialog) { ui->setupUi(this); + + hexEdit = new QHexEdit(this); + hexEdit->setVisible(false); + reset(); } @@ -24,6 +29,7 @@ void EditDialog::reset() curCol = -1; ui->editData->setPlainText(""); ui->editData->setFocus(); + hexEdit->setData(QByteArray()); setDataType(kSQLiteMediaType_Void, 0); } @@ -57,14 +63,15 @@ void EditDialog::closeEvent(QCloseEvent*) emit goingAway(); } -void EditDialog::loadText(const QString& text, int row, int col) +void EditDialog::loadText(const QByteArray& data, int row, int col) { - ui->editData->setPlainText(text); + ui->editData->setPlainText(data); ui->editData->setFocus(); ui->editData->selectAll(); + hexEdit->setData(data); curRow = row; curCol = col; - if(ui->editData->toPlainText().length() > 0) + if(hexEdit->data().length() > 0) setDataType(kSQLiteMediaType_String, 0); else setDataType(kSQLiteMediaType_Void, 0); @@ -84,11 +91,12 @@ void EditDialog::importData() QFile file(fileName); if(file.open(QIODevice::ReadOnly)) { - QTextStream stream(&file); - ui->editData->setPlainText(stream.readAll()); + QByteArray d = file.readAll(); + hexEdit->setData(d); + ui->editData->setPlainText(d); file.close(); } - setDataType(type, ui->editData->toPlainText().length()); + setDataType(type, hexEdit->data().length()); } } @@ -119,8 +127,7 @@ void EditDialog::exportData() QFile file(fileName); if(file.open(QIODevice::WriteOnly)) { - QTextStream stream(&file); - stream << ui->editData->toPlainText(); + file.write(hexEdit->data()); file.close(); } } @@ -134,13 +141,14 @@ void EditDialog::exportData() void EditDialog::clearData() { ui->editData->setPlainText(""); + hexEdit->setData(QByteArray()); setDataType(kSQLiteMediaType_Void, 0); } void EditDialog::accept() { if(dataType == kSQLiteMediaType_String) - emit updateRecordText(curRow, curCol, ui->editData->toPlainText()); + emit updateRecordText(curRow, curCol, hexEdit->data()); if (dataType == kSQLiteMediaType_Void) emit updateRecordText(curRow, curCol, ""); @@ -150,8 +158,11 @@ void EditDialog::accept() void EditDialog::editTextChanged() { + if(ui->editData->hasFocus()) + hexEdit->setData(ui->editData->toPlainText().toUtf8()); + int newtype = kSQLiteMediaType_String; - if(ui->editData->toPlainText().length() == 0) + if(hexEdit->data().length() == 0) newtype = kSQLiteMediaType_Void; - setDataType(newtype, ui->editData->toPlainText().length()); + setDataType(newtype, hexEdit->data().length()); } diff --git a/src/EditDialog.h b/src/EditDialog.h index aa8bff2b..4a1f1e84 100644 --- a/src/EditDialog.h +++ b/src/EditDialog.h @@ -2,6 +2,7 @@ #define __EDITDIALOG_H__ #include +class QHexEdit; namespace Ui { class EditDialog; @@ -23,7 +24,7 @@ public: public slots: virtual void reset(); - virtual void loadText(const QString& text, int row, int col); + virtual void loadText(const QByteArray& data, int row, int col); private slots: virtual void enableExport(bool enabled); @@ -37,10 +38,11 @@ private slots: signals: void goingAway(); - void updateRecordText(int, int, const QString&); + void updateRecordText(int, int, const QByteArray&); private: Ui::EditDialog* ui; + QHexEdit* hexEdit; int dataType; int dataSize; int curCol; diff --git a/src/ExportCsvDialog.cpp b/src/ExportCsvDialog.cpp index 90a3d804..30cb29ba 100644 --- a/src/ExportCsvDialog.cpp +++ b/src/ExportCsvDialog.cpp @@ -73,7 +73,7 @@ void ExportCsvDialog::accept() rowList data = pdb->browseRecs; for(int i=0;i row = data[i]; for(int j=1;jdataTable->horizontalHeader(), SIGNAL(sectionClicked(int)), this, SLOT(browseTableHeaderClicked(int))); connect(ui->dataTable->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(setRecordsetLabel())); connect(editWin, SIGNAL(goingAway()), this, SLOT(editWinAway())); - connect(editWin, SIGNAL(updateRecordText(int, int , QString)), this, SLOT(updateRecordText(int, int , QString))); + connect(editWin, SIGNAL(updateRecordText(int, int, QByteArray)), this, SLOT(updateRecordText(int, int , QByteArray))); // Load window settings QSettings settings(QApplication::organizationName(), QApplication::organizationName()); @@ -449,10 +449,10 @@ void MainWindow::updateTableView(int lineToSelect, bool keepColumnWidths) rowLabel.setNum(rowNum+1); browseTableModel->setVerticalHeaderItem(rowNum, new QStandardItem(rowLabel)); colNum = 0; - QStringList& rt = tab[i]; + QList rt = tab[i]; for (int e = 1; e < rt.size(); ++e) { - QString& content = rt[e]; + QString content = rt[e]; QStandardItem* item = new QStandardItem(content); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); @@ -704,20 +704,19 @@ void MainWindow::helpAbout() dialog.exec(); } -void MainWindow::updateRecordText(int row, int col, const QString& newtext) +void MainWindow::updateRecordText(int row, int col, const QByteArray& newtext) { if (!db.updateRecord(row, col, newtext)){ QMessageBox::information( this, QApplication::applicationName(), tr("Data could not be updated")); } rowList tab = db.browseRecs; - QStringList& rt = tab[row]; - QString& cv = rt[col+1];//must account for rowid + QList& rt = tab[row]; + QByteArray& cv = rt[col+1];//must account for rowid - QStandardItem* item = new QStandardItem(cv); + QStandardItem* item = new QStandardItem(QString(cv)); item->setToolTip( wrapText(cv) ); browseTableModel->setItem(row, col, item); - } void MainWindow::editWinAway() @@ -730,8 +729,8 @@ void MainWindow::editWinAway() void MainWindow::editText(int row, int col) { rowList tab = db.browseRecs; - QStringList& rt = tab[row]; - QString cv = rt[col+1];//must account for rowid + QList rt = tab[row]; + QByteArray cv = rt[col+1];//must account for rowid editWin->loadText(cv , row, col); editWin->show(); diff --git a/src/MainWindow.h b/src/MainWindow.h index d8ed23a0..5f8d616c 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -127,7 +127,7 @@ private slots: virtual void editTable(); virtual void helpWhatsThis(); virtual void helpAbout(); - virtual void updateRecordText( int row, int col, const QString& newtext ); + virtual void updateRecordText(int row, int col, const QByteArray& newtext); virtual void editWinAway(); virtual void editText( int row, int col ); virtual void doubleClickTable(const QModelIndex& index); diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index 9bbda46a..671f79b1 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -386,8 +386,8 @@ bool DBBrowserDB::deleteRecord( int wrow) if (!isOpen()) return false; bool ok = false; rowList tab = browseRecs; - QStringList& rt = tab[wrow]; - QString& rowid = rt[0]; + QList rt = tab[wrow]; + QString rowid = rt[0]; lastErrorMessage = QString("no error"); QString statement = QString("DELETE FROM `%1` WHERE rowid=%2;").arg(curBrowseTableName).arg(rowid); @@ -406,45 +406,52 @@ bool DBBrowserDB::deleteRecord( int wrow) return ok; } -bool DBBrowserDB::updateRecord(int wrow, int wcol, const QString & wtext) +bool DBBrowserDB::updateRecord(int wrow, int wcol, const QByteArray& wtext) { - char * errmsg; if (!hasValidBrowseSet) return false; if (!isOpen()) return false; - bool ok = false; lastErrorMessage = QString("no error"); - QStringList& rt = browseRecs[wrow]; - QString& rowid = rt[0]; - QString& cv = rt[wcol+1];//must account for rowid + QList& rt = browseRecs[wrow]; + QString rowid = rt[0]; + QByteArray& cv = rt[wcol+1];//must account for rowid QString ct = browseFields.at(wcol); - QString statement = QString("UPDATE `%1` SET `%2`=").arg(curBrowseTableName).arg(ct); + QString sql = QString("UPDATE `%1` SET `%2`=? WHERE rowid=%4;").arg(curBrowseTableName).arg(ct).arg(rowid); - char * formSQL = sqlite3_mprintf("%Q", wtext.toUtf8().constData()); - statement.append(formSQL); - if (formSQL) sqlite3_free(formSQL); - - statement.append(" WHERE rowid="); - statement.append(rowid); - statement.append(";"); + logSQL(sql, kLogMsg_App); + setRestorePoint(); - if (_db){ - logSQL(statement, kLogMsg_App); - setRestorePoint(); - if (SQLITE_OK==sqlite3_exec(_db,statement.toUtf8(), - NULL,NULL,&errmsg)){ - ok=true; - /*update local copy*/ - cv = wtext; - } else { - lastErrorMessage = QString::fromUtf8(errmsg); - qCritical() << "update Record: " << lastErrorMessage; - } + sqlite3_stmt* stmt; + if(sqlite3_prepare(_db, sql.toUtf8(), -1, &stmt, 0) != SQLITE_OK) + { + lastErrorMessage = sqlite3_errmsg(_db); + qCritical() << "updateRecord: " << lastErrorMessage; + return false; + } + if(sqlite3_bind_blob(stmt, 1, wtext.constData(), wtext.length(), SQLITE_STATIC) != SQLITE_OK) + { + lastErrorMessage = sqlite3_errmsg(_db); + qCritical() << "updateRecord: " << lastErrorMessage; + return false; + } + if(sqlite3_step(stmt) != SQLITE_DONE) + { + lastErrorMessage = sqlite3_errmsg(_db); + qCritical() << "updateRecord: " << lastErrorMessage; + return false; + } + if(sqlite3_finalize(stmt) != SQLITE_OK) + { + lastErrorMessage = sqlite3_errmsg(_db); + qCritical() << "updateRecord: " << lastErrorMessage; + return false; } - return ok; + cv = wtext; + + return true; } bool DBBrowserDB::browseTable( const QString & tablename, const QString& orderby ) @@ -664,44 +671,43 @@ bool DBBrowserDB::renameTable(const QString& from_table, const QString& to_table void DBBrowserDB::getTableRecords( const QString & tablename, const QString& orderby ) { - sqlite3_stmt *vm; - const char *tail; + sqlite3_stmt* stmt; int ncol; - QStringList r; - // char *errmsg; - int err=0; - // int tabnum = 0; + QList r; browseRecs.clear(); idmap.clear(); lastErrorMessage = QObject::tr("no error"); - QString statement = QString("SELECT rowid, * FROM `%1` ORDER BY %2;").arg(tablename).arg(orderby); - logSQL(statement, kLogMsg_App); - QByteArray utf8Statement = statement.toUtf8(); - err=sqlite3_prepare(_db,utf8Statement,utf8Statement.length(), - &vm, &tail); - if (err == SQLITE_OK){ - int rownum = 0; - - while ( sqlite3_step(vm) == SQLITE_ROW ){ - r.clear(); - ncol = sqlite3_data_count(vm); - for (int e=0; e(sqlite3_column_blob(stmt, e)), sqlite3_column_bytes(stmt, e)); + r.append(rv); + + if(e == 0) + { + idmap.insert(rv.toInt(), rownum); + rownum++; + } + } + browseRecs.append(r); + } + + sqlite3_finalize(stmt); } resultMap DBBrowserDB::getFindResults( const QString & wstatement) diff --git a/src/sqlitedb.h b/src/sqlitedb.h index a085361f..659cc17e 100644 --- a/src/sqlitedb.h +++ b/src/sqlitedb.h @@ -26,7 +26,7 @@ typedef QMap fieldMap; typedef QMultiMap objectMap; typedef QMap rowIdMap; -typedef QList rowList; +typedef QList > rowList; typedef QMap resultMap; class DBBrowserField @@ -97,7 +97,7 @@ public: void updateSchema() ; bool addRecord(); bool deleteRecord(int wrow); - bool updateRecord(int wrow, int wcol, const QString & wtext); + bool updateRecord(int wrow, int wcol, const QByteArray& wtext); bool browseTable( const QString & tablename, const QString& orderby = "rowid" ); bool createTable(const QString& name, const QList& structure);