storage/src/FileSystemModule.cpp

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:e8e2a53ec5a7
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "FileSystemModule.h"
8
9 #include "sqlite3.h"
10 #include "nsString.h"
11 #include "nsISimpleEnumerator.h"
12 #include "nsIFile.h"
13
14 namespace {
15
16 struct VirtualTableCursorBase
17 {
18 VirtualTableCursorBase()
19 {
20 memset(&mBase, 0, sizeof(mBase));
21 }
22
23 sqlite3_vtab_cursor mBase;
24 };
25
26 struct VirtualTableCursor : public VirtualTableCursorBase
27 {
28 public:
29 VirtualTableCursor()
30 : mRowId(-1)
31 {
32 mCurrentFileName.SetIsVoid(true);
33 }
34
35 const nsString& DirectoryPath() const
36 {
37 return mDirectoryPath;
38 }
39
40 const nsString& CurrentFileName() const
41 {
42 return mCurrentFileName;
43 }
44
45 int64_t RowId() const
46 {
47 return mRowId;
48 }
49
50 nsresult Init(const nsAString& aPath);
51 nsresult NextFile();
52
53 private:
54 nsCOMPtr<nsISimpleEnumerator> mEntries;
55
56 nsString mDirectoryPath;
57 nsString mCurrentFileName;
58
59 int64_t mRowId;
60 };
61
62 nsresult
63 VirtualTableCursor::Init(const nsAString& aPath)
64 {
65 nsCOMPtr<nsIFile> directory =
66 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
67 NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
68
69 nsresult rv = directory->InitWithPath(aPath);
70 NS_ENSURE_SUCCESS(rv, rv);
71
72 rv = directory->GetPath(mDirectoryPath);
73 NS_ENSURE_SUCCESS(rv, rv);
74
75 rv = directory->GetDirectoryEntries(getter_AddRefs(mEntries));
76 NS_ENSURE_SUCCESS(rv, rv);
77
78 rv = NextFile();
79 NS_ENSURE_SUCCESS(rv, rv);
80
81 return NS_OK;
82 }
83
84 nsresult
85 VirtualTableCursor::NextFile()
86 {
87 bool hasMore;
88 nsresult rv = mEntries->HasMoreElements(&hasMore);
89 NS_ENSURE_SUCCESS(rv, rv);
90
91 if (!hasMore) {
92 mCurrentFileName.SetIsVoid(true);
93 return NS_OK;
94 }
95
96 nsCOMPtr<nsISupports> entry;
97 rv = mEntries->GetNext(getter_AddRefs(entry));
98 NS_ENSURE_SUCCESS(rv, rv);
99
100 nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
101 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
102
103 rv = file->GetLeafName(mCurrentFileName);
104 NS_ENSURE_SUCCESS(rv, rv);
105
106 mRowId++;
107
108 return NS_OK;
109 }
110
111 int Connect(sqlite3* aDB, void* aAux, int aArgc, const char* const* aArgv,
112 sqlite3_vtab** aVtab, char** aErr)
113 {
114 static const char virtualTableSchema[] =
115 "CREATE TABLE fs ("
116 "name TEXT, "
117 "path TEXT"
118 ")";
119
120 int rc = sqlite3_declare_vtab(aDB, virtualTableSchema);
121 if (rc != SQLITE_OK) {
122 return rc;
123 }
124
125 sqlite3_vtab* vt = new sqlite3_vtab();
126 memset(vt, 0, sizeof(*vt));
127
128 *aVtab = vt;
129
130 return SQLITE_OK;
131 }
132
133 int Disconnect(sqlite3_vtab* aVtab )
134 {
135 delete aVtab;
136
137 return SQLITE_OK;
138 }
139
140 int BestIndex(sqlite3_vtab* aVtab, sqlite3_index_info* aInfo)
141 {
142 // Here we specify what index constraints we want to handle. That is, there
143 // might be some columns with particular constraints in which we can help
144 // SQLite narrow down the result set.
145 //
146 // For example, take the "path = x" where x is a directory. In this case,
147 // we can narrow our search to just this directory instead of the entire file
148 // system. This can be a significant optimization. So, we want to handle that
149 // constraint. To do so, we would look for two specific input conditions:
150 //
151 // 1. aInfo->aConstraint[i].iColumn == 1
152 // 2. aInfo->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_EQ
153 //
154 // The first states that the path column is being used in one of the input
155 // constraints and the second states that the constraint involves the equal
156 // operator.
157 //
158 // An even more specific search would be for name='xxx', in which case we
159 // can limit the search to a single file, if it exists.
160 //
161 // What we have to do here is look for all of our index searches and select
162 // the narrowest. We can only pick one, so obviously we want the one that
163 // is the most specific, which leads to the smallest result set.
164
165 for(int i = 0; i < aInfo->nConstraint; i++) {
166 if (aInfo->aConstraint[i].iColumn == 1 && aInfo->aConstraint[i].usable) {
167 if (aInfo->aConstraint[i].op & SQLITE_INDEX_CONSTRAINT_EQ) {
168 aInfo->aConstraintUsage[i].argvIndex = 1;
169 }
170 break;
171 }
172
173 // TODO: handle single files (constrained also by the name column)
174 }
175
176 return SQLITE_OK;
177 }
178
179 int Open(sqlite3_vtab* aVtab, sqlite3_vtab_cursor** aCursor)
180 {
181 VirtualTableCursor* cursor = new VirtualTableCursor();
182
183 *aCursor = reinterpret_cast<sqlite3_vtab_cursor*>(cursor);
184
185 return SQLITE_OK;
186 }
187
188 int Close(sqlite3_vtab_cursor* aCursor)
189 {
190 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
191
192 delete cursor;
193
194 return SQLITE_OK;
195 }
196
197 int Filter(sqlite3_vtab_cursor* aCursor, int aIdxNum, const char* aIdxStr,
198 int aArgc, sqlite3_value** aArgv)
199 {
200 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
201
202 if(aArgc <= 0) {
203 return SQLITE_OK;
204 }
205
206 nsDependentString path(
207 reinterpret_cast<const char16_t*>(::sqlite3_value_text16(aArgv[0])));
208
209 nsresult rv = cursor->Init(path);
210 NS_ENSURE_SUCCESS(rv, SQLITE_ERROR);
211
212 return SQLITE_OK;
213 }
214
215 int Next(sqlite3_vtab_cursor* aCursor)
216 {
217 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
218
219 nsresult rv = cursor->NextFile();
220 NS_ENSURE_SUCCESS(rv, SQLITE_ERROR);
221
222 return SQLITE_OK;
223 }
224
225 int Eof(sqlite3_vtab_cursor* aCursor)
226 {
227 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
228 return cursor->CurrentFileName().IsVoid() ? 1 : 0;
229 }
230
231 int Column(sqlite3_vtab_cursor* aCursor, sqlite3_context* aContext,
232 int aColumnIndex)
233 {
234 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
235
236 switch (aColumnIndex) {
237 // name
238 case 0: {
239 const nsString& name = cursor->CurrentFileName();
240 sqlite3_result_text16(aContext, name.get(),
241 name.Length() * sizeof(char16_t),
242 SQLITE_TRANSIENT);
243 break;
244 }
245
246 // path
247 case 1: {
248 const nsString& path = cursor->DirectoryPath();
249 sqlite3_result_text16(aContext, path.get(),
250 path.Length() * sizeof(char16_t),
251 SQLITE_TRANSIENT);
252 break;
253 }
254 default:
255 NS_NOTREACHED("Unsupported column!");
256 }
257
258 return SQLITE_OK;
259 }
260
261 int RowId(sqlite3_vtab_cursor* aCursor, sqlite3_int64* aRowid)
262 {
263 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
264
265 *aRowid = cursor->RowId();
266
267 return SQLITE_OK;
268 }
269
270 } // anonymous namespace
271
272 namespace mozilla {
273 namespace storage {
274
275 int RegisterFileSystemModule(sqlite3* aDB, const char* aName)
276 {
277 static sqlite3_module module = {
278 1,
279 Connect,
280 Connect,
281 BestIndex,
282 Disconnect,
283 Disconnect,
284 Open,
285 Close,
286 Filter,
287 Next,
288 Eof,
289 Column,
290 RowId,
291 nullptr,
292 nullptr,
293 nullptr,
294 nullptr,
295 nullptr,
296 nullptr,
297 nullptr
298 };
299
300 return sqlite3_create_module(aDB, aName, &module, nullptr);
301 }
302
303 } // namespace storage
304 } // namespace mozilla

mercurial