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 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla.
17 : *
18 : * The Initial Developer of the Original Code is Mozilla Foundation.
19 : * Portions created by the Initial Developer are Copyright (C) 2011
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "FileSystemModule.h"
39 :
40 : #include "sqlite3.h"
41 : #include "nsString.h"
42 : #include "nsISimpleEnumerator.h"
43 : #include "nsIFile.h"
44 : #include "nsILocalFile.h"
45 :
46 : namespace {
47 :
48 : struct VirtualTableCursorBase
49 : {
50 52 : VirtualTableCursorBase()
51 : {
52 52 : memset(&mBase, 0, sizeof(mBase));
53 52 : }
54 :
55 : sqlite3_vtab_cursor mBase;
56 : };
57 :
58 : struct VirtualTableCursor : public VirtualTableCursorBase
59 52 : {
60 : public:
61 52 : VirtualTableCursor()
62 52 : : mRowId(-1)
63 : {
64 52 : mCurrentFileName.SetIsVoid(true);
65 52 : }
66 :
67 0 : const nsString& DirectoryPath() const
68 : {
69 0 : return mDirectoryPath;
70 : }
71 :
72 52 : const nsString& CurrentFileName() const
73 : {
74 52 : return mCurrentFileName;
75 : }
76 :
77 0 : PRInt64 RowId() const
78 : {
79 0 : return mRowId;
80 : }
81 :
82 : nsresult Init(const nsAString& aPath);
83 : nsresult NextFile();
84 :
85 : private:
86 : nsCOMPtr<nsISimpleEnumerator> mEntries;
87 :
88 : nsString mDirectoryPath;
89 : nsString mCurrentFileName;
90 :
91 : PRInt64 mRowId;
92 : };
93 :
94 : nsresult
95 52 : VirtualTableCursor::Init(const nsAString& aPath)
96 : {
97 : nsCOMPtr<nsILocalFile> directory =
98 104 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
99 52 : NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
100 :
101 52 : nsresult rv = directory->InitWithPath(aPath);
102 52 : NS_ENSURE_SUCCESS(rv, rv);
103 :
104 52 : rv = directory->GetPath(mDirectoryPath);
105 52 : NS_ENSURE_SUCCESS(rv, rv);
106 :
107 52 : rv = directory->GetDirectoryEntries(getter_AddRefs(mEntries));
108 52 : NS_ENSURE_SUCCESS(rv, rv);
109 :
110 52 : rv = NextFile();
111 52 : NS_ENSURE_SUCCESS(rv, rv);
112 :
113 52 : return NS_OK;
114 : }
115 :
116 : nsresult
117 52 : VirtualTableCursor::NextFile()
118 : {
119 : bool hasMore;
120 52 : nsresult rv = mEntries->HasMoreElements(&hasMore);
121 52 : NS_ENSURE_SUCCESS(rv, rv);
122 :
123 52 : if (!hasMore) {
124 52 : mCurrentFileName.SetIsVoid(true);
125 52 : return NS_OK;
126 : }
127 :
128 0 : nsCOMPtr<nsISupports> entry;
129 0 : rv = mEntries->GetNext(getter_AddRefs(entry));
130 0 : NS_ENSURE_SUCCESS(rv, rv);
131 :
132 0 : nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
133 0 : NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
134 :
135 0 : rv = file->GetLeafName(mCurrentFileName);
136 0 : NS_ENSURE_SUCCESS(rv, rv);
137 :
138 0 : mRowId++;
139 :
140 0 : return NS_OK;
141 : }
142 :
143 52 : int Connect(sqlite3* aDB, void* aAux, int aArgc, const char* const* aArgv,
144 : sqlite3_vtab** aVtab, char** aErr)
145 : {
146 : static const char virtualTableSchema[] =
147 : "CREATE TABLE fs ("
148 : "name TEXT, "
149 : "path TEXT"
150 : ")";
151 :
152 52 : int rc = sqlite3_declare_vtab(aDB, virtualTableSchema);
153 52 : if (rc != SQLITE_OK) {
154 0 : return rc;
155 : }
156 :
157 52 : sqlite3_vtab* vt = new sqlite3_vtab();
158 52 : memset(vt, 0, sizeof(*vt));
159 :
160 52 : *aVtab = vt;
161 :
162 52 : return SQLITE_OK;
163 : }
164 :
165 52 : int Disconnect(sqlite3_vtab* aVtab )
166 : {
167 : delete aVtab;
168 :
169 52 : return SQLITE_OK;
170 : }
171 :
172 52 : int BestIndex(sqlite3_vtab* aVtab, sqlite3_index_info* aInfo)
173 : {
174 : // Here we specify what index constraints we want to handle. That is, there
175 : // might be some columns with particular constraints in which we can help
176 : // SQLite narrow down the result set.
177 : //
178 : // For example, take the "path = x" where x is a directory. In this case,
179 : // we can narrow our search to just this directory instead of the entire file
180 : // system. This can be a significant optimization. So, we want to handle that
181 : // constraint. To do so, we would look for two specific input conditions:
182 : //
183 : // 1. aInfo->aConstraint[i].iColumn == 1
184 : // 2. aInfo->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_EQ
185 : //
186 : // The first states that the path column is being used in one of the input
187 : // constraints and the second states that the constraint involves the equal
188 : // operator.
189 : //
190 : // An even more specific search would be for name='xxx', in which case we
191 : // can limit the search to a single file, if it exists.
192 : //
193 : // What we have to do here is look for all of our index searches and select
194 : // the narrowest. We can only pick one, so obviously we want the one that
195 : // is the most specific, which leads to the smallest result set.
196 :
197 52 : for(int i = 0; i < aInfo->nConstraint; i++) {
198 52 : if (aInfo->aConstraint[i].iColumn == 1 && aInfo->aConstraint[i].usable) {
199 52 : if (aInfo->aConstraint[i].op & SQLITE_INDEX_CONSTRAINT_EQ) {
200 52 : aInfo->aConstraintUsage[i].argvIndex = 1;
201 : }
202 52 : break;
203 : }
204 :
205 : // TODO: handle single files (constrained also by the name column)
206 : }
207 :
208 52 : return SQLITE_OK;
209 : }
210 :
211 52 : int Open(sqlite3_vtab* aVtab, sqlite3_vtab_cursor** aCursor)
212 : {
213 52 : VirtualTableCursor* cursor = new VirtualTableCursor();
214 :
215 52 : *aCursor = reinterpret_cast<sqlite3_vtab_cursor*>(cursor);
216 :
217 52 : return SQLITE_OK;
218 : }
219 :
220 52 : int Close(sqlite3_vtab_cursor* aCursor)
221 : {
222 52 : VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
223 :
224 52 : delete cursor;
225 :
226 52 : return SQLITE_OK;
227 : }
228 :
229 52 : int Filter(sqlite3_vtab_cursor* aCursor, int aIdxNum, const char* aIdxStr,
230 : int aArgc, sqlite3_value** aArgv)
231 : {
232 52 : VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
233 :
234 52 : if(aArgc <= 0) {
235 0 : return SQLITE_OK;
236 : }
237 :
238 : nsDependentString path(
239 104 : reinterpret_cast<const PRUnichar*>(::sqlite3_value_text16(aArgv[0])));
240 :
241 52 : nsresult rv = cursor->Init(path);
242 52 : NS_ENSURE_SUCCESS(rv, SQLITE_ERROR);
243 :
244 52 : return SQLITE_OK;
245 : }
246 :
247 0 : int Next(sqlite3_vtab_cursor* aCursor)
248 : {
249 0 : VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
250 :
251 0 : nsresult rv = cursor->NextFile();
252 0 : NS_ENSURE_SUCCESS(rv, SQLITE_ERROR);
253 :
254 0 : return SQLITE_OK;
255 : }
256 :
257 52 : int Eof(sqlite3_vtab_cursor* aCursor)
258 : {
259 52 : VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
260 52 : return cursor->CurrentFileName().IsVoid() ? 1 : 0;
261 : }
262 :
263 0 : int Column(sqlite3_vtab_cursor* aCursor, sqlite3_context* aContext,
264 : int aColumnIndex)
265 : {
266 0 : VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
267 :
268 0 : switch (aColumnIndex) {
269 : // name
270 : case 0: {
271 0 : const nsString& name = cursor->CurrentFileName();
272 0 : sqlite3_result_text16(aContext, name.get(),
273 0 : name.Length() * sizeof(PRUnichar),
274 0 : SQLITE_TRANSIENT);
275 0 : break;
276 : }
277 :
278 : // path
279 : case 1: {
280 0 : const nsString& path = cursor->DirectoryPath();
281 0 : sqlite3_result_text16(aContext, path.get(),
282 0 : path.Length() * sizeof(PRUnichar),
283 0 : SQLITE_TRANSIENT);
284 0 : break;
285 : }
286 : default:
287 0 : NS_NOTREACHED("Unsupported column!");
288 : }
289 :
290 0 : return SQLITE_OK;
291 : }
292 :
293 0 : int RowId(sqlite3_vtab_cursor* aCursor, sqlite3_int64* aRowid)
294 : {
295 0 : VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
296 :
297 0 : *aRowid = cursor->RowId();
298 :
299 0 : return SQLITE_OK;
300 : }
301 :
302 : } // anonymous namespace
303 :
304 : namespace mozilla {
305 : namespace storage {
306 :
307 76 : int RegisterFileSystemModule(sqlite3* aDB, const char* aName)
308 : {
309 : static sqlite3_module module = {
310 : 1,
311 : Connect,
312 : Connect,
313 : BestIndex,
314 : Disconnect,
315 : Disconnect,
316 : Open,
317 : Close,
318 : Filter,
319 : Next,
320 : Eof,
321 : Column,
322 : RowId,
323 : nsnull,
324 : nsnull,
325 : nsnull,
326 : nsnull,
327 : nsnull,
328 : nsnull,
329 : nsnull
330 : };
331 :
332 76 : return sqlite3_create_module(aDB, aName, &module, nsnull);
333 : }
334 :
335 : } // namespace storage
336 : } // namespace mozilla
|