Skip to content
This repository has been archived by the owner on Jan 3, 2024. It is now read-only.

Commit

Permalink
rgw/sfs: sqlite_modern_cpp blobs, users and objects changes
Browse files Browse the repository at this point in the history
Adds another step on moving the sqlite code to sqlite_modern_cpp.

Adds the code to store/retrieve ceph types as blobs.
Adds a new mechanism to declare when a type that has encode and decode
functions needs to be stored as a blob.

In order to add a new type, simple add the type to the following tuple.
```c++
using BlobTypes = std::tuple<
    rgw::sal::Attrs, ACLOwner, rgw_placement_rule,
    std::map<std::string, RGWAccessKey>, std::map<std::string, RGWSubUser>,
    RGWUserCaps, std::list<std::string>, std::map<int, std::string>,
    RGWQuotaInfo, std::set<std::string>, RGWBucketWebsiteConf,
    std::map<std::string, uint32_t>, RGWObjectLock, rgw_sync_policy_info>
```

Adds a new header (`dbabpi_type_wrapper.h`) file that should be included when declaring type
bindings for `sqlite_modern_cpp`.
We need this to avoid circular dependencies with the `bind_col_in_db`
function from `sqlite_moden_cpp`.
This (from the main `sqlite_modern_cpp.h` file):
```c++
	template<typename T> database_binder &operator<<(database_binder& db, index_binding_helper<T> val) {
		db._next_index(); --db._inx;
		int result = bind_col_in_db(db._stmt.get(), val.index, std::forward<T>(val.value));
		if(result != SQLITE_OK)
			exceptions::throw_sqlite_error(result, db.sql(), sqlite3_errmsg(db._db.get()));
		return db;
	}
```
calls `bind_col_in_db`, but the function specialisation needs to be
declared first.
So, when declaring type bindings we should only include
`sqlite_modern_cpp/type_wrapper.h`

Changes all BLOB types in Users to the binding types.
Changes all functions in `sqlite_users` to use `sqlite_modern_cpp`.
Deletes the conversions files for Users. (conversion is still needed
because we need to create the `RGWUserInfo` object needed for SAL
layer, the conversion required is only db->SAL )

Changes all funcions in `sqlite_objects` to use `sqlite_modern_cpp`

Signed-off-by: Xavi Garcia <[email protected]>
  • Loading branch information
