mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-05-18 19:48:23 -05:00
Add "Save Database As..." action
The feature is implemented using the SQLite backup API. https://sqlite.org/backup.html This allows saving in-memory databases and saving database files to another file name.
This commit is contained in:
@@ -687,6 +687,30 @@ void MainWindow::refreshTableBrowsers(bool force_refresh)
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
bool MainWindow::fileSaveAs() {
|
||||
|
||||
QString fileName = FileDialog::getSaveFileName(
|
||||
OpenDatabaseFile,
|
||||
this,
|
||||
tr("Choose a database file to save under"),
|
||||
FileDialog::getSqlDatabaseFileFilter()
|
||||
);
|
||||
// catch situation where user has canceled file selection from dialog
|
||||
if(!fileName.isEmpty()) {
|
||||
bool result = db.saveAs(fileName.toStdString());
|
||||
if(result) {
|
||||
setCurrentFile(fileName);
|
||||
addToRecentFilesMenu(fileName);
|
||||
} else {
|
||||
QMessageBox::warning(this, QApplication::applicationName(),
|
||||
tr("Error while saving the database to the new file."));
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWindow::fileClose()
|
||||
{
|
||||
// Stop any running SQL statements before closing the database
|
||||
@@ -1415,6 +1439,9 @@ void MainWindow::dbState(bool dirty)
|
||||
{
|
||||
ui->fileSaveAction->setEnabled(dirty);
|
||||
ui->fileRevertAction->setEnabled(dirty);
|
||||
// Unfortunately, sqlite does not allow to backup the DB while there are pending savepoints,
|
||||
// so we cannot "Save As" when the DB is dirty.
|
||||
ui->fileSaveAsAction->setEnabled(!dirty);
|
||||
}
|
||||
|
||||
void MainWindow::fileSave()
|
||||
@@ -1824,6 +1851,7 @@ void MainWindow::activateFields(bool enable)
|
||||
bool tempDb = db.currentFile() == ":memory:";
|
||||
|
||||
ui->fileCloseAction->setEnabled(enable);
|
||||
ui->fileSaveAsAction->setEnabled(enable);
|
||||
ui->fileAttachAction->setEnabled(enable);
|
||||
ui->fileCompactAction->setEnabled(enable && write);
|
||||
ui->fileExportJsonAction->setEnabled(enable);
|
||||
|
||||
@@ -167,6 +167,7 @@ private slots:
|
||||
void fileNewInMemoryDatabase();
|
||||
void refreshTableBrowsers(bool force_refresh = false);
|
||||
bool fileClose();
|
||||
bool fileSaveAs();
|
||||
void createTable();
|
||||
void createIndex();
|
||||
void compact();
|
||||
|
||||
@@ -785,6 +785,7 @@ You can drag SQL statements from an object row and drop them into other applicat
|
||||
<addaction name="fileOpenReadOnlyAction"/>
|
||||
<addaction name="fileAttachAction"/>
|
||||
<addaction name="fileRecentFiles"/>
|
||||
<addaction name="fileSaveAsAction"/>
|
||||
<addaction name="fileCloseAction"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="fileSaveAction"/>
|
||||
@@ -2240,6 +2241,21 @@ You can drag SQL statements from the Schema column and drop them into the SQL ed
|
||||
<string>This shows the number of rows for each table and view in the database.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="fileSaveAsAction">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
<normaloff>:/icons/db_save</normaloff>:/icons/db_save</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save Database &As...</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Save the current database as a different file</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
@@ -2350,6 +2366,22 @@ You can drag SQL statements from the Schema column and drop them into the SQL ed
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>fileSaveAsAction</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>fileSaveAs()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>399</x>
|
||||
<y>299</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>fileCompactAction</sender>
|
||||
<signal>triggered()</signal>
|
||||
|
||||
@@ -755,6 +755,62 @@ bool DBBrowserDB::close()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DBBrowserDB::saveAs(const std::string& filename) {
|
||||
int rc;
|
||||
sqlite3_backup *pBackup;
|
||||
sqlite3 *pTo;
|
||||
|
||||
if(!_db)
|
||||
return false;
|
||||
|
||||
waitForDbRelease();
|
||||
|
||||
// Open the database file identified by filename. Exit early if this fails
|
||||
// for any reason.
|
||||
rc = sqlite3_open(filename.c_str(), &pTo);
|
||||
if(rc!=SQLITE_OK) {
|
||||
qWarning() << tr("Cannot open destination file: '%1'").arg(filename.c_str());
|
||||
return false;
|
||||
} else {
|
||||
// Set up the backup procedure to copy from the "main" database of
|
||||
// connection _db to the main database of connection pTo.
|
||||
// If something goes wrong, pBackup will be set to nullptr and an error
|
||||
// code and message left in connection pTo.
|
||||
//
|
||||
// If the backup object is successfully created, call backup_step()
|
||||
// to copy data from _db to pTo. Then call backup_finish()
|
||||
// to release resources associated with the pBackup object. If an
|
||||
// error occurred, then an error code and message will be left in
|
||||
// connection pTo. If no error occurred, then the error code belonging
|
||||
// to pTo is set to SQLITE_OK.
|
||||
//
|
||||
pBackup = sqlite3_backup_init(pTo, "main", _db, "main");
|
||||
if(pBackup == nullptr) {
|
||||
qWarning() << tr("Cannot backup to file: '%1'. Message: %2").arg(filename.c_str()).arg(sqlite3_errmsg(pTo));
|
||||
sqlite3_close(pTo);
|
||||
return false;
|
||||
} else {
|
||||
sqlite3_backup_step(pBackup, -1);
|
||||
sqlite3_backup_finish(pBackup);
|
||||
}
|
||||
rc = sqlite3_errcode(pTo);
|
||||
}
|
||||
|
||||
if(rc == SQLITE_OK) {
|
||||
// Close current database and set backup as current
|
||||
sqlite3_close(_db);
|
||||
_db = pTo;
|
||||
curDBFilename = QString::fromStdString(filename);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
qWarning() << tr("Cannot backup to file: '%1'. Message: %2").arg(filename.c_str()).arg(sqlite3_errmsg(pTo));
|
||||
// Close failed database connection.
|
||||
sqlite3_close(pTo);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DBBrowserDB::db_pointer_type DBBrowserDB::get(const QString& user, bool force_wait)
|
||||
{
|
||||
if(!_db)
|
||||
|
||||
@@ -80,6 +80,7 @@ public:
|
||||
bool detach(const std::string& attached_as);
|
||||
bool create ( const QString & db);
|
||||
bool close();
|
||||
bool saveAs(const std::string& filename);
|
||||
|
||||
// This returns the SQLite version as well as the SQLCipher if DB4S is compiled with encryption support
|
||||
static void getSqliteVersion(QString& sqlite, QString& sqlcipher);
|
||||
|
||||
Reference in New Issue
Block a user