#include "matador/sql/connection.hpp" #include "matador/sql/backend_provider.hpp" #include "matador/sql/dialect.hpp" #include "matador/sql/dialect_token.hpp" #include "matador/sql/error_code.hpp" #include "matador/sql/interface/connection_impl.hpp" #include #include #include namespace matador::sql { namespace internal { class connection_statement_proxy final : public statement_proxy { public: explicit connection_statement_proxy(std::unique_ptr&& stmt) : statement_proxy(std::move(stmt)) {} utils::result execute(interface::parameter_binder& bindings) override { return statement_->execute(bindings); } utils::result, utils::error> fetch(interface::parameter_binder& bindings) override { return statement_->fetch(bindings); } }; } connection::connection(connection_info info, const std::shared_ptr &sql_logger) : connection_info_(std::move(info)) , logger_(sql_logger) { connection_.reset(backend_provider::instance().create_connection(connection_info_.type, connection_info_)); } connection::connection(const std::string& dns, const std::shared_ptr &sql_logger) : connection(connection_info::parse(dns), sql_logger) {} connection::connection(const connection &x) : connection_info_(x.connection_info_) { if (x.connection_) { throw std::runtime_error("couldn't copy connection with valid connection impl"); } } connection &connection::operator=(const connection &x) { if (this == &x) { return *this; } connection_info_ = x.connection_info_; if (x.connection_) { throw std::runtime_error("couldn't copy connection with valid connection impl"); } return *this; } connection::connection( connection&& x ) noexcept : connection_info_(std::move(x.connection_info_)) , connection_(std::move(x.connection_)) , logger_(std::move(x.logger_)) {} connection & connection::operator=(connection &&x) noexcept { connection_info_ = std::move(x.connection_info_); connection_ = std::move(x.connection_); logger_ = std::move(x.logger_); return *this; } connection::~connection() { if (!connection_) { return; } if (connection_->is_open()) { connection_->close(); } backend_provider::instance().destroy_connection(connection_info_.type, connection_.release()); connection_ = nullptr; } utils::result connection::open() const { const auto res = is_open(); if (res.is_error()) { return utils::failure(res.err()); } if (!*res) { logger_->on_connect(); return connection_->open(); } return utils::ok(); } utils::result connection::close() const { logger_->on_close(); return connection_->close(); } utils::result connection::is_open() const { return connection_->is_open(); } const connection_info &connection::info() const { return connection_info_; } std::string connection::type() const { return connection_info_.type; } utils::result connection::begin() const { const auto res = connection_->execute(dialect().token_at(dialect_token::BEGIN)); if (res.is_error()) { return utils::failure(res.err()); } return utils::ok(); } utils::result connection::commit() const { const auto res = connection_->execute(dialect().token_at(dialect_token::COMMIT)); if (res.is_error()) { return utils::failure(res.err()); } return utils::ok(); } utils::result connection::rollback() const { const auto res = connection_->execute(dialect().token_at(dialect_token::ROLLBACK)); if (res.is_error()) { return utils::failure(res.err()); } return utils::ok(); } utils::result, utils::error> connection::describe(const std::string &table_name) const { return connection_->describe(table_name); } utils::result connection::exists(const std::string &schema_name, const std::string &table_name) const { return connection_->exists(schema_name, table_name); } utils::result connection::exists(const std::string &table_name) const { return connection_->exists(dialect().default_schema_name(), table_name); } utils::result connection::execute(const std::string &sql) const { logger_->on_execute(sql); return connection_->execute(sql); } bool has_unknown_columns(const std::vector &columns) { return std::any_of(std::begin(columns), std::end(columns), [](const auto &col) { return col.type() == utils::basic_type::type_null; }); } // query_result connection::fetch(const query_context &ctx) const // { // if (ctx.prototype.empty() || is_unknown(ctx.prototype)) { // const auto table_prototype = describe(ctx.table.name); // for (auto &col : ctx.prototype) { // const auto rit = std::find_if(std::begin(table_prototype), std::end(table_prototype), [&col](const auto &value) { // return value.name() == col.name(); // }); // if (col.type() == data_type::type_unknown && rit != table_prototype.end()) { // const_cast(col).type(rit->type()); // } // } // } // // auto it = prototypes_.find(q.table_name); // // if (it == prototypes_.end()) { // // it = prototypes_.emplace(q.table_name, describe(q.table_name)).first; // // } // // // adjust columns from given query // // for (auto &col : q.prototype) { // // if (const auto rit = it->second.find(col.name()); col.type() == data_type_t::type_unknown && rit != it->second.end()) { // // const_cast(col).type(rit->type()); // // } // // } // auto res = fetch(ctx.sql); // return query_result{std::move(res), ctx.prototype}; // } utils::result, utils::error> connection::fetch(const query_context &ctx) const { logger_->on_fetch(ctx.sql); return connection_->fetch(ctx); } utils::result connection::execute(const query_context& ctx) const { return execute(ctx.sql); } utils::result connection::prepare(const query_context &ctx) { auto result = perform_prepare(ctx); if (!result) { return utils::failure(result.err()); } return utils::ok(statement( std::make_shared(result.release()), logger_) ); } std::string connection::str( const query_context& ctx ) const { return ctx.sql; } const class dialect &connection::dialect() const { return connection_->dialect(); } utils::result, utils::error> connection::perform_prepare(const query_context& ctx) const { if (ctx.command != sql_command::SQL_CREATE_TABLE && (ctx.prototype.empty() || has_unknown_columns(ctx.prototype))) { if (const auto result = describe(ctx.table.name); result.is_ok()) { for (auto &col: ctx.prototype) { const auto rit = std::find_if( std::begin(*result), std::end(*result), [&col](const auto &value) { return value.name() == col.name(); } ); if (col.type() == utils::basic_type::type_null && rit != result->end()) { const_cast(col).type(rit->type()); } } } } auto result = connection_->prepare(ctx); if (!result) { return utils::failure(result.err()); } return utils::ok(result.release()); } }