Hello community, here is the log from the commit of package sqlitebrowser for openSUSE:Factory checked in at 2018-06-29 22:37:01 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/sqlitebrowser (Old) and /work/SRC/openSUSE:Factory/.sqlitebrowser.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "sqlitebrowser" Fri Jun 29 22:37:01 2018 rev:10 rq:619772 version:3.10.1 Changes: -------- --- /work/SRC/openSUSE:Factory/sqlitebrowser/sqlitebrowser.changes 2017-09-04 12:30:36.955654839 +0200 +++ /work/SRC/openSUSE:Factory/.sqlitebrowser.new/sqlitebrowser.changes 2018-06-29 22:37:05.769965586 +0200 @@ -1,0 +2,29 @@ +Fri Jun 29 11:00:50 UTC 2018 - wba...@tmo.at + +- update to version 3.10.1 + * Bug fixes + - General + + cipher: Fix passphrases containing single quotes + + cipher: Change tab order in encryption dialog + + Fix typo in Russian translation + + Pass /norestart to vcredist installer + + Fix index updating when removing an indexed column from a table + + Don't automatically commit all changes when editing a table column + + Make text selectable in Edit dock even if db is opened as read only + + Add '<>NULL' filter + + Fix custom type saving when only focus changes for user-entered type + - DBHub.io related + + dbhub: Tweak certificate UI in the preferences dialog + + dbhub: Fix progress dialog for very large files + + dbhub: Remove closing "." from the progress dialog + + dbhub: Ask user what to do when trying to open an updated database + + dbhub: Enforce name and commit message length limits in push dialog + + dbhub: Add tooltip to cog tool button + + dbhub: Redownload database if local copy has been deleted + + dbhub: Fix wrong file size being shown for very large files + + dbhub: Support pushing to different branches than "master" + + dbhub: Improve file size format + + dbhub: Optimise code + + dbhub: Fix branch list in push dialog + +------------------------------------------------------------------- Old: ---- sqlitebrowser-3.10.0.tar.gz New: ---- sqlitebrowser-3.10.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ sqlitebrowser.spec ++++++ --- /var/tmp/diff_new_pack.RbAmbO/_old 2018-06-29 22:37:06.429965013 +0200 +++ /var/tmp/diff_new_pack.RbAmbO/_new 2018-06-29 22:37:06.433965010 +0200 @@ -1,7 +1,7 @@ # # spec file for package sqlitebrowser # -# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: sqlitebrowser -Version: 3.10.0 +Version: 3.10.1 Release: 0 Summary: Create, design and edit SQLite Databases License: GPL-3.0+ AND MPL-2.0 ++++++ sqlitebrowser-3.10.0.tar.gz -> sqlitebrowser-3.10.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/CMakeLists.txt new/sqlitebrowser-3.10.1/CMakeLists.txt --- old/sqlitebrowser-3.10.0/CMakeLists.txt 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/CMakeLists.txt 2017-09-20 15:59:51.000000000 +0200 @@ -413,7 +413,7 @@ set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") set(CPACK_PACKAGE_VERSION_MAJOR "3") set(CPACK_PACKAGE_VERSION_MINOR "10") -set(CPACK_PACKAGE_VERSION_PATCH "0") +set(CPACK_PACKAGE_VERSION_PATCH "1") set(CPACK_PACKAGE_INSTALL_DIRECTORY "DB Browser for SQLite") if(WIN32 AND NOT UNIX) # There is a bug in NSIS that does not handle full unix paths properly. Make @@ -433,7 +433,7 @@ # VS redist list(APPEND CPACK_NSIS_EXTRA_INSTALL_COMMANDS " - ExecWait '\\\"$INSTDIR\\\\redist\\\\${VSREDIST}\\\" /install /passive /quiet' + ExecWait '\\\"$INSTDIR\\\\redist\\\\${VSREDIST}\\\" /install /passive /norestart /quiet' Delete '\\\"$INSTDIR\\\\redist\\\\${VSREDIST}\\\"' ") else(WIN32 AND NOT UNIX) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/CipherDialog.cpp new/sqlitebrowser-3.10.1/src/CipherDialog.cpp --- old/sqlitebrowser-3.10.0/src/CipherDialog.cpp 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/CipherDialog.cpp 2017-09-20 15:59:51.000000000 +0200 @@ -40,7 +40,7 @@ QString CipherDialog::password() const { if(keyFormat() == KeyFormats::Passphrase) - return QString("'%1'").arg(ui->editPassword->text()); + return QString("'%1'").arg(ui->editPassword->text().replace("'", "''")); else return QString("\"x'%1'\"").arg(ui->editPassword->text().mid(2)); // Remove the '0x' part at the beginning } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/CipherDialog.ui new/sqlitebrowser-3.10.1/src/CipherDialog.ui --- old/sqlitebrowser-3.10.0/src/CipherDialog.ui 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/CipherDialog.ui 2017-09-20 15:59:51.000000000 +0200 @@ -126,8 +126,8 @@ </layout> </widget> <tabstops> - <tabstop>comboKeyFormat</tabstop> <tabstop>editPassword</tabstop> + <tabstop>comboKeyFormat</tabstop> <tabstop>editPassword2</tabstop> <tabstop>spinPageSize</tabstop> </tabstops> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/EditDialog.cpp new/sqlitebrowser-3.10.1/src/EditDialog.cpp --- old/sqlitebrowser-3.10.0/src/EditDialog.cpp 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/EditDialog.cpp 2017-09-20 15:59:51.000000000 +0200 @@ -464,6 +464,8 @@ ui->buttonApply->setEnabled(!ro); ui->buttonNull->setEnabled(!ro); ui->buttonImport->setEnabled(!ro); + ui->editorText->setReadOnly(ro); + ui->editorBinary->setEnabled(!ro); // We disable the entire hex editor here instead of setting it to read only because it doesn't have a setReadOnly() method } // Update the information labels in the bottom left corner of the dialog diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/EditTableDialog.cpp new/sqlitebrowser-3.10.1/src/EditTableDialog.cpp --- old/sqlitebrowser-3.10.0/src/EditTableDialog.cpp 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/EditTableDialog.cpp 2017-09-20 15:59:51.000000000 +0200 @@ -114,6 +114,7 @@ index = typeBox->count() - 1; } typeBox->setCurrentIndex(index); + typeBox->installEventFilter(this); connect(typeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTypes())); ui->treeWidget->setItemWidget(tbitem, kType, typeBox); @@ -222,13 +223,13 @@ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid); } -void EditTableDialog::updateTypes() +void EditTableDialog::updateTypes(QObject *object) { - QComboBox* typeBox = qobject_cast<QComboBox*>(sender()); + QComboBox* typeBox = qobject_cast<QComboBox*>(object); if(typeBox) { QString type = typeBox->currentText(); - QString column = sender()->property("column").toString(); + QString column = typeBox->property("column").toString(); int index; for(index=0; index < m_table.fields().size(); ++index) @@ -244,6 +245,20 @@ } } +void EditTableDialog::updateTypes() +{ + updateTypes(sender()); +} + +bool EditTableDialog::eventFilter(QObject *object, QEvent *event) +{ + if(event->type() == QEvent::FocusOut) + { + updateTypes(object); + } + return false; +} + void EditTableDialog::itemChanged(QTreeWidgetItem *item, int column) { int index = ui->treeWidget->indexOfTopLevelItem(item); @@ -528,6 +543,7 @@ } ui->treeWidget->setItemWidget(tbitem, kType, typeBox); + typeBox->installEventFilter(this); connect(typeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTypes())); tbitem->setCheckState(kNotNull, Qt::Unchecked); @@ -631,7 +647,8 @@ QComboBox* oldCombo = qobject_cast<QComboBox*>(ui->treeWidget->itemWidget(ui->treeWidget->topLevelItem(currentRow), kType)); QComboBox* newCombo = new QComboBox(ui->treeWidget); newCombo->setProperty("column", oldCombo->property("column")); - connect(newCombo, SIGNAL(activated(int)), this, SLOT(updateTypes())); + newCombo->installEventFilter(this); + connect(newCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTypes())); newCombo->setEditable(true); for(int i=0; i < oldCombo->count(); ++i) newCombo->addItem(oldCombo->itemText(i)); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/EditTableDialog.h new/sqlitebrowser-3.10.1/src/EditTableDialog.h --- old/sqlitebrowser-3.10.0/src/EditTableDialog.h 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/EditTableDialog.h 2017-09-20 15:59:51.000000000 +0200 @@ -51,6 +51,8 @@ virtual void reject(); void checkInput(); void itemChanged(QTreeWidgetItem* item, int column); + void updateTypes(QObject *object); + bool eventFilter(QObject *object, QEvent *event); void updateTypes(); void moveUp(); void moveDown(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/MainWindow.cpp new/sqlitebrowser-3.10.1/src/MainWindow.cpp --- old/sqlitebrowser-3.10.0/src/MainWindow.cpp 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/MainWindow.cpp 2017-09-20 15:59:51.000000000 +0200 @@ -111,6 +111,9 @@ ui->dockPlot->setWidget(plotDock); ui->dockRemote->setWidget(remoteDock); + // Set up edit dock + editDock->setReadOnly(true); + // Restore window geometry restoreGeometry(Settings::getValue("MainWindow", "geometry").toByteArray()); restoreState(Settings::getValue("MainWindow", "windowState").toByteArray()); @@ -851,7 +854,7 @@ } // * Don't allow editing of other objects than tables (on the browse table) * - bool isEditingAllowed = (m_currentTabTableModel == m_browseTableModel) && + bool isEditingAllowed = !db.readOnly() && m_currentTabTableModel == m_browseTableModel && (db.getObjectByName(ui->comboBrowseTable->currentText())->type() == sqlb::Object::Types::Table); // Enable or disable the Apply, Null, & Import buttons in the Edit Cell @@ -875,7 +878,7 @@ return; } - bool editingAllowed = (m_currentTabTableModel == m_browseTableModel) && + bool editingAllowed = !db.readOnly() && (m_currentTabTableModel == m_browseTableModel) && (db.getObjectByName(ui->comboBrowseTable->currentText())->type() == sqlb::Object::Types::Table); // Don't allow editing of other objects than tables @@ -1486,7 +1489,7 @@ ui->actionSaveProject->setEnabled(enable); ui->actionEncryption->setEnabled(enable && write); ui->buttonClearFilters->setEnabled(enable); - ui->dockEdit->setEnabled(enable && write); + ui->dockEdit->setEnabled(enable); ui->dockPlot->setEnabled(enable); remoteDock->enableButtons(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/PreferencesDialog.ui new/sqlitebrowser-3.10.1/src/PreferencesDialog.ui --- old/sqlitebrowser-3.10.0/src/PreferencesDialog.ui 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/PreferencesDialog.ui 2017-09-20 15:59:51.000000000 +0200 @@ -1035,6 +1035,9 @@ <property name="horizontalScrollMode"> <enum>QAbstractItemView::ScrollPerPixel</enum> </property> + <attribute name="horizontalHeaderHighlightSections"> + <bool>false</bool> + </attribute> <column> <property name="text"> <string>Subject CN</string> @@ -1094,6 +1097,9 @@ <property name="horizontalScrollMode"> <enum>QAbstractItemView::ScrollPerPixel</enum> </property> + <attribute name="horizontalHeaderHighlightSections"> + <bool>false</bool> + </attribute> <column> <property name="text"> <string>File</string> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/RemoteDatabase.cpp new/sqlitebrowser-3.10.1/src/RemoteDatabase.cpp --- old/sqlitebrowser-3.10.0/src/RemoteDatabase.cpp 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/RemoteDatabase.cpp 2017-09-20 15:59:51.000000000 +0200 @@ -181,6 +181,27 @@ emit gotLicenceList(licences); break; } + case RequestTypeBranchList: + { + // Read and check results + QJsonDocument json = QJsonDocument::fromJson(reply->readAll()); + if(json.isNull() || !json.isObject()) + break; + QJsonObject obj = json.object(); + QJsonObject obj_branches = obj["branches"].toObject(); + + // Parse data and assemble branch list + QStringList branches; + for(auto it=obj_branches.constBegin();it!=obj_branches.constEnd();++it) + branches.append(it.key()); + + // Get default branch + QString default_branch = obj["default_branch"].toString("master"); + + // Send branch list to anyone who is interested + emit gotBranchList(branches, default_branch); + break; + } case RequestTypePush: emit uploadFinished(reply->url().toString()); break; @@ -238,9 +259,14 @@ m_progress->reset(); } else { // It's still downloading and we know the current progress + + // Were using a range 0 to 10000 here, the progress dialog will calculate 0% to 100% values from that. The reason we're not using + // the byte counts as-is is that they're 64bit wide while the progress dialog takes only 32bit values, so for large files the values + // would lose precision. The reason why we're not using a range 0 to 100 is that our range increases the precision a bit and this way + // we're prepared if the progress dialog will show decimal numbers one day on one platform. m_progress->setMinimum(0); - m_progress->setMaximum(bytesTotal); - m_progress->setValue(bytesTransmitted); + m_progress->setMaximum(10000); + m_progress->setValue(static_cast<int>((static_cast<float>(bytesTransmitted) / static_cast<float>(bytesTotal)) * 10000.0f)); } // Check if the Cancel button has been pressed @@ -320,9 +346,9 @@ // Set dialog text if(upload) - m_progress->setLabelText(tr("Uploading remote database to\n%1.").arg(url)); + m_progress->setLabelText(tr("Uploading remote database to\n%1").arg(url)); else - m_progress->setLabelText(tr("Downloading remote database from\n%1.").arg(url)); + m_progress->setLabelText(tr("Downloading remote database from\n%1").arg(url)); // Show dialog m_progress->show(); @@ -387,7 +413,7 @@ } void RemoteDatabase::push(const QString& filename, const QString& url, const QString& clientCert, const QString& remotename, - const QString& commitMessage, const QString& licence, bool isPublic) + const QString& commitMessage, const QString& licence, bool isPublic, const QString& branch) { // Check if network is accessible. If not, abort right here if(m_manager->networkAccessible() == QNetworkAccessManager::NotAccessible) @@ -416,6 +442,7 @@ addPart(multipart, "commitmsg", commitMessage); addPart(multipart, "licence", licence); addPart(multipart, "public", isPublic ? "true" : "false"); + addPart(multipart, "branch", branch); // Set SSL configuration when trying to access a file via the HTTPS protocol bool https = QUrl(url).scheme().compare("https", Qt::CaseInsensitive) == 0; @@ -574,8 +601,6 @@ // Extract commit id from url and remove query part afterwards QString url_commit_id = QUrlQuery(url).queryItemValue("commit"); - QUrl url_without_query = url; - url_without_query.setQuery(QString()); // Query commit id and filename for the given combination of url and identity QString sql = QString("SELECT id, commit_id, file FROM local WHERE url=? AND identity=?"); @@ -583,7 +608,7 @@ if(sqlite3_prepare_v2(m_dbLocal, sql.toUtf8(), -1, &stmt, 0) != SQLITE_OK) return QString(); - if(sqlite3_bind_text(stmt, 1, url_without_query.toString().toUtf8(), url_without_query.toString().toUtf8().length(), SQLITE_TRANSIENT)) + if(sqlite3_bind_text(stmt, 1, url.toString(QUrl::PrettyDecoded | QUrl::RemoveQuery).toUtf8(), url.toString(QUrl::PrettyDecoded | QUrl::RemoveQuery).toUtf8().length(), SQLITE_TRANSIENT)) { sqlite3_finalize(stmt); return QString(); @@ -616,15 +641,79 @@ // is newer, or the local commit id is newer. if(local_commit_id == url_commit_id) { - // Both commit ids are the same. That's the perfect match, so just return the path to the local file - return Settings::getValue("remote", "clonedirectory").toString() + "/" + local_file; + // Both commit ids are the same. That's the perfect match, so we can download the local file if it still exists + return localCheckFile(local_file); } else { - // In all the other cases just treat the remote database as a completely new database for now. + // The commit ids differ. That means we have another version locally checked out than we're trying to download. Because the commit ids are + // only calculated on the server side and we're currently always checking out the latest version this can only mean that the remote version has + // been updated, i.e. is newer than the local version. + + // TODO Support multiple checkouts of the same database at different versions at the same time. For this we need to be more intelligent with + // comparing the commit ids. + + // Ask the user what to do: open the local version or updating to the new remote version + if(QMessageBox::question(nullptr, qApp->applicationName(), + tr("The remote database has been updated since the last checkout. Do you want to update the local database to the newest version? Note " + "that this discards any changes you have made locally! If you don't want to lose local changes, click No to open the local version."), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) + { + // User wants to download the newest version. So delete the entry from the clones database and delete the local database copy and return an empty + // string to indicate a redownload request. + + // Build full path to database file and delete it + QFile::remove(Settings::getValue("remote", "clonedirectory").toString() + "/" + local_file); - // TODO Add some way to update the local clone here. Maybe ask the user what to do because I don't really know what the - // most sensible way to go is in the two remaining cases. We can use the local_id variable (see above) to update the - // record afterwards. + // Remove the old entry from the local clones database to enforce a redownload. The file column should be unique for the entire table because the + // files are all in the same directory and their names need to be unique because of this. + QString sql = QString("DELETE FROM local WHERE file=?"); + sqlite3_stmt* stmt; + if(sqlite3_prepare_v2(m_dbLocal, sql.toUtf8(), -1, &stmt, 0) != SQLITE_OK) + return QString(); + if(sqlite3_bind_text(stmt, 1, local_file.toUtf8(), local_file.toUtf8().length(), SQLITE_TRANSIENT)) + { + sqlite3_finalize(stmt); + return QString(); + } + sqlite3_step(stmt); + sqlite3_finalize(stmt); + + // Return an empty string to indicate a redownload request + return QString(); + } else { + // User wants to open the local version. So build the full path and return it if the file still exists. + return localCheckFile(local_file); + } + } +} + +QString RemoteDatabase::localCheckFile(const QString& local_file) +{ + // This function takes the file name of a locally cloned database and checks if this file still exists. If it has been deleted in the meantime it returns + // an empty string and deletes the file from the clone database. If the file still exists, it returns the full path to the file. + + // Build the full path to where the file should be + QString full_path = Settings::getValue("remote", "clonedirectory").toString() + "/" + local_file; + + // Check if the database still exists. If so return its path, if not return an empty string to redownload it + if(QFile::exists(full_path)) + { + return full_path; + } else { + // Remove the apparently invalid entry from the local clones database to avoid future lookups and confusions. The file column should + // be unique for the entire table because the files are all in the same directory and their names need to be unique because of this. + QString sql = QString("DELETE FROM local WHERE file=?"); + sqlite3_stmt* stmt; + if(sqlite3_prepare_v2(m_dbLocal, sql.toUtf8(), -1, &stmt, 0) != SQLITE_OK) + return QString(); + if(sqlite3_bind_text(stmt, 1, local_file.toUtf8(), local_file.toUtf8().length(), SQLITE_TRANSIENT)) + { + sqlite3_finalize(stmt); + return QString(); + } + sqlite3_step(stmt); + sqlite3_finalize(stmt); + // Return empty string to indicate a redownload request return QString(); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/RemoteDatabase.h new/sqlitebrowser-3.10.1/src/RemoteDatabase.h --- old/sqlitebrowser-3.10.0/src/RemoteDatabase.h 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/RemoteDatabase.h 2017-09-20 15:59:51.000000000 +0200 @@ -41,11 +41,12 @@ RequestTypeNewVersionCheck, RequestTypePush, RequestTypeLicenceList, + RequestTypeBranchList, }; void fetch(const QString& url, RequestType type, const QString& clientCert = QString(), QVariant userdata = QVariant()); void push(const QString& filename, const QString& url, const QString& clientCert, const QString& remotename, - const QString& commitMessage = QString(), const QString& licence = QString(), bool isPublic = false); + const QString& commitMessage = QString(), const QString& licence = QString(), bool isPublic = false, const QString& branch = QString("master")); signals: // The openFile signal is emitted whenever a remote database file shall be opened in the main window. This happens when the @@ -57,6 +58,7 @@ void gotDirList(QString json, QVariant userdata); void gotCurrentVersion(QString version, QString url); void gotLicenceList(QMap<QString, QString> licences); + void gotBranchList(QStringList branches, QString default_branch); // The uploadFinished() signal is emitted when a push() call is finished, i.e. a database upload has completed. void uploadFinished(QString url); @@ -73,6 +75,7 @@ void localAssureOpened(); void localAdd(QString filename, QString identity, const QUrl& url); QString localExists(const QUrl& url, QString identity); + QString localCheckFile(const QString& local_file); // Helper functions for building multi-part HTTP requests void addPart(QHttpMultiPart* multipart, const QString& name, const QString& value); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/RemoteDock.cpp new/sqlitebrowser-3.10.1/src/RemoteDock.cpp --- old/sqlitebrowser-3.10.0/src/RemoteDock.cpp 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/RemoteDock.cpp 2017-09-20 15:59:51.000000000 +0200 @@ -111,7 +111,7 @@ // Push database remoteDatabase.push(mainWindow->getDb().currentFile(), url, remoteModel->currentClientCertificate(), pushDialog.name(), - pushDialog.commitMessage(), pushDialog.licence(), pushDialog.isPublic()); + pushDialog.commitMessage(), pushDialog.licence(), pushDialog.isPublic(), pushDialog.branch()); } void RemoteDock::newDirectoryNode(const QModelIndex& parent) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/RemoteDock.ui new/sqlitebrowser-3.10.1/src/RemoteDock.ui --- old/sqlitebrowser-3.10.0/src/RemoteDock.ui 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/RemoteDock.ui 2017-09-20 15:59:51.000000000 +0200 @@ -35,6 +35,9 @@ </item> <item> <widget class="QToolButton" name="buttonLogin"> + <property name="toolTip"> + <string>Connect to the remote server using the currently selected identity. The correct server is taken from the identity as well.</string> + </property> <property name="text"> <string>Go</string> </property> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/RemoteModel.cpp new/sqlitebrowser-3.10.1/src/RemoteModel.cpp --- old/sqlitebrowser-3.10.0/src/RemoteModel.cpp 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/RemoteModel.cpp 2017-09-20 15:59:51.000000000 +0200 @@ -233,7 +233,7 @@ return QVariant(); // Convert size to human readable format - float size = item->value(RemoteModelColumnSize).toInt(); + float size = item->value(RemoteModelColumnSize).toLongLong(); QStringList list; list << "KiB" << "MiB" << "GiB" << "TiB"; QStringListIterator it(list); @@ -243,7 +243,7 @@ unit = it.next(); size /= 1024.0; } - return QString().setNum(size, 'f', 2) + " " + unit; + return QString().setNum(size, 'f', 2).remove(".00") + " " + unit; } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/RemotePushDialog.cpp new/sqlitebrowser-3.10.1/src/RemotePushDialog.cpp --- old/sqlitebrowser-3.10.0/src/RemotePushDialog.cpp 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/RemotePushDialog.cpp 2017-09-20 15:59:51.000000000 +0200 @@ -1,4 +1,5 @@ #include <QPushButton> +#include <QUrlQuery> #include "RemotePushDialog.h" #include "ui_RemotePushDialog.h" @@ -7,6 +8,8 @@ RemotePushDialog::RemotePushDialog(QWidget* parent, RemoteDatabase& remote, const QString& host, const QString& clientCert, const QString& name) : QDialog(parent), ui(new Ui::RemotePushDialog), + m_host(host), + m_clientCert(clientCert), remoteDatabase(remote) { // Create UI @@ -21,6 +24,10 @@ // Fetch list of available licences connect(&remoteDatabase, &RemoteDatabase::gotLicenceList, this, &RemotePushDialog::fillInLicences); remoteDatabase.fetch(host + "licence/list", RemoteDatabase::RequestTypeLicenceList, clientCert); + + // Prepare fetching list of available branches + connect(&remoteDatabase, &RemoteDatabase::gotBranchList, this, &RemotePushDialog::fillInBranches); + reloadBranchList(); } RemotePushDialog::~RemotePushDialog() @@ -42,6 +49,12 @@ if(ui->editName->text().trimmed().isEmpty()) valid = false; + if(ui->editCommitMessage->toPlainText().size() > 1024) + valid = false; + + if(ui->comboBranch->currentText().size() < 1 || ui->comboBranch->currentText().size() > 32) + valid = false; + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid); } @@ -67,7 +80,12 @@ bool RemotePushDialog::isPublic() const { - return ui->checkPublic->isChecked(); + return ui->checkPublic->isChecked(); +} + +QString RemotePushDialog::branch() const +{ + return ui->comboBranch->currentText(); } void RemotePushDialog::fillInLicences(const QMap<QString, QString>& licences) @@ -80,3 +98,31 @@ for(auto it=licences.constBegin();it!=licences.constEnd();++it) ui->comboLicence->addItem(it.value(), it.key()); } + +void RemotePushDialog::fillInBranches(const QStringList& branches, const QString& default_branch) +{ + // Clear branch list and add the default branch + ui->comboBranch->clear(); + ui->comboBranch->addItem(default_branch); + + // Add rest of the branch list to the combo box + foreach(const QString& branch, branches) + { + if(branch != default_branch) + ui->comboBranch->addItem(branch); + } +} + +void RemotePushDialog::reloadBranchList() +{ + // Assemble query URL + QUrl url(m_host + "branch/list"); + QUrlQuery query; + query.addQueryItem("username", remoteDatabase.getInfoFromClientCert(m_clientCert, RemoteDatabase::CertInfoUser)); + query.addQueryItem("folder", "/"); + query.addQueryItem("dbname", ui->editName->text()); + url.setQuery(query); + + // Send request + remoteDatabase.fetch(url.toString(), RemoteDatabase::RequestTypeBranchList, m_clientCert); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/RemotePushDialog.h new/sqlitebrowser-3.10.1/src/RemotePushDialog.h --- old/sqlitebrowser-3.10.0/src/RemotePushDialog.h 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/RemotePushDialog.h 2017-09-20 15:59:51.000000000 +0200 @@ -21,10 +21,15 @@ QString commitMessage() const; QString licence() const; bool isPublic() const; + QString branch() const; private: Ui::RemotePushDialog* ui; + // Connection details + QString m_host; + QString m_clientCert; + // Reference to the remote database object which is stored somewhere in the main window RemoteDatabase& remoteDatabase; @@ -32,7 +37,10 @@ void checkInput(); virtual void accept(); + void reloadBranchList(); + void fillInLicences(const QMap<QString, QString>& licences); + void fillInBranches(const QStringList& branches, const QString& default_branch); }; #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/RemotePushDialog.ui new/sqlitebrowser-3.10.1/src/RemotePushDialog.ui --- old/sqlitebrowser-3.10.0/src/RemotePushDialog.ui 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/RemotePushDialog.ui 2017-09-20 15:59:51.000000000 +0200 @@ -27,7 +27,11 @@ </widget> </item> <item row="0" column="1"> - <widget class="QLineEdit" name="editName"/> + <widget class="QLineEdit" name="editName"> + <property name="maxLength"> + <number>256</number> + </property> + </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="label_3"> @@ -53,7 +57,7 @@ </property> </widget> </item> - <item row="3" column="0"> + <item row="4" column="0"> <widget class="QLabel" name="label_2"> <property name="text"> <string>Database licence</string> @@ -63,7 +67,7 @@ </property> </widget> </item> - <item row="3" column="1"> + <item row="4" column="1"> <widget class="QComboBox" name="comboLicence"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> @@ -73,7 +77,7 @@ </property> </widget> </item> - <item row="2" column="0"> + <item row="3" column="0"> <widget class="QLabel" name="label_4"> <property name="text"> <string>Public</string> @@ -83,9 +87,29 @@ </property> </widget> </item> - <item row="2" column="1"> + <item row="3" column="1"> <widget class="QCheckBox" name="checkPublic"/> </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Branch</string> + </property> + <property name="buddy"> + <cstring>comboBranch</cstring> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="comboBranch"> + <property name="editable"> + <bool>true</bool> + </property> + <property name="duplicatesEnabled"> + <bool>true</bool> + </property> + </widget> + </item> </layout> </item> <item> @@ -103,6 +127,7 @@ <tabstops> <tabstop>editName</tabstop> <tabstop>editCommitMessage</tabstop> + <tabstop>comboBranch</tabstop> <tabstop>checkPublic</tabstop> <tabstop>comboLicence</tabstop> </tabstops> @@ -172,8 +197,57 @@ </hint> </hints> </connection> + <connection> + <sender>editCommitMessage</sender> + <signal>textChanged()</signal> + <receiver>RemotePushDialog</receiver> + <slot>checkInput()</slot> + <hints> + <hint type="sourcelabel"> + <x>175</x> + <y>113</y> + </hint> + <hint type="destinationlabel"> + <x>91</x> + <y>111</y> + </hint> + </hints> + </connection> + <connection> + <sender>editName</sender> + <signal>editingFinished()</signal> + <receiver>RemotePushDialog</receiver> + <slot>reloadBranchList()</slot> + <hints> + <hint type="sourcelabel"> + <x>176</x> + <y>25</y> + </hint> + <hint type="destinationlabel"> + <x>77</x> + <y>3</y> + </hint> + </hints> + </connection> + <connection> + <sender>comboBranch</sender> + <signal>currentTextChanged(QString)</signal> + <receiver>RemotePushDialog</receiver> + <slot>checkInput()</slot> + <hints> + <hint type="sourcelabel"> + <x>172</x> + <y>138</y> + </hint> + <hint type="destinationlabel"> + <x>33</x> + <y>151</y> + </hint> + </hints> + </connection> </connections> <slots> <slot>checkInput()</slot> + <slot>reloadBranchList()</slot> </slots> </ui> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/app.plist new/sqlitebrowser-3.10.1/src/app.plist --- old/sqlitebrowser-3.10.0/src/app.plist 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/app.plist 2017-09-20 15:59:51.000000000 +0200 @@ -52,7 +52,7 @@ <key>CFBundleExecutable</key> <string>@EXECUTABLE@</string> <key>CFBundleGetInfoString</key> - <string>3.10.0</string> + <string>3.10.1</string> <key>CFBundleIconFile</key> <string>@ICON@</string> <key>CFBundleIdentifier</key> @@ -64,11 +64,11 @@ <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> - <string>3.10.0</string> + <string>3.10.1</string> <key>CFBundleSignature</key> <string>SqLB</string> <key>CFBundleVersion</key> - <string>3.10.0</string> + <string>3.10.1</string> <key>NSPrincipalClass</key> <string>NSApplication</string> <key>NSHighResolutionCapable</key> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/sqlitedb.cpp new/sqlitebrowser-3.10.1/src/sqlitedb.cpp --- old/sqlitebrowser-3.10.0/src/sqlitedb.cpp 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/sqlitedb.cpp 2017-09-20 15:59:51.000000000 +0200 @@ -1157,12 +1157,25 @@ if((*it)->type() == sqlb::Object::Types::Index) { sqlb::IndexPtr idx = (*it).dynamicCast<sqlb::Index>(); - for(int i=0;i<idx->columns().size();i++) + + // Are we updating a field name or are we removing a field entirely? + if(to) { - if(idx->column(i)->name() == name) - idx->column(i)->setName(to->name()); + // We're updating a field name. So search for it in the index and replace it whereever it is found + for(int i=0;i<idx->columns().size();i++) + { + if(idx->column(i)->name() == name) + idx->column(i)->setName(to->name()); + } + } else { + // We're removing a field. So remove it from any indices, too. + while(idx->removeColumn(name)) + ; } - otherObjectsSql << idx->sql(); + + // Only try to add the index later if it has any columns remaining + if(idx->columns().size()) + otherObjectsSql << idx->sql(); } else { // If it's a view or a trigger we don't have any chance to corrections yet. Just store the statement as is and // hope for the best. @@ -1401,7 +1414,13 @@ // Set the pragma value QString sql = QString("PRAGMA %1 = \"%2\";").arg(pragma).arg(value); - releaseSavepoint(); + // In general, we want to commit changes before running pragmas because most of them can't be rolled back and some of them + // even fail when run in a transaction. However, the defer_foreign_keys pragma has neither problem and we need it to be settable + // inside transactions (see the renameColumn() function where it is set and reset at some point and where we don't want the changes + // to be committed just because of this pragma). + if(pragma != "defer_foreign_keys") + releaseSavepoint(); + bool res = executeSQL(sql, false, true); // PRAGMA statements are usually not transaction bound, so we can't revert if( !res ) qWarning() << tr("Error setting pragma %1 to %2: %3").arg(pragma).arg(value).arg(lastErrorMessage); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/sqlitetablemodel.cpp new/sqlitebrowser-3.10.1/src/sqlitetablemodel.cpp --- old/sqlitebrowser-3.10.0/src/sqlitetablemodel.cpp 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/sqlitetablemodel.cpp 2017-09-20 15:59:51.000000000 +0200 @@ -711,13 +711,23 @@ val2.clear(); if(value.left(2) == ">=" || value.left(2) == "<=" || value.left(2) == "<>") { - bool ok; - value.mid(2).toFloat(&ok); - if(ok) + // Check if we're filtering for '<> NULL'. In this case we need a special comparison operator. + if(value.left(2) == "<>" && value.mid(2) == "NULL") { - op = value.left(2); - val = value.mid(2); + // We are filtering for '<> NULL'. Override the comparison operator to search for NULL values in this column. Also treat search value (NULL) as number, + // in order to avoid putting quotes around it. + op = "IS NOT"; numeric = true; + val = "NULL"; + } else { + bool ok; + value.mid(2).toFloat(&ok); + if(ok) + { + op = value.left(2); + val = value.mid(2); + numeric = true; + } } } else if(value.left(1) == ">" || value.left(1) == "<") { bool ok; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/translations/sqlb_ru.ts new/sqlitebrowser-3.10.1/src/translations/sqlb_ru.ts --- old/sqlitebrowser-3.10.0/src/translations/sqlb_ru.ts 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/translations/sqlb_ru.ts 2017-09-20 15:59:51.000000000 +0200 @@ -3988,7 +3988,7 @@ <message> <location filename="../SqlUiLexer.cpp" line="107"/> <source>(X) The count(X) function returns a count of the number of times that X is not NULL in a group.</source> - <translation>(X) Функция count(X) возвращает количесвто строк, в которых X не равно NULL в группе.</translation> + <translation>(X) Функция count(X) возвращает количество строк, в которых X не равно NULL в группе.</translation> </message> <message> <location filename="../SqlUiLexer.cpp" line="108"/> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sqlitebrowser-3.10.0/src/version.h new/sqlitebrowser-3.10.1/src/version.h --- old/sqlitebrowser-3.10.0/src/version.h 2017-08-18 20:15:39.000000000 +0200 +++ new/sqlitebrowser-3.10.1/src/version.h 2017-09-20 15:59:51.000000000 +0200 @@ -2,7 +2,7 @@ #define GEN_VERSION_H #define MAJOR_VERSION 3 #define MINOR_VERSION 10 -#define PATCH_VERSION 0 +#define PATCH_VERSION 1 #define str(s) #s #define xstr(s) str(s)