From e8d6303d783a124aa5ba2fcf797df80a88ed546f Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Mon, 5 Mar 2018 22:08:59 +0200 Subject: Bump Mapbox GL Native mapbox-gl-native @ ae0e583590ce8d7a564c8d9cb7c0fcee5515125e --- platform/default/mbgl/storage/offline_database.cpp | 508 ++++++++++----------- platform/default/mbgl/storage/offline_database.hpp | 20 +- platform/default/sqlite3.cpp | 320 ++++++++----- platform/default/sqlite3.hpp | 101 +++- platform/qt/src/qmapbox.cpp | 14 +- platform/qt/src/qmapboxgl.cpp | 4 +- platform/qt/src/qmapboxgl_map_observer.cpp | 2 +- platform/qt/src/sqlite3.cpp | 213 ++++----- src/mbgl/renderer/layers/render_heatmap_layer.cpp | 2 +- src/mbgl/util/tiny_sdf.cpp | 2 +- 10 files changed, 647 insertions(+), 539 deletions(-) diff --git a/platform/default/mbgl/storage/offline_database.cpp b/platform/default/mbgl/storage/offline_database.cpp index 65c2097182..4611e69f43 100644 --- a/platform/default/mbgl/storage/offline_database.cpp +++ b/platform/default/mbgl/storage/offline_database.cpp @@ -10,11 +10,6 @@ namespace mbgl { -OfflineDatabase::Statement::~Statement() { - stmt.reset(); - stmt.clearBindings(); -} - OfflineDatabase::OfflineDatabase(std::string path_, uint64_t maximumCacheSize_) : path(std::move(path_)), maximumCacheSize(maximumCacheSize_) { @@ -28,7 +23,7 @@ OfflineDatabase::~OfflineDatabase() { statements.clear(); db.reset(); } catch (mapbox::sqlite::Exception& ex) { - Log::Error(Event::Database, ex.code, ex.what()); + Log::Error(Event::Database, (int)ex.code, ex.what()); } } @@ -57,13 +52,13 @@ void OfflineDatabase::ensureSchema() { removeExisting(); connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create); } catch (mapbox::sqlite::Exception& ex) { - if (ex.code != mapbox::sqlite::Exception::Code::CANTOPEN && ex.code != mapbox::sqlite::Exception::Code::NOTADB) { + if (ex.code != mapbox::sqlite::ResultCode::CantOpen && ex.code != mapbox::sqlite::ResultCode::NotADB) { Log::Error(Event::Database, "Unexpected error connecting to database: %s", ex.what()); throw; } try { - if (ex.code == mapbox::sqlite::Exception::Code::NOTADB) { + if (ex.code == mapbox::sqlite::ResultCode::NotADB) { removeExisting(); } connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create); @@ -92,9 +87,7 @@ void OfflineDatabase::ensureSchema() { } int OfflineDatabase::userVersion() { - auto stmt = db->prepare("PRAGMA user_version"); - stmt.run(); - return stmt.get(0); + return static_cast(getPragma("PRAGMA user_version")); } void OfflineDatabase::removeExisting() { @@ -135,14 +128,12 @@ void OfflineDatabase::migrateToVersion6() { transaction.commit(); } -OfflineDatabase::Statement OfflineDatabase::getStatement(const char * sql) { +mapbox::sqlite::Statement& OfflineDatabase::getStatement(const char* sql) { auto it = statements.find(sql); - - if (it != statements.end()) { - return Statement(*it->second); + if (it == statements.end()) { + it = statements.emplace(sql, std::make_unique(*db, sql)).first; } - - return Statement(*statements.emplace(sql, std::make_unique(db->prepare(sql))).first->second); + return *it->second; } optional OfflineDatabase::get(const Resource& resource) { @@ -209,41 +200,40 @@ std::pair OfflineDatabase::putInternal(const Resource& resource, } optional> OfflineDatabase::getResource(const Resource& resource) { - // clang-format off - Statement accessedStmt = getStatement( - "UPDATE resources SET accessed = ?1 WHERE url = ?2"); - // clang-format on - - accessedStmt->bind(1, util::now()); - accessedStmt->bind(2, resource.url); - accessedStmt->run(); + // Update accessed timestamp used for LRU eviction. + { + 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(); + } // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( // 0 1 2 3 4 5 "SELECT etag, expires, must_revalidate, modified, data, compressed " "FROM resources " - "WHERE url = ?"); + "WHERE url = ?") }; // clang-format on - stmt->bind(1, resource.url); + query.bind(1, resource.url); - if (!stmt->run()) { + if (!query.run()) { return {}; } Response response; uint64_t size = 0; - response.etag = stmt->get>(0); - response.expires = stmt->get>(1); - response.mustRevalidate = stmt->get(2); - response.modified = stmt->get>(3); + response.etag = query.get>(0); + response.expires = query.get>(1); + response.mustRevalidate = query.get(2); + response.modified = query.get>(3); - optional data = stmt->get>(4); + auto data = query.get>(4); if (!data) { response.noContent = true; - } else if (stmt->get(5)) { + } else if (query.get(5)) { response.data = std::make_shared(util::decompress(*data)); size = data->length(); } else { @@ -255,16 +245,13 @@ optional> OfflineDatabase::getResource(const Resou } optional OfflineDatabase::hasResource(const Resource& resource) { - // clang-format off - Statement stmt = getStatement("SELECT length(data) FROM resources WHERE url = ?"); - // clang-format on - - stmt->bind(1, resource.url); - if (!stmt->run()) { + mapbox::sqlite::Query query{ getStatement("SELECT length(data) FROM resources WHERE url = ?") }; + query.bind(1, resource.url); + if (!query.run()) { return {}; } - return stmt->get>(0); + return query.get>(0); } bool OfflineDatabase::putResource(const Resource& resource, @@ -273,19 +260,19 @@ bool OfflineDatabase::putResource(const Resource& resource, bool compressed) { if (response.notModified) { // clang-format off - Statement update = getStatement( + mapbox::sqlite::Query notModifiedQuery{ getStatement( "UPDATE resources " "SET accessed = ?1, " " expires = ?2, " " must_revalidate = ?3 " - "WHERE url = ?4 "); + "WHERE url = ?4 ") }; // clang-format on - update->bind(1, util::now()); - update->bind(2, response.expires); - update->bind(3, response.mustRevalidate); - update->bind(4, resource.url); - update->run(); + notModifiedQuery.bind(1, util::now()); + notModifiedQuery.bind(2, response.expires); + notModifiedQuery.bind(3, response.mustRevalidate); + notModifiedQuery.bind(4, resource.url); + notModifiedQuery.run(); return false; } @@ -296,7 +283,7 @@ bool OfflineDatabase::putResource(const Resource& resource, mapbox::sqlite::Transaction transaction(*db, mapbox::sqlite::Transaction::Immediate); // clang-format off - Statement update = getStatement( + mapbox::sqlite::Query updateQuery{ getStatement( "UPDATE resources " "SET kind = ?1, " " etag = ?2, " @@ -306,81 +293,83 @@ bool OfflineDatabase::putResource(const Resource& resource, " accessed = ?6, " " data = ?7, " " compressed = ?8 " - "WHERE url = ?9 "); + "WHERE url = ?9 ") }; // clang-format on - update->bind(1, int(resource.kind)); - update->bind(2, response.etag); - update->bind(3, response.expires); - update->bind(4, response.mustRevalidate); - update->bind(5, response.modified); - update->bind(6, util::now()); - update->bind(9, resource.url); + updateQuery.bind(1, int(resource.kind)); + updateQuery.bind(2, response.etag); + updateQuery.bind(3, response.expires); + updateQuery.bind(4, response.mustRevalidate); + updateQuery.bind(5, response.modified); + updateQuery.bind(6, util::now()); + updateQuery.bind(9, resource.url); if (response.noContent) { - update->bind(7, nullptr); - update->bind(8, false); + updateQuery.bind(7, nullptr); + updateQuery.bind(8, false); } else { - update->bindBlob(7, data.data(), data.size(), false); - update->bind(8, compressed); + updateQuery.bindBlob(7, data.data(), data.size(), false); + updateQuery.bind(8, compressed); } - update->run(); - if (update->changes() != 0) { + updateQuery.run(); + if (updateQuery.changes() != 0) { transaction.commit(); return false; } // clang-format off - Statement insert = getStatement( + mapbox::sqlite::Query insertQuery{ getStatement( "INSERT INTO resources (url, kind, etag, expires, must_revalidate, modified, accessed, data, compressed) " - "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9) "); + "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9) ") }; // clang-format on - insert->bind(1, resource.url); - insert->bind(2, int(resource.kind)); - insert->bind(3, response.etag); - insert->bind(4, response.expires); - insert->bind(5, response.mustRevalidate); - insert->bind(6, response.modified); - insert->bind(7, util::now()); + insertQuery.bind(1, resource.url); + insertQuery.bind(2, int(resource.kind)); + insertQuery.bind(3, response.etag); + insertQuery.bind(4, response.expires); + insertQuery.bind(5, response.mustRevalidate); + insertQuery.bind(6, response.modified); + insertQuery.bind(7, util::now()); if (response.noContent) { - insert->bind(8, nullptr); - insert->bind(9, false); + insertQuery.bind(8, nullptr); + insertQuery.bind(9, false); } else { - insert->bindBlob(8, data.data(), data.size(), false); - insert->bind(9, compressed); + insertQuery.bindBlob(8, data.data(), data.size(), false); + insertQuery.bind(9, compressed); } - insert->run(); + insertQuery.run(); transaction.commit(); return true; } optional> OfflineDatabase::getTile(const Resource::TileData& tile) { - // clang-format off - Statement accessedStmt = 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 + { + // 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 - accessedStmt->bind(1, util::now()); - accessedStmt->bind(2, tile.urlTemplate); - accessedStmt->bind(3, tile.pixelRatio); - accessedStmt->bind(4, tile.x); - accessedStmt->bind(5, tile.y); - accessedStmt->bind(6, tile.z); - accessedStmt->run(); + 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(); + } // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( // 0 1 2, 3, 4, 5 "SELECT etag, expires, must_revalidate, modified, data, compressed " "FROM tiles " @@ -388,31 +377,31 @@ optional> OfflineDatabase::getTile(const Resource: " AND pixel_ratio = ?2 " " AND x = ?3 " " AND y = ?4 " - " AND z = ?5 "); + " AND z = ?5 ") }; // clang-format on - stmt->bind(1, tile.urlTemplate); - stmt->bind(2, tile.pixelRatio); - stmt->bind(3, tile.x); - stmt->bind(4, tile.y); - stmt->bind(5, tile.z); + query.bind(1, tile.urlTemplate); + query.bind(2, tile.pixelRatio); + query.bind(3, tile.x); + query.bind(4, tile.y); + query.bind(5, tile.z); - if (!stmt->run()) { + if (!query.run()) { return {}; } Response response; uint64_t size = 0; - response.etag = stmt->get>(0); - response.expires = stmt->get>(1); - response.mustRevalidate = stmt->get(2); - response.modified = stmt->get>(3); + response.etag = query.get>(0); + response.expires = query.get>(1); + response.mustRevalidate = query.get(2); + response.modified = query.get>(3); - optional data = stmt->get>(4); + optional data = query.get>(4); if (!data) { response.noContent = true; - } else if (stmt->get(5)) { + } else if (query.get(5)) { response.data = std::make_shared(util::decompress(*data)); size = data->length(); } else { @@ -425,27 +414,27 @@ optional> OfflineDatabase::getTile(const Resource: optional OfflineDatabase::hasTile(const Resource::TileData& tile) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query size{ getStatement( "SELECT length(data) " "FROM tiles " "WHERE url_template = ?1 " " AND pixel_ratio = ?2 " " AND x = ?3 " " AND y = ?4 " - " AND z = ?5 "); + " AND z = ?5 ") }; // clang-format on - stmt->bind(1, tile.urlTemplate); - stmt->bind(2, tile.pixelRatio); - stmt->bind(3, tile.x); - stmt->bind(4, tile.y); - stmt->bind(5, tile.z); + size.bind(1, tile.urlTemplate); + size.bind(2, tile.pixelRatio); + size.bind(3, tile.x); + size.bind(4, tile.y); + size.bind(5, tile.z); - if (!stmt->run()) { + if (!size.run()) { return {}; } - return stmt->get>(0); + return size.get>(0); } bool OfflineDatabase::putTile(const Resource::TileData& tile, @@ -454,7 +443,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, bool compressed) { if (response.notModified) { // clang-format off - Statement update = getStatement( + mapbox::sqlite::Query notModifiedQuery{ getStatement( "UPDATE tiles " "SET accessed = ?1, " " expires = ?2, " @@ -463,18 +452,18 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, " AND pixel_ratio = ?5 " " AND x = ?6 " " AND y = ?7 " - " AND z = ?8 "); + " AND z = ?8 ") }; // clang-format on - update->bind(1, util::now()); - update->bind(2, response.expires); - update->bind(3, response.mustRevalidate); - update->bind(4, tile.urlTemplate); - update->bind(5, tile.pixelRatio); - update->bind(6, tile.x); - update->bind(7, tile.y); - update->bind(8, tile.z); - update->run(); + notModifiedQuery.bind(1, util::now()); + notModifiedQuery.bind(2, response.expires); + notModifiedQuery.bind(3, response.mustRevalidate); + notModifiedQuery.bind(4, tile.urlTemplate); + notModifiedQuery.bind(5, tile.pixelRatio); + notModifiedQuery.bind(6, tile.x); + notModifiedQuery.bind(7, tile.y); + notModifiedQuery.bind(8, tile.z); + notModifiedQuery.run(); return false; } @@ -485,7 +474,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, mapbox::sqlite::Transaction transaction(*db, mapbox::sqlite::Transaction::Immediate); // clang-format off - Statement update = getStatement( + mapbox::sqlite::Query updateQuery{ getStatement( "UPDATE tiles " "SET modified = ?1, " " etag = ?2, " @@ -498,78 +487,75 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, " AND pixel_ratio = ?9 " " AND x = ?10 " " AND y = ?11 " - " AND z = ?12 "); + " AND z = ?12 ") }; // clang-format on - update->bind(1, response.modified); - update->bind(2, response.etag); - update->bind(3, response.expires); - update->bind(4, response.mustRevalidate); - update->bind(5, util::now()); - update->bind(8, tile.urlTemplate); - update->bind(9, tile.pixelRatio); - update->bind(10, tile.x); - update->bind(11, tile.y); - update->bind(12, tile.z); + updateQuery.bind(1, response.modified); + updateQuery.bind(2, response.etag); + updateQuery.bind(3, response.expires); + updateQuery.bind(4, response.mustRevalidate); + updateQuery.bind(5, util::now()); + updateQuery.bind(8, tile.urlTemplate); + updateQuery.bind(9, tile.pixelRatio); + updateQuery.bind(10, tile.x); + updateQuery.bind(11, tile.y); + updateQuery.bind(12, tile.z); if (response.noContent) { - update->bind(6, nullptr); - update->bind(7, false); + updateQuery.bind(6, nullptr); + updateQuery.bind(7, false); } else { - update->bindBlob(6, data.data(), data.size(), false); - update->bind(7, compressed); + updateQuery.bindBlob(6, data.data(), data.size(), false); + updateQuery.bind(7, compressed); } - update->run(); - if (update->changes() != 0) { + updateQuery.run(); + if (updateQuery.changes() != 0) { transaction.commit(); return false; } // clang-format off - Statement insert = getStatement( + mapbox::sqlite::Query insertQuery{ getStatement( "INSERT INTO tiles (url_template, pixel_ratio, x, y, z, modified, must_revalidate, etag, expires, accessed, data, compressed) " - "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)"); + "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)") }; // clang-format on - insert->bind(1, tile.urlTemplate); - insert->bind(2, tile.pixelRatio); - insert->bind(3, tile.x); - insert->bind(4, tile.y); - insert->bind(5, tile.z); - insert->bind(6, response.modified); - insert->bind(7, response.mustRevalidate); - insert->bind(8, response.etag); - insert->bind(9, response.expires); - insert->bind(10, util::now()); + insertQuery.bind(1, tile.urlTemplate); + insertQuery.bind(2, tile.pixelRatio); + insertQuery.bind(3, tile.x); + insertQuery.bind(4, tile.y); + insertQuery.bind(5, tile.z); + insertQuery.bind(6, response.modified); + insertQuery.bind(7, response.mustRevalidate); + insertQuery.bind(8, response.etag); + insertQuery.bind(9, response.expires); + insertQuery.bind(10, util::now()); if (response.noContent) { - insert->bind(11, nullptr); - insert->bind(12, false); + insertQuery.bind(11, nullptr); + insertQuery.bind(12, false); } else { - insert->bindBlob(11, data.data(), data.size(), false); - insert->bind(12, compressed); + insertQuery.bindBlob(11, data.data(), data.size(), false); + insertQuery.bind(12, compressed); } - insert->run(); + insertQuery.run(); transaction.commit(); return true; } std::vector OfflineDatabase::listRegions() { - // clang-format off - Statement stmt = getStatement( - "SELECT id, definition, description FROM regions"); - // clang-format on + mapbox::sqlite::Query query{ getStatement("SELECT id, definition, description FROM regions") }; std::vector result; - while (stmt->run()) { + while (query.run()) { result.push_back(OfflineRegion( - stmt->get(0), - decodeOfflineRegionDefinition(stmt->get(1)), - stmt->get>(2))); + query.get(0), + decodeOfflineRegionDefinition(query.get(1)), + query.get>(2))); } return result; @@ -578,39 +564,37 @@ std::vector OfflineDatabase::listRegions() { OfflineRegion OfflineDatabase::createRegion(const OfflineRegionDefinition& definition, const OfflineRegionMetadata& metadata) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "INSERT INTO regions (definition, description) " - "VALUES (?1, ?2) "); + "VALUES (?1, ?2) ") }; // clang-format on - stmt->bind(1, encodeOfflineRegionDefinition(definition)); - stmt->bindBlob(2, metadata); - stmt->run(); + query.bind(1, encodeOfflineRegionDefinition(definition)); + query.bindBlob(2, metadata); + query.run(); - return OfflineRegion(stmt->lastInsertRowId(), definition, metadata); + return OfflineRegion(query.lastInsertRowId(), definition, metadata); } OfflineRegionMetadata OfflineDatabase::updateMetadata(const int64_t regionID, const OfflineRegionMetadata& metadata) { // clang-format off - Statement stmt = getStatement( - "UPDATE regions SET description = ?1" - "WHERE id = ?2"); + mapbox::sqlite::Query query{ getStatement( + "UPDATE regions SET description = ?1 " + "WHERE id = ?2") }; // clang-format on - stmt->bindBlob(1, metadata); - stmt->bind(2, regionID); - stmt->run(); + query.bindBlob(1, metadata); + query.bind(2, regionID); + query.run(); return metadata; } void OfflineDatabase::deleteRegion(OfflineRegion&& region) { - // clang-format off - Statement stmt = getStatement( - "DELETE FROM regions WHERE id = ?"); - // clang-format on - - stmt->bind(1, region.getID()); - stmt->run(); + { + mapbox::sqlite::Query query{ getStatement("DELETE FROM regions WHERE id = ?") }; + query.bind(1, region.getID()); + query.run(); + } evict(0); db->exec("PRAGMA incremental_vacuum"); @@ -656,7 +640,7 @@ uint64_t OfflineDatabase::putRegionResource(int64_t regionID, const Resource& re bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { if (resource.kind == Resource::Kind::Tile) { // clang-format off - Statement insert = getStatement( + mapbox::sqlite::Query insertQuery{ getStatement( "INSERT OR IGNORE INTO region_tiles (region_id, tile_id) " "SELECT ?1, tiles.id " "FROM tiles " @@ -664,24 +648,24 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { " AND pixel_ratio = ?3 " " AND x = ?4 " " AND y = ?5 " - " AND z = ?6 "); + " AND z = ?6 ") }; // clang-format on const Resource::TileData& tile = *resource.tileData; - insert->bind(1, regionID); - insert->bind(2, tile.urlTemplate); - insert->bind(3, tile.pixelRatio); - insert->bind(4, tile.x); - insert->bind(5, tile.y); - insert->bind(6, tile.z); - insert->run(); - - if (insert->changes() == 0) { + insertQuery.bind(1, regionID); + insertQuery.bind(2, tile.urlTemplate); + insertQuery.bind(3, tile.pixelRatio); + insertQuery.bind(4, tile.x); + insertQuery.bind(5, tile.y); + insertQuery.bind(6, tile.z); + insertQuery.run(); + + if (insertQuery.changes() == 0) { return false; } // clang-format off - Statement select = getStatement( + mapbox::sqlite::Query selectQuery{ getStatement( "SELECT region_id " "FROM region_tiles, tiles " "WHERE region_id != ?1 " @@ -690,58 +674,54 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { " AND x = ?4 " " AND y = ?5 " " AND z = ?6 " - "LIMIT 1 "); + "LIMIT 1 ") }; // clang-format on - select->bind(1, regionID); - select->bind(2, tile.urlTemplate); - select->bind(3, tile.pixelRatio); - select->bind(4, tile.x); - select->bind(5, tile.y); - select->bind(6, tile.z); - return !select->run(); + selectQuery.bind(1, regionID); + selectQuery.bind(2, tile.urlTemplate); + selectQuery.bind(3, tile.pixelRatio); + selectQuery.bind(4, tile.x); + selectQuery.bind(5, tile.y); + selectQuery.bind(6, tile.z); + return !selectQuery.run(); } else { // clang-format off - Statement insert = getStatement( + mapbox::sqlite::Query insertQuery{ getStatement( "INSERT OR IGNORE INTO region_resources (region_id, resource_id) " "SELECT ?1, resources.id " "FROM resources " - "WHERE resources.url = ?2 "); + "WHERE resources.url = ?2 ") }; // clang-format on - insert->bind(1, regionID); - insert->bind(2, resource.url); - insert->run(); + insertQuery.bind(1, regionID); + insertQuery.bind(2, resource.url); + insertQuery.run(); - if (insert->changes() == 0) { + if (insertQuery.changes() == 0) { return false; } // clang-format off - Statement select = getStatement( + mapbox::sqlite::Query selectQuery{ getStatement( "SELECT region_id " "FROM region_resources, resources " "WHERE region_id != ?1 " " AND resources.url = ?2 " - "LIMIT 1 "); + "LIMIT 1 ") }; // clang-format on - select->bind(1, regionID); - select->bind(2, resource.url); - return !select->run(); + selectQuery.bind(1, regionID); + selectQuery.bind(2, resource.url); + return !selectQuery.run(); } } OfflineRegionDefinition OfflineDatabase::getRegionDefinition(int64_t regionID) { - // clang-format off - Statement stmt = getStatement( - "SELECT definition FROM regions WHERE id = ?1"); - // clang-format on - - stmt->bind(1, regionID); - stmt->run(); + mapbox::sqlite::Query query{ getStatement("SELECT definition FROM regions WHERE id = ?1") }; + query.bind(1, regionID); + query.run(); - return decodeOfflineRegionDefinition(stmt->get(0)); + return decodeOfflineRegionDefinition(query.get(0)); } OfflineRegionStatus OfflineDatabase::getRegionCompletedStatus(int64_t regionID) { @@ -760,35 +740,35 @@ OfflineRegionStatus OfflineDatabase::getRegionCompletedStatus(int64_t regionID) std::pair OfflineDatabase::getCompletedResourceCountAndSize(int64_t regionID) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "SELECT COUNT(*), SUM(LENGTH(data)) " "FROM region_resources, resources " "WHERE region_id = ?1 " - "AND resource_id = resources.id "); + "AND resource_id = resources.id ") }; // clang-format on - stmt->bind(1, regionID); - stmt->run(); - return { stmt->get(0), stmt->get(1) }; + query.bind(1, regionID); + query.run(); + return { query.get(0), query.get(1) }; } std::pair OfflineDatabase::getCompletedTileCountAndSize(int64_t regionID) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "SELECT COUNT(*), SUM(LENGTH(data)) " "FROM region_tiles, tiles " "WHERE region_id = ?1 " - "AND tile_id = tiles.id "); + "AND tile_id = tiles.id ") }; // clang-format on - stmt->bind(1, regionID); - stmt->run(); - return { stmt->get(0), stmt->get(1) }; + query.bind(1, regionID); + query.run(); + return { query.get(0), query.get(1) }; } template -T OfflineDatabase::getPragma(const char * sql) { - Statement stmt = getStatement(sql); - stmt->run(); - return stmt->get(0); +T OfflineDatabase::getPragma(const char* sql) { + mapbox::sqlite::Query query{ getStatement(sql) }; + query.run(); + return query.get(0); } // Remove least-recently used resources and tiles until the used database size, @@ -813,7 +793,7 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { // size, and because pages can get fragmented on the database. while (usedSize() + neededFreeSize + pageSize > maximumCacheSize) { // clang-format off - Statement accessedStmt = getStatement( + mapbox::sqlite::Query accessedQuery{ getStatement( "SELECT max(accessed) " "FROM ( " " SELECT accessed " @@ -829,16 +809,16 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { " WHERE tile_id IS NULL " " ORDER BY accessed ASC LIMIT ?1 " ") " - ); - accessedStmt->bind(1, 50); + ) }; + accessedQuery.bind(1, 50); // clang-format on - if (!accessedStmt->run()) { + if (!accessedQuery.run()) { return false; } - Timestamp accessed = accessedStmt->get(0); + Timestamp accessed = accessedQuery.get(0); // clang-format off - Statement stmt1 = getStatement( + mapbox::sqlite::Query resourceQuery{ getStatement( "DELETE FROM resources " "WHERE id IN ( " " SELECT id FROM resources " @@ -846,14 +826,14 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { " ON resource_id = resources.id " " WHERE resource_id IS NULL " " AND accessed <= ?1 " - ") "); + ") ") }; // clang-format on - stmt1->bind(1, accessed); - stmt1->run(); - uint64_t changes1 = stmt1->changes(); + resourceQuery.bind(1, accessed); + resourceQuery.run(); + const uint64_t resourceChanges = resourceQuery.changes(); // clang-format off - Statement stmt2 = getStatement( + mapbox::sqlite::Query tileQuery{ getStatement( "DELETE FROM tiles " "WHERE id IN ( " " SELECT id FROM tiles " @@ -861,16 +841,16 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { " ON tile_id = tiles.id " " WHERE tile_id IS NULL " " AND accessed <= ?1 " - ") "); + ") ") }; // clang-format on - stmt2->bind(1, accessed); - stmt2->run(); - uint64_t changes2 = stmt2->changes(); + tileQuery.bind(1, accessed); + tileQuery.run(); + const uint64_t tileChanges = tileQuery.changes(); // The cached value of offlineTileCount does not need to be updated // here because only non-offline tiles can be removed by eviction. - if (changes1 == 0 && changes2 == 0) { + if (resourceChanges == 0 && tileChanges == 0) { return false; } } @@ -901,16 +881,16 @@ uint64_t OfflineDatabase::getOfflineMapboxTileCount() { } // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "SELECT COUNT(DISTINCT id) " "FROM region_tiles, tiles " "WHERE tile_id = tiles.id " - "AND url_template LIKE 'mapbox://%' "); + "AND url_template LIKE 'mapbox://%' ") }; // clang-format on - stmt->run(); + query.run(); - offlineMapboxTileCount = stmt->get(0); + offlineMapboxTileCount = query.get(0); return *offlineMapboxTileCount; } diff --git a/platform/default/mbgl/storage/offline_database.hpp b/platform/default/mbgl/storage/offline_database.hpp index 91b544a9e0..9673ad8212 100644 --- a/platform/default/mbgl/storage/offline_database.hpp +++ b/platform/default/mbgl/storage/offline_database.hpp @@ -15,6 +15,7 @@ namespace mapbox { namespace sqlite { class Database; class Statement; +class Query; } // namespace sqlite } // namespace mapbox @@ -66,20 +67,7 @@ private: void migrateToVersion5(); void migrateToVersion6(); - class Statement { - public: - explicit Statement(mapbox::sqlite::Statement& stmt_) : stmt(stmt_) {} - Statement(Statement&&) = default; - Statement(const Statement&) = delete; - ~Statement(); - - mapbox::sqlite::Statement* operator->() { return &stmt; }; - - private: - mapbox::sqlite::Statement& stmt; - }; - - Statement getStatement(const char *); + mapbox::sqlite::Statement& getStatement(const char *); optional> getTile(const Resource::TileData&); optional hasTile(const Resource::TileData&); @@ -102,8 +90,8 @@ private: std::pair getCompletedTileCountAndSize(int64_t regionID); const std::string path; - std::unique_ptr<::mapbox::sqlite::Database> db; - std::unordered_map> statements; + std::unique_ptr db; + std::unordered_map> statements; template T getPragma(const char *); diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp index 2e08354fdf..8a567d602e 100644 --- a/platform/default/sqlite3.cpp +++ b/platform/default/sqlite3.cpp @@ -69,14 +69,84 @@ public: template using optional = std::experimental::optional; +static const char* codeToString(const int err) { + switch (err) { + case SQLITE_OK: return "SQLITE_OK"; + case SQLITE_ERROR: return "SQLITE_ERROR"; + case SQLITE_INTERNAL: return "SQLITE_INTERNAL"; + case SQLITE_PERM: return "SQLITE_PERM"; + case SQLITE_ABORT: return "SQLITE_ABORT"; + case SQLITE_BUSY: return "SQLITE_BUSY"; + case SQLITE_LOCKED: return "SQLITE_LOCKED"; + case SQLITE_NOMEM: return "SQLITE_NOMEM"; + case SQLITE_READONLY: return "SQLITE_READONLY"; + case SQLITE_INTERRUPT: return "SQLITE_INTERRUPT"; + case SQLITE_IOERR: return "SQLITE_IOERR"; + case SQLITE_CORRUPT: return "SQLITE_CORRUPT"; + case SQLITE_NOTFOUND: return "SQLITE_NOTFOUND"; + case SQLITE_FULL: return "SQLITE_FULL"; + case SQLITE_CANTOPEN: return "SQLITE_CANTOPEN"; + case SQLITE_PROTOCOL: return "SQLITE_PROTOCOL"; + case SQLITE_EMPTY: return "SQLITE_EMPTY"; + case SQLITE_SCHEMA: return "SQLITE_SCHEMA"; + case SQLITE_TOOBIG: return "SQLITE_TOOBIG"; + case SQLITE_CONSTRAINT: return "SQLITE_CONSTRAINT"; + case SQLITE_MISMATCH: return "SQLITE_MISMATCH"; + case SQLITE_MISUSE: return "SQLITE_MISUSE"; + case SQLITE_NOLFS: return "SQLITE_NOLFS"; + case SQLITE_AUTH: return "SQLITE_AUTH"; + case SQLITE_FORMAT: return "SQLITE_FORMAT"; + case SQLITE_RANGE: return "SQLITE_RANGE"; + case SQLITE_NOTADB: return "SQLITE_NOTADB"; + case SQLITE_NOTICE: return "SQLITE_NOTICE"; + case SQLITE_WARNING: return "SQLITE_WARNING"; + case SQLITE_ROW: return "SQLITE_ROW"; + case SQLITE_DONE: return "SQLITE_DONE"; + default: return ""; + } +} + static void errorLogCallback(void *, const int err, const char *msg) { - if (err == SQLITE_ERROR) { - mbgl::Log::Error(mbgl::Event::Database, "%s (Code %i)", msg, err); - } else if (err == SQLITE_WARNING) { - mbgl::Log::Warning(mbgl::Event::Database, "%s (Code %i)", msg, err); - } else { - mbgl::Log::Info(mbgl::Event::Database, "%s (Code %i)", msg, err); + auto severity = mbgl::EventSeverity::Info; + + switch (err) { + case SQLITE_ERROR: // Generic error + case SQLITE_INTERNAL: // Internal logic error in SQLite + case SQLITE_PERM: // Access permission denied + case SQLITE_ABORT: // Callback routine requested an abort + case SQLITE_BUSY: // The database file is locked + case SQLITE_LOCKED: // A table in the database is locked + case SQLITE_NOMEM: // A malloc() failed + case SQLITE_READONLY: // Attempt to write a readonly database + case SQLITE_INTERRUPT: // Operation terminated by sqlite3_interrupt( + case SQLITE_IOERR: // Some kind of disk I/O error occurred + case SQLITE_CORRUPT: // The database disk image is malformed + case SQLITE_NOTFOUND: // Unknown opcode in sqlite3_file_control() + case SQLITE_FULL: // Insertion failed because database is full + case SQLITE_CANTOPEN: // Unable to open the database file + case SQLITE_PROTOCOL: // Database lock protocol error + case SQLITE_EMPTY: // Internal use only + case SQLITE_SCHEMA: // The database schema changed + case SQLITE_TOOBIG: // String or BLOB exceeds size limit + case SQLITE_CONSTRAINT: // Abort due to constraint violation + case SQLITE_MISMATCH: // Data type mismatch + case SQLITE_MISUSE: // Library used incorrectly + case SQLITE_NOLFS: // Uses OS features not supported on host + case SQLITE_AUTH: // Authorization denied + case SQLITE_FORMAT: // Not used + case SQLITE_RANGE: // 2nd parameter to sqlite3_bind out of range + case SQLITE_NOTADB: // File opened that is not a database file + severity = mbgl::EventSeverity::Error; + break; + case SQLITE_WARNING: // Warnings from sqlite3_log() + severity = mbgl::EventSeverity::Warning; + break; + case SQLITE_NOTICE: // Notifications from sqlite3_log() + default: + break; } + + mbgl::Log::Record(severity, mbgl::Event::Database, "%s (%s)", msg, codeToString(err)); } const static bool sqliteVersionCheck __attribute__((unused)) = []() { @@ -131,85 +201,93 @@ void Database::exec(const std::string &sql) { } } -Statement Database::prepare(const char *query) { - assert(impl); - return Statement(this, query); +Statement::Statement(Database& db, const char* sql) + : impl(std::make_unique(db.impl->db, sql)) { } -Statement::Statement(Database *db, const char *sql) - : impl(std::make_unique(db->impl->db, sql)) -{ +Statement::~Statement() { +#ifndef NDEBUG + // Crash if we're destructing this object while we know a Query object references this. + assert(!used); +#endif } -Statement::Statement(Statement &&other) { - *this = std::move(other); -} +Query::Query(Statement& stmt_) : stmt(stmt_) { + assert(stmt.impl); -Statement &Statement::operator=(Statement &&other) { - std::swap(impl, other.impl); - return *this; +#ifndef NDEBUG + assert(!stmt.used); + stmt.used = true; +#endif } -Statement::~Statement() = default; +Query::~Query() { + reset(); + clearBindings(); -template <> void Statement::bind(int offset, std::nullptr_t) { - assert(impl); - impl->check(sqlite3_bind_null(impl->stmt, offset)); +#ifndef NDEBUG + stmt.used = false; +#endif } -template <> void Statement::bind(int offset, int8_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, std::nullptr_t) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_null(stmt.impl->stmt, offset)); } -template <> void Statement::bind(int offset, int16_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, int8_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, int32_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, int16_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, int64_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, int32_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, uint8_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, int64_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, uint16_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, uint8_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, uint32_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, uint16_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, float value) { - assert(impl); - impl->check(sqlite3_bind_double(impl->stmt, offset, value)); +template <> void Query::bind(int offset, uint32_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, double value) { - assert(impl); - impl->check(sqlite3_bind_double(impl->stmt, offset, value)); +template <> void Query::bind(int offset, float value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_double(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, bool value) { - assert(impl); - impl->check(sqlite3_bind_int(impl->stmt, offset, value)); +template <> void Query::bind(int offset, double value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_double(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, const char *value) { - assert(impl); - impl->check(sqlite3_bind_text(impl->stmt, offset, value, -1, SQLITE_STATIC)); +template <> void Query::bind(int offset, bool value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int(stmt.impl->stmt, offset, value)); +} + +template <> void Query::bind(int offset, const char *value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_text(stmt.impl->stmt, offset, value, -1, SQLITE_STATIC)); } // We currently cannot use sqlite3_bind_blob64 / sqlite3_bind_text64 because they @@ -219,40 +297,40 @@ template <> void Statement::bind(int offset, const char *value) { // According to http://stackoverflow.com/questions/14288128/what-version-of-sqlite-does-ios-provide, // the first iOS version with 3.8.7+ was 9.0, with 3.8.8. -void Statement::bind(int offset, const char * value, std::size_t length, bool retain) { - assert(impl); +void Query::bind(int offset, const char * value, std::size_t length, bool retain) { + assert(stmt.impl); if (length > std::numeric_limits::max()) { throw std::range_error("value too long for sqlite3_bind_text"); } - impl->check(sqlite3_bind_text(impl->stmt, offset, value, int(length), + stmt.impl->check(sqlite3_bind_text(stmt.impl->stmt, offset, value, int(length), retain ? SQLITE_TRANSIENT : SQLITE_STATIC)); } -void Statement::bind(int offset, const std::string& value, bool retain) { +void Query::bind(int offset, const std::string& value, bool retain) { bind(offset, value.data(), value.size(), retain); } -void Statement::bindBlob(int offset, const void * value, std::size_t length, bool retain) { - assert(impl); +void Query::bindBlob(int offset, const void * value, std::size_t length, bool retain) { + assert(stmt.impl); if (length > std::numeric_limits::max()) { throw std::range_error("value too long for sqlite3_bind_text"); } - impl->check(sqlite3_bind_blob(impl->stmt, offset, value, int(length), + stmt.impl->check(sqlite3_bind_blob(stmt.impl->stmt, offset, value, int(length), retain ? SQLITE_TRANSIENT : SQLITE_STATIC)); } -void Statement::bindBlob(int offset, const std::vector& value, bool retain) { +void Query::bindBlob(int offset, const std::vector& value, bool retain) { bindBlob(offset, value.data(), value.size(), retain); } template <> -void Statement::bind( +void Query::bind( int offset, std::chrono::time_point value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, std::chrono::system_clock::to_time_t(value))); + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, std::chrono::system_clock::to_time_t(value))); } -template <> void Statement::bind(int offset, optional value) { +template <> void Query::bind(int offset, optional value) { if (!value) { bind(offset, nullptr); } else { @@ -261,7 +339,7 @@ template <> void Statement::bind(int offset, optional value) { } template <> -void Statement::bind( +void Query::bind( int offset, optional> value) { if (!value) { @@ -271,86 +349,86 @@ void Statement::bind( } } -bool Statement::run() { - assert(impl); - const int err = sqlite3_step(impl->stmt); - impl->lastInsertRowId = sqlite3_last_insert_rowid(sqlite3_db_handle(impl->stmt)); - impl->changes = sqlite3_changes(sqlite3_db_handle(impl->stmt)); +bool Query::run() { + assert(stmt.impl); + const int err = sqlite3_step(stmt.impl->stmt); + stmt.impl->lastInsertRowId = sqlite3_last_insert_rowid(sqlite3_db_handle(stmt.impl->stmt)); + stmt.impl->changes = sqlite3_changes(sqlite3_db_handle(stmt.impl->stmt)); if (err == SQLITE_DONE) { return false; } else if (err == SQLITE_ROW) { return true; } else if (err != SQLITE_OK) { - throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(impl->stmt)) }; + throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt.impl->stmt)) }; } else { return false; } } -template <> bool Statement::get(int offset) { - assert(impl); - return sqlite3_column_int(impl->stmt, offset); +template <> bool Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_int(stmt.impl->stmt, offset); } -template <> int Statement::get(int offset) { - assert(impl); - return sqlite3_column_int(impl->stmt, offset); +template <> int Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_int(stmt.impl->stmt, offset); } -template <> int64_t Statement::get(int offset) { - assert(impl); - return sqlite3_column_int64(impl->stmt, offset); +template <> int64_t Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_int64(stmt.impl->stmt, offset); } -template <> double Statement::get(int offset) { - assert(impl); - return sqlite3_column_double(impl->stmt, offset); +template <> double Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_double(stmt.impl->stmt, offset); } -template <> std::string Statement::get(int offset) { - assert(impl); +template <> std::string Query::get(int offset) { + assert(stmt.impl); return { - reinterpret_cast(sqlite3_column_blob(impl->stmt, offset)), - size_t(sqlite3_column_bytes(impl->stmt, offset)) + reinterpret_cast(sqlite3_column_blob(stmt.impl->stmt, offset)), + size_t(sqlite3_column_bytes(stmt.impl->stmt, offset)) }; } -template <> std::vector Statement::get(int offset) { - assert(impl); - const auto* begin = reinterpret_cast(sqlite3_column_blob(impl->stmt, offset)); - const uint8_t* end = begin + sqlite3_column_bytes(impl->stmt, offset); +template <> std::vector Query::get(int offset) { + assert(stmt.impl); + const auto* begin = reinterpret_cast(sqlite3_column_blob(stmt.impl->stmt, offset)); + const uint8_t* end = begin + sqlite3_column_bytes(stmt.impl->stmt, offset); return { begin, end }; } template <> std::chrono::time_point -Statement::get(int offset) { - assert(impl); +Query::get(int offset) { + assert(stmt.impl); return std::chrono::time_point_cast( - std::chrono::system_clock::from_time_t(sqlite3_column_int64(impl->stmt, offset))); + std::chrono::system_clock::from_time_t(sqlite3_column_int64(stmt.impl->stmt, offset))); } -template <> optional Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +template <> optional Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return optional(); } else { return get(offset); } } -template <> optional Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +template <> optional Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return optional(); } else { return get(offset); } } -template <> optional Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +template <> optional Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return optional(); } else { return get(offset); @@ -359,9 +437,9 @@ template <> optional Statement::get(int offset) { template <> optional> -Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return {}; } else { return get>( @@ -369,24 +447,24 @@ Statement::get(int offset) { } } -void Statement::reset() { - assert(impl); - sqlite3_reset(impl->stmt); +void Query::reset() { + assert(stmt.impl); + sqlite3_reset(stmt.impl->stmt); } -void Statement::clearBindings() { - assert(impl); - sqlite3_clear_bindings(impl->stmt); +void Query::clearBindings() { + assert(stmt.impl); + sqlite3_clear_bindings(stmt.impl->stmt); } -int64_t Statement::lastInsertRowId() const { - assert(impl); - return impl->lastInsertRowId; +int64_t Query::lastInsertRowId() const { + assert(stmt.impl); + return stmt.impl->lastInsertRowId; } -uint64_t Statement::changes() const { - assert(impl); - auto changes_ = impl->changes; +uint64_t Query::changes() const { + assert(stmt.impl); + auto changes_ = stmt.impl->changes; return (changes_ < 0 ? 0 : changes_); } diff --git a/platform/default/sqlite3.hpp b/platform/default/sqlite3.hpp index 82e3ceff6d..20d09b550c 100644 --- a/platform/default/sqlite3.hpp +++ b/platform/default/sqlite3.hpp @@ -19,21 +19,55 @@ enum OpenFlag : int { PrivateCache = 0x00040000, }; -struct Exception : std::runtime_error { - enum Code : int { - OK = 0, - CANTOPEN = 14, - NOTADB = 26 - }; +enum class ResultCode : int { + OK = 0, + Error = 1, + Internal = 2, + Perm = 3, + Abort = 4, + Busy = 5, + Locked = 6, + NoMem = 7, + ReadOnly = 8, + Interrupt = 9, + IOErr = 10, + Corrupt = 11, + NotFound = 12, + Full = 13, + CantOpen = 14, + Protocol = 15, + Schema = 17, + TooBig = 18, + Constraint = 19, + Mismatch = 20, + Misuse = 21, + NoLFS = 22, + Auth = 23, + Range = 25, + NotADB = 26 +}; - Exception(int err, const char *msg) : std::runtime_error(msg), code(err) {} - Exception(int err, const std::string& msg) : std::runtime_error(msg), code(err) {} - const int code = OK; +class Exception : public std::runtime_error { +public: + Exception(int err, const char* msg) + : std::runtime_error(msg), code(static_cast(err)) { + } + Exception(ResultCode err, const char* msg) + : std::runtime_error(msg), code(err) { + } + Exception(int err, const std::string& msg) + : std::runtime_error(msg), code(static_cast(err)) { + } + Exception(ResultCode err, const std::string& msg) + : std::runtime_error(msg), code(err) { + } + const ResultCode code = ResultCode::OK; }; class DatabaseImpl; class Statement; class StatementImpl; +class Query; class Database { private: @@ -48,7 +82,6 @@ public: void setBusyTimeout(std::chrono::milliseconds); void exec(const std::string &sql); - Statement prepare(const char *query); private: std::unique_ptr impl; @@ -56,28 +89,54 @@ private: friend class Statement; }; +// A Statement object represents a prepared statement that can be run repeatedly run with a Query object. class Statement { +public: + Statement(Database& db, const char* sql); + Statement(const Statement&) = delete; + Statement(Statement&&) = delete; + Statement& operator=(const Statement&) = delete; + Statement& operator=(Statement&&) = delete; + ~Statement(); + + friend class Query; + private: - Statement(const Statement &) = delete; - Statement &operator=(const Statement &) = delete; + std::unique_ptr impl; + +#ifndef NDEBUG + // This flag stores whether there exists a Query object that uses this prepared statement. + // There may only be one Query object at a time. Statement objects must outlive Query objects. + // While a Query object exists, a Statement object may not be moved or deleted. + bool used = false; +#endif +}; +// A Query object is used to run a database query with a prepared statement (stored in a Statement +// object). There may only exist one Query object per Statement object. Query objects are designed +// to be constructed and destroyed frequently. +class Query { public: - Statement(Database *db, const char *sql); - Statement(Statement &&); - ~Statement(); - Statement &operator=(Statement &&); + Query(Statement&); + Query(const Query&) = delete; + Query(Query&&) = delete; + Query& operator=(const Query&) = delete; + Query& operator=(Query&&) = delete; + ~Query(); - template void bind(int offset, T value); + template + void bind(int offset, T value); // Text - void bind(int offset, const char *, std::size_t length, bool retain = true); + void bind(int offset, const char*, std::size_t length, bool retain = true); void bind(int offset, const std::string&, bool retain = true); // Blob - void bindBlob(int offset, const void *, std::size_t length, bool retain = true); + void bindBlob(int offset, const void*, std::size_t length, bool retain = true); void bindBlob(int offset, const std::vector&, bool retain = true); - template T get(int offset); + template + T get(int offset); bool run(); void reset(); @@ -87,7 +146,7 @@ public: uint64_t changes() const; private: - std::unique_ptr impl; + Statement& stmt; }; class Transaction { diff --git a/platform/qt/src/qmapbox.cpp b/platform/qt/src/qmapbox.cpp index ec76ebfe53..1386a4b7aa 100644 --- a/platform/qt/src/qmapbox.cpp +++ b/platform/qt/src/qmapbox.cpp @@ -24,7 +24,7 @@ namespace QMapbox { /*! \namespace QMapbox - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Contains miscellaneous Mapbox bindings used throughout QMapboxGL. */ @@ -74,7 +74,7 @@ namespace QMapbox { /*! \class QMapbox::Feature - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents \l {https://www.mapbox.com/help/define-features/}{map features} via its \a type (PointType, LineStringType or PolygonType), \a geometry, \a @@ -94,7 +94,7 @@ namespace QMapbox { /*! \class QMapbox::ShapeAnnotationGeometry - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents a shape annotation geometry. */ @@ -113,7 +113,7 @@ namespace QMapbox { /*! \class QMapbox::SymbolAnnotation - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt A symbol annotation comprises of its geometry and an icon identifier. */ @@ -121,7 +121,7 @@ namespace QMapbox { /*! \class QMapbox::LineAnnotation - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents a line annotation object, along with its properties. @@ -131,7 +131,7 @@ namespace QMapbox { /*! \class QMapbox::FillAnnotation - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents a fill annotation object, along with its properties. @@ -195,7 +195,7 @@ namespace QMapbox { /*! \class QMapbox::CustomLayerRenderParameters - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt QMapbox::CustomLayerRenderParameters provides the data passed on each render pass for a custom layer. diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 414b65255c..982f4b3b35 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -136,7 +136,7 @@ std::unique_ptr toStyleImage(const QString &id, const QImage \class QMapboxGLSettings \brief The QMapboxGLSettings class stores the initial configuration for QMapboxGL. - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt QMapboxGLSettings is used to configure QMapboxGL at the moment of its creation. Once created, the QMapboxGLSettings of a QMapboxGL can no longer be changed. @@ -454,7 +454,7 @@ void QMapboxGLSettings::setResourceTransform(const std::function(QString(sql), QSqlDatabase::database(db->impl->connectionName))) { +Statement::Statement(Database& db, const char* sql) + : impl(std::make_unique(QString(sql), + QSqlDatabase::database(db.impl->connectionName))) { assert(impl); } -Statement::Statement(Statement &&other) - : impl(std::move(other.impl)) { - assert(impl); +Statement::~Statement() { +#ifndef NDEBUG + // Crash if we're destructing this object while we know a Query object references this. + assert(!used); +#endif } -Statement &Statement::operator=(Statement &&other) { - assert(impl); - std::swap(impl, other.impl); - return *this; +Query::Query(Statement& stmt_) : stmt(stmt_) { + assert(stmt.impl); + +#ifndef NDEBUG + assert(!stmt.used); + stmt.used = true; +#endif } -Statement::~Statement() { +Query::~Query() { + reset(); + clearBindings(); + +#ifndef NDEBUG + stmt.used = false; +#endif } -template void Statement::bind(int, int64_t); +template void Query::bind(int, int64_t); template -void Statement::bind(int offset, T value) { - assert(impl); +void Query::bind(int offset, T value) { + assert(stmt.impl); // Field numbering starts at 0. - impl->query.bindValue(offset - 1, QVariant::fromValue(value), QSql::In); - checkQueryError(impl->query); + stmt.impl->query.bindValue(offset - 1, QVariant::fromValue(value), QSql::In); + checkQueryError(stmt.impl->query); } template <> -void Statement::bind(int offset, std::nullptr_t) { - assert(impl); +void Query::bind(int offset, std::nullptr_t) { + assert(stmt.impl); // Field numbering starts at 0. - impl->query.bindValue(offset - 1, QVariant(QVariant::Invalid), QSql::In); - checkQueryError(impl->query); + stmt.impl->query.bindValue(offset - 1, QVariant(QVariant::Invalid), QSql::In); + checkQueryError(stmt.impl->query); } template <> -void Statement::bind(int offset, int32_t value) { +void Query::bind(int offset, int32_t value) { bind(offset, static_cast(value)); } template <> -void Statement::bind(int offset, bool value) { +void Query::bind(int offset, bool value) { bind(offset, static_cast(value)); } template <> -void Statement::bind(int offset, int8_t value) { +void Query::bind(int offset, int8_t value) { bind(offset, static_cast(value)); } template <> -void Statement::bind(int offset, uint8_t value) { +void Query::bind(int offset, uint8_t value) { bind(offset, static_cast(value)); } template <> -void Statement::bind(int offset, mbgl::Timestamp value) { +void Query::bind(int offset, mbgl::Timestamp value) { bind(offset, std::chrono::system_clock::to_time_t(value)); } template <> -void Statement::bind(int offset, optional value) { +void Query::bind(int offset, optional value) { if (value) { bind(offset, *value); } else { @@ -262,7 +270,7 @@ void Statement::bind(int offset, optional value) { } template <> -void Statement::bind(int offset, optional value) { +void Query::bind(int offset, optional value) { if (value) { bind(offset, *value); } else { @@ -270,30 +278,25 @@ void Statement::bind(int offset, optional value) { } } -void Statement::bind(int offset, const char* value, std::size_t length, bool retain) { - assert(impl); +void Query::bind(int offset, const char* value, std::size_t length, bool /* retain */) { + assert(stmt.impl); if (length > std::numeric_limits::max()) { // Kept for consistence with the default implementation. throw std::range_error("value too long"); } - // Qt SQLite driver treats QByteArray as blob: we need to explicitly - // declare the variant type as string. - QVariant text(QVariant::Type::String); - text.setValue(retain ? QByteArray(value, length) : QByteArray::fromRawData(value, length)); - // Field numbering starts at 0. - impl->query.bindValue(offset - 1, std::move(text), QSql::In); + stmt.impl->query.bindValue(offset - 1, QString(QByteArray(value, length)), QSql::In); - checkQueryError(impl->query); + checkQueryError(stmt.impl->query); } -void Statement::bind(int offset, const std::string& value, bool retain) { +void Query::bind(int offset, const std::string& value, bool retain) { bind(offset, value.data(), value.size(), retain); } -void Statement::bindBlob(int offset, const void* value_, std::size_t length, bool retain) { - assert(impl); +void Query::bindBlob(int offset, const void* value_, std::size_t length, bool retain) { + assert(stmt.impl); const char* value = reinterpret_cast(value_); if (length > std::numeric_limits::max()) { // Kept for consistence with the default implementation. @@ -301,123 +304,123 @@ void Statement::bindBlob(int offset, const void* value_, std::size_t length, boo } // Field numbering starts at 0. - impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) : + stmt.impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) : QByteArray::fromRawData(value, length), QSql::In | QSql::Binary); - checkQueryError(impl->query); + checkQueryError(stmt.impl->query); } -void Statement::bindBlob(int offset, const std::vector& value, bool retain) { +void Query::bindBlob(int offset, const std::vector& value, bool retain) { bindBlob(offset, value.data(), value.size(), retain); } -bool Statement::run() { - assert(impl); +bool Query::run() { + assert(stmt.impl); - if (!impl->query.isValid()) { - if (impl->query.exec()) { - impl->lastInsertRowId = impl->query.lastInsertId().value(); - impl->changes = impl->query.numRowsAffected(); + if (!stmt.impl->query.isValid()) { + if (stmt.impl->query.exec()) { + stmt.impl->lastInsertRowId = stmt.impl->query.lastInsertId().value(); + stmt.impl->changes = stmt.impl->query.numRowsAffected(); } else { - checkQueryError(impl->query); + checkQueryError(stmt.impl->query); } } - const bool hasNext = impl->query.next(); - if (!hasNext) impl->query.finish(); + const bool hasNext = stmt.impl->query.next(); + if (!hasNext) stmt.impl->query.finish(); return hasNext; } -template bool Statement::get(int); -template int Statement::get(int); -template int64_t Statement::get(int); -template double Statement::get(int); +template bool Query::get(int); +template int Query::get(int); +template int64_t Query::get(int); +template double Query::get(int); -template T Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template T Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); return value.value(); } -template <> std::vector Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QByteArray byteArray = impl->query.value(offset).toByteArray(); - checkQueryError(impl->query); +template <> std::vector Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QByteArray byteArray = stmt.impl->query.value(offset).toByteArray(); + checkQueryError(stmt.impl->query); std::vector blob(byteArray.begin(), byteArray.end()); return blob; } -template <> mbgl::Timestamp Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> mbgl::Timestamp Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); return std::chrono::time_point_cast( std::chrono::system_clock::from_time_t(value.value<::time_t>())); } -template <> optional Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> optional Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { value.value() }; } -template <> optional Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> optional Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { value.value() }; } -template <> std::string Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QByteArray value = impl->query.value(offset).toByteArray(); - checkQueryError(impl->query); +template <> std::string Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QByteArray value = stmt.impl->query.value(offset).toByteArray(); + checkQueryError(stmt.impl->query); return std::string(value.constData(), value.size()); } -template <> optional Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QByteArray value = impl->query.value(offset).toByteArray(); - checkQueryError(impl->query); +template <> optional Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QByteArray value = stmt.impl->query.value(offset).toByteArray(); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { std::string(value.constData(), value.size()) }; } -template <> optional Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> optional Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { std::chrono::time_point_cast( std::chrono::system_clock::from_time_t(value.value<::time_t>())) }; } -void Statement::reset() { - assert(impl); - impl->query.finish(); +void Query::reset() { + assert(stmt.impl); + stmt.impl->query.finish(); } -void Statement::clearBindings() { +void Query::clearBindings() { // no-op } -int64_t Statement::lastInsertRowId() const { - assert(impl); - return impl->lastInsertRowId; +int64_t Query::lastInsertRowId() const { + assert(stmt.impl); + return stmt.impl->lastInsertRowId; } -uint64_t Statement::changes() const { - assert(impl); - return (impl->changes < 0 ? 0 : impl->changes); +uint64_t Query::changes() const { + assert(stmt.impl); + return (stmt.impl->changes < 0 ? 0 : stmt.impl->changes); } Transaction::Transaction(Database& db_, Mode mode) diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.cpp b/src/mbgl/renderer/layers/render_heatmap_layer.cpp index 0f9e3239ef..4f2e899220 100644 --- a/src/mbgl/renderer/layers/render_heatmap_layer.cpp +++ b/src/mbgl/renderer/layers/render_heatmap_layer.cpp @@ -67,7 +67,7 @@ void RenderHeatmapLayer::render(PaintParameters& parameters, RenderSource*) { } } - if (!renderTexture) { + if (!parameters.context.supportsHalfFloatTextures || !renderTexture) { renderTexture = OffscreenTexture(parameters.context, size, gl::TextureType::UnsignedByte); renderTexture->bind(); } diff --git a/src/mbgl/util/tiny_sdf.cpp b/src/mbgl/util/tiny_sdf.cpp index 60839357d5..6edcd83bc2 100644 --- a/src/mbgl/util/tiny_sdf.cpp +++ b/src/mbgl/util/tiny_sdf.cpp @@ -95,7 +95,7 @@ AlphaImage transformRasterToSDF(const AlphaImage& rasterInput, double radius, do for (uint32_t i = 0; i < size; i++) { double distance = gridOuter[i] - gridInner[i]; - sdf.data[i] = std::max(0l, std::min(255l, std::lround(255.0 - 255.0 * (distance / radius + cutoff)))); + sdf.data[i] = std::max(0l, std::min(255l, ::lround(255.0 - 255.0 * (distance / radius + cutoff)))); } return sdf; -- cgit v1.2.1