polardbxengine/unittest/gunit/components/mysql_server/dynamic_loader-t.cc

668 lines
22 KiB
C++

/* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
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 <audit_api_message_service_imp.h>
#include <component_status_var_service.h>
#include <component_sys_var_service.h>
#include <components/mysql_server/mysql_page_track.h>
#include <example_services.h>
#include <gtest/gtest.h>
#include <keyring_iterator_service_imp.h>
#include <mysql.h>
#include <mysql/components/component_implementation.h>
#include <mysql/components/my_service.h>
#include <mysql/components/service.h>
#include <mysql/components/service_implementation.h>
#include <mysql/components/services/backup_lock_service.h>
#include <mysql/components/services/clone_protocol_service.h>
#include <mysql/components/services/component_sys_var_service.h>
#include <mysql/components/services/dynamic_loader.h>
#include <mysql/components/services/mysql_socket_bits.h>
#include <mysql/components/services/page_track_service.h>
#include <mysql/components/services/persistent_dynamic_loader.h>
#include <mysql/components/services/psi_statement_bits.h>
#include <mysql/components/services/psi_thread_bits.h>
#include <mysql/mysql_lex_string.h>
#include <mysql_ongoing_transaction_query.h>
#include <security_context_imp.h>
#include <server_component.h>
#include <stddef.h>
#include <system_variable_source_imp.h>
#include "components/mysql_server/persistent_dynamic_loader.h"
#include "host_application_signal_imp.h"
#include "lex_string.h"
#include "m_ctype.h"
#include "my_inttypes.h"
#include "my_io.h"
#include "my_sys.h"
#include "mysql_current_thread_reader_imp.h"
#include "scope_guard.h"
#include "sql/auth/dynamic_privileges_impl.h"
#include "sql/udf_registration_imp.h"
extern mysql_component_t COMPONENT_REF(mysql_server);
struct mysql_component_t *mysql_builtin_components[] = {
&COMPONENT_REF(mysql_server), 0};
DEFINE_BOOL_METHOD(mysql_component_mysql_current_thread_reader_imp::get,
(MYSQL_THD *)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_component_host_application_signal_imp::signal,
(int, void *)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_persistent_dynamic_loader_imp::load,
(void *, const char *[], int)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_persistent_dynamic_loader_imp::unload,
(void *, const char *[], int)) {
return true;
}
DEFINE_BOOL_METHOD(dynamic_privilege_services_impl::register_privilege,
(const char *, size_t)) {
return true;
}
DEFINE_BOOL_METHOD(dynamic_privilege_services_impl::unregister_privilege,
(const char *, size_t)) {
return true;
}
DEFINE_BOOL_METHOD(dynamic_privilege_services_impl::has_global_grant,
(Security_context_handle, const char *, size_t)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_udf_registration_imp::udf_unregister,
(const char *, int *)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_udf_registration_imp::udf_register_aggregate,
(const char *, enum Item_result, Udf_func_any, Udf_func_init,
Udf_func_deinit, Udf_func_add, Udf_func_clear)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_udf_registration_imp::udf_register,
(const char *, Item_result, Udf_func_any, Udf_func_init,
Udf_func_deinit)) {
return true;
}
void component_sys_var_init() {}
void component_sys_var_deinit() {}
DEFINE_BOOL_METHOD(mysql_component_sys_variable_imp::register_variable,
(const char *, const char *, int, const char *,
mysql_sys_var_check_func, mysql_sys_var_update_func, void *,
void *)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_component_sys_variable_imp::get_variable,
(const char *, const char *, void **, size_t *)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_component_sys_variable_imp::unregister_variable,
(const char *, const char *)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_status_variable_registration_imp::register_variable,
(SHOW_VAR *)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_status_variable_registration_imp::unregister_variable,
(SHOW_VAR *)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_system_variable_source_imp::get,
(const char *, unsigned int, enum enum_variable_source *)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_acquire_backup_lock,
(MYSQL_THD, enum enum_backup_lock_service_lock_kind,
unsigned long)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_release_backup_lock, (MYSQL_THD)) { return true; }
DEFINE_METHOD(void, mysql_clone_start_statement,
(THD *&, PSI_thread_key, PSI_statement_key)) {
return;
}
DEFINE_METHOD(void, mysql_clone_finish_statement, (THD *)) { return; }
DEFINE_METHOD(int, mysql_clone_get_charsets, (THD *, Mysql_Clone_Values &)) {
return (0);
}
DEFINE_METHOD(int, mysql_clone_validate_charsets,
(THD *, Mysql_Clone_Values &)) {
return (0);
}
DEFINE_METHOD(int, mysql_clone_get_configs, (THD *, Mysql_Clone_Key_Values &)) {
return (0);
}
DEFINE_METHOD(int, mysql_clone_validate_configs,
(THD *, Mysql_Clone_Key_Values &)) {
return (0);
}
DEFINE_METHOD(MYSQL *, mysql_clone_connect,
(THD *, const char *, uint32_t, const char *, const char *,
mysql_clone_ssl_context *, MYSQL_SOCKET *)) {
return nullptr;
}
DEFINE_METHOD(int, mysql_clone_send_command,
(THD *, MYSQL *, bool, uchar, uchar *, size_t)) {
return 0;
}
DEFINE_METHOD(int, mysql_clone_get_response,
(THD *, MYSQL *, bool, uint32_t, uchar **, size_t *, size_t *)) {
return 0;
}
DEFINE_METHOD(int, mysql_clone_kill, (MYSQL *, MYSQL *)) { return 0; }
DEFINE_METHOD(void, mysql_clone_disconnect, (THD *, MYSQL *, bool, bool)) {
return;
}
DEFINE_METHOD(void, mysql_clone_get_error, (THD *, uint32_t *, const char **)) {
return;
}
DEFINE_METHOD(int, mysql_clone_get_command,
(THD *, uchar *, uchar **, size_t *)) {
return 0;
}
DEFINE_METHOD(int, mysql_clone_send_response, (THD *, bool, uchar *, size_t)) {
return 0;
}
DEFINE_METHOD(int, mysql_clone_send_error, (THD *, uchar, bool)) { return 0; }
DEFINE_BOOL_METHOD(mysql_security_context_imp::get,
(void *, Security_context_handle *)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_security_context_imp::set,
(void *, Security_context_handle)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_security_context_imp::create,
(Security_context_handle *)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_security_context_imp::destroy,
(Security_context_handle)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_security_context_imp::copy,
(Security_context_handle, Security_context_handle *)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_security_context_imp::lookup,
(Security_context_handle, const char *, const char *,
const char *, const char *)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_security_context_imp::get,
(Security_context_handle, const char *, void *)) {
return true;
}
DEFINE_BOOL_METHOD(mysql_security_context_imp::set,
(Security_context_handle, const char *, void *)) {
return true;
}
DEFINE_BOOL_METHOD(
mysql_ongoing_transactions_query_imp::get_ongoing_server_transactions,
(unsigned long **, unsigned long *)) {
return 0;
}
DEFINE_BOOL_METHOD(
mysql_audit_api_message_imp::emit,
(mysql_event_message_subclass_t type MY_ATTRIBUTE((unused)),
const char *component MY_ATTRIBUTE((unused)),
size_t component_length MY_ATTRIBUTE((unused)),
const char *producer MY_ATTRIBUTE((unused)),
size_t producer_length MY_ATTRIBUTE((unused)),
const char *message MY_ATTRIBUTE((unused)),
size_t message_length MY_ATTRIBUTE((unused)),
mysql_event_message_key_value_t *key_value_map MY_ATTRIBUTE((unused)),
size_t key_value_map_length MY_ATTRIBUTE((unused)))) {
return true;
}
DEFINE_METHOD(int, Page_track_implementation::start,
(MYSQL_THD, Page_Track_SE, uint64_t *)) {
return (0);
}
DEFINE_METHOD(int, Page_track_implementation::stop,
(MYSQL_THD, Page_Track_SE, uint64_t *)) {
return (0);
}
DEFINE_METHOD(int, Page_track_implementation::get_page_ids,
(MYSQL_THD, Page_Track_SE, uint64_t *, uint64_t *,
unsigned char *, size_t, Page_Track_Callback, void *)) {
return (0);
}
DEFINE_METHOD(int, Page_track_implementation::get_num_page_ids,
(MYSQL_THD, Page_Track_SE, uint64_t *, uint64_t *, uint64_t *)) {
return (0);
}
DEFINE_METHOD(int, Page_track_implementation::purge,
(MYSQL_THD, Page_Track_SE, uint64_t *)) {
return (0);
}
DEFINE_METHOD(int, Page_track_implementation::get_status,
(MYSQL_THD, Page_Track_SE, uint64_t *, uint64_t *)) {
return (0);
}
DEFINE_BOOL_METHOD(mysql_keyring_iterator_imp::init,
(my_h_keyring_iterator * iterator MY_ATTRIBUTE((unused)))) {
return true;
}
DEFINE_BOOL_METHOD(mysql_keyring_iterator_imp::deinit,
(my_h_keyring_iterator iterator MY_ATTRIBUTE((unused)))) {
return true;
}
DEFINE_BOOL_METHOD(mysql_keyring_iterator_imp::get,
(my_h_keyring_iterator iterator MY_ATTRIBUTE((unused)),
char *key_id MY_ATTRIBUTE((unused)),
size_t key_id_size MY_ATTRIBUTE((unused)),
char *user_id MY_ATTRIBUTE((unused)),
size_t user_id_size MY_ATTRIBUTE((unused)))) {
return true;
}
/* TODO following code resembles symbols used in sql library, these should be
some day extracted to be reused both in sql library and server component
unit tests. */
struct CHARSET_INFO;
CHARSET_INFO *system_charset_info = &my_charset_latin1;
char opt_plugin_dir[FN_REFLEN];
bool check_string_char_length(const LEX_CSTRING &, const char *, size_t,
const CHARSET_INFO *, bool) {
return false;
}
bool check_valid_path(const char *path, size_t len) {
size_t prefix = my_strcspn(system_charset_info, path, path + len, FN_DIRSEP,
strlen(FN_DIRSEP));
return prefix < len;
}
namespace dynamic_loader_unittest {
using registry_type_t = SERVICE_TYPE_NO_CONST(registry);
using loader_type_t = SERVICE_TYPE_NO_CONST(dynamic_loader);
class dynamic_loader : public ::testing::Test {
protected:
virtual void SetUp() {
my_getwd(opt_plugin_dir, FN_REFLEN, MYF(0));
reg = NULL;
loader = NULL;
ASSERT_FALSE(mysql_services_bootstrap(&reg));
ASSERT_FALSE(reg->acquire("dynamic_loader",
reinterpret_cast<my_h_service *>(
const_cast<loader_type_t **>(&loader))));
}
virtual void TearDown() {
if (reg) {
ASSERT_FALSE(reg->release(
reinterpret_cast<my_h_service>(const_cast<registry_type_t *>(reg))));
}
if (loader) {
ASSERT_FALSE(reg->release(
reinterpret_cast<my_h_service>(const_cast<loader_type_t *>(loader))));
}
shutdown_dynamic_loader();
ASSERT_FALSE(mysql_services_shutdown());
}
SERVICE_TYPE(registry) * reg;
SERVICE_TYPE(dynamic_loader) * loader;
};
TEST_F(dynamic_loader, bootstrap) { ASSERT_TRUE(loader != NULL); }
TEST_F(dynamic_loader, try_load_component) {
static const char *urns[] = {"file://component_example_component1"};
ASSERT_FALSE(loader->load(urns, 1));
ASSERT_FALSE(loader->unload(urns, 1));
}
TEST_F(dynamic_loader, try_unload_the_same_component_in_group) {
static const char *urns[] = {"file://component_example_component1"};
ASSERT_FALSE(loader->load(urns, 1));
static const char *urns_bad[] = {"file://component_example_component1",
"file://component_example_component1"};
ASSERT_TRUE(loader->unload(urns_bad, 2));
ASSERT_FALSE(loader->unload(urns, 1));
}
TEST_F(dynamic_loader, try_load_twice) {
static const char *urns[] = {"file://component_example_component1"};
ASSERT_FALSE(loader->load(urns, 1));
ASSERT_TRUE(loader->load(urns, 1));
{
my_service<SERVICE_TYPE(example_math)> service("example_math", reg);
ASSERT_FALSE((bool)service);
}
ASSERT_FALSE(loader->unload(urns, 1));
}
TEST_F(dynamic_loader, try_load_not_existing) {
static const char *urns[] = {"file://component_example_component0"};
ASSERT_TRUE(loader->load(urns, 1));
}
TEST_F(dynamic_loader, try_load_with_unsatisfied_dependencies) {
static const char *urns[] = {"file://component_example_component3"};
ASSERT_TRUE(loader->load(urns, 1));
}
TEST_F(dynamic_loader, try_load_and_forget) {
static const char *urns[] = {"file://component_example_component1"};
ASSERT_FALSE(loader->load(urns, 1));
}
TEST_F(dynamic_loader, try_unload_not_existing) {
static const char *urns[] = {"file://component_example_component0"};
ASSERT_TRUE(loader->unload(urns, 1));
}
TEST_F(dynamic_loader, load_different_components) {
static const char *urns1[] = {"file://component_example_component1"};
static const char *urns2[] = {"file://component_example_component2",
"file://component_example_component3"};
{
my_service<SERVICE_TYPE(example_math)> service("example_math", reg);
ASSERT_TRUE((bool)service);
}
ASSERT_FALSE(loader->load(urns1, 1));
{
my_service<SERVICE_TYPE(example_math)> service("example_math", reg);
ASSERT_FALSE((bool)service);
}
ASSERT_FALSE(loader->unload(urns1, 1));
ASSERT_FALSE(loader->load(urns2, 2));
{
my_service<SERVICE_TYPE(example_math)> service("example_math", reg);
ASSERT_FALSE((bool)service);
}
ASSERT_FALSE(loader->unload(urns2, 2));
{
my_service<SERVICE_TYPE(example_math)> service("example_math", reg);
ASSERT_TRUE((bool)service);
}
}
TEST_F(dynamic_loader, dependencies) {
static const char *urns1[] = {"file://component_example_component3"};
static const char *urns2[] = {"file://component_example_component1",
"file://component_example_component3"};
{
my_service<SERVICE_TYPE(example_math)> service("example_math", reg);
ASSERT_TRUE((bool)service);
}
ASSERT_TRUE(loader->load(urns1, 1));
{
my_service<SERVICE_TYPE(example_math)> service("example_math", reg);
ASSERT_TRUE((bool)service);
}
ASSERT_FALSE(loader->load(urns2, 2));
ASSERT_FALSE(loader->unload(urns2, 2));
{
my_service<SERVICE_TYPE(example_math)> service("example_math", reg);
ASSERT_TRUE((bool)service);
}
}
TEST_F(dynamic_loader, cyclic_dependencies) {
static const char *urns_self_depends[] = {
"file://component_self_required_test_component"};
static const char *urns_cyclic_depends_broken1[] = {
"file://component_cyclic_dependency_test_component_1"};
static const char *urns_cyclic_depends_broken2[] = {
"file://component_cyclic_dependency_test_component_2"};
static const char *urns_cyclic_depends[] = {
"file://component_cyclic_dependency_test_component_1",
"file://component_cyclic_dependency_test_component_2"};
/* Self-provided requirements should pass. */
ASSERT_FALSE(loader->load(urns_self_depends, 1));
ASSERT_FALSE(loader->unload(urns_self_depends, 1));
/* Broken cyclic dependency. */
ASSERT_TRUE(loader->load(urns_cyclic_depends_broken1, 1));
ASSERT_TRUE(loader->load(urns_cyclic_depends_broken2, 1));
/* Correct cyclic dependency.*/
ASSERT_FALSE(loader->load(urns_cyclic_depends, 2));
ASSERT_FALSE(loader->unload(urns_cyclic_depends, 2));
}
TEST_F(dynamic_loader, first_dependency) {
static const char *urn1[] = {"file://component_example_component1"};
static const char *urn2[] = {"file://component_example_component2"};
static const char *urn3[] = {"file://component_example_component3"};
ASSERT_TRUE(loader->load(urn3, 1));
ASSERT_FALSE(loader->load(urn1, 1));
ASSERT_FALSE(loader->load(urn3, 1));
ASSERT_FALSE(loader->load(urn2, 1));
/*
lib2 would be sufficient for lib3 to satisfy its dependencies, but lib3 is
already using actively dependency on lib1, so we can't unload it here.
*/
ASSERT_TRUE(loader->unload(urn1, 1));
}
TEST_F(dynamic_loader, iteration) {
my_service<SERVICE_TYPE(dynamic_loader_query)> service("dynamic_loader_query",
reg);
ASSERT_FALSE(service);
my_h_component_iterator iterator;
const char *name;
const char *urn;
int count = 0;
bool test_library_found = false;
/* No components to iterate over. */
ASSERT_TRUE(service->create(&iterator));
static const char *urns[] = {"file://component_example_component1",
"file://component_example_component2",
"file://component_example_component3"};
ASSERT_FALSE(loader->load(urns, 3));
ASSERT_FALSE(service->create(&iterator));
auto guard = create_scope_guard(
[&service, &iterator]() { service->release(iterator); });
service->release(my_h_component_iterator{});
ASSERT_TRUE(service->get(my_h_component_iterator{}, &name, &urn));
ASSERT_TRUE(service->next(my_h_component_iterator{}));
ASSERT_TRUE(service->is_valid(my_h_component_iterator{}));
for (; !service->is_valid(iterator); service->next(iterator)) {
ASSERT_FALSE(service->get(iterator, &name, &urn));
count++;
test_library_found |= !strcmp(name, "mysql:example_component1") &&
!strcmp(urn, "file://component_example_component1");
}
ASSERT_TRUE(service->get(iterator, &name, &urn));
ASSERT_TRUE(service->next(iterator));
ASSERT_TRUE(service->is_valid(iterator));
/* there should be at least 3 test components loaded. */
ASSERT_GE(count, 3);
ASSERT_TRUE(test_library_found);
}
TEST_F(dynamic_loader, metadata) {
my_service<SERVICE_TYPE(dynamic_loader_query)> query_service(
"dynamic_loader_query", reg);
ASSERT_FALSE(query_service);
my_service<SERVICE_TYPE(dynamic_loader_metadata_enumerate)> metadata_service(
"dynamic_loader_metadata_enumerate", reg);
ASSERT_FALSE(metadata_service);
my_service<SERVICE_TYPE(dynamic_loader_metadata_query)>
metadata_query_service("dynamic_loader_metadata_query", reg);
ASSERT_FALSE(metadata_query_service);
static const char *urns[] = {"file://component_example_component1",
"file://component_example_component2",
"file://component_example_component3"};
ASSERT_FALSE(loader->load(urns, 3));
my_h_component_iterator iterator;
const char *name;
const char *urn;
const char *value;
int count = 0;
bool property_found = false;
ASSERT_FALSE(query_service->create(&iterator));
auto guard = create_scope_guard(
[&query_service, &iterator]() { query_service->release(iterator); });
for (; !query_service->is_valid(iterator); query_service->next(iterator)) {
ASSERT_FALSE(query_service->get(iterator, &name, &urn));
if (!strcmp(urn, "file://component_example_component1")) {
ASSERT_FALSE(
metadata_query_service->get_value(iterator, "mysql.author", &value));
ASSERT_STREQ(value, "Oracle Corporation");
ASSERT_FALSE(
metadata_query_service->get_value(iterator, "mysql.license", &value));
ASSERT_STREQ(value, "GPL");
ASSERT_FALSE(
metadata_query_service->get_value(iterator, "test_property", &value));
ASSERT_TRUE(metadata_query_service->get_value(
iterator, "non_existing_test_property", &value));
my_h_component_metadata_iterator metadata_iterator;
ASSERT_FALSE(metadata_service->create(iterator, &metadata_iterator));
auto guard =
create_scope_guard([&metadata_service, &metadata_iterator]() {
metadata_service->release(metadata_iterator);
});
metadata_service->release(my_h_component_metadata_iterator{});
ASSERT_TRUE(metadata_service->get(my_h_component_metadata_iterator{},
&name, &value));
ASSERT_TRUE(metadata_service->next(my_h_component_metadata_iterator{}));
ASSERT_TRUE(
metadata_service->is_valid(my_h_component_metadata_iterator{}));
for (; !metadata_service->is_valid(metadata_iterator);
metadata_service->next(metadata_iterator)) {
ASSERT_FALSE(metadata_service->get(metadata_iterator, &name, &value));
count++;
property_found |= strcmp(name, "test_property");
}
ASSERT_TRUE(metadata_service->get(metadata_iterator, &name, &value));
ASSERT_TRUE(metadata_service->next(metadata_iterator));
ASSERT_TRUE(metadata_service->is_valid(metadata_iterator));
/* there should be at least 3 properties. */
ASSERT_GE(count, 3);
ASSERT_TRUE(property_found);
}
}
}
} // namespace dynamic_loader_unittest
/* mandatory main function */
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
MY_INIT(argv[0]);
char realpath_buf[FN_REFLEN];
char basedir_buf[FN_REFLEN];
my_realpath(realpath_buf, my_progname, 0);
size_t res_length;
dirname_part(basedir_buf, realpath_buf, &res_length);
if (res_length > 0) basedir_buf[res_length - 1] = '\0';
my_setwd(basedir_buf, 0);
int retval = RUN_ALL_TESTS();
my_end(0);
return retval;
}