sqlite: split Sqlite.h

Summary: This diff splits Sqlite.h into smaller files so it's easier to change.

Reviewed By: xavierd

Differential Revision: D26656315

fbshipit-source-id: 8156b1d8feef47e5bad4ab66ad5f5f96cf32547e
This commit is contained in:
Zeyi (Rice) Fan 2021-03-15 11:57:58 -07:00 committed by Facebook GitHub Bot
parent 1651dd1898
commit 6162023789
10 changed files with 177 additions and 158 deletions

View File

@ -14,7 +14,7 @@
#include <thrift/lib/cpp2/protocol/Serializer.h>
#include <iostream>
#include "eden/fs/inodes/InodeNumber.h"
#include "eden/fs/sqlite/Sqlite.h"
#include "eden/fs/sqlite/SqliteStatement.h"
#include "eden/fs/store/StoreResult.h"
#include "eden/fs/utils/Bug.h"
#include "eden/fs/utils/PathFuncs.h"

View File

@ -10,7 +10,7 @@
#include "eden/fs/inodes/IOverlay.h"
#include "eden/fs/inodes/overlay/gen-cpp2/overlay_types.h"
#include "eden/fs/sqlite/Sqlite.h"
#include "eden/fs/sqlite/SqliteDatabase.h"
namespace facebook {
namespace eden {

View File

@ -12,7 +12,8 @@
#include <memory>
#include <fmt/format.h>
#include "eden/fs/sqlite/Sqlite.h"
#include "eden/fs/sqlite/SqliteDatabase.h"
#include "eden/fs/sqlite/SqliteStatement.h"
#include "eden/fs/utils/PathFuncs.h"
struct sqlite3;

View File

@ -13,7 +13,7 @@
#include "eden/fs/inodes/InodeNumber.h"
#include "eden/fs/inodes/overlay/gen-cpp2/overlay_types.h"
#include "eden/fs/model/Hash.h"
#include "eden/fs/sqlite/Sqlite.h"
#include "eden/fs/sqlite/SqliteDatabase.h"
#include "eden/fs/utils/DirType.h"
#include "eden/fs/utils/PathFuncs.h"

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#include "eden/fs/sqlite/SqliteDatabase.h"
#include <folly/logging/xlog.h>
#include "eden/fs/sqlite/SqliteStatement.h"
namespace facebook::eden {
void checkSqliteResult(sqlite3* db, int result) {
if (result == SQLITE_OK) {
return;
}
// Sometimes the db instance holds more useful context
if (db) {
auto error = fmt::format(
"sqlite error ({}): {} {}",
result,
sqlite3_errstr(result),
sqlite3_errmsg(db));
XLOG(DBG6) << error;
throw std::runtime_error(error);
} else {
// otherwise resort to a simpler number->string mapping
auto error =
fmt::format("sqlite error ({}): {}", result, sqlite3_errstr(result));
XLOG(DBG6) << error;
throw std::runtime_error(error);
}
}
SqliteDatabase::SqliteDatabase(const char* addr) {
sqlite3* db = nullptr;
auto result = sqlite3_open(addr, &db);
if (result != SQLITE_OK) {
// sqlite3_close handles nullptr fine
// @lint-ignore CLANGTIDY
sqlite3_close(db);
checkSqliteResult(nullptr, result);
}
db_ = db;
}
void SqliteDatabase::close() {
auto db = db_.wlock();
if (*db) {
sqlite3_close(*db);
*db = nullptr;
}
}
SqliteDatabase::~SqliteDatabase() {
close();
}
SqliteDatabase::Connection SqliteDatabase::lock() {
return db_.wlock();
}
void SqliteDatabase::transaction(const std::function<void(Connection&)>& func) {
auto conn = lock();
try {
SqliteStatement(conn, "BEGIN TRANSACTION").step();
func(conn);
SqliteStatement(conn, "COMMIT").step();
} catch (const std::exception& ex) {
SqliteStatement(conn, "ROLLBACK").step();
XLOG(WARN) << "SQLite transaction failed: " << ex.what();
throw;
}
}
} // namespace facebook::eden

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#pragma once
#include <folly/Synchronized.h>
#include <sqlite3.h>
#include "eden/fs/utils/PathFuncs.h"
namespace facebook::eden {
// Given a sqlite result code, if the result was not successful
// (SQLITE_OK), format an error message and throw an exception.
void checkSqliteResult(sqlite3* db, int result);
/** A helper class for managing a handle to a sqlite database. */
class SqliteDatabase {
public:
using Connection = folly::Synchronized<sqlite3*>::LockedPtr;
constexpr static struct InMemory {
} inMemory{};
/** Open a handle to the database at the specified path.
* Will throw an exception if the database fails to open.
* The database will be created if it didn't already exist.
*/
explicit SqliteDatabase(AbsolutePathPiece path)
: SqliteDatabase(path.copy().c_str()) {}
/**
* Create a SQLite database in memory. It will throw an exception if the
* database fails to open. This should be only used in testing.
*/
explicit SqliteDatabase(InMemory) : SqliteDatabase(":memory:") {}
// Not copyable...
SqliteDatabase(const SqliteDatabase&) = delete;
SqliteDatabase& operator=(const SqliteDatabase&) = delete;
// But movable.
SqliteDatabase(SqliteDatabase&&) = default;
SqliteDatabase& operator=(SqliteDatabase&&) = default;
/** Close the handle.
* This will happen implicitly at destruction but is provided
* here for convenience. */
void close();
~SqliteDatabase();
/** Obtain a locked database pointer suitable for passing
* to the SqliteStatement class. */
Connection lock();
/**
* Executes a SQLite transaction. If the lambda body throws any error, the
* transaction will be rolled back. This function returns a boolean to
* indicate whether the transaction is successfully committed.
*
* Example usage:
*
* ```
* db_->transaction([](auto& conn) {
* SqliteStatement(conn, "SELECT * ...").step();
* SqliteStatement(conn, "INSERT INTO ...").step();
* };
* ```
*/
void transaction(const std::function<void(Connection&)>& func);
private:
explicit SqliteDatabase(const char* address);
folly::Synchronized<sqlite3*> db_{nullptr};
};
} // namespace facebook::eden

View File

@ -5,81 +5,11 @@
* GNU General Public License version 2.
*/
#include "eden/fs/sqlite/Sqlite.h"
#include "eden/fs/sqlite/SqliteStatement.h"
#include <folly/logging/xlog.h>
using folly::StringPiece;
using folly::Synchronized;
using folly::to;
using std::string;
namespace facebook {
namespace eden {
// Given a sqlite result code, if the result was not successful
// (SQLITE_OK), format an error message and throw an exception.
void checkSqliteResult(sqlite3* db, int result) {
if (result == SQLITE_OK) {
return;
}
// Sometimes the db instance holds more useful context
if (db) {
auto error = fmt::format(
"sqlite error ({}): {} {}",
result,
sqlite3_errstr(result),
sqlite3_errmsg(db));
XLOG(DBG6) << error;
throw std::runtime_error(error);
} else {
auto error =
fmt::format("sqlite error ({}): {}", result, sqlite3_errstr(result));
XLOG(DBG6) << error;
// otherwise resort to a simpler number->string mapping
throw std::runtime_error(error);
}
}
SqliteDatabase::SqliteDatabase(const char* addr) {
sqlite3* db = nullptr;
auto result = sqlite3_open(addr, &db);
if (result != SQLITE_OK) {
sqlite3_close(db);
checkSqliteResult(nullptr, result);
}
db_ = db;
}
void SqliteDatabase::close() {
auto db = db_.wlock();
if (*db) {
sqlite3_close(*db);
*db = nullptr;
}
}
SqliteDatabase::~SqliteDatabase() {
close();
}
Synchronized<sqlite3*>::LockedPtr SqliteDatabase::lock() {
return db_.wlock();
}
void SqliteDatabase::transaction(const std::function<void(Connection&)>& func) {
auto conn = lock();
try {
SqliteStatement(conn, "BEGIN TRANSACTION").step();
func(conn);
SqliteStatement(conn, "COMMIT").step();
} catch (const std::exception& ex) {
SqliteStatement(conn, "ROLLBACK").step();
XLOG(WARN) << "SQLite transaction failed: " << ex.what();
throw;
}
}
#include "eden/fs/sqlite/SqliteDatabase.h"
namespace facebook::eden {
SqliteStatement::SqliteStatement(
folly::Synchronized<sqlite3*>::LockedPtr& db,
folly::StringPiece query)
@ -148,19 +78,18 @@ void SqliteStatement::reset() {
checkSqliteResult(db_, sqlite3_clear_bindings(stmt_));
}
StringPiece SqliteStatement::columnBlob(size_t colNo) const {
folly::StringPiece SqliteStatement::columnBlob(size_t colNo) const {
auto col = unsignedNoToInt(colNo);
return StringPiece(
return folly::StringPiece(
reinterpret_cast<const char*>(sqlite3_column_blob(stmt_, col)),
sqlite3_column_bytes(stmt_, col));
}
uint64_t SqliteStatement::columnUint64(size_t colNo) const {
return sqlite3_column_int64(stmt_, folly::to_signed(colNo));
return sqlite3_column_int64(stmt_, unsignedNoToInt(colNo));
}
SqliteStatement::~SqliteStatement() {
sqlite3_finalize(stmt_);
}
} // namespace eden
} // namespace facebook
}; // namespace facebook::eden

View File

@ -6,80 +6,12 @@
*/
#pragma once
#include <folly/String.h>
#include <folly/Synchronized.h>
#include <sqlite3.h>
#include "eden/fs/utils/PathFuncs.h"
namespace facebook {
namespace eden {
// Given a sqlite result code, if the result was not successful
// (SQLITE_OK), format an error message and throw an exception.
void checkSqliteResult(sqlite3* db, int result);
/** A helper class for managing a handle to a sqlite database. */
class SqliteDatabase {
public:
using Connection = folly::Synchronized<sqlite3*>::LockedPtr;
constexpr static struct InMemory {
} inMemory{};
/** Open a handle to the database at the specified path.
* Will throw an exception if the database fails to open.
* The database will be created if it didn't already exist.
*/
explicit SqliteDatabase(AbsolutePathPiece path)
: SqliteDatabase(path.copy().c_str()) {}
/**
* Create a SQLite database in memory. It will throw an exception if the
* database fails to open. This should be only used in testing.
*/
explicit SqliteDatabase(InMemory) : SqliteDatabase(":memory:") {}
// Not copyable...
SqliteDatabase(const SqliteDatabase&) = delete;
SqliteDatabase& operator=(const SqliteDatabase&) = delete;
// But movable.
SqliteDatabase(SqliteDatabase&&) = default;
SqliteDatabase& operator=(SqliteDatabase&&) = default;
/** Close the handle.
* This will happen implicitly at destruction but is provided
* here for convenience. */
void close();
~SqliteDatabase();
/** Obtain a locked database pointer suitable for passing
* to the SqliteStatement class. */
Connection lock();
/**
* Executes a SQLite transaction. If the lambda body throws any error, the
* transaction will be rolled back. This function re-throws any exception
* thrown by the closure.
*
* Example usage:
*
* ```
* db_->transaction([](auto& conn) {
* SqliteStatement(conn, "SELECT * ...").step();
* SqliteStatement(conn, "INSERT INTO ...").step();
* };
* ```
*/
void transaction(const std::function<void(Connection&)>& func);
private:
explicit SqliteDatabase(const char* address);
folly::Synchronized<sqlite3*> db_{nullptr};
};
namespace facebook::eden {
/** Represents the sqlite vm that will execute a SQL statement.
* The class can only be created while holding a lock on the SqliteDatabase;
* this is enforced by the compiler. However, the statement class
@ -91,7 +23,9 @@ class SqliteDatabase {
class SqliteStatement {
public:
/** Prepare to execute the statement described by the `query` parameter */
SqliteStatement(SqliteDatabase::Connection& db, folly::StringPiece query);
SqliteStatement(
folly::Synchronized<sqlite3*>::LockedPtr& db,
folly::StringPiece query);
/** Join together the arguments as a single query string and prepare a
* statement to execute them.
@ -101,7 +35,7 @@ class SqliteStatement {
* in the cases where the query string is known at compile time. */
template <typename Arg1, typename Arg2, typename... Args>
SqliteStatement(
SqliteDatabase::Connection& db,
folly::Synchronized<sqlite3*>::LockedPtr& db,
Arg1&& first,
Arg2&& second,
Args&&... args)
@ -179,5 +113,4 @@ class SqliteStatement {
/** The statement handle */
sqlite3_stmt* stmt_;
};
} // namespace eden
} // namespace facebook
} // namespace facebook::eden

View File

@ -11,7 +11,7 @@
#include <folly/container/Array.h>
#include <folly/logging/xlog.h>
#include "eden/fs/sqlite/Sqlite.h"
#include "eden/fs/sqlite/SqliteStatement.h"
#include "eden/fs/store/StoreResult.h"
namespace facebook {

View File

@ -7,7 +7,7 @@
#pragma once
#include <folly/Synchronized.h>
#include "eden/fs/sqlite/Sqlite.h"
#include "eden/fs/sqlite/SqliteDatabase.h"
#include "eden/fs/store/LocalStore.h"
namespace facebook {