1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/storage/src/FileSystemModule.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,304 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "FileSystemModule.h" 1.11 + 1.12 +#include "sqlite3.h" 1.13 +#include "nsString.h" 1.14 +#include "nsISimpleEnumerator.h" 1.15 +#include "nsIFile.h" 1.16 + 1.17 +namespace { 1.18 + 1.19 +struct VirtualTableCursorBase 1.20 +{ 1.21 + VirtualTableCursorBase() 1.22 + { 1.23 + memset(&mBase, 0, sizeof(mBase)); 1.24 + } 1.25 + 1.26 + sqlite3_vtab_cursor mBase; 1.27 +}; 1.28 + 1.29 +struct VirtualTableCursor : public VirtualTableCursorBase 1.30 +{ 1.31 +public: 1.32 + VirtualTableCursor() 1.33 + : mRowId(-1) 1.34 + { 1.35 + mCurrentFileName.SetIsVoid(true); 1.36 + } 1.37 + 1.38 + const nsString& DirectoryPath() const 1.39 + { 1.40 + return mDirectoryPath; 1.41 + } 1.42 + 1.43 + const nsString& CurrentFileName() const 1.44 + { 1.45 + return mCurrentFileName; 1.46 + } 1.47 + 1.48 + int64_t RowId() const 1.49 + { 1.50 + return mRowId; 1.51 + } 1.52 + 1.53 + nsresult Init(const nsAString& aPath); 1.54 + nsresult NextFile(); 1.55 + 1.56 +private: 1.57 + nsCOMPtr<nsISimpleEnumerator> mEntries; 1.58 + 1.59 + nsString mDirectoryPath; 1.60 + nsString mCurrentFileName; 1.61 + 1.62 + int64_t mRowId; 1.63 +}; 1.64 + 1.65 +nsresult 1.66 +VirtualTableCursor::Init(const nsAString& aPath) 1.67 +{ 1.68 + nsCOMPtr<nsIFile> directory = 1.69 + do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); 1.70 + NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE); 1.71 + 1.72 + nsresult rv = directory->InitWithPath(aPath); 1.73 + NS_ENSURE_SUCCESS(rv, rv); 1.74 + 1.75 + rv = directory->GetPath(mDirectoryPath); 1.76 + NS_ENSURE_SUCCESS(rv, rv); 1.77 + 1.78 + rv = directory->GetDirectoryEntries(getter_AddRefs(mEntries)); 1.79 + NS_ENSURE_SUCCESS(rv, rv); 1.80 + 1.81 + rv = NextFile(); 1.82 + NS_ENSURE_SUCCESS(rv, rv); 1.83 + 1.84 + return NS_OK; 1.85 +} 1.86 + 1.87 +nsresult 1.88 +VirtualTableCursor::NextFile() 1.89 +{ 1.90 + bool hasMore; 1.91 + nsresult rv = mEntries->HasMoreElements(&hasMore); 1.92 + NS_ENSURE_SUCCESS(rv, rv); 1.93 + 1.94 + if (!hasMore) { 1.95 + mCurrentFileName.SetIsVoid(true); 1.96 + return NS_OK; 1.97 + } 1.98 + 1.99 + nsCOMPtr<nsISupports> entry; 1.100 + rv = mEntries->GetNext(getter_AddRefs(entry)); 1.101 + NS_ENSURE_SUCCESS(rv, rv); 1.102 + 1.103 + nsCOMPtr<nsIFile> file = do_QueryInterface(entry); 1.104 + NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); 1.105 + 1.106 + rv = file->GetLeafName(mCurrentFileName); 1.107 + NS_ENSURE_SUCCESS(rv, rv); 1.108 + 1.109 + mRowId++; 1.110 + 1.111 + return NS_OK; 1.112 +} 1.113 + 1.114 +int Connect(sqlite3* aDB, void* aAux, int aArgc, const char* const* aArgv, 1.115 + sqlite3_vtab** aVtab, char** aErr) 1.116 +{ 1.117 + static const char virtualTableSchema[] = 1.118 + "CREATE TABLE fs (" 1.119 + "name TEXT, " 1.120 + "path TEXT" 1.121 + ")"; 1.122 + 1.123 + int rc = sqlite3_declare_vtab(aDB, virtualTableSchema); 1.124 + if (rc != SQLITE_OK) { 1.125 + return rc; 1.126 + } 1.127 + 1.128 + sqlite3_vtab* vt = new sqlite3_vtab(); 1.129 + memset(vt, 0, sizeof(*vt)); 1.130 + 1.131 + *aVtab = vt; 1.132 + 1.133 + return SQLITE_OK; 1.134 +} 1.135 + 1.136 +int Disconnect(sqlite3_vtab* aVtab ) 1.137 +{ 1.138 + delete aVtab; 1.139 + 1.140 + return SQLITE_OK; 1.141 +} 1.142 + 1.143 +int BestIndex(sqlite3_vtab* aVtab, sqlite3_index_info* aInfo) 1.144 +{ 1.145 + // Here we specify what index constraints we want to handle. That is, there 1.146 + // might be some columns with particular constraints in which we can help 1.147 + // SQLite narrow down the result set. 1.148 + // 1.149 + // For example, take the "path = x" where x is a directory. In this case, 1.150 + // we can narrow our search to just this directory instead of the entire file 1.151 + // system. This can be a significant optimization. So, we want to handle that 1.152 + // constraint. To do so, we would look for two specific input conditions: 1.153 + // 1.154 + // 1. aInfo->aConstraint[i].iColumn == 1 1.155 + // 2. aInfo->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_EQ 1.156 + // 1.157 + // The first states that the path column is being used in one of the input 1.158 + // constraints and the second states that the constraint involves the equal 1.159 + // operator. 1.160 + // 1.161 + // An even more specific search would be for name='xxx', in which case we 1.162 + // can limit the search to a single file, if it exists. 1.163 + // 1.164 + // What we have to do here is look for all of our index searches and select 1.165 + // the narrowest. We can only pick one, so obviously we want the one that 1.166 + // is the most specific, which leads to the smallest result set. 1.167 + 1.168 + for(int i = 0; i < aInfo->nConstraint; i++) { 1.169 + if (aInfo->aConstraint[i].iColumn == 1 && aInfo->aConstraint[i].usable) { 1.170 + if (aInfo->aConstraint[i].op & SQLITE_INDEX_CONSTRAINT_EQ) { 1.171 + aInfo->aConstraintUsage[i].argvIndex = 1; 1.172 + } 1.173 + break; 1.174 + } 1.175 + 1.176 + // TODO: handle single files (constrained also by the name column) 1.177 + } 1.178 + 1.179 + return SQLITE_OK; 1.180 +} 1.181 + 1.182 +int Open(sqlite3_vtab* aVtab, sqlite3_vtab_cursor** aCursor) 1.183 +{ 1.184 + VirtualTableCursor* cursor = new VirtualTableCursor(); 1.185 + 1.186 + *aCursor = reinterpret_cast<sqlite3_vtab_cursor*>(cursor); 1.187 + 1.188 + return SQLITE_OK; 1.189 +} 1.190 + 1.191 +int Close(sqlite3_vtab_cursor* aCursor) 1.192 +{ 1.193 + VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); 1.194 + 1.195 + delete cursor; 1.196 + 1.197 + return SQLITE_OK; 1.198 +} 1.199 + 1.200 +int Filter(sqlite3_vtab_cursor* aCursor, int aIdxNum, const char* aIdxStr, 1.201 + int aArgc, sqlite3_value** aArgv) 1.202 +{ 1.203 + VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); 1.204 + 1.205 + if(aArgc <= 0) { 1.206 + return SQLITE_OK; 1.207 + } 1.208 + 1.209 + nsDependentString path( 1.210 + reinterpret_cast<const char16_t*>(::sqlite3_value_text16(aArgv[0]))); 1.211 + 1.212 + nsresult rv = cursor->Init(path); 1.213 + NS_ENSURE_SUCCESS(rv, SQLITE_ERROR); 1.214 + 1.215 + return SQLITE_OK; 1.216 +} 1.217 + 1.218 +int Next(sqlite3_vtab_cursor* aCursor) 1.219 +{ 1.220 + VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); 1.221 + 1.222 + nsresult rv = cursor->NextFile(); 1.223 + NS_ENSURE_SUCCESS(rv, SQLITE_ERROR); 1.224 + 1.225 + return SQLITE_OK; 1.226 +} 1.227 + 1.228 +int Eof(sqlite3_vtab_cursor* aCursor) 1.229 +{ 1.230 + VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); 1.231 + return cursor->CurrentFileName().IsVoid() ? 1 : 0; 1.232 +} 1.233 + 1.234 +int Column(sqlite3_vtab_cursor* aCursor, sqlite3_context* aContext, 1.235 + int aColumnIndex) 1.236 +{ 1.237 + VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); 1.238 + 1.239 + switch (aColumnIndex) { 1.240 + // name 1.241 + case 0: { 1.242 + const nsString& name = cursor->CurrentFileName(); 1.243 + sqlite3_result_text16(aContext, name.get(), 1.244 + name.Length() * sizeof(char16_t), 1.245 + SQLITE_TRANSIENT); 1.246 + break; 1.247 + } 1.248 + 1.249 + // path 1.250 + case 1: { 1.251 + const nsString& path = cursor->DirectoryPath(); 1.252 + sqlite3_result_text16(aContext, path.get(), 1.253 + path.Length() * sizeof(char16_t), 1.254 + SQLITE_TRANSIENT); 1.255 + break; 1.256 + } 1.257 + default: 1.258 + NS_NOTREACHED("Unsupported column!"); 1.259 + } 1.260 + 1.261 + return SQLITE_OK; 1.262 +} 1.263 + 1.264 +int RowId(sqlite3_vtab_cursor* aCursor, sqlite3_int64* aRowid) 1.265 +{ 1.266 + VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor); 1.267 + 1.268 + *aRowid = cursor->RowId(); 1.269 + 1.270 + return SQLITE_OK; 1.271 +} 1.272 + 1.273 +} // anonymous namespace 1.274 + 1.275 +namespace mozilla { 1.276 +namespace storage { 1.277 + 1.278 +int RegisterFileSystemModule(sqlite3* aDB, const char* aName) 1.279 +{ 1.280 + static sqlite3_module module = { 1.281 + 1, 1.282 + Connect, 1.283 + Connect, 1.284 + BestIndex, 1.285 + Disconnect, 1.286 + Disconnect, 1.287 + Open, 1.288 + Close, 1.289 + Filter, 1.290 + Next, 1.291 + Eof, 1.292 + Column, 1.293 + RowId, 1.294 + nullptr, 1.295 + nullptr, 1.296 + nullptr, 1.297 + nullptr, 1.298 + nullptr, 1.299 + nullptr, 1.300 + nullptr 1.301 + }; 1.302 + 1.303 + return sqlite3_create_module(aDB, aName, &module, nullptr); 1.304 +} 1.305 + 1.306 +} // namespace storage 1.307 +} // namespace mozilla