Allow configuring VFS through DatabaseOptions
This introduces no behavior change for existing databases. This will allow selecting a VFS (https://www.sqlite.org/vfs.html) at the database level through DatabaseOptions. This is useful for use-cases where access to the file system needs to be mediated. Change-Id: Ib7406cee83887765ea5279b00ce7f2ead854d0f2 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6039445 Commit-Queue: Olivier Li <olivierli@chromium.org> Auto-Submit: Olivier Li <olivierli@chromium.org> Reviewed-by: Greg Thompson <grt@chromium.org> Cr-Commit-Position: refs/heads/main@{#1391162}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
559167194c
commit
e8a59fb145
@ -1936,7 +1936,7 @@ bool Database::OpenInternal(const std::string& db_file_path) {
|
||||
db_file_path);
|
||||
base::ElapsedTimer library_call_timer;
|
||||
sqlite_result_code = ToSqliteResultCode(sqlite3_open_v2(
|
||||
uri_file_path.c_str(), &db, open_flags, /*zVfs=*/nullptr));
|
||||
uri_file_path.c_str(), &db, open_flags, options_.vfs_name_discouraged));
|
||||
RecordTimingHistogram("Sql.Database.Success.SqliteOpenTime.",
|
||||
library_call_timer.Elapsed());
|
||||
}
|
||||
|
@ -208,6 +208,12 @@ struct COMPONENT_EXPORT(SQL) DatabaseOptions {
|
||||
// If this option is false, CREATE VIEW and DROP VIEW succeed, but SELECT
|
||||
// statements targeting views fail.
|
||||
bool enable_views_discouraged = false;
|
||||
|
||||
// If non-null, specifies the vfs implementation for the database to look for.
|
||||
// Most use-cases do not require the use of a
|
||||
// VFS(https://www.sqlite.org/vfs.html). This option should only be used when
|
||||
// there is a clear need for it.
|
||||
const char* vfs_name_discouraged = nullptr;
|
||||
};
|
||||
|
||||
// Holds database diagnostics in a structured format.
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@ -23,6 +24,7 @@
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/containers/contains.h"
|
||||
#include "base/containers/span.h"
|
||||
#include "base/files/file.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/files/scoped_temp_dir.h"
|
||||
@ -32,6 +34,7 @@
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/scoped_refptr.h"
|
||||
#include "base/ranges/algorithm.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/sequence_checker.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
@ -55,6 +58,7 @@
|
||||
#include "sql/transaction.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
|
||||
#include "third_party/sqlite/sqlite3.h"
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
@ -627,6 +631,87 @@ class LifeTracker {
|
||||
raw_ptr<bool> flag_ptr_ GUARDED_BY_CONTEXT(sequence_checker_);
|
||||
};
|
||||
|
||||
int TestVfsOpen(sqlite3_vfs* vfs,
|
||||
const char* full_path,
|
||||
sqlite3_file* result_file,
|
||||
int requested_flags,
|
||||
int* granted_flags) {
|
||||
uint64_t* call_count = reinterpret_cast<uint64_t*>(vfs->pAppData);
|
||||
++*call_count;
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
int TestVfsFullPathname(sqlite3_vfs* vfs,
|
||||
const char* file_path,
|
||||
int result_size,
|
||||
char* result) {
|
||||
uint64_t* call_count = reinterpret_cast<uint64_t*>(vfs->pAppData);
|
||||
++*call_count;
|
||||
|
||||
if (result_size < 0) {
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
|
||||
const size_t expected_result_size = result_size;
|
||||
base::cstring_view file_path_view(file_path);
|
||||
if (expected_result_size < file_path_view.size() + sizeof(*file_path)) {
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
|
||||
// `copy()` returns an output iterator just past the last char copied. Write
|
||||
// the string terminator to that location.
|
||||
*base::ranges::copy(file_path_view,
|
||||
base::span(result, expected_result_size).begin()) = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
TEST_P(SQLDatabaseTest, UseVfs) {
|
||||
uint64_t call_count = 0;
|
||||
|
||||
constexpr const char kVFSName[] = "test_vfs";
|
||||
static constexpr int kSqliteVfsApiVersion = 3;
|
||||
static constexpr int kSqliteMaxPathSize = 512;
|
||||
|
||||
sqlite3_vfs vfs{
|
||||
kSqliteVfsApiVersion,
|
||||
sizeof(sqlite3_vfs),
|
||||
kSqliteMaxPathSize,
|
||||
/*pNext=*/nullptr,
|
||||
kVFSName,
|
||||
// Provide pointer to `call_count` so it can be modified from within calls
|
||||
// to the VFS and used in test assertions.
|
||||
/*pAppData=*/&call_count,
|
||||
TestVfsOpen,
|
||||
/*xDelete*/ nullptr,
|
||||
/*xAccess*/ nullptr,
|
||||
TestVfsFullPathname,
|
||||
/*xDlOpen=*/nullptr,
|
||||
/*xDlError=*/nullptr,
|
||||
/*xDlSym=*/nullptr,
|
||||
/*xDlClose=*/nullptr,
|
||||
/*xRandomness*/ nullptr,
|
||||
/*xSleep*/ nullptr,
|
||||
/*xCurrentTime=*/nullptr,
|
||||
/*xGetLastError*/ nullptr,
|
||||
/*xCurrentTimeInt64*/ nullptr,
|
||||
/*xSetSystemCall=*/nullptr,
|
||||
/*xGetSystemCall=*/nullptr,
|
||||
/*xNextSystemCall=*/nullptr,
|
||||
};
|
||||
|
||||
sqlite3_vfs_register(&vfs, /*makeDflt=*/false);
|
||||
absl::Cleanup vfs_unregisterer = [&vfs]() { sqlite3_vfs_unregister(&vfs); };
|
||||
|
||||
DatabaseOptions options = GetDBOptions();
|
||||
options.vfs_name_discouraged = kVFSName;
|
||||
Database other_db(options);
|
||||
|
||||
// Since the vfs's Open function is not implemented `Open()` will fail.
|
||||
ASSERT_FALSE(other_db.Open(db_path_));
|
||||
|
||||
// Vfs implementation called twice, once for open and once for path name.
|
||||
ASSERT_EQ(call_count, 2ull);
|
||||
}
|
||||
|
||||
// base::BindRepeating() can curry arguments to be passed by const reference to
|
||||
// the callback function. If the error callback function calls
|
||||
// reset_error_callback() and the Database doesn't hang onto the callback while
|
||||
|
Reference in New Issue
Block a user