mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-02 17:09:26 +02:00
Revert "AUP3: Reworked CopyTo() yet again"
A bit more thought needs to do into this.
This commit is contained in:
parent
e3c5563d35
commit
a0008831e0
@ -19,10 +19,8 @@ Paul Licameli split from AudacityProject.cpp
|
|||||||
#include "ProjectFileIORegistry.h"
|
#include "ProjectFileIORegistry.h"
|
||||||
#include "ProjectSerializer.h"
|
#include "ProjectSerializer.h"
|
||||||
#include "ProjectSettings.h"
|
#include "ProjectSettings.h"
|
||||||
#include "SampleBlock.h"
|
|
||||||
#include "Tags.h"
|
#include "Tags.h"
|
||||||
#include "ViewInfo.h"
|
#include "ViewInfo.h"
|
||||||
#include "WaveClip.h"
|
|
||||||
#include "WaveTrack.h"
|
#include "WaveTrack.h"
|
||||||
#include "widgets/AudacityMessageBox.h"
|
#include "widgets/AudacityMessageBox.h"
|
||||||
#include "widgets/NumericTextCtrl.h"
|
#include "widgets/NumericTextCtrl.h"
|
||||||
@ -43,10 +41,10 @@ static const int ProjectFileVersion = 1;
|
|||||||
// to sampleblocks.
|
// to sampleblocks.
|
||||||
|
|
||||||
static const char *ProjectFileSchema =
|
static const char *ProjectFileSchema =
|
||||||
"PRAGMA <dbname>.application_id = %d;"
|
"PRAGMA application_id = %d;"
|
||||||
"PRAGMA <dbname>.user_version = %d;"
|
"PRAGMA user_version = %d;"
|
||||||
"PRAGMA <dbname>.journal_mode = WAL;"
|
"PRAGMA journal_mode = WAL;"
|
||||||
"PRAGMA <dbname>.locking_mode = EXCLUSIVE;"
|
"PRAGMA locking_mode = EXCLUSIVE;"
|
||||||
""
|
""
|
||||||
// project is a binary representation of an XML file.
|
// project is a binary representation of an XML file.
|
||||||
// it's in binary for speed.
|
// it's in binary for speed.
|
||||||
@ -60,7 +58,7 @@ static const char *ProjectFileSchema =
|
|||||||
// There is no limit to document blob size.
|
// There is no limit to document blob size.
|
||||||
// dict will be smallish, with an entry for each
|
// dict will be smallish, with an entry for each
|
||||||
// kind of field.
|
// kind of field.
|
||||||
"CREATE TABLE IF NOT EXISTS <dbname>.project"
|
"CREATE TABLE IF NOT EXISTS project"
|
||||||
"("
|
"("
|
||||||
" id INTEGER PRIMARY KEY,"
|
" id INTEGER PRIMARY KEY,"
|
||||||
" dict BLOB,"
|
" dict BLOB,"
|
||||||
@ -80,7 +78,7 @@ static const char *ProjectFileSchema =
|
|||||||
// There is no limit to document blob size.
|
// There is no limit to document blob size.
|
||||||
// dict will be smallish, with an entry for each
|
// dict will be smallish, with an entry for each
|
||||||
// kind of field.
|
// kind of field.
|
||||||
"CREATE TABLE IF NOT EXISTS <dbname>.autosave"
|
"CREATE TABLE IF NOT EXISTS autosave"
|
||||||
"("
|
"("
|
||||||
" id INTEGER PRIMARY KEY,"
|
" id INTEGER PRIMARY KEY,"
|
||||||
" dict BLOB,"
|
" dict BLOB,"
|
||||||
@ -89,7 +87,7 @@ static const char *ProjectFileSchema =
|
|||||||
""
|
""
|
||||||
// CREATE SQL tags
|
// CREATE SQL tags
|
||||||
// tags is not used (yet)
|
// tags is not used (yet)
|
||||||
"CREATE TABLE IF NOT EXISTS <dbname>.tags"
|
"CREATE TABLE IF NOT EXISTS tags"
|
||||||
"("
|
"("
|
||||||
" name TEXT,"
|
" name TEXT,"
|
||||||
" value BLOB"
|
" value BLOB"
|
||||||
@ -106,7 +104,7 @@ static const char *ProjectFileSchema =
|
|||||||
// blockID is a 64 bit number.
|
// blockID is a 64 bit number.
|
||||||
//
|
//
|
||||||
// summin to summary64K are summaries at 3 distance scales.
|
// summin to summary64K are summaries at 3 distance scales.
|
||||||
"CREATE TABLE IF NOT EXISTS <dbname>.sampleblocks"
|
"CREATE TABLE IF NOT EXISTS sampleblocks"
|
||||||
"("
|
"("
|
||||||
" blockid INTEGER PRIMARY KEY AUTOINCREMENT,"
|
" blockid INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||||
" sampleformat INTEGER,"
|
" sampleformat INTEGER,"
|
||||||
@ -611,7 +609,7 @@ bool ProjectFileIO::CheckVersion()
|
|||||||
// must be a new project file.
|
// must be a new project file.
|
||||||
if (wxStrtol<char **>(result, nullptr, 10) == 0)
|
if (wxStrtol<char **>(result, nullptr, 10) == 0)
|
||||||
{
|
{
|
||||||
return InstallSchema(db);
|
return InstallSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for our application ID
|
// Check for our application ID
|
||||||
@ -655,15 +653,19 @@ bool ProjectFileIO::CheckVersion()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProjectFileIO::InstallSchema(sqlite3 *db, const char *dbname /* = "main" */)
|
bool ProjectFileIO::InstallSchema()
|
||||||
{
|
{
|
||||||
|
auto db = DB();
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
wxString sql;
|
char sql[1024];
|
||||||
sql.Printf(ProjectFileSchema, ProjectFileID, ProjectFileVersion);
|
sqlite3_snprintf(sizeof(sql),
|
||||||
sql.Replace("<dbname>", dbname);
|
sql,
|
||||||
|
ProjectFileSchema,
|
||||||
|
ProjectFileID,
|
||||||
|
ProjectFileVersion);
|
||||||
|
|
||||||
rc = sqlite3_exec(db, sql.mb_str().data(), nullptr, nullptr, nullptr);
|
rc = sqlite3_exec(db, sql, nullptr, nullptr, nullptr);
|
||||||
if (rc != SQLITE_OK)
|
if (rc != SQLITE_OK)
|
||||||
{
|
{
|
||||||
SetDBError(
|
SetDBError(
|
||||||
@ -686,12 +688,12 @@ bool ProjectFileIO::UpgradeSchema()
|
|||||||
// An SQLite function that takes a blockid and looks it up in a set of
|
// An SQLite function that takes a blockid and looks it up in a set of
|
||||||
// blockids captured during project load. If the blockid isn't found
|
// blockids captured during project load. If the blockid isn't found
|
||||||
// in the set, it will be deleted.
|
// in the set, it will be deleted.
|
||||||
void ProjectFileIO::InSet(sqlite3_context *context, int argc, sqlite3_value **argv)
|
void ProjectFileIO::isorphan(sqlite3_context *context, int argc, sqlite3_value **argv)
|
||||||
{
|
{
|
||||||
BlockIDs *blockids = (BlockIDs *) sqlite3_user_data(context);
|
BlockIDs *blockids = (BlockIDs *) sqlite3_user_data(context);
|
||||||
SampleBlockID blockid = sqlite3_value_int64(argv[0]);
|
SampleBlockID blockid = sqlite3_value_int64(argv[0]);
|
||||||
|
|
||||||
sqlite3_result_int(context, blockids->find(blockid) != blockids->end());
|
sqlite3_result_int(context, blockids->find(blockid) == blockids->end());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProjectFileIO::CheckForOrphans(BlockIDs &blockids)
|
bool ProjectFileIO::CheckForOrphans(BlockIDs &blockids)
|
||||||
@ -699,25 +701,18 @@ bool ProjectFileIO::CheckForOrphans(BlockIDs &blockids)
|
|||||||
auto db = DB();
|
auto db = DB();
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
auto cleanup = finally([&]
|
// Add our function that will verify blockid against the set of valid blockids
|
||||||
{
|
rc = sqlite3_create_function(db, "isorphan", 1, SQLITE_UTF8, &blockids, isorphan, nullptr, nullptr);
|
||||||
// Remove our function, whether it was successfully defined or not.
|
|
||||||
sqlite3_create_function(db, "inset", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, nullptr, nullptr, nullptr);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add the function used to verify each rows blockid against the set of active blockids
|
|
||||||
rc = sqlite3_create_function(db, "inset", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, &blockids, InSet, nullptr, nullptr);
|
|
||||||
if (rc != SQLITE_OK)
|
if (rc != SQLITE_OK)
|
||||||
{
|
{
|
||||||
wxLogDebug(wxT("Unable to add 'inset' function"));
|
//asdfasdf
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete all rows that are orphaned
|
// Delete all rows that are orphaned
|
||||||
rc = sqlite3_exec(db, "DELETE FROM sampleblocks WHERE NOT inset(blockid);", nullptr, nullptr, nullptr);
|
rc = sqlite3_exec(db, "DELETE FROM sampleblocks WHERE isorphan(blockid);", nullptr, nullptr, nullptr);
|
||||||
if (rc != SQLITE_OK)
|
if (rc != SQLITE_OK)
|
||||||
{
|
{
|
||||||
wxLogWarning(XO("Cleanup of orphan blocks failed").Translation());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -750,81 +745,32 @@ static int progress_callback(void *data)
|
|||||||
|
|
||||||
sqlite3 *ProjectFileIO::CopyTo(const FilePath &destpath)
|
sqlite3 *ProjectFileIO::CopyTo(const FilePath &destpath)
|
||||||
{
|
{
|
||||||
// Get access to the active tracklist
|
|
||||||
auto pProject = mpProject.lock();
|
|
||||||
if (!pProject)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto &tracklist = TrackList::Get(*pProject);
|
|
||||||
|
|
||||||
// Collect all active blockids
|
|
||||||
BlockIDs blockids;
|
|
||||||
for (auto wt : tracklist.Any<const WaveTrack>())
|
|
||||||
{
|
|
||||||
// Scan all clips within current track
|
|
||||||
for (const auto &clip : wt->GetAllClips())
|
|
||||||
{
|
|
||||||
// Scan all blockfiles within current clip
|
|
||||||
auto blocks = clip->GetSequenceBlockArray();
|
|
||||||
for (const auto &block : *blocks)
|
|
||||||
{
|
|
||||||
blockids.insert(block.sb->GetBlockID());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto db = DB();
|
auto db = DB();
|
||||||
sqlite3 *destdb = nullptr;
|
|
||||||
bool success = false;
|
|
||||||
int rc;
|
int rc;
|
||||||
ProgressResult res = ProgressResult::Success;
|
ProgressResult res = ProgressResult::Success;
|
||||||
|
|
||||||
// Cleanup in case things go awry
|
sqlite3_stmt *stmt = nullptr;
|
||||||
auto cleanup = finally([&]
|
auto cleanup = finally([&]
|
||||||
{
|
{
|
||||||
// Detach the destination database, whether it was successfully attached or not
|
if (stmt)
|
||||||
sqlite3_exec(db, "DETACH DATABASE dest;", nullptr, nullptr, nullptr);
|
|
||||||
|
|
||||||
// Remove our function, whether it was successfully defined or not.
|
|
||||||
sqlite3_create_function(db, "inset", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, nullptr, nullptr, nullptr);
|
|
||||||
|
|
||||||
if (!success)
|
|
||||||
{
|
{
|
||||||
sqlite3_close(destdb);
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
wxRemoveFile(destpath);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add the function used to verify each rows blockid against the set of active blockids
|
rc = sqlite3_prepare_v2(db, "VACUUM INTO ?;", -1, &stmt, 0);
|
||||||
rc = sqlite3_create_function(db, "inset", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, &blockids, InSet, nullptr, nullptr);
|
|
||||||
if (rc != SQLITE_OK)
|
if (rc != SQLITE_OK)
|
||||||
{
|
{
|
||||||
SetDBError(
|
SetDBError(
|
||||||
XO("Unable to add 'inset' function")
|
XO("Unable to prepare project file command")
|
||||||
);
|
);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach the destination database
|
rc = sqlite3_bind_text(stmt, 1, destpath.mb_str().data(), destpath.mb_str().length(), SQLITE_STATIC);
|
||||||
wxString sql;
|
|
||||||
sql.Printf("ATTACH DATABASE '%s' AS dest;", destpath);
|
|
||||||
|
|
||||||
rc = sqlite3_exec(db, sql.mb_str().data(), nullptr, nullptr, nullptr);
|
|
||||||
if (rc != SQLITE_OK)
|
if (rc != SQLITE_OK)
|
||||||
{
|
{
|
||||||
SetDBError(
|
THROW_INCONSISTENCY_EXCEPTION;
|
||||||
XO("Unable to attach destination database")
|
|
||||||
);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Install our schema into the new database
|
|
||||||
if (!InstallSchema(db, "dest"))
|
|
||||||
{
|
|
||||||
// Message already set
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -834,37 +780,27 @@ sqlite3 *ProjectFileIO::CopyTo(const FilePath &destpath)
|
|||||||
|
|
||||||
sqlite3_progress_handler(db, 10, progress_callback, &progress);
|
sqlite3_progress_handler(db, 10, progress_callback, &progress);
|
||||||
|
|
||||||
rc = sqlite3_exec(db,
|
rc = sqlite3_step(stmt);
|
||||||
"INSERT INTO dest.tags"
|
|
||||||
" SELECT * FROM main.tags;",
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr);
|
|
||||||
if (rc == SQLITE_OK)
|
|
||||||
{
|
|
||||||
rc = sqlite3_exec(db,
|
|
||||||
"INSERT INTO dest.sampleblocks"
|
|
||||||
" SELECT * FROM main.sampleblocks"
|
|
||||||
" WHERE inset(blockid);",
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlite3_progress_handler(db, 0, nullptr, nullptr);
|
sqlite3_progress_handler(db, 0, nullptr, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy failed
|
sqlite3_finalize(stmt);
|
||||||
if (rc != SQLITE_OK)
|
stmt = nullptr;
|
||||||
|
|
||||||
|
// VACUUMing failed
|
||||||
|
if (rc != SQLITE_DONE)
|
||||||
{
|
{
|
||||||
SetDBError(
|
SetDBError(
|
||||||
XO("Failed to copy project file")
|
XO("Project file copy failed")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
wxRemoveFile(destpath);
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the newly created database
|
sqlite3 *destdb = nullptr;
|
||||||
rc = sqlite3_open(destpath, &destdb);
|
rc = sqlite3_open(destpath, &destdb);
|
||||||
if (rc != SQLITE_OK)
|
if (rc != SQLITE_OK)
|
||||||
{
|
{
|
||||||
@ -872,12 +808,13 @@ sqlite3 *ProjectFileIO::CopyTo(const FilePath &destpath)
|
|||||||
XO("Failed to open copy of project file")
|
XO("Failed to open copy of project file")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
sqlite3_close(destdb);
|
||||||
|
|
||||||
|
wxRemoveFile(destpath);
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell cleanup everything is good to go
|
|
||||||
success = true;
|
|
||||||
|
|
||||||
return destdb;
|
return destdb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1454,6 +1391,14 @@ bool ProjectFileIO::SaveProject(const FilePath &fileName)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We need to remove the autosave info from the file since it is now
|
||||||
|
// clean and unmodified. Otherwise, it would be considered "recovered"
|
||||||
|
// when next opened.
|
||||||
|
if (!AutoSaveDelete())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Reaching this point defines success and all the rest are no-fail
|
// Reaching this point defines success and all the rest are no-fail
|
||||||
// operations:
|
// operations:
|
||||||
|
|
||||||
@ -1508,6 +1453,14 @@ bool ProjectFileIO::SaveCopy(const FilePath& fileName)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We need to remove the autosave info from the new DB since it is now
|
||||||
|
// clean and unmodified. Otherwise, it would be considered "recovered"
|
||||||
|
// when next opened.
|
||||||
|
if (!AutoSaveDelete(db))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Tell the finally block to behave
|
// Tell the finally block to behave
|
||||||
success = true;
|
success = true;
|
||||||
|
|
||||||
|
@ -150,17 +150,15 @@ private:
|
|||||||
bool GetBlob(const char *sql, wxMemoryBuffer &buffer);
|
bool GetBlob(const char *sql, wxMemoryBuffer &buffer);
|
||||||
|
|
||||||
bool CheckVersion();
|
bool CheckVersion();
|
||||||
bool InstallSchema(sqlite3 *db, const char *dbname = "main");
|
bool InstallSchema();
|
||||||
bool UpgradeSchema();
|
bool UpgradeSchema();
|
||||||
|
|
||||||
// Write project or autosave XML (binary) documents
|
// Write project or autosave XML (binary) documents
|
||||||
bool WriteDoc(const char *table, const ProjectSerializer &autosave, sqlite3 *db = nullptr);
|
bool WriteDoc(const char *table, const ProjectSerializer &autosave, sqlite3 *db = nullptr);
|
||||||
|
|
||||||
// Application defined function to verify blockid exists is in set of blockids
|
|
||||||
using BlockIDs = std::set<SampleBlockID>;
|
|
||||||
static void InSet(sqlite3_context *context, int argc, sqlite3_value **argv);
|
|
||||||
|
|
||||||
// Checks for orphan blocks. This will go away at a future date
|
// Checks for orphan blocks. This will go away at a future date
|
||||||
|
using BlockIDs = std::set<SampleBlockID>;
|
||||||
|
static void isorphan(sqlite3_context *context, int argc, sqlite3_value **argv);
|
||||||
bool CheckForOrphans(BlockIDs &blockids);
|
bool CheckForOrphans(BlockIDs &blockids);
|
||||||
|
|
||||||
// Return a database connection if successful, which caller must close
|
// Return a database connection if successful, which caller must close
|
||||||
|
@ -599,9 +599,10 @@ bool ProjectFileManager::SaveCopy(const FilePath &fileName /* = wxT("") */)
|
|||||||
|
|
||||||
if (!projectFileIO.SaveCopy(filename.GetFullPath()))
|
if (!projectFileIO.SaveCopy(filename.GetFullPath()))
|
||||||
{
|
{
|
||||||
|
// Overwrite disallowed. The destination project is open in another window.
|
||||||
AudacityMessageDialog m(
|
AudacityMessageDialog m(
|
||||||
nullptr,
|
nullptr,
|
||||||
XO("The project was not saved. See Help->Diagnostics->Show Log for more info."),
|
XO("The project will not saved because the selected project is open in another window.\nPlease try again and select an original name."),
|
||||||
XO("Error Saving Project"),
|
XO("Error Saving Project"),
|
||||||
wxOK | wxICON_ERROR);
|
wxOK | wxICON_ERROR);
|
||||||
|
|
||||||
|
@ -705,7 +705,7 @@ bool AUPImportFileHandle::HandleProject(XMLTagHandler *&handler)
|
|||||||
if (projName.empty())
|
if (projName.empty())
|
||||||
{
|
{
|
||||||
AudacityMessageBox(
|
AudacityMessageBox(
|
||||||
XO("Couldn't find the project data folder: \"%s\"").Format(value),
|
XO("Couldn't find the project data folder: \"%s\"").Format(*value),
|
||||||
XO("Error Opening Project"),
|
XO("Error Opening Project"),
|
||||||
wxOK | wxCENTRE,
|
wxOK | wxCENTRE,
|
||||||
&window);
|
&window);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user