1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 16:10:06 +02:00

Various unitary fixes (#622)

* some comments

* No intermediate arrays (of arrays) of strings for query results...

... instead, let any query pass its own lambda to collect row data directly
however it needs to, for a bit of efficiency.  Also the precautions of a new
GuardedCall
This commit is contained in:
Paul Licameli 2020-07-20 18:11:43 -04:00 committed by GitHub
parent af23a14bdb
commit 9ffd169aa7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 70 additions and 79 deletions

View File

@ -107,12 +107,15 @@ static const char *ProjectFileSchema =
// 'samples' are fixed size blocks of int16, int32 or float32 numbers.
// The blocks may be partially empty.
// The quantity of valid data in the blocks is
// provided in the project XML.
// provided in the project blob.
//
// sampleformat specifies the format of the samples stored.
//
// blockID is a 64 bit number.
//
// Rows are immutable -- never updated after addition, but may be
// deleted.
//
// summin to summary64K are summaries at 3 distance scales.
"CREATE TABLE IF NOT EXISTS <schema>.sampleblocks"
"("
@ -635,52 +638,45 @@ bool ProjectFileIO::TransactionRollback(const wxString &name)
return rc == SQLITE_OK;
}
/* static */
int ProjectFileIO::ExecCallback(void *data, int cols, char **vals, char **names)
static int ExecCallback(void *data, int cols, char **vals, char **names)
{
ExecParm *parms = static_cast<ExecParm *>(data);
return parms->func(parms->result, cols, vals, names);
auto &cb = *static_cast<const ProjectFileIO::ExecCB *>(data);
// Be careful not to throw anything across sqlite3's stack frames.
return GuardedCall<int>(
[&]{ return cb(cols, vals, names); },
MakeSimpleGuard( 1 )
);
}
int ProjectFileIO::Exec(const char *query, ExecCB callback, ExecResult &result)
int ProjectFileIO::Exec(const char *query, const ExecCB &callback)
{
char *errmsg = nullptr;
ExecParm ep = {callback, result};
int rc = sqlite3_exec(DB(), query, ExecCallback, &ep, &errmsg);
const void *ptr = &callback;
int rc = sqlite3_exec(DB(), query, ExecCallback,
const_cast<void*>(ptr), &errmsg);
if (errmsg)
if (rc != SQLITE_ABORT && errmsg)
{
SetDBError(
XO("Failed to execute a project file command:\n\n%s").Format(query)
);
mLibraryError = Verbatim(errmsg);
}
if (errmsg)
{
sqlite3_free(errmsg);
}
return rc;
}
bool ProjectFileIO::Query(const char *sql, ExecResult &result)
bool ProjectFileIO::Query(const char *sql, const ExecCB &callback)
{
result.clear();
auto getresult = [](ExecResult &result, int cols, char **vals, char **names)
{
std::vector<wxString> row;
for (int i = 0; i < cols; ++i)
{
row.push_back(vals[i]);
}
result.push_back(row);
return SQLITE_OK;
};
int rc = Exec(sql, getresult, result);
if (rc != SQLITE_OK)
int rc = Exec(sql, callback);
// SQLITE_ABORT is a non-error return only meaning the callback
// stopped the iteration of rows early
if ( !(rc == SQLITE_OK || rc == SQLITE_ABORT) )
{
return false;
}
@ -690,21 +686,16 @@ bool ProjectFileIO::Query(const char *sql, ExecResult &result)
bool ProjectFileIO::GetValue(const char *sql, wxString &result)
{
// Retrieve the first column in the first row, if any
result.clear();
auto cb = [&result](int cols, char **vals, char **){
if (cols > 0)
result = vals[0];
// Stop after one row
return 1;
};
ExecResult holder;
if (!Query(sql, holder))
{
return false;
}
// Return the first column in the first row, if any
if (holder.size() && holder[0].size())
{
result.assign(holder[0][0]);
}
return true;
return Query(sql, cb);
}
bool ProjectFileIO::GetBlob(const char *sql, wxMemoryBuffer &buffer)
@ -929,19 +920,17 @@ sqlite3 *ProjectFileIO::CopyTo(const FilePath &destpath,
// Collect ALL blockids
else
{
ExecResult holder;
if (!Query("SELECT blockid FROM sampleblocks;", holder))
auto cb = [&blockids](int cols, char **vals, char **){
SampleBlockID blockid;
wxString{ vals[0] }.ToLongLong(&blockid);
blockids.insert(blockid);
return 0;
};
if (!Query("SELECT blockid FROM sampleblocks;", cb))
{
return nullptr;
}
for (auto block : holder)
{
SampleBlockID blockid;
block[0].ToLongLong(&blockid);
blockids.insert(blockid);
}
}
// Create the project doc
@ -1152,26 +1141,32 @@ bool ProjectFileIO::ShouldVacuum(const std::shared_ptr<TrackList> &tracks)
}
// Get the number of blocks and total length from the project file.
ExecResult holder;
if (!Query("SELECT Count(*), Sum(Length(summary256)) + Sum(Length(summary64k)) + Sum(Length(samples)) FROM sampleblocks;", holder))
{
// Shouldn't vacuum since we don't have the full picture
return false;
}
// Verify we got the results we asked for
if (holder.size() != 1 || holder[0].size() != 2)
{
// Shouldn't vacuum since we don't have the full picture
return false;
}
// Convert
unsigned long long blockcount = 0;
holder[0][0].ToULongLong(&blockcount);
unsigned long long total = 0;
holder[0][1].ToULongLong(&total);
auto cb =
[&blockcount, &total](int cols, char **vals, char **){
if ( cols != 2 )
// Should have two exactly!
return 1;
if ( total > 0 ) {
// Should not have multiple rows!
total = 0;
return 1;
}
// Convert
wxString{ vals[0] }.ToULongLong( &blockcount );
wxString{ vals[1] }.ToULongLong( &total );
return 0;
};
if (!Query("SELECT Count(*), "
"Sum(Length(summary256)) + Sum(Length(summary64k)) + Sum(Length(samples)) "
"FROM sampleblocks;", cb)
|| total == 0)
{
// Shouldn't vacuum since we don't have the full picture
return false;
}
// Remember if we had unused blocks in the project file
mHadUnused = (blockcount > active.size());

View File

@ -120,6 +120,10 @@ public:
bool TransactionCommit(const wxString &name);
bool TransactionRollback(const wxString &name);
// Type of function that is given the fields of one row and returns
// 0 for success or non-zero to stop the query
using ExecCB = std::function<int(int cols, char **vals, char **names)>;
private:
void WriteXMLHeader(XMLWriter &xmlFile) const;
void WriteXML(XMLWriter &xmlFile, bool recording = false, const std::shared_ptr<TrackList> &tracks = nullptr) /* not override */;
@ -130,15 +134,7 @@ private:
void UpdatePrefs() override;
using ExecResult = std::vector<std::vector<wxString>>;
using ExecCB = std::function<int(ExecResult &result, int cols, char **vals, char **names)>;
struct ExecParm
{
ExecCB func;
ExecResult &result;
};
static int ExecCallback(void *data, int cols, char **vals, char **names);
int Exec(const char *query, ExecCB callback, ExecResult &result);
int Exec(const char *query, const ExecCB &callback);
// The opening of the database may be delayed until demanded.
// Returns a non-null pointer to an open database, or throws an exception
@ -165,7 +161,7 @@ private:
bool CloseDB();
bool DeleteDB();
bool Query(const char *sql, ExecResult &result);
bool Query(const char *sql, const ExecCB &callback);
bool GetValue(const char *sql, wxString &value);
bool GetBlob(const char *sql, wxMemoryBuffer &buffer);