diff options
author | Thiago Marcos P. Santos <tmpsantos@gmail.com> | 2020-01-04 02:10:21 +0200 |
---|---|---|
committer | Thiago Marcos P. Santos <tmpsantos@gmail.com> | 2020-01-08 13:07:12 +0200 |
commit | d08bb49c345839ca6e3986eea7d954b51719c7e9 (patch) | |
tree | 01e1b4b2762ab16df046a78437a3af755ade2c04 /platform | |
parent | f306957a6de5e89dad0a5053f809e1ef93664179 (diff) | |
download | qtlocation-mapboxgl-d08bb49c345839ca6e3986eea7d954b51719c7e9.tar.gz |
[core] Make it possible to reopen the database read-only for testing
Needed for Android, because the database is in readonly filesystem
and also because it is annoying to see files modified every time we
run the tests.
Diffstat (limited to 'platform')
3 files changed, 113 insertions, 38 deletions
diff --git a/platform/default/include/mbgl/storage/offline_database.hpp b/platform/default/include/mbgl/storage/offline_database.hpp index 67a19fcf26..8c39090fbc 100644 --- a/platform/default/include/mbgl/storage/offline_database.hpp +++ b/platform/default/include/mbgl/storage/offline_database.hpp @@ -38,8 +38,6 @@ struct MapboxTileLimitExceededException : util::Exception { class OfflineDatabase : private util::noncopyable { public: - // Limits affect ambient caching (put) only; resources required by offline - // regions are exempt. OfflineDatabase(std::string path); ~OfflineDatabase(); @@ -96,6 +94,9 @@ public: std::exception_ptr pack(); void runPackDatabaseAutomatically(bool autopack_) { autopack = autopack_; } + // For testing only + void reopenDatabaseReadOnlyForTesting(); + private: void initialize(); void handleError(const mapbox::sqlite::Exception&, const char* action); @@ -151,6 +152,7 @@ private: bool evict(uint64_t neededFreeSize); bool autopack = true; + bool readOnly = false; }; } // namespace mbgl diff --git a/platform/default/src/mbgl/storage/default_file_source.cpp b/platform/default/src/mbgl/storage/default_file_source.cpp index e6cdb411bb..2d96a5a9a2 100644 --- a/platform/default/src/mbgl/storage/default_file_source.cpp +++ b/platform/default/src/mbgl/storage/default_file_source.cpp @@ -182,6 +182,8 @@ public: onlineFileSource.setOnlineStatus(status); } + void reopenDatabaseReadOnlyForTesting() { offlineDatabase->reopenDatabaseReadOnlyForTesting(); } + void setMaximumConcurrentRequests(uint32_t maximumConcurrentRequests_) { onlineFileSource.setMaximumConcurrentRequests(maximumConcurrentRequests_); } @@ -385,6 +387,10 @@ void DefaultFileSource::setOnlineStatus(const bool status) { impl->actor().invoke(&Impl::setOnlineStatus, status); } +void DefaultFileSource::reopenDatabaseReadOnlyForTesting() { + impl->actor().invoke(&Impl::reopenDatabaseReadOnlyForTesting); +} + void DefaultFileSource::setMaximumConcurrentRequests(uint32_t maximumConcurrentRequests_) { impl->actor().invoke(&Impl::setMaximumConcurrentRequests, maximumConcurrentRequests_); } diff --git a/platform/default/src/mbgl/storage/offline_database.cpp b/platform/default/src/mbgl/storage/offline_database.cpp index 974815191b..695bbd82f5 100644 --- a/platform/default/src/mbgl/storage/offline_database.cpp +++ b/platform/default/src/mbgl/storage/offline_database.cpp @@ -31,6 +31,15 @@ void OfflineDatabase::initialize() { assert(!db); assert(statements.empty()); + if (readOnly) { + db = std::make_unique<mapbox::sqlite::Database>(mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadOnly)); + + db->setBusyTimeout(Milliseconds::max()); + db->exec("PRAGMA foreign_keys = ON"); + + return; + } + db = std::make_unique<mapbox::sqlite::Database>( mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadWriteCreate)); db->setBusyTimeout(Milliseconds::max()); @@ -153,12 +162,16 @@ void OfflineDatabase::removeExisting() { void OfflineDatabase::removeOldCacheTable() { assert(db); + assert(!readOnly); + db->exec("DROP TABLE IF EXISTS http_cache"); if (autopack) vacuum(); } void OfflineDatabase::createSchema() { assert(db); + assert(!readOnly); + vacuum(); db->exec("PRAGMA journal_mode = DELETE"); db->exec("PRAGMA synchronous = FULL"); @@ -170,6 +183,8 @@ void OfflineDatabase::createSchema() { void OfflineDatabase::migrateToVersion3() { assert(db); + assert(!readOnly); + vacuum(); db->exec("PRAGMA user_version = 3"); } @@ -182,6 +197,8 @@ void OfflineDatabase::migrateToVersion3() { void OfflineDatabase::migrateToVersion5() { assert(db); + assert(!readOnly); + db->exec("PRAGMA journal_mode = DELETE"); db->exec("PRAGMA synchronous = FULL"); db->exec("PRAGMA user_version = 5"); @@ -189,6 +206,8 @@ void OfflineDatabase::migrateToVersion5() { void OfflineDatabase::migrateToVersion6() { assert(db); + assert(!readOnly); + mapbox::sqlite::Transaction transaction(*db); db->exec("ALTER TABLE resources ADD COLUMN must_revalidate INTEGER NOT NULL DEFAULT 0"); db->exec("ALTER TABLE tiles ADD COLUMN must_revalidate INTEGER NOT NULL DEFAULT 0"); @@ -198,6 +217,8 @@ void OfflineDatabase::migrateToVersion6() { void OfflineDatabase::vacuum() { assert(db); + assert(!readOnly); + if (getPragma<int64_t>("PRAGMA auto_vacuum") != 2 /*INCREMENTAL*/) { db->exec("PRAGMA auto_vacuum = INCREMENTAL"); db->exec("VACUUM"); @@ -248,6 +269,8 @@ optional<int64_t> OfflineDatabase::hasInternal(const Resource& resource) { } std::pair<bool, uint64_t> OfflineDatabase::put(const Resource& resource, const Response& response) try { + assert(!readOnly); + if (!db) { initialize(); } @@ -266,6 +289,8 @@ std::pair<bool, uint64_t> OfflineDatabase::put(const Resource& resource, const R } std::pair<bool, uint64_t> OfflineDatabase::putInternal(const Resource& resource, const Response& response, bool evict_) { + assert(!readOnly); + if (response.error) { return { false, 0 }; } @@ -303,19 +328,20 @@ std::pair<bool, uint64_t> OfflineDatabase::putInternal(const Resource& resource, optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resource& resource) { // Update accessed timestamp used for LRU eviction. - try { - mapbox::sqlite::Query accessedQuery{ getStatement("UPDATE resources SET accessed = ?1 WHERE url = ?2") }; - accessedQuery.bind(1, util::now()); - accessedQuery.bind(2, resource.url); - accessedQuery.run(); - } catch (const mapbox::sqlite::Exception& ex) { - if (ex.code == mapbox::sqlite::ResultCode::NotADB || - ex.code == mapbox::sqlite::ResultCode::Corrupt) { - throw; - } + if (!readOnly) { + try { + mapbox::sqlite::Query accessedQuery{getStatement("UPDATE resources SET accessed = ?1 WHERE url = ?2")}; + accessedQuery.bind(1, util::now()); + accessedQuery.bind(2, resource.url); + accessedQuery.run(); + } catch (const mapbox::sqlite::Exception& ex) { + if (ex.code == mapbox::sqlite::ResultCode::NotADB || ex.code == mapbox::sqlite::ResultCode::Corrupt) { + throw; + } - // If we don't have any indication that the database is corrupt, continue as usual. - Log::Warning(Event::Database, static_cast<int>(ex.code), "Can't update timestamp: %s", ex.what()); + // If we don't have any indication that the database is corrupt, continue as usual. + Log::Warning(Event::Database, static_cast<int>(ex.code), "Can't update timestamp: %s", ex.what()); + } } // clang-format off @@ -368,6 +394,8 @@ bool OfflineDatabase::putResource(const Resource& resource, const Response& response, const std::string& data, bool compressed) { + assert(!readOnly); + if (response.notModified) { // clang-format off mapbox::sqlite::Query notModifiedQuery{ getStatement( @@ -451,32 +479,34 @@ bool OfflineDatabase::putResource(const Resource& resource, optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource::TileData& tile) { // Update accessed timestamp used for LRU eviction. - try { - // clang-format off - mapbox::sqlite::Query accessedQuery{ getStatement( - "UPDATE tiles " - "SET accessed = ?1 " - "WHERE url_template = ?2 " - " AND pixel_ratio = ?3 " - " AND x = ?4 " - " AND y = ?5 " - " AND z = ?6 ") }; - // clang-format on + if (!readOnly) { + try { + // clang-format off + mapbox::sqlite::Query accessedQuery{ getStatement( + "UPDATE tiles " + "SET accessed = ?1 " + "WHERE url_template = ?2 " + " AND pixel_ratio = ?3 " + " AND x = ?4 " + " AND y = ?5 " + " AND z = ?6 ") }; + // clang-format on + + accessedQuery.bind(1, util::now()); + accessedQuery.bind(2, tile.urlTemplate); + accessedQuery.bind(3, tile.pixelRatio); + accessedQuery.bind(4, tile.x); + accessedQuery.bind(5, tile.y); + accessedQuery.bind(6, tile.z); + accessedQuery.run(); + } catch (const mapbox::sqlite::Exception& ex) { + if (ex.code == mapbox::sqlite::ResultCode::NotADB || ex.code == mapbox::sqlite::ResultCode::Corrupt) { + throw; + } - accessedQuery.bind(1, util::now()); - accessedQuery.bind(2, tile.urlTemplate); - accessedQuery.bind(3, tile.pixelRatio); - accessedQuery.bind(4, tile.x); - accessedQuery.bind(5, tile.y); - accessedQuery.bind(6, tile.z); - accessedQuery.run(); - } catch (const mapbox::sqlite::Exception& ex) { - if (ex.code == mapbox::sqlite::ResultCode::NotADB || ex.code == mapbox::sqlite::ResultCode::Corrupt) { - throw; + // If we don't have any indication that the database is corrupt, continue as usual. + Log::Warning(Event::Database, static_cast<int>(ex.code), "Can't update timestamp: %s", ex.what()); } - - // If we don't have any indication that the database is corrupt, continue as usual. - Log::Warning(Event::Database, static_cast<int>(ex.code), "Can't update timestamp: %s", ex.what()); } // clang-format off @@ -552,6 +582,8 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, const Response& response, const std::string& data, bool compressed) { + assert(!readOnly); + if (response.notModified) { // clang-format off mapbox::sqlite::Query notModifiedQuery{ getStatement( @@ -652,6 +684,8 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, } std::exception_ptr OfflineDatabase::invalidateAmbientCache() try { + assert(!readOnly); + // clang-format off mapbox::sqlite::Query tileQuery{ getStatement( "UPDATE tiles " @@ -683,6 +717,8 @@ std::exception_ptr OfflineDatabase::invalidateAmbientCache() try { } std::exception_ptr OfflineDatabase::clearAmbientCache() try { + assert(!readOnly); + // clang-format off mapbox::sqlite::Query tileQuery{ getStatement( "DELETE FROM tiles " @@ -714,6 +750,8 @@ std::exception_ptr OfflineDatabase::clearAmbientCache() try { } std::exception_ptr OfflineDatabase::invalidateRegion(int64_t regionID) try { + assert(!readOnly); + { // clang-format off mapbox::sqlite::Query tileQuery{ getStatement( @@ -776,6 +814,8 @@ expected<OfflineRegions, std::exception_ptr> OfflineDatabase::listRegions() try expected<OfflineRegion, std::exception_ptr> OfflineDatabase::createRegion(const OfflineRegionDefinition& definition, const OfflineRegionMetadata& metadata) try { + assert(!readOnly); + // clang-format off mapbox::sqlite::Query query{ getStatement( "INSERT INTO regions (definition, description) " @@ -793,6 +833,8 @@ OfflineDatabase::createRegion(const OfflineRegionDefinition& definition, expected<OfflineRegions, std::exception_ptr> OfflineDatabase::mergeDatabase(const std::string& sideDatabasePath) { + assert(!readOnly); + try { // clang-format off mapbox::sqlite::Query query{ getStatement("ATTACH DATABASE ?1 AS side") }; @@ -869,6 +911,8 @@ OfflineDatabase::mergeDatabase(const std::string& sideDatabasePath) { expected<OfflineRegionMetadata, std::exception_ptr> OfflineDatabase::updateMetadata(const int64_t regionID, const OfflineRegionMetadata& metadata) try { + assert(!readOnly); + // clang-format off mapbox::sqlite::Query query{ getStatement( "UPDATE regions SET description = ?1 " @@ -885,6 +929,8 @@ OfflineDatabase::updateMetadata(const int64_t regionID, const OfflineRegionMetad } std::exception_ptr OfflineDatabase::deleteRegion(OfflineRegion&& region) try { + assert(!readOnly); + { mapbox::sqlite::Query query{ getStatement("DELETE FROM regions WHERE id = ?") }; query.bind(1, region.getID()); @@ -920,6 +966,8 @@ optional<int64_t> OfflineDatabase::hasRegionResource(const Resource& resource) t uint64_t OfflineDatabase::putRegionResource(int64_t regionID, const Resource& resource, const Response& response) try { + assert(!readOnly); + if (!db) { initialize(); } @@ -935,6 +983,8 @@ uint64_t OfflineDatabase::putRegionResource(int64_t regionID, void OfflineDatabase::putRegionResources(int64_t regionID, const std::list<std::tuple<Resource, Response>>& resources, OfflineRegionStatus& status) try { + assert(!readOnly); + if (!db) { initialize(); } @@ -978,6 +1028,8 @@ void OfflineDatabase::putRegionResources(int64_t regionID, } uint64_t OfflineDatabase::putRegionResourceInternal(int64_t regionID, const Resource& resource, const Response& response) { + assert(!readOnly); + uint64_t size = putInternal(resource, response, false).second; bool previouslyUnused = markUsed(regionID, resource); @@ -996,6 +1048,8 @@ uint64_t OfflineDatabase::putRegionResourceInternal(int64_t regionID, const Reso } bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { + assert(!readOnly); + if (resource.kind == Resource::Kind::Tile) { // clang-format off mapbox::sqlite::Query insertQuery{ getStatement( @@ -1148,6 +1202,8 @@ T OfflineDatabase::getPragma(const char* sql) { // delete an arbitrary number of old cache entries. The free pages approach saves // us from calling VACUUM or keeping a running total, which can be costly. bool OfflineDatabase::evict(uint64_t neededFreeSize) { + assert(!readOnly); + uint64_t pageSize = getPragma<int64_t>("PRAGMA page_size"); uint64_t pageCount = getPragma<int64_t>("PRAGMA page_count"); @@ -1322,4 +1378,15 @@ std::exception_ptr OfflineDatabase::resetDatabase() try { return std::current_exception(); } +void OfflineDatabase::reopenDatabaseReadOnlyForTesting() { + readOnly = true; + + try { + cleanup(); + initialize(); + } catch (...) { + handleError("reopen database read-only"); + } +} + } // namespace mbgl |