summaryrefslogtreecommitdiff
path: root/platform/default/include/mbgl/storage/offline_database.hpp
blob: 7eb546b3f620c581dd289dd04c3c77c82a26106f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#pragma once

#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/offline.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/mapbox.hpp>
#include <mbgl/util/expected.hpp>

#include <list>
#include <map>
#include <memory>
#include <string>

namespace mapbox {
namespace sqlite {
class Database;
class Statement;
class Query;
class Exception;
} // namespace sqlite
} // namespace mapbox

namespace mbgl {

class Response;
class TileID;

namespace util {
struct IOException;
} // namespace util

struct MapboxTileLimitExceededException :  util::Exception {
    MapboxTileLimitExceededException() : util::Exception("Mapbox tile limit exceeded") {}
};

class OfflineDatabase {
public:
    OfflineDatabase(std::string path);
    ~OfflineDatabase();

    void changePath(const std::string&);
    std::exception_ptr resetDatabase();

    optional<Response> get(const Resource&);

    // Return value is (inserted, stored size)
    std::pair<bool, uint64_t> put(const Resource&, const Response&);

    // Force Mapbox GL Native to revalidate tiles stored in the ambient
    // cache with the tile server before using them, making sure they
    // are the latest version. This is more efficient than cleaning the
    // cache because if the tile is considered valid after the server
    // lookup, it will not get downloaded again.
    std::exception_ptr invalidateAmbientCache();

    // Clear the tile cache, freeing resources. This operation can be
    // potentially slow because it will trigger a VACUUM on SQLite,
    // forcing the database to move pages on the filesystem.
    std::exception_ptr clearAmbientCache();

    expected<OfflineRegions, std::exception_ptr> listRegions();

    expected<OfflineRegion, std::exception_ptr> createRegion(const OfflineRegionDefinition&,
                                                             const OfflineRegionMetadata&);

    expected<OfflineRegions, std::exception_ptr>
    mergeDatabase(const std::string& sideDatabasePath);

    expected<OfflineRegionMetadata, std::exception_ptr>
    updateMetadata(const int64_t regionID, const OfflineRegionMetadata&);

    std::exception_ptr deleteRegion(OfflineRegion&&);
    std::exception_ptr invalidateRegion(int64_t regionID);

    // Return value is (response, stored size)
    optional<std::pair<Response, uint64_t>> getRegionResource(const Resource&);
    optional<int64_t> hasRegionResource(const Resource&);
    uint64_t putRegionResource(int64_t regionID, const Resource&, const Response&);
    void putRegionResources(int64_t regionID, const std::list<std::tuple<Resource, Response>>&, OfflineRegionStatus&);

    expected<OfflineRegionDefinition, std::exception_ptr> getRegionDefinition(int64_t regionID);
    expected<OfflineRegionStatus, std::exception_ptr> getRegionCompletedStatus(int64_t regionID);

    std::exception_ptr setMaximumAmbientCacheSize(uint64_t);
    void setOfflineMapboxTileCountLimit(uint64_t);
    uint64_t getOfflineMapboxTileCountLimit();
    bool offlineMapboxTileCountLimitExceeded();
    uint64_t getOfflineMapboxTileCount();
    bool exceedsOfflineMapboxTileCountLimit(const Resource&);
    void markUsedResources(int64_t regionID, const std::list<Resource>&);
    std::exception_ptr pack();
    void runPackDatabaseAutomatically(bool autopack_) { autopack = autopack_; }

    void reopenDatabaseReadOnly(bool readOnly);

private:
    class DatabaseSizeChangeStats;

    void initialize();
    void handleError(const mapbox::sqlite::Exception&, const char* action);
    void handleError(const util::IOException&, const char* action);
    void handleError(const std::runtime_error& ex, const char* action);
    void handleError(const char* action);

    void removeExisting();
    void removeOldCacheTable();
    void createSchema();
    void migrateToVersion5();
    void migrateToVersion3();
    void migrateToVersion6();
    void cleanup();
    bool disabled();
    void vacuum();
    void checkFlags();

    mapbox::sqlite::Statement& getStatement(const char *);

    optional<std::pair<Response, uint64_t>> getTile(const Resource::TileData&);
    optional<int64_t> hasTile(const Resource::TileData&);
    bool putTile(const Resource::TileData&, const Response&,
                 const std::string&, bool compressed);

    optional<std::pair<Response, uint64_t>> getResource(const Resource&);
    optional<int64_t> hasResource(const Resource&);
    bool putResource(const Resource&, const Response&,
                     const std::string&, bool compressed);

    uint64_t putRegionResourceInternal(int64_t regionID, const Resource&, const Response&);

    optional<std::pair<Response, uint64_t>> getInternal(const Resource&);
    optional<int64_t> hasInternal(const Resource&);
    std::pair<bool, uint64_t> putInternal(const Resource&, const Response&, bool evict);

    // Return value is true iff the resource was previously unused by any other regions.
    bool markUsed(int64_t regionID, const Resource&);

    std::pair<int64_t, int64_t> getCompletedResourceCountAndSize(int64_t regionID);
    std::pair<int64_t, int64_t> getCompletedTileCountAndSize(int64_t regionID);

    std::string path;
    std::unique_ptr<mapbox::sqlite::Database> db;
    std::map<const char*, const std::unique_ptr<mapbox::sqlite::Statement>> statements;

    template <class T>
    T getPragma(const char *);

    uint64_t maximumAmbientCacheSize = util::DEFAULT_MAX_CACHE_SIZE;
    uint64_t offlineMapboxTileCountLimit = util::mapbox::DEFAULT_OFFLINE_TILE_COUNT_LIMIT;

    optional<uint64_t> offlineMapboxTileCount;

    bool evict(uint64_t neededFreeSize, DatabaseSizeChangeStats& stats);

    class DatabaseSizeChangeStats {
    public:
        explicit DatabaseSizeChangeStats(OfflineDatabase*);

        // Returns difference between current database size and
        // database size at the time of creation of this object.
        int64_t diff() const;

        // Returns how many bytes were released comparing to a database
        // size at the time of creation of this object.
        uint64_t bytesReleased() const;

        // Returns page size for the database.
        uint64_t pageSize() const;

    private:
        uint64_t pageSize_ = 0u;
        uint64_t pageCount_ = 0u;
        uint64_t initialSize_ = 0u;
        OfflineDatabase* db = nullptr;
    };

    friend class DatabaseSizeChangeStats;

    // Lazily initializes currentAmbientCacheSize.
    std::exception_ptr initAmbientCacheSize();
    optional<uint64_t> currentAmbientCacheSize;
    void updateAmbientCacheSize(DatabaseSizeChangeStats&);

    bool autopack = true;
    bool readOnly = false;
};

} // namespace mbgl