/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 "filemanager.hpp" |
44 | | |
45 | | #include <cstdlib> |
46 | | #include <cstring> |
47 | | #include <sstream> // std::ostringstream |
48 | | |
49 | | NS_PROJ_START |
50 | | |
51 | | // --------------------------------------------------------------------------- |
52 | | |
53 | 0 | pj_sqlite3_vfs::~pj_sqlite3_vfs() = default; |
54 | | |
55 | | // --------------------------------------------------------------------------- |
56 | | |
57 | 0 | SQLite3VFS::SQLite3VFS(pj_sqlite3_vfs *vfs) : vfs_(vfs) {} |
58 | | |
59 | | // --------------------------------------------------------------------------- |
60 | | |
61 | 0 | SQLite3VFS::~SQLite3VFS() { |
62 | 0 | if (vfs_) { |
63 | 0 | sqlite3_vfs_unregister(&(vfs_->base)); |
64 | 0 | delete vfs_; |
65 | 0 | } |
66 | 0 | } |
67 | | |
68 | | // --------------------------------------------------------------------------- |
69 | | |
70 | 0 | const char *SQLite3VFS::name() const { return vfs_->namePtr.c_str(); } |
71 | | |
72 | | // --------------------------------------------------------------------------- |
73 | | |
74 | | typedef int (*ClosePtr)(sqlite3_file *); |
75 | | |
76 | | // --------------------------------------------------------------------------- |
77 | | |
78 | 0 | static int VFSClose(sqlite3_file *file) { |
79 | 0 | sqlite3_vfs *defaultVFS = sqlite3_vfs_find(nullptr); |
80 | 0 | assert(defaultVFS); |
81 | 0 | ClosePtr defaultClosePtr; |
82 | 0 | std::memcpy(&defaultClosePtr, |
83 | 0 | reinterpret_cast<char *>(file) + defaultVFS->szOsFile, |
84 | 0 | sizeof(ClosePtr)); |
85 | 0 | void *methods = const_cast<sqlite3_io_methods *>(file->pMethods); |
86 | 0 | int ret = defaultClosePtr(file); |
87 | 0 | std::free(methods); |
88 | 0 | return ret; |
89 | 0 | } |
90 | | |
91 | | // --------------------------------------------------------------------------- |
92 | | |
93 | 0 | static int VSFNoOpLockUnlockSync(sqlite3_file *, int) { return SQLITE_OK; } |
94 | | |
95 | | // --------------------------------------------------------------------------- |
96 | | |
97 | | namespace { |
98 | | |
99 | | struct pj_sqlite3_customvfs_appdata { |
100 | | sqlite3_vfs *defaultVFS = nullptr; |
101 | | bool fakeSync = false; |
102 | | bool fakeLock = false; |
103 | | }; |
104 | | |
105 | | struct pj_sqlite3_customvfs : public pj_sqlite3_vfs { |
106 | 0 | ~pj_sqlite3_customvfs() override { |
107 | 0 | delete static_cast<pj_sqlite3_customvfs_appdata *>(base.pAppData); |
108 | 0 | base.pAppData = nullptr; |
109 | 0 | } |
110 | | }; |
111 | | } // namespace |
112 | | |
113 | | static int VFSCustomOpen(sqlite3_vfs *vfs, const char *name, sqlite3_file *file, |
114 | 0 | int flags, int *outFlags) { |
115 | 0 | pj_sqlite3_customvfs_appdata *appdata = |
116 | 0 | static_cast<pj_sqlite3_customvfs_appdata *>(vfs->pAppData); |
117 | 0 | sqlite3_vfs *defaultVFS = appdata->defaultVFS; |
118 | 0 | int ret = defaultVFS->xOpen(defaultVFS, name, file, flags, outFlags); |
119 | 0 | if (ret == SQLITE_OK) { |
120 | 0 | ClosePtr defaultClosePtr = file->pMethods->xClose; |
121 | 0 | assert(defaultClosePtr); |
122 | 0 | sqlite3_io_methods *methods = static_cast<sqlite3_io_methods *>( |
123 | 0 | std::malloc(sizeof(sqlite3_io_methods))); |
124 | 0 | if (!methods) { |
125 | 0 | file->pMethods->xClose(file); |
126 | 0 | return SQLITE_NOMEM; |
127 | 0 | } |
128 | 0 | memcpy(methods, file->pMethods, sizeof(sqlite3_io_methods)); |
129 | 0 | methods->xClose = VFSClose; |
130 | 0 | if (appdata->fakeSync) { |
131 | | // Disable xSync because it can be significantly slow and we don't |
132 | | // need |
133 | | // that level of data integrity guarantee for the cache. |
134 | 0 | methods->xSync = VSFNoOpLockUnlockSync; |
135 | 0 | } |
136 | 0 | if (appdata->fakeLock) { |
137 | 0 | methods->xLock = VSFNoOpLockUnlockSync; |
138 | 0 | methods->xUnlock = VSFNoOpLockUnlockSync; |
139 | 0 | } |
140 | 0 | file->pMethods = methods; |
141 | | // Save original xClose pointer at end of file structure |
142 | 0 | std::memcpy(reinterpret_cast<char *>(file) + defaultVFS->szOsFile, |
143 | 0 | &defaultClosePtr, sizeof(ClosePtr)); |
144 | 0 | } |
145 | 0 | return ret; |
146 | 0 | } |
147 | | |
148 | | // --------------------------------------------------------------------------- |
149 | | |
150 | | static int VFSCustomAccess(sqlite3_vfs *vfs, const char *zName, int flags, |
151 | 0 | int *pResOut) { |
152 | 0 | sqlite3_vfs *defaultVFS = static_cast<sqlite3_vfs *>(vfs->pAppData); |
153 | | // Do not bother stat'ing for journal or wal files |
154 | 0 | if (std::strstr(zName, "-journal") || std::strstr(zName, "-wal")) { |
155 | 0 | *pResOut = false; |
156 | 0 | return SQLITE_OK; |
157 | 0 | } |
158 | 0 | return defaultVFS->xAccess(defaultVFS, zName, flags, pResOut); |
159 | 0 | } |
160 | | |
161 | | // --------------------------------------------------------------------------- |
162 | | |
163 | | // SQLite3 logging infrastructure |
164 | 0 | static void projSqlite3LogCallback(void *, int iErrCode, const char *zMsg) { |
165 | 0 | fprintf(stderr, "SQLite3 message: (code %d) %s\n", iErrCode, zMsg); |
166 | 0 | } |
167 | | |
168 | | namespace { |
169 | | struct InstallSqliteLogger { |
170 | 0 | InstallSqliteLogger() { |
171 | 0 | if (getenv("PROJ_LOG_SQLITE3") != nullptr) { |
172 | 0 | sqlite3_config(SQLITE_CONFIG_LOG, projSqlite3LogCallback, nullptr); |
173 | 0 | } |
174 | 0 | } |
175 | | |
176 | 0 | static InstallSqliteLogger &GetSingleton() { |
177 | 0 | static InstallSqliteLogger installSqliteLogger; |
178 | 0 | return installSqliteLogger; |
179 | 0 | } |
180 | | }; |
181 | | } // namespace |
182 | | |
183 | | // --------------------------------------------------------------------------- |
184 | | |
185 | | std::unique_ptr<SQLite3VFS> SQLite3VFS::create(bool fakeSync, bool fakeLock, |
186 | 0 | bool skipStatJournalAndWAL) { |
187 | | |
188 | | // Install SQLite3 logger if PROJ_LOG_SQLITE3 env var is defined |
189 | 0 | InstallSqliteLogger::GetSingleton(); |
190 | | |
191 | | // Call to sqlite3_initialize() is normally not needed, except for |
192 | | // people building SQLite3 with -DSQLITE_OMIT_AUTOINIT |
193 | 0 | sqlite3_initialize(); |
194 | 0 | sqlite3_vfs *defaultVFS = sqlite3_vfs_find(nullptr); |
195 | 0 | assert(defaultVFS); |
196 | |
|
197 | 0 | auto vfs = new pj_sqlite3_customvfs(); |
198 | |
|
199 | 0 | auto vfsUnique = std::unique_ptr<SQLite3VFS>(new SQLite3VFS(vfs)); |
200 | |
|
201 | 0 | std::ostringstream buffer; |
202 | 0 | buffer << vfs; |
203 | 0 | vfs->namePtr = buffer.str(); |
204 | |
|
205 | 0 | vfs->base.iVersion = 1; |
206 | 0 | vfs->base.szOsFile = defaultVFS->szOsFile + sizeof(ClosePtr); |
207 | 0 | vfs->base.mxPathname = defaultVFS->mxPathname; |
208 | 0 | vfs->base.zName = vfs->namePtr.c_str(); |
209 | 0 | pj_sqlite3_customvfs_appdata *appdata = new pj_sqlite3_customvfs_appdata; |
210 | 0 | appdata->fakeSync = fakeSync; |
211 | 0 | appdata->fakeLock = fakeLock; |
212 | 0 | appdata->defaultVFS = defaultVFS; |
213 | 0 | vfs->base.pAppData = appdata; |
214 | 0 | vfs->base.xOpen = VFSCustomOpen; |
215 | 0 | vfs->base.xDelete = defaultVFS->xDelete; |
216 | 0 | vfs->base.xAccess = |
217 | 0 | skipStatJournalAndWAL ? VFSCustomAccess : defaultVFS->xAccess; |
218 | 0 | vfs->base.xFullPathname = defaultVFS->xFullPathname; |
219 | 0 | vfs->base.xDlOpen = defaultVFS->xDlOpen; |
220 | 0 | vfs->base.xDlError = defaultVFS->xDlError; |
221 | 0 | vfs->base.xDlSym = defaultVFS->xDlSym; |
222 | 0 | vfs->base.xDlClose = defaultVFS->xDlClose; |
223 | 0 | vfs->base.xRandomness = defaultVFS->xRandomness; |
224 | 0 | vfs->base.xSleep = defaultVFS->xSleep; |
225 | 0 | vfs->base.xCurrentTime = defaultVFS->xCurrentTime; |
226 | 0 | vfs->base.xGetLastError = defaultVFS->xGetLastError; |
227 | 0 | vfs->base.xCurrentTimeInt64 = defaultVFS->xCurrentTimeInt64; |
228 | 0 | if (sqlite3_vfs_register(&(vfs->base), false) == SQLITE_OK) { |
229 | 0 | return vfsUnique; |
230 | 0 | } |
231 | 0 | delete vfsUnique->vfs_; |
232 | 0 | vfsUnique->vfs_ = nullptr; |
233 | 0 | return nullptr; |
234 | 0 | } |
235 | | |
236 | | // --------------------------------------------------------------------------- |
237 | | |
238 | 0 | static int VFSNetworkClose(sqlite3_file *file) { |
239 | 0 | File *pj_file; |
240 | 0 | memcpy(&pj_file, reinterpret_cast<char *>(file) + sizeof(sqlite3_file), |
241 | 0 | sizeof(pj_file)); |
242 | 0 | delete pj_file; |
243 | 0 | return SQLITE_OK; |
244 | 0 | } |
245 | | |
246 | | // --------------------------------------------------------------------------- |
247 | | |
248 | | static int VFSNetworkRead(sqlite3_file *file, void *pBuffer, int iAmt, |
249 | 0 | sqlite3_int64 iOffset) { |
250 | 0 | File *pj_file; |
251 | 0 | memcpy(&pj_file, reinterpret_cast<char *>(file) + sizeof(sqlite3_file), |
252 | 0 | sizeof(pj_file)); |
253 | 0 | if (!pj_file->seek(iOffset)) |
254 | 0 | return SQLITE_IOERR_READ; |
255 | 0 | const int nRead = static_cast<int>(pj_file->read(pBuffer, iAmt)); |
256 | 0 | if (nRead < iAmt) { |
257 | 0 | memset(static_cast<char *>(pBuffer) + nRead, 0, iAmt - nRead); |
258 | 0 | return SQLITE_IOERR_SHORT_READ; |
259 | 0 | } |
260 | 0 | return SQLITE_OK; |
261 | 0 | } |
262 | | |
263 | | // --------------------------------------------------------------------------- |
264 | | |
265 | 0 | static int VFSNetworkWrite(sqlite3_file *, const void *, int, sqlite3_int64) { |
266 | 0 | return SQLITE_IOERR_WRITE; |
267 | 0 | } |
268 | | |
269 | | // --------------------------------------------------------------------------- |
270 | | |
271 | 0 | static int VFSNetworkFileSize(sqlite3_file *file, sqlite3_int64 *pSize) { |
272 | 0 | File *pj_file; |
273 | 0 | memcpy(&pj_file, reinterpret_cast<char *>(file) + sizeof(sqlite3_file), |
274 | 0 | sizeof(pj_file)); |
275 | 0 | pj_file->seek(0, SEEK_END); |
276 | 0 | *pSize = pj_file->tell(); |
277 | 0 | return SQLITE_OK; |
278 | 0 | } |
279 | | |
280 | | // --------------------------------------------------------------------------- |
281 | | |
282 | 0 | static int VFSNetworkTruncate(sqlite3_file *, sqlite3_int64) { |
283 | 0 | return SQLITE_IOERR_TRUNCATE; |
284 | 0 | } |
285 | | |
286 | | // --------------------------------------------------------------------------- |
287 | | |
288 | 0 | static int VFSNetworkSync(sqlite3_file *, int) { return SQLITE_OK; } |
289 | | |
290 | | // --------------------------------------------------------------------------- |
291 | | |
292 | 0 | static int VFSNetworkLock(sqlite3_file *, int) { return SQLITE_OK; } |
293 | | |
294 | | // --------------------------------------------------------------------------- |
295 | | |
296 | 0 | static int VFSNetworkUnlock(sqlite3_file *, int) { return SQLITE_OK; } |
297 | | |
298 | | // --------------------------------------------------------------------------- |
299 | | |
300 | 0 | static int VFSNetworkCheckReservedLock(sqlite3_file *, int *pResOut) { |
301 | 0 | *pResOut = 0; |
302 | 0 | return SQLITE_OK; |
303 | 0 | } |
304 | | |
305 | | // --------------------------------------------------------------------------- |
306 | | |
307 | 0 | static int VFSNetworkFileControl(sqlite3_file *, int, void *) { |
308 | 0 | return SQLITE_NOTFOUND; |
309 | 0 | } |
310 | | |
311 | | // --------------------------------------------------------------------------- |
312 | | |
313 | 0 | static int VFSNetworkSectorSize(sqlite3_file *) { return 0; } |
314 | | |
315 | | // --------------------------------------------------------------------------- |
316 | | |
317 | 0 | static int VFSNetworkDeviceCharacteristics(sqlite3_file *) { return 0; } |
318 | | |
319 | | // --------------------------------------------------------------------------- |
320 | | |
321 | | static const sqlite3_io_methods VFSNetworkIOMethods = { |
322 | | 1, |
323 | | VFSNetworkClose, |
324 | | VFSNetworkRead, |
325 | | VFSNetworkWrite, |
326 | | VFSNetworkTruncate, |
327 | | VFSNetworkSync, |
328 | | VFSNetworkFileSize, |
329 | | VFSNetworkLock, |
330 | | VFSNetworkUnlock, |
331 | | VFSNetworkCheckReservedLock, |
332 | | VFSNetworkFileControl, |
333 | | VFSNetworkSectorSize, |
334 | | VFSNetworkDeviceCharacteristics, |
335 | | nullptr, // xShmMap |
336 | | nullptr, // xShmLock |
337 | | nullptr, // xShmBarrier |
338 | | nullptr, // xShmUnmap |
339 | | nullptr, // xFetch |
340 | | nullptr, // xUnfetch |
341 | | }; |
342 | | |
343 | | static int VFSNetworkOpen(sqlite3_vfs *vfs, const char *name, |
344 | | sqlite3_file *file, int /*flags*/, |
345 | 0 | int * /*outFlags*/) { |
346 | 0 | if (!name) |
347 | 0 | return SQLITE_CANTOPEN; |
348 | 0 | PJ_CONTEXT *ctx = static_cast<PJ_CONTEXT *>(vfs->pAppData); |
349 | 0 | auto pjFile = pj_network_file_open(ctx, name); |
350 | 0 | if (!pjFile) |
351 | 0 | return SQLITE_CANTOPEN; |
352 | 0 | file->pMethods = &VFSNetworkIOMethods; |
353 | 0 | File *pj_file_raw = pjFile.release(); |
354 | 0 | memcpy(reinterpret_cast<char *>(file) + sizeof(sqlite3_file), &pj_file_raw, |
355 | 0 | sizeof(pj_file_raw)); |
356 | |
|
357 | 0 | return SQLITE_OK; |
358 | 0 | } |
359 | | |
360 | | // --------------------------------------------------------------------------- |
361 | | |
362 | | static int VFSNetworkFullPathname(sqlite3_vfs *, const char *zName, int nOut, |
363 | 0 | char *zOut) { |
364 | 0 | if (static_cast<int>(strlen(zName)) >= nOut) { |
365 | 0 | fprintf(stderr, "Maximum pathname length reserved for SQLite3 VFS " |
366 | 0 | "isn't large enough.\n"); |
367 | 0 | return SQLITE_CANTOPEN; |
368 | 0 | } |
369 | 0 | strncpy(zOut, zName, nOut); |
370 | 0 | zOut[nOut - 1] = '\0'; |
371 | 0 | return SQLITE_OK; |
372 | 0 | } |
373 | | |
374 | | // --------------------------------------------------------------------------- |
375 | | |
376 | 0 | static int VFSNetworkAccess(sqlite3_vfs *, const char *, int, int *pResOut) { |
377 | 0 | *pResOut = 0; |
378 | 0 | return SQLITE_OK; |
379 | 0 | } |
380 | | |
381 | | // --------------------------------------------------------------------------- |
382 | | |
383 | 0 | static int VFSNetworkDelete(sqlite3_vfs *, const char *, int) { |
384 | 0 | return SQLITE_IOERR_DELETE; |
385 | 0 | } |
386 | | |
387 | | // --------------------------------------------------------------------------- |
388 | | |
389 | | /* static */ std::unique_ptr<SQLite3VFS> |
390 | 0 | SQLite3VFS::createNetwork(PJ_CONTEXT *ctx) { |
391 | | // Install SQLite3 logger if PROJ_LOG_SQLITE3 env var is defined |
392 | 0 | InstallSqliteLogger::GetSingleton(); |
393 | | |
394 | | // Call to sqlite3_initialize() is normally not needed, except for |
395 | | // people building SQLite3 with -DSQLITE_OMIT_AUTOINIT |
396 | 0 | sqlite3_initialize(); |
397 | 0 | sqlite3_vfs *defaultVFS = sqlite3_vfs_find(nullptr); |
398 | 0 | assert(defaultVFS); |
399 | |
|
400 | 0 | auto vfs = new pj_sqlite3_vfs(); |
401 | |
|
402 | 0 | std::ostringstream buffer; |
403 | 0 | buffer << vfs; |
404 | 0 | vfs->namePtr = buffer.str(); |
405 | |
|
406 | 0 | vfs->base.iVersion = 1; |
407 | 0 | vfs->base.szOsFile = sizeof(sqlite3_file) + sizeof(File *); |
408 | 0 | vfs->base.mxPathname = defaultVFS->mxPathname; |
409 | 0 | vfs->base.zName = vfs->namePtr.c_str(); |
410 | 0 | vfs->base.pAppData = ctx; |
411 | 0 | vfs->base.xOpen = VFSNetworkOpen; |
412 | 0 | vfs->base.xDelete = VFSNetworkDelete; |
413 | 0 | vfs->base.xAccess = VFSNetworkAccess; |
414 | 0 | vfs->base.xFullPathname = VFSNetworkFullPathname; |
415 | 0 | vfs->base.xDlOpen = defaultVFS->xDlOpen; |
416 | 0 | vfs->base.xDlError = defaultVFS->xDlError; |
417 | 0 | vfs->base.xDlSym = defaultVFS->xDlSym; |
418 | 0 | vfs->base.xDlClose = defaultVFS->xDlClose; |
419 | 0 | vfs->base.xRandomness = defaultVFS->xRandomness; |
420 | 0 | vfs->base.xSleep = defaultVFS->xSleep; |
421 | 0 | vfs->base.xCurrentTime = defaultVFS->xCurrentTime; |
422 | 0 | vfs->base.xGetLastError = defaultVFS->xGetLastError; |
423 | 0 | vfs->base.xCurrentTimeInt64 = defaultVFS->xCurrentTimeInt64; |
424 | 0 | if (sqlite3_vfs_register(&(vfs->base), false) == SQLITE_OK) { |
425 | 0 | return std::unique_ptr<SQLite3VFS>(new SQLite3VFS(vfs)); |
426 | 0 | } |
427 | 0 | return nullptr; |
428 | 0 | } |
429 | | |
430 | | // --------------------------------------------------------------------------- |
431 | | |
432 | | #ifdef EMBED_RESOURCE_FILES |
433 | | |
434 | | struct pj_sqlite3_memvfs : public pj_sqlite3_vfs { |
435 | | ~pj_sqlite3_memvfs() override; |
436 | | }; |
437 | | |
438 | 0 | pj_sqlite3_memvfs::~pj_sqlite3_memvfs() { |
439 | 0 | pj_sqlite3_memvfs_deallocate_user_data(&base); |
440 | 0 | } |
441 | | |
442 | | /* static */ |
443 | | std::unique_ptr<SQLite3VFS> SQLite3VFS::createMem(const void *membuffer, |
444 | 0 | size_t bufferSize) { |
445 | | // Install SQLite3 logger if PROJ_LOG_SQLITE3 env var is defined |
446 | 0 | InstallSqliteLogger::GetSingleton(); |
447 | | |
448 | | // Call to sqlite3_initialize() is normally not needed, except for |
449 | | // people building SQLite3 with -DSQLITE_OMIT_AUTOINIT |
450 | 0 | sqlite3_initialize(); |
451 | |
|
452 | 0 | auto vfs = new pj_sqlite3_memvfs(); |
453 | |
|
454 | 0 | auto vfsUnique = std::unique_ptr<SQLite3VFS>(new SQLite3VFS(vfs)); |
455 | |
|
456 | 0 | std::ostringstream buffer; |
457 | 0 | buffer << vfs; |
458 | 0 | vfs->namePtr = buffer.str(); |
459 | 0 | if (pj_sqlite3_memvfs_init(&(vfs->base), vfs->namePtr.c_str(), membuffer, |
460 | 0 | bufferSize) == SQLITE_OK) { |
461 | 0 | return vfsUnique; |
462 | 0 | } |
463 | 0 | delete vfsUnique->vfs_; |
464 | 0 | vfsUnique->vfs_ = nullptr; |
465 | 0 | return nullptr; |
466 | 0 | } |
467 | | |
468 | | #endif |
469 | | |
470 | | // --------------------------------------------------------------------------- |
471 | | |
472 | 0 | SQLiteStatement::SQLiteStatement(sqlite3_stmt *hStmtIn) : hStmt(hStmtIn) {} |
473 | | |
474 | | // --------------------------------------------------------------------------- |
475 | | |
476 | | NS_PROJ_END |