Audacity 3.2.0
DBConnection.cpp
Go to the documentation of this file.
1/*!********************************************************************
2
3Audacity: A Digital Audio Editor
4
5@file DBConnection.cpp
6@brief Implements DBConnection
7
8Paul Licameli -- split from ProjectFileIO.cpp
9
10**********************************************************************/
11
12#include "DBConnection.h"
13
14#include "sqlite3.h"
15
16#include <wx/string.h>
17
18#include "AudacityLogger.h"
19#include "BasicUI.h"
20#include "FileNames.h"
21#include "Internat.h"
22#include "Project.h"
23#include "FileException.h"
24#include "wxFileNameWrapper.h"
25#include "SentryHelper.h"
26
27#define AUDACITY_PROJECT_PAGE_SIZE 65536
28
29#define xstr(a) str(a)
30#define str(a) #a
31
32static const char* PageSizeConfig =
33 "PRAGMA <schema>.page_size = " xstr(AUDACITY_PROJECT_PAGE_SIZE) ";"
34 "VACUUM;";
35
36// Configuration to provide "safe" connections
37static const char* SafeConfig =
38 "PRAGMA <schema>.busy_timeout = 5000;"
39 "PRAGMA <schema>.locking_mode = SHARED;"
40 "PRAGMA <schema>.synchronous = NORMAL;"
41 "PRAGMA <schema>.journal_mode = WAL;"
42 "PRAGMA <schema>.wal_autocheckpoint = 0;";
43
44// Configuration to provide "Fast" connections
45static const char *FastConfig =
46 "PRAGMA <schema>.busy_timeout = 5000;"
47 "PRAGMA <schema>.locking_mode = SHARED;"
48 "PRAGMA <schema>.synchronous = OFF;"
49 "PRAGMA <schema>.journal_mode = OFF;";
50
52 const std::weak_ptr<AudacityProject> &pProject,
53 const std::shared_ptr<DBConnectionErrors> &pErrors,
55: mpProject{ pProject }
56, mpErrors{ pErrors }
57, mCallback{ std::move(callback) }
58{
59 mDB = nullptr;
60 mCheckpointDB = nullptr;
61 mBypass = false;
62}
63
65{
66 wxASSERT(mDB == nullptr);
67 if (mDB)
68 {
69 wxLogMessage("Database left open at connection destruction %s\n",
70 sqlite3_db_filename(mDB, nullptr));
71 }
72}
73
74void DBConnection::SetBypass( bool bypass )
75{
76 mBypass = bypass;
77}
78
80{
81 return mBypass;
82}
83
85 const TranslatableString &msg, const TranslatableString &libraryError, int errorCode)
86{
87 mpErrors->mErrorCode = errorCode;
88
89 mpErrors->mLastError = msg;
90
91 mpErrors->mLibraryError = errorCode && libraryError.empty()
92 ? XO("(%d): %s").Format(errorCode, sqlite3_errstr(errorCode))
93 : libraryError;
94
95 wxLogMessage("DBConnection SetError\n"
96 "\tErrorCode: %d\n"
97 "\tLastError: %s\n"
98 "\tLibraryError: %s",
99 mpErrors->mErrorCode,
100 mpErrors->mLastError.Debug(),
101 mpErrors->mLibraryError.Debug());
102
103 auto logger = AudacityLogger::Get();
104 if (logger)
105 {
106 mpErrors->mLog = logger->GetLog(10);
107 }
108}
109
111 const TranslatableString &msg, const TranslatableString &libraryError, int errorCode)
112{
113 auto db = DB();
114
115 mpErrors->mErrorCode = errorCode < 0 && db
116 ? sqlite3_errcode(db)
117 : errorCode;
118
119 mpErrors->mLastError = msg.empty()
120 ? XO("(%d): %s").Format(mpErrors->mErrorCode, sqlite3_errstr(mpErrors->mErrorCode))
121 : msg;
122
123 mpErrors->mLibraryError = libraryError.empty() && db
124 ? Verbatim(sqlite3_errmsg(db))
125 : libraryError;
126
127 wxLogMessage("DBConnection SetDBError\n"
128 "\tErrorCode: %d\n"
129 "\tLastError: %s\n"
130 "\tLibraryError: %s",
131 mpErrors->mErrorCode,
132 mpErrors->mLastError.Debug(),
133 mpErrors->mLibraryError.Debug());
134
135 auto logger = AudacityLogger::Get();
136 if (logger)
137 {
138 mpErrors->mLog = logger->GetLog(10);
139 }
140}
141
142int DBConnection::Open(const FilePath fileName)
143{
144 wxASSERT(mDB == nullptr);
145 int rc;
146
147 // Initialize checkpoint controls
148 mCheckpointStop = false;
149 mCheckpointPending = false;
150 mCheckpointActive = false;
151 rc = OpenStepByStep( fileName );
152 if ( rc != SQLITE_OK)
153 {
154 if (mCheckpointDB)
155 {
156 sqlite3_close(mCheckpointDB);
157 mCheckpointDB = nullptr;
158 }
159
160 if (mDB)
161 {
162 sqlite3_close(mDB);
163 mDB = nullptr;
164 }
165 }
166 return rc;
167}
168
170{
171 const char *name = fileName.ToUTF8();
172
173 bool success = false;
174 int rc = sqlite3_open(name, &mDB);
175 if (rc != SQLITE_OK)
176 {
177 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
178 ADD_EXCEPTION_CONTEXT("sqlite3.context", "DBConnection::OpenStepByStep::open");
179
180 wxLogMessage("Failed to open primary connection to %s: %d, %s\n",
181 fileName,
182 rc,
183 sqlite3_errstr(rc));
184 return rc;
185 }
186
187 rc = SetPageSize();
188
189 if (rc != SQLITE_OK)
190 {
191 SetDBError(XO("Failed to set page size for database %s")
192 .Format(fileName));
193 return rc;
194 }
195
196 // Set default mode
197 // (See comments in ProjectFileIO::SaveProject() about threading
198 rc = SafeMode();
199 if (rc != SQLITE_OK)
200 {
201 SetDBError(XO("Failed to set safe mode on primary connection to %s").Format(fileName));
202 return rc;
203 }
204
205 rc = sqlite3_open(name, &mCheckpointDB);
206 if (rc != SQLITE_OK)
207 {
208 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
209 ADD_EXCEPTION_CONTEXT("sqlite3.context", "DBConnection::OpenStepByStep::open_checkpoint");
210
211 wxLogMessage("Failed to open checkpoint connection to %s: %d, %s\n",
212 fileName,
213 rc,
214 sqlite3_errstr(rc));
215 return rc;
216 }
217
218 rc = ModeConfig(mCheckpointDB, "main", SafeConfig);
219 if (rc != SQLITE_OK) {
220 SetDBError(XO("Failed to set safe mode on checkpoint connection to %s").Format(fileName));
221 return rc;
222 }
223
224 auto db = mCheckpointDB;
225 mCheckpointThread = std::thread(
226 [this, db, fileName]{ CheckpointThread(db, fileName); });
227
228 // Install our checkpoint hook
229 sqlite3_wal_hook(mDB, CheckpointHook, this);
230 return rc;
231}
232
234{
235 wxASSERT(mDB != nullptr);
236 int rc;
237
238 // Protect...
239 if (mDB == nullptr)
240 {
241 return true;
242 }
243
244 // Uninstall our checkpoint hook so that no additional checkpoints
245 // are sent our way. (Though this shouldn't really happen.)
246 sqlite3_wal_hook(mDB, nullptr, nullptr);
247
248 // Display a progress dialog if there's active or pending checkpoints
250 {
251 TranslatableString title = XO("Checkpointing project");
252
253 // Get access to the active project
254 auto project = mpProject.lock();
255 if (project)
256 {
257 title = XO("Checkpointing %s").Format(project->GetProjectName());
258 }
259
260 // Provides a progress dialog with indeterminate mode
261 using namespace BasicUI;
262 auto pd = MakeGenericProgress({},
263 title, XO("This may take several seconds"));
264 wxASSERT(pd);
265
266 // Wait for the checkpoints to end
268 {
269 using namespace std::chrono;
270 std::this_thread::sleep_for(50ms);
271 pd->Pulse();
272 }
273 }
274
275 // Tell the checkpoint thread to shutdown
276 {
277 std::lock_guard<std::mutex> guard(mCheckpointMutex);
278 mCheckpointStop = true;
279 mCheckpointCondition.notify_one();
280 }
281
282 // And wait for it to do so
283 if (mCheckpointThread.joinable())
284 {
285 mCheckpointThread.join();
286 }
287
288 // We're done with the prepared statements
289 {
290 std::lock_guard<std::mutex> guard(mStatementMutex);
291 for (auto stmt : mStatements)
292 {
293 // No need to process return code, but log it for diagnosis
294 rc = sqlite3_finalize(stmt.second);
295 if (rc != SQLITE_OK)
296 {
297 wxLogMessage("Failed to finalize statement on %s\n"
298 "\tErrMsg: %s\n"
299 "\tSQL: %s",
300 sqlite3_db_filename(mDB, nullptr),
301 sqlite3_errmsg(mDB),
302 stmt.second);
303 }
304 }
305 mStatements.clear();
306 }
307
308 // Not much we can do if the closes fail, so just report the error
309
310 // Close the checkpoint connection
311 rc = sqlite3_close(mCheckpointDB);
312 if (rc != SQLITE_OK)
313 {
314 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
315 ADD_EXCEPTION_CONTEXT("sqlite3.context", "DBConnection::Close::close_checkpoint");
316
317 wxLogMessage("Failed to close checkpoint connection for %s\n"
318 "\tError: %s\n",
319 sqlite3_db_filename(mCheckpointDB, nullptr),
320 sqlite3_errmsg(mCheckpointDB));
321 }
322 mCheckpointDB = nullptr;
323
324 // Close the primary connection
325 rc = sqlite3_close(mDB);
326 if (rc != SQLITE_OK)
327 {
328 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
329 ADD_EXCEPTION_CONTEXT("sqlite3.context", "DBConnection::OpenStepByStep::close");
330
331 wxLogMessage("Failed to close %s\n"
332 "\tError: %s\n",
333 sqlite3_db_filename(mDB, nullptr),
334 sqlite3_errmsg(mDB));
335 }
336 mDB = nullptr;
337
338 return true;
339}
340
341[[noreturn]] void DBConnection::ThrowException( bool write ) const
342{
343 // Sqlite3 documentation says returned character string
344 // does NOT require freeing by us.
345 wxString dbName{ sqlite3_db_filename(mDB, "main") };
346 // Now we have an absolute path. Throw a message box exception that
347 // formats a helpful message just as used to be done before sqlite3
348 // was used for projects.
349 throw FileException{
351 dbName
352 };
353}
354
355int DBConnection::SafeMode(const char *schema /* = "main" */)
356{
357 return ModeConfig(mDB, schema, SafeConfig);
358}
359
360int DBConnection::FastMode(const char *schema /* = "main" */)
361{
362 return ModeConfig(mDB, schema, FastConfig);
363}
364
365int DBConnection::SetPageSize(const char* schema)
366{
367 // First of all - let's check if the database is empty.
368 // Otherwise, VACUUM can take a significant amount of time.
369 // VACUUM is required to force SQLite3 to change the page size.
370 // This function will be the first called on the connection,
371 // so if DB is empty we can assume that journal was not
372 // set to WAL yet.
373 int rc = sqlite3_exec(
374 mDB, "SELECT 1 FROM project LIMIT 1;", nullptr, nullptr, nullptr);
375
376 if (rc == SQLITE_OK)
377 return SQLITE_OK; // Project table exists, too late to VACUUM now
378
379 return ModeConfig(mDB, schema, PageSizeConfig);
380}
381
382int DBConnection::ModeConfig(sqlite3 *db, const char *schema, const char *config)
383{
384 // Ensure attached DB connection gets configured
385 int rc;
386
387 // Replace all schema "keywords" with the schema name
388 wxString sql = config;
389 sql.Replace(wxT("<schema>"), schema);
390
391 // Set the configuration
392 rc = sqlite3_exec(db, sql, nullptr, nullptr, nullptr);
393 if (rc != SQLITE_OK)
394 {
395 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
396 ADD_EXCEPTION_CONTEXT("sqlite3.context", "DBConnection::ModeConfig");
397 ADD_EXCEPTION_CONTEXT("sqlite3.mode", config);
398
399 // Don't store in connection, just report it
400 wxLogMessage("Failed to set mode on %s\n"
401 "\tError: %s\n"
402 "\tSQL: %s",
403 sqlite3_db_filename(mDB, nullptr),
404 sqlite3_errmsg(mDB),
405 sql);
406 }
407
408 return rc;
409}
410
412{
413 wxASSERT(mDB != nullptr);
414
415 return mDB;
416}
417
419{
420 return sqlite3_errcode(mDB);
421}
422
423const wxString DBConnection::GetLastMessage() const
424{
425 return sqlite3_errmsg(mDB);
426}
427
428sqlite3_stmt *DBConnection::Prepare(enum StatementID id, const char *sql)
429{
430 std::lock_guard<std::mutex> guard(mStatementMutex);
431
432 int rc;
433 // See bug 2673
434 // We must not use the same prepared statement from two different threads.
435 // Therefore, in the cache, use the thread id too.
436 StatementIndex ndx(id, std::this_thread::get_id());
437
438 // Return an existing statement if it's already been prepared
439 auto iter = mStatements.find(ndx);
440 if (iter != mStatements.end())
441 {
442 return iter->second;
443 }
444
445 // Prepare the statement
446 sqlite3_stmt *stmt = nullptr;
447 rc = sqlite3_prepare_v3(mDB, sql, -1, SQLITE_PREPARE_PERSISTENT, &stmt, 0);
448 if (rc != SQLITE_OK)
449 {
450 ADD_EXCEPTION_CONTEXT("sqlite3.query", sql);
451 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
452 ADD_EXCEPTION_CONTEXT("sqlite3.context", "DBConnection::Prepare");
453
454 wxLogMessage("Failed to prepare statement for %s\n"
455 "\tError: %s\n"
456 "\tSQL: %s",
457 sqlite3_db_filename(mDB, nullptr),
458 sqlite3_errmsg(mDB),
459 sql);
460
461 // TODO: Look into why this causes an access violation
463 }
464
465 // There are a small number (10 or so) of different id's corresponding
466 // to different SQL statements, see enum StatementID
467 // We have relatively few threads running at any one time,
468 // e.g. main gui thread, a playback thread, a thread for compacting.
469 // However the cache might keep growing, as we start/stop audio,
470 // perhaps, if we chose to use a new thread each time.
471 // For 3.0.0 I think that's OK. If it's a data leak it's a slow
472 // enough one. wxLogDebugs seem to show that the audio play thread
473 // is being reused, not recreated with a new ID, i.e. no leak at all.
474 // ANSWER-ME Just how serious is the data leak? How best to fix?
475
476 // Remember the cached statement.
477 mStatements.insert({ndx, stmt});
478
479 return stmt;
480}
481
482void DBConnection::CheckpointThread(sqlite3 *db, const FilePath &fileName)
483{
484 int rc = SQLITE_OK;
485 bool giveUp = false;
486
487 while (true)
488 {
489 {
490 // Wait for work or the stop signal
491 std::unique_lock<std::mutex> lock(mCheckpointMutex);
492 mCheckpointCondition.wait(lock,
493 [&]
494 {
496 });
497
498 // Requested to stop, so bail
499 if (mCheckpointStop)
500 {
501 break;
502 }
503
504 // Capture the number of pages that need checkpointing and reset
505 mCheckpointActive = true;
506 mCheckpointPending = false;
507 }
508
509 // And kick off the checkpoint. This may not checkpoint ALL frames
510 // in the WAL. They'll be gotten the next time around.
511 using namespace std::chrono;
512 do {
513 rc = giveUp ? SQLITE_OK :
514 sqlite3_wal_checkpoint_v2(
515 db, nullptr, SQLITE_CHECKPOINT_PASSIVE, nullptr, nullptr);
516 }
517 // Contentions for an exclusive lock on the database are possible,
518 // even while the main thread is merely drawing the tracks, which
519 // may perform reads
520 while (rc == SQLITE_BUSY && (std::this_thread::sleep_for(1ms), true));
521
522 // Reset
523 mCheckpointActive = false;
524
525 if (rc != SQLITE_OK)
526 {
527 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
528 ADD_EXCEPTION_CONTEXT("sqlite3.context", "DBConnection::CheckpointThread");
529
530 wxLogMessage("Failed to perform checkpoint on %s\n"
531 "\tErrCode: %d\n"
532 "\tErrMsg: %s",
533 fileName,
534 sqlite3_errcode(db),
535 sqlite3_errmsg(db));
536
537 // Can't checkpoint -- maybe the device has too little space
538 wxFileNameWrapper fName{ fileName };
539 auto path = FileNames::AbbreviatePath(fName);
540 auto name = fName.GetFullName();
541 auto longname = name + "-wal";
542
543 // TODO: Should we return the actual error message if it's not a
544 // disk full condition?
545 auto message1 = rc == SQLITE_FULL
546 ? XO("Could not write to %s.\n").Format(path)
548 auto message = XO(
549 "Disk is full.\n"
550 "%s\n"
551 "For tips on freeing up space, click the help button."
552 ).Format(message1);
553
554 // Stop trying to checkpoint
555 giveUp = true;
556
557 // Stop the audio.
559 [&message, rc] {
561 message, XO("Warning"), "Error:_Disk_full_or_not_writable" }; },
563 [this](AudacityException * e) {
564 // This executes in the main thread.
565 if (mCallback)
566 mCallback();
567 if (e)
568 e->DelayedHandlerAction();
569 }
570 );
571 }
572 }
573
574 return;
575}
576
577int DBConnection::CheckpointHook(void *data, sqlite3 *db, const char *schema, int pages)
578{
579 // Get access to our object
580 DBConnection *that = static_cast<DBConnection *>(data);
581
582 // Queue the database pointer for our checkpoint thread to process
583 std::lock_guard<std::mutex> guard(that->mCheckpointMutex);
584 that->mCheckpointPending = true;
585 that->mCheckpointCondition.notify_one();
586
587 return SQLITE_OK;
588}
589
590// Install an implementation of TransactionScope
591#include "TransactionScope.h"
592
595 : mConnection{ connection } {}
597 bool TransactionStart(const wxString &name) override;
598 bool TransactionCommit(const wxString &name) override;
599 bool TransactionRollback(const wxString &name) override;
600
602};
603
604static TransactionScope::Factory::Scope scope {
605[](AudacityProject &project) -> std::unique_ptr<TransactionScopeImpl> {
606 auto &connectionPtr = ConnectionPtr::Get(project);
607 if (auto pConnection = connectionPtr.mpConnection.get())
608 return
609 std::make_unique<DBConnectionTransactionScopeImpl>(*pConnection);
610 else
611 return nullptr;
612} };
613
615
617{
618 char *errmsg = nullptr;
619
620 int rc = sqlite3_exec(mConnection.DB(),
621 wxT("SAVEPOINT ") + name + wxT(";"),
622 nullptr,
623 nullptr,
624 &errmsg);
625
626 if (errmsg)
627 {
628 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
629 ADD_EXCEPTION_CONTEXT("sqlite3.context", "TransactionScope::TransactionStart");
630
632 XO("Failed to create savepoint:\n\n%s").Format(name)
633 );
634 sqlite3_free(errmsg);
635 }
636
637 return rc == SQLITE_OK;
638}
639
641{
642 char *errmsg = nullptr;
643
644 int rc = sqlite3_exec(mConnection.DB(),
645 wxT("RELEASE ") + name + wxT(";"),
646 nullptr,
647 nullptr,
648 &errmsg);
649
650 if (errmsg)
651 {
652 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
653 ADD_EXCEPTION_CONTEXT("sqlite3.context", "TransactionScope::TransactionCommit");
654
656 XO("Failed to release savepoint:\n\n%s").Format(name)
657 );
658 sqlite3_free(errmsg);
659 }
660
661 return rc == SQLITE_OK;
662}
663
665{
666 char *errmsg = nullptr;
667
668 int rc = sqlite3_exec(mConnection.DB(),
669 wxT("ROLLBACK TO ") + name + wxT(";"),
670 nullptr,
671 nullptr,
672 &errmsg);
673
674 if (errmsg)
675 {
676 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
677 ADD_EXCEPTION_CONTEXT("sqlite3.context", "TransactionScope::TransactionRollback");
679 XO("Failed to release savepoint:\n\n%s").Format(name)
680 );
681 sqlite3_free(errmsg);
682 }
683
684 if (rc != SQLITE_OK)
685 return false;
686
687 // Rollback AND REMOVE the transaction
688 // -- must do both; rolling back a savepoint only rewinds it
689 // without removing it, unlike the ROLLBACK command
690
691 return TransactionCommit(name);
692}
693
695{
696 wxASSERT_MSG(!mpConnection, wxT("Project file was not closed at shutdown"));
697 if (mpConnection)
698 {
699 wxLogMessage("Project file was not closed at connection destruction");
700 }
701}
702
705 []( AudacityProject & ){
706 // Ignore the argument; this is just a holder of a
707 // unique_ptr to DBConnection, which must be filled in later
708 // (when we can get a weak_ptr to the project)
709 auto result = std::make_shared< ConnectionPtr >();
710 return result;
711 }
712};
713
715{
716 auto &result =
717 project.AttachedObjects::Get< ConnectionPtr >( sConnectionPtrKey );
718 return result;
719}
720
722{
723 return Get( const_cast< AudacityProject & >( project ) );
724}
725
@ BadEnvironment
Indicates problems with environment, such as a full disk.
@ Internal
Indicates internal failure from Audacity.
R GuardedCall(const F1 &body, const F2 &handler=F2::Default(), F3 delayedHandler=DefaultDelayedHandlerAction) noexcept(noexcept(handler(std::declval< AudacityException * >())) &&noexcept(handler(nullptr)) &&noexcept(std::function< void(AudacityException *)>{std::move(delayedHandler)}))
Execute some code on any thread; catch any AudacityException; enqueue error report on the main thread...
Toolkit-neutral facade for basic user interface services.
#define AUDACITY_PROJECT_PAGE_SIZE
static const char * PageSizeConfig
static const char * SafeConfig
static const AudacityProject::AttachedObjects::RegisteredFactory sConnectionPtrKey
static const char * FastConfig
static TransactionScope::Factory::Scope scope
#define xstr(a)
Declare DBConnection, which maintains database connection and associated status and background thread...
const TranslatableString name
Definition: Distortion.cpp:82
MessageBoxException for failures of file operations.
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
#define XO(s)
Definition: Internat.h:31
static const auto title
wxString FilePath
Definition: Project.h:20
#define ADD_EXCEPTION_CONTEXT(name, value)
Definition: SentryHelper.h:21
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
Base class for exceptions specially processed by the application.
static AudacityLogger * Get()
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:266
Connection mpConnection
Definition: DBConnection.h:146
~ConnectionPtr() override
static ConnectionPtr & Get(AudacityProject &project)
void CheckpointThread(sqlite3 *db, const FilePath &fileName)
std::shared_ptr< DBConnectionErrors > mpErrors
Definition: DBConnection.h:123
static int CheckpointHook(void *data, sqlite3 *db, const char *schema, int pages)
std::mutex mCheckpointMutex
Definition: DBConnection.h:114
void ThrowException(bool write) const
throw and show appropriate message box
int OpenStepByStep(const FilePath fileName)
void SetError(const TranslatableString &msg, const TranslatableString &libraryError={}, int errorCode={})
Just set stored errors.
const wxString GetLastMessage() const
int GetLastRC() const
std::atomic_bool mCheckpointStop
Definition: DBConnection.h:115
sqlite3 * mCheckpointDB
Definition: DBConnection.h:110
std::atomic_bool mCheckpointActive
Definition: DBConnection.h:117
std::mutex mStatementMutex
Definition: DBConnection.h:119
std::atomic_bool mCheckpointPending
Definition: DBConnection.h:116
int FastMode(const char *schema="main")
std::thread mCheckpointThread
Definition: DBConnection.h:112
void SetBypass(bool bypass)
sqlite3_stmt * Prepare(enum StatementID id, const char *sql)
sqlite3 * mDB
Definition: DBConnection.h:109
int SetPageSize(const char *schema="main")
DBConnection(const std::weak_ptr< AudacityProject > &pProject, const std::shared_ptr< DBConnectionErrors > &pErrors, CheckpointFailureCallback callback)
std::condition_variable mCheckpointCondition
Definition: DBConnection.h:113
sqlite3 * DB()
std::map< StatementIndex, sqlite3_stmt * > mStatements
Definition: DBConnection.h:121
void SetDBError(const TranslatableString &msg, const TranslatableString &libraryError={}, int errorCode=-1)
Set stored errors and write to log; and default libraryError to what database library reports.
int ModeConfig(sqlite3 *db, const char *schema, const char *config)
std::function< void()> CheckpointFailureCallback
Definition: DBConnection.h:44
std::weak_ptr< AudacityProject > mpProject
Definition: DBConnection.h:108
CheckpointFailureCallback mCallback
Definition: DBConnection.h:124
int Open(const FilePath fileName)
std::pair< enum StatementID, std::thread::id > StatementIndex
Definition: DBConnection.h:120
bool ShouldBypass()
int SafeMode(const char *schema="main")
Thrown for failure of file or database operations in deeply nested places.
Definition: FileException.h:19
@ Write
most important to detect when storage space is exhausted
Abstract base class used in importing a file.
A MessageBoxException that shows a given, unvarying string.
Abstract base class for implementation of steps of TransactionScope.
Holds a msgid for the translation catalog; may also bind format arguments.
std::unique_ptr< GenericProgressDialog > MakeGenericProgress(const WindowPlacement &placement, const TranslatableString &title, const TranslatableString &message)
Create and display a progress dialog (return nullptr if Services not installed)
Definition: BasicUI.h:289
FILES_API wxString AbbreviatePath(const wxFileName &fileName)
Give enough of the path to identify the device. (On Windows, drive letter plus ':')
STL namespace.
bool TransactionCommit(const wxString &name) override
bool TransactionRollback(const wxString &name) override
DBConnectionTransactionScopeImpl(DBConnection &connection)
bool TransactionStart(const wxString &name) override
Specialization of SimpleGuard, also defining a default value.