2011-12-16 11:34:24 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
|
|
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
2012-05-21 15:12:37 +04:00
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2011-12-16 11:34:24 +04:00
|
|
|
|
|
|
|
#include "FileSystemModule.h"
|
|
|
|
|
|
|
|
#include "sqlite3.h"
|
|
|
|
#include "nsString.h"
|
2018-08-26 14:15:00 +03:00
|
|
|
#include "nsIDirectoryEnumerator.h"
|
2011-12-16 11:34:24 +04:00
|
|
|
#include "nsIFile.h"
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
struct VirtualTableCursorBase {
|
|
|
|
VirtualTableCursorBase() { memset(&mBase, 0, sizeof(mBase)); }
|
|
|
|
|
|
|
|
sqlite3_vtab_cursor mBase;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct VirtualTableCursor : public VirtualTableCursorBase {
|
|
|
|
public:
|
|
|
|
VirtualTableCursor() : mRowId(-1) { mCurrentFileName.SetIsVoid(true); }
|
|
|
|
|
|
|
|
const nsString& DirectoryPath() const { return mDirectoryPath; }
|
|
|
|
|
|
|
|
const nsString& CurrentFileName() const { return mCurrentFileName; }
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
int64_t RowId() const { return mRowId; }
|
2011-12-16 11:34:24 +04:00
|
|
|
|
|
|
|
nsresult Init(const nsAString& aPath);
|
|
|
|
nsresult NextFile();
|
|
|
|
|
|
|
|
private:
|
2018-08-26 14:15:00 +03:00
|
|
|
nsCOMPtr<nsIDirectoryEnumerator> mEntries;
|
2011-12-16 11:34:24 +04:00
|
|
|
|
|
|
|
nsString mDirectoryPath;
|
|
|
|
nsString mCurrentFileName;
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
int64_t mRowId;
|
2011-12-16 11:34:24 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
nsresult VirtualTableCursor::Init(const nsAString& aPath) {
|
|
|
|
nsCOMPtr<nsIFile> directory = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
|
|
|
|
NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
|
|
|
|
|
|
|
|
nsresult rv = directory->InitWithPath(aPath);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
rv = directory->GetPath(mDirectoryPath);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
rv = directory->GetDirectoryEntries(getter_AddRefs(mEntries));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
rv = NextFile();
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult VirtualTableCursor::NextFile() {
|
|
|
|
bool hasMore;
|
|
|
|
nsresult rv = mEntries->HasMoreElements(&hasMore);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
if (!hasMore) {
|
|
|
|
mCurrentFileName.SetIsVoid(true);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsISupports> entry;
|
|
|
|
rv = mEntries->GetNext(getter_AddRefs(entry));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
|
|
|
|
NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
|
|
|
|
|
|
|
|
rv = file->GetLeafName(mCurrentFileName);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
mRowId++;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Connect(sqlite3* aDB, void* aAux, int aArgc, const char* const* aArgv,
|
|
|
|
sqlite3_vtab** aVtab, char** aErr) {
|
|
|
|
static const char virtualTableSchema[] =
|
|
|
|
"CREATE TABLE fs ("
|
|
|
|
"name TEXT, "
|
|
|
|
"path TEXT"
|
|
|
|
")";
|
|
|
|
|
|
|
|
int rc = sqlite3_declare_vtab(aDB, virtualTableSchema);
|
|
|
|
if (rc != SQLITE_OK) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
sqlite3_vtab* vt = new sqlite3_vtab();
|
|
|
|
memset(vt, 0, sizeof(*vt));
|
|
|
|
|
|
|
|
*aVtab = vt;
|
|
|
|
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Disconnect(sqlite3_vtab* aVtab) {
|
|
|
|
delete aVtab;
|
|
|
|
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int BestIndex(sqlite3_vtab* aVtab, sqlite3_index_info* aInfo) {
|
|
|
|
// Here we specify what index constraints we want to handle. That is, there
|
|
|
|
// might be some columns with particular constraints in which we can help
|
|
|
|
// SQLite narrow down the result set.
|
|
|
|
//
|
|
|
|
// For example, take the "path = x" where x is a directory. In this case,
|
|
|
|
// we can narrow our search to just this directory instead of the entire file
|
|
|
|
// system. This can be a significant optimization. So, we want to handle that
|
|
|
|
// constraint. To do so, we would look for two specific input conditions:
|
|
|
|
//
|
|
|
|
// 1. aInfo->aConstraint[i].iColumn == 1
|
|
|
|
// 2. aInfo->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_EQ
|
|
|
|
//
|
|
|
|
// The first states that the path column is being used in one of the input
|
|
|
|
// constraints and the second states that the constraint involves the equal
|
|
|
|
// operator.
|
|
|
|
//
|
|
|
|
// An even more specific search would be for name='xxx', in which case we
|
|
|
|
// can limit the search to a single file, if it exists.
|
|
|
|
//
|
|
|
|
// What we have to do here is look for all of our index searches and select
|
|
|
|
// the narrowest. We can only pick one, so obviously we want the one that
|
|
|
|
// is the most specific, which leads to the smallest result set.
|
|
|
|
|
|
|
|
for (int i = 0; i < aInfo->nConstraint; i++) {
|
|
|
|
if (aInfo->aConstraint[i].iColumn == 1 && aInfo->aConstraint[i].usable) {
|
|
|
|
if (aInfo->aConstraint[i].op & SQLITE_INDEX_CONSTRAINT_EQ) {
|
|
|
|
aInfo->aConstraintUsage[i].argvIndex = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: handle single files (constrained also by the name column)
|
|
|
|
}
|
|
|
|
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Open(sqlite3_vtab* aVtab, sqlite3_vtab_cursor** aCursor) {
|
|
|
|
VirtualTableCursor* cursor = new VirtualTableCursor();
|
|
|
|
|
|
|
|
*aCursor = reinterpret_cast<sqlite3_vtab_cursor*>(cursor);
|
|
|
|
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Close(sqlite3_vtab_cursor* aCursor) {
|
|
|
|
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
|
|
|
|
|
|
|
delete cursor;
|
|
|
|
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Filter(sqlite3_vtab_cursor* aCursor, int aIdxNum, const char* aIdxStr,
|
|
|
|
int aArgc, sqlite3_value** aArgv) {
|
|
|
|
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
|
|
|
|
|
|
|
if (aArgc <= 0) {
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
Bug 1543295 - Pass the text length when creating an `nsString` from a SQLite text result. r=mak
This commit updates mozStorage to always:
* Pass the length, using sqlite3_{column, value}_bytes16, when
creating an nsDependentString from a pointer.
* Call sqlite3_{column, value}_bytes{16} after
sqlite3_{column, value}_{text, blob, text16}, per the
recommendation in https://www.sqlite.org/c3ref/column_blob.html.
Some callers did this before, or in unclear order, since C++ doesn't
specify one for evaluating function arguments.
* Pass the byte length to sqlite3_result_text16.
Differential Revision: https://phabricator.services.mozilla.com/D26848
--HG--
extra : moz-landing-system : lando
2019-04-11 06:19:39 +03:00
|
|
|
const char16_t* value =
|
|
|
|
static_cast<const char16_t*>(::sqlite3_value_text16(aArgv[0]));
|
|
|
|
|
|
|
|
nsDependentString path(value,
|
|
|
|
::sqlite3_value_bytes16(aArgv[0]) / sizeof(char16_t));
|
2011-12-16 11:34:24 +04:00
|
|
|
|
|
|
|
nsresult rv = cursor->Init(path);
|
|
|
|
NS_ENSURE_SUCCESS(rv, SQLITE_ERROR);
|
|
|
|
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Next(sqlite3_vtab_cursor* aCursor) {
|
|
|
|
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
|
|
|
|
|
|
|
nsresult rv = cursor->NextFile();
|
|
|
|
NS_ENSURE_SUCCESS(rv, SQLITE_ERROR);
|
|
|
|
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Eof(sqlite3_vtab_cursor* aCursor) {
|
|
|
|
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
|
|
|
return cursor->CurrentFileName().IsVoid() ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Column(sqlite3_vtab_cursor* aCursor, sqlite3_context* aContext,
|
|
|
|
int aColumnIndex) {
|
|
|
|
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
|
|
|
|
|
|
|
switch (aColumnIndex) {
|
|
|
|
// name
|
|
|
|
case 0: {
|
|
|
|
const nsString& name = cursor->CurrentFileName();
|
|
|
|
sqlite3_result_text16(aContext, name.get(),
|
2014-01-04 19:02:17 +04:00
|
|
|
name.Length() * sizeof(char16_t), SQLITE_TRANSIENT);
|
2011-12-16 11:34:24 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// path
|
|
|
|
case 1: {
|
|
|
|
const nsString& path = cursor->DirectoryPath();
|
|
|
|
sqlite3_result_text16(aContext, path.get(),
|
2014-01-04 19:02:17 +04:00
|
|
|
path.Length() * sizeof(char16_t), SQLITE_TRANSIENT);
|
2011-12-16 11:34:24 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2018-06-18 08:43:11 +03:00
|
|
|
MOZ_ASSERT_UNREACHABLE("Unsupported column!");
|
2011-12-16 11:34:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int RowId(sqlite3_vtab_cursor* aCursor, sqlite3_int64* aRowid) {
|
|
|
|
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
|
|
|
|
|
|
|
*aRowid = cursor->RowId();
|
|
|
|
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
2015-07-13 18:25:42 +03:00
|
|
|
} // namespace
|
2011-12-16 11:34:24 +04:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace storage {
|
|
|
|
|
|
|
|
int RegisterFileSystemModule(sqlite3* aDB, const char* aName) {
|
|
|
|
static sqlite3_module module = {
|
|
|
|
1, Connect, Connect, BestIndex, Disconnect, Disconnect, Open,
|
2012-07-30 18:20:58 +04:00
|
|
|
Close, Filter, Next, Eof, Column, RowId, nullptr,
|
|
|
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
|
2011-12-16 11:34:24 +04:00
|
|
|
|
2012-07-30 18:20:58 +04:00
|
|
|
return sqlite3_create_module(aDB, aName, &module, nullptr);
|
2011-12-16 11:34:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace storage
|
|
|
|
} // namespace mozilla
|