1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qsql_sqlite_vfs_p.h"
5
6#include <QFile>
7
8#include <limits.h> // defines PATH_MAX on unix
9#include <sqlite3.h>
10#include <stdio.h> // defines FILENAME_MAX everywhere
11
12#ifndef PATH_MAX
13# define PATH_MAX FILENAME_MAX
14#endif
15#if SQLITE_VERSION_NUMBER < 3040000
16typedef const char *sqlite3_filename;
17#endif
18
19QT_BEGIN_NAMESPACE
20
21namespace {
22struct Vfs : sqlite3_vfs {
23 sqlite3_vfs *pVfs;
24 sqlite3_io_methods ioMethods;
25};
26
27struct File : sqlite3_file {
28 class QtFile : public QFile {
29 public:
30 QtFile(const QString &name, bool removeOnClose)
31 : QFile(name)
32 , removeOnClose(removeOnClose)
33 {}
34
35 ~QtFile() override
36 {
37 if (removeOnClose)
38 remove();
39 }
40 private:
41 bool removeOnClose;
42 };
43 QtFile *pFile;
44};
45
46
47int xClose(sqlite3_file *sfile)
48{
49 auto file = static_cast<File *>(sfile);
50 delete file->pFile;
51 file->pFile = nullptr;
52 return SQLITE_OK;
53}
54
55int xRead(sqlite3_file *sfile, void *ptr, int iAmt, sqlite3_int64 iOfst)
56{
57 auto file = static_cast<File *>(sfile);
58 if (!file->pFile->seek(offset: iOfst))
59 return SQLITE_IOERR_READ;
60
61 auto sz = file->pFile->read(data: static_cast<char *>(ptr), maxlen: iAmt);
62 if (sz < iAmt) {
63 memset(s: static_cast<char *>(ptr) + sz, c: 0, n: size_t(iAmt - sz));
64 return SQLITE_IOERR_SHORT_READ;
65 }
66 return SQLITE_OK;
67}
68
69int xWrite(sqlite3_file *sfile, const void *data, int iAmt, sqlite3_int64 iOfst)
70{
71 auto file = static_cast<File *>(sfile);
72 if (!file->pFile->seek(offset: iOfst))
73 return SQLITE_IOERR_SEEK;
74 return file->pFile->write(data: reinterpret_cast<const char*>(data), len: iAmt) == iAmt ? SQLITE_OK : SQLITE_IOERR_WRITE;
75}
76
77int xTruncate(sqlite3_file *sfile, sqlite3_int64 size)
78{
79 auto file = static_cast<File *>(sfile);
80 return file->pFile->resize(sz: size) ? SQLITE_OK : SQLITE_IOERR_TRUNCATE;
81}
82
83int xSync(sqlite3_file *sfile, int /*flags*/)
84{
85 static_cast<File *>(sfile)->pFile->flush();
86 return SQLITE_OK;
87}
88
89int xFileSize(sqlite3_file *sfile, sqlite3_int64 *pSize)
90{
91 auto file = static_cast<File *>(sfile);
92 *pSize = file->pFile->size();
93 return SQLITE_OK;
94}
95
96// No lock/unlock for QFile, QLockFile doesn't work for me
97
98int xLock(sqlite3_file *, int) { return SQLITE_OK; }
99
100int xUnlock(sqlite3_file *, int) { return SQLITE_OK; }
101
102int xCheckReservedLock(sqlite3_file *, int *pResOut)
103{
104 *pResOut = 0;
105 return SQLITE_OK;
106}
107
108int xFileControl(sqlite3_file *, int, void *) { return SQLITE_NOTFOUND; }
109
110int xSectorSize(sqlite3_file *)
111{
112 return 4096;
113}
114
115int xDeviceCharacteristics(sqlite3_file *)
116{
117 return 0; // no SQLITE_IOCAP_XXX
118}
119
120int xOpen(sqlite3_vfs *svfs, sqlite3_filename zName, sqlite3_file *sfile,
121 int flags, int *pOutFlags)
122{
123 auto vfs = static_cast<Vfs *>(svfs);
124 auto file = static_cast<File *>(sfile);
125 memset(s: file, c: 0, n: sizeof(File));
126 QIODeviceBase::OpenMode mode = QIODeviceBase::NotOpen;
127 if (!zName || (flags & SQLITE_OPEN_MEMORY))
128 return SQLITE_PERM;
129 if ((flags & SQLITE_OPEN_READONLY) &&
130 !(flags & SQLITE_OPEN_READWRITE) &&
131 !(flags & SQLITE_OPEN_CREATE) &&
132 !(flags & SQLITE_OPEN_DELETEONCLOSE)) {
133 mode |= QIODeviceBase::OpenModeFlag::ReadOnly;
134 } else {
135 /*
136 ** ^The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction
137 ** with the [SQLITE_OPEN_CREATE] flag, which are both directly
138 ** analogous to the O_EXCL and O_CREAT flags of the POSIX open()
139 ** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the
140 ** SQLITE_OPEN_CREATE, is used to indicate that file should always
141 ** be created, and that it is an error if it already exists.
142 ** It is <i>not</i> used to indicate the file should be opened
143 ** for exclusive access.
144 */
145 if ((flags & SQLITE_OPEN_CREATE) && (flags & SQLITE_OPEN_EXCLUSIVE))
146 mode |= QIODeviceBase::OpenModeFlag::NewOnly;
147
148 if (flags & SQLITE_OPEN_READWRITE)
149 mode |= QIODeviceBase::OpenModeFlag::ReadWrite;
150 }
151
152 file->pMethods = &vfs->ioMethods;
153 file->pFile = new File::QtFile(QString::fromUtf8(utf8: zName), bool(flags & SQLITE_OPEN_DELETEONCLOSE));
154 if (!file->pFile->open(flags: mode))
155 return SQLITE_CANTOPEN;
156 if (pOutFlags)
157 *pOutFlags = flags;
158
159 return SQLITE_OK;
160}
161
162int xDelete(sqlite3_vfs *, const char *zName, int)
163{
164 return QFile::remove(fileName: QString::fromUtf8(utf8: zName)) ? SQLITE_OK : SQLITE_ERROR;
165}
166
167int xAccess(sqlite3_vfs */*svfs*/, const char *zName, int flags, int *pResOut)
168{
169 *pResOut = 0;
170 switch (flags) {
171 case SQLITE_ACCESS_EXISTS:
172 case SQLITE_ACCESS_READ:
173 *pResOut = QFile::exists(fileName: QString::fromUtf8(utf8: zName));
174 break;
175 default:
176 break;
177 }
178 return SQLITE_OK;
179}
180
181int xFullPathname(sqlite3_vfs *, const char *zName, int nOut, char *zOut)
182{
183 if (!zName)
184 return SQLITE_ERROR;
185
186 int i = 0;
187 for (;zName[i] && i < nOut; ++i)
188 zOut[i] = zName[i];
189
190 if (i >= nOut)
191 return SQLITE_ERROR;
192
193 zOut[i] = '\0';
194 return SQLITE_OK;
195}
196
197int xRandomness(sqlite3_vfs *svfs, int nByte, char *zOut)
198{
199 auto vfs = static_cast<Vfs *>(svfs)->pVfs;
200 return vfs->xRandomness(vfs, nByte, zOut);
201}
202
203int xSleep(sqlite3_vfs *svfs, int microseconds)
204{
205 auto vfs = static_cast<Vfs *>(svfs)->pVfs;
206 return vfs->xSleep(vfs, microseconds);
207}
208
209int xCurrentTime(sqlite3_vfs *svfs, double *zOut)
210{
211 auto vfs = static_cast<Vfs *>(svfs)->pVfs;
212 return vfs->xCurrentTime(vfs, zOut);
213}
214
215int xGetLastError(sqlite3_vfs *, int, char *)
216{
217 return 0;
218}
219
220int xCurrentTimeInt64(sqlite3_vfs *svfs, sqlite3_int64 *zOut)
221{
222 auto vfs = static_cast<Vfs *>(svfs)->pVfs;
223 return vfs->xCurrentTimeInt64(vfs, zOut);
224}
225} // namespace {
226
227void register_qt_vfs()
228{
229 static Vfs vfs;
230 memset(s: &vfs, c: 0, n: sizeof(Vfs));
231 vfs.iVersion = 1;
232 vfs.szOsFile = sizeof(File);
233 vfs.mxPathname = PATH_MAX;
234 vfs.zName = "QtVFS";
235 vfs.xOpen = &xOpen;
236 vfs.xDelete = &xDelete;
237 vfs.xAccess = &xAccess;
238 vfs.xFullPathname = &xFullPathname;
239 vfs.xRandomness = &xRandomness;
240 vfs.xSleep = &xSleep;
241 vfs.xCurrentTime = &xCurrentTime;
242 vfs.xGetLastError = &xGetLastError;
243 vfs.xCurrentTimeInt64 = &xCurrentTimeInt64;
244 vfs.pVfs = sqlite3_vfs_find(zVfsName: nullptr);
245 vfs.ioMethods.iVersion = 1;
246 vfs.ioMethods.xClose = &xClose;
247 vfs.ioMethods.xRead = &xRead;
248 vfs.ioMethods.xWrite = &xWrite;
249 vfs.ioMethods.xTruncate = &xTruncate;
250 vfs.ioMethods.xSync = &xSync;
251 vfs.ioMethods.xFileSize = &xFileSize;
252 vfs.ioMethods.xLock = &xLock;
253 vfs.ioMethods.xUnlock = &xUnlock;
254 vfs.ioMethods.xCheckReservedLock = &xCheckReservedLock;
255 vfs.ioMethods.xFileControl = &xFileControl;
256 vfs.ioMethods.xSectorSize = &xSectorSize;
257 vfs.ioMethods.xDeviceCharacteristics = &xDeviceCharacteristics;
258
259 sqlite3_vfs_register(&vfs, makeDflt: 0);
260}
261
262QT_END_NAMESPACE
263

source code of qtbase/src/plugins/sqldrivers/sqlite/qsql_sqlite_vfs.cpp