|
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 |