From: Soumya Koduri Date: Tue, 29 Jun 2021 13:41:10 +0000 (+0530) Subject: rgw/dbstore: DB backend for RGW X-Git-Tag: v17.1.0~1246^2~4 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=67be96413d606b4cdb7497d58eb93ec0c2ea66cb;p=ceph.git rgw/dbstore: DB backend for RGW As part of Zipper, adding support for DB backend (using SQLite) for Stackable RGW. The base class methods implemented are generic which could be extended to any backend database, not just SQLite. More details on design/implementation can be found at - https://docs.google.com/document/d/1xCoHT5DCujqbe1pnEfYpSaSqiBcW8z87CgQCC5LivOo/edit# Current status: [Done] - User related APIs - Bucket APIs - Testcases (using Gtest) to test the DBStore APIs [Not handled in the first pass] - Quota - Usage Stats - Swift Users - Multiple Zones/Zonegroups [WIP] - Object APIs Signed-off-by: Soumya Koduri --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e289346112b..35ec58187605 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -403,6 +403,7 @@ option(WITH_RADOSGW_BEAST_OPENSSL "Rados Gateway's Beast frontend uses OpenSSL" option(WITH_RADOSGW_AMQP_ENDPOINT "Rados Gateway's pubsub support for AMQP push endpoint" ON) option(WITH_RADOSGW_KAFKA_ENDPOINT "Rados Gateway's pubsub support for Kafka push endpoint" ON) option(WITH_RADOSGW_LUA_PACKAGES "Rados Gateway's support for dynamically adding lua packagess" ON) +option(WITH_RADOSGW_DBSTORE "DBStore backend for Rados Gateway" ON) if(WITH_RADOSGW) find_package(EXPAT REQUIRED) diff --git a/cmake/modules/FindSQLITE3.cmake b/cmake/modules/FindSQLITE3.cmake new file mode 100644 index 000000000000..b75639cd6331 --- /dev/null +++ b/cmake/modules/FindSQLITE3.cmake @@ -0,0 +1,38 @@ +# Copyright (C) 2007-2009 LuaDist. +# Created by Peter Kapec +# Redistribution and use of this file is allowed according to the terms of the MIT license. +# For details see the COPYRIGHT file distributed with LuaDist. +# Note: +# Searching headers and libraries is very simple and is NOT as powerful as scripts +# distributed with CMake, because LuaDist defines directories to search for. +# Everyone is encouraged to contact the author with improvements. Maybe this file +# becomes part of CMake distribution sometimes. + +# - Find sqlite3 +# Find the native SQLITE3 headers and libraries. +# +# SQLITE3_INCLUDE_DIRS - where to find sqlite3.h, etc. +# SQLITE3_LIBRARIES - List of libraries when using sqlite. +# SQLITE3_FOUND - True if sqlite found. + +# Look for the header file. +FIND_PATH(SQLITE3_INCLUDE_DIR NAMES sqlite3.h) + +# Look for the library. +FIND_LIBRARY(SQLITE3_LIBRARY NAMES sqlite sqlite3) + +# Handle the QUIETLY and REQUIRED arguments and set SQLITE3_FOUND to TRUE if all listed variables are TRUE. +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SQLITE3 + REQUIRED_VARS SQLITE3_LIBRARY SQLITE3_INCLUDE_DIR + FAIL_MESSAGE "Could not find sqlite3.h and/or libsqlite3.so") +# Copy the results to the output variables. +IF(SQLITE3_FOUND) + SET(SQLITE3_LIBRARIES ${SQLITE3_LIBRARY}) + SET(SQLITE3_INCLUDE_DIRS ${SQLITE3_INCLUDE_DIR}) +ELSE(SQLITE3_FOUND) + SET(SQLITE3_LIBRARIES) + SET(SQLITE3_INCLUDE_DIRS) +ENDIF(SQLITE3_FOUND) + +MARK_AS_ADVANCED(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES) diff --git a/src/include/config-h.in.cmake b/src/include/config-h.in.cmake index a1d89447356d..a465fc6b3a5a 100644 --- a/src/include/config-h.in.cmake +++ b/src/include/config-h.in.cmake @@ -351,6 +351,9 @@ /* Defined if lua packages can be installed by radosgw */ #cmakedefine WITH_RADOSGW_LUA_PACKAGES +/* Backend dbstore for Rados Gateway */ +#cmakedefine WITH_RADOSGW_DBSTORE + /* Defined if std::map::merge() is supported */ #cmakedefine HAVE_STDLIB_MAP_SPLICING diff --git a/src/rgw/CMakeLists.txt b/src/rgw/CMakeLists.txt index 05cdb6ea37de..62e95bcba109 100644 --- a/src/rgw/CMakeLists.txt +++ b/src/rgw/CMakeLists.txt @@ -126,6 +126,7 @@ set(librgw_common_srcs rgw_role.cc rgw_sal.cc rgw_sal_rados.cc + rgw_sal_dbstore.cc rgw_string.cc rgw_tag.cc rgw_tag_s3.cc @@ -228,6 +229,10 @@ if(WITH_JAEGER) add_dependencies(rgw_common jaegertracing::libjaegertracing) endif() +if(WITH_RADOSGW_DBSTORE) + add_subdirectory(store/dbstore) +endif() + set(rgw_a_srcs rgw_auth_keystone.cc rgw_client_io.cc @@ -295,6 +300,10 @@ set(rgw_libs rgw_a) list(APPEND rgw_libs ${LUA_LIBRARIES}) +if(WITH_RADOSGW_DBSTORE) + target_link_libraries(rgw_a PRIVATE dbstore) +endif() + set(rgw_schedulers_srcs rgw_dmclock_scheduler_ctx.cc rgw_dmclock_sync_scheduler.cc @@ -327,6 +336,7 @@ target_link_libraries(radosgw if(WITH_RADOSGW_BEAST_OPENSSL) # used by rgw_asio_frontend.cc target_link_libraries(radosgw PRIVATE OpenSSL::SSL) + target_link_libraries(rgw_a PRIVATE OpenSSL::SSL) endif() set_target_properties(radosgw PROPERTIES OUTPUT_NAME radosgw VERSION 2.0.0 SOVERSION 2) @@ -414,10 +424,13 @@ target_link_libraries(rgw if(WITH_RADOSGW_AMQP_ENDPOINT) target_link_libraries(rgw PRIVATE RabbitMQ::RabbitMQ) target_link_libraries(rgw PRIVATE OpenSSL::SSL) + target_link_libraries(rgw_a PRIVATE RabbitMQ::RabbitMQ) + target_link_libraries(rgw_a PRIVATE OpenSSL::SSL) endif() if(WITH_RADOSGW_KAFKA_ENDPOINT) target_link_libraries(rgw PRIVATE RDKafka::RDKafka) + target_link_libraries(rgw_a PRIVATE RDKafka::RDKafka) endif() target_link_libraries(rgw PRIVATE ${LUA_LIBRARIES}) diff --git a/src/rgw/rgw_sal.cc b/src/rgw/rgw_sal.cc index 31cf9fd36068..d05a4a1b8261 100644 --- a/src/rgw/rgw_sal.cc +++ b/src/rgw/rgw_sal.cc @@ -24,11 +24,13 @@ #include "rgw_sal.h" #include "rgw_sal_rados.h" #include "rgw_d3n_datacache.h" +#include "rgw_sal_dbstore.h" #define dout_subsys ceph_subsys_rgw extern "C" { extern rgw::sal::Store* newStore(void); +extern rgw::sal::Store* newRGWDBStore(void); } rgw::sal::Store* StoreManager::init_storage_provider(const DoutPrefixProvider* dpp, CephContext* cct, const std::string svc, bool use_gc_thread, bool use_lc_thread, bool quota_threads, bool run_sync_thread, bool run_reshard_thread, bool use_cache, bool use_gc) @@ -69,6 +71,15 @@ rgw::sal::Store* StoreManager::init_storage_provider(const DoutPrefixProvider* d return store; } + if (svc.compare("dbstore") == 0) { + store = newRGWDBStore(); + + /* Initialize the dbstore with cct & dpp */ + DBStore *db = static_cast(store)->getDBStore(); + db->set_context(cct); + return store; + } + return nullptr; } @@ -93,6 +104,9 @@ rgw::sal::Store* StoreManager::init_raw_storage_provider(const DoutPrefixProvide } } + if (svc.compare("dbstore") == 0) { + store = newRGWDBStore(); + } return store; } diff --git a/src/rgw/rgw_sal_dbstore.cc b/src/rgw/rgw_sal_dbstore.cc new file mode 100644 index 000000000000..1fa3231b2812 --- /dev/null +++ b/src/rgw/rgw_sal_dbstore.cc @@ -0,0 +1,869 @@ +// -*- 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 + * + * Copyright (C) 2021 Red Hat, Inc. + * + * 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. + * + */ + +#include +#include +#include +#include +#include + +#include "common/Clock.h" +#include "common/errno.h" + +#include "rgw_sal.h" +#include "rgw_sal_dbstore.h" +#include "rgw_bucket.h" + +#define dout_subsys ceph_subsys_rgw + +namespace rgw::sal { + + int DBUser::list_buckets(const DoutPrefixProvider *dpp, const string& marker, + const string& end_marker, uint64_t max, bool need_stats, + BucketList &buckets, optional_yield y) + { + RGWUserBuckets ulist; + bool is_truncated = false; + int ret; + + buckets.clear(); + ret = store->getDBStore()->list_buckets(info.user_id, marker, end_marker, max, + need_stats, &ulist, &is_truncated); + if (ret < 0) + return ret; + + buckets.set_truncated(is_truncated); + for (const auto& ent : ulist.get_buckets()) { + buckets.add(std::unique_ptr(new DBBucket(this->store, ent.second, this))); + } + + return 0; + } + + Bucket* DBUser::create_bucket(rgw_bucket& bucket, + ceph::real_time creation_time) + { + return NULL; + } + + int DBUser::read_attrs(const DoutPrefixProvider* dpp, optional_yield y) + { + int ret; + ret = store->getDBStore()->get_user(string("user_id"), "", info, &attrs, + &objv_tracker); + return ret; + } + + int DBUser::read_stats(const DoutPrefixProvider *dpp, + optional_yield y, RGWStorageStats* stats, + ceph::real_time *last_stats_sync, + ceph::real_time *last_stats_update) + { + return 0; + } + + /* stats - Not for first pass */ + int DBUser::read_stats_async(const DoutPrefixProvider *dpp, RGWGetUserStats_CB *cb) + { + return 0; + } + + int DBUser::complete_flush_stats(const DoutPrefixProvider *dpp, optional_yield y) + { + return 0; + } + + int DBUser::read_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch, uint32_t max_entries, + bool *is_truncated, RGWUsageIter& usage_iter, + map& usage) + { + return 0; + } + + int DBUser::trim_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch) + { + return 0; + } + + int DBUser::load_user(const DoutPrefixProvider *dpp, optional_yield y) + { + int ret = 0; + + ret = store->getDBStore()->get_user(string("user_id"), "", info, &attrs, + &objv_tracker); + + return ret; + } + + int DBUser::store_user(const DoutPrefixProvider* dpp, optional_yield y, bool exclusive, RGWUserInfo* old_info) + { + int ret = 0; + + ret = store->getDBStore()->store_user(info, exclusive, &attrs, &objv_tracker, old_info); + + return ret; + } + + int DBUser::remove_user(const DoutPrefixProvider* dpp, optional_yield y) + { + int ret = 0; + + ret = store->getDBStore()->remove_user(info, &objv_tracker); + + return ret; + } + + Object *DBBucket::create_object(const rgw_obj_key &key) + { + return nullptr; + } + + int DBBucket::remove_bucket(const DoutPrefixProvider *dpp, bool delete_children, std::string prefix, std::string delimiter, bool forward_to_master, req_info* req_info, optional_yield y) + { + int ret; + + ret = get_bucket_info(dpp, y); + if (ret < 0) + return ret; + + /* XXX: handle delete_children */ + + ret = store->getDBStore()->remove_bucket(info); + + return ret; + } + + int DBBucket::get_bucket_info(const DoutPrefixProvider *dpp, optional_yield y) + { + int ret = 0; + + ret = store->getDBStore()->get_bucket_info(string("name"), "", info, &attrs, + &mtime, &bucket_version); + + return ret; + } + + /* stats - Not for first pass */ + int DBBucket::get_bucket_stats(const DoutPrefixProvider *dpp, int shard_id, + std::string *bucket_ver, std::string *master_ver, + std::map& stats, + std::string *max_marker, bool *syncstopped) + { + return 0; + } + + int DBBucket::get_bucket_stats_async(const DoutPrefixProvider *dpp, int shard_id, RGWGetBucketStats_CB *ctx) + { + return 0; + } + + int DBBucket::read_bucket_stats(const DoutPrefixProvider *dpp, optional_yield y) + { + return 0; + } + + int DBBucket::sync_user_stats(const DoutPrefixProvider *dpp, optional_yield y) + { + return 0; + } + + int DBBucket::update_container_stats(const DoutPrefixProvider *dpp) + { + return 0; + } + + int DBBucket::check_bucket_shards(const DoutPrefixProvider *dpp) + { + return 0; + } + + int DBBucket::chown(const DoutPrefixProvider *dpp, User* new_user, User* old_user, optional_yield y, const std::string* marker) + { + int ret; + + ret = store->getDBStore()->update_bucket("owner", info, false, &(new_user->get_id()), nullptr, nullptr, nullptr); + + /* XXX: Update policies of all the bucket->objects with new user */ + return ret; + } + + int DBBucket::put_instance_info(const DoutPrefixProvider *dpp, bool exclusive, ceph::real_time _mtime) + { + int ret; + + ret = store->getDBStore()->update_bucket("info", info, exclusive, nullptr, nullptr, &_mtime, &info.objv_tracker); + + return ret; + + } + + int DBBucket::remove_metadata(const DoutPrefixProvider* dpp, RGWObjVersionTracker* objv, optional_yield y) + { + /* XXX: same as DBBUcket::remove_bucket() but should return error if there are objects + * in that bucket. */ + + int ret = store->getDBStore()->remove_bucket(info); + + return ret; + } + + /* Make sure to call get_bucket_info() if you need it first */ + bool DBBucket::is_owner(User* user) + { + return (info.owner.compare(user->get_id()) == 0); + } + + int DBBucket::check_empty(const DoutPrefixProvider *dpp, optional_yield y) + { + /* XXX: Check if bucket contains any objects */ + return 0; + } + + int DBBucket::check_quota(const DoutPrefixProvider *dpp, RGWQuotaInfo& user_quota, RGWQuotaInfo& bucket_quota, uint64_t obj_size, + optional_yield y, bool check_size_only) + { + /* Not Handled in the first pass as stats are also needed */ + return 0; + } + + int DBBucket::set_instance_attrs(const DoutPrefixProvider *dpp, Attrs& attrs, optional_yield y) + { + int ret = 0; + + /* XXX: handle has_instance_obj like in set_bucket_instance_attrs() */ + + ret = store->getDBStore()->update_bucket("attrs", info, false, nullptr, &attrs, nullptr, &get_info().objv_tracker); + + return ret; + } + + int DBBucket::try_refresh_info(const DoutPrefixProvider *dpp, ceph::real_time *pmtime) + { + int ret = 0; + + ret = store->getDBStore()->get_bucket_info(string("name"), "", info, &attrs, + pmtime, &bucket_version); + + return ret; + } + + /* XXX: usage and stats not supported in the first pass */ + int DBBucket::read_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch, + uint32_t max_entries, bool *is_truncated, + RGWUsageIter& usage_iter, + map& usage) + { + return 0; + } + + int DBBucket::trim_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch) + { + return 0; + } + + int DBBucket::remove_objs_from_index(const DoutPrefixProvider *dpp, std::list& objs_to_unlink) + { + /* XXX: CHECK: Unlike RadosStore, there is no seperate bucket index table. + * Delete all the object in the list from the object table of this + * bucket + */ + return 0; + } + + int DBBucket::check_index(const DoutPrefixProvider *dpp, std::map& existing_stats, std::map& calculated_stats) + { + /* XXX: stats not supported yet */ + return 0; + } + + int DBBucket::rebuild_index(const DoutPrefixProvider *dpp) + { + /* there is no index table in dbstore. Not applicable */ + return 0; + } + + int DBBucket::set_tag_timeout(const DoutPrefixProvider *dpp, uint64_t timeout) + { + /* XXX: CHECK: set tag timeout for all the bucket objects? */ + return 0; + } + + int DBBucket::purge_instance(const DoutPrefixProvider *dpp) + { + /* XXX: CHECK: for dbstore only single instance supported. + * Remove all the objects for that instance? Anything extra needed? + */ + return 0; + } + + int DBBucket::set_acl(const DoutPrefixProvider *dpp, RGWAccessControlPolicy &acl, optional_yield y) + { + int ret = 0; + bufferlist aclbl; + + acls = acl; + acl.encode(aclbl); + + Attrs attrs = get_attrs(); + attrs[RGW_ATTR_ACL] = aclbl; + + ret = store->getDBStore()->update_bucket("attrs", info, false, &(acl.get_owner().get_id()), &attrs, nullptr, nullptr); + + return ret; + } + + std::unique_ptr DBBucket::get_object(const rgw_obj_key& k) + { + return nullptr; + } + + int DBBucket::list(const DoutPrefixProvider *dpp, ListParams& params, int max, ListResults& results, optional_yield y) + { + /* XXX: Objects */ + return 0; + } + + void RGWDBStore::finalize(void) + { + if (dbsm) + dbsm->destroyAllHandles(); + } + + const RGWZoneGroup& DBZone::get_zonegroup() + { + return *zonegroup; + } + + int DBZone::get_zonegroup(const std::string& id, RGWZoneGroup& zg) + { + /* XXX: for now only one zonegroup supported */ + zg = *zonegroup; + return 0; + } + + const RGWZoneParams& DBZone::get_params() + { + return *zone_params; + } + + const rgw_zone_id& DBZone::get_id() + { + return cur_zone_id; + } + + const RGWRealm& DBZone::get_realm() + { + return *realm; + } + + const std::string& DBZone::get_name() const + { + return zone_params->get_name(); + } + + bool DBZone::is_writeable() + { + return true; + } + + bool DBZone::get_redirect_endpoint(std::string* endpoint) + { + return false; + } + + bool DBZone::has_zonegroup_api(const std::string& api) const + { + return false; + } + + const std::string& DBZone::get_current_period_id() + { + return current_period->get_id(); + } + + std::unique_ptr RGWDBStore::get_lua_script_manager() + { + return std::unique_ptr(new DBLuaScriptManager(this)); + } + + + std::unique_ptr RGWDBStore::get_role(std::string name, + std::string tenant, + std::string path, + std::string trust_policy, + std::string max_session_duration_str) + { + RGWRole* p = nullptr; + return std::unique_ptr(p); + } + + std::unique_ptr RGWDBStore::get_role(std::string id) + { + RGWRole* p = nullptr; + return std::unique_ptr(p); + } + + int RGWDBStore::get_roles(const DoutPrefixProvider *dpp, + optional_yield y, + const std::string& path_prefix, + const std::string& tenant, + vector>& roles) + { + return 0; + } + + std::unique_ptr RGWDBStore::get_oidc_provider() + { + RGWOIDCProvider* p = nullptr; + return std::unique_ptr(p); + } + + int RGWDBStore::get_oidc_providers(const DoutPrefixProvider *dpp, + const std::string& tenant, + vector>& providers) + { + return 0; + } + + std::unique_ptr RGWDBStore::get_user(const rgw_user &u) + { + return std::unique_ptr(new DBUser(this, u)); + } + + int RGWDBStore::get_user_by_access_key(const DoutPrefixProvider *dpp, const std::string& key, optional_yield y, std::unique_ptr* user) + { + RGWUserInfo uinfo; + User *u; + int ret = 0; + RGWObjVersionTracker objv_tracker; + + ret = getDBStore()->get_user(string("access_key"), key, uinfo, nullptr, + &objv_tracker); + + if (ret < 0) + return ret; + + u = new DBUser(this, uinfo); + + if (!u) + return -ENOMEM; + + u->get_version_tracker() = objv_tracker; + user->reset(u); + + return 0; + } + + int RGWDBStore::get_user_by_email(const DoutPrefixProvider *dpp, const std::string& email, optional_yield y, std::unique_ptr* user) + { + RGWUserInfo uinfo; + User *u; + int ret = 0; + RGWObjVersionTracker objv_tracker; + + ret = getDBStore()->get_user(string("email"), email, uinfo, nullptr, + &objv_tracker); + + if (ret < 0) + return ret; + + u = new DBUser(this, uinfo); + + if (!u) + return -ENOMEM; + + u->get_version_tracker() = objv_tracker; + user->reset(u); + + return ret; + } + + int RGWDBStore::get_user_by_swift(const DoutPrefixProvider *dpp, const std::string& user_str, optional_yield y, std::unique_ptr* user) + { + /* Swift keys and subusers are not supported for now */ + return 0; + } + + std::unique_ptr RGWDBStore::get_object(const rgw_obj_key& k) + { + return NULL; + } + + + int RGWDBStore::get_bucket(const DoutPrefixProvider *dpp, User* u, const rgw_bucket& b, std::unique_ptr* bucket, optional_yield y) + { + int ret; + Bucket* bp; + + bp = new DBBucket(this, b, u); + ret = bp->get_bucket_info(dpp, y); + if (ret < 0) { + delete bp; + return ret; + } + + bucket->reset(bp); + return 0; + } + + int RGWDBStore::get_bucket(User* u, const RGWBucketInfo& i, std::unique_ptr* bucket) + { + Bucket* bp; + + bp = new DBBucket(this, i, u); + /* Don't need to fetch the bucket info, use the provided one */ + + bucket->reset(bp); + return 0; + } + + int RGWDBStore::get_bucket(const DoutPrefixProvider *dpp, User* u, const std::string& tenant, const std::string& name, std::unique_ptr* bucket, optional_yield y) + { + rgw_bucket b; + + b.tenant = tenant; + b.name = name; + + return get_bucket(dpp, u, b, bucket, y); + } + + int RGWDBStore::create_bucket(const DoutPrefixProvider *dpp, + User* u, const rgw_bucket& b, + const string& zonegroup_id, + rgw_placement_rule& placement_rule, + string& swift_ver_location, + const RGWQuotaInfo * pquota_info, + const RGWAccessControlPolicy& policy, + Attrs& attrs, + RGWBucketInfo& info, + obj_version& ep_objv, + bool exclusive, + bool obj_lock_enabled, + bool *existed, + req_info& req_info, + std::unique_ptr* bucket_out, + optional_yield y) + { + int ret; + bufferlist in_data; + RGWBucketInfo master_info; + rgw_bucket *pmaster_bucket = nullptr; + uint32_t *pmaster_num_shards = nullptr; + real_time creation_time; + std::unique_ptr bucket; + obj_version objv, *pobjv = NULL; + + /* If it exists, look it up; otherwise create it */ + ret = get_bucket(dpp, u, b, &bucket, y); + if (ret < 0 && ret != -ENOENT) + return ret; + + if (ret != -ENOENT) { + RGWAccessControlPolicy old_policy(ctx()); + *existed = true; + if (swift_ver_location.empty()) { + swift_ver_location = bucket->get_info().swift_ver_location; + } + placement_rule.inherit_from(bucket->get_info().placement_rule); + + // don't allow changes to the acl policy + /* int r = rgw_op_get_bucket_policy_from_attr(dpp, this, u, bucket->get_attrs(), + &old_policy, y); + if (r >= 0 && old_policy != policy) { + bucket_out->swap(bucket); + return -EEXIST; + }*/ + } else { + bucket = std::unique_ptr(new DBBucket(this, b, u)); + *existed = false; + bucket->set_attrs(attrs); + // XXX: For now single default zone and STANDARD storage class + // supported. + placement_rule.name = "default"; + placement_rule.storage_class = "STANDARD"; + } + + /* + * XXX: If not master zone, fwd the request to master zone. + * For now DBStore has single zone. + */ + std::string zid = zonegroup_id; + /* if (zid.empty()) { + zid = svc()->zone->get_zonegroup().get_id(); + } */ + + if (*existed) { + rgw_placement_rule selected_placement_rule; + /* XXX: Handle this when zone is implemented + ret = svc()->zone->select_bucket_placement(u.get_info(), + zid, placement_rule, + &selected_placement_rule, nullptr, y); + if (selected_placement_rule != info.placement_rule) { + ret = -EEXIST; + bucket_out->swap(bucket); + return ret; + } */ + } else { + + /* XXX: We may not need to send all these params. Cleanup the unused ones */ + ret = getDBStore()->create_bucket(u->get_info(), bucket->get_key(), + zid, placement_rule, swift_ver_location, pquota_info, + attrs, info, pobjv, &ep_objv, creation_time, + pmaster_bucket, pmaster_num_shards, y, dpp, + exclusive); + if (ret == -EEXIST) { + *existed = true; + ret = 0; + } else if (ret != 0) { + return ret; + } + } + + bucket->set_version(ep_objv); + bucket->get_info() = info; + + bucket_out->swap(bucket); + + return ret; + } + + bool RGWDBStore::is_meta_master() + { + return true; + } + + int RGWDBStore::forward_request_to_master(const DoutPrefixProvider *dpp, User* user, obj_version *objv, + bufferlist& in_data, + JSONParser *jp, req_info& info, + optional_yield y) + { + return 0; + } + + int RGWDBStore::defer_gc(const DoutPrefixProvider *dpp, RGWObjectCtx *rctx, Bucket* bucket, Object* obj, optional_yield y) + { + return 0; + } + + std::string RGWDBStore::zone_unique_id(uint64_t unique_num) + { + return ""; + } + + std::string RGWDBStore::zone_unique_trans_id(const uint64_t unique_num) + { + return ""; + } + + int RGWDBStore::cluster_stat(RGWClusterStat& stats) + { + return 0; + } + + std::unique_ptr RGWDBStore::get_lifecycle(void) + { + return 0; + } + + std::unique_ptr RGWDBStore::get_completions(void) + { + return 0; + } + + std::unique_ptr RGWDBStore::get_notification(rgw::sal::Object* obj, + struct req_state* s, + rgw::notify::EventType event_type) + { + return 0; + } + + std::unique_ptr RGWDBStore::get_gc_chain(rgw::sal::Object* obj) + { + return 0; + } + + std::unique_ptr RGWDBStore::get_writer(Aio *aio, rgw::sal::Bucket* bucket, + RGWObjectCtx& obj_ctx, std::unique_ptr _head_obj, + const DoutPrefixProvider *dpp, optional_yield y) + { + return 0; + } + + int RGWDBStore::delete_raw_obj(const DoutPrefixProvider *dpp, const rgw_raw_obj& obj) + { + return 0; + } + + int RGWDBStore::delete_raw_obj_aio(const DoutPrefixProvider *dpp, const rgw_raw_obj& obj, Completions* aio) + { + return 0; + } + + void RGWDBStore::get_raw_obj(const rgw_placement_rule& placement_rule, const rgw_obj& obj, rgw_raw_obj* raw_obj) + { + return; + } + + int RGWDBStore::get_raw_chunk_size(const DoutPrefixProvider *dpp, const rgw_raw_obj& obj, uint64_t* chunk_size) + { + return 0; + } + + int RGWDBStore::log_usage(const DoutPrefixProvider *dpp, map& usage_info) + { + return 0; + } + + int RGWDBStore::log_op(const DoutPrefixProvider *dpp, string& oid, bufferlist& bl) + { + return 0; + } + + int RGWDBStore::register_to_service_map(const DoutPrefixProvider *dpp, const string& daemon_type, + const map& meta) + { + return 0; + } + + void RGWDBStore::get_quota(RGWQuotaInfo& bucket_quota, RGWQuotaInfo& user_quota) + { + // XXX: Not handled for the first pass + return; + } + + int RGWDBStore::set_buckets_enabled(const DoutPrefixProvider *dpp, vector& buckets, bool enabled) + { + int ret = 0; + + vector::iterator iter; + + for (iter = buckets.begin(); iter != buckets.end(); ++iter) { + rgw_bucket& bucket = *iter; + if (enabled) { + ldpp_dout(dpp, 20) << "enabling bucket name=" << bucket.name << dendl; + } else { + ldpp_dout(dpp, 20) << "disabling bucket name=" << bucket.name << dendl; + } + + RGWBucketInfo info; + map attrs; + int r = getDBStore()->get_bucket_info(string("name"), "", info, &attrs, + nullptr, nullptr); + if (r < 0) { + ldpp_dout(dpp, 0) << "NOTICE: get_bucket_info on bucket=" << bucket.name << " returned err=" << r << ", skipping bucket" << dendl; + ret = r; + continue; + } + if (enabled) { + info.flags &= ~BUCKET_SUSPENDED; + } else { + info.flags |= BUCKET_SUSPENDED; + } + + r = getDBStore()->update_bucket("info", info, false, nullptr, &attrs, nullptr, &info.objv_tracker); + if (r < 0) { + ldpp_dout(dpp, 0) << "NOTICE: put_bucket_info on bucket=" << bucket.name << " returned err=" << r << ", skipping bucket" << dendl; + ret = r; + continue; + } + } + return ret; + } + + int RGWDBStore::get_sync_policy_handler(const DoutPrefixProvider *dpp, + std::optional zone, + std::optional bucket, + RGWBucketSyncPolicyHandlerRef *phandler, + optional_yield y) + { + return 0; + } + + RGWDataSyncStatusManager* RGWDBStore::get_data_sync_manager(const rgw_zone_id& source_zone) + { + return 0; + } + + int RGWDBStore::read_all_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch, + uint32_t max_entries, bool *is_truncated, + RGWUsageIter& usage_iter, + map& usage) + { + return 0; + } + + int RGWDBStore::trim_all_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch) + { + return 0; + } + + int RGWDBStore::get_config_key_val(string name, bufferlist *bl) + { + return 0; + } + + int RGWDBStore::meta_list_keys_init(const DoutPrefixProvider *dpp, const string& section, const string& marker, void** phandle) + { + return 0; + } + + int RGWDBStore::meta_list_keys_next(const DoutPrefixProvider *dpp, void* handle, int max, list& keys, bool* truncated) + { + return 0; + } + + void RGWDBStore::meta_list_keys_complete(void* handle) + { + return; + } + + std::string RGWDBStore::meta_get_marker(void* handle) + { + return ""; + } + + int RGWDBStore::meta_remove(const DoutPrefixProvider *dpp, string& metadata_key, optional_yield y) + { + return 0; + } + +} // namespace rgw::sal + +extern "C" { + + void *newRGWDBStore(void) + { + rgw::sal::RGWDBStore *store = new rgw::sal::RGWDBStore(); + if (store) { + DBStoreManager *dbsm = new DBStoreManager(); + + if (!dbsm ) { + delete store; store = nullptr; + } + + DBStore *dbstore = dbsm->getDBStore(); + if (!dbstore ) { + delete dbsm; + delete store; store = nullptr; + } + + store->setDBStoreManager(dbsm); + store->setDBStore(dbstore); + } + + return store; + } + +} diff --git a/src/rgw/rgw_sal_dbstore.h b/src/rgw/rgw_sal_dbstore.h new file mode 100644 index 000000000000..818752fcca23 --- /dev/null +++ b/src/rgw/rgw_sal_dbstore.h @@ -0,0 +1,373 @@ +// -*- 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 + * + * Copyright (C) 2021 Red Hat, Inc. + * + * 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 "rgw_sal.h" +#include "rgw_oidc_provider.h" +#include "rgw_role.h" + +#include "store/dbstore/common/dbstore.h" +#include "store/dbstore/dbstore_mgr.h" + +namespace rgw { namespace sal { + + class RGWDBStore; + + class DBUser : public User { + private: + RGWDBStore *store; + + public: + DBUser(RGWDBStore *_st, const rgw_user& _u) : User(_u), store(_st) { } + DBUser(RGWDBStore *_st, const RGWUserInfo& _i) : User(_i), store(_st) { } + DBUser(RGWDBStore *_st) : store(_st) { } + DBUser(DBUser& _o) = default; + DBUser() {} + + virtual std::unique_ptr clone() override { + return std::unique_ptr(new DBUser(*this)); + } + int list_buckets(const DoutPrefixProvider *dpp, const std::string& marker, const std::string& end_marker, + uint64_t max, bool need_stats, BucketList& buckets, optional_yield y) override; + virtual Bucket* create_bucket(rgw_bucket& bucket, ceph::real_time creation_time) override; + virtual int read_attrs(const DoutPrefixProvider* dpp, optional_yield y) override; + virtual int read_stats(const DoutPrefixProvider *dpp, + optional_yield y, RGWStorageStats* stats, + ceph::real_time *last_stats_sync = nullptr, + ceph::real_time *last_stats_update = nullptr) override; + virtual int read_stats_async(const DoutPrefixProvider *dpp, RGWGetUserStats_CB* cb) override; + virtual int complete_flush_stats(const DoutPrefixProvider *dpp, optional_yield y) override; + virtual int read_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch, uint32_t max_entries, + bool* is_truncated, RGWUsageIter& usage_iter, + map& usage) override; + virtual int trim_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch) override; + + /* Placeholders */ + virtual int load_user(const DoutPrefixProvider* dpp, optional_yield y) override; + virtual int store_user(const DoutPrefixProvider* dpp, optional_yield y, bool exclusive, RGWUserInfo* old_info = nullptr) override; + virtual int remove_user(const DoutPrefixProvider* dpp, optional_yield y) override; + + friend class DBBucket; + }; + + class DBBucket : public Bucket { + private: + RGWDBStore *store; + RGWAccessControlPolicy acls; + + public: + DBBucket(RGWDBStore *_st) + : store(_st), + acls() { + } + + DBBucket(RGWDBStore *_st, User* _u) + : Bucket(_u), + store(_st), + acls() { + } + + DBBucket(RGWDBStore *_st, const rgw_bucket& _b) + : Bucket(_b), + store(_st), + acls() { + } + + DBBucket(RGWDBStore *_st, const RGWBucketEnt& _e) + : Bucket(_e), + store(_st), + acls() { + } + + DBBucket(RGWDBStore *_st, const RGWBucketInfo& _i) + : Bucket(_i), + store(_st), + acls() { + } + + DBBucket(RGWDBStore *_st, const rgw_bucket& _b, User* _u) + : Bucket(_b, _u), + store(_st), + acls() { + } + + DBBucket(RGWDBStore *_st, const RGWBucketEnt& _e, User* _u) + : Bucket(_e, _u), + store(_st), + acls() { + } + + DBBucket(RGWDBStore *_st, const RGWBucketInfo& _i, User* _u) + : Bucket(_i, _u), + store(_st), + acls() { + } + + ~DBBucket() { } + + virtual std::unique_ptr get_object(const rgw_obj_key& k) override; + virtual int list(const DoutPrefixProvider *dpp, ListParams&, int, ListResults&, optional_yield y) override; + Object* create_object(const rgw_obj_key& key /* Attributes */) override; + virtual int remove_bucket(const DoutPrefixProvider *dpp, bool delete_children, std::string prefix, std::string delimiter, bool forward_to_master, req_info* req_info, optional_yield y) override; + virtual RGWAccessControlPolicy& get_acl(void) override { return acls; } + virtual int set_acl(const DoutPrefixProvider *dpp, RGWAccessControlPolicy& acl, optional_yield y) override; + virtual int get_bucket_info(const DoutPrefixProvider *dpp, optional_yield y) override; + virtual int get_bucket_stats(const DoutPrefixProvider *dpp, int shard_id, + std::string *bucket_ver, std::string *master_ver, + std::map& stats, + std::string *max_marker = nullptr, + bool *syncstopped = nullptr) override; + virtual int get_bucket_stats_async(const DoutPrefixProvider *dpp, int shard_id, RGWGetBucketStats_CB* ctx) override; + virtual int read_bucket_stats(const DoutPrefixProvider *dpp, optional_yield y) override; + virtual int sync_user_stats(const DoutPrefixProvider *dpp, optional_yield y) override; + virtual int update_container_stats(const DoutPrefixProvider *dpp) override; + virtual int check_bucket_shards(const DoutPrefixProvider *dpp) override; + virtual int chown(const DoutPrefixProvider *dpp, User* new_user, User* old_user, optional_yield y, const std::string* marker = nullptr) override; + virtual int put_instance_info(const DoutPrefixProvider *dpp, bool exclusive, ceph::real_time mtime) override; + virtual int remove_metadata(const DoutPrefixProvider* dpp, RGWObjVersionTracker* objv, optional_yield y) override; + virtual bool is_owner(User* user) override; + virtual int check_empty(const DoutPrefixProvider *dpp, optional_yield y) override; + virtual int check_quota(const DoutPrefixProvider *dpp, RGWQuotaInfo& user_quota, RGWQuotaInfo& bucket_quota, uint64_t obj_size, optional_yield y, bool check_size_only = false) override; + virtual int set_instance_attrs(const DoutPrefixProvider *dpp, Attrs& attrs, optional_yield y) override; + virtual int try_refresh_info(const DoutPrefixProvider *dpp, ceph::real_time *pmtime) override; + virtual int read_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch, uint32_t max_entries, + bool *is_truncated, RGWUsageIter& usage_iter, + map& usage) override; + virtual int trim_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch) override; + virtual int remove_objs_from_index(const DoutPrefixProvider *dpp, std::list& objs_to_unlink) override; + virtual int check_index(const DoutPrefixProvider *dpp, std::map& existing_stats, std::map& calculated_stats) override; + virtual int rebuild_index(const DoutPrefixProvider *dpp) override; + virtual int set_tag_timeout(const DoutPrefixProvider *dpp, uint64_t timeout) override; + virtual int purge_instance(const DoutPrefixProvider *dpp) override; + virtual std::unique_ptr clone() override { + return std::make_unique(*this); + } + + friend class RGWDBStore; + }; + + class DBZone : public Zone { + protected: + RGWDBStore* store; + RGWRealm *realm{nullptr}; + RGWZoneGroup *zonegroup{nullptr}; + RGWZone *zone_public_config{nullptr}; /* external zone params, e.g., entrypoints, log flags, etc. */ + RGWZoneParams *zone_params{nullptr}; /* internal zone params, e.g., rados pools */ + RGWPeriod *current_period{nullptr}; + rgw_zone_id cur_zone_id; + + public: + DBZone(RGWDBStore* _store) : store(_store) { + realm = new RGWRealm(); + zonegroup = new RGWZoneGroup(); + zone_public_config = new RGWZone(); + zone_params = new RGWZoneParams(); + current_period = new RGWPeriod(); + cur_zone_id = rgw_zone_id(zone_params->get_id()); + + // XXX: only default and STANDARD supported for now + RGWZonePlacementInfo info; + RGWZoneStorageClasses sc; + sc.set_storage_class("STANDARD", nullptr, nullptr); + info.storage_classes = sc; + zone_params->placement_pools["default"] = info; + } + ~DBZone() = default; + + virtual const RGWZoneGroup& get_zonegroup() override; + virtual int get_zonegroup(const std::string& id, RGWZoneGroup& zonegroup) override; + virtual const RGWZoneParams& get_params() override; + virtual const rgw_zone_id& get_id() override; + virtual const RGWRealm& get_realm() override; + virtual const std::string& get_name() const override; + virtual bool is_writeable() override; + virtual bool get_redirect_endpoint(std::string* endpoint) override; + virtual bool has_zonegroup_api(const std::string& api) const override; + virtual const std::string& get_current_period_id() override; + }; + + class DBLuaScriptManager : public LuaScriptManager { + RGWDBStore* store; + + public: + DBLuaScriptManager(RGWDBStore* _s) : store(_s) + { + } + virtual ~DBLuaScriptManager() = default; + + virtual int get(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, std::string& script) override { return -ENOENT; } + virtual int put(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, const std::string& script) override { return -ENOENT; } + virtual int del(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key) override { return -ENOENT; } + }; + + class DBOIDCProvider : public RGWOIDCProvider { + RGWDBStore* store; + public: + DBOIDCProvider(RGWDBStore* _store) : store(_store) {} + ~DBOIDCProvider() = default; + + virtual int store_url(const DoutPrefixProvider *dpp, const std::string& url, bool exclusive, optional_yield y) override { return 0; } + virtual int read_url(const DoutPrefixProvider *dpp, const std::string& url, const std::string& tenant) override { return 0; } + virtual int delete_obj(const DoutPrefixProvider *dpp, optional_yield y) override { return 0;} + + void encode(bufferlist& bl) const { + RGWOIDCProvider::encode(bl); + } + void decode(bufferlist::const_iterator& bl) { + RGWOIDCProvider::decode(bl); + } + }; + + class RGWDBStore : public Store { + private: + /* DBStoreManager is used in case multiple + * connections are needed one for each tenant. + */ + DBStoreManager *dbsm; + /* default dbstore (single connection). If needed + * multiple db handles (for eg., one for each tenant), + * use dbsm->getDBStore(tenant) */ + DBStore *dbstore; + string luarocks_path; + DBZone zone; + RGWSyncModuleInstanceRef sync_module; + + public: + RGWDBStore(): dbsm(nullptr), zone(this) {} + ~RGWDBStore() { delete dbsm; } + + virtual std::unique_ptr get_user(const rgw_user& u) override; + virtual int get_user_by_access_key(const DoutPrefixProvider *dpp, const std::string& key, optional_yield y, std::unique_ptr* user) override; + virtual int get_user_by_email(const DoutPrefixProvider *dpp, const std::string& email, optional_yield y, std::unique_ptr* user) override; + virtual int get_user_by_swift(const DoutPrefixProvider *dpp, const std::string& user_str, optional_yield y, std::unique_ptr* user) override; + virtual std::unique_ptr get_object(const rgw_obj_key& k) override; + virtual int get_bucket(const DoutPrefixProvider *dpp, User* u, const rgw_bucket& b, std::unique_ptr* bucket, optional_yield y) override; + virtual int get_bucket(User* u, const RGWBucketInfo& i, std::unique_ptr* bucket) override; + virtual int get_bucket(const DoutPrefixProvider *dpp, User* u, const std::string& tenant, const std::string&name, std::unique_ptr* bucket, optional_yield y) override; + virtual int create_bucket(const DoutPrefixProvider* dpp, + User* u, const rgw_bucket& b, + const std::string& zonegroup_id, + rgw_placement_rule& placement_rule, + std::string& swift_ver_location, + const RGWQuotaInfo* pquota_info, + const RGWAccessControlPolicy& policy, + Attrs& attrs, + RGWBucketInfo& info, + obj_version& ep_objv, + bool exclusive, + bool obj_lock_enabled, + bool* existed, + req_info& req_info, + std::unique_ptr* bucket, + optional_yield y) override; + virtual bool is_meta_master() override; + virtual int forward_request_to_master(const DoutPrefixProvider *dpp, User* user, obj_version* objv, + bufferlist& in_data, JSONParser *jp, req_info& info, + optional_yield y) override; + virtual int defer_gc(const DoutPrefixProvider *dpp, RGWObjectCtx *rctx, Bucket* bucket, Object* obj, + optional_yield y) override; + virtual Zone* get_zone() { return &zone; } + virtual std::string zone_unique_id(uint64_t unique_num) override; + virtual std::string zone_unique_trans_id(const uint64_t unique_num) override; + virtual int cluster_stat(RGWClusterStat& stats) override; + virtual std::unique_ptr get_lifecycle(void) override; + virtual std::unique_ptr get_completions(void) override; + virtual std::unique_ptr get_notification(rgw::sal::Object* obj, struct req_state* s, rgw::notify::EventType event_type) override; + virtual std::unique_ptr get_gc_chain(rgw::sal::Object* obj) override; + virtual std::unique_ptr get_writer(Aio *aio, rgw::sal::Bucket* bucket, + RGWObjectCtx& obj_ctx, std::unique_ptr _head_obj, + const DoutPrefixProvider *dpp, optional_yield y) override; + virtual RGWLC* get_rgwlc(void) override { return NULL; } + virtual RGWCoroutinesManagerRegistry* get_cr_registry() override { return NULL; } + virtual int delete_raw_obj(const DoutPrefixProvider *dpp, const rgw_raw_obj& obj) override; + virtual int delete_raw_obj_aio(const DoutPrefixProvider *dpp, const rgw_raw_obj& obj, Completions* aio) override; + virtual void get_raw_obj(const rgw_placement_rule& placement_rule, const rgw_obj& obj, rgw_raw_obj* raw_obj) override; + virtual int get_raw_chunk_size(const DoutPrefixProvider *dpp, const rgw_raw_obj& obj, uint64_t* chunk_size) override; + + virtual int log_usage(const DoutPrefixProvider *dpp, map& usage_info) override; + virtual int log_op(const DoutPrefixProvider *dpp, std::string& oid, bufferlist& bl) override; + virtual int register_to_service_map(const DoutPrefixProvider *dpp, const string& daemon_type, + const map& meta) override; + virtual void get_quota(RGWQuotaInfo& bucket_quota, RGWQuotaInfo& user_quota) override; + virtual int set_buckets_enabled(const DoutPrefixProvider *dpp, vector& buckets, bool enabled) override; + virtual uint64_t get_new_req_id() override { return 0; } + virtual int get_sync_policy_handler(const DoutPrefixProvider *dpp, + std::optional zone, + std::optional bucket, + RGWBucketSyncPolicyHandlerRef *phandler, + optional_yield y) override; + virtual RGWDataSyncStatusManager* get_data_sync_manager(const rgw_zone_id& source_zone) override; + virtual void wakeup_meta_sync_shards(set& shard_ids) override { return; } + virtual void wakeup_data_sync_shards(const DoutPrefixProvider *dpp, const rgw_zone_id& source_zone, map >& shard_ids) override { return; } + virtual int clear_usage(const DoutPrefixProvider *dpp) override { return 0; } + virtual int read_all_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch, + uint32_t max_entries, bool *is_truncated, + RGWUsageIter& usage_iter, + map& usage) override; + virtual int trim_all_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch) override; + virtual int get_config_key_val(std::string name, bufferlist* bl) override; + virtual int meta_list_keys_init(const DoutPrefixProvider *dpp, const std::string& section, const std::string& marker, void** phandle) override; + virtual int meta_list_keys_next(const DoutPrefixProvider *dpp, void* handle, int max, list& keys, bool* truncated) override; + virtual void meta_list_keys_complete(void* handle) override; + virtual std::string meta_get_marker(void *handle) override; + virtual int meta_remove(const DoutPrefixProvider *dpp, string& metadata_key, optional_yield y) override; + + virtual const RGWSyncModuleInstanceRef& get_sync_module() { return sync_module; } + virtual std::string get_host_id() { return ""; } + + virtual std::unique_ptr get_lua_script_manager() override; + virtual std::unique_ptr get_role(string name, + string tenant, + string path="", + string trust_policy="", + string max_session_duration_str="") override; + virtual std::unique_ptr get_role(std::string id) override; + virtual int get_roles(const DoutPrefixProvider *dpp, + optional_yield y, + const std::string& path_prefix, + const std::string& tenant, + vector>& roles) override; + virtual std::unique_ptr get_oidc_provider() override; + virtual int get_oidc_providers(const DoutPrefixProvider *dpp, + const std::string& tenant, + vector>& providers) override; + + + virtual void finalize(void) override; + + virtual CephContext *ctx(void) override { + return dbstore->ctx(); + } + + virtual const std::string& get_luarocks_path() const override { + return luarocks_path; + } + + virtual void set_luarocks_path(const std::string& path) override { + luarocks_path = path; + } + + /* Unique to DBStore */ + void setDBStoreManager(DBStoreManager *stm) { dbsm = stm; } + DBStoreManager *getDBStoreManager(void) { return dbsm; } + + void setDBStore(DBStore * st) { dbstore = st; } + DBStore *getDBStore(void) { return dbstore; } + + DBStore *getDBStore(string tenant) { return dbsm->getDBStore(tenant, false); } + }; + +} } // namespace rgw::sal diff --git a/src/rgw/store/dbstore/CMakeLists.txt b/src/rgw/store/dbstore/CMakeLists.txt new file mode 100644 index 000000000000..984fb93cfd4e --- /dev/null +++ b/src/rgw/store/dbstore/CMakeLists.txt @@ -0,0 +1,56 @@ +#need to update cmake version here +cmake_minimum_required(VERSION 3.14.0) +project(dbstore) + +option(USE_SQLITE "Enable SQLITE DB" ON) + +set (CMAKE_INCLUDE_DIR ${CMAKE_INCLUDE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/common") + +set(dbstore_srcs + common/dbstore_log.h + common/dbstore.h + common/dbstore.cc) + +set(dbstore_mgr_srcs + dbstore_mgr.h + dbstore_mgr.cc + ) + +add_library(dbstore_lib ${dbstore_srcs}) +target_include_directories(dbstore_lib PUBLIC "${CMAKE_SOURCE_DIR}/src/fmt/include") +target_include_directories(dbstore_lib PUBLIC "${CMAKE_SOURCE_DIR}/src/rgw") +target_link_libraries(dbstore_lib PUBLIC spawn) + +set (CMAKE_LINK_LIBRARIES ${CMAKE_LINK_LIBRARIES} dbstore_lib) + +IF(USE_SQLITE) + add_subdirectory(sqlite) + set(CMAKE_INCLUDE_DIR ${CMAKE_INCLUDE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/sqlite") + add_compile_definitions(SQLITE_ENABLED=1) + set (CMAKE_LINK_LIBRARIES ${CMAKE_LINK_LIBRARIES} rgw_a) + set (CMAKE_LINK_LIBRARIES ${CMAKE_LINK_LIBRARIES} sqlite_db) + add_dependencies(sqlite_db dbstore_lib) +ENDIF() + +# add pthread library +set (CMAKE_LINK_LIBRARIES ${CMAKE_LINK_LIBRARIES} pthread) + +find_package(gtest QUIET) +if(WITH_TESTS) + add_subdirectory(tests) +else() + message(WARNING "Gtest not enabled") +endif() + +include_directories(${CMAKE_INCLUDE_DIR}) +add_library(dbstore ${dbstore_mgr_srcs}) +target_link_libraries(dbstore ${CMAKE_LINK_LIBRARIES}) + +# testing purpose +set(dbstore_main_srcs + dbstore_main.cc) + +set (CMAKE_LINK_LIBRARIES ${CMAKE_LINK_LIBRARIES} dbstore) +add_executable(dbstore-bin ${dbstore_main_srcs}) +add_dependencies(dbstore-bin dbstore) +target_link_libraries(dbstore-bin ${CMAKE_LINK_LIBRARIES}) diff --git a/src/rgw/store/dbstore/README.md b/src/rgw/store/dbstore/README.md new file mode 100644 index 000000000000..b84ec727d7a2 --- /dev/null +++ b/src/rgw/store/dbstore/README.md @@ -0,0 +1,37 @@ +# dbstore +DBStore for Rados Gateway (RGW) + +## Pre-install +fmt(-devel) and gtest(-devel) packages need to be installed + +## Build +cd build + +ninja src/rgw/store/dbstore/install + +## Gtests +To execute Gtest cases, from build directory + +./bin/dbstore-tests + +## Execute Sample test file + +./bin/dbstore-bin + +## Logging +Different loglevels are supported + +ERROR - 0 + +EVENT - 1 + +DEBUG - 2 + +FULLDEBUG - 3 + +By default log level is set to EVENT and logs are stored in dbstore.log + +[To provide custom log file and change log level] + +./dbstore-bin log_file log_level + diff --git a/src/rgw/store/dbstore/common/dbstore.cc b/src/rgw/store/dbstore/common/dbstore.cc new file mode 100644 index 000000000000..6d55617edfed --- /dev/null +++ b/src/rgw/store/dbstore/common/dbstore.cc @@ -0,0 +1,735 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "dbstore.h" + +using namespace std; + +map DBStore::objectmap = {}; + +map DBStore::getObjectMap() { + return DBStore::objectmap; +} + +/* Custom Logging initialization */ +ofstream fileout; +ostream *dbout; +int LogLevel = L_FULLDEBUG; +string LogFile = "dbstore.log"; + +static void LogInit(string logfile, int loglevel) { + if (loglevel >= L_ERR && loglevel <= L_FULLDEBUG) + LogLevel = loglevel; + + if (!logfile.empty()) { + LogFile = logfile; + } + + fileout.open(LogFile); + dbout = &fileout; + + return; +} + +static void LogDestroy() { + if(dbout && (dbout != &cout)) + fileout.close(); + return; +} + +int DBStore::Initialize(string logfile, int loglevel) +{ + int ret = -1; + + LogInit(logfile, loglevel); + + db = openDB(); + + if (!db) { + dbout(L_ERR)<<"Failed to open database \n"; + return ret; + } + + ret = LockInit(); + + if (ret) { + dbout(L_ERR)<<"Error: mutex is NULL \n"; + closeDB(); + db = NULL; + return ret; + } + + ret = InitializeDBOps(); + + if (ret) { + dbout(L_ERR)<<"InitializeDBOps failed \n"; + LockDestroy(); + closeDB(); + db = NULL; + return ret; + } + + dbout(L_FULLDEBUG)<< "DBStore successfully initialized - name:" \ + << db_name << "\n"; + + return ret; +} + +int DBStore::Destroy() +{ + if (!db) + return 0; + + closeDB(); + + LockDestroy(); + + FreeDBOps(); + + dbout(L_FULLDEBUG)<<"DBStore successfully destroyed - name:" \ + <::iterator iter; + class ObjectOp* Ob; + + iter = DBStore::objectmap.find(params->op.bucket.info.bucket.name); + + if (iter == DBStore::objectmap.end()) { + dbout(L_EVENT)<<"No objectmap found for bucket: " \ + <op.bucket.info.bucket.name<<"\n"; + /* not found */ + return NULL; + } + + Ob = iter->second; + + if (!Op.compare("InsertObject")) + return Ob->InsertObject; + if (!Op.compare("RemoveObject")) + return Ob->RemoveObject; + if (!Op.compare("ListObject")) + return Ob->ListObject; + if (!Op.compare("PutObjectData")) + return Ob->PutObjectData; + if (!Op.compare("GetObjectData")) + return Ob->GetObjectData; + if (!Op.compare("DeleteObjectData")) + return Ob->DeleteObjectData; + + return NULL; +} + +int DBStore::objectmapInsert(string bucket, void *ptr) +{ + map::iterator iter; + class ObjectOp *Ob; + + iter = DBStore::objectmap.find(bucket); + + if (iter != DBStore::objectmap.end()) { + // entry already exists + // return success or replace it or + // return error ? + // return success for now + dbout(L_DEBUG)<<"Objectmap entry already exists for bucket("\ + <InitializeObjectOps(); + + DBStore::objectmap.insert(pair(bucket, Ob)); + + return 0; +} + +int DBStore::objectmapDelete(string bucket) +{ + map::iterator iter; + class ObjectOp *Ob; + + iter = DBStore::objectmap.find(bucket); + + if (iter == DBStore::objectmap.end()) { + // entry doesn't exist + // return success or return error ? + // return success for now + dbout(L_DEBUG)<<"Objectmap entry for bucket("<second); + Ob->FreeObjectOps(); + + DBStore::objectmap.erase(iter); + + return 0; +} + +int DBStore::InitializeParams(string Op, DBOpParams *params) +{ + int ret = -1; + + if (!params) + goto out; + + //reset params here + params->user_table = user_table; + params->bucket_table = bucket_table; + + ret = 0; +out: + return ret; +} + +int DBStore::ProcessOp(string Op, struct DBOpParams *params) { + int ret = -1; + class DBOp *db_op; + + Lock(); + db_op = getDBOp(Op, params); + + if (!db_op) { + dbout(L_ERR)<<"No db_op found for Op("<Execute(params); + + Unlock(); + if (ret) { + dbout(L_ERR)<<"In Process op Execute failed for fop(" \ + < *pattrs, + RGWObjVersionTracker *pobjv_tracker) { + int ret = 0; + + if (query_str.empty()) { + // not checking for query_str_val as the query can be to fetch + // entries with null values + return -1; + } + + DBOpParams params = {}; + InitializeParams("GetUser", ¶ms); + + params.op.query_str = query_str; + + // validate query_str with UserTable entries names + if (query_str == "username") { + params.op.user.uinfo.display_name = query_str_val; + } else if (query_str == "email") { + params.op.user.uinfo.user_email = query_str_val; + } else if (query_str == "access_key") { + RGWAccessKey k(query_str_val, ""); + map keys; + keys[query_str_val] = k; + params.op.user.uinfo.access_keys = keys; + } else if (query_str == "user_id") { + params.op.user.uinfo.user_id = uinfo.user_id; + } else { + dbout(L_ERR)<<"In GetUser Invalid query string :" <read_version = params.op.user.user_version; + } + +out: + return ret; +} + +int DBStore::store_user(RGWUserInfo& uinfo, bool exclusive, map *pattrs, + RGWObjVersionTracker *pobjv, RGWUserInfo* pold_info) +{ + DBOpParams params = {}; + InitializeParams("CreateUser", ¶ms); + int ret = 0; + + /* Check if the user already exists and return the old info, caller will have a use for it */ + RGWUserInfo orig_info; + RGWObjVersionTracker objv_tracker = {}; + obj_version& obj_ver = objv_tracker.read_version; + + orig_info.user_id = uinfo.user_id; + ret = get_user(string("user_id"), "", orig_info, nullptr, &objv_tracker); + + if (!ret && obj_ver.ver) { + /* already exists. */ + + if (pold_info) { + *pold_info = orig_info; + } + + if (pobjv && (pobjv->read_version.ver != obj_ver.ver)) { + /* Object version mismatch.. return ECANCELED */ + ret = -ECANCELED; + dbout(L_ERR)<<"User Read version mismatch err:(" <read_version = obj_ver; + pobjv->write_version = obj_ver; + } + +out: + return ret; +} + +int DBStore::remove_user(RGWUserInfo& uinfo, RGWObjVersionTracker *pobjv) +{ + DBOpParams params = {}; + InitializeParams("CreateUser", ¶ms); + int ret = 0; + + RGWUserInfo orig_info; + RGWObjVersionTracker objv_tracker = {}; + + orig_info.user_id = uinfo.user_id; + ret = get_user(string("user_id"), "", orig_info, nullptr, &objv_tracker); + + if (!ret && objv_tracker.read_version.ver) { + /* already exists. */ + + if (pobjv && (pobjv->read_version.ver != objv_tracker.read_version.ver)) { + /* Object version mismatch.. return ECANCELED */ + ret = -ECANCELED; + dbout(L_ERR)<<"User Read version mismatch err:(" <& attrs, + RGWBucketInfo& info, + obj_version *pobjv, + obj_version *pep_objv, + real_time creation_time, + rgw_bucket *pmaster_bucket, + uint32_t *pmaster_num_shards, + optional_yield y, + const DoutPrefixProvider *dpp, + bool exclusive) +{ + /* + * XXX: Simple creation for now. + * + * Referring to RGWRados::create_bucket(), + * Check if bucket already exists, select_bucket_placement, + * is explicit put/remove instance info needed? - should not be ideally + */ + + DBOpParams params = {}; + InitializeParams("CreateBucket", ¶ms); + int ret = 0; + + /* Check if the bucket already exists and return the old info, caller will have a use for it */ + RGWBucketInfo orig_info; + orig_info.bucket.name = bucket.name; + ret = get_bucket_info(string("name"), "", orig_info, nullptr, nullptr, nullptr); + + if (!ret && !orig_info.owner.id.empty() && exclusive) { + /* already exists. Return the old info */ + + info = std::move(orig_info); + return ret; + } + + RGWObjVersionTracker& objv_tracker = info.objv_tracker; + + objv_tracker.read_version.clear(); + + if (pobjv) { + objv_tracker.write_version = *pobjv; + } else { + objv_tracker.generate_new_write_ver(cct); + } + params.op.bucket.bucket_version = objv_tracker.write_version; + objv_tracker.read_version = params.op.bucket.bucket_version; + + uint64_t bid = next_bucket_id(); + string s = getDBname() + "." + std::to_string(bid); + bucket.marker = bucket.bucket_id = s; + + info.bucket = bucket; + info.owner = owner.user_id; + info.zonegroup = zonegroup_id; + info.placement_rule = placement_rule; + info.swift_ver_location = swift_ver_location; + info.swift_versioning = (!swift_ver_location.empty()); + + info.requester_pays = false; + if (real_clock::is_zero(creation_time)) { + info.creation_time = ceph::real_clock::now(); + } else { + info.creation_time = creation_time; + } + if (pquota_info) { + info.quota = *pquota_info; + } + + params.op.bucket.info = info; + params.op.bucket.bucket_attrs = attrs; + params.op.bucket.mtime = ceph::real_time(); + params.op.user.uinfo.user_id.id = owner.user_id.id; + + ret = ProcessOp("InsertBucket", ¶ms); + + if (ret) { + dbout(L_ERR)<<"create_bucket failed with err:(" <add(std::move(entry)); + /* cout << "entry.bucket.marker: " << entry.bucket.marker << " min_marker: " << marker; + + if (entry.bucket.marker < marker) { + cout << " lesser" << "\n"; + } else if (entry.bucket.marker > marker) { + cout << " greater" << "\n"; + } else { + cout << " equal" << "\n"; + } */ + } +out: + return ret; +} + +int DBStore::update_bucket(const std::string& query_str, + RGWBucketInfo& info, + bool exclusive, + const rgw_user* powner_id, + map* pattrs, + ceph::real_time* pmtime, + RGWObjVersionTracker* pobjv) +{ + int ret = 0; + DBOpParams params = {}; + obj_version bucket_version; + RGWBucketInfo orig_info; + + /* Check if the bucket already exists and return the old info, caller will have a use for it */ + orig_info.bucket.name = info.bucket.name; + params.op.bucket.info.bucket.name = info.bucket.name; + ret = get_bucket_info(string("name"), "", orig_info, nullptr, nullptr, + &bucket_version); + + if (ret) { + dbout(L_ERR)<<"Failed to read bucket info err:(" <read_version.ver != bucket_version.ver) { + dbout(L_ERR)<<"Read version mismatch err:(" <id; + } else { + params.op.user.uinfo.user_id.id = orig_info.owner.id; + } + + /* Update version & mtime */ + params.op.bucket.bucket_version.ver = ++(bucket_version.ver); + + if (pmtime) { + params.op.bucket.mtime = *pmtime;; + } else { + params.op.bucket.mtime = ceph::real_time(); + } + + if (query_str == "attrs") { + params.op.query_str = "attrs"; + params.op.bucket.bucket_attrs = *pattrs; + } else if (query_str == "owner") { + /* Update only owner i.e, chown. + * Update creation_time too */ + params.op.query_str = "owner"; + params.op.bucket.info.creation_time = params.op.bucket.mtime; + } else if (query_str == "info") { + params.op.query_str = "info"; + params.op.bucket.info = info; + } else { + ret = -1; + dbout(L_ERR)<<"In UpdateBucket Invalid query_str : " << query_str <<" \n"; + goto out; + } + + ret = ProcessOp("UpdateBucket", ¶ms); + + if (ret) { + dbout(L_ERR)<<"In UpdateBucket failed err:(" <read_version = params.op.bucket.bucket_version; + pobjv->write_version = params.op.bucket.bucket_version; + } + +out: + return ret; +} diff --git a/src/rgw/store/dbstore/common/dbstore.h b/src/rgw/store/dbstore/common/dbstore.h new file mode 100644 index 000000000000..bb7879183999 --- /dev/null +++ b/src/rgw/store/dbstore/common/dbstore.h @@ -0,0 +1,858 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef DB_STORE_H +#define DB_STORE_H + +#include +#include +#include +#include +#include +// this seems safe to use, at least for now--arguably, we should +// prefer header-only fmt, in general +#undef FMT_HEADER_ONLY +#define FMT_HEADER_ONLY 1 +#include "fmt/format.h" +#include +#include "dbstore_log.h" +#include "rgw/rgw_sal.h" +#include "rgw/rgw_bucket.h" +//#include "rgw/rgw_common.h" + +using namespace std; + +class DBStore; + +struct DBOpUserInfo { + RGWUserInfo uinfo = {}; + obj_version user_version; + rgw::sal::Attrs user_attrs; +}; + +struct DBOpBucketInfo { + RGWBucketEnt ent; // maybe not needed. not used in create/get_bucket + RGWBucketInfo info; + RGWUser* owner = nullptr; + rgw::sal::Attrs bucket_attrs; + obj_version bucket_version; + ceph::real_time mtime; + // used for list query + string min_marker; + string max_marker; + list list_entries; +}; + +struct DBOpInfo { + string name; // Op name + /* Support only single access_key for now. So store + * it separately as primary access_key_id & secret to + * be able to query easily. + * + * XXX: Swift keys and subuser not supported for now */ + DBOpUserInfo user; + string query_str; + DBOpBucketInfo bucket; + uint64_t list_max_count; +}; + +struct DBOpParams { + /* Tables */ + string user_table; + string bucket_table; + string object_table; + + /* Ops*/ + DBOpInfo op; + + /* Below are subject to change */ + string objectdata_table; + string quota_table; + string object; + size_t offset; + string data; + size_t datalen; +}; + +/* Used for prepared schemas. + * Difference with above structure is that all + * the fields are strings here to accommodate any + * style identifiers used by backend db. By default + * initialized with sqlitedb style, can be overriden + * using InitPrepareParams() + * + * These identifiers are used in prepare and bind statements + * to get the right index of each param. + */ +struct DBOpUserPrepareInfo { + string user_id = ":user_id"; + string tenant = ":tenant"; + string ns = ":ns"; + string display_name = ":display_name"; + string user_email = ":user_email"; + /* Support only single access_key for now. So store + * it separately as primary access_key_id & secret to + * be able to query easily. + * + * In future, when need to support & query from multiple + * access keys, better to maintain them in a separate table. + */ + string access_keys_id = ":access_keys_id"; + string access_keys_secret = ":access_keys_secret"; + string access_keys = ":access_keys"; + string swift_keys = ":swift_keys"; + string subusers = ":subusers"; + string suspended = ":suspended"; + string max_buckets = ":max_buckets"; + string op_mask = ":op_mask"; + string user_caps = ":user_caps"; + string admin = ":admin"; + string system = ":system"; + string placement_name = ":placement_name"; + string placement_storage_class = ":placement_storage_class"; + string placement_tags = ":placement_tags"; + string bucket_quota = ":bucket_quota"; + string temp_url_keys = ":temp_url_keys"; + string user_quota = ":user_quota"; + string type = ":type"; + string mfa_ids = ":mfa_ids"; + string assumed_role_arn = ":assumed_role_arn"; + string user_attrs = ":user_attrs"; + string user_ver = ":user_vers"; + string user_ver_tag = ":user_ver_tag"; +}; + +struct DBOpBucketPrepareInfo { + string bucket_name = ":bucket_name"; + string tenant = ":tenant"; + string marker = ":marker"; + string bucket_id = ":bucket_id"; + string size = ":size"; + string size_rounded = ":size_rounded"; + string creation_time = ":creation_time"; + string count = ":count"; + string placement_name = ":placement_name"; + string placement_storage_class = ":placement_storage_class"; + /* ownerid - maps to DBOpUserPrepareInfo */ + string flags = ":flags"; + string zonegroup = ":zonegroup"; + string has_instance_obj = ":has_instance_obj"; + string quota = ":quota"; + string requester_pays = ":requester_pays"; + string has_website = ":has_website"; + string website_conf = ":website_conf"; + string swift_versioning = ":swift_versioning"; + string swift_ver_location = ":swift_ver_location"; + string mdsearch_config = ":mdsearch_config"; + string new_bucket_instance_id = ":new_bucket_instance_id"; + string obj_lock = ":obj_lock"; + string sync_policy_info_groups = ":sync_policy_info_groups"; + string bucket_attrs = ":bucket_attrs"; + string bucket_ver = ":bucket_vers"; + string bucket_ver_tag = ":bucket_ver_tag"; + string mtime = ":mtime"; + string min_marker = ":min_marker"; + string max_marker = ":max_marker"; +}; + +struct DBOpPrepareInfo { + DBOpUserPrepareInfo user; + string query_str = ":query_str"; + DBOpBucketPrepareInfo bucket; + string list_max_count = ":list_max_count"; +}; + +struct DBOpPrepareParams { + /* Tables */ + string user_table = ":user_table"; + string bucket_table = ":bucket_table"; + string object_table = ":object_table"; + + /* Ops */ + DBOpPrepareInfo op; + + + /* below subject to change */ + string objectdata_table = ":objectdata_table"; + string quota_table = ":quota_table"; + string object = ":object"; + string offset = ":offset"; + string data = ":data"; + string datalen = ":datalen"; +}; + +struct DBOps { + class InsertUserOp *InsertUser; + class RemoveUserOp *RemoveUser; + class GetUserOp *GetUser; + class InsertBucketOp *InsertBucket; + class UpdateBucketOp *UpdateBucket; + class RemoveBucketOp *RemoveBucket; + class GetBucketOp *GetBucket; + class ListUserBucketsOp *ListUserBuckets; +}; + +class ObjectOp { + public: + ObjectOp() {}; + + virtual ~ObjectOp() {} + + class InsertObjectOp *InsertObject; + class RemoveObjectOp *RemoveObject; + class ListObjectOp *ListObject; + class PutObjectDataOp *PutObjectData; + class GetObjectDataOp *GetObjectData; + class DeleteObjectDataOp *DeleteObjectData; + + virtual int InitializeObjectOps() { return 0; } + virtual int FreeObjectOps() { return 0; } +}; + +class DBOp { + private: + const string CreateUserTableQ = + /* Corresponds to RGWUser + * + * For now only UserID is made Primary key. + * If multiple tenants are stored in single .db handle, should + * make both (UserID, Tenant) as Primary Key. + * + * XXX: + * - AccessKeys, SwiftKeys, Subusers (map<>) are stored as blob. + * To enable easy query, first accesskey is stored in separate fields + * AccessKeysID, AccessKeysSecret. + * In future, may be have separate table to store these keys and + * query on that table. + * - Quota stored as blob .. should be linked to quota table. + */ + "CREATE TABLE IF NOT EXISTS '{}' ( \ + UserID TEXT NOT NULL UNIQUE, \ + Tenant TEXT , \ + NS TEXT , \ + DisplayName TEXT , \ + UserEmail TEXT , \ + AccessKeysID TEXT , \ + AccessKeysSecret TEXT , \ + AccessKeys BLOB , \ + SwiftKeys BLOB , \ + SubUsers BLOB , \ + Suspended INTEGER , \ + MaxBuckets INTEGER , \ + OpMask INTEGER , \ + UserCaps BLOB , \ + Admin INTEGER , \ + System INTEGER , \ + PlacementName TEXT , \ + PlacementStorageClass TEXT , \ + PlacementTags BLOB , \ + BucketQuota BLOB , \ + TempURLKeys BLOB , \ + UserQuota BLOB , \ + TYPE INTEGER , \ + MfaIDs BLOB , \ + AssumedRoleARN TEXT , \ + UserAttrs BLOB, \ + UserVersion INTEGER, \ + UserVersionTag TEXT, \ + PRIMARY KEY (UserID) \n);"; + + const string CreateBucketTableQ = + /* Corresponds to RGWBucket + * + * For now only BucketName is made Primary key. + * If multiple tenants are stored in single .db handle, should + * make both (BucketName, Tenant) as Primary Key. Also should + * reference (UserID, Tenant) as Foreign key. + * + * leaving below RADOS specific fields + * - rgw_data_placement_target explicit_placement (struct rgw_bucket) + * - rgw::BucketLayout layout (struct RGWBucketInfo) + * - const static uint32_t NUM_SHARDS_BLIND_BUCKET (struct RGWBucketInfo), + * should be '0' indicating no sharding. + * - cls_rgw_reshard_status reshard_status (struct RGWBucketInfo) + * + * XXX: + * - Quota stored as blob .. should be linked to quota table. + * - WebsiteConf stored as BLOB..if required, should be split + * - Storing bucket_version (struct RGWBucket), objv_tracker + * (struct RGWBucketInfo) separately. Are they same? + * + */ + "CREATE TABLE IF NOT EXISTS '{}' ( \ + BucketName TEXT NOT NULL UNIQUE , \ + Tenant TEXT, \ + Marker TEXT, \ + BucketID TEXT, \ + Size INTEGER, \ + SizeRounded INTEGER,\ + CreationTime BLOB, \ + Count INTEGER, \ + PlacementName TEXT , \ + PlacementStorageClass TEXT , \ + OwnerID TEXT NOT NULL, \ + Flags INTEGER, \ + Zonegroup TEXT, \ + HasInstanceObj BOOLEAN, \ + Quota BLOB, \ + RequesterPays BOOLEAN, \ + HasWebsite BOOLEAN, \ + WebsiteConf BLOB, \ + SwiftVersioning BOOLEAN, \ + SwiftVerLocation TEXT, \ + MdsearchConfig BLOB, \ + NewBucketInstanceID TEXT,\ + ObjectLock BLOB, \ + SyncPolicyInfoGroups BLOB, \ + BucketAttrs BLOB, \ + BucketVersion INTEGER, \ + BucketVersionTag TEXT, \ + Mtime BLOB, \ + PRIMARY KEY (BucketName) \ + FOREIGN KEY (OwnerID) \ + REFERENCES '{}' (UserID) ON DELETE CASCADE ON UPDATE CASCADE \n);"; + const string CreateObjectTableQ = + "CREATE TABLE IF NOT EXISTS '{}' ( \ + BucketName TEXT NOT NULL , \ + ObjectName TEXT NOT NULL , \ + PRIMARY KEY (BucketName, ObjectName), \ + FOREIGN KEY (BucketName) \ + REFERENCES '{}' (BucketName) ON DELETE CASCADE ON UPDATE CASCADE \n);"; + const string CreateObjectDataTableQ = + "CREATE TABLE IF NOT EXISTS '{}' ( \ + BucketName TEXT NOT NULL , \ + ObjectName TEXT NOT NULL , \ + Offset INTEGER NOT NULL, \ + Data BLOB, \ + Size INTEGER NOT NULL, \ + PRIMARY KEY (BucketName, ObjectName, Offset), \ + FOREIGN KEY (BucketName, ObjectName) \ + REFERENCES '{}' (BucketName, ObjectName) ON DELETE CASCADE ON UPDATE CASCADE \n);"; + + const string CreateQuotaTableQ = + "CREATE TABLE IF NOT EXISTS '{}' ( \ + QuotaID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE , \ + MaxSizeSoftThreshold INTEGER , \ + MaxObjsSoftThreshold INTEGER , \ + MaxSize INTEGER , \ + MaxObjects INTEGER , \ + Enabled Boolean , \ + CheckOnRaw Boolean \n);"; + + const string DropQ = "DROP TABLE IF EXISTS '{}'"; + const string ListAllQ = "SELECT * from '{}'"; + + public: + DBOp() {}; + virtual ~DBOp() {}; + + string CreateTableSchema(string type, DBOpParams *params) { + if (!type.compare("User")) + return fmt::format(CreateUserTableQ.c_str(), + params->user_table.c_str()); + if (!type.compare("Bucket")) + return fmt::format(CreateBucketTableQ.c_str(), + params->bucket_table.c_str(), + params->user_table.c_str()); + if (!type.compare("Object")) + return fmt::format(CreateObjectTableQ.c_str(), + params->object_table.c_str(), + params->bucket_table.c_str()); + if (!type.compare("ObjectData")) + return fmt::format(CreateObjectDataTableQ.c_str(), + params->objectdata_table.c_str(), + params->object_table.c_str()); + if (!type.compare("Quota")) + return fmt::format(CreateQuotaTableQ.c_str(), + params->quota_table.c_str()); + + dbout(L_ERR)<<"Incorrect table type("< objectmap; + pthread_mutex_t mutex; // to protect objectmap and other shared + // objects if any. This mutex is taken + // before processing every fop (i.e, in + // ProcessOp()). If required this can be + // made further granular by taking separate + // locks for objectmap and db operations etc. + + protected: + void *db; + CephContext *cct; + uint64_t max_bucket_id = 0; + + public: + DBStore(string db_name) : db_name(db_name), + user_table(db_name+".user.table"), + bucket_table(db_name+".bucket.table"), + quota_table(db_name+".quota.table") + {} + /* DBStore() {}*/ + + DBStore() : db_name("default_db"), + user_table("user.table"), + bucket_table("bucket.table"), + quota_table("quota.table") + {} + virtual ~DBStore() {} + + const string getDBname() { return db_name + ".db"; } + const string getUserTable() { return user_table; } + const string getBucketTable() { return bucket_table; } + const string getQuotaTable() { return quota_table; } + + map getObjectMap(); + + struct DBOps dbops; // DB operations, make it private? + + void set_context(CephContext *_cct) { + cct = _cct; + } + + CephContext *ctx() { return cct; } + int Initialize(string logfile, int loglevel); + int Destroy(); + int LockInit(); + int LockDestroy(); + int Lock(); + int Unlock(); + + int InitializeParams(string Op, DBOpParams *params); + int ProcessOp(string Op, DBOpParams *params); + DBOp* getDBOp(string Op, struct DBOpParams *params); + int objectmapInsert(string bucket, void *ptr); + int objectmapDelete(string bucket); + + virtual void *openDB() { return NULL; } + virtual int closeDB() { return 0; } + virtual int createTables() { return 0; } + virtual int InitializeDBOps() { return 0; } + virtual int FreeDBOps() { return 0; } + virtual int InitPrepareParams(DBOpPrepareParams ¶ms) = 0; + + virtual int ListAllBuckets(DBOpParams *params) = 0; + virtual int ListAllUsers(DBOpParams *params) = 0; + virtual int ListAllObjects(DBOpParams *params) = 0; + + int get_user(const std::string& query_str, const std::string& query_str_val, + RGWUserInfo& uinfo, map *pattrs, + RGWObjVersionTracker *pobjv_tracker); + int store_user(RGWUserInfo& uinfo, bool exclusive, map *pattrs, + RGWObjVersionTracker *pobjv_tracker, RGWUserInfo* pold_info); + int remove_user(RGWUserInfo& uinfo, RGWObjVersionTracker *pobjv_tracker); + int get_bucket_info(const std::string& query_str, + const std::string& query_str_val, + RGWBucketInfo& info, rgw::sal::Attrs* pattrs, ceph::real_time* pmtime, + obj_version* pbucket_version); + int create_bucket(const RGWUserInfo& owner, rgw_bucket& bucket, + const string& zonegroup_id, + const rgw_placement_rule& placement_rule, + const string& swift_ver_location, + const RGWQuotaInfo * pquota_info, + map& attrs, + RGWBucketInfo& info, + obj_version *pobjv, + obj_version *pep_objv, + real_time creation_time, + rgw_bucket *pmaster_bucket, + uint32_t *pmaster_num_shards, + optional_yield y, + const DoutPrefixProvider *dpp, + bool exclusive); + + int next_bucket_id() { return ++max_bucket_id; }; + + int remove_bucket(const RGWBucketInfo info); + int list_buckets(const rgw_user& user, + const string& marker, + const string& end_marker, + uint64_t max, + bool need_stats, + RGWUserBuckets *buckets, + bool *is_truncated); + int update_bucket(const std::string& query_str, RGWBucketInfo& info, bool exclusive, + const rgw_user* powner_id, map* pattrs, + ceph::real_time* pmtime, RGWObjVersionTracker* pobjv); +}; +#endif diff --git a/src/rgw/store/dbstore/common/dbstore_log.h b/src/rgw/store/dbstore/common/dbstore_log.h new file mode 100644 index 000000000000..daff539c296a --- /dev/null +++ b/src/rgw/store/dbstore/common/dbstore_log.h @@ -0,0 +1,30 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef DB_STORE_LOG_H +#define DB_STORE_LOG_H + +#include +#include +#include +#include +#include +#include + +using namespace std; + +#define L_ERR 0 +#define L_EVENT 1 // Default LogLevel +#define L_DEBUG 2 +#define L_FULLDEBUG 3 + +extern int LogLevel; +extern string LogFile; +extern ofstream fileout; +extern ostream *dbout; + +#define dbout_prefix *dbout<<__PRETTY_FUNCTION__<<":-" + +#define dbout(n) if (n <= LogLevel) dbout_prefix + +#endif diff --git a/src/rgw/store/dbstore/dbstore_main.cc b/src/rgw/store/dbstore/dbstore_main.cc new file mode 100644 index 000000000000..ad9801bfc5fb --- /dev/null +++ b/src/rgw/store/dbstore/dbstore_main.cc @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include + +#include "dbstore_mgr.h" +#include +#include + +struct thr_args { + class DBStore *dbs; + int thr_id; +}; + +int loglevel = L_FULLDEBUG; + +void* process(void *arg) +{ + struct thr_args *t_args = (struct thr_args*)arg; + + class DBStore *db = t_args->dbs; + int thr_id = t_args->thr_id; + int ret = -1; + + dbout(L_EVENT)<<"Entered thread:"<InitializeParams("InsertUser", ¶ms); + + params.op.user.uinfo.display_name = user1; + params.op.user.uinfo.user_id.tenant = "tenant"; + params.op.user.uinfo.user_id.id = user1; + params.op.user.uinfo.suspended = 123; + params.op.user.uinfo.max_buckets = 456; + params.op.user.uinfo.assumed_role_arn = "role"; + params.op.user.uinfo.placement_tags.push_back("tags1"); + params.op.user.uinfo.placement_tags.push_back("tags2"); + + RGWAccessKey k1("id1", "key1"); + RGWAccessKey k2("id2", "key2"); + params.op.user.uinfo.access_keys.insert(make_pair("key1", k1)); + params.op.user.uinfo.access_keys.insert(make_pair("key2", k2)); + + ret = db->ProcessOp("InsertUser", ¶ms); + cout << "InsertUser return value: " << ret << "\n"; + + struct DBOpParams params2 = {}; + params.op.user.uinfo.user_id.tenant = "tenant2"; + + db->InitializeParams("GetUser", ¶ms2); + params2.op.user.uinfo.display_name = user1; + ret = db->ProcessOp("GetUser", ¶ms2); + + cout << "GetUser return value: " << ret << "\n"; + + cout << "tenant: " << params2.op.user.uinfo.user_id.tenant << "\n"; + cout << "suspended: " << (int)params2.op.user.uinfo.suspended << "\n"; + cout << "assumed_role_arn: " << params2.op.user.uinfo.assumed_role_arn << "\n"; + + list::iterator it = params2.op.user.uinfo.placement_tags.begin(); + + while (it != params2.op.user.uinfo.placement_tags.end()) { + cout << "list = " << *it << "\n"; + it++; + } + + map::iterator it2 = params2.op.user.uinfo.access_keys.begin(); + + while (it2 != params2.op.user.uinfo.access_keys.end()) { + cout << "keys = " << it2->first << "\n"; + RGWAccessKey k = it2->second; + cout << "id = " << k.id << ", keys = " << k.key << "\n"; + it2++; + } + + params.op.bucket.info.bucket.name = bucketa; + db->ProcessOp("InsertBucket", ¶ms); + + params.op.user.uinfo.display_name = user2; + params.op.user.uinfo.user_id.id = user2; + db->ProcessOp("InsertUser", ¶ms); + + params.op.bucket.info.bucket.name = bucketb; + db->ProcessOp("InsertBucket", ¶ms); + + db->ProcessOp("GetUser", ¶ms); + db->ProcessOp("GetBucket", ¶ms); + + db->ListAllUsers(¶ms); + db->ListAllBuckets(¶ms); + + params.op.bucket.info.bucket.name = bucketb; + + db->ProcessOp("RemoveBucket", ¶ms); + + params.op.user.uinfo.user_id.id = user2; + db->ProcessOp("RemoveUser", ¶ms); + + db->ListAllUsers(¶ms); + db->ListAllBuckets(¶ms); + dbout(L_EVENT)<<"Exiting thread:"<::iterator iter; + DBStore *dbs = nullptr; + pair::iterator,bool> ret; + + if (tenant.empty()) + return default_dbstore; + + if (DBStoreHandles.empty()) + goto not_found; + + iter = DBStoreHandles.find(tenant); + + if (iter != DBStoreHandles.end()) + return iter->second; + +not_found: + if (!create) + return NULL; + + dbs = createDBStore(tenant); + + return dbs; +} + +/* Create DBStore instance */ +DBStore* DBStoreManager::createDBStore(string tenant) { + DBStore *dbs = nullptr; + pair::iterator,bool> ret; + + /* Create the handle */ +#ifdef SQLITE_ENABLED + dbs = new SQLiteDB(tenant); +#else + dbs = new DBStore(tenant); +#endif + + /* API is DBStore::Initialize(string logfile, int loglevel); + * If none provided, by default write in to dbstore.log file + * created in current working directory with loglevel L_EVENT. + * XXX: need to align these logs to ceph location + */ + if (dbs->Initialize("", -1) < 0) { + cout<<"^^^^^^^^^^^^DB initialization failed for tenant("<(tenant, dbs)); + + /* + * Its safe to check for already existing entry (just + * incase other thread raced and created the entry) + */ + if (ret.second == false) { + /* Entry already created by another thread */ + delete dbs; + + dbs = ret.first->second; + } + + return dbs; +} + +void DBStoreManager::deleteDBStore(string tenant) { + map::iterator iter; + DBStore *dbs = nullptr; + + if (tenant.empty() || DBStoreHandles.empty()) + return; + + /* XXX: Check if we need to perform this operation under a lock */ + iter = DBStoreHandles.find(tenant); + + if (iter == DBStoreHandles.end()) + return; + + dbs = iter->second; + + DBStoreHandles.erase(iter); + dbs->Destroy(); + delete dbs; + + return; +} + +void DBStoreManager::deleteDBStore(DBStore *dbs) { + if (!dbs) + return; + + (void)deleteDBStore(dbs->getDBname()); +} + + +void DBStoreManager::destroyAllHandles(){ + map::iterator iter; + DBStore *dbs = nullptr; + + if (DBStoreHandles.empty()) + return; + + for (iter = DBStoreHandles.begin(); iter != DBStoreHandles.end(); + ++iter) { + dbs = iter->second; + dbs->Destroy(); + delete dbs; + } + + DBStoreHandles.clear(); + + return; +} + + diff --git a/src/rgw/store/dbstore/dbstore_mgr.h b/src/rgw/store/dbstore/dbstore_mgr.h new file mode 100644 index 000000000000..780de2dd5851 --- /dev/null +++ b/src/rgw/store/dbstore/dbstore_mgr.h @@ -0,0 +1,43 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#pragma once + +#include +#include +#include +#include +#include +#include + +using namespace std; + +/* XXX: Should be a dbstore config option */ +const static string default_tenant = "default_ns"; + +using namespace std; +class DBStore; + +class DBStoreManager { +private: + map DBStoreHandles; + DBStore *default_dbstore = NULL; + +public: + DBStoreManager(): DBStoreHandles() { + default_dbstore = createDBStore(default_tenant); + }; + ~DBStoreManager() { destroyAllHandles(); }; + + /* XXX: TBD based on testing + * 1) Lock to protect DBStoreHandles map. + * 2) Refcount of each DBStore to protect from + * being deleted while using it. + */ + DBStore* getDBStore () { return default_dbstore; }; + DBStore* getDBStore (string tenant, bool create); + DBStore* createDBStore (string tenant); + void deleteDBStore (string tenant); + void deleteDBStore (DBStore* db); + void destroyAllHandles(); +}; diff --git a/src/rgw/store/dbstore/sqlite/CMakeLists.txt b/src/rgw/store/dbstore/sqlite/CMakeLists.txt new file mode 100644 index 000000000000..02fdc260f947 --- /dev/null +++ b/src/rgw/store/dbstore/sqlite/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.14.0) +project(sqlite_db) + +find_package(SQLITE3 REQUIRED) + +set(sqlite_db_srcs + sqliteDB.h + sqliteDB.cc) + +include_directories(${CMAKE_INCLUDE_DIR}) + +set(SQLITE_COMPILE_FLAGS "-DSQLITE_THREADSAFE=1") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SQLITE_COMPILE_FLAGS}") + +add_library(sqlite_db ${sqlite_db_srcs}) +target_link_libraries(sqlite_db sqlite3 dbstore_lib) diff --git a/src/rgw/store/dbstore/sqlite/sqliteDB.cc b/src/rgw/store/dbstore/sqlite/sqliteDB.cc new file mode 100644 index 000000000000..f3daf12972a4 --- /dev/null +++ b/src/rgw/store/dbstore/sqlite/sqliteDB.cc @@ -0,0 +1,1767 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "sqliteDB.h" + +#define SQL_PREPARE(params, sdb, stmt, ret, Op) \ + do { \ + string schema; \ + schema = Schema(params); \ + sqlite3_prepare_v2 (*sdb, schema.c_str(), \ + -1, &stmt , NULL); \ + if (!stmt) { \ + cout<<"failed to prepare statement " \ + <<"for Op("<(blob), blob_len); \ + \ + decode(param, b); \ + }while(0); + +#define SQL_EXECUTE(params, stmt, cbk, args...) \ + do{ \ + if (!stmt) { \ + ret = Prepare(params); \ + } \ + \ + if (!stmt) { \ + cout<<"No prepared statement \n"; \ + goto out; \ + } \ + \ + ret = Bind(params); \ + if (ret) { \ + cout<<"Bind parameters failed for stmt(" <op, stmt, cbk); \ + \ + Reset(stmt); \ + \ + if (ret) { \ + cout<<"Execution failed for stmt(" <db); + dbops.RemoveUser = new SQLRemoveUser(&this->db); + dbops.GetUser = new SQLGetUser(&this->db); + dbops.InsertBucket = new SQLInsertBucket(&this->db); + dbops.UpdateBucket = new SQLUpdateBucket(&this->db); + dbops.RemoveBucket = new SQLRemoveBucket(&this->db); + dbops.GetBucket = new SQLGetBucket(&this->db); + dbops.ListUserBuckets = new SQLListUserBuckets(&this->db); + + return 0; +} + +int SQLiteDB::FreeDBOps() +{ + delete dbops.InsertUser; + delete dbops.RemoveUser; + delete dbops.GetUser; + delete dbops.InsertBucket; + delete dbops.UpdateBucket; + delete dbops.RemoveBucket; + delete dbops.GetBucket; + delete dbops.ListUserBuckets; + + return 0; +} + +void *SQLiteDB::openDB() +{ + string dbname; + int rc = 0; + + dbname = getDBname(); + if (dbname.empty()) { + dbout(L_ERR)<<"dbname is NULL\n"; + goto out; + } + + rc = sqlite3_open_v2(dbname.c_str(), (sqlite3**)&db, + SQLITE_OPEN_READWRITE | + SQLITE_OPEN_CREATE | + SQLITE_OPEN_FULLMUTEX, + NULL); + + if (rc) { + dbout(L_ERR)<<"Cant open "<user_table); + + ret = exec(schema.c_str(), NULL); + if (ret) + dbout(L_ERR)<<"DeleteUserTable failed \n"; + + dbout(L_FULLDEBUG)<<"DeleteUserTable suceeded \n"; + + return ret; +} + +int SQLiteDB::DeleteBucketTable(DBOpParams *params) +{ + int ret = -1; + string schema; + + schema = DeleteTableSchema(params->bucket_table); + + ret = exec(schema.c_str(), NULL); + if (ret) + dbout(L_ERR)<<"DeletebucketTable failed \n"; + + dbout(L_FULLDEBUG)<<"DeletebucketTable suceeded \n"; + + return ret; +} + +int SQLiteDB::DeleteObjectTable(DBOpParams *params) +{ + int ret = -1; + string schema; + + schema = DeleteTableSchema(params->object_table); + + ret = exec(schema.c_str(), NULL); + if (ret) + dbout(L_ERR)<<"DeleteObjectTable failed \n"; + + dbout(L_FULLDEBUG)<<"DeleteObjectTable suceeded \n"; + + return ret; +} + +int SQLiteDB::DeleteObjectDataTable(DBOpParams *params) +{ + int ret = -1; + string schema; + + schema = DeleteTableSchema(params->objectdata_table); + + ret = exec(schema.c_str(), NULL); + if (ret) + dbout(L_ERR)<<"DeleteObjectDataTable failed \n"; + + dbout(L_FULLDEBUG)<<"DeleteObjectDataTable suceeded \n"; + + return ret; +} + +int SQLiteDB::ListAllUsers(DBOpParams *params) +{ + int ret = -1; + string schema; + + schema = ListTableSchema(params->user_table); + cout<<"########### Listing all Users #############\n"; + ret = exec(schema.c_str(), &list_callback); + if (ret) + dbout(L_ERR)<<"GetUsertable failed \n"; + + dbout(L_FULLDEBUG)<<"GetUserTable suceeded \n"; + + return ret; +} + +int SQLiteDB::ListAllBuckets(DBOpParams *params) +{ + int ret = -1; + string schema; + + schema = ListTableSchema(params->bucket_table); + + cout<<"########### Listing all Buckets #############\n"; + ret = exec(schema.c_str(), &list_callback); + if (ret) + dbout(L_ERR)<<"Listbuckettable failed \n"; + + dbout(L_FULLDEBUG)<<"ListbucketTable suceeded \n"; + + return ret; +} + +int SQLiteDB::ListAllObjects(DBOpParams *params) +{ + int ret = -1; + string schema; + map::iterator iter; + map objectmap; + string bucket; + + cout<<"########### Listing all Objects #############\n"; + + objectmap = getObjectMap(); + + if (objectmap.empty()) + dbout(L_DEBUG)<<"objectmap empty \n"; + + for (iter = objectmap.begin(); iter != objectmap.end(); ++iter) { + bucket = iter->first; + params->object_table = bucket + + ".object.table"; + schema = ListTableSchema(params->object_table); + + ret = exec(schema.c_str(), &list_callback); + if (ret) + dbout(L_ERR)<<"ListObjecttable failed \n"; + + dbout(L_FULLDEBUG)<<"ListObjectTable suceeded \n"; + } + + return ret; +} + +int SQLObjectOp::InitializeObjectOps() +{ + InsertObject = new SQLInsertObject(sdb); + RemoveObject = new SQLRemoveObject(sdb); + ListObject = new SQLListObject(sdb); + PutObjectData = new SQLPutObjectData(sdb); + GetObjectData = new SQLGetObjectData(sdb); + DeleteObjectData = new SQLDeleteObjectData(sdb); + + return 0; +} + +int SQLObjectOp::FreeObjectOps() +{ + delete InsertObject; + delete RemoveObject; + delete ListObject; + delete PutObjectData; + delete GetObjectData; + delete DeleteObjectData; + + return 0; +} + +int SQLInsertUser::Prepare(struct DBOpParams *params) +{ + int ret = -1; + struct DBOpPrepareParams p_params = PrepareParams; + + if (!*sdb) { + dbout(L_ERR)<<"In SQLInsertUser - no db\n"; + goto out; + } + + p_params.user_table = params->user_table; + + SQL_PREPARE(p_params, sdb, stmt, ret, "PrepareInsertUser"); +out: + return ret; +} + +int SQLInsertUser::Bind(struct DBOpParams *params) +{ + int index = -1; + int rc = 0; + struct DBOpPrepareParams p_params = PrepareParams; + + SQL_BIND_INDEX(stmt, index, p_params.op.user.tenant.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.user.uinfo.user_id.tenant.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.user_id.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.user.uinfo.user_id.id.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.ns.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.user.uinfo.user_id.ns.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.display_name.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.user.uinfo.display_name.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.user_email.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.user.uinfo.user_email.c_str(), sdb); + + if (!params->op.user.uinfo.access_keys.empty()) { + string access_key; + string key; + map::const_iterator it = + params->op.user.uinfo.access_keys.begin(); + const RGWAccessKey& k = it->second; + access_key = k.id; + key = k.key; + + SQL_BIND_INDEX(stmt, index, p_params.op.user.access_keys_id.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, access_key.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.access_keys_secret.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, key.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.access_keys.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.user.uinfo.access_keys, sdb); + } + + SQL_BIND_INDEX(stmt, index, p_params.op.user.swift_keys.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.user.uinfo.swift_keys, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.subusers.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.user.uinfo.subusers, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.suspended.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.user.uinfo.suspended, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.max_buckets.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.user.uinfo.max_buckets, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.op_mask.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.user.uinfo.op_mask, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.user_caps.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.user.uinfo.caps, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.admin.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.user.uinfo.admin, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.system.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.user.uinfo.system, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.placement_name.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.user.uinfo.default_placement.name.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.placement_storage_class.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.user.uinfo.default_placement.storage_class.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.placement_tags.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.user.uinfo.placement_tags, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.bucket_quota.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.user.uinfo.bucket_quota, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.temp_url_keys.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.user.uinfo.temp_url_keys, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.user_quota.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.user.uinfo.user_quota, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.type.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.user.uinfo.type, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.mfa_ids.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.user.uinfo.mfa_ids, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.assumed_role_arn.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.user.uinfo.assumed_role_arn.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.user_attrs.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.user.user_attrs, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.user_ver.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.user.user_version.ver, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.user.user_ver_tag.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.user.user_version.tag.c_str(), sdb); + +out: + return rc; +} + +int SQLInsertUser::Execute(struct DBOpParams *params) +{ + int ret = -1; + + SQL_EXECUTE(params, stmt, NULL); +out: + return ret; +} + +int SQLRemoveUser::Prepare(struct DBOpParams *params) +{ + int ret = -1; + struct DBOpPrepareParams p_params = PrepareParams; + + if (!*sdb) { + dbout(L_ERR)<<"In SQLRemoveUser - no db\n"; + goto out; + } + + p_params.user_table = params->user_table; + + SQL_PREPARE(p_params, sdb, stmt, ret, "PrepareRemoveUser"); +out: + return ret; +} + +int SQLRemoveUser::Bind(struct DBOpParams *params) +{ + int index = -1; + int rc = 0; + struct DBOpPrepareParams p_params = PrepareParams; + + SQL_BIND_INDEX(stmt, index, p_params.op.user.user_id.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.user.uinfo.user_id.id.c_str(), sdb); + +out: + return rc; +} + +int SQLRemoveUser::Execute(struct DBOpParams *params) +{ + int ret = -1; + + SQL_EXECUTE(params, stmt, NULL); +out: + return ret; +} + +int SQLGetUser::Prepare(struct DBOpParams *params) +{ + int ret = -1; + struct DBOpPrepareParams p_params = PrepareParams; + + if (!*sdb) { + dbout(L_ERR)<<"In SQLGetUser - no db\n"; + goto out; + } + + p_params.user_table = params->user_table; + p_params.op.query_str = params->op.query_str; + + if (params->op.query_str == "email") { + SQL_PREPARE(p_params, sdb, email_stmt, ret, "PrepareGetUser"); + } else if (params->op.query_str == "access_key") { + SQL_PREPARE(p_params, sdb, ak_stmt, ret, "PrepareGetUser"); + } else if (params->op.query_str == "user_id") { + SQL_PREPARE(p_params, sdb, userid_stmt, ret, "PrepareGetUser"); + } else { // by default by userid + SQL_PREPARE(p_params, sdb, stmt, ret, "PrepareGetUser"); + } +out: + return ret; +} + +int SQLGetUser::Bind(struct DBOpParams *params) +{ + int index = -1; + int rc = 0; + struct DBOpPrepareParams p_params = PrepareParams; + + if (params->op.query_str == "email") { + SQL_BIND_INDEX(email_stmt, index, p_params.op.user.user_email.c_str(), sdb); + SQL_BIND_TEXT(email_stmt, index, params->op.user.uinfo.user_email.c_str(), sdb); + } else if (params->op.query_str == "access_key") { + if (!params->op.user.uinfo.access_keys.empty()) { + string access_key; + map::const_iterator it = + params->op.user.uinfo.access_keys.begin(); + const RGWAccessKey& k = it->second; + access_key = k.id; + + SQL_BIND_INDEX(ak_stmt, index, p_params.op.user.access_keys_id.c_str(), sdb); + SQL_BIND_TEXT(ak_stmt, index, access_key.c_str(), sdb); + } + } else if (params->op.query_str == "user_id") { + SQL_BIND_INDEX(userid_stmt, index, p_params.op.user.tenant.c_str(), sdb); + SQL_BIND_TEXT(userid_stmt, index, params->op.user.uinfo.user_id.tenant.c_str(), sdb); + + SQL_BIND_INDEX(userid_stmt, index, p_params.op.user.user_id.c_str(), sdb); + SQL_BIND_TEXT(userid_stmt, index, params->op.user.uinfo.user_id.id.c_str(), sdb); + + SQL_BIND_INDEX(userid_stmt, index, p_params.op.user.ns.c_str(), sdb); + SQL_BIND_TEXT(userid_stmt, index, params->op.user.uinfo.user_id.ns.c_str(), sdb); + } else { // by default by userid + SQL_BIND_INDEX(stmt, index, p_params.op.user.user_id.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.user.uinfo.user_id.id.c_str(), sdb); + } + +out: + return rc; +} + +int SQLGetUser::Execute(struct DBOpParams *params) +{ + int ret = -1; + + if (params->op.query_str == "email") { + SQL_EXECUTE(params, email_stmt, list_user); + } else if (params->op.query_str == "access_key") { + SQL_EXECUTE(params, ak_stmt, list_user); + } else if (params->op.query_str == "user_id") { + SQL_EXECUTE(params, userid_stmt, list_user); + } else { // by default by userid + SQL_EXECUTE(params, stmt, list_user); + } + +out: + return ret; +} + +int SQLInsertBucket::Prepare(struct DBOpParams *params) +{ + int ret = -1; + struct DBOpPrepareParams p_params = PrepareParams; + + if (!*sdb) { + dbout(L_ERR)<<"In SQLInsertBucket - no db\n"; + goto out; + } + + p_params.bucket_table = params->bucket_table; + + SQL_PREPARE(p_params, sdb, stmt, ret, "PrepareInsertBucket"); + +out: + return ret; +} + +int SQLInsertBucket::Bind(struct DBOpParams *params) +{ + int index = -1; + int rc = 0; + struct DBOpPrepareParams p_params = PrepareParams; + + SQL_BIND_INDEX(stmt, index, p_params.op.user.user_id.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.user.uinfo.user_id.id.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.bucket_name.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.tenant.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.bucket.tenant.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.marker.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.bucket.marker.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.bucket_id.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.bucket.bucket_id.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.size.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.bucket.ent.size, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.size_rounded.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.bucket.ent.size_rounded, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.creation_time.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.bucket.info.creation_time, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.count.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.bucket.ent.count, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.placement_name.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.placement_rule.name.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.placement_storage_class.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.placement_rule.storage_class.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.flags.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.bucket.info.flags, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.zonegroup.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.zonegroup.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.has_instance_obj.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.bucket.info.has_instance_obj, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.quota.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.bucket.info.quota, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.requester_pays.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.bucket.info.requester_pays, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.has_website.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.bucket.info.has_website, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.website_conf.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.bucket.info.website_conf, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.swift_versioning.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.bucket.info.swift_versioning, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.swift_ver_location.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.swift_ver_location.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.mdsearch_config.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.bucket.info.mdsearch_config, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.new_bucket_instance_id.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.new_bucket_instance_id.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.obj_lock.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.bucket.info.obj_lock, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.sync_policy_info_groups.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.bucket.info.sync_policy, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.bucket_attrs.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.bucket.bucket_attrs, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.bucket_ver.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.bucket.bucket_version.ver, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.bucket_ver_tag.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.bucket.bucket_version.tag.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.mtime.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(stmt, index, params->op.bucket.mtime, sdb); + +out: + return rc; +} + +int SQLInsertBucket::Execute(struct DBOpParams *params) +{ + int ret = -1; + class SQLObjectOp *ObPtr = NULL; + string bucket_name = params->op.bucket.info.bucket.name; + + ObPtr = new SQLObjectOp(sdb); + + objectmapInsert(bucket_name, ObPtr); + + params->object_table = bucket_name + ".object.table"; + + (void)createObjectTable(params); + + SQL_EXECUTE(params, stmt, NULL); +out: + return ret; +} + +int SQLUpdateBucket::Prepare(struct DBOpParams *params) +{ + int ret = -1; + struct DBOpPrepareParams p_params = PrepareParams; + + if (!*sdb) { + dbout(L_ERR)<<"In SQLUpdateBucket - no db\n"; + goto out; + } + + p_params.op.query_str = params->op.query_str; + p_params.bucket_table = params->bucket_table; + + if (params->op.query_str == "attrs") { + SQL_PREPARE(p_params, sdb, attrs_stmt, ret, "PrepareUpdateBucket"); + } else if (params->op.query_str == "owner") { + SQL_PREPARE(p_params, sdb, owner_stmt, ret, "PrepareUpdateBucket"); + } else if (params->op.query_str == "info") { + SQL_PREPARE(p_params, sdb, info_stmt, ret, "PrepareUpdateBucket"); + } else { + dbout(L_ERR)<<"In SQLUpdateBucket invalid query_str:" << + params->op.query_str << "\n"; + goto out; + } + +out: + return ret; +} + +int SQLUpdateBucket::Bind(struct DBOpParams *params) +{ + int index = -1; + int rc = 0; + struct DBOpPrepareParams p_params = PrepareParams; + sqlite3_stmt** stmt = NULL; // Prepared statement + + /* All below fields for attrs */ + if (params->op.query_str == "attrs") { + stmt = &attrs_stmt; + } else if (params->op.query_str == "owner") { + stmt = &owner_stmt; + } else if (params->op.query_str == "info") { + stmt = &info_stmt; + } else { + dbout(L_ERR)<<"In SQLUpdateBucket invalid query_str:" << + params->op.query_str << "\n"; + goto out; + } + + if (params->op.query_str == "attrs") { + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.bucket_attrs.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(*stmt, index, params->op.bucket.bucket_attrs, sdb); + } else if (params->op.query_str == "owner") { + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.creation_time.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(*stmt, index, params->op.bucket.info.creation_time, sdb); + } else if (params->op.query_str == "info") { + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.tenant.c_str(), sdb); + SQL_BIND_TEXT(*stmt, index, params->op.bucket.info.bucket.tenant.c_str(), sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.marker.c_str(), sdb); + SQL_BIND_TEXT(*stmt, index, params->op.bucket.info.bucket.marker.c_str(), sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.bucket_id.c_str(), sdb); + SQL_BIND_TEXT(*stmt, index, params->op.bucket.info.bucket.bucket_id.c_str(), sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.creation_time.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(*stmt, index, params->op.bucket.info.creation_time, sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.count.c_str(), sdb); + SQL_BIND_INT(*stmt, index, params->op.bucket.ent.count, sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.placement_name.c_str(), sdb); + SQL_BIND_TEXT(*stmt, index, params->op.bucket.info.placement_rule.name.c_str(), sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.placement_storage_class.c_str(), sdb); + SQL_BIND_TEXT(*stmt, index, params->op.bucket.info.placement_rule.storage_class.c_str(), sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.flags.c_str(), sdb); + SQL_BIND_INT(*stmt, index, params->op.bucket.info.flags, sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.zonegroup.c_str(), sdb); + SQL_BIND_TEXT(*stmt, index, params->op.bucket.info.zonegroup.c_str(), sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.has_instance_obj.c_str(), sdb); + SQL_BIND_INT(*stmt, index, params->op.bucket.info.has_instance_obj, sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.quota.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(*stmt, index, params->op.bucket.info.quota, sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.requester_pays.c_str(), sdb); + SQL_BIND_INT(*stmt, index, params->op.bucket.info.requester_pays, sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.has_website.c_str(), sdb); + SQL_BIND_INT(*stmt, index, params->op.bucket.info.has_website, sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.website_conf.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(*stmt, index, params->op.bucket.info.website_conf, sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.swift_versioning.c_str(), sdb); + SQL_BIND_INT(*stmt, index, params->op.bucket.info.swift_versioning, sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.swift_ver_location.c_str(), sdb); + SQL_BIND_TEXT(*stmt, index, params->op.bucket.info.swift_ver_location.c_str(), sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.mdsearch_config.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(*stmt, index, params->op.bucket.info.mdsearch_config, sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.new_bucket_instance_id.c_str(), sdb); + SQL_BIND_TEXT(*stmt, index, params->op.bucket.info.new_bucket_instance_id.c_str(), sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.obj_lock.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(*stmt, index, params->op.bucket.info.obj_lock, sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.sync_policy_info_groups.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(*stmt, index, params->op.bucket.info.sync_policy, sdb); + } + + SQL_BIND_INDEX(*stmt, index, p_params.op.user.user_id.c_str(), sdb); + SQL_BIND_TEXT(*stmt, index, params->op.user.uinfo.user_id.id.c_str(), sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.bucket_name.c_str(), sdb); + SQL_BIND_TEXT(*stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.bucket_ver.c_str(), sdb); + SQL_BIND_INT(*stmt, index, params->op.bucket.bucket_version.ver, sdb); + + SQL_BIND_INDEX(*stmt, index, p_params.op.bucket.mtime.c_str(), sdb); + SQL_ENCODE_BLOB_PARAM(*stmt, index, params->op.bucket.mtime, sdb); + +out: + return rc; +} + +int SQLUpdateBucket::Execute(struct DBOpParams *params) +{ + int ret = -1; + sqlite3_stmt** stmt = NULL; // Prepared statement + + if (params->op.query_str == "attrs") { + stmt = &attrs_stmt; + } else if (params->op.query_str == "owner") { + stmt = &owner_stmt; + } else if (params->op.query_str == "info") { + stmt = &info_stmt; + } else { + dbout(L_ERR)<<"In SQLUpdateBucket invalid query_str:" << + params->op.query_str << "\n"; + goto out; + } + + SQL_EXECUTE(params, *stmt, NULL); +out: + return ret; +} + +int SQLRemoveBucket::Prepare(struct DBOpParams *params) +{ + int ret = -1; + struct DBOpPrepareParams p_params = PrepareParams; + + if (!*sdb) { + dbout(L_ERR)<<"In SQLRemoveBucket - no db\n"; + goto out; + } + + p_params.bucket_table = params->bucket_table; + + SQL_PREPARE(p_params, sdb, stmt, ret, "PrepareRemoveBucket"); + +out: + return ret; +} + +int SQLRemoveBucket::Bind(struct DBOpParams *params) +{ + int index = -1; + int rc = 0; + struct DBOpPrepareParams p_params = PrepareParams; + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.bucket_name.c_str(), sdb); + + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb); + +out: + return rc; +} + +int SQLRemoveBucket::Execute(struct DBOpParams *params) +{ + int ret = -1; + + objectmapDelete(params->op.bucket.info.bucket.name); + + SQL_EXECUTE(params, stmt, NULL); +out: + return ret; +} + +int SQLGetBucket::Prepare(struct DBOpParams *params) +{ + int ret = -1; + struct DBOpPrepareParams p_params = PrepareParams; + + if (!*sdb) { + dbout(L_ERR)<<"In SQLGetBucket - no db\n"; + goto out; + } + + p_params.bucket_table = params->bucket_table; + p_params.user_table = params->user_table; + + SQL_PREPARE(p_params, sdb, stmt, ret, "PrepareGetBucket"); + +out: + return ret; +} + +int SQLGetBucket::Bind(struct DBOpParams *params) +{ + int index = -1; + int rc = 0; + struct DBOpPrepareParams p_params = PrepareParams; + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.bucket_name.c_str(), sdb); + + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb); + +out: + return rc; +} + +int SQLGetBucket::Execute(struct DBOpParams *params) +{ + int ret = -1; + + params->op.name = "GetBucket"; + SQL_EXECUTE(params, stmt, list_bucket); +out: + return ret; +} + +int SQLListUserBuckets::Prepare(struct DBOpParams *params) +{ + int ret = -1; + struct DBOpPrepareParams p_params = PrepareParams; + + if (!*sdb) { + dbout(L_ERR)<<"In SQLListUserBuckets - no db\n"; + goto out; + } + + p_params.bucket_table = params->bucket_table; + + SQL_PREPARE(p_params, sdb, stmt, ret, "PrepareListUserBuckets"); + +out: + return ret; +} + +int SQLListUserBuckets::Bind(struct DBOpParams *params) +{ + int index = -1; + int rc = 0; + struct DBOpPrepareParams p_params = PrepareParams; + + SQL_BIND_INDEX(stmt, index, p_params.op.user.user_id.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.user.uinfo.user_id.id.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.min_marker.c_str(), sdb); + SQL_BIND_TEXT(stmt, index, params->op.bucket.min_marker.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.list_max_count.c_str(), sdb); + SQL_BIND_INT(stmt, index, params->op.list_max_count, sdb); + +out: + return rc; +} + +int SQLListUserBuckets::Execute(struct DBOpParams *params) +{ + int ret = -1; + + SQL_EXECUTE(params, stmt, list_bucket); +out: + return ret; +} + +int SQLInsertObject::Prepare(struct DBOpParams *params) +{ + int ret = -1; + struct DBOpPrepareParams p_params = PrepareParams; + struct DBOpParams copy = *params; + string bucket_name; + + if (!*sdb) { + dbout(L_ERR)<<"In SQLInsertObject - no db\n"; + goto out; + } + + bucket_name = params->op.bucket.info.bucket.name; + p_params.object_table = bucket_name + ".object.table"; + + SQL_PREPARE(p_params, sdb, stmt, ret, "PrepareInsertObject"); + +out: + return ret; +} + +int SQLInsertObject::Bind(struct DBOpParams *params) +{ + int index = -1; + int rc = 0; + struct DBOpPrepareParams p_params = PrepareParams; + + SQL_BIND_INDEX(stmt, index, p_params.object.c_str(), sdb); + + SQL_BIND_TEXT(stmt, index, params->object.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.bucket_name.c_str(), sdb); + + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb); + +out: + return rc; +} + +int SQLInsertObject::Execute(struct DBOpParams *params) +{ + int ret = -1; + + SQL_EXECUTE(params, stmt, NULL); +out: + return ret; +} + +int SQLRemoveObject::Prepare(struct DBOpParams *params) +{ + int ret = -1; + struct DBOpPrepareParams p_params = PrepareParams; + struct DBOpParams copy = *params; + string bucket_name; + + if (!*sdb) { + dbout(L_ERR)<<"In SQLRemoveObject - no db\n"; + goto out; + } + + bucket_name = params->op.bucket.info.bucket.name; + p_params.object_table = bucket_name + ".object.table"; + copy.object_table = bucket_name + ".object.table"; + + (void)createObjectTable(©); + + SQL_PREPARE(p_params, sdb, stmt, ret, "PrepareRemoveObject"); + +out: + return ret; +} + +int SQLRemoveObject::Bind(struct DBOpParams *params) +{ + int index = -1; + int rc = 0; + struct DBOpPrepareParams p_params = PrepareParams; + + SQL_BIND_INDEX(stmt, index, p_params.object.c_str(), sdb); + + SQL_BIND_TEXT(stmt, index, params->object.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.bucket_name.c_str(), sdb); + + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb); + +out: + return rc; +} + +int SQLRemoveObject::Execute(struct DBOpParams *params) +{ + int ret = -1; + + SQL_EXECUTE(params, stmt, NULL); +out: + return ret; +} + +int SQLListObject::Prepare(struct DBOpParams *params) +{ + int ret = -1; + struct DBOpPrepareParams p_params = PrepareParams; + struct DBOpParams copy = *params; + string bucket_name; + + if (!*sdb) { + dbout(L_ERR)<<"In SQLListObject - no db\n"; + goto out; + } + + bucket_name = params->op.bucket.info.bucket.name; + p_params.object_table = bucket_name + ".object.table"; + copy.object_table = bucket_name + ".object.table"; + + (void)createObjectTable(©); + + + SQL_PREPARE(p_params, sdb, stmt, ret, "PrepareListObject"); + +out: + return ret; +} + +int SQLListObject::Bind(struct DBOpParams *params) +{ + int index = -1; + int rc = 0; + struct DBOpPrepareParams p_params = PrepareParams; + + SQL_BIND_INDEX(stmt, index, p_params.object.c_str(), sdb); + + SQL_BIND_TEXT(stmt, index, params->object.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.bucket_name.c_str(), sdb); + + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb); + +out: + return rc; +} + +int SQLListObject::Execute(struct DBOpParams *params) +{ + int ret = -1; + + SQL_EXECUTE(params, stmt, list_object); +out: + return ret; +} + +int SQLPutObjectData::Prepare(struct DBOpParams *params) +{ + int ret = -1; + struct DBOpPrepareParams p_params = PrepareParams; + struct DBOpParams copy = *params; + string bucket_name; + + if (!*sdb) { + dbout(L_ERR)<<"In SQLPutObjectData - no db\n"; + goto out; + } + + bucket_name = params->op.bucket.info.bucket.name; + p_params.object_table = bucket_name + ".object.table"; + p_params.objectdata_table = bucket_name + ".objectdata.table"; + copy.object_table = bucket_name + ".object.table"; + copy.objectdata_table = bucket_name + ".objectdata.table"; + + (void)createObjectDataTable(©); + + SQL_PREPARE(p_params, sdb, stmt, ret, "PreparePutObjectData"); + +out: + return ret; +} + +int SQLPutObjectData::Bind(struct DBOpParams *params) +{ + int index = -1; + int rc = 0; + struct DBOpPrepareParams p_params = PrepareParams; + + SQL_BIND_INDEX(stmt, index, p_params.object.c_str(), sdb); + + SQL_BIND_TEXT(stmt, index, params->object.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.bucket_name.c_str(), sdb); + + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.offset.c_str(), sdb); + + SQL_BIND_INT(stmt, 3, params->offset, sdb); + + SQL_BIND_INDEX(stmt, index, p_params.data.c_str(), sdb); + + SQL_BIND_BLOB(stmt, index, params->data.c_str(), params->data.length(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.datalen.c_str(), sdb); + + SQL_BIND_INT(stmt, index, params->data.length(), sdb); + +out: + return rc; +} + +int SQLPutObjectData::Execute(struct DBOpParams *params) +{ + int ret = -1; + + SQL_EXECUTE(params, stmt, NULL); +out: + return ret; +} + +int SQLGetObjectData::Prepare(struct DBOpParams *params) +{ + int ret = -1; + struct DBOpPrepareParams p_params = PrepareParams; + struct DBOpParams copy = *params; + string bucket_name; + + if (!*sdb) { + dbout(L_ERR)<<"In SQLGetObjectData - no db\n"; + goto out; + } + + bucket_name = params->op.bucket.info.bucket.name; + p_params.object_table = bucket_name + ".object.table"; + p_params.objectdata_table = bucket_name + ".objectdata.table"; + copy.object_table = bucket_name + ".object.table"; + copy.objectdata_table = bucket_name + ".objectdata.table"; + + (void)createObjectDataTable(©); + + SQL_PREPARE(p_params, sdb, stmt, ret, "PrepareGetObjectData"); + +out: + return ret; +} + +int SQLGetObjectData::Bind(struct DBOpParams *params) +{ + int index = -1; + int rc = 0; + struct DBOpPrepareParams p_params = PrepareParams; + + SQL_BIND_INDEX(stmt, index, p_params.object.c_str(), sdb); + + SQL_BIND_TEXT(stmt, index, params->object.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.bucket_name.c_str(), sdb); + + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb); +out: + return rc; +} + +int SQLGetObjectData::Execute(struct DBOpParams *params) +{ + int ret = -1; + + SQL_EXECUTE(params, stmt, get_objectdata); +out: + return ret; +} + +int SQLDeleteObjectData::Prepare(struct DBOpParams *params) +{ + int ret = -1; + struct DBOpPrepareParams p_params = PrepareParams; + struct DBOpParams copy = *params; + string bucket_name; + + if (!*sdb) { + dbout(L_ERR)<<"In SQLDeleteObjectData - no db\n"; + goto out; + } + + bucket_name = params->op.bucket.info.bucket.name; + p_params.object_table = bucket_name + ".object.table"; + p_params.objectdata_table = bucket_name + ".objectdata.table"; + copy.object_table = bucket_name + ".object.table"; + copy.objectdata_table = bucket_name + ".objectdata.table"; + + (void)createObjectDataTable(©); + + SQL_PREPARE(p_params, sdb, stmt, ret, "PrepareDeleteObjectData"); + +out: + return ret; +} + +int SQLDeleteObjectData::Bind(struct DBOpParams *params) +{ + int index = -1; + int rc = 0; + struct DBOpPrepareParams p_params = PrepareParams; + + SQL_BIND_INDEX(stmt, index, p_params.object.c_str(), sdb); + + SQL_BIND_TEXT(stmt, index, params->object.c_str(), sdb); + + SQL_BIND_INDEX(stmt, index, p_params.op.bucket.bucket_name.c_str(), sdb); + + SQL_BIND_TEXT(stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb); +out: + return rc; +} + +int SQLDeleteObjectData::Execute(struct DBOpParams *params) +{ + int ret = -1; + + SQL_EXECUTE(params, stmt, NULL); +out: + return ret; +} diff --git a/src/rgw/store/dbstore/sqlite/sqliteDB.h b/src/rgw/store/dbstore/sqlite/sqliteDB.h new file mode 100644 index 000000000000..70408d80e748 --- /dev/null +++ b/src/rgw/store/dbstore/sqlite/sqliteDB.h @@ -0,0 +1,324 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef SQLITE_DB_H +#define SQLITE_DB_H + +#include +#include +#include +#include +#include "rgw/store/dbstore/common/dbstore.h" + +using namespace std; + +class SQLiteDB : public DBStore, public DBOp{ + private: + sqlite3_mutex *mutex = NULL; + + public: + sqlite3_stmt *stmt = NULL; + DBOpPrepareParams PrepareParams; + + SQLiteDB(string db_name) : DBStore(db_name) { + InitPrepareParams(PrepareParams); + } + SQLiteDB(sqlite3 *dbi) : DBStore() { + db = (void*)dbi; + InitPrepareParams(PrepareParams); + } + ~SQLiteDB() {} + + int exec(const char *schema, + int (*callback)(void*,int,char**,char**)); + void *openDB(); + int closeDB(); + int Step(DBOpInfo &op, sqlite3_stmt *stmt, + int (*cbk)(DBOpInfo &op, sqlite3_stmt *stmt)); + int Reset(sqlite3_stmt *stmt); + int InitializeDBOps(); + int FreeDBOps(); + /* default value matches with sqliteDB style */ + int InitPrepareParams(DBOpPrepareParams ¶ms) { return 0; } + + + int createTables(); + int createBucketTable(DBOpParams *params); + int createUserTable(DBOpParams *params); + int createObjectTable(DBOpParams *params); + int createObjectDataTable(DBOpParams *params); + int createQuotaTable(DBOpParams *params); + + int DeleteBucketTable(DBOpParams *params); + int DeleteUserTable(DBOpParams *params); + int DeleteObjectTable(DBOpParams *params); + int DeleteObjectDataTable(DBOpParams *params); + + int ListAllBuckets(DBOpParams *params); + int ListAllUsers(DBOpParams *params); + int ListAllObjects(DBOpParams *params); +}; + +class SQLObjectOp : public ObjectOp { + private: + sqlite3 **sdb = NULL; + + public: + SQLObjectOp(sqlite3 **sdbi) : sdb(sdbi) {}; + ~SQLObjectOp() {} + + int InitializeObjectOps(); + int FreeObjectOps(); +}; + +class SQLInsertUser : public SQLiteDB, public InsertUserOp { + private: + sqlite3 **sdb = NULL; + sqlite3_stmt *stmt = NULL; // Prepared statement + + public: + SQLInsertUser(void **db) : SQLiteDB((sqlite3 *)(*db)), sdb((sqlite3 **)db) {} + ~SQLInsertUser() { + if (stmt) + sqlite3_finalize(stmt); + } + int Prepare(DBOpParams *params); + int Execute(DBOpParams *params); + int Bind(DBOpParams *params); +}; + +class SQLRemoveUser : public SQLiteDB, public RemoveUserOp { + private: + sqlite3 **sdb = NULL; + sqlite3_stmt *stmt = NULL; // Prepared statement + + public: + SQLRemoveUser(void **db) : SQLiteDB((sqlite3 *)(*db)), sdb((sqlite3 **)db) {} + ~SQLRemoveUser() { + if (stmt) + sqlite3_finalize(stmt); + } + int Prepare(DBOpParams *params); + int Execute(DBOpParams *params); + int Bind(DBOpParams *params); +}; + +class SQLGetUser : public SQLiteDB, public GetUserOp { + private: + sqlite3 **sdb = NULL; + sqlite3_stmt *stmt = NULL; // Prepared statement + sqlite3_stmt *email_stmt = NULL; // Prepared statement to query by useremail + sqlite3_stmt *ak_stmt = NULL; // Prepared statement to query by access_key_id + sqlite3_stmt *userid_stmt = NULL; // Prepared statement to query by user_id + + public: + SQLGetUser(void **db) : SQLiteDB((sqlite3 *)(*db)), sdb((sqlite3 **)db) {} + ~SQLGetUser() { + if (stmt) + sqlite3_finalize(stmt); + if (email_stmt) + sqlite3_finalize(email_stmt); + if (ak_stmt) + sqlite3_finalize(ak_stmt); + if (userid_stmt) + sqlite3_finalize(userid_stmt); + } + int Prepare(DBOpParams *params); + int Execute(DBOpParams *params); + int Bind(DBOpParams *params); +}; + +class SQLInsertBucket : public SQLiteDB, public InsertBucketOp { + private: + sqlite3 **sdb = NULL; + sqlite3_stmt *stmt = NULL; // Prepared statement + + public: + SQLInsertBucket(void **db) : SQLiteDB((sqlite3 *)(*db)), sdb((sqlite3 **)db) {} + ~SQLInsertBucket() { + if (stmt) + sqlite3_finalize(stmt); + } + int Prepare(DBOpParams *params); + int Execute(DBOpParams *params); + int Bind(DBOpParams *params); +}; + +class SQLUpdateBucket : public SQLiteDB, public UpdateBucketOp { + private: + sqlite3 **sdb = NULL; + sqlite3_stmt *info_stmt = NULL; // Prepared statement + sqlite3_stmt *attrs_stmt = NULL; // Prepared statement + sqlite3_stmt *owner_stmt = NULL; // Prepared statement + + public: + SQLUpdateBucket(void **db) : SQLiteDB((sqlite3 *)(*db)), sdb((sqlite3 **)db) {} + ~SQLUpdateBucket() { + if (info_stmt) + sqlite3_finalize(info_stmt); + if (attrs_stmt) + sqlite3_finalize(attrs_stmt); + if (owner_stmt) + sqlite3_finalize(owner_stmt); + } + int Prepare(DBOpParams *params); + int Execute(DBOpParams *params); + int Bind(DBOpParams *params); +}; + +class SQLRemoveBucket : public SQLiteDB, public RemoveBucketOp { + private: + sqlite3 **sdb = NULL; + sqlite3_stmt *stmt = NULL; // Prepared statement + + public: + SQLRemoveBucket(void **db) : SQLiteDB((sqlite3 *)(*db)), sdb((sqlite3 **)db) {} + ~SQLRemoveBucket() { + if (stmt) + sqlite3_finalize(stmt); + } + int Prepare(DBOpParams *params); + int Execute(DBOpParams *params); + int Bind(DBOpParams *params); +}; + +class SQLGetBucket : public SQLiteDB, public GetBucketOp { + private: + sqlite3 **sdb = NULL; + sqlite3_stmt *stmt = NULL; // Prepared statement + + public: + SQLGetBucket(void **db) : SQLiteDB((sqlite3 *)(*db)), sdb((sqlite3 **)db) {} + ~SQLGetBucket() { + if (stmt) + sqlite3_finalize(stmt); + } + int Prepare(DBOpParams *params); + int Execute(DBOpParams *params); + int Bind(DBOpParams *params); +}; + +class SQLListUserBuckets : public SQLiteDB, public ListUserBucketsOp { + private: + sqlite3 **sdb = NULL; + sqlite3_stmt *stmt = NULL; // Prepared statement + + public: + SQLListUserBuckets(void **db) : SQLiteDB((sqlite3 *)(*db)), sdb((sqlite3 **)db) {} + ~SQLListUserBuckets() { + if (stmt) + sqlite3_finalize(stmt); + } + int Prepare(DBOpParams *params); + int Execute(DBOpParams *params); + int Bind(DBOpParams *params); +}; + +class SQLInsertObject : public SQLiteDB, public InsertObjectOp { + private: + sqlite3 **sdb = NULL; + sqlite3_stmt *stmt = NULL; // Prepared statement + + public: + SQLInsertObject(void **db) : SQLiteDB((sqlite3 *)(*db)), sdb((sqlite3 **)db) {} + SQLInsertObject(sqlite3 **sdbi) : SQLiteDB(*sdbi), sdb(sdbi) {} + + ~SQLInsertObject() { + if (stmt) + sqlite3_finalize(stmt); + } + int Prepare(DBOpParams *params); + int Execute(DBOpParams *params); + int Bind(DBOpParams *params); +}; + +class SQLRemoveObject : public SQLiteDB, public RemoveObjectOp { + private: + sqlite3 **sdb = NULL; + sqlite3_stmt *stmt = NULL; // Prepared statement + + public: + SQLRemoveObject(void **db) : SQLiteDB((sqlite3 *)(*db)), sdb((sqlite3 **)db) {} + SQLRemoveObject(sqlite3 **sdbi) : SQLiteDB(*sdbi), sdb(sdbi) {} + + ~SQLRemoveObject() { + if (stmt) + sqlite3_finalize(stmt); + } + int Prepare(DBOpParams *params); + int Execute(DBOpParams *params); + int Bind(DBOpParams *params); +}; + +class SQLListObject : public SQLiteDB, public ListObjectOp { + private: + sqlite3 **sdb = NULL; + sqlite3_stmt *stmt = NULL; // Prepared statement + + public: + SQLListObject(void **db) : SQLiteDB((sqlite3 *)(*db)), sdb((sqlite3 **)db) {} + SQLListObject(sqlite3 **sdbi) : SQLiteDB(*sdbi), sdb(sdbi) {} + + ~SQLListObject() { + if (stmt) + sqlite3_finalize(stmt); + } + int Prepare(DBOpParams *params); + int Execute(DBOpParams *params); + int Bind(DBOpParams *params); +}; + +class SQLPutObjectData : public SQLiteDB, public PutObjectDataOp { + private: + sqlite3 **sdb = NULL; + sqlite3_stmt *stmt = NULL; // Prepared statement + + public: + SQLPutObjectData(void **db) : SQLiteDB((sqlite3 *)(*db)), sdb((sqlite3 **)db) {} + SQLPutObjectData(sqlite3 **sdbi) : SQLiteDB(*sdbi), sdb(sdbi) {} + + ~SQLPutObjectData() { + if (stmt) + sqlite3_finalize(stmt); + } + int Prepare(DBOpParams *params); + int Execute(DBOpParams *params); + int Bind(DBOpParams *params); +}; + +class SQLGetObjectData : public SQLiteDB, public GetObjectDataOp { + private: + sqlite3 **sdb = NULL; + sqlite3_stmt *stmt = NULL; // Prepared statement + + public: + SQLGetObjectData(void **db) : SQLiteDB((sqlite3 *)(*db)), sdb((sqlite3 **)db) {} + SQLGetObjectData(sqlite3 **sdbi) : SQLiteDB(*sdbi), sdb(sdbi) {} + + ~SQLGetObjectData() { + if (stmt) + sqlite3_finalize(stmt); + } + int Prepare(DBOpParams *params); + int Execute(DBOpParams *params); + int Bind(DBOpParams *params); +}; + +class SQLDeleteObjectData : public SQLiteDB, public DeleteObjectDataOp { + private: + sqlite3 **sdb = NULL; + sqlite3_stmt *stmt = NULL; // Prepared statement + + public: + SQLDeleteObjectData(void **db) : SQLiteDB((sqlite3 *)(*db)), sdb((sqlite3 **)db) {} + SQLDeleteObjectData(sqlite3 **sdbi) : SQLiteDB(*sdbi), sdb(sdbi) {} + + ~SQLDeleteObjectData() { + if (stmt) + sqlite3_finalize(stmt); + } + int Prepare(DBOpParams *params); + int Execute(DBOpParams *params); + int Bind(DBOpParams *params); +}; +#endif diff --git a/src/rgw/store/dbstore/tests/CMakeLists.txt b/src/rgw/store/dbstore/tests/CMakeLists.txt new file mode 100644 index 000000000000..e7a2a1e48db9 --- /dev/null +++ b/src/rgw/store/dbstore/tests/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.14.0) +project(dbstore-tests) + +set (CMAKE_LINK_LIBRARIES ${CMAKE_LINK_LIBRARIES} gtest) + +set(dbstore_tests_srcs + dbstore_tests.cc) + +include_directories(${CMAKE_INCLUDE_DIR}) + +add_executable(dbstore-tests ${dbstore_tests_srcs}) +target_link_libraries(dbstore-tests ${CMAKE_LINK_LIBRARIES}) diff --git a/src/rgw/store/dbstore/tests/dbstore_tests.cc b/src/rgw/store/dbstore/tests/dbstore_tests.cc new file mode 100644 index 000000000000..f99c8addb3c7 --- /dev/null +++ b/src/rgw/store/dbstore/tests/dbstore_tests.cc @@ -0,0 +1,674 @@ +#include "gtest/gtest.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace gtest { + class Environment* env; + + class Environment : public ::testing::Environment { + public: + Environment(): tenant("default_ns"), db(nullptr), + db_type("SQLite"), ret(-1) {} + + Environment(string tenantname, string db_typename): + tenant("tenantname"), db(nullptr), + db_type("db_typename"), ret(-1) {} + + virtual ~Environment() {} + + void SetUp() override { + if (!db_type.compare("SQLite")) { + db = new SQLiteDB(tenant); + ASSERT_TRUE(db != nullptr); + + ret = db->Initialize("", -1); + ASSERT_GE(ret, 0); + } + } + + void TearDown() override { + if (!db) + return; + db->Destroy(); + delete db; + } + + string tenant; + class DBStore *db; + string db_type; + int ret; + }; +} + +ceph::real_time bucket_mtime = real_clock::now(); +string marker1; + +namespace { + + class DBStoreBaseTest : public ::testing::Test { + protected: + int ret; + DBStore *db = nullptr; + string user1 = "user1"; + string user_id1 = "user_id1"; + string bucket1 = "bucket1"; + string object1 = "object1"; + string data = "Hello World"; + DBOpParams GlobalParams = {}; + + DBStoreBaseTest() {} + void SetUp() { + db = gtest::env->db; + ASSERT_TRUE(db != nullptr); + + GlobalParams.op.user.uinfo.display_name = user1; + GlobalParams.op.user.uinfo.user_id.id = user_id1; + GlobalParams.op.bucket.info.bucket.name = bucket1; + GlobalParams.object = object1; + GlobalParams.offset = 0; + GlobalParams.data = data; + GlobalParams.datalen = data.length(); + + /* As of now InitializeParams doesnt do anything + * special based on fop. Hence its okay to do + * global initialization once. + */ + ret = db->InitializeParams("", &GlobalParams); + ASSERT_EQ(ret, 0); + } + + void TearDown() { + } + }; +} + +TEST_F(DBStoreBaseTest, InsertUser) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + params.op.user.uinfo.user_id.tenant = "tenant"; + params.op.user.uinfo.user_email = "user1@dbstore.com"; + params.op.user.uinfo.suspended = 123; + params.op.user.uinfo.max_buckets = 456; + params.op.user.uinfo.assumed_role_arn = "role"; + params.op.user.uinfo.placement_tags.push_back("tags"); + RGWAccessKey k1("id1", "key1"); + RGWAccessKey k2("id2", "key2"); + params.op.user.uinfo.access_keys["id1"] = k1; + params.op.user.uinfo.access_keys["id2"] = k2; + params.op.user.user_version.ver = 1; + params.op.user.user_version.tag = "UserTAG"; + + ret = db->ProcessOp("InsertUser", ¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreBaseTest, GetUser) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ProcessOp("GetUser", ¶ms); + ASSERT_EQ(ret, 0); + ASSERT_EQ(params.op.user.uinfo.user_id.tenant, "tenant"); + ASSERT_EQ(params.op.user.uinfo.user_email, "user1@dbstore.com"); + ASSERT_EQ(params.op.user.uinfo.user_id.id, "user_id1"); + ASSERT_EQ(params.op.user.uinfo.suspended, 123); + ASSERT_EQ(params.op.user.uinfo.max_buckets, 456); + ASSERT_EQ(params.op.user.uinfo.assumed_role_arn, "role"); + ASSERT_EQ(params.op.user.uinfo.placement_tags.back(), "tags"); + RGWAccessKey k; + map::iterator it2 = params.op.user.uinfo.access_keys.begin(); + k = it2->second; + ASSERT_EQ(k.id, "id1"); + ASSERT_EQ(k.key, "key1"); + it2++; + k = it2->second; + ASSERT_EQ(k.id, "id2"); + ASSERT_EQ(k.key, "key2"); + +} + +TEST_F(DBStoreBaseTest, GetUserQuery) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + params.op.query_str = "email"; + params.op.user.uinfo.user_email = "user1@dbstore.com"; + + ret = db->ProcessOp("GetUser", ¶ms); + ASSERT_EQ(ret, 0); + ASSERT_EQ(params.op.user.uinfo.user_id.tenant, "tenant"); + ASSERT_EQ(params.op.user.uinfo.user_email, "user1@dbstore.com"); + ASSERT_EQ(params.op.user.uinfo.user_id.id, "user_id1"); + ASSERT_EQ(params.op.user.uinfo.suspended, 123); + ASSERT_EQ(params.op.user.uinfo.max_buckets, 456); + ASSERT_EQ(params.op.user.uinfo.assumed_role_arn, "role"); + ASSERT_EQ(params.op.user.uinfo.placement_tags.back(), "tags"); + RGWAccessKey k; + map::iterator it2 = params.op.user.uinfo.access_keys.begin(); + k = it2->second; + ASSERT_EQ(k.id, "id1"); + ASSERT_EQ(k.key, "key1"); + it2++; + k = it2->second; + ASSERT_EQ(k.id, "id2"); + ASSERT_EQ(k.key, "key2"); + +} + +TEST_F(DBStoreBaseTest, GetUserQueryByEmail) { + int ret = -1; + RGWUserInfo uinfo; + string email = "user1@dbstore.com"; + map attrs; + RGWObjVersionTracker objv; + + ret = db->get_user("email", email, uinfo, &attrs, &objv); + ASSERT_EQ(ret, 0); + ASSERT_EQ(uinfo.user_id.tenant, "tenant"); + ASSERT_EQ(uinfo.user_email, "user1@dbstore.com"); + ASSERT_EQ(uinfo.user_id.id, "user_id1"); + ASSERT_EQ(uinfo.suspended, 123); + ASSERT_EQ(uinfo.max_buckets, 456); + ASSERT_EQ(uinfo.assumed_role_arn, "role"); + ASSERT_EQ(uinfo.placement_tags.back(), "tags"); + RGWAccessKey k; + map::iterator it2 = uinfo.access_keys.begin(); + k = it2->second; + ASSERT_EQ(k.id, "id1"); + ASSERT_EQ(k.key, "key1"); + it2++; + k = it2->second; + ASSERT_EQ(k.id, "id2"); + ASSERT_EQ(k.key, "key2"); + ASSERT_EQ(objv.read_version.ver, 1); +} + +TEST_F(DBStoreBaseTest, GetUserQueryByAccessKey) { + int ret = -1; + RGWUserInfo uinfo; + string key = "id1"; + + ret = db->get_user("access_key", key, uinfo, nullptr, nullptr); + ASSERT_EQ(ret, 0); + ASSERT_EQ(uinfo.user_id.tenant, "tenant"); + ASSERT_EQ(uinfo.user_email, "user1@dbstore.com"); + ASSERT_EQ(uinfo.user_id.id, "user_id1"); + ASSERT_EQ(uinfo.suspended, 123); + ASSERT_EQ(uinfo.max_buckets, 456); + ASSERT_EQ(uinfo.assumed_role_arn, "role"); + ASSERT_EQ(uinfo.placement_tags.back(), "tags"); + RGWAccessKey k; + map::iterator it2 = uinfo.access_keys.begin(); + k = it2->second; + ASSERT_EQ(k.id, "id1"); + ASSERT_EQ(k.key, "key1"); + it2++; + k = it2->second; + ASSERT_EQ(k.id, "id2"); + ASSERT_EQ(k.key, "key2"); +} + +TEST_F(DBStoreBaseTest, StoreUser) { + struct DBOpParams params = GlobalParams; + int ret = -1; + RGWUserInfo uinfo, old_uinfo; + map attrs; + RGWObjVersionTracker objv_tracker; + + bufferlist attr1, attr2; + encode("attrs1", attr1); + attrs["attr1"] = attr1; + encode("attrs2", attr2); + attrs["attr2"] = attr2; + + uinfo.user_id.id = "user_id2"; + uinfo.user_id.tenant = "tenant"; + uinfo.user_email = "user2@dbstore.com"; + uinfo.suspended = 123; + uinfo.max_buckets = 456; + uinfo.assumed_role_arn = "role"; + uinfo.placement_tags.push_back("tags"); + RGWAccessKey k1("id1", "key1"); + RGWAccessKey k2("id2", "key2"); + uinfo.access_keys["id1"] = k1; + uinfo.access_keys["id2"] = k2; + + /* non exclusive create..should create new one */ + ret = db->store_user(uinfo, true, &attrs, &objv_tracker, &old_uinfo); + ASSERT_EQ(ret, 0); + ASSERT_EQ(old_uinfo.user_email, ""); + ASSERT_EQ(objv_tracker.read_version.ver, 1); + ASSERT_EQ(objv_tracker.read_version.tag, "UserTAG"); + + /* invalid version number */ + objv_tracker.read_version.ver = 4; + ret = db->store_user(uinfo, true, &attrs, &objv_tracker, &old_uinfo); + ASSERT_EQ(ret, -125); /* returns ECANCELED */ + ASSERT_EQ(old_uinfo.user_id.id, uinfo.user_id.id); + ASSERT_EQ(old_uinfo.user_email, uinfo.user_email); + + /* exclusive create..should not create new one */ + uinfo.user_email = "user2_new@dbstore.com"; + objv_tracker.read_version.ver = 1; + ret = db->store_user(uinfo, true, &attrs, &objv_tracker, &old_uinfo); + ASSERT_EQ(ret, 0); + ASSERT_EQ(old_uinfo.user_email, "user2@dbstore.com"); + ASSERT_EQ(objv_tracker.read_version.ver, 1); + + ret = db->store_user(uinfo, false, &attrs, &objv_tracker, &old_uinfo); + ASSERT_EQ(ret, 0); + ASSERT_EQ(old_uinfo.user_email, "user2@dbstore.com"); + ASSERT_EQ(objv_tracker.read_version.ver, 2); + ASSERT_EQ(objv_tracker.read_version.tag, "UserTAG"); +} + +TEST_F(DBStoreBaseTest, GetUserQueryByUserID) { + int ret = -1; + RGWUserInfo uinfo; + map attrs; + RGWObjVersionTracker objv; + + uinfo.user_id.tenant = "tenant"; + uinfo.user_id.id = "user_id2"; + + ret = db->get_user("user_id", "", uinfo, &attrs, &objv); + ASSERT_EQ(ret, 0); + ASSERT_EQ(uinfo.user_id.tenant, "tenant"); + ASSERT_EQ(uinfo.user_email, "user2_new@dbstore.com"); + ASSERT_EQ(uinfo.user_id.id, "user_id2"); + ASSERT_EQ(uinfo.suspended, 123); + ASSERT_EQ(uinfo.max_buckets, 456); + ASSERT_EQ(uinfo.assumed_role_arn, "role"); + ASSERT_EQ(uinfo.placement_tags.back(), "tags"); + RGWAccessKey k; + map::iterator it = uinfo.access_keys.begin(); + k = it->second; + ASSERT_EQ(k.id, "id1"); + ASSERT_EQ(k.key, "key1"); + it++; + k = it->second; + ASSERT_EQ(k.id, "id2"); + ASSERT_EQ(k.key, "key2"); + + ASSERT_EQ(objv.read_version.ver, 2); + + bufferlist k1, k2; + string attr; + map::iterator it2 = attrs.begin(); + k1 = it2->second; + decode(attr, k1); + ASSERT_EQ(attr, "attrs1"); + it2++; + k2 = it2->second; + decode(attr, k2); + ASSERT_EQ(attr, "attrs2"); +} + +TEST_F(DBStoreBaseTest, ListAllUsers) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ListAllUsers(¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreBaseTest, InsertBucket) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + params.op.bucket.info.bucket.name = "bucket1"; + params.op.bucket.info.bucket.tenant = "tenant"; + params.op.bucket.info.bucket.marker = "marker1"; + + params.op.bucket.ent.size = 1024; + + params.op.bucket.info.has_instance_obj = false; + params.op.bucket.bucket_version.ver = 1; + params.op.bucket.bucket_version.tag = "read_tag"; + + params.op.bucket.mtime = bucket_mtime; + + ret = db->ProcessOp("InsertBucket", ¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreBaseTest, UpdateBucketAttrs) { + int ret = -1; + RGWBucketInfo info; + map attrs; + RGWObjVersionTracker objv; + + bufferlist aclbl, aclbl2; + encode("attrs1", aclbl); + attrs["attr1"] = aclbl; + encode("attrs2", aclbl2); + attrs["attr2"] = aclbl2; + + info.bucket.name = "bucket1"; + + /* invalid version number */ + objv.read_version.ver = 4; + ret = db->update_bucket("attrs", info, false, nullptr, &attrs, &bucket_mtime, &objv); + ASSERT_EQ(ret, -125); /* returns ECANCELED */ + + /* right version number */ + objv.read_version.ver = 1; + ret = db->update_bucket("attrs", info, false, nullptr, &attrs, &bucket_mtime, &objv); + ASSERT_EQ(ret, 0); + ASSERT_EQ(objv.read_version.ver, 2); +} + +TEST_F(DBStoreBaseTest, BucketChown) { + int ret = -1; + RGWBucketInfo info; + rgw_user user; + user.id = "user_id2"; + + info.bucket.name = "bucket1"; + + ret = db->update_bucket("owner", info, false, &user, nullptr, &bucket_mtime, nullptr); + ASSERT_EQ(ret, 0); + ASSERT_EQ(info.objv_tracker.read_version.ver, 3); +} + +TEST_F(DBStoreBaseTest, UpdateBucketInfo) { + struct DBOpParams params = GlobalParams; + int ret = -1; + RGWBucketInfo info; + + params.op.bucket.info.bucket.name = "bucket1"; + + ret = db->ProcessOp("GetBucket", ¶ms); + ASSERT_EQ(ret, 0); + + info = params.op.bucket.info; + + info.bucket.marker = "marker2"; + ret = db->update_bucket("info", info, false, nullptr, nullptr, &bucket_mtime, nullptr); + ASSERT_EQ(ret, 0); + ASSERT_EQ(info.objv_tracker.read_version.ver, 4); +} + +TEST_F(DBStoreBaseTest, GetBucket) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ProcessOp("GetBucket", ¶ms); + ASSERT_EQ(ret, 0); + ASSERT_EQ(params.op.bucket.info.bucket.name, "bucket1"); + ASSERT_EQ(params.op.bucket.info.bucket.tenant, "tenant"); + ASSERT_EQ(params.op.bucket.info.bucket.marker, "marker2"); + ASSERT_EQ(params.op.bucket.ent.size, 1024); + ASSERT_EQ(params.op.bucket.ent.bucket.name, "bucket1"); + ASSERT_EQ(params.op.bucket.ent.bucket.tenant, "tenant"); + ASSERT_EQ(params.op.bucket.info.has_instance_obj, false); + ASSERT_EQ(params.op.bucket.info.objv_tracker.read_version.ver, 4); + ASSERT_EQ(params.op.bucket.info.objv_tracker.read_version.tag, "read_tag"); + ASSERT_EQ(params.op.bucket.mtime, bucket_mtime); + ASSERT_EQ(params.op.bucket.info.owner.id, "user_id2"); + bufferlist k, k2; + string acl; + map::iterator it2 = params.op.bucket.bucket_attrs.begin(); + k = it2->second; + decode(acl, k); + ASSERT_EQ(acl, "attrs1"); + it2++; + k2 = it2->second; + decode(acl, k2); + ASSERT_EQ(acl, "attrs2"); +} + +TEST_F(DBStoreBaseTest, RemoveBucketAPI) { + int ret = -1; + RGWBucketInfo info; + + info.bucket.name = "bucket1"; + + ret = db->remove_bucket(info); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreBaseTest, RemoveUserAPI) { + int ret = -1; + RGWUserInfo uinfo; + RGWObjVersionTracker objv; + + uinfo.user_id.tenant = "tenant"; + uinfo.user_id.id = "user_id2"; + + /* invalid version number...should fail */ + objv.read_version.ver = 4; + ret = db->remove_user(uinfo, &objv); + ASSERT_EQ(ret, -125); + + /* invalid version number...should fail */ + objv.read_version.ver = 2; + ret = db->remove_user(uinfo, &objv); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreBaseTest, CreateBucket) { + struct DBOpParams params = GlobalParams; + int ret = -1; + RGWBucketInfo info; + RGWUserInfo owner; + rgw_bucket bucket; + obj_version objv; + rgw_placement_rule rule; + map attrs; + + owner.user_id.id = "user_id1"; + bucket.name = "bucket1"; + bucket.tenant = "tenant"; + + objv.ver = 2; + objv.tag = "write_tag"; + + rule.name = "rule1"; + rule.storage_class = "sc1"; + + ret = db->create_bucket(owner, bucket, "zid", rule, "swift_ver", NULL, + attrs, info, &objv, NULL, bucket_mtime, NULL, NULL, + null_yield, NULL, false); + ASSERT_EQ(ret, 0); + bucket.name = "bucket2"; + ret = db->create_bucket(owner, bucket, "zid", rule, "swift_ver", NULL, + attrs, info, &objv, NULL, bucket_mtime, NULL, NULL, + null_yield, NULL, false); + ASSERT_EQ(ret, 0); + bucket.name = "bucket3"; + ret = db->create_bucket(owner, bucket, "zid", rule, "swift_ver", NULL, + attrs, info, &objv, NULL, bucket_mtime, NULL, NULL, + null_yield, NULL, false); + ASSERT_EQ(ret, 0); + bucket.name = "bucket4"; + ret = db->create_bucket(owner, bucket, "zid", rule, "swift_ver", NULL, + attrs, info, &objv, NULL, bucket_mtime, NULL, NULL, + null_yield, NULL, false); + ASSERT_EQ(ret, 0); + bucket.name = "bucket5"; + ret = db->create_bucket(owner, bucket, "zid", rule, "swift_ver", NULL, + attrs, info, &objv, NULL, bucket_mtime, NULL, NULL, + null_yield, NULL, false); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreBaseTest, GetBucketQueryByName) { + int ret = -1; + RGWBucketInfo binfo; + binfo.bucket.name = "bucket2"; + rgw::sal::Attrs attrs; + ceph::real_time mtime; + obj_version objv; + + ret = db->get_bucket_info("name", "", binfo, &attrs, &mtime, &objv); + ASSERT_EQ(ret, 0); + ASSERT_EQ(binfo.bucket.name, "bucket2"); + ASSERT_EQ(binfo.bucket.tenant, "tenant"); + ASSERT_EQ(binfo.owner.id, "user_id1"); + ASSERT_EQ(binfo.objv_tracker.read_version.ver, 2); + ASSERT_EQ(binfo.objv_tracker.read_version.tag, "write_tag"); + ASSERT_EQ(binfo.zonegroup, "zid"); + ASSERT_EQ(binfo.creation_time, bucket_mtime); + ASSERT_EQ(binfo.placement_rule.name, "rule1"); + ASSERT_EQ(binfo.placement_rule.storage_class, "sc1"); + ASSERT_EQ(objv.ver, 2); + ASSERT_EQ(objv.tag, "write_tag"); + + marker1 = binfo.bucket.marker; +} + +TEST_F(DBStoreBaseTest, ListUserBuckets) { + struct DBOpParams params = GlobalParams; + int ret = -1; + rgw_user owner; + int max = 2; + bool need_stats = true; + bool is_truncated = false; + RGWUserBuckets ulist; + + owner.id = "user_id1"; + + marker1 = ""; + do { + is_truncated = false; + ret = db->list_buckets(owner, marker1, "", max, need_stats, &ulist, &is_truncated); + ASSERT_EQ(ret, 0); + + cout << "marker1 :" << marker1 << "\n"; + + cout << "is_truncated :" << is_truncated << "\n"; + + for (const auto& ent: ulist.get_buckets()) { + RGWBucketEnt e = ent.second; + cout << "###################### \n"; + cout << "ent.bucket.id : " << e.bucket.name << "\n"; + cout << "ent.bucket.marker : " << e.bucket.marker << "\n"; + cout << "ent.bucket.bucket_id : " << e.bucket.bucket_id << "\n"; + cout << "ent.size : " << e.size << "\n"; + cout << "ent.rule.name : " << e.placement_rule.name << "\n"; + + marker1 = e.bucket.name; + } + ulist.clear(); + } while(is_truncated); +} + +TEST_F(DBStoreBaseTest, ListAllBuckets) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ListAllBuckets(¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreBaseTest, InsertObject) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ProcessOp("InsertObject", ¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreBaseTest, ListObject) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ProcessOp("ListObject", ¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreBaseTest, ListAllObjects) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ListAllObjects(¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreBaseTest, PutObjectData) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ProcessOp("PutObjectData", ¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreBaseTest, GetObjectData) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ProcessOp("GetObjectData", ¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreBaseTest, DeleteObjectData) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ProcessOp("DeleteObjectData", ¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreBaseTest, RemoveObject) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ProcessOp("RemoveObject", ¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreBaseTest, RemoveBucket) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ProcessOp("RemoveBucket", ¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreBaseTest, RemoveUser) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ProcessOp("RemoveUser", ¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreBaseTest, InsertTestIDUser) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + params.op.user.uinfo.user_id.id = "testid"; + params.op.user.uinfo.display_name = "M. Tester"; + params.op.user.uinfo.user_id.tenant = "tenant"; + params.op.user.uinfo.user_email = "tester@ceph.com"; + RGWAccessKey k1("0555b35654ad1656d804", "h7GhxuBLTrlhVUyxSPUKUV8r/2EI4ngqJxD7iBdBYLhwluN30JaT3Q=="); + params.op.user.uinfo.access_keys["0555b35654ad1656d804"] = k1; + params.op.user.user_version.ver = 1; + params.op.user.user_version.tag = "UserTAG"; + + ret = db->ProcessOp("InsertUser", ¶ms); + ASSERT_EQ(ret, 0); +} + +int main(int argc, char **argv) +{ + int ret = -1; + + ::testing::InitGoogleTest(&argc, argv); + + gtest::env = new gtest::Environment(); + ::testing::AddGlobalTestEnvironment(gtest::env); + + ret = RUN_ALL_TESTS(); + + return ret; +}