mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-01 00:19:27 +02:00
First stab at responding to whitebox review
In some cases, I've simply left the REVIEW: in and responded to it. In other cases, I've resolved what was mentioned in the review.
This commit is contained in:
parent
3d632d397d
commit
c9d37675f7
@ -167,6 +167,7 @@ bool DBConnection::Close()
|
|||||||
// We're done with the prepared statements
|
// We're done with the prepared statements
|
||||||
for (auto stmt : mStatements)
|
for (auto stmt : mStatements)
|
||||||
{
|
{
|
||||||
|
// No need to check return code.
|
||||||
sqlite3_finalize(stmt.second);
|
sqlite3_finalize(stmt.second);
|
||||||
}
|
}
|
||||||
mStatements.clear();
|
mStatements.clear();
|
||||||
@ -179,6 +180,9 @@ bool DBConnection::Close()
|
|||||||
// the hook, but who knows if that would work either.
|
// the hook, but who knows if that would work either.
|
||||||
//
|
//
|
||||||
// Should we throw an error???
|
// Should we throw an error???
|
||||||
|
//
|
||||||
|
// LLL: Probably not worthwhile since the DB will just be recovered when
|
||||||
|
// next opened.
|
||||||
}
|
}
|
||||||
|
|
||||||
mDB = nullptr;
|
mDB = nullptr;
|
||||||
@ -222,7 +226,7 @@ bool DBConnection::ModeConfig(sqlite3 *db, const char *schema, const char *confi
|
|||||||
// Set the configuration
|
// Set the configuration
|
||||||
rc = sqlite3_exec(db, sql, nullptr, nullptr, nullptr);
|
rc = sqlite3_exec(db, sql, nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
return rc != SQLITE_OK;
|
return rc == SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3 *DBConnection::DB()
|
sqlite3 *DBConnection::DB()
|
||||||
|
@ -726,6 +726,7 @@ bool ProjectFileIO::CopyTo(const FilePath &destpath,
|
|||||||
|
|
||||||
if (!Query("SELECT blockid FROM sampleblocks;", cb))
|
if (!Query("SELECT blockid FROM sampleblocks;", cb))
|
||||||
{
|
{
|
||||||
|
// Error message already captured.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -738,7 +739,7 @@ bool ProjectFileIO::CopyTo(const FilePath &destpath,
|
|||||||
auto db = DB();
|
auto db = DB();
|
||||||
Connection destConn = nullptr;
|
Connection destConn = nullptr;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
int rc;
|
int rc = SQLITE_OK;
|
||||||
ProgressResult res = ProgressResult::Success;
|
ProgressResult res = ProgressResult::Success;
|
||||||
|
|
||||||
// Cleanup in case things go awry
|
// Cleanup in case things go awry
|
||||||
@ -753,10 +754,22 @@ bool ProjectFileIO::CopyTo(const FilePath &destpath,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rollback transaction in case one was active.
|
// Rollback transaction in case one was active.
|
||||||
// REVIEW: Rollback could fail. Does that matter?
|
// If this fails (probably due to memory or disk space), the transaction will
|
||||||
sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr);
|
// (presumably) stil be active, so further updates to the project file will
|
||||||
|
// fail as well. Not really much we can do about it except tell the user.
|
||||||
|
auto result = sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
// And detach the outbound DB in case it's attached.
|
// Only capture the error if there wasn't a previous error
|
||||||
|
if (result != SQLITE_OK && (rc == SQLITE_DONE || rc == SQLITE_OK))
|
||||||
|
{
|
||||||
|
SetDBError(
|
||||||
|
XO("Failed to rollback transaction during import")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// And detach the outbound DB in case (if it's attached). Don't check for
|
||||||
|
// errors since it may not be attached. But, if it is and the DETACH fails,
|
||||||
|
// subsequent CopyTo() actions will fail until Audacity is relaunched.
|
||||||
sqlite3_exec(db, "DETACH DATABASE outbound;", nullptr, nullptr, nullptr);
|
sqlite3_exec(db, "DETACH DATABASE outbound;", nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
// RemoveProject not necessary to clean up attached database
|
// RemoveProject not necessary to clean up attached database
|
||||||
@ -781,7 +794,14 @@ bool ProjectFileIO::CopyTo(const FilePath &destpath,
|
|||||||
//
|
//
|
||||||
// NOTE: Between the above attach and setting the mode here, a normal DELETE
|
// NOTE: Between the above attach and setting the mode here, a normal DELETE
|
||||||
// mode journal will be used and will briefly appear in the filesystem.
|
// mode journal will be used and will briefly appear in the filesystem.
|
||||||
pConn->FastMode("outbound");
|
if (!pConn->FastMode("outbound"))
|
||||||
|
{
|
||||||
|
SetDBError(
|
||||||
|
XO("Unable to switch to fast journaling mode")
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Install our schema into the new database
|
// Install our schema into the new database
|
||||||
if (!InstallSchema(db, "outbound"))
|
if (!InstallSchema(db, "outbound"))
|
||||||
@ -812,6 +832,7 @@ bool ProjectFileIO::CopyTo(const FilePath &destpath,
|
|||||||
{
|
{
|
||||||
if (stmt)
|
if (stmt)
|
||||||
{
|
{
|
||||||
|
// No need to check return code
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -853,9 +874,14 @@ bool ProjectFileIO::CopyTo(const FilePath &destpath,
|
|||||||
for (auto blockid : blockids)
|
for (auto blockid : blockids)
|
||||||
{
|
{
|
||||||
// Bind statement parameters
|
// Bind statement parameters
|
||||||
if (sqlite3_bind_int64(stmt, 1, blockid) != SQLITE_OK)
|
rc = sqlite3_bind_int64(stmt, 1, blockid);
|
||||||
|
if (rc != SQLITE_OK)
|
||||||
{
|
{
|
||||||
wxASSERT_MSG(false, wxT("Binding failed...bug!!!"));
|
SetDBError(
|
||||||
|
XO("Failed to bind SQL parameter")
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process it
|
// Process it
|
||||||
@ -1153,14 +1179,15 @@ void ProjectFileIO::Compact(
|
|||||||
// Haven't compacted yet
|
// Haven't compacted yet
|
||||||
mWasCompacted = false;
|
mWasCompacted = false;
|
||||||
|
|
||||||
// Assume we have unused block until we found out otherwise. That way cleanup
|
// Assume we have unused blocks until we found out otherwise. That way cleanup
|
||||||
// at project close time will still occur.
|
// at project close time will still occur.
|
||||||
mHadUnused = true;
|
mHadUnused = true;
|
||||||
|
|
||||||
// Don't compact if this is a temporary project or if it's determined there are not
|
// If forcing compaction, bypass inspection.
|
||||||
// enough unused blocks to make it worthwhile
|
|
||||||
if (!force)
|
if (!force)
|
||||||
{
|
{
|
||||||
|
// Don't compact if this is a temporary project or if it's determined there are not
|
||||||
|
// enough unused blocks to make it worthwhile.
|
||||||
if (IsTemporary() || !ShouldCompact(tracks))
|
if (IsTemporary() || !ShouldCompact(tracks))
|
||||||
{
|
{
|
||||||
// Delete the AutoSave doc it if exists
|
// Delete the AutoSave doc it if exists
|
||||||
@ -1171,6 +1198,8 @@ void ProjectFileIO::Compact(
|
|||||||
// at the last saved state.
|
// at the last saved state.
|
||||||
// REVIEW: Could the autosave file be corrupt though at that point, and so
|
// REVIEW: Could the autosave file be corrupt though at that point, and so
|
||||||
// prevent recovery?
|
// prevent recovery?
|
||||||
|
// LLL: I believe Paul is correct since it's deleted with a single SQLite
|
||||||
|
// transaction. The next time the file opens will just invoke recovery.
|
||||||
(void) AutoSaveDelete();
|
(void) AutoSaveDelete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1185,6 +1214,8 @@ void ProjectFileIO::Compact(
|
|||||||
// Copy the original database to a new database. Only prune sample blocks if
|
// Copy the original database to a new database. Only prune sample blocks if
|
||||||
// we have a tracklist.
|
// we have a tracklist.
|
||||||
// REVIEW: Compact can fail on the CopyTo with no error messages. That's OK?
|
// REVIEW: Compact can fail on the CopyTo with no error messages. That's OK?
|
||||||
|
// LLL: We could display an error message or just ignore the failure and allow
|
||||||
|
// the file to be compacted the next time it's saved.
|
||||||
if (CopyTo(tempName, XO("Compacting project"), IsTemporary(), !tracks.empty(), tracks))
|
if (CopyTo(tempName, XO("Compacting project"), IsTemporary(), !tracks.empty(), tracks))
|
||||||
{
|
{
|
||||||
// Must close the database to rename it
|
// Must close the database to rename it
|
||||||
@ -1657,13 +1688,22 @@ bool ProjectFileIO::ImportProject(const FilePath &fileName)
|
|||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
bool restore = true;
|
bool restore = true;
|
||||||
int rc;
|
int rc = SQLITE_OK;
|
||||||
|
|
||||||
// Ensure the inbound database gets detached
|
// Ensure the inbound database gets detached
|
||||||
auto detach = finally([&]
|
auto detach = finally([&]
|
||||||
{
|
{
|
||||||
// REVIEW: Just a memory leak if this fails?
|
// If this DETACH fails, subsequent project imports will fail until Audacity
|
||||||
sqlite3_exec(db, "DETACH DATABASE inbound;", nullptr, nullptr, nullptr);
|
// is relaunched.
|
||||||
|
auto result = sqlite3_exec(db, "DETACH DATABASE inbound;", nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
|
// Only capture the error if there wasn't a previous error
|
||||||
|
if (result != SQLITE_OK && (rc == SQLITE_DONE || rc == SQLITE_OK))
|
||||||
|
{
|
||||||
|
SetDBError(
|
||||||
|
XO("Failed to detach project during import")
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Attach the inbound project file
|
// Attach the inbound project file
|
||||||
@ -1723,12 +1763,19 @@ bool ProjectFileIO::ImportProject(const FilePath &fileName)
|
|||||||
wxXmlDocument doc;
|
wxXmlDocument doc;
|
||||||
if (!doc.Load(in))
|
if (!doc.Load(in))
|
||||||
{
|
{
|
||||||
|
SetError(XO("Unable to parse the project document"));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the root ("project") node
|
// Get the root ("project") node
|
||||||
wxXmlNode *root = doc.GetRoot();
|
wxXmlNode *root = doc.GetRoot();
|
||||||
wxASSERT(root->GetName().IsSameAs(wxT("project")));
|
if (!root->GetName().IsSameAs(wxT("project")))
|
||||||
|
{
|
||||||
|
SetError(XO("Missing root node in project document"));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Soft delete all non-essential attributes to prevent updating the active
|
// Soft delete all non-essential attributes to prevent updating the active
|
||||||
// project. This takes advantage of the knowledge that when a project is
|
// project. This takes advantage of the knowledge that when a project is
|
||||||
@ -1805,6 +1852,7 @@ bool ProjectFileIO::ImportProject(const FilePath &fileName)
|
|||||||
// Ensure the prepared statement gets cleaned up
|
// Ensure the prepared statement gets cleaned up
|
||||||
if (stmt)
|
if (stmt)
|
||||||
{
|
{
|
||||||
|
// Ignore the return code since it will have already been captured below.
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1836,7 +1884,14 @@ bool ProjectFileIO::ImportProject(const FilePath &fileName)
|
|||||||
wxLongLong_t count = 0;
|
wxLongLong_t count = 0;
|
||||||
wxLongLong_t total = blocknodes.size();
|
wxLongLong_t total = blocknodes.size();
|
||||||
|
|
||||||
sqlite3_exec(db, "BEGIN;", nullptr, nullptr, nullptr);
|
rc = sqlite3_exec(db, "BEGIN;", nullptr, nullptr, nullptr);
|
||||||
|
if (rc != SQLITE_OK)
|
||||||
|
{
|
||||||
|
SetDBError(
|
||||||
|
XO("Unable to start a transaction during import")
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Copy all the sample blocks from the inbound project file into
|
// Copy all the sample blocks from the inbound project file into
|
||||||
// the active one, while remembering which were copied.
|
// the active one, while remembering which were copied.
|
||||||
@ -1859,11 +1914,14 @@ bool ProjectFileIO::ImportProject(const FilePath &fileName)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Bind statement parameters
|
// Bind statement parameters
|
||||||
// Might return SQL_MISUSE which means it's our mistake that we violated
|
rc = sqlite3_bind_int64(stmt, 1, blockid);
|
||||||
// preconditions; should return SQL_OK which is 0
|
if (rc != SQLITE_OK)
|
||||||
if (sqlite3_bind_int64(stmt, 1, blockid) != SQLITE_OK)
|
|
||||||
{
|
{
|
||||||
wxASSERT_MSG(false, wxT("Binding failed...bug!!!"));
|
SetDBError(
|
||||||
|
XO("Failed to bind SQL parameter")
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process it
|
// Process it
|
||||||
@ -1898,13 +1956,35 @@ bool ProjectFileIO::ImportProject(const FilePath &fileName)
|
|||||||
// import or it completed, then we continue on.
|
// import or it completed, then we continue on.
|
||||||
if (rc != SQLITE_DONE || result == ProgressResult::Cancelled || result == ProgressResult::Failed)
|
if (rc != SQLITE_DONE || result == ProgressResult::Cancelled || result == ProgressResult::Failed)
|
||||||
{
|
{
|
||||||
sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr);
|
// If this fails (probably due to memory or disk space), the transaction will
|
||||||
|
// (presumably) stil be active, so further updates to the project file will
|
||||||
|
// fail as well. Not really much we can do about it except tell the user.
|
||||||
|
auto result = sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
|
// Only capture the error if there wasn't a previous error
|
||||||
|
if (result != SQLITE_OK && rc == SQLITE_DONE)
|
||||||
|
{
|
||||||
|
SetDBError(
|
||||||
|
XO("Failed to rollback transaction during import")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go ahead and commit now
|
// Go ahead and commit now. If this fails (probably due to memory or disk space),
|
||||||
// REVIEW: Return code not checked.
|
// the transaction will (presumably) stil be active, so further updates to the
|
||||||
sqlite3_exec(db, "COMMIT;", nullptr, nullptr, nullptr);
|
// project file will fail as well. Not really much we can do about it except tell
|
||||||
|
// the user.
|
||||||
|
rc = sqlite3_exec(db, "COMMIT;", nullptr, nullptr, nullptr);
|
||||||
|
if (rc != SQLITE_OK)
|
||||||
|
{
|
||||||
|
SetDBError(
|
||||||
|
XO("Unable to commit transaction during import")
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Copy over tags...likely to produce duplicates...needs work once used
|
// Copy over tags...likely to produce duplicates...needs work once used
|
||||||
rc = sqlite3_exec(db,
|
rc = sqlite3_exec(db,
|
||||||
@ -1922,9 +2002,17 @@ bool ProjectFileIO::ImportProject(const FilePath &fileName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recreate the project doc with the revisions we've made above
|
// Recreate the project doc with the revisions we've made above. If this fails
|
||||||
|
// it's probably due to memory.
|
||||||
wxStringOutputStream output;
|
wxStringOutputStream output;
|
||||||
doc.Save(output);
|
if (!doc.Save(output))
|
||||||
|
{
|
||||||
|
SetError(
|
||||||
|
XO("Unable to recreate project information.")
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Now load the document as normal
|
// Now load the document as normal
|
||||||
XMLFileReader xmlFile;
|
XMLFileReader xmlFile;
|
||||||
@ -2072,14 +2160,17 @@ bool ProjectFileIO::UpdateSaved(const TrackList *tracks)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Autosave no longer needed
|
// Autosave no longer needed
|
||||||
// REVIEW: Failure OK?
|
if (!AutoSaveDelete())
|
||||||
AutoSaveDelete();
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// REVIEW: This function is believed to report an error to the user in all cases
|
// REVIEW: This function is believed to report an error to the user in all cases
|
||||||
// of failure. Callers are believed not to need to do so if they receive 'false'.
|
// of failure. Callers are believed not to need to do so if they receive 'false'.
|
||||||
|
// LLL: All failures checks should now be displaying an error.
|
||||||
bool ProjectFileIO::SaveProject(
|
bool ProjectFileIO::SaveProject(
|
||||||
const FilePath &fileName, const TrackList *lastSaved)
|
const FilePath &fileName, const TrackList *lastSaved)
|
||||||
{
|
{
|
||||||
@ -2191,19 +2282,39 @@ bool ProjectFileIO::SaveProject(
|
|||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
SetDBError(
|
// Additional help via a Help button links to the manual.
|
||||||
XO("Failed to open copy of project file")
|
ShowErrorDialog(nullptr,
|
||||||
);
|
XO("Error Saving Project"),
|
||||||
|
XO("The project failed to open, possibly to due limited space\n"
|
||||||
|
"on the storage device.\n\n%s").Format(GetLastError()),
|
||||||
|
"Error:_Disk_full_or_not_writable");
|
||||||
|
|
||||||
newConn = nullptr;
|
newConn = nullptr;
|
||||||
|
|
||||||
|
// Clean up the destination project
|
||||||
|
wxRemoveFile(fileName);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Autosave no longer needed in original project file
|
// Autosave no longer needed in original project file.
|
||||||
// REVIEW: Failure OK?
|
if (!AutoSaveDelete())
|
||||||
AutoSaveDelete();
|
{
|
||||||
|
// Additional help via a Help button links to the manual.
|
||||||
|
ShowErrorDialog(nullptr,
|
||||||
|
XO("Error Saving Project"),
|
||||||
|
XO("Unable to remove autosave information, possibly to due limited space\n"
|
||||||
|
"on the storage device.\n\n%s").Format(GetLastError()),
|
||||||
|
"Error:_Disk_full_or_not_writable");
|
||||||
|
|
||||||
|
newConn = nullptr;
|
||||||
|
|
||||||
|
// Clean up the destination project
|
||||||
|
wxRemoveFile(fileName);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (lastSaved) {
|
if (lastSaved) {
|
||||||
// Bug2605: Be sure not to save orphan blocks
|
// Bug2605: Be sure not to save orphan blocks
|
||||||
@ -2215,11 +2326,13 @@ bool ProjectFileIO::SaveProject(
|
|||||||
mRecovered = recovered;
|
mRecovered = recovered;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to compact the original project file
|
// Try to compact the original project file.
|
||||||
auto empty = TrackList::Create(&mProject);
|
auto empty = TrackList::Create(&mProject);
|
||||||
Compact( { lastSaved ? lastSaved : empty.get() }, true );
|
Compact( { lastSaved ? lastSaved : empty.get() }, true );
|
||||||
|
|
||||||
// Save to close the original project file now
|
// Safe to close the original project file now. Not much we can do if this fails,
|
||||||
|
// but we should still be in good shape since we'll be switching to the newly
|
||||||
|
// saved database below.
|
||||||
CloseProject();
|
CloseProject();
|
||||||
|
|
||||||
// And make it the active project file
|
// And make it the active project file
|
||||||
@ -2510,6 +2623,7 @@ int64_t ProjectFileIO::GetDiskUsage(DBConnection &conn, SampleBlockID blockid /*
|
|||||||
{
|
{
|
||||||
// REVIEW: Likely harmless failure - says size is zero on
|
// REVIEW: Likely harmless failure - says size is zero on
|
||||||
// this error.
|
// this error.
|
||||||
|
// LLL: Yea, but not much else we can do.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1151,7 +1151,6 @@ bool ProjectFileManager::Import(
|
|||||||
|
|
||||||
// Handle AUP3 ("project") files directly
|
// Handle AUP3 ("project") files directly
|
||||||
if (fileName.AfterLast('.').IsSameAs(wxT("aup3"), false)) {
|
if (fileName.AfterLast('.').IsSameAs(wxT("aup3"), false)) {
|
||||||
// REVIEW: If ImportProject fails, will the failure be reported?
|
|
||||||
if (projectFileIO.ImportProject(fileName)) {
|
if (projectFileIO.ImportProject(fileName)) {
|
||||||
auto &history = ProjectHistory::Get(project);
|
auto &history = ProjectHistory::Get(project);
|
||||||
|
|
||||||
@ -1170,6 +1169,16 @@ bool ProjectFileManager::Import(
|
|||||||
FileHistory::Global().Append(fileName);
|
FileHistory::Global().Append(fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
errorMessage = projectFileIO.GetLastError();
|
||||||
|
if (errorMessage.empty()) {
|
||||||
|
errorMessage = XO("Failed to import project");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional help via a Help button links to the manual.
|
||||||
|
ShowErrorDialog(&GetProjectFrame( project ),XO("Error Importing"),
|
||||||
|
errorMessage, wxT("Importing_Audio"));
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user