1 /** 2 * @file Database.h 3 * @ingroup SQLiteCpp 4 * @brief Management of a SQLite Database Connection. 5 * 6 * Copyright (c) 2012-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) 7 * 8 * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt 9 * or copy at http://opensource.org/licenses/MIT) 10 */ 11 #pragma once 12 13 #include <SQLiteCpp/Column.h> 14 15 // c++17: MinGW GCC version > 8 16 // c++17: Visual Studio 2017 version 15.7 17 #if ((__cplusplus >= 201703L) && ((!defined(__MINGW32__) && !defined(__MINGW64__)) || (__GNUC__ > 8))) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L)) 18 #include <filesystem> 19 #endif // c++17 20 21 #include <memory> 22 #include <string.h> 23 24 // Forward declarations to avoid inclusion of <sqlite3.h> in a header 25 struct sqlite3; 26 struct sqlite3_context; 27 28 #ifndef SQLITE_USE_LEGACY_STRUCT // Since SQLITE 3.19 (used by default since SQLiteCpp 2.1.0) 29 typedef struct sqlite3_value sqlite3_value; 30 #else // Before SQLite 3.19 (legacy struct forward declaration can be activated with CMake SQLITECPP_LEGACY_STRUCT var) 31 struct Mem; 32 typedef struct Mem sqlite3_value; 33 #endif 34 35 36 namespace SQLite 37 { 38 39 // Those public constants enable most usages of SQLiteCpp without including <sqlite3.h> in the client application. 40 41 /// The database is opened in read-only mode. If the database does not already exist, an error is returned. 42 extern const int OPEN_READONLY; // SQLITE_OPEN_READONLY 43 /// The database is opened for reading and writing if possible, or reading only if the file is write protected 44 /// by the operating system. In either case the database must already exist, otherwise an error is returned. 45 extern const int OPEN_READWRITE; // SQLITE_OPEN_READWRITE 46 /// With OPEN_READWRITE: The database is opened for reading and writing, and is created if it does not already exist. 47 extern const int OPEN_CREATE; // SQLITE_OPEN_CREATE 48 /// Enable URI filename interpretation, parsed according to RFC 3986 (ex. "file:data.db?mode=ro&cache=private") 49 extern const int OPEN_URI; // SQLITE_OPEN_URI 50 /// Open in memory database 51 extern const int OPEN_MEMORY; // SQLITE_OPEN_MEMORY 52 /// Open database in multi-thread threading mode 53 extern const int OPEN_NOMUTEX; // SQLITE_OPEN_NOMUTEX 54 /// Open database with thread-safety in serialized threading mode 55 extern const int OPEN_FULLMUTEX; // SQLITE_OPEN_FULLMUTEX 56 /// Open database with shared cache enabled 57 extern const int OPEN_SHAREDCACHE; // SQLITE_OPEN_SHAREDCACHE 58 /// Open database with shared cache disabled 59 extern const int OPEN_PRIVATECACHE; // SQLITE_OPEN_PRIVATECACHE 60 /// Database filename is not allowed to be a symbolic link (Note: only since SQlite 3.31.0 from 2020-01-22) 61 extern const int OPEN_NOFOLLOW; // SQLITE_OPEN_NOFOLLOW 62 63 64 extern const int OK; ///< SQLITE_OK (used by check() bellow) 65 66 extern const char* VERSION; ///< SQLITE_VERSION string from the sqlite3.h used at compile time 67 extern const int VERSION_NUMBER; ///< SQLITE_VERSION_NUMBER from the sqlite3.h used at compile time 68 69 /// Return SQLite version string using runtime call to the compiled library 70 const char* getLibVersion() noexcept; 71 /// Return SQLite version number using runtime call to the compiled library 72 int getLibVersionNumber() noexcept; 73 74 // Public structure for representing all fields contained within the SQLite header. 75 // Official documentation for fields: https://www.sqlite.org/fileformat.html#the_database_header 76 struct Header { 77 unsigned char headerStr[16]; 78 unsigned int pageSizeBytes; 79 unsigned char fileFormatWriteVersion; 80 unsigned char fileFormatReadVersion; 81 unsigned char reservedSpaceBytes; 82 unsigned char maxEmbeddedPayloadFrac; 83 unsigned char minEmbeddedPayloadFrac; 84 unsigned char leafPayloadFrac; 85 unsigned long fileChangeCounter; 86 unsigned long databaseSizePages; 87 unsigned long firstFreelistTrunkPage; 88 unsigned long totalFreelistPages; 89 unsigned long schemaCookie; 90 unsigned long schemaFormatNumber; 91 unsigned long defaultPageCacheSizeBytes; 92 unsigned long largestBTreePageNumber; 93 unsigned long databaseTextEncoding; 94 unsigned long userVersion; 95 unsigned long incrementalVaccumMode; 96 unsigned long applicationId; 97 unsigned long versionValidFor; 98 unsigned long sqliteVersion; 99 }; 100 101 /** 102 * @brief RAII management of a SQLite Database Connection. 103 * 104 * A Database object manage a list of all SQLite Statements associated with the 105 * underlying SQLite 3 database connection. 106 * 107 * Resource Acquisition Is Initialization (RAII) means that the Database Connection 108 * is opened in the constructor and closed in the destructor, so that there is 109 * no need to worry about memory management or the validity of the underlying SQLite Connection. 110 * 111 * Thread-safety: a Database object shall not be shared by multiple threads, because : 112 * 1) in the SQLite "Thread Safe" mode, "SQLite can be safely used by multiple threads 113 * provided that no single database connection is used simultaneously in two or more threads." 114 * 2) the SQLite "Serialized" mode is not supported by SQLiteC++, 115 * because of the way it shares the underling SQLite precompiled statement 116 * in a custom shared pointer (See the inner class "Statement::Ptr"). 117 */ 118 class Database 119 { 120 friend class Statement; // Give Statement constructor access to the mSQLitePtr Connection Handle 121 122 public: 123 /** 124 * @brief Open the provided database UTF-8 filename. 125 * 126 * Uses sqlite3_open_v2() with readonly default flag, which is the opposite behavior 127 * of the old sqlite3_open() function (READWRITE+CREATE). 128 * This makes sense if you want to use it on a readonly filesystem 129 * or to prevent creation of a void file when a required file is missing. 130 * 131 * Exception is thrown in case of error, then the Database object is NOT constructed. 132 * 133 * @param[in] apFilename UTF-8 path/uri to the database file ("filename" sqlite3 parameter) 134 * @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE... 135 * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout()) 136 * @param[in] apVfs UTF-8 name of custom VFS to use, or nullptr for sqlite3 default 137 * 138 * @throw SQLite::Exception in case of error 139 */ 140 Database(const char* apFilename, 141 const int aFlags = SQLite::OPEN_READONLY, 142 const int aBusyTimeoutMs = 0, 143 const char* apVfs = nullptr); 144 145 /** 146 * @brief Open the provided database UTF-8 filename. 147 * 148 * Uses sqlite3_open_v2() with readonly default flag, which is the opposite behavior 149 * of the old sqlite3_open() function (READWRITE+CREATE). 150 * This makes sense if you want to use it on a readonly filesystem 151 * or to prevent creation of a void file when a required file is missing. 152 * 153 * Exception is thrown in case of error, then the Database object is NOT constructed. 154 * 155 * @param[in] aFilename UTF-8 path/uri to the database file ("filename" sqlite3 parameter) 156 * @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE... 157 * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout()) 158 * @param[in] aVfs UTF-8 name of custom VFS to use, or empty string for sqlite3 default 159 * 160 * @throw SQLite::Exception in case of error 161 */ 162 Database(const std::string& aFilename, 163 const int aFlags = SQLite::OPEN_READONLY, 164 const int aBusyTimeoutMs = 0, 165 const std::string& aVfs = "") : 166 Database(aFilename.c_str(), aFlags, aBusyTimeoutMs, aVfs.empty() ? nullptr : aVfs.c_str()) 167 { 168 } 169 170 // c++17: MinGW GCC version > 8 171 // c++17: Visual Studio 2017 version 15.7 172 #if ((__cplusplus >= 201703L) && ((!defined(__MINGW32__) && !defined(__MINGW64__)) || (__GNUC__ > 8))) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L)) 173 174 /** 175 * @brief Open the provided database std::filesystem::path. 176 * 177 * @note This feature requires std=C++17 178 * 179 * Uses sqlite3_open_v2() with readonly default flag, which is the opposite behavior 180 * of the old sqlite3_open() function (READWRITE+CREATE). 181 * This makes sense if you want to use it on a readonly filesystem 182 * or to prevent creation of a void file when a required file is missing. 183 * 184 * Exception is thrown in case of error, then the Database object is NOT constructed. 185 * 186 * @param[in] apFilename Path/uri to the database file ("filename" sqlite3 parameter) 187 * @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE... 188 * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout()) 189 * @param[in] apVfs UTF-8 name of custom VFS to use, or nullptr for sqlite3 default 190 * 191 * @throw SQLite::Exception in case of error 192 */ 193 Database(const std::filesystem::path& apFilename, 194 const int aFlags = SQLite::OPEN_READONLY, 195 const int aBusyTimeoutMs = 0, 196 const std::string& aVfs = "") : 197 Database(reinterpret_cast<const char*>(apFilename.u8string().c_str()), 198 aFlags, aBusyTimeoutMs, aVfs.empty() ? nullptr : aVfs.c_str()) 199 { 200 } 201 202 #endif // c++17 203 204 // Database is non-copyable 205 Database(const Database&) = delete; 206 Database& operator=(const Database&) = delete; 207 208 // Database is movable 209 Database(Database&& aDatabase) = default; 210 Database& operator=(Database&& aDatabase) = default; 211 212 /** 213 * @brief Close the SQLite database connection. 214 * 215 * All SQLite statements must have been finalized before, 216 * so all Statement objects must have been unregistered. 217 * 218 * @warning assert in case of error 219 */ 220 ~Database() = default; 221 222 // Deleter functor to use with smart pointers to close the SQLite database connection in an RAII fashion. 223 struct Deleter 224 { 225 void operator()(sqlite3* apSQLite); 226 }; 227 228 /** 229 * @brief Set a busy handler that sleeps for a specified amount of time when a table is locked. 230 * 231 * This is useful in multithreaded program to handle case where a table is locked for writing by a thread. 232 * Any other thread cannot access the table and will receive a SQLITE_BUSY error: 233 * setting a timeout will wait and retry up to the time specified before returning this SQLITE_BUSY error. 234 * Reading the value of timeout for current connection can be done with SQL query "PRAGMA busy_timeout;". 235 * Default busy timeout is 0ms. 236 * 237 * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY 238 * 239 * @throw SQLite::Exception in case of error 240 */ 241 void setBusyTimeout(const int aBusyTimeoutMs); 242 243 /** 244 * @brief Shortcut to execute one or multiple statements without results. 245 * 246 * This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : 247 * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" 248 * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" 249 * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" 250 * 251 * @see Database::tryExec() to execute, returning the sqlite result code 252 * @see Statement::exec() to handle precompiled statements (for better performances) without results 253 * @see Statement::executeStep() to handle "SELECT" queries with results 254 * 255 * @param[in] apQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements 256 * 257 * @return number of rows modified by the *last* INSERT, UPDATE or DELETE statement (beware of multiple statements) 258 * @warning undefined for CREATE or DROP table: returns the value of a previous INSERT, UPDATE or DELETE statement. 259 * 260 * @throw SQLite::Exception in case of error 261 */ 262 int exec(const char* apQueries); 263 264 /** 265 * @brief Shortcut to execute one or multiple statements without results. 266 * 267 * This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : 268 * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" 269 * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" 270 * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" 271 * 272 * @see Database::tryExec() to execute, returning the sqlite result code 273 * @see Statement::exec() to handle precompiled statements (for better performances) without results 274 * @see Statement::executeStep() to handle "SELECT" queries with results 275 * 276 * @param[in] aQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements 277 * 278 * @return number of rows modified by the *last* INSERT, UPDATE or DELETE statement (beware of multiple statements) 279 * @warning undefined for CREATE or DROP table: returns the value of a previous INSERT, UPDATE or DELETE statement. 280 * 281 * @throw SQLite::Exception in case of error 282 */ exec(const std::string & aQueries)283 int exec(const std::string& aQueries) 284 { 285 return exec(aQueries.c_str()); 286 } 287 288 /** 289 * @brief Try to execute one or multiple statements, returning the sqlite result code. 290 * 291 * This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : 292 * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" 293 * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" 294 * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" 295 * 296 * @see exec() to execute, returning number of rows modified 297 * 298 * @param[in] aQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements 299 * 300 * @return the sqlite result code. 301 */ 302 int tryExec(const char* apQueries) noexcept; 303 304 /** 305 * @brief Try to execute one or multiple statements, returning the sqlite result code. 306 * 307 * This is useful for any kind of statements other than the Data Query Language (DQL) "SELECT" : 308 * - Data Manipulation Language (DML) statements "INSERT", "UPDATE" and "DELETE" 309 * - Data Definition Language (DDL) statements "CREATE", "ALTER" and "DROP" 310 * - Data Control Language (DCL) statements "GRANT", "REVOKE", "COMMIT" and "ROLLBACK" 311 * 312 * @see exec() to execute, returning number of rows modified 313 * 314 * @param[in] aQueries one or multiple UTF-8 encoded, semicolon-separate SQL statements 315 * 316 * @return the sqlite result code. 317 */ tryExec(const std::string aQueries)318 int tryExec(const std::string aQueries) noexcept 319 { 320 return tryExec(aQueries.c_str()); 321 } 322 323 /** 324 * @brief Shortcut to execute a one step query and fetch the first column of the result. 325 * 326 * This is a shortcut to execute a simple statement with a single result. 327 * This should be used only for non reusable queries (else you should use a Statement with bind()). 328 * This should be used only for queries with expected results (else an exception is fired). 329 * 330 * @warning WARNING: Be very careful with this dangerous method: you have to 331 * make a COPY OF THE result, else it will be destroy before the next line 332 * (when the underlying temporary Statement and Column objects are destroyed) 333 * 334 * @see also Statement class for handling queries with multiple results 335 * 336 * @param[in] apQuery an UTF-8 encoded SQL query 337 * 338 * @return a temporary Column object with the first column of result. 339 * 340 * @throw SQLite::Exception in case of error 341 */ 342 Column execAndGet(const char* apQuery); 343 344 /** 345 * @brief Shortcut to execute a one step query and fetch the first column of the result. 346 * 347 * This is a shortcut to execute a simple statement with a single result. 348 * This should be used only for non reusable queries (else you should use a Statement with bind()). 349 * This should be used only for queries with expected results (else an exception is fired). 350 * 351 * @warning WARNING: Be very careful with this dangerous method: you have to 352 * make a COPY OF THE result, else it will be destroy before the next line 353 * (when the underlying temporary Statement and Column objects are destroyed) 354 * 355 * @see also Statement class for handling queries with multiple results 356 * 357 * @param[in] aQuery an UTF-8 encoded SQL query 358 * 359 * @return a temporary Column object with the first column of result. 360 * 361 * @throw SQLite::Exception in case of error 362 */ execAndGet(const std::string & aQuery)363 Column execAndGet(const std::string& aQuery) 364 { 365 return execAndGet(aQuery.c_str()); 366 } 367 368 /** 369 * @brief Shortcut to test if a table exists. 370 * 371 * Table names are case sensitive. 372 * 373 * @param[in] apTableName an UTF-8 encoded case sensitive Table name 374 * 375 * @return true if the table exists. 376 * 377 * @throw SQLite::Exception in case of error 378 */ 379 bool tableExists(const char* apTableName); 380 381 /** 382 * @brief Shortcut to test if a table exists. 383 * 384 * Table names are case sensitive. 385 * 386 * @param[in] aTableName an UTF-8 encoded case sensitive Table name 387 * 388 * @return true if the table exists. 389 * 390 * @throw SQLite::Exception in case of error 391 */ tableExists(const std::string & aTableName)392 bool tableExists(const std::string& aTableName) 393 { 394 return tableExists(aTableName.c_str()); 395 } 396 397 /** 398 * @brief Get the rowid of the most recent successful INSERT into the database from the current connection. 399 * 400 * Each entry in an SQLite table always has a unique 64-bit signed integer key called the rowid. 401 * If the table has a column of type INTEGER PRIMARY KEY, then it is an alias for the rowid. 402 * 403 * @return Rowid of the most recent successful INSERT into the database, or 0 if there was none. 404 */ 405 long long getLastInsertRowid() const noexcept; 406 407 /// Get total number of rows modified by all INSERT, UPDATE or DELETE statement since connection (not DROP table). 408 int getTotalChanges() const noexcept; 409 410 /// Return the numeric result code for the most recent failed API call (if any). 411 int getErrorCode() const noexcept; 412 /// Return the extended numeric result code for the most recent failed API call (if any). 413 int getExtendedErrorCode() const noexcept; 414 /// Return UTF-8 encoded English language explanation of the most recent failed API call (if any). 415 const char* getErrorMsg() const noexcept; 416 417 /// Return the filename used to open the database. getFilename()418 const std::string& getFilename() const noexcept 419 { 420 return mFilename; 421 } 422 423 /** 424 * @brief Return raw pointer to SQLite Database Connection Handle. 425 * 426 * This is often needed to mix this wrapper with other libraries or for advance usage not supported by SQLiteCpp. 427 */ getHandle()428 sqlite3* getHandle() const noexcept 429 { 430 return mSQLitePtr.get(); 431 } 432 433 /** 434 * @brief Create or redefine a SQL function or aggregate in the sqlite database. 435 * 436 * This is the equivalent of the sqlite3_create_function_v2 command. 437 * @see http://www.sqlite.org/c3ref/create_function.html 438 * 439 * @note UTF-8 text encoding assumed. 440 * 441 * @param[in] apFuncName Name of the SQL function to be created or redefined 442 * @param[in] aNbArg Number of arguments in the function 443 * @param[in] abDeterministic Optimize for deterministic functions (most are). A random number generator is not. 444 * @param[in] apApp Arbitrary pointer of user data, accessible with sqlite3_user_data(). 445 * @param[in] apFunc Pointer to a C-function to implement a scalar SQL function (apStep & apFinal nullptr) 446 * @param[in] apStep Pointer to a C-function to implement an aggregate SQL function (apFunc nullptr) 447 * @param[in] apFinal Pointer to a C-function to implement an aggregate SQL function (apFunc nullptr) 448 * @param[in] apDestroy If not nullptr, then it is the destructor for the application data pointer. 449 * 450 * @throw SQLite::Exception in case of error 451 */ 452 void createFunction(const char* apFuncName, 453 int aNbArg, 454 bool abDeterministic, 455 void* apApp, 456 void (*apFunc)(sqlite3_context *, int, sqlite3_value **), 457 void (*apStep)(sqlite3_context *, int, sqlite3_value **) = nullptr, 458 void (*apFinal)(sqlite3_context *) = nullptr, // NOLINT(readability/casting) 459 void (*apDestroy)(void *) = nullptr); 460 461 /** 462 * @brief Load a module into the current sqlite database instance. 463 * 464 * This is the equivalent of the sqlite3_load_extension call, but additionally enables 465 * module loading support prior to loading the requested module. 466 * 467 * @see http://www.sqlite.org/c3ref/load_extension.html 468 * 469 * @note UTF-8 text encoding assumed. 470 * 471 * @param[in] apExtensionName Name of the shared library containing extension 472 * @param[in] apEntryPointName Name of the entry point (nullptr to let sqlite work it out) 473 * 474 * @throw SQLite::Exception in case of error 475 */ 476 void loadExtension(const char* apExtensionName, const char* apEntryPointName); 477 478 /** 479 * @brief Set the key for the current sqlite database instance. 480 * 481 * This is the equivalent of the sqlite3_key call and should thus be called 482 * directly after opening the database. 483 * Open encrypted database -> call db.key("secret") -> database ready 484 * 485 * @param[in] aKey Key to decode/encode the database 486 * 487 * @throw SQLite::Exception in case of error 488 */ 489 void key(const std::string& aKey) const; 490 491 /** 492 * @brief Reset the key for the current sqlite database instance. 493 * 494 * This is the equivalent of the sqlite3_rekey call and should thus be called 495 * after the database has been opened with a valid key. To decrypt a 496 * database, call this method with an empty string. 497 * Open normal database -> call db.rekey("secret") -> encrypted database, database ready 498 * Open encrypted database -> call db.key("secret") -> call db.rekey("newsecret") -> change key, database ready 499 * Open encrypted database -> call db.key("secret") -> call db.rekey("") -> decrypted database, database ready 500 * 501 * @param[in] aNewKey New key to encode the database 502 * 503 * @throw SQLite::Exception in case of error 504 */ 505 void rekey(const std::string& aNewKey) const; 506 507 /** 508 * @brief Test if a file contains an unencrypted database. 509 * 510 * This is a simple test that reads the first bytes of a database file and 511 * compares them to the standard header for unencrypted databases. If the 512 * header does not match the standard string, we assume that we have an 513 * encrypted file. 514 * 515 * @param[in] aFilename path/uri to a file 516 * 517 * @return true if the database has the standard header. 518 * 519 * @throw SQLite::Exception in case of error 520 */ 521 static bool isUnencrypted(const std::string& aFilename); 522 523 /** 524 * @brief Parse SQLite header data from a database file. 525 * 526 * This function reads the first 100 bytes of a SQLite database file 527 * and reconstructs groups of individual bytes into the associated fields 528 * in a Header object. 529 * 530 * @param[in] aFilename path/uri to a file 531 * 532 * @return Header object containing file data 533 * 534 * @throw SQLite::Exception in case of error 535 */ 536 static Header getHeaderInfo(const std::string& aFilename); 537 538 // Parse SQLite header data from a database file. getHeaderInfo()539 Header getHeaderInfo() 540 { 541 return getHeaderInfo(mFilename); 542 } 543 544 /** 545 * @brief BackupType for the backup() method 546 */ 547 enum BackupType { Save, Load }; 548 549 /** 550 * @brief Load or save the database content. 551 * 552 * This function is used to load the contents of a database file on disk 553 * into the "main" database of open database connection, or to save the current 554 * contents of the database into a database file on disk. 555 * 556 * @throw SQLite::Exception in case of error 557 */ 558 void backup(const char* apFilename, BackupType aType); 559 560 /** 561 * @brief Check if aRet equal SQLITE_OK, else throw a SQLite::Exception with the SQLite error message 562 */ check(const int aRet)563 void check(const int aRet) const 564 { 565 if (SQLite::OK != aRet) 566 { 567 throw SQLite::Exception(getHandle(), aRet); 568 } 569 } 570 571 private: 572 // TODO: perhaps switch to having Statement sharing a pointer to the Connexion 573 std::unique_ptr<sqlite3, Deleter> mSQLitePtr; ///< Pointer to SQLite Database Connection Handle 574 std::string mFilename; ///< UTF-8 filename used to open the database 575 }; 576 577 578 } // namespace SQLite 579