]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
rgw/dbstore: DB backend for RGW
authorSoumya Koduri <skoduri@redhat.com>
Tue, 29 Jun 2021 13:41:10 +0000 (19:11 +0530)
committerSoumya Koduri <skoduri@redhat.com>
Wed, 28 Jul 2021 06:40:16 +0000 (12:10 +0530)
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 <skoduri@redhat.com>
20 files changed:
CMakeLists.txt
cmake/modules/FindSQLITE3.cmake [new file with mode: 0644]
src/include/config-h.in.cmake
src/rgw/CMakeLists.txt
src/rgw/rgw_sal.cc
src/rgw/rgw_sal_dbstore.cc [new file with mode: 0644]
src/rgw/rgw_sal_dbstore.h [new file with mode: 0644]
src/rgw/store/dbstore/CMakeLists.txt [new file with mode: 0644]
src/rgw/store/dbstore/README.md [new file with mode: 0644]
src/rgw/store/dbstore/common/dbstore.cc [new file with mode: 0644]
src/rgw/store/dbstore/common/dbstore.h [new file with mode: 0644]
src/rgw/store/dbstore/common/dbstore_log.h [new file with mode: 0644]
src/rgw/store/dbstore/dbstore_main.cc [new file with mode: 0644]
src/rgw/store/dbstore/dbstore_mgr.cc [new file with mode: 0644]
src/rgw/store/dbstore/dbstore_mgr.h [new file with mode: 0644]
src/rgw/store/dbstore/sqlite/CMakeLists.txt [new file with mode: 0644]
src/rgw/store/dbstore/sqlite/sqliteDB.cc [new file with mode: 0644]
src/rgw/store/dbstore/sqlite/sqliteDB.h [new file with mode: 0644]
src/rgw/store/dbstore/tests/CMakeLists.txt [new file with mode: 0644]
src/rgw/store/dbstore/tests/dbstore_tests.cc [new file with mode: 0644]

