diff --git a/include/my_bitmap.h b/include/my_bitmap.h index 40685f8e5b44..f65b5253f8ce 100644 --- a/include/my_bitmap.h +++ b/include/my_bitmap.h @@ -70,7 +70,7 @@ extern uint bitmap_bits_set(const MY_BITMAP *map); extern void bitmap_free(MY_BITMAP *map); extern void bitmap_set_above(MY_BITMAP *map, uint from_byte, bool use_bit); extern void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size); -extern void bitmap_intersect(MY_BITMAP *to, const MY_BITMAP *from); +extern void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2); extern void bitmap_subtract(MY_BITMAP *map, const MY_BITMAP *map2); extern void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2); extern void bitmap_xor(MY_BITMAP *map, const MY_BITMAP *map2); @@ -135,4 +135,4 @@ static inline void bitmap_set_all(MY_BITMAP *map) { memset(map->bitmap, 0xFF, 4 * no_words_in_map(map)); } -#endif // MY_BITMAP_INCLUDED +#endif /* MY_BITMAP_INCLUDED */ diff --git a/include/my_sys.h b/include/my_sys.h index 4b9510c20a74..7d096519e015 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -602,6 +602,9 @@ extern size_t my_fwrite(FILE *stream, const uchar *Buffer, size_t Count, myf MyFlags); extern my_off_t my_fseek(FILE *stream, my_off_t pos, int whence); extern my_off_t my_ftell(FILE *stream); +#if !defined(HAVE_MEMSET_S) +void memset_s(void *dest, size_t dest_max, int c, size_t n); +#endif /* implemented in my_syslog.c */ diff --git a/mysql-test/include/plugin.defs b/mysql-test/include/plugin.defs index 258f6f59f748..1ce55fa52a05 100644 --- a/mysql-test/include/plugin.defs +++ b/mysql-test/include/plugin.defs @@ -181,3 +181,13 @@ component_test_server_telemetry_traces plugin_output_directory no TEST_SER # component_test_mysql_thd_store_service component_test_mysql_thd_store_service plugin_output_directory no COMPONENT_TEST_MYSQL_THD_STORE_SERVICE + +# Percona additions +auth_socket plugin_output_directory no SOCKET_AUTH +audit_log plugin_output_directory no AUDIT_LOG audit_log +ha_tokudb plugin_output_directory no TOKUDB tokudb,tokudb_trx,tokudb_locks,tokudb_lock_waits,tokudb_fractal_tree_info,tokudb_background_job_status,tokudb_file_map +tokudb_backup plugin_output_directory no TOKUDB_BACKUP tokudb_backup +ha_rocksdb plugin_output_directory no ROCKSDB rocksdb,rocksdb_cfstats,rocksdb_dbstats,rocksdb_perf_context,rocksdb_perf_context_global,rocksdb_cf_options,rocksdb_compaction_history,rocksdb_compaction_stats,rocksdb_active_compaction_stats,rocksdb_global_info,rocksdb_ddl,rocksdb_index_file_map,rocksdb_locks,rocksdb_trx,rocksdb_deadlock,rocksdb_sst_props,rocksdb_live_files_metadata +auth_pam plugin_output_directory no AUTH_PAM +auth_pam_compat plugin_output_directory no AUTH_PAM_COMPAT +keyring_vault plugin_output_directory no KEYRING_VAULT_PLUGIN keyring_vault diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index 8861e497ce39..8bf62fa5bd36 100644 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -26,6 +26,8 @@ SET(MY_TIME_SOURCES my_time.cc my_systime.cc) ADD_CONVENIENCE_LIBRARY(mytime ${MY_TIME_SOURCES}) +INCLUDE_DIRECTORIES(SYSTEM ${BOOST_PATCHES_DIR} ${BOOST_INCLUDE_DIR}) + SET(MYSYS_SOURCES array.cc charset.cc diff --git a/mysys/my_malloc.cc b/mysys/my_malloc.cc index 2e42f140e523..d755fdb8fa6c 100644 --- a/mysys/my_malloc.cc +++ b/mysys/my_malloc.cc @@ -561,3 +561,16 @@ char *my_strndup(PSI_memory_key key, const char *from, size_t length, } return ptr; } + +#if !defined(HAVE_MEMSET_S) +void memset_s(void *dest, size_t dest_max, int c, size_t n) { +#if defined(WIN32) + SecureZeroMemory(dest, n); +#else + volatile unsigned char *p = static_cast(dest); + while (dest_max-- && n--) { + *p++ = c; + } +#endif +} +#endif diff --git a/plugin/audit_log/CMakeLists.txt b/plugin/audit_log/CMakeLists.txt new file mode 100644 index 000000000000..c7aa10ed53aa --- /dev/null +++ b/plugin/audit_log/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (c) 2014 Percona LLC 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 as published by +# the Free Software Foundation; version 2 of the License. +# +# 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 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 + +MYSQL_ADD_PLUGIN(audit_log audit_log.cc file_logger.cc buffer.cc audit_file.cc + audit_syslog.cc filter.cc + MODULE_ONLY MODULE_OUTPUT_NAME "audit_log") + +IF(UNIX) + IF(INSTALL_MYSQLTESTDIR) + INSTALL(DIRECTORY tests/mtr/ DESTINATION ${INSTALL_MYSQLTESTDIR}/suite/audit_log COMPONENT Test) + ENDIF() +ENDIF() diff --git a/plugin/audit_log/audit_file.cc b/plugin/audit_log/audit_file.cc new file mode 100644 index 000000000000..ee8ae42439b6 --- /dev/null +++ b/plugin/audit_log/audit_file.cc @@ -0,0 +1,183 @@ +/* Copyright (c) 2014 Percona LLC 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 + as published by the Free Software Foundation; version 2 of + the License. + + 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 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_handler.h" +#include "audit_log.h" +#include "buffer.h" +#include "my_dbug.h" +#include "my_sys.h" +#include "mysql/service_mysql_alloc.h" + +struct audit_handler_file_data_t { + size_t struct_size; + LOGGER_HANDLE *logger; + logger_prolog_func_t header; + logger_epilog_func_t footer; + bool sync_on_write; + bool use_buffer; + audit_log_buffer_t *buffer; +}; + +static int audit_handler_file_write(audit_handler_t *handler, const char *buf, + size_t len); +static int audit_handler_file_flush(audit_handler_t *handler) noexcept; +static int audit_handler_file_close(audit_handler_t *handler) noexcept; +static int audit_handler_file_write_nobuf(LOGGER_HANDLE *logger, + const char *buf, size_t len, + log_record_state_t state) noexcept; +static int audit_handler_file_write_buf(audit_log_buffer_t *buffer, + const char *buf, size_t len); +static void audit_handler_file_set_option(audit_handler_t *handler, + audit_handler_option_t opt, + void *val) noexcept; + +static int write_callback(void *data, const char *buf, size_t len, + log_record_state_t state) noexcept { + audit_handler_t *handler = (audit_handler_t *)data; + audit_handler_file_data_t *hdata = (audit_handler_file_data_t *)handler->data; + + assert(hdata->struct_size == sizeof(audit_handler_file_data_t)); + + return audit_handler_file_write_nobuf(hdata->logger, buf, len, state); +} + +audit_handler_t *audit_handler_file_open( + audit_handler_file_config_t *opts) noexcept { + audit_handler_t *handler = (audit_handler_t *)my_malloc( + key_memory_audit_log_handler, + sizeof(audit_handler_t) + sizeof(audit_handler_file_data_t), MY_ZEROFILL); + if (handler != nullptr) { + audit_handler_file_data_t *data = + (audit_handler_file_data_t *)(handler + 1); + data->struct_size = sizeof(audit_handler_file_data_t); + data->footer = opts->footer; + data->header = opts->header; + data->sync_on_write = opts->sync_on_write; + data->use_buffer = opts->use_buffer; + if (data->use_buffer) { + data->buffer = audit_log_buffer_init( + opts->buffer_size, opts->can_drop_data, write_callback, handler); + if (data->buffer == nullptr) goto error; + } + data->logger = logger_open(opts->name, opts->rotate_on_size, + opts->rotate_on_size ? opts->rotations : 0, + !opts->use_buffer, opts->header); + if (data->logger == nullptr) { + goto error; + } + handler->data = data; + handler->write = audit_handler_file_write; + handler->flush = audit_handler_file_flush; + handler->close = audit_handler_file_close; + handler->set_option = audit_handler_file_set_option; + goto success; + error: + if (data->buffer) { + audit_log_buffer_shutdown(data->buffer); + } + my_free(handler); + handler = nullptr; + } +success: + return handler; +} + +static int audit_handler_file_write_nobuf(LOGGER_HANDLE *logger, + const char *buf, size_t len, + log_record_state_t state) noexcept { + return logger_write(logger, buf, len, state); +} + +static int audit_handler_file_write_buf(audit_log_buffer_t *buffer, + const char *buf, size_t len) { + return audit_log_buffer_write(buffer, buf, len); +} + +static int audit_handler_file_write(audit_handler_t *handler, const char *buf, + size_t len) { + audit_handler_file_data_t *data = (audit_handler_file_data_t *)handler->data; + int res; + + assert(data->struct_size == sizeof(audit_handler_file_data_t)); + + if (data->use_buffer) { + assert(data->buffer); + res = audit_handler_file_write_buf(data->buffer, buf, len); + } else { + assert(data->logger); + res = audit_handler_file_write_nobuf(data->logger, buf, len, + log_record_state_t::COMPLETE); + + if (data->sync_on_write) { + logger_sync(data->logger); + } + } + + return res; +} + +static int audit_handler_file_flush(audit_handler_t *handler) noexcept { + audit_handler_file_data_t *data = (audit_handler_file_data_t *)handler->data; + LOGGER_HANDLE *logger; + int res; + + assert(data->struct_size == sizeof(audit_handler_file_data_t)); + + logger = data->logger; + + if (data->use_buffer) audit_log_buffer_pause(data->buffer); + + res = logger_reopen(logger, data->header, data->footer); + + if (data->use_buffer) audit_log_buffer_resume(data->buffer); + + return res; +} + +static int audit_handler_file_close(audit_handler_t *handler) noexcept { + audit_handler_file_data_t *data = (audit_handler_file_data_t *)handler->data; + int res; + LOGGER_HANDLE *logger; + + assert(data->struct_size == sizeof(audit_handler_file_data_t)); + + logger = data->logger; + + if (data->use_buffer) { + audit_log_buffer_shutdown(data->buffer); + } + + res = logger_close(logger, data->footer); + + my_free(handler); + + return res; +} + +static void audit_handler_file_set_option(audit_handler_t *handler, + audit_handler_option_t opt, + void *val) noexcept { + audit_handler_file_data_t *data = (audit_handler_file_data_t *)handler->data; + + switch (opt) { + case audit_handler_option_t::ROTATE_ON_SIZE: + logger_set_size_limit(data->logger, *(ulonglong *)(val)); + break; + case audit_handler_option_t::ROTATIONS: + logger_set_rotations(data->logger, *(ulonglong *)(val)); + break; + } +} diff --git a/plugin/audit_log/audit_handler.h b/plugin/audit_log/audit_handler.h new file mode 100644 index 000000000000..a5bd77336a01 --- /dev/null +++ b/plugin/audit_log/audit_handler.h @@ -0,0 +1,92 @@ +/* Copyright (c) 2014 Percona LLC 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 + as published by the Free Software Foundation; version 2 of + the License. + + 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 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 AUDIT_HANDLER_INCLUDED +#define AUDIT_HANDLER_INCLUDED + +#include "logger.h" + +struct audit_handler_file_config_t; +struct audit_handler_syslog_config_t; +struct audit_handler_buffered_t; +typedef void *audit_handler_data_t; + +enum class audit_handler_option_t { ROTATE_ON_SIZE, ROTATIONS }; + +struct audit_handler_t { + int (*write)(audit_handler_t *, const char *, size_t); + int (*flush)(audit_handler_t *); + int (*close)(audit_handler_t *); + void (*set_option)(audit_handler_t *, audit_handler_option_t, void *); + audit_handler_data_t data; +}; + +struct audit_handler_file_config_t { + const char *name; + size_t rotate_on_size; + size_t rotations; + bool sync_on_write; + bool use_buffer; + size_t buffer_size; + bool can_drop_data; + logger_prolog_func_t header; + logger_epilog_func_t footer; +}; + +struct audit_handler_syslog_config_t { + const char *ident; + int facility; + int priority; + logger_prolog_func_t header; + logger_epilog_func_t footer; +}; + +static inline int audit_handler_write(audit_handler_t *handler, const char *buf, + size_t len) { + if (handler != nullptr && handler->write != nullptr) { + return handler->write(handler, buf, len); + } + return len; +} + +static inline int audit_handler_flush(audit_handler_t *handler) { + if (handler != nullptr && handler->flush != nullptr) { + return handler->flush(handler); + } + return 0; +} + +static inline int audit_handler_close(audit_handler_t *handler) { + if (handler != nullptr && handler->close != nullptr) { + return handler->close(handler); + } + return 0; +} + +static inline void audit_handler_set_option(audit_handler_t *handler, + audit_handler_option_t opt, + void *val) { + if (handler != nullptr && handler->set_option != nullptr) { + handler->set_option(handler, opt, val); + } +} + +audit_handler_t *audit_handler_file_open( + audit_handler_file_config_t *opts) noexcept; +audit_handler_t *audit_handler_syslog_open( + audit_handler_syslog_config_t *opts) noexcept; + +#endif diff --git a/plugin/audit_log/audit_log.cc b/plugin/audit_log/audit_log.cc new file mode 100644 index 000000000000..fa8506ef0993 --- /dev/null +++ b/plugin/audit_log/audit_log.cc @@ -0,0 +1,1716 @@ +/* Copyright (c) 2014-2016 Percona LLC 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 + as published by the Free Software Foundation; version 2 of + the License. + + 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 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 +#include +#include +#include +#include + +#include "m_ctype.h" +#include "my_sys.h" +#include "mysql/components/services/component_sys_var_service.h" +#include "mysql/plugin.h" +#include "mysql/plugin_audit.h" +#include "mysql/psi/mysql_memory.h" +#include "mysql/service_security_context.h" +#include "mysql_com.h" +#include "mysql_version.h" +#include "mysqld_error.h" +#include "sql/mysqld.h" +#include "typelib.h" + +#include "audit_handler.h" +#include "audit_log.h" +#include "buffer.h" +#include "filter.h" +#include "logger.h" + +#define PLUGIN_VERSION 0x0002 + +enum audit_log_policy_t { ALL, NONE, LOGINS, QUERIES }; +enum audit_log_strategy_t { + ASYNCHRONOUS, + PERFORMANCE, + SEMISYNCHRONOUS, + SYNCHRONOUS +}; +enum audit_log_format_t { OLD, NEW, JSON, CSV }; +enum audit_log_handler_t { HANDLER_FILE, HANDLER_SYSLOG }; + +typedef void (*escape_buf_func_t)(const char *, size_t *, char *, size_t *); + +static audit_handler_t *log_handler = nullptr; +static std::atomic record_id{0}; +static time_t log_file_time = 0; +static char *audit_log_file; +static const char default_audit_log_file[] = "audit.log"; +static ulong audit_log_policy = audit_log_policy_t::ALL; +static ulong audit_log_strategy = audit_log_strategy_t::ASYNCHRONOUS; +static ulonglong audit_log_buffer_size = 1048576; +static ulonglong audit_log_rotate_on_size = 0; +static ulonglong audit_log_rotations = 0; +static bool audit_log_flush = false; +static ulong audit_log_format = audit_log_format_t::OLD; +static ulong audit_log_handler = audit_log_handler_t::HANDLER_FILE; +static char *audit_log_syslog_ident; +static const char default_audit_log_syslog_ident[] = "percona-audit"; +static ulong audit_log_syslog_facility = 0; +static ulong audit_log_syslog_priority = 0; +static char *audit_log_exclude_accounts = nullptr; +static char *audit_log_include_accounts = nullptr; +static char *audit_log_exclude_databases = nullptr; +static char *audit_log_include_databases = nullptr; +static char *audit_log_exclude_commands = nullptr; +static char *audit_log_include_commands = nullptr; + +PSI_memory_key key_memory_audit_log_logger_handle; +PSI_memory_key key_memory_audit_log_handler; +PSI_memory_key key_memory_audit_log_buffer; +PSI_memory_key key_memory_audit_log_accounts; +PSI_memory_key key_memory_audit_log_databases; +PSI_memory_key key_memory_audit_log_commands; + +static PSI_memory_info all_audit_log_memory[] = { + {&key_memory_audit_log_logger_handle, "audit_log_logger_handle", + PSI_FLAG_ONLY_GLOBAL_STAT, PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, + {&key_memory_audit_log_handler, "audit_log_handler", + PSI_FLAG_ONLY_GLOBAL_STAT, PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, + {&key_memory_audit_log_buffer, "audit_log_buffer", + PSI_FLAG_ONLY_GLOBAL_STAT, PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, + {&key_memory_audit_log_accounts, "audit_log_accounts", + PSI_FLAG_ONLY_GLOBAL_STAT, PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, + {&key_memory_audit_log_databases, "audit_log_databases", + PSI_FLAG_ONLY_GLOBAL_STAT, PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, + {&key_memory_audit_log_commands, "audit_log_commands", + PSI_FLAG_ONLY_GLOBAL_STAT, PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, +}; + +static const int audit_log_syslog_facility_codes[] = { + LOG_USER, LOG_AUTHPRIV, LOG_CRON, LOG_DAEMON, LOG_FTP, LOG_KERN, + LOG_LPR, LOG_MAIL, LOG_NEWS, +#if (defined LOG_SECURITY) + LOG_SECURITY, +#endif + LOG_SYSLOG, LOG_AUTH, LOG_UUCP, LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, + LOG_LOCAL3, LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7, 0}; + +static const char *audit_log_syslog_facility_names[] = { + "LOG_USER", "LOG_AUTHPRIV", "LOG_CRON", "LOG_DAEMON", + "LOG_FTP", "LOG_KERN", "LOG_LPR", "LOG_MAIL", + "LOG_NEWS", +#if (defined LOG_SECURITY) + "LOG_SECURITY", +#endif + "LOG_SYSLOG", "LOG_AUTH", "LOG_UUCP", "LOG_LOCAL0", + "LOG_LOCAL1", "LOG_LOCAL2", "LOG_LOCAL3", "LOG_LOCAL4", + "LOG_LOCAL5", "LOG_LOCAL6", "LOG_LOCAL7", 0}; + +static const int audit_log_syslog_priority_codes[] = { + LOG_INFO, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, + LOG_NOTICE, LOG_EMERG, LOG_DEBUG, 0}; + +static const char *audit_log_syslog_priority_names[] = { + "LOG_INFO", "LOG_ALERT", "LOG_CRIT", "LOG_ERR", "LOG_WARNING", + "LOG_NOTICE", "LOG_EMERG", "LOG_DEBUG", 0}; + +static MYSQL_PLUGIN plugin_ptr; + +static void init_record_id(off_t size) noexcept { + record_id.store(size, std::memory_order_relaxed); +} + +static ulonglong next_record_id() noexcept { + return record_id.fetch_add(1, std::memory_order_relaxed) + 1; +} + +static const constexpr auto MAX_RECORD_ID_SIZE = 50; +static const constexpr auto MAX_TIMESTAMP_SIZE = 25; + +static char *make_timestamp(char *buf, size_t buf_len, time_t t) noexcept { + tm tm; + + memset(&tm, 0, sizeof(tm)); + strftime(buf, buf_len, "%FT%TZ", gmtime_r(&t, &tm)); + + return buf; +} + +static char *make_record_id(char *buf, size_t buf_len) noexcept { + tm tm; + size_t len; + + memset(&tm, 0, sizeof(tm)); + len = snprintf(buf, buf_len, "%llu_", next_record_id()); + + strftime(buf + len, buf_len - len, "%FT%T", gmtime_r(&log_file_time, &tm)); + + return buf; +} + +struct escape_rule_t { + char character; + size_t length; + const char *replacement; +}; + +static void escape_buf(const char *in, size_t *inlen, char *out, size_t *outlen, + const escape_rule_t *control_escape_rules, + const escape_rule_t *other_escape_rules) noexcept { + char *outstart = out; + const char *base = in; + char *outend = out + *outlen; + + const char *inend = in + (*inlen); + + while ((in < inend) && (out < outend)) { + const escape_rule_t *replace_rule = nullptr; + if ((unsigned char)(*in) < 32) { + if (control_escape_rules[(unsigned int)*in].character) { + replace_rule = &control_escape_rules[(unsigned int)*in]; + } + } else { + const escape_rule_t *rule = nullptr; + for (rule = other_escape_rules; rule->character; rule++) { + if (*in == rule->character) { + replace_rule = rule; + break; + } + } + } + if (replace_rule) { + if ((outend - out) < (ptrdiff_t)replace_rule->length) break; + memcpy(out, replace_rule->replacement, replace_rule->length); + out += replace_rule->length; + } else { + *out++ = *in; + } + ++in; + } + *outlen = out - outstart; + *inlen = in - base; +} + +static void xml_escape(const char *in, size_t *inlen, char *out, + size_t *outlen) noexcept { + // Although most control sequences aren't supported in XML 1.0, we are better + // off printing them anyway instead of the original control characters + static const escape_rule_t control_rules[] = { + {0, 1, "?"}, {1, 4, ""}, {2, 4, ""}, + {3, 4, ""}, {4, 4, ""}, {5, 4, ""}, + {6, 4, ""}, {7, 4, ""}, {8, 4, ""}, + {'\t', 4, " "}, {'\n', 5, " "}, {11, 5, " "}, + {12, 5, " "}, {'\r', 5, " "}, {14, 5, ""}, + {15, 5, ""}, {16, 5, ""}, {17, 5, ""}, + {18, 5, ""}, {19, 5, ""}, {20, 5, ""}, + {21, 5, ""}, {22, 5, ""}, {23, 5, ""}, + {24, 5, ""}, {25, 5, ""}, {26, 5, ""}, + {27, 5, ""}, {28, 5, ""}, {29, 5, ""}, + {30, 5, ""}, {31, 5, ""}, + }; + static const escape_rule_t other_rules[] = {{'<', 4, "<"}, + {'>', 4, ">"}, + {'&', 5, "&"}, + {'"', 6, """}, + {0, 0, nullptr}}; + + escape_buf(in, inlen, out, outlen, control_rules, other_rules); +} + +static void json_escape(const char *in, size_t *inlen, char *out, + size_t *outlen) noexcept { + static const escape_rule_t control_rules[] = { + {0, 6, "\\u0000"}, {1, 6, "\\u0001"}, {2, 6, "\\u0002"}, + {3, 6, "\\u0003"}, {4, 6, "\\u0004"}, {5, 6, "\\u0005"}, + {6, 6, "\\u0006"}, {7, 6, "\\u0007"}, {'\b', 2, "\\b"}, + {'\t', 2, "\\t"}, {'\n', 2, "\\n"}, {11, 6, "\\u000B"}, + {'\f', 2, "\\f"}, {'\r', 2, "\\r"}, {14, 6, "\\u000E"}, + {15, 6, "\\u000F"}, {16, 6, "\\u0010"}, {17, 6, "\\u0011"}, + {18, 6, "\\u0012"}, {19, 6, "\\u0013"}, {20, 6, "\\u0014"}, + {21, 6, "\\u0015"}, {22, 6, "\\u0016"}, {23, 6, "\\u0017"}, + {24, 6, "\\u0018"}, {25, 6, "\\u0019"}, {26, 6, "\\u001A"}, + {27, 6, "\\u001B"}, {28, 6, "\\u001C"}, {29, 6, "\\u001D"}, + {30, 6, "\\u001E"}, {31, 6, "\\u001F"}, + }; + + static const escape_rule_t other_rules[] = { + {'\\', 2, "\\\\"}, {'"', 2, "\\\""}, {'/', 2, "\\/"}, {0, 0, NULL}}; + + escape_buf(in, inlen, out, outlen, control_rules, other_rules); +} + +static void csv_escape(const char *in, size_t *inlen, char *out, + size_t *outlen) noexcept { + // We do not have any standard control escape rules for CSVs + static const escape_rule_t control_rules[] = { + {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, + {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, + {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, + {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, + {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, + {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, + {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, + {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, + }; + + static const escape_rule_t other_rules[] = {{'"', 2, "\"\""}, + {0, 0, nullptr}}; + + escape_buf(in, inlen, out, outlen, control_rules, other_rules); +} + +static const escape_buf_func_t format_escape_func[] = {xml_escape, xml_escape, + json_escape, csv_escape}; + +/* + Calculate the size of the otput bufer needed to escape the string. + + @param[in] in Input string + @param[in] len Length of the input string + + @return + size of the otput bufer including trailing zero +*/ +static size_t calculate_escape_string_buf_len(const char *in, + size_t len) noexcept { + char tmp[128]; + size_t full_outlen = 0; + + while (len > 0) { + size_t tmp_size = sizeof(tmp); + size_t inlen = len; + format_escape_func[static_cast(audit_log_format)](in, &inlen, tmp, + &tmp_size); + in += inlen; + len -= inlen; + full_outlen += tmp_size; + } + return full_outlen + 1; +} + +/* + Escape string according to audit_log_format. + + @param[in] in Input string + @param[in] inlen Length of the input string + @param[in] out Output buffer + @param[in] outlen Length of the output buffer + @param[out] endptr A pointer to the character after the + last escaped character in the output + buffer + @param[out] full_outlen Length of the output buffer that would + be needed to store complete non-truncated + escaped input buffer + + @return + pointer to the beginning of the output buffer +*/ +static char *escape_string(const char *in, size_t inlen, char *out, + size_t outlen, char **endptr, + size_t *full_outlen) noexcept { + if (outlen == 0) { + if (endptr) *endptr = out; + if (full_outlen) *full_outlen += calculate_escape_string_buf_len(in, inlen); + } else if (in != nullptr) { + size_t inlen_res = inlen; + --outlen; + format_escape_func[static_cast(audit_log_format)](in, &inlen_res, out, + &outlen); + out[outlen] = 0; + if (endptr) *endptr = out + outlen + 1; + if (full_outlen) { + *full_outlen += outlen; + *full_outlen += + calculate_escape_string_buf_len(in + inlen_res, inlen - inlen_res); + } + } else { + *out = 0; + if (endptr) *endptr = out + 1; + if (full_outlen) ++(*full_outlen); + } + return out; +} + +static void my_plugin_perror(void) noexcept { + char errbuf[MYSYS_STRERROR_SIZE]; + my_strerror(errbuf, sizeof(errbuf), errno); + my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, "Error: %s", errbuf); +} + +static void audit_log_write(const char *buf, size_t len) { + static bool write_error = false; + + if (audit_handler_write(log_handler, buf, len) < 0) { + if (!write_error) { + write_error = true; + my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, + "Error writing to file %s.", audit_log_file); + my_plugin_perror(); + } + } else { + write_error = false; + } +} + +static char *make_argv(char *buf, size_t len, int argc, char **argv) noexcept { + size_t left = len; + + buf[0] = 0; + while (argc > 0 && left > 0) { + const int ret = + snprintf(buf + len - left, left, "%s%c", *argv, argc > 1 ? ' ' : 0); + assert(ret > 0); + if (ret < 0 || static_cast(ret) >= left) break; + left -= ret; + argc--; + argv++; + } + + return buf; +} + +static char *audit_log_audit_record(char *buf, size_t buflen, const char *name, + time_t t, size_t *outlen) noexcept { + char id_str[MAX_RECORD_ID_SIZE]; + char timestamp[MAX_TIMESTAMP_SIZE]; + char arg_buf[512]; + static const char *format_string[] = { + "\n", + + "\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " " MACHINE_TYPE "-" SYSTEM_TYPE + "\n" + "\n", + + "{\"audit_record\":{\"name\":\"%s\",\"record\":\"%s\"," + "\"timestamp\":\"%s\",\"mysql_version\":\"%s\"," + "\"startup_optionsi\":\"%s\"," + "\"os_version\":\"" MACHINE_TYPE "-" SYSTEM_TYPE "\"}}\n", + + "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"," + "\"" MACHINE_TYPE "-" SYSTEM_TYPE "\"\n"}; + + *outlen = snprintf( + buf, buflen, format_string[static_cast(audit_log_format)], name, + make_record_id(id_str, sizeof(id_str)), + make_timestamp(timestamp, sizeof(timestamp), t), server_version, + make_argv(arg_buf, sizeof(arg_buf), orig_argc - 1, orig_argv + 1)); + + /* make sure that record is not truncated */ + assert(buf + *outlen <= buf + buflen); + + return buf; +} + +static char *audit_log_general_record(char *buf, size_t buflen, + const char *name, time_t t, int status, + const mysql_event_general &event, + const char *default_db, + size_t *outlen) noexcept { + char id_str[MAX_RECORD_ID_SIZE]; + char timestamp[MAX_TIMESTAMP_SIZE]; + char *query, *user, *host, *external_user, *ip, *db; + char *endptr = buf, *endbuf = buf + buflen; + size_t full_outlen = 0, buflen_estimated; + size_t query_length; + + static const char *format_string[] = { + "\n", + + "\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %lu\n" + " %d\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + "\n", + + "{\"audit_record\":" + "{\"name\":\"%s\"," + "\"record\":\"%s\"," + "\"timestamp\":\"%s\"," + "\"command_class\":\"%s\"," + "\"connection_id\":\"%lu\"," + "\"status\":%d," + "\"sqltext\":\"%s\"," + "\"user\":\"%s\"," + "\"host\":\"%s\"," + "\"os_user\":\"%s\"," + "\"ip\":\"%s\"," + "\"db\":\"%s\"}}\n", + + ("\"%s\",\"%s\",\"%s\",\"%s\",\"%lu\",%d,\"%s\",\"%s\"," + "\"%s\",\"%s\",\"%s\",\"%s\"\n")}; + + query_length = + my_charset_utf8mb4_general_ci.mbmaxlen * event.general_query.length; + + if (query_length < (size_t)(endbuf - endptr)) { + uint errors; + query_length = + my_convert(endptr, query_length, &my_charset_utf8mb4_general_ci, + event.general_query.str, event.general_query.length, + event.general_charset, &errors); + query = endptr; + endptr += query_length; + + full_outlen += query_length; + + query = escape_string(query, query_length, endptr, endbuf - endptr, &endptr, + &full_outlen); + } else { + endptr = endbuf; + query = escape_string(event.general_query.str, event.general_query.length, + endptr, endbuf - endptr, &endptr, &full_outlen); + full_outlen += full_outlen * my_charset_utf8mb4_general_ci.mbmaxlen; + } + + user = escape_string(event.general_user.str, event.general_user.length, + endptr, endbuf - endptr, &endptr, &full_outlen); + host = escape_string(event.general_host.str, event.general_host.length, + endptr, endbuf - endptr, &endptr, &full_outlen); + external_user = escape_string(event.general_external_user.str, + event.general_external_user.length, endptr, + endbuf - endptr, &endptr, &full_outlen); + ip = escape_string(event.general_ip.str, event.general_ip.length, endptr, + endbuf - endptr, &endptr, &full_outlen); + db = escape_string(default_db, strlen(default_db), endptr, endbuf - endptr, + &endptr, &full_outlen); + + buflen_estimated = full_outlen * 2 + + strlen(format_string[static_cast(audit_log_format)]) + + strlen(name) + event.general_sql_command.length + + 20 + /* general_thread_id */ + 20 + /* status */ + MAX_RECORD_ID_SIZE + MAX_TIMESTAMP_SIZE; + if (buflen_estimated > buflen) { + *outlen = buflen_estimated; + return NULL; + } + + *outlen = snprintf(endptr, endbuf - endptr, + format_string[static_cast(audit_log_format)], name, + make_record_id(id_str, sizeof(id_str)), + make_timestamp(timestamp, sizeof(timestamp), t), + event.general_sql_command.str, event.general_thread_id, + status, query, user, host, external_user, ip, db); + + /* make sure that record is not truncated */ + assert(endptr + *outlen <= buf + buflen); + + return endptr; +} + +static char *audit_log_connection_record(char *buf, size_t buflen, + const char *name, time_t t, + const mysql_event_connection &event, + size_t *outlen) noexcept { + char id_str[MAX_RECORD_ID_SIZE]; + char timestamp[MAX_TIMESTAMP_SIZE]; + char *user, *priv_user, *external_user, *proxy_user, *host, *ip, *database; + char *endptr = buf, *endbuf = buf + buflen; + + static const char *format_string[] = { + "\n", + + "\n" + " %s\n" + " %s\n" + " %s\n" + " %lu\n" + " %d\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + "\n", + + "{\"audit_record\":" + "{\"name\":\"%s\"," + "\"record\":\"%s\"," + "\"timestamp\":\"%s\"," + "\"connection_id\":\"%lu\"," + "\"status\":%d," + "\"user\":\"%s\"," + "\"priv_user\":\"%s\"," + "\"os_login\":\"%s\"," + "\"proxy_user\":\"%s\"," + "\"host\":\"%s\"," + "\"ip\":\"%s\"," + "\"db\":\"%s\"}}\n", + + ("\"%s\",\"%s\",\"%s\",\"%lu\",%d,\"%s\",\"%s\",\"%s\"," + "\"%s\",\"%s\",\"%s\",\"%s\"\n")}; + + user = escape_string(event.user.str, event.user.length, endptr, + endbuf - endptr, &endptr, nullptr); + priv_user = escape_string(event.priv_user.str, event.priv_user.length, endptr, + endbuf - endptr, &endptr, nullptr); + external_user = + escape_string(event.external_user.str, event.external_user.length, endptr, + endbuf - endptr, &endptr, nullptr); + proxy_user = escape_string(event.proxy_user.str, event.proxy_user.length, + endptr, endbuf - endptr, &endptr, nullptr); + host = escape_string(event.host.str, event.host.length, endptr, + endbuf - endptr, &endptr, nullptr); + ip = escape_string(event.ip.str, event.ip.length, endptr, endbuf - endptr, + &endptr, nullptr); + database = escape_string(event.database.str, event.database.length, endptr, + endbuf - endptr, &endptr, nullptr); + + assert((endptr - buf) * 2 + + strlen(format_string[static_cast(audit_log_format)]) + + strlen(name) + MAX_RECORD_ID_SIZE + MAX_TIMESTAMP_SIZE + + 20 + /* event.thread_id */ + 20 /* event.status */ + < buflen); + + *outlen = snprintf(endptr, endbuf - endptr, + format_string[static_cast(audit_log_format)], name, + make_record_id(id_str, sizeof(id_str)), + make_timestamp(timestamp, sizeof(timestamp), t), + event.connection_id, event.status, user, priv_user, + external_user, proxy_user, host, ip, database); + + /* make sure that record is not truncated */ + assert(endptr + *outlen <= buf + buflen); + + return endptr; +} + +static size_t audit_log_header(MY_STAT *stat, char *buf, + size_t buflen) noexcept { + static const char *const format_string[5] = { + "\n" + "\n", + "\n" + "\n", + "", ""}; + + assert(strcmp(system_charset_info->csname, "utf8mb3") == 0); + + log_file_time = stat->st_mtime; + + init_record_id(stat->st_size); + + if (buf == nullptr) { + return 0; + } + + return snprintf(buf, buflen, "%s", format_string[audit_log_format]); +} + +static size_t audit_log_footer(char *buf, size_t buflen) noexcept { + static const char *const format_string[] = {"\n", "\n", "", + ""}; + + if (buf == nullptr) { + return 0; + } + + return snprintf(buf, buflen, "%s", format_string[audit_log_format]); +} + +static int init_new_log_file() noexcept { + if (audit_log_handler == audit_log_handler_t::HANDLER_FILE) { + audit_handler_file_config_t opts; + opts.name = audit_log_file; + opts.rotate_on_size = audit_log_rotate_on_size; + opts.rotations = audit_log_rotations; + opts.sync_on_write = + audit_log_strategy == audit_log_strategy_t::SYNCHRONOUS; + opts.use_buffer = + audit_log_strategy < audit_log_strategy_t::SEMISYNCHRONOUS; + opts.buffer_size = audit_log_buffer_size; + opts.can_drop_data = + audit_log_strategy == audit_log_strategy_t::PERFORMANCE; + opts.header = audit_log_header; + opts.footer = audit_log_footer; + + log_handler = audit_handler_file_open(&opts); + if (log_handler == nullptr) { + my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, "Cannot open file %s.", + audit_log_file); + my_plugin_perror(); + return (1); + } + } else { + audit_handler_syslog_config_t opts; + opts.facility = audit_log_syslog_facility_codes[audit_log_syslog_facility]; + opts.ident = audit_log_syslog_ident; + opts.priority = audit_log_syslog_priority_codes[audit_log_syslog_priority]; + opts.header = audit_log_header; + opts.footer = audit_log_footer; + + log_handler = audit_handler_syslog_open(&opts); + if (log_handler == nullptr) { + my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, "Cannot open syslog."); + my_plugin_perror(); + return (1); + } + } + + return (0); +} + +static int reopen_log_file() { + if (audit_handler_flush(log_handler)) { + my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, "Cannot open file %s.", + audit_log_file); + my_plugin_perror(); + return (1); + } + + return (0); +} + +struct query_stack_frame { + /* number of included databases */ + int databases_included; + /* number of excluded databases */ + int databases_excluded; + /* number of accessed databases */ + int databases_accessed; + /* query */ + const char *query; +}; + +struct query_stack { + size_t size; + size_t top; + query_stack_frame *frames; +}; + +/* + Struct to store various THD specific data + */ +struct audit_log_thd_local { + /* size of allocated large buffer for record formatting */ + size_t record_buffer_size; + /* large buffer for record formatting */ + char *record_buffer; + /* skip session logging */ + bool skip_session; + /* skip logging for the next query */ + bool skip_query; + /* default database */ + char db[NAME_LEN + 1]; + /* default database candidate */ + char init_db_query[NAME_LEN + 1]; + /* call stack */ + query_stack stack; +}; + +/* + Return pointer to THD specific data. + */ +static audit_log_thd_local *get_thd_local(MYSQL_THD thd) noexcept; + +/* + Allocate and return buffer of given size. + */ +static char *get_record_buffer(MYSQL_THD thd, size_t size) noexcept; + +/* + Allocate and return given number of stack frames. + */ +static query_stack_frame *realloc_stack_frames(MYSQL_THD thd, + size_t size) noexcept; + +static int audit_log_plugin_init(MYSQL_PLUGIN plugin_info) { + char buf[1024]; + size_t len; + int count; + + plugin_ptr = plugin_info; + + count = array_elements(all_audit_log_memory); + mysql_memory_register(AUDIT_LOG_PSI_CATEGORY, all_audit_log_memory, count); + logger_init_mutexes(); + + audit_log_filter_init(); + + if (audit_log_exclude_accounts != nullptr && + audit_log_include_accounts != nullptr) { + my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, + "Both 'audit_log_exclude_accounts' and " + "'audit_log_include_accounts' are not NULL\n"); + goto validation_error; + } + + if (audit_log_exclude_commands != nullptr && + audit_log_include_commands != nullptr) { + my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, + "Both 'audit_log_exclude_commands' and " + "'audit_log_include_commands' are not NULL\n"); + goto validation_error; + } + + if (audit_log_exclude_databases != nullptr && + audit_log_include_databases != nullptr) { + my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, + "Both 'audit_log_exclude_databases' and " + "'audit_log_include_databases' are not NULL\n"); + goto validation_error; + } + + if (audit_log_exclude_accounts != nullptr) { + audit_log_exclude_accounts = my_strdup( + PSI_NOT_INSTRUMENTED, audit_log_exclude_accounts, MYF(MY_FAE)); + audit_log_set_exclude_accounts(audit_log_exclude_accounts); + } + if (audit_log_include_accounts != nullptr) { + audit_log_include_accounts = my_strdup( + PSI_NOT_INSTRUMENTED, audit_log_include_accounts, MYF(MY_FAE)); + audit_log_set_include_accounts(audit_log_include_accounts); + } + if (audit_log_exclude_commands != nullptr) { + audit_log_exclude_commands = my_strdup( + PSI_NOT_INSTRUMENTED, audit_log_exclude_commands, MYF(MY_FAE)); + audit_log_set_exclude_commands(audit_log_exclude_commands); + } + if (audit_log_include_commands != nullptr) { + audit_log_include_commands = my_strdup( + PSI_NOT_INSTRUMENTED, audit_log_include_commands, MYF(MY_FAE)); + audit_log_set_include_commands(audit_log_include_commands); + } + if (audit_log_exclude_databases != nullptr) { + audit_log_exclude_databases = my_strdup( + PSI_NOT_INSTRUMENTED, audit_log_exclude_databases, MYF(MY_FAE)); + audit_log_set_exclude_databases(audit_log_exclude_databases); + } + if (audit_log_include_databases != nullptr) { + audit_log_include_databases = my_strdup( + PSI_NOT_INSTRUMENTED, audit_log_include_databases, MYF(MY_FAE)); + audit_log_set_include_databases(audit_log_include_databases); + } + + if (init_new_log_file()) return (1); + + if (audit_log_audit_record(buf, sizeof(buf), "Audit", time(nullptr), &len)) + audit_log_write(buf, len); + + return 0; + +validation_error: + + audit_log_exclude_accounts = audit_log_include_accounts = nullptr; + audit_log_exclude_commands = audit_log_include_commands = nullptr; + audit_log_exclude_databases = audit_log_include_databases = nullptr; + + return 1; +} + +static int audit_log_plugin_deinit(void *arg [[maybe_unused]]) { + char buf[1024]; + size_t len; + + if (audit_log_audit_record(buf, sizeof(buf), "NoAudit", time(nullptr), &len)) + audit_log_write(buf, len); + + audit_handler_close(log_handler); + + audit_log_filter_destroy(); + + my_free(audit_log_include_accounts); + my_free(audit_log_exclude_accounts); + + my_free(audit_log_include_databases); + my_free(audit_log_exclude_databases); + + my_free(audit_log_include_commands); + my_free(audit_log_exclude_commands); + + return (0); +} + +static bool is_event_class_allowed_by_policy(mysql_event_class_t event_class, + ulong policy) noexcept { + static const unsigned int class_mask[] = { + /* ALL */ + (1 << MYSQL_AUDIT_GENERAL_CLASS) | (1 << MYSQL_AUDIT_CONNECTION_CLASS), + 0, /* NONE */ + (1 << MYSQL_AUDIT_CONNECTION_CLASS), /* LOGINS */ + (1 << MYSQL_AUDIT_GENERAL_CLASS), /* QUERIES */ + }; + + return (class_mask[policy] & (1 << event_class)) != 0; +} + +static const char *next_word(const char *str, size_t *len, + const CHARSET_INFO *charset) noexcept { + while (*str && my_isspace(charset, *str)) { + if (*str == '/' && str[1] == '*' && str[2] == '!') + str += 3; + else if (*str == '/' && str[1] == '*') { + while (*str && !(*str == '*' && str[1] == '/')) str++; + } else + str++; + } + + *len = 0; + while (str[*len] && my_isvar(charset, str[*len])) (*len)++; + + if (*len == 0 && *str == '`') { + (*len)++; + while (str[*len]) { + if (str[*len] == '`' && str[*len + 1] == '`') + (*len)++; + else if (str[*len] == '`') + break; + (*len)++; + } + (*len)++; + } + + return str; +} + +static bool audit_log_update_thd_local(MYSQL_THD thd, + audit_log_thd_local *local, + unsigned int event_class, + const void *event) { + assert(audit_log_include_accounts == nullptr || + audit_log_exclude_accounts == nullptr); + + assert(audit_log_include_databases == nullptr || + audit_log_exclude_databases == nullptr); + + assert(audit_log_include_commands == nullptr || + audit_log_exclude_commands == nullptr); + + if (event_class == MYSQL_AUDIT_CONNECTION_CLASS) { + const mysql_event_connection *event_connection = + (const mysql_event_connection *)event; + LEX_STRING priv_user, priv_host; + MYSQL_SECURITY_CONTEXT ctx; + + if (thd_get_security_context(thd, &ctx)) { + my_message(ER_AUDIT_API_ABORT, "Error: can not get security context", + MYF(0)); + return false; + } + + if (security_context_get_option(ctx, "priv_user", &priv_user)) { + my_message(ER_AUDIT_API_ABORT, + "Error: can not get priv_user from " + "security context", + MYF(0)); + return false; + } + + if (security_context_get_option(ctx, "priv_host", &priv_host)) { + my_message(ER_AUDIT_API_ABORT, + "Error: can not get priv_host from " + "security context", + MYF(0)); + return false; + } + + local->skip_session = false; + if (audit_log_include_accounts != nullptr && + !audit_log_check_account_included(priv_user.str, priv_user.length, + priv_host.str, priv_host.length)) + local->skip_session = true; + if (audit_log_exclude_accounts != nullptr && + audit_log_check_account_excluded(priv_user.str, priv_user.length, + priv_host.str, priv_host.length)) + local->skip_session = true; + + if (event_connection->status == 0) { + /* track default DB change */ + assert(event_connection->database.length <= sizeof(local->db)); + if (event_connection->database.str != nullptr) + memcpy(local->db, event_connection->database.str, + event_connection->database.length); + local->db[event_connection->database.length] = 0; + } + } else if (event_class == MYSQL_AUDIT_GENERAL_CLASS) { + const mysql_event_general *event_general = + (const mysql_event_general *)event; + + if (event_general->event_subclass == MYSQL_AUDIT_GENERAL_STATUS) { + local->skip_query = false; + + if (local->stack.frames[local->stack.top].query == + event_general->general_query.str) { + local->skip_query |= + audit_log_include_databases && + local->stack.frames[local->stack.top].databases_accessed > 0 && + local->stack.frames[local->stack.top].databases_included == 0; + + local->skip_query |= + audit_log_exclude_databases && + local->stack.frames[local->stack.top].databases_accessed > 0 && + local->stack.frames[local->stack.top].databases_excluded == + local->stack.frames[local->stack.top].databases_accessed; + + local->stack.frames[local->stack.top].databases_included = 0; + local->stack.frames[local->stack.top].databases_accessed = 0; + local->stack.frames[local->stack.top].databases_excluded = 0; + local->stack.frames[local->stack.top].query = nullptr; + + if (local->stack.top > 0) --local->stack.top; + } + + local->skip_query |= audit_log_include_commands && + !audit_log_check_command_included( + event_general->general_sql_command.str, + event_general->general_sql_command.length); + + local->skip_query |= audit_log_exclude_commands && + audit_log_check_command_excluded( + event_general->general_sql_command.str, + event_general->general_sql_command.length); + + if (!local->skip_query && + ((event_general->general_command.length == 4 && + strncmp(event_general->general_command.str, "Quit", 4) == 0) || + (event_general->general_command.length == 11 && + strncmp(event_general->general_command.str, "Change user", 11) == + 0))) + local->skip_query = true; + } + + if (event_general->event_subclass == MYSQL_AUDIT_GENERAL_LOG && + event_general->general_command.length == 7 && + strncmp(event_general->general_command.str, "Init DB", 7) == 0 && + event_general->general_query.str != nullptr && + strpbrk("\n\r\t ", event_general->general_query.str) == nullptr) { + /* Database is about to be changed. Server doesn't provide database + name in STATUS event, so remember it now. */ + + assert(event_general->general_query.length <= sizeof(local->db)); + memcpy(local->db, event_general->general_query.str, + event_general->general_query.length); + local->db[event_general->general_query.length] = 0; + } + if (event_general->event_subclass == MYSQL_AUDIT_GENERAL_STATUS && + event_general->general_sql_command.length == 9 && + strncmp(event_general->general_sql_command.str, "change_db", 9) == 0 && + event_general->general_command.length == 5 && + strncmp(event_general->general_command.str, "Query", 5) == 0 && + event_general->general_error_code == 0) { + /* it's "use dbname" query */ + + size_t len; + const char *word; + + word = next_word(event_general->general_query.str, &len, + event_general->general_charset); + if (strncasecmp("use", word, len) == 0) { + uint errors; + + word = next_word(word + len, &len, event_general->general_charset); + if (*word == '`') { + word++; + len -= 2; + } + len = my_convert(local->db, sizeof(local->db) - 1, system_charset_info, + word, len, event_general->general_charset, &errors); + local->db[len] = 0; + } + } + } else if (event_class == MYSQL_AUDIT_TABLE_ACCESS_CLASS) { + const mysql_event_table_access *event_table = + (const mysql_event_table_access *)event; + + if (local->stack.frames[local->stack.top].query != event_table->query.str && + local->stack.frames[local->stack.top].query != nullptr) { + if (++local->stack.top >= local->stack.size) + realloc_stack_frames(thd, local->stack.size * 2); + } + local->stack.frames[local->stack.top].query = event_table->query.str; + + ++local->stack.frames[local->stack.top].databases_accessed; + + if (audit_log_include_databases != nullptr && + audit_log_check_database_included(event_table->table_database.str, + event_table->table_database.length)) + ++local->stack.frames[local->stack.top].databases_included; + + if (audit_log_exclude_databases != nullptr && + audit_log_check_database_excluded(event_table->table_database.str, + event_table->table_database.length)) + ++local->stack.frames[local->stack.top].databases_excluded; + } + return true; +} + +static int audit_log_notify(MYSQL_THD thd, mysql_event_class_t event_class, + const void *event) { + audit_log_thd_local *local = get_thd_local(thd); + if (!audit_log_update_thd_local(thd, local, event_class, event)) return 1; + + if (!is_event_class_allowed_by_policy(event_class, audit_log_policy)) + return 0; + + if (local->skip_session) return 0; + + char buf[4096]; + char *log_rec; + size_t len, buflen; + if (event_class == MYSQL_AUDIT_GENERAL_CLASS) { + const mysql_event_general *event_general = + (const mysql_event_general *)event; + switch (event_general->event_subclass) { + case MYSQL_AUDIT_GENERAL_STATUS: { + if (local->skip_query) break; + + /* use allocated buffer if available */ + char *allocated_buf = get_record_buffer(thd, 0); + if (allocated_buf != nullptr) { + log_rec = allocated_buf; + buflen = local->record_buffer_size; + } else { + log_rec = buf; + buflen = sizeof(buf); + } + log_rec = audit_log_general_record( + log_rec, buflen, event_general->general_command.str, + event_general->general_time, event_general->general_error_code, + *event_general, local->db, &len); + if (len > buflen) { + buflen = len * 4; + log_rec = audit_log_general_record( + get_record_buffer(thd, buflen), buflen, + event_general->general_command.str, event_general->general_time, + event_general->general_error_code, *event_general, local->db, + &len); + } + if (log_rec) audit_log_write(log_rec, len); + break; + } + case MYSQL_AUDIT_GENERAL_LOG: + case MYSQL_AUDIT_GENERAL_ERROR: + case MYSQL_AUDIT_GENERAL_RESULT: + break; + } + } else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS) { + const mysql_event_connection *event_connection = + (const mysql_event_connection *)event; + switch (event_connection->event_subclass) { + case MYSQL_AUDIT_CONNECTION_CONNECT: + log_rec = + audit_log_connection_record(buf, sizeof(buf), "Connect", + time(nullptr), *event_connection, &len); + break; + case MYSQL_AUDIT_CONNECTION_DISCONNECT: + log_rec = audit_log_connection_record( + buf, sizeof(buf), "Quit", time(nullptr), *event_connection, &len); + break; + case MYSQL_AUDIT_CONNECTION_CHANGE_USER: + log_rec = + audit_log_connection_record(buf, sizeof(buf), "Change user", + time(nullptr), *event_connection, &len); + break; + default: + log_rec = nullptr; + len = 0; + break; + } + if (log_rec) audit_log_write(log_rec, len); + } + return 0; +} + +/* + * Plugin system vars + */ + +static MYSQL_SYSVAR_STR(file, audit_log_file, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY | + PLUGIN_VAR_MEMALLOC, + "The name of the log file.", nullptr, nullptr, + default_audit_log_file); + +static const char *audit_log_policy_names[] = {"ALL", "NONE", "LOGINS", + "QUERIES", nullptr}; + +static TYPELIB audit_log_policy_typelib = { + array_elements(audit_log_policy_names) - 1, "audit_log_policy_typelib", + audit_log_policy_names, nullptr}; + +static MYSQL_SYSVAR_ENUM( + policy, audit_log_policy, PLUGIN_VAR_RQCMDARG, + "The policy controlling the information written by the audit log " + "plugin to its log file.", + NULL, NULL, audit_log_policy_t::ALL, &audit_log_policy_typelib); + +static const char *audit_log_strategy_names[] = { + "ASYNCHRONOUS", "PERFORMANCE", "SEMISYNCHRONOUS", "SYNCHRONOUS", nullptr}; +static TYPELIB audit_log_strategy_typelib = { + array_elements(audit_log_strategy_names) - 1, "audit_log_strategy_typelib", + audit_log_strategy_names, nullptr}; + +static MYSQL_SYSVAR_ENUM(strategy, audit_log_strategy, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "The logging method used by the audit log plugin, " + "if FILE handler is used.", + nullptr, nullptr, ASYNCHRONOUS, + &audit_log_strategy_typelib); + +static const char *audit_log_format_names[] = {"OLD", "NEW", "JSON", "CSV", + nullptr}; +static TYPELIB audit_log_format_typelib = { + array_elements(audit_log_format_names) - 1, "audit_log_format_typelib", + audit_log_format_names, nullptr}; + +static MYSQL_SYSVAR_ENUM(format, audit_log_format, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "The audit log file format.", nullptr, nullptr, + ASYNCHRONOUS, &audit_log_format_typelib); + +static const char *audit_log_handler_names[] = {"FILE", "SYSLOG", nullptr}; +static TYPELIB audit_log_handler_typelib = { + array_elements(audit_log_handler_names) - 1, "audit_log_handler_typelib", + audit_log_handler_names, nullptr}; + +static MYSQL_SYSVAR_ENUM(handler, audit_log_handler, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "The audit log handler.", nullptr, nullptr, + HANDLER_FILE, &audit_log_handler_typelib); + +static MYSQL_SYSVAR_ULONGLONG( + buffer_size, audit_log_buffer_size, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "The size of the buffer for asynchronous logging, " + "if FILE handler is used.", + NULL, NULL, 1048576UL, 4096UL, ULLONG_MAX, 4096UL); + +static void audit_log_rotate_on_size_update( + MYSQL_THD thd [[maybe_unused]], SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], const void *save) noexcept { + ulonglong new_val = *(const ulonglong *)(save); + + audit_handler_set_option(log_handler, audit_handler_option_t::ROTATE_ON_SIZE, + &new_val); + + audit_log_rotate_on_size = new_val; +} + +static MYSQL_SYSVAR_ULONGLONG( + rotate_on_size, audit_log_rotate_on_size, PLUGIN_VAR_RQCMDARG, + "Maximum size of the log to start the rotation, if FILE handler is used.", + NULL, audit_log_rotate_on_size_update, 0UL, 0UL, ULLONG_MAX, 4096UL); + +static void audit_log_rotations_update(MYSQL_THD thd [[maybe_unused]], + SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], + const void *save) noexcept { + ulonglong new_val = *(const ulonglong *)(save); + + audit_handler_set_option(log_handler, audit_handler_option_t::ROTATIONS, + &new_val); + + audit_log_rotations = new_val; +} + +static MYSQL_SYSVAR_ULONGLONG( + rotations, audit_log_rotations, PLUGIN_VAR_RQCMDARG, + "Maximum number of rotations to keep, if FILE handler is used.", NULL, + audit_log_rotations_update, 0UL, 0UL, 999UL, 1UL); + +static void audit_log_flush_update(MYSQL_THD thd [[maybe_unused]], + SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], + const void *save) { + char new_val = *(const char *)(save); + + if (new_val != audit_log_flush && new_val) { + audit_log_flush = true; + reopen_log_file(); + audit_log_flush = false; + } +} + +static MYSQL_SYSVAR_BOOL(flush, audit_log_flush, PLUGIN_VAR_OPCMDARG, + "Flush the log file.", nullptr, audit_log_flush_update, + false); + +static MYSQL_SYSVAR_STR( + syslog_ident, audit_log_syslog_ident, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC, + "The string that will be prepended to each log message, " + "if SYSLOG handler is used.", + nullptr, nullptr, default_audit_log_syslog_ident); + +static TYPELIB audit_log_syslog_facility_typelib = { + array_elements(audit_log_syslog_facility_names) - 1, + "audit_log_syslog_facility_typelib", audit_log_syslog_facility_names, + nullptr}; + +static MYSQL_SYSVAR_ENUM( + syslog_facility, audit_log_syslog_facility, PLUGIN_VAR_RQCMDARG, + "The syslog facility to assign to messages, if SYSLOG handler is used.", + nullptr, nullptr, 0, &audit_log_syslog_facility_typelib); + +static TYPELIB audit_log_syslog_priority_typelib = { + array_elements(audit_log_syslog_priority_names) - 1, + "audit_log_syslog_priority_typelib", audit_log_syslog_priority_names, + nullptr}; + +static MYSQL_SYSVAR_ENUM( + syslog_priority, audit_log_syslog_priority, PLUGIN_VAR_RQCMDARG, + "Priority to be assigned to all messages written to syslog.", nullptr, + nullptr, 0, &audit_log_syslog_priority_typelib); + +static MYSQL_THDVAR_STR(record_buffer, + PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC | + PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT, + "Buffer for query formatting.", nullptr, nullptr, ""); + +static MYSQL_THDVAR_STR(query_stack, + PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC | + PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT, + "Query stack.", nullptr, nullptr, ""); + +static const char *val_strmake(MYSQL_THD thd, + struct st_mysql_value *mysql_val) { + char buf[STRING_BUFFER_USUAL_SIZE]; + int len = sizeof(buf); + const char *val = mysql_val->val_str(mysql_val, buf, &len); + + if (val != NULL) val = thd_strmake(thd, val, len); + + return val; +} + +static int audit_log_exclude_accounts_validate(MYSQL_THD thd, + SYS_VAR *var [[maybe_unused]], + void *save, + st_mysql_value *value) { + if (audit_log_include_accounts) return 1; + + *(const char **)(save) = val_strmake(thd, value); + + return 0; +} + +static void audit_log_exclude_accounts_update( + MYSQL_THD thd [[maybe_unused]], SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], const void *save) { + const char *new_val = *(const char * const*)(save); + + assert(audit_log_include_accounts == nullptr); + + my_free(audit_log_exclude_accounts); + audit_log_exclude_accounts = nullptr; + + if (new_val != nullptr) { + audit_log_exclude_accounts = + my_strdup(PSI_NOT_INSTRUMENTED, new_val, MYF(MY_FAE)); + audit_log_set_exclude_accounts(audit_log_exclude_accounts); + } else { + audit_log_set_exclude_accounts(""); + } +} + +static MYSQL_SYSVAR_STR(exclude_accounts, audit_log_exclude_accounts, + PLUGIN_VAR_RQCMDARG, + "Comma separated list of accounts " + "for which events should not be logged.", + audit_log_exclude_accounts_validate, + audit_log_exclude_accounts_update, nullptr); + +static int audit_log_include_accounts_validate(MYSQL_THD thd, + SYS_VAR *var [[maybe_unused]], + void *save, + st_mysql_value *value) { + if (audit_log_exclude_accounts) return 1; + + *(const char **)(save) = val_strmake(thd, value); + + return 0; +} + +static void audit_log_include_accounts_update( + MYSQL_THD thd [[maybe_unused]], SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], const void *save) { + const char *new_val = *(const char * const*)(save); + + assert(audit_log_exclude_accounts == nullptr); + + my_free(audit_log_include_accounts); + audit_log_include_accounts = nullptr; + + if (new_val != nullptr) { + audit_log_include_accounts = + my_strdup(PSI_NOT_INSTRUMENTED, new_val, MYF(MY_FAE)); + audit_log_set_include_accounts(audit_log_include_accounts); + } else { + audit_log_set_include_accounts(""); + } +} + +static MYSQL_SYSVAR_STR( + include_accounts, audit_log_include_accounts, PLUGIN_VAR_RQCMDARG, + "Comma separated list of accounts for which events should be logged.", + audit_log_include_accounts_validate, audit_log_include_accounts_update, + nullptr); + +static int audit_log_exclude_databases_validate(MYSQL_THD thd, + SYS_VAR *var [[maybe_unused]], + void *save, + st_mysql_value *value) { + if (audit_log_include_databases) return 1; + + *(const char **)(save) = val_strmake(thd, value); + + return 0; +} + +static void audit_log_exclude_databases_update( + MYSQL_THD thd [[maybe_unused]], SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], const void *save) { + const char *new_val = *(const char * const*)(save); + + assert(audit_log_include_databases == nullptr); + + my_free(audit_log_exclude_databases); + audit_log_exclude_databases = nullptr; + + if (new_val != nullptr) { + audit_log_exclude_databases = + my_strdup(PSI_NOT_INSTRUMENTED, new_val, MYF(MY_FAE)); + audit_log_set_exclude_databases(audit_log_exclude_databases); + } else { + audit_log_set_exclude_databases(""); + } +} + +static MYSQL_SYSVAR_STR(exclude_databases, audit_log_exclude_databases, + PLUGIN_VAR_RQCMDARG, + "Comma separated list of databases " + "for which events should not be logged.", + audit_log_exclude_databases_validate, + audit_log_exclude_databases_update, nullptr); + +static int audit_log_include_databases_validate(MYSQL_THD thd, + SYS_VAR *var [[maybe_unused]], + void *save, + st_mysql_value *value) { + if (audit_log_exclude_databases) return 1; + + *(const char **)(save) = val_strmake(thd, value); + + return 0; +} + +static void audit_log_include_databases_update( + MYSQL_THD thd [[maybe_unused]], SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], const void *save) { + const char *new_val = *(const char * const*)(save); + + assert(audit_log_exclude_databases == nullptr); + + my_free(audit_log_include_databases); + audit_log_include_databases = nullptr; + + if (new_val != nullptr) { + audit_log_include_databases = + my_strdup(PSI_NOT_INSTRUMENTED, new_val, MYF(MY_FAE)); + audit_log_set_include_databases(audit_log_include_databases); + } else { + audit_log_set_include_databases(""); + } +} + +static MYSQL_SYSVAR_STR( + include_databases, audit_log_include_databases, PLUGIN_VAR_RQCMDARG, + "Comma separated list of databases for which events should be logged.", + audit_log_include_databases_validate, audit_log_include_databases_update, + nullptr); + +static int audit_log_exclude_commands_validate(MYSQL_THD thd, + SYS_VAR *var [[maybe_unused]], + void *save, + st_mysql_value *value) { + if (audit_log_include_commands) return 1; + + *(const char **)(save) = val_strmake(thd, value); + + return 0; +} + +static void audit_log_exclude_commands_update( + MYSQL_THD thd [[maybe_unused]], SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], const void *save) { + const char *new_val = *(const char * const*)(save); + + assert(audit_log_include_commands == nullptr); + + my_free(audit_log_exclude_commands); + audit_log_exclude_commands = nullptr; + + if (new_val != nullptr) { + audit_log_exclude_commands = + my_strdup(PSI_NOT_INSTRUMENTED, new_val, MYF(MY_FAE)); + audit_log_set_exclude_commands(audit_log_exclude_commands); + } else { + audit_log_set_exclude_commands(""); + } +} + +static MYSQL_SYSVAR_STR(exclude_commands, audit_log_exclude_commands, + PLUGIN_VAR_RQCMDARG, + "Comma separated list of commands " + "for which events should not be logged.", + audit_log_exclude_commands_validate, + audit_log_exclude_commands_update, nullptr); + +static int audit_log_include_commands_validate(MYSQL_THD thd, + SYS_VAR *var [[maybe_unused]], + void *save, + st_mysql_value *value) { + if (audit_log_exclude_commands) return 1; + + *(const char **)(save) = val_strmake(thd, value); + + return 0; +} + +static void audit_log_include_commands_update( + MYSQL_THD thd [[maybe_unused]], SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], const void *save) { + const char *new_val = *(const char * const*)(save); + + assert(audit_log_exclude_commands == nullptr); + + my_free(audit_log_include_commands); + audit_log_include_commands = nullptr; + + if (new_val != nullptr) { + audit_log_include_commands = + my_strdup(PSI_NOT_INSTRUMENTED, new_val, MYF(MY_FAE)); + audit_log_set_include_commands(audit_log_include_commands); + } else { + audit_log_set_include_commands(""); + } +} + +static MYSQL_SYSVAR_STR( + include_commands, audit_log_include_commands, PLUGIN_VAR_RQCMDARG, + "Comma separated list of commands for which events should be logged.", + audit_log_include_commands_validate, audit_log_include_commands_update, + nullptr); + +static MYSQL_THDVAR_STR(local, + PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC | + PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT, + "Local store.", nullptr, nullptr, ""); + +static MYSQL_THDVAR_ULONG(local_ptr, + PLUGIN_VAR_READONLY | PLUGIN_VAR_NOSYSVAR | + PLUGIN_VAR_NOCMDOPT, + "Local store ptr.", nullptr, nullptr, 0, 0, ULONG_MAX, + 0); + +static SYS_VAR *audit_log_system_variables[] = {MYSQL_SYSVAR(file), + MYSQL_SYSVAR(policy), + MYSQL_SYSVAR(strategy), + MYSQL_SYSVAR(format), + MYSQL_SYSVAR(buffer_size), + MYSQL_SYSVAR(rotate_on_size), + MYSQL_SYSVAR(rotations), + MYSQL_SYSVAR(flush), + MYSQL_SYSVAR(handler), + MYSQL_SYSVAR(syslog_ident), + MYSQL_SYSVAR(syslog_priority), + MYSQL_SYSVAR(syslog_facility), + MYSQL_SYSVAR(record_buffer), + MYSQL_SYSVAR(query_stack), + MYSQL_SYSVAR(exclude_accounts), + MYSQL_SYSVAR(include_accounts), + MYSQL_SYSVAR(exclude_databases), + MYSQL_SYSVAR(include_databases), + MYSQL_SYSVAR(exclude_commands), + MYSQL_SYSVAR(include_commands), + MYSQL_SYSVAR(local), + MYSQL_SYSVAR(local_ptr), + NULL}; + +static char thd_local_init_buf[sizeof(audit_log_thd_local)]; + +void MY_ATTRIBUTE((constructor)) audit_log_so_init() noexcept { + memset(thd_local_init_buf, 1, sizeof(thd_local_init_buf) - 1); + thd_local_init_buf[sizeof(thd_local_init_buf) - 1] = 0; +} + +/* + Return pointer to THD specific data. + */ +static audit_log_thd_local *get_thd_local(MYSQL_THD thd) noexcept { + audit_log_thd_local *local = (audit_log_thd_local *)THDVAR(thd, local_ptr); + + static_assert(sizeof(THDVAR(thd, local_ptr)) >= sizeof(void *), + "THD::local_ptr must be at least pointer-big"); + + if (unlikely(local == nullptr)) { + THDVAR_SET(thd, local, thd_local_init_buf); + local = (audit_log_thd_local *)THDVAR(thd, local); + memset(local, 0, sizeof(audit_log_thd_local)); + THDVAR(thd, local_ptr) = (ulong)local; + + realloc_stack_frames(thd, 4); + } + return local; +} + +/* + Allocate and return buffer of given size. + */ +static char *get_record_buffer(MYSQL_THD thd, size_t size) noexcept { + audit_log_thd_local *local = get_thd_local(thd); + char *buf = local->record_buffer; + + if (local->record_buffer_size < size) { + local->record_buffer_size = size; + + buf = (char *)my_malloc(PSI_NOT_INSTRUMENTED, size, MYF(MY_FAE)); + memset(buf, 1, size - 1); + buf[size - 1] = 0; + + THDVAR_SET(thd, record_buffer, buf); + + my_free(buf); + + buf = (char *)THDVAR(thd, record_buffer); + local->record_buffer = buf; + } + + return buf; +} + +/* + Allocate and return given number of stack frames. + */ +static query_stack_frame *realloc_stack_frames(MYSQL_THD thd, + size_t size) noexcept { + audit_log_thd_local *local = get_thd_local(thd); + query_stack_frame *stack = (query_stack_frame *)THDVAR(thd, query_stack); + + if (local->stack.size < size) { + char *buf = (char *)my_malloc( + PSI_NOT_INSTRUMENTED, + (local->stack.size + size) * sizeof(query_stack_frame), MYF(MY_FAE)); + memset(buf + local->stack.size * sizeof(query_stack_frame), 1, + size * sizeof(query_stack_frame) - 1); + buf[(local->stack.size + size) * sizeof(query_stack_frame) - 1] = 0; + if (local->stack.size > 0) + memcpy(buf, stack, local->stack.size * sizeof(query_stack_frame)); + THDVAR_SET(thd, query_stack, + buf + local->stack.size * sizeof(query_stack_frame)); + stack = (query_stack_frame *)THDVAR(thd, query_stack); + memset(stack, 0, size * sizeof(query_stack_frame)); + if (local->stack.size > 0) + memcpy(stack, buf, local->stack.size * sizeof(query_stack_frame)); + local->stack.frames = stack; + local->stack.size = size; + my_free(buf); + } + + return stack; +} + +/* + Plugin type-specific descriptor +*/ +static st_mysql_audit audit_log_descriptor = { + MYSQL_AUDIT_INTERFACE_VERSION, /* interface version */ + nullptr, /* release_thd function */ + audit_log_notify, /* notify function */ + {MYSQL_AUDIT_GENERAL_ALL, MYSQL_AUDIT_CONNECTION_ALL, 0, 0, + MYSQL_AUDIT_TABLE_ACCESS_ALL, 0, 0, 0, 0, 0} /* class mask */ +}; + +/* + Plugin status variables for SHOW STATUS +*/ + +static SHOW_VAR audit_log_status_variables[] = { + {NullS, NullS, SHOW_LONG, SHOW_SCOPE_GLOBAL}}; + +/* + Plugin library descriptor +*/ + +mysql_declare_plugin(audit_log){ + MYSQL_AUDIT_PLUGIN, /* type */ + &audit_log_descriptor, /* descriptor */ + "audit_log", /* name */ + "Percona LLC and/or its affiliates.", /* author */ + "Audit log", /* description */ + PLUGIN_LICENSE_GPL, + audit_log_plugin_init, /* init function (when loaded) */ + nullptr, + audit_log_plugin_deinit, /* deinit function (when unloaded) */ + PLUGIN_VERSION, /* version */ + audit_log_status_variables, /* status variables */ + audit_log_system_variables, /* system variables */ + nullptr, + 0, +} mysql_declare_plugin_end; diff --git a/plugin/audit_log/audit_log.h b/plugin/audit_log/audit_log.h new file mode 100644 index 000000000000..c952b6751cb8 --- /dev/null +++ b/plugin/audit_log/audit_log.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2015-2016 Percona LLC 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 + as published by the Free Software Foundation; version 2 of + the License. + + 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 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 AUDIT_LOG_H_INCLUDED +#define AUDIT_LOG_H_INCLUDED + +#include "mysql/psi/psi_memory.h" + +extern PSI_memory_key key_memory_audit_log_logger_handle; +extern PSI_memory_key key_memory_audit_log_handler; +extern PSI_memory_key key_memory_audit_log_buffer; +extern PSI_memory_key key_memory_audit_log_accounts; +extern PSI_memory_key key_memory_audit_log_databases; +extern PSI_memory_key key_memory_audit_log_commands; + +static const constexpr auto AUDIT_LOG_PSI_CATEGORY = "audit_log"; + +#endif /* AUDIT_LOG_H_INCLUDED */ diff --git a/plugin/audit_log/audit_syslog.cc b/plugin/audit_log/audit_syslog.cc new file mode 100644 index 000000000000..8693aa46ec17 --- /dev/null +++ b/plugin/audit_log/audit_syslog.cc @@ -0,0 +1,89 @@ +/* Copyright (c) 2014 Percona LLC 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 + as published by the Free Software Foundation; version 2 of + the License. + + 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 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 +#include +#include "audit_handler.h" +#include "audit_log.h" +#include "my_dbug.h" +#include "my_sys.h" +#include "mysql/service_mysql_alloc.h" + +struct audit_handler_syslog_data_t { + size_t struct_size; + int priority; + logger_prolog_func_t header; + logger_epilog_func_t footer; +}; + +int audit_handler_syslog_write(audit_handler_t *handler, const char *buf, + size_t len); +int audit_handler_syslog_flush(audit_handler_t *handler); +int audit_handler_syslog_close(audit_handler_t *handler); + +audit_handler_t *audit_handler_syslog_open( + audit_handler_syslog_config_t *opts) noexcept { + audit_handler_t *handler = (audit_handler_t *)my_malloc( + key_memory_audit_log_handler, + sizeof(audit_handler_t) + sizeof(audit_handler_syslog_data_t), + MY_ZEROFILL); + if (handler != nullptr) { + audit_handler_syslog_data_t *data = + (audit_handler_syslog_data_t *)(handler + 1); + + data->struct_size = sizeof(audit_handler_syslog_data_t); + data->priority = opts->priority; + data->header = opts->header; + data->footer = opts->footer; + openlog(opts->ident, 0, opts->facility); + MY_STAT stat_arg; + memset(&stat_arg, 0, sizeof(stat_arg)); + opts->header(&stat_arg, nullptr, 0); + handler->data = data; + handler->write = audit_handler_syslog_write; + handler->flush = audit_handler_syslog_flush; + handler->close = audit_handler_syslog_close; + } + return handler; +} + +int audit_handler_syslog_write(audit_handler_t *handler, const char *buf, + size_t len) { + audit_handler_syslog_data_t *data = + (audit_handler_syslog_data_t *)handler->data; + assert(data->struct_size == sizeof(audit_handler_syslog_data_t)); + syslog(data->priority, "%s", buf); + return len; +} + +int audit_handler_syslog_flush(audit_handler_t *handler) { + audit_handler_syslog_data_t *data = + (audit_handler_syslog_data_t *)handler->data; + MY_STAT stat_arg; + memset(&stat_arg, 0, sizeof(stat_arg)); + data->header(&stat_arg, nullptr, 0); + data->footer(nullptr, 0); + return 0; +} + +int audit_handler_syslog_close(audit_handler_t *handler) { + audit_handler_syslog_data_t *data = + (audit_handler_syslog_data_t *)handler->data; + data->footer(nullptr, 0); + closelog(); + my_free(handler); + return 0; +} diff --git a/plugin/audit_log/buffer.cc b/plugin/audit_log/buffer.cc new file mode 100644 index 000000000000..01fc9b0d4bf6 --- /dev/null +++ b/plugin/audit_log/buffer.cc @@ -0,0 +1,198 @@ +/* Copyright (c) 2014 Percona LLC 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 + as published by the Free Software Foundation; version 2 of + the License. + + 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 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 "buffer.h" + +#include "audit_log.h" +#include "my_dbug.h" +#include "my_sys.h" +#include "my_systime.h" +#include "my_thread.h" +#include "mysql/psi/mysql_cond.h" +#include "mysql/psi/mysql_mutex.h" +#include "mysql/psi/psi_cond.h" +#include "mysql/psi/psi_mutex.h" +#include "mysql/service_mysql_alloc.h" +#include "template_utils.h" +#include "thr_mutex.h" + +struct audit_log_buffer_t { + char *buf; + size_t size; + size_t write_pos; + size_t flush_pos; + pthread_t flush_worker_thread; + bool stop; + bool drop_if_full; + void *write_func_data; + audit_log_write_func write_func; + mysql_mutex_t mutex; + mysql_cond_t flushed_cond; + mysql_cond_t written_cond; + log_record_state_t state; +}; + +#if defined(HAVE_PSI_INTERFACE) +/* These belong to the service initialization */ +static PSI_mutex_key key_log_mutex; +static PSI_mutex_info mutex_key_list[] = { + {&key_log_mutex, "audit_log_buffer::mutex", PSI_FLAG_SINGLETON, 0, + PSI_DOCUMENT_ME}}; + +static PSI_cond_key key_log_written_cond, key_log_flushed_cond; +static PSI_cond_info cond_key_list[] = { + {&key_log_written_cond, "audit_log_buffer::written_cond", + PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}, + {&key_log_flushed_cond, "audit_log_buffer::flushed_cond", + PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}}; + +#endif + +static void audit_log_flush(audit_log_buffer_t *log) { + mysql_mutex_lock(&log->mutex); + while (log->flush_pos == log->write_pos) { + if (log->stop) { + mysql_mutex_unlock(&log->mutex); + return; + } + timespec abstime; + set_timespec(&abstime, 1); + mysql_cond_timedwait(&log->written_cond, &log->mutex, &abstime); + } + + if (log->flush_pos >= log->write_pos % log->size) { + log->state = log_record_state_t::INCOMPLETE; + mysql_mutex_unlock(&log->mutex); + log->write_func(log->write_func_data, log->buf + log->flush_pos, + log->size - log->flush_pos, log_record_state_t::INCOMPLETE); + mysql_mutex_lock(&log->mutex); + log->flush_pos = 0; + log->write_pos %= log->size; + } else { + const size_t flushlen = log->write_pos - log->flush_pos; + mysql_mutex_unlock(&log->mutex); + log->write_func(log->write_func_data, log->buf + log->flush_pos, flushlen, + log_record_state_t::COMPLETE); + mysql_mutex_lock(&log->mutex); + log->flush_pos += flushlen; + log->state = log_record_state_t::COMPLETE; + } + assert(log->write_pos >= log->flush_pos); + mysql_cond_broadcast(&log->flushed_cond); + mysql_mutex_unlock(&log->mutex); +} + +static void *audit_log_flush_worker(void *arg) { + audit_log_buffer_t *log = (audit_log_buffer_t *)arg; + + my_thread_init(); + while (!(log->stop && log->flush_pos == log->write_pos)) { + audit_log_flush(log); + } + my_thread_end(); + + return nullptr; +} + +audit_log_buffer_t *audit_log_buffer_init(size_t size, bool drop_if_full, + audit_log_write_func write_func, + void *data) noexcept { + audit_log_buffer_t *log = (audit_log_buffer_t *)my_malloc( + key_memory_audit_log_buffer, sizeof(audit_log_buffer_t) + size, + MY_ZEROFILL); + +#ifdef HAVE_PSI_INTERFACE + mysql_mutex_register(AUDIT_LOG_PSI_CATEGORY, mutex_key_list, + array_elements(mutex_key_list)); + mysql_cond_register(AUDIT_LOG_PSI_CATEGORY, cond_key_list, + array_elements(cond_key_list)); +#endif /* HAVE_PSI_INTERFACE */ + + if (log != nullptr) { + log->buf = ((char *)log + sizeof(audit_log_buffer_t)); + log->drop_if_full = drop_if_full; + log->write_func = write_func; + log->write_func_data = data; + log->size = size; + log->state = log_record_state_t::COMPLETE; + + mysql_mutex_init(key_log_mutex, &log->mutex, MY_MUTEX_INIT_FAST); + mysql_cond_init(key_log_flushed_cond, &log->flushed_cond); + mysql_cond_init(key_log_written_cond, &log->written_cond); + pthread_create(&log->flush_worker_thread, nullptr, audit_log_flush_worker, + log); + } + + return log; +} + +void audit_log_buffer_shutdown(audit_log_buffer_t *log) noexcept { + log->stop = true; + + pthread_join(log->flush_worker_thread, nullptr); + mysql_cond_destroy(&log->flushed_cond); + mysql_cond_destroy(&log->written_cond); + mysql_mutex_destroy(&log->mutex); + + my_free(log); +} + +void audit_log_buffer_pause(audit_log_buffer_t *log) noexcept { + mysql_mutex_lock(&log->mutex); + while (log->state == log_record_state_t::INCOMPLETE) { + mysql_cond_wait(&log->flushed_cond, &log->mutex); + } +} + +void audit_log_buffer_resume(audit_log_buffer_t *log) noexcept { + mysql_mutex_unlock(&log->mutex); +} + +int audit_log_buffer_write(audit_log_buffer_t *log, const char *buf, + size_t len) { + if (len > log->size) { + if (!log->drop_if_full) { + /* pause flushing thread and write out one record bypassing the buffer */ + audit_log_buffer_pause(log); + log->write_func(log->write_func_data, buf, len, + log_record_state_t::COMPLETE); + audit_log_buffer_resume(log); + } + return (0); + } + + mysql_mutex_lock(&log->mutex); +loop: + if (log->write_pos + len <= log->flush_pos + log->size) { + const size_t wrlen = + std::min(len, log->size - (log->write_pos % log->size)); + memcpy(log->buf + (log->write_pos % log->size), buf, wrlen); + if (wrlen < len) memcpy(log->buf, buf + wrlen, len - wrlen); + log->write_pos = log->write_pos + len; + assert(log->write_pos >= log->flush_pos); + } else { + if (!log->drop_if_full) { + mysql_cond_wait(&log->flushed_cond, &log->mutex); + goto loop; + } + } + if (log->write_pos > log->flush_pos + log->size / 2) { + mysql_cond_signal(&log->written_cond); + } + mysql_mutex_unlock(&log->mutex); + + return (0); +} diff --git a/plugin/audit_log/buffer.h b/plugin/audit_log/buffer.h new file mode 100644 index 000000000000..a59115b3af68 --- /dev/null +++ b/plugin/audit_log/buffer.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2014 Percona LLC 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 + as published by the Free Software Foundation; version 2 of + the License. + + 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 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 AUDIT_LOG_BUFFER_INCLUDED +#define AUDIT_LOG_BUFFER_INCLUDED + +#include +#include "logger.h" + +struct audit_log_buffer_t; + +typedef int (*audit_log_write_func)(void *data, const char *buf, size_t len, + log_record_state_t state); + +audit_log_buffer_t *audit_log_buffer_init(size_t size, bool drop_if_full, + audit_log_write_func write_func, + void *data) noexcept; +void audit_log_buffer_shutdown(audit_log_buffer_t *log) noexcept; +int audit_log_buffer_write(audit_log_buffer_t *log, const char *buf, + size_t len); +void audit_log_buffer_pause(audit_log_buffer_t *log) noexcept; +void audit_log_buffer_resume(audit_log_buffer_t *log) noexcept; + +#endif diff --git a/plugin/audit_log/file_logger.cc b/plugin/audit_log/file_logger.cc new file mode 100644 index 000000000000..5c6d15b06138 --- /dev/null +++ b/plugin/audit_log/file_logger.cc @@ -0,0 +1,296 @@ +/* Copyright (C) 2012 Monty Program Ab + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + 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 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 Street, Fifth Floor, Boston, MA 02110-1301, + USA */ + +#include +#include "my_dir.h" +#include "my_sys.h" +#include "mysql/plugin.h" +#include "sql/mysqld.h" + +#include "audit_log.h" +#include "logger.h" + +#ifndef FLOGGER_NO_PSI +#define flogger_mutex_init(A, B, C) \ + if ((B)->thread_safe) mysql_mutex_init(A, &((B)->lock), C) + +#define flogger_mutex_destroy(A) \ + if ((A)->thread_safe) mysql_mutex_destroy(&((A)->lock)) + +#define flogger_mutex_lock(A) \ + if ((A)->thread_safe) mysql_mutex_lock(&((A)->lock)) + +#define flogger_mutex_unlock(A) \ + if ((A)->thread_safe) mysql_mutex_unlock(&((A)->lock)) +#else +#define flogger_mutex_init(A, B, C) \ + if ((B)->thread_safe) pthread_mutex_init(&((B)->lock.m_mutex), C) + +#define flogger_mutex_destroy(A) \ + if ((A)->thread_safe) pthread_mutex_destroy(&((A)->lock.m_mutex)) + +#define flogger_mutex_lock(A) \ + if ((A)->thread_safe) pthread_mutex_lock(&((A)->lock.m_mutex)) + +#define flogger_mutex_unlock(A) \ + if ((A)->thread_safe) pthread_mutex_unlock(&((A)->lock.m_mutex)) +#endif /*!FLOGGER_NO_PSI*/ + +#if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI) +/* These belong to the service initialization */ +static PSI_mutex_key key_LOCK_logger_service; +static PSI_mutex_info mutex_list[] = { + {&key_LOCK_logger_service, "file_logger::lock", PSI_FLAG_SINGLETON, + PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}}; +#else +#define key_LOCK_logger_service nullptr +#endif /*HAVE_PSI_INTERFACE && !FLOGGER_NO_PSI*/ + +struct LOGGER_HANDLE { + File file; + char path[FN_REFLEN]; + unsigned long long size_limit; + unsigned int rotations; + size_t path_len; + mysql_mutex_t lock; + bool thread_safe; +}; + +#define LOG_FLAGS (O_APPEND | O_CREAT | O_WRONLY) + +static constexpr unsigned int n_dig(unsigned int i) noexcept { + return (i == 0) ? 0 : ((i < 10) ? 1 : ((i < 100) ? 2 : 3)); +} + +LOGGER_HANDLE *logger_open(const char *path, unsigned long long size_limit, + unsigned int rotations, bool thread_safe, + logger_prolog_func_t header) noexcept { + /* + I don't think we ever need more rotations, + but if it's so, the rotation procedure should be adapted to it. + */ + if (rotations > 999) return 0; + + LOGGER_HANDLE new_log; + new_log.rotations = rotations; + new_log.size_limit = size_limit; + new_log.path_len = strlen( + fn_format(new_log.path, path, mysql_data_home, "", MY_UNPACK_FILENAME)); + new_log.thread_safe = thread_safe; + + if (new_log.path_len + n_dig(rotations) + 1 > FN_REFLEN) { + errno = ENAMETOOLONG; + /* File path too long */ + return 0; + } + + if ((new_log.file = my_open(new_log.path, LOG_FLAGS, 0666)) < 0) { + errno = my_errno(); + /* Check errno for the cause */ + return 0; + } + + MY_STAT stat_arg; + if (my_fstat(new_log.file, &stat_arg)) { + errno = my_errno(); + my_close(new_log.file, MYF(0)); + new_log.file = -1; + return 0; + } + + LOGGER_HANDLE *l_perm; + if (!(l_perm = (LOGGER_HANDLE *)my_malloc(key_memory_audit_log_logger_handle, + sizeof(LOGGER_HANDLE), MYF(0)))) { + my_close(new_log.file, MYF(0)); + new_log.file = -1; + return 0; /* End of memory */ + } + *l_perm = new_log; + + flogger_mutex_init(key_LOCK_logger_service, l_perm, MY_MUTEX_INIT_FAST); + + char buf[128]; + size_t len; + len = header(&stat_arg, buf, sizeof(buf)); + my_write(l_perm->file, (uchar *)buf, len, MYF(0)); + + return l_perm; +} + +int logger_close(LOGGER_HANDLE *log, logger_epilog_func_t footer) noexcept { + File file = log->file; + char buf[128]; + const size_t len = footer(buf, sizeof(buf)); + my_write(file, (uchar *)buf, len, MYF(0)); + + flogger_mutex_destroy(log); + my_free(log); + int result; + if ((result = my_close(file, MYF(0)))) errno = my_errno(); + return result; +} + +int logger_reopen(LOGGER_HANDLE *log, logger_prolog_func_t header, + logger_epilog_func_t footer) noexcept { + flogger_mutex_lock(log); + + char buf[128]; + size_t len = footer(buf, sizeof(buf)); + my_write(log->file, (uchar *)buf, len, MYF(0)); + + int result = 0; + if ((result = my_close(log->file, MYF(0)))) { + errno = my_errno(); + goto error; + } + + if ((log->file = my_open(log->path, LOG_FLAGS, MYF(0))) < 0) { + errno = my_errno(); + result = 1; + goto error; + } + + MY_STAT stat_arg; + if ((result = my_fstat(log->file, &stat_arg))) { + errno = my_errno(); + goto error; + } + + len = header(&stat_arg, buf, sizeof(buf)); + my_write(log->file, (uchar *)buf, len, MYF(0)); + +error: + flogger_mutex_unlock(log); + + return result; +} + +static char *logname(const LOGGER_HANDLE &log, char *buf, size_t buf_len, + unsigned int n_log) noexcept { + snprintf(buf + log.path_len, buf_len, ".%0*u", n_dig(log.rotations), n_log); + return buf; +} + +static int do_rotate(LOGGER_HANDLE *log) { + if (log->rotations == 0) return 0; + + char namebuf[FN_REFLEN]; + memcpy(namebuf, log->path, log->path_len); + + int result; + char *buf_new = logname(*log, namebuf, sizeof(namebuf), log->rotations); + char *buf_old = log->path; + for (auto i = log->rotations - 1; i > 0; i--) { + logname(*log, buf_old, FN_REFLEN, i); + if (!access(buf_old, F_OK) && + (result = my_rename(buf_old, buf_new, MYF(0)))) + goto exit; + char *tmp = buf_old; + buf_old = buf_new; + buf_new = tmp; + } + if ((result = my_close(log->file, MYF(0)))) goto exit; + namebuf[log->path_len] = 0; + result = my_rename(namebuf, logname(*log, log->path, FN_REFLEN, 1), MYF(0)); + log->file = my_open(namebuf, LOG_FLAGS, MYF(0)); +exit: + errno = my_errno(); + return log->file < 0 || result; +} + +int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, va_list ap) noexcept { + char cvtbuf[1024]; + size_t n_bytes; + + int result; + flogger_mutex_lock(log); + if (log->rotations > 0) { + my_off_t filesize; + if ((filesize = my_tell(log->file, MYF(0))) == (my_off_t)-1 || + ((unsigned long long)filesize >= log->size_limit && do_rotate(log))) { + result = -1; + errno = my_errno(); + goto exit; /* Log rotation needed but failed */ + } + } + + n_bytes = vsnprintf(cvtbuf, sizeof(cvtbuf), fmt, ap); + if (n_bytes >= sizeof(cvtbuf)) n_bytes = sizeof(cvtbuf) - 1; + + result = my_write(log->file, (uchar *)cvtbuf, n_bytes, MYF(0)); + +exit: + flogger_mutex_unlock(log); + return result; +} + +int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size, + log_record_state_t state) noexcept { + flogger_mutex_lock(log); + + int result = my_write(log->file, (const uchar *)buffer, size, MYF(0)); + + if (state == log_record_state_t::COMPLETE && log->rotations > 0) { + my_off_t filesize; + if ((filesize = my_tell(log->file, MYF(0))) == (my_off_t)-1 || + ((unsigned long long)filesize >= log->size_limit && do_rotate(log))) { + result = -1; + errno = my_errno(); + } + } + + flogger_mutex_unlock(log); + return result; +} + +int logger_rotate(LOGGER_HANDLE *log) { + flogger_mutex_lock(log); + const int result = do_rotate(log); + flogger_mutex_unlock(log); + return result; +} + +#ifndef __clang__ +MY_ATTRIBUTE((format(gnu_printf, 2, 3))) +#endif +int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...) noexcept { + va_list args; + va_start(args, fmt); + const int result = logger_vprintf(log, fmt, args); + va_end(args); + return result; +} + +void logger_init_mutexes() noexcept { +#if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI) && \ + !defined(FLOGGER_NO_THREADSAFE) + mysql_mutex_register(AUDIT_LOG_PSI_CATEGORY, mutex_list, + array_elements(mutex_list)); +#endif /*HAVE_PSI_INTERFACE && !FLOGGER_NO_PSI*/ +} + +int logger_sync(LOGGER_HANDLE *log) noexcept { + return my_sync(log->file, MYF(0)); +} + +void logger_set_size_limit(LOGGER_HANDLE *log, + unsigned long long size_limit) noexcept { + log->size_limit = size_limit; +} + +void logger_set_rotations(LOGGER_HANDLE *log, unsigned int rotations) noexcept { + log->rotations = rotations; +} diff --git a/plugin/audit_log/filter.cc b/plugin/audit_log/filter.cc new file mode 100644 index 000000000000..746f446f11c7 --- /dev/null +++ b/plugin/audit_log/filter.cc @@ -0,0 +1,389 @@ +/* Copyright (c) 2016 Percona LLC 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 + as published by the Free Software Foundation; version 2 of + the License. + + 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 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 "filter.h" + +#include +#include "audit_log.h" +#include "m_ctype.h" +#include "map_helpers.h" +#include "my_hostname.h" +#include "my_sys.h" +#include "my_user.h" +#include "mysql/psi/mysql_rwlock.h" +#include "mysql_com.h" + +static std::string make_account_string(const char *user, size_t user_length, + const char *host, size_t host_length) { + std::string result(user, user_length); + result.reserve(user_length + host_length + 2); + result.append(1, '@'); + result.append(host, host_length); + result.append(1, '\0'); + return result; +} + +static std::string make_command_string(const char *name, size_t length) { + std::string result(name, length); + std::transform(result.begin(), result.end(), result.begin(), ::tolower); + result.shrink_to_fit(); + return result; +} + +using account_set_t = collation_unordered_set; + +static account_set_t *include_accounts; +static account_set_t *exclude_accounts; + +using db_set_t = collation_unordered_set; + +static db_set_t *include_databases; +static db_set_t *exclude_databases; + +using command_set_t = malloc_unordered_set; + +static command_set_t *include_commands; +static command_set_t *exclude_commands; + +#if defined(HAVE_PSI_INTERFACE) + +static PSI_rwlock_key key_LOCK_account_list; +static PSI_rwlock_key key_LOCK_database_list; +static PSI_rwlock_key key_LOCK_command_list; +static PSI_rwlock_info all_rwlock_list[] = { + {&key_LOCK_account_list, "audit_log_filter::account_list", + PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}, + {&key_LOCK_database_list, "audit_log_filter::database_list", + PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}, + {&key_LOCK_account_list, "audit_log_filter::command_list", + PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}}; + +#endif + +static mysql_rwlock_t LOCK_account_list; +static mysql_rwlock_t LOCK_database_list; +static mysql_rwlock_t LOCK_command_list; + +/* + Allocate memory and initialize new command +*/ + +/* + Remove enclosing quotes from string if any. +*/ +static void unquote_string(char *string, size_t *string_length) noexcept { + if (string[0] == '\'' && string[*string_length - 1] == '\'') { + *string_length -= 2; + memmove(string, string + 1, *string_length); + string[*string_length] = 0; + } +} + +/* + Parse comma-separated list of accounts and add it into account list. + Empty user name is allowed. +*/ +static void account_list_from_string(account_set_t *account_set, + const char *string) { + account_set->clear(); + + char *string_copy = my_strdup(PSI_NOT_INSTRUMENTED, string, MYF(MY_FAE)); + const size_t string_length = strlen(string_copy); + char *entry = string_copy; + while (entry - string_copy < static_cast(string_length)) { + size_t entry_length = 0; + bool quote = false; + + while (*entry == ' ') entry++; + + entry_length = 0; + while ( + ((entry[entry_length] != ' ' && entry[entry_length] != ',') || quote) && + entry[entry_length] != 0) { + if (entry[entry_length] == '\'') quote = !quote; + entry_length++; + } + + entry[entry_length] = 0; + + char user[USERNAME_LENGTH + 1], host[HOSTNAME_LENGTH + 1]; + size_t user_length, host_length; + parse_user(entry, entry_length, user, &user_length, host, &host_length); + unquote_string(user, &user_length); + unquote_string(host, &host_length); + my_casedn_str(system_charset_info, host); + + account_set->emplace( + make_account_string(user, user_length, host, host_length)); + + entry += entry_length + 1; + } + + my_free(string_copy); +} + +static void database_list_from_string(db_set_t *db_set, const char *string) { + const char *entry = string; + + db_set->clear(); + + while (*entry) { + while (*entry == ' ') entry++; + + size_t entry_length = 0; + bool quote = false; + char name[NAME_LEN + 1]; + size_t name_length = 0; + while ( + ((entry[entry_length] != ' ' && entry[entry_length] != ',') || quote) && + entry[entry_length] != 0) { + if (quote && entry[entry_length] == '`' && + entry[entry_length + 1] == '`') { + name[name_length++] = '`'; + entry_length += 1; + } else if (entry[entry_length] == '`') + quote = !quote; + else if (name_length < sizeof(name)) + name[name_length++] = entry[entry_length]; + entry_length++; + } + + if (name_length > 0) { + name[name_length] = 0; + db_set->emplace(name, name_length); + } + + entry += entry_length; + + if (*entry == ',') entry++; + } +} + +/* + Parse comma-separated list of command and add it into command hash. +*/ +static void command_list_from_string(command_set_t *command_set, + const char *string) { + std::string lcase_str(string); + std::transform(lcase_str.begin(), lcase_str.end(), lcase_str.begin(), + ::tolower); + + command_set->clear(); + + auto it = lcase_str.cbegin(); + while (it != lcase_str.cend()) { + std::string::size_type len = 0; + while (it != lcase_str.cend() && (*it == ' ' || *it == ',')) it++; + while (it + len != lcase_str.cend() && it[len] != ' ' && it[len] != ',') + len++; + if (len > 0) { + command_set->emplace(&(*it), len); + it += len; + } + } +} + +/* public interface */ + +void audit_log_filter_init() { +#ifdef HAVE_PSI_INTERFACE + mysql_rwlock_register(AUDIT_LOG_PSI_CATEGORY, all_rwlock_list, + array_elements(all_rwlock_list)); +#endif /* HAVE_PSI_INTERFACE */ + mysql_rwlock_init(key_LOCK_account_list, &LOCK_account_list); + mysql_rwlock_init(key_LOCK_database_list, &LOCK_database_list); + mysql_rwlock_init(key_LOCK_command_list, &LOCK_command_list); + + include_accounts = + new account_set_t(&my_charset_bin, key_memory_audit_log_accounts); + + exclude_accounts = + new account_set_t(&my_charset_bin, key_memory_audit_log_accounts); + + include_databases = + new db_set_t(&my_charset_bin, key_memory_audit_log_databases); + + exclude_databases = + new db_set_t(&my_charset_bin, key_memory_audit_log_databases); + + include_commands = new command_set_t(key_memory_audit_log_commands); + + exclude_commands = new command_set_t(key_memory_audit_log_commands); +} + +void audit_log_filter_destroy() noexcept { + delete include_accounts; + delete exclude_accounts; + delete include_databases; + delete exclude_databases; + delete include_commands; + delete exclude_commands; + mysql_rwlock_destroy(&LOCK_account_list); + mysql_rwlock_destroy(&LOCK_database_list); + mysql_rwlock_destroy(&LOCK_account_list); + mysql_rwlock_destroy(&LOCK_command_list); +} + +/* + Parse and store the list of included accounts. +*/ +void audit_log_set_include_accounts(const char *val) { + mysql_rwlock_wrlock(&LOCK_account_list); + account_list_from_string(include_accounts, val); + mysql_rwlock_unlock(&LOCK_account_list); +} + +/* + Parse and store the list of excluded accounts. +*/ +void audit_log_set_exclude_accounts(const char *val) { + mysql_rwlock_wrlock(&LOCK_account_list); + account_list_from_string(exclude_accounts, val); + mysql_rwlock_unlock(&LOCK_account_list); +} + +/* + Check if account has to be included. +*/ +bool audit_log_check_account_included(const char *user, size_t user_length, + const char *host, size_t host_length) { + const std::string acc{ + make_account_string(user, user_length, host, host_length)}; + + mysql_rwlock_rdlock(&LOCK_account_list); + const auto &it = include_accounts->find(acc); + const bool res = it != include_accounts->cend(); + mysql_rwlock_unlock(&LOCK_account_list); + + return res; +} + +/* + Check if account has to be excluded. +*/ +bool audit_log_check_account_excluded(const char *user, size_t user_length, + const char *host, size_t host_length) { + const std::string acc{ + make_account_string(user, user_length, host, host_length)}; + + mysql_rwlock_rdlock(&LOCK_account_list); + const auto &it = exclude_accounts->find(acc); + const bool res = it != exclude_accounts->cend(); + ; + mysql_rwlock_unlock(&LOCK_account_list); + + return res; +} + +/* + Parse and store the list of included databases. +*/ +void audit_log_set_include_databases(const char *val) { + mysql_rwlock_wrlock(&LOCK_database_list); + database_list_from_string(include_databases, val); + mysql_rwlock_unlock(&LOCK_database_list); +} + +/* + Parse and store the list of excluded databases. +*/ +void audit_log_set_exclude_databases(const char *val) { + mysql_rwlock_wrlock(&LOCK_database_list); + database_list_from_string(exclude_databases, val); + mysql_rwlock_unlock(&LOCK_database_list); +} + +/* + Check if database has to be included. +*/ +bool audit_log_check_database_included(const char *name, size_t length) { + if (length == 0) return false; + + const std::string db(name, length); + + mysql_rwlock_rdlock(&LOCK_database_list); + const auto &it = include_databases->find(db); + const bool res = it != include_databases->cend(); + mysql_rwlock_unlock(&LOCK_database_list); + + return res; +} + +/* + Check if database has to be excluded. +*/ +bool audit_log_check_database_excluded(const char *name, size_t length) { + if (length == 0) return false; + + const std::string db(name, length); + + mysql_rwlock_rdlock(&LOCK_database_list); + const auto &it = exclude_databases->find(db); + const bool res = it != exclude_databases->cend(); + mysql_rwlock_unlock(&LOCK_database_list); + + return res; +} + +/* + Parse and store the list of included commands. +*/ +void audit_log_set_include_commands(const char *val) { + mysql_rwlock_wrlock(&LOCK_command_list); + command_list_from_string(include_commands, val); + mysql_rwlock_unlock(&LOCK_command_list); +} + +/* + Parse and store the list of excluded commands. +*/ +void audit_log_set_exclude_commands(const char *val) { + mysql_rwlock_wrlock(&LOCK_command_list); + command_list_from_string(exclude_commands, val); + mysql_rwlock_unlock(&LOCK_command_list); +} + +/* + Check if command has to be included. +*/ +bool audit_log_check_command_included(const char *name, size_t length) { + if (length == 0) return false; + + const std::string cmd{make_command_string(name, length)}; + + mysql_rwlock_rdlock(&LOCK_command_list); + const auto &it = include_commands->find(cmd); + const bool res = it != include_commands->cend(); + mysql_rwlock_unlock(&LOCK_command_list); + + return res; +} + +/* + Check if command has to be excluded. +*/ +bool audit_log_check_command_excluded(const char *name, size_t length) { + if (length == 0) return false; + + const std::string cmd{make_command_string(name, length)}; + + mysql_rwlock_rdlock(&LOCK_command_list); + const auto &it = exclude_commands->find(cmd); + const bool res = it != exclude_commands->cend(); + mysql_rwlock_unlock(&LOCK_command_list); + + return res; +} diff --git a/plugin/audit_log/filter.h b/plugin/audit_log/filter.h new file mode 100644 index 000000000000..1622ef2f6d32 --- /dev/null +++ b/plugin/audit_log/filter.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2016 Percona LLC 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 + as published by the Free Software Foundation; version 2 of + the License. + + 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 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 AUDIT_LOG_FILTER_INCLUDED +#define AUDIT_LOG_FILTER_INCLUDED + +#include + +void audit_log_set_include_accounts(const char *val); +void audit_log_set_exclude_accounts(const char *val); +bool audit_log_check_account_included(const char *user, size_t user_length, + const char *host, size_t host_length); +bool audit_log_check_account_excluded(const char *user, size_t user_length, + const char *host, size_t host_length); + +void audit_log_set_include_databases(const char *val); +void audit_log_set_exclude_databases(const char *val); +bool audit_log_check_database_included(const char *name, size_t length); +bool audit_log_check_database_excluded(const char *name, size_t length); + +void audit_log_set_include_commands(const char *val); +void audit_log_set_exclude_commands(const char *val); +bool audit_log_check_command_included(const char *command, size_t length); +bool audit_log_check_command_excluded(const char *command, size_t length); + +void audit_log_filter_init(); +void audit_log_filter_destroy() noexcept; + +#endif diff --git a/plugin/audit_log/logger.h b/plugin/audit_log/logger.h new file mode 100644 index 000000000000..fd1503b1da80 --- /dev/null +++ b/plugin/audit_log/logger.h @@ -0,0 +1,88 @@ +/* Copyright (C) 2012 Monty Program Ab + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + 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 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 Street, Fifth Floor, Boston, MA 02110-1301, + USA */ + +#ifndef MYSQL_SERVICE_LOGGER_INCLUDED +#define MYSQL_SERVICE_LOGGER_INCLUDED + +/** + @file + logger service + + Log file with rotation implementation. + + This service implements logging with possible rotation + of the log files. Interface intentionally tries to be similar to FILE* + related functions. + + So that one can open the log with logger_open(), specifying + the limit on the logfile size and the rotations number. + + Then it's possible to write messages to the log with + logger_printf or logger_vprintf functions. + + As the size of the logfile grows over the specified limit, + it is renamed to 'logfile.1'. The former 'logfile.1' becomes + 'logfile.2', etc. The file 'logfile.rotations' is removed. + That's how the rotation works. + + The rotation can be forced with the logger_rotate() call. + + Finally the log should be closed with logger_close(). + +@notes: + Implementation checks the size of the log file before it starts new + printf into it. So the size of the file gets over the limit when it rotates. + + The access is secured with the mutex, so the log is threadsafe. +*/ + +#include +#include +#include "my_compiler.h" +#include "my_dir.h" + +struct LOGGER_HANDLE; + +typedef size_t (*logger_prolog_func_t)(MY_STAT *, char *buf, size_t buflen); +typedef size_t (*logger_epilog_func_t)(char *buf, size_t buflen); + +enum class log_record_state_t { COMPLETE, INCOMPLETE }; + +void logger_init_mutexes() noexcept; +LOGGER_HANDLE *logger_open(const char *path, unsigned long long size_limit, + unsigned int rotations, bool thread_safe, + logger_prolog_func_t header) noexcept; +int logger_close(LOGGER_HANDLE *log, logger_epilog_func_t footer) noexcept; +#ifndef __clang__ +MY_ATTRIBUTE((format(gnu_printf, 2, 0))) +#endif +int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, + va_list argptr) noexcept; +#ifndef __clang__ +MY_ATTRIBUTE((format(gnu_printf, 2, 3))) +#endif +int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...) noexcept; +int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size, + log_record_state_t state) noexcept; +int logger_rotate(LOGGER_HANDLE *log); +int logger_sync(LOGGER_HANDLE *log) noexcept; +int logger_reopen(LOGGER_HANDLE *log, logger_prolog_func_t header, + logger_epilog_func_t footer) noexcept; +void logger_set_size_limit(LOGGER_HANDLE *log, + unsigned long long size_limit) noexcept; +void logger_set_rotations(LOGGER_HANDLE *log, unsigned int rotations) noexcept; + +#endif /*MYSQL_SERVICE_LOGGER_INCLUDED*/ diff --git a/plugin/audit_log/tests/mtr/audit_log_charset-master.opt b/plugin/audit_log/tests/mtr/audit_log_charset-master.opt new file mode 100644 index 000000000000..ad7f91c65bd5 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_charset-master.opt @@ -0,0 +1,6 @@ +$AUDIT_LOG_OPT +$AUDIT_LOG_LOAD +--audit_log_file=test_audit.log +--audit_log_policy=ALL +--audit-log-format=CSV +--audit_log_strategy=SYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_charset.result b/plugin/audit_log/tests/mtr/audit_log_charset.result new file mode 100644 index 000000000000..56221f2d92dc --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_charset.result @@ -0,0 +1,74 @@ +SET GLOBAL audit_log_flush=ON; +set names tis620; +SET NAMES utf8mb4; +INSERT IGNORE INTO t VALUES ('𦉘𦟌𦧲'); +Warnings: +Warning 1366 Incorrect string value: '\xF0\xA6\x89\x98\xF0\xA6...' for column 'txt' at row 1 +CREATE DATABASE 𦉘𦟌𦧲; +Warnings: +Warning 1300 Cannot convert string '\xF0\xA6\x89\x98\xF0\xA6...' from utf8mb4 to utf8mb3 +SHOW DATABASES; +Database +??? +information_schema +mtr +mysql +performance_schema +sys +test +ฐานข้อมูล +use 𦉘𦟌𦧲; +Warnings: +Warning 1300 Cannot convert string '\xF0\xA6\x89\x98\xF0\xA6...' from utf8mb4 to utf8mb3 +use ฐานข้อมูล; +SET NAMES utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +SELECT * FROM t WHERE txt LIKE 'ขุนนาง%'; +txt +ขุนนางใช่พ่อแม่ หินแง่ใช่ตายาย +use test; +SELECT * FROM ฐานข้อมูล.t LIMIT 1; +txt +ขุนนางใช่พ่อแม่ หินแง่ใช่ตายาย +use ฐานข้อมูล; +DROP DATABASE ฐานข้อมูล; +DROP DATABASE `???`; +use test; +SET @@character_set_client=cp1256; +CREATE t \217\355ݏ\355ݏ\355\335(\217\260\241\217\260\241\217\260\241 char) DEFAULT CHARSET=ujis engine=TokuDB; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 't \217\355ف?\355ف?\355\335(\217\260\241\217\260\241\217\260\241 char) DEFAULT ' at line 1 +set global audit_log_flush= ON; +=================================================================== +"Query","","","set_option","",0,"SET GLOBAL audit_log_flush=ON","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"set names tis620","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT x'b0d2b9a2e9cdc1d9c5'","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT x'a2d8b9b9d2a7e3aae8bee8cde1c1e820cbd4b9e1a7e8e3aae8b5d2c2d2c2'","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT x'a1a7e0a1c7d5c2b9a1d3e0a1c7d5c2b9'","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT REPEAT('กงเกวียนกำเกวียน ', 400)","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT 'ฐานข้อมูล'","root[root] @ localhost []","localhost","","","test" +"Query","","","create_db","",0,"CREATE DATABASE `ฐานข้อมูล`","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"use `ฐานข้อมูล`","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","create_table","",0,"CREATE TABLE t (txt TEXT) charset='utf8'","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","insert","",0,"INSERT INTO t VALUES ('ขุนนางใช่พ่อแม่ หินแง่ใช่ตายาย')","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","insert","",0,"INSERT INTO t VALUES ('กงเกวียนกำเกวียน')","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","insert","",0,"INSERT INTO t VALUES ('กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน ')","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","insert","",0,"INSERT IGNORE INTO t VALUES ('𦉘𦟌𦧲')","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","show_warnings","",0,"SHOW WARNINGS","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","create_db","",0,"CREATE DATABASE 𦉘𦟌𦧲","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","show_warnings","",0,"SHOW WARNINGS","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","show_databases","",0,"SHOW DATABASES","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","change_db","",0,"use 𦉘𦟌𦧲","root[root] @ localhost []","localhost","","","???" +"Query","","","show_warnings","",0,"SHOW WARNINGS","root[root] @ localhost []","localhost","","","???" +"Query","","","change_db","",0,"use ฐานข้อมูล","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","show_warnings","",0,"SHOW WARNINGS","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","select","",0,"SELECT * FROM t WHERE txt LIKE 'ขุนนาง%'","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","change_db","",0,"use test","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT * FROM ฐานข้อมูล.t LIMIT 1","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"use ฐานข้อมูล","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","drop_db","",0,"DROP DATABASE ฐานข้อมูล","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","drop_db","",0,"DROP DATABASE `???`","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","change_db","",0,"use test","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET @@character_set_client=cp1256","root[root] @ localhost []","localhost","","","test" +"Query","","","error","",1064,"CREATE t \217\355ف?\355ف?\355\335(\217\260\241\217\260\241\217\260\241 char) DEFAULT CHARSET=ujis engine=TokuDB","root[root] @ localhost []","localhost","","","test" +=================================================================== diff --git a/plugin/audit_log/tests/mtr/audit_log_charset.test b/plugin/audit_log/tests/mtr/audit_log_charset.test new file mode 100644 index 000000000000..a549832c0203 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_charset.test @@ -0,0 +1,64 @@ +# test encodings suppport by audit plugin + +let $MYSQLD_DATADIR= `select @@datadir`; +let $log_file=$MYSQLD_DATADIR/test_audit.log; + +--remove_file $log_file +SET GLOBAL audit_log_flush=ON; + +set names tis620; + +--disable_query_log +--disable_result_log + +let $db=`SELECT x'b0d2b9a2e9cdc1d9c5'`; +let $text1=`SELECT x'a2d8b9b9d2a7e3aae8bee8cde1c1e820cbd4b9e1a7e8e3aae8b5d2c2d2c2'`; +let $text2=`SELECT x'a1a7e0a1c7d5c2b9a1d3e0a1c7d5c2b9'`; +let $very_long_text=`SELECT REPEAT('$text2 ', 400)`; + +eval SELECT '$db'; + +eval CREATE DATABASE `$db`; + +eval use `$db`; + +CREATE TABLE t (txt TEXT) charset='utf8'; + +eval INSERT INTO t VALUES ('$text1'); +eval INSERT INTO t VALUES ('$text2'); +eval INSERT INTO t VALUES ('$very_long_text'); + +--enable_query_log +--enable_result_log + +SET NAMES utf8mb4; + +INSERT IGNORE INTO t VALUES ('𦉘𦟌𦧲'); +CREATE DATABASE 𦉘𦟌𦧲; + +SHOW DATABASES; + +use 𦉘𦟌𦧲; + +use ฐานข้อมูล; + +SET NAMES utf8; + +SELECT * FROM t WHERE txt LIKE 'ขุนนาง%'; + +use test; + +SELECT * FROM ฐานข้อมูล.t LIMIT 1; + +use ฐานข้อมูล; + +DROP DATABASE ฐานข้อมูล; +DROP DATABASE `???`; + +use test; + +SET @@character_set_client=cp1256; +--error ER_PARSE_ERROR +CREATE t \217\355ݏ\355ݏ\355\335(\217\260\241\217\260\241\217\260\241 char) DEFAULT CHARSET=ujis engine=TokuDB; + +--source audit_log_echo.inc diff --git a/plugin/audit_log/tests/mtr/audit_log_csv-master.opt b/plugin/audit_log/tests/mtr/audit_log_csv-master.opt new file mode 100644 index 000000000000..fb919dedf07b --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_csv-master.opt @@ -0,0 +1,3 @@ +--audit_log_file=test_audit.log +--audit-log-format=CSV +--audit_log_strategy=SEMISYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_csv.result b/plugin/audit_log/tests/mtr/audit_log_csv.result new file mode 100644 index 000000000000..c94a9338321b --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_csv.result @@ -0,0 +1,96 @@ +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +CREATE TABLE t1 (c1 INT, c2 CHAR(20)); +CREATE TABLE t1 +(c1 INT, +c2 CHAR(20)); +ERROR 42S01: Table 't1' already exists +INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c'); +SELECT * FROM t1; +c1 c2 +1 a +2 b +3 c +SELECT * FROM t2; +ERROR 42S02: Table 'test.t2' doesn't exist +DROP TABLE t1; +PREPARE stmt1 FROM 'SELECT 1'; +EXECUTE stmt1; +1 +1 +SHOW STATUS LIKE 'audit_log%'; +Variable_name Value +DEALLOCATE PREPARE stmt1; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 1048576 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file test_audit.log +audit_log_flush OFF +audit_log_format CSV +audit_log_handler FILE +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy ALL +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy SEMISYNCHRONOUS +audit_log_syslog_facility LOG_USER +audit_log_syslog_ident percona-audit +audit_log_syslog_priority LOG_INFO +connect(localhost,no_such_user,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'no_such_user'@'localhost' (using password: NO) +create table t1 (id int); +create table t2 (id int); +insert into t1 values (1), (2); +insert into t2 values (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2); +select * from t1; +id +1 +2 +alter table t1 rename renamed_t1; +select * from t_doesnt_exist; +ERROR 42S02: Table 'test.t_doesnt_exist' doesn't exist +syntax_error_query; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'syntax_error_query' at line 1 +drop table renamed_t1, t2; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 1048576 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file test_audit.log +audit_log_flush OFF +audit_log_format CSV +audit_log_handler FILE +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy ALL +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy SEMISYNCHRONOUS +audit_log_syslog_facility LOG_USER +audit_log_syslog_ident percona-audit +audit_log_syslog_priority LOG_INFO +create database sa_db; +create table t1 (id2 int); +insert into t1 values (1), (2); +select * from t1; +id2 +1 +2 +drop table t1; +use sa_db; +create table sa_t1(id int); +insert into sa_t1 values (1), (2); +drop table sa_t1; +drop database sa_db; +select '&;&&&""""<><<>>>>'; +&;&&&""""<><<>>>> +&;&&&""""<><<>>>> +set global audit_log_flush= ON; diff --git a/plugin/audit_log/tests/mtr/audit_log_csv.test b/plugin/audit_log/tests/mtr/audit_log_csv.test new file mode 100644 index 000000000000..9cec7df34492 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_csv.test @@ -0,0 +1,23 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +let MYSQLD_DATADIR= $MYSQLD_DATADIR; + +SET GLOBAL audit_log_flush=ON; +--remove_file $MYSQLD_DATADIR/test_audit.log +SET GLOBAL audit_log_flush=ON; + +--source audit_log_events.inc + +--move_file $MYSQLD_DATADIR/test_audit.log $MYSQLD_DATADIR/test_audit_csv.log +set global audit_log_flush= ON; +perl; + eval "use Text::CSV; 1" or exit 0; + my $p = Text::CSV->new({ binary => 1, sep_char => ',' }); + open my $file, $ENV{'MYSQLD_DATADIR'} . '/test_audit_csv.log' or die "Could not open log: $!"; + while ($p->getline($file)) {}; + if (not $p->eof) { + die "CSV parse error " . $p->error_diag(); + } + close $file; +EOF +--remove_file $MYSQLD_DATADIR/test_audit.log +--remove_file $MYSQLD_DATADIR/test_audit_csv.log diff --git a/plugin/audit_log/tests/mtr/audit_log_default_db-master.opt b/plugin/audit_log/tests/mtr/audit_log_default_db-master.opt new file mode 100644 index 000000000000..033178764ec5 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_default_db-master.opt @@ -0,0 +1,4 @@ +--audit_log_file=test_audit.log +--audit_log_policy=ALL +--audit-log-format=CSV +--audit_log_strategy=SYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_default_db.result b/plugin/audit_log/tests/mtr/audit_log_default_db.result new file mode 100644 index 000000000000..6c274bad24ac --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_default_db.result @@ -0,0 +1,102 @@ +set names utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +CREATE USER 'user1'@'%' IDENTIFIED BY '111'; +CREATE USER 'user2'@'%' IDENTIFIED BY '111'; +CREATE DATABASE db1; +CREATE DATABASE db2; +CREATE DATABASE `ąžąžąžą`; +CREATE TABLE db1.t (a VARCHAR(100)); +CREATE TABLE db2.t (a VARCHAR(100)); +CREATE TABLE ąžąžąžą.t (a VARCHAR(100)) charset=utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +INSERT INTO db1.t VALUES ('db1'); +INSERT INTO db2.t VALUES ('db2'); +INSERT INTO ąžąžąžą.t VALUES ('ąžąžąžą'); +GRANT ALL PRIVILEGES ON db1.* TO 'user1'@'%'; +GRANT ALL PRIVILEGES ON db2.* TO 'user1'@'%'; +GRANT ALL PRIVILEGES ON db2.* TO 'user2'@'%'; +GRANT ALL PRIVILEGES ON ąžąžąžą.* TO 'user1'@'%'; +GRANT ALL PRIVILEGES ON ąžąžąžą.* TO 'user2'@'%'; +UNINSTALL PLUGIN audit_log; +Warnings: +Warning 1620 Plugin is busy and will be uninstalled on shutdown +INSTALL PLUGIN audit_log SONAME 'audit_log.so'; +SELECT * FROM t; +a +db2 +use `db1`; +ERROR 42000: Access denied for user 'user2'@'%' to database 'db1' +connect(localhost,user2,111,db1,MASTER_MYPORT,MASTER_MYSOCK); +ERROR 42000: Access denied for user 'user2'@'%' to database 'db1' +connect(localhost,user2,112,db2,MASTER_MYPORT,MASTER_MYSOCK); +ERROR 28000: Access denied for user 'user2'@'localhost' (using password: YES) +connect(localhost,user3,111,db2,MASTER_MYPORT,MASTER_MYSOCK); +ERROR 28000: Access denied for user 'user3'@'localhost' (using password: YES) +set names utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +SELECT * FROM t; +a +db2 +use `db1`; +ERROR 42000: Access denied for user 'user2'@'%' to database 'db1' +SELECT * FROM t; +a +db2 +set names utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +SELECT * FROM t; +a +db1 +use `db2`; +SELECT * FROM t; +a +db2 +use ąžąžąžą; +SELECT * FROM t; +a +ąžąžąžą +a +db1 +set global audit_log_flush= ON; +=================================================================== +"Query","","","install_plugin","",0,"INSTALL PLUGIN audit_log SONAME 'audit_log.so'","root[root] @ localhost []","localhost","","","" +"Quit","","","",0,"root","root","","","localhost","","test" +"Connect","","","",0,"user1","user1","","","localhost","","db2" +"Query","","","select","",0,"SELECT * FROM t","user1[user1] @ localhost []","localhost","","","db2" +"Query","","","change_db","",0,"use `db1`","user1[user1] @ localhost []","localhost","","","db1" +"Change user","","","",1044,"user2","user2","","","localhost","","" +"Quit","","","",0,"user1","user1","","","localhost","","db1" +"Connect","","","",1044,"user2","user2","","","localhost","","" +"Connect","","","",1045,"user2","user2","","","localhost","","" +"Connect","","","",1045,"user3","","","","localhost","","" +"Connect","","","",0,"user2","user2","","","localhost","","db2" +"Query","","","set_option","",0,"set names utf8","user2[user2] @ localhost []","localhost","","","db2" +"Query","","","show_warnings","",0,"SHOW WARNINGS","user2[user2] @ localhost []","localhost","","","db2" +"Query","","","select","",0,"SELECT * FROM t","user2[user2] @ localhost []","localhost","","","db2" +"Query","","","change_db","",1044,"use `db1`","user2[user2] @ localhost []","localhost","","","db2" +"Query","","","select","",0,"SELECT * FROM t","user2[user2] @ localhost []","localhost","","","db2" +"Change user","","","",0,"user1","user1","","","localhost","","db1" +"Query","","","set_option","",0,"set names utf8","user1[user1] @ localhost []","localhost","","","db1" +"Query","","","show_warnings","",0,"SHOW WARNINGS","user1[user1] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM t","user1[user1] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"use `db2`","user1[user1] @ localhost []","localhost","","","db2" +"Query","","","select","",0,"SELECT * FROM t","user1[user1] @ localhost []","localhost","","","db2" +"Query","","","change_db","",0,"use ąžąžąžą","user1[user1] @ localhost []","localhost","","","ąžąžąžą" +"Query","","","select","",0,"SELECT * FROM t","user1[user1] @ localhost []","localhost","","","ąžąžąžą" +"Quit","","","",0,"user1","user1","","","localhost","","ąžąžąžą" +"Connect","","","",0,"user1","user1","","","localhost","","test" +"Query","","","select","",0,"select @@version_comment limit 1","user1[user1] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT DATABASE()","user1[user1] @ localhost []","localhost","","","test" +"Init DB","","","error","",0,"","user1[user1] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT * FROM t","user1[user1] @ localhost []","localhost","","","test" +"Quit","","","",0,"user1","user1","","","localhost","","db1" +=================================================================== +DROP DATABASE db1; +DROP DATABASE db2; +DROP DATABASE ąžąžąžą; +DROP USER user1; +DROP USER user2; diff --git a/plugin/audit_log/tests/mtr/audit_log_default_db.test b/plugin/audit_log/tests/mtr/audit_log_default_db.test new file mode 100644 index 000000000000..e17e298e67da --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_default_db.test @@ -0,0 +1,100 @@ +# test correctness of default_db field + +let $MYSQLD_DATADIR= `select @@datadir`; +let $log_file=$MYSQLD_DATADIR/test_audit.log; + +set names utf8; + +CREATE USER 'user1'@'%' IDENTIFIED BY '111'; +CREATE USER 'user2'@'%' IDENTIFIED BY '111'; + +CREATE DATABASE db1; +CREATE DATABASE db2; +CREATE DATABASE `ąžąžąžą`; + +CREATE TABLE db1.t (a VARCHAR(100)); +CREATE TABLE db2.t (a VARCHAR(100)); +CREATE TABLE ąžąžąžą.t (a VARCHAR(100)) charset=utf8; +INSERT INTO db1.t VALUES ('db1'); +INSERT INTO db2.t VALUES ('db2'); +INSERT INTO ąžąžąžą.t VALUES ('ąžąžąžą'); + +GRANT ALL PRIVILEGES ON db1.* TO 'user1'@'%'; + +GRANT ALL PRIVILEGES ON db2.* TO 'user1'@'%'; +GRANT ALL PRIVILEGES ON db2.* TO 'user2'@'%'; + +GRANT ALL PRIVILEGES ON ąžąžąžą.* TO 'user1'@'%'; +GRANT ALL PRIVILEGES ON ąžąžąžą.* TO 'user2'@'%'; + +# truncate audit log +UNINSTALL PLUGIN audit_log; +--source include/disconnect_connections.inc +let $wait_condition= + SELECT count(*) = 0 FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE 'audit_log'; +--source include/wait_condition.inc +--remove_file $log_file + +--source include/count_sessions.inc +connect (root,localhost,root,,,); +connection root; +INSTALL PLUGIN audit_log SONAME 'audit_log.so'; +disconnect root; +connection default; +--source include/wait_until_count_sessions.inc + +connect (test,localhost,user1,111,db2,); +connection test; +SELECT * FROM t; + +use `db1`; +--error ER_DBACCESS_DENIED_ERROR +change_user user2,111,db1,do_not_reconnect_on_fail; +disconnect test; +connection default; +--source include/wait_until_count_sessions.inc +--replace_result $MASTER_MYPORT MASTER_MYPORT $MASTER_MYSOCK MASTER_MYSOCK +--error ER_DBACCESS_DENIED_ERROR +connect (test,localhost,user2,111,db1,); +connection default; +--source include/wait_until_count_sessions.inc +--replace_result $MASTER_MYPORT MASTER_MYPORT $MASTER_MYSOCK MASTER_MYSOCK +--error ER_ACCESS_DENIED_ERROR +connect (test,localhost,user2,112,db2,); +connection default; +--source include/wait_until_count_sessions.inc +--replace_result $MASTER_MYPORT MASTER_MYPORT $MASTER_MYSOCK MASTER_MYSOCK +--error ER_ACCESS_DENIED_ERROR +connect (test,localhost,user3,111,db2,); +connection default; +--source include/wait_until_count_sessions.inc +connect (test,localhost,user2,111,db2,); +connection test; +set names utf8; +SELECT * FROM t; +--error ER_DBACCESS_DENIED_ERROR +use `db1`; +SELECT * FROM t; +change_user user1,111,db1; +set names utf8; +SELECT * FROM t; +use `db2`; +SELECT * FROM t; +use ąžąžąžą; +SELECT * FROM t; +disconnect test; +connection default; + +--source include/wait_until_count_sessions.inc +--exec $MYSQL --user=user1 --password=111 test -e "use db1; SELECT * FROM t;" +--source include/wait_until_count_sessions.inc + +--source audit_log_echo.inc + +DROP DATABASE db1; +DROP DATABASE db2; +DROP DATABASE ąžąžąžą; + +DROP USER user1; +DROP USER user2; + diff --git a/plugin/audit_log/tests/mtr/audit_log_echo.inc b/plugin/audit_log/tests/mtr/audit_log_echo.inc new file mode 100644 index 000000000000..81d75b85af1f --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_echo.inc @@ -0,0 +1,47 @@ +# Echo the contents of the audit log (in CSV format) +# log_file is the name of the log file + +--move_file $log_file $log_file.copy +set global audit_log_flush= ON; +let log_file=$log_file; +perl; + print "===================================================================\n"; + open my $file, $ENV{'log_file'} . '.copy' or die "Can not open log: $!"; + while ($line = <$file>) { + if ($line =~ /SET NAMES/) { + # change_user does automatic reconnect and messing up 'SET NAMES' around + next; + } + if ($line =~ /Threads_connected/ || $line =~ /SELECT \d <= \d/ + || /SELECT.*FROM.*INFORMATION_SCHEMA.PLUGINS/) { + # part of wait_until_count_sessions.inc and wait_condition.inc scripts + next; + } + if ($line =~ /^"Audit"/) { + # skip opening log record and disconnect record + next; + } + if ($line =~ /SELECT count\(\*\)=1 FROM t WHERE a=7/) { + # skip checking for event shot + next; + } + if ($line =~ /INSERT INTO t VALUES \(7\)/) { + # don't show the last statement of the event + next; + } + if ($line =~ /SELECT 600 \* 10/) { + # skip checking for event shot + next; + } + $line =~ s/"([a-zA-Z_ ]*)","([0-9]+)_[0-9_ :T-]*","[0-9_ :A-Z-]*"/"$1","",""/; + $line =~ s/"(Connect|Quit|Change user)","","","[0-9]+"/"$1","","",""/; + $line =~ s/"([A-Za-z ]+)","","","([a-z_]+)","[0-9]+"/"$1","","","$2",""/; + if ($line =~ /SET GLOBAL .*_accounts/) { + print "*************************************************************\n"; + } + print "$line"; + }; + close $file; + print "===================================================================\n"; +EOF +--remove_file $log_file.copy diff --git a/plugin/audit_log/tests/mtr/audit_log_events.inc b/plugin/audit_log/tests/mtr/audit_log_events.inc new file mode 100644 index 000000000000..980252ade274 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_events.inc @@ -0,0 +1,102 @@ +# produce some events for audit log + +CREATE TABLE t1 (c1 INT, c2 CHAR(20)); +--error ER_TABLE_EXISTS_ERROR +CREATE TABLE t1 + (c1 INT, + c2 CHAR(20)); +INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c'); +-- if ($test_control_chars) { +INSERT INTO `t1` VALUES (4,NULL); +# can't add the zero ascii character, as that's a syntax error in MySQL +INSERT INTO `t1` VALUES (6,''); +INSERT INTO `t1` VALUES (7,''); +INSERT INTO `t1` VALUES (8,''); +INSERT INTO `t1` VALUES (9,''); +INSERT INTO `t1` VALUES (10,''); +INSERT INTO `t1` VALUES (11,''); +INSERT INTO `t1` VALUES (12,''); +INSERT INTO `t1` VALUES (13,''); +INSERT INTO `t1` VALUES (14,' '); +INSERT INTO `t1` VALUES (15,' +'); +INSERT INTO `t1` VALUES (16,' '); +INSERT INTO `t1` VALUES (17,' '); +INSERT INTO `t1` VALUES (18,' '); +INSERT INTO `t1` VALUES (19,''); +INSERT INTO `t1` VALUES (20,''); +INSERT INTO `t1` VALUES (21,''); +INSERT INTO `t1` VALUES (22,''); +INSERT INTO `t1` VALUES (23,''); +INSERT INTO `t1` VALUES (24,''); +INSERT INTO `t1` VALUES (25,''); +INSERT INTO `t1` VALUES (26,''); +INSERT INTO `t1` VALUES (27,''); +INSERT INTO `t1` VALUES (28,''); +INSERT INTO `t1` VALUES (29,''); +INSERT INTO `t1` VALUES (30,''); +INSERT INTO `t1` VALUES (31,''); +INSERT INTO `t1` VALUES (32,''); +INSERT INTO `t1` VALUES (33,''); +INSERT INTO `t1` VALUES (34,''); +INSERT INTO `t1` VALUES (35,''); +INSERT INTO `t1` VALUES (36,''); +-- } +SELECT * FROM t1; +--error ER_NO_SUCH_TABLE +SELECT * FROM t2; +DROP TABLE t1; + +PREPARE stmt1 FROM 'SELECT 1'; +EXECUTE stmt1; +SHOW STATUS LIKE 'audit_log%'; + +DEALLOCATE PREPARE stmt1; + +show variables like 'audit_log%'; +--source include/count_sessions.inc +connect (con1,localhost,root,,mysql); +connection default; +disconnect con1; +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error ER_ACCESS_DENIED_ERROR +connect (con1,localhost,no_such_user,,mysql); +connection default; +create table t1 (id int); +create table t2 (id int); +insert into t1 values (1), (2); +# query is longer than 4k +insert into t2 values (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2); +select * from t1; +alter table t1 rename renamed_t1; +--error ER_NO_SUCH_TABLE +select * from t_doesnt_exist; +--error 1064 +syntax_error_query; +drop table renamed_t1, t2; +show variables like 'audit_log%'; +create database sa_db; +connect (con1,localhost,root,,test); +connection con1; +create table t1 (id2 int); +insert into t1 values (1), (2); +select * from t1; +drop table t1; +use sa_db; +create table sa_t1(id int); +insert into sa_t1 values (1), (2); +drop table sa_t1; +drop database sa_db; +connection default; +# IDENTIFIED BY will be logged as IDENTIFIED ... AS, containing control chars +if ($test_control_chars) { + create user 'jeffrey'@'localhost' IDENTIFIED BY 'mypass'; + drop user 'jeffrey'@'localhost'; +} +select '&;&&&""""<><<>>>>'; +if ($test_control_chars) { + let $str=`SELECT x'2009080c0a0d2f225c5c'`; + eval select '$str'; +} +disconnect con1; +--source include/wait_until_count_sessions.inc diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_commands-master.opt b/plugin/audit_log/tests/mtr/audit_log_filter_commands-master.opt new file mode 100644 index 000000000000..ad7f91c65bd5 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_commands-master.opt @@ -0,0 +1,6 @@ +$AUDIT_LOG_OPT +$AUDIT_LOG_LOAD +--audit_log_file=test_audit.log +--audit_log_policy=ALL +--audit-log-format=CSV +--audit_log_strategy=SYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_commands.result b/plugin/audit_log/tests/mtr/audit_log_filter_commands.result new file mode 100644 index 000000000000..e1cf0e3c38ce --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_commands.result @@ -0,0 +1,217 @@ +SET GLOBAL audit_log_include_commands= 'create_table,create_procedure,create_trigger,insert'; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +create_table,create_procedure,create_trigger,insert NULL +SET GLOBAL audit_log_exclude_commands= 'alter_db_upgrade,change_db,drop_table,drop_db'; +ERROR 42000: Variable 'audit_log_exclude_commands' can't be set to the value of 'alter_db_upgrade,change_db,drop_table,drop_db' +SET GLOBAL audit_log_exclude_commands= NULL; +ERROR 42000: Variable 'audit_log_exclude_commands' can't be set to the value of 'NULL' +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +create_table,create_procedure,create_trigger,insert NULL +SET GLOBAL audit_log_include_commands= 'alter_db_upgrade,change_db,drop_table,drop_db'; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +alter_db_upgrade,change_db,drop_table,drop_db NULL +SET GLOBAL audit_log_include_commands= ''; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands + NULL +SET GLOBAL audit_log_exclude_commands= 'insert,call_procedure,call_procedure,set_option,assign_to_keycache'; +ERROR 42000: Variable 'audit_log_exclude_commands' can't be set to the value of 'insert,call_procedure,call_procedure,set_option,assign_to_keycache' +SET GLOBAL audit_log_include_commands= NULL; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +NULL NULL +SET GLOBAL audit_log_exclude_commands= "insert,call_procedure,call_procedure,set_option,assign_to_keycache"; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +NULL insert,call_procedure,call_procedure,set_option,assign_to_keycache +SET GLOBAL audit_log_include_commands= 'change_db,drop_table@localhost'; +ERROR 42000: Variable 'audit_log_include_commands' can't be set to the value of 'change_db,drop_table@localhost' +SET GLOBAL audit_log_include_commands= NULL; +ERROR 42000: Variable 'audit_log_include_commands' can't be set to the value of 'NULL' +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +NULL insert,call_procedure,call_procedure,set_option,assign_to_keycache +SET GLOBAL audit_log_exclude_commands= 'change_db,drop_table'; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +NULL change_db,drop_table +SET GLOBAL audit_log_exclude_commands= ''; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +NULL +SET GLOBAL audit_log_include_commands= 'change_db'; +ERROR 42000: Variable 'audit_log_include_commands' can't be set to the value of 'change_db' +SET GLOBAL audit_log_exclude_commands= NULL; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +NULL NULL +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_exclude_commands= NULL; +SET GLOBAL audit_log_include_commands= NULL; +CREATE DATABASE db1 DEFAULT CHARACTER SET latin1; +USE db1; +CREATE TABLE t1 (a INT, b INT, KEY(b)) engine=MyISAM; +CREATE TABLE t2 (a INT, b INT, KEY(b)) engine=InnoDB; +CREATE PROCEDURE p1() +BEGIN +INSERT INTO t1 (a, b) VALUES (1, 1); +END// +CREATE TRIGGER trigger1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 (a, b) VALUES (new.a, new.b); +INSERT INTO t1 VALUES (5,5); +CALL p1(); +SET GLOBAL keycache1.key_buffer_size=128*1024; +Warnings: +Warning 1287 keycache1.key_buffer_size syntax is deprecated and will be removed in a future release +CACHE INDEX t1 IN keycache1; +Table Op Msg_type Msg_text +db1.t1 assign_to_keycache status OK +ALTER DATABASE db1 DEFAULT CHARACTER SET utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +USE test; +DROP TABLE db1.t1; +DROP DATABASE db1; +SET GLOBAL audit_log_include_commands= 'set_option,creaTE_DB,CHANGE_DB,CREATE_TABLE,CREATE_TABLE,CREATE_PROCEDURE,CREATE_TRIGger,insert,insert,insert,call_procedure,call_procedure,set_option,assign_to_keycache,alter_db,alter_db_upgrade,change_db,drop_table,drop_db'; +CREATE DATABASE db1 DEFAULT CHARACTER SET latin1; +USE db1; +CREATE TABLE t1 (a INT, b INT, KEY(b)) engine=MyISAM; +CREATE TABLE t2 (a INT, b INT, KEY(b)) engine=InnoDB; +CREATE PROCEDURE p1() +BEGIN +INSERT INTO t1 (a, b) VALUES (1, 1); +END// +CREATE TRIGGER trigger1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 (a, b) VALUES (new.a, new.b); +INSERT INTO t1 VALUES (5,5); +CALL p1(); +SET GLOBAL keycache1.key_buffer_size=128*1024; +Warnings: +Warning 1287 keycache1.key_buffer_size syntax is deprecated and will be removed in a future release +CACHE INDEX t1 IN keycache1; +Table Op Msg_type Msg_text +db1.t1 assign_to_keycache status OK +ALTER DATABASE db1 DEFAULT CHARACTER SET utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +USE test; +DROP TABLE db1.t1; +DROP DATABASE db1; +SET GLOBAL audit_log_include_commands= 'set_option,creaTE_DB,CHANGE_DB,assign_to_keycache,alter_db,alter_db_upgrade'; +CREATE DATABASE db1 DEFAULT CHARACTER SET latin1; +USE db1; +CREATE TABLE t1 (a INT, b INT, KEY(b)) engine=MyISAM; +CREATE TABLE t2 (a INT, b INT, KEY(b)) engine=InnoDB; +CREATE PROCEDURE p1() +BEGIN +INSERT INTO t1 (a, b) VALUES (1, 1); +END// +CREATE TRIGGER trigger1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 (a, b) VALUES (new.a, new.b); +INSERT INTO t1 VALUES (5,5); +CALL p1(); +SET GLOBAL keycache1.key_buffer_size=128*1024; +Warnings: +Warning 1287 keycache1.key_buffer_size syntax is deprecated and will be removed in a future release +CACHE INDEX t1 IN keycache1; +Table Op Msg_type Msg_text +db1.t1 assign_to_keycache status OK +ALTER DATABASE db1 DEFAULT CHARACTER SET utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +USE test; +DROP TABLE db1.t1; +DROP DATABASE db1; +SET GLOBAL audit_log_include_commands= NULL; +SET GLOBAL audit_log_exclude_commands= 'set_option,create_db,change_db,create_table,create_table,create_prOCEDURE,CREATE_TRIGGER,INSERT,INSERT,INSERT,CALL_PROCEDURE,CALL_PROCEDUre,set_option,assign_to_keycache,alter_db,alter_db_upgrade,change_db,drop_table,drop_db'; +CREATE DATABASE db1 DEFAULT CHARACTER SET latin1; +USE db1; +CREATE TABLE t1 (a INT, b INT, KEY(b)) engine=MyISAM; +CREATE TABLE t2 (a INT, b INT, KEY(b)) engine=InnoDB; +CREATE PROCEDURE p1() +BEGIN +INSERT INTO t1 (a, b) VALUES (1, 1); +END// +CREATE TRIGGER trigger1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 (a, b) VALUES (new.a, new.b); +INSERT INTO t1 VALUES (5,5); +CALL p1(); +SET GLOBAL keycache1.key_buffer_size=128*1024; +Warnings: +Warning 1287 keycache1.key_buffer_size syntax is deprecated and will be removed in a future release +CACHE INDEX t1 IN keycache1; +Table Op Msg_type Msg_text +db1.t1 assign_to_keycache status OK +ALTER DATABASE db1 DEFAULT CHARACTER SET utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +USE test; +DROP TABLE db1.t1; +DROP DATABASE db1; +SET GLOBAL audit_log_exclude_commands= NULL; +SET GLOBAL audit_log_include_commands= NULL; +set global audit_log_flush= ON; +=================================================================== +"Query","","","set_option","",0,"SET GLOBAL audit_log_flush=ON","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_exclude_commands= NULL","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_commands= NULL","root[root] @ localhost []","localhost","","","test" +"Ping","","","error","",0,"","root[root] @ localhost []","localhost","","","test" +"Query","","","create_db","",0,"CREATE DATABASE db1 DEFAULT CHARACTER SET latin1","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"USE db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE t1 (a INT, b INT, KEY(b)) engine=MyISAM","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE t2 (a INT, b INT, KEY(b)) engine=InnoDB","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p1() +BEGIN +INSERT INTO t1 (a, b) VALUES (1, 1); +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER trigger1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 (a, b) VALUES (new.a, new.b)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO t2 (a, b) VALUES (new.a, new.b)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO t1 VALUES (5,5)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO t2 (a, b) VALUES (new.a, new.b)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"INSERT INTO t1 (a, b) VALUES (1, 1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p1()","root[root] @ localhost []","localhost","","","db1" +"Query","","","set_option","",0,"SET GLOBAL keycache1.key_buffer_size=128*1024","root[root] @ localhost []","localhost","","","db1" +"Query","","","show_warnings","",0,"SHOW WARNINGS","root[root] @ localhost []","localhost","","","db1" +"Query","","","assign_to_keycache","",0,"CACHE INDEX t1 IN keycache1","root[root] @ localhost []","localhost","","","db1" +"Query","","","alter_db","",0,"ALTER DATABASE db1 DEFAULT CHARACTER SET utf8","root[root] @ localhost []","localhost","","","db1" +"Query","","","show_warnings","",0,"SHOW WARNINGS","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE test","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.t1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_db","",0,"DROP DATABASE db1","root[root] @ localhost []","localhost","","","test" +"Ping","","","error","",0,"","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_commands= 'set_option,creaTE_DB,CHANGE_DB,CREATE_TABLE,CREATE_TABLE,CREATE_PROCEDURE,CREATE_TRIGger,insert,insert,insert,call_procedure,call_procedure,set_option,assign_to_keycache,alter_db,alter_db_upgrade,change_db,drop_table,drop_db'","root[root] @ localhost []","localhost","","","test" +"Query","","","create_db","",0,"CREATE DATABASE db1 DEFAULT CHARACTER SET latin1","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"USE db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE t1 (a INT, b INT, KEY(b)) engine=MyISAM","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE t2 (a INT, b INT, KEY(b)) engine=InnoDB","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p1() +BEGIN +INSERT INTO t1 (a, b) VALUES (1, 1); +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER trigger1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 (a, b) VALUES (new.a, new.b)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO t2 (a, b) VALUES (new.a, new.b)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO t1 VALUES (5,5)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO t2 (a, b) VALUES (new.a, new.b)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"INSERT INTO t1 (a, b) VALUES (1, 1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p1()","root[root] @ localhost []","localhost","","","db1" +"Query","","","set_option","",0,"SET GLOBAL keycache1.key_buffer_size=128*1024","root[root] @ localhost []","localhost","","","db1" +"Query","","","assign_to_keycache","",0,"CACHE INDEX t1 IN keycache1","root[root] @ localhost []","localhost","","","db1" +"Query","","","alter_db","",0,"ALTER DATABASE db1 DEFAULT CHARACTER SET utf8","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE test","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.t1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_db","",0,"DROP DATABASE db1","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_commands= 'set_option,creaTE_DB,CHANGE_DB,assign_to_keycache,alter_db,alter_db_upgrade'","root[root] @ localhost []","localhost","","","test" +"Query","","","create_db","",0,"CREATE DATABASE db1 DEFAULT CHARACTER SET latin1","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"USE db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","set_option","",0,"SET GLOBAL keycache1.key_buffer_size=128*1024","root[root] @ localhost []","localhost","","","db1" +"Query","","","assign_to_keycache","",0,"CACHE INDEX t1 IN keycache1","root[root] @ localhost []","localhost","","","db1" +"Query","","","alter_db","",0,"ALTER DATABASE db1 DEFAULT CHARACTER SET utf8","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE test","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_commands= NULL","root[root] @ localhost []","localhost","","","test" +"Ping","","","error","",0,"","root[root] @ localhost []","localhost","","","test" +"Query","","","show_warnings","",0,"SHOW WARNINGS","root[root] @ localhost []","localhost","","","db1" +"Query","","","show_warnings","",0,"SHOW WARNINGS","root[root] @ localhost []","localhost","","","db1" +"Ping","","","error","",0,"","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_exclude_commands= NULL","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_commands= NULL","root[root] @ localhost []","localhost","","","test" +=================================================================== diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_commands.test b/plugin/audit_log/tests/mtr/audit_log_filter_commands.test new file mode 100644 index 000000000000..efbb73524d3c --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_commands.test @@ -0,0 +1,71 @@ +# test filtering by sql_command + +# test set/unset filters + +SET GLOBAL audit_log_include_commands= 'create_table,create_procedure,create_trigger,insert'; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_commands= 'alter_db_upgrade,change_db,drop_table,drop_db'; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_commands= NULL; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +SET GLOBAL audit_log_include_commands= 'alter_db_upgrade,change_db,drop_table,drop_db'; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +SET GLOBAL audit_log_include_commands= ''; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_commands= 'insert,call_procedure,call_procedure,set_option,assign_to_keycache'; +SET GLOBAL audit_log_include_commands= NULL; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; + +SET GLOBAL audit_log_exclude_commands= "insert,call_procedure,call_procedure,set_option,assign_to_keycache"; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_commands= 'change_db,drop_table@localhost'; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_commands= NULL; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +SET GLOBAL audit_log_exclude_commands= 'change_db,drop_table'; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +SET GLOBAL audit_log_exclude_commands= ''; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_commands= 'change_db'; +SET GLOBAL audit_log_exclude_commands= NULL; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; + +# test filtering + +let $MYSQLD_DATADIR= `select @@datadir`; +let $log_file=$MYSQLD_DATADIR/test_audit.log; + +SET GLOBAL audit_log_flush=ON; +--remove_file $log_file +SET GLOBAL audit_log_flush=ON; + +# log everything +SET GLOBAL audit_log_exclude_commands= NULL; +SET GLOBAL audit_log_include_commands= NULL; + +--source audit_log_filter_commands_events.inc + +# log everything once again +SET GLOBAL audit_log_include_commands= 'set_option,creaTE_DB,CHANGE_DB,CREATE_TABLE,CREATE_TABLE,CREATE_PROCEDURE,CREATE_TRIGger,insert,insert,insert,call_procedure,call_procedure,set_option,assign_to_keycache,alter_db,alter_db_upgrade,change_db,drop_table,drop_db'; + +--source audit_log_filter_commands_events.inc + +# log some of the commands +SET GLOBAL audit_log_include_commands= 'set_option,creaTE_DB,CHANGE_DB,assign_to_keycache,alter_db,alter_db_upgrade'; + +--source audit_log_filter_commands_events.inc + +# log nothing +SET GLOBAL audit_log_include_commands= NULL; +SET GLOBAL audit_log_exclude_commands= 'set_option,create_db,change_db,create_table,create_table,create_prOCEDURE,CREATE_TRIGGER,INSERT,INSERT,INSERT,CALL_PROCEDURE,CALL_PROCEDUre,set_option,assign_to_keycache,alter_db,alter_db_upgrade,change_db,drop_table,drop_db'; + +--source audit_log_filter_commands_events.inc + +SET GLOBAL audit_log_exclude_commands= NULL; +SET GLOBAL audit_log_include_commands= NULL; + +--source audit_log_echo.inc diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_commands_events.inc b/plugin/audit_log/tests/mtr/audit_log_filter_commands_events.inc new file mode 100644 index 000000000000..362d4adbc050 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_commands_events.inc @@ -0,0 +1,28 @@ + +PING; +CREATE DATABASE db1 DEFAULT CHARACTER SET latin1; +USE db1; +CREATE TABLE t1 (a INT, b INT, KEY(b)) engine=MyISAM; +CREATE TABLE t2 (a INT, b INT, KEY(b)) engine=InnoDB; + +DELIMITER //; +CREATE PROCEDURE p1() +BEGIN + INSERT INTO t1 (a, b) VALUES (1, 1); +END// +DELIMITER ;// + +CREATE TRIGGER trigger1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 (a, b) VALUES (new.a, new.b); + +INSERT INTO t1 VALUES (5,5); +CALL p1(); + +SET GLOBAL keycache1.key_buffer_size=128*1024; +CACHE INDEX t1 IN keycache1; +ALTER DATABASE db1 DEFAULT CHARACTER SET utf8; + +USE test; +DROP TABLE db1.t1; +DROP DATABASE db1; + +PING; diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_db-master.opt b/plugin/audit_log/tests/mtr/audit_log_filter_db-master.opt new file mode 100644 index 000000000000..0adac3307e34 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_db-master.opt @@ -0,0 +1,5 @@ +--audit_log_file=test_audit.log +--audit_log_policy=ALL +--audit-log-format=CSV +--audit_log_strategy=SYNCHRONOUS +--thread_stack=16777216 \ No newline at end of file diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_db.result b/plugin/audit_log/tests/mtr/audit_log_filter_db.result new file mode 100644 index 000000000000..767e93f862f7 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_db.result @@ -0,0 +1,1582 @@ +CREATE DATABASE db1; +CREATE DATABASE db2; +CREATE DATABASE ```db3"`; +CREATE DATABASE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; +SHOW DATABASES; +Database +`db3" +db1 +db2 +information_schema +mtr +mysql +performance_schema +some_very_long,database_na'me`some_very_long_database_n"ame____q +sys +test +SET GLOBAL audit_log_include_databases= '`some_very_long,database_na\'me``some_very_long_database_n"ame____q`,```db1"`,db3'; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +`some_very_long,database_na'me``some_very_long_database_n"ame____q`,```db1"`,db3 NULL +SET GLOBAL audit_log_exclude_databases= 'db2'; +ERROR 42000: Variable 'audit_log_exclude_databases' can't be set to the value of 'db2' +SET GLOBAL audit_log_exclude_databases= NULL; +ERROR 42000: Variable 'audit_log_exclude_databases' can't be set to the value of 'NULL' +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +`some_very_long,database_na'me``some_very_long_database_n"ame____q`,```db1"`,db3 NULL +SET GLOBAL audit_log_include_databases= 'db1, db2, db3'; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +db1, db2, db3 NULL +SET GLOBAL audit_log_include_databases= ''; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases + NULL +SET GLOBAL audit_log_exclude_databases= 'db1'; +ERROR 42000: Variable 'audit_log_exclude_databases' can't be set to the value of 'db1' +SET GLOBAL audit_log_include_databases= NULL; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +NULL NULL +SET GLOBAL audit_log_exclude_databases= 'db2,`db3 `'; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +NULL db2,`db3 ` +SET GLOBAL audit_log_include_databases= 'db1, db2, db3'; +ERROR 42000: Variable 'audit_log_include_databases' can't be set to the value of 'db1, db2, db3' +SET GLOBAL audit_log_include_databases= NULL; +ERROR 42000: Variable 'audit_log_include_databases' can't be set to the value of 'NULL' +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +NULL db2,`db3 ` +SET GLOBAL audit_log_exclude_databases= 'db1, db2, db3'; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +NULL db1, db2, db3 +SET GLOBAL audit_log_exclude_databases= ''; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +NULL +SET GLOBAL audit_log_include_databases= 'db2'; +ERROR 42000: Variable 'audit_log_include_databases' can't be set to the value of 'db2' +SET GLOBAL audit_log_exclude_databases= NULL; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +NULL NULL +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_include_databases= 'db1,```db3"`'; +CREATE TABLE db1.t (a INT); +CREATE TABLE db1.trig (a INT); +CREATE TABLE db2.t (a INT); +CREATE TABLE ```db3"`.t (a INT); +CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t (a INT); +CREATE TABLE db1.words (id INT, word TEXT); +USE db1; +CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t; +CREATE VIEW vup AS SELECT * FROM db2.t; +CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +CREATE EVENT ev1 ON SCHEDULE AT CURRENT_TIMESTAMP DO BEGIN INSERT INTO t VALUES (77); INSERT INTO t VALUES (7); END// +CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100); +INSERT INTO t VALUES (1), (2), (3); +CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END// +CALL p1(); +CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END// +SET max_sp_recursion_depth = 20; +CALL p2(10); +INSERT INTO trig VALUES (1), (2), (3); +INSERT INTO words VALUES (0, 'one'), (2, 'two'), (3, 'three'); +CREATE TABLE a0 (a INT); +CREATE TABLE a1 (a INT); +CREATE TABLE a2 (a INT); +CREATE TABLE a3 (a INT); +CREATE TABLE a4 (a INT); +CREATE TABLE a5 (a INT); +CREATE TABLE a6 (a INT); +CREATE TABLE a7 (a INT); +CREATE TABLE a8 (a INT); +CREATE TABLE a9 (a INT); +CREATE TABLE a10 (a INT); +CREATE TABLE a11 (a INT); +CREATE TABLE a12 (a INT); +CREATE TABLE a13 (a INT); +CREATE TABLE a14 (a INT); +CREATE TABLE a15 (a INT); +CREATE TABLE a16 (a INT); +CREATE TABLE a17 (a INT); +CREATE TABLE a18 (a INT); +CREATE TABLE a19 (a INT); +CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a); +CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a); +CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a); +CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a); +CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a); +CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a); +CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a); +CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a); +CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a); +CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a); +CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a); +CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a); +CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a); +CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a); +CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a); +CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a); +CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a); +CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a); +CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a); +CREATE TABLE b0 (a INT); +CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END// +INSERT INTO a19 VALUES (1); +INSERT INTO b0 VALUES (1); +SELECT * FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2; +a id word a +1 NULL NULL 2 +2 2 two NULL +3 3 three NULL +77 NULL NULL NULL +7 NULL NULL NULL +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2 WHERE t.a > 2; +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2; +USE db2; +INSERT INTO t VALUES (1), (2), (3), (6); +INSERT INTO t SELECT * FROM db1.t; +USE ```db3"`; +INSERT INTO t VALUES (1), (2), (3); +USE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; +INSERT INTO t VALUES (1), (2), (3); +use db1; +INSERT INTO vup VALUES (1); +SELECT * FROM vjoin; +s a +3 3 +SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +s a +3 3 +SELECT * FROM vmat; +s +3 +SELECT a FROM vjoin; +a +3 +SELECT s FROM vjoin; +s +3 +SELECT a,s FROM vjoin; +a s +3 3 +SELECT s FROM vmat; +s +3 +SELECT a FROM vup; +a +207 +210 +101 +102 +103 +1 +2 +3 +6 +1 +2 +1 +UPDATE vup SET a=a+1; +UPDATE vjoin SET a=a+1; +USE test; +SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t); +a +1 +2 +SELECT 1; +1 +1 +DROP TABLE db1.t; +DROP TABLE db1.words; +DROP TABLE db1.trig; +DROP TABLE db2.t; +DROP TABLE ```db3"`.t; +DROP TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t; +DROP PROCEDURE db1.p1; +DROP PROCEDURE db1.p2; +DROP VIEW db1.vmat; +DROP VIEW db1.vup; +DROP VIEW db1.vjoin; +DROP TABLE db1.a0; +DROP TABLE db1.a1; +DROP TABLE db1.a2; +DROP TABLE db1.a3; +DROP TABLE db1.a4; +DROP TABLE db1.a5; +DROP TABLE db1.a6; +DROP TABLE db1.a7; +DROP TABLE db1.a8; +DROP TABLE db1.a9; +DROP TABLE db1.a10; +DROP TABLE db1.a11; +DROP TABLE db1.a12; +DROP TABLE db1.a13; +DROP TABLE db1.a14; +DROP TABLE db1.a15; +DROP TABLE db1.a16; +DROP TABLE db1.a17; +DROP TABLE db1.a18; +DROP TABLE db1.a19; +DROP TABLE db1.b0; +SET GLOBAL audit_log_include_databases= 'db2,```db3"`'; +CREATE TABLE db1.t (a INT); +CREATE TABLE db1.trig (a INT); +CREATE TABLE db2.t (a INT); +CREATE TABLE ```db3"`.t (a INT); +CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t (a INT); +CREATE TABLE db1.words (id INT, word TEXT); +USE db1; +CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t; +CREATE VIEW vup AS SELECT * FROM db2.t; +CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +CREATE EVENT ev1 ON SCHEDULE AT CURRENT_TIMESTAMP DO BEGIN INSERT INTO t VALUES (77); INSERT INTO t VALUES (7); END// +CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100); +INSERT INTO t VALUES (1), (2), (3); +CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END// +CALL p1(); +CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END// +SET max_sp_recursion_depth = 20; +CALL p2(10); +INSERT INTO trig VALUES (1), (2), (3); +INSERT INTO words VALUES (0, 'one'), (2, 'two'), (3, 'three'); +CREATE TABLE a0 (a INT); +CREATE TABLE a1 (a INT); +CREATE TABLE a2 (a INT); +CREATE TABLE a3 (a INT); +CREATE TABLE a4 (a INT); +CREATE TABLE a5 (a INT); +CREATE TABLE a6 (a INT); +CREATE TABLE a7 (a INT); +CREATE TABLE a8 (a INT); +CREATE TABLE a9 (a INT); +CREATE TABLE a10 (a INT); +CREATE TABLE a11 (a INT); +CREATE TABLE a12 (a INT); +CREATE TABLE a13 (a INT); +CREATE TABLE a14 (a INT); +CREATE TABLE a15 (a INT); +CREATE TABLE a16 (a INT); +CREATE TABLE a17 (a INT); +CREATE TABLE a18 (a INT); +CREATE TABLE a19 (a INT); +CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a); +CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a); +CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a); +CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a); +CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a); +CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a); +CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a); +CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a); +CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a); +CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a); +CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a); +CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a); +CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a); +CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a); +CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a); +CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a); +CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a); +CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a); +CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a); +CREATE TABLE b0 (a INT); +CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END// +INSERT INTO a19 VALUES (1); +INSERT INTO b0 VALUES (1); +SELECT * FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2; +a id word a +1 NULL NULL 2 +2 2 two NULL +3 3 three NULL +77 NULL NULL NULL +7 NULL NULL NULL +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2 WHERE t.a > 2; +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2; +USE db2; +INSERT INTO t VALUES (1), (2), (3), (6); +INSERT INTO t SELECT * FROM db1.t; +USE ```db3"`; +INSERT INTO t VALUES (1), (2), (3); +USE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; +INSERT INTO t VALUES (1), (2), (3); +use db1; +INSERT INTO vup VALUES (1); +SELECT * FROM vjoin; +s a +3 3 +SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +s a +3 3 +SELECT * FROM vmat; +s +3 +SELECT a FROM vjoin; +a +3 +SELECT s FROM vjoin; +s +3 +SELECT a,s FROM vjoin; +a s +3 3 +SELECT s FROM vmat; +s +3 +SELECT a FROM vup; +a +207 +210 +101 +102 +103 +1 +2 +3 +6 +1 +2 +1 +UPDATE vup SET a=a+1; +UPDATE vjoin SET a=a+1; +USE test; +SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t); +a +1 +2 +SELECT 1; +1 +1 +DROP TABLE db1.t; +DROP TABLE db1.words; +DROP TABLE db1.trig; +DROP TABLE db2.t; +DROP TABLE ```db3"`.t; +DROP TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t; +DROP PROCEDURE db1.p1; +DROP PROCEDURE db1.p2; +DROP VIEW db1.vmat; +DROP VIEW db1.vup; +DROP VIEW db1.vjoin; +DROP TABLE db1.a0; +DROP TABLE db1.a1; +DROP TABLE db1.a2; +DROP TABLE db1.a3; +DROP TABLE db1.a4; +DROP TABLE db1.a5; +DROP TABLE db1.a6; +DROP TABLE db1.a7; +DROP TABLE db1.a8; +DROP TABLE db1.a9; +DROP TABLE db1.a10; +DROP TABLE db1.a11; +DROP TABLE db1.a12; +DROP TABLE db1.a13; +DROP TABLE db1.a14; +DROP TABLE db1.a15; +DROP TABLE db1.a16; +DROP TABLE db1.a17; +DROP TABLE db1.a18; +DROP TABLE db1.a19; +DROP TABLE db1.b0; +SET GLOBAL audit_log_include_databases= NULL; +CREATE TABLE db1.t (a INT); +CREATE TABLE db1.trig (a INT); +CREATE TABLE db2.t (a INT); +CREATE TABLE ```db3"`.t (a INT); +CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t (a INT); +CREATE TABLE db1.words (id INT, word TEXT); +USE db1; +CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t; +CREATE VIEW vup AS SELECT * FROM db2.t; +CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +CREATE EVENT ev1 ON SCHEDULE AT CURRENT_TIMESTAMP DO BEGIN INSERT INTO t VALUES (77); INSERT INTO t VALUES (7); END// +CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100); +INSERT INTO t VALUES (1), (2), (3); +CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END// +CALL p1(); +CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END// +SET max_sp_recursion_depth = 20; +CALL p2(10); +INSERT INTO trig VALUES (1), (2), (3); +INSERT INTO words VALUES (0, 'one'), (2, 'two'), (3, 'three'); +CREATE TABLE a0 (a INT); +CREATE TABLE a1 (a INT); +CREATE TABLE a2 (a INT); +CREATE TABLE a3 (a INT); +CREATE TABLE a4 (a INT); +CREATE TABLE a5 (a INT); +CREATE TABLE a6 (a INT); +CREATE TABLE a7 (a INT); +CREATE TABLE a8 (a INT); +CREATE TABLE a9 (a INT); +CREATE TABLE a10 (a INT); +CREATE TABLE a11 (a INT); +CREATE TABLE a12 (a INT); +CREATE TABLE a13 (a INT); +CREATE TABLE a14 (a INT); +CREATE TABLE a15 (a INT); +CREATE TABLE a16 (a INT); +CREATE TABLE a17 (a INT); +CREATE TABLE a18 (a INT); +CREATE TABLE a19 (a INT); +CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a); +CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a); +CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a); +CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a); +CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a); +CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a); +CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a); +CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a); +CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a); +CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a); +CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a); +CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a); +CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a); +CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a); +CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a); +CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a); +CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a); +CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a); +CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a); +CREATE TABLE b0 (a INT); +CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END// +INSERT INTO a19 VALUES (1); +INSERT INTO b0 VALUES (1); +SELECT * FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2; +a id word a +1 NULL NULL 2 +2 2 two NULL +3 3 three NULL +77 NULL NULL NULL +7 NULL NULL NULL +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2 WHERE t.a > 2; +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2; +USE db2; +INSERT INTO t VALUES (1), (2), (3), (6); +INSERT INTO t SELECT * FROM db1.t; +USE ```db3"`; +INSERT INTO t VALUES (1), (2), (3); +USE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; +INSERT INTO t VALUES (1), (2), (3); +use db1; +INSERT INTO vup VALUES (1); +SELECT * FROM vjoin; +s a +3 3 +SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +s a +3 3 +SELECT * FROM vmat; +s +3 +SELECT a FROM vjoin; +a +3 +SELECT s FROM vjoin; +s +3 +SELECT a,s FROM vjoin; +a s +3 3 +SELECT s FROM vmat; +s +3 +SELECT a FROM vup; +a +207 +210 +101 +102 +103 +1 +2 +3 +6 +1 +2 +1 +UPDATE vup SET a=a+1; +UPDATE vjoin SET a=a+1; +USE test; +SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t); +a +1 +2 +SELECT 1; +1 +1 +DROP TABLE db1.t; +DROP TABLE db1.words; +DROP TABLE db1.trig; +DROP TABLE db2.t; +DROP TABLE ```db3"`.t; +DROP TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t; +DROP PROCEDURE db1.p1; +DROP PROCEDURE db1.p2; +DROP VIEW db1.vmat; +DROP VIEW db1.vup; +DROP VIEW db1.vjoin; +DROP TABLE db1.a0; +DROP TABLE db1.a1; +DROP TABLE db1.a2; +DROP TABLE db1.a3; +DROP TABLE db1.a4; +DROP TABLE db1.a5; +DROP TABLE db1.a6; +DROP TABLE db1.a7; +DROP TABLE db1.a8; +DROP TABLE db1.a9; +DROP TABLE db1.a10; +DROP TABLE db1.a11; +DROP TABLE db1.a12; +DROP TABLE db1.a13; +DROP TABLE db1.a14; +DROP TABLE db1.a15; +DROP TABLE db1.a16; +DROP TABLE db1.a17; +DROP TABLE db1.a18; +DROP TABLE db1.a19; +DROP TABLE db1.b0; +SET GLOBAL audit_log_exclude_databases= 'db1,`some_very_long,database_na\'me``some_very_long_database_n"ame____q`'; +CREATE TABLE db1.t (a INT); +CREATE TABLE db1.trig (a INT); +CREATE TABLE db2.t (a INT); +CREATE TABLE ```db3"`.t (a INT); +CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t (a INT); +CREATE TABLE db1.words (id INT, word TEXT); +USE db1; +CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t; +CREATE VIEW vup AS SELECT * FROM db2.t; +CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +CREATE EVENT ev1 ON SCHEDULE AT CURRENT_TIMESTAMP DO BEGIN INSERT INTO t VALUES (77); INSERT INTO t VALUES (7); END// +CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100); +INSERT INTO t VALUES (1), (2), (3); +CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END// +CALL p1(); +CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END// +SET max_sp_recursion_depth = 20; +CALL p2(10); +INSERT INTO trig VALUES (1), (2), (3); +INSERT INTO words VALUES (0, 'one'), (2, 'two'), (3, 'three'); +CREATE TABLE a0 (a INT); +CREATE TABLE a1 (a INT); +CREATE TABLE a2 (a INT); +CREATE TABLE a3 (a INT); +CREATE TABLE a4 (a INT); +CREATE TABLE a5 (a INT); +CREATE TABLE a6 (a INT); +CREATE TABLE a7 (a INT); +CREATE TABLE a8 (a INT); +CREATE TABLE a9 (a INT); +CREATE TABLE a10 (a INT); +CREATE TABLE a11 (a INT); +CREATE TABLE a12 (a INT); +CREATE TABLE a13 (a INT); +CREATE TABLE a14 (a INT); +CREATE TABLE a15 (a INT); +CREATE TABLE a16 (a INT); +CREATE TABLE a17 (a INT); +CREATE TABLE a18 (a INT); +CREATE TABLE a19 (a INT); +CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a); +CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a); +CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a); +CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a); +CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a); +CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a); +CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a); +CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a); +CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a); +CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a); +CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a); +CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a); +CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a); +CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a); +CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a); +CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a); +CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a); +CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a); +CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a); +CREATE TABLE b0 (a INT); +CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END// +INSERT INTO a19 VALUES (1); +INSERT INTO b0 VALUES (1); +SELECT * FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2; +a id word a +1 NULL NULL 2 +2 2 two NULL +3 3 three NULL +77 NULL NULL NULL +7 NULL NULL NULL +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2 WHERE t.a > 2; +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2; +USE db2; +INSERT INTO t VALUES (1), (2), (3), (6); +INSERT INTO t SELECT * FROM db1.t; +USE ```db3"`; +INSERT INTO t VALUES (1), (2), (3); +USE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; +INSERT INTO t VALUES (1), (2), (3); +use db1; +INSERT INTO vup VALUES (1); +SELECT * FROM vjoin; +s a +3 3 +SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +s a +3 3 +SELECT * FROM vmat; +s +3 +SELECT a FROM vjoin; +a +3 +SELECT s FROM vjoin; +s +3 +SELECT a,s FROM vjoin; +a s +3 3 +SELECT s FROM vmat; +s +3 +SELECT a FROM vup; +a +207 +210 +101 +102 +103 +1 +2 +3 +6 +1 +2 +1 +UPDATE vup SET a=a+1; +UPDATE vjoin SET a=a+1; +USE test; +SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t); +a +1 +2 +SELECT 1; +1 +1 +DROP TABLE db1.t; +DROP TABLE db1.words; +DROP TABLE db1.trig; +DROP TABLE db2.t; +DROP TABLE ```db3"`.t; +DROP TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t; +DROP PROCEDURE db1.p1; +DROP PROCEDURE db1.p2; +DROP VIEW db1.vmat; +DROP VIEW db1.vup; +DROP VIEW db1.vjoin; +DROP TABLE db1.a0; +DROP TABLE db1.a1; +DROP TABLE db1.a2; +DROP TABLE db1.a3; +DROP TABLE db1.a4; +DROP TABLE db1.a5; +DROP TABLE db1.a6; +DROP TABLE db1.a7; +DROP TABLE db1.a8; +DROP TABLE db1.a9; +DROP TABLE db1.a10; +DROP TABLE db1.a11; +DROP TABLE db1.a12; +DROP TABLE db1.a13; +DROP TABLE db1.a14; +DROP TABLE db1.a15; +DROP TABLE db1.a16; +DROP TABLE db1.a17; +DROP TABLE db1.a18; +DROP TABLE db1.a19; +DROP TABLE db1.b0; +SET GLOBAL audit_log_exclude_databases= 'db1,db2'; +CREATE TABLE db1.t (a INT); +CREATE TABLE db1.trig (a INT); +CREATE TABLE db2.t (a INT); +CREATE TABLE ```db3"`.t (a INT); +CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t (a INT); +CREATE TABLE db1.words (id INT, word TEXT); +USE db1; +CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t; +CREATE VIEW vup AS SELECT * FROM db2.t; +CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +CREATE EVENT ev1 ON SCHEDULE AT CURRENT_TIMESTAMP DO BEGIN INSERT INTO t VALUES (77); INSERT INTO t VALUES (7); END// +CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100); +INSERT INTO t VALUES (1), (2), (3); +CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END// +CALL p1(); +CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END// +SET max_sp_recursion_depth = 20; +CALL p2(10); +INSERT INTO trig VALUES (1), (2), (3); +INSERT INTO words VALUES (0, 'one'), (2, 'two'), (3, 'three'); +CREATE TABLE a0 (a INT); +CREATE TABLE a1 (a INT); +CREATE TABLE a2 (a INT); +CREATE TABLE a3 (a INT); +CREATE TABLE a4 (a INT); +CREATE TABLE a5 (a INT); +CREATE TABLE a6 (a INT); +CREATE TABLE a7 (a INT); +CREATE TABLE a8 (a INT); +CREATE TABLE a9 (a INT); +CREATE TABLE a10 (a INT); +CREATE TABLE a11 (a INT); +CREATE TABLE a12 (a INT); +CREATE TABLE a13 (a INT); +CREATE TABLE a14 (a INT); +CREATE TABLE a15 (a INT); +CREATE TABLE a16 (a INT); +CREATE TABLE a17 (a INT); +CREATE TABLE a18 (a INT); +CREATE TABLE a19 (a INT); +CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a); +CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a); +CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a); +CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a); +CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a); +CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a); +CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a); +CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a); +CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a); +CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a); +CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a); +CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a); +CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a); +CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a); +CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a); +CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a); +CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a); +CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a); +CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a); +CREATE TABLE b0 (a INT); +CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END// +INSERT INTO a19 VALUES (1); +INSERT INTO b0 VALUES (1); +SELECT * FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2; +a id word a +1 NULL NULL 2 +2 2 two NULL +3 3 three NULL +77 NULL NULL NULL +7 NULL NULL NULL +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2 WHERE t.a > 2; +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2; +USE db2; +INSERT INTO t VALUES (1), (2), (3), (6); +INSERT INTO t SELECT * FROM db1.t; +USE ```db3"`; +INSERT INTO t VALUES (1), (2), (3); +USE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; +INSERT INTO t VALUES (1), (2), (3); +use db1; +INSERT INTO vup VALUES (1); +SELECT * FROM vjoin; +s a +3 3 +SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +s a +3 3 +SELECT * FROM vmat; +s +3 +SELECT a FROM vjoin; +a +3 +SELECT s FROM vjoin; +s +3 +SELECT a,s FROM vjoin; +a s +3 3 +SELECT s FROM vmat; +s +3 +SELECT a FROM vup; +a +207 +210 +101 +102 +103 +1 +2 +3 +6 +1 +2 +1 +UPDATE vup SET a=a+1; +UPDATE vjoin SET a=a+1; +USE test; +SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t); +a +1 +2 +SELECT 1; +1 +1 +DROP TABLE db1.t; +DROP TABLE db1.words; +DROP TABLE db1.trig; +DROP TABLE db2.t; +DROP TABLE ```db3"`.t; +DROP TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t; +DROP PROCEDURE db1.p1; +DROP PROCEDURE db1.p2; +DROP VIEW db1.vmat; +DROP VIEW db1.vup; +DROP VIEW db1.vjoin; +DROP TABLE db1.a0; +DROP TABLE db1.a1; +DROP TABLE db1.a2; +DROP TABLE db1.a3; +DROP TABLE db1.a4; +DROP TABLE db1.a5; +DROP TABLE db1.a6; +DROP TABLE db1.a7; +DROP TABLE db1.a8; +DROP TABLE db1.a9; +DROP TABLE db1.a10; +DROP TABLE db1.a11; +DROP TABLE db1.a12; +DROP TABLE db1.a13; +DROP TABLE db1.a14; +DROP TABLE db1.a15; +DROP TABLE db1.a16; +DROP TABLE db1.a17; +DROP TABLE db1.a18; +DROP TABLE db1.a19; +DROP TABLE db1.b0; +SET GLOBAL audit_log_exclude_databases= NULL; +set global audit_log_flush= ON; +=================================================================== +"Query","","","set_option","",0,"SET GLOBAL audit_log_flush=ON","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_databases= 'db1,```db3""`'","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.trig (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db2.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE ```db3""`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.words (id INT, word TEXT)","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"USE db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vup AS SELECT * FROM db2.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"INSERT INTO t VALUES (77)","root[root] @ localhost [localhost]","localhost","","localhost","" +"Query","","","create_trigger","",0,"CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p1()","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","set_option","",0,"SET max_sp_recursion_depth = 20","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p2(10)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO trig VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO words VALUES (0, 'one'), (2, 'two'), (3, 'three')","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a1 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a2 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a3 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a4 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a5 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a6 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a7 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a8 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a9 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a10 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a11 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a12 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a13 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a14 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a15 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a16 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a17 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a18 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a19 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE b0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a0 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a1 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a2 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a3 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a4 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a5 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a6 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a7 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a8 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a9 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a10 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a11 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a12 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a13 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a14 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a15 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a16 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a17 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a18 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a19 VALUES (1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @tmp=1","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @abc='cba'","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO b0 VALUES (1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2","root[root] @ localhost []","localhost","","","db1" +"Query","","","delete_multi","",0,"DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2 WHERE t.a > 2","root[root] @ localhost []","localhost","","","db1" +"Query","","","delete_multi","",0,"DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE db2","root[root] @ localhost []","localhost","","","db2" +"Query","","","insert_select","",0,"INSERT INTO t SELECT * FROM db1.t","root[root] @ localhost []","localhost","","","db2" +"Query","","","change_db","",0,"USE ```db3""`","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","change_db","",0,"USE `some_very_long,database_na'me``some_very_long_database_n""ame____q`","root[root] @ localhost []","localhost","","","some_very_long,database_na'me``some_very_long_database_n""ame____q" +"Query","","","change_db","",0,"use db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vmat","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT s FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a,s FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT s FROM vmat","root[root] @ localhost []","localhost","","","db1" +"Query","","","update","",0,"UPDATE vjoin SET a=a+1","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE test","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t)","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT 1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.words","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.trig","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db2.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE ```db3""`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vmat","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vup","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vjoin","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a0","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a3","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a4","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a5","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a6","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a7","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a8","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a9","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a10","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a11","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a12","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a13","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a14","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a15","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a16","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a17","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a18","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a19","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.b0","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_databases= 'db2,```db3""`'","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.trig (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db2.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE ```db3""`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.words (id INT, word TEXT)","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"USE db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vup AS SELECT * FROM db2.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"INSERT INTO db2.t VALUES (207)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p1()","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","set_option","",0,"SET max_sp_recursion_depth = 20","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"INSERT INTO db2.t VALUES (200 + a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p2(10)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO trig VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a1 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a2 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a3 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a4 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a5 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a6 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a7 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a8 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a9 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a10 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a11 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a12 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a13 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a14 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a15 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a16 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a17 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a18 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a19 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE b0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @tmp=1","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @abc='cba'","root[root] @ localhost []","localhost","","","db1" +"Query","","","delete_multi","",0,"DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE db2","root[root] @ localhost []","localhost","","","db2" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3), (6)","root[root] @ localhost []","localhost","","","db2" +"Query","","","insert_select","",0,"INSERT INTO t SELECT * FROM db1.t","root[root] @ localhost []","localhost","","","db2" +"Query","","","change_db","",0,"USE ```db3""`","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","change_db","",0,"USE `some_very_long,database_na'me``some_very_long_database_n""ame____q`","root[root] @ localhost []","localhost","","","some_very_long,database_na'me``some_very_long_database_n""ame____q" +"Query","","","change_db","",0,"use db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO vup VALUES (1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT s FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a,s FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a FROM vup","root[root] @ localhost []","localhost","","","db1" +"Query","","","update","",0,"UPDATE vup SET a=a+1","root[root] @ localhost []","localhost","","","db1" +"Query","","","update","",0,"UPDATE vjoin SET a=a+1","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE test","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT 1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.words","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.trig","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db2.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE ```db3""`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vmat","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vup","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vjoin","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a0","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a3","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a4","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a5","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a6","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a7","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a8","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a9","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a10","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a11","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a12","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a13","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a14","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a15","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a16","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a17","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a18","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a19","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.b0","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_databases= NULL","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.trig (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db2.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE ```db3""`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.words (id INT, word TEXT)","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"USE db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vup AS SELECT * FROM db2.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"INSERT INTO t VALUES (77)","root[root] @ localhost [localhost]","localhost","","localhost","" +"Query","","","create_trigger","",0,"CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"INSERT INTO db2.t VALUES (207)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p1()","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","set_option","",0,"SET max_sp_recursion_depth = 20","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"INSERT INTO db2.t VALUES (200 + a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p2(10)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO trig VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO words VALUES (0, 'one'), (2, 'two'), (3, 'three')","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a1 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a2 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a3 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a4 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a5 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a6 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a7 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a8 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a9 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a10 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a11 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a12 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a13 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a14 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a15 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a16 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a17 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a18 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a19 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE b0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a0 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a1 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a2 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a3 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a4 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a5 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a6 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a7 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a8 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a9 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a10 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a11 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a12 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a13 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a14 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a15 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a16 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a17 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a18 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a19 VALUES (1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @tmp=1","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @abc='cba'","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO b0 VALUES (1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2","root[root] @ localhost []","localhost","","","db1" +"Query","","","delete_multi","",0,"DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2 WHERE t.a > 2","root[root] @ localhost []","localhost","","","db1" +"Query","","","delete_multi","",0,"DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE db2","root[root] @ localhost []","localhost","","","db2" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3), (6)","root[root] @ localhost []","localhost","","","db2" +"Query","","","insert_select","",0,"INSERT INTO t SELECT * FROM db1.t","root[root] @ localhost []","localhost","","","db2" +"Query","","","change_db","",0,"USE ```db3""`","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","change_db","",0,"USE `some_very_long,database_na'me``some_very_long_database_n""ame____q`","root[root] @ localhost []","localhost","","","some_very_long,database_na'me``some_very_long_database_n""ame____q" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","some_very_long,database_na'me``some_very_long_database_n""ame____q" +"Query","","","change_db","",0,"use db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO vup VALUES (1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vmat","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT s FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a,s FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT s FROM vmat","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a FROM vup","root[root] @ localhost []","localhost","","","db1" +"Query","","","update","",0,"UPDATE vup SET a=a+1","root[root] @ localhost []","localhost","","","db1" +"Query","","","update","",0,"UPDATE vjoin SET a=a+1","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE test","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t)","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT 1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.words","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.trig","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db2.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE ```db3""`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vmat","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vup","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vjoin","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a0","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a3","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a4","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a5","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a6","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a7","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a8","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a9","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a10","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a11","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a12","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a13","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a14","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a15","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a16","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a17","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a18","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a19","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.b0","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_exclude_databases= 'db1,`some_very_long,database_na\'me``some_very_long_database_n""ame____q`'","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.trig (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db2.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE ```db3""`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.words (id INT, word TEXT)","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"USE db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vup AS SELECT * FROM db2.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"INSERT INTO db2.t VALUES (207)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p1()","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","set_option","",0,"SET max_sp_recursion_depth = 20","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"INSERT INTO db2.t VALUES (200 + a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p2(10)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO trig VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a1 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a2 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a3 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a4 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a5 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a6 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a7 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a8 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a9 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a10 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a11 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a12 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a13 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a14 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a15 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a16 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a17 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a18 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a19 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE b0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @tmp=1","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @abc='cba'","root[root] @ localhost []","localhost","","","db1" +"Query","","","delete_multi","",0,"DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE db2","root[root] @ localhost []","localhost","","","db2" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3), (6)","root[root] @ localhost []","localhost","","","db2" +"Query","","","insert_select","",0,"INSERT INTO t SELECT * FROM db1.t","root[root] @ localhost []","localhost","","","db2" +"Query","","","change_db","",0,"USE ```db3""`","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","change_db","",0,"USE `some_very_long,database_na'me``some_very_long_database_n""ame____q`","root[root] @ localhost []","localhost","","","some_very_long,database_na'me``some_very_long_database_n""ame____q" +"Query","","","change_db","",0,"use db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO vup VALUES (1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT s FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a,s FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a FROM vup","root[root] @ localhost []","localhost","","","db1" +"Query","","","update","",0,"UPDATE vup SET a=a+1","root[root] @ localhost []","localhost","","","db1" +"Query","","","update","",0,"UPDATE vjoin SET a=a+1","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE test","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT 1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.words","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.trig","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db2.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE ```db3""`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vmat","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vup","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vjoin","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a0","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a3","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a4","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a5","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a6","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a7","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a8","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a9","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a10","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a11","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a12","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a13","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a14","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a15","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a16","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a17","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a18","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a19","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.b0","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_exclude_databases= 'db1,db2'","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.trig (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db2.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE ```db3""`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.words (id INT, word TEXT)","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"USE db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vup AS SELECT * FROM db2.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p1()","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","set_option","",0,"SET max_sp_recursion_depth = 20","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p2(10)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a1 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a2 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a3 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a4 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a5 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a6 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a7 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a8 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a9 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a10 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a11 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a12 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a13 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a14 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a15 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a16 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a17 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a18 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a19 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE b0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @tmp=1","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @abc='cba'","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE db2","root[root] @ localhost []","localhost","","","db2" +"Query","","","change_db","",0,"USE ```db3""`","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","change_db","",0,"USE `some_very_long,database_na'me``some_very_long_database_n""ame____q`","root[root] @ localhost []","localhost","","","some_very_long,database_na'me``some_very_long_database_n""ame____q" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","some_very_long,database_na'me``some_very_long_database_n""ame____q" +"Query","","","change_db","",0,"use db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE test","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t)","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT 1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.words","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.trig","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db2.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE ```db3""`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vmat","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vup","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vjoin","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a0","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a3","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a4","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a5","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a6","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a7","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a8","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a9","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a10","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a11","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a12","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a13","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a14","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a15","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a16","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a17","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a18","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a19","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.b0","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_exclude_databases= NULL","root[root] @ localhost []","localhost","","","test" +=================================================================== +DROP DATABASE db1; +DROP DATABASE db2; +DROP DATABASE ```db3"`; +DROP DATABASE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_db.test b/plugin/audit_log/tests/mtr/audit_log_filter_db.test new file mode 100644 index 000000000000..39c24cbbb098 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_db.test @@ -0,0 +1,85 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +let $log_file=$MYSQLD_DATADIR/test_audit.log; + +# setup some databases + +CREATE DATABASE db1; +CREATE DATABASE db2; +CREATE DATABASE ```db3"`; +CREATE DATABASE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; + +SHOW DATABASES; + +# test set/unset filters + +SET GLOBAL audit_log_include_databases= '`some_very_long,database_na\'me``some_very_long_database_n"ame____q`,```db1"`,db3'; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_databases= 'db2'; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_databases= NULL; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +SET GLOBAL audit_log_include_databases= 'db1, db2, db3'; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +SET GLOBAL audit_log_include_databases= ''; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_databases= 'db1'; +SET GLOBAL audit_log_include_databases= NULL; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; + +SET GLOBAL audit_log_exclude_databases= 'db2,`db3 `'; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_databases= 'db1, db2, db3'; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_databases= NULL; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +SET GLOBAL audit_log_exclude_databases= 'db1, db2, db3'; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +SET GLOBAL audit_log_exclude_databases= ''; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_databases= 'db2'; +SET GLOBAL audit_log_exclude_databases= NULL; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; + +let $MYSQLD_DATADIR= `select @@datadir`; +let MYSQLD_DATADIR= $MYSQLD_DATADIR; + +SET GLOBAL audit_log_flush=ON; +--remove_file $MYSQLD_DATADIR/test_audit.log +SET GLOBAL audit_log_flush=ON; + +# testing include +SET GLOBAL audit_log_include_databases= 'db1,```db3"`'; + +--source audit_log_filter_db_events.inc + +SET GLOBAL audit_log_include_databases= 'db2,```db3"`'; + +--source audit_log_filter_db_events.inc + +# log everything +SET GLOBAL audit_log_include_databases= NULL; + +--source audit_log_filter_db_events.inc + +# testing exclude +SET GLOBAL audit_log_exclude_databases= 'db1,`some_very_long,database_na\'me``some_very_long_database_n"ame____q`'; + +--source audit_log_filter_db_events.inc + +SET GLOBAL audit_log_exclude_databases= 'db1,db2'; + +--source audit_log_filter_db_events.inc + +SET GLOBAL audit_log_exclude_databases= NULL; + +--source audit_log_echo.inc + +# cleanup databases +DROP DATABASE db1; +DROP DATABASE db2; +DROP DATABASE ```db3"`; +DROP DATABASE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_db_events.inc b/plugin/audit_log/tests/mtr/audit_log_filter_db_events.inc new file mode 100644 index 000000000000..7bf2d4dbfb76 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_db_events.inc @@ -0,0 +1,119 @@ +CREATE TABLE db1.t (a INT); +CREATE TABLE db1.trig (a INT); +CREATE TABLE db2.t (a INT); +CREATE TABLE ```db3"`.t (a INT); +CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t (a INT); + +CREATE TABLE db1.words (id INT, word TEXT); + +USE db1; + +CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t; +CREATE VIEW vup AS SELECT * FROM db2.t; +CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; + +DELIMITER //; +CREATE EVENT ev1 ON SCHEDULE AT CURRENT_TIMESTAMP DO BEGIN INSERT INTO t VALUES (77); INSERT INTO t VALUES (7); END// +DELIMITER ;// +let $wait_condition=SELECT count(*)=1 FROM t WHERE a=7; +let $wait_timeout=600; +--source include/wait_condition.inc +CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100); +INSERT INTO t VALUES (1), (2), (3); +DELIMITER //; +CREATE PROCEDURE p1() +BEGIN + INSERT INTO db2.t VALUES (207); +END// +DELIMITER ;// +CALL p1(); +DELIMITER //; +CREATE PROCEDURE p2(a INT) +BEGIN + INSERT INTO db2.t VALUES (200 + a); + IF a = 0 THEN + CALL p2(a - 1); + END IF; +END// +DELIMITER ;// +SET max_sp_recursion_depth = 20; +CALL p2(10); +INSERT INTO trig VALUES (1), (2), (3); +INSERT INTO words VALUES (0, 'one'), (2, 'two'), (3, 'three'); + +let $i=0; +while ($i < 20) +{ +eval CREATE TABLE a$i (a INT); +inc $i; +} + +let $i=1; +let $j=0; +while ($i < 20) +{ +eval CREATE TRIGGER tr$i BEFORE INSERT ON a$i FOR EACH ROW INSERT INTO a$j VALUES (new.a); +inc $i; +inc $j; +} + +CREATE TABLE b0 (a INT); +DELIMITER //; +CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END// +DELIMITER ;// + +INSERT INTO a19 VALUES (1); + +INSERT INTO b0 VALUES (1); + +SELECT * FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2; +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2 WHERE t.a > 2; +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2; + +USE db2; +INSERT INTO t VALUES (1), (2), (3), (6); +INSERT INTO t SELECT * FROM db1.t; + +USE ```db3"`; +INSERT INTO t VALUES (1), (2), (3); + +USE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; +INSERT INTO t VALUES (1), (2), (3); + +use db1; +INSERT INTO vup VALUES (1); +SELECT * FROM vjoin; +SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +SELECT * FROM vmat; +SELECT a FROM vjoin; +SELECT s FROM vjoin; +SELECT a,s FROM vjoin; +SELECT s FROM vmat; +SELECT a FROM vup; +UPDATE vup SET a=a+1; +UPDATE vjoin SET a=a+1; + +USE test; + +SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t); + +SELECT 1; + +DROP TABLE db1.t; +DROP TABLE db1.words; +DROP TABLE db1.trig; +DROP TABLE db2.t; +DROP TABLE ```db3"`.t; +DROP TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t; +DROP PROCEDURE db1.p1; +DROP PROCEDURE db1.p2; +DROP VIEW db1.vmat; +DROP VIEW db1.vup; +DROP VIEW db1.vjoin; +let $i=0; +while ($i < 20) +{ +eval DROP TABLE db1.a$i; +inc $i; +} +DROP TABLE db1.b0; diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_events.inc b/plugin/audit_log/tests/mtr/audit_log_filter_events.inc new file mode 100644 index 000000000000..9103f954a691 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_events.inc @@ -0,0 +1,43 @@ +--source include/count_sessions.inc + +connect (test,127.0.0.1,user1,password1,,$MASTER_PORT,); +connection test; +SELECT 'user1'; +disconnect test; +connection default; +--source include/wait_until_count_sessions.inc + +connect (test,localhost,user22,password1,,); +connection test; +SELECT 'user22'; +disconnect test; +connection default; +--source include/wait_until_count_sessions.inc + +connect (test,localhost,22user,password1,,); +connection test; +SELECT '22user'; +change_user user22,password1; +SELECT 'user22'; +disconnect test; +connection default; +--source include/wait_until_count_sessions.inc + +connect (test,127.0.0.1,admin,password1,,$MASTER_PORT,); +connection test; +SELECT 'admin'; +disconnect test; +connection default; +--source include/wait_until_count_sessions.inc + +connect (test,localhost,"us,er1",password1,,); +connection test; +SELECT 'us,er1'; +disconnect test; +connection default; +--source include/wait_until_count_sessions.inc + +connection default; + +connection default; +--source include/wait_until_count_sessions.inc diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_users-master.opt b/plugin/audit_log/tests/mtr/audit_log_filter_users-master.opt new file mode 100644 index 000000000000..033178764ec5 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_users-master.opt @@ -0,0 +1,4 @@ +--audit_log_file=test_audit.log +--audit_log_policy=ALL +--audit-log-format=CSV +--audit_log_strategy=SYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_users.result b/plugin/audit_log/tests/mtr/audit_log_filter_users.result new file mode 100644 index 000000000000..50f350175f67 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_users.result @@ -0,0 +1,213 @@ +CREATE USER 'user1'@'127.0.0.1' IDENTIFIED BY 'password1'; +CREATE USER 'user22'@'%' IDENTIFIED BY 'password1'; +CREATE USER '22user'@'LOCALHOST' IDENTIFIED BY 'password1'; +CREATE USER 'admin'@'%' IDENTIFIED BY 'password1'; +CREATE USER 'us,er1'@'localhost' IDENTIFIED BY 'password1'; +SET GLOBAL audit_log_include_accounts= 'user1@localhost,, user22@127.0.0.1,admin@%'; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +user1@localhost,, user22@127.0.0.1,admin@% NULL +SET GLOBAL audit_log_exclude_accounts= '22useer@localhost'; +ERROR 42000: Variable 'audit_log_exclude_accounts' can't be set to the value of '22useer@localhost' +SET GLOBAL audit_log_exclude_accounts= NULL; +ERROR 42000: Variable 'audit_log_exclude_accounts' can't be set to the value of 'NULL' +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +user1@localhost,, user22@127.0.0.1,admin@% NULL +SET GLOBAL audit_log_include_accounts= 'user1@localhost, user2@localhost, user3@localhost'; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +user1@localhost, user2@localhost, user3@localhost NULL +SET GLOBAL audit_log_include_accounts= ''; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts + NULL +SET GLOBAL audit_log_exclude_accounts= '22useer@localhost'; +ERROR 42000: Variable 'audit_log_exclude_accounts' can't be set to the value of '22useer@localhost' +SET GLOBAL audit_log_include_accounts= NULL; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +NULL NULL +SET GLOBAL audit_log_exclude_accounts= "'us,er1'@'localhost',, user22@127.0.0.1,admin@%"; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +NULL 'us,er1'@'localhost',, user22@127.0.0.1,admin@% +SET GLOBAL audit_log_include_accounts= '22useer@localhost'; +ERROR 42000: Variable 'audit_log_include_accounts' can't be set to the value of '22useer@localhost' +SET GLOBAL audit_log_include_accounts= NULL; +ERROR 42000: Variable 'audit_log_include_accounts' can't be set to the value of 'NULL' +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +NULL 'us,er1'@'localhost',, user22@127.0.0.1,admin@% +SET GLOBAL audit_log_exclude_accounts= 'user1@localhost, user2@localhost, user3@localhost'; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +NULL user1@localhost, user2@localhost, user3@localhost +SET GLOBAL audit_log_exclude_accounts= ''; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +NULL +SET GLOBAL audit_log_include_accounts= '22useer@localhost'; +ERROR 42000: Variable 'audit_log_include_accounts' can't be set to the value of '22useer@localhost' +SET GLOBAL audit_log_exclude_accounts= NULL; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +NULL NULL +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_include_accounts= ''; +SELECT 'user1'; +user1 +user1 +SELECT 'user22'; +user22 +user22 +SELECT '22user'; +22user +22user +SELECT 'user22'; +user22 +user22 +SELECT 'admin'; +admin +admin +SELECT 'us,er1'; +us,er1 +us,er1 +SET GLOBAL audit_log_include_accounts= 'user1@localhost,, user22@127.0.0.1,admin@%,veryveryveryveryveryveryveryveryveryveryveryveryveryveryveryloooooooooooooongusername@veryveryveryveryveryveryveryveryveryveryveryveryveryveryveryloooooooooooooonghostname'; +SELECT 'user1'; +user1 +user1 +SELECT 'user22'; +user22 +user22 +SELECT '22user'; +22user +22user +SELECT 'user22'; +user22 +user22 +SELECT 'admin'; +admin +admin +SELECT 'us,er1'; +us,er1 +us,er1 +SET GLOBAL audit_log_include_accounts= NULL; +SELECT 'user1'; +user1 +user1 +SELECT 'user22'; +user22 +user22 +SELECT '22user'; +22user +22user +SELECT 'user22'; +user22 +user22 +SELECT 'admin'; +admin +admin +SELECT 'us,er1'; +us,er1 +us,er1 +SET GLOBAL audit_log_exclude_accounts= 'user1@localhost,, user22@127.0.0.1,admin@%'; +SELECT 'user1'; +user1 +user1 +SELECT 'user22'; +user22 +user22 +SELECT '22user'; +22user +22user +SELECT 'user22'; +user22 +user22 +SELECT 'admin'; +admin +admin +SELECT 'us,er1'; +us,er1 +us,er1 +SET GLOBAL audit_log_exclude_accounts= NULL; +SET GLOBAL audit_log_include_accounts= '22user@LocalHost,User22@%'; +SELECT 'user1'; +user1 +user1 +SELECT 'user22'; +user22 +user22 +SELECT '22user'; +22user +22user +SELECT 'user22'; +user22 +user22 +SELECT 'admin'; +admin +admin +SELECT 'us,er1'; +us,er1 +us,er1 +SET GLOBAL audit_log_include_accounts= NULL; +set global audit_log_flush= ON; +=================================================================== +"Query","","","set_option","",0,"SET GLOBAL audit_log_flush=ON","root[root] @ localhost []","localhost","","","test" +************************************************************* +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_accounts= ''","root[root] @ localhost []","localhost","","","test" +************************************************************* +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_accounts= 'user1@localhost,, user22@127.0.0.1,admin@%,veryveryveryveryveryveryveryveryveryveryveryveryveryveryveryloooooooooooooongusername@veryveryveryveryveryveryveryveryveryveryveryveryveryveryveryloooooooooooooonghostname'","root[root] @ localhost []","localhost","","","test" +"Connect","","","",0,"admin","admin","","","localhost","127.0.0.1","test" +"Query","","","select","",0,"SELECT 'admin'","admin[admin] @ localhost [127.0.0.1]","localhost","","127.0.0.1","test" +"Quit","","","",0,"admin","admin","","","localhost","127.0.0.1","test" +************************************************************* +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_accounts= NULL","root[root] @ localhost []","localhost","","","test" +"Connect","","","",0,"user1","user1","","","localhost","127.0.0.1","test" +"Query","","","select","",0,"SELECT 'user1'","user1[user1] @ localhost [127.0.0.1]","localhost","","127.0.0.1","test" +"Quit","","","",0,"user1","user1","","","localhost","127.0.0.1","test" +"Connect","","","",0,"user22","user22","","","localhost","","test" +"Query","","","select","",0,"SELECT 'user22'","user22[user22] @ localhost []","localhost","","","test" +"Quit","","","",0,"user22","user22","","","localhost","","test" +"Connect","","","",0,"22user","22user","","","localhost","","test" +"Query","","","select","",0,"SELECT '22user'","22user[22user] @ localhost []","localhost","","","test" +"Change user","","","",0,"user22","user22","","","localhost","","" +"Query","","","select","",0,"SELECT 'user22'","user22[user22] @ localhost []","localhost","","","" +"Quit","","","",0,"user22","user22","","","localhost","","" +"Connect","","","",0,"admin","admin","","","localhost","127.0.0.1","test" +"Query","","","select","",0,"SELECT 'admin'","admin[admin] @ localhost [127.0.0.1]","localhost","","127.0.0.1","test" +"Quit","","","",0,"admin","admin","","","localhost","127.0.0.1","test" +"Connect","","","",0,"us,er1","us,er1","","","localhost","","test" +"Query","","","select","",0,"SELECT 'us,er1'","us,er1[us,er1] @ localhost []","localhost","","","test" +"Quit","","","",0,"us,er1","us,er1","","","localhost","","test" +************************************************************* +"Query","","","set_option","",0,"SET GLOBAL audit_log_exclude_accounts= 'user1@localhost,, user22@127.0.0.1,admin@%'","root[root] @ localhost []","localhost","","","test" +"Connect","","","",0,"user1","user1","","","localhost","127.0.0.1","test" +"Query","","","select","",0,"SELECT 'user1'","user1[user1] @ localhost [127.0.0.1]","localhost","","127.0.0.1","test" +"Quit","","","",0,"user1","user1","","","localhost","127.0.0.1","test" +"Connect","","","",0,"user22","user22","","","localhost","","test" +"Query","","","select","",0,"SELECT 'user22'","user22[user22] @ localhost []","localhost","","","test" +"Quit","","","",0,"user22","user22","","","localhost","","test" +"Connect","","","",0,"22user","22user","","","localhost","","test" +"Query","","","select","",0,"SELECT '22user'","22user[22user] @ localhost []","localhost","","","test" +"Change user","","","",0,"user22","user22","","","localhost","","" +"Query","","","select","",0,"SELECT 'user22'","user22[user22] @ localhost []","localhost","","","" +"Quit","","","",0,"user22","user22","","","localhost","","" +"Connect","","","",0,"us,er1","us,er1","","","localhost","","test" +"Query","","","select","",0,"SELECT 'us,er1'","us,er1[us,er1] @ localhost []","localhost","","","test" +"Quit","","","",0,"us,er1","us,er1","","","localhost","","test" +************************************************************* +"Query","","","set_option","",0,"SET GLOBAL audit_log_exclude_accounts= NULL","root[root] @ localhost []","localhost","","","test" +************************************************************* +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_accounts= '22user@LocalHost,User22@%'","root[root] @ localhost []","localhost","","","test" +"Connect","","","",0,"22user","22user","","","localhost","","test" +"Query","","","select","",0,"SELECT '22user'","22user[22user] @ localhost []","localhost","","","test" +************************************************************* +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_accounts= NULL","root[root] @ localhost []","localhost","","","test" +=================================================================== +DROP USER 'user1'@'127.0.0.1'; +DROP USER 'user22'@'%'; +DROP USER '22user'@'localhost'; +DROP USER 'admin'@'%'; +DROP USER 'us,er1'@'localhost'; diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_users.test b/plugin/audit_log/tests/mtr/audit_log_filter_users.test new file mode 100644 index 000000000000..f8b915c76a99 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_users.test @@ -0,0 +1,86 @@ +# setup some user accounts + +CREATE USER 'user1'@'127.0.0.1' IDENTIFIED BY 'password1'; +CREATE USER 'user22'@'%' IDENTIFIED BY 'password1'; +CREATE USER '22user'@'LOCALHOST' IDENTIFIED BY 'password1'; +CREATE USER 'admin'@'%' IDENTIFIED BY 'password1'; +CREATE USER 'us,er1'@'localhost' IDENTIFIED BY 'password1'; + +# test set/unset filters + +SET GLOBAL audit_log_include_accounts= 'user1@localhost,, user22@127.0.0.1,admin@%'; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_accounts= '22useer@localhost'; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_accounts= NULL; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +SET GLOBAL audit_log_include_accounts= 'user1@localhost, user2@localhost, user3@localhost'; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +SET GLOBAL audit_log_include_accounts= ''; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_accounts= '22useer@localhost'; +SET GLOBAL audit_log_include_accounts= NULL; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; + +SET GLOBAL audit_log_exclude_accounts= "'us,er1'@'localhost',, user22@127.0.0.1,admin@%"; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_accounts= '22useer@localhost'; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_accounts= NULL; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +SET GLOBAL audit_log_exclude_accounts= 'user1@localhost, user2@localhost, user3@localhost'; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +SET GLOBAL audit_log_exclude_accounts= ''; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_accounts= '22useer@localhost'; +SET GLOBAL audit_log_exclude_accounts= NULL; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; + +let $MYSQLD_DATADIR= `select @@datadir`; +let $log_file=$MYSQLD_DATADIR/test_audit.log; + +SET GLOBAL audit_log_flush=ON; +--remove_file $log_file +SET GLOBAL audit_log_flush=ON; + +# log nothing +SET GLOBAL audit_log_include_accounts= ''; + +--source audit_log_filter_events.inc + +# testing include +SET GLOBAL audit_log_include_accounts= 'user1@localhost,, user22@127.0.0.1,admin@%,veryveryveryveryveryveryveryveryveryveryveryveryveryveryveryloooooooooooooongusername@veryveryveryveryveryveryveryveryveryveryveryveryveryveryveryloooooooooooooonghostname'; + +--source audit_log_filter_events.inc + +# log everything +SET GLOBAL audit_log_include_accounts= NULL; + +--source audit_log_filter_events.inc + +# testing exclude +SET GLOBAL audit_log_exclude_accounts= 'user1@localhost,, user22@127.0.0.1,admin@%'; + +--source audit_log_filter_events.inc + +SET GLOBAL audit_log_exclude_accounts= NULL; + +# testing case sensitivity +SET GLOBAL audit_log_include_accounts= '22user@LocalHost,User22@%'; + +--source audit_log_filter_events.inc + +SET GLOBAL audit_log_include_accounts= NULL; + +--source audit_log_echo.inc + +# cleanup users +DROP USER 'user1'@'127.0.0.1'; +DROP USER 'user22'@'%'; +DROP USER '22user'@'localhost'; +DROP USER 'admin'@'%'; +DROP USER 'us,er1'@'localhost'; diff --git a/plugin/audit_log/tests/mtr/audit_log_install_bug1435606-master.opt b/plugin/audit_log/tests/mtr/audit_log_install_bug1435606-master.opt new file mode 100644 index 000000000000..af01e3de44d7 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_install_bug1435606-master.opt @@ -0,0 +1 @@ +--audit_log_file=/path/does/not/exist/audit.log diff --git a/plugin/audit_log/tests/mtr/audit_log_install_bug1435606.result b/plugin/audit_log/tests/mtr/audit_log_install_bug1435606.result new file mode 100644 index 000000000000..773641dd1599 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_install_bug1435606.result @@ -0,0 +1,7 @@ +call mtr.add_suppression("Plugin 'audit_log' init function returned error"); +call mtr.add_suppression("Plugin 'audit_log' registration as a AUDIT failed"); +call mtr.add_suppression("Plugin audit_log reported: 'Cannot open file"); +call mtr.add_suppression("Plugin audit_log reported: 'Error: No such file or directory'"); +SELECT COUNT(*) AS should_be_0 FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = 'audit_log'; +should_be_0 +0 diff --git a/plugin/audit_log/tests/mtr/audit_log_install_bug1435606.test b/plugin/audit_log/tests/mtr/audit_log_install_bug1435606.test new file mode 100644 index 000000000000..2d205893c188 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_install_bug1435606.test @@ -0,0 +1,9 @@ +# Bug1435606: server crashes if audit log plugin cannot create file + +call mtr.add_suppression("Plugin 'audit_log' init function returned error"); +call mtr.add_suppression("Plugin 'audit_log' registration as a AUDIT failed"); +call mtr.add_suppression("Plugin audit_log reported: 'Cannot open file"); +call mtr.add_suppression("Plugin audit_log reported: 'Error: No such file or directory'"); + +# Should have failed to install +SELECT COUNT(*) AS should_be_0 FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = 'audit_log'; diff --git a/plugin/audit_log/tests/mtr/audit_log_json-master.opt b/plugin/audit_log/tests/mtr/audit_log_json-master.opt new file mode 100644 index 000000000000..0e988b238f8b --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_json-master.opt @@ -0,0 +1,3 @@ +--audit_log_file=test_audit.log +--audit-log-format=JSON +--audit_log_strategy=SEMISYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_json.result b/plugin/audit_log/tests/mtr/audit_log_json.result new file mode 100644 index 000000000000..1abd6a550728 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_json.result @@ -0,0 +1,169 @@ +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +CREATE TABLE t1 (c1 INT, c2 CHAR(20)); +CREATE TABLE t1 +(c1 INT, +c2 CHAR(20)); +ERROR 42S01: Table 't1' already exists +INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c'); +INSERT INTO `t1` VALUES (4,NULL); +INSERT INTO `t1` VALUES (6,''); +INSERT INTO `t1` VALUES (7,''); +INSERT INTO `t1` VALUES (8,''); +INSERT INTO `t1` VALUES (9,''); +INSERT INTO `t1` VALUES (10,''); +INSERT INTO `t1` VALUES (11,''); +INSERT INTO `t1` VALUES (12,''); +INSERT INTO `t1` VALUES (13,''); +INSERT INTO `t1` VALUES (14,' '); +INSERT INTO `t1` VALUES (15,' +'); +INSERT INTO `t1` VALUES (16,' '); +INSERT INTO `t1` VALUES (17,' '); +INSERT INTO `t1` VALUES (18,' '); +INSERT INTO `t1` VALUES (19,''); +INSERT INTO `t1` VALUES (20,''); +INSERT INTO `t1` VALUES (21,''); +INSERT INTO `t1` VALUES (22,''); +INSERT INTO `t1` VALUES (23,''); +INSERT INTO `t1` VALUES (24,''); +INSERT INTO `t1` VALUES (25,''); +INSERT INTO `t1` VALUES (26,''); +INSERT INTO `t1` VALUES (27,''); +INSERT INTO `t1` VALUES (28,''); +INSERT INTO `t1` VALUES (29,''); +INSERT INTO `t1` VALUES (30,''); +INSERT INTO `t1` VALUES (31,''); +INSERT INTO `t1` VALUES (32,''); +INSERT INTO `t1` VALUES (33,''); +INSERT INTO `t1` VALUES (34,''); +INSERT INTO `t1` VALUES (35,''); +INSERT INTO `t1` VALUES (36,''); +SELECT * FROM t1; +c1 c2 +1 a +2 b +3 c +4 NULL +6  +7  +8  +9  +10  +11  +12  +13  +14 +15 + +16 +17 +18 +19  +20  +21  +22  +23  +24  +25  +26  +27  +28  +29  +30  +31  +32  +33  +34  +35  +36  +SELECT * FROM t2; +ERROR 42S02: Table 'test.t2' doesn't exist +DROP TABLE t1; +PREPARE stmt1 FROM 'SELECT 1'; +EXECUTE stmt1; +1 +1 +SHOW STATUS LIKE 'audit_log%'; +Variable_name Value +DEALLOCATE PREPARE stmt1; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 1048576 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file test_audit.log +audit_log_flush OFF +audit_log_format JSON +audit_log_handler FILE +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy ALL +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy SEMISYNCHRONOUS +audit_log_syslog_facility LOG_USER +audit_log_syslog_ident percona-audit +audit_log_syslog_priority LOG_INFO +connect(localhost,no_such_user,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'no_such_user'@'localhost' (using password: NO) +create table t1 (id int); +create table t2 (id int); +insert into t1 values (1), (2); +insert into t2 values (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2); +select * from t1; +id +1 +2 +alter table t1 rename renamed_t1; +select * from t_doesnt_exist; +ERROR 42S02: Table 'test.t_doesnt_exist' doesn't exist +syntax_error_query; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'syntax_error_query' at line 1 +drop table renamed_t1, t2; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 1048576 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file test_audit.log +audit_log_flush OFF +audit_log_format JSON +audit_log_handler FILE +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy ALL +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy SEMISYNCHRONOUS +audit_log_syslog_facility LOG_USER +audit_log_syslog_ident percona-audit +audit_log_syslog_priority LOG_INFO +create database sa_db; +create table t1 (id2 int); +insert into t1 values (1), (2); +select * from t1; +id2 +1 +2 +drop table t1; +use sa_db; +create table sa_t1(id int); +insert into sa_t1 values (1), (2); +drop table sa_t1; +drop database sa_db; +create user 'jeffrey'@'localhost' IDENTIFIED BY 'mypass'; +drop user 'jeffrey'@'localhost'; +select '&;&&&""""<><<>>>>'; +&;&&&""""<><<>>>> +&;&&&""""<><<>>>> +select '  + /"\\'; +/"\ +  + /"\ +set global audit_log_flush= ON; diff --git a/plugin/audit_log/tests/mtr/audit_log_json.test b/plugin/audit_log/tests/mtr/audit_log_json.test new file mode 100644 index 000000000000..f1760f22a147 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_json.test @@ -0,0 +1,45 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +let MYSQLD_DATADIR= $MYSQLD_DATADIR; + +SET GLOBAL audit_log_flush=ON; +--remove_file $MYSQLD_DATADIR/test_audit.log +SET GLOBAL audit_log_flush=ON; + +--let $test_control_chars=1; +--source audit_log_events.inc + +--move_file $MYSQLD_DATADIR/test_audit.log $MYSQLD_DATADIR/test_audit_json.log +set global audit_log_flush= ON; +perl; + eval "use JSON qw(decode_json); 1" or exit 0; + open my $file, $ENV{'MYSQLD_DATADIR'} . '/test_audit_json.log' or die "Could not open log: $!"; + my $found_1st_control_char = 0; + my $last_control_char = 0; + my $control_char_count = 0; + while (my $line = <$file>) { + my $json = decode_json($line); + my $entry_type = $json->{audit_record}->{name}; + if($entry_type eq "Query") { + my $query = $json->{audit_record}->{sqltext}; + my @query_chars = sort($query =~ /./sg); + my $minimum_character = ord($query_chars[0]); + if ($minimum_character == 1) { + $found_1st_control_char = 1; + } + if ($found_1st_control_char && $control_char_count < 31) { + $control_char_count = $control_char_count + 1; + my $expected = $last_control_char + 1; + if ($expected != $minimum_character) { + print "Incorrect control character in output: Expected $expected, got $minimum_character\n"; + exit l; + } + $last_control_char = $minimum_character; + } + } + } + if ($control_char_count != 31) { + print "Missing control characters from the output. Expected 31, got $control_char_count\n"; + exit 2; + } + close $file; +EOF diff --git a/plugin/audit_log/tests/mtr/audit_log_long_records-master.opt b/plugin/audit_log/tests/mtr/audit_log_long_records-master.opt new file mode 100644 index 000000000000..1a976d9f6076 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_long_records-master.opt @@ -0,0 +1,4 @@ +--audit_log_file=test_audit.log +--audit_log_buffer_size=8192 +--audit_log_strategy=ASYNCHRONOUS +--audit_log_format=OLD diff --git a/plugin/audit_log/tests/mtr/audit_log_long_records.result b/plugin/audit_log/tests/mtr/audit_log_long_records.result new file mode 100644 index 000000000000..62cad62a6523 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_long_records.result @@ -0,0 +1,3 @@ +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush= ON; diff --git a/plugin/audit_log/tests/mtr/audit_log_long_records.test b/plugin/audit_log/tests/mtr/audit_log_long_records.test new file mode 100644 index 000000000000..220cd7d44950 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_long_records.test @@ -0,0 +1,40 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +let MYSQLD_DATADIR= $MYSQLD_DATADIR; + +SET GLOBAL audit_log_flush=ON; +--remove_file $MYSQLD_DATADIR/test_audit.log +SET GLOBAL audit_log_flush=ON; + +let $i= 2; +let $xx= xx; +while ($i < 3500) +{ +let $xx= x$xx; +inc $i; +} +--disable_query_log +--disable_result_log +while ($i < 8500) +{ +eval SELECT '$xx'; +let $xx= x$xx; +inc $i; +} +while ($i < 9000) +{ +eval SELECT '$xx'; +let $xx= xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx$xx; +inc $i; +} +--enable_query_log +--enable_result_log + +--move_file $MYSQLD_DATADIR/test_audit.log $MYSQLD_DATADIR/test_audit_long.log +SET GLOBAL audit_log_flush= ON; +perl; + eval "use XML::Parser; 1" or exit 0; + $p = new XML::Parser; + $p->parsefile($ENV{'MYSQLD_DATADIR'} . '/test_audit_long.log'); +EOF +--remove_file $MYSQLD_DATADIR/test_audit.log +--remove_file $MYSQLD_DATADIR/test_audit_long.log diff --git a/plugin/audit_log/tests/mtr/audit_log_many_connections.result b/plugin/audit_log/tests/mtr/audit_log_many_connections.result new file mode 100644 index 000000000000..395d3a6bffae --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_many_connections.result @@ -0,0 +1,7 @@ +SET @saved_max_connections = @@global.max_connections; +SET GLOBAL max_connections = 2; +connect(localhost,root,,test,MYSQL_PORT,MYSQL_SOCK); +ERROR HY000: Too many connections +connect(localhost,root,,test,MYSQL_PORT,MYSQL_SOCK); +ERROR HY000: Too many connections +SET GLOBAL max_connections= @saved_max_connections; diff --git a/plugin/audit_log/tests/mtr/audit_log_many_connections.test b/plugin/audit_log/tests/mtr/audit_log_many_connections.test new file mode 100644 index 000000000000..8fcd7350e2b6 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_many_connections.test @@ -0,0 +1,31 @@ +# +# Bug #1633988: Assertion `thd == _current_thd()' failed +# +# Test audit log handling of too many connections error +# + +--disable_query_log +call mtr.add_suppression("Too many connections"); +--enable_query_log + +--source include/count_sessions.inc + +SET @saved_max_connections = @@global.max_connections; +SET GLOBAL max_connections = 2; + +--connect (con1, localhost, root) +--connect (con2, localhost, root) +--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK +--error ER_CON_COUNT_ERROR +--connect (con3, localhost, root) +--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK +--error ER_CON_COUNT_ERROR +--connect (con4, localhost, root) + +connection default; +SET GLOBAL max_connections= @saved_max_connections; + +--disconnect con2 +--disconnect con1 + +--source include/wait_until_count_sessions.inc diff --git a/plugin/audit_log/tests/mtr/audit_log_new-master.opt b/plugin/audit_log/tests/mtr/audit_log_new-master.opt new file mode 100644 index 000000000000..d4fdcdc41943 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_new-master.opt @@ -0,0 +1,4 @@ +--audit_log_file=test_audit.log +--audit_log_policy=LOGINS +--audit-log-format=NEW +--audit_log_strategy=SEMISYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_new.result b/plugin/audit_log/tests/mtr/audit_log_new.result new file mode 100644 index 000000000000..301af4f4c887 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_new.result @@ -0,0 +1,96 @@ +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +CREATE TABLE t1 (c1 INT, c2 CHAR(20)); +CREATE TABLE t1 +(c1 INT, +c2 CHAR(20)); +ERROR 42S01: Table 't1' already exists +INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c'); +SELECT * FROM t1; +c1 c2 +1 a +2 b +3 c +SELECT * FROM t2; +ERROR 42S02: Table 'test.t2' doesn't exist +DROP TABLE t1; +PREPARE stmt1 FROM 'SELECT 1'; +EXECUTE stmt1; +1 +1 +SHOW STATUS LIKE 'audit_log%'; +Variable_name Value +DEALLOCATE PREPARE stmt1; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 1048576 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file test_audit.log +audit_log_flush OFF +audit_log_format NEW +audit_log_handler FILE +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy LOGINS +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy SEMISYNCHRONOUS +audit_log_syslog_facility LOG_USER +audit_log_syslog_ident percona-audit +audit_log_syslog_priority LOG_INFO +connect(localhost,no_such_user,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'no_such_user'@'localhost' (using password: NO) +create table t1 (id int); +create table t2 (id int); +insert into t1 values (1), (2); +insert into t2 values (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2); +select * from t1; +id +1 +2 +alter table t1 rename renamed_t1; +select * from t_doesnt_exist; +ERROR 42S02: Table 'test.t_doesnt_exist' doesn't exist +syntax_error_query; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'syntax_error_query' at line 1 +drop table renamed_t1, t2; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 1048576 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file test_audit.log +audit_log_flush OFF +audit_log_format NEW +audit_log_handler FILE +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy LOGINS +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy SEMISYNCHRONOUS +audit_log_syslog_facility LOG_USER +audit_log_syslog_ident percona-audit +audit_log_syslog_priority LOG_INFO +create database sa_db; +create table t1 (id2 int); +insert into t1 values (1), (2); +select * from t1; +id2 +1 +2 +drop table t1; +use sa_db; +create table sa_t1(id int); +insert into sa_t1 values (1), (2); +drop table sa_t1; +drop database sa_db; +select '&;&&&""""<><<>>>>'; +&;&&&""""<><<>>>> +&;&&&""""<><<>>>> +set global audit_log_flush= ON; diff --git a/plugin/audit_log/tests/mtr/audit_log_new.test b/plugin/audit_log/tests/mtr/audit_log_new.test new file mode 100644 index 000000000000..fafd6881f2dc --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_new.test @@ -0,0 +1,18 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +let MYSQLD_DATADIR= $MYSQLD_DATADIR; + +SET GLOBAL audit_log_flush=ON; +--remove_file $MYSQLD_DATADIR/test_audit.log +SET GLOBAL audit_log_flush=ON; + +--source audit_log_events.inc + +--move_file $MYSQLD_DATADIR/test_audit.log $MYSQLD_DATADIR/test_audit_new.log +set global audit_log_flush= ON; +perl; + eval "use XML::Parser; 1" or exit 0; + $p = new XML::Parser; + $p->parsefile($ENV{'MYSQLD_DATADIR'} . '/test_audit_new.log'); +EOF +--remove_file $MYSQLD_DATADIR/test_audit.log +--remove_file $MYSQLD_DATADIR/test_audit_new.log diff --git a/plugin/audit_log/tests/mtr/audit_log_old-master.opt b/plugin/audit_log/tests/mtr/audit_log_old-master.opt new file mode 100644 index 000000000000..2524574eb92a --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_old-master.opt @@ -0,0 +1,3 @@ +--audit_log_file=test_audit.log +--audit_log_buffer_size=4096 +--audit_log_strategy=ASYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_old.result b/plugin/audit_log/tests/mtr/audit_log_old.result new file mode 100644 index 000000000000..d9923de6069c --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_old.result @@ -0,0 +1,96 @@ +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +CREATE TABLE t1 (c1 INT, c2 CHAR(20)); +CREATE TABLE t1 +(c1 INT, +c2 CHAR(20)); +ERROR 42S01: Table 't1' already exists +INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c'); +SELECT * FROM t1; +c1 c2 +1 a +2 b +3 c +SELECT * FROM t2; +ERROR 42S02: Table 'test.t2' doesn't exist +DROP TABLE t1; +PREPARE stmt1 FROM 'SELECT 1'; +EXECUTE stmt1; +1 +1 +SHOW STATUS LIKE 'audit_log%'; +Variable_name Value +DEALLOCATE PREPARE stmt1; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 4096 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file test_audit.log +audit_log_flush OFF +audit_log_format OLD +audit_log_handler FILE +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy ALL +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy ASYNCHRONOUS +audit_log_syslog_facility LOG_USER +audit_log_syslog_ident percona-audit +audit_log_syslog_priority LOG_INFO +connect(localhost,no_such_user,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'no_such_user'@'localhost' (using password: NO) +create table t1 (id int); +create table t2 (id int); +insert into t1 values (1), (2); +insert into t2 values (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2); +select * from t1; +id +1 +2 +alter table t1 rename renamed_t1; +select * from t_doesnt_exist; +ERROR 42S02: Table 'test.t_doesnt_exist' doesn't exist +syntax_error_query; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'syntax_error_query' at line 1 +drop table renamed_t1, t2; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 4096 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file test_audit.log +audit_log_flush OFF +audit_log_format OLD +audit_log_handler FILE +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy ALL +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy ASYNCHRONOUS +audit_log_syslog_facility LOG_USER +audit_log_syslog_ident percona-audit +audit_log_syslog_priority LOG_INFO +create database sa_db; +create table t1 (id2 int); +insert into t1 values (1), (2); +select * from t1; +id2 +1 +2 +drop table t1; +use sa_db; +create table sa_t1(id int); +insert into sa_t1 values (1), (2); +drop table sa_t1; +drop database sa_db; +select '&;&&&""""<><<>>>>'; +&;&&&""""<><<>>>> +&;&&&""""<><<>>>> +set global audit_log_flush= ON; diff --git a/plugin/audit_log/tests/mtr/audit_log_old.test b/plugin/audit_log/tests/mtr/audit_log_old.test new file mode 100644 index 000000000000..ad312e99e595 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_old.test @@ -0,0 +1,18 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +let MYSQLD_DATADIR= $MYSQLD_DATADIR; + +SET GLOBAL audit_log_flush=ON; +--remove_file $MYSQLD_DATADIR/test_audit.log +SET GLOBAL audit_log_flush=ON; + +--source audit_log_events.inc + +--move_file $MYSQLD_DATADIR/test_audit.log $MYSQLD_DATADIR/test_audit_old.log +set global audit_log_flush= ON; +perl; + eval "use XML::Parser; 1" or exit 0; + $p = new XML::Parser; + $p->parsefile($ENV{'MYSQLD_DATADIR'} . '/test_audit_old.log'); +EOF +# --remove_file $MYSQLD_DATADIR/test_audit.log +# --remove_file $MYSQLD_DATADIR/test_audit_old.log diff --git a/plugin/audit_log/tests/mtr/audit_log_rotate-master.opt b/plugin/audit_log/tests/mtr/audit_log_rotate-master.opt new file mode 100644 index 000000000000..36b97341b9e8 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_rotate-master.opt @@ -0,0 +1,6 @@ +--audit_log_file=test_audit.log +--audit_log_format=JSON +--audit_log_strategy=SEMISYNCHRONOUS +--audit_log_rotate_on_size=4096 +--audit_log_buffer_size=5000 +--audit_log_rotations=10 diff --git a/plugin/audit_log/tests/mtr/audit_log_rotate.result b/plugin/audit_log/tests/mtr/audit_log_rotate.result new file mode 100644 index 000000000000..c66c32f360eb --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_rotate.result @@ -0,0 +1,14 @@ +# +# Rotate with "rotations" and "size" given as startup options +# +success +# +# PS-4950: Invalid audit log file size when audit_log_rotations is changed during runtime +# +SET @audit_log_rotations_orig = @@audit_log_rotations; +SET @audit_log_rotate_on_size_orig = @@audit_log_rotate_on_size; +SET GLOBAL audit_log_rotations = 3; +SET GLOBAL audit_log_rotate_on_size = 4096; +SET GLOBAL audit_log_rotations = @audit_log_rotations_orig; +SET GLOBAL audit_log_rotate_on_size = @audit_log_rotate_on_size_orig; +success diff --git a/plugin/audit_log/tests/mtr/audit_log_rotate.test b/plugin/audit_log/tests/mtr/audit_log_rotate.test new file mode 100644 index 000000000000..c8bc53e9a1d2 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_rotate.test @@ -0,0 +1,67 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +let MYSQLD_DATADIR= $MYSQLD_DATADIR; + +--echo # +--echo # Rotate with "rotations" and "size" given as startup options +--echo # + +--disable_result_log +--disable_query_log +--source audit_log_events.inc +--source audit_log_events.inc +--source audit_log_events.inc +--source audit_log_events.inc +--enable_query_log +--enable_result_log + +perl; + eval "use JSON qw(decode_json); 1" or exit 0; + my @files = glob ($ENV{'MYSQLD_DATADIR'} . "/test_audit.log.[0-9][0-9]"); + foreach (@files) { + open my $file, $_ or die "Could not open log: $!"; + while (my $line = <$file>) { + decode_json($line); + } + close $file; + } + die "Rotation doesn't work!" unless scalar(@files) > 1 +EOF + +--echo success + +--echo # +--echo # PS-4950: Invalid audit log file size when audit_log_rotations is changed during runtime +--echo # + +SET @audit_log_rotations_orig = @@audit_log_rotations; +SET @audit_log_rotate_on_size_orig = @@audit_log_rotate_on_size; +SET GLOBAL audit_log_rotations = 3; +SET GLOBAL audit_log_rotate_on_size = 4096; + +--remove_files_wildcard $MYSQLD_DATADIR test_audit.log.* + +--disable_result_log +--disable_query_log +--source audit_log_events.inc +--source audit_log_events.inc +--source audit_log_events.inc +--source audit_log_events.inc +--enable_query_log +--enable_result_log + +perl; + my @files = glob ($ENV{'MYSQLD_DATADIR'} . "/test_audit.log.[0-9][0-9]"); + foreach (@files) { + my $size = -s $_; + print $_; + die "Files are too small!" unless $size >= 4096; + } + die "Too many rotations!" unless scalar(@files) <= 3; +EOF + +SET GLOBAL audit_log_rotations = @audit_log_rotations_orig; +SET GLOBAL audit_log_rotate_on_size = @audit_log_rotate_on_size_orig; + +--remove_files_wildcard $MYSQLD_DATADIR test_audit.log* + +--echo success diff --git a/plugin/audit_log/tests/mtr/audit_log_startup.result b/plugin/audit_log/tests/mtr/audit_log_startup.result new file mode 100644 index 000000000000..c49a551253e5 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_startup.result @@ -0,0 +1,29 @@ +call mtr.add_suppression("Plugin 'audit_log' init function returned error."); +call mtr.add_suppression("Plugin 'audit_log' registration as a AUDIT failed."); +call mtr.add_suppression("Plugin audit_log reported: 'Both"); +call mtr.add_suppression("Plugin audit_log reported: 'Cannot open file"); +call mtr.add_suppression("Plugin audit_log reported: 'Error: No such file or directory'"); +# restart: --audit_log_exclude_accounts=user@localhost +SET GLOBAL audit_log_exclude_accounts='info@localhost'; +# restart: --audit_log_include_accounts=user@localhost +SET GLOBAL audit_log_include_accounts='info@localhost'; +# restart: --audit_log_exclude_commands=alter_table +SET GLOBAL audit_log_exclude_commands='insert'; +# restart: --audit_log_include_commands=alter_table +SET GLOBAL audit_log_include_commands='insert'; +# restart: --audit_log_exclude_databases=test +SET GLOBAL audit_log_exclude_databases='mysql'; +# restart: --audit_log_include_databases=test +SET GLOBAL audit_log_include_databases='mysql'; +# restart: --audit_log_exclude_accounts=user@localhost --audit_log_include_accounts=user@localhost +SHOW VARIABLES LIKE 'audit_log%'; +Variable_name Value +# restart: --audit_log_exclude_commands=delete --audit_log_include_commands=delete +SHOW VARIABLES LIKE 'audit_log%'; +Variable_name Value +# restart: --audit_log_exclude_databases=test --audit_log_include_databases=test +SHOW VARIABLES LIKE 'audit_log%'; +Variable_name Value +# restart: --audit_log_exclude_accounts=user@localhost --audit_log_file=./directory/file +SHOW VARIABLES LIKE 'audit_log%'; +Variable_name Value diff --git a/plugin/audit_log/tests/mtr/audit_log_startup.test b/plugin/audit_log/tests/mtr/audit_log_startup.test new file mode 100644 index 000000000000..f11302b23a5b --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_startup.test @@ -0,0 +1,70 @@ +# Bug 1641910: Trying to set audit_log_exclude_accounts crashes server. + +call mtr.add_suppression("Plugin 'audit_log' init function returned error."); +call mtr.add_suppression("Plugin 'audit_log' registration as a AUDIT failed."); +call mtr.add_suppression("Plugin audit_log reported: 'Both"); +call mtr.add_suppression("Plugin audit_log reported: 'Cannot open file"); +call mtr.add_suppression("Plugin audit_log reported: 'Error: No such file or directory'"); + +--let $restart_parameters="restart: --audit_log_exclude_accounts='user@localhost'" +--source include/restart_mysqld.inc + +SET GLOBAL audit_log_exclude_accounts='info@localhost'; + + +--let $restart_parameters="restart: --audit_log_include_accounts='user@localhost'" +--source include/restart_mysqld.inc + +SET GLOBAL audit_log_include_accounts='info@localhost'; + + +--let $restart_parameters="restart: --audit_log_exclude_commands='alter_table'" +--source include/restart_mysqld.inc + +SET GLOBAL audit_log_exclude_commands='insert'; + + +--let $restart_parameters="restart: --audit_log_include_commands='alter_table'" +--source include/restart_mysqld.inc + +SET GLOBAL audit_log_include_commands='insert'; + + +--let $restart_parameters="restart: --audit_log_exclude_databases='test'" +--source include/restart_mysqld.inc + +SET GLOBAL audit_log_exclude_databases='mysql'; + + +--let $restart_parameters="restart: --audit_log_include_databases='test'" +--source include/restart_mysqld.inc + +SET GLOBAL audit_log_include_databases='mysql'; + + +--let $restart_parameters="restart: --audit_log_exclude_accounts='user@localhost' --audit_log_include_accounts='user@localhost'" +--source include/restart_mysqld.inc + +# there should be no audit_log% variables shown +SHOW VARIABLES LIKE 'audit_log%'; + + +--let $restart_parameters="restart: --audit_log_exclude_commands='delete' --audit_log_include_commands='delete'" +--source include/restart_mysqld.inc + +# there should be no audit_log% variables shown +SHOW VARIABLES LIKE 'audit_log%'; + + +--let $restart_parameters="restart: --audit_log_exclude_databases='test' --audit_log_include_databases='test'" +--source include/restart_mysqld.inc + +# there should be no audit_log% variables shown +SHOW VARIABLES LIKE 'audit_log%'; + + +--let $restart_parameters="restart: --audit_log_exclude_accounts='user@localhost' --audit_log_file=./directory/file" +--source include/restart_mysqld.inc + +# there should be no audit_log% variables shown +SHOW VARIABLES LIKE 'audit_log%'; diff --git a/plugin/audit_log/tests/mtr/audit_log_syslog-master.opt b/plugin/audit_log/tests/mtr/audit_log_syslog-master.opt new file mode 100644 index 000000000000..eb692e1c268f --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_syslog-master.opt @@ -0,0 +1,5 @@ +--audit-log-format=CSV +--audit_log_handler=SYSLOG +--audit_log_syslog_ident=test_audit +--audit_log_syslog_facility=LOG_AUTH +--audit_log_syslog_priority=LOG_INFO diff --git a/plugin/audit_log/tests/mtr/audit_log_syslog.result b/plugin/audit_log/tests/mtr/audit_log_syslog.result new file mode 100644 index 000000000000..4515f8770e8e --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_syslog.result @@ -0,0 +1,93 @@ +CREATE TABLE t1 (c1 INT, c2 CHAR(20)); +CREATE TABLE t1 +(c1 INT, +c2 CHAR(20)); +ERROR 42S01: Table 't1' already exists +INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c'); +SELECT * FROM t1; +c1 c2 +1 a +2 b +3 c +SELECT * FROM t2; +ERROR 42S02: Table 'test.t2' doesn't exist +DROP TABLE t1; +PREPARE stmt1 FROM 'SELECT 1'; +EXECUTE stmt1; +1 +1 +SHOW STATUS LIKE 'audit_log%'; +Variable_name Value +DEALLOCATE PREPARE stmt1; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 1048576 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file audit.log +audit_log_flush OFF +audit_log_format CSV +audit_log_handler SYSLOG +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy ALL +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy ASYNCHRONOUS +audit_log_syslog_facility LOG_AUTH +audit_log_syslog_ident test_audit +audit_log_syslog_priority LOG_INFO +connect(localhost,no_such_user,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'no_such_user'@'localhost' (using password: NO) +create table t1 (id int); +create table t2 (id int); +insert into t1 values (1), (2); +insert into t2 values (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2); +select * from t1; +id +1 +2 +alter table t1 rename renamed_t1; +select * from t_doesnt_exist; +ERROR 42S02: Table 'test.t_doesnt_exist' doesn't exist +syntax_error_query; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'syntax_error_query' at line 1 +drop table renamed_t1, t2; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 1048576 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file audit.log +audit_log_flush OFF +audit_log_format CSV +audit_log_handler SYSLOG +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy ALL +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy ASYNCHRONOUS +audit_log_syslog_facility LOG_AUTH +audit_log_syslog_ident test_audit +audit_log_syslog_priority LOG_INFO +create database sa_db; +create table t1 (id2 int); +insert into t1 values (1), (2); +select * from t1; +id2 +1 +2 +drop table t1; +use sa_db; +create table sa_t1(id int); +insert into sa_t1 values (1), (2); +drop table sa_t1; +drop database sa_db; +select '&;&&&""""<><<>>>>'; +&;&&&""""<><<>>>> +&;&&&""""<><<>>>> diff --git a/plugin/audit_log/tests/mtr/audit_log_syslog.test b/plugin/audit_log/tests/mtr/audit_log_syslog.test new file mode 100644 index 000000000000..20cded3282a3 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_syslog.test @@ -0,0 +1 @@ +--source audit_log_events.inc diff --git a/plugin/audit_log/tests/mtr/audit_log_threadpool-master.opt b/plugin/audit_log/tests/mtr/audit_log_threadpool-master.opt new file mode 100644 index 000000000000..7aae3717b96d --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_threadpool-master.opt @@ -0,0 +1,3 @@ +--audit_log_strategy=SYNCHRONOUS +--audit_log_file=test_audit_threadpool.log +--thread_handling=pool-of-threads diff --git a/plugin/audit_log/tests/mtr/audit_log_threadpool.result b/plugin/audit_log/tests/mtr/audit_log_threadpool.result new file mode 100644 index 000000000000..f78eb60b346c --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_threadpool.result @@ -0,0 +1,10 @@ +set global audit_log_flush= ON; +set global audit_log_flush= ON; +select 1; +1 +1 +select '1more'; +1more +1more +set global audit_log_flush= ON; +1 connects, 2 disconnects diff --git a/plugin/audit_log/tests/mtr/audit_log_threadpool.test b/plugin/audit_log/tests/mtr/audit_log_threadpool.test new file mode 100644 index 000000000000..2f3c4faae709 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_threadpool.test @@ -0,0 +1,38 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +let MYSQLD_DATADIR= $MYSQLD_DATADIR; + +set global audit_log_flush= ON; +--remove_file $MYSQLD_DATADIR/test_audit_threadpool.log +set global audit_log_flush= ON; + +--source include/count_sessions.inc +connect (con1,localhost,root,,mysql); +select 1; +connection default; +disconnect con1; +--source include/wait_until_count_sessions.inc + +connection default; +select '1more'; + +--move_file $MYSQLD_DATADIR/test_audit_threadpool.log $MYSQLD_DATADIR/test_audit_threadpool_done.log +set global audit_log_flush= ON; + +perl; + use strict; + use warnings; + + my $logfile = $ENV{'MYSQLD_DATADIR'} . '/test_audit_threadpool_done.log'; + + open(FILE, $logfile) or die "cannot open $logfile!"; + my @buf = ; + close(FILE); + + my $connect_count = grep(/Connect/, @buf); + my $disconnect_count = grep(/Quit/, @buf); + + print "$connect_count connects, $disconnect_count disconnects\n"; +EOF + +--remove_file $MYSQLD_DATADIR/test_audit_threadpool.log +--remove_file $MYSQLD_DATADIR/test_audit_threadpool_done.log diff --git a/plugin/audit_log/tests/mtr/audit_log_xml_escape-master.opt b/plugin/audit_log/tests/mtr/audit_log_xml_escape-master.opt new file mode 100644 index 000000000000..5db58a723e3d --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_xml_escape-master.opt @@ -0,0 +1,4 @@ +--audit_log_file=test_audit.log +--audit_log_policy=QUERIES +--audit-log-format=NEW +--audit_log_strategy=SEMISYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_xml_escape.result b/plugin/audit_log/tests/mtr/audit_log_xml_escape.result new file mode 100644 index 000000000000..868241b4a393 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_xml_escape.result @@ -0,0 +1,10 @@ +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +CREATE TABLE a (id int) ENGINE = InnoDB; +INSERT INTO a +VALUES (1); +INSERT INTO a VALUES (2); +INSERT INTO a VALUES (3); +set global audit_log_flush= ON; +Escape rules Ok +DROP TABLE a; diff --git a/plugin/audit_log/tests/mtr/audit_log_xml_escape.test b/plugin/audit_log/tests/mtr/audit_log_xml_escape.test new file mode 100644 index 000000000000..d1a115611ea8 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_xml_escape.test @@ -0,0 +1,62 @@ +--let $MYSQL_TMP_DIR = `select @@tmpdir` +--let $MYSQLD_DATADIR = `select @@datadir` +--let $TMP_QUERY_FILE = $MYSQL_TMP_DIR/tmp_query + +--let MYSQLD_DATADIR = $MYSQLD_DATADIR +--let TMP_QUERY_FILE = $TMP_QUERY_FILE + +--perl + use strict; + use warnings; + + my $query_file = $ENV{'TMP_QUERY_FILE'} or die "TMP_QUERY_FILE not set"; + open(my $fh, ">", $query_file) or die "Can't open < $query_file: $!"; + + print $fh "CREATE TABLE a (id int) ENGINE = InnoDB;\n"; + print $fh "INSERT INTO a \nVALUES (1);\n"; + print $fh "INSERT INTO a \tVALUES (2);\n"; + print $fh "INSERT INTO a \rVALUES (3);\n"; + + close($fh); +EOF + +SET GLOBAL audit_log_flush=ON; +--remove_file $MYSQLD_DATADIR/test_audit.log +SET GLOBAL audit_log_flush=ON; + +--source $TMP_QUERY_FILE +--move_file $MYSQLD_DATADIR/test_audit.log $MYSQLD_DATADIR/test_audit_new.log +set global audit_log_flush= ON; + +perl; + use strict; + use warnings; + + my $log_file = "$ENV{MYSQLD_DATADIR}/test_audit_new.log"; + open(my $fh, "<", $log_file) or die "Can't open < $log_file: $!"; + + my $escape_seq_count = 0; + my $expected_escape_seq_count = 3; + + while (my $line = <$fh>) { + if ($line =~ m/INSERT\sINTO\sa\s VALUES\s\(1\)/ + || $line =~ m/INSERT\sINTO\sa\s VALUES\s\(2\)/ + || $line =~ m/INSERT\sINTO\sa\s VALUES\s\(3\)/) { + ++$escape_seq_count; + } + } + + if ($escape_seq_count == $expected_escape_seq_count) { + print "Escape rules Ok\n"; + } + else { + print "Found $escape_seq_count escape sequences, expected $expected_escape_seq_count\n"; + } + + close($fh); +EOF + +--remove_file $MYSQLD_DATADIR/test_audit.log +--remove_file $MYSQLD_DATADIR/test_audit_new.log + +DROP TABLE a; diff --git a/plugin/audit_log/tests/mtr/percona_bug_ps3867.result b/plugin/audit_log/tests/mtr/percona_bug_ps3867.result new file mode 100644 index 000000000000..ee83ce6f8a44 --- /dev/null +++ b/plugin/audit_log/tests/mtr/percona_bug_ps3867.result @@ -0,0 +1,37 @@ +CREATE USER 'user1'@'127.0.0.1' IDENTIFIED BY 'password1'; +CREATE USER 'user22'@'%' IDENTIFIED BY 'password1'; +CREATE USER '22user'@'LOCALHOST' IDENTIFIED BY 'password1'; +CREATE USER 'admin'@'%' IDENTIFIED BY 'password1'; +CREATE USER 'us,er1'@'localhost' IDENTIFIED BY 'password1'; +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +SELECT 'user1'; +user1 +user1 +SELECT 'user22'; +user22 +user22 +SELECT '22user'; +22user +22user +SELECT 'user22'; +user22 +user22 +SELECT 'admin'; +admin +admin +SELECT 'us,er1'; +us,er1 +us,er1 +SET GLOBAL audit_log_flush=ON; +set global audit_log_flush= ON; +=================================================================== +"Connect","","","",0,"user1","user1","","","localhost","127.0.0.1","test" +"Query","","","select","",0,"SELECT 'user1'","user1[user1] @ localhost [127.0.0.1]","localhost","","127.0.0.1","test" +"Quit","","","",0,"user1","user1","","","localhost","127.0.0.1","test" +=================================================================== +DROP USER 'user1'@'127.0.0.1'; +DROP USER 'user22'@'%'; +DROP USER '22user'@'localhost'; +DROP USER 'admin'@'%'; +DROP USER 'us,er1'@'localhost'; diff --git a/plugin/audit_log/tests/mtr/percona_bug_ps3867.test b/plugin/audit_log/tests/mtr/percona_bug_ps3867.test new file mode 100644 index 000000000000..5a9d8a2876c7 --- /dev/null +++ b/plugin/audit_log/tests/mtr/percona_bug_ps3867.test @@ -0,0 +1,35 @@ +# PS-3867: audit_log_include_accunts was incorrect when set on the command line + +# setup some user accounts + +CREATE USER 'user1'@'127.0.0.1' IDENTIFIED BY 'password1'; +CREATE USER 'user22'@'%' IDENTIFIED BY 'password1'; +CREATE USER '22user'@'LOCALHOST' IDENTIFIED BY 'password1'; +CREATE USER 'admin'@'%' IDENTIFIED BY 'password1'; +CREATE USER 'us,er1'@'localhost' IDENTIFIED BY 'password1'; + +let $MYSQLD_DATADIR= `select @@datadir`; +let $log_file=$MYSQLD_DATADIR/test_audit.log; + + +--disable_result_log +--let $restart_parameters="restart: $AUDIT_LOG_OPT $AUDIT_LOG_LOAD --audit_log_include_accounts='user1@127.0.0.1' --audit_log_file=test_audit.log --audit_log_policy=ALL --audit-log-format=CSV --audit_log_strategy=SYNCHRONOUS" +--source include/restart_mysqld.inc +--enable_result_log + +SET GLOBAL audit_log_flush=ON; +--remove_file $log_file +SET GLOBAL audit_log_flush=ON; + +--source audit_log_filter_events.inc + +SET GLOBAL audit_log_flush=ON; + +--source audit_log_echo.inc + +# cleanup users +DROP USER 'user1'@'127.0.0.1'; +DROP USER 'user22'@'%'; +DROP USER '22user'@'localhost'; +DROP USER 'admin'@'%'; +DROP USER 'us,er1'@'localhost'; diff --git a/plugin/audit_log/tests/mtr/suite.opt b/plugin/audit_log/tests/mtr/suite.opt new file mode 100644 index 000000000000..60755382b7b3 --- /dev/null +++ b/plugin/audit_log/tests/mtr/suite.opt @@ -0,0 +1,2 @@ +$AUDIT_LOG_OPT +$AUDIT_LOG_LOAD diff --git a/plugin/daemon_example/daemon_example.cc b/plugin/daemon_example/daemon_example.cc index 7b7b8c92b2fd..b2fd6e20b035 100644 --- a/plugin/daemon_example/daemon_example.cc +++ b/plugin/daemon_example/daemon_example.cc @@ -27,6 +27,7 @@ #include #include #include +#include #include "m_string.h" // strlen #include "my_dbug.h" @@ -61,6 +62,7 @@ static void init_deamon_example_psi_keys() { struct mysql_heartbeat_context { my_thread_handle heartbeat_thread; File heartbeat_file; + std::atomic_bool done; }; static void *mysql_heartbeat(void *p) { @@ -70,7 +72,7 @@ static void *mysql_heartbeat(void *p) { time_t result; struct tm tm_tmp; - while (true) { + while (!con->done.load()) { sleep(5); result = time(nullptr); @@ -123,6 +125,7 @@ static int daemon_example_plugin_init(void *p) { MY_REPLACE_EXT | MY_UNPACK_FILENAME); unlink(heartbeat_filename); con->heartbeat_file = my_open(heartbeat_filename, O_CREAT | O_RDWR, MYF(0)); + con->done.store(false); /* No threads exist at this point in time, so this is thread safe. @@ -171,7 +174,7 @@ static int daemon_example_plugin_deinit(void *p) { struct tm tm_tmp; void *dummy_retval; - my_thread_cancel(&con->heartbeat_thread); + con->done.store(true); localtime_r(&result, &tm_tmp); snprintf(buffer, sizeof(buffer), diff --git a/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/network/xcom_network_provider_ssl_native_lib.cc b/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/network/xcom_network_provider_ssl_native_lib.cc index e4f1fa9914b3..c2b9efcc8ea3 100644 --- a/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/network/xcom_network_provider_ssl_native_lib.cc +++ b/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/network/xcom_network_provider_ssl_native_lib.cc @@ -42,6 +42,11 @@ #include "openssl/engine.h" +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#include +#endif + #include "xcom/task_debug.h" #include "xcom/x_platform.h" diff --git a/plugin/innodb_memcached/daemon_memcached/daemon/memcached_mysql.cc b/plugin/innodb_memcached/daemon_memcached/daemon/memcached_mysql.cc index f6ebb3badc5b..3a2fb7be4ca2 100644 --- a/plugin/innodb_memcached/daemon_memcached/daemon/memcached_mysql.cc +++ b/plugin/innodb_memcached/daemon_memcached/daemon/memcached_mysql.cc @@ -100,8 +100,8 @@ static SYS_VAR *daemon_memcached_sys_var[] = { THD *thd_get_current_thd(); // from sql_class.cc static void emit_deprecation_message() { - push_deprecated_warn_no_replacement(thd_get_current_thd(), - "InnoDB Memcached Plugin"); + push_deprecated_warn_no_replacement(thd_get_current_thd(), + "InnoDB Memcached Plugin"); } static int daemon_memcached_plugin_deinit(void *p) @@ -159,49 +159,45 @@ static int daemon_memcached_plugin_init(void *p) pthread_attr_t attr; struct st_plugin_int* plugin = (struct st_plugin_int *)p; - emit_deprecation_message(); + emit_deprecation_message(); - con = (mysql_memcached_context*) my_malloc(PSI_INSTRUMENT_ME, + con = (mysql_memcached_context *)my_malloc(PSI_INSTRUMENT_ME, sizeof(*con), MYF(0)); - if (mci_engine_library) { - char* lib_path = (mci_eng_lib_path) - ? mci_eng_lib_path : opt_plugin_dir; - int lib_len = strlen(lib_path) - + strlen(mci_engine_library) - + strlen(FN_DIRSEP) + 1; - - con->memcached_conf.m_engine_library = (char*) my_malloc( - PSI_INSTRUMENT_ME, - lib_len, MYF(0)); - - strxmov(con->memcached_conf.m_engine_library, lib_path, - FN_DIRSEP, mci_engine_library, NullS); - } else { - con->memcached_conf.m_engine_library = NULL; - } + if (mci_engine_library) { + char *lib_path = + (mci_eng_lib_path) ? mci_eng_lib_path : opt_plugin_dir; + int lib_len = strlen(lib_path) + strlen(mci_engine_library) + + strlen(FN_DIRSEP) + 1; - con->memcached_conf.m_mem_option = mci_memcached_option; - con->memcached_conf.m_innodb_api_cb = plugin->data; - con->memcached_conf.m_r_batch_size = mci_r_batch_size; - con->memcached_conf.m_w_batch_size = mci_w_batch_size; - con->memcached_conf.m_enable_binlog = mci_enable_binlog; - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - - /* now create the thread */ - if (pthread_create(&con->memcached_thread, &attr, - daemon_memcached_main, - (void *)&con->memcached_conf) != 0) - { - fprintf(stderr,"Could not create memcached daemon thread!\n"); - exit(0); - } + con->memcached_conf.m_engine_library = + (char *)my_malloc(PSI_INSTRUMENT_ME, lib_len, MYF(0)); - plugin->data= (void *)con; + strxmov(con->memcached_conf.m_engine_library, lib_path, FN_DIRSEP, + mci_engine_library, NullS); + } else { + con->memcached_conf.m_engine_library = NULL; + } - return(0); + con->memcached_conf.m_mem_option = mci_memcached_option; + con->memcached_conf.m_innodb_api_cb = plugin->data; + con->memcached_conf.m_r_batch_size = mci_r_batch_size; + con->memcached_conf.m_w_batch_size = mci_w_batch_size; + con->memcached_conf.m_enable_binlog = mci_enable_binlog; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + /* now create the thread */ + if (pthread_create(&con->memcached_thread, &attr, daemon_memcached_main, + (void *)&con->memcached_conf) != 0) { + fprintf(stderr, "Could not create memcached daemon thread!\n"); + exit(0); + } + + plugin->data = (void *)con; + + return (0); } struct st_mysql_daemon daemon_memcached_plugin = diff --git a/plugin/innodb_memcached/daemon_memcached/daemon/topkeys.c b/plugin/innodb_memcached/daemon_memcached/daemon/topkeys.c index 3dc3e1d47005..a127c3735707 100644 --- a/plugin/innodb_memcached/daemon_memcached/daemon/topkeys.c +++ b/plugin/innodb_memcached/daemon_memcached/daemon/topkeys.c @@ -122,29 +122,6 @@ topkey_item_t *topkeys_item_get_or_create(topkeys_t *tk, const void *key, size_t return item; } -static inline void append_stat(const void *cookie, - const char *name, - size_t namelen, - const char *key, - size_t nkey, - int value, - ADD_STAT add_stats) { - char key_str[128]; - char val_str[128]; - int klen, vlen; - - klen = sizeof(key_str) - namelen - 2; - if (nkey < klen) { - klen = nkey; - } - memcpy(key_str, key, klen); - key_str[klen] = '.'; - memcpy(&key_str[klen+1], name, namelen + 1); - klen += namelen + 1; - vlen = snprintf(val_str, sizeof(val_str) - 1, "%d", value); - add_stats(key_str, klen, val_str, vlen, cookie); -} - struct tk_context { const void *cookie; ADD_STAT add_stat; diff --git a/plugin/innodb_memcached/innodb_memcache/CMakeLists.txt b/plugin/innodb_memcached/innodb_memcache/CMakeLists.txt index 45cb62db5bb8..54ee4e155292 100644 --- a/plugin/innodb_memcached/innodb_memcache/CMakeLists.txt +++ b/plugin/innodb_memcached/innodb_memcache/CMakeLists.txt @@ -33,6 +33,9 @@ INCLUDE_DIRECTORIES( IF(CMAKE_C_FLAGS MATCHES "-Werror") STRING(REGEX REPLACE "-Werror( |$)" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") ENDIF(CMAKE_C_FLAGS MATCHES "-Werror") +IF(CMAKE_CXX_FLAGS MATCHES "-Werror") + STRING(REGEX REPLACE "-Werror( |$)" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +ENDIF(CMAKE_CXX_FLAGS MATCHES "-Werror") # Set extra flags for the C/CXX compiler IF(MY_COMPILER_IS_GNU_OR_CLANG) diff --git a/plugin/keyring/buffered_file_io.cc b/plugin/keyring/buffered_file_io.cc index 100956fa7104..18d80d8e51aa 100644 --- a/plugin/keyring/buffered_file_io.cc +++ b/plugin/keyring/buffered_file_io.cc @@ -299,7 +299,7 @@ bool Buffered_file_io::check_if_keyring_file_can_be_opened_or_created() { @retval true - there was an error with initializing keyring file @retval false - keyring file has been initialized successfully */ -bool Buffered_file_io::init(std::string *keyring_filename) { +bool Buffered_file_io::init(const std::string *keyring_filename) { // file name can't be empty assert(keyring_filename->empty() == false); diff --git a/plugin/keyring/buffered_file_io.h b/plugin/keyring/buffered_file_io.h index bfc537e2be06..23c8387183dd 100644 --- a/plugin/keyring/buffered_file_io.h +++ b/plugin/keyring/buffered_file_io.h @@ -50,7 +50,7 @@ class Buffered_file_io : public IKeyring_io { // ================= IKeyring_io implementation ================= // - bool init(std::string *keyring_filename) override; + bool init(const std::string *keyring_filename) override; bool flush_to_backup(ISerialized_object *serialized_object) override; bool flush_to_storage(ISerialized_object *serialized_object) override; ISerializer *get_serializer() override; diff --git a/plugin/keyring/common/i_keyring_io.h b/plugin/keyring/common/i_keyring_io.h index 538efa562daa..406e5ce31b29 100644 --- a/plugin/keyring/common/i_keyring_io.h +++ b/plugin/keyring/common/i_keyring_io.h @@ -30,7 +30,7 @@ namespace keyring { class IKeyring_io : public Keyring_alloc { public: - virtual bool init(std::string *keyring_storage_url) = 0; + virtual bool init(const std::string *keyring_storage_url) = 0; virtual bool flush_to_backup(ISerialized_object *serialized_object) = 0; virtual bool flush_to_storage(ISerialized_object *serialized_object) = 0; diff --git a/plugin/keyring/common/i_keyring_key.h b/plugin/keyring/common/i_keyring_key.h index a83a29438a65..d45d735a6cd1 100644 --- a/plugin/keyring/common/i_keyring_key.h +++ b/plugin/keyring/common/i_keyring_key.h @@ -49,6 +49,7 @@ struct IKey : public Keyring_alloc { virtual size_t get_key_data_size() = 0; virtual size_t get_key_pod_size() const = 0; virtual uchar *release_key_data() = 0; + virtual void xor_data(uchar *data, size_t data_len) = 0; virtual void xor_data() = 0; virtual void set_key_data(uchar *key_data, size_t key_data_size) = 0; virtual void set_key_type(const std::string *key_type) = 0; diff --git a/plugin/keyring/common/keyring_key.cc b/plugin/keyring/common/keyring_key.cc index c0bb4743945b..a5eb437ea73c 100644 --- a/plugin/keyring/common/keyring_key.cc +++ b/plugin/keyring/common/keyring_key.cc @@ -189,12 +189,16 @@ size_t Key::get_key_pod_size() const { return key_pod_size_aligned; } -void Key::xor_data() { - if (key == nullptr) return; +void Key::xor_data(uchar *data, size_t data_len) { static const char *obfuscate_str = "*305=Ljt0*!@$Hnm(*-9-w;:"; - for (size_t i = 0, l = 0; i < key_len; + for (size_t i = 0, l = 0; i < data_len; ++i, l = ((l + 1) % strlen(obfuscate_str))) - key.get()[i] ^= obfuscate_str[l]; + data[i] ^= obfuscate_str[l]; +} + +void Key::xor_data() { + if (key == nullptr) return; + xor_data(key.get(), key_len); } bool Key::is_key_id_valid() { return key_id.length() > 0; } diff --git a/plugin/keyring/common/keyring_key.h b/plugin/keyring/common/keyring_key.h index 79a23160f4b3..f308b1a56559 100644 --- a/plugin/keyring/common/keyring_key.h +++ b/plugin/keyring/common/keyring_key.h @@ -52,6 +52,7 @@ struct Key : IKey { size_t get_key_data_size() override; size_t get_key_pod_size() const override; uchar *release_key_data() override; + void xor_data(uchar *data, size_t data_len) override; void xor_data() override; void set_key_data(uchar *key_data, size_t key_data_size) override; void set_key_type(const std::string *key_type) override; @@ -68,7 +69,7 @@ struct Key : IKey { const void *a_key, size_t a_key_len); void clear_key_data(); - void create_key_signature() const; + virtual void create_key_signature() const; bool load_string_from_buffer(const uchar *buffer, size_t *buffer_position, size_t key_pod_size, std::string *string, size_t string_length); diff --git a/plugin/keyring/common/keyring_memory.h b/plugin/keyring/common/keyring_memory.h index 7750bb32ce67..ad4f7207d482 100644 --- a/plugin/keyring/common/keyring_memory.h +++ b/plugin/keyring/common/keyring_memory.h @@ -23,10 +23,13 @@ #ifndef MYSQL_KEYRING_MEMORY_H #define MYSQL_KEYRING_MEMORY_H -#include +#include #include #include +#include "my_sys.h" +#include "mysql/service_mysql_alloc.h" + namespace keyring { extern PSI_memory_key key_memory_KEYRING; @@ -48,6 +51,43 @@ class Keyring_alloc { static void operator delete(void *ptr, std::size_t) { my_free(ptr); } static void operator delete[](void *ptr, std::size_t) { my_free(ptr); } }; + +template +class Secure_allocator { + public: + using value_type = T; + + Secure_allocator() noexcept {} + + template + Secure_allocator(const Secure_allocator &) noexcept {} + + T *allocate(size_t n) { + if (n == 0) + return nullptr; + else if (n > INT_MAX) + throw std::bad_alloc(); + return keyring_malloc(n * sizeof(T)); + } + + void deallocate(T *p, size_t n) noexcept { + memset_s(p, n, 0, n); + my_free(p); + } +}; + +template +bool operator==(const Secure_allocator &, + const Secure_allocator &) noexcept { + return true; +} + +template +bool operator!=(const Secure_allocator &, + const Secure_allocator &) noexcept { + return false; +} + } // namespace keyring #endif // MYSQL_KEYRING_MEMORY_H diff --git a/plugin/keyring/common/secure_string.h b/plugin/keyring/common/secure_string.h new file mode 100644 index 000000000000..35fa7f6d366c --- /dev/null +++ b/plugin/keyring/common/secure_string.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2018 Percona LLC 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 + as published by the Free Software Foundation; version 2 of + the License. + + 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 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_KEYRING_SECURE_STRING +#define MYSQL_KEYRING_SECURE_STRING + +#include +#include "keyring_memory.h" + +namespace keyring { +typedef std::basic_string, Secure_allocator> + Secure_string; +typedef std::basic_ostringstream, + Secure_allocator> + Secure_ostringstream; +typedef std::basic_istringstream, + Secure_allocator> + Secure_istringstream; +} // namespace keyring + +#endif // MYSQL_KEYRING_SECURE_STRING diff --git a/plugin/keyring/keyring_file.version b/plugin/keyring/keyring_file.version new file mode 100644 index 000000000000..023b6b555b21 --- /dev/null +++ b/plugin/keyring/keyring_file.version @@ -0,0 +1,8 @@ +KEYRING_FILE_VERSION_1.0 { + global: + _mysql_*; + mysql_malloc_service; + my_plugin_log_service; + security_context_service; + local: *; +}; diff --git a/plugin/percona-pam-for-mysql/CMakeLists.txt b/plugin/percona-pam-for-mysql/CMakeLists.txt new file mode 100644 index 000000000000..7c7bb8de2808 --- /dev/null +++ b/plugin/percona-pam-for-mysql/CMakeLists.txt @@ -0,0 +1,42 @@ +# (C) 2011-2013 Percona LLC 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 as published by +# the Free Software Foundation; version 2 of the License. +# +# 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 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 Street, Fifth Floor, Boston, MA 02110-1301, USA +# +IF(WITH_PAM) +INCLUDE (CheckLibraryExists) +CHECK_LIBRARY_EXISTS(pam pam_authenticate "" HAVE_PAM) +IF(NOT HAVE_PAM) + MESSAGE(FATAL_ERROR "Required PAM dev library not found. Please install PAM development files!") +ENDIF(NOT HAVE_PAM) +CHECK_SYMBOL_EXISTS(getpwnam_r "pwd.h" HAVE_GETPWNAM_R) +CHECK_SYMBOL_EXISTS(getgrgid_r "grp.h" HAVE_GETGRGID_R) +CHECK_INCLUDE_FILES (security/pam_misc.h HAVE_SECURITY_PAM_MISC_H) +CHECK_INCLUDE_FILES (security/openpam.h HAVE_SECURITY_OPENPAM_H) +CHECK_INCLUDE_FILES (dlfcn.h HAVE_DLFCN_H) +ADD_DEFINITIONS(-Dget_tty_password=dialog_mysql_get_tty_password) +IF(HAVE_PAM AND HAVE_GETPWNAM_R AND HAVE_GETGRGID_R AND HAVE_DLFCN_H) + SET(AUTH_PAM_COMMON_SOURCES + src/auth_pam_common.cc src/lib_auth_pam_client.c src/lib_auth_pam_client.h + src/auth_mapping.h src/auth_mapping.cc src/groups.cc src/groups.h) + SET(AUTH_PAM_SOURCES ${AUTH_PAM_COMMON_SOURCES} src/auth_pam.cc) + SET(AUTH_PAM_COMPAT_SOURCES ${AUTH_PAM_COMMON_SOURCES} src/auth_pam_compat.cc) + MYSQL_ADD_PLUGIN(auth_pam ${AUTH_PAM_SOURCES} LINK_LIBRARIES pam MODULE_ONLY) + MYSQL_ADD_PLUGIN(auth_pam_compat ${AUTH_PAM_COMPAT_SOURCES} LINK_LIBRARIES pam MODULE_ONLY) + MYSQL_ADD_PLUGIN(dialog + src/dialog.cc + ../../sql-common/get_password.cc + LINK_LIBRARIES perconaserverclient + MODULE_ONLY) +ENDIF(HAVE_PAM AND HAVE_GETPWNAM_R AND HAVE_GETGRGID_R AND HAVE_DLFCN_H) +ENDIF(WITH_PAM) diff --git a/plugin/percona-pam-for-mysql/doc/make.bat b/plugin/percona-pam-for-mysql/doc/make.bat new file mode 100644 index 000000000000..4bc6b71e63ac --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/make.bat @@ -0,0 +1,170 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PerconaXtraBackup.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PerconaXtraBackup.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/plugin/percona-pam-for-mysql/doc/source/_static/percona-pam-plugin-logo.jpg b/plugin/percona-pam-for-mysql/doc/source/_static/percona-pam-plugin-logo.jpg new file mode 100644 index 000000000000..6c9f9d668bba Binary files /dev/null and b/plugin/percona-pam-for-mysql/doc/source/_static/percona-pam-plugin-logo.jpg differ diff --git a/plugin/percona-pam-for-mysql/doc/source/_static/percona_favicon.ico b/plugin/percona-pam-for-mysql/doc/source/_static/percona_favicon.ico new file mode 100644 index 000000000000..f426064d6f50 Binary files /dev/null and b/plugin/percona-pam-for-mysql/doc/source/_static/percona_favicon.ico differ diff --git a/plugin/percona-pam-for-mysql/doc/source/conf.py b/plugin/percona-pam-for-mysql/doc/source/conf.py new file mode 100644 index 000000000000..31287b000008 --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/source/conf.py @@ -0,0 +1,245 @@ +# -*- coding: utf-8 -*- +# +# Percona PAM authentication plugin documentation build configuration file, created by +# sphinx-quickstart on Mon Jun 27 22:27:15 2011. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. + +extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.todo', + 'sphinx.ext.coverage', 'sphinx.ext.ifconfig', + 'sphinx.ext.extlinks'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Percona PAM authentication plugin for MySQL' +copyright = u'2012, Percona Inc' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.1' +# The full version, including alpha/beta/rc tags. +release = '0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +rst_prolog = ''' +.. |check| replace:: ``|[[---CHECK---]]|`` + +.. |xtrabackup| replace:: :program:`xtrabackup` + +.. |innobackupex| replace:: :program:`innobackupex` + +.. |XtraBackup| replace:: *XtraBackup* + +.. |Percona Server| replace:: *Percona Server* + +.. |Percona| replace:: *Percona* + +.. |MySQL| replace:: *MySQL* + +.. |Drizzle| replace:: *Drizzle* + +.. |MariaDB| replace:: *MariaDB* + + +''' + +extlinks = {'bug': ('https://bugs.launchpad.net/percona-pam-for-mysql/+bug/%s', + '#')} + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'percona-theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +html_theme_path = ['.', './percona-theme'] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = 'Percona PAM authenticatino plugin for MySQL Documentation' + +# A shorter title for the navigation bar. Default is the same as html_title. +html_short_title = 'PAM Plugin Docs' + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +html_logo = 'percona-pam-plugin-logo.png' + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +html_favicon = 'percona_favicon.ico' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'PerconaPAMForMySQL' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'PerconaPAMForMySQL.tex', u'Percona PAM Authentication Plugin for MySQL Documentation', + u'Percona Inc', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ +# ('index', 'perconapamplugin', u'Percona PAM Authentication Plugin for MySQL Documentation', +# [u'Percona Inc'], 1) +] diff --git a/plugin/percona-pam-for-mysql/doc/source/faq.rst b/plugin/percona-pam-for-mysql/doc/source/faq.rst new file mode 100644 index 000000000000..0188c77d35a2 --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/source/faq.rst @@ -0,0 +1,51 @@ +============================ + Frequently Asked Questions +============================ + +Is there a Windows version? +=========================== + +No, Windows does not support PAM, so there will not be a Windows version. + +Can I use it with MySQL? +======================== + +Yes. + +Can I use it with Percona Server? +================================= + +Yes. + + +Is it Free and Open Source Software? +==================================== + +Yes. + + +Can I use the PAM plugin to authenticate against /etc/shadow? +============================================================= + +Yes, you need to add the mysql user to the shadow group. Because PAM libraries, such as 'pam_unix.so', need to access /etc/shadow. + +For example this is how you can do it in *Ubuntu*: :: + + root@lucid64:/var/lib/mysql# getent group shadow + shadow:x:42:mysql + + root@lucid64:/var/lib/mysql# ls -alhs /etc/shadow + 4.0K -rw-r----- 1 root shadow 912 Dec 21 10:39 /etc/shadow + +After you restart mysqld for changes to take effect, pam_unix authentication will work. + +The other option is to run mysqld as root. This should be used for testing only or as a last resort method. + + +I'm getting the: "ERROR 2059 (HY000): Authentication plugin 'auth_pam' cannot be loaded" +======================================================================================== + +This means that the default client :option:`plugin-dir` setting doesn't work or it isn't set up properly. You'll need to add the location of the plugin folder to your client configuration: :: + + [client] + plugin_dir='/usr/lib/mysql/plugin' diff --git a/plugin/percona-pam-for-mysql/doc/source/glossary.rst b/plugin/percona-pam-for-mysql/doc/source/glossary.rst new file mode 100644 index 000000000000..70834a733210 --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/source/glossary.rst @@ -0,0 +1,8 @@ +========== + Glossary +========== + +.. glossary:: + + PAM + Pluggable Authentication Module - allows integrating multiple authentication mechanisms which can be written independently of the underlying authentication scheme that's being used in the application. diff --git a/plugin/percona-pam-for-mysql/doc/source/index.rst b/plugin/percona-pam-for-mysql/doc/source/index.rst new file mode 100644 index 000000000000..b249b9fceff4 --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/source/index.rst @@ -0,0 +1,51 @@ +============================================================ + Percona PAM authentication plugin For MySQL - Documentation +============================================================ + +Percona PAM authentication plugin for MySQL. + +Introduction +============ + +.. toctree:: + :maxdepth: 1 + :glob: + + intro + +Installation +============ + +.. toctree:: + :maxdepth: 2 + :glob: + + installation + +User's Manual +============= + +.. toctree:: + :maxdepth: 2 + :glob: + + manual + +Miscellaneous +============= + +.. toctree:: + :maxdepth: 1 + :glob: + + faq + release-notes + glossary + +Indices and tables +================== + +* :ref:`genindex` + +* :ref:`search` + diff --git a/plugin/percona-pam-for-mysql/doc/source/installation.rst b/plugin/percona-pam-for-mysql/doc/source/installation.rst new file mode 100644 index 000000000000..30aec3add935 --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/source/installation.rst @@ -0,0 +1,50 @@ +======================================================= + Installing Percona PAM Authentication Plugin for MySQL +======================================================= + +.. toctree:: + :hidden: + +Compiling from Source +===================== + +You will need both the PAM headers and the MySQL 5.5 headers and corresponding `mysql_config` binary available on your system. + +If you are not using one of the pre-built binary packages, you will need to compile the plugin from source. You can either use a source tarball or the source repository. + +For getting a copy of the latest development bzr tree: :: + + $ bzr branch lp:percona-pam-for-mysql + +If you are building from bzr, you will need to generate the configure script: :: + + $ ./bootstrap + +You do not need to run `bootstrap` if you are using a source tarball. + +You then need to build the plugin: :: + + $ ./configure + $ make + +To install, you can simply run (as root or using sudo or similar): :: + + $ make install + +Installing server-side plugin +============================= + +The shared library that holds the plugin, auth_pam.so, needs to be stored in the plugindir directory of mysql. You can get this value via the command: :: + + $ mysql_config --plugindir + +Make sure that after installed, the library has got the appropiate permissions (file execution is required). + +Most packages should do this for you, so this is likely only required with the binary tarballs. + +In order to load the plugin into the working server, issue the following command: :: + + mysql> INSTALL PLUGIN auth_pam SONAME 'auth_pam.so'; + + +You can now create a PAM configuration for the MySQL server and create users that are authenticated by PAM. diff --git a/plugin/percona-pam-for-mysql/doc/source/intro.rst b/plugin/percona-pam-for-mysql/doc/source/intro.rst new file mode 100644 index 000000000000..600e2f57a6a8 --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/source/intro.rst @@ -0,0 +1,23 @@ +================================================== + About Percona PAM Authentication Plugin for MySQL +================================================== + +Percona PAM Authentication Plugin is a free and Open Source implementation of the MySQL's authentication plugin. This plugin acts as a mediator between the MySQL server, the MySQL client, and the PAM stack. The server plugin requests authentication from the PAM stack, forwards any requests and messages from the PAM stack over the wire to the client (in cleartext) and reads back any replies for the PAM stack. + + PAM plugin uses dialog as its client side plugin. Dialog plugin can be loaded to any client application that uses libmysqlclient library (or compatible client library such as libperconaserverclient). + +Here are some of the benefits that Percona dialog plugin offers over the default one: + + * It correctly recognizes whether PAM wants input to be echoed or not, while the default one always echoes the input on the user's console. + * It can use the password which is passed to |MySQL| client via "-p" parameter. + * Dialog client `installation bug `_ has been fixed. + * This plugin works on |MySQL| and |Percona Server|. + +Percona offers two versions of this plugin: + + * Full PAM plugin called *auth_pam*. This plugin uses *dialog.so*. It fully supports the PAM protocol with arbitrary communication between client and server. + * Oracle-compatible PAM called *auth_pam_compat*. This plugin uses *mysql_clear_password* which is a part of Oracle MySQL client. It also has some limitations, such as, it supports only one password input. You must use "-p" option in order to pass the password to auth_pam_compat. + +These two versions of plugins are physically different. To choose which one you want used, you must use *IDENTIFIED WITH 'auth_pam'* for auth_pam, and *IDENTIFIED WITH 'auth_pam_compat'* for auth_pam_compat. + + diff --git a/plugin/percona-pam-for-mysql/doc/source/manual.rst b/plugin/percona-pam-for-mysql/doc/source/manual.rst new file mode 100644 index 000000000000..f96e0883f2d6 --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/source/manual.rst @@ -0,0 +1,45 @@ +.. _user-manual: + +========================================================== + *Percona PAM authentication plugin for MySQL* User Manual +========================================================== + +.. toctree:: + :maxdepth: 1 + :hidden: + +Configuring PAM for MySQL +========================= + +You will need to configure PAM on your system for how it should authenticate for MySQL. A simple setup can be to use the standard UNIX authentication method. + +*NOTE:* Using pam_unix means the MySQL Server needs to read the `/etc/shadow` file, which usually means it has to be run as `root` - usually not a recommended configuration. + +A sample `/etc/pam.d/mysqld` file: :: + + auth required pam_unix.so + account required pam_unix.so + +For added information in the system log, you can expand it to be: :: + + auth required pam_warn.so + auth required pam_unix.so audit + account required pam_unix.so audit + + +Creating A User +=============== + +You will need to execute `CREATE USER` with specifying the PAM plugin. For example: :: + + CREATE USER 'username'@'host' IDENTIFIED WITH auth_pam; + +This creates a user `username` that can connect from `host` and will be authenticated using the PAM plugin. If you are using the `pam_unix` method in PAM (or similar) you will need to have an account for `username` existing on the system. + +Supplementary groups support +============================ + +|Percona Server| has implemented PAM plugin support for supplementary groups. Supplementary or secondary groups are extra groups a specific user is member of. For example user ``joe`` might be a member of groups: ``joe`` (his primary group) and secondary groups ``developers`` and ``dba``. A complete list of groups and users belonging to them can be checked with ``cat /etc/group`` command. + +This feature enables using secondary groups in the mapping part of the authentication string, like "``mysql, developers=joe, dba=mark``". Previously only primary groups could have been specified there. If user is a member of both ``developers`` and ``dba``, PAM plugin will map it to the ``joe`` because ``developers`` matches first. + diff --git a/plugin/percona-pam-for-mysql/doc/source/percona-pam-plugin-logo.png b/plugin/percona-pam-for-mysql/doc/source/percona-pam-plugin-logo.png new file mode 100644 index 000000000000..32afd1cab4fa Binary files /dev/null and b/plugin/percona-pam-for-mysql/doc/source/percona-pam-plugin-logo.png differ diff --git a/plugin/percona-pam-for-mysql/doc/source/percona_favicon.ico b/plugin/percona-pam-for-mysql/doc/source/percona_favicon.ico new file mode 100644 index 000000000000..f426064d6f50 Binary files /dev/null and b/plugin/percona-pam-for-mysql/doc/source/percona_favicon.ico differ diff --git a/plugin/percona-pam-for-mysql/doc/source/release-notes.rst b/plugin/percona-pam-for-mysql/doc/source/release-notes.rst new file mode 100644 index 000000000000..e025381cf6db --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/source/release-notes.rst @@ -0,0 +1,19 @@ +============================================================ + |Percona| PAM authentication plugin for MySQL Release Notes +============================================================ + +General availability (GA) +========================= + +Percona is pleased to announce that Percona's PAM Authentication plugin for MySQL has been merged to |Percona Server| `5.5.24-26.0 `_ on June 1st, 2012. With this release PAM Authentication plugin became GA. Further information on how to install and use the plugin can be found in the |Percona Server| documentation. + +Preview Release +=============== + +Percona is pleased to announce availability of an early access version of Percona's PAM Authentication plugin for MySQL on December 5th, 2011. This plugin supports |MySQL|-5.5.x, |Percona Server| 5.5.x and MariaDB 5.2.x. The PAM Authentication plugin can be used for: + * |MySQL| authentication using operating system users (``pam_unix``) + * |MySQL| authentication from LDAP server (pam_ldap) + * authentication against RSA SecurID server + * any other authentication methods that provides access via PAM + +Percona PAM Authentication Plugin for MySQL is fully open source, free of charge and can be used on an unlimited amount of servers. diff --git a/plugin/percona-pam-for-mysql/src/auth_mapping.cc b/plugin/percona-pam-for-mysql/src/auth_mapping.cc new file mode 100644 index 000000000000..b5215674bad7 --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/auth_mapping.cc @@ -0,0 +1,227 @@ +/* +(C) 2012, 2016 Percona LLC 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 as published by +the Free Software Foundation; version 2 of the License. + +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 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 Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "auth_mapping.h" +#include +#include +#include +#include "groups.h" + +#include "auth_pam_common.h" + +/** Token representation: + token type, string repr, length of token */ +struct token { + enum class ctype { id, comma, eq, eof } type; + const char *token; + size_t token_len; +}; + +/** Iterator in key-value mapping: + position and length of key, + position and length of value, + current position in string */ +struct mapping_iter { + const char *key; + size_t key_len; + const char *value; + size_t value_len; + const char *ptr; +}; + +/** Get next token from buf. Returns new buf position. */ +static const char *get_token(struct token *token, const char *buf) { + const char *ptr = buf; + + while (*ptr && isspace(*ptr)) ++ptr; + + token->token = ptr; + switch (*ptr) { + case '\0': + token->type = token::ctype::eof; + break; + case ',': + token->token_len = 1; + token->type = token::ctype::comma; + ++ptr; + break; + case '=': + token->token_len = 1; + token->type = token::ctype::eq; + ++ptr; + break; + case '"': + token->token_len = 0; + ++ptr; + token->token = ptr; + while (*ptr && *ptr != '"') { + ++token->token_len; + ++ptr; + } + token->type = token::ctype::id; + if (*ptr) ++ptr; + break; + default: + token->token_len = 0; + while (*ptr && !isspace(*ptr) && *ptr != ',' && *ptr != '=') { + ++token->token_len; + ++ptr; + } + token->type = token::ctype::id; + } + + return ptr; +} + +/** Create iterator through mapping string. + Initially iterator set to position before first + key-value pair. On success non-NULL pointer returned, otherwise NULL */ +struct mapping_iter *mapping_iter_new(const char *mapping_string) { + struct mapping_iter *it = static_cast( + my_malloc(key_memory_pam_mapping_iter, sizeof(struct mapping_iter), 0)); + if (it != nullptr) { + it->key = nullptr; + it->value = nullptr; + /* eat up service name and move to (, key = value)* part */ + struct token token; + it->ptr = get_token(&token, mapping_string); + } + return it; +} + +/** Move iterator to next key-value pair. + On success pointer to key position in string returned, + otherwise NULL */ +const char *mapping_iter_next(struct mapping_iter *it) { + struct token token[4] = {{token::ctype::id, 0, 0}}; + + /* read next 4 tokens */ + it->ptr = get_token( + token + 3, + get_token(token + 2, get_token(token + 1, get_token(token, it->ptr)))); + + /* was it ", id = id"? */ + if (!((token[0].type == token::ctype::comma) && + (token[1].type == token::ctype::id) && + (token[2].type == token::ctype::eq) && + (token[3].type == token::ctype::id))) { + /* we got something inconsistent */ + return nullptr; + } + + /* set key */ + it->key = token[1].token; + it->key_len = token[1].token_len; + + /* set value */ + it->value = token[3].token; + it->value_len = token[3].token_len; + + return it->key; +} + +/** Finish iteration and release iterator */ +void mapping_iter_free(struct mapping_iter *it) { my_free(it); } + +/** Get mapped value for given user name. + Value is looked up by using all user groups as a key. + Auth string is iterated only once, while groups are iterated + for every key-value pair. This is mean than auth string order + is dominant. + + Example: + + given: + user "foo" is the member of "wheel", "staff" and "bar". + auth string is "mysql, root=user1, bar=user2, staff=user3" + + result is "user2". + + On success value_buf returned, otherwise NULL */ +char *mapping_lookup_user(const char *user_name, char *value_buf, + size_t value_buf_len, const char *mapping_string) { + /* Iterate through the key-value list stored in auth_string and + find key (which is interpreted as group name) in the list of groups + for specified user. If match is found, store appropriate value in + the authenticated_as field. */ + struct mapping_iter *keyval_it = mapping_iter_new(mapping_string); + if (keyval_it == nullptr) return nullptr; + + struct groups_iter *group_it = groups_iter_new(user_name); + if (group_it == nullptr) { + mapping_iter_free(keyval_it); + return nullptr; + } + + const char *key; + const char *group; + while ((key = mapping_iter_next(keyval_it)) != nullptr) { + while ((group = groups_iter_next(group_it)) != nullptr) { + if (keyval_it->key_len == strlen(group) && + strncmp(key, group, keyval_it->key_len) == 0) { + /* match is found */ + memcpy(value_buf, keyval_it->value, + std::min(value_buf_len, keyval_it->value_len)); + value_buf[std::min(value_buf_len, keyval_it->value_len)] = '\0'; + groups_iter_free(group_it); + mapping_iter_free(keyval_it); + return value_buf; + } + } + groups_iter_reset(group_it); + } + + groups_iter_free(group_it); + mapping_iter_free(keyval_it); + + return nullptr; +} + +/** Get key in current iterator pos. On success buf returned, + otherwise NULL */ +char *mapping_iter_get_key(struct mapping_iter *it, char *buf, size_t buf_len) { + if (it->key == nullptr) return nullptr; + memcpy(buf, it->key, std::min(buf_len, it->key_len)); + buf[std::min(buf_len, it->key_len)] = '\0'; + return buf; +} + +/** Get value in current iterator pos. On success buf returned, + otherwise NULL */ +char *mapping_iter_get_value(struct mapping_iter *it, char *buf, + size_t buf_len) { + if (it->value == nullptr) return nullptr; + memcpy(buf, it->value, std::min(buf_len, it->value_len)); + buf[std::min(buf_len, it->value_len)] = '\0'; + return buf; +} + +/** Get value by key. On success pointer to service_name + returned, otherwise NULL */ +char *mapping_get_service_name(char *buf, size_t buf_len, + const char *mapping_string) { + struct token token; + + get_token(&token, mapping_string); + if (token.type == token::ctype::id) { + memcpy(buf, token.token, std::min(buf_len, token.token_len)); + buf[std::min(buf_len, token.token_len)] = '\0'; + return buf; + } + + return nullptr; +} diff --git a/plugin/percona-pam-for-mysql/src/auth_mapping.h b/plugin/percona-pam-for-mysql/src/auth_mapping.h new file mode 100644 index 000000000000..7a98e30bcb0a --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/auth_mapping.h @@ -0,0 +1,73 @@ +#ifndef AUTH_MAPPING_INCLUDED +#define AUTH_MAPPING_INCLUDED +/* + (C) 2012, 2013 Percona LLC 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 as published by + the Free Software Foundation; version 2 of the License. + + 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 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 Street, Fifth Floor, Boston, MA 02110-1301, USA + +*/ + +/** + @file + + PAM authentication for MySQL, interface for user mapping. + +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Mapping iterator. It's not exposed outsude */ +struct mapping_iter; + +/** Create iterator through mapping string. + Initially iterator set to position before first + key-value pair. On success non-NULL pointer returned, otherwise NULL */ +struct mapping_iter *mapping_iter_new(const char *mapping_string); + +/** Move iterator to next key-value pair. + On success pointer to key position in string returned, + otherwise NULL */ +const char *mapping_iter_next(struct mapping_iter *it); + +/** Finish iteration and release iterator */ +void mapping_iter_free(struct mapping_iter *it); + +/** Get key at current iterator position. On success buf returned, + otherwise NULL */ +char *mapping_iter_get_key(struct mapping_iter *it, char *buf, size_t buf_len); + +/** Get value at current iterator position. On success buf returned, + otherwise NULL */ +char *mapping_iter_get_value(struct mapping_iter *it, char *buf, + size_t buf_len); + +/** Get value by given key. On success value_buf returned, + otherwise NULL */ +char *mapping_lookup_user(const char *key, char *value_buf, + size_t value_buf_len, const char *mapping_string); + +/** Get service name for auth_string. On success buf returned, + otherwise NULL */ +char *mapping_get_service_name(char *buf, size_t buf_len, + const char *mapping_string); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugin/percona-pam-for-mysql/src/auth_pam.cc b/plugin/percona-pam-for-mysql/src/auth_pam.cc new file mode 100644 index 000000000000..5fabfe161466 --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/auth_pam.cc @@ -0,0 +1,179 @@ +/* +(C) 2012, 2015 Percona LLC 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 as published by +the Free Software Foundation; version 2 of the License. + +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 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 Street, Fifth Floor, Boston, MA 02110-1301, USA + +*/ + +/** + @file + + PAM authentication for MySQL, server-side plugin for the + production use. + + A general-purpose PAM authentication plugin for MySQL. Acts as a mediator + between the MySQL server, the MySQL client, and the PAM backend. Dialog plugin + used as client plugin. + + The server plugin requests authentication from the PAM backend, forwards any + requests and messages from the PAM backend over the wire to the client (in + cleartext) and reads back any replies for the backend. + + This plugin does not encrypt the communication channel in any way. If this is + required, a SSL connection should be used. + + To install this plugin, copy the .so file to the plugin directory and do + + INSTALL PLUGIN auth_pam SONAME 'auth_pam.so'; + + To use this plugin for one particular user, specify it at user's creation time + (TODO: tested with localhost only): + + CREATE USER 'username'@'hostname' IDENTIFIED WITH auth_pam; + + Alternatively UPDATE the mysql.user table to set the plugin value for an + existing user. + + Also it is possible to use this plugin to authenticate anonymous users: + + CREATE USER ''@'hostname' IDENTIFIED WITH auth_pam; + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "auth_pam_common.h" +#include "my_sys.h" +#include "mysql/psi/mysql_memory.h" + +MYSQL_PLUGIN auth_pam_plugin_info; + +/** The maximum length of buffered PAM messages, i.e. any messages up to the + next PAM reply-requiring message. 10K should be more than enough by order + of magnitude. */ +static const constexpr auto max_pam_buffered_msg_len = 10240; + +static PSI_memory_key key_memory_pam_msg_buf; + +static PSI_memory_info pam_auth_memory[] = { + {&key_memory_pam_msg_buf, "auth_pam_msg_buf", PSI_FLAG_ONLY_GLOBAL_STAT, + PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, +}; + +struct pam_msg_buf { + unsigned char buf[max_pam_buffered_msg_len]; + unsigned char *ptr; +}; + +static char pam_msg_style_to_char(int pam_msg_style) { + /* Magic byte for the dialog plugin, '\2' is defined as ORDINARY_QUESTION + and '\4' as PASSWORD_QUESTION there. */ + return (pam_msg_style == PAM_PROMPT_ECHO_ON) ? '\2' : '\4'; +} + +int auth_pam_client_talk_init(void **talk_data) { + struct pam_msg_buf *msg_buf = static_cast(my_malloc( + key_memory_pam_msg_buf, sizeof(struct pam_msg_buf), MY_ZEROFILL)); + *talk_data = (void *)msg_buf; + if (msg_buf != nullptr) { + msg_buf->ptr = msg_buf->buf + 1; + return PAM_SUCCESS; + } + return PAM_BUF_ERR; +} + +void auth_pam_client_talk_finalize(void *talk_data) { my_free(talk_data); } + +int auth_pam_talk_perform(const struct pam_message *msg, + struct pam_response *resp, struct pam_conv_data *data, + void *talk_data) { + struct pam_msg_buf *msg_buf = (struct pam_msg_buf *)talk_data; + + /* Append the PAM message or prompt to the unsent message buffer */ + if (msg->msg) { + unsigned char *last_buf_pos = msg_buf->buf + max_pam_buffered_msg_len - 1; + if (msg_buf->ptr + strlen(msg->msg) >= last_buf_pos) { + /* Cannot happen: the PAM message buffer too small. */ + MY_ASSERT_UNREACHABLE(); + return PAM_CONV_ERR; + } + memcpy(msg_buf->ptr, msg->msg, strlen(msg->msg)); + msg_buf->ptr += strlen(msg->msg); + *(msg_buf->ptr)++ = '\n'; + } + + if (msg->msg_style == PAM_PROMPT_ECHO_OFF || + msg->msg_style == PAM_PROMPT_ECHO_ON) { + int pkt_len; + unsigned char *pkt; + + msg_buf->buf[0] = pam_msg_style_to_char(msg->msg_style); + + /* Write the message. */ + if (data->vio->write_packet(data->vio, msg_buf->buf, + msg_buf->ptr - msg_buf->buf - 1)) + return PAM_CONV_ERR; + + /* Read the answer */ + if ((pkt_len = data->vio->read_packet(data->vio, &pkt)) < 0) + return PAM_CONV_ERR; + + resp->resp = static_cast(malloc(pkt_len + 1)); + if (resp->resp == nullptr) return PAM_BUF_ERR; + + strncpy(resp->resp, (char *)pkt, pkt_len); + resp->resp[pkt_len] = '\0'; + + if (msg->msg_style == PAM_PROMPT_ECHO_OFF) + data->info->password_used = PASSWORD_USED_YES; + + msg_buf->ptr = msg_buf->buf + 1; + } + + return PAM_SUCCESS; +} + +static int auth_pam_init(MYSQL_PLUGIN plugin_info) { + int count; + auth_pam_common_init("auth_pam"); + count = array_elements(pam_auth_memory); + mysql_memory_register("auth_pam", pam_auth_memory, count); + auth_pam_plugin_info = plugin_info; + return 0; +} + +static struct st_mysql_auth pam_auth_handler = { + MYSQL_AUTHENTICATION_INTERFACE_VERSION, + "dialog", + &authenticate_user_with_pam_server, + &auth_pam_generate_auth_string_hash, + &auth_pam_validate_auth_string_hash, + &auth_pam_set_salt, + 0UL, + nullptr}; + +mysql_declare_plugin(auth_pam) { + MYSQL_AUTHENTICATION_PLUGIN, &pam_auth_handler, "auth_pam", "Percona, Inc.", + "PAM authentication plugin", PLUGIN_LICENSE_GPL, auth_pam_init, nullptr, + nullptr, 0x0001, nullptr, nullptr, nullptr +#if MYSQL_PLUGIN_INTERFACE_VERSION >= 0x103 + , + 0 +#endif +} +mysql_declare_plugin_end; diff --git a/plugin/percona-pam-for-mysql/src/auth_pam_common.cc b/plugin/percona-pam-for-mysql/src/auth_pam_common.cc new file mode 100644 index 000000000000..11067fd9d3a7 --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/auth_pam_common.cc @@ -0,0 +1,208 @@ +/* +(C) 2011-2015 Percona LLC 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 as published by +the Free Software Foundation; version 2 of the License. + +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 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 Street, Fifth Floor, Boston, MA 02110-1301, USA + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "auth_mapping.h" +#include "auth_pam_common.h" +#include "groups.h" +#include "my_sys.h" +#include "mysql/psi/mysql_memory.h" + +/* The server plugin */ + +PSI_memory_key key_memory_pam_mapping_iter; +PSI_memory_key key_memory_pam_group_iter; + +static PSI_memory_info common_pam_memory[] = { + {&key_memory_pam_mapping_iter, "auth_pam_mapping_iterator", + PSI_FLAG_ONLY_GLOBAL_STAT, PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, + {&key_memory_pam_group_iter, "auth_pam_group_iterator", + PSI_FLAG_ONLY_GLOBAL_STAT, PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, +}; + +/** The MySQL service name for PAM configuration */ +static const char *service_name_default = "mysqld"; + +void auth_pam_common_init(const char *psi_category) { + int count = array_elements(common_pam_memory); + mysql_memory_register(psi_category, common_pam_memory, count); +} + +static bool valid_pam_msg_style(int pam_msg_style) { + switch (pam_msg_style) { + case PAM_PROMPT_ECHO_OFF: + case PAM_PROMPT_ECHO_ON: + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + return true; + default: + return false; + } +} + +/** The maximum length of service name. It shouldn't be too long as it's + filename in pam.d directory also */ +static const constexpr auto max_pam_service_name_len = 64; + +static void free_pam_response(struct pam_response **resp, int n) { + for (int i = 0; i < n; i++) { + free((*resp)[i].resp); + } + free(*resp); + *resp = nullptr; +} + +static int vio_server_conv(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) { + struct pam_conv_data *data = (struct pam_conv_data *)appdata_ptr; + + if (data == nullptr) { + MY_ASSERT_UNREACHABLE(); + return PAM_CONV_ERR; + } + + *resp = (struct pam_response *)calloc(sizeof(struct pam_response), num_msg); + if (*resp == nullptr) return PAM_BUF_ERR; + + void *talk_data; + int error = auth_pam_client_talk_init(&talk_data); + if (error != PAM_SUCCESS) { + free_pam_response(resp, 0); + return error; + } + + for (int i = 0; i < num_msg; i++) { + if (!valid_pam_msg_style(msg[i]->msg_style)) { + auth_pam_client_talk_finalize(talk_data); + free_pam_response(resp, i); + return PAM_CONV_ERR; + } + + error = auth_pam_talk_perform(msg[i], &(*resp)[i], data, talk_data); + if (error != PAM_SUCCESS) { + auth_pam_client_talk_finalize(talk_data); + free_pam_response(resp, i); + return error; + } + } + auth_pam_client_talk_finalize(talk_data); + return PAM_SUCCESS; +} + +int authenticate_user_with_pam_server(MYSQL_PLUGIN_VIO *vio, + MYSQL_SERVER_AUTH_INFO *info) { + /* Set service name as specified in auth_string. If no auth_string + provided or parsing error occurs, then keep default value */ + char service_name[max_pam_service_name_len]; + strcpy(service_name, service_name_default); + if (info->auth_string) + mapping_get_service_name(service_name, sizeof(service_name), + info->auth_string); + + info->password_used = PASSWORD_USED_NO_MENTION; + + pam_handle_t *pam_handle; + struct pam_conv_data data = {vio, info}; + struct pam_conv conv_func_info = {&vio_server_conv, &data}; + int error = + pam_start(service_name, info->user_name, &conv_func_info, &pam_handle); + if (error != PAM_SUCCESS) return CR_ERROR; + + error = pam_set_item(pam_handle, PAM_RUSER, info->user_name); + if (error != PAM_SUCCESS) { + pam_end(pam_handle, error); + return CR_ERROR; + } + + error = pam_set_item(pam_handle, PAM_RHOST, info->host_or_ip); + if (error != PAM_SUCCESS) { + pam_end(pam_handle, error); + return CR_ERROR; + } + + error = pam_authenticate(pam_handle, 0); + if (error != PAM_SUCCESS) { + pam_end(pam_handle, error); + return CR_ERROR; + } + + error = pam_acct_mgmt(pam_handle, 0); + if (error != PAM_SUCCESS) { + pam_end(pam_handle, error); + return CR_ERROR; + } + + /* Get the authenticated user name from PAM */ + char *pam_mapped_user_name; + error = pam_get_item(pam_handle, PAM_USER, + const_cast( + reinterpret_cast(&pam_mapped_user_name))); + if (error != PAM_SUCCESS) { + pam_end(pam_handle, error); + return CR_ERROR; + } + + /* Check if user name from PAM is the same as provided for MySQL. If + different, use the new user name for MySQL authorization and as + CURRENT_USER() value. */ + if (strcmp(info->user_name, pam_mapped_user_name)) { + strncpy(info->authenticated_as, pam_mapped_user_name, + MYSQL_USERNAME_LENGTH); + info->authenticated_as[MYSQL_USERNAME_LENGTH] = '\0'; + } + + if (info->auth_string) { + mapping_lookup_user(pam_mapped_user_name, info->authenticated_as, + MYSQL_USERNAME_LENGTH, info->auth_string); + } + + error = pam_end(pam_handle, error); + if (error != PAM_SUCCESS) return CR_ERROR; + + return CR_OK; +} + +int auth_pam_generate_auth_string_hash(char *outbuf, unsigned int *buflen, + const char *inbuf, + unsigned int inbuflen) { + /* + fail if buffer specified by server cannot be copied to output buffer + */ + if (*buflen < inbuflen) return 1; /* error */ + strncpy(outbuf, inbuf, inbuflen); + *buflen = strlen(inbuf); + return 0; /* success */ +} + +int auth_pam_validate_auth_string_hash(char *const buf __attribute__((unused)), + unsigned int len + __attribute__((unused))) { + return 0; /* success */ +} + +int auth_pam_set_salt(const char *password __attribute__((unused)), + unsigned int password_len __attribute__((unused)), + unsigned char *salt __attribute__((unused)), + unsigned char *salt_len) { + *salt_len = 0; + return 0; /* success */ +} diff --git a/plugin/percona-pam-for-mysql/src/auth_pam_common.h b/plugin/percona-pam-for-mysql/src/auth_pam_common.h new file mode 100644 index 000000000000..9976adebe238 --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/auth_pam_common.h @@ -0,0 +1,82 @@ +#ifndef AUTH_PAM_COMMON_INCLUDED +#define AUTH_PAM_COMMON_INCLUDED +/* + (C) 2012 Percona Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + 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 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 Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** + @file + + PAM authentication for MySQL, common definitions for side plugins. + + For the general description, see the top comment in auth_pam_common.c. +*/ + +#include +#include +#ifdef HAVE_SECURITY_PAM_MISC_H +#include +#elif defined(HAVE_SECURITY_OPENPAM_H) +#include +#endif + +#include "mysql/client_plugin.h" +#include "mysql/plugin.h" +#include "mysql/plugin_auth.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct pam_conv_data { + MYSQL_PLUGIN_VIO *vio; + MYSQL_SERVER_AUTH_INFO *info; +}; + +extern MYSQL_PLUGIN auth_pam_plugin_info; + +extern PSI_memory_key key_memory_pam_mapping_iter; +extern PSI_memory_key key_memory_pam_group_iter; + +void auth_pam_common_init(const char *psi_category); + +/** Define following three functions for your specific client plugin */ + +int auth_pam_client_talk_init(void **talk_data); + +int auth_pam_talk_perform(const struct pam_message *msg, + struct pam_response *resp, struct pam_conv_data *data, + void *talk_data); + +void auth_pam_client_talk_finalize(void *talk_data); + +int authenticate_user_with_pam_server(MYSQL_PLUGIN_VIO *vio, + MYSQL_SERVER_AUTH_INFO *info); + +int auth_pam_generate_auth_string_hash(char *outbuf, unsigned int *buflen, + const char *inbuf, + unsigned int inbuflen); + +int auth_pam_validate_auth_string_hash(char *const buf, unsigned int len); + +int auth_pam_set_salt(const char *password, unsigned int password_len, + unsigned char *salt, unsigned char *salt_len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugin/percona-pam-for-mysql/src/auth_pam_compat.cc b/plugin/percona-pam-for-mysql/src/auth_pam_compat.cc new file mode 100644 index 000000000000..c855f5f8f49c --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/auth_pam_compat.cc @@ -0,0 +1,129 @@ +/* +(C) 2012, 2015 Percona LLC 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 as published by +the Free Software Foundation; version 2 of the License. + +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 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 Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** + @file + + PAM authentication for MySQL, server-side plugin for the + production use. + + Oracle MySQL-compatible plugin. Acts as a mediator + between the MySQL server, the MySQL client, and the PAM backend. + + The server plugin requests authentication from the PAM backend, and reads one + phrase from client plugin. mysql_clear_password plugin used as client plugin. + + This plugin does not encrypt the communication channel in any way. If this is + required, a SSL connection should be used. + + To install this plugin, copy the .so file to the plugin directory and do + + INSTALL PLUGIN auth_pam SONAME 'auth_pam_compat.so'; + + To use this plugin for one particular user, specify it at user's creation time + (TODO: tested with localhost only): + + CREATE USER 'username'@'hostname' IDENTIFIED WITH auth_pam_compat; + + Alternatively UPDATE the mysql.user table to set the plugin value for an + existing user. + + Also it is possible to use this plugin to authenticate anonymous users: + + CREATE USER ''@'hostname' IDENTIFIED WITH auth_pam_compat; + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "auth_pam_common.h" +#include "my_sys.h" + +MYSQL_PLUGIN auth_pam_plugin_info; + +int auth_pam_client_talk_init(void **talk_data) { + int *num_talks = static_cast( + my_malloc(PSI_NOT_INSTRUMENTED, sizeof(int), MY_ZEROFILL)); + *talk_data = (void *)num_talks; + return (num_talks != nullptr) ? PAM_SUCCESS : PAM_BUF_ERR; +} + +void auth_pam_client_talk_finalize(void *talk_data) { my_free(talk_data); } + +int auth_pam_talk_perform(const struct pam_message *msg, + struct pam_response *resp, struct pam_conv_data *data, + void *talk_data) { + if (msg->msg_style == PAM_PROMPT_ECHO_OFF || + msg->msg_style == PAM_PROMPT_ECHO_ON) { + /* mysql_clear_password plugin has support for only single phrase */ + int *num_talks = (int *)talk_data; + if (*num_talks > 1) return PAM_CONV_ERR; + + /* Read the answer */ + unsigned char *pkt; + int pkt_len = data->vio->read_packet(data->vio, &pkt); + if (pkt_len < 0) return PAM_CONV_ERR; + + resp->resp = static_cast(malloc(pkt_len + 1)); + if (resp->resp == nullptr) return PAM_BUF_ERR; + + strncpy(resp->resp, (char *)pkt, pkt_len); + resp->resp[pkt_len] = '\0'; + + /** + we could only guess whether password was used or not + normally we would set PASSWORD_USED_NO_MENTION but + because of http://bugs.mysql.com/bug.php?id=72536 + we set PASSWORD_USED_YES. + */ + data->info->password_used = PASSWORD_USED_YES; + ++(*num_talks); + } + + return PAM_SUCCESS; +} + +static int auth_pam_compat_init(MYSQL_PLUGIN plugin_info) { + auth_pam_common_init("auth_pam_compat"); + auth_pam_plugin_info = plugin_info; + return 0; +} + +static struct st_mysql_auth pam_auth_handler = { + MYSQL_AUTHENTICATION_INTERFACE_VERSION, + "mysql_clear_password", + &authenticate_user_with_pam_server, + &auth_pam_generate_auth_string_hash, + &auth_pam_validate_auth_string_hash, + &auth_pam_set_salt, + 0UL, + nullptr}; + +mysql_declare_plugin(auth_pam) { + MYSQL_AUTHENTICATION_PLUGIN, &pam_auth_handler, "auth_pam_compat", + "Percona, Inc.", "PAM authentication plugin", PLUGIN_LICENSE_GPL, + auth_pam_compat_init, nullptr, nullptr, 0x0001, nullptr, nullptr, nullptr +#if MYSQL_PLUGIN_INTERFACE_VERSION >= 0x103 + , + 0 +#endif +} +mysql_declare_plugin_end; diff --git a/plugin/percona-pam-for-mysql/src/dialog.cc b/plugin/percona-pam-for-mysql/src/dialog.cc new file mode 100644 index 000000000000..e676ab046d19 --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/dialog.cc @@ -0,0 +1,327 @@ +/* Copyright (c) 2010, 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 as + published by the Free Software Foundation; version 2 of the + License. + + 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 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 */ + +/** + @file + + dialog client authentication plugin with examples + + dialog is a general purpose client authentication plugin, it simply + asks the user the question, as provided by the server and reports + the answer back to the server. No encryption is involved, + the answers are sent in clear text. + + Two examples are provided: two_questions server plugin, that asks + the password and an "Are you sure?" question with a reply "yes, of course". + It demonstrates the usage of "password" (input is hidden) and "ordinary" + (input can be echoed) questions, and how to mark the last question, + to avoid an extra roundtrip. + + And three_attempts plugin that gives the user three attempts to enter + a correct password. It shows the situation when a number of questions + is not known in advance. +*/ +#if defined(WIN32) && !defined(RTLD_DEFAULT) +#define RTLD_DEFAULT GetModuleHandle(NULL) +#endif + +#include +#include +#include +#include "mysql.h" +#include "mysql/client_plugin.h" +#include "mysql/plugin_auth.h" + +#ifdef HAVE_DLFCN_H +#include +#endif + +#if !defined(_GNU_SOURCE) +#define _GNU_SOURCE /* for RTLD_DEFAULT */ +#endif + +/** + first byte of the question string is the question "type". + It can be an "ordinary" or a "password" question. + The last bit set marks a last question in the authentication exchange. +*/ +#define ORDINARY_QUESTION "\2" +#define LAST_QUESTION "\3" +#define PASSWORD_QUESTION "\4" +#define LAST_PASSWORD "\5" + +/********************* SERVER SIDE ****************************************/ + +/** + dialog demo with two questions, one password and one, the last, ordinary. +*/ +static int two_questions(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) { + /* send a password question */ + if (vio->write_packet( + vio, + (const unsigned char *)PASSWORD_QUESTION "Password, please:", 18)) + return CR_ERROR; + + /* read the answer */ + unsigned char *pkt; + int pkt_len = vio->read_packet(vio, &pkt); + if (pkt_len < 0) return CR_ERROR; + + info->password_used = PASSWORD_USED_YES; + + /* fail if the password is wrong */ + if (strcmp((const char *)pkt, info->auth_string)) return CR_ERROR; + + /* send the last, ordinary, question */ + if (vio->write_packet( + vio, (const unsigned char *)LAST_QUESTION "Are you sure ?", 15)) + return CR_ERROR; + + /* read the answer */ + if ((pkt_len = vio->read_packet(vio, &pkt)) < 0) return CR_ERROR; + + /* check the reply */ + return strcmp((const char *)pkt, "yes, of course") ? CR_ERROR : CR_OK; +} + +static struct st_mysql_auth two_handler = { + MYSQL_AUTHENTICATION_INTERFACE_VERSION, + "dialog", /* requires dialog client plugin */ + two_questions, + nullptr, + nullptr, + nullptr, + 0UL, + nullptr}; + +/* dialog demo where the number of questions is not known in advance */ +static int three_attempts(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) { + for (int i = 0; i < 3; i++) { + /* send the prompt */ + if (vio->write_packet( + vio, + (const unsigned char *)PASSWORD_QUESTION "Password, please:", 18)) + return CR_ERROR; + + /* read the password */ + unsigned char *pkt; + int pkt_len = vio->read_packet(vio, &pkt); + if (pkt_len < 0) return CR_ERROR; + + info->password_used = PASSWORD_USED_YES; + + /* + finish, if the password is correct. + note, that we did not mark the prompt packet as "last" + */ + if (strcmp((const char *)pkt, info->auth_string) == 0) return CR_OK; + } + + return CR_ERROR; +} + +static struct st_mysql_auth three_handler = { + MYSQL_AUTHENTICATION_INTERFACE_VERSION, + "dialog", /* requires dialog client plugin */ + three_attempts, + nullptr, + nullptr, + nullptr, + 0UL, + nullptr}; + +mysql_declare_plugin(dialog){ + MYSQL_AUTHENTICATION_PLUGIN, + &two_handler, + "two_questions", + "Sergei Golubchik", + "Dialog plugin demo 1", + PLUGIN_LICENSE_GPL, + nullptr, + nullptr, + nullptr, + 0x0100, + nullptr, + nullptr, + nullptr, + 0, +}, + { + MYSQL_AUTHENTICATION_PLUGIN, + &three_handler, + "three_attempts", + "Sergei Golubchik", + "Dialog plugin demo 2", + PLUGIN_LICENSE_GPL, + nullptr, + nullptr, + nullptr, + 0x0100, + nullptr, + nullptr, + nullptr, + 0, + } mysql_declare_plugin_end; + +/********************* CLIENT SIDE ***************************************/ +/* + This plugin performs a dialog with the user, asking questions and + reading answers. Depending on the client it may be desirable to do it + using GUI, or console, with or without curses, or read answers + from a smartcard, for example. + + To support all this variety, the dialog plugin has a callback function + "authentication_dialog_ask". If the client has a function of this name + dialog plugin will use it for communication with the user. Otherwise + a default fgets() based implementation will be used. +*/ + +/** + type of the mysql_authentication_dialog_ask function + + @param mysql mysql + @param type type of the input + 1 - ordinary string input + 2 - password string + @param prompt prompt + @param buf a buffer to store the use input + @param buf_len the length of the buffer + + @retval a pointer to the user input string. + It may be equal to 'buf' or to 'mysql->password'. + In all other cases it is assumed to be an allocated + string, and the "dialog" plugin will free() it. +*/ +typedef char *(*mysql_authentication_dialog_ask_t)(MYSQL *mysql, int type, + const char *prompt, + char *buf, int buf_len); + +static mysql_authentication_dialog_ask_t ask; + +static char *builtin_ask(MYSQL *mysql [[maybe_unused]], + int type [[maybe_unused]], const char *prompt, + char *buf, int buf_len) { + if (type == 2) /* password */ + { + char *const password = get_tty_password(prompt); + strncpy(buf, password, buf_len - 1); + buf[buf_len - 1] = 0; + free(password); + } else { + if (!fgets(buf, buf_len - 1, stdin)) + buf[0] = 0; + else { + const int len = strlen(buf); + if (len && buf[len - 1] == '\n') buf[len - 1] = 0; + } + } + + return buf; +} + +/** + The main function of the dialog plugin. + + Read the prompt, ask the question, send the reply, repeat until + the server is satisfied. + + @note + 1. this plugin shows how a client authentication plugin + may read a MySQL protocol OK packet internally - which is important + where a number of packets is not known in advance. + 2. the first byte of the prompt is special. it is not + shown to the user, but signals whether it is the last question + (prompt[0] & 1 == 1) or not last (prompt[0] & 1 == 0), + and whether the input is a password (not echoed). + 3. the prompt is expected to be sent zero-terminated +*/ +static int perform_dialog(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) { + unsigned char cmd = 0; + char reply_buf[1024]; + bool first = true; + + do { + /* read the prompt */ + unsigned char *pkt; + int pkt_len = vio->read_packet(vio, &pkt); + if (pkt_len < 0) return CR_ERROR; + + char *reply; + if (pkt == nullptr && first) { + /* + in mysql_change_user() the client sends the first packet, so + the first vio->read_packet() does nothing (pkt == 0). + + We send the "password", assuming the client knows what it's doing. + (in other words, the dialog plugin should be only set as a default + authentication plugin on the client if the first question + asks for a password - which will be sent in clear text, by the way) + */ + reply = mysql->passwd; + } else { + cmd = *pkt++; + + /* is it MySQL protocol packet ? */ + if (cmd == 0 || cmd == 254) + return CR_OK_HANDSHAKE_COMPLETE; /* yes. we're done */ + + /* + asking for a password in the first packet mean mysql->password, if it's + set otherwise we ask the user and read the reply + */ + if ((cmd >> 1) == 2 && first && mysql->passwd[0]) + reply = mysql->passwd; + else + reply = ask(mysql, cmd >> 1, (const char *)pkt, reply_buf, + sizeof(reply_buf)); + if (!reply) return CR_ERROR; + } + /* send the reply to the server */ + const int res = + vio->write_packet(vio, (const unsigned char *)reply, strlen(reply) + 1); + + if (reply != mysql->passwd && reply != reply_buf) free(reply); + + if (res) return CR_ERROR; + + /* repeat unless it was the last question */ + first = false; + } while ((cmd & 1) != 1); + + /* the job of reading the ok/error packet is left to the server */ + return CR_OK; +} + +/** + initialization function of the dialog plugin + + Pick up the client's authentication_dialog_ask() function, if exists, + or fall back to the default implementation. +*/ + +static int init_dialog(char *unused1 [[maybe_unused]], + size_t unused2 [[maybe_unused]], + int unused3 [[maybe_unused]], + va_list unused4 [[maybe_unused]]) { + void *sym = dlsym(RTLD_DEFAULT, "mysql_authentication_dialog_ask"); + ask = sym ? (mysql_authentication_dialog_ask_t)sym : builtin_ask; + return 0; +} + +mysql_declare_client_plugin(AUTHENTICATION) "dialog", "Sergei Golubchik", + "Dialog Client Authentication Plugin", {0, 1, 0}, "GPL", + nullptr, init_dialog, nullptr, nullptr, nullptr, perform_dialog, + nullptr, mysql_end_client_plugin; diff --git a/plugin/percona-pam-for-mysql/src/groups.cc b/plugin/percona-pam-for-mysql/src/groups.cc new file mode 100644 index 000000000000..27b9f1f8114d --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/groups.cc @@ -0,0 +1,146 @@ +/* +(C) 2013, 2016 Percona LLC 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 as published by +the Free Software Foundation; version 2 of the License. + +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 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 Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include +#include +#include +#include +#include +#include + +#include "auth_pam_common.h" + +#include "my_sys.h" + +static int gr_buf_size = 0; + +#ifdef __APPLE__ +using my_gid_t = int; +#else +using my_gid_t = gid_t; +#endif + +/** Groups iterator. It's not exposed outsude */ +struct groups_iter { + char *buf; + my_gid_t *groups; + int current_group; + int ngroups; + int buf_size; +}; + +/** Create iterator through user groups. + Initially iterator set to position before first + group. On success non-NULL pointer returned, otherwise NULL */ +struct groups_iter *groups_iter_new(const char *user_name) { + if (gr_buf_size <= 0) { + long gr_size_max, pw_size_max; + gr_size_max = sysconf(_SC_GETGR_R_SIZE_MAX); + pw_size_max = sysconf(_SC_GETPW_R_SIZE_MAX); + gr_buf_size = gr_size_max > pw_size_max ? gr_size_max : pw_size_max; + } + + struct groups_iter *const it = (struct groups_iter *)my_malloc( + key_memory_pam_group_iter, sizeof(struct groups_iter), + MYF(MY_FAE | MY_ZEROFILL)); + + it->buf_size = gr_buf_size; + if (it->buf_size <= 0) it->buf_size = 1024; + + it->buf = + (char *)my_malloc(key_memory_pam_group_iter, it->buf_size, MYF(MY_FAE)); + + struct passwd pwd, *pwd_result; + int error; + while ((error = getpwnam_r(user_name, &pwd, it->buf, it->buf_size, + &pwd_result)) == ERANGE) { + it->buf_size = it->buf_size * 2; + it->buf = (char *)my_realloc(key_memory_pam_group_iter, it->buf, + it->buf_size, MYF(MY_FAE)); + } + if (error != 0 || pwd_result == nullptr) { + my_plugin_log_message(&auth_pam_plugin_info, MY_ERROR_LEVEL, + "Unable to obtain the passwd entry for the user " + "'%s'.", + user_name); + my_free(it->buf); + my_free(it); + return nullptr; + } + + gr_buf_size = it->buf_size; + + it->ngroups = 1024; + it->groups = static_cast(my_malloc( + key_memory_pam_group_iter, it->ngroups * sizeof(gid_t), MYF(MY_FAE))); + error = getgrouplist(user_name, pwd_result->pw_gid, it->groups, &it->ngroups); + if (error == -1) { + it->groups = static_cast( + my_realloc(key_memory_pam_group_iter, it->groups, + it->ngroups * sizeof(gid_t), MYF(MY_FAE))); + error = + getgrouplist(user_name, pwd_result->pw_gid, it->groups, &it->ngroups); + if (error == -1) { + my_plugin_log_message(&auth_pam_plugin_info, MY_ERROR_LEVEL, + "Unable to obtain the group access list for " + "the user '%s'.", + user_name); + my_free(it->buf); + my_free(it->groups); + my_free(it); + return nullptr; + } + } + + return it; +} + +/** Move iterator to next group. + On success group name is returned, + otherwise NULL */ +const char *groups_iter_next(struct groups_iter *it) { + if (it->current_group >= it->ngroups) return nullptr; + + int error; + struct group grp, *grp_result; + while ((error = getgrgid_r(it->groups[it->current_group], &grp, it->buf, + it->buf_size, &grp_result)) == ERANGE) { + it->buf_size = it->buf_size * 2; + it->buf = (char *)my_realloc(key_memory_pam_group_iter, it->buf, + it->buf_size, MYF(MY_FAE)); + } + if (error != 0 || grp_result == nullptr) { + my_plugin_log_message(&auth_pam_plugin_info, MY_ERROR_LEVEL, + "Unable to obtain the group record for the group " + "id %d.", + (int)it->groups[it->current_group]); + return nullptr; + } + ++it->current_group; + + return grp_result->gr_name; +} + +/** Make iterator to point to the beginning again */ +void groups_iter_reset(struct groups_iter *it) { it->current_group = 0; } + +/** Finish iteration and release iterator */ +void groups_iter_free(struct groups_iter *it) { + my_free(it->buf); + my_free(it->groups); + my_free(it); +} diff --git a/plugin/percona-pam-for-mysql/src/groups.h b/plugin/percona-pam-for-mysql/src/groups.h new file mode 100644 index 000000000000..30b937468aeb --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/groups.h @@ -0,0 +1,45 @@ +#ifndef AUTH_PAM_GROUPS_INCLUDED +#define AUTH_PAM_GROUPS_INCLUDED +/* + (C) 2013 Percona LLC 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 as published by + the Free Software Foundation; version 2 of the License. + + 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 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 Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** + @file + + PAM authentication for MySQL, interface for groups enumeration. + +*/ + +struct groups_iter; + +/** Create iterator through user groups. + Initially iterator set to position before first + group. On success non-NULL pointer returned, otherwise NULL */ +struct groups_iter *groups_iter_new(const char *user_name); + +/** Move iterator to next group. + On success group name is returned, + otherwise NULL */ +const char *groups_iter_next(struct groups_iter *it); + +/** Make iterator to point to beginning again */ +void groups_iter_reset(struct groups_iter *it); + +/** Finish iteration and release iterator */ +void groups_iter_free(struct groups_iter *it); + +#endif diff --git a/plugin/percona-pam-for-mysql/src/lib_auth_pam_client.c b/plugin/percona-pam-for-mysql/src/lib_auth_pam_client.c new file mode 100644 index 000000000000..caed7563391a --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/lib_auth_pam_client.c @@ -0,0 +1,72 @@ +/* + (C) 2011 Percona Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + 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 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 Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** + @file + + PAM authentication for MySQL, common code for client-side plugins. + + For the general description, see the top comment in auth_pam.c. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "lib_auth_pam_client.h" + +#include +#include + +#define MY_ASSERT_UNREACHABLE() assert(0) + +int authenticate_user_with_pam_client_common( + MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql __attribute__((unused)), + prompt_fn echoless_prompt_fn, prompt_fn echo_prompt_fn, + info_fn show_error_fn, info_fn show_info_fn) { + do { + char *buf; + int pkt_len; + + if ((pkt_len = vio->read_packet(vio, (unsigned char **)&buf)) < 0) + return CR_ERROR; + + /* The first byte is the message type, followed by the message itself. */ + + if (buf[0] == '\2' || buf[0] == '\3') { + /* '\2' - PAM_PROMPT_ECHO_OFF, '\3' - PAM_PROMPT_ECHO_ON */ + char *reply = (buf[0] == '\2') ? echoless_prompt_fn(&buf[1]) + : echo_prompt_fn(&buf[1]); + if (!reply) return CR_ERROR; + if (vio->write_packet(vio, (unsigned char *)reply, strlen(reply) + 1)) { + free(reply); + return CR_ERROR; + } + free(reply); + } else if (buf[0] == '\4') /* PAM_ERROR_MSG */ + show_error_fn(&buf[1]); + else if (buf[0] == '\5') /* PAM_TEXT_INFO */ + show_info_fn(&buf[1]); + else if (buf[0] == '\0') /* end-of-authorization */ + return CR_OK; + else + return CR_ERROR; /* Unknown! */ + } while (1); + + /* Should not come here */ + MY_ASSERT_UNREACHABLE(); + return CR_ERROR; +} diff --git a/plugin/percona-pam-for-mysql/src/lib_auth_pam_client.h b/plugin/percona-pam-for-mysql/src/lib_auth_pam_client.h new file mode 100644 index 000000000000..6aa1c7b27a41 --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/lib_auth_pam_client.h @@ -0,0 +1,77 @@ +#ifndef LIB_AUTH_PAM_CLIENT_INCLUDED +#define LIB_AUTH_PAM_CLIENT_INCLUDED +/* + (C) 2011 Percona Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + 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 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 Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** + @file + + PAM authentication for MySQL, common definitions for client-side plugins. + + For the general description, see the top comment in auth_pam.c. +*/ + +#define STDCALL + +#include "mysql/client_plugin.h" + +/** + Callback type for functions that prompt the user for (echoed or silent) input + and return it. Should returns a pointer to malloc-allocated string, the + caller is responsible for freeing it. Should return NULL in the case of a + memory allocation or I/O error. */ +typedef char *(*prompt_fn)(const char *); + +/** + Callback type for functions that show user some info (error or notification). +*/ +typedef void (*info_fn)(const char *); + +struct st_mysql; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Client-side PAM auth plugin implementation. + + Communicates with the server-side plugin and does user interaction using the + provided callbacks. + + @param vio TODO + @param mysql TODO + @param echoless_prompt_fn callback to use to prompt the user for non-echoed + input (e.g. password) + @param echo_prompt_fn callback to use to prompt the user for echoed input + (e.g. user name) + @param show_error_fn callback to use to show the user an error message + @param show_info_fn callback to use to show the user an informational message + + @return Authentication conversation status + @retval CR_OK the authentication dialog is completed successfully + @retval CR_ERROR the authentication dialog is aborted due to error +*/ +int authenticate_user_with_pam_client_common( + MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql, prompt_fn echoless_prompt_fn, + prompt_fn echo_prompt_fn, info_fn show_error_fn, info_fn show_info_fn); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugin/percona-pam-for-mysql/src/test_auth_pam_client.c b/plugin/percona-pam-for-mysql/src/test_auth_pam_client.c new file mode 100644 index 000000000000..875068e30eb7 --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/test_auth_pam_client.c @@ -0,0 +1,69 @@ +/* + (C) 2011 Percona Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + 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 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 Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** + @file + + PAM authentication for MySQL, the test version of the client-side plugin. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#define STDCALL + +#include "mysql/client_plugin.h" +#include "mysql/plugin_auth.h" + +#include "lib_auth_pam_client.h" + +const char *echo_off_reply_1 = "aaaaaaa"; +const char *echo_off_reply_2 = "AAAAAAA"; + +const char *echo_on_reply_1 = "bbbbbbbbbb"; +const char *echo_on_reply_2 = "BBBBBBBBBB"; + +/* Returns alternating echo_off_reply_1 and echo_off_reply_2 */ +static char *test_prompt_echo_off(const char *prompt __attribute__((unused))) { + static unsigned call_no = 0; + return strdup((call_no++ % 2) == 0 ? echo_off_reply_1 : echo_off_reply_2); +} + +/* Returns alternating echo_on_reply_1 and echo_on_reply_2 */ +static char *test_prompt_echo_on(const char *prompt __attribute__((unused))) { + static unsigned call_no = 0; + return strdup((call_no++ % 2) == 0 ? echo_on_reply_1 : echo_on_reply_2); +} + +/* Pretend we have shown the message to the user */ +static void test_show_anything(const char *message __attribute__((unused))) {} + +static int test_pam_auth_client(MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql) { + return authenticate_user_with_pam_client_common( + vio, mysql, &test_prompt_echo_off, &test_prompt_echo_on, + &test_show_anything, &test_show_anything); +} + +mysql_declare_client_plugin(AUTHENTICATION) "auth_pam_test", "Percona, Inc.", + "Test version of the client PAM authentication plugin. " + "DO NOT USE IN PRODUCTION.", + {0, 1, 0}, "GPL", NULL, NULL, /* init */ + NULL, /* deinit */ + NULL, /* options */ + &test_pam_auth_client mysql_end_client_plugin; diff --git a/plugin/percona-pam-for-mysql/test/dbqp/percona_tests/percona_pam/pam_mapping_test.py b/plugin/percona-pam-for-mysql/test/dbqp/percona_tests/percona_pam/pam_mapping_test.py new file mode 100755 index 000000000000..03f84afe5209 --- /dev/null +++ b/plugin/percona-pam-for-mysql/test/dbqp/percona_tests/percona_pam/pam_mapping_test.py @@ -0,0 +1,118 @@ +#! /usr/bin/env python +# -*- mode: python; indent-tabs-mode: nil; -*- +# vim:expandtab:shiftwidth=2:tabstop=2:smarttab: +# +# Copyright (C) 2013 Percona LLC 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 as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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 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 + +import os +import time +import shutil +import signal +import subprocess +import re +import grp + +from lib.util.mysqlBaseTestCase import mysqlBaseTestCase +from lib.util.mysql_methods import execute_cmd + + +server_requirements = [[]] +servers = [] +server_manager = None +test_executor = None +pamcfg = '/etc/pam.d/mysqld' + +def group_exists(groupname): + try: + grp.getgrnam(groupname)[0] + except KeyError: + return False + return True + +class basicTest(mysqlBaseTestCase): + + def test_pam_basic(self): + percent_string = '%' + opt_matrix_req = ['pam_plugin_dir'] + self.servers = servers + logging = test_executor.logging + master_server = servers[0] + output_path = os.path.join(master_server.vardir, 'pam.out') + test_executor.matrix_manager.matrix_check_req(opt_matrix_req) + # This is a master + if test_executor.matrix_manager.option_matrix['pam_user']: + pam_user = test_executor.matrix_manager.option_matrix['pam_user'] + else: + pam_user = 'pamuser' + + groups = ['grp%d' % (n) for n in xrange(3)] + users = ['user1%d' % (n) for n in xrange(3)] + + for grp in groups: + if not group_exists(grp): + subprocess.call(["groupadd", grp]) + + # Create UNIX system account + if not test_executor.system_manager.user_exists(pam_user): + subprocess.call(["useradd", pam_user, "-g", groups[0], "-G", ",".join(groups[1:]) ]) + else: + subprocess.call(["usermod", "-g", groups[0], "-G", ",".join(groups[1:]), pam_user ]) + + # Create PAM config + if (os.path.isfile(pamcfg)): + os.remove(pamcfg) + + pamcfg_fh = open("/etc/pam.d/mysqld", "wb") + pamcfg_fh.write("auth\trequired\tpam_permit.so\n") + pamcfg_fh.write("account\trequired\tpam_permit.so\n") + pamcfg_fh.close(); + + master_server.stop() + + # setup plugin, users, privileges + groups.reverse() + groups = [ "grp21", "grp22" ] + groups + users = [ "usr21", "usr22" ] + users + queries = [ "INSTALL PLUGIN auth_pam SONAME 'auth_pam.so';" ] + \ + [ "CREATE USER '%s'@'localhost';" % (user) for user in users ] + \ + [ "CREATE USER ''@'' IDENTIFIED WITH auth_pam AS 'mysqld, %s';" \ + % ( ",".join([ user + "=" + group for user, group in zip(groups, users) ] ) ) ] + \ + [ "GRANT PROXY ON '%s'@'localhost' TO ''@'';" % (user) for user in users ] + \ + [ "SELECT user, host, authentication_string FROM mysql.user;", \ + "FLUSH PRIVILEGES;", "SHOW VARIABLES LIKE 'plugin%'" ] + + master_server.server_options.append('--plugin-dir=%s' %(test_executor.matrix_manager.option_matrix['pam_plugin_dir'])) + + master_server.start() + self.assertEqual( master_server.status, 1, msg = 'Server failed to restart') + + cmd = "%s --protocol=tcp --port=%d -uroot -e \"%s\"" %(master_server.mysql_client + , master_server.master_port + , "\n".join(queries) ) + retcode, output = execute_cmd(cmd, output_path, None, True) + + query = "SELECT CONCAT(USER(), CURRENT_USER(), @@PROXY_USER) as res;" + expected_result = "res%s@localhostuser10@localhost''@''" % (pam_user) + cmd = "%s --plugin-dir=/usr/lib/mysql/plugin/ --protocol=tcp --port=%d --user=%s --password=\'\' -e \"%s\" test" %(master_server.mysql_client + , master_server.master_port + , pam_user + , query ) + retcode, output = execute_cmd(cmd, output_path, None, True) + output = re.sub(r'\s+', '', output) + self.assertEqual(retcode, 0, msg = output) + self.assertEqual(output, expected_result, msg = "%s || %s" %(output, expected_result)) diff --git a/plugin/percona-udf/CMakeLists.txt b/plugin/percona-udf/CMakeLists.txt new file mode 100644 index 000000000000..cc98e8f1d380 --- /dev/null +++ b/plugin/percona-udf/CMakeLists.txt @@ -0,0 +1,3 @@ +MYSQL_ADD_PLUGIN(libfnv1a_udf fnv1a_udf.cc MODULE_ONLY) +MYSQL_ADD_PLUGIN(libfnv_udf fnv_udf.cc MODULE_ONLY) +MYSQL_ADD_PLUGIN(libmurmur_udf murmur_udf.cc MODULE_ONLY) diff --git a/plugin/percona-udf/fnv1a_udf.cc b/plugin/percona-udf/fnv1a_udf.cc new file mode 100644 index 000000000000..386ef579bfa4 --- /dev/null +++ b/plugin/percona-udf/fnv1a_udf.cc @@ -0,0 +1,196 @@ +/* This file implements a 64-bit FNV-1a hash UDF (user-defined function) for + * MySQL. The function accepts any number of arguments and returns a 64-bit + * unsigned integer. MySQL actually interprets the result as a signed integer, + * but you should ignore that. I chose not to return the number as a + * hexadecimal string because using an integer makes it possible to use it + * efficiently with BIT_XOR(). + * + * The function never returns NULL, even when you give it NULL arguments. + * + * To compile and install, execute the following commands. The function name + * fnv1a_64 in the mysql command is case-sensitive! (Of course, when you + * actually call the function, it is case-insensitive just like any other SQL + * function). + * + * gcc -fPIC -Wall -I/usr/include/mysql -shared -o fnv1a_udf.so fnv1a_udf.cc + * cp fnv1a_udf.so /lib * OR: * cp fnv1a_udf.so /usr/lib + * mysql mysql -e "CREATE FUNCTION fnv1a_64 RETURNS INTEGER SONAME + * 'fnv1a_udf.so'" + * + * For MySQL version 4.1 or older you must add the following flag to the gcc + * command above: -DNO_DECIMAL_RESULT + * Otherwise you will get an error like: + * fnv1a_udf.cc:167: `DECIMAL_RESULT' undeclared (first use this function) + * (See http://code.google.com/p/maatkit/issues/detail?id=89) + * + * If you get the error "ERROR 1126 (HY000): Can't open shared library + * 'fnv1a_udf.so' (errno: 22 fnv1a_udf.so: cannot open shared object file: No + * such file or directory)" then you may need to copy the .so file to another + * location in your system. Look at your environment's $LD_LIBRARY_PATH + * variable for clues. If none is set, you may need to set this variable to + * something like /lib. + * + * If you get the error "ERROR 1126 (HY000): Can't open shared library + * 'libfnv1a_udf.so' (errno: 22 /lib/libfnv1a_udf.so: undefined symbol: + * __gxx_personality_v0)" then you may need to use g++ instead of gcc. + * + * Try both /lib and /usr/lib before changing LD_LIBRARY_PATH. + * + * On Mac OSX, use -dynamiclib instead of -shared and add -lstdc++ to the + * compile flags. + * + * Once installed successfully, you should be able to call the function. Here's + * a faster alternative to MD5 hashing, with the added ability to hash multiple + * arguments in a single call: + * + * mysql> SELECT FNV1A_64('hello', 'world'); + * + * Here's a way to reduce an entire table to a single order-independent hash: + * + * mysql> SELECT BIT_XOR(CAST(FNV1A_64(col1, col2, col3) AS UNSIGNED)) FROM tbl; + * + */ + +/* The following header is from hash_64a.c: + * + * hash_64 - 64 bit Fowler/Noll/Vo-0 FNV-1a hash code + * + * @(#) $Revision: 5.1 $ + * @(#) $Id: hash_64a.c,v 5.1 2009/06/30 09:01:38 chongo Exp $ + * @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_64a.c,v $ + * + *** + * + * Fowler/Noll/Vo hash + * + * The basis of this hash algorithm was taken from an idea sent + * as reviewer comments to the IEEE POSIX P1003.2 committee by: + * + * Phong Vo (http://www.research.att.com/info/kpv/) + * Glenn Fowler (http://www.research.att.com/~gsf/) + * + * In a subsequent ballot round: + * + * Landon Curt Noll (http://www.isthe.com/chongo/) + * + * improved on their algorithm. Some people tried this hash + * and found that it worked rather well. In an EMail message + * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash. + * + * FNV hashes are designed to be fast while maintaining a low + * collision rate. The FNV speed allows one to quickly hash lots + * of data while maintaining a reasonable collision rate. See: + * + * http://www.isthe.com/chongo/tech/comp/fnv/index.html + * + * for more details as well as other forms of the FNV hash. + * + *** + * + * To use the recommended 64 bit FNV-1a hash, pass FNV1A_64_INIT as the + * Fnv64_t hashval argument to fnv_64a_buf() or fnv_64a_str(). + * + *** + * + * Please do not copyright this code. This code is in the public domain. + * + * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO + * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * By: + * chongo /\oo/\ + * http://www.isthe.com/chongo/ + * + * Share and Enjoy! :-) + */ + +#include +#include +#include "my_sys.h" +#include "mysql.h" + +/* On the first call, use this as the initial_value. */ +#define FNV1A_64_INIT 0xcbf29ce484222325ULL +/* Default for NULLs, just so the result is never NULL. */ +#define HASH_NULL_DEFAULT 0x0a0b0c0d +/* Magic number for the hashing. */ +#define FNV_64_PRIME 0x100000001b3ULL + +/* Prototypes */ + +extern "C" { +ulonglong hash64a(const void *buf, size_t len, ulonglong hval); +bool fnv1a_64_init(UDF_INIT *initid, UDF_ARGS *args, char *message); +ulonglong fnv1a_64(UDF_INIT *initid, UDF_ARGS *args, char *is_null, + char *error); +} + +/* Implementations */ + +ulonglong hash64a(const void *buf, size_t len, ulonglong hval) { + const unsigned char *bp = (const unsigned char *)buf; + const unsigned char *be = bp + len; + + /* FNV-1a hash each octet of the buffer */ + for (; bp != be; ++bp) { + /* xor the bottom with the current octet */ + hval ^= (ulonglong)*bp; + /* multiply by the 64 bit FNV magic prime mod 2^64 */ + hval *= FNV_64_PRIME; + } + + return hval; +} + +bool fnv1a_64_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { + if (args->arg_count == 0) { + strcpy(message, "FNV1A_64 requires at least one argument"); + return true; + } + initid->maybe_null = 0; /* The result will never be NULL */ + return false; +} + +ulonglong fnv1a_64(UDF_INIT *initid [[maybe_unused]], UDF_ARGS *args, + char *is_null [[maybe_unused]], + char *error [[maybe_unused]]) { + uint null_default = HASH_NULL_DEFAULT; + ulonglong result = FNV1A_64_INIT; + uint i; + + for (i = 0; i < args->arg_count; ++i) { + if (args->args[i] != NULL) { + switch (args->arg_type[i]) { + case STRING_RESULT: +#ifdef NO_DECIMAL_RESULT +#else + case DECIMAL_RESULT: +#endif + result = + hash64a((const void *)args->args[i], args->lengths[i], result); + break; + case REAL_RESULT: { + double real_val; + real_val = *((double *)args->args[i]); + result = hash64a((const void *)&real_val, sizeof(double), result); + } break; + case INT_RESULT: { + long long int_val; + int_val = *((long long *)args->args[i]); + result = hash64a((const void *)&int_val, sizeof(ulonglong), result); + } break; + default: + break; + } + } else { + result = + hash64a((const void *)&null_default, sizeof(null_default), result); + } + } + return result; +} diff --git a/plugin/percona-udf/fnv_udf.cc b/plugin/percona-udf/fnv_udf.cc new file mode 100644 index 000000000000..2d367432d8f9 --- /dev/null +++ b/plugin/percona-udf/fnv_udf.cc @@ -0,0 +1,199 @@ +/* This file implements a 64-bit FNV-1 hash UDF (user-defined function) for + * MySQL. The function accepts any number of arguments and returns a 64-bit + * unsigned integer. MySQL actually interprets the result as a signed integer, + * but you should ignore that. I chose not to return the number as a + * hexadecimal string because using an integer makes it possible to use it + * efficiently with BIT_XOR(). + * + * The function never returns NULL, even when you give it NULL arguments. + * + * To compile and install, execute the following commands. The function name + * fnv_64 in the mysql command is case-sensitive! (Of course, when you actually + * call the function, it is case-insensitive just like any other SQL function). + * + * gcc -fPIC -Wall -I/usr/include/mysql -shared -o fnv_udf.so fnv_udf.cc + * cp fnv_udf.so /lib * OR: * cp fnv_udf.so /usr/lib + * mysql mysql -e "CREATE FUNCTION fnv_64 RETURNS INTEGER SONAME 'fnv_udf.so'" + * + * For MySQL version 4.1 or older you must add the following flag to the gcc + * command above: -DNO_DECIMAL_RESULT + * Otherwise you will get an error like: + * fnv_udf.cc:167: `DECIMAL_RESULT' undeclared (first use this function) + * (See http://code.google.com/p/maatkit/issues/detail?id=89) + * + * If you get the error "ERROR 1126 (HY000): Can't open shared library + * 'fnv_udf.so' (errno: 22 fnv_udf.so: cannot open shared object file: No such + * file or directory)" then you may need to copy the .so file to another + * location in your system. Look at your environment's $LD_LIBRARY_PATH + * variable for clues. If none is set, you may need to set this variable to + * something like /lib. + * + * If you get the error "ERROR 1126 (HY000): Can't open shared library + * 'libfnv_udf.so' (errno: 22 /lib/libfnv_udf.so: undefined symbol: + * __gxx_personality_v0)" then you may need to use g++ instead of gcc. + * + * Try both /lib and /usr/lib before changing LD_LIBRARY_PATH. + * + * On Mac OSX, use -dynamiclib instead of -shared and add -lstdc++ to the + * compile flags. + * + * Once installed successfully, you should be able to call the function. Here's + * a faster alternative to MD5 hashing, with the added ability to hash multiple + * arguments in a single call: + * + * mysql> SELECT FNV_64('hello', 'world'); + * + * Here's a way to reduce an entire table to a single order-independent hash: + * + * mysql> SELECT BIT_XOR(CAST(FNV_64(col1, col2, col3) AS UNSIGNED)) FROM tbl1; + * + */ + +/* The following header is from hash_64.c: + * + * hash_64 - 64 bit Fowler/Noll/Vo-0 hash code + * + * @(#) $Revision: 1.8 $ + * @(#) $Id: hash_64.c,v 1.8 2003/10/03 20:37:04 chongo Exp $ + * @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_64.c,v $ + * + *** + * + * Fowler/Noll/Vo hash + * + * The basis of this hash algorithm was taken from an idea sent + * as reviewer comments to the IEEE POSIX P1003.2 committee by: + * + * Phong Vo (http://www.research.att.com/info/kpv/) + * Glenn Fowler (http://www.research.att.com/~gsf/) + * + * In a subsequent ballot round: + * + * Landon Curt Noll (http://www.isthe.com/chongo/) + * + * improved on their algorithm. Some people tried this hash + * and found that it worked rather well. In an EMail message + * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash. + * + * FNV hashes are designed to be fast while maintaining a low + * collision rate. The FNV speed allows one to quickly hash lots + * of data while maintaining a reasonable collision rate. See: + * + * http://www.isthe.com/chongo/tech/comp/fnv/index.html + * + * for more details as well as other forms of the FNV hash. + * + *** + * + * NOTE: The FNV-0 historic hash is not recommended. One should use + * the FNV-1 hash instead. + * + * To use the 64 bit FNV-0 historic hash, pass FNV0_64_INIT as the + * Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str(). + * + * To use the recommended 64 bit FNV-1 hash, pass FNV1_64_INIT as the + * Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str(). + * + *** + * + * Please do not copyright this code. This code is in the public domain. + * + * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO + * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * By: + * chongo /\oo/\ + * http://www.isthe.com/chongo/ + * + * Share and Enjoy! :-) + */ + +#include +#include +#include +#include + +/* On the first call, use this as the initial_value. */ +#define HASH_64_INIT 0x84222325cbf29ce4ULL +/* Default for NULLs, just so the result is never NULL. */ +#define HASH_NULL_DEFAULT 0x0a0b0c0d +/* Magic number for the hashing. */ +#define FNV_64_PRIME 0x100000001b3ULL + +/* Prototypes */ + +extern "C" { +ulonglong hash64(const void *buf, size_t len, ulonglong hval); +bool fnv_64_init(UDF_INIT *initid, UDF_ARGS *args, char *message); +ulonglong fnv_64(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error); +} + +/* Implementations */ + +ulonglong hash64(const void *buf, size_t len, ulonglong hval) { + const unsigned char *bp = (const unsigned char *)buf; + const unsigned char *be = bp + len; + + /* FNV-1 hash each octet of the buffer */ + for (; bp != be; ++bp) { + /* multiply by the 64 bit FNV magic prime mod 2^64 */ + hval *= FNV_64_PRIME; + /* xor the bottom with the current octet */ + hval ^= (ulonglong)*bp; + } + + return hval; +} + +bool fnv_64_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { + if (args->arg_count == 0) { + strcpy(message, "FNV_64 requires at least one argument"); + return true; + } + initid->maybe_null = 0; /* The result will never be NULL */ + return false; +} + +ulonglong fnv_64(UDF_INIT *initid [[maybe_unused]], UDF_ARGS *args, + char *is_null [[maybe_unused]], + char *error [[maybe_unused]]) { + uint null_default = HASH_NULL_DEFAULT; + ulonglong result = HASH_64_INIT; + uint i; + + for (i = 0; i < args->arg_count; ++i) { + if (args->args[i] != NULL) { + switch (args->arg_type[i]) { + case STRING_RESULT: +#ifdef NO_DECIMAL_RESULT +#else + case DECIMAL_RESULT: +#endif + result = + hash64((const void *)args->args[i], args->lengths[i], result); + break; + case REAL_RESULT: { + double real_val; + real_val = *((double *)args->args[i]); + result = hash64((const void *)&real_val, sizeof(double), result); + } break; + case INT_RESULT: { + long long int_val; + int_val = *((long long *)args->args[i]); + result = hash64((const void *)&int_val, sizeof(ulonglong), result); + } break; + default: + break; + } + } else { + result = + hash64((const void *)&null_default, sizeof(null_default), result); + } + } + return result; +} diff --git a/plugin/percona-udf/murmur_udf.cc b/plugin/percona-udf/murmur_udf.cc new file mode 100644 index 000000000000..6ce9dfb0d3eb --- /dev/null +++ b/plugin/percona-udf/murmur_udf.cc @@ -0,0 +1,178 @@ +/* License: This code is in the public domain. + * + * See http://murmurhash.googlepages.com/ for more about the Murmur hash. The + * Murmur hash is by Austin Appleby. + * + * This file implements a 64-bit Murmur-2 hash UDF (user-defined function) for + * MySQL. The function accepts any number of arguments and returns a 64-bit + * unsigned integer. MySQL actually interprets the result as a signed integer, + * but you should ignore that. I chose not to return the number as a + * hexadecimal string because using an integer makes it possible to use it + * efficiently with BIT_XOR(). + * + * The function never returns NULL, even when you give it NULL arguments. + * + * To compile and install, execute the following commands. The function name + * murmur_hash in the mysql command is case-sensitive! (Of course, when you + * actually call the function, it is case-insensitive just like any other SQL + * function). + * + * g++ -fPIC -Wall -I/usr/include/mysql -shared -o murmur_udf.so murmur_udf.cc + * cp murmur_udf.so /lib + * mysql mysql -e "CREATE FUNCTION murmur_hash RETURNS INTEGER SONAME + * 'murmur_udf.so'" + * + * Once installed successfully, you should be able to call the function. Here's + * a faster alternative to MD5 hashing, with the added ability to hash multiple + * arguments in a single call: + * + * mysql> SELECT MURMUR_HASH('hello', 'world'); + * + * Here's a way to reduce an entire table to a single order-independent hash: + * + * mysql> SELECT BIT_XOR(CAST(MURMUR_HASH(col1, col2, col3) AS UNSIGNED)) FROM + * tbl1; + * + * Note - This code makes a few assumptions about how your machine behaves - + * 1. We can read a 4-byte value from any address without crashing + * 2. sizeof(int) == 4 + * + * And it has a few limitations: + * 1. It will not work incrementally. + * 2. It will not produce the same results on little-endian and big-endian + * machines. + */ + +#include +#include +#include "my_sys.h" +#include "mysql.h" + +/* On the first call, use this as the initial_value. */ +#define HASH_64_INIT 0x84222325cbf29ce4ULL +/* Default for NULLs, just so the result is never NULL. */ +#define HASH_NULL_DEFAULT 0x0a0b0c0d + +/* Prototypes */ + +extern "C" { +ulonglong MurmurHash2(const void *key, int len, unsigned int seed); +bool murmur_hash_init(UDF_INIT *initid, UDF_ARGS *args, char *message); +ulonglong murmur_hash(UDF_INIT *initid, UDF_ARGS *args, char *is_null, + char *error); +} + +/* Implementations */ + +/* + * MurmurHash2, by Austin Appleby + * This is the 64 bit version of MurmurHash2 for 32-bit platforms. + */ + +ulonglong MurmurHash2(const void *key, int len, unsigned int seed) { + const unsigned int m = 0x5bd1e995; + const int r = 24; + + unsigned int h1 = seed ^ len; + unsigned int h2 = 0; + + const unsigned int *data = (const unsigned int *)key; + + while (len >= 8) { + unsigned int k1 = *data++; + k1 *= m; + k1 ^= k1 >> r; + k1 *= m; + h1 *= m; + h1 ^= k1; + len -= 4; + + unsigned int k2 = *data++; + k2 *= m; + k2 ^= k2 >> r; + k2 *= m; + h2 *= m; + h2 ^= k2; + len -= 4; + } + + if (len >= 4) { + unsigned int k1 = *data++; + k1 *= m; + k1 ^= k1 >> r; + k1 *= m; + h1 *= m; + h1 ^= k1; + len -= 4; + } + + switch (len) { + case 3: + h2 ^= ((const unsigned char *)data)[2] << 16; + [[fallthrough]]; + case 2: + h2 ^= ((const unsigned char *)data)[1] << 8; + [[fallthrough]]; + case 1: + h2 ^= ((const unsigned char *)data)[0]; + h2 *= m; + }; + + h1 ^= h2 >> 18; + h1 *= m; + h2 ^= h1 >> 22; + h2 *= m; + h1 ^= h2 >> 17; + h1 *= m; + + ulonglong h = h1; + + h = (h << 32) | h2; + return h; +} + +bool murmur_hash_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { + if (args->arg_count == 0) { + strcpy(message, "MURMUR_HASH requires at least one argument"); + return true; + } + initid->maybe_null = 0; /* The result will never be NULL */ + return false; +} + +ulonglong murmur_hash(UDF_INIT *initid [[maybe_unused]], UDF_ARGS *args, + char *is_null [[maybe_unused]], + char *error [[maybe_unused]]) { + uint null_default = HASH_NULL_DEFAULT; + ulonglong result = HASH_64_INIT; + uint i; + + for (i = 0; i < args->arg_count; ++i) { + if (args->args[i] != NULL) { + switch (args->arg_type[i]) { + case STRING_RESULT: + case DECIMAL_RESULT: + result = MurmurHash2((const void *)args->args[i], args->lengths[i], + result); + break; + case REAL_RESULT: { + double real_val; + real_val = *((double *)args->args[i]); + result = MurmurHash2((const void *)&real_val, sizeof(double), result); + } break; + case INT_RESULT: { + long long int_val; + int_val = *((long long *)args->args[i]); + result = + MurmurHash2((const void *)&int_val, sizeof(ulonglong), result); + } break; + default: + break; + } + } else { + result = MurmurHash2((const void *)&null_default, sizeof(null_default), + result); + } + } + return result; +} diff --git a/plugin/semisync/semisync_replica.cc b/plugin/semisync/semisync_replica.cc index 486aebf0d997..1bd56a44b2f3 100644 --- a/plugin/semisync/semisync_replica.cc +++ b/plugin/semisync/semisync_replica.cc @@ -110,7 +110,7 @@ int ReplSemiSyncSlave::slaveReply(MYSQL *mysql, const char *binlog_filename, function_enter(kWho); DBUG_EXECUTE_IF("rpl_semisync_before_send_ack", { - const char act[] = "now SIGNAL sending_ack WAIT_FOR continue"; + const char act[] = "now WAIT_FOR continue"; assert(opt_debug_sync_timeout > 0); assert(!debug_sync_set_action(current_thd, STRING_WITH_LEN(act))); };); diff --git a/unittest/gunit/keyring/CMakeLists.txt b/unittest/gunit/keyring/CMakeLists.txt index 07bc04bf7191..d85086b5c532 100644 --- a/unittest/gunit/keyring/CMakeLists.txt +++ b/unittest/gunit/keyring/CMakeLists.txt @@ -22,7 +22,6 @@ SET(TESTS keyring-api - keys_container buffered_file_io converter file_io diff --git a/unittest/gunit/keyring/keyring-api-t.cc b/unittest/gunit/keyring/keyring-api-t.cc index 746363911003..90133e5e9892 100644 --- a/unittest/gunit/keyring/keyring-api-t.cc +++ b/unittest/gunit/keyring/keyring-api-t.cc @@ -245,23 +245,6 @@ TEST_F(Keyring_api_test, StoreTwiceTheSameDifferentTypes) { 1); } -TEST_F(Keyring_api_test, KeyGenerate) { - EXPECT_EQ(mysql_key_generate("Robert_key", "AES", "Robert", 128), 0); - char *key_type; - size_t key_len; - void *key; - EXPECT_EQ(mysql_key_fetch("Robert_key", &key_type, "Robert", &key, &key_len), - 0); - EXPECT_STREQ("AES", key_type); - EXPECT_EQ(key_len, (size_t)128); - // Try accessing the last byte of key - char ch = ((char *)key)[key_len - 1]; - // Just to get rid of unused variable compiler error - (void)ch; - my_free(key); - my_free(key_type); -} - TEST_F(Keyring_api_test, InitWithDifferentKeyringFile) { EXPECT_EQ( mysql_key_store("Robert_key", "AES", "Robert", sample_key_data.c_str(), @@ -329,6 +312,9 @@ TEST_F(Keyring_api_test, InitWithDifferentKeyringFile) { my_free(key); key = nullptr; remove("./new_keyring"); + // backup will stay as adding percona_binlog key will be unsuccessful - we + // have already added it in keyring + remove("./new_keyring.backup"); } TEST_F(Keyring_api_test, NullUser) { diff --git a/unittest/gunit/keyring/keys_container-t.cc b/unittest/gunit/keyring/keys_container-t.cc deleted file mode 100644 index 3d9f67cfd66a..000000000000 --- a/unittest/gunit/keyring/keys_container-t.cc +++ /dev/null @@ -1,1482 +0,0 @@ -/* Copyright (c) 2016, 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 -#include -#include -#include - -#include "my_inttypes.h" -#include "plugin/keyring/buffered_file_io.h" -#include "plugin/keyring/common/i_serialized_object.h" -#include "plugin/keyring/common/keys_container.h" -#include "unittest/gunit/keyring/buffered_file_io_10.h" -#include "unittest/gunit/keyring/mock_logger.h" - -namespace keyring__keys_container_unittest { -using namespace keyring; -using ::testing::_; -using ::testing::DoAll; -using ::testing::InSequence; -using ::testing::Return; -using ::testing::SetArgPointee; -using ::testing::StrEq; - -bool check_if_file_exists_and_TAG_is_correct(const char *file_name) { - char tag[4]; - std::fstream file; - - file.open(file_name, std::fstream::in | std::fstream::binary); - if (!file.is_open()) return false; - file.seekg(0, file.end); - if (file.tellg() < (3 + 32)) // tag + sha256 - return false; // File do not contains tag - file.seekg(-(3 + 32), file.end); - if (file.good() == false) return false; - file.read(tag, 3); - size_t chars_read = file.gcount(); - if (file.good() == false || chars_read < 3) return false; - tag[3] = '\0'; - file.close(); - return strcmp(tag, "EOF") == 0; -} - -class Keys_container_test : public ::testing::Test { - public: - Keys_container_test() : file_name("./keyring") {} - - protected: - void SetUp() override { - sample_key_data = "Robi"; - sample_key = new Key("Roberts_key", "AES", "Robert", - sample_key_data.c_str(), sample_key_data.length() + 1); - - remove(file_name.c_str()); - remove("./keyring.backup"); - - logger = new Mock_logger(); - keys_container = new Keys_container(logger); - } - void TearDown() override { - remove(file_name.c_str()); - delete keys_container; - delete logger; - } - void create_keyring_file(const char *file_name, const char *keyring_buffer); - void generate_keyring_file_with_correct_ver_1_0_structure( - const char *file_name); - void generate_keyring_file_with_correct_ver_2_0_structure( - const char *file_name); - void generate_keyring_file_with_incorrect_file_version(const char *file_name); - void generate_keyring_file_with_incorrect_TAG(const char *file_name); - - protected: - Keys_container *keys_container; - ILogger *logger; - Key *sample_key; - std::string sample_key_data; - std::string file_name; -}; - -void Keys_container_test::create_keyring_file(const char *file_name, - const char *keyring_buffer) { - std::fstream file; - file.open(file_name, - std::fstream::out | std::fstream::binary | std::fstream::trunc); - ASSERT_TRUE(file.is_open()); - file.write(keyring_buffer, strlen(keyring_buffer)); - file.close(); -} - -void Keys_container_test::generate_keyring_file_with_correct_ver_1_0_structure( - const char *file_name) { - static const char *keyring_buffer = "Keyring file version:1.0EOF"; - create_keyring_file(file_name, keyring_buffer); -} - -void Keys_container_test::generate_keyring_file_with_correct_ver_2_0_structure( - const char *file_name) { - static const char *keyring_buffer = - "Keyring file version:2.0EOF" - "01234567890123456789012345678901"; - create_keyring_file(file_name, keyring_buffer); -} - -void Keys_container_test::generate_keyring_file_with_incorrect_file_version( - const char *file_name) { - static const char *keyring_buffer = "Keyring file version:3.0EOF"; - create_keyring_file(file_name, keyring_buffer); -} - -void Keys_container_test::generate_keyring_file_with_incorrect_TAG( - const char *file_name) { - static const char *keyring_buffer = "Keyring file version:2.0EF"; - create_keyring_file(file_name, keyring_buffer); -} - -TEST_F(Keys_container_test, InitWithFileWithCorrect_1_0_Struct) { - const char *keyring_correct_struct = "./keyring_correct_1_0_struct"; - remove(keyring_correct_struct); - std::vector allowedFileVersionsToInit; - // this keyring will work with keyring files in the following versions: - allowedFileVersionsToInit.push_back(keyring::keyring_file_version_2_0); - allowedFileVersionsToInit.push_back(keyring::keyring_file_version_1_0); - generate_keyring_file_with_correct_ver_1_0_structure(keyring_correct_struct); - IKeyring_io *keyring_io = - new Buffered_file_io(logger, &allowedFileVersionsToInit); - EXPECT_EQ(keys_container->init(keyring_io, keyring_correct_struct), 0); - remove(keyring_correct_struct); - delete sample_key; // unused in this test -} - -TEST_F(Keys_container_test, InitWithFileWithCorrect_2_0_Struct) { - const char *keyring_correct_struct = "./keyring_correct_2_0_struct"; - remove(keyring_correct_struct); - generate_keyring_file_with_correct_ver_2_0_structure(keyring_correct_struct); - Buffered_file_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, keyring_correct_struct), 0); - remove(keyring_correct_struct); - delete sample_key; // unused in this test -} - -TEST_F(Keys_container_test, InitWithFileWithIncorrectKeyringVersion) { - const char *keyring_incorrect_version = "./keyring_incorrect_version"; - remove(keyring_incorrect_version); - generate_keyring_file_with_incorrect_file_version(keyring_incorrect_version); - Buffered_file_io *keyring_io = new Buffered_file_io(logger); - EXPECT_CALL(*((Mock_logger *)logger), - log(ERROR_LEVEL, StrEq("Incorrect Keyring file"))); - EXPECT_CALL(*((Mock_logger *)logger), - log(ERROR_LEVEL, StrEq("Error while loading keyring content." - " The keyring might be malformed"))); - EXPECT_EQ(keys_container->init(keyring_io, keyring_incorrect_version), 1); - remove(keyring_incorrect_version); - delete sample_key; // unused in this test -} - -TEST_F(Keys_container_test, InitWithFileWithIncorrectTAG) { - const char *keyring_incorrect_tag = "./keyring_incorrect_tag"; - remove(keyring_incorrect_tag); - generate_keyring_file_with_incorrect_TAG(keyring_incorrect_tag); - Buffered_file_io *keyring_io = new Buffered_file_io(logger); - EXPECT_CALL(*((Mock_logger *)logger), - log(ERROR_LEVEL, StrEq("Incorrect Keyring file"))); - EXPECT_CALL(*((Mock_logger *)logger), - log(ERROR_LEVEL, StrEq("Error while loading keyring content. The " - "keyring might be malformed"))); - EXPECT_EQ(keys_container->init(keyring_io, keyring_incorrect_tag), 1); - remove(keyring_incorrect_tag); - delete sample_key; // unused in this test -} - -TEST_F(Keys_container_test, StoreFetchRemove) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - Key key_id("Roberts_key", nullptr, "Robert", nullptr, 0); - IKey *fetched_key = keys_container->fetch_key(&key_id); - - ASSERT_TRUE(fetched_key != nullptr); - std::string expected_key_signature = "Roberts_keyRobert"; - EXPECT_STREQ(fetched_key->get_key_signature()->c_str(), - expected_key_signature.c_str()); - EXPECT_EQ(fetched_key->get_key_signature()->length(), - expected_key_signature.length()); - uchar *key_data_fetched = fetched_key->get_key_data(); - size_t key_data_fetched_size = fetched_key->get_key_data_size(); - EXPECT_STREQ(sample_key_data.c_str(), - reinterpret_cast(key_data_fetched)); - EXPECT_STREQ("AES", fetched_key->get_key_type_as_string()->c_str()); - ASSERT_TRUE(sample_key_data.length() + 1 == key_data_fetched_size); - - keys_container->remove_key(&key_id); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - my_free(fetched_key->release_key_data()); -} - -TEST_F(Keys_container_test, FetchNotExisting) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - Key key_id("Roberts_key", nullptr, "Robert", nullptr, 0); - IKey *fetched_key = keys_container->fetch_key(&key_id); - ASSERT_TRUE(fetched_key == nullptr); - delete sample_key; // unused in this test -} - -TEST_F(Keys_container_test, RemoveNotExisting) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - Key key_id("Roberts_key", "AES", "Robert", nullptr, 0); - ASSERT_TRUE(keys_container->remove_key(&key_id) == true); - delete sample_key; // unused in this test -} - -TEST_F(Keys_container_test, StoreFetchNotExisting) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - Key key_id("NotRoberts_key", nullptr, "NotRobert", nullptr, 0); - IKey *fetched_key = keys_container->fetch_key(&key_id); - ASSERT_TRUE(fetched_key == nullptr); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); -} - -TEST_F(Keys_container_test, StoreRemoveNotExisting) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - Key key_id("NotRoberts_key", "AES", "NotRobert", nullptr, 0); - // Failed to remove key - ASSERT_TRUE(keys_container->remove_key(&key_id) == true); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); -} - -TEST_F(Keys_container_test, StoreStoreStoreFetchRemove) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - std::string key_data1("Robi1"); - Key *key1 = new Key("Roberts_key1", "AES", "Robert", key_data1.c_str(), - key_data1.length() + 1); - - EXPECT_EQ(keys_container->store_key(key1), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - - std::string key_data2("Robi2"); - Key *key2 = new Key("Roberts_key2", "AES", "Robert", key_data2.c_str(), - key_data2.length() + 1); - - EXPECT_EQ(keys_container->store_key(key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 3); - - std::string key_data3("Robi3"); - Key *key3 = new Key("Roberts_key3", "AES", "Robert", key_data3.c_str(), - key_data3.length() + 1); - - EXPECT_EQ(keys_container->store_key(key3), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 4); - - Key key2_id("Roberts_key2", nullptr, "Robert", nullptr, 0); - IKey *fetched_key = keys_container->fetch_key(&key2_id); - - ASSERT_TRUE(fetched_key != nullptr); - std::string expected_key_signature = "Roberts_key2Robert"; - EXPECT_STREQ(fetched_key->get_key_signature()->c_str(), - expected_key_signature.c_str()); - EXPECT_EQ(fetched_key->get_key_signature()->length(), - expected_key_signature.length()); - uchar *key_data_fetched = fetched_key->get_key_data(); - size_t key_data_fetched_size = fetched_key->get_key_data_size(); - EXPECT_STREQ(key_data2.c_str(), - reinterpret_cast(key_data_fetched)); - ASSERT_TRUE(key_data2.length() + 1 == key_data_fetched_size); - - Key key3_id("Roberts_key3", "AES", "Robert", nullptr, 0); - keys_container->remove_key(&key3_id); - ASSERT_TRUE(keys_container->get_number_of_keys() == 3); - - my_free(fetched_key->release_key_data()); -} - -TEST_F(Keys_container_test, StoreTwiceTheSame) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - EXPECT_EQ(keys_container->store_key(sample_key), 1); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); -} - -class Buffered_file_io_20 : public Buffered_file_io { - public: - Buffered_file_io_20(ILogger *logger) : Buffered_file_io(logger) {} - void set_memory_needed_for_buffer(size_t memory_needed) { - memory_needed_for_buffer = memory_needed; - } -}; - -TEST_F(Keys_container_test, StoreKeyInVer10StoreKeyInVer20FetchKeyInVer20) { - size_t memory_needed_for_buffer; - { - Buffered_file_io_10 keyring_io_10(logger); - EXPECT_EQ(keys_container->init(&keyring_io_10, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - memory_needed_for_buffer = keyring_io_10.get_memory_needed_for_buffer(); - } - Buffered_file_io_20 *keyring_io_20 = new Buffered_file_io_20(logger); - EXPECT_EQ(keyring_io_20->init(&file_name), 0); - keyring_io_20->set_memory_needed_for_buffer(memory_needed_for_buffer); - keys_container->set_keyring_io(keyring_io_20); - - std::string key_data1("Robi1"); - Key key_1_id("Roberts_key1", nullptr, "Robert", nullptr, 0); - Key *key1 = new Key("Roberts_key1", "AES", "Robert", key_data1.c_str(), - key_data1.length() + 1); - EXPECT_EQ(keys_container->store_key(key1), 0); - - Key key_id("Roberts_key", nullptr, "Robert", nullptr, 0); - IKey *fetched_key = keys_container->fetch_key(&key_id); - ASSERT_TRUE(fetched_key != nullptr); - std::string expected_key_signature = "Roberts_keyRobert"; - EXPECT_STREQ(fetched_key->get_key_signature()->c_str(), - expected_key_signature.c_str()); - EXPECT_EQ(fetched_key->get_key_signature()->length(), - expected_key_signature.length()); - uchar *key_data_fetched = fetched_key->get_key_data(); - size_t key_data_fetched_size = fetched_key->get_key_data_size(); - EXPECT_STREQ(sample_key_data.c_str(), - reinterpret_cast(key_data_fetched)); - ASSERT_TRUE(sample_key_data.length() + 1 == key_data_fetched_size); - - keys_container->remove_key(&key_id); - - IKey *fetched_key_1 = keys_container->fetch_key(&key_1_id); - ASSERT_TRUE(fetched_key_1 != nullptr); - expected_key_signature = "Roberts_key1Robert"; - EXPECT_STREQ(fetched_key_1->get_key_signature()->c_str(), - expected_key_signature.c_str()); - EXPECT_EQ(fetched_key_1->get_key_signature()->length(), - expected_key_signature.length()); - key_data_fetched = fetched_key_1->get_key_data(); - key_data_fetched_size = fetched_key_1->get_key_data_size(); - EXPECT_STREQ(key_data1.c_str(), - reinterpret_cast(key_data_fetched)); - ASSERT_TRUE(key_data1.length() + 1 == key_data_fetched_size); - - keys_container->remove_key(&key_1_id); - - my_free(fetched_key->release_key_data()); - my_free(fetched_key_1->release_key_data()); -} - -TEST_F(Keys_container_test, - CheckIfKeyIsNotDumpedIntoKeyringFileIfKeyringFileDoesnotExist) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - remove("./keyring"); - std::string key_data1("Robi1"); - Key *key1 = new Key("Roberts_key1", "AES", "Robert", key_data1.c_str(), - key_data1.length() + 1); - EXPECT_CALL( - *((Mock_logger *)logger), - log(ERROR_LEVEL, StrEq("Could not flush keys to keyring's backup"))); - // it should not be possible to store_key if the keyring file does not exist - EXPECT_EQ(keys_container->store_key(key1), 1); - delete key1; - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring"), 0); -} - -TEST_F(Keys_container_test, - CheckIfKeyIsNotDumpedIntoKeyringFileIfKeyringFileHasInvalidDigest) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - - std::fstream keyring_file("./keyring"); - ASSERT_TRUE(keyring_file.is_open()); - keyring_file.seekp(-3, std::ios_base::end); - keyring_file.write("a", 1); // changed digest - keyring_file.close(); - EXPECT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring")); - - std::string key_data1("Robi1"); - Key *key1 = new Key("Roberts_key1", "AES", "Robert", key_data1.c_str(), - key_data1.length() + 1); - EXPECT_CALL( - *((Mock_logger *)logger), - log(ERROR_LEVEL, StrEq("Could not flush keys to keyring's backup"))); - EXPECT_CALL(*((Mock_logger *)logger), - log(ERROR_LEVEL, StrEq("Incorrect Keyring file"))); - - // it should not be possible to store_key if the keyring file was changed - EXPECT_EQ(keys_container->store_key(key1), 1); - delete key1; - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring"), true); -} - -class Buffered_file_io_dont_remove_backup : public Buffered_file_io { - public: - Buffered_file_io_dont_remove_backup(ILogger *logger) - : Buffered_file_io(logger) {} - - bool remove_backup(myf) { return false; } -}; - -class Keys_container_test_dont_close : public ::testing::Test { - public: - Keys_container_test_dont_close() : file_name("./keyring") {} - - protected: - void SetUp() override { - sample_key_data = "Robi"; - sample_key = new Key("Roberts_key", "AES", "Robert", - sample_key_data.c_str(), sample_key_data.length() + 1); - std::string sample_key_data2 = "xobi2"; - sample_key2 = - new Key("Roberts_key2", "AES", "Robert", sample_key_data2.c_str(), - sample_key_data2.length() + 1); - - // Remove Keyring files just to be save - remove(file_name.c_str()); - remove("./keyring.backup"); - remove("./keyring.backup.backup"); - } - void TearDown() override { remove(file_name.c_str()); } - void generate_malformed_keyring_file_without_tag(const char *file_name); - - protected: - Key *sample_key; - Key *sample_key2; - std::string sample_key_data; - std::string file_name; -}; - -void Keys_container_test_dont_close:: - generate_malformed_keyring_file_without_tag(const char *file_name) { - static const char *malformed_keyring_buffer = - "Key1AESRobertKEYDATA" - "Key2AESZibiDATAKey3DATA...crashing"; - - std::fstream file; - file.open(file_name, - std::fstream::out | std::fstream::binary | std::fstream::trunc); - ASSERT_TRUE(file.is_open()); - file.write(malformed_keyring_buffer, strlen(malformed_keyring_buffer)); - file.close(); -} - -TEST_F(Keys_container_test_dont_close, - CheckIfCorrectBackupFileIsCreatedAfterStoringOneKey) { - ILogger *logger = new Mock_logger(); - IKeyring_io *keyring_io_dont_remove_backup = - new Buffered_file_io_dont_remove_backup(logger); - Keys_container *keys_container = new Keys_container(logger); - - EXPECT_EQ(keys_container->init(keyring_io_dont_remove_backup, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), true); - - // Check if backup file is empty - delete keys_container; - delete logger; - logger = new Mock_logger(); - IKeyring_io *keyring_io = new Buffered_file_io(logger); - keys_container = new Keys_container(logger); - ASSERT_TRUE(keys_container->init(keyring_io, "./keyring.backup") == 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - - remove("./keyring.backup"); - remove("./keyring.backup.backup"); // leftover from initializing keyring with - // backup file - remove(file_name.c_str()); - delete keys_container; - delete logger; - delete sample_key2; // unused in this test -} - -TEST_F(Keys_container_test_dont_close, - CheckIfCorrectBackupFileIsCreatedAfterStoringTwoKeys) { - ILogger *logger = new Mock_logger(); - IKeyring_io *keyring_io = new Buffered_file_io(logger); - Keys_container *keys_container = new Keys_container(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - // successfully stored the key - backup file does not exist - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - delete keys_container; - delete logger; - - logger = new Mock_logger(); - IKeyring_io *keyring_io_dont_remove_backup = - new Buffered_file_io_dont_remove_backup(logger); - keys_container = new Keys_container(logger); - - EXPECT_EQ(keys_container->init(keyring_io_dont_remove_backup, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), true); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring"), true); - - delete keys_container; - delete logger; - // Check that backup file contains sample_key only - logger = new Mock_logger(); - IKeyring_io *keyring_io_2 = new Buffered_file_io(logger); - keys_container = new Keys_container(logger); - EXPECT_EQ(keys_container->init(keyring_io_2, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - Key sample_key_id("Roberts_key", nullptr, "Robert", nullptr, 0); - IKey *fetchedKey = keys_container->fetch_key(&sample_key_id); - ASSERT_TRUE(fetchedKey != nullptr); - - ASSERT_TRUE(*fetchedKey->get_key_signature() == "Roberts_keyRobert"); - ASSERT_TRUE(memcmp(fetchedKey->get_key_data(), "Robi", - fetchedKey->get_key_data_size()) == 0); - - remove("./keyring.backup"); - remove("./keyring.backup.backup"); // leftover from initializing keyring with - // backup file - remove(file_name.c_str()); - delete keys_container; - delete logger; - my_free(fetchedKey->release_key_data()); -} - -TEST_F(Keys_container_test_dont_close, - CheckIfCorrectBackupFileIsCreatedBeforeRemovingKey) { - ILogger *logger = new Mock_logger(); - IKeyring_io *keyring_io = new Buffered_file_io(logger); - Keys_container *keys_container = new Keys_container(logger); - - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - // successfully stored the key - backup file does not exist - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - EXPECT_EQ(keys_container->store_key(sample_key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - - delete keys_container; - delete logger; - logger = new Mock_logger(); - IKeyring_io *keyring_io_dont_remove_backup = - new Buffered_file_io_dont_remove_backup(logger); - keys_container = new Keys_container(logger); - - ASSERT_TRUE(keys_container->init(keyring_io_dont_remove_backup, file_name) == - 0); - Key sample_key_id("Roberts_key", "AES", "Robert", nullptr, 0); - EXPECT_EQ(keys_container->remove_key(&sample_key_id), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), true); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring"), true); - - delete keys_container; - delete logger; - // Check that backup file contains sample_key and sample_key2 - logger = new Mock_logger(); - IKeyring_io *keyring_io_2 = new Buffered_file_io(logger); - keys_container = new Keys_container(logger); - EXPECT_EQ(keys_container->init(keyring_io_2, "./keyring.backup"), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - Key sample_key2_id("Roberts_key2", nullptr, "Robert", nullptr, 0); - IKey *fetchedKey = keys_container->fetch_key(&sample_key2_id); - ASSERT_TRUE(fetchedKey != nullptr); - ASSERT_TRUE(*fetchedKey->get_key_signature() == "Roberts_key2Robert"); - ASSERT_TRUE(memcmp(fetchedKey->get_key_data(), "xobi2", - fetchedKey->get_key_data_size()) == 0); - - remove("./keyring.backup"); - remove("./keyring.backup.backup"); // leftover from initializing keyring with - // backup file - remove(file_name.c_str()); - delete keys_container; - delete logger; - my_free(fetchedKey->release_key_data()); -} - -TEST_F(Keys_container_test_dont_close, - CheckIfBackupFileIsNotCreatedForFetching) { - ILogger *logger = new Mock_logger(); - IKeyring_io *keyring_io = new Buffered_file_io(logger); - Keys_container *keys_container = new Keys_container(logger); - - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - // successfully stored the key - backup file does not exist - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - EXPECT_EQ(keys_container->store_key(sample_key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - - delete keys_container; - delete logger; - logger = new Mock_logger(); - IKeyring_io *keyring_io_dont_remove_backup = - new Buffered_file_io_dont_remove_backup(logger); - keys_container = new Keys_container(logger); - - EXPECT_EQ(keys_container->init(keyring_io_dont_remove_backup, file_name), 0); - Key sample_key_id("Roberts_key", nullptr, "Robert", nullptr, 0); - IKey *fetchedKey = keys_container->fetch_key(&sample_key_id); - ASSERT_TRUE(fetchedKey != nullptr); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - // check if the backup file was not created - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring"), true); - - remove("./keyring.backup"); - remove(file_name.c_str()); - delete keys_container; - delete logger; - my_free(fetchedKey->release_key_data()); -} - -TEST_F(Keys_container_test_dont_close, - KeyringfileIsMalformedCheckIfBackupIsLoaded) { - ILogger *logger = new Mock_logger(); - IKeyring_io *keyring_io = new Buffered_file_io(logger); - Keys_container *keys_container = new Keys_container(logger); - - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - // successfully stored the key - backup file does not exist - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - EXPECT_EQ(keys_container->store_key(sample_key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - // Now we have correct backup file - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - - delete keys_container; - delete logger; - logger = new Mock_logger(); - IKeyring_io *keyring_io_dont_remove_backup = - new Buffered_file_io_dont_remove_backup(logger); - keys_container = new Keys_container(logger); - - // this key will not be in backup file thus we do not care about it - Key *sample_key3 = - new Key("Roberts_key3", "ZZZZ", "MaybeRobert", "DATA", strlen("DATA")); - - EXPECT_EQ(keys_container->init(keyring_io_dont_remove_backup, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key3), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 3); - // Now we have correct backup file - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), true); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - - delete keys_container; - delete logger; - remove("./keyring"); - generate_malformed_keyring_file_without_tag("./keyring"); - logger = new Mock_logger(); - IKeyring_io *keyring_io_2 = new Buffered_file_io(logger); - keys_container = new Keys_container(logger); - - ASSERT_TRUE(keys_container->init(keyring_io_2, file_name) == 0); - // Check that keyring from backup was loaded as the keyring file is corrupted - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - Key sample_key_id("Roberts_key", nullptr, "Robert", nullptr, 0); - Key sample_key2_id("Roberts_key2", nullptr, "Robert", nullptr, 0); - IKey *fetchedKey = keys_container->fetch_key(&sample_key2_id); - ASSERT_TRUE(fetchedKey != nullptr); - ASSERT_TRUE(*fetchedKey->get_key_signature() == "Roberts_key2Robert"); - ASSERT_TRUE(memcmp(fetchedKey->get_key_data(), "xobi2", - fetchedKey->get_key_data_size()) == 0); - IKey *fetchedKey2 = keys_container->fetch_key(&sample_key_id); - ASSERT_TRUE(fetchedKey2 != nullptr); - ASSERT_TRUE(*fetchedKey2->get_key_signature() == "Roberts_keyRobert"); - ASSERT_TRUE(memcmp(fetchedKey2->get_key_data(), "Robi", - fetchedKey2->get_key_data_size()) == 0); - - // check if the backup file was removed - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring"), true); - - remove("./keyring.backup"); - remove(file_name.c_str()); - delete keys_container; - delete logger; - my_free(fetchedKey->release_key_data()); - my_free(fetchedKey2->release_key_data()); -} - -TEST_F(Keys_container_test_dont_close, - BackupfileIsMalformedCheckItIsIgnoredAndDeleted) { - ILogger *logger = new Mock_logger(); - IKeyring_io *keyring_io = new Buffered_file_io(logger); - Keys_container *keys_container = new Keys_container(logger); - - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - // successfully stored the key - backup file does not exist - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - EXPECT_EQ(keys_container->store_key(sample_key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - - delete keys_container; - delete logger; - generate_malformed_keyring_file_without_tag("./keyring.backup"); - logger = new Mock_logger(); - IKeyring_io *keyring_io_2 = new Buffered_file_io(logger); - keys_container = new Keys_container(logger); - - // Check that backup file was ignored (as backup file is malformed) - EXPECT_CALL(*((Mock_logger *)logger), - log(ERROR_LEVEL, StrEq("Incorrect Keyring file"))); - EXPECT_CALL(*((Mock_logger *)logger), - log(WARNING_LEVEL, - StrEq("Found malformed keyring backup file - removing it"))); - EXPECT_EQ(keys_container->init(keyring_io_2, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - Key sample_key_id("Roberts_key", nullptr, "Robert", nullptr, 0); - Key sample_key2_id("Roberts_key2", nullptr, "Robert", nullptr, 0); - IKey *fetchedKey = keys_container->fetch_key(&sample_key2_id); - ASSERT_TRUE(fetchedKey != nullptr); - ASSERT_TRUE(*fetchedKey->get_key_signature() == "Roberts_key2Robert"); - ASSERT_TRUE(memcmp(fetchedKey->get_key_data(), "xobi2", - fetchedKey->get_key_data_size()) == 0); - IKey *fetchedKey2 = keys_container->fetch_key(&sample_key_id); - ASSERT_TRUE(fetchedKey2 != nullptr); - ASSERT_TRUE(*fetchedKey2->get_key_signature() == "Roberts_keyRobert"); - ASSERT_TRUE(memcmp(fetchedKey2->get_key_data(), "Robi", - fetchedKey2->get_key_data_size()) == 0); - - // check if the backup file was removed - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring"), true); - - delete keys_container; - delete logger; - my_free(fetchedKey->release_key_data()); - my_free(fetchedKey2->release_key_data()); -} - -TEST_F( - Keys_container_test_dont_close, - CheckIfBackupIsCreatedAfterEachOperationAndIsUsedWhenKeyringDoesNotExist) { - ILogger *logger = new Mock_logger(); - IKeyring_io *keyring_io = new Buffered_file_io_dont_remove_backup(logger); - Keys_container *keys_container = new Keys_container(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), true); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - - remove("./keyring.backup"); - rename("keyring", "keyring.backup"); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == false); - // Now keyring file should be recreated based on keyring.backup - delete keys_container; - keyring_io = new Buffered_file_io_dont_remove_backup(logger); - keys_container = new Keys_container(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - EXPECT_EQ(keys_container->store_key(sample_key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), true); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring"), true); - - Key sample_key_id("Roberts_key", nullptr, "Robert", nullptr, 0); - IKey *fetchedKey = keys_container->fetch_key(&sample_key_id); - ASSERT_TRUE(fetchedKey != nullptr); - - ASSERT_TRUE(*fetchedKey->get_key_signature() == "Roberts_keyRobert"); - ASSERT_TRUE(memcmp(fetchedKey->get_key_data(), "Robi", - fetchedKey->get_key_data_size()) == 0); - - remove("./keyring.backup"); - remove(file_name.c_str()); - delete keys_container; - delete logger; - my_free(fetchedKey->release_key_data()); - // fetchedKey->release_key_data(); -} - -class Mock_keyring_io : public IKeyring_io { - public: - MOCK_METHOD1(init, bool(std::string *keyring_filename)); - MOCK_METHOD1(flush_to_backup, bool(ISerialized_object *serialized_object)); - MOCK_METHOD1(flush_to_storage, bool(ISerialized_object *serialized_object)); - MOCK_METHOD0(get_serializer, ISerializer *()); - MOCK_METHOD1(get_serialized_object, - bool(ISerialized_object **serialized_object)); - MOCK_METHOD0(has_next_serialized_object, bool()); -}; - -class Mock_serialized_object : public ISerialized_object { - public: - MOCK_METHOD1(get_next_key, bool(IKey **key)); - MOCK_METHOD0(has_next_key, bool()); - MOCK_METHOD0(get_key_operation, Key_operation()); - MOCK_METHOD1(set_key_operation, void(Key_operation)); -}; - -class Mock_serializer : public ISerializer { - public: - MOCK_METHOD3( - serialize, - ISerialized_object *( - const collation_unordered_map> &, - IKey *, Key_operation)); -}; - -class Keys_container_with_mocked_io_test : public ::testing::Test { - protected: - void SetUp() override { - std::string sample_key_data("Robi"); - sample_key = new Key("Roberts_key", "AES", "Robert", - sample_key_data.c_str(), sample_key_data.length() + 1); - - file_name = "/home/rob/write_key"; - } - void TearDown() override { - remove(file_name.c_str()); - delete keys_container; - } - - protected: - Keys_container *keys_container; - Mock_keyring_io *keyring_io; - Key *sample_key; - char *sample_key_data; - std::string file_name; - - void expect_calls_on_init(); - void expect_calls_on_store_sample_key(); -}; - -void Keys_container_with_mocked_io_test::expect_calls_on_init() { - Mock_serialized_object *mock_serialized_object = new Mock_serialized_object; - - EXPECT_CALL(*keyring_io, init(Pointee(StrEq(file_name)))) - .WillOnce(Return(0)); // init successful - EXPECT_CALL(*keyring_io, get_serialized_object(_)) - .WillOnce(DoAll(SetArgPointee<0>(mock_serialized_object), Return(false))); - EXPECT_CALL(*mock_serialized_object, has_next_key()) - .WillOnce(Return(false)); // no keys to read - EXPECT_CALL(*keyring_io, has_next_serialized_object()) - .WillOnce(Return(false)); -} - -TEST_F(Keys_container_with_mocked_io_test, - ErrorFromIODuringInitOnGettingSerializedObject) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - - EXPECT_CALL(*keyring_io, init(Pointee(StrEq(file_name)))) - .WillOnce(Return(0)); // init successful - EXPECT_CALL(*keyring_io, get_serialized_object(_)).WillOnce(Return(true)); - EXPECT_CALL(*logger, - log(ERROR_LEVEL, StrEq("Error while loading keyring content. The " - "keyring might be malformed"))); - - EXPECT_EQ(keys_container->init(keyring_io, file_name), 1); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - delete logger; - delete sample_key; // unused in this test -} - -TEST_F(Keys_container_with_mocked_io_test, - ErrorFromIODuringInitInvalidKeyAndMockedSerializedObject) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - - IKey *invalid_key = new Key(); - std::string invalid_key_type("ZZZ"); - invalid_key->set_key_type(&invalid_key_type); - - Mock_serialized_object *mock_serialized_object = new Mock_serialized_object; - - EXPECT_CALL(*keyring_io, init(Pointee(StrEq(file_name)))) - .WillOnce(Return(0)); // init successful - { - InSequence dummy; - EXPECT_CALL(*keyring_io, get_serialized_object(_)) - .WillOnce( - DoAll(SetArgPointee<0>(mock_serialized_object), Return(false))); - EXPECT_CALL(*mock_serialized_object, has_next_key()).WillOnce(Return(true)); - EXPECT_CALL(*mock_serialized_object, get_next_key(_)) - .WillOnce(DoAll(SetArgPointee<0>(sample_key), Return(false))); - EXPECT_CALL(*mock_serialized_object, has_next_key()).WillOnce(Return(true)); - EXPECT_CALL(*mock_serialized_object, get_next_key(_)) - .WillOnce(DoAll(SetArgPointee<0>(invalid_key), Return(false))); - - EXPECT_CALL(*logger, - log(ERROR_LEVEL, StrEq("Error while loading keyring content. " - "The keyring might be malformed"))); - } - - EXPECT_EQ(keys_container->init(keyring_io, file_name), 1); - ASSERT_EQ(0u, keys_container->get_number_of_keys()); - delete logger; -} - -TEST_F(Keys_container_with_mocked_io_test, ErrorFromIODuringInitInvalidKey) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - - IKey *invalid_key = new Key(); - std::string invalid_key_type("ZZZ"); - invalid_key->set_key_type(&invalid_key_type); - - Buffer *buffer = new Buffer(sample_key->get_key_pod_size() + - invalid_key->get_key_pod_size()); - sample_key->store_in_buffer(buffer->data, &(buffer->position)); - invalid_key->store_in_buffer(buffer->data, &(buffer->position)); - buffer->position = 0; // rewind buffer - - EXPECT_CALL(*keyring_io, init(Pointee(StrEq(file_name)))) - .WillOnce(Return(0)); // init successful - { - InSequence dummy; - EXPECT_CALL(*keyring_io, get_serialized_object(_)) - .WillOnce(DoAll(SetArgPointee<0>(buffer), Return(false))); - EXPECT_CALL(*logger, - log(ERROR_LEVEL, StrEq("Error while loading keyring content. " - "The keyring might be malformed"))); - } - EXPECT_EQ(keys_container->init(keyring_io, file_name), 1); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - delete logger; - delete invalid_key; - delete sample_key; // unused in this test -} - -TEST_F(Keys_container_with_mocked_io_test, - ErrorFromSerializerOnFlushToBackupWhenStoringKey) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - expect_calls_on_init(); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - Mock_serializer *mock_serializer = new Mock_serializer; - - { - InSequence dummy; - - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return((ISerialized_object *)nullptr)); - EXPECT_CALL( - *logger, - log(ERROR_LEVEL, StrEq("Could not flush keys to keyring's backup"))); - } - EXPECT_EQ(keys_container->store_key(sample_key), 1); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - - delete logger; - delete sample_key; - delete mock_serializer; -} - -TEST_F(Keys_container_with_mocked_io_test, - ErrorFromSerializerOnFlushToKeyringWhenStoringKey) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - expect_calls_on_init(); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - Mock_serializer *mock_serializer = new Mock_serializer; - - ISerialized_object *empty_serialized_object = new Buffer; - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(empty_serialized_object)); - EXPECT_CALL(*keyring_io, flush_to_backup(empty_serialized_object)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, STORE_KEY)) - .WillOnce(Return((ISerialized_object *)nullptr)); - EXPECT_CALL(*logger, - log(ERROR_LEVEL, StrEq("Could not flush keys to keyring"))); - } - EXPECT_EQ(keys_container->store_key(sample_key), 1); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - - delete logger; - delete sample_key; - delete mock_serializer; -} - -TEST_F(Keys_container_with_mocked_io_test, - ErrorFromSerializerOnFlushToBackupWhenRemovingKey) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - expect_calls_on_init(); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - Mock_serializer *mock_serializer = new Mock_serializer; - - ISerialized_object *empty_serialized_object = new Buffer; - Buffer *serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(empty_serialized_object)); - EXPECT_CALL(*keyring_io, flush_to_backup(empty_serialized_object)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, STORE_KEY)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_storage(serialized_object_with_sample_key)); - } - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - { - InSequence dummy; - - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return((ISerialized_object *)nullptr)); - EXPECT_CALL( - *logger, - log(ERROR_LEVEL, StrEq("Could not flush keys to keyring's backup"))); - } - EXPECT_EQ(keys_container->remove_key(sample_key), 1); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - delete logger; - delete mock_serializer; -} - -TEST_F(Keys_container_with_mocked_io_test, - ErrorFromSerializerOnFlushToKeyringWhenRemovingKey) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - expect_calls_on_init(); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - Mock_serializer *mock_serializer = new Mock_serializer; - - ISerialized_object *empty_serialized_object = new Buffer; - Buffer *serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(empty_serialized_object)); - EXPECT_CALL(*keyring_io, flush_to_backup(empty_serialized_object)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, STORE_KEY)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_storage(serialized_object_with_sample_key)); - } - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_backup(serialized_object_with_sample_key)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, REMOVE_KEY)) - .WillOnce(Return((ISerialized_object *)nullptr)); - EXPECT_CALL(*logger, - log(ERROR_LEVEL, StrEq("Could not flush keys to keyring"))); - } - - EXPECT_EQ(keys_container->remove_key(sample_key), 1); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - delete logger; - delete mock_serializer; -} - -TEST_F(Keys_container_with_mocked_io_test, StoreAndRemoveKey) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - expect_calls_on_init(); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - Mock_serializer *mock_serializer = new Mock_serializer; - - ISerialized_object *empty_serialized_object = new Buffer; - Buffer *serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(empty_serialized_object)); - EXPECT_CALL(*keyring_io, flush_to_backup(empty_serialized_object)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, STORE_KEY)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_storage(serialized_object_with_sample_key)); - } - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - // recreate serialized objects - empty_serialized_object = new Buffer; - - serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_backup(serialized_object_with_sample_key)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, REMOVE_KEY)) - .WillOnce(Return(empty_serialized_object)); - EXPECT_CALL(*keyring_io, flush_to_storage(empty_serialized_object)); - } - - EXPECT_EQ(keys_container->remove_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - - delete logger; - delete mock_serializer; -} - -TEST_F(Keys_container_with_mocked_io_test, - ErrorFromIOWhileRemovingKeyAfterAdding2Keys) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - expect_calls_on_init(); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - Mock_serializer *mock_serializer = new Mock_serializer; - - ISerialized_object *empty_serialized_object = new Buffer; - Buffer *serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(empty_serialized_object)); - EXPECT_CALL(*keyring_io, flush_to_backup(empty_serialized_object)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, STORE_KEY)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_storage(serialized_object_with_sample_key)); - } - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - std::string key_data2("Robi2"); - Key *key2 = new Key("Roberts_key2", "AES", "Robert", key_data2.c_str(), - key_data2.length() + 1); - - serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - Buffer *serialized_object_with_sample_key_and_key2 = - new Buffer(sample_key->get_key_pod_size() + key2->get_key_pod_size()); - sample_key->store_in_buffer( - serialized_object_with_sample_key_and_key2->data, - &(serialized_object_with_sample_key_and_key2->position)); - key2->store_in_buffer( - serialized_object_with_sample_key_and_key2->data, - &(serialized_object_with_sample_key_and_key2->position)); - serialized_object_with_sample_key_and_key2->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_backup(serialized_object_with_sample_key)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, key2, STORE_KEY)) - .WillOnce(Return(serialized_object_with_sample_key_and_key2)); - EXPECT_CALL(*keyring_io, - flush_to_storage(serialized_object_with_sample_key_and_key2)); - } - EXPECT_EQ(keys_container->store_key(key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - - serialized_object_with_sample_key_and_key2 = - new Buffer(sample_key->get_key_pod_size() + key2->get_key_pod_size()); - sample_key->store_in_buffer( - serialized_object_with_sample_key_and_key2->data, - &(serialized_object_with_sample_key_and_key2->position)); - key2->store_in_buffer( - serialized_object_with_sample_key_and_key2->data, - &(serialized_object_with_sample_key_and_key2->position)); - serialized_object_with_sample_key_and_key2->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(serialized_object_with_sample_key_and_key2)); - EXPECT_CALL(*keyring_io, - flush_to_backup(serialized_object_with_sample_key_and_key2)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, REMOVE_KEY)) - .WillOnce(Return((ISerialized_object *)nullptr)); - EXPECT_CALL(*logger, - log(ERROR_LEVEL, StrEq("Could not flush keys to keyring"))); - } - - EXPECT_EQ(keys_container->remove_key(sample_key), 1); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - - delete logger; - delete mock_serializer; -} - -TEST_F(Keys_container_with_mocked_io_test, Store2KeysAndRemoveThem) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - expect_calls_on_init(); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - Mock_serializer *mock_serializer = new Mock_serializer; - - ISerialized_object *empty_serialized_object = new Buffer; - Buffer *serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(empty_serialized_object)); - EXPECT_CALL(*keyring_io, flush_to_backup(empty_serialized_object)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, STORE_KEY)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_storage(serialized_object_with_sample_key)); - } - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - std::string key_data2("Robi2"); - Key *key2 = new Key("Roberts_key2", "AES", "Robert", key_data2.c_str(), - key_data2.length() + 1); - - serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - Buffer *serialized_object_with_sample_key_and_key2 = - new Buffer(sample_key->get_key_pod_size() + key2->get_key_pod_size()); - sample_key->store_in_buffer( - serialized_object_with_sample_key_and_key2->data, - &(serialized_object_with_sample_key_and_key2->position)); - key2->store_in_buffer( - serialized_object_with_sample_key_and_key2->data, - &(serialized_object_with_sample_key_and_key2->position)); - serialized_object_with_sample_key_and_key2->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_backup(serialized_object_with_sample_key)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, key2, STORE_KEY)) - .WillOnce(Return(serialized_object_with_sample_key_and_key2)); - EXPECT_CALL(*keyring_io, - flush_to_storage(serialized_object_with_sample_key_and_key2)); - } - EXPECT_EQ(keys_container->store_key(key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - - serialized_object_with_sample_key_and_key2 = - new Buffer(sample_key->get_key_pod_size() + key2->get_key_pod_size()); - sample_key->store_in_buffer( - serialized_object_with_sample_key_and_key2->data, - &(serialized_object_with_sample_key_and_key2->position)); - key2->store_in_buffer( - serialized_object_with_sample_key_and_key2->data, - &(serialized_object_with_sample_key_and_key2->position)); - serialized_object_with_sample_key_and_key2->position = 0; // rewind buffer - - Buffer *serialized_object_with_key2 = new Buffer(key2->get_key_pod_size()); - key2->store_in_buffer(serialized_object_with_key2->data, - &(serialized_object_with_key2->position)); - serialized_object_with_key2->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(serialized_object_with_sample_key_and_key2)); - EXPECT_CALL(*keyring_io, - flush_to_backup(serialized_object_with_sample_key_and_key2)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, REMOVE_KEY)) - .WillOnce(Return(serialized_object_with_key2)); - EXPECT_CALL(*keyring_io, flush_to_storage(serialized_object_with_key2)); - } - - EXPECT_EQ(keys_container->remove_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - serialized_object_with_key2 = new Buffer(key2->get_key_pod_size()); - key2->store_in_buffer(serialized_object_with_key2->data, - &(serialized_object_with_key2->position)); - serialized_object_with_key2->position = 0; // rewind buffer - - empty_serialized_object = new Buffer; - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(serialized_object_with_key2)); - EXPECT_CALL(*keyring_io, flush_to_backup(serialized_object_with_key2)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, key2, REMOVE_KEY)) - .WillOnce(Return(empty_serialized_object)); - EXPECT_CALL(*keyring_io, flush_to_storage(empty_serialized_object)); - } - - EXPECT_EQ(keys_container->remove_key(key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - - delete logger; - delete mock_serializer; -} - -} // namespace keyring__keys_container_unittest diff --git a/unittest/gunit/keyring/system_keys_container-t.cc b/unittest/gunit/keyring/system_keys_container-t.cc new file mode 100644 index 000000000000..1b1b247cb968 --- /dev/null +++ b/unittest/gunit/keyring/system_keys_container-t.cc @@ -0,0 +1,697 @@ +/* Copyright (c) 2018 Percona LLC 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 + as published by the Free Software Foundation; version 2 of + the License. + + 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 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 +#include +#include +#include "mock_logger.h" +#include "mysql/service_mysql_keyring.h" +#include "plugin/keyring/common/keyring_key.h" +#include "plugin/keyring/common/system_keys_container.h" +#include "system_key.h" + +namespace keyring__system_keys_container_unittest { +using namespace keyring; +using keyring::Key; +using ::testing::StrEq; + +class System_keys_container_test : public ::testing::Test { + public: + virtual void SetUp() { + logger = new Mock_logger; + sys_keys_container = new System_keys_container(logger); + } + virtual void TearDown() { + delete sys_keys_container; + delete logger; + } + + protected: + ILogger *logger; + System_keys_container *sys_keys_container; +}; + +TEST_F(System_keys_container_test, StoreFetchPBkeyStoreFetchSystemKey) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("percona_binlog:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + key1->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + Key key_id("percona_binlog", nullptr, nullptr, nullptr, 0); + + IKey *system_key = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + std::string key_data_with_version = "0:" + key_data1; + Key key(system_key->get_key_id()->c_str(), + system_key->get_key_type_as_string()->c_str(), + system_key->get_user_id()->c_str(), system_key->get_key_data(), + system_key->get_key_data_size()); + key.xor_data(); + uchar *key_data_fetched = key.get_key_data(); + size_t key_data_fetched_size = key.get_key_data_size(); + EXPECT_STREQ(key.get_key_id()->c_str(), "percona_binlog:0"); + EXPECT_STREQ(key_data_with_version.c_str(), + reinterpret_cast(key_data_fetched)); + EXPECT_STREQ("AES", key.get_key_type_as_string()->c_str()); + ASSERT_TRUE(key_data_with_version.length() + 1 == key_data_fetched_size); + + std::string key_data2("system_key_data_2"); + Key *key_innodb_sk = new Key("percona_innodb123:0", "AES", nullptr, + key_data2.c_str(), key_data2.length() + 1); + key_innodb_sk->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key_innodb_sk); + + Key innodb_key_id("percona_innodb123", nullptr, nullptr, nullptr, 0); + + system_key = sys_keys_container->get_latest_key_if_system_key_without_version( + &innodb_key_id); + + ASSERT_TRUE(system_key != nullptr); + + key_data_with_version = "0:" + key_data2; + Key key_2(system_key->get_key_id()->c_str(), + system_key->get_key_type_as_string()->c_str(), + system_key->get_user_id()->c_str(), system_key->get_key_data(), + system_key->get_key_data_size()); + key_2.xor_data(); + key_data_fetched = key_2.get_key_data(); + key_data_fetched_size = key_2.get_key_data_size(); + EXPECT_STREQ(key_2.get_key_id()->c_str(), "percona_innodb123:0"); + EXPECT_STREQ(key_data_with_version.c_str(), + reinterpret_cast(key_data_fetched)); + EXPECT_STREQ("AES", key_2.get_key_type_as_string()->c_str()); + ASSERT_TRUE(key_data_with_version.length() + 1 == key_data_fetched_size); + + delete key1; + delete key_innodb_sk; +} + +TEST_F(System_keys_container_test, + StoreKey1StoreKey1FetchStoreKey2StoreKey2Fetch) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("percona_binlog:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + key1->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + std::string key_data2("system_key_data_2"); + Key *key2 = new Key("percona_binlog:1", "AES", nullptr, key_data2.c_str(), + key_data2.length() + 1); + key2->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + Key key_id("percona_binlog", nullptr, nullptr, nullptr, 0); + + IKey *system_key = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + Key key(system_key->get_key_id()->c_str(), + system_key->get_key_type_as_string()->c_str(), + system_key->get_user_id()->c_str(), system_key->get_key_data(), + system_key->get_key_data_size()); + key.xor_data(); + uchar *key_data_fetched = key.get_key_data(); + size_t key_data_fetched_size = key.get_key_data_size(); + std::string key_data_with_version = "1:" + key_data2; + EXPECT_STREQ(key.get_key_id()->c_str(), "percona_binlog:1"); + EXPECT_STREQ(key_data_with_version.c_str(), + reinterpret_cast(key_data_fetched)); + EXPECT_STREQ("AES", key.get_key_type_as_string()->c_str()); + ASSERT_TRUE(key_data_with_version.length() + 1 == key_data_fetched_size); + + std::string key_data3("1234XXXYYYZZZZ5335"); + Key *key1_sk = new Key("percona_system_key:0", "AES", nullptr, + key_data3.c_str(), key_data3.length() + 1); + key1_sk->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key1_sk); + + std::string key_data4("CCCSADSDa___DFsdfk0001___"); + Key *key2_sk = new Key("percona_system_key:1", "AES", nullptr, + key_data4.c_str(), key_data4.length() + 1); + key2_sk->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key2_sk); + + Key system_key_id("percona_system_key", nullptr, nullptr, nullptr, 0); + + system_key = sys_keys_container->get_latest_key_if_system_key_without_version( + &system_key_id); + + Key key_sk(system_key->get_key_id()->c_str(), + system_key->get_key_type_as_string()->c_str(), + system_key->get_user_id()->c_str(), system_key->get_key_data(), + system_key->get_key_data_size()); + key_sk.xor_data(); + key_data_fetched = key_sk.get_key_data(); + key_data_fetched_size = key_sk.get_key_data_size(); + key_data_with_version = "1:" + key_data4; + EXPECT_STREQ(key_sk.get_key_id()->c_str(), "percona_system_key:1"); + EXPECT_STREQ(key_data_with_version.c_str(), + reinterpret_cast(key_data_fetched)); + EXPECT_STREQ("AES", key_sk.get_key_type_as_string()->c_str()); + ASSERT_TRUE(key_data_with_version.length() + 1 == key_data_fetched_size); + + delete key1; + delete key2; + delete key1_sk; + delete key2_sk; +} + +TEST_F(System_keys_container_test, StoreStoreStoreFetch) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("percona_key:0:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + key1->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + std::string key_data2("system_key_data_2"); + Key *key2 = new Key("percona_key:0:1", "AES", nullptr, key_data2.c_str(), + key_data2.length() + 1); + key2->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + std::string key_data3("system_key_data_3"); + Key *key3 = new Key("percona_key:0:2", "AES", nullptr, key_data3.c_str(), + key_data3.length() + 1); + key3->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key3); + + Key key_id("percona_key:0", nullptr, nullptr, nullptr, 0); + + IKey *system_key = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + ASSERT_TRUE(system_key != nullptr); + + Key key(system_key->get_key_id()->c_str(), + system_key->get_key_type_as_string()->c_str(), + system_key->get_user_id()->c_str(), system_key->get_key_data(), + system_key->get_key_data_size()); + key.xor_data(); + uchar *key_data_fetched = key.get_key_data(); + size_t key_data_fetched_size = key.get_key_data_size(); + std::string key_data_with_version = "2:" + key_data3; + EXPECT_STREQ(key.get_key_id()->c_str(), "percona_key:0:2"); + EXPECT_STREQ(key_data_with_version.c_str(), + reinterpret_cast(key_data_fetched)); + EXPECT_STREQ("AES", key.get_key_type_as_string()->c_str()); + ASSERT_TRUE(key_data_with_version.length() + 1 == key_data_fetched_size); + + delete key1; + delete key2; + delete key3; +} + +TEST_F(System_keys_container_test, StoreKeyWithTheSameIdTwice) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("percona_binlog:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + key1->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + std::string key_data2("system_key_data_2"); + Key *key2 = new Key("percona_binlog:0", "AES", nullptr, key_data2.c_str(), + key_data2.length() + 1); + key2->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + Key key_id("percona_binlog", nullptr, nullptr, nullptr, 0); + + IKey *system_key = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + Key key(system_key->get_key_id()->c_str(), + system_key->get_key_type_as_string()->c_str(), + system_key->get_user_id()->c_str(), system_key->get_key_data(), + system_key->get_key_data_size()); + key.xor_data(); + uchar *key_data_fetched = key.get_key_data(); + size_t key_data_fetched_size = key.get_key_data_size(); + std::string key_data_with_version = "0:" + key_data1; + EXPECT_STREQ(key.get_key_id()->c_str(), "percona_binlog:0"); + EXPECT_STREQ(key_data_with_version.c_str(), + reinterpret_cast(key_data_fetched)); + EXPECT_STREQ("AES", key.get_key_type_as_string()->c_str()); + ASSERT_TRUE(key_data_with_version.length() + 1 == key_data_fetched_size); + + delete key1; + delete key2; +} + +TEST_F(System_keys_container_test, + StoreKeyWithTheSameIdTwiceAndThenWithDifferentOne) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("percona_binlog:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + key1->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + std::string key_data2("system_key_data_2"); + Key *key2 = new Key("percona_binlog:0", "AES", nullptr, key_data2.c_str(), + key_data2.length() + 1); + key2->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + std::string key_data3("system_key_data_3"); + Key *key3 = new Key("percona_binlog:1", "AES", nullptr, key_data3.c_str(), + key_data3.length() + 1); + key3->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key3); + + Key key_id("percona_binlog", nullptr, nullptr, nullptr, 0); + + IKey *system_key = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + Key key(system_key->get_key_id()->c_str(), + system_key->get_key_type_as_string()->c_str(), + system_key->get_user_id()->c_str(), system_key->get_key_data(), + system_key->get_key_data_size()); + key.xor_data(); + uchar *key_data_fetched = key.get_key_data(); + size_t key_data_fetched_size = key.get_key_data_size(); + std::string key_data_with_version = "1:" + key_data3; + EXPECT_STREQ(key.get_key_id()->c_str(), "percona_binlog:1"); + EXPECT_STREQ(key_data_with_version.c_str(), + reinterpret_cast(key_data_fetched)); + EXPECT_STREQ("AES", key.get_key_type_as_string()->c_str()); + ASSERT_TRUE(key_data_with_version.length() + 1 == key_data_fetched_size); + + delete key1; + delete key2; + delete key3; +} + +TEST_F(System_keys_container_test, StoreKey1StoreKey1StoreKey2FetchKey1) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("percona_binlog:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + key1->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + std::string key_data2("system_key_data_2"); + Key *key2 = new Key("percona_binlog:1", "AES", nullptr, key_data2.c_str(), + key_data2.length() + 1); + key2->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + std::string key_data3("system_key_data_3"); + Key *key3 = new Key("percona_key:2", "AES", nullptr, key_data3.c_str(), + key_data3.length() + 1); + key3->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key3); + + Key key_id("percona_binlog", nullptr, nullptr, nullptr, 0); + + IKey *system_key = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + Key key(system_key->get_key_id()->c_str(), + system_key->get_key_type_as_string()->c_str(), + system_key->get_user_id()->c_str(), system_key->get_key_data(), + system_key->get_key_data_size()); + key.xor_data(); + uchar *key_data_fetched = key.get_key_data(); + size_t key_data_fetched_size = key.get_key_data_size(); + std::string key_data_with_version = "1:" + key_data2; + EXPECT_STREQ(key.get_key_id()->c_str(), "percona_binlog:1"); + EXPECT_STREQ(key_data_with_version.c_str(), + reinterpret_cast(key_data_fetched)); + EXPECT_STREQ("AES", key.get_key_type_as_string()->c_str()); + ASSERT_TRUE(key_data_with_version.length() + 1 == key_data_fetched_size); + + delete key1; + delete key2; + delete key3; +} + +TEST_F(System_keys_container_test, IfSystemKey) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("percona_binlog:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + + EXPECT_EQ(sys_keys_container->is_system_key(key1), true); + + std::string key_data2("system_key_data_2"); + Key *key2 = new Key("percona_key231dqwldk__23_:1", "AES", nullptr, + key_data2.c_str(), key_data2.length() + 1); + + EXPECT_EQ(sys_keys_container->is_system_key(key2), true); + + std::string key_data3("system_key_data_3"); + Key *key3 = new Key("percona_binlog:2", "AES", nullptr, key_data3.c_str(), + key_data3.length() + 1); + + EXPECT_EQ(sys_keys_container->is_system_key(key3), true); + + std::string key_data("system_key_data"); + Key *key_without_version = new Key("percona_binlog", "AES", nullptr, + key_data.c_str(), key_data.length() + 1); + + EXPECT_EQ(sys_keys_container->is_system_key(key_without_version), true); + + std::string not_system_key_data("not_system_key_data"); + Key *not_system_key = + new Key("unicorn_binlog", "AES", nullptr, not_system_key_data.c_str(), + not_system_key_data.length() + 1); + + EXPECT_EQ(sys_keys_container->is_system_key(not_system_key), false); + + delete key1; + delete key2; + delete key3; + delete key_without_version; + delete not_system_key; +} + +TEST_F(System_keys_container_test, GetKeyWithRotatedId) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("percona_binlog:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + Key *percona_binlog_key = + new Key("percona_binlog", "AES", nullptr, "sys_key", 8); + EXPECT_EQ(sys_keys_container->rotate_key_id_if_system_key_without_version( + percona_binlog_key), + false); + EXPECT_STREQ(percona_binlog_key->get_key_id()->c_str(), "percona_binlog:1"); + + std::string key_data2("system_key_data_2"); + Key *key2 = new Key("percona_binlog:1", "AES", nullptr, key_data2.c_str(), + key_data2.length() + 1); + + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + Key *percona_binlog_key2 = + new Key("percona_binlog", "AES", nullptr, "sys_key", 8); + EXPECT_EQ(sys_keys_container->rotate_key_id_if_system_key_without_version( + percona_binlog_key2), + false); + EXPECT_STREQ(percona_binlog_key2->get_key_id()->c_str(), "percona_binlog:2"); + + std::string key_data3("system_key_data_3"); + Key *key3 = new Key("percona_binlog:2", "AES", nullptr, key_data3.c_str(), + key_data3.length() + 1); + + sys_keys_container->store_or_update_if_system_key_with_version(key3); + + Key *percona_binlog_key3 = + new Key("percona_binlog", "AES", nullptr, "sys_key", 8); + EXPECT_EQ(sys_keys_container->rotate_key_id_if_system_key_without_version( + percona_binlog_key3), + false); + EXPECT_STREQ(percona_binlog_key3->get_key_id()->c_str(), "percona_binlog:3"); + + Key *key1_sk = new Key("percona_key:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + + sys_keys_container->store_or_update_if_system_key_with_version(key1_sk); + + Key *percona_key = new Key("percona_key", "AES", nullptr, "sys_key", 8); + EXPECT_EQ(sys_keys_container->rotate_key_id_if_system_key_without_version( + percona_key), + false); + EXPECT_STREQ(percona_key->get_key_id()->c_str(), "percona_key:1"); + + delete key1; + delete key2; + delete key3; + delete key1_sk; + delete percona_binlog_key; + delete percona_binlog_key2; + delete percona_binlog_key3; + delete percona_key; +} + +TEST_F(System_keys_container_test, RotateOnNotSystemKey) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("not_system_key:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + Key *key_1_id = new Key("not_system_key:0", "AES", nullptr, "sys_key", 8); + EXPECT_EQ( + sys_keys_container->rotate_key_id_if_system_key_without_version(key_1_id), + false); + EXPECT_STREQ(key_1_id->get_key_id()->c_str(), "not_system_key:0"); + + delete key1; + delete key_1_id; +} + +TEST_F(System_keys_container_test, RotateToMaxKeyId) { + std::string key_data1("system_key_data"); + std::ostringstream correct_percona_binlog_key_id_ss; + correct_percona_binlog_key_id_ss << "percona_binlog:"; + correct_percona_binlog_key_id_ss << (UINT_MAX - 1); + std::string correct_percona_binlog_key_id = + correct_percona_binlog_key_id_ss.str(); + + Key *key1 = new Key(correct_percona_binlog_key_id.c_str(), "AES", nullptr, + key_data1.c_str(), key_data1.length() + 1); + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + Key *percona_binlog_key = + new Key("percona_binlog", "AES", nullptr, "sys_key", 8); + EXPECT_EQ(sys_keys_container->rotate_key_id_if_system_key_without_version( + percona_binlog_key), + false); + + std::ostringstream max_percona_binlog_key_id_ss; + max_percona_binlog_key_id_ss << "percona_binlog:"; + max_percona_binlog_key_id_ss << UINT_MAX; + std::string max_percona_binlog_key_id = max_percona_binlog_key_id_ss.str(); + + EXPECT_STREQ(percona_binlog_key->get_key_id()->c_str(), + max_percona_binlog_key_id.c_str()); + + std::ostringstream correct_percona_key_id_ss; + correct_percona_key_id_ss << "percona_key:"; + correct_percona_key_id_ss << (UINT_MAX - 1); + std::string correct_percona_key_id = correct_percona_key_id_ss.str(); + + Key *key2 = new Key(correct_percona_key_id.c_str(), "AES", nullptr, + key_data1.c_str(), key_data1.length() + 1); + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + Key *percona_key = new Key("percona_key", "AES", nullptr, "sys_key", 8); + EXPECT_EQ(sys_keys_container->rotate_key_id_if_system_key_without_version( + percona_key), + false); + + std::ostringstream max_percona_key_id_ss; + max_percona_key_id_ss << "percona_key:"; + max_percona_key_id_ss << UINT_MAX; + std::string max_percona_key_id = max_percona_key_id_ss.str(); + + EXPECT_STREQ(percona_key->get_key_id()->c_str(), max_percona_key_id.c_str()); + + delete key1; + delete key2; + delete percona_binlog_key; + delete percona_key; +} + +TEST_F(System_keys_container_test, RotateFromMaxKeyId) { + std::string key_data1("system_key_data"); + std::ostringstream max_percona_binlog_key_id_ss; + max_percona_binlog_key_id_ss << "percona_binlog:"; + max_percona_binlog_key_id_ss << UINT_MAX; + std::string max_percona_binlog_key_id = max_percona_binlog_key_id_ss.str(); + + Key *key1 = new Key(max_percona_binlog_key_id.c_str(), "AES", nullptr, + key_data1.c_str(), key_data1.length() + 1); + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + Key *percona_binlog_key = + new Key("percona_binlog", "AES", nullptr, "sys_key", 8); + + EXPECT_CALL( + *((Mock_logger *)logger), + log(MY_ERROR_LEVEL, StrEq("System key cannot be rotated anymore, the " + "maximum key version has been reached."))); + EXPECT_EQ(sys_keys_container->rotate_key_id_if_system_key_without_version( + percona_binlog_key), + true); + + EXPECT_STREQ(percona_binlog_key->get_key_id()->c_str(), "percona_binlog"); + + std::ostringstream max_percona_key_id_ss; + max_percona_key_id_ss << "percona_key:"; + max_percona_key_id_ss << UINT_MAX; + std::string max_percona_key_id = max_percona_key_id_ss.str(); + + Key *key2 = new Key(max_percona_key_id.c_str(), "AES", nullptr, + key_data1.c_str(), key_data1.length() + 1); + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + Key *percona_key = new Key("percona_key", "AES", nullptr, "sys_key", 8); + + EXPECT_CALL( + *((Mock_logger *)logger), + log(MY_ERROR_LEVEL, StrEq("System key cannot be rotated anymore, the " + "maximum key version has been reached."))); + EXPECT_EQ(sys_keys_container->rotate_key_id_if_system_key_without_version( + percona_key), + true); + + EXPECT_STREQ(percona_key->get_key_id()->c_str(), "percona_key"); + + delete key1; + delete key2; + delete percona_binlog_key; + delete percona_key; +} + +TEST_F(System_keys_container_test, FetchFromEmptyContainer) { + Key key_id("percona_binlog", nullptr, nullptr, nullptr, 0); + + IKey *system_key = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + ASSERT_TRUE(system_key == nullptr); +} + +TEST_F(System_keys_container_test, StoreKeyWithOnlySemicolon) { + std::string key_data1("system_key_data_1"); + Key *key1 = + new Key(":", "AES", nullptr, key_data1.c_str(), key_data1.length() + 1); + key1->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + Key key_id(":", nullptr, nullptr, nullptr, 0); + + IKey *system_key = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + ASSERT_TRUE(system_key == nullptr); + + Key *key2 = + new Key(":0", "AES", nullptr, key_data1.c_str(), key_data1.length() + 1); + key2->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + Key key2_id(":0", nullptr, nullptr, nullptr, 0); + + IKey *system_key2 = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + ASSERT_TRUE(system_key2 == nullptr); + + delete key1; + delete key2; +} + +TEST_F(System_keys_container_test, ParseSystemKey) { + std::string system_key("12:0123456789012345"); + uchar *key_data = nullptr; + uint key_version = 0; + size_t key_data_length = 0; + uchar *return_val = parse_system_key( + reinterpret_cast(system_key.c_str()), system_key.length(), + &key_version, &key_data, &key_data_length); + ASSERT_TRUE(return_val != 0); + EXPECT_EQ(key_data_length, static_cast(16)); + EXPECT_EQ(key_version, static_cast(12)); + EXPECT_TRUE(memcmp(key_data, "0123456789012345", key_data_length) == 0); + EXPECT_EQ(return_val, key_data); + my_free(key_data); +} + +TEST_F(System_keys_container_test, ParseNotSystemKey) { + std::string key("0123456789012345"); + uchar *key_data = nullptr; + uint key_version = 0; + size_t key_data_length = 0; + uchar *return_val = + parse_system_key(reinterpret_cast(key.c_str()), + key.length(), &key_version, &key_data, &key_data_length); + ASSERT_TRUE(key_data == 0); + ASSERT_TRUE(return_val == 0); + EXPECT_EQ(key_data_length, static_cast(0)); + EXPECT_EQ(key_version, static_cast(0)); +} + +TEST_F(System_keys_container_test, ParseSystemKeyWithTwoColons) { + std::string system_key("0:0123456:789012345"); + uchar *key_data = nullptr; + uint key_version = 0; + size_t key_data_length = 0; + uchar *return_val = parse_system_key( + reinterpret_cast(system_key.c_str()), system_key.length(), + &key_version, &key_data, &key_data_length); + ASSERT_TRUE(return_val != 0); + EXPECT_EQ(key_data_length, static_cast(17)); + EXPECT_EQ(key_version, static_cast(0)); + EXPECT_TRUE(memcmp(key_data, "0123456:789012345", key_data_length) == 0); + EXPECT_EQ(return_val, key_data); + my_free(key_data); +} + +TEST_F(System_keys_container_test, ParseSystemKeyWithAlphaVersion) { + std::string system_key("A:key01234567890123"); + uchar *key_data = nullptr; + uint key_version = 0; + size_t key_data_length = 0; + uchar *return_val = parse_system_key( + reinterpret_cast(system_key.c_str()), system_key.length(), + &key_version, &key_data, &key_data_length); + ASSERT_TRUE(key_data == 0); + ASSERT_TRUE(return_val == 0); + EXPECT_EQ(key_data_length, static_cast(0)); + EXPECT_EQ(key_version, static_cast(0)); +} + +TEST_F(System_keys_container_test, ParseSystemKeyWithAlphaKey) { + std::string system_key("234:key01234567890123"); + uchar *key_data = nullptr; + uint key_version = 0; + size_t key_data_length = 0; + uchar *return_val = parse_system_key( + reinterpret_cast(system_key.c_str()), system_key.length(), + &key_version, &key_data, &key_data_length); + ASSERT_TRUE(return_val != 0); + EXPECT_EQ(key_data_length, static_cast(17)); + EXPECT_EQ(key_version, static_cast(234)); + EXPECT_TRUE(memcmp(key_data, "key01234567890123", key_data_length) == 0); + EXPECT_EQ(return_val, key_data); + my_free(key_data); +} + +} // namespace keyring__system_keys_container_unittest