Coverage Report

Created: 2025-06-20 06:58

/src/PROJ/src/sqlite3_utils.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 * Project:  PROJ
3
 * Purpose:  SQLite3 related utilities
4
 * Author:   Even Rouault, <even.rouault at spatialys.com>
5
 *
6
 ******************************************************************************
7
 * Copyright (c) 2019, Even Rouault, <even.rouault at spatialys.com>
8
 *
9
 * Permission is hereby granted, free of charge, to any person obtaining a
10
 * copy of this software and associated documentation files (the "Software"),
11
 * to deal in the Software without restriction, including without limitation
12
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13
 * and/or sell copies of the Software, and to permit persons to whom the
14
 * Software is furnished to do so, subject to the following conditions:
15
 *
16
 * The above copyright notice and this permission notice shall be included
17
 * in all copies or substantial portions of the Software.
18
 *
19
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25
 * DEALINGS IN THE SOFTWARE.
26
 *****************************************************************************/
27
28
#ifdef __GNUC__
29
#pragma GCC diagnostic push
30
#pragma GCC diagnostic ignored "-Weffc++"
31
#endif
32
33
#include "sqlite3_utils.hpp"
34
35
#ifdef __GNUC__
36
#pragma GCC diagnostic pop
37
#endif
38
39
#ifdef EMBED_RESOURCE_FILES
40
#include "memvfs.h"
41
#endif
42
43
#include <cstdlib>
44
#include <cstring>
45
#include <sstream> // std::ostringstream
46
47
NS_PROJ_START
48
49
// ---------------------------------------------------------------------------
50
51
14.0k
pj_sqlite3_vfs::~pj_sqlite3_vfs() = default;
52
53
// ---------------------------------------------------------------------------
54
55
14.0k
SQLite3VFS::SQLite3VFS(pj_sqlite3_vfs *vfs) : vfs_(vfs) {}
56
57
// ---------------------------------------------------------------------------
58
59
14.0k
SQLite3VFS::~SQLite3VFS() {
60
14.0k
    if (vfs_) {
61
14.0k
        sqlite3_vfs_unregister(&(vfs_->base));
62
14.0k
        delete vfs_;
63
14.0k
    }
64
14.0k
}
65
66
// ---------------------------------------------------------------------------
67
68
14.0k
const char *SQLite3VFS::name() const { return vfs_->namePtr.c_str(); }
69
70
// ---------------------------------------------------------------------------
71
72
typedef int (*ClosePtr)(sqlite3_file *);
73
74
// ---------------------------------------------------------------------------
75
76
0
static int VFSClose(sqlite3_file *file) {
77
0
    sqlite3_vfs *defaultVFS = sqlite3_vfs_find(nullptr);
78
0
    assert(defaultVFS);
79
0
    ClosePtr defaultClosePtr;
80
0
    std::memcpy(&defaultClosePtr,
81
0
                reinterpret_cast<char *>(file) + defaultVFS->szOsFile,
82
0
                sizeof(ClosePtr));
83
0
    void *methods = const_cast<sqlite3_io_methods *>(file->pMethods);
84
0
    int ret = defaultClosePtr(file);
85
0
    std::free(methods);
86
0
    return ret;
87
0
}
88
89
// ---------------------------------------------------------------------------
90
91
0
static int VSFNoOpLockUnlockSync(sqlite3_file *, int) { return SQLITE_OK; }
92
93
// ---------------------------------------------------------------------------
94
95
namespace {
96
97
struct pj_sqlite3_customvfs_appdata {
98
    sqlite3_vfs *defaultVFS = nullptr;
99
    bool fakeSync = false;
100
    bool fakeLock = false;
101
};
102
103
struct pj_sqlite3_customvfs : public pj_sqlite3_vfs {
104
0
    ~pj_sqlite3_customvfs() override {
105
0
        delete static_cast<pj_sqlite3_customvfs_appdata *>(base.pAppData);
106
0
        base.pAppData = nullptr;
107
0
    }
108
};
109
} // namespace
110
111
static int VFSCustomOpen(sqlite3_vfs *vfs, const char *name, sqlite3_file *file,
112
0
                         int flags, int *outFlags) {
113
0
    pj_sqlite3_customvfs_appdata *appdata =
114
0
        static_cast<pj_sqlite3_customvfs_appdata *>(vfs->pAppData);
115
0
    sqlite3_vfs *defaultVFS = appdata->defaultVFS;
116
0
    int ret = defaultVFS->xOpen(defaultVFS, name, file, flags, outFlags);
117
0
    if (ret == SQLITE_OK) {
118
0
        ClosePtr defaultClosePtr = file->pMethods->xClose;
119
0
        assert(defaultClosePtr);
120
0
        sqlite3_io_methods *methods = static_cast<sqlite3_io_methods *>(
121
0
            std::malloc(sizeof(sqlite3_io_methods)));
122
0
        if (!methods) {
123
0
            file->pMethods->xClose(file);
124
0
            return SQLITE_NOMEM;
125
0
        }
126
0
        memcpy(methods, file->pMethods, sizeof(sqlite3_io_methods));
127
0
        methods->xClose = VFSClose;
128
0
        if (appdata->fakeSync) {
129
            // Disable xSync because it can be significantly slow and we don't
130
            // need
131
            // that level of data integrity guarantee for the cache.
132
0
            methods->xSync = VSFNoOpLockUnlockSync;
133
0
        }
134
0
        if (appdata->fakeLock) {
135
0
            methods->xLock = VSFNoOpLockUnlockSync;
136
0
            methods->xUnlock = VSFNoOpLockUnlockSync;
137
0
        }
138
0
        file->pMethods = methods;
139
        // Save original xClose pointer at end of file structure
140
0
        std::memcpy(reinterpret_cast<char *>(file) + defaultVFS->szOsFile,
141
0
                    &defaultClosePtr, sizeof(ClosePtr));
142
0
    }
143
0
    return ret;
144
0
}
145
146
// ---------------------------------------------------------------------------
147
148
static int VFSCustomAccess(sqlite3_vfs *vfs, const char *zName, int flags,
149
0
                           int *pResOut) {
150
0
    sqlite3_vfs *defaultVFS = static_cast<sqlite3_vfs *>(vfs->pAppData);
151
    // Do not bother stat'ing for journal or wal files
152
0
    if (std::strstr(zName, "-journal") || std::strstr(zName, "-wal")) {
153
0
        *pResOut = false;
154
0
        return SQLITE_OK;
155
0
    }
156
0
    return defaultVFS->xAccess(defaultVFS, zName, flags, pResOut);
157
0
}
158
159
// ---------------------------------------------------------------------------
160
161
// SQLite3 logging infrastructure
162
0
static void projSqlite3LogCallback(void *, int iErrCode, const char *zMsg) {
163
0
    fprintf(stderr, "SQLite3 message: (code %d) %s\n", iErrCode, zMsg);
164
0
}
165
166
namespace {
167
struct InstallSqliteLogger {
168
1
    InstallSqliteLogger() {
169
1
        if (getenv("PROJ_LOG_SQLITE3") != nullptr) {
170
0
            sqlite3_config(SQLITE_CONFIG_LOG, projSqlite3LogCallback, nullptr);
171
0
        }
172
1
    }
173
174
14.0k
    static InstallSqliteLogger &GetSingleton() {
175
14.0k
        static InstallSqliteLogger installSqliteLogger;
176
14.0k
        return installSqliteLogger;
177
14.0k
    }
178
};
179
} // namespace
180
181
// ---------------------------------------------------------------------------
182
183
std::unique_ptr<SQLite3VFS> SQLite3VFS::create(bool fakeSync, bool fakeLock,
184
0
                                               bool skipStatJournalAndWAL) {
185
186
    // Install SQLite3 logger if PROJ_LOG_SQLITE3 env var is defined
187
0
    InstallSqliteLogger::GetSingleton();
188
189
    // Call to sqlite3_initialize() is normally not needed, except for
190
    // people building SQLite3 with -DSQLITE_OMIT_AUTOINIT
191
0
    sqlite3_initialize();
192
0
    sqlite3_vfs *defaultVFS = sqlite3_vfs_find(nullptr);
193
0
    assert(defaultVFS);
194
195
0
    auto vfs = new pj_sqlite3_customvfs();
196
197
0
    auto vfsUnique = std::unique_ptr<SQLite3VFS>(new SQLite3VFS(vfs));
198
199
0
    std::ostringstream buffer;
200
0
    buffer << vfs;
201
0
    vfs->namePtr = buffer.str();
202
203
0
    vfs->base.iVersion = 1;
204
0
    vfs->base.szOsFile = defaultVFS->szOsFile + sizeof(ClosePtr);
205
0
    vfs->base.mxPathname = defaultVFS->mxPathname;
206
0
    vfs->base.zName = vfs->namePtr.c_str();
207
0
    pj_sqlite3_customvfs_appdata *appdata = new pj_sqlite3_customvfs_appdata;
208
0
    appdata->fakeSync = fakeSync;
209
0
    appdata->fakeLock = fakeLock;
210
0
    appdata->defaultVFS = defaultVFS;
211
0
    vfs->base.pAppData = appdata;
212
0
    vfs->base.xOpen = VFSCustomOpen;
213
0
    vfs->base.xDelete = defaultVFS->xDelete;
214
0
    vfs->base.xAccess =
215
0
        skipStatJournalAndWAL ? VFSCustomAccess : defaultVFS->xAccess;
216
0
    vfs->base.xFullPathname = defaultVFS->xFullPathname;
217
0
    vfs->base.xDlOpen = defaultVFS->xDlOpen;
218
0
    vfs->base.xDlError = defaultVFS->xDlError;
219
0
    vfs->base.xDlSym = defaultVFS->xDlSym;
220
0
    vfs->base.xDlClose = defaultVFS->xDlClose;
221
0
    vfs->base.xRandomness = defaultVFS->xRandomness;
222
0
    vfs->base.xSleep = defaultVFS->xSleep;
223
0
    vfs->base.xCurrentTime = defaultVFS->xCurrentTime;
224
0
    vfs->base.xGetLastError = defaultVFS->xGetLastError;
225
0
    vfs->base.xCurrentTimeInt64 = defaultVFS->xCurrentTimeInt64;
226
0
    if (sqlite3_vfs_register(&(vfs->base), false) == SQLITE_OK) {
227
0
        return vfsUnique;
228
0
    }
229
0
    delete vfsUnique->vfs_;
230
0
    vfsUnique->vfs_ = nullptr;
231
0
    return nullptr;
232
0
}
233
234
// ---------------------------------------------------------------------------
235
236
#ifdef EMBED_RESOURCE_FILES
237
238
struct pj_sqlite3_memvfs : public pj_sqlite3_vfs {
239
    ~pj_sqlite3_memvfs() override;
240
};
241
242
14.0k
pj_sqlite3_memvfs::~pj_sqlite3_memvfs() {
243
14.0k
    pj_sqlite3_memvfs_deallocate_user_data(&base);
244
14.0k
}
245
246
/* static */
247
std::unique_ptr<SQLite3VFS> SQLite3VFS::createMem(const void *membuffer,
248
14.0k
                                                  size_t bufferSize) {
249
    // Install SQLite3 logger if PROJ_LOG_SQLITE3 env var is defined
250
14.0k
    InstallSqliteLogger::GetSingleton();
251
252
    // Call to sqlite3_initialize() is normally not needed, except for
253
    // people building SQLite3 with -DSQLITE_OMIT_AUTOINIT
254
14.0k
    sqlite3_initialize();
255
256
14.0k
    auto vfs = new pj_sqlite3_memvfs();
257
258
14.0k
    auto vfsUnique = std::unique_ptr<SQLite3VFS>(new SQLite3VFS(vfs));
259
260
14.0k
    std::ostringstream buffer;
261
14.0k
    buffer << vfs;
262
14.0k
    vfs->namePtr = buffer.str();
263
14.0k
    if (pj_sqlite3_memvfs_init(&(vfs->base), vfs->namePtr.c_str(), membuffer,
264
14.0k
                               bufferSize) == SQLITE_OK) {
265
14.0k
        return vfsUnique;
266
14.0k
    }
267
0
    delete vfsUnique->vfs_;
268
0
    vfsUnique->vfs_ = nullptr;
269
0
    return nullptr;
270
14.0k
}
271
272
#endif
273
274
// ---------------------------------------------------------------------------
275
276
0
SQLiteStatement::SQLiteStatement(sqlite3_stmt *hStmtIn) : hStmt(hStmtIn) {}
277
278
// ---------------------------------------------------------------------------
279
280
NS_PROJ_END