index 0e289346112b52a736c30a2fa0247e7e25290acd..35ec581876054678aad0af2b901d86f6d3c5508a 100644 (file)
@@ -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 (file)
index 0000000..b75639c
--- /dev/null
@@ -0,0 +1,38 @@
+# Copyright (C) 2007-2009 LuaDist.
+# Created by Peter Kapec <kapecp@gmail.com>
+# 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)
index a1d89447356d187119b9336fe7e6f86d881ec460..a465fc6b3a5af45c9da0db9637cea2b93542869c 100644 (file)
 /* 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
 
index 05cdb6ea37de3f0f85ab95112bd40b364ba8935e..62e95bcba109596050bc9bf8bf91a409d7163081 100644 (file)
@@ -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})
index 31cf9fd36068f368e639a3f5cc4d94f5f419f016..d05a4a1b8261ff527dda6a3cdc49c0fe1cc9d0c8 100644 (file)
 #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<rgw::sal::RGWDBStore *>(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 (file)
index 0000000..1fa3231
--- /dev/null
@@ -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 <errno.h>
+#include <stdlib.h>
+#include <system_error>
+#include <unistd.h>
+#include <sstream>
+
+#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<Bucket>(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<rgw_user_bucket, rgw_usage_log_entry>& 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<RGWObjCategory, RGWStorageStats>& 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<rgw_user_bucket, rgw_usage_log_entry>& 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<rgw_obj_index_key>& 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<RGWObjCategory, RGWStorageStats>& existing_stats, std::map<RGWObjCategory, RGWStorageStats>& 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<Object> 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<LuaScriptManager> RGWDBStore::get_lua_script_manager()
+  {
+    return std::unique_ptr<LuaScriptManager>(new DBLuaScriptManager(this));
+  }
+
+
+  std::unique_ptr<RGWRole> 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<RGWRole>(p);
+  }
+
+  std::unique_ptr<RGWRole> RGWDBStore::get_role(std::string id)
+  {
+    RGWRole* p = nullptr;
+    return std::unique_ptr<RGWRole>(p);
+  }
+
+  int RGWDBStore::get_roles(const DoutPrefixProvider *dpp,
+      optional_yield y,
+      const std::string& path_prefix,
+      const std::string& tenant,
+      vector<std::unique_ptr<RGWRole>>& roles)
+  {
+    return 0;
+  }
+
+  std::unique_ptr<RGWOIDCProvider> RGWDBStore::get_oidc_provider()
+  {
+    RGWOIDCProvider* p = nullptr;
+    return std::unique_ptr<RGWOIDCProvider>(p);
+  }
+
+  int RGWDBStore::get_oidc_providers(const DoutPrefixProvider *dpp,
+      const std::string& tenant,
+      vector<std::unique_ptr<RGWOIDCProvider>>& providers)
+  {
+    return 0;
+  }
+
+  std::unique_ptr<User> RGWDBStore::get_user(const rgw_user &u)
+  {
+    return std::unique_ptr<User>(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>* 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>* 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>* user)
+  {
+    /* Swift keys and subusers are not supported for now */
+    return 0;
+  }
+
+  std::unique_ptr<Object> 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>* 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)
+  {
+    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>* 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>* 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> 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<Bucket>(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<Lifecycle> RGWDBStore::get_lifecycle(void)
+  {
+    return 0;
+  }
+
+  std::unique_ptr<Completions> RGWDBStore::get_completions(void)
+  {
+    return 0;
+  }
+
+  std::unique_ptr<Notification> RGWDBStore::get_notification(rgw::sal::Object* obj,
+      struct req_state* s,
+      rgw::notify::EventType event_type)
+  {
+    return 0;
+  }
+
+  std::unique_ptr<GCChain> RGWDBStore::get_gc_chain(rgw::sal::Object* obj)
+  {
+    return 0;
+  }
+
+  std::unique_ptr<Writer> RGWDBStore::get_writer(Aio *aio, rgw::sal::Bucket* bucket,
+      RGWObjectCtx& obj_ctx, std::unique_ptr<rgw::sal::Object> _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<rgw_user_bucket, RGWUsageBatch>& 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<string, string>& 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<rgw_bucket>& buckets, bool enabled)
+  {
+    int ret = 0;
+
+    vector<rgw_bucket>::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<string, bufferlist> 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<rgw_zone_id> zone,
+      std::optional<rgw_bucket> 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<rgw_user_bucket, rgw_usage_log_entry>& 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<string>& 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 (file)
index 0000000..818752f
--- /dev/null
@@ -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<User> clone() override {
+        return std::unique_ptr<User>(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<rgw_user_bucket, rgw_usage_log_entry>& 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<Object> 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<RGWObjCategory, RGWStorageStats>& 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<rgw_user_bucket, rgw_usage_log_entry>& 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<rgw_obj_index_key>& objs_to_unlink) override;
+      virtual int check_index(const DoutPrefixProvider *dpp, std::map<RGWObjCategory, RGWStorageStats>& existing_stats, std::map<RGWObjCategory, RGWStorageStats>& 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<Bucket> clone() override {
+        return std::make_unique<DBBucket>(*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<User> 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>* user) override;
+      virtual int get_user_by_email(const DoutPrefixProvider *dpp, const std::string& email, optional_yield y, std::unique_ptr<User>* user) override;
+      virtual int get_user_by_swift(const DoutPrefixProvider *dpp, const std::string& user_str, optional_yield y, std::unique_ptr<User>* user) override;
+      virtual std::unique_ptr<Object> 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>* bucket, optional_yield y) override;
+      virtual int get_bucket(User* u, const RGWBucketInfo& i, std::unique_ptr<Bucket>* bucket) override;
+      virtual int get_bucket(const DoutPrefixProvider *dpp, User* u, const std::string& tenant, const std::string&name, std::unique_ptr<Bucket>* 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>* 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<Lifecycle> get_lifecycle(void) override;
+      virtual std::unique_ptr<Completions> get_completions(void) override;
+      virtual std::unique_ptr<Notification> get_notification(rgw::sal::Object* obj, struct req_state* s, rgw::notify::EventType event_type) override;
+      virtual std::unique_ptr<GCChain> get_gc_chain(rgw::sal::Object* obj) override;
+      virtual std::unique_ptr<Writer> get_writer(Aio *aio, rgw::sal::Bucket* bucket,
+          RGWObjectCtx& obj_ctx, std::unique_ptr<rgw::sal::Object> _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<rgw_user_bucket, RGWUsageBatch>& 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<string, string>& meta) override;
+      virtual void get_quota(RGWQuotaInfo& bucket_quota, RGWQuotaInfo& user_quota) override;
+      virtual int set_buckets_enabled(const DoutPrefixProvider *dpp, vector<rgw_bucket>& 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<rgw_zone_id> zone,
+          std::optional<rgw_bucket> 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<int>& shard_ids) override { return; }
+      virtual void wakeup_data_sync_shards(const DoutPrefixProvider *dpp, const rgw_zone_id& source_zone, map<int, set<string> >& 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<rgw_user_bucket, rgw_usage_log_entry>& 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<std::string>& 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<LuaScriptManager> get_lua_script_manager() override;
+      virtual std::unique_ptr<RGWRole> get_role(string name,
+          string tenant,
+          string path="",
+          string trust_policy="",
+          string max_session_duration_str="") override;
+      virtual std::unique_ptr<RGWRole> 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<std::unique_ptr<RGWRole>>& roles) override;
+      virtual std::unique_ptr<RGWOIDCProvider> get_oidc_provider() override;
+      virtual int get_oidc_providers(const DoutPrefixProvider *dpp,
+          const std::string& tenant,
+          vector<std::unique_ptr<RGWOIDCProvider>>& 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 (file)
index 0000000..984fb93
--- /dev/null
@@ -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 (file)
index 0000000..b84ec72
--- /dev/null
@@ -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 (file)
index 0000000..6d55617
--- /dev/null
@@ -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<string, class ObjectOp*> DBStore::objectmap = {};
+
+map<string, class ObjectOp*> 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:" \
+    <<db_name<<"\n";
+
+  LogDestroy();
+
+  return 0;
+}
+
+int DBStore::LockInit() {
+  int ret;
+
+  ret = pthread_mutex_init(&mutex, NULL);
+
+  if (ret)
+    dbout(L_ERR)<<"pthread_mutex_init failed \n";
+
+  return ret;
+}
+
+int DBStore::LockDestroy() {
+  int ret;
+
+  ret = pthread_mutex_destroy(&mutex);
+
+  if (ret)
+    dbout(L_ERR)<<"pthread_mutex_destroy failed \n";
+
+  return ret;
+}
+
+int DBStore::Lock() {
+  int ret;
+
+  ret = pthread_mutex_lock(&mutex);
+
+  if (ret)
+    dbout(L_ERR)<<"pthread_mutex_lock failed \n";
+
+  return ret;
+}
+
+int DBStore::Unlock() {
+  int ret;
+
+  ret = pthread_mutex_unlock(&mutex);
+
+  if (ret)
+    dbout(L_ERR)<<"pthread_mutex_unlock failed \n";
+
+  return ret;
+}
+
+DBOp * DBStore::getDBOp(string Op, struct DBOpParams *params)
+{
+  if (!Op.compare("InsertUser"))
+    return dbops.InsertUser;
+  if (!Op.compare("RemoveUser"))
+    return dbops.RemoveUser;
+  if (!Op.compare("GetUser"))
+    return dbops.GetUser;
+  if (!Op.compare("InsertBucket"))
+    return dbops.InsertBucket;
+  if (!Op.compare("UpdateBucket"))
+    return dbops.UpdateBucket;
+  if (!Op.compare("RemoveBucket"))
+    return dbops.RemoveBucket;
+  if (!Op.compare("GetBucket"))
+    return dbops.GetBucket;
+  if (!Op.compare("ListUserBuckets"))
+    return dbops.ListUserBuckets;
+
+  /* Object Operations */
+  map<string, class ObjectOp*>::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: " \
+      <<params->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<string, class ObjectOp*>::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("\
+      <<bucket<<"). Not inserted \n";
+    return 0;
+  }
+
+  Ob = (class ObjectOp*) ptr;
+  Ob->InitializeObjectOps();
+
+  DBStore::objectmap.insert(pair<string, class ObjectOp*>(bucket, Ob));
+
+  return 0;
+}
+
+int DBStore::objectmapDelete(string bucket)
+{
+  map<string, class ObjectOp*>::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("<<bucket<<") "
+      <<"doesnt exist to delete \n";
+    return 0;
+  }
+
+  Ob = (class ObjectOp*) (iter->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("<<Op<<")\n";
+    Unlock();
+    return ret;
+  }
+  ret = db_op->Execute(params);
+
+  Unlock();
+  if (ret) {
+    dbout(L_ERR)<<"In Process op Execute failed for fop(" \
+      <<Op.c_str()<<") \n";
+  } else {
+    dbout(L_FULLDEBUG)<<"Successfully processed fop(" \
+      <<Op.c_str()<<") \n";
+  }
+
+  return ret;
+}
+
+int DBStore::get_user(const std::string& query_str, const std::string& query_str_val,
+    RGWUserInfo& uinfo, map<string, bufferlist> *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", &params);
+
+  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<string, RGWAccessKey> 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 :" <<query_str.c_str()<<") \n";
+    return -1;
+  }
+
+  ret = ProcessOp("GetUser", &params);
+
+  if (ret)
+    goto out;
+
+  uinfo = params.op.user.uinfo;
+
+  if (pattrs) {
+    *pattrs = params.op.user.user_attrs;
+  }
+
+  if (pobjv_tracker) {
+    pobjv_tracker->read_version = params.op.user.user_version;
+  }
+
+out:
+  return ret;
+}
+
+int DBStore::store_user(RGWUserInfo& uinfo, bool exclusive, map<string, bufferlist> *pattrs,
+    RGWObjVersionTracker *pobjv, RGWUserInfo* pold_info)
+{
+  DBOpParams params = {};
+  InitializeParams("CreateUser", &params);
+  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:(" <<ret<<") \n";
+      return ret;
+    }
+
+    if (exclusive) {
+      // return
+      return ret;
+    }
+    obj_ver.ver++;
+  } else {
+    obj_ver.ver = 1;
+    obj_ver.tag = "UserTAG";
+  }
+
+  params.op.user.user_version = obj_ver;
+  params.op.user.uinfo = uinfo;
+
+  if (pattrs) {
+    params.op.user.user_attrs = *pattrs;
+  }
+
+  ret = ProcessOp("InsertUser", &params);
+
+  if (ret) {
+    dbout(L_ERR)<<"store_user failed with err:(" <<ret<<") \n";
+    goto out;
+  }
+
+  if (pobjv) {
+    pobjv->read_version = obj_ver;
+    pobjv->write_version = obj_ver;
+  }
+
+out:
+  return ret;
+}
+
+int DBStore::remove_user(RGWUserInfo& uinfo, RGWObjVersionTracker *pobjv)
+{
+  DBOpParams params = {};
+  InitializeParams("CreateUser", &params);
+  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:(" <<ret<<") \n";
+      return ret;
+    }
+  }
+
+  params.op.user.uinfo.user_id = uinfo.user_id;
+
+  ret = ProcessOp("RemoveUser", &params);
+
+  if (ret) {
+    dbout(L_ERR)<<"remove_user failed with err:(" <<ret<<") \n";
+    goto out;
+  }
+
+out:
+  return ret;
+}
+
+int DBStore::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 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 = {};
+  DBOpParams params2 = {};
+  InitializeParams("GetBucket", &params);
+
+  if (query_str == "name") {
+    params.op.bucket.info.bucket.name = info.bucket.name;
+  } else {
+    dbout(L_ERR)<<"In GetBucket Invalid query string :" <<query_str.c_str()<<") \n";
+    return -1;
+  }
+
+  ret = ProcessOp("GetBucket", &params);
+
+  if (ret) {
+    dbout(L_ERR)<<"In GetBucket failed err:(" <<ret<<") \n";
+    goto out;
+  }
+
+  if (!ret && params.op.bucket.info.bucket.marker.empty()) {
+    return -ENOENT;
+  }
+  info = params.op.bucket.info;
+
+  if (pattrs) {
+    *pattrs = params.op.bucket.bucket_attrs;
+  }
+
+  if (pmtime) {
+    *pmtime = params.op.bucket.mtime;
+  }
+  if (pbucket_version) {
+    *pbucket_version = params.op.bucket.bucket_version;
+  }
+
+out:
+  return ret;
+}
+
+int DBStore::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<std::string, bufferlist>& 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", &params);
+  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", &params);
+
+  if (ret) {
+    dbout(L_ERR)<<"create_bucket failed with err:(" <<ret<<") \n";
+    goto out;
+  }
+
+out:
+  return ret;
+}
+
+int DBStore::remove_bucket(const RGWBucketInfo info) {
+  int ret = 0;
+
+  DBOpParams params = {};
+  InitializeParams("RemoveBucket", &params);
+
+  params.op.bucket.info.bucket.name = info.bucket.name;
+
+  ret = ProcessOp("RemoveBucket", &params);
+
+  if (ret) {
+    dbout(L_ERR)<<"In RemoveBucket failed err:(" <<ret<<") \n";
+    goto out;
+  }
+
+out:
+  return ret;
+}
+
+int DBStore::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 ret = 0;
+
+  DBOpParams params = {};
+  InitializeParams("ListUserBuckets", &params);
+
+  params.op.user.uinfo.user_id = user;
+  params.op.bucket.min_marker = marker;
+  params.op.bucket.max_marker = end_marker;
+  params.op.list_max_count = max;
+
+  ret = ProcessOp("ListUserBuckets", &params);
+
+  if (ret) {
+    dbout(L_ERR)<<"In ListUserBuckets failed err:(" <<ret<<") \n";
+    goto out;
+  }
+
+  /* need_stats: stats are already part of entries... In case they are maintained in
+   * separate table , maybe use "Inner Join" with stats table for the query.
+   */
+  if (params.op.bucket.list_entries.size() == max)
+    *is_truncated = true;
+
+  for (auto& entry : params.op.bucket.list_entries) {
+    if (!end_marker.empty() &&
+        end_marker.compare(entry.bucket.marker) <= 0) {
+      *is_truncated = false;
+      break;
+    }
+    buckets->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<std::string, bufferlist>* 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:(" <<ret<<") \n";
+    goto out;
+  }
+
+  if (!orig_info.owner.id.empty() && exclusive) {
+    /* already exists. Return the old info */
+
+    info = std::move(orig_info);
+    return ret;
+  }
+
+  /* Verify if the objv read_ver matches current bucket version */
+  if (pobjv) {
+    if (pobjv->read_version.ver != bucket_version.ver) {
+      dbout(L_ERR)<<"Read version mismatch err:(" <<ret<<") \n";
+      ret = -ECANCELED;
+      goto out;
+    }
+  } else {
+    pobjv = &info.objv_tracker;
+  }
+
+  InitializeParams("UpdateBucket", &params);
+
+  params.op.bucket.info.bucket.name = info.bucket.name;
+
+  if (powner_id) {
+    params.op.user.uinfo.user_id.id = powner_id->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", &params);
+
+  if (ret) {
+    dbout(L_ERR)<<"In UpdateBucket failed err:(" <<ret<<") \n";
+    goto out;
+  }
+
+  if (pobjv) {
+    pobjv->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 (file)
index 0000000..bb78791
--- /dev/null
@@ -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 <errno.h>
+#include <stdlib.h>
+#include <string>
+#include <stdio.h>
+#include <iostream>
+// 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 <map>
+#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<RGWBucketEnt> 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("<<type<<") specified \n";
+
+      return NULL;
+    }
+
+    string DeleteTableSchema(string table) {
+      return fmt::format(DropQ.c_str(), table.c_str());
+    }
+    string ListTableSchema(string table) {
+      return fmt::format(ListAllQ.c_str(), table.c_str());
+    }
+
+    virtual int Prepare(DBOpParams *params) { return 0; }
+    virtual int Execute(DBOpParams *params) { return 0; }
+};
+
+class InsertUserOp : public DBOp {
+  private:
+    /* For existing entires, -
+     * (1) INSERT or REPLACE - it will delete previous entry and then
+     * inserts new one. Since it deletes previos enties, it will
+     * trigger all foriegn key cascade deletes or other triggers.
+     * (2) INSERT or UPDATE - this will set NULL values to unassigned
+     * fields.
+     * more info: https://code-examples.net/en/q/377728
+     *
+     * For now using INSERT or REPLACE. If required of updating existing
+     * record, will use another query.
+     */
+    const string Query = "INSERT OR REPLACE INTO '{}'  \
+                          (UserID, Tenant, NS, DisplayName, UserEmail, \
+                           AccessKeysID, AccessKeysSecret, AccessKeys, SwiftKeys,\
+                           SubUsers, Suspended, MaxBuckets, OpMask, UserCaps, Admin, \
+                           System, PlacementName, PlacementStorageClass, PlacementTags, \
+                           BucketQuota, TempURLKeys, UserQuota, Type, MfaIDs, AssumedRoleARN, \
+                           UserAttrs, UserVersion, UserVersionTag) \
+                          VALUES ({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, \
+                              {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {});";
+
+  public:
+    virtual ~InsertUserOp() {}
+
+    string Schema(DBOpPrepareParams &params) {
+      return fmt::format(Query.c_str(), params.user_table.c_str(),
+          params.op.user.user_id.c_str(), params.op.user.tenant, params.op.user.ns,
+          params.op.user.display_name, params.op.user.user_email,
+          params.op.user.access_keys_id, params.op.user.access_keys_secret,
+          params.op.user.access_keys, params.op.user.swift_keys,
+          params.op.user.subusers, params.op.user.suspended,
+          params.op.user.max_buckets, params.op.user.op_mask,
+          params.op.user.user_caps, params.op.user.admin, params.op.user.system,
+          params.op.user.placement_name, params.op.user.placement_storage_class,
+          params.op.user.placement_tags, params.op.user.bucket_quota,
+          params.op.user.temp_url_keys, params.op.user.user_quota,
+          params.op.user.type, params.op.user.mfa_ids,
+          params.op.user.assumed_role_arn, params.op.user.user_attrs,
+          params.op.user.user_ver, params.op.user.user_ver_tag);
+    }
+
+};
+
+class RemoveUserOp: public DBOp {
+  private:
+    const string Query =
+      "DELETE from '{}' where UserID = {}";
+
+  public:
+    virtual ~RemoveUserOp() {}
+
+    string Schema(DBOpPrepareParams &params) {
+      return fmt::format(Query.c_str(), params.user_table.c_str(),
+          params.op.user.user_id.c_str());
+    }
+};
+
+class GetUserOp: public DBOp {
+  private:
+    /* If below query columns are updated, make sure to update the indexes
+     * in list_user() cbk in sqliteDB.cc */
+    const string Query = "SELECT \
+                          UserID, Tenant, NS, DisplayName, UserEmail, \
+                          AccessKeysID, AccessKeysSecret, AccessKeys, SwiftKeys,\
+                          SubUsers, Suspended, MaxBuckets, OpMask, UserCaps, Admin, \
+                          System, PlacementName, PlacementStorageClass, PlacementTags, \
+                          BucketQuota, TempURLKeys, UserQuota, Type, MfaIDs, AssumedRoleARN, \
+                          UserAttrs, UserVersion, UserVersionTag from '{}' where UserID = {}";
+
+    const string QueryByEmail = "SELECT \
+                                 UserID, Tenant, NS, DisplayName, UserEmail, \
+                                 AccessKeysID, AccessKeysSecret, AccessKeys, SwiftKeys,\
+                                 SubUsers, Suspended, MaxBuckets, OpMask, UserCaps, Admin, \
+                                 System, PlacementName, PlacementStorageClass, PlacementTags, \
+                                 BucketQuota, TempURLKeys, UserQuota, Type, MfaIDs, AssumedRoleARN, \
+                                 UserAttrs, UserVersion, UserVersionTag from '{}' where UserEmail = {}";
+
+    const string QueryByAccessKeys = "SELECT \
+                                      UserID, Tenant, NS, DisplayName, UserEmail, \
+                                      AccessKeysID, AccessKeysSecret, AccessKeys, SwiftKeys,\
+                                      SubUsers, Suspended, MaxBuckets, OpMask, UserCaps, Admin, \
+                                      System, PlacementName, PlacementStorageClass, PlacementTags, \
+                                      BucketQuota, TempURLKeys, UserQuota, Type, MfaIDs, AssumedRoleARN, \
+                                      UserAttrs, UserVersion, UserVersionTag from '{}' where AccessKeysID = {}";
+
+    const string QueryByUserID = "SELECT \
+                                  UserID, Tenant, NS, DisplayName, UserEmail, \
+                                  AccessKeysID, AccessKeysSecret, AccessKeys, SwiftKeys,\
+                                  SubUsers, Suspended, MaxBuckets, OpMask, UserCaps, Admin, \
+                                  System, PlacementName, PlacementStorageClass, PlacementTags, \
+                                  BucketQuota, TempURLKeys, UserQuota, Type, MfaIDs, AssumedRoleARN, \
+                                  UserAttrs, UserVersion, UserVersionTag \
+                                  from '{}' where Tenant = {} and UserID = {} and NS = {}";
+
+  public:
+    virtual ~GetUserOp() {}
+
+    string Schema(DBOpPrepareParams &params) {
+      if (params.op.query_str == "email") {
+        return fmt::format(QueryByEmail.c_str(), params.user_table.c_str(),
+            params.op.user.user_email.c_str());
+      } else if (params.op.query_str == "access_key") {
+        return fmt::format(QueryByAccessKeys.c_str(),
+            params.user_table.c_str(),
+            params.op.user.access_keys_id.c_str());
+      } else if (params.op.query_str == "user_id") {
+        return fmt::format(QueryByUserID.c_str(),
+            params.user_table.c_str(),
+            params.op.user.tenant.c_str(),
+            params.op.user.user_id.c_str(),
+            params.op.user.ns.c_str());
+      } else {
+        return fmt::format(Query.c_str(), params.user_table.c_str(),
+            params.op.user.user_id.c_str());
+      }
+    }
+};
+
+class InsertBucketOp: public DBOp {
+  private:
+    const string Query =
+      "INSERT OR REPLACE INTO '{}' \
+      (BucketName, Tenant, Marker, BucketID, Size, SizeRounded, CreationTime, \
+       Count, PlacementName, PlacementStorageClass, OwnerID, Flags, Zonegroup, \
+       HasInstanceObj, Quota, RequesterPays, HasWebsite, WebsiteConf, \
+       SwiftVersioning, SwiftVerLocation, \
+       MdsearchConfig, NewBucketInstanceID, ObjectLock, \
+       SyncPolicyInfoGroups, BucketAttrs, BucketVersion, BucketVersionTag, Mtime) \
+      VALUES ({}, {}, {}, {}, {}, {}, {}, {}, {}, \
+          {}, {}, {}, {}, {}, {}, {}, {}, {}, \
+          {}, {}, {}, {}, {}, {}, {}, {}, {}, {})";
+
+  public:
+    virtual ~InsertBucketOp() {}
+
+    string Schema(DBOpPrepareParams &params) {
+      return fmt::format(Query.c_str(), params.bucket_table.c_str(),
+          params.op.bucket.bucket_name, params.op.bucket.tenant,
+          params.op.bucket.marker, params.op.bucket.bucket_id,
+          params.op.bucket.size, params.op.bucket.size_rounded,
+          params.op.bucket.creation_time, params.op.bucket.count,
+          params.op.bucket.placement_name, params.op.bucket.placement_storage_class,
+          params.op.user.user_id,
+          params.op.bucket.flags, params.op.bucket.zonegroup, params.op.bucket.has_instance_obj,
+          params.op.bucket.quota, params.op.bucket.requester_pays, params.op.bucket.has_website,
+          params.op.bucket.website_conf, params.op.bucket.swift_versioning,
+          params.op.bucket.swift_ver_location, params.op.bucket.mdsearch_config,
+          params.op.bucket.new_bucket_instance_id, params.op.bucket.obj_lock,
+          params.op.bucket.sync_policy_info_groups, params.op.bucket.bucket_attrs,
+          params.op.bucket.bucket_ver, params.op.bucket.bucket_ver_tag,
+          params.op.bucket.mtime);
+    }
+};
+
+class UpdateBucketOp: public DBOp {
+  private:
+    // Updates Info, Mtime, Version
+    const string InfoQuery =
+      "UPDATE '{}' SET Tenant = {}, Marker = {}, BucketID = {}, CreationTime = {}, \
+      Count = {}, PlacementName = {}, PlacementStorageClass = {}, OwnerID = {}, Flags = {}, \
+      Zonegroup = {}, HasInstanceObj = {}, Quota = {}, RequesterPays = {}, HasWebsite = {}, \
+      WebsiteConf = {}, SwiftVersioning = {}, SwiftVerLocation = {}, MdsearchConfig = {}, \
+      NewBucketInstanceID = {}, ObjectLock = {}, SyncPolicyInfoGroups = {}, \
+      BucketVersion = {}, Mtime = {} WHERE BucketName = {}";
+    // Updates Attrs, OwnerID, Mtime, Version
+    const string AttrsQuery =
+      "UPDATE '{}' SET OwnerID = {}, BucketAttrs = {}, Mtime = {}, BucketVersion = {} \
+      WHERE BucketName = {}";
+    // Updates OwnerID, CreationTime, Mtime, Version
+    const string OwnerQuery =
+      "UPDATE '{}' SET OwnerID = {}, CreationTime = {}, Mtime = {}, BucketVersion = {} WHERE BucketName = {}";
+
+  public:
+    virtual ~UpdateBucketOp() {}
+
+    string Schema(DBOpPrepareParams &params) {
+      if (params.op.query_str == "info") {
+        return fmt::format(InfoQuery.c_str(), params.bucket_table.c_str(),
+            params.op.bucket.tenant, params.op.bucket.marker, params.op.bucket.bucket_id,
+            params.op.bucket.creation_time, params.op.bucket.count,
+            params.op.bucket.placement_name, params.op.bucket.placement_storage_class,
+            params.op.user.user_id,
+            params.op.bucket.flags, params.op.bucket.zonegroup, params.op.bucket.has_instance_obj,
+            params.op.bucket.quota, params.op.bucket.requester_pays, params.op.bucket.has_website,
+            params.op.bucket.website_conf, params.op.bucket.swift_versioning,
+            params.op.bucket.swift_ver_location, params.op.bucket.mdsearch_config,
+            params.op.bucket.new_bucket_instance_id, params.op.bucket.obj_lock,
+            params.op.bucket.sync_policy_info_groups,
+            params.op.bucket.bucket_ver, params.op.bucket.mtime,
+            params.op.bucket.bucket_name);
+      }
+      if (params.op.query_str == "attrs") {
+        return fmt::format(AttrsQuery.c_str(), params.bucket_table.c_str(),
+            params.op.user.user_id, params.op.bucket.bucket_attrs,
+            params.op.bucket.mtime,
+            params.op.bucket.bucket_ver, params.op.bucket.bucket_name.c_str());
+      }
+      if (params.op.query_str == "owner") {
+        return fmt::format(OwnerQuery.c_str(), params.bucket_table.c_str(),
+            params.op.user.user_id, params.op.bucket.creation_time,
+            params.op.bucket.mtime,
+            params.op.bucket.bucket_ver, params.op.bucket.bucket_name.c_str());
+      }
+      return "";
+    }
+};
+
+class RemoveBucketOp: public DBOp {
+  private:
+    const string Query =
+      "DELETE from '{}' where BucketName = {}";
+
+  public:
+    virtual ~RemoveBucketOp() {}
+
+    string Schema(DBOpPrepareParams &params) {
+      return fmt::format(Query.c_str(), params.bucket_table.c_str(),
+          params.op.bucket.bucket_name.c_str());
+    }
+};
+
+class GetBucketOp: public DBOp {
+  private:
+    const string Query = "SELECT  \
+                          BucketName, BucketTable.Tenant, Marker, BucketID, Size, SizeRounded, CreationTime, \
+                          Count, BucketTable.PlacementName, BucketTable.PlacementStorageClass, OwnerID, Flags, Zonegroup, \
+                          HasInstanceObj, Quota, RequesterPays, HasWebsite, WebsiteConf, \
+                          SwiftVersioning, SwiftVerLocation, \
+                          MdsearchConfig, NewBucketInstanceID, ObjectLock, \
+                          SyncPolicyInfoGroups, BucketAttrs, BucketVersion, BucketVersionTag, Mtime, NS \
+                          from '{}' as BucketTable INNER JOIN '{}' ON OwnerID = UserID where BucketName = {}";
+
+  public:
+    virtual ~GetBucketOp() {}
+
+    string Schema(DBOpPrepareParams &params) {
+      //return fmt::format(Query.c_str(), params.op.bucket.bucket_name.c_str(),
+      //          params.bucket_table.c_str(), params.user_table.c_str());
+      return fmt::format(Query.c_str(),
+          params.bucket_table.c_str(), params.user_table.c_str(),
+          params.op.bucket.bucket_name.c_str());
+    }
+};
+
+class ListUserBucketsOp: public DBOp {
+  private:
+    // once we have stats also stored, may have to update this query to join
+    // these two tables.
+    const string Query = "SELECT  \
+                          BucketName, Tenant, Marker, BucketID, Size, SizeRounded, CreationTime, \
+                          Count, PlacementName, PlacementStorageClass, OwnerID, Flags, Zonegroup, \
+                          HasInstanceObj, Quota, RequesterPays, HasWebsite, WebsiteConf, \
+                          SwiftVersioning, SwiftVerLocation, \
+                          MdsearchConfig, NewBucketInstanceID, ObjectLock, \
+                          SyncPolicyInfoGroups, BucketAttrs, BucketVersion, BucketVersionTag, Mtime \
+                          FROM '{}' WHERE OwnerID = {} AND BucketName > {} ORDER BY BucketName ASC LIMIT {}";
+
+  public:
+    virtual ~ListUserBucketsOp() {}
+
+    string Schema(DBOpPrepareParams &params) {
+      return fmt::format(Query.c_str(), params.bucket_table.c_str(),
+          params.op.user.user_id.c_str(), params.op.bucket.min_marker.c_str(),
+          params.op.list_max_count.c_str());
+    }
+};
+
+class InsertObjectOp: public DBOp {
+  private:
+    const string Query =
+      "INSERT OR REPLACE INTO '{}' (BucketName, ObjectName) VALUES ({}, {})";
+
+  public:
+    virtual ~InsertObjectOp() {}
+
+    string Schema(DBOpPrepareParams &params) {
+      return fmt::format(Query.c_str(),
+          params.object_table.c_str(), params.op.bucket.bucket_name.c_str(),
+          params.object.c_str());
+    }
+};
+
+class RemoveObjectOp: public DBOp {
+  private:
+    const string Query =
+      "DELETE from '{}' where BucketName = {} and ObjectName = {}";
+
+  public:
+    virtual ~RemoveObjectOp() {}
+
+    string Schema(DBOpPrepareParams &params) {
+      return fmt::format(Query.c_str(), params.object_table.c_str(),
+          params.op.bucket.bucket_name.c_str(), params.object.c_str());
+    }
+};
+
+class ListObjectOp: public DBOp {
+  private:
+    const string Query =
+      "SELECT  * from '{}' where BucketName = {} and ObjectName = {}";
+    // XXX: Include queries for specific bucket and user too
+
+  public:
+    virtual ~ListObjectOp() {}
+
+    string Schema(DBOpPrepareParams &params) {
+      return fmt::format(Query.c_str(), params.object_table.c_str(),
+          params.op.bucket.bucket_name.c_str(), params.object.c_str());
+    }
+};
+
+class PutObjectDataOp: public DBOp {
+  private:
+    const string Query =
+      "INSERT OR REPLACE INTO '{}' (BucketName, ObjectName, Offset, Data, Size) \
+      VALUES ({}, {}, {}, {}, {})";
+
+  public:
+    virtual ~PutObjectDataOp() {}
+
+    string Schema(DBOpPrepareParams &params) {
+      return fmt::format(Query.c_str(),
+          params.objectdata_table.c_str(),
+          params.op.bucket.bucket_name.c_str(), params.object.c_str(),
+          params.offset.c_str(), params.data.c_str(),
+          params.datalen.c_str());
+    }
+};
+
+class GetObjectDataOp: public DBOp {
+  private:
+    const string Query =
+      "SELECT * from '{}' where BucketName = {} and ObjectName = {}";
+
+  public:
+    virtual ~GetObjectDataOp() {}
+
+    string Schema(DBOpPrepareParams &params) {
+      return fmt::format(Query.c_str(),
+          params.objectdata_table.c_str(), params.op.bucket.bucket_name.c_str(),
+          params.object.c_str());
+    }
+};
+
+class DeleteObjectDataOp: public DBOp {
+  private:
+    const string Query =
+      "DELETE from '{}' where BucketName = {} and ObjectName = {}";
+
+  public:
+    virtual ~DeleteObjectDataOp() {}
+
+    string Schema(DBOpPrepareParams &params) {
+      return fmt::format(Query.c_str(),
+          params.objectdata_table.c_str(), params.op.bucket.bucket_name.c_str(),
+          params.object.c_str());
+    }
+};
+
+class DBStore {
+  private:
+    const string db_name;
+    const string user_table;
+    const string bucket_table;
+    const string quota_table;
+    static map<string, class ObjectOp*> 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<string, class ObjectOp*> 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 &params) = 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<string, bufferlist> *pattrs,
+        RGWObjVersionTracker *pobjv_tracker);
+    int store_user(RGWUserInfo& uinfo, bool exclusive, map<string, bufferlist> *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<std::string, bufferlist>& 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<std::string, bufferlist>* 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 (file)
index 0000000..daff539
--- /dev/null
@@ -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 <errno.h>
+#include <stdlib.h>
+#include <string>
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+
+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 (file)
index 0000000..ad9801b
--- /dev/null
@@ -0,0 +1,192 @@
+#include <stdio.h>
+#include <sqlite3.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "dbstore_mgr.h"
+#include <dbstore.h>
+#include <dbstore_log.h>
+
+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:"<<thr_id<<"\n";
+
+  string user1 = "User1";
+  string bucketa = "rgw";
+  string objecta1 = "bugfixing";
+  string objecta2 = "zipper";
+  string bucketb = "gluster";
+  string objectb1 = "bugfixing";
+  string objectb2 = "delegations";
+
+  string user2 = "User2";
+  string bucketc = "qe";
+  string objectc1 = "rhhi";
+  string objectc2 = "cns";
+
+  struct DBOpParams params = {};
+
+  db->InitializeParams("InsertUser", &params);
+
+  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", &params);
+  cout << "InsertUser return value: " <<  ret << "\n";
+
+  struct DBOpParams params2 = {};
+  params.op.user.uinfo.user_id.tenant = "tenant2";
+
+  db->InitializeParams("GetUser", &params2);
+  params2.op.user.uinfo.display_name = user1;
+  ret = db->ProcessOp("GetUser", &params2);
+
+  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<string>::iterator it = params2.op.user.uinfo.placement_tags.begin();
+
+  while (it != params2.op.user.uinfo.placement_tags.end()) {
+    cout << "list = " << *it << "\n";
+    it++;
+  }
+
+  map<string, RGWAccessKey>::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", &params);
+
+  params.op.user.uinfo.display_name = user2;
+  params.op.user.uinfo.user_id.id = user2;
+  db->ProcessOp("InsertUser", &params);
+
+  params.op.bucket.info.bucket.name = bucketb;
+  db->ProcessOp("InsertBucket", &params);
+
+  db->ProcessOp("GetUser", &params);
+  db->ProcessOp("GetBucket", &params);
+
+  db->ListAllUsers(&params);
+  db->ListAllBuckets(&params);
+
+  params.op.bucket.info.bucket.name = bucketb;
+
+  db->ProcessOp("RemoveBucket", &params);
+
+  params.op.user.uinfo.user_id.id = user2;
+  db->ProcessOp("RemoveUser", &params);
+
+  db->ListAllUsers(&params);
+  db->ListAllBuckets(&params);
+  dbout(L_EVENT)<<"Exiting thread:"<<thr_id<<"\n";
+
+  return 0;
+}
+
+int main(int argc, char *argv[])
+{
+  string tenant = "Redhat";
+  string logfile;
+
+  class DBStoreManager dbsm;
+  class DBStore *dbs;
+  int rc = 0, tnum = 0;
+  void *res;
+
+  pthread_attr_t attr;
+  int num_thr = 2;
+  pthread_t threads[num_thr];
+  struct thr_args t_args[num_thr];
+
+  cout << "loglevel  " << loglevel << "\n";
+  // format: ./dbstore logfile loglevel
+  if (argc == 3) {
+    logfile = argv[1];
+    loglevel = (atoi)(argv[2]);
+    cout << "loglevel set to " << loglevel << "\n";
+  }
+
+  dbs = dbsm.getDBStore(tenant, true);
+
+  dbout(L_EVENT)<<"No. of threads being created = "<<num_thr<<"\n";
+
+  /* Initialize thread creation attributes */
+  rc = pthread_attr_init(&attr);
+
+  if (rc != 0) {
+    dbout(L_ERR)<<" error in pthread_attr_init \n";
+    goto out;
+  }
+
+  for (tnum = 0; tnum < num_thr; tnum++) {
+    t_args[tnum].dbs = dbs;
+    t_args[tnum].thr_id = tnum;
+    rc = pthread_create((pthread_t*)&threads[tnum], &attr, &process,
+        &t_args[tnum]);
+    if (rc != 0) {
+      dbout(L_ERR)<<" error in pthread_create \n";
+      goto out;
+    }
+
+    dbout(L_FULLDEBUG)<<"Created thread (thread-id:"<<tnum<<")\n";
+  }
+
+  /* Destroy the thread attributes object, since it is no
+     longer needed */
+
+  rc = pthread_attr_destroy(&attr);
+  if (rc != 0) {
+    dbout(loglevel)<<"error in pthread_attr_destroy \n";
+  }
+
+  /* Now join with each thread, and display its returned value */
+
+  for (tnum = 0; tnum < num_thr; tnum++) {
+    rc = pthread_join(threads[tnum], &res);
+    if (rc != 0) {
+      dbout(L_ERR)<<"error in pthread_join \n";
+    } else {
+      dbout(L_EVENT)<<"Joined with thread "<<tnum<<"\n";
+    }
+  }
+
+out:
+  dbsm.destroyAllHandles();
+
+  return 0;
+}
diff --git a/src/rgw/store/dbstore/dbstore_mgr.cc b/src/rgw/store/dbstore/dbstore_mgr.cc
new file mode 100644 (file)
index 0000000..26c4240
--- /dev/null
@@ -0,0 +1,130 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "dbstore_mgr.h"
+#include "common/dbstore.h"
+#include "sqlite/sqliteDB.h"
+
+
+/* Given a tenant, find and return the DBStore handle.
+ * If not found and 'create' set to true, create one
+ * and return
+ */
+DBStore* DBStoreManager::getDBStore (string tenant, bool create)
+{
+  map<string, DBStore*>::iterator iter;
+  DBStore *dbs = nullptr;
+  pair<map<string, DBStore*>::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<map<string, DBStore*>::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<<")^^^^^^^^^^^^^^^^^^^^^^^^^^ \n";
+
+    delete dbs;
+    return NULL;
+  }
+
+  /* XXX: Do we need lock to protect this map?
+  */
+  ret = DBStoreHandles.insert(pair<string, DBStore*>(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<string, DBStore*>::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<string, DBStore*>::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 (file)
index 0000000..780de2d
--- /dev/null
@@ -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 <map>
+#include <errno.h>
+#include <stdlib.h>
+#include <string>
+#include <stdio.h>
+#include <iostream>
+
+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<string, DBStore*> 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 (file)
index 0000000..02fdc26
--- /dev/null
@@ -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 (file)
index 0000000..f3daf12
--- /dev/null
@@ -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("<<Op<<"); Errmsg -"\
+      <<sqlite3_errmsg(*sdb)<<"\n";\
+      ret = -1;                                \
+      goto out;                                \
+    }                                          \
+    dbout(L_DEBUG)<<"Successfully Prepared stmt for Op("<<Op   \
+    <<") schema("<<schema<<") stmt("<<stmt<<")\n";     \
+    ret = 0;                                   \
+  } while(0);
+
+#define SQL_BIND_INDEX(stmt, index, str, sdb)  \
+  do {                                         \
+    index = sqlite3_bind_parameter_index(stmt, str);     \
+    \
+    if (index <=0)  {                               \
+      cout<<"failed to fetch bind parameter"\
+      " index for str("<<str<<") in "   \
+      <<"stmt("<<stmt<<"); Errmsg -"    \
+      <<sqlite3_errmsg(*sdb)<<"\n";         \
+      rc = -1;                              \
+      goto out;                                     \
+    }                                               \
+    dbout(L_FULLDEBUG)<<"Bind parameter index for str("  \
+    <<str<<") in stmt("<<stmt<<") is "  \
+    <<index<<"\n";                          \
+  }while(0);
+
+#define SQL_BIND_TEXT(stmt, index, str, sdb)                   \
+  do {                                                         \
+    rc = sqlite3_bind_text(stmt, index, str, -1, SQLITE_TRANSIENT);    \
+    \
+    if (rc != SQLITE_OK) {                                             \
+      dbout(L_ERR)<<"sqlite bind text failed for index("       \
+      <<index<<"), str("<<str<<") in stmt("    \
+      <<stmt<<"); Errmsg - "<<sqlite3_errmsg(*sdb) \
+      <<"\n";                          \
+      rc = -1;                                 \
+      goto out;                                        \
+    }                                                  \
+  }while(0);
+
+#define SQL_BIND_INT(stmt, index, num, sdb)                    \
+  do {                                                         \
+    rc = sqlite3_bind_int(stmt, index, num);           \
+    \
+    if (rc != SQLITE_OK) {                                     \
+      dbout(L_ERR)<<"sqlite bind int failed for index("        \
+      <<index<<"), num("<<num<<") in stmt("    \
+      <<stmt<<"); Errmsg - "<<sqlite3_errmsg(*sdb) \
+      <<"\n";                          \
+      rc = -1;                                 \
+      goto out;                                        \
+    }                                                  \
+  }while(0);
+
+#define SQL_BIND_BLOB(stmt, index, blob, size, sdb)            \
+  do {                                                         \
+    rc = sqlite3_bind_blob(stmt, index, blob, size, SQLITE_TRANSIENT);  \
+    \
+    if (rc != SQLITE_OK) {                                     \
+      dbout(L_ERR)<<"sqlite bind blob failed for index("       \
+      <<index<<"), blob("<<blob<<") in stmt("          \
+      <<stmt<<"); Errmsg - "<<sqlite3_errmsg(*sdb) \
+      <<"\n";                          \
+      rc = -1;                                 \
+      goto out;                                        \
+    }                                                  \
+  }while(0);
+
+#define SQL_ENCODE_BLOB_PARAM(stmt, index, param, sdb)         \
+  do {                                                         \
+    bufferlist b;                                              \
+    encode(param, b);                                  \
+    SQL_BIND_BLOB(stmt, index, b.c_str(), b.length(), sdb); \
+  }while(0);
+
+#define SQL_READ_BLOB(stmt, index, void_ptr, len)              \
+  do {                                                         \
+    void_ptr = NULL;                                   \
+    void_ptr = (void *)sqlite3_column_blob(stmt, index);       \
+    len = sqlite3_column_bytes(stmt, index);           \
+    \
+    if (!void_ptr || len == 0) {                               \
+      dbout(L_FULLDEBUG)<<"Null value for blob index("  \
+      <<index<<") in stmt("<<stmt<<") \n";   \
+    }                                                  \
+  }while(0);
+
+#define SQL_DECODE_BLOB_PARAM(stmt, index, param, sdb)         \
+  do {                                                         \
+    bufferlist b;                                              \
+    void *blob;                                                \
+    int blob_len = 0;                                  \
+    \
+    SQL_READ_BLOB(stmt, index, blob, blob_len);                \
+    \
+    b.append(reinterpret_cast<char *>(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(" <<stmt<<") \n";         \
+      goto out;                        \
+    }                                  \
+    \
+    ret = Step(params->op, stmt, cbk);         \
+    \
+    Reset(stmt);                               \
+    \
+    if (ret) {                         \
+      cout<<"Execution failed for stmt(" <<stmt<<")\n";                \
+      goto out;                        \
+    }                                  \
+  }while(0);
+
+static int list_callback(void *None, int argc, char **argv, char **aname)
+{
+  int i;
+  for(i=0; i<argc; i++) {
+    string arg = argv[i] ? argv[i] : "NULL";
+    cout<<aname[i]<<" = "<<arg<<"\n";
+  }
+  return 0;
+}
+
+enum GetUser {
+  UserID = 0,
+  Tenant,
+  NS,
+  DisplayName,
+  UserEmail,
+  AccessKeysID,
+  AccessKeysSecret,
+  AccessKeys,
+  SwiftKeys,
+  SubUsers,
+  Suspended,
+  MaxBuckets,
+  OpMask,
+  UserCaps,
+  Admin,
+  System,
+  PlacementName,
+  PlacementStorageClass,
+  PlacementTags,
+  BucketQuota,
+  TempURLKeys,
+  UserQuota,
+  TYPE,
+  MfaIDs,
+  AssumedRoleARN,
+  UserAttrs,
+  UserVersion,
+  UserVersionTag,
+};
+
+enum GetBucket {
+  BucketName = 0,
+  Bucket_Tenant, //Tenant
+  Marker,
+  BucketID,
+  Size,
+  SizeRounded,
+  CreationTime,
+  Count,
+  Bucket_PlacementName,
+  Bucket_PlacementStorageClass,
+  OwnerID,
+  Flags,
+  Zonegroup,
+  HasInstanceObj,
+  Quota,
+  RequesterPays,
+  HasWebsite,
+  WebsiteConf,
+  SwiftVersioning,
+  SwiftVerLocation,
+  MdsearchConfig,
+  NewBucketInstanceID,
+  ObjectLock,
+  SyncPolicyInfoGroups,
+  BucketAttrs,
+  BucketVersion,
+  BucketVersionTag,
+  Mtime,
+  Bucket_User_NS
+};
+
+static int list_user(DBOpInfo &op, sqlite3_stmt *stmt) {
+  if (!stmt)
+    return -1;
+
+  cout<<sqlite3_column_text(stmt, 0)<<"\n";
+  /* Ensure the column names match with the user table defined in dbstore.h                     
+     UserID TEXT ,             \ - 0
+     Tenant TEXT ,             \ - 1
+     NS TEXT ,         \ - 2
+     DisplayName TEXT , \ - 3
+     UserEmail TEXT ,  \ - 4
+     AccessKeysID TEXT ,       \ - 5
+     AccessKeysSecret TEXT ,   \ - 6
+     AccessKeys BLOB , \ - 7
+     SwiftKeys BLOB ,  \ - 8
+     SubUsers BLOB ,           \ - 9
+     Suspended INTEGER ,       \ - 10
+     MaxBuckets INTEGER ,      \ - 11
+     OpMask    INTEGER ,       \ - 12
+     UserCaps BLOB ,           \ - 13
+     Admin     INTEGER ,       \ - 14
+     System INTEGER ,  \ - 15
+     PlacementName TEXT ,      \ - 16
+     PlacementStorageClass TEXT ,      \ - 17
+     PlacementTags BLOB ,      \ - 18
+     BucketQuota BLOB ,        \ - 19
+     TempURLKeys BLOB ,        \ - 20
+     UserQuota BLOB ,  \ - 21
+     TYPE INTEGER ,            \ - 22
+     MfaIDs INTEGER ,  \ - 23
+     AssumedRoleARN TEXT \n);"; - 24
+     */
+
+  op.user.uinfo.user_id.tenant = (const char*)sqlite3_column_text(stmt, Tenant);
+  op.user.uinfo.user_id.id = (const char*)sqlite3_column_text(stmt, UserID);
+  op.user.uinfo.user_id.ns = (const char*)sqlite3_column_text(stmt, NS);
+  op.user.uinfo.display_name = (const char*)sqlite3_column_text(stmt, DisplayName); // user_name
+  op.user.uinfo.user_email = (const char*)sqlite3_column_text(stmt, UserEmail);
+
+  SQL_DECODE_BLOB_PARAM(stmt, AccessKeys, op.user.uinfo.access_keys, sdb);
+  SQL_DECODE_BLOB_PARAM(stmt, SwiftKeys, op.user.uinfo.swift_keys, sdb);
+  SQL_DECODE_BLOB_PARAM(stmt, SubUsers, op.user.uinfo.subusers, sdb);
+
+  op.user.uinfo.suspended = sqlite3_column_int(stmt, Suspended);
+  op.user.uinfo.max_buckets = sqlite3_column_int(stmt, MaxBuckets);
+  op.user.uinfo.op_mask = sqlite3_column_int(stmt, OpMask);
+
+  SQL_DECODE_BLOB_PARAM(stmt, UserCaps, op.user.uinfo.caps, sdb);
+
+  op.user.uinfo.admin = sqlite3_column_int(stmt, Admin);
+  op.user.uinfo.system = sqlite3_column_int(stmt, System);
+
+  op.user.uinfo.default_placement.name = (const char*)sqlite3_column_text(stmt, PlacementName);
+
+  op.user.uinfo.default_placement.storage_class = (const char*)sqlite3_column_text(stmt, PlacementStorageClass);
+
+  SQL_DECODE_BLOB_PARAM(stmt, PlacementTags, op.user.uinfo.placement_tags, sdb);
+
+  SQL_DECODE_BLOB_PARAM(stmt, BucketQuota, op.user.uinfo.bucket_quota, sdb);
+  SQL_DECODE_BLOB_PARAM(stmt, TempURLKeys, op.user.uinfo.temp_url_keys, sdb);
+  SQL_DECODE_BLOB_PARAM(stmt, UserQuota, op.user.uinfo.user_quota, sdb);
+
+  op.user.uinfo.type = sqlite3_column_int(stmt, TYPE);
+
+  SQL_DECODE_BLOB_PARAM(stmt, MfaIDs, op.user.uinfo.mfa_ids, sdb);
+
+  op.user.uinfo.assumed_role_arn = (const char*)sqlite3_column_text(stmt, AssumedRoleARN);
+
+  SQL_DECODE_BLOB_PARAM(stmt, UserAttrs, op.user.user_attrs, sdb);
+  op.user.user_version.ver = sqlite3_column_int(stmt, UserVersion);
+  op.user.user_version.tag = (const char*)sqlite3_column_text(stmt, UserVersionTag);
+
+  return 0;
+}
+
+static int list_bucket(DBOpInfo &op, sqlite3_stmt *stmt) {
+  if (!stmt)
+    return -1;
+
+  cout<<sqlite3_column_text(stmt, 0)<<", ";
+  cout<<sqlite3_column_text(stmt, 1)<<"\n";
+
+  op.bucket.ent.bucket.name = (const char*)sqlite3_column_text(stmt, BucketName);
+  op.bucket.ent.bucket.tenant = (const char*)sqlite3_column_text(stmt, Bucket_Tenant);
+  op.bucket.ent.bucket.marker = (const char*)sqlite3_column_text(stmt, Marker);
+  op.bucket.ent.bucket.bucket_id = (const char*)sqlite3_column_text(stmt, BucketID);
+  op.bucket.ent.size = sqlite3_column_int(stmt, Size);
+  op.bucket.ent.size_rounded = sqlite3_column_int(stmt, SizeRounded);
+  SQL_DECODE_BLOB_PARAM(stmt, CreationTime, op.bucket.ent.creation_time, sdb);
+  op.bucket.ent.count = sqlite3_column_int(stmt, Count);
+  op.bucket.ent.placement_rule.name = (const char*)sqlite3_column_text(stmt, Bucket_PlacementName);
+  op.bucket.ent.placement_rule.storage_class = (const char*)sqlite3_column_text(stmt, Bucket_PlacementStorageClass);
+
+  op.bucket.info.bucket = op.bucket.ent.bucket;
+  op.bucket.info.placement_rule = op.bucket.ent.placement_rule;
+  op.bucket.info.creation_time = op.bucket.ent.creation_time;
+
+  op.bucket.info.owner.id = (const char*)sqlite3_column_text(stmt, OwnerID);
+  op.bucket.info.owner.tenant = op.bucket.ent.bucket.tenant;
+
+  if (op.name == "GetBucket") {
+    op.bucket.info.owner.ns = (const char*)sqlite3_column_text(stmt, Bucket_User_NS);
+  }
+
+  op.bucket.info.flags = sqlite3_column_int(stmt, Flags);
+  op.bucket.info.zonegroup = (const char*)sqlite3_column_text(stmt, Zonegroup);
+  op.bucket.info.has_instance_obj = sqlite3_column_int(stmt, HasInstanceObj);
+
+  SQL_DECODE_BLOB_PARAM(stmt, Quota, op.bucket.info.quota, sdb);
+  op.bucket.info.requester_pays = sqlite3_column_int(stmt, RequesterPays);
+  op.bucket.info.has_website = sqlite3_column_int(stmt, HasWebsite);
+  SQL_DECODE_BLOB_PARAM(stmt, WebsiteConf, op.bucket.info.website_conf, sdb);
+  op.bucket.info.swift_versioning = sqlite3_column_int(stmt, SwiftVersioning);
+  op.bucket.info.swift_ver_location = (const char*)sqlite3_column_text(stmt, SwiftVerLocation);
+  SQL_DECODE_BLOB_PARAM(stmt, MdsearchConfig, op.bucket.info.mdsearch_config, sdb);
+  op.bucket.info.new_bucket_instance_id = (const char*)sqlite3_column_text(stmt, NewBucketInstanceID);
+  SQL_DECODE_BLOB_PARAM(stmt, ObjectLock, op.bucket.info.obj_lock, sdb);
+  SQL_DECODE_BLOB_PARAM(stmt, SyncPolicyInfoGroups, op.bucket.info.sync_policy, sdb);
+  SQL_DECODE_BLOB_PARAM(stmt, BucketAttrs, op.bucket.bucket_attrs, sdb);
+  op.bucket.bucket_version.ver = sqlite3_column_int(stmt, BucketVersion);
+  op.bucket.bucket_version.tag = (const char*)sqlite3_column_text(stmt, BucketVersionTag);
+
+  /* Read bucket version into info.objv_tracker.read_ver. No need
+   * to set write_ver as its not used anywhere. Still keeping its
+   * value same as read_ver */
+  op.bucket.info.objv_tracker.read_version = op.bucket.bucket_version;
+  op.bucket.info.objv_tracker.write_version = op.bucket.bucket_version;
+
+  SQL_DECODE_BLOB_PARAM(stmt, Mtime, op.bucket.mtime, sdb);
+
+  op.bucket.list_entries.push_back(op.bucket.ent);
+
+  return 0;
+}
+
+static int list_object(DBOpInfo &op, sqlite3_stmt *stmt) {
+  if (!stmt)
+    return -1;
+
+  cout<<sqlite3_column_text(stmt, 0)<<", ";
+  cout<<sqlite3_column_text(stmt, 1)<<"\n";
+
+  return 0;
+}
+
+static int get_objectdata(DBOpInfo &op, sqlite3_stmt *stmt) {
+  if (!stmt)
+    return -1;
+
+  int datalen = 0;
+  const void *blob = NULL;
+
+  blob = sqlite3_column_blob(stmt, 3);
+  datalen = sqlite3_column_bytes(stmt, 3);
+
+  cout<<sqlite3_column_text(stmt, 0)<<", ";
+  cout<<sqlite3_column_text(stmt, 1)<<",";
+  cout<<sqlite3_column_int(stmt, 2)<<",";
+  char data[datalen+1] = {};
+  if (blob)
+    strncpy(data, (const char *)blob, datalen);
+
+  cout<<data<<","<<datalen<<"\n";
+
+  return 0;
+}
+
+int SQLiteDB::InitializeDBOps()
+{
+  (void)createTables();
+  dbops.InsertUser = new SQLInsertUser(&this->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 "<<dbname<<"; Errmsg - "\
+      <<sqlite3_errmsg((sqlite3*)db)<<"\n";
+  } else {
+    dbout(L_DEBUG)<<"Opened database("<<dbname<<") successfully\n";
+  }
+
+  exec("PRAGMA foreign_keys=ON", NULL);
+
+out:
+  return db;
+}
+
+int SQLiteDB::closeDB()
+{
+  if (db)
+    sqlite3_close((sqlite3 *)db);
+
+  db = NULL;
+
+  return 0;
+}
+
+int SQLiteDB::Reset(sqlite3_stmt *stmt)
+{
+  int ret = -1;
+
+  if (!stmt) {
+    return -1;
+  }
+  sqlite3_clear_bindings(stmt);
+  ret = sqlite3_reset(stmt);
+
+  return ret;
+}
+
+int SQLiteDB::Step(DBOpInfo &op, sqlite3_stmt *stmt,
+    int (*cbk)(DBOpInfo &op, sqlite3_stmt *stmt))
+{
+  int ret = -1;
+
+  if (!stmt) {
+    return -1;
+  }
+
+again:
+  ret = sqlite3_step(stmt);
+
+  if ((ret != SQLITE_DONE) && (ret != SQLITE_ROW)) {
+    dbout(L_ERR)<<"sqlite step failed for stmt("<<stmt \
+      <<"); Errmsg - "<<sqlite3_errmsg((sqlite3*)db)<<"\n";
+    return -1;
+  } else if (ret == SQLITE_ROW) {
+    if (cbk) {
+      (*cbk)(op, stmt);
+    } else {
+    }
+    goto again;
+  }
+
+  dbout(L_FULLDEBUG)<<"sqlite step successfully executed for stmt(" \
+    <<stmt<<")  ret = " << ret <<"\n";
+
+  return 0;
+}
+
+int SQLiteDB::exec(const char *schema,
+    int (*callback)(void*,int,char**,char**))
+{
+  int ret = -1;
+  char *errmsg = NULL;
+
+  if (!db)
+    goto out;
+
+  ret = sqlite3_exec((sqlite3*)db, schema, callback, 0, &errmsg);
+  if (ret != SQLITE_OK) {
+    dbout(L_ERR)<<"sqlite exec failed for schema("<<schema \
+      <<"); Errmsg - "<<errmsg<<"\n";
+    sqlite3_free(errmsg);
+    goto out;
+  }
+  ret = 0;
+  dbout(L_FULLDEBUG)<<"sqlite exec successfully processed for schema(" \
+    <<schema<<")\n";
+out:
+  return ret;
+}
+
+int SQLiteDB::createTables()
+{
+  int ret = -1;
+  int cu, cb = -1;
+  DBOpParams params = {};
+
+  params.user_table = getUserTable();
+  params.bucket_table = getBucketTable();
+
+  if ((cu = createUserTable(&params)))
+    goto out;
+
+  if ((cb = createBucketTable(&params)))
+    goto out;
+
+  if ((cb = createQuotaTable(&params)))
+    goto out;
+
+  ret = 0;
+out:
+  if (ret) {
+    if (cu)
+      DeleteUserTable(&params);
+    if (cb)
+      DeleteBucketTable(&params);
+    dbout(L_ERR)<<"Creation of tables failed \n";
+  }
+
+  return ret;
+}
+
+int SQLiteDB::createUserTable(DBOpParams *params)
+{
+  int ret = -1;
+  string schema;
+
+  schema = CreateTableSchema("User", params);
+
+  ret = exec(schema.c_str(), NULL);
+  if (ret)
+    dbout(L_ERR)<<"CreateUserTable failed \n";
+
+  dbout(L_FULLDEBUG)<<"CreateUserTable suceeded \n";
+
+  return ret;
+}
+
+int SQLiteDB::createBucketTable(DBOpParams *params)
+{
+  int ret = -1;
+  string schema;
+
+  schema = CreateTableSchema("Bucket", params);
+
+  ret = exec(schema.c_str(), NULL);
+  if (ret)
+    dbout(L_ERR)<<"CreateBucketTable failed \n";
+
+  dbout(L_FULLDEBUG)<<"CreateBucketTable suceeded \n";
+
+  return ret;
+}
+
+int SQLiteDB::createObjectTable(DBOpParams *params)
+{
+  int ret = -1;
+  string schema;
+
+  schema = CreateTableSchema("Object", params);
+
+  ret = exec(schema.c_str(), NULL);
+  if (ret)
+    dbout(L_ERR)<<"CreateObjectTable failed \n";
+
+  dbout(L_FULLDEBUG)<<"CreateObjectTable suceeded \n";
+
+  return ret;
+}
+
+int SQLiteDB::createQuotaTable(DBOpParams *params)
+{
+  int ret = -1;
+  string schema;
+
+  schema = CreateTableSchema("Quota", params);
+
+  ret = exec(schema.c_str(), NULL);
+  if (ret)
+    dbout(L_ERR)<<"CreateQuotaTable failed \n";
+
+  dbout(L_FULLDEBUG)<<"CreateQuotaTable suceeded \n";
+
+  return ret;
+}
+
+int SQLiteDB::createObjectDataTable(DBOpParams *params)
+{
+  int ret = -1;
+  string schema;
+
+  schema = CreateTableSchema("ObjectData", params);
+
+  ret = exec(schema.c_str(), NULL);
+  if (ret)
+    dbout(L_ERR)<<"CreateObjectDataTable failed \n";
+
+  dbout(L_FULLDEBUG)<<"CreateObjectDataTable suceeded \n";
+
+  return ret;
+}
+
+int SQLiteDB::DeleteUserTable(DBOpParams *params)
+{
+  int ret = -1;
+  string schema;
+
+  schema = DeleteTableSchema(params->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<string, class ObjectOp*>::iterator iter;
+  map<string, class ObjectOp*> 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<string, RGWAccessKey>::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<string, RGWAccessKey>::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(&copy);
+
+  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(&copy);
+
+
+  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(&copy);
+
+  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(&copy);
+
+  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(&copy);
+
+  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 (file)
index 0000000..70408d8
--- /dev/null
@@ -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 <errno.h>
+#include <stdlib.h>
+#include <string>
+#include <sqlite3.h>
+#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 &params) { 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 (file)
index 0000000..e7a2a1e
--- /dev/null
@@ -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 (file)
index 0000000..f99c8ad
--- /dev/null
@@ -0,0 +1,674 @@
+#include "gtest/gtest.h"
+#include <iostream>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dbstore.h>
+#include <sqliteDB.h>
+
+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", &params);
+       ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreBaseTest, GetUser) {
+       struct DBOpParams params = GlobalParams;
+       int ret = -1;
+
+       ret = db->ProcessOp("GetUser", &params);
+       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<string, RGWAccessKey>::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", &params);
+       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<string, RGWAccessKey>::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<std::string, bufferlist> 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<string, RGWAccessKey>::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<string, RGWAccessKey>::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<std::string, bufferlist> 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<std::string, bufferlist> 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<string, RGWAccessKey>::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<std::string, bufferlist>::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(&params);
+       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", &params);
+       ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreBaseTest, UpdateBucketAttrs) {
+       int ret = -1;
+    RGWBucketInfo info;
+    map<std::string, bufferlist> 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", &params);
+       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", &params);
+       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<std::string, bufferlist>::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<std::string, bufferlist> 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(&params);
+       ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreBaseTest, InsertObject) {
+       struct DBOpParams params = GlobalParams;
+       int ret = -1;
+
+       ret = db->ProcessOp("InsertObject", &params);
+       ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreBaseTest, ListObject) {
+       struct DBOpParams params = GlobalParams;
+       int ret = -1;
+
+       ret = db->ProcessOp("ListObject", &params);
+       ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreBaseTest, ListAllObjects) {
+       struct DBOpParams params = GlobalParams;
+       int ret = -1;
+
+       ret = db->ListAllObjects(&params);
+       ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreBaseTest, PutObjectData) {
+       struct DBOpParams params = GlobalParams;
+       int ret = -1;
+
+       ret = db->ProcessOp("PutObjectData", &params);
+       ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreBaseTest, GetObjectData) {
+       struct DBOpParams params = GlobalParams;
+       int ret = -1;
+
+       ret = db->ProcessOp("GetObjectData", &params);
+       ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreBaseTest, DeleteObjectData) {
+       struct DBOpParams params = GlobalParams;
+       int ret = -1;
+
+       ret = db->ProcessOp("DeleteObjectData", &params);
+       ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreBaseTest, RemoveObject) {
+       struct DBOpParams params = GlobalParams;
+       int ret = -1;
+
+       ret = db->ProcessOp("RemoveObject", &params);
+       ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreBaseTest, RemoveBucket) {
+       struct DBOpParams params = GlobalParams;
+       int ret = -1;
+
+       ret = db->ProcessOp("RemoveBucket", &params);
+       ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreBaseTest, RemoveUser) {
+       struct DBOpParams params = GlobalParams;
+       int ret = -1;
+
+       ret = db->ProcessOp("RemoveUser", &params);
+       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", &params);
+       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;
+}