Line data Source code
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 0 : VirtualTableCursorBase()
19 0 : {
20 0 : memset(&mBase, 0, sizeof(mBase));
21 0 : }
22 :
23 : sqlite3_vtab_cursor mBase;
24 : };
25 :
26 0 : struct VirtualTableCursor : public VirtualTableCursorBase
27 : {
28 : public:
29 0 : VirtualTableCursor()
30 0 : : mRowId(-1)
31 : {
32 0 : mCurrentFileName.SetIsVoid(true);
33 0 : }
34 :
35 0 : const nsString& DirectoryPath() const
36 : {
37 0 : return mDirectoryPath;
38 : }
39 :
40 0 : const nsString& CurrentFileName() const
41 : {
42 0 : return mCurrentFileName;
43 : }
44 :
45 0 : int64_t RowId() const
46 : {
47 0 : 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 0 : VirtualTableCursor::Init(const nsAString& aPath)
64 : {
65 : nsCOMPtr<nsIFile> directory =
66 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
67 0 : NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
68 :
69 0 : nsresult rv = directory->InitWithPath(aPath);
70 0 : NS_ENSURE_SUCCESS(rv, rv);
71 :
72 0 : rv = directory->GetPath(mDirectoryPath);
73 0 : NS_ENSURE_SUCCESS(rv, rv);
74 :
75 0 : rv = directory->GetDirectoryEntries(getter_AddRefs(mEntries));
76 0 : NS_ENSURE_SUCCESS(rv, rv);
77 :
78 0 : rv = NextFile();
79 0 : NS_ENSURE_SUCCESS(rv, rv);
80 :
81 0 : return NS_OK;
82 : }
83 :
84 : nsresult
85 0 : VirtualTableCursor::NextFile()
86 : {
87 : bool hasMore;
88 0 : nsresult rv = mEntries->HasMoreElements(&hasMore);
89 0 : NS_ENSURE_SUCCESS(rv, rv);
90 :
91 0 : if (!hasMore) {
92 0 : mCurrentFileName.SetIsVoid(true);
93 0 : return NS_OK;
94 : }
95 :
96 0 : nsCOMPtr<nsISupports> entry;
97 0 : rv = mEntries->GetNext(getter_AddRefs(entry));
98 0 : NS_ENSURE_SUCCESS(rv, rv);
99 :
100 0 : nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
101 0 : NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
102 :
103 0 : rv = file->GetLeafName(mCurrentFileName);
104 0 : NS_ENSURE_SUCCESS(rv, rv);
105 :
106 0 : mRowId++;
107 :
108 0 : return NS_OK;
109 : }
110 :
111 0 : 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 0 : int rc = sqlite3_declare_vtab(aDB, virtualTableSchema);
121 0 : if (rc != SQLITE_OK) {
122 0 : return rc;
123 : }
124 :
125 0 : sqlite3_vtab* vt = new sqlite3_vtab();
126 0 : memset(vt, 0, sizeof(*vt));
127 :
128 0 : *aVtab = vt;
129 :
130 0 : return SQLITE_OK;
131 : }
132 :
133 0 : int Disconnect(sqlite3_vtab* aVtab )
134 : {
135 : delete aVtab;
136 :
137 0 : return SQLITE_OK;
138 : }
139 :
140 0 : 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 0 : for(int i = 0; i < aInfo->nConstraint; i++) {
166 0 : if (aInfo->aConstraint[i].iColumn == 1 && aInfo->aConstraint[i].usable) {
167 0 : if (aInfo->aConstraint[i].op & SQLITE_INDEX_CONSTRAINT_EQ) {
168 0 : aInfo->aConstraintUsage[i].argvIndex = 1;
169 : }
170 0 : break;
171 : }
172 :
173 : // TODO: handle single files (constrained also by the name column)
174 : }
175 :
176 0 : return SQLITE_OK;
177 : }
178 :
179 0 : int Open(sqlite3_vtab* aVtab, sqlite3_vtab_cursor** aCursor)
180 : {
181 0 : VirtualTableCursor* cursor = new VirtualTableCursor();
182 :
183 0 : *aCursor = reinterpret_cast<sqlite3_vtab_cursor*>(cursor);
184 :
185 0 : return SQLITE_OK;
186 : }
187 :
188 0 : int Close(sqlite3_vtab_cursor* aCursor)
189 : {
190 0 : VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
191 :
192 0 : delete cursor;
193 :
194 0 : return SQLITE_OK;
195 : }
196 :
197 0 : int Filter(sqlite3_vtab_cursor* aCursor, int aIdxNum, const char* aIdxStr,
198 : int aArgc, sqlite3_value** aArgv)
199 : {
200 0 : VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
201 :
202 0 : if(aArgc <= 0) {
203 0 : return SQLITE_OK;
204 : }
205 :
206 : nsDependentString path(
207 0 : reinterpret_cast<const char16_t*>(::sqlite3_value_text16(aArgv[0])));
208 :
209 0 : nsresult rv = cursor->Init(path);
210 0 : NS_ENSURE_SUCCESS(rv, SQLITE_ERROR);
211 :
212 0 : return SQLITE_OK;
213 : }
214 :
215 0 : int Next(sqlite3_vtab_cursor* aCursor)
216 : {
217 0 : VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
218 :
219 0 : nsresult rv = cursor->NextFile();
220 0 : NS_ENSURE_SUCCESS(rv, SQLITE_ERROR);
221 :
222 0 : return SQLITE_OK;
223 : }
224 :
225 0 : int Eof(sqlite3_vtab_cursor* aCursor)
226 : {
227 0 : VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
228 0 : return cursor->CurrentFileName().IsVoid() ? 1 : 0;
229 : }
230 :
231 0 : int Column(sqlite3_vtab_cursor* aCursor, sqlite3_context* aContext,
232 : int aColumnIndex)
233 : {
234 0 : VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
235 :
236 0 : switch (aColumnIndex) {
237 : // name
238 : case 0: {
239 0 : const nsString& name = cursor->CurrentFileName();
240 0 : sqlite3_result_text16(aContext, name.get(),
241 0 : name.Length() * sizeof(char16_t),
242 0 : SQLITE_TRANSIENT);
243 0 : break;
244 : }
245 :
246 : // path
247 : case 1: {
248 0 : const nsString& path = cursor->DirectoryPath();
249 0 : sqlite3_result_text16(aContext, path.get(),
250 0 : path.Length() * sizeof(char16_t),
251 0 : SQLITE_TRANSIENT);
252 0 : break;
253 : }
254 : default:
255 0 : NS_NOTREACHED("Unsupported column!");
256 : }
257 :
258 0 : return SQLITE_OK;
259 : }
260 :
261 0 : int RowId(sqlite3_vtab_cursor* aCursor, sqlite3_int64* aRowid)
262 : {
263 0 : VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
264 :
265 0 : *aRowid = cursor->RowId();
266 :
267 0 : return SQLITE_OK;
268 : }
269 :
270 : } // namespace
271 :
272 : namespace mozilla {
273 : namespace storage {
274 :
275 0 : 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 0 : return sqlite3_create_module(aDB, aName, &module, nullptr);
301 : }
302 :
303 : } // namespace storage
304 : } // namespace mozilla
|