Coverage Report

Created: 2026-04-09 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PROJ/src/sqlite3_utils.cpp
Line
Count
Source
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
9.71k
pj_sqlite3_vfs::~pj_sqlite3_vfs() = default;
52
53
// ---------------------------------------------------------------------------
54
55
9.71k
SQLite3VFS::SQLite3VFS(pj_sqlite3_vfs *vfs) : vfs_(vfs) {}
56
57
// ---------------------------------------------------------------------------
58
59
9.71k
SQLite3VFS::~SQLite3VFS() {
60
9.71k
    if (vfs_) {
61
9.71k
        sqlite3_vfs_unregister(&(vfs_->base));
62
9.71k
        delete vfs_;
63
9.71k
    }
64
9.71k
}
65
66
// ---------------------------------------------------------------------------
67
68
9.71k
const char *SQLite3VFS::name() const { return vfs_->namePtr.c_str(); }
69
70
// ---------------------------------------------------------------------------
71
72
typedef int (*ClosePtr)(sqlite3_file *);
73
74
// ---------------------------------------------------------------------------
75
76
12.7k
static int VFSClose(sqlite3_file *file) {
77
12.7k
    sqlite3_vfs *defaultVFS = sqlite3_vfs_find(nullptr);
78
12.7k
    assert(defaultVFS);
79
12.7k
    ClosePtr defaultClosePtr;
80
12.7k
    std::memcpy(&defaultClosePtr,
81
12.7k
                reinterpret_cast<char *>(file) + defaultVFS->szOsFile,
82
12.7k
                sizeof(ClosePtr));
83
12.7k
    void *methods = const_cast<sqlite3_io_methods *>(file->pMethods);
84
12.7k
    int ret = defaultClosePtr(file);
85
12.7k
    std::free(methods);
86
12.7k
    return ret;
87
12.7k
}
88
89
// ---------------------------------------------------------------------------
90
91
2.88M
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
9.71k
    ~pj_sqlite3_customvfs() override {
105
9.71k
        delete static_cast<pj_sqlite3_customvfs_appdata *>(base.pAppData);
106
9.71k
        base.pAppData = nullptr;
107
9.71k
    }
108
};
109
} // namespace
110
111
static int VFSCustomOpen(sqlite3_vfs *vfs, const char *name, sqlite3_file *file,
112
12.7k
                         int flags, int *outFlags) {
113
12.7k
    pj_sqlite3_customvfs_appdata *appdata =
114
12.7k
        static_cast<pj_sqlite3_customvfs_appdata *>(vfs->pAppData);
115
12.7k
    sqlite3_vfs *defaultVFS = appdata->defaultVFS;
116
12.7k
    int ret = defaultVFS->xOpen(defaultVFS, name, file, flags, outFlags);
117
12.7k
    if (ret == SQLITE_OK) {
118
12.7k
        ClosePtr defaultClosePtr = file->pMethods->xClose;
119
12.7k
        assert(defaultClosePtr);
120
12.7k
        sqlite3_io_methods *methods = static_cast<sqlite3_io_methods *>(
121
12.7k
            std::malloc(sizeof(sqlite3_io_methods)));
122
12.7k
        if (!methods) {
123
0
            file->pMethods->xClose(file);
124
0
            return SQLITE_NOMEM;
125
0
        }
126
12.7k
        memcpy(methods, file->pMethods, sizeof(sqlite3_io_methods));
127
12.7k
        methods->xClose = VFSClose;
128
12.7k
        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
12.7k
        if (appdata->fakeLock) {
135
12.7k
            methods->xLock = VSFNoOpLockUnlockSync;
136
12.7k
            methods->xUnlock = VSFNoOpLockUnlockSync;
137
12.7k
        }
138
12.7k
        file->pMethods = methods;
139
        // Save original xClose pointer at end of file structure
140
12.7k
        std::memcpy(reinterpret_cast<char *>(file) + defaultVFS->szOsFile,
141
12.7k
                    &defaultClosePtr, sizeof(ClosePtr));
142
12.7k
    }
143
12.7k
    return ret;
144
12.7k
}
145
146
// ---------------------------------------------------------------------------
147
148
static int VFSCustomAccess(sqlite3_vfs *vfs, const char *zName, int flags,
149
2.87M
                           int *pResOut) {
150
2.87M
    sqlite3_vfs *defaultVFS = static_cast<sqlite3_vfs *>(vfs->pAppData);
151
    // Do not bother stat'ing for journal or wal files
152
2.87M
    if (std::strstr(zName, "-journal") || std::strstr(zName, "-wal")) {
153
2.87M
        *pResOut = false;
154
2.87M
        return SQLITE_OK;
155
2.87M
    }
156
0
    return defaultVFS->xAccess(defaultVFS, zName, flags, pResOut);
157
2.87M
}
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
9.71k
    static InstallSqliteLogger &GetSingleton() {
175
9.71k
        static InstallSqliteLogger installSqliteLogger;
176
9.71k
        return installSqliteLogger;
177
9.71k
    }
