diff --git a/components/libminchassis/CMakeLists.txt b/components/libminchassis/CMakeLists.txt index 977777f150c3..a7ab1a7eea9e 100644 --- a/components/libminchassis/CMakeLists.txt +++ b/components/libminchassis/CMakeLists.txt @@ -33,6 +33,7 @@ SET(LIBMINCHASSIS_SOURCES mysql_component.cc mysql_service_implementation.cc registry.cc + registry_no_lock.cc rwlock_scoped_lock.cc ) diff --git a/components/libminchassis/minimal_chassis.cc b/components/libminchassis/minimal_chassis.cc index a88f74129041..1382434df546 100644 --- a/components/libminchassis/minimal_chassis.cc +++ b/components/libminchassis/minimal_chassis.cc @@ -34,6 +34,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "dynamic_loader_scheme_file_imp.h" #include "minimal_chassis_runtime_error_imp.h" #include "registry_imp.h" +#include "registry_no_lock_imp.h" extern SERVICE_TYPE(registry) imp_mysql_minimal_chassis_registry; @@ -52,10 +53,21 @@ BEGIN_SERVICE_IMPLEMENTATION(mysql_minimal_chassis, registry) mysql_registry_imp::acquire, mysql_registry_imp::acquire_related, mysql_registry_imp::release END_SERVICE_IMPLEMENTATION(); +BEGIN_SERVICE_IMPLEMENTATION(mysql_minimal_chassis_no_lock, registry) +mysql_registry_no_lock_imp::acquire, + mysql_registry_no_lock_imp::acquire_related, + mysql_registry_no_lock_imp::release END_SERVICE_IMPLEMENTATION(); + BEGIN_SERVICE_IMPLEMENTATION(mysql_minimal_chassis, registry_registration) mysql_registry_imp::register_service, mysql_registry_imp::unregister, mysql_registry_imp::set_default END_SERVICE_IMPLEMENTATION(); +BEGIN_SERVICE_IMPLEMENTATION(mysql_minimal_chassis_no_lock, + registry_registration) +mysql_registry_no_lock_imp::register_service, + mysql_registry_no_lock_imp::unregister, + mysql_registry_no_lock_imp::set_default END_SERVICE_IMPLEMENTATION(); + BEGIN_SERVICE_IMPLEMENTATION(mysql_minimal_chassis, registry_query) mysql_registry_imp::iterator_create, mysql_registry_imp::iterator_get, mysql_registry_imp::iterator_next, mysql_registry_imp::iterator_is_valid, @@ -104,7 +116,9 @@ mysql_runtime_error_imp::emit END_SERVICE_IMPLEMENTATION(); BEGIN_COMPONENT_PROVIDES(mysql_minimal_chassis) PROVIDES_SERVICE(mysql_minimal_chassis, registry), + PROVIDES_SERVICE(mysql_minimal_chassis_no_lock, registry), PROVIDES_SERVICE(mysql_minimal_chassis, registry_registration), + PROVIDES_SERVICE(mysql_minimal_chassis_no_lock, registry_registration), PROVIDES_SERVICE(mysql_minimal_chassis, registry_query), PROVIDES_SERVICE(mysql_minimal_chassis, registry_metadata_enumerate), PROVIDES_SERVICE(mysql_minimal_chassis, registry_metadata_query), diff --git a/components/libminchassis/registry.cc b/components/libminchassis/registry.cc index a1c6c73fce35..93ce41f95fdc 100644 --- a/components/libminchassis/registry.cc +++ b/components/libminchassis/registry.cc @@ -21,15 +21,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "component_common.h" -#include "mysql_service_implementation.h" #include "registry_imp.h" -#include -#include -#include -#include -#include - // pfs instrumentation headers #include @@ -51,9 +44,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ static PSI_rwlock_key key_rwlock_LOCK_registry; -typedef std::map - my_service_registry; - struct my_h_service_iterator_imp { my_service_registry::const_iterator m_it; minimal_chassis::rwlock_scoped_lock m_lock; @@ -85,14 +75,13 @@ void mysql_registry_imp::init() { mysql_rwlock_init(key_rwlock_LOCK_registry, &mysql_registry_imp::LOCK_registry); } + /** De-initializes registry. De-initializes RW lock, all other structures are cleaned up. */ void mysql_registry_imp::deinit() { - mysql_registry_imp::service_registry.clear(); - mysql_registry_imp::interface_mapping.clear(); - + mysql_registry_no_lock_imp::deinit(); mysql_rwlock_destroy(&mysql_registry_imp::LOCK_registry); } @@ -114,238 +103,6 @@ mysql_registry_imp::lock_registry_for_write() { __LINE__); } -/** - Finds a Service Implementation data structure based on the pointer to - interface struct supplied. Assumes caller has at least a read lock on the - Registry. - - @param interface A pointer to the interface structure of the Service - Implementation to look for. - @return A pointer to respective Service Implementation data structure, or - NULL if no such interface pointer is registered within the Registry. -*/ -mysql_service_implementation * -mysql_registry_imp::get_service_implementation_by_interface( - my_h_service interface) { - my_interface_mapping::const_iterator iter = - mysql_registry_imp::interface_mapping.find(interface); - if (iter == mysql_registry_imp::interface_mapping.cend()) { - return nullptr; - } - - return iter->second; -} - -/** - Gets current reference count for a Service Implementation related to the - specified pointer to the interface structure. Assumes caller has at least - a read lock on the Registry. - - @param interface A pointer to the interface structure of the Service - Implementation to get reference count of. - @return A current reference count for specified Service Implementation. - Returns 0 in case there is no such interface or it is not referenced. -*/ -uint64_t mysql_registry_imp::get_service_implementation_reference_count( - my_h_service interface) { - my_interface_mapping::const_iterator iter = - mysql_registry_imp::interface_mapping.find(interface); - if (iter == mysql_registry_imp::interface_mapping.cend()) { - return -1; - } - - return iter->second->get_reference_count(); -} - -/** - Finds and acquires a Service by name. A name of the Service or the Service - Implementation can be specified. In case of the Service name, the default - Service Implementation for Service specified will be returned. Assumes - caller has at least a read lock on the Registry. - - @param service_name Name of Service or Service Implementation to acquire. - @param [out] out_service Pointer to Service handle to set acquired Service. - @return Status of performed operation - @retval false success - @retval true failure -*/ -bool mysql_registry_imp::acquire_nolock(const char *service_name, - my_h_service *out_service) { - try { - if (out_service == nullptr) { - return true; - } - my_service_registry::const_iterator iter; - - iter = mysql_registry_imp::service_registry.find(service_name); - - if (iter == mysql_registry_imp::service_registry.cend()) return true; - - mysql_service_implementation *imp = iter->second; - imp->add_reference(); - *out_service = imp->interface(); - return false; - } catch (...) { - } - return true; -} - -/** - Releases the Service Implementation previously acquired. After the call to - this method the usage of the Service Implementation handle will lead to - unpredicted results. Assumes caller has at least a read lock on the - Registry. - - @param service Service Implementation handle of already acquired Service. - @return Status of performed operation - @retval false success - @retval true failure -*/ -bool mysql_registry_imp::release_nolock(my_h_service service) { - try { - if (service == nullptr) { - return true; - } - - mysql_service_implementation *service_implementation = - mysql_registry_imp::get_service_implementation_by_interface(service); - if (service_implementation == nullptr) { - return true; - } - return service_implementation->release_reference(); - } catch (...) { - } - return true; -} - -/** - Registers a new Service Implementation. If it is the first Service - Implementation for the specified Service then it is made a default one. - Assumes caller has a write lock on the Registry. - - @param service_implementation_name Name of the Service Implementation to - register. - @param ptr Pointer to the Service Implementation structure. - @return Status of performed operation - @retval false success - @retval true failure -*/ -bool mysql_registry_imp::register_service_nolock( - const char *service_implementation_name, my_h_service ptr) { - try { - std::unique_ptr imp = - std::unique_ptr( - new mysql_service_implementation(ptr, service_implementation_name)); - - if (imp->interface() == nullptr) { - return true; - } - - /* Register the implementation name. */ - std::pair addition_result = - mysql_registry_imp::service_registry.emplace(imp->name_c_str(), - imp.get()); - - /* Fail if it was present already. */ - if (!addition_result.second) { - return true; - } else { - try { - /* Register interface in mapping */ - mysql_registry_imp::interface_mapping.emplace(imp->interface(), - imp.get()); - - /* Register the Service Implementation as default for Service name in - case none were registered before. */ - mysql_registry_imp::service_registry.emplace_hint( - addition_result.first, imp->service_name_c_str(), imp.get()); - } catch (...) { - mysql_registry_imp::service_registry.erase(addition_result.first); - /* unique_ptr still has ownership over implementation object, we - don't have to delete it explicitly. */ - return true; - } - } - - /* Pointer is stored in registry, thous we release ownership. */ - imp.release(); - - return false; - } catch (...) { - } - return true; -} - -/** - Removes previously registered Service Implementation from registry. If it is - the default one for specified Service then any one still registered is made - default. If there is no other, the default entry is removed from the - Registry too. Assumes caller has a write lock on the Registry. - - @param service_implementation_name Name of the Service Implementation to - unregister. - @return Status of performed operation - @retval false success - @retval true Failure. May happen when Service is still being referenced. -*/ -bool mysql_registry_imp::unregister_nolock( - const char *service_implementation_name) { - try { - std::unique_ptr imp; - - { - /* Find the implementation and check if it is not being referenced. */ - my_service_registry::iterator imp_iter = - mysql_registry_imp::service_registry.find( - service_implementation_name); - if (imp_iter == mysql_registry_imp::service_registry.end() || - imp_iter->second->get_reference_count() > 0) { - return true; - } - - /* First remove specified implementation, to not include it in search - for new default one. Take ownership on implementation object. */ - imp.reset(imp_iter->second); - mysql_registry_imp::service_registry.erase(imp_iter); - /* After deletion, implementation iterator is not valid, we go out of - scope to prevent it from being reused. */ - } - - /* Remove interface mapping. */ - mysql_registry_imp::interface_mapping.erase( - mysql_registry_imp::interface_mapping.find(imp->interface())); - - /* Look if it is the default implementation. */ - my_service_registry::iterator default_iter = - mysql_registry_imp::service_registry.find(imp->service_name_c_str()); - if (default_iter == mysql_registry_imp::service_registry.end()) { - /* A Service Implementation and no default present. The state is not - consistent. */ - return true; - } - - if (default_iter->second == imp.get()) { - /* Remove the default implementation too. */ - my_service_registry::iterator new_default_iter = - mysql_registry_imp::service_registry.erase(default_iter); - - /* Search for a new default implementation. */ - if (new_default_iter != mysql_registry_imp::service_registry.end() && - !strcmp(imp->service_name_c_str(), - new_default_iter->second->service_name_c_str())) { - /* Set as default implementation. */ - mysql_service_implementation *new_default = new_default_iter->second; - mysql_registry_imp::service_registry.emplace_hint( - new_default_iter, new_default->service_name_c_str(), new_default); - } - } - - return false; - } catch (...) { - } - return true; -} - /** Finds and acquires a Service by name. A name of the Service or the Service Implementation can be specified. In case of the Service name, the default @@ -362,7 +119,7 @@ DEFINE_BOOL_METHOD(mysql_registry_imp::acquire, minimal_chassis::rwlock_scoped_lock lock(&LOCK_registry, false, __FILE__, __LINE__); - return mysql_registry_imp::acquire_nolock(service_name, out_service); + return mysql_registry_no_lock_imp::acquire(service_name, out_service); } /** @@ -381,37 +138,11 @@ DEFINE_BOOL_METHOD(mysql_registry_imp::acquire, DEFINE_BOOL_METHOD(mysql_registry_imp::acquire_related, (const char *service_name, my_h_service service, my_h_service *out_service)) { - try { - minimal_chassis::rwlock_scoped_lock lock(&mysql_registry_imp::LOCK_registry, - false, __FILE__, __LINE__); + minimal_chassis::rwlock_scoped_lock lock(&mysql_registry_imp::LOCK_registry, + false, __FILE__, __LINE__); - mysql_service_implementation *service_implementation = - mysql_registry_imp::get_service_implementation_by_interface(service); - if (service_implementation == nullptr) { - return true; - } - /* Find dot, the component name is right after the dot. */ - const char *component_part = - strchr(service_implementation->name_c_str(), '.'); - if (component_part == nullptr) { - return true; - } - /* Assure given service_name is not fully qualified. */ - if (strchr(service_name, '.') != nullptr) { - return true; - } - my_string service_implementation_name = - my_string(service_name) + component_part; - /* Try to acquire such Service. */ - if (mysql_registry_imp::acquire_nolock(service_implementation_name.c_str(), - out_service)) { - /* service is not found */ - return true; - } - return false; - } catch (...) { - } - return true; + return mysql_registry_no_lock_imp::acquire_related(service_name, service, + out_service); } /** @@ -428,7 +159,7 @@ DEFINE_BOOL_METHOD(mysql_registry_imp::release, (my_h_service service)) { minimal_chassis::rwlock_scoped_lock lock(&mysql_registry_imp::LOCK_registry, false, __FILE__, __LINE__); - return mysql_registry_imp::release_nolock(service); + return mysql_registry_no_lock_imp::release(service); } /** @@ -448,7 +179,7 @@ DEFINE_BOOL_METHOD(mysql_registry_imp::register_service, minimal_chassis::rwlock_scoped_lock lock(&mysql_registry_imp::LOCK_registry, true, __FILE__, __LINE__); - return mysql_registry_imp::register_service_nolock( + return mysql_registry_no_lock_imp::register_service( service_implementation_name, ptr); } @@ -469,7 +200,7 @@ DEFINE_BOOL_METHOD(mysql_registry_imp::unregister, minimal_chassis::rwlock_scoped_lock lock(&mysql_registry_imp::LOCK_registry, true, __FILE__, __LINE__); - return mysql_registry_imp::unregister_nolock(service_implementation_name); + return mysql_registry_no_lock_imp::unregister(service_implementation_name); } /** @@ -483,31 +214,10 @@ DEFINE_BOOL_METHOD(mysql_registry_imp::unregister, */ DEFINE_BOOL_METHOD(mysql_registry_imp::set_default, (const char *service_implementation_name)) { - try { - my_service_registry::const_iterator iter; - - minimal_chassis::rwlock_scoped_lock lock(&mysql_registry_imp::LOCK_registry, - true, __FILE__, __LINE__); - - /* register the implementation name */ - iter = - mysql_registry_imp::service_registry.find(service_implementation_name); - - if (iter == mysql_registry_imp::service_registry.cend()) { - return true; - } - mysql_service_implementation *imp = iter->second; - /* We have to remove and reinsert value as key, the string pointer will - not be valid if we unregister previous default implementation. */ - iter = mysql_registry_imp::service_registry.erase( - mysql_registry_imp::service_registry.find(imp->service_name_c_str())); - mysql_registry_imp::service_registry.emplace_hint( - iter, imp->service_name_c_str(), imp); + minimal_chassis::rwlock_scoped_lock lock(&mysql_registry_imp::LOCK_registry, + true, __FILE__, __LINE__); - return false; - } catch (...) { - } - return true; + return mysql_registry_no_lock_imp::set_default(service_implementation_name); } /** @@ -542,9 +252,10 @@ DEFINE_BOOL_METHOD(mysql_registry_imp::iterator_create, my_service_registry::const_iterator r = (!service_name_pattern || !*service_name_pattern) - ? mysql_registry_imp::service_registry.cbegin() - : mysql_registry_imp::service_registry.find(service_name_pattern); - if (r == mysql_registry_imp::service_registry.cend()) { + ? mysql_registry_no_lock_imp::service_registry.cbegin() + : mysql_registry_no_lock_imp::service_registry.find( + service_name_pattern); + if (r == mysql_registry_no_lock_imp::service_registry.cend()) { return true; } @@ -607,7 +318,7 @@ DEFINE_BOOL_METHOD(mysql_registry_imp::iterator_get, my_service_registry::const_iterator &iter = reinterpret_cast(iterator)->m_it; - if (iter != mysql_registry_imp::service_registry.cend()) { + if (iter != mysql_registry_no_lock_imp::service_registry.cend()) { *out_name = iter->second->name_c_str(); return false; @@ -635,9 +346,9 @@ DEFINE_BOOL_METHOD(mysql_registry_imp::iterator_next, my_service_registry::const_iterator &iter = reinterpret_cast(iterator)->m_it; - if (iter != mysql_registry_imp::service_registry.cend()) { + if (iter != mysql_registry_no_lock_imp::service_registry.cend()) { ++iter; - return iter == mysql_registry_imp::service_registry.cend(); + return iter == mysql_registry_no_lock_imp::service_registry.cend(); } } catch (...) { } @@ -661,7 +372,7 @@ DEFINE_BOOL_METHOD(mysql_registry_imp::iterator_is_valid, my_service_registry::const_iterator &iter = reinterpret_cast(iterator)->m_it; - return iter == mysql_registry_imp::service_registry.cend(); + return iter == mysql_registry_no_lock_imp::service_registry.cend(); } catch (...) { } return true; @@ -673,7 +384,7 @@ DEFINE_BOOL_METHOD(mysql_registry_imp::iterator_is_valid, types, but also static members with different name, so usage of templates is not enough to reuse that part of code. */ #define REGISTRY_IMP mysql_registry_imp -#define REGISTRY mysql_registry_imp::service_registry +#define REGISTRY mysql_registry_no_lock_imp::service_registry #define REGISTRY_TYPE my_service_registry #define LOCK mysql_registry_imp::LOCK_registry #define ITERATOR_TYPE my_h_service_iterator_imp @@ -684,6 +395,4 @@ DEFINE_BOOL_METHOD(mysql_registry_imp::iterator_is_valid, #include "registry_metadata.cc.inc" /* static members for mysql_registry_imp */ -my_service_registry mysql_registry_imp::service_registry; -mysql_registry_imp::my_interface_mapping mysql_registry_imp::interface_mapping; mysql_rwlock_t mysql_registry_imp::LOCK_registry; diff --git a/components/libminchassis/registry_imp.h b/components/libminchassis/registry_imp.h index d1ecdbac4f5d..28a9de4f03fa 100644 --- a/components/libminchassis/registry_imp.h +++ b/components/libminchassis/registry_imp.h @@ -23,26 +23,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MYSQL_SERVER_REGISTRY_H #define MYSQL_SERVER_REGISTRY_H -#include -#include -#include -#include -#include - -#include "c_string_less.h" -#include "mysql_service_implementation.h" +#include "registry_no_lock_imp.h" #include "rwlock_scoped_lock.h" -typedef std::map - my_service_registry; - -class mysql_registry_imp { - typedef std::map - my_interface_mapping; - - /* contain the actual fields definitions */ - static my_service_registry service_registry; - static my_interface_mapping interface_mapping; +class mysql_registry_imp : public mysql_registry_no_lock_imp { static mysql_rwlock_t LOCK_registry; public: @@ -51,6 +35,7 @@ class mysql_registry_imp { should be empty. Shouldn't be called multiple times. */ static void init(); + /** De-initializes registry. De-initializes RW lock, all other structures are cleaned up. @@ -67,77 +52,7 @@ class mysql_registry_imp { */ static minimal_chassis::rwlock_scoped_lock lock_registry_for_write(); - /** - Gets current reference count for a Service Implementation related to the - specified pointer to the interface structure. Assumes caller has at least - a read lock on the Registry. - - @param interface A pointer to the interface structure of the Service - Implementation to get reference count of. - @return A current reference count for specified Service Implementation. - Returns 0 in case there is no such interface or it is not referenced. - */ - static uint64_t get_service_implementation_reference_count( - my_h_service interface); - - /** - Finds and acquires a Service by name. A name of the Service or the Service - Implementation can be specified. In case of the Service name, the default - Service Implementation for Service specified will be returned. Assumes - caller has at least a read lock on the Registry. - - @param service_name Name of Service or Service Implementation to acquire. - @param [out] out_service Pointer to Service handle to set acquired Service. - @return Status of performed operation - @retval false success - @retval true failure - */ - static bool acquire_nolock(const char *service_name, - my_h_service *out_service); - - /** - Releases the Service Implementation previously acquired. After the call to - this method the usage of the Service Implementation handle will lead to - unpredicted results. Assumes caller has at least a read lock on the - Registry. - - @param service Service Implementation handle of already acquired Service. - @return Status of performed operation - @retval false success - @retval true failure - */ - static bool release_nolock(my_h_service service); - - /** - Registers a new Service Implementation. If it is the first Service - Implementation for the specified Service then it is made a default one. - Assumes caller has a write lock on the Registry. - - @param service_implementation_name Name of the Service Implementation to - register. - @param ptr Pointer to the Service Implementation structure. - @return Status of performed operation - @retval false success - @retval true failure - */ - static bool register_service_nolock(const char *service_implementation_name, - my_h_service ptr); - - /** - Removes previously registered Service Implementation from registry. If it is - the default one for specified Service then any one still registered is made - default. If there is no other, the default entry is removed from the - Registry too. Assumes caller has a write lock on the Registry. - - @param service_implementation_name Name of the Service Implementation to - unregister. - @return Status of performed operation - @retval false success - @retval true Failure. May happen when Service is still being referenced. - */ - static bool unregister_nolock(const char *service_implementation_name); - - public: /* Service Implementations */ + /* Service Implementations */ /** Finds and acquires a Service by name. A name of the Service or the Service Implementation can be specified. In case of the Service name, the default @@ -293,28 +208,13 @@ class mysql_registry_imp { (my_h_service_iterator iterator)); /* This includes metadata-related method implementations that are shared - by registry and dynamic_loader, so we don't duplicate the code. Following - defines set up all required symbols. Unfortunately they are not only the - types, but also static members with different name, so usage of templates - is not enough to reuse that part of code. */ + by registry and dynamic_loader, so we don't duplicate the code. Following + defines set up all required symbols. Unfortunately they are not only the + types, but also static members with different name, so usage of templates + is not enough to reuse that part of code. */ #define OBJECT_ITERATOR my_h_service_iterator #define METADATA_ITERATOR my_h_service_metadata_iterator #include "registry_metadata.h.inc" - - private: - /** - Finds a Service Implementation data structure based on the pointer to - interface struct supplied. Assumes caller has at least a read lock on the - Registry. - - @param interface A pointer to the interface structure of the Service - Implementation to look for. - @return A pointer to respective Service Implementation data structure, or - NULL if no such interface pointer is registered within the Registry. - */ - static mysql_service_implementation *get_service_implementation_by_interface( - my_h_service interface); }; - #endif /* MYSQL_SERVER_REGISTRY_H */ diff --git a/components/libminchassis/registry_no_lock.cc b/components/libminchassis/registry_no_lock.cc new file mode 100644 index 000000000000..131e16218221 --- /dev/null +++ b/components/libminchassis/registry_no_lock.cc @@ -0,0 +1,460 @@ +/* Copyright (c) 2023, 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 also distributed 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 included with MySQL. + +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 "mysql_service_implementation.h" +#include "registry_no_lock_imp.h" + +#include +#include +#include +#include +#include + +typedef std::map + my_service_registry; + +/** + De-initializes registry, other structures. +*/ +void mysql_registry_no_lock_imp::deinit() { + mysql_registry_no_lock_imp::service_registry.clear(); + mysql_registry_no_lock_imp::interface_mapping.clear(); +} + +/** + Finds a Service Implementation data structure based on the pointer to + interface struct supplied. Assumes caller has at least a read lock on the + Registry. + + @param interface A pointer to the interface structure of the Service + Implementation to look for. + @return A pointer to respective Service Implementation data structure, or + NULL if no such interface pointer is registered within the Registry. +*/ +mysql_service_implementation * +mysql_registry_no_lock_imp::get_service_implementation_by_interface( + my_h_service interface) { + my_interface_mapping::const_iterator iter = + mysql_registry_no_lock_imp::interface_mapping.find(interface); + if (iter == mysql_registry_no_lock_imp::interface_mapping.cend()) { + return nullptr; + } + + return iter->second; +} + +/** + Gets current reference count for a Service Implementation related to the + specified pointer to the interface structure. Assumes caller has at least + a read lock on the Registry. + + @param interface A pointer to the interface structure of the Service + Implementation to get reference count of. + @return A current reference count for specified Service Implementation. + Returns 0 in case there is no such interface or it is not referenced. +*/ +uint64_t mysql_registry_no_lock_imp::get_service_implementation_reference_count( + my_h_service interface) { + my_interface_mapping::const_iterator iter = + mysql_registry_no_lock_imp::interface_mapping.find(interface); + if (iter == mysql_registry_no_lock_imp::interface_mapping.cend()) { + return -1; + } + + return iter->second->get_reference_count(); +} + +/** + Finds and acquires a Service by name. A name of the Service or the Service + Implementation can be specified. In case of the Service name, the default + Service Implementation for Service specified will be returned. Assumes + caller has at least a read lock on the Registry. + + @param service_name Name of Service or Service Implementation to acquire. + @param [out] out_service Pointer to Service handle to set acquired Service. + @return Status of performed operation + @retval false success + @retval true failure +*/ +bool mysql_registry_no_lock_imp::acquire_nolock(const char *service_name, + my_h_service *out_service) { + try { + if (out_service == nullptr) { + return true; + } + my_service_registry::const_iterator iter; + + iter = mysql_registry_no_lock_imp::service_registry.find(service_name); + + if (iter == mysql_registry_no_lock_imp::service_registry.cend()) + return true; + + mysql_service_implementation *imp = iter->second; + imp->add_reference(); + *out_service = imp->interface(); + return false; + } catch (...) { + } + return true; +} + +/** + Releases the Service Implementation previously acquired. After the call to + this method the usage of the Service Implementation handle will lead to + unpredicted results. Assumes caller has at least a read lock on the + Registry. + + @param service Service Implementation handle of already acquired Service. + @return Status of performed operation + @retval false success + @retval true failure +*/ +bool mysql_registry_no_lock_imp::release_nolock(my_h_service service) { + try { + if (service == nullptr) { + return true; + } + + mysql_service_implementation *service_implementation = + mysql_registry_no_lock_imp::get_service_implementation_by_interface( + service); + if (service_implementation == nullptr) { + return true; + } + return service_implementation->release_reference(); + } catch (...) { + } + return true; +} + +/** + Registers a new Service Implementation. If it is the first Service + Implementation for the specified Service then it is made a default one. + Assumes caller has a write lock on the Registry. + + @param service_implementation_name Name of the Service Implementation to + register. + @param ptr Pointer to the Service Implementation structure. + @return Status of performed operation + @retval false success + @retval true failure +*/ +bool mysql_registry_no_lock_imp::register_service_nolock( + const char *service_implementation_name, my_h_service ptr) { + try { + std::unique_ptr imp = + std::unique_ptr( + new mysql_service_implementation(ptr, service_implementation_name)); + + if (imp->interface() == nullptr) { + return true; + } + + /* Register the implementation name. */ + std::pair addition_result = + mysql_registry_no_lock_imp::service_registry.emplace(imp->name_c_str(), + imp.get()); + + /* Fail if it was present already. */ + if (!addition_result.second) { + return true; + } else { + try { + /* Register interface in mapping */ + mysql_registry_no_lock_imp::interface_mapping.emplace(imp->interface(), + imp.get()); + + /* Register the Service Implementation as default for Service name in + case none were registered before. */ + mysql_registry_no_lock_imp::service_registry.emplace_hint( + addition_result.first, imp->service_name_c_str(), imp.get()); + } catch (...) { + mysql_registry_no_lock_imp::service_registry.erase( + addition_result.first); + /* unique_ptr still has ownership over implementation object, we + don't have to delete it explicitly. */ + return true; + } + } + + /* Pointer is stored in registry, thous we release ownership. */ + imp.release(); + + return false; + } catch (...) { + } + return true; +} + +/** + Removes previously registered Service Implementation from registry. If it is + the default one for specified Service then any one still registered is made + default. If there is no other, the default entry is removed from the + Registry too. Assumes caller has a write lock on the Registry. + + @param service_implementation_name Name of the Service Implementation to + unregister. + @return Status of performed operation + @retval false success + @retval true Failure. May happen when Service is still being referenced. +*/ +bool mysql_registry_no_lock_imp::unregister_nolock( + const char *service_implementation_name) { + try { + std::unique_ptr imp; + + { + /* Find the implementation and check if it is not being referenced. */ + my_service_registry::iterator imp_iter = + mysql_registry_no_lock_imp::service_registry.find( + service_implementation_name); + if (imp_iter == mysql_registry_no_lock_imp::service_registry.end() || + imp_iter->second->get_reference_count() > 0) { + return true; + } + + /* First remove specified implementation, to not include it in search + for new default one. Take ownership on implementation object. */ + imp.reset(imp_iter->second); + mysql_registry_no_lock_imp::service_registry.erase(imp_iter); + /* After deletion, implementation iterator is not valid, we go out of + scope to prevent it from being reused. */ + } + + /* Remove interface mapping. */ + mysql_registry_no_lock_imp::interface_mapping.erase( + mysql_registry_no_lock_imp::interface_mapping.find(imp->interface())); + + /* Look if it is the default implementation. */ + my_service_registry::iterator default_iter = + mysql_registry_no_lock_imp::service_registry.find( + imp->service_name_c_str()); + if (default_iter == mysql_registry_no_lock_imp::service_registry.end()) { + /* A Service Implementation and no default present. The state is not + consistent. */ + return true; + } + + if (default_iter->second == imp.get()) { + /* Remove the default implementation too. */ + my_service_registry::iterator new_default_iter = + mysql_registry_no_lock_imp::service_registry.erase(default_iter); + + /* Search for a new default implementation. */ + if (new_default_iter != + mysql_registry_no_lock_imp::service_registry.end() && + !strcmp(imp->service_name_c_str(), + new_default_iter->second->service_name_c_str())) { + /* Set as default implementation. */ + mysql_service_implementation *new_default = new_default_iter->second; + mysql_registry_no_lock_imp::service_registry.emplace_hint( + new_default_iter, new_default->service_name_c_str(), new_default); + } + } + + return false; + } catch (...) { + } + return true; +} + +/** + Finds and acquires a Service by name. A name of the Service or the Service + Implementation can be specified. In case of the Service name, the default + Service Implementation for Service specified will be returned. + + This does not take any lock on the registry. It must not be used unless + absolutely necessary. Use the mysql_registry_imp version instead. + + @param service_name Name of Service or Service Implementation to acquire. + @param [out] out_service Pointer to Service handle to set acquired Service. + @return Status of performed operation + @retval false success + @retval true failure +*/ +DEFINE_BOOL_METHOD(mysql_registry_no_lock_imp::acquire, + (const char *service_name, my_h_service *out_service)) { + return mysql_registry_no_lock_imp::acquire_nolock(service_name, out_service); +} + +/** + Finds a Service by name. If there is a Service Implementation with the same + Component part of name as the input Service then the found Service is + returned. + + This does not take any lock on the registry. It must not be used unless + absolutely necessary. Use the mysql_registry_imp version instead. + + @param service_name Name of Service or Service Implementation to acquire. + @param service Service handle already acquired Service Implementation. + @param [out] out_service Pointer to Service Implementation handle to set + acquired Service Implementation. + @return Status of performed operation + @retval false success + @retval true failure +*/ +DEFINE_BOOL_METHOD(mysql_registry_no_lock_imp::acquire_related, + (const char *service_name, my_h_service service, + my_h_service *out_service)) { + try { + mysql_service_implementation *service_implementation = + mysql_registry_no_lock_imp::get_service_implementation_by_interface( + service); + if (service_implementation == nullptr) { + return true; + } + /* Find dot, the component name is right after the dot. */ + const char *component_part = + strchr(service_implementation->name_c_str(), '.'); + if (component_part == nullptr) { + return true; + } + /* Assure given service_name is not fully qualified. */ + if (strchr(service_name, '.') != nullptr) { + return true; + } + my_string service_implementation_name = + my_string(service_name) + component_part; + /* Try to acquire such Service. */ + if (mysql_registry_no_lock_imp::acquire_nolock( + service_implementation_name.c_str(), out_service)) { + /* service is not found */ + return true; + } + return false; + } catch (...) { + } + return true; +} + +/** + Releases the Service Implementation previously acquired. After the call to + this method the usage of the Service Implementation handle will lead to + unpredicted results. + + This does not take any lock on the registry. It must not be used unless + absolutely necessary. Use the mysql_registry_imp version instead. + + @param service Service Implementation handle of already acquired Service. + @return Status of performed operation + @retval false success + @retval true failure +*/ +DEFINE_BOOL_METHOD(mysql_registry_no_lock_imp::release, + (my_h_service service)) { + return mysql_registry_no_lock_imp::release_nolock(service); +} + +/** + Registers a new Service Implementation. If it is the first Service + Implementation for the specified Service then it is made a default one. + + This does not take any lock on the registry. It must not be used unless + absolutely necessary. Use the mysql_registry_imp version instead. + + @param service_implementation_name Name of the Service Implementation to + register. + @param ptr Pointer to the Service Implementation structure. + @return Status of performed operation + @retval false success + @retval true failure +*/ +DEFINE_BOOL_METHOD(mysql_registry_no_lock_imp::register_service, + (const char *service_implementation_name, + my_h_service ptr)) { + return mysql_registry_no_lock_imp::register_service_nolock( + service_implementation_name, ptr); +} + +/** + Removes previously registered Service Implementation from registry. If it is + the default one for specified Service then any one still registered is made + default. If there is no other, the default entry is removed from the + Registry too. + + This does not take any lock on the registry. It must not be used unless + absolutely necessary. Use the mysql_registry_imp version instead. + + @param service_implementation_name Name of the Service Implementation to + unregister. + @return Status of performed operation + @retval false success + @retval true Failure. May happen when Service is still being referenced. +*/ +DEFINE_BOOL_METHOD(mysql_registry_no_lock_imp::unregister, + (const char *service_implementation_name)) { + return mysql_registry_no_lock_imp::unregister_nolock( + service_implementation_name); +} + +/** + Sets new default Service Implementation for corresponding Service name. + + This does not take any lock on the registry. It must not be used unless + absolutely necessary. Use the mysql_registry_imp version instead. + + @param service_implementation_name Name of the Service Implementation to + set as default one. + @return Status of performed operation + @retval false success + @retval true failure +*/ +DEFINE_BOOL_METHOD(mysql_registry_no_lock_imp::set_default, + (const char *service_implementation_name)) { + try { + my_service_registry::const_iterator iter; + + /* register the implementation name */ + iter = mysql_registry_no_lock_imp::service_registry.find( + service_implementation_name); + + if (iter == mysql_registry_no_lock_imp::service_registry.cend()) { + return true; + } + mysql_service_implementation *imp = iter->second; + /* We have to remove and reinsert value as key, the string pointer will + not be valid if we unregister previous default implementation. */ + iter = mysql_registry_no_lock_imp::service_registry.erase( + mysql_registry_no_lock_imp::service_registry.find( + imp->service_name_c_str())); + mysql_registry_no_lock_imp::service_registry.emplace_hint( + iter, imp->service_name_c_str(), imp); + + return false; + } catch (...) { + } + return true; +} + +/* This includes metadata-related method implementations that are shared + by registry and dynamic_loader, so we don't duplicate the code. Following + defines set up all required symbols. Unfortunately they are not only the + types, but also static members with different name, so usage of templates + is not enough to reuse that part of code. */ +#define REGISTRY_IMP mysql_registry_no_lock_imp +#define REGISTRY mysql_registry_no_lock_imp::service_registry +#define REGISTRY_TYPE my_service_registry + +/* static members for mysql_registry_no_lock_imp */ +my_service_registry mysql_registry_no_lock_imp::service_registry; +mysql_registry_no_lock_imp::my_interface_mapping + mysql_registry_no_lock_imp::interface_mapping; diff --git a/components/libminchassis/registry_no_lock_imp.h b/components/libminchassis/registry_no_lock_imp.h new file mode 100644 index 000000000000..d8ae1225e5db --- /dev/null +++ b/components/libminchassis/registry_no_lock_imp.h @@ -0,0 +1,241 @@ +/* Copyright (c) 2023, 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 also distributed 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 included with MySQL. + +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 */ + +#ifndef MYSQL_SERVER_REGISTRY_NO_LOCK_H +#define MYSQL_SERVER_REGISTRY_NO_LOCK_H + +#include +#include +#include +#include +#include + +#include "c_string_less.h" +#include "mysql_service_implementation.h" + +typedef std::map + my_service_registry; + +class mysql_registry_no_lock_imp { + protected: + typedef std::map + my_interface_mapping; + + /* contain the actual fields definitions */ + static my_service_registry service_registry; + static my_interface_mapping interface_mapping; + + public: + /** + De-initializes registry, other structures. + */ + static void deinit(); + + /** + Gets current reference count for a Service Implementation related to the + specified pointer to the interface structure. Assumes caller has at least + a read lock on the Registry. + + @param interface A pointer to the interface structure of the Service + Implementation to get reference count of. + @return A current reference count for specified Service Implementation. + Returns 0 in case there is no such interface or it is not referenced. + */ + static uint64_t get_service_implementation_reference_count( + my_h_service interface); + + /** + Finds and acquires a Service by name. A name of the Service or the Service + Implementation can be specified. In case of the Service name, the default + Service Implementation for Service specified will be returned. Assumes + caller has at least a read lock on the Registry. + + @param service_name Name of Service or Service Implementation to acquire. + @param [out] out_service Pointer to Service handle to set acquired Service. + @return Status of performed operation + @retval false success + @retval true failure + */ + static bool acquire_nolock(const char *service_name, + my_h_service *out_service); + + /** + Releases the Service Implementation previously acquired. After the call to + this method the usage of the Service Implementation handle will lead to + unpredicted results. Assumes caller has at least a read lock on the + Registry. + + @param service Service Implementation handle of already acquired Service. + @return Status of performed operation + @retval false success + @retval true failure + */ + static bool release_nolock(my_h_service service); + + /** + Registers a new Service Implementation. If it is the first Service + Implementation for the specified Service then it is made a default one. + Assumes caller has a write lock on the Registry. + + @param service_implementation_name Name of the Service Implementation to + register. + @param ptr Pointer to the Service Implementation structure. + @return Status of performed operation + @retval false success + @retval true failure + */ + static bool register_service_nolock(const char *service_implementation_name, + my_h_service ptr); + + /** + Removes previously registered Service Implementation from registry. If it is + the default one for specified Service then any one still registered is made + default. If there is no other, the default entry is removed from the + Registry too. Assumes caller has a write lock on the Registry. + + @param service_implementation_name Name of the Service Implementation to + unregister. + @return Status of performed operation + @retval false success + @retval true Failure. May happen when Service is still being referenced. + */ + static bool unregister_nolock(const char *service_implementation_name); + + /* Service Implementations */ + /** + Finds and acquires a Service by name. A name of the Service or the Service + Implementation can be specified. In case of the Service name, the default + Service Implementation for Service specified will be returned. + + This does not take any lock on the registry. It must not be used unless + absolutely necessary. Use the mysql_registry_imp version instead. + + @param service_name Name of Service or Service Implementation to acquire. + @param [out] out_service Pointer to Service handle to set acquired Service. + @return Status of performed operation + @retval false success + @retval true failure + */ + static DEFINE_BOOL_METHOD(acquire, (const char *service_name, + my_h_service *out_service)); + + /** + Finds a Service by name. If there is a Service Implementation with the same + Component part of name as the input Service then the found Service is + returned. + + This does not take any lock on the registry. It must not be used unless + absolutely necessary. Use the mysql_registry_imp version instead. + + @param service_name Name of Service or Service Implementation to acquire. + @param service Service handle already acquired Service Implementation. + @param [out] out_service Pointer to Service Implementation handle to set + acquired Service Implementation. + @return Status of performed operation + @retval false success + @retval true failure + */ + static DEFINE_BOOL_METHOD(acquire_related, + (const char *service_name, my_h_service service, + my_h_service *out_service)); + + /** + Releases the Service Implementation previously acquired. After the call to + this method the usage of the Service Implementation handle will lead to + unpredicted results. + + This does not take any lock on the registry. It must not be used unless + absolutely necessary. Use the mysql_registry_imp version instead. + + @param service Service Implementation handle of already acquired Service. + @return Status of performed operation + @retval false success + @retval true failure + */ + static DEFINE_BOOL_METHOD(release, (my_h_service service)); + + /** + Registers a new Service Implementation. If it is the first Service + Implementation for the specified Service then it is made a default one. + + This does not take any lock on the registry. It must not be used unless + absolutely necessary. Use the mysql_registry_imp version instead. + + @param service_implementation_name Name of the Service Implementation to + register. + @param ptr Pointer to the Service Implementation structure. + @return Status of performed operation + @retval false success + @retval true failure + */ + static DEFINE_BOOL_METHOD(register_service, + (const char *service_implementation_name, + my_h_service ptr)); + + /** + Removes previously registered Service Implementation from registry. If it is + the default one for specified Service then any one still registered is made + default. If there is no other, the default entry is removed from the + Registry too. + + This does not take any lock on the registry. It must not be used unless + absolutely necessary. Use the mysql_registry_imp version instead. + + @param service_implementation_name Name of the Service Implementation to + unregister. + @return Status of performed operation + @retval false success + @retval true Failure. May happen when Service is still being referenced. + */ + static DEFINE_BOOL_METHOD(unregister, + (const char *service_implementation_name)); + + /** + Sets new default Service Implementation for corresponding Service name. + + This does not take any lock on the registry. It must not be used unless + absolutely necessary. Use the mysql_registry_imp version instead. + + @param service_implementation_name Name of the Service Implementation to + set as default one. + @return Status of performed operation + @retval false success + @retval true failure + */ + static DEFINE_BOOL_METHOD(set_default, + (const char *service_implementation_name)); + + private: + /** + Finds a Service Implementation data structure based on the pointer to + interface struct supplied. Assumes caller has at least a read lock on the + Registry. + + @param interface A pointer to the interface structure of the Service + Implementation to look for. + @return A pointer to respective Service Implementation data structure, or + NULL if no such interface pointer is registered within the Registry. + */ + static mysql_service_implementation *get_service_implementation_by_interface( + my_h_service interface); +}; +#endif /* MYSQL_SERVER_REGISTRY_NO_LOCK_H */ diff --git a/include/mysql/components/services/mysql_command_services.h b/include/mysql/components/services/mysql_command_services.h index b426b81df7db..bce0864b6957 100644 --- a/include/mysql/components/services/mysql_command_services.h +++ b/include/mysql/components/services/mysql_command_services.h @@ -60,7 +60,8 @@ enum mysql_command_option { MYSQL_COMMAND_PROTOCOL, MYSQL_COMMAND_USER_NAME, MYSQL_COMMAND_HOST_NAME, - MYSQL_COMMAND_TCPIP_PORT + MYSQL_COMMAND_TCPIP_PORT, + MYSQL_NO_LOCK_REGISTRY }; /** diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 1952a0d45671..0abc763cf9aa 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2011,6 +2011,7 @@ SERVICE_TYPE(mysql_runtime_error) * error_service; SERVICE_TYPE(mysql_psi_system_v1) * system_service; SERVICE_TYPE(mysql_rwlock_v1) * rwlock_service; SERVICE_TYPE_NO_CONST(registry) * srv_registry; +SERVICE_TYPE_NO_CONST(registry) * srv_registry_no_lock; SERVICE_TYPE(dynamic_loader_scheme_file) * scheme_file_srv; using loader_type_t = SERVICE_TYPE_NO_CONST(dynamic_loader); using runtime_error_type_t = SERVICE_TYPE_NO_CONST(mysql_runtime_error); @@ -2044,6 +2045,10 @@ static bool component_infrastructure_init() { LogErr(ERROR_LEVEL, ER_COMPONENTS_INFRASTRUCTURE_BOOTSTRAP); return true; } + srv_registry->acquire( + "registry.mysql_minimal_chassis_no_lock", + reinterpret_cast(&srv_registry_no_lock)); + /* Here minimal_chassis dynamic_loader_scheme_file service has to be acquired */ srv_registry->acquire( @@ -2158,6 +2163,7 @@ static bool component_infrastructure_deinit() { srv_registry->release(reinterpret_cast( const_cast(scheme_file_srv))); + srv_registry->release(reinterpret_cast(srv_registry_no_lock)); srv_registry->release(reinterpret_cast( const_cast(dynamic_loader_srv))); srv_registry->release(reinterpret_cast( diff --git a/sql/mysqld.h b/sql/mysqld.h index fb2de49c0e44..517ccb138898 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -829,6 +829,7 @@ extern mysql_component_t mysql_component_performance_schema; /* This variable is a registry handler, defined in mysql_server component and used as a output parameter for minimal chassis. */ extern SERVICE_TYPE_NO_CONST(registry) * srv_registry; +extern SERVICE_TYPE_NO_CONST(registry) * srv_registry_no_lock; /* These global variables which are defined and used in mysql_server component */ extern SERVICE_TYPE(dynamic_loader_scheme_file) * scheme_file_srv; diff --git a/sql/server_component/mysql_command_backend.cc b/sql/server_component/mysql_command_backend.cc index fb16ee57872d..8ac87307c888 100644 --- a/sql/server_component/mysql_command_backend.cc +++ b/sql/server_component/mysql_command_backend.cc @@ -36,6 +36,7 @@ #include "sql/srv_session.h" extern SERVICE_TYPE_NO_CONST(registry) * srv_registry; +extern SERVICE_TYPE_NO_CONST(registry) * srv_registry_no_lock; namespace cs { @@ -62,60 +63,11 @@ MYSQL_METHODS mysql_methods = { nullptr, /* read_change_user_result_nonblocking */ }; -mysql_state_machine_status cssm_begin_connect(mysql_async_connect *ctx) { - MYSQL *mysql = ctx->mysql; - auto mcs_extn = MYSQL_COMMAND_SERVICE_EXTN(mysql); - assert(mcs_extn); - const char *host = ctx->host; - const char *user = ctx->user; - const char *db = ctx->db; - MYSQL_THD thd; +static mysql_state_machine_status acquire_services( + mysql_command_consumer_refs *consumer_refs, + mysql_service_registry_t *srv_registry) { my_h_service h_command_consumer = nullptr; my_h_service h_command_consumer_srv = nullptr; - - MYSQL_SESSION mysql_session = nullptr; - if (mcs_extn->mcs_thd == nullptr || mcs_extn->session_svc == nullptr) { - /* - Avoid possibility of nested txn in the current thd. - If it is called, for example from a UDF. - */ - my_service service( - "mysql_admin_session.mysql_server", srv_registry); - if (service.is_valid()) { - mysql_session = service->open(nullptr, ctx); - if (mysql_session == nullptr) return STATE_MACHINE_FAILED; - } else - return STATE_MACHINE_FAILED; - thd = mysql_session->get_thd(); - mcs_extn->is_thd_associated = false; - Security_context_handle sc; - if (mysql_security_context_imp::get(thd, &sc)) return STATE_MACHINE_FAILED; - if (mysql_security_context_imp::lookup(sc, user, host, nullptr, db)) - return STATE_MACHINE_FAILED; - mcs_extn->mcs_thd = thd; - mysql->thd = thd; - mcs_extn->session_svc = mysql_session; - } else { - mysql->thd = reinterpret_cast(mcs_extn->mcs_thd); - } - /* - These references might be created in mysql_command_services_imp::set api. - If not, we will create here. - */ - if (mcs_extn->command_consumer_services == nullptr) { - /* - Provide default implementations for mysql command consumer services - and will be released in close() api. - */ - mcs_extn->command_consumer_services = new mysql_command_consumer_refs(); - } - mysql_command_consumer_refs *consumer_refs = - (mysql_command_consumer_refs *)mcs_extn->command_consumer_services; - /* The above new allocation failed */ - if (consumer_refs == nullptr) return STATE_MACHINE_FAILED; - /* If the service is not acquired by mysql_command_services_imp::set api, - then it will be acquired below. The same will be applicable for all - other below services. */ if (consumer_refs->factory_srv == nullptr) { if (srv_registry->acquire("mysql_text_consumer_factory_v1.mysql_server", &h_command_consumer)) @@ -236,6 +188,68 @@ mysql_state_machine_status cssm_begin_connect(mysql_async_connect *ctx) { mysql_text_consumer_client_capabilities_v1) *>( h_command_consumer_srv); } + return STATE_MACHINE_DONE; +} + +mysql_state_machine_status cssm_begin_connect(mysql_async_connect *ctx) { + MYSQL *mysql = ctx->mysql; + Mysql_handle mysql_handle; + mysql_handle.mysql = mysql; + auto mcs_extn = MYSQL_COMMAND_SERVICE_EXTN(mysql); + assert(mcs_extn); + const char *host = ctx->host; + const char *user = ctx->user; + const char *db = ctx->db; + MYSQL_THD thd; + bool no_lock_registry = false; + MYSQL_SESSION mysql_session = nullptr; + + if (mysql_command_services_imp::get( + (MYSQL_H)&mysql_handle, MYSQL_NO_LOCK_REGISTRY, &no_lock_registry)) + return STATE_MACHINE_FAILED; + mysql_service_registry_t *registry_service = + no_lock_registry ? srv_registry_no_lock : srv_registry; + + if (mcs_extn->mcs_thd == nullptr || mcs_extn->session_svc == nullptr) { + /* + Avoid possibility of nested txn in the current thd. + If it is called, for example from a UDF. + */ + my_service service( + "mysql_admin_session.mysql_server", registry_service); + if (service.is_valid()) mysql_session = service->open(nullptr, ctx); + if (mysql_session == nullptr) return STATE_MACHINE_FAILED; + thd = mysql_session->get_thd(); + mcs_extn->is_thd_associated = false; + Security_context_handle sc; + if (mysql_security_context_imp::get(thd, &sc)) return STATE_MACHINE_FAILED; + if (mysql_security_context_imp::lookup(sc, user, host, nullptr, db)) + return STATE_MACHINE_FAILED; + mcs_extn->mcs_thd = thd; + mysql->thd = thd; + mcs_extn->session_svc = mysql_session; + } else { + mysql->thd = reinterpret_cast(mcs_extn->mcs_thd); + } + /* + These references might be created in mysql_command_services_imp::set api. + If not, we will create here. + */ + if (mcs_extn->command_consumer_services == nullptr) { + /* + Provide default implementations for mysql command consumer services + and will be released in close() api. + */ + mcs_extn->command_consumer_services = new mysql_command_consumer_refs(); + } + mysql_command_consumer_refs *consumer_refs = + (mysql_command_consumer_refs *)mcs_extn->command_consumer_services; + /* The above new allocation failed */ + if (consumer_refs == nullptr) return STATE_MACHINE_FAILED; + /* If the services are not acquired by mysql_command_services_imp::set api, + then it will be acquired. */ + auto status = acquire_services(consumer_refs, registry_service); + if (status == STATE_MACHINE_FAILED) return status; mysql->client_flag = 0; /* For handshake */ mysql->server_status = SERVER_STATUS_AUTOCOMMIT; return STATE_MACHINE_DONE; diff --git a/sql/server_component/mysql_command_services_imp.cc b/sql/server_component/mysql_command_services_imp.cc index 22367f8979cf..b3fca5e6292b 100644 --- a/sql/server_component/mysql_command_services_imp.cc +++ b/sql/server_component/mysql_command_services_imp.cc @@ -159,6 +159,81 @@ DEFINE_BOOL_METHOD(mysql_command_services_imp::reset, (MYSQL_H mysql_h)) { } } +/** + Release services. + + @param[in] consumer_refs A valid mysql_command_consumer_refs object. + @param[in] mcs_ext A valid mysql_command_service_extn object. + @param[in] srv_registry Registry service pointer. +*/ +static void release_services(mysql_command_consumer_refs *consumer_refs, + mysql_command_service_extn *mcs_ext, + mysql_service_registry_t *srv_registry) { + if (consumer_refs) { + if (consumer_refs->factory_srv) { + /* This service call is used to free the memory, the allocation + was happened through factory_srv->start() service api. */ + consumer_refs->factory_srv->end( + reinterpret_cast(mcs_ext->consumer_srv_data)); + srv_registry->release(reinterpret_cast( + const_cast( + consumer_refs->factory_srv))); + } + if (consumer_refs->metadata_srv) + srv_registry->release(reinterpret_cast( + const_cast( + consumer_refs->metadata_srv))); + if (consumer_refs->row_factory_srv) + srv_registry->release(reinterpret_cast( + const_cast( + consumer_refs->row_factory_srv))); + if (consumer_refs->error_srv) + srv_registry->release(reinterpret_cast( + const_cast( + consumer_refs->error_srv))); + if (consumer_refs->get_null_srv) + srv_registry->release(reinterpret_cast( + const_cast( + consumer_refs->get_null_srv))); + if (consumer_refs->get_integer_srv) + srv_registry->release(reinterpret_cast( + const_cast( + consumer_refs->get_integer_srv))); + if (consumer_refs->get_longlong_srv) + srv_registry->release(reinterpret_cast( + const_cast( + consumer_refs->get_longlong_srv))); + if (consumer_refs->get_decimal_srv) + srv_registry->release(reinterpret_cast( + const_cast( + consumer_refs->get_decimal_srv))); + if (consumer_refs->get_double_srv) + srv_registry->release(reinterpret_cast( + const_cast( + consumer_refs->get_double_srv))); + if (consumer_refs->get_date_time_srv) + srv_registry->release(reinterpret_cast( + const_cast( + consumer_refs->get_date_time_srv))); + if (consumer_refs->get_string_srv) + srv_registry->release(reinterpret_cast( + const_cast( + consumer_refs->get_string_srv))); + if (consumer_refs->client_capabilities_srv) + srv_registry->release(reinterpret_cast( + const_cast( + consumer_refs->client_capabilities_srv))); + } +} + /** Calls mysql_close api to closes a server connection. @@ -175,76 +250,14 @@ DEFINE_BOOL_METHOD(mysql_command_services_imp::close, (MYSQL_H mysql_h)) { auto mcs_ext = MYSQL_COMMAND_SERVICE_EXTN(mysql); mysql_command_consumer_refs *consumer_refs = (mysql_command_consumer_refs *)(mcs_ext->command_consumer_services); + bool no_lock_registry = false; + if (get(mysql_h, MYSQL_NO_LOCK_REGISTRY, &no_lock_registry)) return true; + no_lock_registry + ? release_services(consumer_refs, mcs_ext, srv_registry_no_lock) + : release_services(consumer_refs, mcs_ext, srv_registry); + delete consumer_refs; + consumer_refs = nullptr; - if (consumer_refs) { - if (consumer_refs->factory_srv) { - /* This service call is used to free the memory, the allocation - was happened through factory_srv->start() service api. */ - consumer_refs->factory_srv->end( - reinterpret_cast(mcs_ext->consumer_srv_data)); - srv_registry->release(reinterpret_cast( - const_cast( - consumer_refs->factory_srv))); - } - if (consumer_refs->metadata_srv) - srv_registry->release(reinterpret_cast( - const_cast( - consumer_refs->metadata_srv))); - if (consumer_refs->row_factory_srv) - srv_registry->release(reinterpret_cast( - const_cast( - consumer_refs->row_factory_srv))); - if (consumer_refs->error_srv) - srv_registry->release(reinterpret_cast( - const_cast( - consumer_refs->error_srv))); - if (consumer_refs->get_null_srv) - srv_registry->release(reinterpret_cast( - const_cast( - consumer_refs->get_null_srv))); - if (consumer_refs->get_integer_srv) - srv_registry->release(reinterpret_cast( - const_cast( - consumer_refs->get_integer_srv))); - if (consumer_refs->get_longlong_srv) - srv_registry->release(reinterpret_cast( - const_cast( - consumer_refs->get_longlong_srv))); - if (consumer_refs->get_decimal_srv) - srv_registry->release(reinterpret_cast( - const_cast( - consumer_refs->get_decimal_srv))); - if (consumer_refs->get_double_srv) - srv_registry->release(reinterpret_cast( - const_cast( - consumer_refs->get_double_srv))); - if (consumer_refs->get_date_time_srv) - srv_registry->release(reinterpret_cast( - const_cast( - consumer_refs->get_date_time_srv))); - if (consumer_refs->get_string_srv) - srv_registry->release(reinterpret_cast( - const_cast( - consumer_refs->get_string_srv))); - if (consumer_refs->client_capabilities_srv) - srv_registry->release(reinterpret_cast( - const_cast( - consumer_refs->client_capabilities_srv))); - - delete consumer_refs; - consumer_refs = nullptr; - } if (mcs_ext->is_thd_associated) delete (mcs_ext->session_svc); else { @@ -365,6 +378,10 @@ const char * |MYSQL_COMMAND_HOST_NAME |The host name to use to | | |security context. | --------------+-------------------------------+--------------------------------+ int |MYSQL_COMMAND_TCPIP_PORT |The port to use to connect. | +--------------+-------------------------------+--------------------------------+ +bool |MYSQL_NO_LOCK_REGISTRY |Flag to use the _no_lock | + | |implementation of registry | + | |service. | --------------+-------------------------------+--------------------------------+ @note For the other mysql client options it calls the mysql_options api from @@ -692,6 +709,9 @@ DEFINE_BOOL_METHOD(mysql_command_services_imp::set, mcs_ext->mcs_tcpip_port = *static_cast(arg); } break; + case MYSQL_NO_LOCK_REGISTRY: + mcs_ext->no_lock_registry = static_cast(arg); + break; default: if (mysql_options(m_handle->mysql, static_cast(option), arg) != 0) @@ -720,10 +740,18 @@ DEFINE_BOOL_METHOD(mysql_command_services_imp::get, (MYSQL_H mysql_h, int option, const void *arg)) { try { Mysql_handle *m_handle = reinterpret_cast(mysql_h); - if (m_handle == nullptr) return true; - if (mysql_get_option(m_handle->mysql, - static_cast(option), arg) != 0) - return true; + auto mcs_ext = MYSQL_COMMAND_SERVICE_EXTN(m_handle->mysql); + if (m_handle == nullptr || !arg) return true; + switch (option) { + case MYSQL_NO_LOCK_REGISTRY: + *(const_cast(static_cast(arg))) = + mcs_ext->no_lock_registry; + break; + default: + if (mysql_get_option(m_handle->mysql, + static_cast(option), arg) != 0) + return true; + } } catch (...) { mysql_components_handle_std_exception(__func__); return true; diff --git a/sql/server_component/mysql_command_services_imp.h b/sql/server_component/mysql_command_services_imp.h index b720c580d82c..ddd4d59dfeae 100644 --- a/sql/server_component/mysql_command_services_imp.h +++ b/sql/server_component/mysql_command_services_imp.h @@ -71,6 +71,7 @@ struct mysql_command_service_extn { int mcs_tcpip_port; const char *mcs_db = nullptr; uint32_t mcs_client_flag = 0; + bool no_lock_registry = false; }; #define MYSQL_COMMAND_SERVICE_EXTN(H) \