mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-02 08:39:46 +02:00
AUP3: Detect and delete orphan blocks at startup
This commit is contained in:
parent
f7d9513f8d
commit
af6a23696b
@ -501,28 +501,26 @@ int ProjectFileIO::Exec(const char *query, ExecCB callback, wxString *result)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString ProjectFileIO::GetValue(const char *sql)
|
bool ProjectFileIO::GetValue(const char *sql, wxString &result)
|
||||||
{
|
{
|
||||||
auto getresult = [&](wxString *result, int cols, char **vals, char **names)
|
auto getresult = [](wxString *result, int cols, char **vals, char **names)
|
||||||
{
|
{
|
||||||
if (cols == 1 && vals[0])
|
if (cols == 1 && vals[0])
|
||||||
{
|
{
|
||||||
result->append(vals[0]);
|
result->assign(vals[0]);
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SQLITE_ABORT;
|
return SQLITE_ABORT;
|
||||||
};
|
};
|
||||||
|
|
||||||
wxString value;
|
int rc = Exec(sql, getresult, &result);
|
||||||
|
|
||||||
int rc = Exec(sql, getresult, &value);
|
|
||||||
if (rc != SQLITE_OK)
|
if (rc != SQLITE_OK)
|
||||||
{
|
{
|
||||||
// Message already captured
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProjectFileIO::GetBlob(const char *sql, wxMemoryBuffer &buffer)
|
bool ProjectFileIO::GetBlob(const char *sql, wxMemoryBuffer &buffer)
|
||||||
@ -549,6 +547,13 @@ bool ProjectFileIO::GetBlob(const char *sql, wxMemoryBuffer &buffer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
rc = sqlite3_step(stmt);
|
rc = sqlite3_step(stmt);
|
||||||
|
|
||||||
|
// A row wasn't found...not an error
|
||||||
|
if (rc == SQLITE_DONE)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (rc != SQLITE_ROW)
|
if (rc != SQLITE_ROW)
|
||||||
{
|
{
|
||||||
SetDBError(
|
SetDBError(
|
||||||
@ -561,6 +566,7 @@ bool ProjectFileIO::GetBlob(const char *sql, wxMemoryBuffer &buffer)
|
|||||||
const void *blob = sqlite3_column_blob(stmt, 0);
|
const void *blob = sqlite3_column_blob(stmt, 0);
|
||||||
int size = sqlite3_column_bytes(stmt, 0);
|
int size = sqlite3_column_bytes(stmt, 0);
|
||||||
|
|
||||||
|
buffer.Clear();
|
||||||
buffer.AppendData(blob, size);
|
buffer.AppendData(blob, size);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -572,43 +578,40 @@ bool ProjectFileIO::CheckVersion()
|
|||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
// Install our schema if this is an empty DB
|
// Install our schema if this is an empty DB
|
||||||
long count = -1;
|
wxString result;
|
||||||
GetValue("SELECT Count(*) FROM sqlite_master WHERE type='table';").ToLong(&count);
|
if (!GetValue("SELECT Count(*) FROM sqlite_master WHERE type='table';", result))
|
||||||
if (count == -1)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the return count is zero, then there are no tables defined, so this
|
// If the return count is zero, then there are no tables defined, so this
|
||||||
// must be a new project file.
|
// must be a new project file.
|
||||||
if (count == 0)
|
if (wxStrtol<char **>(result, nullptr, 10) == 0)
|
||||||
{
|
{
|
||||||
return InstallSchema();
|
return InstallSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for our application ID
|
// Check for our application ID
|
||||||
long appid = -1;
|
if (!GetValue("PRAGMA application_ID;", result))
|
||||||
GetValue("PRAGMA application_ID;").ToLong(&appid);
|
|
||||||
if (appid == -1)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It's a database that SQLite recognizes, but it's not one of ours
|
// It's a database that SQLite recognizes, but it's not one of ours
|
||||||
if (appid != ProjectFileID)
|
if (wxStrtoul<char **>(result, nullptr, 10) != ProjectFileID)
|
||||||
{
|
{
|
||||||
SetError(XO("This is not an Audacity project file"));
|
SetError(XO("This is not an Audacity project file"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the project file version
|
// Get the project file version
|
||||||
long version = -1;
|
if (!GetValue("PRAGMA user_version;", result))
|
||||||
GetValue("PRAGMA user_version;").ToLong(&version);
|
|
||||||
if (version == -1)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long version = wxStrtol<char **>(result, nullptr, 10);
|
||||||
|
|
||||||
// Project file version is higher than ours. We will refuse to
|
// Project file version is higher than ours. We will refuse to
|
||||||
// process it since we can't trust anything about it.
|
// process it since we can't trust anything about it.
|
||||||
if (version > ProjectFileVersion)
|
if (version > ProjectFileVersion)
|
||||||
@ -658,6 +661,53 @@ bool ProjectFileIO::UpgradeSchema()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProjectFileIO::LoadedBlock(int64_t sbid)
|
||||||
|
{
|
||||||
|
if (sbid > mHighestBlockID)
|
||||||
|
{
|
||||||
|
mHighestBlockID = sbid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProjectFileIO::CheckForOrphans()
|
||||||
|
{
|
||||||
|
auto db = DB();
|
||||||
|
|
||||||
|
char sql[256];
|
||||||
|
sqlite3_snprintf(sizeof(sql),
|
||||||
|
sql,
|
||||||
|
"DELETE FROM sampleblocks WHERE blockid > %lld",
|
||||||
|
mHighestBlockID);
|
||||||
|
|
||||||
|
char *errmsg = nullptr;
|
||||||
|
|
||||||
|
int rc = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
|
||||||
|
|
||||||
|
if (errmsg)
|
||||||
|
{
|
||||||
|
wxLogDebug(wxT("Failed to delete orphaned blocks: %s"), errmsg);
|
||||||
|
sqlite3_free(errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc != SQLITE_OK)
|
||||||
|
{
|
||||||
|
SetDBError(
|
||||||
|
XO("Failed to delete orphaned blocks")
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int changes = sqlite3_changes(db);
|
||||||
|
if (changes > 0)
|
||||||
|
{
|
||||||
|
wxLogInfo(XO("Total orphan blocks deleted %d").Translation(), changes);
|
||||||
|
mRecovered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
sqlite3 *ProjectFileIO::CopyTo(const FilePath &destpath)
|
sqlite3 *ProjectFileIO::CopyTo(const FilePath &destpath)
|
||||||
{
|
{
|
||||||
auto db = DB();
|
auto db = DB();
|
||||||
@ -1150,15 +1200,22 @@ bool ProjectFileIO::LoadProject(const FilePath &fileName)
|
|||||||
// Get the autosave doc, if any
|
// Get the autosave doc, if any
|
||||||
bool wasAutosave = false;
|
bool wasAutosave = false;
|
||||||
wxMemoryBuffer buffer;
|
wxMemoryBuffer buffer;
|
||||||
if (GetBlob("SELECT dict || doc FROM autosave WHERE id = 1;", buffer))
|
if (!GetBlob("SELECT dict || doc FROM autosave WHERE id = 1;", buffer))
|
||||||
|
{
|
||||||
|
// Error already set
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.GetDataLen() > 0)
|
||||||
{
|
{
|
||||||
doc = AutoSaveFile::Decode(buffer);
|
doc = AutoSaveFile::Decode(buffer);
|
||||||
wasAutosave = true;
|
wasAutosave = true;
|
||||||
}
|
}
|
||||||
// Otherwise, get the project doc
|
// Otherwise, get the project doc
|
||||||
else
|
else if (!GetValue("SELECT doc FROM project;", doc))
|
||||||
{
|
{
|
||||||
doc = GetValue("SELECT doc FROM project;");
|
// Error already set
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doc.empty())
|
if (doc.empty())
|
||||||
@ -1168,6 +1225,9 @@ bool ProjectFileIO::LoadProject(const FilePath &fileName)
|
|||||||
|
|
||||||
XMLFileReader xmlFile;
|
XMLFileReader xmlFile;
|
||||||
|
|
||||||
|
// Reset the highest blockid
|
||||||
|
mHighestBlockID = 0;
|
||||||
|
|
||||||
success = xmlFile.ParseString(this, doc);
|
success = xmlFile.ParseString(this, doc);
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
@ -1180,6 +1240,16 @@ bool ProjectFileIO::LoadProject(const FilePath &fileName)
|
|||||||
|
|
||||||
// Remember if it was recovered or not
|
// Remember if it was recovered or not
|
||||||
mRecovered = wasAutosave;
|
mRecovered = wasAutosave;
|
||||||
|
|
||||||
|
// Check for orphans blocks...set mRecovered if any deleted
|
||||||
|
if (mHighestBlockID > 0)
|
||||||
|
{
|
||||||
|
if (!CheckForOrphans())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mRecovered)
|
if (mRecovered)
|
||||||
{
|
{
|
||||||
mModified = true;
|
mModified = true;
|
||||||
@ -1188,9 +1258,13 @@ bool ProjectFileIO::LoadProject(const FilePath &fileName)
|
|||||||
// A previously saved project will have a document in the project table, so
|
// A previously saved project will have a document in the project table, so
|
||||||
// we use that knowledge to determine if this file is an unsaved/temporary
|
// we use that knowledge to determine if this file is an unsaved/temporary
|
||||||
// file or not
|
// file or not
|
||||||
long count = 0;
|
wxString result;
|
||||||
GetValue("SELECT Count(*) FROM project;").ToLong(&count);
|
if (!GetValue("SELECT Count(*) FROM project;", result))
|
||||||
mTemporary = (count != 1);
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mTemporary = (wxStrtol<char **>(result, nullptr, 10) != 1);
|
||||||
|
|
||||||
SetFileName(fileName);
|
SetFileName(fileName);
|
||||||
|
|
||||||
|
@ -98,6 +98,8 @@ public:
|
|||||||
void Bypass(bool bypass);
|
void Bypass(bool bypass);
|
||||||
bool ShouldBypass();
|
bool ShouldBypass();
|
||||||
|
|
||||||
|
void LoadedBlock(int64_t blockID);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// XMLTagHandler callback methods
|
// XMLTagHandler callback methods
|
||||||
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override;
|
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override;
|
||||||
@ -139,13 +141,16 @@ private:
|
|||||||
bool TransactionCommit(const wxString &name);
|
bool TransactionCommit(const wxString &name);
|
||||||
bool TransactionRollback(const wxString &name);
|
bool TransactionRollback(const wxString &name);
|
||||||
|
|
||||||
wxString GetValue(const char *sql);
|
bool GetValue(const char *sql, wxString &value);
|
||||||
bool GetBlob(const char *sql, wxMemoryBuffer &buffer);
|
bool GetBlob(const char *sql, wxMemoryBuffer &buffer);
|
||||||
|
|
||||||
bool CheckVersion();
|
bool CheckVersion();
|
||||||
bool InstallSchema();
|
bool InstallSchema();
|
||||||
bool UpgradeSchema();
|
bool UpgradeSchema();
|
||||||
|
|
||||||
|
// Checks for orphan blocks. This will go away at a future date
|
||||||
|
bool CheckForOrphans();
|
||||||
|
|
||||||
// Return a database connection if successful, which caller must close
|
// Return a database connection if successful, which caller must close
|
||||||
sqlite3 *CopyTo(const FilePath &destpath);
|
sqlite3 *CopyTo(const FilePath &destpath);
|
||||||
|
|
||||||
@ -177,6 +182,9 @@ private:
|
|||||||
TranslatableString mLastError;
|
TranslatableString mLastError;
|
||||||
TranslatableString mLibraryError;
|
TranslatableString mLibraryError;
|
||||||
|
|
||||||
|
// Track the highest blockid while loading a project
|
||||||
|
int64_t mHighestBlockID;
|
||||||
|
|
||||||
friend SqliteSampleBlock;
|
friend SqliteSampleBlock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ public:
|
|||||||
void SaveXML(XMLWriter &xmlFile) override;
|
void SaveXML(XMLWriter &xmlFile) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool Load(SampleBlockID sbid);
|
void Load(SampleBlockID sbid);
|
||||||
bool GetSummary(float *dest,
|
bool GetSummary(float *dest,
|
||||||
size_t frameoffset,
|
size_t frameoffset,
|
||||||
size_t numframes,
|
size_t numframes,
|
||||||
@ -186,6 +186,11 @@ SampleBlockPtr SqliteSampleBlockFactory::DoCreateFromXML(
|
|||||||
{
|
{
|
||||||
// This may throw
|
// This may throw
|
||||||
sb->Load((SampleBlockID) nValue);
|
sb->Load((SampleBlockID) nValue);
|
||||||
|
|
||||||
|
// Tell the IO manager the blockid we just loaded so it can track
|
||||||
|
// the highest one encountered.
|
||||||
|
mpIO->LoadedBlock(nValue);
|
||||||
|
|
||||||
found++;
|
found++;
|
||||||
}
|
}
|
||||||
else if (wxStrcmp(attr, wxT("samplecount")) == 0)
|
else if (wxStrcmp(attr, wxT("samplecount")) == 0)
|
||||||
@ -518,7 +523,7 @@ size_t SqliteSampleBlock::GetBlob(void *dest,
|
|||||||
return srcbytes;
|
return srcbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SqliteSampleBlock::Load(SampleBlockID sbid)
|
void SqliteSampleBlock::Load(SampleBlockID sbid)
|
||||||
{
|
{
|
||||||
auto db = mIO.DB();
|
auto db = mIO.DB();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user