178
};
179
} // namespace
180
181
// ---------------------------------------------------------------------------
182
183
std::unique_ptr<SQLite3VFS> SQLite3VFS::create(bool fakeSync, bool fakeLock,
184
9.71k
                                               bool skipStatJournalAndWAL) {
185
186
    // Install SQLite3 logger if PROJ_LOG_SQLITE3 env var is defined
187
9.71k
    InstallSqliteLogger::GetSingleton();
188
189
    // Call to sqlite3_initialize() is normally not needed, except for
190
    // people building SQLite3 with -DSQLITE_OMIT_AUTOINIT
191
9.71k
    sqlite3_initialize();
192
9.71k
    sqlite3_vfs *defaultVFS = sqlite3_vfs_find(nullptr);
193
9.71k
    assert(defaultVFS);
194
195
9.71k
    auto vfs = new pj_sqlite3_customvfs();
196
197
9.71k
    auto vfsUnique = std::unique_ptr<SQLite3VFS>(new SQLite3VFS(vfs));
198
199
9.71k
    std::ostringstream buffer;
200
9.71k
    buffer << vfs;
201
9.71k
    vfs->namePtr = buffer.str();
202
203
9.71k
    vfs->base.iVersion = 1;
204
9.71k
    vfs->base.szOsFile = defaultVFS->szOsFile + sizeof(ClosePtr);
205
9.71k
    vfs->base.mxPathname = defaultVFS->mxPathname;
206
9.71k
    vfs->base.zName = vfs->namePtr.c_str();
207
9.71k
    pj_sqlite3_customvfs_appdata *appdata = new pj_sqlite3_customvfs_appdata;
208
9.71k
    appdata->fakeSync = fakeSync;
209
9.71k
    appdata->fakeLock = fakeLock;
210
9.71k
    appdata->defaultVFS = defaultVFS;
211
9.71k
    vfs->base.pAppData = appdata;
212
9.71k
    vfs->base.xOpen = VFSCustomOpen;
213
9.71k
    vfs->base.xDelete = defaultVFS->xDelete;
214
9.71k
    vfs->base.xAccess =
215
9.71k
        skipStatJournalAndWAL ? VFSCustomAccess : defaultVFS->xAccess;
216
9.71k
    vfs->base.xFullPathname = defaultVFS->xFullPathname;
217
9.71k
    vfs->base.xDlOpen = defaultVFS->xDlOpen;
218
9.71k
    vfs->base.xDlError = defaultVFS->xDlError;
219
9.71k
    vfs->base.xDlSym = defaultVFS->xDlSym;
220
9.71k
    vfs->base.xDlClose = defaultVFS->xDlClose;
221
9.71k
    vfs->base.xRandomness = defaultVFS->xRandomness;
222
9.71k
    vfs->base.xSleep = defaultVFS->xSleep;
223
9.71k
    vfs->base.xCurrentTime = defaultVFS->xCurrentTime;
224
9.71k
    vfs->base.xGetLastError = defaultVFS->xGetLastError;
225
9.71k
    vfs->base.xCurrentTimeInt64 = defaultVFS->xCurrentTimeInt64;
226
9.71k
    if (sqlite3_vfs_register(&(vfs->base), false) == SQLITE_OK) {
227
9.71k
        return vfsUnique;
228
9.71k
    }
229
0
    delete vfsUnique->vfs_;
230
0
    vfsUnique->vfs_ = nullptr;
231
0
    return nullptr;
232
9.71k
}
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
0
pj_sqlite3_memvfs::~pj_sqlite3_memvfs() {
243
0
    pj_sqlite3_memvfs_deallocate_user_data(&base);
244
0
}
245
246
/* static */
247
std::unique_ptr<SQLite3VFS> SQLite3VFS::createMem(const void *membuffer,
248
0
                                                  size_t bufferSize) {
249
    // Install SQLite3 logger if PROJ_LOG_SQLITE3 env var is defined
250
0
    InstallSqliteLogger::GetSingleton();
251
252
    // Call to sqlite3_initialize() is normally not needed, except for
253
    // people building SQLite3 with -DSQLITE_OMIT_AUTOINIT
254
0
    sqlite3_initialize();
255
256
0
    auto vfs = new pj_sqlite3_memvfs();
257
258
0
    auto vfsUnique = std::unique_ptr<SQLite3VFS>(new SQLite3VFS(vfs));
259
260
0
    std::ostringstream buffer;
261
0
    buffer << vfs;
262
0
    vfs->namePtr = buffer.str();
263
0
    if (pj_sqlite3_memvfs_init(&(vfs->base), vfs->namePtr.c_str(), membuffer,
264
0
                               bufferSize) == SQLITE_OK) {
265
0
        return vfsUnique;
266
0
    }
267
0
    delete vfsUnique->vfs_;
268
0
    vfsUnique->vfs_ = nullptr;
269
0
    return nullptr;
270
0
}
271
272
#endif
273
274
// ---------------------------------------------------------------------------
275
276
0
SQLiteStatement::SQLiteStatement(sqlite3_stmt *hStmtIn) : hStmt(hStmtIn) {}
277
278
// ---------------------------------------------------------------------------
279
280
NS_PROJ_END