summaryrefslogtreecommitdiffstats
path: root/src/rgw/driver/dbstore/sqlite/statement.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rgw/driver/dbstore/sqlite/statement.cc')
-rw-r--r--src/rgw/driver/dbstore/sqlite/statement.cc196
1 files changed, 196 insertions, 0 deletions
diff --git a/src/rgw/driver/dbstore/sqlite/statement.cc b/src/rgw/driver/dbstore/sqlite/statement.cc
new file mode 100644
index 000000000..dcf7dba9c
--- /dev/null
+++ b/src/rgw/driver/dbstore/sqlite/statement.cc
@@ -0,0 +1,196 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "common/dout.h"
+#include "error.h"
+#include "statement.h"
+
+#define dout_subsys ceph_subsys_rgw_dbstore
+
+namespace rgw::dbstore::sqlite {
+
+// owning pointer to arbitrary memory allocated and returned by sqlite3
+struct sqlite_deleter {
+ template <typename T>
+ void operator()(T* p) { ::sqlite3_free(p); }
+};
+template <typename T>
+using sqlite_ptr = std::unique_ptr<T, sqlite_deleter>;
+
+
+stmt_ptr prepare_statement(const DoutPrefixProvider* dpp,
+ sqlite3* db, std::string_view sql)
+{
+ sqlite3_stmt* stmt = nullptr;
+ int result = ::sqlite3_prepare_v2(db, sql.data(), sql.size(), &stmt, nullptr);
+ auto ec = std::error_code{result, sqlite::error_category()};
+ if (ec != sqlite::errc::ok) {
+ const char* errmsg = ::sqlite3_errmsg(db);
+ ldpp_dout(dpp, 1) << "preparation failed: " << errmsg
+ << " (" << ec << ")\nstatement: " << sql << dendl;
+ throw sqlite::error(errmsg, ec);
+ }
+ return stmt_ptr{stmt};
+}
+
+static int bind_index(const DoutPrefixProvider* dpp,
+ const stmt_binding& stmt, const char* name)
+{
+ const int index = ::sqlite3_bind_parameter_index(stmt.get(), name);
+ if (index <= 0) {
+ ldpp_dout(dpp, 1) << "binding failed on parameter name="
+ << name << dendl;
+ sqlite3* db = ::sqlite3_db_handle(stmt.get());
+ throw sqlite::error(db);
+ }
+ return index;
+}
+
+void bind_text(const DoutPrefixProvider* dpp, const stmt_binding& stmt,
+ const char* name, std::string_view value)
+{
+ const int index = bind_index(dpp, stmt, name);
+
+ int result = ::sqlite3_bind_text(stmt.get(), index, value.data(),
+ value.size(), SQLITE_STATIC);
+ auto ec = std::error_code{result, sqlite::error_category()};
+ if (ec != sqlite::errc::ok) {
+ ldpp_dout(dpp, 1) << "binding failed on parameter name="
+ << name << " value=" << value << dendl;
+ sqlite3* db = ::sqlite3_db_handle(stmt.get());
+ throw sqlite::error(db, ec);
+ }
+}
+
+void bind_int(const DoutPrefixProvider* dpp, const stmt_binding& stmt,
+ const char* name, int value)
+{
+ const int index = bind_index(dpp, stmt, name);
+
+ int result = ::sqlite3_bind_int(stmt.get(), index, value);
+ auto ec = std::error_code{result, sqlite::error_category()};
+ if (ec != sqlite::errc::ok) {
+ ldpp_dout(dpp, 1) << "binding failed on parameter name="
+ << name << " value=" << value << dendl;
+ sqlite3* db = ::sqlite3_db_handle(stmt.get());
+ throw sqlite::error(db, ec);
+ }
+}
+
+void eval0(const DoutPrefixProvider* dpp, const stmt_execution& stmt)
+{
+ sqlite_ptr<char> sql;
+ if (dpp->get_cct()->_conf->subsys.should_gather<dout_subsys, 20>()) {
+ sql.reset(::sqlite3_expanded_sql(stmt.get()));
+ }
+
+ const int result = ::sqlite3_step(stmt.get());
+ auto ec = std::error_code{result, sqlite::error_category()};
+ sqlite3* db = ::sqlite3_db_handle(stmt.get());
+
+ if (ec != sqlite::errc::done) {
+ const char* errmsg = ::sqlite3_errmsg(db);
+ ldpp_dout(dpp, 20) << "evaluation failed: " << errmsg
+ << " (" << ec << ")\nstatement: " << sql.get() << dendl;
+ throw sqlite::error(errmsg, ec);
+ }
+ ldpp_dout(dpp, 20) << "evaluation succeeded: " << sql.get() << dendl;
+}
+
+void eval1(const DoutPrefixProvider* dpp, const stmt_execution& stmt)
+{
+ sqlite_ptr<char> sql;
+ if (dpp->get_cct()->_conf->subsys.should_gather<dout_subsys, 20>()) {
+ sql.reset(::sqlite3_expanded_sql(stmt.get()));
+ }
+
+ const int result = ::sqlite3_step(stmt.get());
+ auto ec = std::error_code{result, sqlite::error_category()};
+ if (ec != sqlite::errc::row) {
+ sqlite3* db = ::sqlite3_db_handle(stmt.get());
+ const char* errmsg = ::sqlite3_errmsg(db);
+ ldpp_dout(dpp, 1) << "evaluation failed: " << errmsg << " (" << ec
+ << ")\nstatement: " << sql.get() << dendl;
+ throw sqlite::error(errmsg, ec);
+ }
+ ldpp_dout(dpp, 20) << "evaluation succeeded: " << sql.get() << dendl;
+}
+
+int column_int(const stmt_execution& stmt, int column)
+{
+ return ::sqlite3_column_int(stmt.get(), column);
+}
+
+std::string column_text(const stmt_execution& stmt, int column)
+{
+ const unsigned char* text = ::sqlite3_column_text(stmt.get(), column);
+ // may be NULL
+ if (text) {
+ const std::size_t size = ::sqlite3_column_bytes(stmt.get(), column);
+ return {reinterpret_cast<const char*>(text), size};
+ } else {
+ return {};
+ }
+}
+
+auto read_text_rows(const DoutPrefixProvider* dpp,
+ const stmt_execution& stmt,
+ std::span<std::string> entries)
+ -> std::span<std::string>
+{
+ sqlite_ptr<char> sql;
+ if (dpp->get_cct()->_conf->subsys.should_gather<dout_subsys, 20>()) {
+ sql.reset(::sqlite3_expanded_sql(stmt.get()));
+ }
+
+ std::size_t count = 0;
+ while (count < entries.size()) {
+ const int result = ::sqlite3_step(stmt.get());
+ auto ec = std::error_code{result, sqlite::error_category()};
+ if (ec == sqlite::errc::done) {
+ break;
+ }
+ if (ec != sqlite::errc::row) {
+ sqlite3* db = ::sqlite3_db_handle(stmt.get());
+ const char* errmsg = ::sqlite3_errmsg(db);
+ ldpp_dout(dpp, 1) << "evaluation failed: " << errmsg << " (" << ec
+ << ")\nstatement: " << sql.get() << dendl;
+ throw sqlite::error(errmsg, ec);
+ }
+ entries[count] = column_text(stmt, 0);
+ ++count;
+ }
+ ldpp_dout(dpp, 20) << "statement evaluation produced " << count
+ << " results: " << sql.get() << dendl;
+
+ return entries.first(count);
+}
+
+void execute(const DoutPrefixProvider* dpp, sqlite3* db, const char* query,
+ sqlite3_callback callback, void* arg)
+{
+ char* errmsg = nullptr;
+ const int result = ::sqlite3_exec(db, query, callback, arg, &errmsg);
+ auto ec = std::error_code{result, sqlite::error_category()};
+ auto ptr = sqlite_ptr<char>{errmsg}; // free on destruction
+ if (ec != sqlite::errc::ok) {
+ ldpp_dout(dpp, 1) << "query execution failed: " << errmsg << " (" << ec
+ << ")\nquery: " << query << dendl;
+ throw sqlite::error(errmsg, ec);
+ }
+ ldpp_dout(dpp, 20) << "query execution succeeded: " << query << dendl;
+}
+
+} // namespace rgw::dbstore::sqlite