summaryrefslogtreecommitdiffstats
path: root/src/rgw/driver/dbstore
diff options
context:
space:
mode:
Diffstat (limited to 'src/rgw/driver/dbstore')
-rw-r--r--src/rgw/driver/dbstore/CMakeLists.txt71
-rw-r--r--src/rgw/driver/dbstore/README.md53
-rw-r--r--src/rgw/driver/dbstore/common/connection_pool.h147
-rw-r--r--src/rgw/driver/dbstore/common/dbstore.cc2252
-rw-r--r--src/rgw/driver/dbstore/common/dbstore.h2016
-rw-r--r--src/rgw/driver/dbstore/common/dbstore_log.h15
-rw-r--r--src/rgw/driver/dbstore/config/sqlite.cc2070
-rw-r--r--src/rgw/driver/dbstore/config/sqlite.h172
-rw-r--r--src/rgw/driver/dbstore/config/sqlite_schema.h299
-rw-r--r--src/rgw/driver/dbstore/config/store.cc38
-rw-r--r--src/rgw/driver/dbstore/config/store.h27
-rw-r--r--src/rgw/driver/dbstore/dbstore_main.cc199
-rw-r--r--src/rgw/driver/dbstore/dbstore_mgr.cc140
-rw-r--r--src/rgw/driver/dbstore/dbstore_mgr.h56
-rw-r--r--src/rgw/driver/dbstore/sqlite/CMakeLists.txt16
-rw-r--r--src/rgw/driver/dbstore/sqlite/connection.cc34
-rw-r--r--src/rgw/driver/dbstore/sqlite/connection.h64
-rw-r--r--src/rgw/driver/dbstore/sqlite/error.cc37
-rw-r--r--src/rgw/driver/dbstore/sqlite/error.h81
-rw-r--r--src/rgw/driver/dbstore/sqlite/sqliteDB.cc2996
-rw-r--r--src/rgw/driver/dbstore/sqlite/sqliteDB.h551
-rw-r--r--src/rgw/driver/dbstore/sqlite/statement.cc196
-rw-r--r--src/rgw/driver/dbstore/sqlite/statement.h83
-rw-r--r--src/rgw/driver/dbstore/tests/CMakeLists.txt17
-rw-r--r--src/rgw/driver/dbstore/tests/dbstore_mgr_tests.cc157
-rw-r--r--src/rgw/driver/dbstore/tests/dbstore_tests.cc1417
26 files changed, 13204 insertions, 0 deletions
diff --git a/src/rgw/driver/dbstore/CMakeLists.txt b/src/rgw/driver/dbstore/CMakeLists.txt
new file mode 100644
index 000000000..a3aca7a64
--- /dev/null
+++ b/src/rgw/driver/dbstore/CMakeLists.txt
@@ -0,0 +1,71 @@
+#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
+ config/store.cc)
+IF(USE_SQLITE)
+ list(APPEND dbstore_srcs
+ config/sqlite.cc
+ sqlite/connection.cc
+ sqlite/error.cc
+ sqlite/statement.cc)
+endif()
+
+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/rgw"
+ PUBLIC "${CMAKE_SOURCE_DIR}/src/rgw/store/rados"
+ PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
+set(link_targets spawn)
+if(WITH_JAEGER)
+ list(APPEND link_targets jaeger_base)
+endif()
+list(APPEND link_targets rgw_common)
+target_link_libraries(dbstore_lib PUBLIC ${link_targets})
+
+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_common)
+ 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 STATIC ${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/driver/dbstore/README.md b/src/rgw/driver/dbstore/README.md
new file mode 100644
index 000000000..0867bc2cc
--- /dev/null
+++ b/src/rgw/driver/dbstore/README.md
@@ -0,0 +1,53 @@
+# DBStore
+Standalone Rados Gateway (RGW) on DBStore (Experimental)
+
+
+## CMake Option
+Add below cmake option (enabled by default)
+
+ -DWITH_RADOSGW_DBSTORE=ON
+
+
+## Build
+
+ cd build
+ ninja [vstart]
+
+
+## Running Test cluster
+Edit ceph.conf to add below option
+
+ [client]
+ rgw backend store = dbstore
+
+Start vstart cluster
+
+ [..] RGW=1 ../src/vstart.sh -o rgw_backend_store=dbstore -n -d
+
+The above vstart command brings up RGW server on dbstore and creates few default users (eg., testid) to be used for s3 operations.
+
+`radosgw-admin` can be used to create and remove other users.
+
+
+By default, dbstore creates .db file *'/var/lib/ceph/radosgw/dbstore-default_ns.db'* to store the data. This can be configured using below options in ceph.conf
+
+ [client]
+ dbstore db dir = <path for the directory for storing the db backend store data>
+ dbstore db name prefix = <prefix to the file names created by db backend store>
+
+
+## DBStore Unit Tests
+To execute DBStore unit test cases (using Gtest framework), from build directory
+
+ ninja unittest_dbstore_tests
+ ./bin/unittest_dbstore_tests [logfile] [loglevel]
+ (default logfile: rgw_dbstore_tests.log, loglevel: 20)
+ ninja unittest_dbstore_mgr_tests
+ ./bin/unittest_dbstore_mgr_tests
+
+To execute Sample test file
+
+ ninja src/rgw/driver/dbstore/install
+ ./bin/dbstore-bin [logfile] [loglevel]
+ (default logfile: rgw_dbstore_bin.log, loglevel: 20)
+
diff --git a/src/rgw/driver/dbstore/common/connection_pool.h b/src/rgw/driver/dbstore/common/connection_pool.h
new file mode 100644
index 000000000..07f3c81c3
--- /dev/null
+++ b/src/rgw/driver/dbstore/common/connection_pool.h
@@ -0,0 +1,147 @@
+// -*- 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) 2022 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 <concepts>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <boost/circular_buffer.hpp>
+#include "common/dout.h"
+
+namespace rgw::dbstore {
+
+template <typename Connection>
+class ConnectionHandle;
+
+/// A thread-safe base class that manages a fixed-size pool of generic database
+/// connections and supports the reclamation of ConnectionHandles. This class
+/// is the subset of ConnectionPool which doesn't depend on the Factory type.
+template <typename Connection>
+class ConnectionPoolBase {
+ public:
+ ConnectionPoolBase(std::size_t max_connections)
+ : connections(max_connections)
+ {}
+ private:
+ friend class ConnectionHandle<Connection>;
+
+ // TODO: the caller may detect a connection error that prevents the connection
+ // from being reused. allow them to indicate these errors here
+ void put(std::unique_ptr<Connection> connection)
+ {
+ auto lock = std::scoped_lock{mutex};
+ connections.push_back(std::move(connection));
+
+ if (connections.size() == 1) { // was empty
+ cond.notify_one();
+ }
+ }
+ protected:
+ std::mutex mutex;
+ std::condition_variable cond;
+ boost::circular_buffer<std::unique_ptr<Connection>> connections;
+};
+
+/// Handle to a database connection borrowed from the pool. Automatically
+/// returns the connection to its pool on the handle's destruction.
+template <typename Connection>
+class ConnectionHandle {
+ ConnectionPoolBase<Connection>* pool = nullptr;
+ std::unique_ptr<Connection> conn;
+ public:
+ ConnectionHandle() noexcept = default;
+ ConnectionHandle(ConnectionPoolBase<Connection>* pool,
+ std::unique_ptr<Connection> conn) noexcept
+ : pool(pool), conn(std::move(conn)) {}
+
+ ~ConnectionHandle() {
+ if (conn) {
+ pool->put(std::move(conn));
+ }
+ }
+
+ ConnectionHandle(ConnectionHandle&&) = default;
+ ConnectionHandle& operator=(ConnectionHandle&& o) noexcept {
+ if (conn) {
+ pool->put(std::move(conn));
+ }
+ conn = std::move(o.conn);
+ pool = o.pool;
+ return *this;
+ }
+
+ explicit operator bool() const noexcept { return static_cast<bool>(conn); }
+ Connection& operator*() const noexcept { return *conn; }
+ Connection* operator->() const noexcept { return conn.get(); }
+ Connection* get() const noexcept { return conn.get(); }
+};
+
+
+// factory_of concept requires the function signature:
+// F(const DoutPrefixProvider*) -> std::unique_ptr<T>
+template <typename F, typename T>
+concept factory_of = requires (F factory, const DoutPrefixProvider* dpp) {
+ { factory(dpp) } -> std::same_as<std::unique_ptr<T>>;
+ requires std::move_constructible<F>;
+};
+
+
+/// Generic database connection pool that enforces a limit on open connections.
+template <typename Connection, factory_of<Connection> Factory>
+class ConnectionPool : public ConnectionPoolBase<Connection> {
+ public:
+ ConnectionPool(Factory factory, std::size_t max_connections)
+ : ConnectionPoolBase<Connection>(max_connections),
+ factory(std::move(factory))
+ {}
+
+ /// Borrow a connection from the pool. If all existing connections are in use,
+ /// use the connection factory to create another one. If we've reached the
+ /// limit on open connections, wait on a condition variable for the next one
+ /// returned to the pool.
+ auto get(const DoutPrefixProvider* dpp)
+ -> ConnectionHandle<Connection>
+ {
+ auto lock = std::unique_lock{this->mutex};
+ std::unique_ptr<Connection> conn;
+
+ if (!this->connections.empty()) {
+ // take an existing connection
+ conn = std::move(this->connections.front());
+ this->connections.pop_front();
+ } else if (total < this->connections.capacity()) {
+ // add another connection to the pool
+ conn = factory(dpp);
+ ++total;
+ } else {
+ // wait for the next put()
+ // TODO: support optional_yield
+ ldpp_dout(dpp, 4) << "ConnectionPool waiting on a connection" << dendl;
+ this->cond.wait(lock, [&] { return !this->connections.empty(); });
+ ldpp_dout(dpp, 4) << "ConnectionPool done waiting" << dendl;
+ conn = std::move(this->connections.front());
+ this->connections.pop_front();
+ }
+
+ return {this, std::move(conn)};
+ }
+ private:
+ Factory factory;
+ std::size_t total = 0;
+};
+
+} // namespace rgw::dbstore
diff --git a/src/rgw/driver/dbstore/common/dbstore.cc b/src/rgw/driver/dbstore/common/dbstore.cc
new file mode 100644
index 000000000..dc5a90c31
--- /dev/null
+++ b/src/rgw/driver/dbstore/common/dbstore.cc
@@ -0,0 +1,2252 @@
+// -*- 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;
+
+namespace rgw { namespace store {
+
+map<string, class ObjectOp*> DB::objectmap = {};
+
+map<string, class ObjectOp*> DB::getObjectMap() {
+ return DB::objectmap;
+}
+
+int DB::Initialize(string logfile, int loglevel)
+{
+ int ret = -1;
+ const DoutPrefixProvider *dpp = get_def_dpp();
+
+ if (!cct) {
+ cout << "Failed to Initialize. No ceph Context \n";
+ return -1;
+ }
+
+ if (loglevel > 0) {
+ cct->_conf->subsys.set_log_level(ceph_subsys_rgw, loglevel);
+ }
+ if (!logfile.empty()) {
+ cct->_log->set_log_file(logfile);
+ cct->_log->reopen_log_file();
+ }
+
+
+ db = openDB(dpp);
+
+ if (!db) {
+ ldpp_dout(dpp, 0) <<"Failed to open database " << dendl;
+ return ret;
+ }
+
+ ret = InitializeDBOps(dpp);
+
+ if (ret) {
+ ldpp_dout(dpp, 0) <<"InitializeDBOps failed " << dendl;
+ closeDB(dpp);
+ db = NULL;
+ return ret;
+ }
+
+ ldpp_dout(dpp, 0) << "DB successfully initialized - name:" \
+ << db_name << "" << dendl;
+
+ return ret;
+}
+
+int DB::createGC(const DoutPrefixProvider *dpp) {
+ int ret = 0;
+ /* create gc thread */
+
+ gc_worker = std::make_unique<DB::GC>(dpp, this);
+ gc_worker->create("db_gc");
+
+ return ret;
+}
+
+int DB::stopGC() {
+ if (gc_worker) {
+ gc_worker->signal_stop();
+ gc_worker->join();
+ }
+ return 0;
+}
+
+int DB::Destroy(const DoutPrefixProvider *dpp)
+{
+ if (!db)
+ return 0;
+
+ stopGC();
+
+ closeDB(dpp);
+
+
+ ldpp_dout(dpp, 20)<<"DB successfully destroyed - name:" \
+ <<db_name << dendl;
+
+ return 0;
+}
+
+
+std::shared_ptr<class DBOp> DB::getDBOp(const DoutPrefixProvider *dpp, std::string_view Op,
+ const 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;
+ if (!Op.compare("InsertLCEntry"))
+ return dbops.InsertLCEntry;
+ if (!Op.compare("RemoveLCEntry"))
+ return dbops.RemoveLCEntry;
+ if (!Op.compare("GetLCEntry"))
+ return dbops.GetLCEntry;
+ if (!Op.compare("ListLCEntries"))
+ return dbops.ListLCEntries;
+ if (!Op.compare("InsertLCHead"))
+ return dbops.InsertLCHead;
+ if (!Op.compare("RemoveLCHead"))
+ return dbops.RemoveLCHead;
+ if (!Op.compare("GetLCHead"))
+ return dbops.GetLCHead;
+
+ /* Object Operations */
+ map<string, class ObjectOp*>::iterator iter;
+ class ObjectOp* Ob;
+
+ {
+ const std::lock_guard<std::mutex> lk(mtx);
+ iter = DB::objectmap.find(params->op.bucket.info.bucket.name);
+ }
+
+ if (iter == DB::objectmap.end()) {
+ ldpp_dout(dpp, 30)<<"No objectmap found for bucket: " \
+ <<params->op.bucket.info.bucket.name << dendl;
+ /* not found */
+ return nullptr;
+ }
+
+ Ob = iter->second;
+
+ if (!Op.compare("PutObject"))
+ return Ob->PutObject;
+ if (!Op.compare("DeleteObject"))
+ return Ob->DeleteObject;
+ if (!Op.compare("GetObject"))
+ return Ob->GetObject;
+ if (!Op.compare("UpdateObject"))
+ return Ob->UpdateObject;
+ if (!Op.compare("ListBucketObjects"))
+ return Ob->ListBucketObjects;
+ if (!Op.compare("ListVersionedObjects"))
+ return Ob->ListVersionedObjects;
+ if (!Op.compare("PutObjectData"))
+ return Ob->PutObjectData;
+ if (!Op.compare("UpdateObjectData"))
+ return Ob->UpdateObjectData;
+ if (!Op.compare("GetObjectData"))
+ return Ob->GetObjectData;
+ if (!Op.compare("DeleteObjectData"))
+ return Ob->DeleteObjectData;
+ if (!Op.compare("DeleteStaleObjectData"))
+ return Ob->DeleteStaleObjectData;
+
+ return nullptr;
+}
+
+int DB::objectmapInsert(const DoutPrefixProvider *dpp, string bucket, class ObjectOp* ptr)
+{
+ map<string, class ObjectOp*>::iterator iter;
+ class ObjectOp *Ob;
+
+ const std::lock_guard<std::mutex> lk(mtx);
+ iter = DB::objectmap.find(bucket);
+
+ if (iter != DB::objectmap.end()) {
+ // entry already exists
+ // return success or replace it or
+ // return error ?
+ //
+ // return success for now & delete the newly allocated ptr
+ ldpp_dout(dpp, 30)<<"Objectmap entry already exists for bucket("\
+ <<bucket<<"). Not inserted " << dendl;
+ delete ptr;
+ return 0;
+ }
+
+ Ob = (class ObjectOp*) ptr;
+ Ob->InitializeObjectOps(getDBname(), dpp);
+
+ DB::objectmap.insert(pair<string, class ObjectOp*>(bucket, Ob));
+
+ return 0;
+}
+
+int DB::objectmapDelete(const DoutPrefixProvider *dpp, string bucket)
+{
+ map<string, class ObjectOp*>::iterator iter;
+
+ const std::lock_guard<std::mutex> lk(mtx);
+ iter = DB::objectmap.find(bucket);
+
+ if (iter == DB::objectmap.end()) {
+ // entry doesn't exist
+ // return success or return error ?
+ // return success for now
+ ldpp_dout(dpp, 20)<<"Objectmap entry for bucket("<<bucket<<") "
+ <<"doesnt exist to delete " << dendl;
+ return 0;
+ }
+
+ DB::objectmap.erase(iter);
+
+ return 0;
+}
+
+int DB::InitializeParams(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+
+ if (!params)
+ goto out;
+
+ params->cct = cct;
+
+ //reset params here
+ params->user_table = user_table;
+ params->bucket_table = bucket_table;
+ params->quota_table = quota_table;
+ params->lc_entry_table = lc_entry_table;
+ params->lc_head_table = lc_head_table;
+
+ ret = 0;
+out:
+ return ret;
+}
+
+int DB::ProcessOp(const DoutPrefixProvider *dpp, std::string_view Op, DBOpParams *params) {
+ int ret = -1;
+ shared_ptr<class DBOp> db_op;
+
+ db_op = getDBOp(dpp, Op, params);
+
+ if (!db_op) {
+ ldpp_dout(dpp, 0)<<"No db_op found for Op("<<Op<<")" << dendl;
+ return ret;
+ }
+ ret = db_op->Execute(dpp, params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In Process op Execute failed for fop(" << Op << ")" << dendl;
+ } else {
+ ldpp_dout(dpp, 20)<<"Successfully processed fop(" << Op << ")" << dendl;
+ }
+
+ return ret;
+}
+
+int DB::get_user(const DoutPrefixProvider *dpp,
+ 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() || query_str_val.empty()) {
+ ldpp_dout(dpp, 0)<<"In GetUser - Invalid query(" << query_str <<"), query_str_val(" << query_str_val <<")" << dendl;
+ return -1;
+ }
+
+ DBOpParams params = {};
+ InitializeParams(dpp, &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 {
+ ldpp_dout(dpp, 0)<<"In GetUser Invalid query string :" <<query_str.c_str()<<") " << dendl;
+ return -1;
+ }
+
+ ret = ProcessOp(dpp, "GetUser", &params);
+
+ if (ret)
+ goto out;
+
+ /* Verify if its a valid user */
+ if (params.op.user.uinfo.access_keys.empty() ||
+ params.op.user.uinfo.user_id.id.empty()) {
+ ldpp_dout(dpp, 0)<<"In GetUser - No user with query(" <<query_str.c_str()<<"), user_id(" << uinfo.user_id <<") found" << dendl;
+ return -ENOENT;
+ }
+
+ 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 DB::store_user(const DoutPrefixProvider *dpp,
+ RGWUserInfo& uinfo, bool exclusive, map<string, bufferlist> *pattrs,
+ RGWObjVersionTracker *pobjv, RGWUserInfo* pold_info)
+{
+ DBOpParams params = {};
+ InitializeParams(dpp, &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(dpp, string("user_id"), uinfo.user_id.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;
+ ldpp_dout(dpp, 0)<<"User Read version mismatch err:(" <<ret<<") " << dendl;
+ 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(dpp, "InsertUser", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"store_user failed with err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+ ldpp_dout(dpp, 20)<<"User creation successful - userid:(" <<uinfo.user_id<<") " << dendl;
+
+ if (pobjv) {
+ pobjv->read_version = obj_ver;
+ pobjv->write_version = obj_ver;
+ }
+
+out:
+ return ret;
+}
+
+int DB::remove_user(const DoutPrefixProvider *dpp,
+ RGWUserInfo& uinfo, RGWObjVersionTracker *pobjv)
+{
+ DBOpParams params = {};
+ InitializeParams(dpp, &params);
+ int ret = 0;
+
+ RGWUserInfo orig_info;
+ RGWObjVersionTracker objv_tracker = {};
+
+ orig_info.user_id = uinfo.user_id;
+ ret = get_user(dpp, string("user_id"), uinfo.user_id.id, orig_info, nullptr, &objv_tracker);
+
+ if (ret) {
+ return ret;
+ }
+
+ 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;
+ ldpp_dout(dpp, 0)<<"User Read version mismatch err:(" <<ret<<") " << dendl;
+ return ret;
+ }
+ }
+
+ params.op.user.uinfo.user_id = uinfo.user_id;
+
+ ret = ProcessOp(dpp, "RemoveUser", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"remove_user failed with err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int DB::get_bucket_info(const DoutPrefixProvider *dpp, 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(dpp, &params);
+
+ if (query_str == "name") {
+ params.op.bucket.info.bucket.name = info.bucket.name;
+ } else {
+ ldpp_dout(dpp, 0)<<"In GetBucket Invalid query string :" <<query_str.c_str()<<") " << dendl;
+ return -1;
+ }
+
+ ret = ProcessOp(dpp, "GetBucket", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In GetBucket failed err:(" <<ret<<") " << dendl;
+ 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 DB::create_bucket(const DoutPrefixProvider *dpp,
+ 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,
+ 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(dpp, &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(dpp, 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(dpp, "InsertBucket", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"create_bucket failed with err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int DB::remove_bucket(const DoutPrefixProvider *dpp, const RGWBucketInfo info) {
+ int ret = 0;
+
+ DBOpParams params = {};
+ InitializeParams(dpp, &params);
+
+ params.op.bucket.info.bucket.name = info.bucket.name;
+
+ ret = ProcessOp(dpp, "RemoveBucket", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In RemoveBucket failed err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int DB::list_buckets(const DoutPrefixProvider *dpp, const std::string& query_str,
+ 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(dpp, &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;
+ params.op.query_str = query_str;
+
+ ret = ProcessOp(dpp, "ListUserBuckets", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In ListUserBuckets failed err:(" <<ret<<") " << dendl;
+ 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));
+ }
+
+ if (query_str == "all") {
+ // userID/OwnerID may have changed. Update it.
+ user.id = params.op.bucket.info.owner.id;
+ }
+
+out:
+ return ret;
+}
+
+int DB::update_bucket(const DoutPrefixProvider *dpp, 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(dpp, string("name"), "", orig_info, nullptr, nullptr,
+ &bucket_version);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"Failed to read bucket info err:(" <<ret<<") " << dendl;
+ 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) {
+ ldpp_dout(dpp, 0)<<"Read version mismatch err:(" <<ret<<") " << dendl;
+ ret = -ECANCELED;
+ goto out;
+ }
+ } else {
+ pobjv = &info.objv_tracker;
+ }
+
+ InitializeParams(dpp, &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;
+ ldpp_dout(dpp, 0)<<"In UpdateBucket Invalid query_str : " << query_str << dendl;
+ goto out;
+ }
+
+ ret = ProcessOp(dpp, "UpdateBucket", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In UpdateBucket failed err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+
+ if (pobjv) {
+ pobjv->read_version = params.op.bucket.bucket_version;
+ pobjv->write_version = params.op.bucket.bucket_version;
+ }
+
+out:
+ return ret;
+}
+
+/**
+ * Get ordered listing of the objects in a bucket.
+ *
+ * max_p: maximum number of results to return
+ * bucket: bucket to list contents of
+ * prefix: only return results that match this prefix
+ * delim: do not include results that match this string.
+ * Any skipped results will have the matching portion of their name
+ * inserted in common_prefixes with a "true" mark.
+ * marker: if filled in, begin the listing with this object.
+ * end_marker: if filled in, end the listing with this object.
+ * result: the objects are put in here.
+ * common_prefixes: if delim is filled in, any matching prefixes are
+ * placed here.
+ * is_truncated: if number of objects in the bucket is bigger than
+ * max, then truncated.
+ */
+int DB::Bucket::List::list_objects(const DoutPrefixProvider *dpp, int64_t max,
+ vector<rgw_bucket_dir_entry> *result,
+ map<string, bool> *common_prefixes, bool *is_truncated)
+{
+ int ret = 0;
+ DB *store = target->get_store();
+ int64_t count = 0;
+ std::string prev_obj;
+
+ DBOpParams db_params = {};
+ store->InitializeParams(dpp, &db_params);
+
+ db_params.op.bucket.info = target->get_bucket_info();
+ /* XXX: Handle whole marker? key -> name, instance, ns? */
+ db_params.op.obj.min_marker = params.marker.name;
+ db_params.op.obj.max_marker = params.end_marker.name;
+ db_params.op.obj.prefix = params.prefix + "%";
+ db_params.op.list_max_count = max + 1; /* +1 for next_marker */
+
+ ret = store->ProcessOp(dpp, "ListBucketObjects", &db_params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In ListBucketObjects failed err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+
+ for (auto& entry : db_params.op.obj.list_entries) {
+
+ if (!params.list_versions) {
+ if (entry.flags & rgw_bucket_dir_entry::FLAG_DELETE_MARKER) {
+ prev_obj = entry.key.name;
+ // skip all non-current entries and delete_marker
+ continue;
+ }
+ if (entry.key.name == prev_obj) {
+ // non current versions..skip the entry
+ continue;
+ }
+ entry.flags |= rgw_bucket_dir_entry::FLAG_CURRENT;
+ } else {
+ if (entry.key.name != prev_obj) {
+ // current version
+ entry.flags |= rgw_bucket_dir_entry::FLAG_CURRENT;
+ } else {
+ entry.flags &= ~(rgw_bucket_dir_entry::FLAG_CURRENT);
+ entry.flags |= rgw_bucket_dir_entry::FLAG_VER;
+ }
+ }
+
+ prev_obj = entry.key.name;
+
+ if (count >= max) {
+ *is_truncated = true;
+ next_marker.name = entry.key.name;
+ next_marker.instance = entry.key.instance;
+ break;
+ }
+
+ if (!params.delim.empty()) {
+ const std::string& objname = entry.key.name;
+ const int delim_pos = objname.find(params.delim, params.prefix.size());
+ if (delim_pos >= 0) {
+ /* extract key -with trailing delimiter- for CommonPrefix */
+ const std::string& prefix_key =
+ objname.substr(0, delim_pos + params.delim.length());
+
+ if (common_prefixes &&
+ common_prefixes->find(prefix_key) == common_prefixes->end()) {
+ next_marker = prefix_key;
+ (*common_prefixes)[prefix_key] = true;
+ count++;
+ }
+ continue;
+ }
+ }
+
+ if (!params.end_marker.name.empty() &&
+ params.end_marker.name.compare(entry.key.name) <= 0) {
+ // should not include end_marker
+ *is_truncated = false;
+ break;
+ }
+ count++;
+ result->push_back(std::move(entry));
+ }
+out:
+ return ret;
+}
+
+int DB::raw_obj::InitializeParamsfromRawObj(const DoutPrefixProvider *dpp,
+ DBOpParams* params) {
+ int ret = 0;
+
+ if (!params)
+ return -1;
+
+ params->op.bucket.info.bucket.name = bucket_name;
+ params->op.obj.state.obj.key.name = obj_name;
+ params->op.obj.state.obj.key.instance = obj_instance;
+ params->op.obj.state.obj.key.ns = obj_ns;
+ params->op.obj.obj_id = obj_id;
+
+ if (multipart_part_str != "0.0") {
+ params->op.obj.is_multipart = true;
+ } else {
+ params->op.obj.is_multipart = false;
+ }
+
+ params->op.obj_data.multipart_part_str = multipart_part_str;
+ params->op.obj_data.part_num = part_num;
+
+ return ret;
+}
+
+int DB::Object::InitializeParamsfromObject(const DoutPrefixProvider *dpp,
+ DBOpParams* params) {
+ int ret = 0;
+ string bucket = bucket_info.bucket.name;
+
+ if (!params)
+ return -1;
+
+ params->op.bucket.info.bucket.name = bucket;
+ params->op.obj.state.obj = obj;
+ params->op.obj.obj_id = obj_id;
+
+ return ret;
+}
+
+int DB::Object::get_object_impl(const DoutPrefixProvider *dpp, DBOpParams& params) {
+ int ret = 0;
+
+ if (params.op.obj.state.obj.key.name.empty()) {
+ /* Initialize */
+ store->InitializeParams(dpp, &params);
+ InitializeParamsfromObject(dpp, &params);
+ }
+
+ ret = store->ProcessOp(dpp, "GetObject", &params);
+
+ /* pick one field check if object exists */
+ if (!ret && !params.op.obj.state.exists) {
+ ldpp_dout(dpp, 0)<<"Object(bucket:" << bucket_info.bucket.name << ", Object:"<< obj.key.name << ") doesn't exist" << dendl;
+ ret = -ENOENT;
+ }
+
+ return ret;
+}
+
+int DB::Object::obj_omap_set_val_by_key(const DoutPrefixProvider *dpp,
+ const std::string& key, bufferlist& val,
+ bool must_exist) {
+ int ret = 0;
+
+ DBOpParams params = {};
+
+ ret = get_object_impl(dpp, params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0) <<"get_object_impl failed err:(" <<ret<<")" << dendl;
+ goto out;
+ }
+
+ params.op.obj.omap[key] = val;
+ params.op.query_str = "omap";
+ params.op.obj.state.mtime = real_clock::now();
+
+ ret = store->ProcessOp(dpp, "UpdateObject", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In UpdateObject failed err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int DB::Object::obj_omap_get_vals_by_keys(const DoutPrefixProvider *dpp,
+ const std::string& oid,
+ const std::set<std::string>& keys,
+ std::map<std::string, bufferlist>* vals)
+{
+ int ret = 0;
+ DBOpParams params = {};
+ std::map<std::string, bufferlist> omap;
+
+ if (!vals)
+ return -1;
+
+ ret = get_object_impl(dpp, params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0) <<"get_object_impl failed err:(" <<ret<<")" << dendl;
+ goto out;
+ }
+
+ omap = params.op.obj.omap;
+
+ for (const auto& k : keys) {
+ (*vals)[k] = omap[k];
+ }
+
+out:
+ return ret;
+}
+
+int DB::Object::add_mp_part(const DoutPrefixProvider *dpp,
+ RGWUploadPartInfo info) {
+ int ret = 0;
+
+ DBOpParams params = {};
+
+ ret = get_object_impl(dpp, params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0) <<"get_object_impl failed err:(" <<ret<<")" << dendl;
+ goto out;
+ }
+
+ params.op.obj.mp_parts.push_back(info);
+ params.op.query_str = "mp";
+ params.op.obj.state.mtime = real_clock::now();
+
+ ret = store->ProcessOp(dpp, "UpdateObject", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In UpdateObject failed err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int DB::Object::get_mp_parts_list(const DoutPrefixProvider *dpp,
+ std::list<RGWUploadPartInfo>& info)
+{
+ int ret = 0;
+ DBOpParams params = {};
+ std::map<std::string, bufferlist> omap;
+
+ ret = get_object_impl(dpp, params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0) <<"get_object_impl failed err:(" <<ret<<")" << dendl;
+ goto out;
+ }
+
+ info = params.op.obj.mp_parts;
+
+out:
+ return ret;
+}
+
+/* Taken from rgw_rados.cc */
+void DB::gen_rand_obj_instance_name(rgw_obj_key *target_key)
+{
+#define OBJ_INSTANCE_LEN 32
+ char buf[OBJ_INSTANCE_LEN + 1];
+
+ gen_rand_alphanumeric_no_underscore(cct, buf, OBJ_INSTANCE_LEN); /* don't want it to get url escaped,
+ no underscore for instance name due to the way we encode the raw keys */
+
+ target_key->set_instance(buf);
+}
+
+int DB::Object::obj_omap_get_all(const DoutPrefixProvider *dpp,
+ std::map<std::string, bufferlist> *m)
+{
+ int ret = 0;
+ DBOpParams params = {};
+ std::map<std::string, bufferlist> omap;
+
+ if (!m)
+ return -1;
+
+ ret = get_object_impl(dpp, params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0) <<"get_object_impl failed err:(" <<ret<<")" << dendl;
+ goto out;
+ }
+
+ (*m) = params.op.obj.omap;
+
+out:
+ return ret;
+}
+
+int DB::Object::obj_omap_get_vals(const DoutPrefixProvider *dpp,
+ const std::string& marker,
+ uint64_t max_count,
+ std::map<std::string, bufferlist> *m, bool* pmore)
+{
+ int ret = 0;
+ DBOpParams params = {};
+ std::map<std::string, bufferlist> omap;
+ map<string, bufferlist>::iterator iter;
+ uint64_t count = 0;
+
+ if (!m)
+ return -1;
+
+ ret = get_object_impl(dpp, params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0) <<"get_object_impl failed err:(" <<ret<<")" << dendl;
+ goto out;
+ }
+
+ omap = params.op.obj.omap;
+
+ for (iter = omap.begin(); iter != omap.end(); ++iter) {
+
+ if (iter->first < marker)
+ continue;
+
+ if ((++count) > max_count) {
+ *pmore = true;
+ break;
+ }
+
+ (*m)[iter->first] = iter->second;
+ }
+
+out:
+ return ret;
+}
+
+int DB::Object::set_attrs(const DoutPrefixProvider *dpp,
+ map<string, bufferlist>& setattrs,
+ map<string, bufferlist>* rmattrs)
+{
+ int ret = 0;
+
+ DBOpParams params = {};
+ rgw::sal::Attrs *attrs;
+ map<string, bufferlist>::iterator iter;
+ RGWObjState* state;
+
+ store->InitializeParams(dpp, &params);
+ InitializeParamsfromObject(dpp, &params);
+ ret = get_state(dpp, &state, true);
+
+ if (ret && !state->exists) {
+ ldpp_dout(dpp, 0) <<"get_state failed err:(" <<ret<<")" << dendl;
+ goto out;
+ }
+
+ /* For now lets keep it simple..rmattrs & setattrs ..
+ * XXX: Check rgw_rados::set_attrs
+ */
+ params.op.obj.state = *state;
+ attrs = &params.op.obj.state.attrset;
+ if (rmattrs) {
+ for (iter = rmattrs->begin(); iter != rmattrs->end(); ++iter) {
+ (*attrs).erase(iter->first);
+ }
+ }
+ for (iter = setattrs.begin(); iter != setattrs.end(); ++iter) {
+ (*attrs)[iter->first] = iter->second;
+ }
+
+ params.op.query_str = "attrs";
+ /* As per https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingMetadata.html,
+ * the only way for users to modify object metadata is to make a copy of the object and
+ * set the metadata.
+ * Hence do not update mtime for any other attr changes */
+
+ ret = store->ProcessOp(dpp, "UpdateObject", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In UpdateObject failed err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int DB::Object::transition(const DoutPrefixProvider *dpp,
+ const rgw_placement_rule& rule,
+ const real_time& mtime,
+ uint64_t olh_epoch)
+{
+ int ret = 0;
+
+ DBOpParams params = {};
+ map<string, bufferlist> *attrset;
+
+ store->InitializeParams(dpp, &params);
+ InitializeParamsfromObject(dpp, &params);
+
+ ret = store->ProcessOp(dpp, "GetObject", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0) <<"In GetObject failed err:(" <<ret<<")" << dendl;
+ goto out;
+ }
+
+ /* pick one field check if object exists */
+ if (!params.op.obj.state.exists) {
+ ldpp_dout(dpp, 0)<<"Object(bucket:" << bucket_info.bucket.name << ", Object:"<< obj.key.name << ") doesn't exist" << dendl;
+ return -1;
+ }
+
+ params.op.query_str = "meta";
+ params.op.obj.state.mtime = real_clock::now();
+ params.op.obj.storage_class = rule.storage_class;
+ attrset = &params.op.obj.state.attrset;
+ if (!rule.storage_class.empty()) {
+ bufferlist bl;
+ bl.append(rule.storage_class);
+ (*attrset)[RGW_ATTR_STORAGE_CLASS] = bl;
+ }
+ params.op.obj.versioned_epoch = olh_epoch; // XXX: not sure if needed
+
+ /* Unlike Rados, in dbstore for now, both head and tail objects
+ * refer to same storage class
+ */
+ params.op.obj.head_placement_rule = rule;
+ params.op.obj.tail_placement.placement_rule = rule;
+
+ ret = store->ProcessOp(dpp, "UpdateObject", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In UpdateObject failed err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int DB::raw_obj::read(const DoutPrefixProvider *dpp, int64_t ofs,
+ uint64_t len, bufferlist& bl)
+{
+ int ret = 0;
+ DBOpParams params = {};
+
+ db->InitializeParams(dpp, &params);
+ InitializeParamsfromRawObj(dpp, &params);
+
+ ret = db->ProcessOp(dpp, "GetObjectData", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In GetObjectData failed err:(" <<ret<<")" << dendl;
+ return ret;
+ }
+
+ /* Verify if its valid obj */
+ if (!params.op.obj_data.size) {
+ ret = -ENOENT;
+ ldpp_dout(dpp, 0)<<"In GetObjectData failed err:(" <<ret<<")" << dendl;
+ return ret;
+ }
+
+ bufferlist& read_bl = params.op.obj_data.data;
+
+ unsigned copy_len;
+ copy_len = std::min((uint64_t)read_bl.length() - ofs, len);
+ read_bl.begin(ofs).copy(copy_len, bl);
+ return bl.length();
+}
+
+int DB::raw_obj::write(const DoutPrefixProvider *dpp, int64_t ofs, int64_t write_ofs,
+ uint64_t len, bufferlist& bl)
+{
+ int ret = 0;
+ DBOpParams params = {};
+
+ db->InitializeParams(dpp, &params);
+ InitializeParamsfromRawObj(dpp, &params);
+
+ /* XXX: Check for chunk_size ?? */
+ params.op.obj_data.offset = ofs;
+ unsigned write_len = std::min((uint64_t)bl.length() - write_ofs, len);
+ bl.begin(write_ofs).copy(write_len, params.op.obj_data.data);
+ params.op.obj_data.size = params.op.obj_data.data.length();
+ params.op.obj.state.mtime = real_clock::now();
+
+ ret = db->ProcessOp(dpp, "PutObjectData", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In PutObjectData failed err:(" <<ret<<")" << dendl;
+ return ret;
+ }
+
+ return write_len;
+}
+
+int DB::Object::list_versioned_objects(const DoutPrefixProvider *dpp,
+ std::list<rgw_bucket_dir_entry>& list_entries) {
+ int ret = 0;
+ store = get_store();
+ DBOpParams db_params = {};
+
+ store->InitializeParams(dpp, &db_params);
+ InitializeParamsfromObject(dpp, &db_params);
+
+ db_params.op.list_max_count = MAX_VERSIONED_OBJECTS;
+
+ ret = store->ProcessOp(dpp, "ListVersionedObjects", &db_params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In ListVersionedObjects failed err:(" <<ret<<") " << dendl;
+ } else {
+ list_entries = db_params.op.obj.list_entries;
+ }
+
+ return ret;
+}
+
+int DB::Object::get_obj_state(const DoutPrefixProvider *dpp,
+ const RGWBucketInfo& bucket_info, const rgw_obj& obj,
+ bool follow_olh, RGWObjState** state)
+{
+ int ret = 0;
+
+ DBOpParams params = {};
+ RGWObjState* s;
+
+ if (!obj.key.instance.empty()) {
+ /* Versionid provided. Fetch the object */
+ ret = get_object_impl(dpp, params);
+
+ if (ret && ret != -ENOENT) {
+ ldpp_dout(dpp, 0) <<"get_object_impl failed err:(" <<ret<<")" << dendl;
+ goto out;
+ }
+ } else {
+ /* Instance is empty. May or may not be versioned object.
+ * List all the versions and read the most recent entry */
+ ret = list_versioned_objects(dpp, params.op.obj.list_entries);
+
+ if (params.op.obj.list_entries.size() != 0) {
+ /* Ensure its not a delete marker */
+ auto& ent = params.op.obj.list_entries.front();
+ if (ent.flags & rgw_bucket_dir_entry::FLAG_DELETE_MARKER) {
+ ret = -ENOENT;
+ goto out;
+ }
+ store->InitializeParams(dpp, &params);
+ InitializeParamsfromObject(dpp, &params);
+ params.op.obj.state.obj.key = ent.key;
+
+ ret = get_object_impl(dpp, params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0) <<"get_object_impl of versioned object failed err:(" <<ret<<")" << dendl;
+ goto out;
+ }
+ } else {
+ ret = -ENOENT;
+ return ret;
+ }
+ }
+
+ s = &params.op.obj.state;
+ /* XXX: For now use state->shadow_obj to store ObjectID string */
+ s->shadow_obj = params.op.obj.obj_id;
+
+ *state = &obj_state;
+ **state = *s;
+
+out:
+ return ret;
+
+}
+
+int DB::Object::get_state(const DoutPrefixProvider *dpp, RGWObjState** pstate, bool follow_olh)
+{
+ return get_obj_state(dpp, bucket_info, obj, follow_olh, pstate);
+}
+
+int DB::Object::Read::get_attr(const DoutPrefixProvider *dpp, const char *name, bufferlist& dest)
+{
+ RGWObjState* state;
+ int r = source->get_state(dpp, &state, true);
+ if (r < 0)
+ return r;
+ if (!state->exists)
+ return -ENOENT;
+ if (!state->get_attr(name, dest))
+ return -ENODATA;
+
+ return 0;
+}
+
+int DB::Object::Read::prepare(const DoutPrefixProvider *dpp)
+{
+ DB *store = source->get_store();
+ CephContext *cct = store->ctx();
+
+ bufferlist etag;
+
+ map<string, bufferlist>::iterator iter;
+
+ RGWObjState* astate;
+
+ int r = source->get_state(dpp, &astate, true);
+ if (r < 0)
+ return r;
+
+ if (!astate->exists) {
+ return -ENOENT;
+ }
+
+ state.obj = astate->obj;
+ source->obj_id = astate->shadow_obj;
+
+ if (params.target_obj) {
+ *params.target_obj = state.obj;
+ }
+ if (params.attrs) {
+ *params.attrs = astate->attrset;
+ if (cct->_conf->subsys.should_gather<ceph_subsys_rgw, 20>()) {
+ for (iter = params.attrs->begin(); iter != params.attrs->end(); ++iter) {
+ ldpp_dout(dpp, 20) << "Read xattr rgw_rados: " << iter->first << dendl;
+ }
+ }
+ }
+
+ if (conds.if_match || conds.if_nomatch) {
+ r = get_attr(dpp, RGW_ATTR_ETAG, etag);
+ if (r < 0)
+ return r;
+
+ if (conds.if_match) {
+ string if_match_str = rgw_string_unquote(conds.if_match);
+ ldpp_dout(dpp, 10) << "ETag: " << string(etag.c_str(), etag.length()) << " " << " If-Match: " << if_match_str << dendl;
+ if (if_match_str.compare(0, etag.length(), etag.c_str(), etag.length()) != 0) {
+ return -ERR_PRECONDITION_FAILED;
+ }
+ }
+
+ if (conds.if_nomatch) {
+ string if_nomatch_str = rgw_string_unquote(conds.if_nomatch);
+ ldpp_dout(dpp, 10) << "ETag: " << string(etag.c_str(), etag.length()) << " " << " If-NoMatch: " << if_nomatch_str << dendl;
+ if (if_nomatch_str.compare(0, etag.length(), etag.c_str(), etag.length()) == 0) {
+ return -ERR_NOT_MODIFIED;
+ }
+ }
+ }
+
+ if (params.obj_size)
+ *params.obj_size = astate->size;
+ if (params.lastmod)
+ *params.lastmod = astate->mtime;
+
+ return 0;
+}
+
+int DB::Object::Read::range_to_ofs(uint64_t obj_size, int64_t &ofs, int64_t &end)
+{
+ if (ofs < 0) {
+ ofs += obj_size;
+ if (ofs < 0)
+ ofs = 0;
+ end = obj_size - 1;
+ } else if (end < 0) {
+ end = obj_size - 1;
+ }
+
+ if (obj_size > 0) {
+ if (ofs >= (off_t)obj_size) {
+ return -ERANGE;
+ }
+ if (end >= (off_t)obj_size) {
+ end = obj_size - 1;
+ }
+ }
+ return 0;
+}
+
+int DB::Object::Read::read(int64_t ofs, int64_t end, bufferlist& bl, const DoutPrefixProvider *dpp)
+{
+ DB *store = source->get_store();
+
+ uint64_t read_ofs = ofs;
+ uint64_t len, read_len;
+
+ bufferlist read_bl;
+ uint64_t max_chunk_size = store->get_max_chunk_size();
+
+ RGWObjState* astate;
+ int r = source->get_state(dpp, &astate, true);
+ if (r < 0)
+ return r;
+
+ if (!astate->exists) {
+ return -ENOENT;
+ }
+
+ if (astate->size == 0) {
+ end = 0;
+ } else if (end >= (int64_t)astate->size) {
+ end = astate->size - 1;
+ }
+
+ if (end < 0)
+ len = 0;
+ else
+ len = end - ofs + 1;
+
+
+ if (len > max_chunk_size) {
+ len = max_chunk_size;
+ }
+
+ int head_data_size = astate->data.length();
+ bool reading_from_head = (ofs < head_data_size);
+
+ if (reading_from_head) {
+ if (astate) { // && astate->prefetch_data)?
+ if (!ofs && astate->data.length() >= len) {
+ bl = astate->data;
+ return bl.length();
+ }
+
+ if (ofs < astate->data.length()) {
+ unsigned copy_len = std::min((uint64_t)head_data_size - ofs, len);
+ astate->data.begin(ofs).copy(copy_len, bl);
+ return bl.length();
+ }
+ }
+ }
+
+ /* tail object */
+ int part_num = (ofs / max_chunk_size);
+ /* XXX: Handle multipart_str */
+ raw_obj read_obj(store, source->get_bucket_info().bucket.name, astate->obj.key.name,
+ astate->obj.key.instance, astate->obj.key.ns, source->obj_id, "0.0", part_num);
+
+ read_len = len;
+
+ ldpp_dout(dpp, 20) << "dbstore->read obj-ofs=" << ofs << " read_ofs=" << read_ofs << " read_len=" << read_len << dendl;
+
+ // read from non head object
+ r = read_obj.read(dpp, read_ofs, read_len, bl);
+
+ if (r < 0) {
+ return r;
+ }
+
+ return bl.length();
+}
+
+static int _get_obj_iterate_cb(const DoutPrefixProvider *dpp,
+ const DB::raw_obj& read_obj, off_t obj_ofs,
+ off_t len, bool is_head_obj,
+ RGWObjState* astate, void *arg)
+{
+ struct db_get_obj_data* d = static_cast<struct db_get_obj_data*>(arg);
+ return d->store->get_obj_iterate_cb(dpp, read_obj, obj_ofs, len,
+ is_head_obj, astate, arg);
+}
+
+int DB::get_obj_iterate_cb(const DoutPrefixProvider *dpp,
+ const raw_obj& read_obj, off_t obj_ofs,
+ off_t len, bool is_head_obj,
+ RGWObjState* astate, void *arg)
+{
+ struct db_get_obj_data* d = static_cast<struct db_get_obj_data*>(arg);
+ bufferlist bl;
+ int r = 0;
+
+ if (is_head_obj) {
+ bl = astate->data;
+ } else {
+ // read from non head object
+ raw_obj robj = read_obj;
+ /* read entire data. So pass offset as '0' & len as '-1' */
+ r = robj.read(dpp, 0, -1, bl);
+
+ if (r <= 0) {
+ return r;
+ }
+ }
+
+ unsigned read_ofs = 0, read_len = 0;
+ while (read_ofs < bl.length()) {
+ unsigned chunk_len = std::min((uint64_t)bl.length() - read_ofs, (uint64_t)len);
+ r = d->client_cb->handle_data(bl, read_ofs, chunk_len);
+ if (r < 0)
+ return r;
+ read_ofs += chunk_len;
+ read_len += chunk_len;
+ ldpp_dout(dpp, 20) << "dbstore->get_obj_iterate_cb obj-ofs=" << obj_ofs << " len=" << len << " chunk_len = " << chunk_len << " read_len = " << read_len << dendl;
+ }
+
+
+ d->offset += read_len;
+
+ return read_len;
+}
+
+int DB::Object::Read::iterate(const DoutPrefixProvider *dpp, int64_t ofs, int64_t end, RGWGetDataCB *cb)
+{
+ DB *store = source->get_store();
+ const uint64_t chunk_size = store->get_max_chunk_size();
+
+ db_get_obj_data data(store, cb, ofs);
+
+ int r = source->iterate_obj(dpp, source->get_bucket_info(), state.obj,
+ ofs, end, chunk_size, _get_obj_iterate_cb, &data);
+ if (r < 0) {
+ ldpp_dout(dpp, 0) << "iterate_obj() failed with " << r << dendl;
+ return r;
+ }
+
+ return 0;
+}
+
+int DB::Object::iterate_obj(const DoutPrefixProvider *dpp,
+ const RGWBucketInfo& bucket_info, const rgw_obj& obj,
+ off_t ofs, off_t end, uint64_t max_chunk_size,
+ iterate_obj_cb cb, void *arg)
+{
+ DB *store = get_store();
+ uint64_t len;
+ RGWObjState* astate;
+
+ int r = get_state(dpp, &astate, true);
+ if (r < 0) {
+ return r;
+ }
+
+ if (!astate->exists) {
+ return -ENOENT;
+ }
+
+ if (end < 0)
+ len = 0;
+ else
+ len = end - ofs + 1;
+
+ /* XXX: Will it really help to store all parts info in astate like manifest in Rados? */
+ int part_num = 0;
+ int head_data_size = astate->data.length();
+
+ while (ofs <= end && (uint64_t)ofs < astate->size) {
+ part_num = (ofs / max_chunk_size);
+ uint64_t read_len = std::min(len, max_chunk_size);
+
+ /* XXX: Handle multipart_str */
+ raw_obj read_obj(store, get_bucket_info().bucket.name, astate->obj.key.name,
+ astate->obj.key.instance, astate->obj.key.ns, obj_id, "0.0", part_num);
+ bool reading_from_head = (ofs < head_data_size);
+
+ r = cb(dpp, read_obj, ofs, read_len, reading_from_head, astate, arg);
+ if (r <= 0) {
+ return r;
+ }
+ /* r refers to chunk_len (no. of bytes) handled in cb */
+ len -= r;
+ ofs += r;
+ }
+
+ return 0;
+}
+
+int DB::Object::Write::prepare(const DoutPrefixProvider* dpp)
+{
+ DB *store = target->get_store();
+
+ int ret = -1;
+
+ /* XXX: handle assume_noent */
+
+ obj_state.obj = target->obj;
+
+ if (target->obj_id.empty()) {
+ if (!target->obj.key.instance.empty() && (target->obj.key.instance != "null")) {
+ /* versioned object. Set obj_id same as versionID/instance */
+ target->obj_id = target->obj.key.instance;
+ } else {
+ // generate obj_id
+ char buf[33];
+ gen_rand_alphanumeric(store->ctx(), buf, sizeof(buf) - 1);
+ target->obj_id = buf;
+ }
+ }
+
+ ret = 0;
+ return ret;
+}
+
+/* writes tail objects */
+int DB::Object::Write::write_data(const DoutPrefixProvider* dpp,
+ bufferlist& data, uint64_t ofs) {
+ DB *store = target->get_store();
+ /* tail objects */
+ /* XXX: Split into parts each of max_chunk_size. But later make tail
+ * object chunk size limit to sqlite blob limit */
+ int part_num = 0;
+
+ uint64_t max_chunk_size = store->get_max_chunk_size();
+
+ /* tail_obj ofs should be greater than max_head_size */
+ if (mp_part_str == "0.0") { // ensure not multipart meta object
+ if (ofs < store->get_max_head_size()) {
+ return -1;
+ }
+ }
+
+ uint64_t end = data.length();
+ uint64_t write_ofs = 0;
+ /* as we are writing max_chunk_size at a time in sal_dbstore DBAtomicWriter::process(),
+ * maybe this while loop is not needed
+ */
+ while (write_ofs < end) {
+ part_num = (ofs / max_chunk_size);
+ uint64_t len = std::min(end, max_chunk_size);
+
+ /* XXX: Handle multipart_str */
+ raw_obj write_obj(store, target->get_bucket_info().bucket.name, obj_state.obj.key.name,
+ obj_state.obj.key.instance, obj_state.obj.key.ns, target->obj_id, mp_part_str, part_num);
+
+
+ ldpp_dout(dpp, 20) << "dbstore->write obj-ofs=" << ofs << " write_len=" << len << dendl;
+
+ // write into non head object
+ int r = write_obj.write(dpp, ofs, write_ofs, len, data);
+ if (r < 0) {
+ return r;
+ }
+ /* r refers to chunk_len (no. of bytes) handled in raw_obj::write */
+ len -= r;
+ ofs += r;
+ write_ofs += r;
+ }
+
+ return 0;
+}
+
+/* Write metadata & head object data */
+int DB::Object::Write::_do_write_meta(const DoutPrefixProvider *dpp,
+ uint64_t size, uint64_t accounted_size,
+ map<string, bufferlist>& attrs,
+ bool assume_noent, bool modify_tail)
+{
+ DB *store = target->get_store();
+
+ RGWObjState* state = &obj_state;
+ map<string, bufferlist> *attrset;
+ DBOpParams params = {};
+ int ret = 0;
+ string etag;
+ string content_type;
+ bufferlist acl_bl;
+ string storage_class;
+
+ map<string, bufferlist>::iterator iter;
+
+ store->InitializeParams(dpp, &params);
+ target->InitializeParamsfromObject(dpp, &params);
+
+ obj_state = params.op.obj.state;
+
+ if (real_clock::is_zero(meta.set_mtime)) {
+ meta.set_mtime = real_clock::now();
+ }
+
+ attrset = &state->attrset;
+ if (target->bucket_info.obj_lock_enabled() && target->bucket_info.obj_lock.has_rule()) {
+ // && meta.flags == PUT_OBJ_CREATE) {
+ auto iter = attrs.find(RGW_ATTR_OBJECT_RETENTION);
+ if (iter == attrs.end()) {
+ real_time lock_until_date = target->bucket_info.obj_lock.get_lock_until_date(meta.set_mtime);
+ string mode = target->bucket_info.obj_lock.get_mode();
+ RGWObjectRetention obj_retention(mode, lock_until_date);
+ bufferlist bl;
+ obj_retention.encode(bl);
+ (*attrset)[RGW_ATTR_OBJECT_RETENTION] = bl;
+ }
+ }
+
+ state->mtime = meta.set_mtime;
+
+ if (meta.data) {
+ /* if we want to overwrite the data, we also want to overwrite the
+ xattrs, so just remove the object */
+ params.op.obj.head_data = *meta.data;
+ }
+
+ if (meta.rmattrs) {
+ for (iter = meta.rmattrs->begin(); iter != meta.rmattrs->end(); ++iter) {
+ const string& name = iter->first;
+ (*attrset).erase(name.c_str());
+ }
+ }
+
+ if (meta.manifest) {
+ storage_class = meta.manifest->get_tail_placement().placement_rule.storage_class;
+
+ /* remove existing manifest attr */
+ iter = attrs.find(RGW_ATTR_MANIFEST);
+ if (iter != attrs.end())
+ attrs.erase(iter);
+
+ bufferlist bl;
+ encode(*meta.manifest, bl);
+ (*attrset)[RGW_ATTR_MANIFEST] = bl;
+ }
+
+ for (iter = attrs.begin(); iter != attrs.end(); ++iter) {
+ const string& name = iter->first;
+ bufferlist& bl = iter->second;
+
+ if (!bl.length())
+ continue;
+
+ (*attrset)[name.c_str()] = bl;
+
+ if (name.compare(RGW_ATTR_ETAG) == 0) {
+ etag = rgw_bl_str(bl);
+ params.op.obj.etag = etag;
+ } else if (name.compare(RGW_ATTR_CONTENT_TYPE) == 0) {
+ content_type = rgw_bl_str(bl);
+ } else if (name.compare(RGW_ATTR_ACL) == 0) {
+ acl_bl = bl;
+ }
+ }
+
+ if (!storage_class.empty()) {
+ bufferlist bl;
+ bl.append(storage_class);
+ (*attrset)[RGW_ATTR_STORAGE_CLASS] = bl;
+ }
+
+ params.op.obj.state = *state ;
+ params.op.obj.state.exists = true;
+ params.op.obj.state.size = size;
+ params.op.obj.state.accounted_size = accounted_size;
+ params.op.obj.owner = target->get_bucket_info().owner.id;
+ params.op.obj.category = meta.category;
+
+ if (meta.mtime) {
+ *meta.mtime = meta.set_mtime;
+ }
+
+ params.op.query_str = "meta";
+ params.op.obj.obj_id = target->obj_id;
+
+ /* Check if versioned */
+ bool is_versioned = !target->obj.key.instance.empty() && (target->obj.key.instance != "null");
+ params.op.obj.is_versioned = is_versioned;
+
+ if (is_versioned && (params.op.obj.category == RGWObjCategory::Main)) {
+ /* versioned object */
+ params.op.obj.flags |= rgw_bucket_dir_entry::FLAG_VER;
+ }
+ ret = store->ProcessOp(dpp, "PutObject", &params);
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In PutObject failed err:(" <<ret<<")" << dendl;
+ goto out;
+ }
+
+
+out:
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: do_write_meta returned ret=" << ret << dendl;
+ }
+
+ meta.canceled = true;
+
+ return ret;
+}
+
+int DB::Object::Write::write_meta(const DoutPrefixProvider *dpp, uint64_t size, uint64_t accounted_size,
+ map<string, bufferlist>& attrs)
+{
+ bool assume_noent = false;
+ /* handle assume_noent */
+ int r = _do_write_meta(dpp, size, accounted_size, attrs, assume_noent, meta.modify_tail);
+ return r;
+}
+
+int DB::Object::Delete::delete_obj(const DoutPrefixProvider *dpp) {
+ int ret = 0;
+ DBOpParams del_params = {};
+ bool versioning_enabled = ((params.versioning_status & BUCKET_VERSIONED) == BUCKET_VERSIONED);
+ bool versioning_suspended = ((params.versioning_status & BUCKET_VERSIONS_SUSPENDED) == BUCKET_VERSIONS_SUSPENDED);
+ bool regular_obj = true;
+ std::string versionid = target->obj.key.instance;
+
+ ret = target->get_object_impl(dpp, del_params);
+
+ if (ret < 0 && ret != -ENOENT) {
+ ldpp_dout(dpp, 0)<<"GetObject during delete failed err:(" <<ret<<")" << dendl;
+ return ret;
+ }
+
+ regular_obj = (del_params.op.obj.category == RGWObjCategory::Main);
+ if (!ret) {
+ if (!versionid.empty()) {
+ // version-id is provided
+ ret = delete_obj_impl(dpp, del_params);
+ return ret;
+ } else { // version-id is empty..
+ /*
+ * case: bucket_versioned
+ * create_delete_marker;
+ * case: bucket_suspended
+ * delete entry
+ * create delete marker with version-id null;
+ * default:
+ * just delete the entry
+ */
+ if (versioning_suspended && regular_obj) {
+ ret = delete_obj_impl(dpp, del_params);
+ ret = create_dm(dpp, del_params);
+ } else if (versioning_enabled && regular_obj) {
+ ret = create_dm(dpp, del_params);
+ } else {
+ ret = delete_obj_impl(dpp, del_params);
+ }
+ }
+ } else { // ret == -ENOENT
+ /* case: VersionID given
+ * return -ENOENT
+ * else: // may or may not be versioned object
+ * Listversionedobjects
+ * if (list_entries.empty()) {
+ * nothing to do..return ENOENT
+ * } else {
+ * read top entry
+ * if (top.flags | FLAG_DELETE_MARKER) {
+ * // nothing to do
+ * return -ENOENT;
+ * }
+ * if (bucket_versioned) {
+ * // create delete marker with new version-id
+ * } else if (bucket_suspended) {
+ * // create delete marker with version-id null
+ * }
+ * bucket cannot be in unversioned state post having versions
+ * }
+ */
+ if (!versionid.empty()) {
+ return -ENOENT;
+ }
+ ret = target->list_versioned_objects(dpp, del_params.op.obj.list_entries);
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"ListVersionedObjects failed err:(" <<ret<<")" << dendl;
+ return ret;
+ }
+ if (del_params.op.obj.list_entries.empty()) {
+ return -ENOENT;
+ }
+ auto &ent = del_params.op.obj.list_entries.front();
+ if (ent.flags & rgw_bucket_dir_entry::FLAG_DELETE_MARKER) {
+ // for now do not create another delete marker..just exit
+ return 0;
+ }
+ ret = create_dm(dpp, del_params);
+ }
+ return ret;
+}
+
+int DB::Object::Delete::delete_obj_impl(const DoutPrefixProvider *dpp,
+ DBOpParams& del_params) {
+ int ret = 0;
+ DB *store = target->get_store();
+
+ ret = store->ProcessOp(dpp, "DeleteObject", &del_params);
+ if (ret) {
+ ldpp_dout(dpp, 0) << "In DeleteObject failed err:(" <<ret<<")" << dendl;
+ return ret;
+ }
+
+ /* Now that tail objects are associated with objectID, they are not deleted
+ * as part of this DeleteObj operation. Such tail objects (with no head object
+ * in *.object.table are cleaned up later by GC thread.
+ *
+ * To avoid races between writes/reads & GC delete, mtime is maintained for each
+ * tail object. This mtime is updated when tail object is written and also when
+ * its corresponding head object is deleted (like here in this case).
+ */
+ DBOpParams update_params = del_params;
+ update_params.op.obj.state.mtime = real_clock::now();
+ ret = store->ProcessOp(dpp, "UpdateObjectData", &update_params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0) << "Updating tail objects mtime failed err:(" <<ret<<")" << dendl;
+ }
+ return ret;
+}
+
+/*
+ * a) if no versionID specified,
+ * - create a delete marker with
+ * - new version/instanceID (if bucket versioned)
+ * - null versionID (if versioning suspended)
+ */
+int DB::Object::Delete::create_dm(const DoutPrefixProvider *dpp,
+ DBOpParams& del_params) {
+
+ DB *store = target->get_store();
+ bool versioning_suspended = ((params.versioning_status & BUCKET_VERSIONS_SUSPENDED) == BUCKET_VERSIONS_SUSPENDED);
+ int ret = -1;
+ DBOpParams olh_params = {};
+ std::string version_id;
+ DBOpParams next_params = del_params;
+
+ version_id = del_params.op.obj.state.obj.key.instance;
+
+ DBOpParams dm_params = del_params;
+
+ // create delete marker
+
+ store->InitializeParams(dpp, &dm_params);
+ target->InitializeParamsfromObject(dpp, &dm_params);
+ dm_params.op.obj.category = RGWObjCategory::None;
+
+ if (versioning_suspended) {
+ dm_params.op.obj.state.obj.key.instance = "null";
+ } else {
+ store->gen_rand_obj_instance_name(&dm_params.op.obj.state.obj.key);
+ dm_params.op.obj.obj_id = dm_params.op.obj.state.obj.key.instance;
+ }
+
+ dm_params.op.obj.flags |= (rgw_bucket_dir_entry::FLAG_DELETE_MARKER);
+
+ ret = store->ProcessOp(dpp, "PutObject", &dm_params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0) << "delete_olh: failed to create delete marker - err:(" <<ret<<")" << dendl;
+ return ret;
+ }
+ result.delete_marker = true;
+ result.version_id = dm_params.op.obj.state.obj.key.instance;
+ return ret;
+}
+
+int DB::get_entry(const std::string& oid, const std::string& marker,
+ std::unique_ptr<rgw::sal::Lifecycle::LCEntry>* entry)
+{
+ int ret = 0;
+ const DoutPrefixProvider *dpp = get_def_dpp();
+
+ DBOpParams params = {};
+ InitializeParams(dpp, &params);
+
+ params.op.lc_entry.index = oid;
+ params.op.lc_entry.entry.set_bucket(marker);
+
+ params.op.query_str = "get_entry";
+ ret = ProcessOp(dpp, "GetLCEntry", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In GetLCEntry failed err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+
+ if (!params.op.lc_entry.entry.get_start_time() == 0) { //ensure entry found
+ rgw::sal::Lifecycle::LCEntry* e;
+ e = new rgw::sal::StoreLifecycle::StoreLCEntry(params.op.lc_entry.entry);
+ if (!e) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ entry->reset(e);
+ }
+
+out:
+ return ret;
+}
+
+int DB::get_next_entry(const std::string& oid, const std::string& marker,
+ std::unique_ptr<rgw::sal::Lifecycle::LCEntry>* entry)
+{
+ int ret = 0;
+ const DoutPrefixProvider *dpp = get_def_dpp();
+
+ DBOpParams params = {};
+ InitializeParams(dpp, &params);
+
+ params.op.lc_entry.index = oid;
+ params.op.lc_entry.entry.set_bucket(marker);
+
+ params.op.query_str = "get_next_entry";
+ ret = ProcessOp(dpp, "GetLCEntry", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In GetLCEntry failed err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+
+ if (!params.op.lc_entry.entry.get_start_time() == 0) { //ensure entry found
+ rgw::sal::Lifecycle::LCEntry* e;
+ e = new rgw::sal::StoreLifecycle::StoreLCEntry(params.op.lc_entry.entry);
+ if (!e) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ entry->reset(e);
+ }
+
+out:
+ return ret;
+}
+
+int DB::set_entry(const std::string& oid, rgw::sal::Lifecycle::LCEntry& entry)
+{
+ int ret = 0;
+ const DoutPrefixProvider *dpp = get_def_dpp();
+
+ DBOpParams params = {};
+ InitializeParams(dpp, &params);
+
+ params.op.lc_entry.index = oid;
+ params.op.lc_entry.entry = entry;
+
+ ret = ProcessOp(dpp, "InsertLCEntry", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In InsertLCEntry failed err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int DB::list_entries(const std::string& oid, const std::string& marker,
+ uint32_t max_entries, std::vector<std::unique_ptr<rgw::sal::Lifecycle::LCEntry>>& entries)
+{
+ int ret = 0;
+ const DoutPrefixProvider *dpp = get_def_dpp();
+
+ entries.clear();
+
+ DBOpParams params = {};
+ InitializeParams(dpp, &params);
+
+ params.op.lc_entry.index = oid;
+ params.op.lc_entry.min_marker = marker;
+ params.op.list_max_count = max_entries;
+
+ ret = ProcessOp(dpp, "ListLCEntries", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In ListLCEntries failed err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+
+ for (auto& entry : params.op.lc_entry.list_entries) {
+ entries.push_back(std::make_unique<rgw::sal::StoreLifecycle::StoreLCEntry>(std::move(entry)));
+ }
+
+out:
+ return ret;
+}
+
+int DB::rm_entry(const std::string& oid, rgw::sal::Lifecycle::LCEntry& entry)
+{
+ int ret = 0;
+ const DoutPrefixProvider *dpp = get_def_dpp();
+
+ DBOpParams params = {};
+ InitializeParams(dpp, &params);
+
+ params.op.lc_entry.index = oid;
+ params.op.lc_entry.entry = entry;
+
+ ret = ProcessOp(dpp, "RemoveLCEntry", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In RemoveLCEntry failed err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int DB::get_head(const std::string& oid, std::unique_ptr<rgw::sal::Lifecycle::LCHead>* head)
+{
+ int ret = 0;
+ const DoutPrefixProvider *dpp = get_def_dpp();
+
+ DBOpParams params = {};
+ InitializeParams(dpp, &params);
+
+ params.op.lc_head.index = oid;
+
+ ret = ProcessOp(dpp, "GetLCHead", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In GetLCHead failed err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+
+ *head = std::make_unique<rgw::sal::StoreLifecycle::StoreLCHead>(params.op.lc_head.head);
+
+out:
+ return ret;
+}
+
+int DB::put_head(const std::string& oid, rgw::sal::Lifecycle::LCHead& head)
+{
+ int ret = 0;
+ const DoutPrefixProvider *dpp = get_def_dpp();
+
+ DBOpParams params = {};
+ InitializeParams(dpp, &params);
+
+ params.op.lc_head.index = oid;
+ params.op.lc_head.head = head;
+
+ ret = ProcessOp(dpp, "InsertLCHead", &params);
+
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"In InsertLCHead failed err:(" <<ret<<") " << dendl;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int DB::delete_stale_objs(const DoutPrefixProvider *dpp, const std::string& bucket,
+ uint32_t min_wait) {
+ DBOpParams params = {};
+ int ret = -1;
+
+ params.op.bucket.info.bucket.name = bucket;
+ /* Verify if bucket exists.
+ * XXX: This is needed for now to create objectmap of bucket
+ * in SQLGetBucket
+ */
+ InitializeParams(dpp, &params);
+ ret = ProcessOp(dpp, "GetBucket", &params);
+ if (ret) {
+ ldpp_dout(dpp, 0) << "In GetBucket failed err:(" <<ret<<")" << dendl;
+ return ret;
+ }
+
+ ldpp_dout(dpp, 20) << " Deleting stale_objs of bucket( " << bucket <<")" << dendl;
+ /* XXX: handle reads racing with delete here. Simple approach is maybe
+ * to use locks or sqlite transactions.
+ */
+ InitializeParams(dpp, &params);
+ params.op.obj.state.mtime = (real_clock::now() - make_timespan(min_wait));
+ ret = ProcessOp(dpp, "DeleteStaleObjectData", &params);
+ if (ret) {
+ ldpp_dout(dpp, 0) << "In DeleteStaleObjectData failed err:(" <<ret<<")" << dendl;
+ }
+
+ return ret;
+}
+
+void *DB::GC::entry() {
+ do {
+ std::unique_lock<std::mutex> lk(mtx);
+
+ ldpp_dout(dpp, 2) << " DB GC started " << dendl;
+ int max = 100;
+ RGWUserBuckets buckets;
+ bool is_truncated = false;
+
+ do {
+ std::string& marker = bucket_marker;
+ rgw_user user;
+ user.id = user_marker;
+ buckets.clear();
+ is_truncated = false;
+
+ int r = db->list_buckets(dpp, "all", user, marker, string(),
+ max, false, &buckets, &is_truncated);
+
+ if (r < 0) { //do nothing? retry later ?
+ break;
+ }
+
+ for (const auto& ent : buckets.get_buckets()) {
+ const std::string &bname = ent.first;
+
+ r = db->delete_stale_objs(dpp, bname, gc_obj_min_wait);
+
+ if (r < 0) { //do nothing? skip to next entry?
+ ldpp_dout(dpp, 2) << " delete_stale_objs failed for bucket( " << bname <<")" << dendl;
+ }
+ bucket_marker = bname;
+ user_marker = user.id;
+
+ /* XXX: If using locks, unlock here and reacquire in the next iteration */
+ cv.wait_for(lk, std::chrono::milliseconds(100));
+ if (stop_signalled) {
+ goto done;
+ }
+ }
+ } while(is_truncated);
+
+ bucket_marker.clear();
+ cv.wait_for(lk, std::chrono::milliseconds(gc_interval*10));
+ } while(! stop_signalled);
+
+done:
+ return nullptr;
+}
+
+} } // namespace rgw::store
+
diff --git a/src/rgw/driver/dbstore/common/dbstore.h b/src/rgw/driver/dbstore/common/dbstore.h
new file mode 100644
index 000000000..b26cc116e
--- /dev/null
+++ b/src/rgw/driver/dbstore/common/dbstore.h
@@ -0,0 +1,2016 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string>
+#include <stdio.h>
+#include <iostream>
+#include <mutex>
+#include <condition_variable>
+#include "fmt/format.h"
+#include <map>
+#include "rgw_sal_store.h"
+#include "rgw_common.h"
+#include "driver/rados/rgw_bucket.h"
+#include "global/global_context.h"
+#include "global/global_init.h"
+#include "common/ceph_context.h"
+#include "rgw_obj_manifest.h"
+#include "rgw_multi.h"
+
+namespace rgw { namespace store {
+
+class DB;
+
+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
+ std::string min_marker;
+ std::string max_marker;
+ std::list<RGWBucketEnt> list_entries;
+};
+
+struct DBOpObjectInfo {
+ RGWAccessControlPolicy acls;
+ RGWObjState state = {};
+
+ /* Below are taken from rgw_bucket_dir_entry */
+ RGWObjCategory category;
+ std::string etag;
+ std::string owner;
+ std::string owner_display_name;
+ std::string content_type;
+ std::string storage_class;
+ bool appendable;
+ uint64_t index_ver;
+ std::string tag;
+ uint16_t flags;
+ uint64_t versioned_epoch;
+
+ /* from state.manifest (RGWObjManifest) */
+ std::map<uint64_t, RGWObjManifestPart> objs;
+ uint64_t head_size{0};
+ rgw_placement_rule head_placement_rule;
+ uint64_t max_head_size{0};
+ std::string obj_id;
+ rgw_bucket_placement tail_placement; /* might be different than the original bucket,
+ as object might have been copied across pools */
+ std::map<uint64_t, RGWObjManifestRule> rules;
+ std::string tail_instance; /* tail object's instance */
+
+
+ /* Obj's omap <key,value> store */
+ std::map<std::string, bufferlist> omap;
+
+ /* Extra fields */
+ bool is_multipart;
+ std::list<RGWUploadPartInfo> mp_parts;
+
+ bufferlist head_data;
+ std::string min_marker;
+ std::string max_marker;
+ std::string prefix;
+ std::list<rgw_bucket_dir_entry> list_entries;
+ /* XXX: Maybe use std::vector instead of std::list */
+
+ /* for versioned objects */
+ bool is_versioned;
+ uint64_t version_num = 0;
+};
+
+struct DBOpObjectDataInfo {
+ RGWObjState state;
+ uint64_t part_num;
+ std::string multipart_part_str;
+ uint64_t offset;
+ uint64_t size;
+ bufferlist data{};
+};
+
+struct DBOpLCHeadInfo {
+ std::string index;
+ rgw::sal::StoreLifecycle::StoreLCHead head;
+};
+
+struct DBOpLCEntryInfo {
+ std::string index;
+ rgw::sal::StoreLifecycle::StoreLCEntry entry;
+ // used for list query
+ std::string min_marker;
+ std::list<rgw::sal::StoreLifecycle::StoreLCEntry> list_entries;
+};
+
+struct DBOpInfo {
+ std::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;
+ std::string query_str;
+ DBOpBucketInfo bucket;
+ DBOpObjectInfo obj;
+ DBOpObjectDataInfo obj_data;
+ DBOpLCHeadInfo lc_head;
+ DBOpLCEntryInfo lc_entry;
+ uint64_t list_max_count;
+};
+
+struct DBOpParams {
+ CephContext *cct;
+
+ /* Tables */
+ std::string user_table;
+ std::string bucket_table;
+ std::string object_table;
+
+ /* Ops*/
+ DBOpInfo op;
+
+ std::string objectdata_table;
+ std::string object_trigger;
+ std::string object_view;
+ std::string quota_table;
+ std::string lc_head_table;
+ std::string lc_entry_table;
+ std::string obj;
+};
+
+/* 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 {
+ static constexpr const char* user_id = ":user_id";
+ static constexpr const char* tenant = ":tenant";
+ static constexpr const char* ns = ":ns";
+ static constexpr const char* display_name = ":display_name";
+ static constexpr const char* 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.
+ */
+ static constexpr const char* access_keys_id = ":access_keys_id";
+ static constexpr const char* access_keys_secret = ":access_keys_secret";
+ static constexpr const char* access_keys = ":access_keys";
+ static constexpr const char* swift_keys = ":swift_keys";
+ static constexpr const char* subusers = ":subusers";
+ static constexpr const char* suspended = ":suspended";
+ static constexpr const char* max_buckets = ":max_buckets";
+ static constexpr const char* op_mask = ":op_mask";
+ static constexpr const char* user_caps = ":user_caps";
+ static constexpr const char* admin = ":admin";
+ static constexpr const char* system = ":system";
+ static constexpr const char* placement_name = ":placement_name";
+ static constexpr const char* placement_storage_class = ":placement_storage_class";
+ static constexpr const char* placement_tags = ":placement_tags";
+ static constexpr const char* bucket_quota = ":bucket_quota";
+ static constexpr const char* temp_url_keys = ":temp_url_keys";
+ static constexpr const char* user_quota = ":user_quota";
+ static constexpr const char* type = ":type";
+ static constexpr const char* mfa_ids = ":mfa_ids";
+ static constexpr const char* user_attrs = ":user_attrs";
+ static constexpr const char* user_ver = ":user_vers";
+ static constexpr const char* user_ver_tag = ":user_ver_tag";
+};
+
+struct DBOpBucketPrepareInfo {
+ static constexpr const char* bucket_name = ":bucket_name";
+ static constexpr const char* tenant = ":tenant";
+ static constexpr const char* marker = ":marker";
+ static constexpr const char* bucket_id = ":bucket_id";
+ static constexpr const char* size = ":size";
+ static constexpr const char* size_rounded = ":size_rounded";
+ static constexpr const char* creation_time = ":creation_time";
+ static constexpr const char* count = ":count";
+ static constexpr const char* placement_name = ":placement_name";
+ static constexpr const char* placement_storage_class = ":placement_storage_class";
+ /* ownerid - maps to DBOpUserPrepareInfo */
+ static constexpr const char* flags = ":flags";
+ static constexpr const char* zonegroup = ":zonegroup";
+ static constexpr const char* has_instance_obj = ":has_instance_obj";
+ static constexpr const char* quota = ":quota";
+ static constexpr const char* requester_pays = ":requester_pays";
+ static constexpr const char* has_website = ":has_website";
+ static constexpr const char* website_conf = ":website_conf";
+ static constexpr const char* swift_versioning = ":swift_versioning";
+ static constexpr const char* swift_ver_location = ":swift_ver_location";
+ static constexpr const char* mdsearch_config = ":mdsearch_config";
+ static constexpr const char* new_bucket_instance_id = ":new_bucket_instance_id";
+ static constexpr const char* obj_lock = ":obj_lock";
+ static constexpr const char* sync_policy_info_groups = ":sync_policy_info_groups";
+ static constexpr const char* bucket_attrs = ":bucket_attrs";
+ static constexpr const char* bucket_ver = ":bucket_vers";
+ static constexpr const char* bucket_ver_tag = ":bucket_ver_tag";
+ static constexpr const char* mtime = ":mtime";
+ static constexpr const char* min_marker = ":min_marker";
+ static constexpr const char* max_marker = ":max_marker";
+};
+
+struct DBOpObjectPrepareInfo {
+ static constexpr const char* obj_name = ":obj_name";
+ static constexpr const char* obj_instance = ":obj_instance";
+ static constexpr const char* obj_ns = ":obj_ns";
+ static constexpr const char* acls = ":acls";
+ static constexpr const char* index_ver = ":index_ver";
+ static constexpr const char* tag = ":tag";
+ static constexpr const char* flags = ":flags";
+ static constexpr const char* versioned_epoch = ":versioned_epoch";
+ static constexpr const char* obj_category = ":obj_category";
+ static constexpr const char* etag = ":etag";
+ static constexpr const char* owner = ":owner";
+ static constexpr const char* owner_display_name = ":owner_display_name";
+ static constexpr const char* storage_class = ":storage_class";
+ static constexpr const char* appendable = ":appendable";
+ static constexpr const char* content_type = ":content_type";
+ static constexpr const char* index_hash_source = ":index_hash_source";
+ static constexpr const char* obj_size = ":obj_size";
+ static constexpr const char* accounted_size = ":accounted_size";
+ static constexpr const char* mtime = ":mtime";
+ static constexpr const char* epoch = ":epoch";
+ static constexpr const char* obj_tag = ":obj_tag";
+ static constexpr const char* tail_tag = ":tail_tag";
+ static constexpr const char* write_tag = ":write_tag";
+ static constexpr const char* fake_tag = ":fake_tag";
+ static constexpr const char* shadow_obj = ":shadow_obj";
+ static constexpr const char* has_data = ":has_data";
+ static constexpr const char* is_versioned = ":is_versioned";
+ static constexpr const char* version_num = ":version_num";
+ static constexpr const char* pg_ver = ":pg_ver";
+ static constexpr const char* zone_short_id = ":zone_short_id";
+ static constexpr const char* obj_version = ":obj_version";
+ static constexpr const char* obj_version_tag = ":obj_version_tag";
+ static constexpr const char* obj_attrs = ":obj_attrs";
+ static constexpr const char* head_size = ":head_size";
+ static constexpr const char* max_head_size = ":max_head_size";
+ static constexpr const char* obj_id = ":obj_id";
+ static constexpr const char* tail_instance = ":tail_instance";
+ static constexpr const char* head_placement_rule_name = ":head_placement_rule_name";
+ static constexpr const char* head_placement_storage_class = ":head_placement_storage_class";
+ static constexpr const char* tail_placement_rule_name = ":tail_placement_rule_name";
+ static constexpr const char* tail_placement_storage_class = ":tail_placement_storage_class";
+ static constexpr const char* manifest_part_objs = ":manifest_part_objs";
+ static constexpr const char* manifest_part_rules = ":manifest_part_rules";
+ static constexpr const char* omap = ":omap";
+ static constexpr const char* is_multipart = ":is_multipart";
+ static constexpr const char* mp_parts = ":mp_parts";
+ static constexpr const char* head_data = ":head_data";
+ static constexpr const char* min_marker = ":min_marker";
+ static constexpr const char* max_marker = ":max_marker";
+ static constexpr const char* prefix = ":prefix";
+ /* Below used to update mp_parts obj name
+ * from meta object to src object on completion */
+ static constexpr const char* new_obj_name = ":new_obj_name";
+ static constexpr const char* new_obj_instance = ":new_obj_instance";
+ static constexpr const char* new_obj_ns = ":new_obj_ns";
+};
+
+struct DBOpObjectDataPrepareInfo {
+ static constexpr const char* part_num = ":part_num";
+ static constexpr const char* offset = ":offset";
+ static constexpr const char* data = ":data";
+ static constexpr const char* size = ":size";
+ static constexpr const char* multipart_part_str = ":multipart_part_str";
+};
+
+struct DBOpLCEntryPrepareInfo {
+ static constexpr const char* index = ":index";
+ static constexpr const char* bucket_name = ":bucket_name";
+ static constexpr const char* start_time = ":start_time";
+ static constexpr const char* status = ":status";
+ static constexpr const char* min_marker = ":min_marker";
+};
+
+struct DBOpLCHeadPrepareInfo {
+ static constexpr const char* index = ":index";
+ static constexpr const char* start_date = ":start_date";
+ static constexpr const char* marker = ":marker";
+};
+
+struct DBOpPrepareInfo {
+ DBOpUserPrepareInfo user;
+ std::string_view query_str; // view into DBOpInfo::query_str
+ DBOpBucketPrepareInfo bucket;
+ DBOpObjectPrepareInfo obj;
+ DBOpObjectDataPrepareInfo obj_data;
+ DBOpLCHeadPrepareInfo lc_head;
+ DBOpLCEntryPrepareInfo lc_entry;
+ static constexpr const char* list_max_count = ":list_max_count";
+};
+
+struct DBOpPrepareParams {
+ /* Tables */
+ std::string user_table;
+ std::string bucket_table;
+ std::string object_table;
+
+ /* Ops */
+ DBOpPrepareInfo op;
+
+
+ std::string objectdata_table;
+ std::string object_trigger;
+ std::string object_view;
+ std::string quota_table;
+ std::string lc_head_table;
+ std::string lc_entry_table;
+};
+
+struct DBOps {
+ std::shared_ptr<class InsertUserOp> InsertUser;
+ std::shared_ptr<class RemoveUserOp> RemoveUser;
+ std::shared_ptr<class GetUserOp> GetUser;
+ std::shared_ptr<class InsertBucketOp> InsertBucket;
+ std::shared_ptr<class UpdateBucketOp> UpdateBucket;
+ std::shared_ptr<class RemoveBucketOp> RemoveBucket;
+ std::shared_ptr<class GetBucketOp> GetBucket;
+ std::shared_ptr<class ListUserBucketsOp> ListUserBuckets;
+ std::shared_ptr<class InsertLCEntryOp> InsertLCEntry;
+ std::shared_ptr<class RemoveLCEntryOp> RemoveLCEntry;
+ std::shared_ptr<class GetLCEntryOp> GetLCEntry;
+ std::shared_ptr<class ListLCEntriesOp> ListLCEntries;
+ std::shared_ptr<class InsertLCHeadOp> InsertLCHead;
+ std::shared_ptr<class RemoveLCHeadOp> RemoveLCHead;
+ std::shared_ptr<class GetLCHeadOp> GetLCHead;
+};
+
+class ObjectOp {
+ public:
+ ObjectOp() {};
+
+ virtual ~ObjectOp() {}
+
+ std::shared_ptr<class PutObjectOp> PutObject;
+ std::shared_ptr<class DeleteObjectOp> DeleteObject;
+ std::shared_ptr<class GetObjectOp> GetObject;
+ std::shared_ptr<class UpdateObjectOp> UpdateObject;
+ std::shared_ptr<class ListBucketObjectsOp> ListBucketObjects;
+ std::shared_ptr<class ListVersionedObjectsOp> ListVersionedObjects;
+ std::shared_ptr<class PutObjectDataOp> PutObjectData;
+ std::shared_ptr<class UpdateObjectDataOp> UpdateObjectData;
+ std::shared_ptr<class GetObjectDataOp> GetObjectData;
+ std::shared_ptr<class DeleteObjectDataOp> DeleteObjectData;
+ std::shared_ptr<class DeleteStaleObjectDataOp> DeleteStaleObjectData;
+
+ virtual int InitializeObjectOps(std::string db_name, const DoutPrefixProvider *dpp) { return 0; }
+};
+
+class DBOp {
+ private:
+ static constexpr std::string_view CreateUserTableQ =
+ /* Corresponds to rgw::sal::User
+ *
+ * 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);";
+
+ static constexpr std::string_view CreateBucketTableQ =
+ /* Corresponds to rgw::sal::Bucket
+ *
+ * For now only BucketName is made Primary key. Since buckets should
+ * be unique across users in rgw, OwnerID is not made part of primary key.
+ * However it is still referenced as foreign 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);";
+
+ static constexpr std::string_view CreateObjectTableTriggerQ =
+ "CREATE TRIGGER IF NOT EXISTS '{}' \
+ AFTER INSERT ON '{}' \
+ BEGIN \
+ UPDATE '{}' \
+ SET VersionNum = (SELECT COALESCE(max(VersionNum), 0) from '{}' where ObjName = new.ObjName) + 1 \
+ where ObjName = new.ObjName and ObjInstance = new.ObjInstance; \
+ END;";
+
+ static constexpr std::string_view CreateObjectTableQ =
+ /* Corresponds to rgw::sal::Object
+ *
+ * For now only BucketName, ObjName is made Primary key.
+ * If multiple tenants are stored in single .db handle, should
+ * include Tenant too in the Primary Key. Also should
+ * reference (BucketID, Tenant) as Foreign key.
+ *
+ * referring to
+ * - rgw_bucket_dir_entry - following are added for now
+ * flags,
+ * versioned_epoch
+ * tag
+ * index_ver
+ * meta.category
+ * meta.etag
+ * meta.storageclass
+ * meta.appendable
+ * meta.content_type
+ * meta.owner
+ * meta.owner_display_name
+ *
+ * - RGWObjState. Below are omitted from that struct
+ * as they seem in-memory variables
+ * * is_atomic, has_atts, exists, prefetch_data, keep_tail,
+ * - RGWObjManifest
+ *
+ * Extra field added "IsMultipart" to flag multipart uploads,
+ * HeadData to store first chunk data.
+ */
+ "CREATE TABLE IF NOT EXISTS '{}' ( \
+ ObjName TEXT NOT NULL , \
+ ObjInstance TEXT, \
+ ObjNS TEXT, \
+ BucketName TEXT NOT NULL , \
+ ACLs BLOB, \
+ IndexVer INTEGER, \
+ Tag TEXT, \
+ Flags INTEGER, \
+ VersionedEpoch INTEGER, \
+ ObjCategory INTEGER, \
+ Etag TEXT, \
+ Owner TEXT, \
+ OwnerDisplayName TEXT, \
+ StorageClass TEXT, \
+ Appendable BOOL, \
+ ContentType TEXT, \
+ IndexHashSource TEXT, \
+ ObjSize INTEGER, \
+ AccountedSize INTEGER, \
+ Mtime BLOB, \
+ Epoch INTEGER, \
+ ObjTag BLOB, \
+ TailTag BLOB, \
+ WriteTag TEXT, \
+ FakeTag BOOL, \
+ ShadowObj TEXT, \
+ HasData BOOL, \
+ IsVersioned BOOL, \
+ VersionNum INTEGER, \
+ PGVer INTEGER, \
+ ZoneShortID INTEGER, \
+ ObjVersion INTEGER, \
+ ObjVersionTag TEXT, \
+ ObjAttrs BLOB, \
+ HeadSize INTEGER, \
+ MaxHeadSize INTEGER, \
+ ObjID TEXT NOT NULL, \
+ TailInstance TEXT, \
+ HeadPlacementRuleName TEXT, \
+ HeadPlacementRuleStorageClass TEXT, \
+ TailPlacementRuleName TEXT, \
+ TailPlacementStorageClass TEXT, \
+ ManifestPartObjs BLOB, \
+ ManifestPartRules BLOB, \
+ Omap BLOB, \
+ IsMultipart BOOL, \
+ MPPartsList BLOB, \
+ HeadData BLOB, \
+ PRIMARY KEY (ObjName, ObjInstance, BucketName), \
+ FOREIGN KEY (BucketName) \
+ REFERENCES '{}' (BucketName) ON DELETE CASCADE ON UPDATE CASCADE \n);";
+
+ static constexpr std::string_view CreateObjectDataTableQ =
+ /* Extra field 'MultipartPartStr' added which signifies multipart
+ * <uploadid + partnum>. For regular object, it is '0.0'
+ *
+ * - part: a collection of stripes that make a contiguous part of an
+ object. A regular object will only have one part (although might have
+ many stripes), a multipart object might have many parts. Each part
+ has a fixed stripe size (ObjChunkSize), although the last stripe of a
+ part might be smaller than that.
+ */
+ "CREATE TABLE IF NOT EXISTS '{}' ( \
+ ObjName TEXT NOT NULL , \
+ ObjInstance TEXT, \
+ ObjNS TEXT, \
+ BucketName TEXT NOT NULL , \
+ ObjID TEXT NOT NULL , \
+ MultipartPartStr TEXT, \
+ PartNum INTEGER NOT NULL, \
+ Offset INTEGER, \
+ Size INTEGER, \
+ Mtime BLOB, \
+ Data BLOB, \
+ PRIMARY KEY (ObjName, BucketName, ObjInstance, ObjID, MultipartPartStr, PartNum), \
+ FOREIGN KEY (BucketName) \
+ REFERENCES '{}' (BucketName) ON DELETE CASCADE ON UPDATE CASCADE \n);";
+
+ static constexpr std::string_view CreateObjectViewQ =
+ /* This query creats temporary view with entries from ObjectData table which have
+ * corresponding head object (i.e, with same ObjName, ObjInstance, ObjNS, ObjID)
+ * in the Object table.
+ *
+ * GC thread can use this view to delete stale entries from the ObjectData table which
+ * do not exist in this view.
+ *
+ * XXX: This view is throwing ForeignKey mismatch error, mostly may be because all the keys
+ * of objectdata table are not referenced here. So this view is not used atm.
+ */
+ "CREATE TEMP VIEW IF NOT EXISTS '{}' AS \
+ SELECT s.ObjName, s.ObjInstance, s.ObjID from '{}' as s INNER JOIN '{}' USING \
+ (ObjName, BucketName, ObjInstance, ObjID);";
+
+
+ static constexpr std::string_view 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);";
+
+ static constexpr std::string_view CreateLCEntryTableQ =
+ "CREATE TABLE IF NOT EXISTS '{}' ( \
+ LCIndex TEXT NOT NULL , \
+ BucketName TEXT NOT NULL , \
+ StartTime INTEGER , \
+ Status INTEGER , \
+ PRIMARY KEY (LCIndex, BucketName) \n);";
+
+ static constexpr std::string_view CreateLCHeadTableQ =
+ "CREATE TABLE IF NOT EXISTS '{}' ( \
+ LCIndex TEXT NOT NULL , \
+ Marker TEXT , \
+ StartDate INTEGER , \
+ PRIMARY KEY (LCIndex) \n);";
+
+ static constexpr std::string_view DropQ = "DROP TABLE IF EXISTS '{}'";
+ static constexpr std::string_view ListAllQ = "SELECT * from '{}'";
+
+ public:
+ DBOp() {}
+ virtual ~DBOp() {}
+ std::mutex mtx; // to protect prepared stmt
+
+ static std::string CreateTableSchema(std::string_view type,
+ const DBOpParams *params) {
+ if (!type.compare("User"))
+ return fmt::format(CreateUserTableQ,
+ params->user_table);
+ if (!type.compare("Bucket"))
+ return fmt::format(CreateBucketTableQ,
+ params->bucket_table,
+ params->user_table);
+ if (!type.compare("Object"))
+ return fmt::format(CreateObjectTableQ,
+ params->object_table,
+ params->bucket_table);
+ if (!type.compare("ObjectTrigger"))
+ return fmt::format(CreateObjectTableTriggerQ,
+ params->object_trigger,
+ params->object_table,
+ params->object_table,
+ params->object_table);
+ if (!type.compare("ObjectData"))
+ return fmt::format(CreateObjectDataTableQ,
+ params->objectdata_table,
+ params->bucket_table);
+ if (!type.compare("ObjectView"))
+ return fmt::format(CreateObjectTableQ,
+ params->object_view,
+ params->objectdata_table,
+ params->object_table);
+ if (!type.compare("Quota"))
+ return fmt::format(CreateQuotaTableQ,
+ params->quota_table);
+ if (!type.compare("LCHead"))
+ return fmt::format(CreateLCHeadTableQ,
+ params->lc_head_table);
+ if (!type.compare("LCEntry"))
+ return fmt::format(CreateLCEntryTableQ,
+ params->lc_entry_table,
+ params->bucket_table);
+
+ ceph_abort_msgf("incorrect table type %.*s", type.size(), type.data());
+ }
+
+ static std::string DeleteTableSchema(std::string_view table) {
+ return fmt::format(DropQ, table);
+ }
+ static std::string ListTableSchema(std::string_view table) {
+ return fmt::format(ListAllQ, table);
+ }
+
+ virtual int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params) { return 0; }
+ virtual int Bind(const DoutPrefixProvider *dpp, DBOpParams *params) { return 0; }
+ virtual int Execute(const DoutPrefixProvider *dpp, DBOpParams *params) { return 0; }
+};
+
+class InsertUserOp : virtual 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.
+ */
+ static constexpr std::string_view 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, \
+ UserAttrs, UserVersion, UserVersionTag) \
+ VALUES ({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, \
+ {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {});";
+
+ public:
+ virtual ~InsertUserOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query, params.user_table,
+ params.op.user.user_id, 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.user_attrs, params.op.user.user_ver,
+ params.op.user.user_ver_tag);
+ }
+
+};
+
+class RemoveUserOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query =
+ "DELETE from '{}' where UserID = {}";
+
+ public:
+ virtual ~RemoveUserOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query, params.user_table,
+ params.op.user.user_id);
+ }
+};
+
+class GetUserOp: virtual public DBOp {
+ private:
+ /* If below query columns are updated, make sure to update the indexes
+ * in list_user() cbk in sqliteDB.cc */
+ static constexpr std::string_view 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 = {}";
+
+ static constexpr std::string_view 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 = {}";
+
+ static constexpr std::string_view 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 = {}";
+
+ static constexpr std::string_view 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 UserID = {}";
+
+ public:
+ virtual ~GetUserOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ if (params.op.query_str == "email") {
+ return fmt::format(QueryByEmail, params.user_table,
+ params.op.user.user_email);
+ } else if (params.op.query_str == "access_key") {
+ return fmt::format(QueryByAccessKeys,
+ params.user_table,
+ params.op.user.access_keys_id);
+ } else if (params.op.query_str == "user_id") {
+ return fmt::format(QueryByUserID,
+ params.user_table,
+ params.op.user.user_id);
+ } else {
+ return fmt::format(Query, params.user_table,
+ params.op.user.user_id);
+ }
+ }
+};
+
+class InsertBucketOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view 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() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query, params.bucket_table,
+ 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: virtual public DBOp {
+ private:
+ // Updates Info, Mtime, Version
+ static constexpr std::string_view 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
+ static constexpr std::string_view AttrsQuery =
+ "UPDATE '{}' SET OwnerID = {}, BucketAttrs = {}, Mtime = {}, BucketVersion = {} \
+ WHERE BucketName = {}";
+ // Updates OwnerID, CreationTime, Mtime, Version
+ static constexpr std::string_view OwnerQuery =
+ "UPDATE '{}' SET OwnerID = {}, CreationTime = {}, Mtime = {}, BucketVersion = {} WHERE BucketName = {}";
+
+ public:
+ virtual ~UpdateBucketOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ if (params.op.query_str == "info") {
+ return fmt::format(InfoQuery, params.bucket_table,
+ 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, params.bucket_table,
+ params.op.user.user_id, params.op.bucket.bucket_attrs,
+ params.op.bucket.mtime,
+ params.op.bucket.bucket_ver, params.op.bucket.bucket_name);
+ }
+ if (params.op.query_str == "owner") {
+ return fmt::format(OwnerQuery, params.bucket_table,
+ params.op.user.user_id, params.op.bucket.creation_time,
+ params.op.bucket.mtime,
+ params.op.bucket.bucket_ver, params.op.bucket.bucket_name);
+ }
+ return "";
+ }
+};
+
+class RemoveBucketOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query =
+ "DELETE from '{}' where BucketName = {}";
+
+ public:
+ virtual ~RemoveBucketOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query, params.bucket_table,
+ params.op.bucket.bucket_name);
+ }
+};
+
+class GetBucketOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view 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() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ //return fmt::format(Query, params.op.bucket.bucket_name,
+ // params.bucket_table, params.user_table);
+ return fmt::format(Query,
+ params.bucket_table, params.user_table,
+ params.op.bucket.bucket_name);
+ }
+};
+
+class ListUserBucketsOp: virtual public DBOp {
+ private:
+ // once we have stats also stored, may have to update this query to join
+ // these two tables.
+ static constexpr std::string_view 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 {}";
+
+ /* BucketNames are unique across users. Hence userid/OwnerID is not used as
+ * marker or for ordering here in the below query
+ */
+ static constexpr std::string_view AllQuery = "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 BucketName > {} ORDER BY BucketName ASC LIMIT {}";
+
+ public:
+ virtual ~ListUserBucketsOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ if (params.op.query_str == "all") {
+ return fmt::format(AllQuery, params.bucket_table,
+ params.op.bucket.min_marker,
+ params.op.list_max_count);
+ } else {
+ return fmt::format(Query, params.bucket_table,
+ params.op.user.user_id, params.op.bucket.min_marker,
+ params.op.list_max_count);
+ }
+ }
+};
+
+class PutObjectOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query =
+ "INSERT OR REPLACE INTO '{}' \
+ (ObjName, ObjInstance, ObjNS, BucketName, ACLs, IndexVer, Tag, \
+ Flags, VersionedEpoch, ObjCategory, Etag, Owner, OwnerDisplayName, \
+ StorageClass, Appendable, ContentType, IndexHashSource, ObjSize, \
+ AccountedSize, Mtime, Epoch, ObjTag, TailTag, WriteTag, FakeTag, \
+ ShadowObj, HasData, IsVersioned, VersionNum, PGVer, ZoneShortID, \
+ ObjVersion, ObjVersionTag, ObjAttrs, HeadSize, MaxHeadSize, \
+ ObjID, TailInstance, HeadPlacementRuleName, HeadPlacementRuleStorageClass, \
+ TailPlacementRuleName, TailPlacementStorageClass, \
+ ManifestPartObjs, ManifestPartRules, Omap, IsMultipart, MPPartsList, \
+ HeadData) \
+ VALUES ({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, \
+ {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, \
+ {}, {}, {}, \
+ {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})";
+
+ public:
+ virtual ~PutObjectOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query,
+ params.object_table, params.op.obj.obj_name,
+ params.op.obj.obj_instance, params.op.obj.obj_ns,
+ params.op.bucket.bucket_name, params.op.obj.acls, params.op.obj.index_ver,
+ params.op.obj.tag, params.op.obj.flags, params.op.obj.versioned_epoch,
+ params.op.obj.obj_category, params.op.obj.etag, params.op.obj.owner,
+ params.op.obj.owner_display_name, params.op.obj.storage_class,
+ params.op.obj.appendable, params.op.obj.content_type,
+ params.op.obj.index_hash_source, params.op.obj.obj_size,
+ params.op.obj.accounted_size, params.op.obj.mtime,
+ params.op.obj.epoch, params.op.obj.obj_tag, params.op.obj.tail_tag,
+ params.op.obj.write_tag, params.op.obj.fake_tag, params.op.obj.shadow_obj,
+ params.op.obj.has_data, params.op.obj.is_versioned,
+ params.op.obj.version_num,
+ params.op.obj.pg_ver, params.op.obj.zone_short_id,
+ params.op.obj.obj_version, params.op.obj.obj_version_tag,
+ params.op.obj.obj_attrs, params.op.obj.head_size,
+ params.op.obj.max_head_size, params.op.obj.obj_id,
+ params.op.obj.tail_instance,
+ params.op.obj.head_placement_rule_name,
+ params.op.obj.head_placement_storage_class,
+ params.op.obj.tail_placement_rule_name,
+ params.op.obj.tail_placement_storage_class,
+ params.op.obj.manifest_part_objs,
+ params.op.obj.manifest_part_rules, params.op.obj.omap,
+ params.op.obj.is_multipart, params.op.obj.mp_parts,
+ params.op.obj.head_data);
+ }
+};
+
+class DeleteObjectOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query =
+ "DELETE from '{}' where BucketName = {} and ObjName = {} and ObjInstance = {}";
+
+ public:
+ virtual ~DeleteObjectOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query, params.object_table,
+ params.op.bucket.bucket_name,
+ params.op.obj.obj_name,
+ params.op.obj.obj_instance);
+ }
+};
+
+class GetObjectOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query =
+ "SELECT \
+ ObjName, ObjInstance, ObjNS, BucketName, ACLs, IndexVer, Tag, \
+ Flags, VersionedEpoch, ObjCategory, Etag, Owner, OwnerDisplayName, \
+ StorageClass, Appendable, ContentType, IndexHashSource, ObjSize, \
+ AccountedSize, Mtime, Epoch, ObjTag, TailTag, WriteTag, FakeTag, \
+ ShadowObj, HasData, IsVersioned, VersionNum, PGVer, ZoneShortID, \
+ ObjVersion, ObjVersionTag, ObjAttrs, HeadSize, MaxHeadSize, \
+ ObjID, TailInstance, HeadPlacementRuleName, HeadPlacementRuleStorageClass, \
+ TailPlacementRuleName, TailPlacementStorageClass, \
+ ManifestPartObjs, ManifestPartRules, Omap, IsMultipart, MPPartsList, \
+ HeadData from '{}' \
+ where BucketName = {} and ObjName = {} and ObjInstance = {}";
+
+ public:
+ virtual ~GetObjectOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query,
+ params.object_table,
+ params.op.bucket.bucket_name,
+ params.op.obj.obj_name,
+ params.op.obj.obj_instance);
+ }
+};
+
+class ListBucketObjectsOp: virtual public DBOp {
+ private:
+ // once we have stats also stored, may have to update this query to join
+ // these two tables.
+ static constexpr std::string_view Query =
+ "SELECT \
+ ObjName, ObjInstance, ObjNS, BucketName, ACLs, IndexVer, Tag, \
+ Flags, VersionedEpoch, ObjCategory, Etag, Owner, OwnerDisplayName, \
+ StorageClass, Appendable, ContentType, IndexHashSource, ObjSize, \
+ AccountedSize, Mtime, Epoch, ObjTag, TailTag, WriteTag, FakeTag, \
+ ShadowObj, HasData, IsVersioned, VersionNum, PGVer, ZoneShortID, \
+ ObjVersion, ObjVersionTag, ObjAttrs, HeadSize, MaxHeadSize, \
+ ObjID, TailInstance, HeadPlacementRuleName, HeadPlacementRuleStorageClass, \
+ TailPlacementRuleName, TailPlacementStorageClass, \
+ ManifestPartObjs, ManifestPartRules, Omap, IsMultipart, MPPartsList, HeadData from '{}' \
+ where BucketName = {} and ObjName >= {} and ObjName LIKE {} ORDER BY ObjName ASC, VersionNum DESC LIMIT {}";
+ public:
+ virtual ~ListBucketObjectsOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ /* XXX: Include obj_id, delim */
+ return fmt::format(Query,
+ params.object_table,
+ params.op.bucket.bucket_name,
+ params.op.obj.min_marker,
+ params.op.obj.prefix,
+ params.op.list_max_count);
+ }
+};
+
+#define MAX_VERSIONED_OBJECTS 20
+class ListVersionedObjectsOp: virtual public DBOp {
+ private:
+ // once we have stats also stored, may have to update this query to join
+ // these two tables.
+ static constexpr std::string_view Query =
+ "SELECT \
+ ObjName, ObjInstance, ObjNS, BucketName, ACLs, IndexVer, Tag, \
+ Flags, VersionedEpoch, ObjCategory, Etag, Owner, OwnerDisplayName, \
+ StorageClass, Appendable, ContentType, IndexHashSource, ObjSize, \
+ AccountedSize, Mtime, Epoch, ObjTag, TailTag, WriteTag, FakeTag, \
+ ShadowObj, HasData, IsVersioned, VersionNum, PGVer, ZoneShortID, \
+ ObjVersion, ObjVersionTag, ObjAttrs, HeadSize, MaxHeadSize, \
+ ObjID, TailInstance, HeadPlacementRuleName, HeadPlacementRuleStorageClass, \
+ TailPlacementRuleName, TailPlacementStorageClass, \
+ ManifestPartObjs, ManifestPartRules, Omap, IsMultipart, MPPartsList, \
+ HeadData from '{}' \
+ where BucketName = {} and ObjName = {} ORDER BY VersionNum DESC LIMIT {}";
+ public:
+ virtual ~ListVersionedObjectsOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ /* XXX: Include obj_id, delim */
+ return fmt::format(Query,
+ params.object_table,
+ params.op.bucket.bucket_name,
+ params.op.obj.obj_name,
+ params.op.list_max_count);
+ }
+};
+
+class UpdateObjectOp: virtual public DBOp {
+ private:
+ // Updates Omap
+ static constexpr std::string_view OmapQuery =
+ "UPDATE '{}' SET Omap = {}, Mtime = {} \
+ where BucketName = {} and ObjName = {} and ObjInstance = {}";
+ static constexpr std::string_view AttrsQuery =
+ "UPDATE '{}' SET ObjAttrs = {}, Mtime = {} \
+ where BucketName = {} and ObjName = {} and ObjInstance = {}";
+ static constexpr std::string_view MPQuery =
+ "UPDATE '{}' SET MPPartsList = {}, Mtime = {} \
+ where BucketName = {} and ObjName = {} and ObjInstance = {}";
+ static constexpr std::string_view MetaQuery =
+ "UPDATE '{}' SET \
+ ObjNS = {}, ACLs = {}, IndexVer = {}, Tag = {}, Flags = {}, VersionedEpoch = {}, \
+ ObjCategory = {}, Etag = {}, Owner = {}, OwnerDisplayName = {}, \
+ StorageClass = {}, Appendable = {}, ContentType = {}, \
+ IndexHashSource = {}, ObjSize = {}, AccountedSize = {}, Mtime = {}, \
+ Epoch = {}, ObjTag = {}, TailTag = {}, WriteTag = {}, FakeTag = {}, \
+ ShadowObj = {}, HasData = {}, IsVersioned = {}, VersionNum = {}, PGVer = {}, \
+ ZoneShortID = {}, ObjVersion = {}, ObjVersionTag = {}, ObjAttrs = {}, \
+ HeadSize = {}, MaxHeadSize = {}, ObjID = {}, TailInstance = {}, \
+ HeadPlacementRuleName = {}, HeadPlacementRuleStorageClass = {}, \
+ TailPlacementRuleName = {}, TailPlacementStorageClass = {}, \
+ ManifestPartObjs = {}, ManifestPartRules = {}, Omap = {}, \
+ IsMultipart = {}, MPPartsList = {}, HeadData = {} \
+ WHERE ObjName = {} and ObjInstance = {} and BucketName = {}";
+
+ public:
+ virtual ~UpdateObjectOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ if (params.op.query_str == "omap") {
+ return fmt::format(OmapQuery,
+ params.object_table, params.op.obj.omap,
+ params.op.obj.mtime,
+ params.op.bucket.bucket_name,
+ params.op.obj.obj_name,
+ params.op.obj.obj_instance);
+ }
+ if (params.op.query_str == "attrs") {
+ return fmt::format(AttrsQuery,
+ params.object_table, params.op.obj.obj_attrs,
+ params.op.obj.mtime,
+ params.op.bucket.bucket_name,
+ params.op.obj.obj_name,
+ params.op.obj.obj_instance);
+ }
+ if (params.op.query_str == "mp") {
+ return fmt::format(MPQuery,
+ params.object_table, params.op.obj.mp_parts,
+ params.op.obj.mtime,
+ params.op.bucket.bucket_name,
+ params.op.obj.obj_name,
+ params.op.obj.obj_instance);
+ }
+ if (params.op.query_str == "meta") {
+ return fmt::format(MetaQuery,
+ params.object_table,
+ params.op.obj.obj_ns, params.op.obj.acls, params.op.obj.index_ver,
+ params.op.obj.tag, params.op.obj.flags, params.op.obj.versioned_epoch,
+ params.op.obj.obj_category, params.op.obj.etag, params.op.obj.owner,
+ params.op.obj.owner_display_name, params.op.obj.storage_class,
+ params.op.obj.appendable, params.op.obj.content_type,
+ params.op.obj.index_hash_source, params.op.obj.obj_size,
+ params.op.obj.accounted_size, params.op.obj.mtime,
+ params.op.obj.epoch, params.op.obj.obj_tag, params.op.obj.tail_tag,
+ params.op.obj.write_tag, params.op.obj.fake_tag, params.op.obj.shadow_obj,
+ params.op.obj.has_data, params.op.obj.is_versioned, params.op.obj.version_num,
+ params.op.obj.pg_ver, params.op.obj.zone_short_id,
+ params.op.obj.obj_version, params.op.obj.obj_version_tag,
+ params.op.obj.obj_attrs, params.op.obj.head_size,
+ params.op.obj.max_head_size, params.op.obj.obj_id,
+ params.op.obj.tail_instance,
+ params.op.obj.head_placement_rule_name,
+ params.op.obj.head_placement_storage_class,
+ params.op.obj.tail_placement_rule_name,
+ params.op.obj.tail_placement_storage_class,
+ params.op.obj.manifest_part_objs,
+ params.op.obj.manifest_part_rules, params.op.obj.omap,
+ params.op.obj.is_multipart, params.op.obj.mp_parts,
+ params.op.obj.head_data,
+ params.op.obj.obj_name, params.op.obj.obj_instance,
+ params.op.bucket.bucket_name);
+ }
+ return "";
+ }
+};
+
+class PutObjectDataOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query =
+ "INSERT OR REPLACE INTO '{}' \
+ (ObjName, ObjInstance, ObjNS, BucketName, ObjID, MultipartPartStr, PartNum, Offset, Size, Mtime, Data) \
+ VALUES ({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})";
+
+ public:
+ virtual ~PutObjectDataOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query,
+ params.objectdata_table,
+ params.op.obj.obj_name, params.op.obj.obj_instance,
+ params.op.obj.obj_ns,
+ params.op.bucket.bucket_name,
+ params.op.obj.obj_id,
+ params.op.obj_data.multipart_part_str,
+ params.op.obj_data.part_num,
+ params.op.obj_data.offset,
+ params.op.obj_data.size,
+ params.op.obj.mtime,
+ params.op.obj_data.data);
+ }
+};
+
+/* XXX: Recheck if this is really needed */
+class UpdateObjectDataOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query =
+ "UPDATE '{}' \
+ SET Mtime = {} WHERE ObjName = {} and ObjInstance = {} and \
+ BucketName = {} and ObjID = {}";
+
+ public:
+ virtual ~UpdateObjectDataOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query,
+ params.objectdata_table,
+ params.op.obj.mtime,
+ params.op.obj.obj_name, params.op.obj.obj_instance,
+ params.op.bucket.bucket_name,
+ params.op.obj.obj_id);
+ }
+};
+
+class GetObjectDataOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query =
+ "SELECT \
+ ObjName, ObjInstance, ObjNS, BucketName, ObjID, MultipartPartStr, PartNum, Offset, Size, Mtime, Data \
+ from '{}' where BucketName = {} and ObjName = {} and ObjInstance = {} and ObjID = {} ORDER BY MultipartPartStr, PartNum";
+
+ public:
+ virtual ~GetObjectDataOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query,
+ params.objectdata_table,
+ params.op.bucket.bucket_name,
+ params.op.obj.obj_name,
+ params.op.obj.obj_instance,
+ params.op.obj.obj_id);
+ }
+};
+
+class DeleteObjectDataOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query =
+ "DELETE from '{}' where BucketName = {} and ObjName = {} and ObjInstance = {} and ObjID = {}";
+
+ public:
+ virtual ~DeleteObjectDataOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query,
+ params.objectdata_table,
+ params.op.bucket.bucket_name,
+ params.op.obj.obj_name,
+ params.op.obj.obj_instance,
+ params.op.obj.obj_id);
+ }
+};
+
+class DeleteStaleObjectDataOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query =
+ "DELETE from '{}' WHERE (ObjName, ObjInstance, ObjID) NOT IN (SELECT s.ObjName, s.ObjInstance, s.ObjID from '{}' as s INNER JOIN '{}' USING (ObjName, BucketName, ObjInstance, ObjID)) and Mtime < {}";
+
+ public:
+ virtual ~DeleteStaleObjectDataOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query,
+ params.objectdata_table,
+ params.objectdata_table,
+ params.object_table,
+ params.op.obj.mtime);
+ }
+};
+
+class InsertLCEntryOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query =
+ "INSERT OR REPLACE INTO '{}' \
+ (LCIndex, BucketName, StartTime, Status) \
+ VALUES ({}, {}, {}, {})";
+
+ public:
+ virtual ~InsertLCEntryOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query, params.lc_entry_table,
+ params.op.lc_entry.index, params.op.lc_entry.bucket_name,
+ params.op.lc_entry.start_time, params.op.lc_entry.status);
+ }
+};
+
+class RemoveLCEntryOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query =
+ "DELETE from '{}' where LCIndex = {} and BucketName = {}";
+
+ public:
+ virtual ~RemoveLCEntryOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query, params.lc_entry_table,
+ params.op.lc_entry.index, params.op.lc_entry.bucket_name);
+ }
+};
+
+class GetLCEntryOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query = "SELECT \
+ LCIndex, BucketName, StartTime, Status \
+ from '{}' where LCIndex = {} and BucketName = {}";
+ static constexpr std::string_view NextQuery = "SELECT \
+ LCIndex, BucketName, StartTime, Status \
+ from '{}' where LCIndex = {} and BucketName > {} ORDER BY BucketName ASC";
+
+ public:
+ virtual ~GetLCEntryOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ if (params.op.query_str == "get_next_entry") {
+ return fmt::format(NextQuery, params.lc_entry_table,
+ params.op.lc_entry.index, params.op.lc_entry.bucket_name);
+ }
+ // default
+ return fmt::format(Query, params.lc_entry_table,
+ params.op.lc_entry.index, params.op.lc_entry.bucket_name);
+ }
+};
+
+class ListLCEntriesOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query = "SELECT \
+ LCIndex, BucketName, StartTime, Status \
+ FROM '{}' WHERE LCIndex = {} AND BucketName > {} ORDER BY BucketName ASC LIMIT {}";
+
+ public:
+ virtual ~ListLCEntriesOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query, params.lc_entry_table,
+ params.op.lc_entry.index, params.op.lc_entry.min_marker,
+ params.op.list_max_count);
+ }
+};
+
+class InsertLCHeadOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query =
+ "INSERT OR REPLACE INTO '{}' \
+ (LCIndex, Marker, StartDate) \
+ VALUES ({}, {}, {})";
+
+ public:
+ virtual ~InsertLCHeadOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query, params.lc_head_table,
+ params.op.lc_head.index, params.op.lc_head.marker,
+ params.op.lc_head.start_date);
+ }
+};
+
+class RemoveLCHeadOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query =
+ "DELETE from '{}' where LCIndex = {}";
+
+ public:
+ virtual ~RemoveLCHeadOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query, params.lc_head_table,
+ params.op.lc_head.index);
+ }
+};
+
+class GetLCHeadOp: virtual public DBOp {
+ private:
+ static constexpr std::string_view Query = "SELECT \
+ LCIndex, Marker, StartDate \
+ from '{}' where LCIndex = {}";
+
+ public:
+ virtual ~GetLCHeadOp() {}
+
+ static std::string Schema(DBOpPrepareParams &params) {
+ return fmt::format(Query, params.lc_head_table,
+ params.op.lc_head.index);
+ }
+};
+
+/* taken from rgw_rados.h::RGWOLHInfo */
+struct DBOLHInfo {
+ rgw_obj target;
+ bool removed;
+ DBOLHInfo() : removed(false) {}
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(target, bl);
+ encode(removed, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(target, bl);
+ decode(removed, bl);
+ DECODE_FINISH(bl);
+ }
+};
+WRITE_CLASS_ENCODER(DBOLHInfo)
+
+class DB {
+ private:
+ const std::string db_name;
+ rgw::sal::Driver* driver;
+ const std::string user_table;
+ const std::string bucket_table;
+ const std::string quota_table;
+ const std::string lc_head_table;
+ const std::string lc_entry_table;
+ static std::map<std::string, class ObjectOp*> objectmap;
+
+ protected:
+ void *db;
+ CephContext *cct;
+ const DoutPrefix dp;
+ uint64_t max_bucket_id = 0;
+ // XXX: default ObjStripeSize or ObjChunk size - 4M, make them configurable?
+ uint64_t ObjHeadSize = 1024; /* 1K - default head data size */
+ uint64_t ObjChunkSize = (get_blob_limit() - 1000); /* 1000 to accommodate other fields */
+ // Below mutex is to protect objectmap and other shared
+ // objects if any.
+ std::mutex mtx;
+
+ public:
+ DB(std::string db_name, CephContext *_cct) : db_name(db_name),
+ user_table(db_name+"_user_table"),
+ bucket_table(db_name+"_bucket_table"),
+ quota_table(db_name+"_quota_table"),
+ lc_head_table(db_name+"_lc_head_table"),
+ lc_entry_table(db_name+"_lc_entry_table"),
+ cct(_cct),
+ dp(_cct, ceph_subsys_rgw, "rgw DBStore backend: ")
+ {}
+ /* DB() {}*/
+
+ DB(CephContext *_cct) : db_name("default_db"),
+ user_table(db_name+"_user_table"),
+ bucket_table(db_name+"_bucket_table"),
+ quota_table(db_name+"_quota_table"),
+ lc_head_table(db_name+"_lc_head_table"),
+ lc_entry_table(db_name+"_lc_entry_table"),
+ cct(_cct),
+ dp(_cct, ceph_subsys_rgw, "rgw DBStore backend: ")
+ {}
+ virtual ~DB() {}
+
+ const std::string getDBname() { return db_name; }
+ const std::string getDBfile() { return db_name + ".db"; }
+ const std::string getUserTable() { return user_table; }
+ const std::string getBucketTable() { return bucket_table; }
+ const std::string getQuotaTable() { return quota_table; }
+ const std::string getLCHeadTable() { return lc_head_table; }
+ const std::string getLCEntryTable() { return lc_entry_table; }
+ const std::string getObjectTable(std::string bucket) {
+ return db_name+"_"+bucket+"_object_table"; }
+ const std::string getObjectDataTable(std::string bucket) {
+ return db_name+"_"+bucket+"_objectdata_table"; }
+ const std::string getObjectView(std::string bucket) {
+ return db_name+"_"+bucket+"_object_view"; }
+ const std::string getObjectTrigger(std::string bucket) {
+ return db_name+"_"+bucket+"_object_trigger"; }
+
+ std::map<std::string, class ObjectOp*> getObjectMap();
+
+ struct DBOps dbops; // DB operations, make it private?
+
+ void set_driver(rgw::sal::Driver* _driver) {
+ driver = _driver;
+ }
+
+ void set_context(CephContext *_cct) {
+ cct = _cct;
+ }
+
+ CephContext *ctx() { return cct; }
+ const DoutPrefixProvider *get_def_dpp() { return &dp; }
+
+ int Initialize(std::string logfile, int loglevel);
+ int Destroy(const DoutPrefixProvider *dpp);
+ int LockInit(const DoutPrefixProvider *dpp);
+ int LockDestroy(const DoutPrefixProvider *dpp);
+ int Lock(const DoutPrefixProvider *dpp);
+ int Unlock(const DoutPrefixProvider *dpp);
+
+ int InitializeParams(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int ProcessOp(const DoutPrefixProvider *dpp, std::string_view Op, DBOpParams *params);
+ std::shared_ptr<class DBOp> getDBOp(const DoutPrefixProvider *dpp, std::string_view Op, const DBOpParams *params);
+ int objectmapInsert(const DoutPrefixProvider *dpp, std::string bucket, class ObjectOp* ptr);
+ int objectmapDelete(const DoutPrefixProvider *dpp, std::string bucket);
+
+ virtual uint64_t get_blob_limit() { return 0; };
+ virtual void *openDB(const DoutPrefixProvider *dpp) { return NULL; }
+ virtual int closeDB(const DoutPrefixProvider *dpp) { return 0; }
+ virtual int createTables(const DoutPrefixProvider *dpp) { return 0; }
+ virtual int InitializeDBOps(const DoutPrefixProvider *dpp) { return 0; }
+ virtual int InitPrepareParams(const DoutPrefixProvider *dpp,
+ DBOpPrepareParams &p_params,
+ DBOpParams* params) = 0;
+ virtual int createLCTables(const DoutPrefixProvider *dpp) = 0;
+
+ virtual int ListAllBuckets(const DoutPrefixProvider *dpp, DBOpParams *params) = 0;
+ virtual int ListAllUsers(const DoutPrefixProvider *dpp, DBOpParams *params) = 0;
+ virtual int ListAllObjects(const DoutPrefixProvider *dpp, DBOpParams *params) = 0;
+
+ int get_user(const DoutPrefixProvider *dpp,
+ const std::string& query_str, const std::string& query_str_val,
+ RGWUserInfo& uinfo, std::map<std::string, bufferlist> *pattrs,
+ RGWObjVersionTracker *pobjv_tracker);
+ int store_user(const DoutPrefixProvider *dpp,
+ RGWUserInfo& uinfo, bool exclusive, std::map<std::string, bufferlist> *pattrs,
+ RGWObjVersionTracker *pobjv_tracker, RGWUserInfo* pold_info);
+ int remove_user(const DoutPrefixProvider *dpp,
+ RGWUserInfo& uinfo, RGWObjVersionTracker *pobjv_tracker);
+ int get_bucket_info(const DoutPrefixProvider *dpp, 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 DoutPrefixProvider *dpp,
+ const RGWUserInfo& owner, rgw_bucket& bucket,
+ const std::string& zonegroup_id,
+ const rgw_placement_rule& placement_rule,
+ const std::string& swift_ver_location,
+ const RGWQuotaInfo * pquota_info,
+ std::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,
+ bool exclusive);
+
+ int next_bucket_id() { return ++max_bucket_id; };
+
+ int remove_bucket(const DoutPrefixProvider *dpp, const RGWBucketInfo info);
+ int list_buckets(const DoutPrefixProvider *dpp, const std::string& query_str,
+ rgw_user& user,
+ const std::string& marker,
+ const std::string& end_marker,
+ uint64_t max,
+ bool need_stats,
+ RGWUserBuckets *buckets,
+ bool *is_truncated);
+ int update_bucket(const DoutPrefixProvider *dpp, const std::string& query_str,
+ RGWBucketInfo& info, bool exclusive,
+ const rgw_user* powner_id, std::map<std::string, bufferlist>* pattrs,
+ ceph::real_time* pmtime, RGWObjVersionTracker* pobjv);
+
+ uint64_t get_max_head_size() { return ObjHeadSize; }
+ uint64_t get_max_chunk_size() { return ObjChunkSize; }
+ void gen_rand_obj_instance_name(rgw_obj_key *target_key);
+
+ // db raw obj string is of format -
+ // "<bucketname>_<objname>_<objinstance>_<multipart-part-str>_<partnum>"
+ static constexpr std::string_view raw_obj_oid = "{0}_{1}_{2}_{3}_{4}";
+
+ std::string to_oid(std::string_view bucket, std::string_view obj_name,
+ std::string_view obj_instance, std::string_view obj_id,
+ std::string_view mp_str, uint64_t partnum) {
+ return fmt::format(raw_obj_oid, bucket, obj_name, obj_instance, obj_id, mp_str, partnum);
+ }
+ int from_oid(const std::string& oid, std::string& bucket, std::string& obj_name, std::string& obj_id,
+ std::string& obj_instance,
+ std::string& mp_str, uint64_t& partnum) {
+ // TODO: use ceph::split() from common/split.h
+ // XXX: doesn't this break if obj_name has underscores in it?
+ std::vector<std::string> result;
+ boost::split(result, oid, boost::is_any_of("_"));
+ bucket = result[0];
+ obj_name = result[1];
+ obj_instance = result[2];
+ obj_id = result[3];
+ mp_str = result[4];
+ partnum = stoi(result[5]);
+
+ return 0;
+ }
+
+ struct raw_obj {
+ DB* db;
+
+ std::string bucket_name;
+ std::string obj_name;
+ std::string obj_instance;
+ std::string obj_ns;
+ std::string obj_id;
+ std::string multipart_part_str;
+ uint64_t part_num;
+
+ std::string obj_table;
+ std::string obj_data_table;
+
+ raw_obj(DB* _db) {
+ db = _db;
+ }
+
+ raw_obj(DB* _db, std::string& _bname, std::string& _obj_name, std::string& _obj_instance,
+ std::string& _obj_ns, std::string& _obj_id, std::string _mp_part_str, int _part_num) {
+ db = _db;
+ bucket_name = _bname;
+ obj_name = _obj_name;
+ obj_instance = _obj_instance;
+ obj_ns = _obj_ns;
+ obj_id = _obj_id;
+ multipart_part_str = _mp_part_str;
+ part_num = _part_num;
+
+ obj_table = bucket_name+".object.table";
+ obj_data_table = bucket_name+".objectdata.table";
+ }
+
+ raw_obj(DB* _db, std::string& oid) {
+ int r;
+
+ db = _db;
+ r = db->from_oid(oid, bucket_name, obj_name, obj_instance, obj_id, multipart_part_str,
+ part_num);
+ if (r < 0) {
+ multipart_part_str = "0.0";
+ part_num = 0;
+ }
+
+ obj_table = db->getObjectTable(bucket_name);
+ obj_data_table = db->getObjectDataTable(bucket_name);
+ }
+
+ int InitializeParamsfromRawObj (const DoutPrefixProvider *dpp, DBOpParams* params);
+
+ int read(const DoutPrefixProvider *dpp, int64_t ofs, uint64_t end, bufferlist& bl);
+ int write(const DoutPrefixProvider *dpp, int64_t ofs, int64_t write_ofs, uint64_t len, bufferlist& bl);
+ };
+
+ class GC : public Thread {
+ const DoutPrefixProvider *dpp;
+ DB *db;
+ /* Default time interval for GC
+ * XXX: Make below options configurable
+ *
+ * gc_interval: The time between successive gc thread runs
+ * gc_obj_min_wait: Min. time to wait before deleting any data post its creation.
+ *
+ */
+ std::mutex mtx;
+ std::condition_variable cv;
+ bool stop_signalled = false;
+ uint32_t gc_interval = 24*60*60; //sec ; default: 24*60*60
+ uint32_t gc_obj_min_wait = 60*60; //60*60sec default
+ std::string bucket_marker;
+ std::string user_marker;
+
+ public:
+ GC(const DoutPrefixProvider *_dpp, DB* _db) :
+ dpp(_dpp), db(_db) {}
+
+ void *entry() override;
+
+ void signal_stop() {
+ std::lock_guard<std::mutex> lk_guard(mtx);
+ stop_signalled = true;
+ cv.notify_one();
+ }
+
+ friend class DB;
+ };
+ std::unique_ptr<DB::GC> gc_worker;
+
+ class Bucket {
+ friend class DB;
+ DB* store;
+
+ RGWBucketInfo bucket_info;
+
+ public:
+ Bucket(DB *_store, const RGWBucketInfo& _binfo) : store(_store), bucket_info(_binfo) {}
+ DB *get_store() { return store; }
+ rgw_bucket& get_bucket() { return bucket_info.bucket; }
+ RGWBucketInfo& get_bucket_info() { return bucket_info; }
+
+ class List {
+ protected:
+ // absolute maximum number of objects that
+ // list_objects_(un)ordered can return
+ static constexpr int64_t bucket_list_objects_absolute_max = 25000;
+
+ DB::Bucket *target;
+ rgw_obj_key next_marker;
+
+ public:
+
+ struct Params {
+ std::string prefix;
+ std::string delim;
+ rgw_obj_key marker;
+ rgw_obj_key end_marker;
+ std::string ns;
+ bool enforce_ns;
+ RGWAccessListFilter* access_list_filter;
+ RGWBucketListNameFilter force_check_filter;
+ bool list_versions;
+ bool allow_unordered;
+
+ Params() :
+ enforce_ns(true),
+ access_list_filter(nullptr),
+ list_versions(false),
+ allow_unordered(false)
+ {}
+ } params;
+
+ explicit List(DB::Bucket *_target) : target(_target) {}
+
+ /* XXX: Handle ordered and unordered separately.
+ * For now returning only ordered entries */
+ int list_objects(const DoutPrefixProvider *dpp, int64_t max,
+ std::vector<rgw_bucket_dir_entry> *result,
+ std::map<std::string, bool> *common_prefixes, bool *is_truncated);
+ rgw_obj_key& get_next_marker() {
+ return next_marker;
+ }
+ };
+ };
+
+ class Object {
+ friend class DB;
+ DB* store;
+
+ RGWBucketInfo bucket_info;
+ rgw_obj obj;
+
+ RGWObjState obj_state;
+ std::string obj_id;
+
+ bool versioning_disabled;
+
+ bool bs_initialized;
+
+ public:
+ Object(DB *_store, const RGWBucketInfo& _bucket_info, const rgw_obj& _obj) : store(_store), bucket_info(_bucket_info),
+ obj(_obj),
+ versioning_disabled(false),
+ bs_initialized(false) {}
+
+ Object(DB *_store, const RGWBucketInfo& _bucket_info, const rgw_obj& _obj, const std::string& _obj_id) : store(_store), bucket_info(_bucket_info), obj(_obj), obj_id(_obj_id) {}
+
+ struct Read {
+ DB::Object *source;
+
+ struct GetObjState {
+ rgw_obj obj;
+ } state;
+
+ struct ConditionParams {
+ const ceph::real_time *mod_ptr;
+ const ceph::real_time *unmod_ptr;
+ bool high_precision_time;
+ uint32_t mod_zone_id;
+ uint64_t mod_pg_ver;
+ const char *if_match;
+ const char *if_nomatch;
+
+ ConditionParams() :
+ mod_ptr(NULL), unmod_ptr(NULL), high_precision_time(false), mod_zone_id(0), mod_pg_ver(0),
+ if_match(NULL), if_nomatch(NULL) {}
+ } conds;
+
+ struct Params {
+ ceph::real_time *lastmod;
+ uint64_t *obj_size;
+ std::map<std::string, bufferlist> *attrs;
+ rgw_obj *target_obj;
+
+ Params() : lastmod(nullptr), obj_size(nullptr), attrs(nullptr),
+ target_obj(nullptr) {}
+ } params;
+
+ explicit Read(DB::Object *_source) : source(_source) {}
+
+ int prepare(const DoutPrefixProvider *dpp);
+ static int range_to_ofs(uint64_t obj_size, int64_t &ofs, int64_t &end);
+ int read(int64_t ofs, int64_t end, bufferlist& bl, const DoutPrefixProvider *dpp);
+ int iterate(const DoutPrefixProvider *dpp, int64_t ofs, int64_t end, RGWGetDataCB *cb);
+ int get_attr(const DoutPrefixProvider *dpp, const char *name, bufferlist& dest);
+ };
+
+ struct Write {
+ DB::Object *target;
+ RGWObjState obj_state;
+ std::string mp_part_str = "0.0"; // multipart num
+
+ struct MetaParams {
+ ceph::real_time *mtime;
+ std::map<std::string, bufferlist>* rmattrs;
+ const bufferlist *data;
+ RGWObjManifest *manifest;
+ const std::string *ptag;
+ std::list<rgw_obj_index_key> *remove_objs;
+ ceph::real_time set_mtime;
+ rgw_user owner;
+ RGWObjCategory category;
+ int flags;
+ const char *if_match;
+ const char *if_nomatch;
+ std::optional<uint64_t> olh_epoch;
+ ceph::real_time delete_at;
+ bool canceled;
+ const std::string *user_data;
+ rgw_zone_set *zones_trace;
+ bool modify_tail;
+ bool completeMultipart;
+ bool appendable;
+
+ MetaParams() : mtime(NULL), rmattrs(NULL), data(NULL), manifest(NULL), ptag(NULL),
+ remove_objs(NULL), category(RGWObjCategory::Main), flags(0),
+ if_match(NULL), if_nomatch(NULL), canceled(false), user_data(nullptr), zones_trace(nullptr),
+ modify_tail(false), completeMultipart(false), appendable(false) {}
+ } meta;
+
+ explicit Write(DB::Object *_target) : target(_target) {}
+
+ void set_mp_part_str(std::string _mp_part_str) { mp_part_str = _mp_part_str;}
+ int prepare(const DoutPrefixProvider* dpp);
+ int write_data(const DoutPrefixProvider* dpp,
+ bufferlist& data, uint64_t ofs);
+ int _do_write_meta(const DoutPrefixProvider *dpp,
+ uint64_t size, uint64_t accounted_size,
+ std::map<std::string, bufferlist>& attrs,
+ bool assume_noent, bool modify_tail);
+ int write_meta(const DoutPrefixProvider *dpp, uint64_t size,
+ uint64_t accounted_size, std::map<std::string, bufferlist>& attrs);
+ };
+
+ struct Delete {
+ DB::Object *target;
+
+ struct DeleteParams {
+ rgw_user bucket_owner;
+ int versioning_status;
+ ACLOwner obj_owner; /* needed for creation of deletion marker */
+ uint64_t olh_epoch;
+ std::string marker_version_id;
+ uint32_t bilog_flags;
+ std::list<rgw_obj_index_key> *remove_objs;
+ ceph::real_time expiration_time;
+ ceph::real_time unmod_since;
+ ceph::real_time mtime; /* for setting delete marker mtime */
+ bool high_precision_time;
+ rgw_zone_set *zones_trace;
+ bool abortmp;
+ uint64_t parts_accounted_size;
+
+ DeleteParams() : versioning_status(0), olh_epoch(0), bilog_flags(0), remove_objs(NULL), high_precision_time(false), zones_trace(nullptr), abortmp(false), parts_accounted_size(0) {}
+ } params;
+
+ struct DeleteResult {
+ bool delete_marker;
+ std::string version_id;
+
+ DeleteResult() : delete_marker(false) {}
+ } result;
+
+ explicit Delete(DB::Object *_target) : target(_target) {}
+
+ int delete_obj(const DoutPrefixProvider *dpp);
+ int delete_obj_impl(const DoutPrefixProvider *dpp, DBOpParams& del_params);
+ int create_dm(const DoutPrefixProvider *dpp, DBOpParams& del_params);
+ };
+
+ /* XXX: the parameters may be subject to change. All we need is bucket name
+ * & obj name,instance - keys */
+ int get_object_impl(const DoutPrefixProvider *dpp, DBOpParams& params);
+ int get_obj_state(const DoutPrefixProvider *dpp, const RGWBucketInfo& bucket_info,
+ const rgw_obj& obj,
+ bool follow_olh, RGWObjState **state);
+ int get_state(const DoutPrefixProvider *dpp, RGWObjState **pstate, bool follow_olh);
+ int list_versioned_objects(const DoutPrefixProvider *dpp,
+ std::list<rgw_bucket_dir_entry>& list_entries);
+
+ DB *get_store() { return store; }
+ rgw_obj& get_obj() { return obj; }
+ RGWBucketInfo& get_bucket_info() { return bucket_info; }
+
+ int InitializeParamsfromObject(const DoutPrefixProvider *dpp, DBOpParams* params);
+ int set_attrs(const DoutPrefixProvider *dpp, std::map<std::string, bufferlist>& setattrs,
+ std::map<std::string, bufferlist>* rmattrs);
+ int transition(const DoutPrefixProvider *dpp,
+ const rgw_placement_rule& rule, const real_time& mtime,
+ uint64_t olh_epoch);
+ int obj_omap_set_val_by_key(const DoutPrefixProvider *dpp, const std::string& key, bufferlist& val, bool must_exist);
+ int obj_omap_get_vals_by_keys(const DoutPrefixProvider *dpp, const std::string& oid,
+ const std::set<std::string>& keys,
+ std::map<std::string, bufferlist>* vals);
+ int obj_omap_get_all(const DoutPrefixProvider *dpp, std::map<std::string, bufferlist> *m);
+ int obj_omap_get_vals(const DoutPrefixProvider *dpp, const std::string& marker, uint64_t count,
+ std::map<std::string, bufferlist> *m, bool* pmore);
+ using iterate_obj_cb = int (*)(const DoutPrefixProvider*, const raw_obj&, off_t, off_t,
+ bool, RGWObjState*, void*);
+ int add_mp_part(const DoutPrefixProvider *dpp, RGWUploadPartInfo info);
+ int get_mp_parts_list(const DoutPrefixProvider *dpp, std::list<RGWUploadPartInfo>& info);
+
+ int iterate_obj(const DoutPrefixProvider *dpp,
+ const RGWBucketInfo& bucket_info, const rgw_obj& obj,
+ off_t ofs, off_t end, uint64_t max_chunk_size,
+ iterate_obj_cb cb, void *arg);
+ };
+ int get_obj_iterate_cb(const DoutPrefixProvider *dpp,
+ const raw_obj& read_obj, off_t obj_ofs,
+ off_t len, bool is_head_obj,
+ RGWObjState *astate, void *arg);
+
+ int get_entry(const std::string& oid, const std::string& marker,
+ std::unique_ptr<rgw::sal::Lifecycle::LCEntry>* entry);
+ int get_next_entry(const std::string& oid, const std::string& marker,
+ std::unique_ptr<rgw::sal::Lifecycle::LCEntry>* entry);
+ int set_entry(const std::string& oid, rgw::sal::Lifecycle::LCEntry& entry);
+ int list_entries(const std::string& oid, const std::string& marker,
+ uint32_t max_entries, std::vector<std::unique_ptr<rgw::sal::Lifecycle::LCEntry>>& entries);
+ int rm_entry(const std::string& oid, rgw::sal::Lifecycle::LCEntry& entry);
+ int get_head(const std::string& oid, std::unique_ptr<rgw::sal::Lifecycle::LCHead>* head);
+ int put_head(const std::string& oid, rgw::sal::Lifecycle::LCHead& head);
+ int delete_stale_objs(const DoutPrefixProvider *dpp, const std::string& bucket,
+ uint32_t min_wait);
+ int createGC(const DoutPrefixProvider *_dpp);
+ int stopGC();
+};
+
+struct db_get_obj_data {
+ DB* store;
+ RGWGetDataCB* client_cb = nullptr;
+ uint64_t offset; // next offset to write to client
+
+ db_get_obj_data(DB* db, RGWGetDataCB* cb, uint64_t offset) :
+ store(db), client_cb(cb), offset(offset) {}
+ ~db_get_obj_data() {}
+};
+
+} } // namespace rgw::store
diff --git a/src/rgw/driver/dbstore/common/dbstore_log.h b/src/rgw/driver/dbstore/common/dbstore_log.h
new file mode 100644
index 000000000..416508369
--- /dev/null
+++ b/src/rgw/driver/dbstore/common/dbstore_log.h
@@ -0,0 +1,15 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <cerrno>
+#include <cstdlib>
+#include <string>
+#include <cstdio>
+#include <iostream>
+#include <fstream>
+#include "common/dout.h"
+
+#undef dout_prefix
+#define dout_prefix *_dout << "rgw dbstore: "
diff --git a/src/rgw/driver/dbstore/config/sqlite.cc b/src/rgw/driver/dbstore/config/sqlite.cc
new file mode 100644
index 000000000..a1b217735
--- /dev/null
+++ b/src/rgw/driver/dbstore/config/sqlite.cc
@@ -0,0 +1,2070 @@
+// -*- 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) 2022 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 <charconv>
+#include <initializer_list>
+#include <map>
+
+#include <fmt/format.h>
+
+#include <sqlite3.h>
+
+#include "include/buffer.h"
+#include "include/encoding.h"
+#include "common/dout.h"
+#include "common/random_string.h"
+#include "rgw_zone.h"
+
+#include "common/connection_pool.h"
+#include "sqlite/connection.h"
+#include "sqlite/error.h"
+#include "sqlite/statement.h"
+#include "sqlite_schema.h"
+#include "sqlite.h"
+
+#define dout_subsys ceph_subsys_rgw_dbstore
+
+namespace rgw::dbstore::config {
+
+struct Prefix : DoutPrefixPipe {
+ std::string_view prefix;
+ Prefix(const DoutPrefixProvider& dpp, std::string_view prefix)
+ : DoutPrefixPipe(dpp), prefix(prefix) {}
+ unsigned get_subsys() const override { return dout_subsys; }
+ void add_prefix(std::ostream& out) const override {
+ out << prefix;
+ }
+};
+
+namespace {
+
+// parameter names for prepared statement bindings
+static constexpr const char* P1 = ":1";
+static constexpr const char* P2 = ":2";
+static constexpr const char* P3 = ":3";
+static constexpr const char* P4 = ":4";
+static constexpr const char* P5 = ":5";
+static constexpr const char* P6 = ":6";
+
+
+void read_text_rows(const DoutPrefixProvider* dpp,
+ const sqlite::stmt_execution& stmt,
+ std::span<std::string> entries,
+ sal::ListResult<std::string>& result)
+{
+ result.entries = sqlite::read_text_rows(dpp, stmt, entries);
+ if (result.entries.size() < entries.size()) { // end of listing
+ result.next.clear();
+ } else {
+ result.next = result.entries.back();
+ }
+}
+
+struct RealmRow {
+ RGWRealm info;
+ int ver;
+ std::string tag;
+};
+
+void read_realm_row(const sqlite::stmt_execution& stmt, RealmRow& row)
+{
+ row.info.id = sqlite::column_text(stmt, 0);
+ row.info.name = sqlite::column_text(stmt, 1);
+ row.info.current_period = sqlite::column_text(stmt, 2);
+ row.info.epoch = sqlite::column_int(stmt, 3);
+ row.ver = sqlite::column_int(stmt, 4);
+ row.tag = sqlite::column_text(stmt, 5);
+}
+
+void read_period_row(const sqlite::stmt_execution& stmt, RGWPeriod& row)
+{
+ // just read the Data column and decode everything else from that
+ std::string data = sqlite::column_text(stmt, 3);
+
+ bufferlist bl = bufferlist::static_from_string(data);
+ auto p = bl.cbegin();
+ decode(row, p);
+}
+
+struct ZoneGroupRow {
+ RGWZoneGroup info;
+ int ver;
+ std::string tag;
+};
+
+void read_zonegroup_row(const sqlite::stmt_execution& stmt, ZoneGroupRow& row)
+{
+ std::string data = sqlite::column_text(stmt, 3);
+ row.ver = sqlite::column_int(stmt, 4);
+ row.tag = sqlite::column_text(stmt, 5);
+
+ bufferlist bl = bufferlist::static_from_string(data);
+ auto p = bl.cbegin();
+ decode(row.info, p);
+}
+
+struct ZoneRow {
+ RGWZoneParams info;
+ int ver;
+ std::string tag;
+};
+
+void read_zone_row(const sqlite::stmt_execution& stmt, ZoneRow& row)
+{
+ std::string data = sqlite::column_text(stmt, 3);
+ row.ver = sqlite::column_int(stmt, 4);
+ row.tag = sqlite::column_text(stmt, 5);
+
+ bufferlist bl = bufferlist::static_from_string(data);
+ auto p = bl.cbegin();
+ decode(row.info, p);
+}
+
+std::string generate_version_tag(CephContext* cct)
+{
+ static constexpr auto TAG_LEN = 24;
+ return gen_rand_alphanumeric(cct, TAG_LEN);
+}
+
+using SQLiteConnectionHandle = ConnectionHandle<sqlite::Connection>;
+
+using SQLiteConnectionPool = ConnectionPool<
+ sqlite::Connection, sqlite::ConnectionFactory>;
+
+} // anonymous namespace
+
+class SQLiteImpl : public SQLiteConnectionPool {
+ public:
+ using SQLiteConnectionPool::SQLiteConnectionPool;
+};
+
+
+SQLiteConfigStore::SQLiteConfigStore(std::unique_ptr<SQLiteImpl> impl)
+ : impl(std::move(impl))
+{
+}
+
+SQLiteConfigStore::~SQLiteConfigStore() = default;
+
+
+// Realm
+
+class SQLiteRealmWriter : public sal::RealmWriter {
+ SQLiteImpl* impl;
+ int ver;
+ std::string tag;
+ std::string realm_id;
+ std::string realm_name;
+ public:
+ SQLiteRealmWriter(SQLiteImpl* impl, int ver, std::string tag,
+ std::string_view realm_id, std::string_view realm_name)
+ : impl(impl), ver(ver), tag(std::move(tag)),
+ realm_id(realm_id), realm_name(realm_name)
+ {}
+
+ int write(const DoutPrefixProvider* dpp, optional_yield y,
+ const RGWRealm& info) override
+ {
+ Prefix prefix{*dpp, "dbconfig:sqlite:realm_write "}; dpp = &prefix;
+
+ if (!impl) {
+ return -EINVAL; // can't write after a conflict or delete
+ }
+ if (realm_id != info.id || realm_name != info.name) {
+ return -EINVAL; // can't modify realm id or name directly
+ }
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["realm_upd"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::realm_update5,
+ P1, P2, P3, P4, P5);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, info.id);
+ sqlite::bind_text(dpp, binding, P2, info.current_period);
+ sqlite::bind_int(dpp, binding, P3, info.epoch);
+ sqlite::bind_int(dpp, binding, P4, ver);
+ sqlite::bind_text(dpp, binding, P5, tag);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval0(dpp, reset);
+
+ if (!::sqlite3_changes(conn->db.get())) { // VersionNumber/Tag mismatch
+ // our version is no longer consistent, so later writes would fail too
+ impl = nullptr;
+ return -ECANCELED;
+ }
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "realm update failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::foreign_key_constraint) {
+ return -EINVAL; // refers to nonexistent CurrentPeriod
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ ++ver;
+ return 0;
+ }
+
+ int rename(const DoutPrefixProvider* dpp, optional_yield y,
+ RGWRealm& info, std::string_view new_name) override
+ {
+ Prefix prefix{*dpp, "dbconfig:sqlite:realm_rename "}; dpp = &prefix;
+
+ if (!impl) {
+ return -EINVAL; // can't write after conflict or delete
+ }
+ if (realm_id != info.id || realm_name != info.name) {
+ return -EINVAL; // can't modify realm id or name directly
+ }
+ if (new_name.empty()) {
+ ldpp_dout(dpp, 0) << "realm cannot have an empty name" << dendl;
+ return -EINVAL;
+ }
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["realm_rename"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::realm_rename4,
+ P1, P2, P3, P4);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, realm_id);
+ sqlite::bind_text(dpp, binding, P2, new_name);
+ sqlite::bind_int(dpp, binding, P3, ver);
+ sqlite::bind_text(dpp, binding, P4, tag);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval0(dpp, reset);
+
+ if (!::sqlite3_changes(conn->db.get())) { // VersionNumber/Tag mismatch
+ impl = nullptr;
+ return -ECANCELED;
+ }
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "realm rename failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::unique_constraint) {
+ return -EEXIST; // Name already taken
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ info.name = std::string{new_name};
+ ++ver;
+ return 0;
+ }
+
+ int remove(const DoutPrefixProvider* dpp, optional_yield y) override
+ {
+ Prefix prefix{*dpp, "dbconfig:sqlite:realm_remove "}; dpp = &prefix;
+
+ if (!impl) {
+ return -EINVAL; // can't write after conflict or delete
+ }
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["realm_del"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::realm_delete3, P1, P2, P3);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, realm_id);
+ sqlite::bind_int(dpp, binding, P2, ver);
+ sqlite::bind_text(dpp, binding, P3, tag);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval0(dpp, reset);
+
+ impl = nullptr; // prevent any further writes after delete
+ if (!::sqlite3_changes(conn->db.get())) {
+ return -ECANCELED; // VersionNumber/Tag mismatch
+ }
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "realm delete failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+ }
+}; // SQLiteRealmWriter
+
+
+int SQLiteConfigStore::write_default_realm_id(const DoutPrefixProvider* dpp,
+ optional_yield y, bool exclusive,
+ std::string_view realm_id)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:write_default_realm_id "}; dpp = &prefix;
+
+ if (realm_id.empty()) {
+ ldpp_dout(dpp, 0) << "requires a realm id" << dendl;
+ return -EINVAL;
+ }
+
+ try {
+ auto conn = impl->get(dpp);
+ sqlite::stmt_ptr* stmt = nullptr;
+ if (exclusive) {
+ stmt = &conn->statements["def_realm_ins"];
+ if (!*stmt) {
+ const std::string sql = fmt::format(schema::default_realm_insert1, P1);
+ *stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ } else {
+ stmt = &conn->statements["def_realm_ups"];
+ if (!*stmt) {
+ const std::string sql = fmt::format(schema::default_realm_upsert1, P1);
+ *stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ }
+ auto binding = sqlite::stmt_binding{stmt->get()};
+ sqlite::bind_text(dpp, binding, P1, realm_id);
+
+ auto reset = sqlite::stmt_execution{stmt->get()};
+ sqlite::eval0(dpp, reset);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "default realm insert failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::primary_key_constraint) {
+ return -EEXIST;
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::read_default_realm_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string& realm_id)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:read_default_realm_id "}; dpp = &prefix;
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["def_realm_sel"];
+ if (!stmt) {
+ static constexpr std::string_view sql = schema::default_realm_select0;
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval1(dpp, reset);
+
+ realm_id = sqlite::column_text(reset, 0);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "default realm select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::delete_default_realm_id(const DoutPrefixProvider* dpp,
+ optional_yield y)
+
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:delete_default_realm_id "}; dpp = &prefix;
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["def_realm_del"];
+ if (!stmt) {
+ static constexpr std::string_view sql = schema::default_realm_delete0;
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval0(dpp, reset);
+
+ if (!::sqlite3_changes(conn->db.get())) {
+ return -ENOENT;
+ }
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "default realm delete failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+
+int SQLiteConfigStore::create_realm(const DoutPrefixProvider* dpp,
+ optional_yield y, bool exclusive,
+ const RGWRealm& info,
+ std::unique_ptr<sal::RealmWriter>* writer)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:create_realm "}; dpp = &prefix;
+
+ if (info.id.empty()) {
+ ldpp_dout(dpp, 0) << "realm cannot have an empty id" << dendl;
+ return -EINVAL;
+ }
+ if (info.name.empty()) {
+ ldpp_dout(dpp, 0) << "realm cannot have an empty name" << dendl;
+ return -EINVAL;
+ }
+
+ int ver = 1;
+ auto tag = generate_version_tag(dpp->get_cct());
+
+ try {
+ auto conn = impl->get(dpp);
+ sqlite::stmt_ptr* stmt = nullptr;
+ if (exclusive) {
+ stmt = &conn->statements["realm_ins"];
+ if (!*stmt) {
+ const std::string sql = fmt::format(schema::realm_insert4,
+ P1, P2, P3, P4);
+ *stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ } else {
+ stmt = &conn->statements["realm_ups"];
+ if (!*stmt) {
+ const std::string sql = fmt::format(schema::realm_upsert4,
+ P1, P2, P3, P4);
+ *stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ }
+ auto binding = sqlite::stmt_binding{stmt->get()};
+ sqlite::bind_text(dpp, binding, P1, info.id);
+ sqlite::bind_text(dpp, binding, P2, info.name);
+ sqlite::bind_int(dpp, binding, P3, ver);
+ sqlite::bind_text(dpp, binding, P4, tag);
+
+ auto reset = sqlite::stmt_execution{stmt->get()};
+ sqlite::eval0(dpp, reset);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "realm insert failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::primary_key_constraint) {
+ return -EEXIST; // ID already taken
+ } else if (e.code() == sqlite::errc::unique_constraint) {
+ return -EEXIST; // Name already taken
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+
+ if (writer) {
+ *writer = std::make_unique<SQLiteRealmWriter>(
+ impl.get(), ver, std::move(tag), info.id, info.name);
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::read_realm_by_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_id,
+ RGWRealm& info,
+ std::unique_ptr<sal::RealmWriter>* writer)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:read_realm_by_id "}; dpp = &prefix;
+
+ if (realm_id.empty()) {
+ ldpp_dout(dpp, 0) << "requires a realm id" << dendl;
+ return -EINVAL;
+ }
+
+ RealmRow row;
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["realm_sel_id"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::realm_select_id1, P1);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, realm_id);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval1(dpp, reset);
+
+ read_realm_row(reset, row);
+ } catch (const buffer::error& e) {
+ ldpp_dout(dpp, 20) << "realm decode failed: " << e.what() << dendl;
+ return -EIO;
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "realm select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::done) {
+ return -ENOENT;
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+
+ info = std::move(row.info);
+ if (writer) {
+ *writer = std::make_unique<SQLiteRealmWriter>(
+ impl.get(), row.ver, std::move(row.tag), info.id, info.name);
+ }
+ return 0;
+}
+
+static void realm_select_by_name(const DoutPrefixProvider* dpp,
+ sqlite::Connection& conn,
+ std::string_view realm_name,
+ RealmRow& row)
+{
+ auto& stmt = conn.statements["realm_sel_name"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::realm_select_name1, P1);
+ stmt = sqlite::prepare_statement(dpp, conn.db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, realm_name);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval1(dpp, reset);
+
+ read_realm_row(reset, row);
+}
+
+int SQLiteConfigStore::read_realm_by_name(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_name,
+ RGWRealm& info,
+ std::unique_ptr<sal::RealmWriter>* writer)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:read_realm_by_name "}; dpp = &prefix;
+
+ if (realm_name.empty()) {
+ ldpp_dout(dpp, 0) << "requires a realm name" << dendl;
+ return -EINVAL;
+ }
+
+ RealmRow row;
+ try {
+ auto conn = impl->get(dpp);
+ realm_select_by_name(dpp, *conn, realm_name, row);
+ } catch (const buffer::error& e) {
+ ldpp_dout(dpp, 20) << "realm decode failed: " << e.what() << dendl;
+ return -EIO;
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "realm select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::done) {
+ return -ENOENT;
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+
+ info = std::move(row.info);
+ if (writer) {
+ *writer = std::make_unique<SQLiteRealmWriter>(
+ impl.get(), row.ver, std::move(row.tag), info.id, info.name);
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::read_default_realm(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ RGWRealm& info,
+ std::unique_ptr<sal::RealmWriter>* writer)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:read_default_realm "}; dpp = &prefix;
+
+ RealmRow row;
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["realm_sel_def"];
+ if (!stmt) {
+ static constexpr std::string_view sql = schema::realm_select_default0;
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval1(dpp, reset);
+
+ read_realm_row(reset, row);
+ } catch (const buffer::error& e) {
+ ldpp_dout(dpp, 20) << "realm decode failed: " << e.what() << dendl;
+ return -EIO;
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "realm select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::done) {
+ return -ENOENT;
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+
+ info = std::move(row.info);
+ if (writer) {
+ *writer = std::make_unique<SQLiteRealmWriter>(
+ impl.get(), row.ver, std::move(row.tag), info.id, info.name);
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::read_realm_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_name,
+ std::string& realm_id)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:read_realm_id "}; dpp = &prefix;
+
+ if (realm_name.empty()) {
+ ldpp_dout(dpp, 0) << "requires a realm name" << dendl;
+ return -EINVAL;
+ }
+
+ try {
+ auto conn = impl->get(dpp);
+
+ RealmRow row;
+ realm_select_by_name(dpp, *conn, realm_name, row);
+
+ realm_id = std::move(row.info.id);
+ } catch (const buffer::error& e) {
+ ldpp_dout(dpp, 20) << "realm decode failed: " << e.what() << dendl;
+ return -EIO;
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "realm select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::done) {
+ return -ENOENT;
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int SQLiteConfigStore::realm_notify_new_period(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ const RGWPeriod& period)
+{
+ return -ENOTSUP;
+}
+
+int SQLiteConfigStore::list_realm_names(const DoutPrefixProvider* dpp,
+ optional_yield y, const std::string& marker,
+ std::span<std::string> entries,
+ sal::ListResult<std::string>& result)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:list_realm_names "}; dpp = &prefix;
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["realm_sel_names"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::realm_select_names2, P1, P2);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, marker);
+ sqlite::bind_int(dpp, binding, P2, entries.size());
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ read_text_rows(dpp, reset, entries, result);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "realm select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+
+// Period
+
+int SQLiteConfigStore::create_period(const DoutPrefixProvider* dpp,
+ optional_yield y, bool exclusive,
+ const RGWPeriod& info)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:create_period "}; dpp = &prefix;
+
+ if (info.id.empty()) {
+ ldpp_dout(dpp, 0) << "period cannot have an empty id" << dendl;
+ return -EINVAL;
+ }
+
+ bufferlist bl;
+ encode(info, bl);
+ const auto data = std::string_view{bl.c_str(), bl.length()};
+
+ try {
+ auto conn = impl->get(dpp);
+ sqlite::stmt_ptr* stmt = nullptr;
+ if (exclusive) {
+ stmt = &conn->statements["period_ins"];
+ if (!*stmt) {
+ const std::string sql = fmt::format(schema::period_insert4,
+ P1, P2, P3, P4);
+ *stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ } else {
+ stmt = &conn->statements["period_ups"];
+ if (!*stmt) {
+ const std::string sql = fmt::format(schema::period_upsert4,
+ P1, P2, P3, P4);
+ *stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ }
+ auto binding = sqlite::stmt_binding{stmt->get()};
+ sqlite::bind_text(dpp, binding, P1, info.id);
+ sqlite::bind_int(dpp, binding, P2, info.epoch);
+ sqlite::bind_text(dpp, binding, P3, info.realm_id);
+ sqlite::bind_text(dpp, binding, P4, data);
+
+ auto reset = sqlite::stmt_execution{stmt->get()};
+ sqlite::eval0(dpp, reset);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "period insert failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::foreign_key_constraint) {
+ return -EINVAL; // refers to nonexistent RealmID
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+static void period_select_epoch(const DoutPrefixProvider* dpp,
+ sqlite::Connection& conn,
+ std::string_view id, uint32_t epoch,
+ RGWPeriod& row)
+{
+ auto& stmt = conn.statements["period_sel_epoch"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::period_select_epoch2, P1, P2);
+ stmt = sqlite::prepare_statement(dpp, conn.db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, id);
+ sqlite::bind_int(dpp, binding, P2, epoch);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval1(dpp, reset);
+
+ read_period_row(reset, row);
+}
+
+static void period_select_latest(const DoutPrefixProvider* dpp,
+ sqlite::Connection& conn,
+ std::string_view id, RGWPeriod& row)
+{
+ auto& stmt = conn.statements["period_sel_latest"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::period_select_latest1, P1);
+ stmt = sqlite::prepare_statement(dpp, conn.db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, id);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval1(dpp, reset);
+
+ read_period_row(reset, row);
+}
+
+int SQLiteConfigStore::read_period(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view period_id,
+ std::optional<uint32_t> epoch,
+ RGWPeriod& info)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:read_period "}; dpp = &prefix;
+
+ if (period_id.empty()) {
+ ldpp_dout(dpp, 0) << "requires a period id" << dendl;
+ return -EINVAL;
+ }
+
+ try {
+ auto conn = impl->get(dpp);
+ if (epoch) {
+ period_select_epoch(dpp, *conn, period_id, *epoch, info);
+ } else {
+ period_select_latest(dpp, *conn, period_id, info);
+ }
+ } catch (const buffer::error& e) {
+ ldpp_dout(dpp, 20) << "period decode failed: " << e.what() << dendl;
+ return -EIO;
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "period select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::done) {
+ return -ENOENT;
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::delete_period(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view period_id)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:delete_period "}; dpp = &prefix;
+
+ if (period_id.empty()) {
+ ldpp_dout(dpp, 0) << "requires a period id" << dendl;
+ return -EINVAL;
+ }
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["period_del"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::period_delete1, P1);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, period_id);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval0(dpp, reset);
+
+ if (!::sqlite3_changes(conn->db.get())) {
+ return -ENOENT;
+ }
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "period delete failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::list_period_ids(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ const std::string& marker,
+ std::span<std::string> entries,
+ sal::ListResult<std::string>& result)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:list_period_ids "}; dpp = &prefix;
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["period_sel_ids"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::period_select_ids2, P1, P2);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, marker);
+ sqlite::bind_int(dpp, binding, P2, entries.size());
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ read_text_rows(dpp, reset, entries, result);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "period select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+
+// ZoneGroup
+
+class SQLiteZoneGroupWriter : public sal::ZoneGroupWriter {
+ SQLiteImpl* impl;
+ int ver;
+ std::string tag;
+ std::string zonegroup_id;
+ std::string zonegroup_name;
+ public:
+ SQLiteZoneGroupWriter(SQLiteImpl* impl, int ver, std::string tag,
+ std::string_view zonegroup_id,
+ std::string_view zonegroup_name)
+ : impl(impl), ver(ver), tag(std::move(tag)),
+ zonegroup_id(zonegroup_id), zonegroup_name(zonegroup_name)
+ {}
+
+ int write(const DoutPrefixProvider* dpp, optional_yield y,
+ const RGWZoneGroup& info) override
+ {
+ Prefix prefix{*dpp, "dbconfig:sqlite:zonegroup_write "}; dpp = &prefix;
+
+ if (!impl) {
+ return -EINVAL; // can't write after conflict or delete
+ }
+ if (zonegroup_id != info.id || zonegroup_name != info.name) {
+ return -EINVAL; // can't modify zonegroup id or name directly
+ }
+
+ bufferlist bl;
+ encode(info, bl);
+ const auto data = std::string_view{bl.c_str(), bl.length()};
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["zonegroup_upd"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::zonegroup_update5,
+ P1, P2, P3, P4, P5);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, info.id);
+ sqlite::bind_text(dpp, binding, P2, info.realm_id);
+ sqlite::bind_text(dpp, binding, P3, data);
+ sqlite::bind_int(dpp, binding, P4, ver);
+ sqlite::bind_text(dpp, binding, P5, tag);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval0(dpp, reset);
+
+ if (!::sqlite3_changes(conn->db.get())) { // VersionNumber/Tag mismatch
+ impl = nullptr;
+ return -ECANCELED;
+ }
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "zonegroup update failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::foreign_key_constraint) {
+ return -EINVAL; // refers to nonexistent RealmID
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+ }
+
+ int rename(const DoutPrefixProvider* dpp, optional_yield y,
+ RGWZoneGroup& info, std::string_view new_name) override
+ {
+ Prefix prefix{*dpp, "dbconfig:sqlite:zonegroup_rename "}; dpp = &prefix;
+
+ if (!impl) {
+ return -EINVAL; // can't write after conflict or delete
+ }
+ if (zonegroup_id != info.get_id() || zonegroup_name != info.get_name()) {
+ return -EINVAL; // can't modify zonegroup id or name directly
+ }
+ if (new_name.empty()) {
+ ldpp_dout(dpp, 0) << "zonegroup cannot have an empty name" << dendl;
+ return -EINVAL;
+ }
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["zonegroup_rename"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::zonegroup_rename4,
+ P1, P2, P3, P4);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, info.id);
+ sqlite::bind_text(dpp, binding, P2, new_name);
+ sqlite::bind_int(dpp, binding, P3, ver);
+ sqlite::bind_text(dpp, binding, P4, tag);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval0(dpp, reset);
+
+ if (!::sqlite3_changes(conn->db.get())) { // VersionNumber/Tag mismatch
+ impl = nullptr;
+ return -ECANCELED;
+ }
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "zonegroup rename failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::unique_constraint) {
+ return -EEXIST; // Name already taken
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ info.name = std::string{new_name};
+ return 0;
+ }
+
+ int remove(const DoutPrefixProvider* dpp, optional_yield y) override
+ {
+ Prefix prefix{*dpp, "dbconfig:sqlite:zonegroup_remove "}; dpp = &prefix;
+
+ if (!impl) {
+ return -EINVAL; // can't write after conflict or delete
+ }
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["zonegroup_del"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::zonegroup_delete3,
+ P1, P2, P3);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, zonegroup_id);
+ sqlite::bind_int(dpp, binding, P2, ver);
+ sqlite::bind_text(dpp, binding, P3, tag);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval0(dpp, reset);
+
+ impl = nullptr;
+ if (!::sqlite3_changes(conn->db.get())) { // VersionNumber/Tag mismatch
+ return -ECANCELED;
+ }
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "zonegroup delete failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+ }
+}; // SQLiteZoneGroupWriter
+
+
+int SQLiteConfigStore::write_default_zonegroup_id(const DoutPrefixProvider* dpp,
+ optional_yield y, bool exclusive,
+ std::string_view realm_id,
+ std::string_view zonegroup_id)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:write_default_zonegroup_id "}; dpp = &prefix;
+
+ try {
+ auto conn = impl->get(dpp);
+ sqlite::stmt_ptr* stmt = nullptr;
+ if (exclusive) {
+ stmt = &conn->statements["def_zonegroup_ins"];
+ if (!*stmt) {
+ const std::string sql = fmt::format(schema::default_zonegroup_insert2,
+ P1, P2);
+ *stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ } else {
+ stmt = &conn->statements["def_zonegroup_ups"];
+ if (!*stmt) {
+ const std::string sql = fmt::format(schema::default_zonegroup_upsert2,
+ P1, P2);
+ *stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ }
+ auto binding = sqlite::stmt_binding{stmt->get()};
+ sqlite::bind_text(dpp, binding, P1, realm_id);
+ sqlite::bind_text(dpp, binding, P2, zonegroup_id);
+
+ auto reset = sqlite::stmt_execution{stmt->get()};
+ sqlite::eval0(dpp, reset);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "default zonegroup insert failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::read_default_zonegroup_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_id,
+ std::string& zonegroup_id)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:read_default_zonegroup_id "}; dpp = &prefix;
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["def_zonegroup_sel"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::default_zonegroup_select1, P1);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, realm_id);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval1(dpp, reset);
+
+ zonegroup_id = sqlite::column_text(reset, 0);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "default zonegroup select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::done) {
+ return -ENOENT;
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::delete_default_zonegroup_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_id)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:delete_default_zonegroup_id "}; dpp = &prefix;
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["def_zonegroup_del"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::default_zonegroup_delete1, P1);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, realm_id);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval0(dpp, reset);
+
+ if (!::sqlite3_changes(conn->db.get())) {
+ return -ENOENT;
+ }
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "default zonegroup delete failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+
+int SQLiteConfigStore::create_zonegroup(const DoutPrefixProvider* dpp,
+ optional_yield y, bool exclusive,
+ const RGWZoneGroup& info,
+ std::unique_ptr<sal::ZoneGroupWriter>* writer)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:create_zonegroup "}; dpp = &prefix;
+
+ if (info.id.empty()) {
+ ldpp_dout(dpp, 0) << "zonegroup cannot have an empty id" << dendl;
+ return -EINVAL;
+ }
+ if (info.name.empty()) {
+ ldpp_dout(dpp, 0) << "zonegroup cannot have an empty name" << dendl;
+ return -EINVAL;
+ }
+
+ int ver = 1;
+ auto tag = generate_version_tag(dpp->get_cct());
+
+ bufferlist bl;
+ encode(info, bl);
+ const auto data = std::string_view{bl.c_str(), bl.length()};
+
+ try {
+ auto conn = impl->get(dpp);
+ sqlite::stmt_ptr* stmt = nullptr;
+ if (exclusive) {
+ stmt = &conn->statements["zonegroup_ins"];
+ if (!*stmt) {
+ const std::string sql = fmt::format(schema::zonegroup_insert6,
+ P1, P2, P3, P4, P5, P6);
+ *stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ } else {
+ stmt = &conn->statements["zonegroup_ups"];
+ if (!*stmt) {
+ const std::string sql = fmt::format(schema::zonegroup_upsert6,
+ P1, P2, P3, P4, P5, P6);
+ *stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ }
+ auto binding = sqlite::stmt_binding{stmt->get()};
+ sqlite::bind_text(dpp, binding, P1, info.id);
+ sqlite::bind_text(dpp, binding, P2, info.name);
+ sqlite::bind_text(dpp, binding, P3, info.realm_id);
+ sqlite::bind_text(dpp, binding, P4, data);
+ sqlite::bind_int(dpp, binding, P5, ver);
+ sqlite::bind_text(dpp, binding, P6, tag);
+
+ auto reset = sqlite::stmt_execution{stmt->get()};
+ sqlite::eval0(dpp, reset);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "zonegroup insert failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::foreign_key_constraint) {
+ return -EINVAL; // refers to nonexistent RealmID
+ } else if (e.code() == sqlite::errc::primary_key_constraint) {
+ return -EEXIST; // ID already taken
+ } else if (e.code() == sqlite::errc::unique_constraint) {
+ return -EEXIST; // Name already taken
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+
+ if (writer) {
+ *writer = std::make_unique<SQLiteZoneGroupWriter>(
+ impl.get(), ver, std::move(tag), info.id, info.name);
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::read_zonegroup_by_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view zonegroup_id,
+ RGWZoneGroup& info,
+ std::unique_ptr<sal::ZoneGroupWriter>* writer)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:read_zonegroup_by_id "}; dpp = &prefix;
+
+ if (zonegroup_id.empty()) {
+ ldpp_dout(dpp, 0) << "requires a zonegroup id" << dendl;
+ return -EINVAL;
+ }
+
+ ZoneGroupRow row;
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["zonegroup_sel_id"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::zonegroup_select_id1, P1);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, zonegroup_id);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval1(dpp, reset);
+
+ read_zonegroup_row(reset, row);
+ } catch (const buffer::error& e) {
+ ldpp_dout(dpp, 20) << "zonegroup decode failed: " << e.what() << dendl;
+ return -EIO;
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "zonegroup select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::done) {
+ return -ENOENT;
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+
+ info = std::move(row.info);
+ if (writer) {
+ *writer = std::make_unique<SQLiteZoneGroupWriter>(
+ impl.get(), row.ver, std::move(row.tag), info.id, info.name);
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::read_zonegroup_by_name(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view zonegroup_name,
+ RGWZoneGroup& info,
+ std::unique_ptr<sal::ZoneGroupWriter>* writer)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:read_zonegroup_by_name "}; dpp = &prefix;
+
+ if (zonegroup_name.empty()) {
+ ldpp_dout(dpp, 0) << "requires a zonegroup name" << dendl;
+ return -EINVAL;
+ }
+
+ ZoneGroupRow row;
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["zonegroup_sel_name"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::zonegroup_select_name1, P1);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, zonegroup_name);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval1(dpp, reset);
+
+ read_zonegroup_row(reset, row);
+ } catch (const buffer::error& e) {
+ ldpp_dout(dpp, 20) << "zonegroup decode failed: " << e.what() << dendl;
+ return -EIO;
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "zonegroup select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::done) {
+ return -ENOENT;
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+
+ info = std::move(row.info);
+ if (writer) {
+ *writer = std::make_unique<SQLiteZoneGroupWriter>(
+ impl.get(), row.ver, std::move(row.tag), info.id, info.name);
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::read_default_zonegroup(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_id,
+ RGWZoneGroup& info,
+ std::unique_ptr<sal::ZoneGroupWriter>* writer)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:read_default_zonegroup "}; dpp = &prefix;
+
+ ZoneGroupRow row;
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["zonegroup_sel_def"];
+ if (!stmt) {
+ static constexpr std::string_view sql = schema::zonegroup_select_default0;
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval1(dpp, reset);
+
+ read_zonegroup_row(reset, row);
+ } catch (const buffer::error& e) {
+ ldpp_dout(dpp, 20) << "zonegroup decode failed: " << e.what() << dendl;
+ return -EIO;
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "zonegroup select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::done) {
+ return -ENOENT;
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+
+ info = std::move(row.info);
+ if (writer) {
+ *writer = std::make_unique<SQLiteZoneGroupWriter>(
+ impl.get(), row.ver, std::move(row.tag), info.id, info.name);
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::list_zonegroup_names(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ const std::string& marker,
+ std::span<std::string> entries,
+ sal::ListResult<std::string>& result)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:list_zonegroup_names "}; dpp = &prefix;
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["zonegroup_sel_names"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::zonegroup_select_names2, P1, P2);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ auto reset = sqlite::stmt_execution{stmt.get()};
+
+ sqlite::bind_text(dpp, binding, P1, marker);
+ sqlite::bind_int(dpp, binding, P2, entries.size());
+
+ read_text_rows(dpp, reset, entries, result);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "zonegroup select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+
+// Zone
+
+class SQLiteZoneWriter : public sal::ZoneWriter {
+ SQLiteImpl* impl;
+ int ver;
+ std::string tag;
+ std::string zone_id;
+ std::string zone_name;
+ public:
+ SQLiteZoneWriter(SQLiteImpl* impl, int ver, std::string tag,
+ std::string_view zone_id, std::string_view zone_name)
+ : impl(impl), ver(ver), tag(std::move(tag)),
+ zone_id(zone_id), zone_name(zone_name)
+ {}
+
+ int write(const DoutPrefixProvider* dpp, optional_yield y,
+ const RGWZoneParams& info) override
+ {
+ Prefix prefix{*dpp, "dbconfig:sqlite:zone_write "}; dpp = &prefix;
+
+ if (!impl) {
+ return -EINVAL; // can't write after conflict or delete
+ }
+ if (zone_id != info.id || zone_name != info.name) {
+ return -EINVAL; // can't modify zone id or name directly
+ }
+
+ bufferlist bl;
+ encode(info, bl);
+ const auto data = std::string_view{bl.c_str(), bl.length()};
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["zone_upd"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::zone_update5,
+ P1, P2, P3, P4, P5);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, info.id);
+ sqlite::bind_text(dpp, binding, P2, info.realm_id);
+ sqlite::bind_text(dpp, binding, P3, data);
+ sqlite::bind_int(dpp, binding, P4, ver);
+ sqlite::bind_text(dpp, binding, P5, tag);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval0(dpp, reset);
+
+ if (!::sqlite3_changes(conn->db.get())) { // VersionNumber/Tag mismatch
+ impl = nullptr;
+ return -ECANCELED;
+ }
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "zone update failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::foreign_key_constraint) {
+ return -EINVAL; // refers to nonexistent RealmID
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ ++ver;
+ return 0;
+ }
+
+ int rename(const DoutPrefixProvider* dpp, optional_yield y,
+ RGWZoneParams& info, std::string_view new_name) override
+ {
+ Prefix prefix{*dpp, "dbconfig:sqlite:zone_rename "}; dpp = &prefix;
+
+ if (!impl) {
+ return -EINVAL; // can't write after conflict or delete
+ }
+ if (zone_id != info.id || zone_name != info.name) {
+ return -EINVAL; // can't modify zone id or name directly
+ }
+ if (new_name.empty()) {
+ ldpp_dout(dpp, 0) << "zonegroup cannot have an empty name" << dendl;
+ return -EINVAL;
+ }
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["zone_rename"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::zone_rename4, P1, P2, P2, P3);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, info.id);
+ sqlite::bind_text(dpp, binding, P2, new_name);
+ sqlite::bind_int(dpp, binding, P3, ver);
+ sqlite::bind_text(dpp, binding, P4, tag);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval0(dpp, reset);
+
+ if (!::sqlite3_changes(conn->db.get())) { // VersionNumber/Tag mismatch
+ impl = nullptr;
+ return -ECANCELED;
+ }
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "zone rename failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::unique_constraint) {
+ return -EEXIST; // Name already taken
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ info.name = std::string{new_name};
+ ++ver;
+ return 0;
+ }
+
+ int remove(const DoutPrefixProvider* dpp, optional_yield y) override
+ {
+ Prefix prefix{*dpp, "dbconfig:sqlite:zone_remove "}; dpp = &prefix;
+
+ if (!impl) {
+ return -EINVAL; // can't write after conflict or delete
+ }
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["zone_del"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::zone_delete3, P1, P2, P3);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, zone_id);
+ sqlite::bind_int(dpp, binding, P2, ver);
+ sqlite::bind_text(dpp, binding, P3, tag);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval0(dpp, reset);
+
+ impl = nullptr;
+ if (!::sqlite3_changes(conn->db.get())) { // VersionNumber/Tag mismatch
+ return -ECANCELED;
+ }
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "zone delete failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+ }
+}; // SQLiteZoneWriter
+
+
+int SQLiteConfigStore::write_default_zone_id(const DoutPrefixProvider* dpp,
+ optional_yield y, bool exclusive,
+ std::string_view realm_id,
+ std::string_view zone_id)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:write_default_zone_id "}; dpp = &prefix;
+
+ if (zone_id.empty()) {
+ ldpp_dout(dpp, 0) << "requires a zone id" << dendl;
+ return -EINVAL;
+ }
+
+ try {
+ auto conn = impl->get(dpp);
+ sqlite::stmt_ptr* stmt = nullptr;
+ if (exclusive) {
+ stmt = &conn->statements["def_zone_ins"];
+ if (!*stmt) {
+ const std::string sql = fmt::format(schema::default_zone_insert2, P1, P2);
+ *stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ } else {
+ stmt = &conn->statements["def_zone_ups"];
+ if (!*stmt) {
+ const std::string sql = fmt::format(schema::default_zone_upsert2, P1, P2);
+ *stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ }
+ auto binding = sqlite::stmt_binding{stmt->get()};
+ sqlite::bind_text(dpp, binding, P1, realm_id);
+ sqlite::bind_text(dpp, binding, P2, zone_id);
+
+ auto reset = sqlite::stmt_execution{stmt->get()};
+ sqlite::eval0(dpp, reset);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "default zone insert failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::read_default_zone_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_id,
+ std::string& zone_id)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:read_default_zone_id "}; dpp = &prefix;
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["def_zone_sel"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::default_zone_select1, P1);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, realm_id);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval1(dpp, reset);
+
+ zone_id = sqlite::column_text(reset, 0);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "default zone select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::done) {
+ return -ENOENT;
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::delete_default_zone_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_id)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:delete_default_zone_id "}; dpp = &prefix;
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["def_zone_del"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::default_zone_delete1, P1);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, realm_id);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval0(dpp, reset);
+
+ if (!::sqlite3_changes(conn->db.get())) {
+ return -ENOENT;
+ }
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "default zone delete failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+
+int SQLiteConfigStore::create_zone(const DoutPrefixProvider* dpp,
+ optional_yield y, bool exclusive,
+ const RGWZoneParams& info,
+ std::unique_ptr<sal::ZoneWriter>* writer)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:create_zone "}; dpp = &prefix;
+
+ if (info.id.empty()) {
+ ldpp_dout(dpp, 0) << "zone cannot have an empty id" << dendl;
+ return -EINVAL;
+ }
+ if (info.name.empty()) {
+ ldpp_dout(dpp, 0) << "zone cannot have an empty name" << dendl;
+ return -EINVAL;
+ }
+
+ int ver = 1;
+ auto tag = generate_version_tag(dpp->get_cct());
+
+ bufferlist bl;
+ encode(info, bl);
+ const auto data = std::string_view{bl.c_str(), bl.length()};
+
+ try {
+ auto conn = impl->get(dpp);
+ sqlite::stmt_ptr* stmt = nullptr;
+ if (exclusive) {
+ stmt = &conn->statements["zone_ins"];
+ if (!*stmt) {
+ const std::string sql = fmt::format(schema::zone_insert6,
+ P1, P2, P3, P4, P5, P6);
+ *stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ } else {
+ stmt = &conn->statements["zone_ups"];
+ if (!*stmt) {
+ const std::string sql = fmt::format(schema::zone_upsert6,
+ P1, P2, P3, P4, P5, P6);
+ *stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ }
+ auto binding = sqlite::stmt_binding{stmt->get()};
+ sqlite::bind_text(dpp, binding, P1, info.id);
+ sqlite::bind_text(dpp, binding, P2, info.name);
+ sqlite::bind_text(dpp, binding, P3, info.realm_id);
+ sqlite::bind_text(dpp, binding, P4, data);
+ sqlite::bind_int(dpp, binding, P5, ver);
+ sqlite::bind_text(dpp, binding, P6, tag);
+
+ auto reset = sqlite::stmt_execution{stmt->get()};
+ sqlite::eval0(dpp, reset);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "zone insert failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::foreign_key_constraint) {
+ return -EINVAL; // refers to nonexistent RealmID
+ } else if (e.code() == sqlite::errc::primary_key_constraint) {
+ return -EEXIST; // ID already taken
+ } else if (e.code() == sqlite::errc::unique_constraint) {
+ return -EEXIST; // Name already taken
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+
+ if (writer) {
+ *writer = std::make_unique<SQLiteZoneWriter>(
+ impl.get(), ver, std::move(tag), info.id, info.name);
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::read_zone_by_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view zone_id,
+ RGWZoneParams& info,
+ std::unique_ptr<sal::ZoneWriter>* writer)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:read_zone_by_id "}; dpp = &prefix;
+
+ if (zone_id.empty()) {
+ ldpp_dout(dpp, 0) << "requires a zone id" << dendl;
+ return -EINVAL;
+ }
+
+ ZoneRow row;
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["zone_sel_id"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::zone_select_id1, P1);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, zone_id);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval1(dpp, reset);
+
+ read_zone_row(reset, row);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "zone select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::done) {
+ return -ENOENT;
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+
+ info = std::move(row.info);
+ if (writer) {
+ *writer = std::make_unique<SQLiteZoneWriter>(
+ impl.get(), row.ver, std::move(row.tag), info.id, info.name);
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::read_zone_by_name(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view zone_name,
+ RGWZoneParams& info,
+ std::unique_ptr<sal::ZoneWriter>* writer)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:read_zone_by_name "}; dpp = &prefix;
+
+ if (zone_name.empty()) {
+ ldpp_dout(dpp, 0) << "requires a zone name" << dendl;
+ return -EINVAL;
+ }
+
+ ZoneRow row;
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["zone_sel_name"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::zone_select_name1, P1);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, zone_name);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval1(dpp, reset);
+
+ read_zone_row(reset, row);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "zone select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::done) {
+ return -ENOENT;
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+
+ info = std::move(row.info);
+ if (writer) {
+ *writer = std::make_unique<SQLiteZoneWriter>(
+ impl.get(), row.ver, std::move(row.tag), info.id, info.name);
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::read_default_zone(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_id,
+ RGWZoneParams& info,
+ std::unique_ptr<sal::ZoneWriter>* writer)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:read_default_zone "}; dpp = &prefix;
+
+ ZoneRow row;
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["zone_sel_def"];
+ if (!stmt) {
+ static constexpr std::string_view sql = schema::zone_select_default0;
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval1(dpp, reset);
+
+ read_zone_row(reset, row);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "zone select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::done) {
+ return -ENOENT;
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+
+ info = std::move(row.info);
+ if (writer) {
+ *writer = std::make_unique<SQLiteZoneWriter>(
+ impl.get(), row.ver, std::move(row.tag), info.id, info.name);
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::list_zone_names(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ const std::string& marker,
+ std::span<std::string> entries,
+ sal::ListResult<std::string>& result)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:list_zone_names "}; dpp = &prefix;
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["zone_sel_names"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::zone_select_names2, P1, P2);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, marker);
+ sqlite::bind_int(dpp, binding, P2, entries.size());
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ read_text_rows(dpp, reset, entries, result);
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "zone select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+
+// PeriodConfig
+
+int SQLiteConfigStore::read_period_config(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_id,
+ RGWPeriodConfig& info)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:read_period_config "}; dpp = &prefix;
+
+ try {
+ auto conn = impl->get(dpp);
+ auto& stmt = conn->statements["period_conf_sel"];
+ if (!stmt) {
+ const std::string sql = fmt::format(schema::period_config_select1, P1);
+ stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ auto binding = sqlite::stmt_binding{stmt.get()};
+ sqlite::bind_text(dpp, binding, P1, realm_id);
+
+ auto reset = sqlite::stmt_execution{stmt.get()};
+ sqlite::eval1(dpp, reset);
+
+ std::string data = sqlite::column_text(reset, 0);
+ bufferlist bl = bufferlist::static_from_string(data);
+ auto p = bl.cbegin();
+ decode(info, p);
+
+ } catch (const buffer::error& e) {
+ ldpp_dout(dpp, 20) << "period config decode failed: " << e.what() << dendl;
+ return -EIO;
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "period config select failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::done) {
+ return -ENOENT;
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+int SQLiteConfigStore::write_period_config(const DoutPrefixProvider* dpp,
+ optional_yield y, bool exclusive,
+ std::string_view realm_id,
+ const RGWPeriodConfig& info)
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:write_period_config "}; dpp = &prefix;
+
+ bufferlist bl;
+ encode(info, bl);
+ const auto data = std::string_view{bl.c_str(), bl.length()};
+
+ try {
+ auto conn = impl->get(dpp);
+ sqlite::stmt_ptr* stmt = nullptr;
+ if (exclusive) {
+ stmt = &conn->statements["period_conf_ins"];
+ if (!*stmt) {
+ const std::string sql = fmt::format(schema::period_config_insert2, P1, P2);
+ *stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ } else {
+ stmt = &conn->statements["period_conf_ups"];
+ if (!*stmt) {
+ const std::string sql = fmt::format(schema::period_config_upsert2, P1, P2);
+ *stmt = sqlite::prepare_statement(dpp, conn->db.get(), sql);
+ }
+ }
+ auto binding = sqlite::stmt_binding{stmt->get()};
+ sqlite::bind_text(dpp, binding, P1, realm_id);
+ sqlite::bind_text(dpp, binding, P2, data);
+
+ auto reset = sqlite::stmt_execution{stmt->get()};
+ sqlite::eval0(dpp, reset);
+ } catch (const buffer::error& e) {
+ ldpp_dout(dpp, 20) << "period config decode failed: " << e.what() << dendl;
+ return -EIO;
+ } catch (const sqlite::error& e) {
+ ldpp_dout(dpp, 20) << "period config insert failed: " << e.what() << dendl;
+ if (e.code() == sqlite::errc::primary_key_constraint) {
+ return -EEXIST;
+ } else if (e.code() == sqlite::errc::busy) {
+ return -EBUSY;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+namespace {
+
+int version_cb(void* user, int count, char** values, char** names)
+{
+ if (count != 1) {
+ return EINVAL;
+ }
+ std::string_view name = names[0];
+ if (name != "user_version") {
+ return EINVAL;
+ }
+ std::string_view value = values[0];
+ auto result = std::from_chars(value.begin(), value.end(),
+ *reinterpret_cast<uint32_t*>(user));
+ if (result.ec != std::errc{}) {
+ return static_cast<int>(result.ec);
+ }
+ return 0;
+}
+
+void apply_schema_migrations(const DoutPrefixProvider* dpp, sqlite3* db)
+{
+ sqlite::execute(dpp, db, "PRAGMA foreign_keys = ON", nullptr, nullptr);
+
+ // initiate a transaction and read the current schema version
+ uint32_t version = 0;
+ sqlite::execute(dpp, db, "BEGIN; PRAGMA user_version", version_cb, &version);
+
+ const uint32_t initial_version = version;
+ ldpp_dout(dpp, 4) << "current schema version " << version << dendl;
+
+ // use the version as an index into schema::migrations
+ auto m = std::next(schema::migrations.begin(), version);
+
+ for (; m != schema::migrations.end(); ++m, ++version) {
+ try {
+ sqlite::execute(dpp, db, m->up, nullptr, nullptr);
+ } catch (const sqlite::error&) {
+ ldpp_dout(dpp, -1) << "ERROR: schema migration failed on v" << version
+ << ": " << m->description << dendl;
+ throw;
+ }
+ }
+
+ if (version > initial_version) {
+ // update the user_version and commit the transaction
+ const auto commit = fmt::format("PRAGMA user_version = {}; COMMIT", version);
+ sqlite::execute(dpp, db, commit.c_str(), nullptr, nullptr);
+
+ ldpp_dout(dpp, 4) << "upgraded database schema to version " << version << dendl;
+ } else {
+ // nothing to commit
+ sqlite::execute(dpp, db, "ROLLBACK", nullptr, nullptr);
+ }
+}
+
+} // anonymous namespace
+
+
+auto create_sqlite_store(const DoutPrefixProvider* dpp, const std::string& uri)
+ -> std::unique_ptr<config::SQLiteConfigStore>
+{
+ Prefix prefix{*dpp, "dbconfig:sqlite:create_sqlite_store "}; dpp = &prefix;
+
+ // build the connection pool
+ int flags = SQLITE_OPEN_CREATE | SQLITE_OPEN_URI | SQLITE_OPEN_READWRITE |
+ SQLITE_OPEN_NOMUTEX;
+ auto factory = sqlite::ConnectionFactory{uri, flags};
+
+ // sqlite does not support concurrent writers. we enforce this limitation by
+ // using a connection pool of size=1
+ static constexpr size_t max_connections = 1;
+ auto impl = std::make_unique<SQLiteImpl>(std::move(factory), max_connections);
+
+ // open a connection to apply schema migrations
+ auto conn = impl->get(dpp);
+ apply_schema_migrations(dpp, conn->db.get());
+
+ return std::make_unique<SQLiteConfigStore>(std::move(impl));
+}
+
+} // namespace rgw::dbstore::config
diff --git a/src/rgw/driver/dbstore/config/sqlite.h b/src/rgw/driver/dbstore/config/sqlite.h
new file mode 100644
index 000000000..d79e04072
--- /dev/null
+++ b/src/rgw/driver/dbstore/config/sqlite.h
@@ -0,0 +1,172 @@
+// -*- 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) 2022 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_config.h"
+
+class DoutPrefixProvider;
+
+namespace rgw::dbstore::config {
+
+struct SQLiteImpl;
+
+class SQLiteConfigStore : public sal::ConfigStore {
+ public:
+ explicit SQLiteConfigStore(std::unique_ptr<SQLiteImpl> impl);
+ ~SQLiteConfigStore() override;
+
+ int write_default_realm_id(const DoutPrefixProvider* dpp,
+ optional_yield y, bool exclusive,
+ std::string_view realm_id) override;
+ int read_default_realm_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string& realm_id) override;
+ int delete_default_realm_id(const DoutPrefixProvider* dpp,
+ optional_yield y) override;
+
+ int create_realm(const DoutPrefixProvider* dpp,
+ optional_yield y, bool exclusive,
+ const RGWRealm& info,
+ std::unique_ptr<sal::RealmWriter>* writer) override;
+ int read_realm_by_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_id,
+ RGWRealm& info,
+ std::unique_ptr<sal::RealmWriter>* writer) override;
+ int read_realm_by_name(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_name,
+ RGWRealm& info,
+ std::unique_ptr<sal::RealmWriter>* writer) override;
+ int read_default_realm(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ RGWRealm& info,
+ std::unique_ptr<sal::RealmWriter>* writer) override;
+ int read_realm_id(const DoutPrefixProvider* dpp,
+ optional_yield y, std::string_view realm_name,
+ std::string& realm_id) override;
+ int realm_notify_new_period(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ const RGWPeriod& period) override;
+ int list_realm_names(const DoutPrefixProvider* dpp,
+ optional_yield y, const std::string& marker,
+ std::span<std::string> entries,
+ sal::ListResult<std::string>& result) override;
+
+ int create_period(const DoutPrefixProvider* dpp,
+ optional_yield y, bool exclusive,
+ const RGWPeriod& info) override;
+ int read_period(const DoutPrefixProvider* dpp,
+ optional_yield y, std::string_view period_id,
+ std::optional<uint32_t> epoch, RGWPeriod& info) override;
+ int delete_period(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view period_id) override;
+ int list_period_ids(const DoutPrefixProvider* dpp,
+ optional_yield y, const std::string& marker,
+ std::span<std::string> entries,
+ sal::ListResult<std::string>& result) override;
+
+ int write_default_zonegroup_id(const DoutPrefixProvider* dpp,
+ optional_yield y, bool exclusive,
+ std::string_view realm_id,
+ std::string_view zonegroup_id) override;
+ int read_default_zonegroup_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_id,
+ std::string& zonegroup_id) override;
+ int delete_default_zonegroup_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_id) override;
+
+ int create_zonegroup(const DoutPrefixProvider* dpp,
+ optional_yield y, bool exclusive,
+ const RGWZoneGroup& info,
+ std::unique_ptr<sal::ZoneGroupWriter>* writer) override;
+ int read_zonegroup_by_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view zonegroup_id,
+ RGWZoneGroup& info,
+ std::unique_ptr<sal::ZoneGroupWriter>* writer) override;
+ int read_zonegroup_by_name(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view zonegroup_name,
+ RGWZoneGroup& info,
+ std::unique_ptr<sal::ZoneGroupWriter>* writer) override;
+ int read_default_zonegroup(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_id,
+ RGWZoneGroup& info,
+ std::unique_ptr<sal::ZoneGroupWriter>* writer) override;
+ int list_zonegroup_names(const DoutPrefixProvider* dpp,
+ optional_yield y, const std::string& marker,
+ std::span<std::string> entries,
+ sal::ListResult<std::string>& result) override;
+
+ int write_default_zone_id(const DoutPrefixProvider* dpp,
+ optional_yield y, bool exclusive,
+ std::string_view realm_id,
+ std::string_view zone_id) override;
+ int read_default_zone_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_id,
+ std::string& zone_id) override;
+ int delete_default_zone_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_id) override;
+
+ int create_zone(const DoutPrefixProvider* dpp,
+ optional_yield y, bool exclusive,
+ const RGWZoneParams& info,
+ std::unique_ptr<sal::ZoneWriter>* writer) override;
+ int read_zone_by_id(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view zone_id,
+ RGWZoneParams& info,
+ std::unique_ptr<sal::ZoneWriter>* writer) override;
+ int read_zone_by_name(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view zone_name,
+ RGWZoneParams& info,
+ std::unique_ptr<sal::ZoneWriter>* writer) override;
+ int read_default_zone(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_id,
+ RGWZoneParams& info,
+ std::unique_ptr<sal::ZoneWriter>* writer) override;
+ int list_zone_names(const DoutPrefixProvider* dpp,
+ optional_yield y, const std::string& marker,
+ std::span<std::string> entries,
+ sal::ListResult<std::string>& result) override;
+
+ int read_period_config(const DoutPrefixProvider* dpp,
+ optional_yield y,
+ std::string_view realm_id,
+ RGWPeriodConfig& info) override;
+ int write_period_config(const DoutPrefixProvider* dpp,
+ optional_yield y, bool exclusive,
+ std::string_view realm_id,
+ const RGWPeriodConfig& info) override;
+
+ private:
+ std::unique_ptr<SQLiteImpl> impl;
+}; // SQLiteConfigStore
+
+
+auto create_sqlite_store(const DoutPrefixProvider* dpp, const std::string& uri)
+ -> std::unique_ptr<config::SQLiteConfigStore>;
+
+} // namespace rgw::dbstore::config
diff --git a/src/rgw/driver/dbstore/config/sqlite_schema.h b/src/rgw/driver/dbstore/config/sqlite_schema.h
new file mode 100644
index 000000000..c8a8fce3e
--- /dev/null
+++ b/src/rgw/driver/dbstore/config/sqlite_schema.h
@@ -0,0 +1,299 @@
+// -*- 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) 2022 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 <initializer_list>
+
+namespace rgw::dbstore::config::schema {
+
+struct Migration {
+ // human-readable description to help with debugging migration errors
+ const char* description = nullptr;
+ // series of sql statements to apply the schema migration
+ const char* up = nullptr;
+ // series of sql statements to undo the schema migration
+ const char* down = nullptr;
+};
+
+static constexpr std::initializer_list<Migration> migrations {{
+ .description = "create the initial ConfigStore tables",
+ .up = R"(
+CREATE TABLE IF NOT EXISTS Realms (
+ ID TEXT PRIMARY KEY NOT NULL,
+ Name TEXT UNIQUE NOT NULL,
+ CurrentPeriod TEXT,
+ Epoch INTEGER DEFAULT 0,
+ VersionNumber INTEGER,
+ VersionTag TEXT
+);
+CREATE TABLE IF NOT EXISTS Periods (
+ ID TEXT NOT NULL,
+ Epoch INTEGER DEFAULT 0,
+ RealmID TEXT NOT NULL REFERENCES Realms (ID),
+ Data TEXT NOT NULL,
+ PRIMARY KEY (ID, Epoch)
+);
+CREATE TABLE IF NOT EXISTS PeriodConfigs (
+ RealmID TEXT PRIMARY KEY NOT NULL REFERENCES Realms (ID),
+ Data TEXT NOT NULL
+);
+CREATE TABLE IF NOT EXISTS ZoneGroups (
+ ID TEXT PRIMARY KEY NOT NULL,
+ Name TEXT UNIQUE NOT NULL,
+ RealmID TEXT NOT NULL REFERENCES Realms (ID),
+ Data TEXT NOT NULL,
+ VersionNumber INTEGER,
+ VersionTag TEXT
+);
+CREATE TABLE IF NOT EXISTS Zones (
+ ID TEXT PRIMARY KEY NOT NULL,
+ Name TEXT UNIQUE NOT NULL,
+ RealmID TEXT NOT NULL REFERENCES Realms (ID),
+ Data TEXT NOT NULL,
+ VersionNumber INTEGER,
+ VersionTag TEXT
+);
+CREATE TABLE IF NOT EXISTS DefaultRealms (
+ ID TEXT,
+ Empty TEXT PRIMARY KEY
+);
+CREATE TABLE IF NOT EXISTS DefaultZoneGroups (
+ ID TEXT,
+ RealmID TEXT PRIMARY KEY REFERENCES Realms (ID)
+);
+CREATE TABLE IF NOT EXISTS DefaultZones (
+ ID TEXT,
+ RealmID TEXT PRIMARY KEY REFERENCES Realms (ID)
+);
+)",
+ .down = R"(
+DROP TABLE IF EXISTS Realms;
+DROP TABLE IF EXISTS Periods;
+DROP TABLE IF EXISTS PeriodConfigs;
+DROP TABLE IF EXISTS ZoneGroups;
+DROP TABLE IF EXISTS Zones;
+DROP TABLE IF EXISTS DefaultRealms;
+DROP TABLE IF EXISTS DefaultZoneGroups;
+DROP TABLE IF EXISTS DefaultZones;
+)"
+ }
+};
+
+
+// DefaultRealms
+
+static constexpr const char* default_realm_insert1 =
+"INSERT INTO DefaultRealms (ID, Empty) VALUES ({}, '')";
+
+static constexpr const char* default_realm_upsert1 =
+R"(INSERT INTO DefaultRealms (ID, Empty) VALUES ({0}, '')
+ON CONFLICT(Empty) DO UPDATE SET ID = {0})";
+
+static constexpr const char* default_realm_select0 =
+"SELECT ID FROM DefaultRealms LIMIT 1";
+
+static constexpr const char* default_realm_delete0 =
+"DELETE FROM DefaultRealms";
+
+
+// Realms
+
+static constexpr const char* realm_update5 =
+"UPDATE Realms SET CurrentPeriod = {1}, Epoch = {2}, VersionNumber = {3} + 1 \
+WHERE ID = {0} AND VersionNumber = {3} AND VersionTag = {4}";
+
+static constexpr const char* realm_rename4 =
+"UPDATE Realms SET Name = {1}, VersionNumber = {2} + 1 \
+WHERE ID = {0} AND VersionNumber = {2} AND VersionTag = {3}";
+
+static constexpr const char* realm_delete3 =
+"DELETE FROM Realms WHERE ID = {} AND VersionNumber = {} AND VersionTag = {}";
+
+static constexpr const char* realm_insert4 =
+"INSERT INTO Realms (ID, Name, VersionNumber, VersionTag) \
+VALUES ({}, {}, {}, {})";
+
+static constexpr const char* realm_upsert4 =
+"INSERT INTO Realms (ID, Name, VersionNumber, VersionTag) \
+VALUES ({0}, {1}, {2}, {3}) \
+ON CONFLICT(ID) DO UPDATE SET Name = {1}, \
+VersionNumber = {2}, VersionTag = {3}";
+
+static constexpr const char* realm_select_id1 =
+"SELECT * FROM Realms WHERE ID = {} LIMIT 1";
+
+static constexpr const char* realm_select_name1 =
+"SELECT * FROM Realms WHERE Name = {} LIMIT 1";
+
+static constexpr const char* realm_select_default0 =
+"SELECT r.* FROM Realms r \
+INNER JOIN DefaultRealms d \
+ON d.ID = r.ID LIMIT 1";
+
+static constexpr const char* realm_select_names2 =
+"SELECT Name FROM Realms WHERE Name > {} \
+ORDER BY Name ASC LIMIT {}";
+
+
+// Periods
+
+static constexpr const char* period_insert4 =
+"INSERT INTO Periods (ID, Epoch, RealmID, Data) \
+VALUES ({}, {}, {}, {})";
+
+static constexpr const char* period_upsert4 =
+"INSERT INTO Periods (ID, Epoch, RealmID, Data) \
+VALUES ({0}, {1}, {2}, {3}) \
+ON CONFLICT DO UPDATE SET RealmID = {2}, Data = {3}";
+
+static constexpr const char* period_select_epoch2 =
+"SELECT * FROM Periods WHERE ID = {} AND Epoch = {} LIMIT 1";
+
+static constexpr const char* period_select_latest1 =
+"SELECT * FROM Periods WHERE ID = {} ORDER BY Epoch DESC LIMIT 1";
+
+static constexpr const char* period_delete1 =
+"DELETE FROM Periods WHERE ID = {}";
+
+static constexpr const char* period_select_ids2 =
+"SELECT ID FROM Periods WHERE ID > {} ORDER BY ID ASC LIMIT {}";
+
+
+// DefaultZoneGroups
+
+static constexpr const char* default_zonegroup_insert2 =
+"INSERT INTO DefaultZoneGroups (RealmID, ID) VALUES ({}, {})";
+
+static constexpr const char* default_zonegroup_upsert2 =
+"INSERT INTO DefaultZoneGroups (RealmID, ID) \
+VALUES ({0}, {1}) \
+ON CONFLICT(RealmID) DO UPDATE SET ID = {1}";
+
+static constexpr const char* default_zonegroup_select1 =
+"SELECT ID FROM DefaultZoneGroups WHERE RealmID = {}";
+
+static constexpr const char* default_zonegroup_delete1 =
+"DELETE FROM DefaultZoneGroups WHERE RealmID = {}";
+
+
+// ZoneGroups
+
+static constexpr const char* zonegroup_update5 =
+"UPDATE ZoneGroups SET RealmID = {1}, Data = {2}, VersionNumber = {3} + 1 \
+WHERE ID = {0} AND VersionNumber = {3} AND VersionTag = {4}";
+
+static constexpr const char* zonegroup_rename4 =
+"UPDATE ZoneGroups SET Name = {1}, VersionNumber = {2} + 1 \
+WHERE ID = {0} AND VersionNumber = {2} AND VersionTag = {3}";
+
+static constexpr const char* zonegroup_delete3 =
+"DELETE FROM ZoneGroups WHERE ID = {} \
+AND VersionNumber = {} AND VersionTag = {}";
+
+static constexpr const char* zonegroup_insert6 =
+"INSERT INTO ZoneGroups (ID, Name, RealmID, Data, VersionNumber, VersionTag) \
+VALUES ({}, {}, {}, {}, {}, {})";
+
+static constexpr const char* zonegroup_upsert6 =
+"INSERT INTO ZoneGroups (ID, Name, RealmID, Data, VersionNumber, VersionTag) \
+VALUES ({0}, {1}, {2}, {3}, {4}, {5}) \
+ON CONFLICT (ID) DO UPDATE SET Name = {1}, RealmID = {2}, \
+Data = {3}, VersionNumber = {4}, VersionTag = {5}";
+
+static constexpr const char* zonegroup_select_id1 =
+"SELECT * FROM ZoneGroups WHERE ID = {} LIMIT 1";
+
+static constexpr const char* zonegroup_select_name1 =
+"SELECT * FROM ZoneGroups WHERE Name = {} LIMIT 1";
+
+static constexpr const char* zonegroup_select_default0 =
+"SELECT z.* FROM ZoneGroups z \
+INNER JOIN DefaultZoneGroups d \
+ON d.ID = z.ID LIMIT 1";
+
+static constexpr const char* zonegroup_select_names2 =
+"SELECT Name FROM ZoneGroups WHERE Name > {} \
+ORDER BY Name ASC LIMIT {}";
+
+
+// DefaultZones
+
+static constexpr const char* default_zone_insert2 =
+"INSERT INTO DefaultZones (RealmID, ID) VALUES ({}, {})";
+
+static constexpr const char* default_zone_upsert2 =
+"INSERT INTO DefaultZones (RealmID, ID) VALUES ({0}, {1}) \
+ON CONFLICT(RealmID) DO UPDATE SET ID = {1}";
+
+static constexpr const char* default_zone_select1 =
+"SELECT ID FROM DefaultZones WHERE RealmID = {}";
+
+static constexpr const char* default_zone_delete1 =
+"DELETE FROM DefaultZones WHERE RealmID = {}";
+
+
+// Zones
+
+static constexpr const char* zone_update5 =
+"UPDATE Zones SET RealmID = {1}, Data = {2}, VersionNumber = {3} + 1 \
+WHERE ID = {0} AND VersionNumber = {3} AND VersionTag = {4}";
+
+static constexpr const char* zone_rename4 =
+"UPDATE Zones SET Name = {1}, VersionNumber = {2} + 1 \
+WHERE ID = {0} AND VersionNumber = {2} AND VersionTag = {3}";
+
+static constexpr const char* zone_delete3 =
+"DELETE FROM Zones WHERE ID = {} AND VersionNumber = {} AND VersionTag = {}";
+
+static constexpr const char* zone_insert6 =
+"INSERT INTO Zones (ID, Name, RealmID, Data, VersionNumber, VersionTag) \
+VALUES ({}, {}, {}, {}, {}, {})";
+
+static constexpr const char* zone_upsert6 =
+"INSERT INTO Zones (ID, Name, RealmID, Data, VersionNumber, VersionTag) \
+VALUES ({0}, {1}, {2}, {3}, {4}, {5}) \
+ON CONFLICT (ID) DO UPDATE SET Name = {1}, RealmID = {2}, \
+Data = {3}, VersionNumber = {4}, VersionTag = {5}";
+
+static constexpr const char* zone_select_id1 =
+"SELECT * FROM Zones WHERE ID = {} LIMIT 1";
+
+static constexpr const char* zone_select_name1 =
+"SELECT * FROM Zones WHERE Name = {} LIMIT 1";
+
+static constexpr const char* zone_select_default0 =
+"SELECT z.* FROM Zones z \
+INNER JOIN DefaultZones d \
+ON d.ID = z.ID LIMIT 1";
+
+static constexpr const char* zone_select_names2 =
+"SELECT Name FROM Zones WHERE Name > {} \
+ORDER BY Name ASC LIMIT {}";
+
+
+// PeriodConfigs
+
+static constexpr const char* period_config_insert2 =
+"INSERT INTO PeriodConfigs (RealmID, Data) VALUES ({}, {})";
+
+static constexpr const char* period_config_upsert2 =
+"INSERT INTO PeriodConfigs (RealmID, Data) VALUES ({0}, {1}) \
+ON CONFLICT (RealmID) DO UPDATE SET Data = {1}";
+
+static constexpr const char* period_config_select1 =
+"SELECT Data FROM PeriodConfigs WHERE RealmID = {} LIMIT 1";
+
+} // namespace rgw::dbstore::config::schema
diff --git a/src/rgw/driver/dbstore/config/store.cc b/src/rgw/driver/dbstore/config/store.cc
new file mode 100644
index 000000000..569a093b7
--- /dev/null
+++ b/src/rgw/driver/dbstore/config/store.cc
@@ -0,0 +1,38 @@
+// -*- 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) 2022 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 <stdexcept>
+
+#include <fmt/format.h>
+
+#include "store.h"
+#ifdef SQLITE_ENABLED
+#include "sqlite.h"
+#endif
+
+namespace rgw::dbstore {
+
+auto create_config_store(const DoutPrefixProvider* dpp, const std::string& uri)
+ -> std::unique_ptr<sal::ConfigStore>
+{
+#ifdef SQLITE_ENABLED
+ if (uri.starts_with("file:")) {
+ return config::create_sqlite_store(dpp, uri);
+ }
+#endif
+ throw std::runtime_error(fmt::format("unrecognized URI {}", uri));
+}
+
+} // namespace rgw::dbstore
diff --git a/src/rgw/driver/dbstore/config/store.h b/src/rgw/driver/dbstore/config/store.h
new file mode 100644
index 000000000..553d9f709
--- /dev/null
+++ b/src/rgw/driver/dbstore/config/store.h
@@ -0,0 +1,27 @@
+// -*- 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) 2022 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 <memory>
+#include "rgw_sal_config.h"
+
+namespace rgw::dbstore {
+
+// ConfigStore factory
+auto create_config_store(const DoutPrefixProvider* dpp, const std::string& uri)
+ -> std::unique_ptr<sal::ConfigStore>;
+
+} // namespace rgw::dbstore
diff --git a/src/rgw/driver/dbstore/dbstore_main.cc b/src/rgw/driver/dbstore/dbstore_main.cc
new file mode 100644
index 000000000..4fff38ced
--- /dev/null
+++ b/src/rgw/driver/dbstore/dbstore_main.cc
@@ -0,0 +1,199 @@
+#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>
+
+using namespace std;
+using namespace rgw::store;
+using DB = rgw::store::DB;
+
+struct thr_args {
+ DB *dbs;
+ int thr_id;
+};
+
+void* process(void *arg)
+{
+ struct thr_args *t_args = (struct thr_args*)arg;
+
+ DB *db = t_args->dbs;
+ int thr_id = t_args->thr_id;
+ int ret = -1;
+
+ cout<<"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";
+
+ DBOpParams params = {};
+ const DoutPrefixProvider *dpp = db->get_def_dpp();
+
+ db->InitializeParams(dpp, &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.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(dpp, "InsertUser", &params);
+ cout << "InsertUser return value: " << ret << "\n";
+
+ DBOpParams params2 = {};
+ params.op.user.uinfo.user_id.tenant = "tenant2";
+
+ db->InitializeParams(dpp, &params2);
+ params2.op.user.uinfo.display_name = user1;
+ ret = db->ProcessOp(dpp, "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";
+
+ 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(dpp, "InsertBucket", &params);
+
+ params.op.user.uinfo.display_name = user2;
+ params.op.user.uinfo.user_id.id = user2;
+ db->ProcessOp(dpp, "InsertUser", &params);
+
+ params.op.bucket.info.bucket.name = bucketb;
+ db->ProcessOp(dpp, "InsertBucket", &params);
+
+ db->ProcessOp(dpp, "GetUser", &params);
+ db->ProcessOp(dpp, "GetBucket", &params);
+
+ db->ListAllUsers(dpp, &params);
+ db->ListAllBuckets(dpp, &params);
+
+ params.op.bucket.info.bucket.name = bucketb;
+
+ db->ProcessOp(dpp, "RemoveBucket", &params);
+
+ params.op.user.uinfo.user_id.id = user2;
+ db->ProcessOp(dpp, "RemoveUser", &params);
+
+ db->ListAllUsers(dpp, &params);
+ db->ListAllBuckets(dpp, &params);
+ cout<<"Exiting thread:"<<thr_id<<"\n";
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ string tenant = "Redhat";
+ string logfile = "rgw_dbstore_bin.log";
+ int loglevel = 20;
+
+ DBStoreManager *dbsm;
+ DB *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-bin logfile loglevel
+ if (argc == 3) {
+ logfile = argv[1];
+ loglevel = (atoi)(argv[2]);
+ cout << "loglevel set to " << loglevel << "\n";
+ }
+
+ vector<const char*> args;
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_DAEMON, CINIT_FLAG_NO_MON_CONFIG, 1);
+ dbsm = new DBStoreManager(cct.get(), logfile, loglevel);
+ dbs = dbsm->getDB(tenant, true);
+
+ cout<<"No. of threads being created = "<<num_thr<<"\n";
+
+ /* Initialize thread creation attributes */
+ rc = pthread_attr_init(&attr);
+
+ if (rc != 0) {
+ cout<<" 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) {
+ cout<<" error in pthread_create \n";
+ goto out;
+ }
+
+ cout<<"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) {
+ cout<<"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) {
+ cout<<"error in pthread_join \n";
+ } else {
+ cout<<"Joined with thread "<<tnum<<"\n";
+ }
+ }
+
+out:
+ dbsm->destroyAllHandles();
+
+ return 0;
+}
diff --git a/src/rgw/driver/dbstore/dbstore_mgr.cc b/src/rgw/driver/dbstore/dbstore_mgr.cc
new file mode 100644
index 000000000..6835f526b
--- /dev/null
+++ b/src/rgw/driver/dbstore/dbstore_mgr.cc
@@ -0,0 +1,140 @@
+// -*- 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_log.h"
+
+#include <filesystem>
+
+static constexpr auto dout_subsys = ceph_subsys_rgw;
+
+using namespace std;
+
+
+/* Given a tenant, find and return the DBStore handle.
+ * If not found and 'create' set to true, create one
+ * and return
+ */
+DB *DBStoreManager::getDB (string tenant, bool create)
+{
+ map<string, DB*>::iterator iter;
+ DB *dbs = nullptr;
+ pair<map<string, DB*>::iterator,bool> ret;
+
+ if (tenant.empty())
+ return default_db;
+
+ if (DBStoreHandles.empty())
+ goto not_found;
+
+ iter = DBStoreHandles.find(tenant);
+
+ if (iter != DBStoreHandles.end())
+ return iter->second;
+
+not_found:
+ if (!create)
+ return nullptr;
+
+ dbs = createDB(tenant);
+
+ return dbs;
+}
+
+/* Create DBStore instance */
+DB *DBStoreManager::createDB(std::string tenant) {
+ DB *dbs = nullptr;
+ pair<map<string, DB*>::iterator,bool> ret;
+ const auto& db_path = g_conf().get_val<std::string>("dbstore_db_dir");
+ const auto& db_name = g_conf().get_val<std::string>("dbstore_db_name_prefix") + "-" + tenant;
+
+ auto db_full_path = std::filesystem::path(db_path) / db_name;
+ ldout(cct, 0) << "DB initialization full db_path("<<db_full_path<<")" << dendl;
+
+ /* Create the handle */
+#ifdef SQLITE_ENABLED
+ dbs = new SQLiteDB(db_full_path.string(), cct);
+#else
+ dbs = new DB(db_full_path.string(), cct);
+#endif
+
+ /* API is DB::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) {
+ ldout(cct, 0) << "DB initialization failed for tenant("<<tenant<<")" << dendl;
+
+ delete dbs;
+ return nullptr;
+ }
+
+ /* XXX: Do we need lock to protect this map?
+ */
+ ret = DBStoreHandles.insert(pair<string, DB*>(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::deleteDB(string tenant) {
+ map<string, DB*>::iterator iter;
+ DB *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(dbs->get_def_dpp());
+ delete dbs;
+
+ return;
+}
+
+void DBStoreManager::deleteDB(DB *dbs) {
+ if (!dbs)
+ return;
+
+ (void)deleteDB(dbs->getDBname());
+}
+
+
+void DBStoreManager::destroyAllHandles(){
+ map<string, DB*>::iterator iter;
+ DB *dbs = nullptr;
+
+ if (DBStoreHandles.empty())
+ return;
+
+ for (iter = DBStoreHandles.begin(); iter != DBStoreHandles.end();
+ ++iter) {
+ dbs = iter->second;
+ dbs->Destroy(dbs->get_def_dpp());
+ delete dbs;
+ }
+
+ DBStoreHandles.clear();
+
+ return;
+}
+
+
diff --git a/src/rgw/driver/dbstore/dbstore_mgr.h b/src/rgw/driver/dbstore/dbstore_mgr.h
new file mode 100644
index 000000000..77fc3aaf7
--- /dev/null
+++ b/src/rgw/driver/dbstore/dbstore_mgr.h
@@ -0,0 +1,56 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <map>
+#include <cerrno>
+#include <cstdlib>
+#include <string>
+#include <cstdio>
+#include <iostream>
+#include <vector>
+
+#include "common/ceph_context.h"
+#include "common/dbstore.h"
+#include "sqlite/sqliteDB.h"
+
+using namespace rgw::store;
+using DB = rgw::store::DB;
+
+/* XXX: Should be a dbstore config option */
+const static std::string default_tenant = "default_ns";
+
+class DBStoreManager {
+private:
+ std::map<std::string, DB*> DBStoreHandles;
+ DB *default_db = nullptr;
+ CephContext *cct;
+
+public:
+ DBStoreManager(CephContext *_cct): DBStoreHandles() {
+ cct = _cct;
+ default_db = createDB(default_tenant);
+ };
+ DBStoreManager(CephContext *_cct, std::string logfile, int loglevel): DBStoreHandles() {
+ /* No ceph context. Create one with log args provided */
+ cct = _cct;
+ cct->_log->set_log_file(logfile);
+ cct->_log->reopen_log_file();
+ cct->_conf->subsys.set_log_level(ceph_subsys_rgw, loglevel);
+ default_db = createDB(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.
+ */
+ DB* getDB () { return default_db; };
+ DB* getDB (std::string tenant, bool create);
+ DB* createDB (std::string tenant);
+ void deleteDB (std::string tenant);
+ void deleteDB (DB* db);
+ void destroyAllHandles();
+};
diff --git a/src/rgw/driver/dbstore/sqlite/CMakeLists.txt b/src/rgw/driver/dbstore/sqlite/CMakeLists.txt
new file mode 100644
index 000000000..909765e30
--- /dev/null
+++ b/src/rgw/driver/dbstore/sqlite/CMakeLists.txt
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 3.14.0)
+project(sqlite_db)
+
+find_package(SQLite3 REQUIRED)
+
+set(sqlite_db_srcs
+ sqliteDB.h
+ sqliteDB.cc)
+
+include_directories(${CMAKE_INCLUDE_DIR})
+
+set(SQLITE_COMPILE_FLAGS "-DSQLITE_THREADSAFE=1")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SQLITE_COMPILE_FLAGS}")
+
+add_library(sqlite_db STATIC ${sqlite_db_srcs})
+target_link_libraries(sqlite_db sqlite3 dbstore_lib rgw_common)
diff --git a/src/rgw/driver/dbstore/sqlite/connection.cc b/src/rgw/driver/dbstore/sqlite/connection.cc
new file mode 100644
index 000000000..143a3a0d5
--- /dev/null
+++ b/src/rgw/driver/dbstore/sqlite/connection.cc
@@ -0,0 +1,34 @@
+// -*- 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) 2022 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 "common/dout.h"
+#include "connection.h"
+#include "error.h"
+
+namespace rgw::dbstore::sqlite {
+
+db_ptr open_database(const char* filename, int flags)
+{
+ sqlite3* db = nullptr;
+ const int result = ::sqlite3_open_v2(filename, &db, flags, nullptr);
+ if (result != SQLITE_OK) {
+ throw std::system_error(result, sqlite::error_category());
+ }
+ // request extended result codes
+ (void) ::sqlite3_extended_result_codes(db, 1);
+ return db_ptr{db};
+}
+
+} // namespace rgw::dbstore::sqlite
diff --git a/src/rgw/driver/dbstore/sqlite/connection.h b/src/rgw/driver/dbstore/sqlite/connection.h
new file mode 100644
index 000000000..6088763fd
--- /dev/null
+++ b/src/rgw/driver/dbstore/sqlite/connection.h
@@ -0,0 +1,64 @@
+// -*- 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) 2022 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 <memory>
+#include <sqlite3.h>
+
+#include <fmt/format.h>
+
+#include "sqlite/statement.h"
+
+class DoutPrefixProvider;
+
+namespace rgw::dbstore::sqlite {
+
+// owning sqlite3 pointer
+struct db_deleter {
+ void operator()(sqlite3* p) const { ::sqlite3_close(p); }
+};
+using db_ptr = std::unique_ptr<sqlite3, db_deleter>;
+
+
+// open the database file or throw on error
+db_ptr open_database(const char* filename, int flags);
+
+
+struct Connection {
+ db_ptr db;
+ // map of statements, prepared on first use
+ std::map<std::string_view, stmt_ptr> statements;
+
+ explicit Connection(db_ptr db) : db(std::move(db)) {}
+};
+
+// sqlite connection factory for ConnectionPool
+class ConnectionFactory {
+ std::string uri;
+ int flags;
+ public:
+ ConnectionFactory(std::string uri, int flags)
+ : uri(std::move(uri)), flags(flags) {}
+
+ auto operator()(const DoutPrefixProvider* dpp)
+ -> std::unique_ptr<Connection>
+ {
+ auto db = open_database(uri.c_str(), flags);
+ return std::make_unique<Connection>(std::move(db));
+ }
+};
+
+} // namespace rgw::dbstore::sqlite
diff --git a/src/rgw/driver/dbstore/sqlite/error.cc b/src/rgw/driver/dbstore/sqlite/error.cc
new file mode 100644
index 000000000..5fe9eb0ae
--- /dev/null
+++ b/src/rgw/driver/dbstore/sqlite/error.cc
@@ -0,0 +1,37 @@
+// -*- 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) 2022 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 "error.h"
+
+namespace rgw::dbstore::sqlite {
+
+const std::error_category& error_category()
+{
+ struct category : std::error_category {
+ const char* name() const noexcept override {
+ return "dbstore:sqlite";
+ }
+ std::string message(int ev) const override {
+ return ::sqlite3_errstr(ev);
+ }
+ std::error_condition default_error_condition(int code) const noexcept override {
+ return {code & 0xFF, category()};
+ }
+ };
+ static category instance;
+ return instance;
+}
+
+} // namespace rgw::dbstore::sqlite
diff --git a/src/rgw/driver/dbstore/sqlite/error.h b/src/rgw/driver/dbstore/sqlite/error.h
new file mode 100644
index 000000000..15396d8ca
--- /dev/null
+++ b/src/rgw/driver/dbstore/sqlite/error.h
@@ -0,0 +1,81 @@
+// -*- 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) 2022 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 <system_error>
+#include <sqlite3.h>
+
+namespace rgw::dbstore::sqlite {
+
+// error category for sqlite extended result codes:
+// https://www.sqlite.org/rescode.html
+const std::error_category& error_category();
+
+
+// sqlite exception type that carries the extended error code and message
+class error : public std::runtime_error {
+ std::error_code ec;
+ public:
+ error(const char* errmsg, std::error_code ec)
+ : runtime_error(errmsg), ec(ec) {}
+ error(sqlite3* db, std::error_code ec) : error(::sqlite3_errmsg(db), ec) {}
+ error(sqlite3* db, int result) : error(db, {result, error_category()}) {}
+ error(sqlite3* db) : error(db, ::sqlite3_extended_errcode(db)) {}
+ std::error_code code() const { return ec; }
+};
+
+
+// sqlite error conditions for primary and extended result codes
+//
+// 'primary' error_conditions will match 'primary' error_codes as well as any
+// 'extended' error_codes whose lowest 8 bits match that primary code. for
+// example, the error_condition for SQLITE_CONSTRAINT will match the error_codes
+// SQLITE_CONSTRAINT and SQLITE_CONSTRAINT_*
+enum class errc {
+ // primary result codes
+ ok = SQLITE_OK,
+ busy = SQLITE_BUSY,
+ constraint = SQLITE_CONSTRAINT,
+ row = SQLITE_ROW,
+ done = SQLITE_DONE,
+
+ // extended result codes
+ primary_key_constraint = SQLITE_CONSTRAINT_PRIMARYKEY,
+ foreign_key_constraint = SQLITE_CONSTRAINT_FOREIGNKEY,
+ unique_constraint = SQLITE_CONSTRAINT_UNIQUE,
+
+ // ..add conditions as needed
+};
+
+inline std::error_code make_error_code(errc e)
+{
+ return {static_cast<int>(e), error_category()};
+}
+
+inline std::error_condition make_error_condition(errc e)
+{
+ return {static_cast<int>(e), error_category()};
+}
+
+} // namespace rgw::dbstore::sqlite
+
+namespace std {
+
+// enable implicit conversions from sqlite::errc to std::error_condition
+template<> struct is_error_condition_enum<
+ rgw::dbstore::sqlite::errc> : public true_type {};
+
+} // namespace std
diff --git a/src/rgw/driver/dbstore/sqlite/sqliteDB.cc b/src/rgw/driver/dbstore/sqlite/sqliteDB.cc
new file mode 100644
index 000000000..dc244c07b
--- /dev/null
+++ b/src/rgw/driver/dbstore/sqlite/sqliteDB.cc
@@ -0,0 +1,2996 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "sqliteDB.h"
+
+using namespace std;
+
+#define SQL_PREPARE(dpp, params, sdb, stmt, ret, Op) \
+ do { \
+ string schema; \
+ schema = Schema(params); \
+ sqlite3_prepare_v2 (*sdb, schema.c_str(), \
+ -1, &stmt , NULL); \
+ if (!stmt) { \
+ ldpp_dout(dpp, 0) <<"failed to prepare statement " \
+ <<"for Op("<<Op<<"); Errmsg -"\
+ <<sqlite3_errmsg(*sdb)<< dendl;\
+ ret = -1; \
+ goto out; \
+ } \
+ ldpp_dout(dpp, 20)<<"Successfully Prepared stmt for Op("<<Op \
+ <<") schema("<<schema<<") stmt("<<stmt<<")"<< dendl; \
+ ret = 0; \
+ } while(0);
+
+#define SQL_BIND_INDEX(dpp, stmt, index, str, sdb) \
+ do { \
+ index = sqlite3_bind_parameter_index(stmt, str); \
+ \
+ if (index <=0) { \
+ ldpp_dout(dpp, 0) <<"failed to fetch bind parameter"\
+ " index for str("<<str<<") in " \
+ <<"stmt("<<stmt<<"); Errmsg -" \
+ <<sqlite3_errmsg(*sdb)<< dendl; \
+ rc = -1; \
+ goto out; \
+ } \
+ ldpp_dout(dpp, 20)<<"Bind parameter index for str(" \
+ <<str<<") in stmt("<<stmt<<") is " \
+ <<index<< dendl; \
+ }while(0);
+
+#define SQL_BIND_TEXT(dpp, stmt, index, str, sdb) \
+ do { \
+ rc = sqlite3_bind_text(stmt, index, str, -1, SQLITE_TRANSIENT); \
+ if (rc != SQLITE_OK) { \
+ ldpp_dout(dpp, 0)<<"sqlite bind text failed for index(" \
+ <<index<<"), str("<<str<<") in stmt(" \
+ <<stmt<<"); Errmsg - "<<sqlite3_errmsg(*sdb) \
+ << dendl; \
+ rc = -1; \
+ goto out; \
+ } \
+ ldpp_dout(dpp, 20)<<"Bind parameter text for index(" \
+ <<index<<") in stmt("<<stmt<<") is " \
+ <<str<< dendl; \
+ }while(0);
+
+#define SQL_BIND_INT(dpp, stmt, index, num, sdb) \
+ do { \
+ rc = sqlite3_bind_int(stmt, index, num); \
+ \
+ if (rc != SQLITE_OK) { \
+ ldpp_dout(dpp, 0)<<"sqlite bind int failed for index(" \
+ <<index<<"), num("<<num<<") in stmt(" \
+ <<stmt<<"); Errmsg - "<<sqlite3_errmsg(*sdb) \
+ << dendl; \
+ rc = -1; \
+ goto out; \
+ } \
+ ldpp_dout(dpp, 20)<<"Bind parameter int for index(" \
+ <<index<<") in stmt("<<stmt<<") is " \
+ <<num<< dendl; \
+ }while(0);
+
+#define SQL_BIND_BLOB(dpp, stmt, index, blob, size, sdb) \
+ do { \
+ rc = sqlite3_bind_blob(stmt, index, blob, size, SQLITE_TRANSIENT); \
+ \
+ if (rc != SQLITE_OK) { \
+ ldpp_dout(dpp, 0)<<"sqlite bind blob failed for index(" \
+ <<index<<"), blob("<<blob<<") in stmt(" \
+ <<stmt<<"); Errmsg - "<<sqlite3_errmsg(*sdb) \
+ << dendl; \
+ rc = -1; \
+ goto out; \
+ } \
+ }while(0);
+
+#define SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, param, sdb) \
+ do { \
+ bufferlist b; \
+ encode(param, b); \
+ SQL_BIND_BLOB(dpp, stmt, index, b.c_str(), b.length(), sdb); \
+ }while(0);
+
+#define SQL_READ_BLOB(dpp, 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) { \
+ ldpp_dout(dpp, 20)<<"Null value for blob index(" \
+ <<index<<") in stmt("<<stmt<<") "<< dendl; \
+ } \
+ }while(0);
+
+#define SQL_DECODE_BLOB_PARAM(dpp, stmt, index, param, sdb) \
+ do { \
+ bufferlist b; \
+ void *blob; \
+ int blob_len = 0; \
+ \
+ SQL_READ_BLOB(dpp, stmt, index, blob, blob_len); \
+ \
+ b.append(reinterpret_cast<char *>(blob), blob_len); \
+ \
+ decode(param, b); \
+ }while(0);
+
+#define SQL_EXECUTE(dpp, params, stmt, cbk, args...) \
+ do{ \
+ const std::lock_guard<std::mutex> lk(((DBOp*)(this))->mtx); \
+ if (!stmt) { \
+ ret = Prepare(dpp, params); \
+ } \
+ \
+ if (!stmt) { \
+ ldpp_dout(dpp, 0) <<"No prepared statement "<< dendl; \
+ goto out; \
+ } \
+ \
+ ret = Bind(dpp, params); \
+ if (ret) { \
+ ldpp_dout(dpp, 0) <<"Bind parameters failed for stmt(" <<stmt<<") "<< dendl; \
+ goto out; \
+ } \
+ \
+ ret = Step(dpp, params->op, stmt, cbk); \
+ \
+ Reset(dpp, stmt); \
+ \
+ if (ret) { \
+ ldpp_dout(dpp, 0) <<"Execution failed for stmt(" <<stmt<<")"<< dendl; \
+ goto out; \
+ } \
+ }while(0);
+
+int SQLiteDB::InitPrepareParams(const DoutPrefixProvider *dpp,
+ DBOpPrepareParams &p_params,
+ DBOpParams* params)
+{
+ std::string bucket;
+
+ if (!params)
+ return -1;
+
+ if (params->user_table.empty()) {
+ params->user_table = getUserTable();
+ }
+ if (params->user_table.empty()) {
+ params->user_table = getUserTable();
+ }
+ if (params->bucket_table.empty()) {
+ params->bucket_table = getBucketTable();
+ }
+ if (params->quota_table.empty()) {
+ params->quota_table = getQuotaTable();
+ }
+ if (params->lc_entry_table.empty()) {
+ params->lc_entry_table = getLCEntryTable();
+ }
+ if (params->lc_head_table.empty()) {
+ params->lc_head_table = getLCHeadTable();
+ }
+
+ p_params.user_table = params->user_table;
+ p_params.bucket_table = params->bucket_table;
+ p_params.quota_table = params->quota_table;
+ p_params.lc_entry_table = params->lc_entry_table;
+ p_params.lc_head_table = params->lc_head_table;
+
+ p_params.op.query_str = params->op.query_str;
+
+ bucket = params->op.bucket.info.bucket.name;
+
+ if (!bucket.empty()) {
+ if (params->object_table.empty()) {
+ params->object_table = getObjectTable(bucket);
+ }
+ if (params->objectdata_table.empty()) {
+ params->objectdata_table = getObjectDataTable(bucket);
+ }
+ if (params->object_view.empty()) {
+ params->object_view = getObjectView(bucket);
+ }
+ if (params->object_trigger.empty()) {
+ params->object_trigger = getObjectTrigger(bucket);
+ }
+ p_params.object_table = params->object_table;
+ p_params.objectdata_table = params->objectdata_table;
+ p_params.object_view = params->object_view;
+ }
+
+ return 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
+};
+
+enum GetObject {
+ ObjName,
+ ObjInstance,
+ ObjNS,
+ ObjBucketName,
+ ACLs,
+ IndexVer,
+ Tag,
+ ObjFlags,
+ VersionedEpoch,
+ ObjCategory,
+ Etag,
+ Owner,
+ OwnerDisplayName,
+ StorageClass,
+ Appendable,
+ ContentType,
+ IndexHashSource,
+ ObjSize,
+ AccountedSize,
+ ObjMtime,
+ Epoch,
+ ObjTag,
+ TailTag,
+ WriteTag,
+ FakeTag,
+ ShadowObj,
+ HasData,
+ IsVersioned,
+ VersionNum,
+ PGVer,
+ ZoneShortID,
+ ObjVersion,
+ ObjVersionTag,
+ ObjAttrs,
+ HeadSize,
+ MaxHeadSize,
+ ObjID,
+ TailInstance,
+ HeadPlacementRuleName,
+ HeadPlacementRuleStorageClass,
+ TailPlacementRuleName,
+ TailPlacementStorageClass,
+ ManifestPartObjs,
+ ManifestPartRules,
+ Omap,
+ IsMultipart,
+ MPPartsList,
+ HeadData,
+ Versions
+};
+
+enum GetObjectData {
+ ObjDataName,
+ ObjDataInstance,
+ ObjDataNS,
+ ObjDataBucketName,
+ ObjDataID,
+ MultipartPartStr,
+ PartNum,
+ Offset,
+ ObjDataSize,
+ ObjDataMtime,
+ ObjData
+};
+
+enum GetLCEntry {
+ LCEntryIndex,
+ LCEntryBucketName,
+ LCEntryStartTime,
+ LCEntryStatus
+};
+
+enum GetLCHead {
+ LCHeadIndex,
+ LCHeadMarker,
+ LCHeadStartDate
+};
+
+static int list_user(const DoutPrefixProvider *dpp, DBOpInfo &op, sqlite3_stmt *stmt) {
+ if (!stmt)
+ return -1;
+
+ 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(dpp, stmt, SwiftKeys, op.user.uinfo.swift_keys, sdb);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, SubUsers, op.user.uinfo.subusers, sdb);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, AccessKeys, op.user.uinfo.access_keys, 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(dpp, 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(dpp, stmt, PlacementTags, op.user.uinfo.placement_tags, sdb);
+
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, BucketQuota, op.user.uinfo.quota.bucket_quota, sdb);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, TempURLKeys, op.user.uinfo.temp_url_keys, sdb);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, UserQuota, op.user.uinfo.quota.user_quota, sdb);
+
+ op.user.uinfo.type = sqlite3_column_int(stmt, TYPE);
+
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, MfaIDs, op.user.uinfo.mfa_ids, sdb);
+
+ SQL_DECODE_BLOB_PARAM(dpp, 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(const DoutPrefixProvider *dpp, DBOpInfo &op, sqlite3_stmt *stmt) {
+ if (!stmt)
+ return -1;
+
+ 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(dpp, 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(dpp, 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(dpp, 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(dpp, 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(dpp, stmt, ObjectLock, op.bucket.info.obj_lock, sdb);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, SyncPolicyInfoGroups, op.bucket.info.sync_policy, sdb);
+ SQL_DECODE_BLOB_PARAM(dpp, 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(dpp, stmt, Mtime, op.bucket.mtime, sdb);
+
+ op.bucket.list_entries.push_back(op.bucket.ent);
+
+ return 0;
+}
+
+static int list_object(const DoutPrefixProvider *dpp, DBOpInfo &op, sqlite3_stmt *stmt) {
+ if (!stmt)
+ return -1;
+
+ //cout<<sqlite3_column_text(stmt, 0)<<", ";
+ //cout<<sqlite3_column_text(stmt, 1) << "\n";
+
+ op.obj.state.exists = true;
+ op.obj.state.obj.key.name = (const char*)sqlite3_column_text(stmt, ObjName);
+ op.bucket.info.bucket.name = (const char*)sqlite3_column_text(stmt, ObjBucketName);
+ op.obj.state.obj.key.instance = (const char*)sqlite3_column_text(stmt, ObjInstance);
+ op.obj.state.obj.key.ns = (const char*)sqlite3_column_text(stmt, ObjNS);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, ACLs, op.obj.acls, sdb);
+ op.obj.index_ver = sqlite3_column_int(stmt, IndexVer);
+ op.obj.tag = (const char*)sqlite3_column_text(stmt, Tag);
+ op.obj.flags = sqlite3_column_int(stmt, ObjFlags);
+ op.obj.versioned_epoch = sqlite3_column_int(stmt, VersionedEpoch);
+ op.obj.category = (RGWObjCategory)sqlite3_column_int(stmt, ObjCategory);
+ op.obj.etag = (const char*)sqlite3_column_text(stmt, Etag);
+ op.obj.owner = (const char*)sqlite3_column_text(stmt, Owner);
+ op.obj.owner_display_name = (const char*)sqlite3_column_text(stmt, OwnerDisplayName);
+ op.obj.storage_class = (const char*)sqlite3_column_text(stmt, StorageClass);
+ op.obj.appendable = sqlite3_column_int(stmt, Appendable);
+ op.obj.content_type = (const char*)sqlite3_column_text(stmt, ContentType);
+ op.obj.state.obj.index_hash_source = (const char*)sqlite3_column_text(stmt, IndexHashSource);
+ op.obj.state.size = sqlite3_column_int(stmt, ObjSize);
+ op.obj.state.accounted_size = sqlite3_column_int(stmt, AccountedSize);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, ObjMtime, op.obj.state.mtime, sdb);
+ op.obj.state.epoch = sqlite3_column_int(stmt, Epoch);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, ObjTag, op.obj.state.obj_tag, sdb);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, TailTag, op.obj.state.tail_tag, sdb);
+ op.obj.state.write_tag = (const char*)sqlite3_column_text(stmt, WriteTag);
+ op.obj.state.fake_tag = sqlite3_column_int(stmt, FakeTag);
+ op.obj.state.shadow_obj = (const char*)sqlite3_column_text(stmt, ShadowObj);
+ op.obj.state.has_data = sqlite3_column_int(stmt, HasData);
+ op.obj.is_versioned = sqlite3_column_int(stmt, IsVersioned);
+ op.obj.version_num = sqlite3_column_int(stmt, VersionNum);
+ op.obj.state.pg_ver = sqlite3_column_int(stmt, PGVer);
+ op.obj.state.zone_short_id = sqlite3_column_int(stmt, ZoneShortID);
+ op.obj.state.objv_tracker.read_version.ver = sqlite3_column_int(stmt, ObjVersion);
+ op.obj.state.objv_tracker.read_version.tag = (const char*)sqlite3_column_text(stmt, ObjVersionTag);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, ObjAttrs, op.obj.state.attrset, sdb);
+ op.obj.head_size = sqlite3_column_int(stmt, HeadSize);
+ op.obj.max_head_size = sqlite3_column_int(stmt, MaxHeadSize);
+ op.obj.obj_id = (const char*)sqlite3_column_text(stmt, ObjID);
+ op.obj.tail_instance = (const char*)sqlite3_column_text(stmt, TailInstance);
+ op.obj.head_placement_rule.name = (const char*)sqlite3_column_text(stmt, HeadPlacementRuleName);
+ op.obj.head_placement_rule.storage_class = (const char*)sqlite3_column_text(stmt, HeadPlacementRuleStorageClass);
+ op.obj.tail_placement.placement_rule.name = (const char*)sqlite3_column_text(stmt, TailPlacementRuleName);
+ op.obj.tail_placement.placement_rule.storage_class = (const char*)sqlite3_column_text(stmt, TailPlacementStorageClass);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, ManifestPartObjs, op.obj.objs, sdb);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, ManifestPartRules, op.obj.rules, sdb);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, Omap, op.obj.omap, sdb);
+ op.obj.is_multipart = sqlite3_column_int(stmt, IsMultipart);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, MPPartsList, op.obj.mp_parts, sdb);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, HeadData, op.obj.head_data, sdb);
+ op.obj.state.data = op.obj.head_data;
+
+ rgw_bucket_dir_entry dent;
+ dent.key.name = op.obj.state.obj.key.name;
+ dent.key.instance = op.obj.state.obj.key.instance;
+ dent.tag = op.obj.tag;
+ dent.flags = op.obj.flags;
+ dent.versioned_epoch = op.obj.versioned_epoch;
+ dent.index_ver = op.obj.index_ver;
+ dent.exists = true;
+ dent.meta.category = op.obj.category;
+ dent.meta.size = op.obj.state.size;
+ dent.meta.accounted_size = op.obj.state.accounted_size;
+ dent.meta.mtime = op.obj.state.mtime;
+ dent.meta.etag = op.obj.etag;
+ dent.meta.owner = op.obj.owner;
+ dent.meta.owner_display_name = op.obj.owner_display_name;
+ dent.meta.content_type = op.obj.content_type;
+ dent.meta.storage_class = op.obj.storage_class;
+ dent.meta.appendable = op.obj.appendable;
+
+ op.obj.list_entries.push_back(dent);
+ return 0;
+}
+
+static int get_objectdata(const DoutPrefixProvider *dpp, DBOpInfo &op, sqlite3_stmt *stmt) {
+ if (!stmt)
+ return -1;
+
+ op.obj.state.obj.key.name = (const char*)sqlite3_column_text(stmt, ObjName);
+ op.bucket.info.bucket.name = (const char*)sqlite3_column_text(stmt, ObjBucketName);
+ op.obj.state.obj.key.instance = (const char*)sqlite3_column_text(stmt, ObjInstance);
+ op.obj.state.obj.key.ns = (const char*)sqlite3_column_text(stmt, ObjNS);
+ op.obj.obj_id = (const char*)sqlite3_column_text(stmt, ObjDataID);
+ op.obj_data.part_num = sqlite3_column_int(stmt, PartNum);
+ op.obj_data.offset = sqlite3_column_int(stmt, Offset);
+ op.obj_data.size = sqlite3_column_int(stmt, ObjDataSize);
+ op.obj_data.multipart_part_str = (const char*)sqlite3_column_text(stmt, MultipartPartStr);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, ObjDataMtime, op.obj.state.mtime, sdb);
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, ObjData, op.obj_data.data, sdb);
+
+ return 0;
+}
+
+static int list_lc_entry(const DoutPrefixProvider *dpp, DBOpInfo &op, sqlite3_stmt *stmt) {
+ if (!stmt)
+ return -1;
+
+ op.lc_entry.index = (const char*)sqlite3_column_text(stmt, LCEntryIndex);
+ op.lc_entry.entry.set_bucket((const char*)sqlite3_column_text(stmt, LCEntryBucketName));
+ op.lc_entry.entry.set_start_time(sqlite3_column_int(stmt, LCEntryStartTime));
+ op.lc_entry.entry.set_status(sqlite3_column_int(stmt, LCEntryStatus));
+
+ op.lc_entry.list_entries.push_back(op.lc_entry.entry);
+
+ return 0;
+}
+
+static int list_lc_head(const DoutPrefixProvider *dpp, DBOpInfo &op, sqlite3_stmt *stmt) {
+ if (!stmt)
+ return -1;
+
+ int64_t start_date;
+
+ op.lc_head.index = (const char*)sqlite3_column_text(stmt, LCHeadIndex);
+ op.lc_head.head.set_marker((const char*)sqlite3_column_text(stmt, LCHeadMarker));
+
+ SQL_DECODE_BLOB_PARAM(dpp, stmt, LCHeadStartDate, start_date, sdb);
+ op.lc_head.head.get_start_date() = start_date;
+
+ return 0;
+}
+
+int SQLiteDB::InitializeDBOps(const DoutPrefixProvider *dpp)
+{
+ (void)createTables(dpp);
+ dbops.InsertUser = make_shared<SQLInsertUser>(&this->db, this->getDBname(), cct);
+ dbops.RemoveUser = make_shared<SQLRemoveUser>(&this->db, this->getDBname(), cct);
+ dbops.GetUser = make_shared<SQLGetUser>(&this->db, this->getDBname(), cct);
+ dbops.InsertBucket = make_shared<SQLInsertBucket>(&this->db, this->getDBname(), cct);
+ dbops.UpdateBucket = make_shared<SQLUpdateBucket>(&this->db, this->getDBname(), cct);
+ dbops.RemoveBucket = make_shared<SQLRemoveBucket>(&this->db, this->getDBname(), cct);
+ dbops.GetBucket = make_shared<SQLGetBucket>(&this->db, this->getDBname(), cct);
+ dbops.ListUserBuckets = make_shared<SQLListUserBuckets>(&this->db, this->getDBname(), cct);
+ dbops.InsertLCEntry = make_shared<SQLInsertLCEntry>(&this->db, this->getDBname(), cct);
+ dbops.RemoveLCEntry = make_shared<SQLRemoveLCEntry>(&this->db, this->getDBname(), cct);
+ dbops.GetLCEntry = make_shared<SQLGetLCEntry>(&this->db, this->getDBname(), cct);
+ dbops.ListLCEntries = make_shared<SQLListLCEntries>(&this->db, this->getDBname(), cct);
+ dbops.InsertLCHead = make_shared<SQLInsertLCHead>(&this->db, this->getDBname(), cct);
+ dbops.RemoveLCHead = make_shared<SQLRemoveLCHead>(&this->db, this->getDBname(), cct);
+ dbops.GetLCHead = make_shared<SQLGetLCHead>(&this->db, this->getDBname(), cct);
+
+ return 0;
+}
+
+void *SQLiteDB::openDB(const DoutPrefixProvider *dpp)
+{
+ string dbname;
+ int rc = 0;
+
+ dbname = getDBfile();
+ if (dbname.empty()) {
+ ldpp_dout(dpp, 0)<<"dbname is NULL" << dendl;
+ goto out;
+ }
+
+ rc = sqlite3_open_v2(dbname.c_str(), (sqlite3**)&db,
+ SQLITE_OPEN_READWRITE |
+ SQLITE_OPEN_CREATE |
+ SQLITE_OPEN_FULLMUTEX,
+ NULL);
+
+ if (rc) {
+ ldpp_dout(dpp, 0) <<"Cant open "<<dbname<<"; Errmsg - "\
+ <<sqlite3_errmsg((sqlite3*)db) << dendl;
+ } else {
+ ldpp_dout(dpp, 0) <<"Opened database("<<dbname<<") successfully" << dendl;
+ }
+
+ exec(dpp, "PRAGMA foreign_keys=ON", NULL);
+
+out:
+ return db;
+}
+
+int SQLiteDB::closeDB(const DoutPrefixProvider *dpp)
+{
+ if (db)
+ sqlite3_close((sqlite3 *)db);
+
+ db = NULL;
+
+ return 0;
+}
+
+int SQLiteDB::Reset(const DoutPrefixProvider *dpp, sqlite3_stmt *stmt)
+{
+ int ret = -1;
+
+ if (!stmt) {
+ return -1;
+ }
+ sqlite3_clear_bindings(stmt);
+ ret = sqlite3_reset(stmt);
+
+ return ret;
+}
+
+int SQLiteDB::Step(const DoutPrefixProvider *dpp, DBOpInfo &op, sqlite3_stmt *stmt,
+ int (*cbk)(const DoutPrefixProvider *dpp, DBOpInfo &op, sqlite3_stmt *stmt))
+{
+ int ret = -1;
+
+ if (!stmt) {
+ return -1;
+ }
+
+again:
+ ret = sqlite3_step(stmt);
+
+ if ((ret != SQLITE_DONE) && (ret != SQLITE_ROW)) {
+ ldpp_dout(dpp, 0)<<"sqlite step failed for stmt("<<stmt \
+ <<"); Errmsg - "<<sqlite3_errmsg((sqlite3*)db) << dendl;
+ return -1;
+ } else if (ret == SQLITE_ROW) {
+ if (cbk) {
+ (*cbk)(dpp, op, stmt);
+ } else {
+ }
+ goto again;
+ }
+
+ ldpp_dout(dpp, 20)<<"sqlite step successfully executed for stmt(" \
+ <<stmt<<") ret = " << ret << dendl;
+
+ return 0;
+}
+
+int SQLiteDB::exec(const DoutPrefixProvider *dpp, 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) {
+ ldpp_dout(dpp, 0) <<"sqlite exec failed for schema("<<schema \
+ <<"); Errmsg - "<<errmsg << dendl;
+ sqlite3_free(errmsg);
+ goto out;
+ }
+ ret = 0;
+ ldpp_dout(dpp, 10) <<"sqlite exec successfully processed for schema(" \
+ <<schema<<")" << dendl;
+out:
+ return ret;
+}
+
+int SQLiteDB::createTables(const DoutPrefixProvider *dpp)
+{
+ int ret = -1;
+ int cu = 0, cb = 0, cq = 0;
+ DBOpParams params = {};
+
+ params.user_table = getUserTable();
+ params.bucket_table = getBucketTable();
+
+ if ((cu = createUserTable(dpp, &params)))
+ goto out;
+
+ if ((cb = createBucketTable(dpp, &params)))
+ goto out;
+
+ if ((cq = createQuotaTable(dpp, &params)))
+ goto out;
+
+ ret = 0;
+out:
+ if (ret) {
+ if (cu)
+ DeleteUserTable(dpp, &params);
+ if (cb)
+ DeleteBucketTable(dpp, &params);
+ ldpp_dout(dpp, 0)<<"Creation of tables failed" << dendl;
+ }
+
+ return ret;
+}
+
+int SQLiteDB::createUserTable(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+
+ schema = CreateTableSchema("User", params);
+
+ ret = exec(dpp, schema.c_str(), NULL);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"CreateUserTable failed" << dendl;
+
+ ldpp_dout(dpp, 20)<<"CreateUserTable suceeded" << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::createBucketTable(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+
+ schema = CreateTableSchema("Bucket", params);
+
+ ret = exec(dpp, schema.c_str(), NULL);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"CreateBucketTable failed " << dendl;
+
+ ldpp_dout(dpp, 20)<<"CreateBucketTable suceeded " << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::createObjectTable(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+
+ schema = CreateTableSchema("Object", params);
+
+ ret = exec(dpp, schema.c_str(), NULL);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"CreateObjectTable failed " << dendl;
+
+ ldpp_dout(dpp, 20)<<"CreateObjectTable suceeded " << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::createObjectTableTrigger(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+
+ schema = CreateTableSchema("ObjectTrigger", params);
+
+ ret = exec(dpp, schema.c_str(), NULL);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"CreateObjectTableTrigger failed " << dendl;
+
+ ldpp_dout(dpp, 20)<<"CreateObjectTableTrigger suceeded " << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::createObjectView(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+
+ schema = CreateTableSchema("ObjectView", params);
+
+ ret = exec(dpp, schema.c_str(), NULL);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"CreateObjectView failed " << dendl;
+
+ ldpp_dout(dpp, 20)<<"CreateObjectView suceeded " << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::createQuotaTable(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+
+ schema = CreateTableSchema("Quota", params);
+
+ ret = exec(dpp, schema.c_str(), NULL);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"CreateQuotaTable failed " << dendl;
+
+ ldpp_dout(dpp, 20)<<"CreateQuotaTable suceeded " << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::createObjectDataTable(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+
+ schema = CreateTableSchema("ObjectData", params);
+
+ ret = exec(dpp, schema.c_str(), NULL);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"CreateObjectDataTable failed " << dendl;
+
+ ldpp_dout(dpp, 20)<<"CreateObjectDataTable suceeded " << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::createLCTables(const DoutPrefixProvider *dpp)
+{
+ int ret = -1;
+ string schema;
+ DBOpParams params = {};
+
+ params.lc_entry_table = getLCEntryTable();
+ params.lc_head_table = getLCHeadTable();
+ params.bucket_table = getBucketTable();
+
+ schema = CreateTableSchema("LCEntry", &params);
+ ret = exec(dpp, schema.c_str(), NULL);
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"CreateLCEntryTable failed" << dendl;
+ return ret;
+ }
+ ldpp_dout(dpp, 20)<<"CreateLCEntryTable suceeded" << dendl;
+
+ schema = CreateTableSchema("LCHead", &params);
+ ret = exec(dpp, schema.c_str(), NULL);
+ if (ret) {
+ ldpp_dout(dpp, 0)<<"CreateLCHeadTable failed" << dendl;
+ (void)DeleteLCEntryTable(dpp, &params);
+ }
+ ldpp_dout(dpp, 20)<<"CreateLCHeadTable suceeded" << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::DeleteUserTable(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+
+ schema = DeleteTableSchema(params->user_table);
+
+ ret = exec(dpp, schema.c_str(), NULL);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"DeleteUserTable failed " << dendl;
+
+ ldpp_dout(dpp, 20)<<"DeleteUserTable suceeded " << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::DeleteBucketTable(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+
+ schema = DeleteTableSchema(params->bucket_table);
+
+ ret = exec(dpp, schema.c_str(), NULL);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"DeletebucketTable failed " << dendl;
+
+ ldpp_dout(dpp, 20)<<"DeletebucketTable suceeded " << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::DeleteObjectTable(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+
+ schema = DeleteTableSchema(params->object_table);
+
+ ret = exec(dpp, schema.c_str(), NULL);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"DeleteObjectTable failed " << dendl;
+
+ ldpp_dout(dpp, 20)<<"DeleteObjectTable suceeded " << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::DeleteObjectDataTable(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+
+ schema = DeleteTableSchema(params->objectdata_table);
+
+ ret = exec(dpp, schema.c_str(), NULL);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"DeleteObjectDataTable failed " << dendl;
+
+ ldpp_dout(dpp, 20)<<"DeleteObjectDataTable suceeded " << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::DeleteQuotaTable(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+
+ schema = DeleteTableSchema(params->quota_table);
+
+ ret = exec(dpp, schema.c_str(), NULL);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"DeleteQuotaTable failed " << dendl;
+
+ ldpp_dout(dpp, 20)<<"DeleteQuotaTable suceeded " << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::DeleteLCEntryTable(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+
+ schema = DeleteTableSchema(params->lc_entry_table);
+ ret = exec(dpp, schema.c_str(), NULL);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"DeleteLCEntryTable failed " << dendl;
+ ldpp_dout(dpp, 20)<<"DeleteLCEntryTable suceeded " << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::DeleteLCHeadTable(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+
+ schema = DeleteTableSchema(params->lc_head_table);
+ ret = exec(dpp, schema.c_str(), NULL);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"DeleteLCHeadTable failed " << dendl;
+ ldpp_dout(dpp, 20)<<"DeleteLCHeadTable suceeded " << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::ListAllUsers(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+
+ schema = ListTableSchema(params->user_table);
+ ret = exec(dpp, schema.c_str(), &list_callback);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"GetUsertable failed " << dendl;
+
+ ldpp_dout(dpp, 20)<<"GetUserTable suceeded " << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::ListAllBuckets(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+
+ schema = ListTableSchema(params->bucket_table);
+
+ ret = exec(dpp, schema.c_str(), &list_callback);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"Listbuckettable failed " << dendl;
+
+ ldpp_dout(dpp, 20)<<"ListbucketTable suceeded " << dendl;
+
+ return ret;
+}
+
+int SQLiteDB::ListAllObjects(const DoutPrefixProvider *dpp, DBOpParams *params)
+{
+ int ret = -1;
+ string schema;
+ map<string, class ObjectOp*>::iterator iter;
+ map<string, class ObjectOp*> objectmap;
+ string bucket;
+
+ objectmap = getObjectMap();
+
+ if (objectmap.empty())
+ ldpp_dout(dpp, 20)<<"objectmap empty " << dendl;
+
+ for (iter = objectmap.begin(); iter != objectmap.end(); ++iter) {
+ bucket = iter->first;
+ params->object_table = getObjectTable(bucket);
+ schema = ListTableSchema(params->object_table);
+
+ ret = exec(dpp, schema.c_str(), &list_callback);
+ if (ret)
+ ldpp_dout(dpp, 0)<<"ListObjecttable failed " << dendl;
+
+ ldpp_dout(dpp, 20)<<"ListObjectTable suceeded " << dendl;
+ }
+
+ return ret;
+}
+
+int SQLObjectOp::InitializeObjectOps(string db_name, const DoutPrefixProvider *dpp)
+{
+ PutObject = make_shared<SQLPutObject>(sdb, db_name, cct);
+ DeleteObject = make_shared<SQLDeleteObject>(sdb, db_name, cct);
+ GetObject = make_shared<SQLGetObject>(sdb, db_name, cct);
+ UpdateObject = make_shared<SQLUpdateObject>(sdb, db_name, cct);
+ ListBucketObjects = make_shared<SQLListBucketObjects>(sdb, db_name, cct);
+ ListVersionedObjects = make_shared<SQLListVersionedObjects>(sdb, db_name, cct);
+ PutObjectData = make_shared<SQLPutObjectData>(sdb, db_name, cct);
+ UpdateObjectData = make_shared<SQLUpdateObjectData>(sdb, db_name, cct);
+ GetObjectData = make_shared<SQLGetObjectData>(sdb, db_name, cct);
+ DeleteObjectData = make_shared<SQLDeleteObjectData>(sdb, db_name, cct);
+ DeleteStaleObjectData = make_shared<SQLDeleteStaleObjectData>(sdb, db_name, cct);
+
+ return 0;
+}
+
+int SQLInsertUser::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLInsertUser - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareInsertUser");
+out:
+ return ret;
+}
+
+int SQLInsertUser::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.tenant, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.user.uinfo.user_id.tenant.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.user_id, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.user.uinfo.user_id.id.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.ns, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.user.uinfo.user_id.ns.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.display_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.user.uinfo.display_name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.user_email, sdb);
+ SQL_BIND_TEXT(dpp, 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(dpp, stmt, index, p_params.op.user.access_keys_id, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, access_key.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.access_keys_secret, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, key.c_str(), sdb);
+
+ }
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.access_keys, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.user.uinfo.access_keys, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.swift_keys, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.user.uinfo.swift_keys, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.subusers, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.user.uinfo.subusers, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.suspended, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.user.uinfo.suspended, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.max_buckets, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.user.uinfo.max_buckets, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.op_mask, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.user.uinfo.op_mask, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.user_caps, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.user.uinfo.caps, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.admin, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.user.uinfo.admin, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.system, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.user.uinfo.system, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.placement_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.user.uinfo.default_placement.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.placement_storage_class, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.user.uinfo.default_placement.storage_class.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.placement_tags, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.user.uinfo.placement_tags, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.bucket_quota, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.user.uinfo.quota.bucket_quota, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.temp_url_keys, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.user.uinfo.temp_url_keys, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.user_quota, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.user.uinfo.quota.user_quota, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.type, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.user.uinfo.type, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.mfa_ids, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.user.uinfo.mfa_ids, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.user_attrs, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.user.user_attrs, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.user_ver, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.user.user_version.ver, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.user_ver_tag, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.user.user_version.tag.c_str(), sdb);
+
+out:
+ return rc;
+}
+
+int SQLInsertUser::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, NULL);
+out:
+ return ret;
+}
+
+int SQLRemoveUser::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLRemoveUser - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareRemoveUser");
+out:
+ return ret;
+}
+
+int SQLRemoveUser::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.user_id, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.user.uinfo.user_id.id.c_str(), sdb);
+
+out:
+ return rc;
+}
+
+int SQLRemoveUser::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, NULL);
+out:
+ return ret;
+}
+
+int SQLGetUser::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLGetUser - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ if (params->op.query_str == "email") {
+ SQL_PREPARE(dpp, p_params, sdb, email_stmt, ret, "PrepareGetUser");
+ } else if (params->op.query_str == "access_key") {
+ SQL_PREPARE(dpp, p_params, sdb, ak_stmt, ret, "PrepareGetUser");
+ } else if (params->op.query_str == "user_id") {
+ SQL_PREPARE(dpp, p_params, sdb, userid_stmt, ret, "PrepareGetUser");
+ } else { // by default by userid
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareGetUser");
+ }
+out:
+ return ret;
+}
+
+int SQLGetUser::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (params->op.query_str == "email") {
+ SQL_BIND_INDEX(dpp, email_stmt, index, p_params.op.user.user_email, sdb);
+ SQL_BIND_TEXT(dpp, 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(dpp, ak_stmt, index, p_params.op.user.access_keys_id, sdb);
+ SQL_BIND_TEXT(dpp, ak_stmt, index, access_key.c_str(), sdb);
+ }
+ } else if (params->op.query_str == "user_id") {
+ SQL_BIND_INDEX(dpp, userid_stmt, index, p_params.op.user.user_id, sdb);
+ SQL_BIND_TEXT(dpp, userid_stmt, index, params->op.user.uinfo.user_id.id.c_str(), sdb);
+ } else { // by default by userid
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.user_id, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.user.uinfo.user_id.id.c_str(), sdb);
+ }
+
+out:
+ return rc;
+}
+
+int SQLGetUser::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ if (params->op.query_str == "email") {
+ SQL_EXECUTE(dpp, params, email_stmt, list_user);
+ } else if (params->op.query_str == "access_key") {
+ SQL_EXECUTE(dpp, params, ak_stmt, list_user);
+ } else if (params->op.query_str == "user_id") {
+ SQL_EXECUTE(dpp, params, userid_stmt, list_user);
+ } else { // by default by userid
+ SQL_EXECUTE(dpp, params, stmt, list_user);
+ }
+
+out:
+ return ret;
+}
+
+int SQLInsertBucket::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLInsertBucket - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareInsertBucket");
+
+out:
+ return ret;
+}
+
+int SQLInsertBucket::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ // user_id here is copied as OwnerID in the bucket table.
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.user.user_id, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.user.uinfo.user_id.id.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.bucket_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.tenant, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.bucket.tenant.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.marker, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.bucket.marker.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.bucket_id, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.bucket.bucket_id.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.size, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.bucket.ent.size, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.size_rounded, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.bucket.ent.size_rounded, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.creation_time, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.bucket.info.creation_time, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.count, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.bucket.ent.count, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.placement_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.placement_rule.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.placement_storage_class, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.placement_rule.storage_class.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.flags, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.bucket.info.flags, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.zonegroup, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.zonegroup.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.has_instance_obj, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.bucket.info.has_instance_obj, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.quota, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.bucket.info.quota, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.requester_pays, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.bucket.info.requester_pays, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.has_website, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.bucket.info.has_website, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.website_conf, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.bucket.info.website_conf, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.swift_versioning, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.bucket.info.swift_versioning, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.swift_ver_location, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.swift_ver_location.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.mdsearch_config, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.bucket.info.mdsearch_config, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.new_bucket_instance_id, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.new_bucket_instance_id.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.obj_lock, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.bucket.info.obj_lock, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.sync_policy_info_groups, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.bucket.info.sync_policy, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.bucket_attrs, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.bucket.bucket_attrs, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.bucket_ver, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.bucket.bucket_version.ver, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.bucket_ver_tag, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.bucket_version.tag.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.mtime, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.bucket.mtime, sdb);
+
+out:
+ return rc;
+}
+
+int SQLInsertBucket::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ class SQLObjectOp *ObPtr = NULL;
+ string bucket_name = params->op.bucket.info.bucket.name;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ ObPtr = new SQLObjectOp(sdb, ctx());
+
+ objectmapInsert(dpp, bucket_name, ObPtr);
+
+ SQL_EXECUTE(dpp, params, stmt, NULL);
+
+ /* Once Bucket is inserted created corresponding object(&data) tables
+ */
+ InitPrepareParams(dpp, p_params, params);
+
+ (void)createObjectTable(dpp, params);
+ (void)createObjectDataTable(dpp, params);
+ (void)createObjectTableTrigger(dpp, params);
+out:
+ return ret;
+}
+
+int SQLUpdateBucket::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLUpdateBucket - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ if (params->op.query_str == "attrs") {
+ SQL_PREPARE(dpp, p_params, sdb, attrs_stmt, ret, "PrepareUpdateBucket");
+ } else if (params->op.query_str == "owner") {
+ SQL_PREPARE(dpp, p_params, sdb, owner_stmt, ret, "PrepareUpdateBucket");
+ } else if (params->op.query_str == "info") {
+ SQL_PREPARE(dpp, p_params, sdb, info_stmt, ret, "PrepareUpdateBucket");
+ } else {
+ ldpp_dout(dpp, 0)<<"In SQLUpdateBucket invalid query_str:" <<
+ params->op.query_str << "" << dendl;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int SQLUpdateBucket::Bind(const DoutPrefixProvider *dpp, 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 {
+ ldpp_dout(dpp, 0)<<"In SQLUpdateBucket invalid query_str:" <<
+ params->op.query_str << "" << dendl;
+ goto out;
+ }
+
+ if (params->op.query_str == "attrs") {
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.bucket_attrs, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.bucket.bucket_attrs, sdb);
+ } else if (params->op.query_str == "owner") {
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.creation_time, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.bucket.info.creation_time, sdb);
+ } else if (params->op.query_str == "info") {
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.tenant, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.bucket.info.bucket.tenant.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.marker, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.bucket.info.bucket.marker.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.bucket_id, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.bucket.info.bucket.bucket_id.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.creation_time, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.bucket.info.creation_time, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.count, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.bucket.ent.count, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.placement_name, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.bucket.info.placement_rule.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.placement_storage_class, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.bucket.info.placement_rule.storage_class.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.flags, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.bucket.info.flags, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.zonegroup, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.bucket.info.zonegroup.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.has_instance_obj, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.bucket.info.has_instance_obj, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.quota, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.bucket.info.quota, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.requester_pays, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.bucket.info.requester_pays, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.has_website, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.bucket.info.has_website, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.website_conf, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.bucket.info.website_conf, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.swift_versioning, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.bucket.info.swift_versioning, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.swift_ver_location, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.bucket.info.swift_ver_location.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.mdsearch_config, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.bucket.info.mdsearch_config, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.new_bucket_instance_id, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.bucket.info.new_bucket_instance_id.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.obj_lock, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.bucket.info.obj_lock, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.sync_policy_info_groups, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.bucket.info.sync_policy, sdb);
+ }
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.user.user_id, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.user.uinfo.user_id.id.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.bucket_name, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.bucket_ver, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.bucket.bucket_version.ver, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.mtime, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.bucket.mtime, sdb);
+
+out:
+ return rc;
+}
+
+int SQLUpdateBucket::Execute(const DoutPrefixProvider *dpp, 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 {
+ ldpp_dout(dpp, 0)<<"In SQLUpdateBucket invalid query_str:" <<
+ params->op.query_str << "" << dendl;
+ goto out;
+ }
+
+ SQL_EXECUTE(dpp, params, *stmt, NULL);
+out:
+ return ret;
+}
+
+int SQLRemoveBucket::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLRemoveBucket - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareRemoveBucket");
+
+out:
+ return ret;
+}
+
+int SQLRemoveBucket::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.bucket_name, sdb);
+
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb);
+
+out:
+ return rc;
+}
+
+int SQLRemoveBucket::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ objectmapDelete(dpp, params->op.bucket.info.bucket.name);
+
+ SQL_EXECUTE(dpp, params, stmt, NULL);
+out:
+ return ret;
+}
+
+int SQLGetBucket::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLGetBucket - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareGetBucket");
+
+out:
+ return ret;
+}
+
+int SQLGetBucket::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.bucket_name, sdb);
+
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb);
+
+out:
+ return rc;
+}
+
+int SQLGetBucket::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ class SQLObjectOp *ObPtr = NULL;
+
+ params->op.name = "GetBucket";
+
+ ObPtr = new SQLObjectOp(sdb, ctx());
+
+ /* For the case when the server restarts, need to reinsert objectmap*/
+ objectmapInsert(dpp, params->op.bucket.info.bucket.name, ObPtr);
+ SQL_EXECUTE(dpp, params, stmt, list_bucket);
+out:
+ return ret;
+}
+
+int SQLListUserBuckets::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLListUserBuckets - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ if (params->op.query_str == "all") {
+ SQL_PREPARE(dpp, p_params, sdb, all_stmt, ret, "PrepareListUserBuckets");
+ }else {
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareListUserBuckets");
+ }
+
+out:
+ return ret;
+}
+
+int SQLListUserBuckets::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+ sqlite3_stmt** pstmt = NULL; // Prepared statement
+
+ if (params->op.query_str == "all") {
+ pstmt = &all_stmt;
+ } else {
+ pstmt = &stmt;
+ }
+
+ if (params->op.query_str != "all") {
+ SQL_BIND_INDEX(dpp, *pstmt, index, p_params.op.user.user_id, sdb);
+ SQL_BIND_TEXT(dpp, *pstmt, index, params->op.user.uinfo.user_id.id.c_str(), sdb);
+ }
+
+ SQL_BIND_INDEX(dpp, *pstmt, index, p_params.op.bucket.min_marker, sdb);
+ SQL_BIND_TEXT(dpp, *pstmt, index, params->op.bucket.min_marker.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *pstmt, index, p_params.op.list_max_count, sdb);
+ SQL_BIND_INT(dpp, *pstmt, index, params->op.list_max_count, sdb);
+
+out:
+ return rc;
+}
+
+int SQLListUserBuckets::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ if (params->op.query_str == "all") {
+ SQL_EXECUTE(dpp, params, all_stmt, list_bucket);
+ } else {
+ SQL_EXECUTE(dpp, params, stmt, list_bucket);
+ }
+out:
+ return ret;
+}
+
+int SQLPutObject::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLPutObject - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PreparePutObject");
+
+out:
+ return ret;
+}
+
+int SQLPutObject::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ int VersionNum = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (params->op.obj.state.obj.key.instance.empty()) {
+ params->op.obj.state.obj.key.instance = "null";
+ }
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.bucket_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_instance, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.instance.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_ns, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.ns.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.acls, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.obj.acls, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.index_ver, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj.index_ver, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.tag, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.tag.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.flags, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj.flags, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.versioned_epoch, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj.versioned_epoch, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_category, sdb);
+ SQL_BIND_INT(dpp, stmt, index, (uint8_t)(params->op.obj.category), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.etag, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.etag.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.owner, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.owner.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.owner_display_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.owner_display_name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.storage_class, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.storage_class.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.appendable, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj.appendable, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.content_type, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.content_type.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.index_hash_source, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.index_hash_source.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_size, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj.state.size, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.accounted_size, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj.state.accounted_size, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.mtime, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.obj.state.mtime, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.epoch, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj.state.epoch, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_tag, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.obj.state.obj_tag, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.tail_tag, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.obj.state.tail_tag, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.write_tag, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.write_tag.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.fake_tag, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj.state.fake_tag, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.shadow_obj, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.shadow_obj.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.has_data, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj.state.has_data, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.is_versioned, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj.is_versioned, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.version_num, sdb);
+ SQL_BIND_INT(dpp, stmt, index, VersionNum, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.pg_ver, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj.state.pg_ver, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.zone_short_id, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj.state.zone_short_id, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_version, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj.state.objv_tracker.read_version.ver, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_version_tag, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.objv_tracker.read_version.tag.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_attrs, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.obj.state.attrset, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.head_size, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj.head_size, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.max_head_size, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj.max_head_size, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_id, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.obj_id.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.tail_instance, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.tail_instance.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.head_placement_rule_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.head_placement_rule.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.head_placement_storage_class, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.head_placement_rule.storage_class.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.tail_placement_rule_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.tail_placement.placement_rule.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.tail_placement_storage_class, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.tail_placement.placement_rule.storage_class.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.manifest_part_objs, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.obj.objs, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.manifest_part_rules, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.obj.rules, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.omap, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.obj.omap, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.is_multipart, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj.is_multipart, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.mp_parts, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.obj.mp_parts, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.head_data, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.obj.head_data, sdb);
+
+out:
+ return rc;
+}
+
+int SQLPutObject::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, NULL);
+out:
+ return ret;
+}
+
+int SQLDeleteObject::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLDeleteObject - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareDeleteObject");
+
+out:
+ return ret;
+}
+
+int SQLDeleteObject::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (params->op.obj.state.obj.key.instance.empty()) {
+ params->op.obj.state.obj.key.instance = "null";
+ }
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.bucket_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_instance, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.instance.c_str(), sdb);
+out:
+ return rc;
+}
+
+int SQLDeleteObject::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, NULL);
+out:
+ return ret;
+}
+
+int SQLGetObject::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLGetObject - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareGetObject");
+
+out:
+ return ret;
+}
+
+int SQLGetObject::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (params->op.obj.state.obj.key.instance.empty()) {
+ params->op.obj.state.obj.key.instance = "null";
+ }
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.bucket_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_instance, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.instance.c_str(), sdb);
+
+out:
+ return rc;
+}
+
+int SQLGetObject::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, list_object);
+out:
+ return ret;
+}
+
+int SQLUpdateObject::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+ struct DBOpParams copy = *params;
+ string bucket_name;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLUpdateObject - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ if (params->op.query_str == "omap") {
+ SQL_PREPARE(dpp, p_params, sdb, omap_stmt, ret, "PrepareUpdateObject");
+ } else if (params->op.query_str == "attrs") {
+ SQL_PREPARE(dpp, p_params, sdb, attrs_stmt, ret, "PrepareUpdateObject");
+ } else if (params->op.query_str == "meta") {
+ SQL_PREPARE(dpp, p_params, sdb, meta_stmt, ret, "PrepareUpdateObject");
+ } else if (params->op.query_str == "mp") {
+ SQL_PREPARE(dpp, p_params, sdb, mp_stmt, ret, "PrepareUpdateObject");
+ } else {
+ ldpp_dout(dpp, 0)<<"In SQLUpdateObject invalid query_str:" <<
+ params->op.query_str << dendl;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int SQLUpdateObject::Bind(const DoutPrefixProvider *dpp, 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 == "omap") {
+ stmt = &omap_stmt;
+ } else if (params->op.query_str == "attrs") {
+ stmt = &attrs_stmt;
+ } else if (params->op.query_str == "meta") {
+ stmt = &meta_stmt;
+ } else if (params->op.query_str == "mp") {
+ stmt = &mp_stmt;
+ } else {
+ ldpp_dout(dpp, 0)<<"In SQLUpdateObject invalid query_str:" <<
+ params->op.query_str << dendl;
+ goto out;
+ }
+
+ if (params->op.obj.state.obj.key.instance.empty()) {
+ params->op.obj.state.obj.key.instance = "null";
+ }
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.bucket.bucket_name, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.obj_name, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.state.obj.key.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.obj_instance, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.state.obj.key.instance.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.mtime, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.obj.state.mtime, sdb);
+
+ if (params->op.query_str == "omap") {
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.omap, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.obj.omap, sdb);
+ }
+ if (params->op.query_str == "attrs") {
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.obj_attrs, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.obj.state.attrset, sdb);
+ }
+ if (params->op.query_str == "mp") {
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.mp_parts, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.obj.mp_parts, sdb);
+ }
+ if (params->op.query_str == "meta") {
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.obj_ns, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.state.obj.key.ns.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.acls, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.obj.acls, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.index_ver, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.index_ver, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.tag, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.tag.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.flags, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.flags, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.versioned_epoch, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.versioned_epoch, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.obj_category, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, (uint8_t)(params->op.obj.category), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.etag, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.etag.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.owner, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.owner.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.owner_display_name, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.owner_display_name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.storage_class, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.storage_class.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.appendable, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.appendable, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.content_type, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.content_type.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.index_hash_source, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.state.obj.index_hash_source.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.obj_size, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.state.size, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.accounted_size, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.state.accounted_size, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.epoch, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.state.epoch, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.obj_tag, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.obj.state.obj_tag, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.tail_tag, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.obj.state.tail_tag, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.write_tag, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.state.write_tag.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.fake_tag, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.state.fake_tag, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.shadow_obj, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.state.shadow_obj.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.has_data, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.state.has_data, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.is_versioned, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.is_versioned, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.version_num, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.version_num, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.pg_ver, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.state.pg_ver, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.zone_short_id, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.state.zone_short_id, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.obj_version, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.state.objv_tracker.read_version.ver, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.obj_version_tag, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.state.objv_tracker.read_version.tag.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.obj_attrs, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.obj.state.attrset, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.head_size, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.head_size, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.max_head_size, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.max_head_size, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.obj_id, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.obj_id.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.tail_instance, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.tail_instance.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.head_placement_rule_name, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.head_placement_rule.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.head_placement_storage_class, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.head_placement_rule.storage_class.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.tail_placement_rule_name, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.tail_placement.placement_rule.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.tail_placement_storage_class, sdb);
+ SQL_BIND_TEXT(dpp, *stmt, index, params->op.obj.tail_placement.placement_rule.storage_class.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.manifest_part_objs, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.obj.objs, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.manifest_part_rules, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.obj.rules, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.omap, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.obj.omap, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.is_multipart, sdb);
+ SQL_BIND_INT(dpp, *stmt, index, params->op.obj.is_multipart, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.mp_parts, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.obj.mp_parts, sdb);
+
+ SQL_BIND_INDEX(dpp, *stmt, index, p_params.op.obj.head_data, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, *stmt, index, params->op.obj.head_data, sdb);
+ }
+
+out:
+ return rc;
+}
+
+int SQLUpdateObject::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ sqlite3_stmt** stmt = NULL; // Prepared statement
+
+ if (params->op.query_str == "omap") {
+ stmt = &omap_stmt;
+ } else if (params->op.query_str == "attrs") {
+ stmt = &attrs_stmt;
+ } else if (params->op.query_str == "meta") {
+ stmt = &meta_stmt;
+ } else if (params->op.query_str == "mp") {
+ stmt = &mp_stmt;
+ } else {
+ ldpp_dout(dpp, 0)<<"In SQLUpdateObject invalid query_str:" <<
+ params->op.query_str << dendl;
+ goto out;
+ }
+
+ SQL_EXECUTE(dpp, params, *stmt, NULL);
+out:
+ return ret;
+}
+
+int SQLListBucketObjects::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLListBucketObjects - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareListBucketObjects");
+
+out:
+ return ret;
+}
+
+int SQLListBucketObjects::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (params->op.obj.state.obj.key.instance.empty()) {
+ params->op.obj.state.obj.key.instance = "null";
+ }
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.bucket_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.min_marker, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.min_marker.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.prefix, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.prefix.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.list_max_count, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.list_max_count, sdb);
+
+out:
+ return rc;
+}
+
+int SQLListBucketObjects::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, list_object);
+out:
+ return ret;
+}
+
+int SQLListVersionedObjects::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLListVersionedObjects - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareListVersionedObjects");
+
+out:
+ return ret;
+}
+
+int SQLListVersionedObjects::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (params->op.obj.state.obj.key.instance.empty()) {
+ params->op.obj.state.obj.key.instance = "null";
+ }
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.bucket_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.list_max_count, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.list_max_count, sdb);
+
+out:
+ return rc;
+}
+
+int SQLListVersionedObjects::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, list_object);
+out:
+ return ret;
+}
+
+int SQLPutObjectData::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLPutObjectData - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PreparePutObjectData");
+
+out:
+ return ret;
+}
+
+int SQLPutObjectData::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (params->op.obj.state.obj.key.instance.empty()) {
+ params->op.obj.state.obj.key.instance = "null";
+ }
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_name, sdb);
+
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_instance, sdb);
+
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.instance.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_ns, sdb);
+
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.ns.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.bucket_name, sdb);
+
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_id, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.obj_id.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj_data.part_num, sdb);
+
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj_data.part_num, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj_data.offset, sdb);
+
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj_data.offset, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj_data.data, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.obj_data.data, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj_data.size, sdb);
+
+ SQL_BIND_INT(dpp, stmt, index, params->op.obj_data.size, sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj_data.multipart_part_str, sdb);
+
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj_data.multipart_part_str.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.mtime, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.obj.state.mtime, sdb);
+
+out:
+ return rc;
+}
+
+int SQLPutObjectData::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, NULL);
+out:
+ return ret;
+}
+
+int SQLUpdateObjectData::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLUpdateObjectData - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareUpdateObjectData");
+
+out:
+ return ret;
+}
+
+int SQLUpdateObjectData::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (params->op.obj.state.obj.key.instance.empty()) {
+ params->op.obj.state.obj.key.instance = "null";
+ }
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_instance, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.instance.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.bucket_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_id, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.obj_id.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.mtime, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.obj.state.mtime, sdb);
+
+out:
+ return rc;
+}
+
+int SQLUpdateObjectData::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, NULL);
+out:
+ return ret;
+}
+
+int SQLGetObjectData::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLGetObjectData - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareGetObjectData");
+
+out:
+ return ret;
+}
+
+int SQLGetObjectData::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (params->op.obj.state.obj.key.instance.empty()) {
+ params->op.obj.state.obj.key.instance = "null";
+ }
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.bucket_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_instance, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.instance.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_id, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.obj_id.c_str(), sdb);
+
+out:
+ return rc;
+}
+
+int SQLGetObjectData::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, get_objectdata);
+out:
+ return ret;
+}
+
+int SQLDeleteObjectData::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLDeleteObjectData - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareDeleteObjectData");
+
+out:
+ return ret;
+}
+
+int SQLDeleteObjectData::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (params->op.obj.state.obj.key.instance.empty()) {
+ params->op.obj.state.obj.key.instance = "null";
+ }
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.bucket.bucket_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.bucket.info.bucket.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.name.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_instance, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.state.obj.key.instance.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.obj_id, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.obj.obj_id.c_str(), sdb);
+
+out:
+ return rc;
+}
+
+int SQLDeleteObjectData::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, NULL);
+out:
+ return ret;
+}
+
+int SQLDeleteStaleObjectData::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLDeleteStaleObjectData - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareDeleteStaleObjectData");
+
+out:
+ return ret;
+}
+
+int SQLDeleteStaleObjectData::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.obj.mtime, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, params->op.obj.state.mtime, sdb);
+
+out:
+ return rc;
+}
+
+int SQLDeleteStaleObjectData::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, NULL);
+out:
+ return ret;
+}
+
+int SQLInsertLCEntry::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLInsertLCEntry - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareInsertLCEntry");
+
+out:
+ return ret;
+}
+
+int SQLInsertLCEntry::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.lc_entry.index, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.lc_entry.index.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.lc_entry.bucket_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.lc_entry.entry.get_bucket().c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.lc_entry.status, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.lc_entry.entry.get_status(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.lc_entry.start_time, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.lc_entry.entry.get_start_time(), sdb);
+
+out:
+ return rc;
+}
+
+int SQLInsertLCEntry::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, NULL);
+out:
+ return ret;
+}
+
+int SQLRemoveLCEntry::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLRemoveLCEntry - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareRemoveLCEntry");
+
+out:
+ return ret;
+}
+
+int SQLRemoveLCEntry::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.lc_entry.index, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.lc_entry.index.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.lc_entry.bucket_name, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.lc_entry.entry.get_bucket().c_str(), sdb);
+
+out:
+ return rc;
+}
+
+int SQLRemoveLCEntry::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, NULL);
+out:
+ return ret;
+}
+
+int SQLGetLCEntry::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ sqlite3_stmt** pstmt = NULL; // Prepared statement
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLGetLCEntry - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ if (params->op.query_str == "get_next_entry") {
+ pstmt = &next_stmt;
+ } else {
+ pstmt = &stmt;
+ }
+ SQL_PREPARE(dpp, p_params, sdb, *pstmt, ret, "PrepareGetLCEntry");
+
+out:
+ return ret;
+}
+
+int SQLGetLCEntry::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+ sqlite3_stmt** pstmt = NULL; // Prepared statement
+
+ if (params->op.query_str == "get_next_entry") {
+ pstmt = &next_stmt;
+ } else {
+ pstmt = &stmt;
+ }
+ SQL_BIND_INDEX(dpp, *pstmt, index, p_params.op.lc_entry.index, sdb);
+ SQL_BIND_TEXT(dpp, *pstmt, index, params->op.lc_entry.index.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, *pstmt, index, p_params.op.lc_entry.bucket_name, sdb);
+ SQL_BIND_TEXT(dpp, *pstmt, index, params->op.lc_entry.entry.get_bucket().c_str(), sdb);
+
+out:
+ return rc;
+}
+
+int SQLGetLCEntry::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ sqlite3_stmt** pstmt = NULL; // Prepared statement
+
+ if (params->op.query_str == "get_next_entry") {
+ pstmt = &next_stmt;
+ } else {
+ pstmt = &stmt;
+ }
+
+ SQL_EXECUTE(dpp, params, *pstmt, list_lc_entry);
+out:
+ return ret;
+}
+
+int SQLListLCEntries::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLListLCEntries - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareListLCEntries");
+
+out:
+ return ret;
+}
+
+int SQLListLCEntries::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.lc_entry.index, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.lc_entry.index.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.lc_entry.min_marker, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.lc_entry.min_marker.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.list_max_count, sdb);
+ SQL_BIND_INT(dpp, stmt, index, params->op.list_max_count, sdb);
+
+out:
+ return rc;
+}
+
+int SQLListLCEntries::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, list_lc_entry);
+out:
+ return ret;
+}
+
+int SQLInsertLCHead::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLInsertLCHead - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareInsertLCHead");
+
+out:
+ return ret;
+}
+
+int SQLInsertLCHead::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.lc_head.index, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.lc_head.index.c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.lc_head.marker, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.lc_head.head.get_marker().c_str(), sdb);
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.lc_head.start_date, sdb);
+ SQL_ENCODE_BLOB_PARAM(dpp, stmt, index, static_cast<int64_t>(params->op.lc_head.head.start_date), sdb);
+
+out:
+ return rc;
+}
+
+int SQLInsertLCHead::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, NULL);
+out:
+ return ret;
+}
+
+int SQLRemoveLCHead::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLRemoveLCHead - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareRemoveLCHead");
+
+out:
+ return ret;
+}
+
+int SQLRemoveLCHead::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.lc_head.index, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.lc_head.index.c_str(), sdb);
+
+out:
+ return rc;
+}
+
+int SQLRemoveLCHead::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ SQL_EXECUTE(dpp, params, stmt, NULL);
+out:
+ return ret;
+}
+
+int SQLGetLCHead::Prepare(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ if (!*sdb) {
+ ldpp_dout(dpp, 0)<<"In SQLGetLCHead - no db" << dendl;
+ goto out;
+ }
+
+ InitPrepareParams(dpp, p_params, params);
+
+ SQL_PREPARE(dpp, p_params, sdb, stmt, ret, "PrepareGetLCHead");
+
+out:
+ return ret;
+}
+
+int SQLGetLCHead::Bind(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int index = -1;
+ int rc = 0;
+ struct DBOpPrepareParams p_params = PrepareParams;
+
+ SQL_BIND_INDEX(dpp, stmt, index, p_params.op.lc_head.index, sdb);
+ SQL_BIND_TEXT(dpp, stmt, index, params->op.lc_head.index.c_str(), sdb);
+
+out:
+ return rc;
+}
+
+int SQLGetLCHead::Execute(const DoutPrefixProvider *dpp, struct DBOpParams *params)
+{
+ int ret = -1;
+
+ // clear the params before fetching the entry
+ params->op.lc_head.head = {};
+ SQL_EXECUTE(dpp, params, stmt, list_lc_head);
+out:
+ return ret;
+}
diff --git a/src/rgw/driver/dbstore/sqlite/sqliteDB.h b/src/rgw/driver/dbstore/sqlite/sqliteDB.h
new file mode 100644
index 000000000..ec0ef2bb2
--- /dev/null
+++ b/src/rgw/driver/dbstore/sqlite/sqliteDB.h
@@ -0,0 +1,551 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string>
+#include <sqlite3.h>
+#include "rgw/driver/dbstore/common/dbstore.h"
+
+using namespace rgw::store;
+
+class SQLiteDB : public DB, virtual public DBOp {
+ private:
+ sqlite3_mutex *mutex = NULL;
+
+ protected:
+ CephContext *cct;
+
+ public:
+ sqlite3_stmt *stmt = NULL;
+ DBOpPrepareParams PrepareParams;
+
+ SQLiteDB(sqlite3 *dbi, std::string db_name, CephContext *_cct) : DB(db_name, _cct), cct(_cct) {
+ db = (void*)dbi;
+ }
+ SQLiteDB(std::string db_name, CephContext *_cct) : DB(db_name, _cct), cct(_cct) {
+ }
+ ~SQLiteDB() {}
+
+ uint64_t get_blob_limit() override { return SQLITE_LIMIT_LENGTH; }
+ void *openDB(const DoutPrefixProvider *dpp) override;
+ int closeDB(const DoutPrefixProvider *dpp) override;
+ int InitializeDBOps(const DoutPrefixProvider *dpp) override;
+
+ int InitPrepareParams(const DoutPrefixProvider *dpp, DBOpPrepareParams &p_params,
+ DBOpParams* params) override;
+
+ int exec(const DoutPrefixProvider *dpp, const char *schema,
+ int (*callback)(void*,int,char**,char**));
+ int Step(const DoutPrefixProvider *dpp, DBOpInfo &op, sqlite3_stmt *stmt,
+ int (*cbk)(const DoutPrefixProvider *dpp, DBOpInfo &op, sqlite3_stmt *stmt));
+ int Reset(const DoutPrefixProvider *dpp, sqlite3_stmt *stmt);
+ /* default value matches with sqliteDB style */
+
+ int createTables(const DoutPrefixProvider *dpp) override;
+ int createBucketTable(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int createUserTable(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int createObjectTable(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int createObjectDataTable(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int createObjectView(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int createObjectTableTrigger(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int createQuotaTable(const DoutPrefixProvider *dpp, DBOpParams *params);
+ void populate_object_params(const DoutPrefixProvider *dpp,
+ struct DBOpPrepareParams& p_params,
+ struct DBOpParams* params, bool data);
+
+ int createLCTables(const DoutPrefixProvider *dpp) override;
+
+ int DeleteBucketTable(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int DeleteUserTable(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int DeleteObjectTable(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int DeleteObjectDataTable(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int DeleteQuotaTable(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int DeleteLCEntryTable(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int DeleteLCHeadTable(const DoutPrefixProvider *dpp, DBOpParams *params);
+
+ int ListAllBuckets(const DoutPrefixProvider *dpp, DBOpParams *params) override;
+ int ListAllUsers(const DoutPrefixProvider *dpp, DBOpParams *params) override;
+ int ListAllObjects(const DoutPrefixProvider *dpp, DBOpParams *params) override;
+};
+
+class SQLObjectOp : public ObjectOp {
+ private:
+ sqlite3 **sdb = NULL;
+ CephContext *cct;
+
+ public:
+ SQLObjectOp(sqlite3 **sdbi, CephContext *_cct) : sdb(sdbi), cct(_cct) {};
+ ~SQLObjectOp() {}
+
+ int InitializeObjectOps(std::string db_name, const DoutPrefixProvider *dpp);
+};
+
+class SQLInsertUser : public SQLiteDB, public InsertUserOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLInsertUser(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ ~SQLInsertUser() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLRemoveUser : public SQLiteDB, public RemoveUserOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLRemoveUser(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ ~SQLRemoveUser() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, 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, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), 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(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLInsertBucket : public SQLiteDB, public InsertBucketOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLInsertBucket(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ ~SQLInsertBucket() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, 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, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), 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(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLRemoveBucket : public SQLiteDB, public RemoveBucketOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLRemoveBucket(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ ~SQLRemoveBucket() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLGetBucket : public SQLiteDB, public GetBucketOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLGetBucket(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ ~SQLGetBucket() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLListUserBuckets : public SQLiteDB, public ListUserBucketsOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+ sqlite3_stmt *all_stmt = NULL; // Prepared statement
+
+ public:
+ SQLListUserBuckets(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ ~SQLListUserBuckets() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ if (all_stmt)
+ sqlite3_finalize(all_stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLPutObject : public SQLiteDB, public PutObjectOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLPutObject(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ SQLPutObject(sqlite3 **sdbi, std::string db_name, CephContext *cct) : SQLiteDB(*sdbi, db_name, cct), sdb(sdbi) {}
+
+ ~SQLPutObject() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLDeleteObject : public SQLiteDB, public DeleteObjectOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLDeleteObject(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ SQLDeleteObject(sqlite3 **sdbi, std::string db_name, CephContext *cct) : SQLiteDB(*sdbi, db_name, cct), sdb(sdbi) {}
+
+ ~SQLDeleteObject() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLGetObject : public SQLiteDB, public GetObjectOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLGetObject(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ SQLGetObject(sqlite3 **sdbi, std::string db_name, CephContext *cct) : SQLiteDB(*sdbi, db_name, cct), sdb(sdbi) {}
+
+ ~SQLGetObject() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLUpdateObject : public SQLiteDB, public UpdateObjectOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *omap_stmt = NULL; // Prepared statement
+ sqlite3_stmt *attrs_stmt = NULL; // Prepared statement
+ sqlite3_stmt *meta_stmt = NULL; // Prepared statement
+ sqlite3_stmt *mp_stmt = NULL; // Prepared statement
+
+ public:
+ SQLUpdateObject(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ SQLUpdateObject(sqlite3 **sdbi, std::string db_name, CephContext *cct) : SQLiteDB(*sdbi, db_name, cct), sdb(sdbi) {}
+
+ ~SQLUpdateObject() {
+ if (omap_stmt)
+ sqlite3_finalize(omap_stmt);
+ if (attrs_stmt)
+ sqlite3_finalize(attrs_stmt);
+ if (meta_stmt)
+ sqlite3_finalize(meta_stmt);
+ }
+
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLListBucketObjects : public SQLiteDB, public ListBucketObjectsOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLListBucketObjects(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ SQLListBucketObjects(sqlite3 **sdbi, std::string db_name, CephContext *cct) : SQLiteDB(*sdbi, db_name, cct), sdb(sdbi) {}
+
+ ~SQLListBucketObjects() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLListVersionedObjects : public SQLiteDB, public ListVersionedObjectsOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLListVersionedObjects(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ SQLListVersionedObjects(sqlite3 **sdbi, std::string db_name, CephContext *cct) : SQLiteDB(*sdbi, db_name, cct), sdb(sdbi) {}
+
+ ~SQLListVersionedObjects() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLPutObjectData : public SQLiteDB, public PutObjectDataOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLPutObjectData(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ SQLPutObjectData(sqlite3 **sdbi, std::string db_name, CephContext *cct) : SQLiteDB(*sdbi, db_name, cct), sdb(sdbi) {}
+
+ ~SQLPutObjectData() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLUpdateObjectData : public SQLiteDB, public UpdateObjectDataOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLUpdateObjectData(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ SQLUpdateObjectData(sqlite3 **sdbi, std::string db_name, CephContext *cct) : SQLiteDB(*sdbi, db_name, cct), sdb(sdbi) {}
+
+ ~SQLUpdateObjectData() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLGetObjectData : public SQLiteDB, public GetObjectDataOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLGetObjectData(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ SQLGetObjectData(sqlite3 **sdbi, std::string db_name, CephContext *cct) : SQLiteDB(*sdbi, db_name, cct), sdb(sdbi) {}
+
+ ~SQLGetObjectData() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLDeleteObjectData : public SQLiteDB, public DeleteObjectDataOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLDeleteObjectData(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ SQLDeleteObjectData(sqlite3 **sdbi, std::string db_name, CephContext *cct) : SQLiteDB(*sdbi, db_name, cct), sdb(sdbi) {}
+
+ ~SQLDeleteObjectData() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLDeleteStaleObjectData : public SQLiteDB, public DeleteStaleObjectDataOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLDeleteStaleObjectData(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ SQLDeleteStaleObjectData(sqlite3 **sdbi, std::string db_name, CephContext *cct) : SQLiteDB(*sdbi, db_name, cct), sdb(sdbi) {}
+
+ ~SQLDeleteStaleObjectData() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLInsertLCEntry : public SQLiteDB, public InsertLCEntryOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLInsertLCEntry(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ ~SQLInsertLCEntry() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLRemoveLCEntry : public SQLiteDB, public RemoveLCEntryOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLRemoveLCEntry(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ ~SQLRemoveLCEntry() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLGetLCEntry : public SQLiteDB, public GetLCEntryOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+ sqlite3_stmt *next_stmt = NULL; // Prepared statement
+
+ public:
+ SQLGetLCEntry(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ ~SQLGetLCEntry() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ if (next_stmt)
+ sqlite3_finalize(next_stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLListLCEntries : public SQLiteDB, public ListLCEntriesOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLListLCEntries(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ ~SQLListLCEntries() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLInsertLCHead : public SQLiteDB, public InsertLCHeadOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLInsertLCHead(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ ~SQLInsertLCHead() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLRemoveLCHead : public SQLiteDB, public RemoveLCHeadOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLRemoveLCHead(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ ~SQLRemoveLCHead() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
+
+class SQLGetLCHead : public SQLiteDB, public GetLCHeadOp {
+ private:
+ sqlite3 **sdb = NULL;
+ sqlite3_stmt *stmt = NULL; // Prepared statement
+
+ public:
+ SQLGetLCHead(void **db, std::string db_name, CephContext *cct) : SQLiteDB((sqlite3 *)(*db), db_name, cct), sdb((sqlite3 **)db) {}
+ ~SQLGetLCHead() {
+ if (stmt)
+ sqlite3_finalize(stmt);
+ }
+ int Prepare(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Execute(const DoutPrefixProvider *dpp, DBOpParams *params);
+ int Bind(const DoutPrefixProvider *dpp, DBOpParams *params);
+};
diff --git a/src/rgw/driver/dbstore/sqlite/statement.cc b/src/rgw/driver/dbstore/sqlite/statement.cc
new file mode 100644
index 000000000..dcf7dba9c
--- /dev/null
+++ b/src/rgw/driver/dbstore/sqlite/statement.cc
@@ -0,0 +1,196 @@
+// -*- 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) 2022 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 "common/dout.h"
+#include "error.h"
+#include "statement.h"
+
+#define dout_subsys ceph_subsys_rgw_dbstore
+
+namespace rgw::dbstore::sqlite {
+
+// owning pointer to arbitrary memory allocated and returned by sqlite3
+struct sqlite_deleter {
+ template <typename T>
+ void operator()(T* p) { ::sqlite3_free(p); }
+};
+template <typename T>
+using sqlite_ptr = std::unique_ptr<T, sqlite_deleter>;
+
+
+stmt_ptr prepare_statement(const DoutPrefixProvider* dpp,
+ sqlite3* db, std::string_view sql)
+{
+ sqlite3_stmt* stmt = nullptr;
+ int result = ::sqlite3_prepare_v2(db, sql.data(), sql.size(), &stmt, nullptr);
+ auto ec = std::error_code{result, sqlite::error_category()};
+ if (ec != sqlite::errc::ok) {
+ const char* errmsg = ::sqlite3_errmsg(db);
+ ldpp_dout(dpp, 1) << "preparation failed: " << errmsg
+ << " (" << ec << ")\nstatement: " << sql << dendl;
+ throw sqlite::error(errmsg, ec);
+ }
+ return stmt_ptr{stmt};
+}
+
+static int bind_index(const DoutPrefixProvider* dpp,
+ const stmt_binding& stmt, const char* name)
+{
+ const int index = ::sqlite3_bind_parameter_index(stmt.get(), name);
+ if (index <= 0) {
+ ldpp_dout(dpp, 1) << "binding failed on parameter name="
+ << name << dendl;
+ sqlite3* db = ::sqlite3_db_handle(stmt.get());
+ throw sqlite::error(db);
+ }
+ return index;
+}
+
+void bind_text(const DoutPrefixProvider* dpp, const stmt_binding& stmt,
+ const char* name, std::string_view value)
+{
+ const int index = bind_index(dpp, stmt, name);
+
+ int result = ::sqlite3_bind_text(stmt.get(), index, value.data(),
+ value.size(), SQLITE_STATIC);
+ auto ec = std::error_code{result, sqlite::error_category()};
+ if (ec != sqlite::errc::ok) {
+ ldpp_dout(dpp, 1) << "binding failed on parameter name="
+ << name << " value=" << value << dendl;
+ sqlite3* db = ::sqlite3_db_handle(stmt.get());
+ throw sqlite::error(db, ec);
+ }
+}
+
+void bind_int(const DoutPrefixProvider* dpp, const stmt_binding& stmt,
+ const char* name, int value)
+{
+ const int index = bind_index(dpp, stmt, name);
+
+ int result = ::sqlite3_bind_int(stmt.get(), index, value);
+ auto ec = std::error_code{result, sqlite::error_category()};
+ if (ec != sqlite::errc::ok) {
+ ldpp_dout(dpp, 1) << "binding failed on parameter name="
+ << name << " value=" << value << dendl;
+ sqlite3* db = ::sqlite3_db_handle(stmt.get());
+ throw sqlite::error(db, ec);
+ }
+}
+
+void eval0(const DoutPrefixProvider* dpp, const stmt_execution& stmt)
+{
+ sqlite_ptr<char> sql;
+ if (dpp->get_cct()->_conf->subsys.should_gather<dout_subsys, 20>()) {
+ sql.reset(::sqlite3_expanded_sql(stmt.get()));
+ }
+
+ const int result = ::sqlite3_step(stmt.get());
+ auto ec = std::error_code{result, sqlite::error_category()};
+ sqlite3* db = ::sqlite3_db_handle(stmt.get());
+
+ if (ec != sqlite::errc::done) {
+ const char* errmsg = ::sqlite3_errmsg(db);
+ ldpp_dout(dpp, 20) << "evaluation failed: " << errmsg
+ << " (" << ec << ")\nstatement: " << sql.get() << dendl;
+ throw sqlite::error(errmsg, ec);
+ }
+ ldpp_dout(dpp, 20) << "evaluation succeeded: " << sql.get() << dendl;
+}
+
+void eval1(const DoutPrefixProvider* dpp, const stmt_execution& stmt)
+{
+ sqlite_ptr<char> sql;
+ if (dpp->get_cct()->_conf->subsys.should_gather<dout_subsys, 20>()) {
+ sql.reset(::sqlite3_expanded_sql(stmt.get()));
+ }
+
+ const int result = ::sqlite3_step(stmt.get());
+ auto ec = std::error_code{result, sqlite::error_category()};
+ if (ec != sqlite::errc::row) {
+ sqlite3* db = ::sqlite3_db_handle(stmt.get());
+ const char* errmsg = ::sqlite3_errmsg(db);
+ ldpp_dout(dpp, 1) << "evaluation failed: " << errmsg << " (" << ec
+ << ")\nstatement: " << sql.get() << dendl;
+ throw sqlite::error(errmsg, ec);
+ }
+ ldpp_dout(dpp, 20) << "evaluation succeeded: " << sql.get() << dendl;
+}
+
+int column_int(const stmt_execution& stmt, int column)
+{
+ return ::sqlite3_column_int(stmt.get(), column);
+}
+
+std::string column_text(const stmt_execution& stmt, int column)
+{
+ const unsigned char* text = ::sqlite3_column_text(stmt.get(), column);
+ // may be NULL
+ if (text) {
+ const std::size_t size = ::sqlite3_column_bytes(stmt.get(), column);
+ return {reinterpret_cast<const char*>(text), size};
+ } else {
+ return {};
+ }
+}
+
+auto read_text_rows(const DoutPrefixProvider* dpp,
+ const stmt_execution& stmt,
+ std::span<std::string> entries)
+ -> std::span<std::string>
+{
+ sqlite_ptr<char> sql;
+ if (dpp->get_cct()->_conf->subsys.should_gather<dout_subsys, 20>()) {
+ sql.reset(::sqlite3_expanded_sql(stmt.get()));
+ }
+
+ std::size_t count = 0;
+ while (count < entries.size()) {
+ const int result = ::sqlite3_step(stmt.get());
+ auto ec = std::error_code{result, sqlite::error_category()};
+ if (ec == sqlite::errc::done) {
+ break;
+ }
+ if (ec != sqlite::errc::row) {
+ sqlite3* db = ::sqlite3_db_handle(stmt.get());
+ const char* errmsg = ::sqlite3_errmsg(db);
+ ldpp_dout(dpp, 1) << "evaluation failed: " << errmsg << " (" << ec
+ << ")\nstatement: " << sql.get() << dendl;
+ throw sqlite::error(errmsg, ec);
+ }
+ entries[count] = column_text(stmt, 0);
+ ++count;
+ }
+ ldpp_dout(dpp, 20) << "statement evaluation produced " << count
+ << " results: " << sql.get() << dendl;
+
+ return entries.first(count);
+}
+
+void execute(const DoutPrefixProvider* dpp, sqlite3* db, const char* query,
+ sqlite3_callback callback, void* arg)
+{
+ char* errmsg = nullptr;
+ const int result = ::sqlite3_exec(db, query, callback, arg, &errmsg);
+ auto ec = std::error_code{result, sqlite::error_category()};
+ auto ptr = sqlite_ptr<char>{errmsg}; // free on destruction
+ if (ec != sqlite::errc::ok) {
+ ldpp_dout(dpp, 1) << "query execution failed: " << errmsg << " (" << ec
+ << ")\nquery: " << query << dendl;
+ throw sqlite::error(errmsg, ec);
+ }
+ ldpp_dout(dpp, 20) << "query execution succeeded: " << query << dendl;
+}
+
+} // namespace rgw::dbstore::sqlite
diff --git a/src/rgw/driver/dbstore/sqlite/statement.h b/src/rgw/driver/dbstore/sqlite/statement.h
new file mode 100644
index 000000000..98b4acfea
--- /dev/null
+++ b/src/rgw/driver/dbstore/sqlite/statement.h
@@ -0,0 +1,83 @@
+// -*- 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) 2022 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 <memory>
+#include <span>
+#include <string>
+
+#include <sqlite3.h>
+
+class DoutPrefixProvider;
+
+namespace rgw::dbstore::sqlite {
+
+// owning sqlite3_stmt pointer
+struct stmt_deleter {
+ void operator()(sqlite3_stmt* p) const { ::sqlite3_finalize(p); }
+};
+using stmt_ptr = std::unique_ptr<sqlite3_stmt, stmt_deleter>;
+
+// non-owning sqlite3_stmt pointer that clears binding state on destruction
+struct stmt_binding_deleter {
+ void operator()(sqlite3_stmt* p) const { ::sqlite3_clear_bindings(p); }
+};
+using stmt_binding = std::unique_ptr<sqlite3_stmt, stmt_binding_deleter>;
+
+// non-owning sqlite3_stmt pointer that clears execution state on destruction
+struct stmt_execution_deleter {
+ void operator()(sqlite3_stmt* p) const { ::sqlite3_reset(p); }
+};
+using stmt_execution = std::unique_ptr<sqlite3_stmt, stmt_execution_deleter>;
+
+
+// prepare the sql statement or throw on error
+stmt_ptr prepare_statement(const DoutPrefixProvider* dpp,
+ sqlite3* db, std::string_view sql);
+
+// bind an input string for the given parameter name
+void bind_text(const DoutPrefixProvider* dpp, const stmt_binding& stmt,
+ const char* name, std::string_view value);
+
+// bind an input integer for the given parameter name
+void bind_int(const DoutPrefixProvider* dpp, const stmt_binding& stmt,
+ const char* name, int value);
+
+// evaluate a prepared statement, expecting no result rows
+void eval0(const DoutPrefixProvider* dpp, const stmt_execution& stmt);
+
+// evaluate a prepared statement, expecting a single result row
+void eval1(const DoutPrefixProvider* dpp, const stmt_execution& stmt);
+
+// return the given column as an integer
+int column_int(const stmt_execution& stmt, int column);
+
+// return the given column as text, or an empty string on NULL
+std::string column_text(const stmt_execution& stmt, int column);
+
+// read the text column from each result row into the given entries, and return
+// the sub-span of entries that contain results
+auto read_text_rows(const DoutPrefixProvider* dpp,
+ const stmt_execution& stmt,
+ std::span<std::string> entries)
+ -> std::span<std::string>;
+
+// execute a raw query without preparing a statement. the optional callback
+// can be used to read results
+void execute(const DoutPrefixProvider* dpp, sqlite3* db, const char* query,
+ sqlite3_callback callback, void* arg);
+
+} // namespace rgw::dbstore::sqlite
diff --git a/src/rgw/driver/dbstore/tests/CMakeLists.txt b/src/rgw/driver/dbstore/tests/CMakeLists.txt
new file mode 100644
index 000000000..4e60dcf5e
--- /dev/null
+++ b/src/rgw/driver/dbstore/tests/CMakeLists.txt
@@ -0,0 +1,17 @@
+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(unittest_dbstore_tests ${dbstore_tests_srcs})
+target_link_libraries(unittest_dbstore_tests ${CMAKE_LINK_LIBRARIES})
+add_ceph_unittest(unittest_dbstore_tests)
+
+add_executable(unittest_dbstore_mgr_tests dbstore_mgr_tests.cc)
+target_link_libraries(unittest_dbstore_mgr_tests dbstore gtest_main)
+add_ceph_unittest(unittest_dbstore_mgr_tests)
diff --git a/src/rgw/driver/dbstore/tests/dbstore_mgr_tests.cc b/src/rgw/driver/dbstore/tests/dbstore_mgr_tests.cc
new file mode 100644
index 000000000..02ecd9f15
--- /dev/null
+++ b/src/rgw/driver/dbstore/tests/dbstore_mgr_tests.cc
@@ -0,0 +1,157 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "common/ceph_context.h"
+#include "rgw/driver/dbstore/dbstore_mgr.h"
+
+#include <filesystem>
+#include <gtest/gtest.h>
+#include <memory>
+
+using namespace rgw;
+namespace fs = std::filesystem;
+const static std::string TEST_DIR = "rgw_dbstore_tests";
+
+bool endsWith(const std::string &mainStr, const std::string &toMatch)
+{
+ if(mainStr.size() >= toMatch.size() &&
+ mainStr.compare(mainStr.size() - toMatch.size(), toMatch.size(), toMatch) == 0)
+ return true;
+ else
+ return false;
+}
+
+class TestDBStoreManager : public ::testing::Test {
+protected:
+ void SetUp() override {
+ ctx_ = std::make_shared<CephContext>(CEPH_ENTITY_TYPE_CLIENT);
+ g_ceph_context = ctx_.get();
+ fs::current_path(fs::temp_directory_path());
+ fs::create_directory(TEST_DIR);
+ }
+
+ void TearDown() override {
+ fs::current_path(fs::temp_directory_path());
+ fs::remove_all(TEST_DIR);
+ }
+
+ std::string getTestDir() const {
+ auto test_dir = fs::temp_directory_path() / TEST_DIR;
+ return test_dir.string();
+ }
+
+ fs::path getDBFullPath(const std::string & base_dir,
+ const std::string & tenant) const {
+ auto db_path = ctx_->_conf.get_val<std::string>("dbstore_db_dir");
+ const auto& db_name = ctx_->_conf.get_val<std::string>("dbstore_db_name_prefix") + "-" + tenant + ".db";
+
+ auto db_full_path = std::filesystem::path(db_path) / db_name;
+ auto db_full_path_test = fs::path(base_dir) / db_full_path;
+ return db_full_path_test;
+ }
+
+ std::string getDBTenant(const std::string & base_dir,
+ const std::string & tenant) const {
+ auto db_name = ctx_->_conf.get_val<std::string>("dbstore_db_name_prefix");
+ db_name += "-" + tenant;
+ auto db_full_path = fs::path(base_dir) / db_name;
+ return db_full_path.string();
+ }
+
+ std::string getDBTenant(const std::string & tenant = default_tenant) const {
+ return getDBTenant(getTestDir(), tenant);
+ }
+
+ fs::path getDBFullPath(const std::string & tenant) const {
+ return getDBFullPath(getTestDir(), tenant);
+ }
+
+ fs::path getLogFilePath(const std::string & log_file) {
+ return fs::temp_directory_path() / log_file;
+ }
+
+ std::shared_ptr<CephContext> getContext() const {
+ return ctx_;
+ }
+
+ private:
+ std::shared_ptr<CephContext> ctx_;
+};
+
+TEST_F(TestDBStoreManager, BasicInstantiateUsingDBDir) {
+ getContext()->_conf.set_val("dbstore_db_dir", getTestDir());
+
+ EXPECT_FALSE(fs::exists(getDBFullPath(default_tenant)));
+ auto dbstore_mgr = std::make_shared<DBStoreManager>(getContext().get());
+ EXPECT_TRUE(fs::exists(getDBFullPath(default_tenant)));
+}
+
+TEST_F(TestDBStoreManager, DBNamePrefix) {
+ getContext()->_conf.set_val("dbstore_db_dir", getTestDir());
+ std::string prefix = "testprefix";
+ getContext()->_conf.set_val("dbstore_db_name_prefix", prefix);
+
+ EXPECT_FALSE(fs::exists(getDBFullPath(default_tenant)));
+ auto dbstore_mgr = std::make_shared<DBStoreManager>(getContext().get());
+ EXPECT_TRUE(fs::exists(getDBFullPath(default_tenant)));
+
+ // check that the database name contains the given prefix
+ std::string expected_db_name = prefix + "-" + default_tenant + ".db";
+ EXPECT_TRUE(endsWith(getDBFullPath(default_tenant), expected_db_name));
+}
+
+TEST_F(TestDBStoreManager, BasicInstantiateSecondConstructor) {
+ getContext()->_conf.set_val("dbstore_db_dir", getTestDir());
+
+ EXPECT_FALSE(fs::exists(getDBFullPath(default_tenant)));
+ auto dbstore_mgr = std::make_shared<DBStoreManager>(getContext().get(), getLogFilePath("test.log").string(), 10);
+ EXPECT_TRUE(fs::exists(getDBFullPath(default_tenant)));
+}
+
+TEST_F(TestDBStoreManager, TestDBName) {
+ getContext()->_conf.set_val("dbstore_db_dir", getTestDir());
+
+ auto dbstore_mgr = std::make_shared<DBStoreManager>(getContext().get());
+ auto db = dbstore_mgr->getDB(default_tenant, false);
+ ASSERT_NE(nullptr, db);
+ EXPECT_EQ(getDBTenant(), db->getDBname());
+}
+
+
+TEST_F(TestDBStoreManager, TestDBNameDefaultDB) {
+ getContext()->_conf.set_val("dbstore_db_dir", getTestDir());
+
+ auto dbstore_mgr = std::make_shared<DBStoreManager>(getContext().get());
+ // passing an empty tenant should return the default_db
+ auto db = dbstore_mgr->getDB("", false);
+ ASSERT_NE(nullptr, db);
+ EXPECT_EQ(getDBTenant(), db->getDBname());
+}
+
+TEST_F(TestDBStoreManager, TestDBBadTenant) {
+ getContext()->_conf.set_val("dbstore_db_dir", getTestDir());
+
+ auto dbstore_mgr = std::make_shared<DBStoreManager>(getContext().get());
+ auto db = dbstore_mgr->getDB("does-not-exist", false);
+ ASSERT_EQ(nullptr, db);
+}
+
+TEST_F(TestDBStoreManager, TestGetNewDB) {
+ getContext()->_conf.set_val("dbstore_db_dir", getTestDir());
+
+ auto dbstore_mgr = std::make_shared<DBStoreManager>(getContext().get());
+
+ auto new_tenant_path = "new_tenant";
+ auto db = dbstore_mgr->getDB(new_tenant_path, true);
+ ASSERT_NE(nullptr, db);
+ EXPECT_EQ(getDBTenant(new_tenant_path), db->getDBname());
+}
+
+TEST_F(TestDBStoreManager, TestDelete) {
+ getContext()->_conf.set_val("dbstore_db_dir", getTestDir());
+
+ auto dbstore_mgr = std::make_shared<DBStoreManager>(getContext().get());
+ dbstore_mgr->deleteDB(default_tenant);
+ auto db = dbstore_mgr->getDB(default_tenant, false);
+ ASSERT_EQ(nullptr, db);
+}
diff --git a/src/rgw/driver/dbstore/tests/dbstore_tests.cc b/src/rgw/driver/dbstore/tests/dbstore_tests.cc
new file mode 100644
index 000000000..27edb7b85
--- /dev/null
+++ b/src/rgw/driver/dbstore/tests/dbstore_tests.cc
@@ -0,0 +1,1417 @@
+#include "gtest/gtest.h"
+#include <iostream>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dbstore.h>
+#include <sqliteDB.h>
+#include "rgw_common.h"
+
+using namespace std;
+using DB = rgw::store::DB;
+
+vector<const char*> args;
+
+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 {
+ cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_DAEMON,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE | CINIT_FLAG_NO_MON_CONFIG | CINIT_FLAG_NO_DAEMON_ACTIONS);
+ if (!db_type.compare("SQLite")) {
+ db = new SQLiteDB(tenant, cct.get());
+ ASSERT_TRUE(db != nullptr);
+ ret = db->Initialize(logfile, loglevel);
+ ASSERT_GE(ret, 0);
+ }
+ }
+
+ void TearDown() override {
+ if (!db)
+ return;
+ db->Destroy(db->get_def_dpp());
+ delete db;
+ }
+
+ string tenant;
+ DB *db;
+ string db_type;
+ int ret;
+ string logfile = "rgw_dbstore_tests.log";
+ int loglevel = 30;
+ boost::intrusive_ptr<CephContext> cct;
+ };
+}
+
+ceph::real_time bucket_mtime = real_clock::now();
+string marker1;
+
+class DBGetDataCB : public RGWGetDataCB {
+ public:
+ bufferlist data_bl;
+ off_t data_ofs, data_len;
+
+ int handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) {
+ data_bl = bl;
+ data_ofs = bl_ofs;
+ data_len = bl_len;
+ return 0;
+ }
+};
+
+namespace {
+
+ class DBStoreTest : public ::testing::Test {
+ protected:
+ int ret;
+ DB *db = nullptr;
+ string user1 = "user1";
+ string user_id1 = "user_id1";
+ string bucket1 = "bucket1";
+ string object1 = "object1";
+ string data = "Hello World";
+ DBOpParams GlobalParams = {};
+ const DoutPrefixProvider *dpp;
+
+ DBStoreTest() {}
+ void SetUp() {
+ db = gtest::env->db;
+ ASSERT_TRUE(db != nullptr);
+ dpp = db->get_def_dpp();
+ ASSERT_TRUE(dpp != 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.op.obj.state.obj.bucket = GlobalParams.op.bucket.info.bucket;
+ GlobalParams.op.obj.state.obj.key.name = object1;
+ GlobalParams.op.obj.state.obj.key.instance = "inst1";
+ GlobalParams.op.obj.obj_id = "obj_id1";
+ GlobalParams.op.obj_data.part_num = 0;
+
+ /* As of now InitializeParams doesnt do anything
+ * special based on fop. Hence its okay to do
+ * global initialization once.
+ */
+ ret = db->InitializeParams(dpp, &GlobalParams);
+ ASSERT_EQ(ret, 0);
+ }
+
+ void TearDown() {
+ }
+
+ int write_object(const DoutPrefixProvider *dpp, DBOpParams params) {
+ DB::Object op_target(db, params.op.bucket.info,
+ params.op.obj.state.obj);
+ DB::Object::Write write_op(&op_target);
+ map<string, bufferlist> setattrs;
+ ret = write_op.prepare(dpp);
+ if (ret)
+ return ret;
+
+ write_op.meta.mtime = &bucket_mtime;
+ write_op.meta.category = RGWObjCategory::Main;
+ write_op.meta.owner = params.op.user.uinfo.user_id;
+
+ bufferlist b1 = params.op.obj.head_data;
+ write_op.meta.data = &b1;
+
+ bufferlist b2;
+ encode("ACL", b2);
+ setattrs[RGW_ATTR_ACL] = b2;
+
+ ret = write_op.write_meta(0, params.op.obj.state.size, b1.length()+1, setattrs);
+ return ret;
+ }
+ };
+}
+
+TEST_F(DBStoreTest, 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.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(dpp, "InsertUser", &params);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, GetUser) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ ret = db->ProcessOp(dpp, "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.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(DBStoreTest, 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(dpp, "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.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(DBStoreTest, GetUserQueryByEmail) {
+ int ret = -1;
+ RGWUserInfo uinfo;
+ string email = "user1@dbstore.com";
+ map<std::string, bufferlist> attrs;
+ RGWObjVersionTracker objv;
+
+ ret = db->get_user(dpp, "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.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(DBStoreTest, GetUserQueryByAccessKey) {
+ int ret = -1;
+ RGWUserInfo uinfo;
+ string key = "id1";
+
+ ret = db->get_user(dpp, "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.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(DBStoreTest, 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.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(dpp, uinfo, false, &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(dpp, 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(dpp, 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(dpp, 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(DBStoreTest, 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(dpp, "user_id", "user_id2", 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.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(DBStoreTest, ListAllUsers) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ ret = db->ListAllUsers(dpp, &params);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, 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(dpp, "InsertBucket", &params);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, 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(dpp, "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(dpp, "attrs", info, false, nullptr, &attrs, &bucket_mtime, &objv);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(objv.read_version.ver, 2);
+}
+
+TEST_F(DBStoreTest, UpdateBucketInfo) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ RGWBucketInfo info;
+
+ params.op.bucket.info.bucket.name = "bucket1";
+
+ ret = db->ProcessOp(dpp, "GetBucket", &params);
+ ASSERT_EQ(ret, 0);
+
+ info = params.op.bucket.info;
+
+ info.bucket.marker = "marker2";
+ ret = db->update_bucket(dpp, "info", info, false, nullptr, nullptr, &bucket_mtime, nullptr);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(info.objv_tracker.read_version.ver, 3);
+}
+
+TEST_F(DBStoreTest, GetBucket) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ params.op.bucket.info.bucket.name = "bucket1";
+ ret = db->ProcessOp(dpp, "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, 3);
+ 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_id1");
+ 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(DBStoreTest, 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(dpp, owner, bucket, "zid", rule, "swift_ver", NULL,
+ attrs, info, &objv, NULL, bucket_mtime, NULL, NULL,
+ null_yield, false);
+ ASSERT_EQ(ret, 0);
+ bucket.name = "bucket2";
+ ret = db->create_bucket(dpp, owner, bucket, "zid", rule, "swift_ver", NULL,
+ attrs, info, &objv, NULL, bucket_mtime, NULL, NULL,
+ null_yield, false);
+ ASSERT_EQ(ret, 0);
+ bucket.name = "bucket3";
+ ret = db->create_bucket(dpp, owner, bucket, "zid", rule, "swift_ver", NULL,
+ attrs, info, &objv, NULL, bucket_mtime, NULL, NULL,
+ null_yield, false);
+ ASSERT_EQ(ret, 0);
+ bucket.name = "bucket4";
+ ret = db->create_bucket(dpp, owner, bucket, "zid", rule, "swift_ver", NULL,
+ attrs, info, &objv, NULL, bucket_mtime, NULL, NULL,
+ null_yield, false);
+ ASSERT_EQ(ret, 0);
+ bucket.name = "bucket5";
+ ret = db->create_bucket(dpp, owner, bucket, "zid", rule, "swift_ver", NULL,
+ attrs, info, &objv, NULL, bucket_mtime, NULL, NULL,
+ null_yield, false);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, 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(dpp, "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(DBStoreTest, 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(dpp, "", 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(DBStoreTest, BucketChown) {
+ int ret = -1;
+ RGWBucketInfo info;
+ rgw_user user;
+ user.id = "user_id2";
+
+ info.bucket.name = "bucket5";
+
+ ret = db->update_bucket(dpp, "owner", info, false, &user, nullptr, &bucket_mtime, nullptr);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(info.objv_tracker.read_version.ver, 3);
+}
+
+TEST_F(DBStoreTest, ListAllBuckets) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ ret = db->ListAllBuckets(dpp, &params);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, ListAllBuckets2) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ rgw_user owner;
+ int max = 2;
+ bool need_stats = true;
+ bool is_truncated = false;
+ RGWUserBuckets ulist;
+
+ marker1 = "";
+ do {
+ is_truncated = false;
+ ret = db->list_buckets(dpp, "all", owner, marker1, "", max, need_stats, &ulist,
+ &is_truncated);
+ ASSERT_EQ(ret, 0);
+
+ cout << "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ \n";
+ cout << "ownerID : " << owner.id << "\n";
+ 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(DBStoreTest, RemoveBucketAPI) {
+ int ret = -1;
+ RGWBucketInfo info;
+
+ info.bucket.name = "bucket5";
+
+ ret = db->remove_bucket(dpp, info);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, 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(dpp, uinfo, &objv);
+ ASSERT_EQ(ret, -125);
+
+ objv.read_version.ver = 2;
+ ret = db->remove_user(dpp, uinfo, &objv);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, PutObject) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ params.op.obj.category = RGWObjCategory::Main;
+ params.op.obj.storage_class = "STANDARD";
+ bufferlist b1;
+ encode("HELLO WORLD", b1);
+ cout<<"XXXXXXXXX Insert b1.length " << b1.length() << "\n";
+ params.op.obj.head_data = b1;
+ params.op.obj.state.size = 12;
+ params.op.obj.state.is_olh = false;
+ ret = db->ProcessOp(dpp, "PutObject", &params);
+ ASSERT_EQ(ret, 0);
+
+ /* Insert another objects */
+ params.op.obj.state.obj.key.name = "object2";
+ params.op.obj.state.obj.key.instance = "inst2";
+ ret = db->ProcessOp(dpp, "PutObject", &params);
+ ASSERT_EQ(ret, 0);
+
+ params.op.obj.state.obj.key.name = "object3";
+ params.op.obj.state.obj.key.instance = "inst3";
+ ret = db->ProcessOp(dpp, "PutObject", &params);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, ListAllObjects) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ ret = db->ListAllObjects(dpp, &params);
+ ASSERT_GE(ret, 0);
+}
+
+TEST_F(DBStoreTest, GetObject) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ ret = db->ProcessOp(dpp, "GetObject", &params);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(params.op.obj.category, RGWObjCategory::Main);
+ ASSERT_EQ(params.op.obj.storage_class, "STANDARD");
+ string data;
+ decode(data, params.op.obj.head_data);
+ ASSERT_EQ(data, "HELLO WORLD");
+ ASSERT_EQ(params.op.obj.state.size, 12);
+ cout << "versionNum :" << params.op.obj.version_num << "\n";
+}
+
+TEST_F(DBStoreTest, GetObjectState) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ RGWObjState* s;
+
+ params.op.obj.state.obj.key.name = "object2";
+ params.op.obj.state.obj.key.instance = "inst2";
+ DB::Object op_target(db, params.op.bucket.info,
+ params.op.obj.state.obj);
+
+ ret = op_target.get_obj_state(dpp, params.op.bucket.info, params.op.obj.state.obj,
+ false, &s);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(s->size, 12);
+ ASSERT_EQ(s->is_olh, false);
+ cout << "versionNum :" << params.op.obj.version_num << "\n";
+
+ /* Recheck with get_state API */
+ ret = op_target.get_state(dpp, &s, false);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(s->size, 12);
+ ASSERT_EQ(s->is_olh, false);
+ cout << "versionNum :" << params.op.obj.version_num << "\n";
+}
+
+TEST_F(DBStoreTest, ObjAttrs) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ map<string, bufferlist> setattrs;
+ map<string, bufferlist> rmattrs;
+ map<string, bufferlist> readattrs;
+
+ bufferlist b1, b2, b3;
+ encode("ACL", b1);
+ setattrs[RGW_ATTR_ACL] = b1;
+ encode("LC", b2);
+ setattrs[RGW_ATTR_LC] = b2;
+ encode("ETAG", b3);
+ setattrs[RGW_ATTR_ETAG] = b3;
+
+ DB::Object op_target(db, params.op.bucket.info,
+ params.op.obj.state.obj);
+
+ /* Set some attrs */
+ ret = op_target.set_attrs(dpp, setattrs, nullptr);
+ ASSERT_EQ(ret, 0);
+
+ /* read those attrs */
+ DB::Object::Read read_op(&op_target);
+ read_op.params.attrs = &readattrs;
+ ret = read_op.prepare(dpp);
+ ASSERT_EQ(ret, 0);
+
+ string val;
+ decode(val, readattrs[RGW_ATTR_ACL]);
+ ASSERT_EQ(val, "ACL");
+ decode(val, readattrs[RGW_ATTR_LC]);
+ ASSERT_EQ(val, "LC");
+ decode(val, readattrs[RGW_ATTR_ETAG]);
+ ASSERT_EQ(val, "ETAG");
+
+ /* Remove some attrs */
+ rmattrs[RGW_ATTR_ACL] = b1;
+ map<string, bufferlist> empty;
+ ret = op_target.set_attrs(dpp, empty, &rmattrs);
+ ASSERT_EQ(ret, 0);
+
+ /* read those attrs */
+ ret = read_op.prepare(dpp);
+ ASSERT_EQ(ret, 0);
+
+ ASSERT_EQ(readattrs.count(RGW_ATTR_ACL), 0);
+ decode(val, readattrs[RGW_ATTR_LC]);
+ ASSERT_EQ(val, "LC");
+ decode(val, readattrs[RGW_ATTR_ETAG]);
+ ASSERT_EQ(val, "ETAG");
+}
+
+TEST_F(DBStoreTest, WriteObject) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ params.op.obj.state.obj.key.name = "object3";
+ params.op.obj.state.obj.key.instance = "inst3";
+ DB::Object op_target(db, params.op.bucket.info,
+ params.op.obj.state.obj);
+
+ bufferlist b1;
+ encode("HELLO WORLD - Object3", b1);
+ params.op.obj.head_data = b1;
+ params.op.obj.state.size = 22;
+
+ ret = write_object(dpp, params);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, ReadObject) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ map<string, bufferlist> readattrs;
+ params.op.obj.state.obj.key.name = "object3";
+ params.op.obj.state.obj.key.instance = "inst3";
+ uint64_t obj_size;
+ DB::Object op_target(db, params.op.bucket.info,
+ params.op.obj.state.obj);
+ DB::Object::Read read_op(&op_target);
+ read_op.params.attrs = &readattrs;
+ read_op.params.obj_size = &obj_size;
+ ret = read_op.prepare(dpp);
+ ASSERT_EQ(ret, 0);
+
+ bufferlist bl;
+ ret = read_op.read(0, 25, bl, dpp);
+ cout<<"XXXXXXXXX Insert bl.length " << bl.length() << "\n";
+ ASSERT_EQ(ret, 25);
+
+ string data;
+ decode(data, bl);
+ ASSERT_EQ(data, "HELLO WORLD - Object3");
+ ASSERT_EQ(obj_size, 22);
+}
+
+TEST_F(DBStoreTest, IterateObject) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ map<string, bufferlist> readattrs;
+ uint64_t obj_size;
+ DBGetDataCB cb;
+
+ DB::Object op_target(db, params.op.bucket.info,
+ params.op.obj.state.obj);
+ DB::Object::Read read_op(&op_target);
+ read_op.params.attrs = &readattrs;
+ read_op.params.obj_size = &obj_size;
+ ret = read_op.prepare(dpp);
+ ASSERT_EQ(ret, 0);
+
+ bufferlist bl;
+ ret = read_op.iterate(dpp, 0, 15, &cb);
+ ASSERT_EQ(ret, 0);
+ string data;
+ decode(data, cb.data_bl);
+ cout << "XXXXXXXXXX iterate data is " << data << ", bl_ofs = " << cb.data_ofs << ", bl_len = " << cb.data_len << "\n";
+ ASSERT_EQ(data, "HELLO WORLD");
+ ASSERT_EQ(cb.data_ofs, 0);
+ ASSERT_EQ(cb.data_len, 15);
+}
+
+TEST_F(DBStoreTest, ListBucketObjects) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ int max = 2;
+ bool is_truncated = false;
+ rgw_obj_key marker1;
+ DB::Bucket target(db, params.op.bucket.info);
+ DB::Bucket::List list_op(&target);
+
+ vector<rgw_bucket_dir_entry> dir_list;
+
+ marker1.name = "";
+ do {
+ is_truncated = false;
+ list_op.params.marker = marker1;
+ ret = list_op.list_objects(dpp, max, &dir_list, nullptr, &is_truncated);
+ ASSERT_EQ(ret, 0);
+
+ cout << "marker1 :" << marker1.name << "\n";
+
+ cout << "is_truncated :" << is_truncated << "\n";
+
+ for (const auto& ent: dir_list) {
+ cls_rgw_obj_key key = ent.key;
+ cout << "###################### \n";
+ cout << "key.name : " << key.name << "\n";
+ cout << "key.instance : " << key.instance << "\n";
+
+ marker1 = list_op.get_next_marker();
+ }
+ dir_list.clear();
+ } while(is_truncated);
+}
+
+TEST_F(DBStoreTest, DeleteObj) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ RGWObjState *s;
+
+ /* delete object2 */
+ params.op.obj.state.obj.key.name = "object2";
+ params.op.obj.state.obj.key.instance = "inst2";
+ DB::Object op_target(db, params.op.bucket.info,
+ params.op.obj.state.obj);
+
+ DB::Object::Delete delete_op(&op_target);
+ ret = delete_op.delete_obj(dpp);
+ ASSERT_EQ(ret, 0);
+
+ /* Should return ENOENT */
+ ret = op_target.get_state(dpp, &s, false);
+ ASSERT_EQ(ret, -2);
+}
+
+TEST_F(DBStoreTest, WriteVersionedObject) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ std::string instances[] = {"inst1", "inst2", "inst3"};
+ bufferlist b1;
+
+ params.op.obj.flags |= rgw_bucket_dir_entry::FLAG_CURRENT;
+ params.op.obj.state.obj.key.name = "object1";
+
+ /* Write versioned objects */
+ DB::Object op_target(db, params.op.bucket.info, params.op.obj.state.obj);
+ DB::Object::Write write_op(&op_target);
+
+ /* Version1 */
+ params.op.obj.state.obj.key.instance = instances[0];
+ encode("HELLO WORLD", b1);
+ params.op.obj.head_data = b1;
+ params.op.obj.state.size = 12;
+ ret = write_object(dpp, params);
+ ASSERT_EQ(ret, 0);
+
+ /* Version2 */
+ params.op.obj.state.obj.key.instance = instances[1];
+ b1.clear();
+ encode("HELLO WORLD ABC", b1);
+ params.op.obj.head_data = b1;
+ params.op.obj.state.size = 16;
+ ret = write_object(dpp, params);
+ ASSERT_EQ(ret, 0);
+
+ /* Version3 */
+ params.op.obj.state.obj.key.instance = instances[2];
+ b1.clear();
+ encode("HELLO WORLD A", b1);
+ params.op.obj.head_data = b1;
+ params.op.obj.state.size = 14;
+ ret = write_object(dpp, params);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, ListVersionedObject) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ std::string instances[] = {"inst1", "inst2", "inst3"};
+ int i = 0;
+
+ /* list versioned objects */
+ params.op.obj.state.obj.key.instance.clear();
+ params.op.list_max_count = MAX_VERSIONED_OBJECTS;
+ ret = db->ProcessOp(dpp, "ListVersionedObjects", &params);
+ ASSERT_EQ(ret, 0);
+
+ i = 2;
+ for (auto ent: params.op.obj.list_entries) {
+
+
+ ASSERT_EQ(ent.key.instance, instances[i]);
+ i--;
+ }
+}
+
+TEST_F(DBStoreTest, ReadVersionedObject) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ std::string instances[] = {"inst1", "inst2", "inst3"};
+ std::string data;
+
+ /* read object.. should fetch latest version */
+ RGWObjState* s;
+ params = GlobalParams;
+ params.op.obj.state.obj.key.instance.clear();
+ DB::Object op_target2(db, params.op.bucket.info, params.op.obj.state.obj);
+ ret = op_target2.get_obj_state(dpp, params.op.bucket.info, params.op.obj.state.obj,
+ true, &s);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(s->obj.key.instance, instances[2]);
+ decode(data, s->data);
+ ASSERT_EQ(data, "HELLO WORLD A");
+ ASSERT_EQ(s->size, 14);
+
+ /* read a particular non-current version */
+ params.op.obj.state.obj.key.instance = instances[1];
+ DB::Object op_target3(db, params.op.bucket.info, params.op.obj.state.obj);
+ ret = op_target3.get_obj_state(dpp, params.op.bucket.info, params.op.obj.state.obj,
+ true, &s);
+ ASSERT_EQ(ret, 0);
+ decode(data, s->data);
+ ASSERT_EQ(data, "HELLO WORLD ABC");
+ ASSERT_EQ(s->size, 16);
+}
+
+TEST_F(DBStoreTest, DeleteVersionedObject) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ std::string instances[] = {"inst1", "inst2", "inst3"};
+ std::string data;
+ std::string dm_instance;
+ int i = 0;
+
+ /* Delete object..should create delete marker */
+ params.op.obj.state.obj.key.instance.clear();
+ DB::Object op_target(db, params.op.bucket.info, params.op.obj.state.obj);
+ DB::Object::Delete delete_op(&op_target);
+ delete_op.params.versioning_status |= BUCKET_VERSIONED;
+
+ ret = delete_op.delete_obj(dpp);
+ ASSERT_EQ(ret, 0);
+
+ /* list versioned objects */
+ params = GlobalParams;
+ params.op.obj.state.obj.key.instance.clear();
+ params.op.list_max_count = MAX_VERSIONED_OBJECTS;
+ ret = db->ProcessOp(dpp, "ListVersionedObjects", &params);
+
+ i = 3;
+ for (auto ent: params.op.obj.list_entries) {
+ string is_delete_marker = (ent.flags & rgw_bucket_dir_entry::FLAG_DELETE_MARKER)? "true" : "false";
+ cout << "ent.name: " << ent.key.name << ". ent.instance: " << ent.key.instance << " is_delete_marker = " << is_delete_marker << "\n";
+
+ if (i == 3) {
+ ASSERT_EQ(is_delete_marker, "true");
+ dm_instance = ent.key.instance;
+ } else {
+ ASSERT_EQ(is_delete_marker, "false");
+ ASSERT_EQ(ent.key.instance, instances[i]);
+ }
+
+ i--;
+ }
+
+ /* read object.. should return -ENOENT */
+ RGWObjState* s;
+ params = GlobalParams;
+ params.op.obj.state.obj.key.instance.clear();
+ DB::Object op_target2(db, params.op.bucket.info, params.op.obj.state.obj);
+ ret = op_target2.get_obj_state(dpp, params.op.bucket.info, params.op.obj.state.obj,
+ true, &s);
+ ASSERT_EQ(ret, -ENOENT);
+
+ /* Delete delete marker..should be able to read object now */
+ params.op.obj.state.obj.key.instance = dm_instance;
+ DB::Object op_target3(db, params.op.bucket.info, params.op.obj.state.obj);
+ DB::Object::Delete delete_op2(&op_target3);
+ delete_op2.params.versioning_status |= BUCKET_VERSIONED;
+
+ ret = delete_op2.delete_obj(dpp);
+ ASSERT_EQ(ret, 0);
+
+ /* read object.. should fetch latest version */
+ params = GlobalParams;
+ params.op.obj.state.obj.key.instance.clear();
+ DB::Object op_target4(db, params.op.bucket.info, params.op.obj.state.obj);
+ ret = op_target4.get_obj_state(dpp, params.op.bucket.info, params.op.obj.state.obj,
+ true, &s);
+ ASSERT_EQ(s->obj.key.instance, instances[2]);
+ decode(data, s->data);
+ ASSERT_EQ(data, "HELLO WORLD A");
+ ASSERT_EQ(s->size, 14);
+
+ /* delete latest version using version-id. Next version should get promoted */
+ params.op.obj.state.obj.key.instance = instances[2];
+ DB::Object op_target5(db, params.op.bucket.info, params.op.obj.state.obj);
+ DB::Object::Delete delete_op3(&op_target5);
+ delete_op3.params.versioning_status |= BUCKET_VERSIONED;
+
+ ret = delete_op3.delete_obj(dpp);
+ ASSERT_EQ(ret, 0);
+
+ /* list versioned objects..only two versions should be present
+ * with second version marked as CURRENT */
+ params = GlobalParams;
+ params.op.obj.state.obj.key.instance.clear();
+ params.op.list_max_count = MAX_VERSIONED_OBJECTS;
+ ret = db->ProcessOp(dpp, "ListVersionedObjects", &params);
+
+ i = 1;
+ for (auto ent: params.op.obj.list_entries) {
+
+ if (i == 1) {
+ dm_instance = ent.key.instance;
+ } else {
+ ASSERT_EQ(ent.key.instance, instances[i]);
+ }
+
+ i--;
+ }
+
+}
+
+TEST_F(DBStoreTest, ObjectOmapSetVal) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ DB::Object op_target(db, params.op.bucket.info,
+ params.op.obj.state.obj);
+
+ string val = "part1_val";
+ bufferlist bl;
+ encode(val, bl);
+ ret = op_target.obj_omap_set_val_by_key(dpp, "part1", bl, false);
+ ASSERT_EQ(ret, 0);
+
+ val = "part2_val";
+ bl.clear();
+ encode(val, bl);
+ ret = op_target.obj_omap_set_val_by_key(dpp, "part2", bl, false);
+ ASSERT_EQ(ret, 0);
+
+ val = "part3_val";
+ bl.clear();
+ encode(val, bl);
+ ret = op_target.obj_omap_set_val_by_key(dpp, "part3", bl, false);
+ ASSERT_EQ(ret, 0);
+
+ val = "part4_val";
+ bl.clear();
+ encode(val, bl);
+ ret = op_target.obj_omap_set_val_by_key(dpp, "part4", bl, false);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, ObjectOmapGetValsByKeys) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ std::set<std::string> keys;
+ std::map<std::string, bufferlist> vals;
+
+ DB::Object op_target(db, params.op.bucket.info,
+ params.op.obj.state.obj);
+
+ keys.insert("part2");
+ keys.insert("part4");
+
+ ret = op_target.obj_omap_get_vals_by_keys(dpp, "", keys, &vals);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(vals.size(), 2);
+
+ string val;
+ decode(val, vals["part2"]);
+ ASSERT_EQ(val, "part2_val");
+ decode(val, vals["part4"]);
+ ASSERT_EQ(val, "part4_val");
+}
+
+TEST_F(DBStoreTest, ObjectOmapGetAll) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ std::map<std::string, bufferlist> vals;
+
+ DB::Object op_target(db, params.op.bucket.info,
+ params.op.obj.state.obj);
+
+ ret = op_target.obj_omap_get_all(dpp, &vals);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(vals.size(), 4);
+
+ string val;
+ decode(val, vals["part1"]);
+ ASSERT_EQ(val, "part1_val");
+ decode(val, vals["part2"]);
+ ASSERT_EQ(val, "part2_val");
+ decode(val, vals["part3"]);
+ ASSERT_EQ(val, "part3_val");
+ decode(val, vals["part4"]);
+ ASSERT_EQ(val, "part4_val");
+}
+
+TEST_F(DBStoreTest, ObjectOmapGetVals) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ std::set<std::string> keys;
+ std::map<std::string, bufferlist> vals;
+ bool pmore;
+
+ DB::Object op_target(db, params.op.bucket.info,
+ params.op.obj.state.obj);
+
+ ret = op_target.obj_omap_get_vals(dpp, "part3", 10, &vals, &pmore);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(vals.size(), 2);
+
+ string val;
+ decode(val, vals["part3"]);
+ ASSERT_EQ(val, "part3_val");
+ decode(val, vals["part4"]);
+ ASSERT_EQ(val, "part4_val");
+}
+
+TEST_F(DBStoreTest, PutObjectData) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ params.op.obj_data.part_num = 1;
+ params.op.obj_data.offset = 10;
+ params.op.obj_data.multipart_part_str = "2";
+ bufferlist b1;
+ encode("HELLO WORLD", b1);
+ params.op.obj_data.data = b1;
+ params.op.obj_data.size = 12;
+ params.op.obj.state.mtime = real_clock::now();
+ ret = db->ProcessOp(dpp, "PutObjectData", &params);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, UpdateObjectData) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ params.op.obj.state.mtime = bucket_mtime;
+ ret = db->ProcessOp(dpp, "UpdateObjectData", &params);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, GetObjectData) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ params.op.obj.state.obj.key.instance = "inst1";
+ params.op.obj.state.obj.key.name = "object1";
+ ret = db->ProcessOp(dpp, "GetObjectData", &params);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(params.op.obj_data.part_num, 1);
+ ASSERT_EQ(params.op.obj_data.offset, 10);
+ ASSERT_EQ(params.op.obj_data.multipart_part_str, "2");
+ ASSERT_EQ(params.op.obj.state.obj.key.instance, "inst1");
+ ASSERT_EQ(params.op.obj.state.obj.key.name, "object1");
+ ASSERT_EQ(params.op.obj.state.mtime, bucket_mtime);
+ string data;
+ decode(data, params.op.obj_data.data);
+ ASSERT_EQ(data, "HELLO WORLD");
+}
+
+TEST_F(DBStoreTest, DeleteObjectData) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ ret = db->ProcessOp(dpp, "DeleteObjectData", &params);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, DeleteObject) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ ret = db->ProcessOp(dpp, "DeleteObject", &params);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, LCTables) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ ret = db->createLCTables(dpp);
+ ASSERT_GE(ret, 0);
+}
+
+TEST_F(DBStoreTest, LCHead) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ std::string index1 = "bucket1";
+ std::string index2 = "bucket2";
+ time_t lc_time = ceph_clock_now();
+ std::unique_ptr<rgw::sal::Lifecycle::LCHead> head;
+ std::string ents[] = {"entry1", "entry2", "entry3"};
+ rgw::sal::StoreLifecycle::StoreLCHead head1(lc_time, 0, ents[0]);
+ rgw::sal::StoreLifecycle::StoreLCHead head2(lc_time, 0, ents[1]);
+ rgw::sal::StoreLifecycle::StoreLCHead head3(lc_time, 0, ents[2]);
+
+ ret = db->put_head(index1, head1);
+ ASSERT_EQ(ret, 0);
+ ret = db->put_head(index2, head2);
+ ASSERT_EQ(ret, 0);
+
+ ret = db->get_head(index1, &head);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(head->get_marker(), "entry1");
+
+ ret = db->get_head(index2, &head);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(head->get_marker(), "entry2");
+
+ // update index1
+ ret = db->put_head(index1, head3);
+ ASSERT_EQ(ret, 0);
+ ret = db->get_head(index1, &head);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(head->get_marker(), "entry3");
+
+}
+TEST_F(DBStoreTest, LCEntry) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+ uint64_t lc_time = ceph_clock_now();
+ std::string index1 = "lcindex1";
+ std::string index2 = "lcindex2";
+ typedef enum {lc_uninitial = 1, lc_complete} status;
+ std::string ents[] = {"bucket1", "bucket2", "bucket3", "bucket4"};
+ std::unique_ptr<rgw::sal::Lifecycle::LCEntry> entry;
+ rgw::sal::StoreLifecycle::StoreLCEntry entry1(ents[0], lc_time, lc_uninitial);
+ rgw::sal::StoreLifecycle::StoreLCEntry entry2(ents[1], lc_time, lc_uninitial);
+ rgw::sal::StoreLifecycle::StoreLCEntry entry3(ents[2], lc_time, lc_uninitial);
+ rgw::sal::StoreLifecycle::StoreLCEntry entry4(ents[3], lc_time, lc_uninitial);
+
+ vector<std::unique_ptr<rgw::sal::Lifecycle::LCEntry>> lc_entries;
+
+ ret = db->set_entry(index1, entry1);
+ ASSERT_EQ(ret, 0);
+ ret = db->set_entry(index1, entry2);
+ ASSERT_EQ(ret, 0);
+ ret = db->set_entry(index1, entry3);
+ ASSERT_EQ(ret, 0);
+ ret = db->set_entry(index2, entry4);
+ ASSERT_EQ(ret, 0);
+
+ // get entry index1, entry1
+ ret = db->get_entry(index1, ents[0], &entry);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(entry->get_status(), lc_uninitial);
+ ASSERT_EQ(entry->get_start_time(), lc_time);
+
+ // get next entry index1, entry2
+ ret = db->get_next_entry(index1, ents[1], &entry);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(entry->get_bucket(), ents[2]);
+ ASSERT_EQ(entry->get_status(), lc_uninitial);
+ ASSERT_EQ(entry->get_start_time(), lc_time);
+
+ // update entry4 to entry5
+ entry4.status = lc_complete;
+ ret = db->set_entry(index2, entry4);
+ ASSERT_EQ(ret, 0);
+ ret = db->get_entry(index2, ents[3], &entry);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(entry->get_status(), lc_complete);
+
+ // list entries
+ ret = db->list_entries(index1, "", 5, lc_entries);
+ ASSERT_EQ(ret, 0);
+ for (const auto& ent: lc_entries) {
+ cout << "###################### \n";
+ cout << "lc entry.bucket : " << ent->get_bucket() << "\n";
+ cout << "lc entry.status : " << ent->get_status() << "\n";
+ }
+
+ // remove index1, entry3
+ ret = db->rm_entry(index1, entry3);
+ ASSERT_EQ(ret, 0);
+
+ // get next entry index1, entry2.. should be null
+ entry.release();
+ ret = db->get_next_entry(index1, ents[1], &entry);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(entry.get(), nullptr);
+}
+
+TEST_F(DBStoreTest, RemoveBucket) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ ret = db->ProcessOp(dpp, "RemoveBucket", &params);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, RemoveUser) {
+ struct DBOpParams params = GlobalParams;
+ int ret = -1;
+
+ ret = db->ProcessOp(dpp, "RemoveUser", &params);
+ ASSERT_EQ(ret, 0);
+}
+
+TEST_F(DBStoreTest, 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(dpp, "InsertUser", &params);
+ ASSERT_EQ(ret, 0);
+}
+
+int main(int argc, char **argv)
+{
+ int ret = -1;
+ string c_logfile = "rgw_dbstore_tests.log";
+ int c_loglevel = 20;
+
+ // format: ./dbstore-tests logfile loglevel
+ if (argc == 3) {
+ c_logfile = argv[1];
+ c_loglevel = (atoi)(argv[2]);
+ cout << "logfile:" << c_logfile << ", loglevel set to " << c_loglevel << "\n";
+ }
+
+ ::testing::InitGoogleTest(&argc, argv);
+
+ gtest::env = new gtest::Environment();
+ gtest::env->logfile = c_logfile;
+ gtest::env->loglevel = c_loglevel;
+ ::testing::AddGlobalTestEnvironment(gtest::env);
+
+ ret = RUN_ALL_TESTS();
+
+ return ret;
+}