From e6918187568dbd01842d8d1d2c808ce16a894239 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 21 Apr 2024 13:54:28 +0200 Subject: Adding upstream version 18.2.2. Signed-off-by: Daniel Baumann --- src/rgw/driver/dbstore/CMakeLists.txt | 71 + src/rgw/driver/dbstore/README.md | 53 + src/rgw/driver/dbstore/common/connection_pool.h | 147 + src/rgw/driver/dbstore/common/dbstore.cc | 2252 ++++++++++++++++ src/rgw/driver/dbstore/common/dbstore.h | 2016 ++++++++++++++ src/rgw/driver/dbstore/common/dbstore_log.h | 15 + src/rgw/driver/dbstore/config/sqlite.cc | 2070 ++++++++++++++ src/rgw/driver/dbstore/config/sqlite.h | 172 ++ src/rgw/driver/dbstore/config/sqlite_schema.h | 299 ++ src/rgw/driver/dbstore/config/store.cc | 38 + src/rgw/driver/dbstore/config/store.h | 27 + src/rgw/driver/dbstore/dbstore_main.cc | 199 ++ src/rgw/driver/dbstore/dbstore_mgr.cc | 140 + src/rgw/driver/dbstore/dbstore_mgr.h | 56 + src/rgw/driver/dbstore/sqlite/CMakeLists.txt | 16 + src/rgw/driver/dbstore/sqlite/connection.cc | 34 + src/rgw/driver/dbstore/sqlite/connection.h | 64 + src/rgw/driver/dbstore/sqlite/error.cc | 37 + src/rgw/driver/dbstore/sqlite/error.h | 81 + src/rgw/driver/dbstore/sqlite/sqliteDB.cc | 2996 +++++++++++++++++++++ src/rgw/driver/dbstore/sqlite/sqliteDB.h | 551 ++++ src/rgw/driver/dbstore/sqlite/statement.cc | 196 ++ src/rgw/driver/dbstore/sqlite/statement.h | 83 + src/rgw/driver/dbstore/tests/CMakeLists.txt | 17 + src/rgw/driver/dbstore/tests/dbstore_mgr_tests.cc | 157 ++ src/rgw/driver/dbstore/tests/dbstore_tests.cc | 1417 ++++++++++ 26 files changed, 13204 insertions(+) create mode 100644 src/rgw/driver/dbstore/CMakeLists.txt create mode 100644 src/rgw/driver/dbstore/README.md create mode 100644 src/rgw/driver/dbstore/common/connection_pool.h create mode 100644 src/rgw/driver/dbstore/common/dbstore.cc create mode 100644 src/rgw/driver/dbstore/common/dbstore.h create mode 100644 src/rgw/driver/dbstore/common/dbstore_log.h create mode 100644 src/rgw/driver/dbstore/config/sqlite.cc create mode 100644 src/rgw/driver/dbstore/config/sqlite.h create mode 100644 src/rgw/driver/dbstore/config/sqlite_schema.h create mode 100644 src/rgw/driver/dbstore/config/store.cc create mode 100644 src/rgw/driver/dbstore/config/store.h create mode 100644 src/rgw/driver/dbstore/dbstore_main.cc create mode 100644 src/rgw/driver/dbstore/dbstore_mgr.cc create mode 100644 src/rgw/driver/dbstore/dbstore_mgr.h create mode 100644 src/rgw/driver/dbstore/sqlite/CMakeLists.txt create mode 100644 src/rgw/driver/dbstore/sqlite/connection.cc create mode 100644 src/rgw/driver/dbstore/sqlite/connection.h create mode 100644 src/rgw/driver/dbstore/sqlite/error.cc create mode 100644 src/rgw/driver/dbstore/sqlite/error.h create mode 100644 src/rgw/driver/dbstore/sqlite/sqliteDB.cc create mode 100644 src/rgw/driver/dbstore/sqlite/sqliteDB.h create mode 100644 src/rgw/driver/dbstore/sqlite/statement.cc create mode 100644 src/rgw/driver/dbstore/sqlite/statement.h create mode 100644 src/rgw/driver/dbstore/tests/CMakeLists.txt create mode 100644 src/rgw/driver/dbstore/tests/dbstore_mgr_tests.cc create mode 100644 src/rgw/driver/dbstore/tests/dbstore_tests.cc (limited to 'src/rgw/driver/dbstore') 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 = + dbstore db name prefix = + + +## 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 +#include +#include +#include +#include +#include "common/dout.h" + +namespace rgw::dbstore { + +template +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 +class ConnectionPoolBase { + public: + ConnectionPoolBase(std::size_t max_connections) + : connections(max_connections) + {} + private: + friend class ConnectionHandle; + + // 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) + { + 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> connections; +}; + +/// Handle to a database connection borrowed from the pool. Automatically +/// returns the connection to its pool on the handle's destruction. +template +class ConnectionHandle { + ConnectionPoolBase* pool = nullptr; + std::unique_ptr conn; + public: + ConnectionHandle() noexcept = default; + ConnectionHandle(ConnectionPoolBase* pool, + std::unique_ptr 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(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 +template +concept factory_of = requires (F factory, const DoutPrefixProvider* dpp) { + { factory(dpp) } -> std::same_as>; + requires std::move_constructible; +}; + + +/// Generic database connection pool that enforces a limit on open connections. +template Factory> +class ConnectionPool : public ConnectionPoolBase { + public: + ConnectionPool(Factory factory, std::size_t max_connections) + : ConnectionPoolBase(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 + { + auto lock = std::unique_lock{this->mutex}; + std::unique_ptr 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 DB::objectmap = {}; + +map 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(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::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::iterator iter; + class ObjectOp* Ob; + + { + const std::lock_guard 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: " \ + <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::iterator iter; + class ObjectOp *Ob; + + const std::lock_guard 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("\ + <InitializeObjectOps(getDBname(), dpp); + + DB::objectmap.insert(pair(bucket, Ob)); + + return 0; +} + +int DB::objectmapDelete(const DoutPrefixProvider *dpp, string bucket) +{ + map::iterator iter; + + const std::lock_guard 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("<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 db_op; + + db_op = getDBOp(dpp, Op, params); + + if (!db_op) { + ldpp_dout(dpp, 0)<<"No db_op found for 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 *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, ¶ms); + + params.op.query_str = query_str; + + // validate query_str with UserTable entries names + if (query_str == "username") { + params.op.user.uinfo.display_name = query_str_val; + } else if (query_str == "email") { + params.op.user.uinfo.user_email = query_str_val; + } else if (query_str == "access_key") { + RGWAccessKey k(query_str_val, ""); + map keys; + keys[query_str_val] = k; + params.op.user.uinfo.access_keys = keys; + } else if (query_str == "user_id") { + params.op.user.uinfo.user_id = uinfo.user_id; + } else { + ldpp_dout(dpp, 0)<<"In GetUser Invalid query string :" <read_version = params.op.user.user_version; + } + +out: + return ret; +} + +int DB::store_user(const DoutPrefixProvider *dpp, + RGWUserInfo& uinfo, bool exclusive, map *pattrs, + RGWObjVersionTracker *pobjv, RGWUserInfo* pold_info) +{ + DBOpParams params = {}; + InitializeParams(dpp, ¶ms); + int ret = 0; + + /* Check if the user already exists and return the old info, caller will have a use for it */ + RGWUserInfo orig_info; + RGWObjVersionTracker objv_tracker = {}; + obj_version& obj_ver = objv_tracker.read_version; + + orig_info.user_id = uinfo.user_id; + ret = get_user(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:(" <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, ¶ms); + 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:(" <& 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, ¶ms); + int ret = 0; + + /* Check if the bucket already exists and return the old info, caller will have a use for it */ + RGWBucketInfo orig_info; + orig_info.bucket.name = bucket.name; + ret = get_bucket_info(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", ¶ms); + + if (ret) { + ldpp_dout(dpp, 0)<<"create_bucket failed with err:(" <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* 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:(" <read_version.ver != bucket_version.ver) { + ldpp_dout(dpp, 0)<<"Read version mismatch err:(" <id; + } else { + params.op.user.uinfo.user_id.id = orig_info.owner.id; + } + + /* Update version & mtime */ + params.op.bucket.bucket_version.ver = ++(bucket_version.ver); + + if (pmtime) { + params.op.bucket.mtime = *pmtime;; + } else { + params.op.bucket.mtime = ceph::real_time(); + } + + if (query_str == "attrs") { + params.op.query_str = "attrs"; + params.op.bucket.bucket_attrs = *pattrs; + } else if (query_str == "owner") { + /* Update only owner i.e, chown. + * Update creation_time too */ + params.op.query_str = "owner"; + params.op.bucket.info.creation_time = params.op.bucket.mtime; + } else if (query_str == "info") { + params.op.query_str = "info"; + params.op.bucket.info = info; + } else { + ret = -1; + ldpp_dout(dpp, 0)<<"In UpdateBucket Invalid query_str : " << query_str << dendl; + goto out; + } + + ret = ProcessOp(dpp, "UpdateBucket", ¶ms); + + if (ret) { + ldpp_dout(dpp, 0)<<"In UpdateBucket failed err:(" <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 *result, + map *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:(" <= 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, ¶ms); + InitializeParamsfromObject(dpp, ¶ms); + } + + ret = store->ProcessOp(dpp, "GetObject", ¶ms); + + /* 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:(" <ProcessOp(dpp, "UpdateObject", ¶ms); + + if (ret) { + ldpp_dout(dpp, 0)<<"In UpdateObject failed err:(" <& keys, + std::map* vals) +{ + int ret = 0; + DBOpParams params = {}; + std::map omap; + + if (!vals) + return -1; + + ret = get_object_impl(dpp, params); + + if (ret) { + ldpp_dout(dpp, 0) <<"get_object_impl failed err:(" <ProcessOp(dpp, "UpdateObject", ¶ms); + + if (ret) { + ldpp_dout(dpp, 0)<<"In UpdateObject failed err:(" <& info) +{ + int ret = 0; + DBOpParams params = {}; + std::map omap; + + ret = get_object_impl(dpp, params); + + if (ret) { + ldpp_dout(dpp, 0) <<"get_object_impl failed err:(" <set_instance(buf); +} + +int DB::Object::obj_omap_get_all(const DoutPrefixProvider *dpp, + std::map *m) +{ + int ret = 0; + DBOpParams params = {}; + std::map omap; + + if (!m) + return -1; + + ret = get_object_impl(dpp, params); + + if (ret) { + ldpp_dout(dpp, 0) <<"get_object_impl failed err:(" < *m, bool* pmore) +{ + int ret = 0; + DBOpParams params = {}; + std::map omap; + map::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:(" <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& setattrs, + map* rmattrs) +{ + int ret = 0; + + DBOpParams params = {}; + rgw::sal::Attrs *attrs; + map::iterator iter; + RGWObjState* state; + + store->InitializeParams(dpp, ¶ms); + InitializeParamsfromObject(dpp, ¶ms); + ret = get_state(dpp, &state, true); + + if (ret && !state->exists) { + ldpp_dout(dpp, 0) <<"get_state failed err:(" <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", ¶ms); + + if (ret) { + ldpp_dout(dpp, 0)<<"In UpdateObject failed err:(" < *attrset; + + store->InitializeParams(dpp, ¶ms); + InitializeParamsfromObject(dpp, ¶ms); + + ret = store->ProcessOp(dpp, "GetObject", ¶ms); + + if (ret) { + ldpp_dout(dpp, 0) <<"In GetObject failed err:(" <ProcessOp(dpp, "UpdateObject", ¶ms); + + if (ret) { + ldpp_dout(dpp, 0)<<"In UpdateObject failed err:(" <InitializeParams(dpp, ¶ms); + InitializeParamsfromRawObj(dpp, ¶ms); + + ret = db->ProcessOp(dpp, "GetObjectData", ¶ms); + + if (ret) { + ldpp_dout(dpp, 0)<<"In GetObjectData failed err:(" <InitializeParams(dpp, ¶ms); + InitializeParamsfromRawObj(dpp, ¶ms); + + /* 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", ¶ms); + + if (ret) { + ldpp_dout(dpp, 0)<<"In PutObjectData failed err:(" <& 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:(" <InitializeParams(dpp, ¶ms); + InitializeParamsfromObject(dpp, ¶ms); + 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:(" <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::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()) { + 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(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(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& attrs, + bool assume_noent, bool modify_tail) +{ + DB *store = target->get_store(); + + RGWObjState* state = &obj_state; + map *attrset; + DBOpParams params = {}; + int ret = 0; + string etag; + string content_type; + bufferlist acl_bl; + string storage_class; + + map::iterator iter; + + store->InitializeParams(dpp, ¶ms); + target->InitializeParamsfromObject(dpp, ¶ms); + + 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", ¶ms); + if (ret) { + ldpp_dout(dpp, 0)<<"In PutObject failed err:(" <list_versioned_objects(dpp, del_params.op.obj.list_entries); + if (ret) { + ldpp_dout(dpp, 0)<<"ListVersionedObjects failed err:(" <get_store(); + + ret = store->ProcessOp(dpp, "DeleteObject", &del_params); + if (ret) { + ldpp_dout(dpp, 0) << "In DeleteObject failed err:(" <ProcessOp(dpp, "UpdateObjectData", &update_params); + + if (ret) { + ldpp_dout(dpp, 0) << "Updating tail objects mtime failed err:(" <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:(" <* entry) +{ + int ret = 0; + const DoutPrefixProvider *dpp = get_def_dpp(); + + DBOpParams params = {}; + InitializeParams(dpp, ¶ms); + + params.op.lc_entry.index = oid; + params.op.lc_entry.entry.set_bucket(marker); + + params.op.query_str = "get_entry"; + ret = ProcessOp(dpp, "GetLCEntry", ¶ms); + + if (ret) { + ldpp_dout(dpp, 0)<<"In GetLCEntry failed err:(" <reset(e); + } + +out: + return ret; +} + +int DB::get_next_entry(const std::string& oid, const std::string& marker, + std::unique_ptr* entry) +{ + int ret = 0; + const DoutPrefixProvider *dpp = get_def_dpp(); + + DBOpParams params = {}; + InitializeParams(dpp, ¶ms); + + 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", ¶ms); + + if (ret) { + ldpp_dout(dpp, 0)<<"In GetLCEntry failed err:(" <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, ¶ms); + + params.op.lc_entry.index = oid; + params.op.lc_entry.entry = entry; + + ret = ProcessOp(dpp, "InsertLCEntry", ¶ms); + + if (ret) { + ldpp_dout(dpp, 0)<<"In InsertLCEntry failed err:(" <>& entries) +{ + int ret = 0; + const DoutPrefixProvider *dpp = get_def_dpp(); + + entries.clear(); + + DBOpParams params = {}; + InitializeParams(dpp, ¶ms); + + params.op.lc_entry.index = oid; + params.op.lc_entry.min_marker = marker; + params.op.list_max_count = max_entries; + + ret = ProcessOp(dpp, "ListLCEntries", ¶ms); + + if (ret) { + ldpp_dout(dpp, 0)<<"In ListLCEntries failed err:(" <(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, ¶ms); + + params.op.lc_entry.index = oid; + params.op.lc_entry.entry = entry; + + ret = ProcessOp(dpp, "RemoveLCEntry", ¶ms); + + if (ret) { + ldpp_dout(dpp, 0)<<"In RemoveLCEntry failed err:(" <* head) +{ + int ret = 0; + const DoutPrefixProvider *dpp = get_def_dpp(); + + DBOpParams params = {}; + InitializeParams(dpp, ¶ms); + + params.op.lc_head.index = oid; + + ret = ProcessOp(dpp, "GetLCHead", ¶ms); + + if (ret) { + ldpp_dout(dpp, 0)<<"In GetLCHead failed err:(" <(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, ¶ms); + + params.op.lc_head.index = oid; + params.op.lc_head.head = head; + + ret = ProcessOp(dpp, "InsertLCHead", ¶ms); + + if (ret) { + ldpp_dout(dpp, 0)<<"In InsertLCHead failed err:(" < 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 +#include +#include +#include +#include +#include +#include +#include "fmt/format.h" +#include +#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 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 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 rules; + std::string tail_instance; /* tail object's instance */ + + + /* Obj's omap store */ + std::map omap; + + /* Extra fields */ + bool is_multipart; + std::list mp_parts; + + bufferlist head_data; + std::string min_marker; + std::string max_marker; + std::string prefix; + std::list 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 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 InsertUser; + std::shared_ptr RemoveUser; + std::shared_ptr GetUser; + std::shared_ptr InsertBucket; + std::shared_ptr UpdateBucket; + std::shared_ptr RemoveBucket; + std::shared_ptr GetBucket; + std::shared_ptr ListUserBuckets; + std::shared_ptr InsertLCEntry; + std::shared_ptr RemoveLCEntry; + std::shared_ptr GetLCEntry; + std::shared_ptr ListLCEntries; + std::shared_ptr InsertLCHead; + std::shared_ptr RemoveLCHead; + std::shared_ptr GetLCHead; +}; + +class ObjectOp { + public: + ObjectOp() {}; + + virtual ~ObjectOp() {} + + std::shared_ptr PutObject; + std::shared_ptr DeleteObject; + std::shared_ptr GetObject; + std::shared_ptr UpdateObject; + std::shared_ptr ListBucketObjects; + std::shared_ptr ListVersionedObjects; + std::shared_ptr PutObjectData; + std::shared_ptr UpdateObjectData; + std::shared_ptr GetObjectData; + std::shared_ptr DeleteObjectData; + std::shared_ptr 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 + * . 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + //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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + /* 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 ¶ms) { + /* 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 ¶ms) { + 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 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 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 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 *pattrs, + RGWObjVersionTracker *pobjv_tracker); + int store_user(const DoutPrefixProvider *dpp, + RGWUserInfo& uinfo, bool exclusive, std::map *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& 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* 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 - + // "____" + 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 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 lk_guard(mtx); + stop_signalled = true; + cv.notify_one(); + } + + friend class DB; + }; + std::unique_ptr 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 *result, + std::map *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 *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* rmattrs; + const bufferlist *data; + RGWObjManifest *manifest; + const std::string *ptag; + std::list *remove_objs; + ceph::real_time set_mtime; + rgw_user owner; + RGWObjCategory category; + int flags; + const char *if_match; + const char *if_nomatch; + std::optional 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& attrs, + bool assume_noent, bool modify_tail); + int write_meta(const DoutPrefixProvider *dpp, uint64_t size, + uint64_t accounted_size, std::map& 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 *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& 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& setattrs, + std::map* 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& keys, + std::map* vals); + int obj_omap_get_all(const DoutPrefixProvider *dpp, std::map *m); + int obj_omap_get_vals(const DoutPrefixProvider *dpp, const std::string& marker, uint64_t count, + std::map *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& 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* entry); + int get_next_entry(const std::string& oid, const std::string& marker, + std::unique_ptr* 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>& entries); + int rm_entry(const std::string& oid, rgw::sal::Lifecycle::LCEntry& entry); + int get_head(const std::string& oid, std::unique_ptr* 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 +#include +#include +#include +#include +#include +#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 +#include +#include + +#include + +#include + +#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 entries, + sal::ListResult& 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; + +using SQLiteConnectionPool = ConnectionPool< + sqlite::Connection, sqlite::ConnectionFactory>; + +} // anonymous namespace + +class SQLiteImpl : public SQLiteConnectionPool { + public: + using SQLiteConnectionPool::SQLiteConnectionPool; +}; + + +SQLiteConfigStore::SQLiteConfigStore(std::unique_ptr 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* 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( + 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* 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( + 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* 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( + 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* 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( + 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 entries, + sal::ListResult& 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 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 entries, + sal::ListResult& 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* 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( + 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* 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( + 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* 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( + 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* 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( + 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 entries, + sal::ListResult& 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* 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( + 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* 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( + 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* 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( + 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* 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( + 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 entries, + sal::ListResult& 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(user)); + if (result.ec != std::errc{}) { + return static_cast(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 +{ + 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(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(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 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* writer) override; + int read_realm_by_id(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view realm_id, + RGWRealm& info, + std::unique_ptr* writer) override; + int read_realm_by_name(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view realm_name, + RGWRealm& info, + std::unique_ptr* writer) override; + int read_default_realm(const DoutPrefixProvider* dpp, + optional_yield y, + RGWRealm& info, + std::unique_ptr* 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 entries, + sal::ListResult& 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 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 entries, + sal::ListResult& 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* writer) override; + int read_zonegroup_by_id(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view zonegroup_id, + RGWZoneGroup& info, + std::unique_ptr* writer) override; + int read_zonegroup_by_name(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view zonegroup_name, + RGWZoneGroup& info, + std::unique_ptr* writer) override; + int read_default_zonegroup(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view realm_id, + RGWZoneGroup& info, + std::unique_ptr* writer) override; + int list_zonegroup_names(const DoutPrefixProvider* dpp, + optional_yield y, const std::string& marker, + std::span entries, + sal::ListResult& 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* writer) override; + int read_zone_by_id(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view zone_id, + RGWZoneParams& info, + std::unique_ptr* writer) override; + int read_zone_by_name(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view zone_name, + RGWZoneParams& info, + std::unique_ptr* writer) override; + int read_default_zone(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view realm_id, + RGWZoneParams& info, + std::unique_ptr* writer) override; + int list_zone_names(const DoutPrefixProvider* dpp, + optional_yield y, const std::string& marker, + std::span entries, + sal::ListResult& 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 impl; +}; // SQLiteConfigStore + + +auto create_sqlite_store(const DoutPrefixProvider* dpp, const std::string& uri) + -> std::unique_ptr; + +} // 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 + +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 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 + +#include + +#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 +{ +#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 +#include "rgw_sal_config.h" + +namespace rgw::dbstore { + +// ConfigStore factory +auto create_config_store(const DoutPrefixProvider* dpp, const std::string& uri) + -> std::unique_ptr; + +} // 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 +#include +#include +#include +#include + +#include "dbstore_mgr.h" +#include +#include + +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:"<get_def_dpp(); + + db->InitializeParams(dpp, ¶ms); + + params.op.user.uinfo.display_name = user1; + params.op.user.uinfo.user_id.tenant = "tenant"; + params.op.user.uinfo.user_id.id = user1; + params.op.user.uinfo.suspended = 123; + params.op.user.uinfo.max_buckets = 456; + params.op.user.uinfo.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", ¶ms); + cout << "InsertUser return value: " << ret << "\n"; + + DBOpParams params2 = {}; + params.op.user.uinfo.user_id.tenant = "tenant2"; + + db->InitializeParams(dpp, ¶ms2); + params2.op.user.uinfo.display_name = user1; + ret = db->ProcessOp(dpp, "GetUser", ¶ms2); + + cout << "GetUser return value: " << ret << "\n"; + + cout << "tenant: " << params2.op.user.uinfo.user_id.tenant << "\n"; + cout << "suspended: " << (int)params2.op.user.uinfo.suspended << "\n"; + + list::iterator it = params2.op.user.uinfo.placement_tags.begin(); + + while (it != params2.op.user.uinfo.placement_tags.end()) { + cout << "list = " << *it << "\n"; + it++; + } + + map::iterator it2 = params2.op.user.uinfo.access_keys.begin(); + + while (it2 != params2.op.user.uinfo.access_keys.end()) { + cout << "keys = " << it2->first << "\n"; + RGWAccessKey k = it2->second; + cout << "id = " << k.id << ", keys = " << k.key << "\n"; + it2++; + } + + params.op.bucket.info.bucket.name = bucketa; + db->ProcessOp(dpp, "InsertBucket", ¶ms); + + params.op.user.uinfo.display_name = user2; + params.op.user.uinfo.user_id.id = user2; + db->ProcessOp(dpp, "InsertUser", ¶ms); + + params.op.bucket.info.bucket.name = bucketb; + db->ProcessOp(dpp, "InsertBucket", ¶ms); + + db->ProcessOp(dpp, "GetUser", ¶ms); + db->ProcessOp(dpp, "GetBucket", ¶ms); + + db->ListAllUsers(dpp, ¶ms); + db->ListAllBuckets(dpp, ¶ms); + + params.op.bucket.info.bucket.name = bucketb; + + db->ProcessOp(dpp, "RemoveBucket", ¶ms); + + params.op.user.uinfo.user_id.id = user2; + db->ProcessOp(dpp, "RemoveUser", ¶ms); + + db->ListAllUsers(dpp, ¶ms); + db->ListAllBuckets(dpp, ¶ms); + cout<<"Exiting thread:"< 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 = "<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 + +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::iterator iter; + DB *dbs = nullptr; + pair::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::iterator,bool> ret; + const auto& db_path = g_conf().get_val("dbstore_db_dir"); + const auto& db_name = g_conf().get_val("dbstore_db_name_prefix") + "-" + tenant; + + auto db_full_path = std::filesystem::path(db_path) / db_name; + ldout(cct, 0) << "DB initialization full db_path("<Initialize("", -1) < 0) { + ldout(cct, 0) << "DB initialization failed for tenant("<(tenant, dbs)); + + /* + * Its safe to check for already existing entry (just + * incase other thread raced and created the entry) + */ + if (ret.second == false) { + /* Entry already created by another thread */ + delete dbs; + + dbs = ret.first->second; + } + + return dbs; +} + +void DBStoreManager::deleteDB(string tenant) { + map::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::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 +#include +#include +#include +#include +#include +#include + +#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 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 +#include + +#include + +#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; + + +// 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 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 + { + auto db = open_database(uri.c_str(), flags); + return std::make_unique(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 +#include + +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(e), error_category()}; +} + +inline std::error_condition make_error_condition(errc e) +{ + return {static_cast(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("<(blob), blob_len); \ + \ + decode(param, b); \ + }while(0); + +#define SQL_EXECUTE(dpp, params, stmt, cbk, args...) \ + do{ \ + const std::lock_guard 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(" <op, stmt, cbk); \ + \ + Reset(dpp, stmt); \ + \ + if (ret) { \ + ldpp_dout(dpp, 0) <<"Execution failed for stmt(" <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<(&this->db, this->getDBname(), cct); + dbops.RemoveUser = make_shared(&this->db, this->getDBname(), cct); + dbops.GetUser = make_shared(&this->db, this->getDBname(), cct); + dbops.InsertBucket = make_shared(&this->db, this->getDBname(), cct); + dbops.UpdateBucket = make_shared(&this->db, this->getDBname(), cct); + dbops.RemoveBucket = make_shared(&this->db, this->getDBname(), cct); + dbops.GetBucket = make_shared(&this->db, this->getDBname(), cct); + dbops.ListUserBuckets = make_shared(&this->db, this->getDBname(), cct); + dbops.InsertLCEntry = make_shared(&this->db, this->getDBname(), cct); + dbops.RemoveLCEntry = make_shared(&this->db, this->getDBname(), cct); + dbops.GetLCEntry = make_shared(&this->db, this->getDBname(), cct); + dbops.ListLCEntries = make_shared(&this->db, this->getDBname(), cct); + dbops.InsertLCHead = make_shared(&this->db, this->getDBname(), cct); + dbops.RemoveLCHead = make_shared(&this->db, this->getDBname(), cct); + dbops.GetLCHead = make_shared(&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 "<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::iterator iter; + map 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(sdb, db_name, cct); + DeleteObject = make_shared(sdb, db_name, cct); + GetObject = make_shared(sdb, db_name, cct); + UpdateObject = make_shared(sdb, db_name, cct); + ListBucketObjects = make_shared(sdb, db_name, cct); + ListVersionedObjects = make_shared(sdb, db_name, cct); + PutObjectData = make_shared(sdb, db_name, cct); + UpdateObjectData = make_shared(sdb, db_name, cct); + GetObjectData = make_shared(sdb, db_name, cct); + DeleteObjectData = make_shared(sdb, db_name, cct); + DeleteStaleObjectData = make_shared(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::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::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(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 +#include +#include +#include +#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 + void operator()(T* p) { ::sqlite3_free(p); } +}; +template +using sqlite_ptr = std::unique_ptr; + + +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 sql; + if (dpp->get_cct()->_conf->subsys.should_gather()) { + 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 sql; + if (dpp->get_cct()->_conf->subsys.should_gather()) { + 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(text), size}; + } else { + return {}; + } +} + +auto read_text_rows(const DoutPrefixProvider* dpp, + const stmt_execution& stmt, + std::span entries) + -> std::span +{ + sqlite_ptr sql; + if (dpp->get_cct()->_conf->subsys.should_gather()) { + 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{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 +#include +#include + +#include + +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; + +// 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; + +// 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; + + +// 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 entries) + -> std::span; + +// 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 +#include +#include + +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(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("dbstore_db_dir"); + const auto& db_name = ctx_->_conf.get_val("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("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 getContext() const { + return ctx_; + } + + private: + std::shared_ptr 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(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(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(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(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(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(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(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(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 +#include +#include +#include +#include +#include +#include +#include "rgw_common.h" + +using namespace std; +using DB = rgw::store::DB; + +vector 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 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 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", ¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreTest, GetUser) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ProcessOp(dpp, "GetUser", ¶ms); + ASSERT_EQ(ret, 0); + ASSERT_EQ(params.op.user.uinfo.user_id.tenant, "tenant"); + ASSERT_EQ(params.op.user.uinfo.user_email, "user1@dbstore.com"); + ASSERT_EQ(params.op.user.uinfo.user_id.id, "user_id1"); + ASSERT_EQ(params.op.user.uinfo.suspended, 123); + ASSERT_EQ(params.op.user.uinfo.max_buckets, 456); + ASSERT_EQ(params.op.user.uinfo.placement_tags.back(), "tags"); + RGWAccessKey k; + map::iterator it2 = params.op.user.uinfo.access_keys.begin(); + k = it2->second; + ASSERT_EQ(k.id, "id1"); + ASSERT_EQ(k.key, "key1"); + it2++; + k = it2->second; + ASSERT_EQ(k.id, "id2"); + ASSERT_EQ(k.key, "key2"); + +} + +TEST_F(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", ¶ms); + ASSERT_EQ(ret, 0); + ASSERT_EQ(params.op.user.uinfo.user_id.tenant, "tenant"); + ASSERT_EQ(params.op.user.uinfo.user_email, "user1@dbstore.com"); + ASSERT_EQ(params.op.user.uinfo.user_id.id, "user_id1"); + ASSERT_EQ(params.op.user.uinfo.suspended, 123); + ASSERT_EQ(params.op.user.uinfo.max_buckets, 456); + ASSERT_EQ(params.op.user.uinfo.placement_tags.back(), "tags"); + RGWAccessKey k; + map::iterator it2 = params.op.user.uinfo.access_keys.begin(); + k = it2->second; + ASSERT_EQ(k.id, "id1"); + ASSERT_EQ(k.key, "key1"); + it2++; + k = it2->second; + ASSERT_EQ(k.id, "id2"); + ASSERT_EQ(k.key, "key2"); + +} + +TEST_F(DBStoreTest, GetUserQueryByEmail) { + int ret = -1; + RGWUserInfo uinfo; + string email = "user1@dbstore.com"; + map 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::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::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 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 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::iterator it = uinfo.access_keys.begin(); + k = it->second; + ASSERT_EQ(k.id, "id1"); + ASSERT_EQ(k.key, "key1"); + it++; + k = it->second; + ASSERT_EQ(k.id, "id2"); + ASSERT_EQ(k.key, "key2"); + + ASSERT_EQ(objv.read_version.ver, 2); + + bufferlist k1, k2; + string attr; + map::iterator it2 = attrs.begin(); + k1 = it2->second; + decode(attr, k1); + ASSERT_EQ(attr, "attrs1"); + it2++; + k2 = it2->second; + decode(attr, k2); + ASSERT_EQ(attr, "attrs2"); +} + +TEST_F(DBStoreTest, ListAllUsers) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ListAllUsers(dpp, ¶ms); + 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", ¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreTest, UpdateBucketAttrs) { + int ret = -1; + RGWBucketInfo info; + map attrs; + RGWObjVersionTracker objv; + + bufferlist aclbl, aclbl2; + encode("attrs1", aclbl); + attrs["attr1"] = aclbl; + encode("attrs2", aclbl2); + attrs["attr2"] = aclbl2; + + info.bucket.name = "bucket1"; + + /* invalid version number */ + objv.read_version.ver = 4; + ret = db->update_bucket(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", ¶ms); + 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", ¶ms); + ASSERT_EQ(ret, 0); + ASSERT_EQ(params.op.bucket.info.bucket.name, "bucket1"); + ASSERT_EQ(params.op.bucket.info.bucket.tenant, "tenant"); + ASSERT_EQ(params.op.bucket.info.bucket.marker, "marker2"); + ASSERT_EQ(params.op.bucket.ent.size, 1024); + ASSERT_EQ(params.op.bucket.ent.bucket.name, "bucket1"); + ASSERT_EQ(params.op.bucket.ent.bucket.tenant, "tenant"); + ASSERT_EQ(params.op.bucket.info.has_instance_obj, false); + ASSERT_EQ(params.op.bucket.info.objv_tracker.read_version.ver, 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::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 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, ¶ms); + 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", ¶ms); + 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", ¶ms); + 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", ¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreTest, ListAllObjects) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ListAllObjects(dpp, ¶ms); + ASSERT_GE(ret, 0); +} + +TEST_F(DBStoreTest, GetObject) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ProcessOp(dpp, "GetObject", ¶ms); + 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 setattrs; + map rmattrs; + map 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 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 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 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 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", ¶ms); + 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", ¶ms); + + 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", ¶ms); + + 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 keys; + std::map 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 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 keys; + std::map 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", ¶ms); + 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", ¶ms); + 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", ¶ms); + 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", ¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreTest, DeleteObject) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ProcessOp(dpp, "DeleteObject", ¶ms); + 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 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 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> 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", ¶ms); + ASSERT_EQ(ret, 0); +} + +TEST_F(DBStoreTest, RemoveUser) { + struct DBOpParams params = GlobalParams; + int ret = -1; + + ret = db->ProcessOp(dpp, "RemoveUser", ¶ms); + 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", ¶ms); + 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; +} -- cgit v1.2.3