devapi/session.cc (375 lines of code) (raw):

/* * Copyright (c) 2015, 2024, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2.0, as * published by the Free Software Foundation. * * This program is designed to work with certain software (including * but not limited to OpenSSL) that is licensed under separate terms, as * designated in a particular file or component or in included license * documentation. The authors of MySQL hereby grant you an additional * permission to link the program and your derivative works with the * separately licensed software that they have either included with * the program or referenced in the documentation. * * Without limiting anything contained in the foregoing, this file, * which is part of Connector/C++, is also subject to the * Universal FOSS Exception, version 1.0, a copy of which can be found at * https://oss.oracle.com/licenses/universal-foss-exception. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License, version 2.0, for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "impl.h" #include "../common/settings.h" #include "../common/result.h" #include <mysql/cdk.h> #include <uri_parser.h> #include <mysqlx/xdevapi.h> PUSH_SYS_WARNINGS #include <iostream> #include <sstream> #include <list> POP_SYS_WARNINGS using namespace ::mysqlx::impl::common; using namespace ::mysqlx::internal; using namespace ::mysqlx; using std::ostream; /* Session settings ================ */ void process_val( cdk::JSON::Processor::Any_prc::Scalar_prc *prc, const mysqlx::Value &val ) { using mysqlx::Value; assert(prc); auto vtype = val.getType(); switch (vtype) { // TODO: avoid unnecessary utf8 conversion case Value::STRING: prc->str(val.get<mysqlx::string>()); break; case Value::INT64: prc->num(val.get<int64_t>()); break; case Value::UINT64: prc->num(val.get<uint64_t>()); break; case Value::BOOL: prc->yesno(val.get<bool>()); break; case Value::VNULL: prc->null(); break; case Value::DOCUMENT: { std::stringstream json; json << val.get<DbDoc>(); prc->str(json.str()); } break; default: mysqlx::throw_error("Invalid type of session option value"); } } void process(cdk::JSON::Processor::Any_prc *prc, const mysqlx::Value &val) { using mysqlx::Value; assert(prc); switch (val.getType()) { case Value::DOCUMENT: { auto *dprc = prc->doc(); if (!dprc) return; DbDoc doc = val; dprc->doc_begin(); for (auto f : doc) { auto *eprc = dprc->key_val(f); if (eprc) process(eprc, doc[f]); } dprc->doc_end(); } break; case Value::ARRAY: { auto *aprc = prc->arr(); if (!aprc) return; aprc->list_begin(); for (Value v : val) { auto *eprc = aprc->list_el(); if (eprc) process(eprc, v); } aprc->list_end(); } break; default: process_val(prc->scalar(), val); break; } } template<> void Settings_detail<Settings_traits>::do_set(session_opt_list_t &&opts) { Setter set(*this); set.doc_begin(); for (auto &opt_val : opts) { int opt = opt_val.first; Value &val = opt_val.second; process(set.key_val(opt), val); } set.doc_end(); } /* Note: old code for ABI compatibility. Some versions of gcc complained if this was not defined inside namespace (even though it did not complain for the do_set() overload defined above) */ namespace mysqlx { MYSQLX_ABI_BEGIN(2,0) namespace internal { template<> void Settings_detail<Settings_traits>::do_set( std::list<std::pair<int, common::Value>> &&opts ) { Setter set(*this); set.doc_begin(); for (auto &opt_val : opts) { int opt = opt_val.first; Value val(opt_val.second); process_val(set.key_val(opt)->scalar(), val); } set.doc_end(); } } // internal MYSQLX_ABI_END(2,0) } // mysqlx /* Client implementation ====================== */ Client_detail::Client_detail(common::Settings_impl &settings) { cdk::ds::Multi_source source; settings.get_data_source(source); m_impl = std::make_shared<Impl>(source); m_impl->set_pool_opts(settings); } Shared_session_pool& Client_detail::get_session_pool() { return m_impl; } void Client_detail::close() { auto p = get_session_pool(); if (p) p->close(); } /* Session implementation ====================== */ Session_detail::Session_detail(common::Settings_impl &settings) { try { cdk::ds::Multi_source source; settings.get_data_source(source); m_impl = std::make_shared<Impl>(source); } catch (const cdk::foundation::connection::TLS::Options::TLS_version::Error &e) { std::stringstream msg; msg << "'" << e.get_ver() << "'"; msg << " not recognized as a valid TLS protocol version"; msg << " (should be one of TLSv1, TLSv1.1, TLSv1.2, TLSv1.3)"; throw_error(msg.str().c_str()); } catch(const cdk::Error &e) { if (e.code() == cdk::cdkerrc::tls_versions) throw_error("No valid TLS version was given, valid versions are: TLSv1.2, TLSv1.3"); else if (e.code() == cdk::cdkerrc::tls_ciphers) throw_error("No valid cipher suite found in the 'tls-ciphersuites' list"); try { throw; } CATCH_AND_WRAP } CATCH_AND_WRAP } Session_detail::Session_detail(Shared_session_pool &pool) { m_impl = std::make_shared<Impl>(pool); } cdk::Session& Session_detail::get_cdk_session() { if (!m_impl) throw Error("Session closed"); return *(m_impl->m_sess); } void Session_detail::prepare_for_cmd() { assert(m_impl); m_impl->prepare_for_cmd(); } void Session_detail::close() { m_impl->release(); m_impl.reset(); } // --------------------------------------------------------------------- /* Transactions. */ void Session_detail::start_transaction() { Op_trx<Trx_op::BEGIN> cmd(m_impl); cmd.execute(); } void Session_detail::commit() { Op_trx<Trx_op::COMMIT> cmd(m_impl); cmd.execute(); } void Session_detail::rollback(const mysqlx::string &sp) { Op_trx<Trx_op::ROLLBACK> cmd(m_impl, sp); cmd.execute(); } mysqlx::string Session_detail::savepoint_set(const mysqlx::string &sp) { Op_trx<Trx_op::SAVEPOINT_SET> cmd(m_impl, sp); cmd.execute(); return cmd.get_name(); } void Session_detail::savepoint_remove(const mysqlx::string &sp) { Op_trx<Trx_op::SAVEPOINT_REMOVE> cmd(m_impl, sp); cmd.execute(); } // --------------------------------------------------------------------- void Session_detail::create_schema(const mysqlx::string &name, bool reuse) { Schema_ref schema(name); create_object<Object_type::SCHEMA>(m_impl, schema, reuse); } void Session_detail::drop_schema(const mysqlx::string &name) { Schema_ref schema(name); drop_object<Object_type::SCHEMA>(m_impl, schema); } mysqlx::string Session_detail::get_default_schema_name() { if (m_impl->m_default_db.empty()) throw Error("No default schema set for the session"); return m_impl->m_default_db; } /* Schema list source. */ Query_src::~Query_src() NOEXCEPT { delete m_res; } Session_detail::Name_src::Name_src( const Session &sess, const mysqlx::string &pattern ) : m_sess(sess) { Op_list<Object_type::SCHEMA> list_op{ sess.m_impl, pattern }; m_res = new Res_impl(list_op.execute()); } auto Session_detail::Schema_src::iterator_get() -> Schema { return { const_cast<Session&>(m_sess), Name_src::iterator_get() }; } /* Schema ====== */ void Schema_detail::create_collection(const mysqlx::string &name, mysqlx::CollectionOptions options) { Object_ref coll(m_name, name); create_object<Object_type::COLLECTION>(m_sess, coll, options.m_data.reuse, options.m_data.validation.m_data.validation_level, options.m_data.validation.m_data.validation_schema.get_json()); } void Schema_detail::modify_collection(const mysqlx::string &name, mysqlx::CollectionOptions options) { Object_ref coll(m_name, name); if(options.m_data.reuse) throw_error("Can't use CollectionOptions::REUSE on collectionModify"); modify_object<Object_type::COLLECTION>( m_sess, coll, options.m_data.validation.m_data.validation_level, std::string(options.m_data.validation.m_data.validation_schema.get_json())); } void Schema_detail::drop_collection(const mysqlx::string& name) { Object_ref coll(m_name, name); drop_object<Object_type::COLLECTION>(m_sess, coll); } Schema_detail::Name_src::Name_src( const Schema &sch, Obj_type type, const mysqlx::string &pattern ) : m_schema(sch) { Schema_ref obj{ sch.getName() }; const auto &sess = sch.Schema_detail::m_sess; switch (type) { case COLLECTION: { Op_list<Object_type::COLLECTION> list_op(sess, obj, pattern); m_res = new Res_impl(list_op.execute()); }; break; case TABLE: { Op_list<Object_type::TABLE> list_op(sess, obj, pattern); m_res = new Res_impl(list_op.execute()); }; break; } } Collection Schema_detail::Collection_src::iterator_get() { return Collection(m_schema, Name_src::iterator_get()); } Table Schema_detail::Table_src::iterator_get() { auto *row = static_cast<const Row_data*>(m_row); assert(1 < row->size()); assert(cdk::TYPE_STRING == m_res->get_column(1).m_type); cdk::string type; m_res->get_column(1).get<cdk::TYPE_STRING>() .m_codec.from_bytes(row->at(1).data(), type); return Table(m_schema, Name_src::iterator_get(), type == "VIEW"); } /* Implementation of List_init<> source class taking items from query results. */ bool Query_src::iterator_next() { assert(m_res); m_row = m_res->get_row(); return nullptr != m_row; } mysqlx::string Query_src::iterator_get() { assert(m_row); auto *row = static_cast<const Row_data*>(m_row); const auto &name_col = m_res->get_column(0); const auto &data = row->at(0).data(); cdk::string name; // TDOD: Investigate why we get column type other than STRING. // This is related to changed default collation in newer servers and logic // we have to interpret BYTES columns as STRING for some collations. // TODO: use Value to do the conversion? switch (name_col.m_type) { case cdk::TYPE_STRING: m_res->get_column(0).get<cdk::TYPE_STRING>() .m_codec.from_bytes(data, name); break; case cdk::TYPE_BYTES: /* Even if we see name column reported as raw bytes, we assume it is like an utf8 string with null byte appended at the end. */ assert(0 < data.size()); assert(0 == *(data.end()-1)); name = std::string(data.begin(), data.end()-1); break; default: assert(false); } return cdk::foundation::ustring(name); } // --------------------------------------------------------------------- // String conversions. // // Note: We use cdk::string to perform required conversion. // template <typename C> inline std::basic_string<C> to_str(const mysqlx::string &other) { return (std::basic_string<C>)cdk::string(other); } template <typename C> inline void from_str(mysqlx::string &to, const std::basic_string<C> &from) { to = cdk::string(from); } std::string mysqlx::string::Impl::to_utf8(const string &other) { return to_str<char>(other); } void mysqlx::string::Impl::from_utf8(string &s, const std::string &other) { from_str(s, other); } std::u32string mysqlx::string::Impl::to_ucs4(const string &other) { return to_str<char32_t>(other); } void mysqlx::string::Impl::from_ucs4(string &s, const std::u32string &other) { from_str(s, other); } std::wstring mysqlx::string::Impl::to_wide(const string &other) { return to_str<wchar_t>(other); } void mysqlx::string::Impl::from_wide(string &s, const std::wstring &other) { from_str(s, other); } // ---------------------------------------------------------------------