0xavi0 committed Nov 14, 2023
1 parent 0e62a28 commit 2a30782
Show file tree
Hide file tree
Showing 15 changed files with 397 additions and 281 deletions.
1 change: 0 additions & 1 deletion src/rgw/driver/sfs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ set(sfs_srcs
sqlite/sqlite_versioned_objects.cc
sqlite/sqlite_lifecycle.cc
sqlite/sqlite_multipart.cc
sqlite/users/users_conversions.cc
sqlite/buckets/bucket_conversions.cc
sqlite/dbconn.cc
sqlite/errors.cc
Expand Down
2 changes: 1 addition & 1 deletion src/rgw/driver/sfs/object_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#if FMT_VERSION >= 90000
#include <fmt/ostream.h>
#endif
#include "sqlite/dbapi.h"
#include "sqlite/dbapi_type_wrapper.h"

namespace rgw::sal::sfs {

Expand Down
93 changes: 83 additions & 10 deletions src/rgw/driver/sfs/sqlite/bindings/blob.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,44 @@
*/
#pragma once

#include <tuple>
#include <type_traits>

#include "rgw/driver/sfs/sqlite/conversion_utils.h"
// we need to include dbapi_type_wrapper.h only because including dbapi.h
// creates circular dependencies
#include "rgw/driver/sfs/sqlite/dbapi_type_wrapper.h"
#include "rgw/driver/sfs/sqlite/sqlite_orm.h"
#include "rgw_common.h"

namespace sqlite_orm {
namespace blob_utils {

template <typename T>
struct __is_sqlite_blob : std::false_type {};
template <typename T, typename Tuple>
struct has_type;

template <typename T>
inline constexpr bool is_sqlite_blob = __is_sqlite_blob<T>::value;
struct has_type<T, std::tuple<>> : std::false_type {};

template <>
struct __is_sqlite_blob<rgw::sal::Attrs> : std::true_type {};
template <typename T, typename U, typename... Ts>
struct has_type<T, std::tuple<U, Ts...>> : has_type<T, std::tuple<Ts...>> {};

template <>
struct __is_sqlite_blob<ACLOwner> : std::true_type {};
template <typename T, typename... Ts>
struct has_type<T, std::tuple<T, Ts...>> : std::true_type {};

template <>
struct __is_sqlite_blob<rgw_placement_rule> : std::true_type {};
// list of types that are stored as blobs and have the encode/decode functions
using BlobTypes = std::tuple<
rgw::sal::Attrs, ACLOwner, rgw_placement_rule,
std::map<std::string, RGWAccessKey>, std::map<std::string, RGWSubUser>,
RGWUserCaps, std::list<std::string>, std::map<int, std::string>,
RGWQuotaInfo, std::set<std::string>, RGWBucketWebsiteConf,
std::map<std::string, uint32_t>, RGWObjectLock, rgw_sync_policy_info>;
} // namespace blob_utils

namespace sqlite_orm {

template <typename T>
inline constexpr bool is_sqlite_blob =
blob_utils::has_type<T, blob_utils::BlobTypes>::value;

template <class T>
struct type_printer<T, typename std::enable_if<is_sqlite_blob<T>, void>::type>
Expand Down Expand Up @@ -77,3 +93,60 @@ struct row_extractor<
}
};
} // namespace sqlite_orm

namespace rgw::sal::sfs::dbapi::sqlite {
template <typename T>
struct has_sqlite_type<T, SQLITE_BLOB, void>
: blob_utils::has_type<T, blob_utils::BlobTypes> {};

template <class T>
inline std::enable_if<sqlite_orm::is_sqlite_blob<T>, int>::type bind_col_in_db(
sqlite3_stmt* stmt, int inx, const T& val
) {
std::vector<char> blobValue;
rgw::sal::sfs::sqlite::encode_blob(val, blobValue);
return dbapi::sqlite::bind_col_in_db(stmt, inx, blobValue);
}
template <class T>
inline std::enable_if<sqlite_orm::is_sqlite_blob<T>, void>::type
store_result_in_db(sqlite3_context* db, const T& val) {
std::vector<char> blobValue;
rgw::sal::sfs::sqlite::encode_blob(val, blobValue);
dbapi::sqlite::store_result_in_db(db, blobValue);
}
template <class T>
inline std::enable_if<sqlite_orm::is_sqlite_blob<T>, T>::type
get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<T>) {
if (sqlite3_column_type(stmt, inx) == SQLITE_NULL) {
ceph_abort_msg("cannot make blob value from NULL");
}
auto blob_data = sqlite3_column_blob(stmt, inx);
auto blob_size = sqlite3_column_bytes(stmt, inx);
if (blob_data == nullptr || blob_size < 0) {
ceph_abort_msg("Invalid blob at column : (" + std::to_string(inx) + ")");
}
T ret;
rgw::sal::sfs::sqlite::decode_blob(
reinterpret_cast<const char*>(blob_data), static_cast<size_t>(blob_size),
ret
);
return ret;
}

template <class T>
inline std::enable_if<sqlite_orm::is_sqlite_blob<T>, T>::type
get_val_from_db(sqlite3_value* value, result_type<T>) {
if (sqlite3_value_type(value) == SQLITE_NULL) {
ceph_abort_msg("cannot make blob value from NULL");
}
std::vector<char> vector_value;
vector_value = get_val_from_db(value, result_type<std::vector<char>>());
T ret;
rgw::sal::sfs::sqlite::decode_blob(
reinterpret_cast<const char*>(vector_value),
static_cast<size_t>(vector_value.size()), ret
);
return ret;
}

} // namespace rgw::sal::sfs::dbapi::sqlite
2 changes: 1 addition & 1 deletion src/rgw/driver/sfs/sqlite/bindings/real_time.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

