/****************************************************************************** * Project: PROJ * Purpose: SQLite3 related utilities * Author: Even Rouault, * ****************************************************************************** * Copyright (c) 2019, Even Rouault, * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. *****************************************************************************/ #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #endif #include "sqlite3_utils.hpp" #ifdef __GNUC__ #pragma GCC diagnostic pop #endif #include #include #include // std::ostringstream NS_PROJ_START // --------------------------------------------------------------------------- SQLite3VFS::SQLite3VFS(pj_sqlite3_vfs *vfs) : vfs_(vfs) {} // --------------------------------------------------------------------------- SQLite3VFS::~SQLite3VFS() { if (vfs_) { sqlite3_vfs_unregister(vfs_); delete vfs_; } } // --------------------------------------------------------------------------- const char *SQLite3VFS::name() const { return vfs_->namePtr.c_str(); } // --------------------------------------------------------------------------- typedef int (*ClosePtr)(sqlite3_file *); // --------------------------------------------------------------------------- static int VFSClose(sqlite3_file *file) { sqlite3_vfs *defaultVFS = sqlite3_vfs_find(nullptr); assert(defaultVFS); ClosePtr defaultClosePtr; std::memcpy(&defaultClosePtr, reinterpret_cast(file) + defaultVFS->szOsFile, sizeof(ClosePtr)); void *methods = const_cast(file->pMethods); int ret = defaultClosePtr(file); std::free(methods); return ret; } // --------------------------------------------------------------------------- static int VSFNoOpLockUnlockSync(sqlite3_file *, int) { return SQLITE_OK; } // --------------------------------------------------------------------------- static int VFSCustomOpen(sqlite3_vfs *vfs, const char *name, sqlite3_file *file, int flags, int *outFlags) { auto realVFS = static_cast(vfs); sqlite3_vfs *defaultVFS = static_cast(vfs->pAppData); int ret = defaultVFS->xOpen(defaultVFS, name, file, flags, outFlags); if (ret == SQLITE_OK) { ClosePtr defaultClosePtr = file->pMethods->xClose; assert(defaultClosePtr); sqlite3_io_methods *methods = static_cast( std::malloc(sizeof(sqlite3_io_methods))); if (!methods) { file->pMethods->xClose(file); return SQLITE_NOMEM; } memcpy(methods, file->pMethods, sizeof(sqlite3_io_methods)); methods->xClose = VFSClose; if (realVFS->fakeSync) { // Disable xSync because it can be significantly slow and we don't // need // that level of data integrity guarantee for the cache. methods->xSync = VSFNoOpLockUnlockSync; } if (realVFS->fakeLock) { methods->xLock = VSFNoOpLockUnlockSync; methods->xUnlock = VSFNoOpLockUnlockSync; } file->pMethods = methods; // Save original xClose pointer at end of file structure std::memcpy(reinterpret_cast(file) + defaultVFS->szOsFile, &defaultClosePtr, sizeof(ClosePtr)); } return ret; } // --------------------------------------------------------------------------- static int VFSCustomAccess(sqlite3_vfs *vfs, const char *zName, int flags, int *pResOut) { sqlite3_vfs *defaultVFS = static_cast(vfs->pAppData); // Do not bother stat'ing for journal or wal files if (std::strstr(zName, "-journal") || std::strstr(zName, "-wal")) { *pResOut = false; return SQLITE_OK; } return defaultVFS->xAccess(defaultVFS, zName, flags, pResOut); } // --------------------------------------------------------------------------- // SQLite3 logging infrastructure static void projSqlite3LogCallback(void *, int iErrCode, const char *zMsg) { fprintf(stderr, "SQLite3 message: (code %d) %s\n", iErrCode, zMsg); } std::unique_ptr SQLite3VFS::create(bool fakeSync, bool fakeLock, bool skipStatJournalAndWAL) { // Install SQLite3 logger if PROJ_LOG_SQLITE3 env var is defined struct InstallSqliteLogger { InstallSqliteLogger() { if (getenv("PROJ_LOG_SQLITE3") != nullptr) { sqlite3_config(SQLITE_CONFIG_LOG, projSqlite3LogCallback, nullptr); } } }; static InstallSqliteLogger installSqliteLogger; // Call to sqlite3_initialize() is normally not needed, except for // people building SQLite3 with -DSQLITE_OMIT_AUTOINIT sqlite3_initialize(); sqlite3_vfs *defaultVFS = sqlite3_vfs_find(nullptr); assert(defaultVFS); auto vfs = new pj_sqlite3_vfs(); vfs->fakeSync = fakeSync; vfs->fakeLock = fakeLock; auto vfsUnique = std::unique_ptr(new SQLite3VFS(vfs)); std::ostringstream buffer; buffer << vfs; vfs->namePtr = buffer.str(); vfs->iVersion = 1; vfs->szOsFile = defaultVFS->szOsFile + sizeof(ClosePtr); vfs->mxPathname = defaultVFS->mxPathname; vfs->zName = vfs->namePtr.c_str(); vfs->pAppData = defaultVFS; vfs->xOpen = VFSCustomOpen; vfs->xDelete = defaultVFS->xDelete; vfs->xAccess = skipStatJournalAndWAL ? VFSCustomAccess : defaultVFS->xAccess; vfs->xFullPathname = defaultVFS->xFullPathname; vfs->xDlOpen = defaultVFS->xDlOpen; vfs->xDlError = defaultVFS->xDlError; vfs->xDlSym = defaultVFS->xDlSym; vfs->xDlClose = defaultVFS->xDlClose; vfs->xRandomness = defaultVFS->xRandomness; vfs->xSleep = defaultVFS->xSleep; vfs->xCurrentTime = defaultVFS->xCurrentTime; vfs->xGetLastError = defaultVFS->xGetLastError; vfs->xCurrentTimeInt64 = defaultVFS->xCurrentTimeInt64; if (sqlite3_vfs_register(vfs, false) == SQLITE_OK) { return vfsUnique; } delete vfsUnique->vfs_; vfsUnique->vfs_ = nullptr; return nullptr; } // --------------------------------------------------------------------------- SQLiteStatement::SQLiteStatement(sqlite3_stmt *hStmtIn) : hStmt(hStmtIn) {} // --------------------------------------------------------------------------- NS_PROJ_END