mirror of
https://github.com/cookiengineer/audacity
synced 2025-11-23 17:30:17 +01:00
Sqlite errors (#601)
* Null checks on return from std::make_shared are unnecessary... ... instead std::bad_alloc would be thrown in case of memory exhaustion, which we don't try to recover from. * Restore uses of the mayThrow arguments in Sequence... ... that became unused at commitd39590cf41. It's important to ignore exceptions from SampleBlocks when only displaying, not editing or playing, and just treat missing data as silence. Pass the boolean into the SampleBlock routines. But the throwing of exceptions is not yet implemented. * SampleBlockFactory functions guaranteed to return non-null or throw... ... which corrects Sequence.cpp, which was assuming non-null results. This supplies the throw statements that the previous commit comment says were still lacking. This corrects the absence of checks of returns from sql_bind_... function calls in SqliteSampleBlock.cpp. (Other calls remain to be checked elsewhere.) User visible error messages, carried by the exceptions, might be improved. * Restore the try/catch in AutoSaveFile::Decode... ... which was introduced at2ba17c78d6but removed atd39590cf41, yet without removing the throw statement which left the program vulnerable to abrupt termination instead of graceful failure, when uninterpretable auto save contents are detected.
This commit is contained in:
@@ -29,17 +29,17 @@ public:
|
||||
void Unlock() override;
|
||||
void CloseLock() override;
|
||||
|
||||
bool SetSamples(samplePtr src, size_t numsamples, sampleFormat srcformat);
|
||||
void SetSamples(samplePtr src, size_t numsamples, sampleFormat srcformat);
|
||||
|
||||
bool SetSilent(size_t numsamples, sampleFormat srcformat);
|
||||
void SetSilent(size_t numsamples, sampleFormat srcformat);
|
||||
|
||||
bool Commit();
|
||||
void Commit();
|
||||
|
||||
void Delete();
|
||||
|
||||
SampleBlockID GetBlockID() override;
|
||||
|
||||
size_t GetSamples(samplePtr dest,
|
||||
size_t DoGetSamples(samplePtr dest,
|
||||
sampleFormat destformat,
|
||||
size_t sampleoffset,
|
||||
size_t numsamples) override;
|
||||
@@ -53,10 +53,10 @@ public:
|
||||
double GetSumRms() const;
|
||||
|
||||
/// Gets extreme values for the specified region
|
||||
MinMaxRMS GetMinMaxRMS(size_t start, size_t len) override;
|
||||
MinMaxRMS DoGetMinMaxRMS(size_t start, size_t len) override;
|
||||
|
||||
/// Gets extreme values for the entire block
|
||||
MinMaxRMS GetMinMaxRMS() const override;
|
||||
MinMaxRMS DoGetMinMaxRMS() const override;
|
||||
|
||||
size_t GetSpaceUsage() const override;
|
||||
void SaveXML(XMLWriter &xmlFile) override;
|
||||
@@ -116,17 +116,17 @@ public:
|
||||
|
||||
~SqliteSampleBlockFactory() override;
|
||||
|
||||
SampleBlockPtr Get(SampleBlockID sbid) override;
|
||||
SampleBlockPtr DoGet(SampleBlockID sbid) override;
|
||||
|
||||
SampleBlockPtr Create(samplePtr src,
|
||||
SampleBlockPtr DoCreate(samplePtr src,
|
||||
size_t numsamples,
|
||||
sampleFormat srcformat) override;
|
||||
|
||||
SampleBlockPtr CreateSilent(
|
||||
SampleBlockPtr DoCreateSilent(
|
||||
size_t numsamples,
|
||||
sampleFormat srcformat) override;
|
||||
|
||||
SampleBlockPtr CreateFromXML(
|
||||
SampleBlockPtr DoCreateFromXML(
|
||||
sampleFormat srcformat,
|
||||
const wxChar **attrs) override;
|
||||
|
||||
@@ -144,40 +144,24 @@ SqliteSampleBlockFactory::SqliteSampleBlockFactory( AudacityProject &project )
|
||||
|
||||
SqliteSampleBlockFactory::~SqliteSampleBlockFactory() = default;
|
||||
|
||||
SampleBlockPtr SqliteSampleBlockFactory::Create(
|
||||
SampleBlockPtr SqliteSampleBlockFactory::DoCreate(
|
||||
samplePtr src, size_t numsamples, sampleFormat srcformat )
|
||||
{
|
||||
auto sb = std::make_shared<SqliteSampleBlock>(&mProject);
|
||||
|
||||
if (sb)
|
||||
{
|
||||
if (sb->SetSamples(src, numsamples, srcformat))
|
||||
{
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
sb->SetSamples(src, numsamples, srcformat);
|
||||
return sb;
|
||||
}
|
||||
|
||||
SampleBlockPtr SqliteSampleBlockFactory::CreateSilent(
|
||||
SampleBlockPtr SqliteSampleBlockFactory::DoCreateSilent(
|
||||
size_t numsamples, sampleFormat srcformat )
|
||||
{
|
||||
auto sb = std::make_shared<SqliteSampleBlock>(&mProject);
|
||||
|
||||
if (sb)
|
||||
{
|
||||
if (sb->SetSilent(numsamples, srcformat))
|
||||
{
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
sb->SetSilent(numsamples, srcformat);
|
||||
return sb;
|
||||
}
|
||||
|
||||
|
||||
SampleBlockPtr SqliteSampleBlockFactory::CreateFromXML(
|
||||
SampleBlockPtr SqliteSampleBlockFactory::DoCreateFromXML(
|
||||
sampleFormat srcformat, const wxChar **attrs )
|
||||
{
|
||||
auto sb = std::make_shared<SqliteSampleBlock>(&mProject);
|
||||
@@ -204,10 +188,8 @@ SampleBlockPtr SqliteSampleBlockFactory::CreateFromXML(
|
||||
{
|
||||
if (wxStrcmp(attr, wxT("blockid")) == 0)
|
||||
{
|
||||
if (!sb->Load((SampleBlockID) nValue))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
// This may throw
|
||||
sb->Load((SampleBlockID) nValue);
|
||||
found++;
|
||||
}
|
||||
else if (wxStrcmp(attr, wxT("samplecount")) == 0)
|
||||
@@ -246,18 +228,10 @@ SampleBlockPtr SqliteSampleBlockFactory::CreateFromXML(
|
||||
return sb;
|
||||
}
|
||||
|
||||
SampleBlockPtr SqliteSampleBlockFactory::Get( SampleBlockID sbid )
|
||||
SampleBlockPtr SqliteSampleBlockFactory::DoGet( SampleBlockID sbid )
|
||||
{
|
||||
auto sb = std::make_shared<SqliteSampleBlock>(&mProject);
|
||||
|
||||
if (sb)
|
||||
{
|
||||
if (!sb->Load(sbid))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
sb->Load(sbid);
|
||||
return sb;
|
||||
}
|
||||
|
||||
@@ -286,7 +260,12 @@ SqliteSampleBlock::~SqliteSampleBlock()
|
||||
// See ProjectFileIO::Bypass() for a description of mIO.mBypass
|
||||
if (mRefCnt == 0 && !mIO.ShouldBypass())
|
||||
{
|
||||
Delete();
|
||||
// In case Delete throws, don't let an exception escape a destructor,
|
||||
// but we can still enqueue the delayed handler so that an error message
|
||||
// is presented to the user.
|
||||
// The failure in this case may be a less harmful waste of space in the
|
||||
// database, which should not cause aborting of the attempted edit.
|
||||
GuardedCall( [this]{ Delete(); } );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,7 +299,7 @@ size_t SqliteSampleBlock::GetSampleCount() const
|
||||
return mSampleCount;
|
||||
}
|
||||
|
||||
size_t SqliteSampleBlock::GetSamples(samplePtr dest,
|
||||
size_t SqliteSampleBlock::DoGetSamples(samplePtr dest,
|
||||
sampleFormat destformat,
|
||||
size_t sampleoffset,
|
||||
size_t numsamples)
|
||||
@@ -333,7 +312,7 @@ size_t SqliteSampleBlock::GetSamples(samplePtr dest,
|
||||
numsamples * SAMPLE_SIZE(mSampleFormat)) / SAMPLE_SIZE(mSampleFormat);
|
||||
}
|
||||
|
||||
bool SqliteSampleBlock::SetSamples(samplePtr src,
|
||||
void SqliteSampleBlock::SetSamples(samplePtr src,
|
||||
size_t numsamples,
|
||||
sampleFormat srcformat)
|
||||
{
|
||||
@@ -346,10 +325,10 @@ bool SqliteSampleBlock::SetSamples(samplePtr src,
|
||||
|
||||
CalcSummary();
|
||||
|
||||
return Commit();
|
||||
Commit();
|
||||
}
|
||||
|
||||
bool SqliteSampleBlock::SetSilent(size_t numsamples, sampleFormat srcformat)
|
||||
void SqliteSampleBlock::SetSilent(size_t numsamples, sampleFormat srcformat)
|
||||
{
|
||||
mSampleFormat = srcformat;
|
||||
|
||||
@@ -362,7 +341,7 @@ bool SqliteSampleBlock::SetSilent(size_t numsamples, sampleFormat srcformat)
|
||||
|
||||
mSilent = true;
|
||||
|
||||
return Commit();
|
||||
Commit();
|
||||
}
|
||||
|
||||
bool SqliteSampleBlock::GetSummary256(float *dest,
|
||||
@@ -413,7 +392,7 @@ double SqliteSampleBlock::GetSumRms() const
|
||||
///
|
||||
/// @param start The offset in this block where the region should begin
|
||||
/// @param len The number of samples to include in the region
|
||||
MinMaxRMS SqliteSampleBlock::GetMinMaxRMS(size_t start, size_t len)
|
||||
MinMaxRMS SqliteSampleBlock::DoGetMinMaxRMS(size_t start, size_t len)
|
||||
{
|
||||
float min = FLT_MAX;
|
||||
float max = -FLT_MAX;
|
||||
@@ -462,7 +441,7 @@ MinMaxRMS SqliteSampleBlock::GetMinMaxRMS(size_t start, size_t len)
|
||||
/// Retrieves the minimum, maximum, and maximum RMS of this entire
|
||||
/// block. This is faster than the other GetMinMax function since
|
||||
/// these values are already computed.
|
||||
MinMaxRMS SqliteSampleBlock::GetMinMaxRMS() const
|
||||
MinMaxRMS SqliteSampleBlock::DoGetMinMaxRMS() const
|
||||
{
|
||||
return { (float) mSumMin, (float) mSumMax, (float) mSumRms };
|
||||
}
|
||||
@@ -541,6 +520,11 @@ size_t SqliteSampleBlock::GetBlob(void *dest,
|
||||
}
|
||||
}
|
||||
|
||||
if ( rc != SQLITE_ROW )
|
||||
// Just showing the user a simple message, not the library error too
|
||||
// which isn't internationalized
|
||||
throw SimpleMessageBoxException{ XO("Failed to retrieve samples") };
|
||||
|
||||
if (srcbytes - minbytes)
|
||||
{
|
||||
memset(dest, 0, srcbytes - minbytes);
|
||||
@@ -587,17 +571,19 @@ bool SqliteSampleBlock::Load(SampleBlockID sbid)
|
||||
if (rc != SQLITE_OK)
|
||||
{
|
||||
wxLogDebug(wxT("SQLITE error %s"), sqlite3_errmsg(db));
|
||||
// handle error
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc != SQLITE_ROW)
|
||||
{
|
||||
wxLogDebug(wxT("SQLITE error %s"), sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc != SQLITE_ROW)
|
||||
{
|
||||
wxLogDebug(wxT("SQLITE error %s"), sqlite3_errmsg(db));
|
||||
// handle error
|
||||
return false;
|
||||
}
|
||||
if ( rc != SQLITE_ROW )
|
||||
// Just showing the user a simple message, not the library error too
|
||||
// which isn't internationalized
|
||||
throw SimpleMessageBoxException{ XO("Failed to retrieve samples") };
|
||||
|
||||
mBlockID = sbid;
|
||||
mSampleFormat = (sampleFormat) sqlite3_column_int(stmt, 0);
|
||||
@@ -610,11 +596,9 @@ bool SqliteSampleBlock::Load(SampleBlockID sbid)
|
||||
mSampleCount = mSampleBytes / SAMPLE_SIZE(mSampleFormat);
|
||||
|
||||
mValid = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SqliteSampleBlock::Commit()
|
||||
void SqliteSampleBlock::Commit()
|
||||
{
|
||||
auto db = mIO.DB();
|
||||
int rc;
|
||||
@@ -638,25 +622,32 @@ bool SqliteSampleBlock::Commit()
|
||||
if (rc != SQLITE_OK)
|
||||
{
|
||||
wxLogDebug(wxT("SQLITE error %s"), sqlite3_errmsg(db));
|
||||
// handle error
|
||||
return false;
|
||||
// Just showing the user a simple message, not the library error too
|
||||
// which isn't internationalized
|
||||
throw SimpleMessageBoxException{ mIO.GetLastError() };
|
||||
}
|
||||
|
||||
// BIND SQL sampleblocks
|
||||
sqlite3_bind_int(stmt, 1, mSampleFormat);
|
||||
sqlite3_bind_double(stmt, 2, mSumMin);
|
||||
sqlite3_bind_double(stmt, 3, mSumMax);
|
||||
sqlite3_bind_double(stmt, 4, mSumRms);
|
||||
sqlite3_bind_blob(stmt, 5, mSummary256.get(), mSummary256Bytes, SQLITE_STATIC);
|
||||
sqlite3_bind_blob(stmt, 6, mSummary64k.get(), mSummary64kBytes, SQLITE_STATIC);
|
||||
sqlite3_bind_blob(stmt, 7, mSamples.get(), mSampleBytes, SQLITE_STATIC);
|
||||
// Might return SQL_MISUSE which means it's our mistake that we violated
|
||||
// preconditions; should return SQL_OK which is 0
|
||||
if (
|
||||
sqlite3_bind_int(stmt, 1, mSampleFormat) ||
|
||||
sqlite3_bind_double(stmt, 2, mSumMin) ||
|
||||
sqlite3_bind_double(stmt, 3, mSumMax) ||
|
||||
sqlite3_bind_double(stmt, 4, mSumRms) ||
|
||||
sqlite3_bind_blob(stmt, 5, mSummary256.get(), mSummary256Bytes, SQLITE_STATIC) ||
|
||||
sqlite3_bind_blob(stmt, 6, mSummary64k.get(), mSummary64kBytes, SQLITE_STATIC) ||
|
||||
sqlite3_bind_blob(stmt, 7, mSamples.get(), mSampleBytes, SQLITE_STATIC)
|
||||
)
|
||||
THROW_INCONSISTENCY_EXCEPTION;
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc != SQLITE_DONE)
|
||||
{
|
||||
wxLogDebug(wxT("SQLITE error %s"), sqlite3_errmsg(db));
|
||||
// handle error
|
||||
return false;
|
||||
// Just showing the user a simple message, not the library error too
|
||||
// which isn't internationalized
|
||||
throw SimpleMessageBoxException{ mIO.GetLastError() };
|
||||
}
|
||||
|
||||
mBlockID = sqlite3_last_insert_rowid(db);
|
||||
@@ -666,8 +657,6 @@ bool SqliteSampleBlock::Commit()
|
||||
mSummary64k.reset();
|
||||
|
||||
mValid = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SqliteSampleBlock::Delete()
|
||||
@@ -688,8 +677,9 @@ void SqliteSampleBlock::Delete()
|
||||
if (rc != SQLITE_OK)
|
||||
{
|
||||
wxLogDebug(wxT("SQLITE error %s"), sqlite3_errmsg(db));
|
||||
// handle error
|
||||
return;
|
||||
// Just showing the user a simple message, not the library error too
|
||||
// which isn't internationalized
|
||||
throw SimpleMessageBoxException{ XO("Failed to purge unused samples") };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user