#include "common/ceph_time.h"
#include "include/ceph_assert.h"
#include "rgw/driver/sfs/sqlite/dbapi.h"
#include "rgw/driver/sfs/sqlite/dbapi_type_wrapper.h"
#include "rgw/driver/sfs/sqlite/sqlite_orm.h"

/// ceph::real_time is represented as a uint64 (unsigned).
Expand Down
40 changes: 40 additions & 0 deletions src/rgw/driver/sfs/sqlite/bindings/uuid_d.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

#include "rgw/driver/sfs/sqlite/sqlite_orm.h"
#include "rgw_common.h"
// we need to include dbapi_type_wrapper.h only because including dbapi.h
// creates circular dependencies
#include "rgw/driver/sfs/sqlite/dbapi_type_wrapper.h"

namespace sqlite_orm {
template <>
Expand Down Expand Up @@ -69,3 +72,40 @@ struct row_extractor<uuid_d> {
}
};
} // namespace sqlite_orm

namespace rgw::sal::sfs::dbapi::sqlite {

template <>
struct has_sqlite_type<uuid_d, SQLITE_TEXT, void> : ::std::true_type {};

inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const uuid_d& val) {
return bind_col_in_db(stmt, inx, val.to_string());
}
inline void store_result_in_db(sqlite3_context* db, const uuid_d& val) {
store_result_in_db(db, val.to_string());
}
inline uuid_d
get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<uuid_d>) {
std::string db_value = get_col_from_db(stmt, inx, result_type<std::string>());
uuid_d ret_value;
if (!ret_value.parse(db_value.c_str())) {
throw std::system_error(
ERANGE, std::system_category(),
"incorrect uuid string (" + db_value + ")"
);
}
return ret_value;
}

inline uuid_d get_val_from_db(sqlite3_value* value, result_type<uuid_d>) {
std::string db_value = get_val_from_db(value, result_type<std::string>());
uuid_d ret_value;
if (!ret_value.parse(db_value.c_str())) {
throw std::system_error(
ERANGE, std::system_category(),
"incorrect uuid string (" + db_value + ")"
);
}
return ret_value;
}
} // namespace rgw::sal::sfs::dbapi::sqlite
9 changes: 9 additions & 0 deletions src/rgw/driver/sfs/sqlite/dbapi_type_wrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

#include <sqlite3.h>

namespace rgw::sal::sfs::dbapi {

#include "sqlite_modern_cpp/hdr/sqlite_modern_cpp/type_wrapper.h"

} // namespace rgw::sal::sfs::dbapi
11 changes: 11 additions & 0 deletions src/rgw/driver/sfs/sqlite/objects/object_definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ struct DBObject {
uuid_d uuid;
std::string bucket_id;
std::string name;

using DBObjectQueryResult = std::tuple<
decltype(DBObject::uuid), decltype(DBObject::bucket_id),
decltype(DBObject::name)>;

DBObject() = default;

explicit DBObject(DBObjectQueryResult values)
: uuid(std::get<0>(values)),
bucket_id(std::get<1>(values)),
name(std::get<2>(values)) {}
};

} // namespace rgw::sal::sfs::sqlite
Expand Down
54 changes: 27 additions & 27 deletions src/rgw/driver/sfs/sqlite/sqlite_objects.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,57 +13,57 @@
*/
#include "sqlite_objects.h"

#include "dbapi.h"
#include "sqlite_query_utils.h"
#include "sqlite_versioned_objects.h"

using namespace sqlite_orm;

