Backed out changeset afdd9e4681c1 (bug 1932137) by developer request. CLOSED TREE
[gecko.git] / storage / FileSystemModule.cpp
blobdf756838f8597dfaa7f0457aa7f00fc2f1efc7a4
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/. */
7 #include "FileSystemModule.h"
9 #include "sqlite3.h"
10 #include "nsComponentManagerUtils.h"
11 #include "nsString.h"
12 #include "nsIDirectoryEnumerator.h"
13 #include "nsIFile.h"
15 namespace {
17 struct VirtualTableCursorBase {
18 VirtualTableCursorBase() { memset(&mBase, 0, sizeof(mBase)); }
20 sqlite3_vtab_cursor mBase;
23 struct VirtualTableCursor : public VirtualTableCursorBase {
24 public:
25 VirtualTableCursor() : mRowId(-1) { mCurrentFileName.SetIsVoid(true); }
27 const nsString& DirectoryPath() const { return mDirectoryPath; }
29 const nsString& CurrentFileName() const { return mCurrentFileName; }
31 int64_t RowId() const { return mRowId; }
33 nsresult Init(const nsAString& aPath);
34 nsresult NextFile();
36 private:
37 nsCOMPtr<nsIDirectoryEnumerator> mEntries;
39 nsString mDirectoryPath;
40 nsString mCurrentFileName;
42 int64_t mRowId;
45 nsresult VirtualTableCursor::Init(const nsAString& aPath) {
46 nsCOMPtr<nsIFile> directory = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
47 NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
49 nsresult rv = directory->InitWithPath(aPath);
50 NS_ENSURE_SUCCESS(rv, rv);
52 rv = directory->GetPath(mDirectoryPath);
53 NS_ENSURE_SUCCESS(rv, rv);
55 rv = directory->GetDirectoryEntries(getter_AddRefs(mEntries));
56 NS_ENSURE_SUCCESS(rv, rv);
58 rv = NextFile();
59 NS_ENSURE_SUCCESS(rv, rv);
61 return NS_OK;
64 nsresult VirtualTableCursor::NextFile() {
65 bool hasMore;
66 nsresult rv = mEntries->HasMoreElements(&hasMore);
67 NS_ENSURE_SUCCESS(rv, rv);
69 if (!hasMore) {
70 mCurrentFileName.SetIsVoid(true);
71 return NS_OK;
74 nsCOMPtr<nsISupports> entry;
75 rv = mEntries->GetNext(getter_AddRefs(entry));
76 NS_ENSURE_SUCCESS(rv, rv);
78 nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
79 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
81 rv = file->GetLeafName(mCurrentFileName);
82 NS_ENSURE_SUCCESS(rv, rv);
84 mRowId++;
86 return NS_OK;
89 int Connect(sqlite3* aDB, void* aAux, int aArgc, const char* const* aArgv,
90 sqlite3_vtab** aVtab, char** aErr) {
91 static const char virtualTableSchema[] =
92 "CREATE TABLE fs ("
93 "name TEXT, "
94 "path TEXT"
95 ")";
97 int rc = sqlite3_declare_vtab(aDB, virtualTableSchema);
98 if (rc != SQLITE_OK) {
99 return rc;
102 sqlite3_vtab* vt = new sqlite3_vtab();
103 memset(vt, 0, sizeof(*vt));
105 *aVtab = vt;
107 return SQLITE_OK;
110 int Disconnect(sqlite3_vtab* aVtab) {
111 delete aVtab;
113 return SQLITE_OK;
116 int BestIndex(sqlite3_vtab* aVtab, sqlite3_index_info* aInfo) {
117 // Here we specify what index constraints we want to handle. That is, there
118 // might be some columns with particular constraints in which we can help
119 // SQLite narrow down the result set.
121 // For example, take the "path = x" where x is a directory. In this case,
122 // we can narrow our search to just this directory instead of the entire file
123 // system. This can be a significant optimization. So, we want to handle that
124 // constraint. To do so, we would look for two specific input conditions:
126 // 1. aInfo->aConstraint[i].iColumn == 1
127 // 2. aInfo->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_EQ
129 // The first states that the path column is being used in one of the input
130 // constraints and the second states that the constraint involves the equal
131 // operator.
133 // An even more specific search would be for name='xxx', in which case we
134 // can limit the search to a single file, if it exists.
136 // What we have to do here is look for all of our index searches and select
137 // the narrowest. We can only pick one, so obviously we want the one that
138 // is the most specific, which leads to the smallest result set.
140 for (int i = 0; i < aInfo->nConstraint; i++) {
141 if (aInfo->aConstraint[i].iColumn == 1 && aInfo->aConstraint[i].usable) {
142 if (aInfo->aConstraint[i].op & SQLITE_INDEX_CONSTRAINT_EQ) {
143 aInfo->aConstraintUsage[i].argvIndex = 1;
145 break;
148 // TODO: handle single files (constrained also by the name column)
151 return SQLITE_OK;
154 int Open(sqlite3_vtab* aVtab, sqlite3_vtab_cursor** aCursor) {
155 VirtualTableCursor* cursor = new VirtualTableCursor();
157 *aCursor = reinterpret_cast<sqlite3_vtab_cursor*>(cursor);
159 return SQLITE_OK;
162 int Close(sqlite3_vtab_cursor* aCursor) {
163 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
165 delete cursor;
167 return SQLITE_OK;
170 int Filter(sqlite3_vtab_cursor* aCursor, int aIdxNum, const char* aIdxStr,
171 int aArgc, sqlite3_value** aArgv) {
172 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
174 if (aArgc <= 0) {
175 return SQLITE_OK;
178 const char16_t* value =
179 static_cast<const char16_t*>(::sqlite3_value_text16(aArgv[0]));
181 nsDependentString path(value,
182 ::sqlite3_value_bytes16(aArgv[0]) / sizeof(char16_t));
184 nsresult rv = cursor->Init(path);
185 NS_ENSURE_SUCCESS(rv, SQLITE_ERROR);
187 return SQLITE_OK;
190 int Next(sqlite3_vtab_cursor* aCursor) {
191 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
193 nsresult rv = cursor->NextFile();
194 NS_ENSURE_SUCCESS(rv, SQLITE_ERROR);
196 return SQLITE_OK;
199 int Eof(sqlite3_vtab_cursor* aCursor) {
200 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
201 return cursor->CurrentFileName().IsVoid() ? 1 : 0;
204 int Column(sqlite3_vtab_cursor* aCursor, sqlite3_context* aContext,
205 int aColumnIndex) {
206 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
208 switch (aColumnIndex) {
209 // name
210 case 0: {
211 const nsString& name = cursor->CurrentFileName();
212 sqlite3_result_text16(aContext, name.get(),
213 name.Length() * sizeof(char16_t), SQLITE_TRANSIENT);
214 break;
217 // path
218 case 1: {
219 const nsString& path = cursor->DirectoryPath();
220 sqlite3_result_text16(aContext, path.get(),
221 path.Length() * sizeof(char16_t), SQLITE_TRANSIENT);
222 break;
224 default:
225 MOZ_ASSERT_UNREACHABLE("Unsupported column!");
228 return SQLITE_OK;
231 int RowId(sqlite3_vtab_cursor* aCursor, sqlite3_int64* aRowid) {
232 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
234 *aRowid = cursor->RowId();
236 return SQLITE_OK;
239 } // namespace
241 namespace mozilla {
242 namespace storage {
244 int RegisterFileSystemModule(sqlite3* aDB, const char* aName) {
245 static sqlite3_module module = {
246 1, Connect, Connect, BestIndex, Disconnect, Disconnect, Open,
247 Close, Filter, Next, Eof, Column, RowId, nullptr,
248 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
250 return sqlite3_create_module(aDB, aName, &module, nullptr);
253 } // namespace storage
254 } // namespace mozilla