// -*- 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