namespace rgw::sal::sfs::sqlite {

SQLiteObjects::SQLiteObjects(DBConnRef _conn) : conn(_conn) {}

std::vector<DBObject> SQLiteObjects::get_objects(const std::string& bucket_id
) const {
auto storage = conn->get_storage();
return storage->get_all<DBObject>(
where(is_equal(&DBObject::bucket_id, bucket_id))
return GetSQLiteObjectsWhere<DBObject>(
conn->get(), "objects", "bucket_id", bucket_id
);
}

std::optional<DBObject> SQLiteObjects::get_object(const uuid_d& uuid) const {
auto storage = conn->get_storage();
auto object = storage->get_pointer<DBObject>(uuid.to_string());
std::optional<DBObject> ret_value;
if (object) {
ret_value = *object;
}
return ret_value;
return GetSQLiteSingleObject<DBObject>(
conn->get(), "objects", "uuid", uuid.to_string()
);
}

std::optional<DBObject> SQLiteObjects::get_object(
const std::string& bucket_id, const std::string& object_name
) const {
auto storage = conn->get_storage();
auto objects = storage->get_all<DBObject>(where(
is_equal(&DBObject::bucket_id, bucket_id) and
is_equal(&DBObject::name, object_name)
));

std::optional<DBObject> ret_value;
// value must be unique
if (objects.size() == 1) {
ret_value = objects[0];
auto rows =
conn->get()
<< R"sql(SELECT * FROM objects WHERE bucket_id = ? AND name = ?;)sql"
<< bucket_id << object_name;
std::optional<DBObject> ret_object;
for (auto&& row : rows) {
ret_object = DBObject(row);
break; // looking for a single object, it should return 0 or 1 entries.
// TODO Return an error in there are more than 1 entry?
}
return ret_value;
return ret_object;
}

void SQLiteObjects::store_object(const DBObject& object) const {
auto storage = conn->get_storage();
storage->replace(object);
dbapi::sqlite::database db = conn->get();
db << R"sql(
REPLACE INTO objects ( uuid, bucket_id, name )
VALUES (?, ?, ?);)sql"
<< object.uuid << object.bucket_id << object.name;
}

void SQLiteObjects::remove_object(const uuid_d& uuid) const {
auto storage = conn->get_storage();
storage->remove<DBObject>(uuid);
dbapi::sqlite::database db = conn->get();
db << R"sql(
DELETE FROM objects
WHERE uuid = ?;)sql"
<< uuid;
}

} // namespace rgw::sal::sfs::sqlite
77 changes: 77 additions & 0 deletions src/rgw/driver/sfs/sqlite/sqlite_query_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t
// vim: ts=8 sw=2 smarttab ft=cpp
/*
* Ceph - scalable distributed file system
* SFS SAL implementation
*
* Copyright (C) 2023 SUSE LLC
*
* This is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software
* Foundation. See file COPYING.
*/
#pragma once

#include <string>
#include <vector>

#include "common/Formatter.h"
#include "rgw/driver/sfs/sqlite/dbapi.h"

namespace rgw::sal::sfs::sqlite {

// Helper to return a vector with all the objects in a given table
// This needs that Target has a constructor with a tuple listing all the
// columns.
// TODO See if we can do this in a more elegant way without investing too much
// time.
template <typename Target>
inline std::vector<Target> GetSQLiteObjects(
dbapi::sqlite::database db, const std::string& table_name
) {
auto rows = db << fmt::format("SELECT * FROM {};", table_name);
std::vector<Target> ret;
for (auto&& row : rows) {
ret.emplace_back(Target(row));
}
return ret;
}

// Helper to get a vector with all the objects in a given table with a single
// condition.
template <typename Target, typename ColumWhereType>
inline std::vector<Target> GetSQLiteObjectsWhere(
dbapi::sqlite::database db, const std::string& table_name,
const std::string& column_name, const ColumWhereType& column_value
) {
auto rows =
db << fmt::format(
"SELECT * FROM {} WHERE {} = ?;", table_name, column_name
)
<< column_value;
std::vector<Target> ret;
for (auto&& row : rows) {
ret.emplace_back(Target(row));
}
return ret;
}

// Helper to get a single object from a table
template <typename Target, typename KeyType>
inline std::optional<Target> GetSQLiteSingleObject(
dbapi::sqlite::database db, const std::string& table_name,
const std::string& key_name, const KeyType& key_value
) {
auto rows =
db << fmt::format("SELECT * FROM {} WHERE {} = ?;", table_name, key_name)
<< key_value;
std::optional<Target> ret;
for (auto&& row : rows) {
ret = Target(row);
break; // looking for a single object, it should return 0 or 1 entries.
// TODO Return an error in there are more than 1 entry?
}
return ret;
}
} // namespace rgw::sal::sfs::sqlite
Loading

0 comments on commit 2a30782

Please sign in to comment.