diff --git a/.github/issue_template.md b/.github/issue_template.md index b3eea73693..0d7f157578 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -1,17 +1,23 @@ -This is the place for paid support and community users to report a **reproducible bug** in ProxySQL, suggest enhancements or make feature requests. This also includes documentation errors and/or missing documentation. +This is the place to report a **reproducible bug**, **documentation error** or **feature request** for ProxySQL. -* Remember, this is the place for fixing and improving ProxySQL rather than the place to get free support. -* If you do not have a paid support contract please try https://groups.google.com/forum/#!forum/proxysql. Due to the high volume of requests, we are unable to always provide timely support for community users. +Support questions will not be answered here. For community support, use the Google forum: https://groups.google.com/forum/#!forum/proxysql +* Note that due to time constraints ProxySQL engineers may be slow to respond to forum posts +* For information on paid support contracts: https://proxysql.com/services/support/ If you are submitting a reproducible bug report, please provide: -[ ] A clear description of your issue -[ ] The version of OS and ProxySQL -[ ] Every step to reproduce the issue -[ ] The FULL error log (not just the last few lines) -[ ] If it is a crashing bug, please check here: https://github.com/sysown/proxysql/wiki/How-to-report-a-crash-bug +- [ ] A clear description of the issue +- [ ] ProxySQL version +- [ ] OS version +- [ ] The steps to reproduce the issue +- [ ] The **full** ProxySQL error log (default location: `/var/lib/proxysql/proxysql.log`) -If the above is not provided, the issue is likely to be closed. +If this is a crashing bug, please also include: +- [ ] The package used to install ProxySQL +- [ ] The compressed proxysql binary +- [ ] The compressed core dump (Note: if you're worried it may contain sensitive data, please contact us for information on sharing it securely: https://proxysql.com/contact-us/) -Please use markdown to format code or SQL: https://guides.github.com/features/mastering-markdown/ +If the above information is not provided, this issue is likely to be closed. + +Please use markdown to format any code or SQL: https://guides.github.com/features/mastering-markdown/ Thank you! diff --git a/.gitignore b/.gitignore index 643411a12d..549562a1f4 100644 --- a/.gitignore +++ b/.gitignore @@ -170,3 +170,6 @@ heaptrack.* #tap tests *-t + +#prometheus in 1.4 +deps/prometheus-cpp/prometheus-cpp-0.9.0/ diff --git a/Makefile b/Makefile index b275a5d45f..a045861eb6 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,10 @@ +ifndef GIT_VERSION +GIT_VERSION := $(shell git describe --long) +ifndef GIT_VERSION +$(error GIT_VERSION is not set) +endif +endif + O0=-O0 O2=-O2 O1=-O1 @@ -11,7 +18,7 @@ DEBUG=${ALL_DEBUG} #export OPTZ #export EXTRALINK export MAKE -export CURVER?=2.0.10 +export CURVER?=2.0.13 ifneq (,$(wildcard /etc/os-release)) DISTRO := $(shell gawk -F= '/^NAME/{print $$2}' /etc/os-release) else @@ -157,7 +164,7 @@ clean: cd src && ${MAKE} clean cd test/tap && ${MAKE} clean -packages: centos7 centos7-dbg centos7-clickhouse centos8 centos8-dbg centos8-clickhouse ubuntu14 ubuntu14-dbg ubuntu16 ubuntu16-dbg ubuntu16-clickhouse ubuntu18 ubuntu18-dbg ubuntu18-clickhouse debian8 debian8-dbg debian9 debian9-dbg debian9-clickhouse debian10 debian10-dbg debian10-clickhouse fedora24 fedora24-dbg fedora24-clickhouse fedora27 fedora27-dbg fedora27-clickhouse fedora28 fedora28-dbg fedora28-clickhouse +packages: centos6.7 centos6.7-dbg centos7 centos7-dbg centos7-clickhouse centos8 centos8-dbg centos8-clickhouse ubuntu14 ubuntu14-dbg ubuntu16 ubuntu16-dbg ubuntu16-clickhouse ubuntu18 ubuntu18-dbg ubuntu18-clickhouse debian8 debian8-dbg debian9 debian9-dbg debian9-clickhouse debian10 debian10-dbg debian10-clickhouse fedora24 fedora24-dbg fedora24-clickhouse fedora27 fedora27-dbg fedora27-clickhouse fedora28 fedora28-dbg fedora28-clickhouse .PHONY: packages centos5: binaries/proxysql-${CURVER}-1-centos5.x86_64.rpm diff --git a/README.md b/README.md index ef3229fe8f..6a0fd9ae1e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -ProxySQL +ProxySQL Introduction ============ @@ -143,7 +143,15 @@ Admin> proxysql restart #### Reinitializing ProxySQL from the config file (after first startup the DB file is used instead of the config file): ```bash +# If you are using the init script run: +/etc/init.d/proxysql initial +# or service proxysql initial + +# If you are using the systemd unit file run: +systemctl start proxysql-initial +# or +service proxysql-initial start ``` ### Upgrades diff --git a/docker-compose.yml b/docker-compose.yml index b5a16e49b8..d0a217efcd 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,32 +1,5 @@ version: "2.0" services: - centos5_build: - image: proxysql/packaging:build-centos5 - volumes: - - ./docker/images/proxysql/rhel-compliant/rpmmacros/:/root/ - - ./docker/images/proxysql/rhel-compliant/entrypoint/:/opt/entrypoint/ - - ./:/opt/proxysql/ - environment: - - MAKE - - MAKEOPT - - CURVER - - PKG_RELEASE=centos5 - command: - - /opt/entrypoint/entrypoint.bash - centos5_dbg_build: - image: proxysql/packaging:build-centos5 - volumes: - - ./docker/images/proxysql/rhel-compliant/rpmmacros/:/root/ - - ./docker/images/proxysql/rhel-compliant/entrypoint/:/opt/entrypoint/ - - ./:/opt/proxysql/ - environment: - - MAKE - - MAKEOPT - - CURVER - - PKG_RELEASE=dbg-centos5 - - PROXYSQL_BUILD_TYPE=debug - command: - - /opt/entrypoint/entrypoint.bash centos67_build: image: proxysql/packaging:build-centos6.7 volumes: @@ -261,33 +234,6 @@ services: - PROXYSQL_BUILD_TYPE=clickhouse command: - /opt/entrypoint/entrypoint.bash - debian7_build: - image: proxysql/packaging:build-debian7 - volumes: - - ./docker/images/proxysql/deb-compliant/pre-systemd/ctl/:/root/ctl/ - - ./docker/images/proxysql/deb-compliant/entrypoint/:/opt/entrypoint/ - - ./:/opt/proxysql/ - environment: - - MAKE - - MAKEOPT - - CURVER - - PKG_RELEASE=debian7 - command: - - /opt/entrypoint/entrypoint.bash - debian7_dbg_build: - image: proxysql/packaging:build-debian7 - volumes: - - ./docker/images/proxysql/deb-compliant/pre-systemd/ctl/:/root/ctl/ - - ./docker/images/proxysql/deb-compliant/entrypoint/:/opt/entrypoint/ - - ./:/opt/proxysql/ - environment: - - MAKE - - MAKEOPT - - CURVER - - PKG_RELEASE=dbg-debian7 - - PROXYSQL_BUILD_TYPE=debug - command: - - /opt/entrypoint/entrypoint.bash debian8_build: image: proxysql/packaging:build-debian8 volumes: @@ -438,33 +384,6 @@ services: - PROXYSQL_BUILD_TYPE=clickhouse command: - /opt/entrypoint/entrypoint.bash - ubuntu12_build: - image: proxysql/packaging:build-ubuntu12 - volumes: - - ./docker/images/proxysql/deb-compliant/pre-systemd/ctl/:/root/ctl/ - - ./docker/images/proxysql/deb-compliant/entrypoint/:/opt/entrypoint/ - - ./:/opt/proxysql/ - environment: - - MAKE - - MAKEOPT - - CURVER - - PKG_RELEASE=ubuntu12 - command: - - /opt/entrypoint/entrypoint.bash - ubuntu12_dbg_build: - image: proxysql/packaging:build-ubuntu12 - volumes: - - ./docker/images/proxysql/deb-compliant/pre-systemd/ctl/:/root/ctl/ - - ./docker/images/proxysql/deb-compliant/entrypoint/:/opt/entrypoint/ - - ./:/opt/proxysql/ - environment: - - MAKE - - MAKEOPT - - CURVER - - PKG_RELEASE=dbg-ubuntu12 - - PROXYSQL_BUILD_TYPE=debug - command: - - /opt/entrypoint/entrypoint.bash ubuntu14_build: image: proxysql/packaging:build-ubuntu14 volumes: diff --git a/docker/images/proxysql/deb-compliant/ctl/proxysql.ctl b/docker/images/proxysql/deb-compliant/ctl/proxysql.ctl index 3e47657975..764b93cf24 100644 --- a/docker/images/proxysql/deb-compliant/ctl/proxysql.ctl +++ b/docker/images/proxysql/deb-compliant/ctl/proxysql.ctl @@ -1,17 +1,19 @@ Section: misc Priority: optional -Homepage: http://www.proxysql.com +Homepage: https://proxysql.com Standards-Version: 3.9.2 Package: proxysql Version: PKG_VERSION_CURVER -Maintainer: Rene Cannao +Maintainer: ProxySQL LLC Architecture: amd64 +Depends: libgnutls28 | libgnutls-deb0-28 | libgnutls30 # Changelog: CHANGELOG.md # Readme: README.md Files: proxysql /usr/bin/ etc/proxysql.cnf / etc/logrotate.d/proxysql /etc/logrotate.d/ + systemd/system/proxysql-initial.service /lib/ systemd/system/proxysql.service /lib/ tools/proxysql_galera_checker.sh /usr/share/proxysql/ tools/proxysql_galera_writer.pl /usr/share/proxysql/ diff --git a/docker/images/proxysql/deb-compliant/latest-package/ctl/proxysql.ctl b/docker/images/proxysql/deb-compliant/latest-package/ctl/proxysql.ctl index aa33787ad4..f333e32e32 100644 --- a/docker/images/proxysql/deb-compliant/latest-package/ctl/proxysql.ctl +++ b/docker/images/proxysql/deb-compliant/latest-package/ctl/proxysql.ctl @@ -1,17 +1,19 @@ Section: misc Priority: optional -Homepage: http://www.proxysql.com +Homepage: https://proxysql.com Standards-Version: 3.9.2 Package: proxysql Version: PKG_VERSION_CURVER -Maintainer: Rene Cannao +Maintainer: ProxySQL LLC Architecture: amd64 +Depends: libgnutls28 | libgnutls30 # Changelog: CHANGELOG.md # Readme: README.md Files: proxysql /usr/bin/ etc/proxysql.cnf /etc/ etc/logrotate.d/proxysql /etc/logrotate.d/ + systemd/system/proxysql-initial.service /lib/systemd/system/ systemd/system/proxysql.service /lib/systemd/system/ tools/proxysql_galera_checker.sh /usr/share/proxysql/tools/ tools/proxysql_galera_writer.pl /usr/share/proxysql/tools/ diff --git a/docker/images/proxysql/deb-compliant/pre-systemd/ctl/proxysql.ctl b/docker/images/proxysql/deb-compliant/pre-systemd/ctl/proxysql.ctl index 04b76dc28c..eaf179bcc8 100644 --- a/docker/images/proxysql/deb-compliant/pre-systemd/ctl/proxysql.ctl +++ b/docker/images/proxysql/deb-compliant/pre-systemd/ctl/proxysql.ctl @@ -1,12 +1,13 @@ Section: misc Priority: optional -Homepage: http://www.proxysql.com +Homepage: https://proxysql.com Standards-Version: 3.9.2 Package: proxysql Version: PKG_VERSION_CURVER -Maintainer: Rene Cannao +Maintainer: ProxySQL LLC Architecture: amd64 +Depends: libgnutls28 | libgnutls30 | libgnutls-deb0-28 # Changelog: CHANGELOG.md # Readme: README.md Files: proxysql /usr/bin/ diff --git a/docker/images/proxysql/rhel-compliant/rhel7/rpmmacros/rpmbuild/SPECS/proxysql.spec b/docker/images/proxysql/rhel-compliant/rhel7/rpmmacros/rpmbuild/SPECS/proxysql.spec index 4049b45dfa..06a41c513f 100644 --- a/docker/images/proxysql/rhel-compliant/rhel7/rpmmacros/rpmbuild/SPECS/proxysql.spec +++ b/docker/images/proxysql/rhel-compliant/rhel7/rpmmacros/rpmbuild/SPECS/proxysql.spec @@ -9,8 +9,8 @@ Release: 1 License: GPL+ Group: Development/Tools SOURCE0 : %{name}-%{version}.tar.gz -URL: http://www.proxysql.com/ - +URL: https://proxysql.com/ +Requires: gnutls BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root %description @@ -84,6 +84,7 @@ fi %config(noreplace) %attr(640,root,%{name}) %{_sysconfdir}/logrotate.d/%{name} %{_bindir}/* %{_sysconfdir}/systemd/system/%{name}.service +%{_sysconfdir}/systemd/system/%{name}-initial.service /usr/share/proxysql/tools/proxysql_galera_checker.sh /usr/share/proxysql/tools/proxysql_galera_writer.pl diff --git a/docker/images/proxysql/rhel-compliant/rpmmacros/rpmbuild/SPECS/proxysql.spec b/docker/images/proxysql/rhel-compliant/rpmmacros/rpmbuild/SPECS/proxysql.spec index 8432928c72..02ace66b50 100644 --- a/docker/images/proxysql/rhel-compliant/rpmmacros/rpmbuild/SPECS/proxysql.spec +++ b/docker/images/proxysql/rhel-compliant/rpmmacros/rpmbuild/SPECS/proxysql.spec @@ -13,7 +13,7 @@ License: GPL+ Group: Development/Tools SOURCE0 : %{name}-%{version}.tar.gz URL: http://www.proxysql.com/ - +Requires: gnutls BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root %description diff --git a/etc/init.d/proxysql b/etc/init.d/proxysql index 768ca07d2b..a823cbd1e2 100755 --- a/etc/init.d/proxysql +++ b/etc/init.d/proxysql @@ -18,7 +18,7 @@ OLDDATADIR="/var/run/proxysql" DATADIR="/var/lib/proxysql" -OPTS="-c /etc/proxysql.cnf -D $DATADIR" +OPTS="--idle-threads -c /etc/proxysql.cnf -D $DATADIR" PIDFILE="$DATADIR/proxysql.pid" USER="proxysql" diff --git a/etc/proxysql.cnf b/etc/proxysql.cnf index 833713d559..19669df532 100644 --- a/etc/proxysql.cnf +++ b/etc/proxysql.cnf @@ -27,7 +27,7 @@ # file is only used to initial the on-disk database read on the first startup. # # In order to FORCE a re-initialise of the on-disk database from the configuration file -# the ProxySQL service should be started with "service proxysql initial". +# the ProxySQL service should be started with "systemctl start proxysql-initial". # ######################################################################################## diff --git a/include/MySQL_HostGroups_Manager.h b/include/MySQL_HostGroups_Manager.h index 4b1514bc8d..069e2e5d5b 100644 --- a/include/MySQL_HostGroups_Manager.h +++ b/include/MySQL_HostGroups_Manager.h @@ -6,6 +6,7 @@ #include #include +#include #include "thread.h" #include "wqueue.h" @@ -313,6 +314,7 @@ class MySQL_HostGroups_Manager { umap_mysql_errors mysql_errors_umap; public: + std::mutex galera_set_writer_mutex; pthread_rwlock_t gtid_rwlock; std::unordered_map gtid_map; struct ev_async * gtid_ev_async; diff --git a/include/MySQL_Thread.h b/include/MySQL_Thread.h index f5d38b8b79..d0db853578 100644 --- a/include/MySQL_Thread.h +++ b/include/MySQL_Thread.h @@ -306,7 +306,7 @@ class MySQL_Threads_Handler char *interfaces; char *server_version; char *keep_multiplexing_variables; - unsigned int default_charset; + //unsigned int default_charset; // removed in 2.0.13 . Obsoleted previously using MySQL_Variables instead unsigned int handle_unknown_charset; bool servers_stats; bool commands_stats; @@ -394,6 +394,7 @@ class MySQL_Threads_Handler } variables; struct { unsigned int mirror_sessions_current; + int threads_initialized = 0; } status_variables; unsigned int num_threads; proxysql_mysql_thread_t *mysql_threads; diff --git a/include/MySQL_Variables.h b/include/MySQL_Variables.h index 674ee42202..79b98b8275 100644 --- a/include/MySQL_Variables.h +++ b/include/MySQL_Variables.h @@ -28,6 +28,9 @@ class MySQL_Variables { static verify_var verifiers[SQL_NAME_LAST]; static update_var updaters[SQL_NAME_LAST]; +public: + std::string variables_regexp; + public: MySQL_Variables(); diff --git a/include/mysql_connection.h b/include/mysql_connection.h index 74b11c90ba..f946dbb1e6 100644 --- a/include/mysql_connection.h +++ b/include/mysql_connection.h @@ -18,6 +18,7 @@ using json = nlohmann::json; #define STATUS_MYSQL_CONNECTION_SQL_LOG_BIN0 0x00000100 #define STATUS_MYSQL_CONNECTION_FOUND_ROWS 0x00000200 #define STATUS_MYSQL_CONNECTION_NO_BACKSLASH_ESCAPES 0x00000400 +#define STATUS_MYSQL_CONNECTION_HAS_SAVEPOINT 0x00000800 class Variable { public: @@ -146,6 +147,7 @@ class MySQL_Connection { void set_status_no_backslash_escapes(bool); void set_status_prepared_statement(bool); void set_status_user_variable(bool); + void set_status_has_savepoint(bool); void set_status_no_multiplex(bool); void set_status_sql_log_bin0(bool); void set_status_found_rows(bool); @@ -157,6 +159,7 @@ class MySQL_Connection { bool get_status_no_backslash_escapes(); bool get_status_prepared_statement(); bool get_status_user_variable(); + bool get_status_has_savepoint(); bool get_status_no_multiplex(); bool get_status_sql_log_bin0(); bool get_status_found_rows(); diff --git a/include/proxysql_structs.h b/include/proxysql_structs.h index 97f8e1e1c5..e9ca940fb1 100644 --- a/include/proxysql_structs.h +++ b/include/proxysql_structs.h @@ -166,6 +166,7 @@ enum variable_name { SQL_MAX_JOIN_SIZE, SQL_LOG_BIN, SQL_WSREP_SYNC_WAIT, + SQL_GROUP_CONCAT_MAX_LEN, SQL_NAME_LAST }; @@ -211,6 +212,7 @@ typedef struct { char * set_variable_name; // what variable name (or string) will be used when setting it to backend char * internal_variable_name; // variable name as displayed in admin , WITHOUT "default_" char * default_value; // default value + bool is_global_variable; // is it a global variable? } mysql_variable_st; #endif @@ -350,12 +352,14 @@ enum MYSQL_COM_QUERY_command { MYSQL_COM_QUERY_OPTIMIZE, MYSQL_COM_QUERY_PREPARE, MYSQL_COM_QUERY_PURGE, + MYSQL_COM_QUERY_RELEASE_SAVEPOINT, MYSQL_COM_QUERY_RENAME_TABLE, MYSQL_COM_QUERY_RESET_MASTER, MYSQL_COM_QUERY_RESET_SLAVE, MYSQL_COM_QUERY_REPLACE, MYSQL_COM_QUERY_REVOKE, MYSQL_COM_QUERY_ROLLBACK, + MYSQL_COM_QUERY_ROLLBACK_SAVEPOINT, MYSQL_COM_QUERY_SAVEPOINT, MYSQL_COM_QUERY_SELECT, MYSQL_COM_QUERY_SELECT_FOR_UPDATE, @@ -988,28 +992,30 @@ typedef struct { char * set_variable_name; // what variable name (or string) will be used when setting it to backend char * internal_variable_name; // variable name as displayed in admin , WITHOUT "default_" char * default_value; // default value + bool is_global_variable; // is it a global variable? } mysql_variable_st; */ mysql_variable_st mysql_tracked_variables[] { - { SQL_CHARACTER_SET, SETTING_CHARSET, false, true, false, false, false, (char *)"CHARSET", (char *)"CHARSET", (char *)"UTF8" } , // should be before SQL_CHARACTER_SET_RESULTS - { SQL_CHARACTER_ACTION, NONE, false, false, false, false, false, (char *)"action", (char *)"action", (char *)"1" } , - { SQL_SET_NAMES, SETTING_SET_NAMES, false, false, false, false, false, (char *)"names", (char *)"names", (char *)"DEFAULT" } , - { SQL_SAFE_UPDATES, SETTING_VARIABLE, true, false, true, false, true, (char *)"sql_safe_updates", (char *)"sql_safe_updates", (char *)"OFF" } , - { SQL_SELECT_LIMIT, SETTING_VARIABLE, false, false, true, true, false, (char *)"sql_select_limit", (char *)"sql_select_limit", (char *)"DEFAULT" } , - { SQL_SQL_MODE, SETTING_VARIABLE, true, false, true, false, false, (char *)"sql_mode" , (char *)"sql_mode" , (char *)"" } , - { SQL_TIME_ZONE, SETTING_VARIABLE, true, false, true, false, false, (char *)"time_zone", (char *)"time_zone", (char *)"SYSTEM" } , - { SQL_CHARACTER_SET_RESULTS, SETTING_VARIABLE, false, false, true, false, false, (char *)"character_set_results", (char *)"character_set_results", (char *)"UTF8" } , - { SQL_CHARACTER_SET_CONNECTION, SETTING_VARIABLE, false, false, true, false, false, (char *)"character_set_connection", (char *)"character_set_connection", (char *)"UTF8" } , - { SQL_CHARACTER_SET_CLIENT, SETTING_VARIABLE, false, false, true, false, false, (char *)"character_set_client", (char *)"character_set_client", (char *)"UTF8" } , - { SQL_CHARACTER_SET_DATABASE, SETTING_VARIABLE, false, false, true, false, false, (char *)"character_set_database", (char *)"character_set_database", (char *)"UTF8" } , - { SQL_ISOLATION_LEVEL, SETTING_ISOLATION_LEVEL, false, true, true, false, false, (char *)"SESSION TRANSACTION ISOLATION LEVEL", (char *)"isolation_level", (char *)"READ COMMITTED" } , - { SQL_TRANSACTION_READ, SETTING_TRANSACTION_READ, false, true, true, false, false, (char *)"SESSION TRANSACTION READ", (char *)"transaction_read", (char *)"WRITE" } , - { SQL_SQL_AUTO_IS_NULL, SETTING_VARIABLE, true, false, true, false, true, (char *)"sql_auto_is_null", (char *)"sql_auto_is_null", (char *)"OFF" } , - { SQL_COLLATION_CONNECTION, SETTING_VARIABLE, true, false, true, false, false, (char *)"collation_connection", (char *)"collation_connection", (char *)"utf8_general_ci" } , - { SQL_NET_WRITE_TIMEOUT, SETTING_VARIABLE, false, false, true, true, false, (char *)"net_write_timeout", (char *)"net_write_timeout", (char *)"60" } , - { SQL_MAX_JOIN_SIZE, SETTING_VARIABLE, false, false, true, true, false, (char *)"max_join_size", (char *)"max_join_size", (char *)"18446744073709551615" } , - { SQL_LOG_BIN, SETTING_VARIABLE, false, false, true, false, false, (char *)"sql_log_bin", (char *)"sql_log_bin", (char *)"1" } , - { SQL_WSREP_SYNC_WAIT, SETTING_VARIABLE, false, false, true, true, false, (char *)"wsrep_sync_wait", (char *)"wsrep_sync_wait", (char *)"0" } , + { SQL_CHARACTER_SET, SETTING_CHARSET, false, true, false, false, false, (char *)"charset", (char *)"charset", (char *)"utf8" , true} , // should be before SQL_CHARACTER_SET_RESULTS + { SQL_CHARACTER_ACTION, NONE, false, false, false, false, false, (char *)"action", (char *)"action", (char *)"1" , false} , + { SQL_SET_NAMES, SETTING_SET_NAMES, false, false, false, false, false, (char *)"names", (char *)"names", (char *)"DEFAULT" , false} , + { SQL_SAFE_UPDATES, SETTING_VARIABLE, true, false, true, false, true, (char *)"sql_safe_updates", (char *)"sql_safe_updates", (char *)"OFF" , false} , + { SQL_SELECT_LIMIT, SETTING_VARIABLE, false, false, true, true, false, (char *)"sql_select_limit", (char *)"sql_select_limit", (char *)"DEFAULT" , false} , + { SQL_SQL_MODE, SETTING_VARIABLE, true, false, true, false, false, (char *)"sql_mode" , (char *)"sql_mode" , (char *)"" , false} , + { SQL_TIME_ZONE, SETTING_VARIABLE, true, false, true, false, false, (char *)"time_zone", (char *)"time_zone", (char *)"SYSTEM" , false} , + { SQL_CHARACTER_SET_RESULTS, SETTING_VARIABLE, false, false, true, false, false, (char *)"character_set_results", (char *)"character_set_results", (char *)"utf8" , false} , + { SQL_CHARACTER_SET_CONNECTION, SETTING_VARIABLE, false, false, true, false, false, (char *)"character_set_connection", (char *)"character_set_connection", (char *)"utf8", false } , + { SQL_CHARACTER_SET_CLIENT, SETTING_VARIABLE, false, false, true, false, false, (char *)"character_set_client", (char *)"character_set_client", (char *)"utf8" , false} , + { SQL_CHARACTER_SET_DATABASE, SETTING_VARIABLE, false, false, true, false, false, (char *)"character_set_database", (char *)"character_set_database", (char *)"utf8" , false} , + { SQL_ISOLATION_LEVEL, SETTING_ISOLATION_LEVEL, false, true, true, false, false, (char *)"SESSION TRANSACTION ISOLATION LEVEL", (char *)"isolation_level", (char *)"READ COMMITTED" , false} , + { SQL_TRANSACTION_READ, SETTING_TRANSACTION_READ, false, true, true, false, false, (char *)"SESSION TRANSACTION READ", (char *)"transaction_read", (char *)"WRITE" , false} , + { SQL_SQL_AUTO_IS_NULL, SETTING_VARIABLE, true, false, true, false, true, (char *)"sql_auto_is_null", (char *)"sql_auto_is_null", (char *)"OFF" , false} , + { SQL_COLLATION_CONNECTION, SETTING_VARIABLE, true, false, true, false, false, (char *)"collation_connection", (char *)"collation_connection", (char *)"utf8_general_ci" , true} , + { SQL_NET_WRITE_TIMEOUT, SETTING_VARIABLE, false, false, true, true, false, (char *)"net_write_timeout", (char *)"net_write_timeout", (char *)"60" , false} , + { SQL_MAX_JOIN_SIZE, SETTING_VARIABLE, false, false, true, true, false, (char *)"max_join_size", (char *)"max_join_size", (char *)"18446744073709551615" , false} , + { SQL_LOG_BIN, SETTING_VARIABLE, false, false, true, false, false, (char *)"sql_log_bin", (char *)"sql_log_bin", (char *)"1" , false} , + { SQL_WSREP_SYNC_WAIT, SETTING_VARIABLE, false, false, true, true, false, (char *)"wsrep_sync_wait", (char *)"wsrep_sync_wait", (char *)"0" , false} , + { SQL_GROUP_CONCAT_MAX_LEN, SETTING_VARIABLE, false, false, true, true, false, (char *)"group_concat_max_len", (char *)"group_concat_max_len", (char *)"1024" , false} , }; #else extern mysql_variable_st mysql_tracked_variables[]; diff --git a/lib/Makefile b/lib/Makefile index ba33b8359a..db3b10d85c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,6 +1,9 @@ - - +ifndef GIT_VERSION GIT_VERSION := $(shell git describe --long) +ifndef GIT_VERSION +$(error GIT_VERSION is not set) +endif +endif DEPS_PATH=../deps diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index cce7f218e2..a2f79f3823 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -15,12 +15,10 @@ #include -#define USE_MYSRVC_ARRAY - -#ifdef USE_MYSRVC_ARRAY +#ifdef TEST_AURORA static unsigned long long array_mysrvc_total = 0; static unsigned long long array_mysrvc_cands = 0; -#endif // USE_MYSRVC_ARRAY +#endif // TEST_AURORA #define SAFE_SQLITE3_STEP(_stmt) do {\ do {\ @@ -172,6 +170,7 @@ static void gtid_async_cb(struct ev_loop *loop, struct ev_async *watcher, int re } static void gtid_timer_cb (struct ev_loop *loop, struct ev_timer *timer, int revents) { + if (GloMTH == nullptr) { return; } ev_timer_stop(loop, timer); ev_timer_set(timer, __sync_add_and_fetch(&GloMTH->variables.binlog_reader_connect_retry_msec,0)/1000, 0); if (glovars.shutdown) { @@ -1713,7 +1712,7 @@ void MySQL_HostGroups_Manager::generate_mysql_gtid_executed_tables() { it = gtid_map.begin(); while(it != gtid_map.end()) { GTID_Server_Data * gtid_si = it->second; - if (gtid_si->active == false) { + if (gtid_si && gtid_si->active == false) { to_remove.push_back(it->first); } it++; @@ -2280,7 +2279,6 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ unsigned int sum=0; unsigned int TotalUsedConn=0; unsigned int l=mysrvs->cnt(); -#ifdef USE_MYSRVC_ARRAY #ifdef TEST_AURORA unsigned long long a1 = array_mysrvc_total/10000; array_mysrvc_total += l; @@ -2295,7 +2293,6 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ if (l>32) { mysrvcCandidates = (MySrvC **)malloc(sizeof(MySrvC *)*l); } -#endif // USE_MYSRVC_ARRAY if (l) { //int j=0; for (j=0; jgtid_exists(mysrvc, gtid_uuid, gtid_trxid)) { sum+=mysrvc->weight; TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); -#ifdef USE_MYSRVC_ARRAY mysrvcCandidates[num_candidates]=mysrvc; num_candidates++; -#endif // USE_MYSRVC_ARRAY } } else { if (max_lag_ms >= 0) { if (max_lag_ms >= mysrvc->aws_aurora_current_lag_us/1000) { sum+=mysrvc->weight; TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); -#ifdef USE_MYSRVC_ARRAY mysrvcCandidates[num_candidates]=mysrvc; num_candidates++; -#endif // USE_MYSRVC_ARRAY } else { sess->thread->status_variables.aws_aurora_replicas_skipped_during_query++; } } else { sum+=mysrvc->weight; TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); -#ifdef USE_MYSRVC_ARRAY mysrvcCandidates[num_candidates]=mysrvc; num_candidates++; -#endif // USE_MYSRVC_ARRAY } } } @@ -2367,28 +2358,22 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ if (MyHGM->gtid_exists(mysrvc, gtid_uuid, gtid_trxid)) { sum+=mysrvc->weight; TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); -#ifdef USE_MYSRVC_ARRAY mysrvcCandidates[num_candidates]=mysrvc; num_candidates++; -#endif // USE_MYSRVC_ARRAY } } else { if (max_lag_ms >= 0) { if (max_lag_ms >= mysrvc->aws_aurora_current_lag_us/1000) { sum+=mysrvc->weight; TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); -#ifdef USE_MYSRVC_ARRAY mysrvcCandidates[num_candidates]=mysrvc; num_candidates++; -#endif // USE_MYSRVC_ARRAY } } else { sum+=mysrvc->weight; TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); -#ifdef USE_MYSRVC_ARRAY mysrvcCandidates[num_candidates]=mysrvc; num_candidates++; -#endif // USE_MYSRVC_ARRAY } } } @@ -2398,7 +2383,6 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ } } } -#ifdef USE_MYSRVC_ARRAY if (max_lag_ms) { // we are using AWS Aurora, as this logic is implemented only here unsigned int min_num_replicas = sess->thread->variables.aurora_max_lag_ms_only_read_from_replicas; if (min_num_replicas) { @@ -2425,7 +2409,6 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ } } } -#endif // USE_MYSRVC_ARRAY if (sum==0) { // per issue #531 , we try a desperate attempt to bring back online any shunned server // we do this lowering the maximum wait time to 10% @@ -2450,28 +2433,22 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ if (MyHGM->gtid_exists(mysrvc, gtid_uuid, gtid_trxid)) { sum+=mysrvc->weight; TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); -#ifdef USE_MYSRVC_ARRAY mysrvcCandidates[num_candidates]=mysrvc; num_candidates++; -#endif // USE_MYSRVC_ARRAY } } else { if (max_lag_ms >= 0) { if (max_lag_ms >= mysrvc->aws_aurora_current_lag_us/1000) { sum+=mysrvc->weight; TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); -#ifdef USE_MYSRVC_ARRAY mysrvcCandidates[num_candidates]=mysrvc; num_candidates++; -#endif // USE_MYSRVC_ARRAY } } else { sum+=mysrvc->weight; TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); -#ifdef USE_MYSRVC_ARRAY mysrvcCandidates[num_candidates]=mysrvc; num_candidates++; -#endif // USE_MYSRVC_ARRAY } } } @@ -2481,12 +2458,12 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ } if (sum==0) { proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySrvC NULL because no backend ONLINE or with weight\n"); -#ifdef USE_MYSRVC_ARRAY if (l>32) { free(mysrvcCandidates); } +#ifdef TEST_AURORA array_mysrvc_cands += num_candidates; -#endif // USE_MYSRVC_ARRAY +#endif // TEST_AURORA return NULL; // if we reach here, we couldn't find any target } @@ -2494,75 +2471,35 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ unsigned int New_TotalUsedConn=0; // we will now scan again to ignore overloaded servers -#ifdef USE_MYSRVC_ARRAY for (j=0; jidx(j); - if (mysrvc->status==MYSQL_SERVER_STATUS_ONLINE) { // consider this server only if ONLINE -#endif // USE_MYSRVC_ARRAY - unsigned int len=mysrvc->ConnectionsUsed->conns_length(); -#ifdef USE_MYSRVC_ARRAY -#else - - if (len < mysrvc->max_connections) { // consider this server only if didn't reach max_connections - if ( mysrvc->current_latency_us < ( mysrvc->max_latency_us ? mysrvc->max_latency_us : mysql_thread___default_max_latency_ms*1000 ) ) { // consider the host only if not too far -#endif // USE_MYSRVC_ARRAY - if ((len * sum) <= (TotalUsedConn * mysrvc->weight * 1.5 + 1)) { + unsigned int len=mysrvc->ConnectionsUsed->conns_length(); + if ((len * sum) <= (TotalUsedConn * mysrvc->weight * 1.5 + 1)) { -#ifdef USE_MYSRVC_ARRAY - New_sum+=mysrvc->weight; - New_TotalUsedConn+=len; -#else - if (gtid_trxid) { - if (MyHGM->gtid_exists(mysrvc, gtid_uuid, gtid_trxid)) { - New_sum+=mysrvc->weight; - New_TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); - } - } else { - if (max_lag_ms >= 0) { - if (max_lag_ms >= mysrvc->aws_aurora_current_lag_us/1000) { - New_sum+=mysrvc->weight; - New_TotalUsedConn+=len; - } - } else { - New_sum+=mysrvc->weight; - New_TotalUsedConn+=len; - } - } -#endif // USE_MYSRVC_ARRAY -#ifdef USE_MYSRVC_ARRAY - } else { - // remove the candidate - if (j+1 < num_candidates) { - mysrvcCandidates[j] = mysrvcCandidates[num_candidates-1]; - } - j--; - num_candidates--; -#endif // USE_MYSRVC_ARRAY - } -#ifdef USE_MYSRVC_ARRAY -#else - } + New_sum+=mysrvc->weight; + New_TotalUsedConn+=len; + } else { + // remove the candidate + if (j+1 < num_candidates) { + mysrvcCandidates[j] = mysrvcCandidates[num_candidates-1]; } + j--; + num_candidates--; } -#endif // USE_MYSRVC_ARRAY } if (New_sum==0) { proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySrvC NULL because no backend ONLINE or with weight\n"); -#ifdef USE_MYSRVC_ARRAY if (l>32) { free(mysrvcCandidates); } +#ifdef TEST_AURORA array_mysrvc_cands += num_candidates; -#endif // USE_MYSRVC_ARRAY +#endif // TEST_AURORA return NULL; // if we reach here, we couldn't find any target } -#ifdef USE_MYSRVC_ARRAY // latency awareness algorithm is enabled only when compiled with USE_MYSRVC_ARRAY if (sess->thread->variables.min_num_servers_lantency_awareness) { if (num_candidates >= sess->thread->variables.min_num_servers_lantency_awareness) { @@ -2603,7 +2540,6 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ } } } -#endif // USE_MYSRVC_ARRAY unsigned int k; @@ -2615,62 +2551,28 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ k++; New_sum=0; -#ifdef USE_MYSRVC_ARRAY for (j=0; jidx(j); - if (mysrvc->status==MYSQL_SERVER_STATUS_ONLINE) { // consider this server only if ONLINE - unsigned int len=mysrvc->ConnectionsUsed->conns_length(); - if (len < mysrvc->max_connections) { // consider this server only if didn't reach max_connections - if ( mysrvc->current_latency_us < ( mysrvc->max_latency_us ? mysrvc->max_latency_us : mysql_thread___default_max_latency_ms*1000 ) ) { // consider the host only if not too far - if ((len * sum) <= (TotalUsedConn * mysrvc->weight * 1.5 + 1)) { -#endif // USE_MYSRVC_ARRAY -#ifdef USE_MYSRVC_ARRAY - New_sum+=mysrvc->weight; -#else - if (gtid_trxid) { - if (MyHGM->gtid_exists(mysrvc, gtid_uuid, gtid_trxid)) { - New_sum+=mysrvc->weight; - //TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); // this line is a bug - } - } else { - if (max_lag_ms >= 0) { - if (max_lag_ms >= mysrvc->aws_aurora_current_lag_us/1000) { - New_sum+=mysrvc->weight; - } - } else { - New_sum+=mysrvc->weight; - } - } -#endif // USE_MYSRVC_ARRAY - if (k<=New_sum) { - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySrvC %p, server %s:%d\n", mysrvc, mysrvc->address, mysrvc->port); -#ifdef USE_MYSRVC_ARRAY - if (l>32) { - free(mysrvcCandidates); - } - array_mysrvc_cands += num_candidates; -#endif // USE_MYSRVC_ARRAY - return mysrvc; - } -#ifdef USE_MYSRVC_ARRAY -#else - } - } + New_sum+=mysrvc->weight; + if (k<=New_sum) { + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySrvC %p, server %s:%d\n", mysrvc, mysrvc->address, mysrvc->port); + if (l>32) { + free(mysrvcCandidates); } +#ifdef TEST_AURORA + array_mysrvc_cands += num_candidates; +#endif // TEST_AURORA + return mysrvc; } -#endif // USE_MYSRVC_ARRAY } } proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySrvC NULL\n"); -#ifdef USE_MYSRVC_ARRAY if (l>32) { free(mysrvcCandidates); } +#ifdef TEST_AURORA array_mysrvc_cands += num_candidates; -#endif // USE_MYSRVC_ARRAY +#endif // TEST_AURORA return NULL; // if we reach here, we couldn't find any target } @@ -4379,7 +4281,7 @@ void MySQL_HostGroups_Manager::update_galera_set_offline(char *_hostname, int _p mydb->execute(query); //free(query); } else { - q=(char *)"UPDATE mysql_servers_incoming SET status=1 WHERE hostname='%s' AND port=%d AND hostgroup_id = %d"; + q=(char *)"UPDATE mysql_servers_incoming SET status=1 WHERE hostname='%s' AND port=%d"; sprintf(query,q,_hostname,_port,_writer_hostgroup); mydb->execute(query); } @@ -4493,7 +4395,7 @@ void MySQL_HostGroups_Manager::update_galera_set_read_only(char *_hostname, int sprintf(query, q, info->reader_hostgroup, _hostname, _port, info->writer_hostgroup, info->backup_writer_hostgroup, info->offline_hostgroup); mydb->execute(query); //free(query); - q=(char *)"DELETE FROM mysql_servers_incoming WHERE hostname='%s' AND port=%d AND hostgroup_id in (%d, %d, %d) FROM mysql_galera_hostgroups WHERE writer_hostgroup=%d)"; + q=(char *)"DELETE FROM mysql_servers_incoming WHERE hostname='%s' AND port=%d AND hostgroup_id in (%d, %d, %d)"; //query=(char *)malloc(strlen(q)+strlen(_hostname)+64); sprintf(query,q,_hostname,_port, info->offline_hostgroup, info->backup_writer_hostgroup, info->writer_hostgroup, info->writer_hostgroup); mydb->execute(query); @@ -4555,8 +4457,7 @@ Galera_Info *MySQL_HostGroups_Manager::get_galera_node_info(int hostgroup) { } void MySQL_HostGroups_Manager::update_galera_set_writer(char *_hostname, int _port, int _writer_hostgroup) { - std::mutex local_mutex; - std::lock_guard lock(local_mutex); + std::lock_guard lock(galera_set_writer_mutex); int cols=0; int affected_rows=0; SQLite3_result *resultset=NULL; @@ -4566,7 +4467,7 @@ void MySQL_HostGroups_Manager::update_galera_set_writer(char *_hostname, int _po q=(char *)"SELECT hostgroup_id FROM mysql_servers JOIN mysql_galera_hostgroups ON hostgroup_id=writer_hostgroup OR hostgroup_id=reader_hostgroup OR hostgroup_id=backup_writer_hostgroup OR hostgroup_id=offline_hostgroup WHERE hostname='%s' AND port=%d"; query=(char *)malloc(strlen(q)+strlen(_hostname)+32); sprintf(query,q,_hostname,_port); - mydb->execute_statement(query, &error, &cols , &affected_rows , &resultset); + mydb->execute_statement(query, &error, &cols , &affected_rows , &resultset); if (error) { free(error); error=NULL; @@ -4639,7 +4540,9 @@ void MySQL_HostGroups_Manager::update_galera_set_writer(char *_hostname, int _po if ( (writer_is_also_reader==0 && found_reader==false) || - (writer_is_also_reader > 0 && found_reader==true) + (writer_is_also_reader == 1 && found_reader==true) + || + (writer_is_also_reader == 2) ) { // either both true or both false delete resultset; resultset=NULL; diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index b66d602cb2..58ccb51b02 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -1549,6 +1549,7 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned //(*myds)->switching_auth_stage=2; charset=(*myds)->tmp_charset; proxy_debug(PROXY_DEBUG_MYSQL_PROTOCOL,2,"Session=%p , DS=%p . Encrypted: %d , switching_auth: %d, auth_plugin_id: %d\n", (*myds)->sess, (*myds), (*myds)->encrypted, (*myds)->switching_auth_stage, auth_plugin_id); + capabilities = (*myds)->myconn->options.client_flag; goto __do_auth; } @@ -1556,6 +1557,7 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned (*myds)->myconn->options.client_flag = capabilities; pkt += sizeof(uint32_t); max_pkt = CPY4(pkt); + (*myds)->myconn->options.max_allowed_pkt = max_pkt; pkt += sizeof(uint32_t); charset = *(uint8_t *)pkt; if ( (*myds)->encrypted == false ) { // client wants to use SSL @@ -2000,7 +2002,6 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned if (ret==true) { - (*myds)->myconn->options.max_allowed_pkt=max_pkt; (*myds)->DSS=STATE_CLIENT_HANDSHAKE; if (!userinfo->username) // if set already, ignore @@ -2311,7 +2312,7 @@ bool MySQL_Protocol::generate_COM_QUERY_from_COM_FIELD_LIST(PtrSize_t *pkt) { (*myds)->com_field_wild=strdup(wild); } - char *qt = (char *)"SELECT * FROM %s WHERE 1=0"; + char *qt = (char *)"SELECT * FROM `%s` WHERE 1=0"; q = (char *)malloc(strlen(qt)+strlen(tablename)); sprintf(q,qt,tablename); l_free(pkt->size, pkt->ptr); diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index a6a78793c0..e447a3ebfd 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -1013,6 +1013,7 @@ void MySQL_Session::generate_proxysql_internal_session_json(json &j) { j["backends"][i]["conn"]["no_backslash_escapes"] = _myconn->options.no_backslash_escapes; j["backends"][i]["conn"]["status"]["get_lock"] = _myconn->get_status_get_lock(); j["backends"][i]["conn"]["status"]["lock_tables"] = _myconn->get_status_lock_tables(); + j["backends"][i]["conn"]["status"]["has_savepoint"] = _myconn->get_status_has_savepoint(); j["backends"][i]["conn"]["status"]["temporary_table"] = _myconn->get_status_temporary_table(); j["backends"][i]["conn"]["status"]["user_variable"] = _myconn->get_status_user_variable(); j["backends"][i]["conn"]["status"]["found_rows"] = _myconn->get_status_found_rows(); @@ -1574,7 +1575,24 @@ bool MySQL_Session::handler_again___verify_backend__generic_variable(uint32_t *b *be_var = strdup(def); uint32_t tmp_int = SpookyHash::Hash32(*be_var, strlen(*be_var), 10); *be_int = tmp_int; + + switch(status) { // this switch can be replaced with a simple previous_status.push(status), but it is here for readibility + case PROCESSING_QUERY: + previous_status.push(PROCESSING_QUERY); + break; + case PROCESSING_STMT_PREPARE: + previous_status.push(PROCESSING_STMT_PREPARE); + break; + case PROCESSING_STMT_EXECUTE: + previous_status.push(PROCESSING_STMT_EXECUTE); + break; + default: + assert(0); + break; + } + NEXT_IMMEDIATE_NEW(next_sess_status); } + if (*fe_int) { if (*fe_int != *be_int) { { @@ -2308,7 +2326,36 @@ bool MySQL_Session::handler_again___status_SETTING_MULTI_STMT(int *_rc) { NEXT_IMMEDIATE_NEW(st); } else { if (rc==-1) { - proxy_error("Error setting multistatement on server %s , %d : %d, %s\n", myconn->parent->address, myconn->parent->port, mysql_errno(myconn->mysql), mysql_error(myconn->mysql)); + // the command failed + int myerr=mysql_errno(myconn->mysql); + if (myerr >= 2000) { + bool retry_conn=false; + // client error, serious + proxy_error("Detected a broken connection during setting MYSQL_OPTION_MULTI_STATEMENTS on %s , %d : %d, %s\n", myconn->parent->address, myconn->parent->port, myerr, mysql_error(myconn->mysql)); + //if ((myds->myconn->reusable==true) && ((myds->myprot.prot_status & SERVER_STATUS_IN_TRANS)==0)) { + if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + retry_conn=true; + } + myds->destroy_MySQL_Connection_From_Pool(false); + myds->fd=0; + if (retry_conn) { + myds->DSS=STATE_NOT_INITIALIZED; + NEXT_IMMEDIATE_NEW(CONNECTING_SERVER); + } + *_rc=-1; // an error happened, we should destroy the Session + return ret; + } else { + proxy_warning("Error during MYSQL_OPTION_MULTI_STATEMENTS : %d, %s\n", myerr, mysql_error(myconn->mysql)); + // we won't go back to PROCESSING_QUERY + st=previous_status.top(); + previous_status.pop(); + char sqlstate[10]; + sprintf(sqlstate,"%s",mysql_sqlstate(myconn->mysql)); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,mysql_errno(myconn->mysql),sqlstate,mysql_error(myconn->mysql)); + myds->destroy_MySQL_Connection_From_Pool(true); + myds->fd=0; + RequestEnd(myds); + } } else { // rc==1 , nothing to do for now } @@ -2560,7 +2607,8 @@ bool MySQL_Session::handler_again___status_CHANGING_USER_SERVER(int *_rc) { __sync_fetch_and_add(&MyHGM->status.backend_change_user, 1); myds->myconn->userinfo->set(client_myds->myconn->userinfo); myds->myconn->reset(); - st=previous_status.top(); + myds->DSS = STATE_MARIADB_GENERIC; + st = previous_status.top(); previous_status.pop(); NEXT_IMMEDIATE_NEW(st); } else { @@ -3282,7 +3330,7 @@ int MySQL_Session::handler() { // break; case _MYSQL_COM_QUIT: proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_QUIT packet\n"); - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_QUIT, this, NULL); + if (GloMyLogger) { GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_QUIT, this, NULL); } l_free(pkt.size,pkt.ptr); handler_ret = -1; return handler_ret; @@ -3369,7 +3417,7 @@ int MySQL_Session::handler() { c=*((unsigned char *)pkt.ptr+sizeof(mysql_hdr)); if (c==_MYSQL_COM_QUIT) { proxy_error("Unexpected COM_QUIT from client %s . Session_status: %d , client_status: %d Disconnecting it\n", buf, status, client_myds->status); - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_QUIT, this, NULL); + if (GloMyLogger) { GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_QUIT, this, NULL); } proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_QUIT packet\n"); l_free(pkt.size,pkt.ptr); if (thread) { @@ -3576,7 +3624,9 @@ int MySQL_Session::handler() { mybe->server_myds->wait_until=0; if (qpo) { if (qpo->timeout > 0) { - mybe->server_myds->wait_until=thread->curtime+qpo->timeout*1000; + unsigned long long qr_timeout=qpo->timeout; + mybe->server_myds->wait_until=thread->curtime; + mybe->server_myds->wait_until+=qr_timeout*1000; } } if (mysql_thread___default_query_timeout) { @@ -4859,7 +4909,7 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Parsing SET command = %s\n", nqn.c_str()); } #endif - if (index(dig,';')) { + if (index(dig,';') && (index(dig,';') != dig + strlen(dig)-1)) { string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); proxy_warning("Unable to parse multi-statements command with SET statement: setting lock hostgroup . Command: %s\n", nqn.c_str()); *lock_hostgroup = true; @@ -5023,7 +5073,7 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C return false; } } - } else if ( (var == "sql_select_limit") || (var == "net_write_timeout") || (var == "max_join_size") || (var == "wsrep_sync_wait") ) { + } else if ( (var == "sql_select_limit") || (var == "net_write_timeout") || (var == "max_join_size") || (var == "wsrep_sync_wait") || (var == "group_concat_max_len") ) { int idx = SQL_NAME_LAST; for (int i = 0 ; i < SQL_NAME_LAST ; i++) { if (mysql_tracked_variables[i].is_number) { diff --git a/lib/MySQL_Thread.cpp b/lib/MySQL_Thread.cpp index 60b6a1fe8e..6f10569725 100644 --- a/lib/MySQL_Thread.cpp +++ b/lib/MySQL_Thread.cpp @@ -364,7 +364,7 @@ static char * mysql_thread_variables_names[]= { (char *)"eventslog_format", (char *)"auditlog_filename", (char *)"auditlog_filesize", - (char *)"default_charset", + //(char *)"default_charset", // removed in 2.0.13 . Obsoleted previously using MySQL_Variables instead (char *)"handle_unknown_charset", (char *)"free_connections_pct", (char *)"connection_warming", @@ -793,6 +793,8 @@ char * MySQL_Threads_Handler::get_variable_string(char *name) { } if (!strncmp(name,"default_",8)) { for (int i=0; i 8) { + if (strncmp(name, "default_", 8) == 0) { + for (unsigned int i = 0; i < SQL_NAME_LAST ; i++) { + if (mysql_tracked_variables[i].is_global_variable) { + size_t var_len = strlen(mysql_tracked_variables[i].internal_variable_name); + if (strlen(name) == (var_len+8)) { + if (!strncmp(name+8, mysql_tracked_variables[i].internal_variable_name, var_len)) { + return strdup(variables.default_variables[i]); + } + } + } + } } } if (!strcasecmp(name,"firewall_whitelist_errormsg")) return strdup(variables.firewall_whitelist_errormsg); @@ -2446,9 +2457,11 @@ bool MySQL_Threads_Handler::set_variable(char *name, const char *value) { // thi if (!strncmp(name,"default_",8)) { for (int i=0; i 8) { - if (strncmp(name, "default_", 8)) { + if (strncmp(name, "default_", 8) == 0) { for (unsigned int i = 0; i < SQL_NAME_LAST ; i++) { - size_t var_len = strlen(mysql_tracked_variables[i].internal_variable_name); - if (strlen(name) == (var_len+8)) { - if (!strncmp(name+8, (mysql_tracked_variables[i].internal_variable_name), var_len)) { - return true; + if (mysql_tracked_variables[i].is_global_variable) { + size_t var_len = strlen(mysql_tracked_variables[i].internal_variable_name); + if (strlen(name) == (var_len+8)) { + if (!strncmp(name+8, mysql_tracked_variables[i].internal_variable_name, var_len)) { + return true; + } } } } @@ -3167,7 +3194,7 @@ MySQL_Thread::~MySQL_Thread() { if (sess->session_type == PROXYSQL_SESSION_ADMIN || sess->session_type == PROXYSQL_SESSION_STATS) { char _buf[1024]; sprintf(_buf,"%s:%d:%s()", __FILE__, __LINE__, __func__); - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_CLOSE, sess, NULL, _buf); + if (GloMyLogger) { GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_CLOSE, sess, NULL, _buf); } } delete sess; } @@ -3373,7 +3400,11 @@ bool MySQL_Thread::init() { match_regexes=(Session_Regex **)malloc(sizeof(Session_Regex *)*4); match_regexes[0]=new Session_Regex((char *)"^SET (|SESSION |@@|@@session.)SQL_LOG_BIN( *)(:|)=( *)"); - match_regexes[1]=new Session_Regex((char *)"^SET (|SESSION |@@|@@session.)(SQL_MODE|TIME_ZONE|CHARACTER_SET_RESULTS|CHARACTER_SET_CLIENT|CHARACTER_SET_DATABASE|SESSION_TRACK_GTIDS|SQL_AUTO_IS_NULL|SQL_SELECT_LIMIT|SQL_SAFE_UPDATES|COLLATION_CONNECTION|CHARACTER_SET_CONNECTION|NET_WRITE_TIMEOUT|WSREP_SYNC_WAIT|TX_ISOLATION|MAX_JOIN_SIZE( *)(:|)=( *))"); + + std::stringstream ss; + ss << "^SET (|SESSION |@@|@@session.)(" << mysql_variables.variables_regexp << "SESSION_TRACK_GTIDS|TX_ISOLATION( *)(:|)=( *))"; + match_regexes[1]=new Session_Regex((char *)ss.str().c_str()); + match_regexes[2]=new Session_Regex((char *)"^SET(?: +)(|SESSION +)TRANSACTION(?: +)(?:(?:(ISOLATION(?: +)LEVEL)(?: +)(REPEATABLE(?: +)READ|READ(?: +)COMMITTED|READ(?: +)UNCOMMITTED|SERIALIZABLE))|(?:(READ)(?: +)(WRITE|ONLY)))"); match_regexes[3]=new Session_Regex((char *)"^(set)(?: +)((charset)|(character +set))(?: )"); @@ -4434,10 +4465,13 @@ void MySQL_Thread::refresh_variables() { for (int i=0; iget_variable_string(buf); + if (mysql_tracked_variables[i].is_global_variable) { + sprintf(buf,"default_%s",mysql_tracked_variables[i].internal_variable_name); + mysql_thread___default_variables[i] = GloMTH->get_variable_string(buf); + } } if (mysql_thread___server_version) free(mysql_thread___server_version); @@ -4587,6 +4621,10 @@ MySQL_Thread::MySQL_Thread() { variables.stats_time_backend_query=false; variables.stats_time_query_processor=false; variables.query_cache_stores_empty_result=true; + + for (int i=0; ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;ishutdown_) return 0; unsigned long long q=0; unsigned int i; for (i=0;iwrlock(); + char * previous_default_charset = GloMTH->get_variable_string((char *)"default_charset"); + char * previous_default_collation_connection = GloMTH->get_variable_string((char *)"default_collation_connection"); + assert(previous_default_charset); + assert(previous_default_collation_connection); for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { SQLite3_row *r=*it; const char *value = r->fields[1]; +/* + // COMPLETELY DISABLING THIS because we disable must mysql-default_ variables + // if (!strcasecmp(r->fields[0], "default_character_set_results") || !strcasecmp(r->fields[0], "default_character_set_client") || !strcasecmp(r->fields[0], "default_character_set_database") || !strcasecmp(r->fields[0], "default_character_set_connection") || !strcasecmp(r->fields[0], "default_charset")) { @@ -5464,6 +5471,7 @@ void ProxySQL_Admin::flush_mysql_variables___database_to_runtime(SQLite3DB *db, GloMTH->set_variable(r->fields[0],ci->csname); } } else { +*/ bool rc=GloMTH->set_variable(r->fields[0],value); if (rc==false) { proxy_debug(PROXY_DEBUG_ADMIN, 4, "Impossible to set variable %s with value \"%s\"\n", r->fields[0],value); @@ -5494,32 +5502,80 @@ void ProxySQL_Admin::flush_mysql_variables___database_to_runtime(SQLite3DB *db, variables.mysql_show_processlist_extended = atoi(value); } } - } +// } } - char* connection = GloMTH->get_variable_string((char *)"default_character_set_connection"); - char* collation= GloMTH->get_variable_string((char *)"default_collation_connection"); - const MARIADB_CHARSET_INFO *ci = NULL; char q[1000]; - ci = proxysql_find_charset_name(connection); - if (strcasecmp(ci->name, collation)) { - proxy_warning("Changing default_collation_connection to %s\n", ci->name); - bool rc=GloMTH->set_variable("default_collation_connection",ci->name); - sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-%s\",\"%s\")","default_collation_connection",ci->name); - GloMTH->set_variable("default_collation_connection",ci->name); - if (ci->nr == 45) { - rc=GloMTH->set_variable("default_collation_connection","utf8mb4_general_ci"); - db->execute("INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_collation_connection\",\"utf8mb4_general_ci\")"); - GloMTH->set_variable("default_collation_connection","utf8mb4_general_ci"); + char * default_charset = GloMTH->get_variable_string((char *)"default_charset"); + char * default_collation_connection = GloMTH->get_variable_string((char *)"default_collation_connection"); + assert(default_charset); + assert(default_collation_connection); + MARIADB_CHARSET_INFO * ci = NULL; + ci = proxysql_find_charset_name(default_charset); + if (ci == NULL) { + // invalid charset + proxy_error("Found an incorrect value for mysql-default_charset: %s\n", default_charset); + // let's try to get a charset from collation connection + ci = proxysql_find_charset_collate(default_collation_connection); + if (ci == NULL) { + proxy_error("Found an incorrect value for mysql-default_collation_connection: %s\n", default_collation_connection); + const char *p = mysql_tracked_variables[SQL_CHARACTER_SET].default_value; + ci = proxysql_find_charset_name(p); + assert(ci); + proxy_info("Resetting mysql-default_charset to hardcoded default value: %s\n", ci->csname); + sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_charset\",\"%s\")", ci->csname); + db->execute(q); + GloMTH->set_variable((char *)"default_charset",ci->csname); + proxy_info("Resetting mysql-default_collation_connection to hardcoded default value: %s\n", ci->name); + sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_collation_connection\",\"%s\")", ci->name); + db->execute(q); + GloMTH->set_variable((char *)"default_collation_connection",ci->name); + } else { + proxy_info("Changing mysql-default_charset to %s using configured mysql-default_collation_connection %s\n", ci->csname, ci->name); + sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_charset\",\"%s\")", ci->csname); + db->execute(q); + GloMTH->set_variable((char *)"default_charset",ci->csname); } } else { - GloMTH->set_variable("default_collation_connection",ci->name); - sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-%s\",\"%s\")","default_collation_connection",ci->name); - db->execute(q); + MARIADB_CHARSET_INFO * cic = NULL; + cic = proxysql_find_charset_collate(default_collation_connection); + if (cic == NULL) { + proxy_error("Found an incorrect value for mysql-default_collation_connection: %s\n", default_collation_connection); + proxy_info("Changing mysql-default_collation_connection to %s using configured mysql-default_charset: %s\n", ci->name, ci->csname); + sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_collation_connection\",\"%s\")", ci->name); + db->execute(q); + GloMTH->set_variable((char *)"default_collation_connection",ci->name); + } else { + if (strcmp(cic->csname,ci->csname)==0) { + // mysql-default_collation_connection and mysql-default_charset are compatible + } else { + proxy_error("Found incompatible values for mysql-default_charset (%s) and mysql-default_collation_connection (%s)\n", default_charset, default_collation_connection); + bool use_collation = true; + if (strcmp(default_charset, previous_default_charset)) { // charset changed + if (strcmp(default_collation_connection, previous_default_collation_connection)==0) { // collation didn't change + // the user has changed the charset but not the collation + // we use charset as source of truth + use_collation = false; + } + } + if (use_collation) { + proxy_info("Changing mysql-default_charset to %s using configured mysql-default_collation_connection %s\n", cic->csname, cic->name); + sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_charset\",\"%s\")", cic->csname); + db->execute(q); + GloMTH->set_variable("default_charset",cic->csname); + } else { + proxy_info("Changing mysql-default_collation_connection to %s using configured mysql-default_charset: %s\n", ci->name, ci->csname); + sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-default_collation_connection\",\"%s\")", ci->name); + db->execute(q); + GloMTH->set_variable((char *)"default_collation_connection",ci->name); + } + } + } } - free(connection); - free(collation); - + free(default_charset); + free(default_collation_connection); + free(previous_default_charset); + free(previous_default_collation_connection); GloMTH->commit(); GloMTH->wrunlock(); } @@ -9955,6 +10011,7 @@ void ProxySQL_Admin::load_mysql_servers_to_runtime() { } // commit all the changes MyHGM->commit(); + GloAdmin->save_mysql_servers_runtime_to_database(true); // clean up if (resultset) delete resultset; diff --git a/lib/Query_Cache.cpp b/lib/Query_Cache.cpp index a4f7d8a892..b807ca8a23 100644 --- a/lib/Query_Cache.cpp +++ b/lib/Query_Cache.cpp @@ -294,7 +294,7 @@ void KV_BtreeArray::empty() { }; uint64_t Query_Cache::get_data_size_total() { - int r=0; + uint64_t r=0; int i; for (i=0; iget_data_size(); diff --git a/lib/Query_Processor.cpp b/lib/Query_Processor.cpp index 4d8eee4662..f47d9e4e8d 100644 --- a/lib/Query_Processor.cpp +++ b/lib/Query_Processor.cpp @@ -480,12 +480,14 @@ Query_Processor::Query_Processor() { commands_counters_desc[MYSQL_COM_QUERY_OPTIMIZE]=(char *)"OPTIMIZE"; commands_counters_desc[MYSQL_COM_QUERY_PREPARE]=(char *)"PREPARE"; commands_counters_desc[MYSQL_COM_QUERY_PURGE]=(char *)"PURGE"; + commands_counters_desc[MYSQL_COM_QUERY_RELEASE_SAVEPOINT]=(char *)"RELEASE_SAVEPOINT"; commands_counters_desc[MYSQL_COM_QUERY_RENAME_TABLE]=(char *)"RENAME_TABLE"; commands_counters_desc[MYSQL_COM_QUERY_RESET_MASTER]=(char *)"RESET_MASTER"; commands_counters_desc[MYSQL_COM_QUERY_RESET_SLAVE]=(char *)"RESET_SLAVE"; commands_counters_desc[MYSQL_COM_QUERY_REPLACE]=(char *)"REPLACE"; commands_counters_desc[MYSQL_COM_QUERY_REVOKE]=(char *)"REVOKE"; commands_counters_desc[MYSQL_COM_QUERY_ROLLBACK]=(char *)"ROLLBACK"; + commands_counters_desc[MYSQL_COM_QUERY_ROLLBACK_SAVEPOINT]=(char *)"ROLLBACK_SAVEPOINT"; commands_counters_desc[MYSQL_COM_QUERY_SAVEPOINT]=(char *)"SAVEPOINT"; commands_counters_desc[MYSQL_COM_QUERY_SELECT]=(char *)"SELECT"; commands_counters_desc[MYSQL_COM_QUERY_SELECT_FOR_UPDATE]=(char *)"SELECT_FOR_UPDATE"; @@ -2262,6 +2264,14 @@ enum MYSQL_COM_QUERY_command Query_Processor::__query_parser_command_type(SQP_pa break; case 'r': case 'R': + if (!strcasecmp("RELEASE",token)) { // RELEASE + token=(char *)tokenize(&tok); + if (token==NULL) break; + if (!strcasecmp("SAVEPOINT",token)) { + ret=MYSQL_COM_QUERY_RELEASE_SAVEPOINT; + break; + } + } if (!strcasecmp("RENAME",token)) { // RENAME token=(char *)tokenize(&tok); if (token==NULL) break; @@ -2292,7 +2302,20 @@ enum MYSQL_COM_QUERY_command Query_Processor::__query_parser_command_type(SQP_pa break; } if (!strcasecmp("ROLLBACK",token)) { // ROLLBACK - ret=MYSQL_COM_QUERY_ROLLBACK; + token=(char *)tokenize(&tok); + if (token==NULL) { + ret=MYSQL_COM_QUERY_ROLLBACK; + break; + } else { + if (!strcasecmp("TO",token)) { + token=(char *)tokenize(&tok); + if (token==NULL) break; + if (!strcasecmp("SAVEPOINT",token)) { + ret=MYSQL_COM_QUERY_ROLLBACK_SAVEPOINT; + break; + } + } + } break; } break; diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index bdb917dd67..7caa5fe823 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -9,6 +9,8 @@ #include "query_processor.h" #include "MySQL_Variables.h" +#include + extern const MARIADB_CHARSET_INFO * proxysql_find_charset_nr(unsigned int nr); MARIADB_CHARSET_INFO * proxysql_find_charset_name(const char *name); @@ -77,7 +79,10 @@ void Variable::fill_client_internal_session(json &j, int idx) { } else if (idx == SQL_CHARACTER_SET_CONNECTION) { const MARIADB_CHARSET_INFO *ci = NULL; - ci = proxysql_find_charset_nr(atoi(value)); + if (!value) + ci = proxysql_find_charset_collate(mysql_tracked_variables[idx].default_value); + else + ci = proxysql_find_charset_nr(atoi(value)); j["conn"][mysql_tracked_variables[idx].internal_variable_name] = (ci && ci->csname)?ci->csname:""; } else if (idx == SQL_COLLATION_CONNECTION) { const MARIADB_CHARSET_INFO *ci = NULL; @@ -387,6 +392,7 @@ MySQL_Connection::~MySQL_Connection() { if (variables[i].value) { free(variables[i].value); variables[i].value = NULL; + var_hash[i] = 0; } } @@ -508,6 +514,14 @@ void MySQL_Connection::set_status_user_variable(bool v) { } } +void MySQL_Connection::set_status_has_savepoint(bool v) { + if (v) { + status_flags |= STATUS_MYSQL_CONNECTION_HAS_SAVEPOINT; + } else { + status_flags &= ~STATUS_MYSQL_CONNECTION_HAS_SAVEPOINT; + } +} + void MySQL_Connection::set_status_prepared_statement(bool v) { if (v) { status_flags |= STATUS_MYSQL_CONNECTION_PREPARED_STATEMENT; @@ -547,6 +561,10 @@ bool MySQL_Connection::get_status_user_variable() { return status_flags & STATUS_MYSQL_CONNECTION_USER_VARIABLE; } +bool MySQL_Connection::get_status_has_savepoint() { + return status_flags & STATUS_MYSQL_CONNECTION_HAS_SAVEPOINT; +} + bool MySQL_Connection::get_status_get_lock() { return status_flags & STATUS_MYSQL_CONNECTION_GET_LOCK; } @@ -1941,6 +1959,12 @@ bool MySQL_Connection::IsActiveTransaction() { ret = true; } } + if (ret == false) { + if (get_status_has_savepoint()) { + // there are savepoints + ret = true; + } + } } return ret; } @@ -1973,7 +1997,7 @@ bool MySQL_Connection::MultiplexDisabled() { // status_flags stores information about the status of the connection // can be used to determine if multiplexing can be enabled or not bool ret=false; - if (status_flags & (STATUS_MYSQL_CONNECTION_TRANSACTION|STATUS_MYSQL_CONNECTION_USER_VARIABLE|STATUS_MYSQL_CONNECTION_PREPARED_STATEMENT|STATUS_MYSQL_CONNECTION_LOCK_TABLES|STATUS_MYSQL_CONNECTION_TEMPORARY_TABLE|STATUS_MYSQL_CONNECTION_GET_LOCK|STATUS_MYSQL_CONNECTION_NO_MULTIPLEX|STATUS_MYSQL_CONNECTION_SQL_LOG_BIN0|STATUS_MYSQL_CONNECTION_FOUND_ROWS|STATUS_MYSQL_CONNECTION_NO_BACKSLASH_ESCAPES) ) { + if (status_flags & (STATUS_MYSQL_CONNECTION_TRANSACTION|STATUS_MYSQL_CONNECTION_USER_VARIABLE|STATUS_MYSQL_CONNECTION_PREPARED_STATEMENT|STATUS_MYSQL_CONNECTION_LOCK_TABLES|STATUS_MYSQL_CONNECTION_TEMPORARY_TABLE|STATUS_MYSQL_CONNECTION_GET_LOCK|STATUS_MYSQL_CONNECTION_NO_MULTIPLEX|STATUS_MYSQL_CONNECTION_SQL_LOG_BIN0|STATUS_MYSQL_CONNECTION_FOUND_ROWS|STATUS_MYSQL_CONNECTION_NO_BACKSLASH_ESCAPES|STATUS_MYSQL_CONNECTION_HAS_SAVEPOINT) ) { ret=true; } if (auto_increment_delay_token) return true; @@ -2158,6 +2182,35 @@ void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { set_status_found_rows(true); } } + if (get_status_has_savepoint()==false) { + if (mysql) { + if ( + (mysql->server_status & SERVER_STATUS_IN_TRANS) + || + ((mysql->server_status & SERVER_STATUS_AUTOCOMMIT) == 0) + ) { + if (!strncasecmp(query_digest_text,"SAVEPOINT ", strlen("SAVEPOINT "))) { + set_status_has_savepoint(true); + } + } + } + } else { + if ( + ( + // make sure we don't have a transaction running + // checking just for COMMIT and ROLLBACK is not enough, because `SET autocommit=1` can commit too + (mysql->server_status & SERVER_STATUS_AUTOCOMMIT) + && + ( (mysql->server_status & SERVER_STATUS_IN_TRANS) == 0 ) + ) + || + !strcasecmp(query_digest_text,"COMMIT") + || + !strcasecmp(query_digest_text,"ROLLBACK") + ) { + set_status_has_savepoint(false); + } + } } void MySQL_Connection::optimize() { @@ -2264,6 +2317,7 @@ void MySQL_Connection::reset() { if (variables[i].value) { free(variables[i].value); variables[i].value = NULL; + var_hash[i] = 0; } } diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index 7b26faf62f..f8f1a55314 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -880,6 +880,7 @@ int MySQL_Data_Stream::buffer2array() { proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Session=%p . Reading the header of a new compressed packet\n", sess); memcpy(&queueIN.hdr,queue_r_ptr(queueIN), sizeof(mysql_hdr)); queue_r(queueIN,sizeof(mysql_hdr)); + pkt_sid=queueIN.hdr.pkt_id; queueIN.pkt.size=queueIN.hdr.pkt_length+sizeof(mysql_hdr)+3; queueIN.pkt.ptr=l_alloc(queueIN.pkt.size); memcpy(queueIN.pkt.ptr, &queueIN.hdr, sizeof(mysql_hdr)); // immediately copy the header into the packet diff --git a/lib/set_parser.cpp b/lib/set_parser.cpp index f700a98ad8..335217c0dc 100644 --- a/lib/set_parser.cpp +++ b/lib/set_parser.cpp @@ -40,7 +40,9 @@ std::map> SetParser::parse1() { #define VAR_VALUE_P1 "(((?:CONCAT\\()*(?:((?: )*REPLACE|IFNULL|CONCAT)\\()+(?: )*(?:NULL|@OLD_SQL_MODE|@@sql_mode),(?:(?:'|\\w|,| |\"|\\))+(?:\\))*)|(?:[@\\w/\\d:\\+\\-]|,)+|(?:)))" const string pattern="(?:" NAMES SPACES QUOTES NAME_VALUE QUOTES "(?: +COLLATE +" QUOTES NAME_VALUE QUOTES "|)" "|" SESSION_P1 VAR_P1 SPACES "(?:|:)=" SPACES QUOTES VAR_VALUE_P1 QUOTES ") *,? *"; +VALGRIND_DISABLE_ERROR_REPORTING; re2::RE2 re(pattern, *opt2); +VALGRIND_ENABLE_ERROR_REPORTING; string var; string value1, value2, value3, value4, value5; re2::StringPiece input(query); diff --git a/src/Makefile b/src/Makefile index 29cbe816aa..1537d641fd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,9 @@ - - +ifndef GIT_VERSION GIT_VERSION := $(shell git describe --long) +ifndef GIT_VERSION +$(error GIT_VERSION is not set) +endif +endif DEPS_PATH=../deps diff --git a/src/main.cpp b/src/main.cpp index 6bc9c778af..f5001ff905 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -935,7 +935,6 @@ void ProxySQL_Main_init_main_modules() { GloQPro=NULL; GloMTH=NULL; GloMyAuth=NULL; - GloMyLdapAuth = NULL; #ifdef PROXYSQLCLICKHOUSE GloClickHouseAuth=NULL; #endif /* PROXYSQLCLICKHOUSE */ @@ -1212,6 +1211,7 @@ void ProxySQL_Main_init() { static void LoadPlugins() { + GloMyLdapAuth = NULL; if (GloVars.web_interface_plugin) { dlerror(); char * dlsym_error = NULL; @@ -1358,6 +1358,7 @@ void ProxySQL_Main_init_phase3___start_all() { do { /* nothing */ } while (load_ != 1); load_ = 0; + __sync_fetch_and_add(&GloMTH->status_variables.threads_initialized, 1); { cpu_timer t; @@ -1754,6 +1755,7 @@ int main(int argc, const char * argv[]) { } else { GloAdmin->flush_error_log(); + GloVars.install_signal_handler(); } __start_label: diff --git a/systemd/system/proxysql-initial.service b/systemd/system/proxysql-initial.service new file mode 100644 index 0000000000..55f3445119 --- /dev/null +++ b/systemd/system/proxysql-initial.service @@ -0,0 +1,13 @@ +[Unit] +Description=High Performance Advanced Proxy for MySQL, this service will reset the database and start ProxySQL +After=network.target + +[Service] +Type=oneshot +ExecStartPre=/bin/systemctl set-environment PROXYSQL_OPTS="--initial" +ExecStart=/bin/systemctl start proxysql +ExecStartPost=/bin/systemctl unset-environment PROXYSQL_OPTS +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/systemd/system/proxysql.service b/systemd/system/proxysql.service index 2a13307939..6506a57b1c 100644 --- a/systemd/system/proxysql.service +++ b/systemd/system/proxysql.service @@ -8,7 +8,7 @@ RuntimeDirectory=proxysql #PermissionsStartOnly=true #ExecStartPre=/usr/bin/mkdir -p /var/run/proxysql /var/run/proxysql #ExecStartPre=/usr/bin/chown -R proxysql: /var/run/proxysql/ -ExecStart=/usr/bin/proxysql -c /etc/proxysql.cnf +ExecStart=/usr/bin/proxysql --idle-threads -c /etc/proxysql.cnf $PROXYSQL_OPTS PIDFile=/var/lib/proxysql/proxysql.pid #StandardError=null # all output is in stderr SyslogIdentifier=proxysql diff --git a/test/tap/Makefile b/test/tap/Makefile index 36ff643bec..ebe60a1fc1 100644 --- a/test/tap/Makefile +++ b/test/tap/Makefile @@ -1,4 +1,3 @@ - .PHONY: all all: tap tests @@ -7,12 +6,12 @@ clean: cd tap && ${MAKE} clean cd tests && ${MAKE} clean -OPT=-O2 +debug: tap tests .PHONY: tap tap: - cd tap && OPTZ="${O0} -ggdb " CC=${CC} CXX=${CXX} ${MAKE} + cd tap && CC=${CC} CXX=${CXX} ${MAKE} $(MAKECMDGOALS) tests: tap - cd tests && OPTZ="${O0} -ggdb " CC=${CC} CXX=${CXX} ${MAKE} + cd tests && CC=${CC} CXX=${CXX} ${MAKE} $(MAKECMDGOALS) diff --git a/test/tap/constants b/test/tap/constants new file mode 100644 index 0000000000..cc385c79fe --- /dev/null +++ b/test/tap/constants @@ -0,0 +1,13 @@ +# This file should be modified locally according to proxysql configuration. +# All TAP tests depend on these environment variables. +# +# Usage: source this file before running TAP tests +# $ . constants +# or +# $ source constants +# +export TAP_HOST=127.0.0.1 +export TAP_PORT=6033 +export TAP_USERNAME=root +export TAP_PASSWORD=REDACTED +export TAP_WORKDIR=./tests/ diff --git a/test/tap/tap/Makefile b/test/tap/tap/Makefile index e783286087..9be38ee36b 100644 --- a/test/tap/tap/Makefile +++ b/test/tap/tap/Makefile @@ -14,6 +14,9 @@ clean: OPT=-O2 +debug: OPT = -O0 -DDEBUG -ggdb +debug: libtap.a + libtap.a: tap.cpp tap.h command_line.cpp command_line.h utils.cpp utils.h g++ -c tap.cpp command_line.cpp utils.cpp -std=c++11 -I$(JSON_IDIR) -I$(MARIADB_IDIR) $(OPT) ar rcs libtap.a tap.o command_line.o utils.o diff --git a/test/tap/tap/command_line.cpp b/test/tap/tap/command_line.cpp index 50c1ef507c..128ebfc1db 100644 --- a/test/tap/tap/command_line.cpp +++ b/test/tap/tap/command_line.cpp @@ -13,7 +13,7 @@ using nlohmann::json; CommandLine::CommandLine() : - host(NULL), username(NULL), password(NULL), admin_username(NULL), admin_password(NULL) {} + host(NULL), username(NULL), password(NULL), admin_username(NULL), admin_password(NULL), workdir(NULL) {} CommandLine::~CommandLine() { if (host) @@ -26,11 +26,13 @@ CommandLine::~CommandLine() { free(admin_username); if (admin_password) free(admin_password); + if (workdir) + free(workdir); } int CommandLine::parse(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "ncsu:p:h:P:")) != -1) { + while ((opt = getopt(argc, argv, "ncsu:p:h:P:W:")) != -1) { switch (opt) { case 'c': checksum = true; @@ -59,11 +61,14 @@ int CommandLine::parse(int argc, char** argv) { case 'U': admin_username = strdup(optarg); break; + case 'W': + workdir = strdup(optarg); + break; case 'S': admin_password = strdup(optarg); break; default: /* '?' */ - fprintf(stderr, "Usage: %s -u username -p password -h host [ -P port ] [ -A port ] [ -U admin_username ] [ -S admin_password ] [ -c ] [ -s ] [ -n ]\n", argv[0]); + fprintf(stderr, "Usage: %s -u username -p password -h host [ -P port ] [ -A port ] [ -U admin_username ] [ -S admin_password ] [ -W workdir] [ -c ] [ -s ] [ -n ]\n", argv[0]); return 0; } } @@ -101,6 +106,7 @@ int CommandLine::read(const std::string& file) { admin_port = 6032; username = strdup("root"); password = strdup("a"); + workdir = strdup("./tests/"); return 0; } @@ -152,5 +158,9 @@ int CommandLine::getEnv() { if(env_port>0 && env_port<65536) admin_port=env_port; + value=getenv("TAP_WORKDIR"); + if(!value) return -1; + workdir = strdup(value); + return 0; } diff --git a/test/tap/tap/command_line.h b/test/tap/tap/command_line.h index a8f93c029c..c4857c2992 100644 --- a/test/tap/tap/command_line.h +++ b/test/tap/tap/command_line.h @@ -17,6 +17,7 @@ class CommandLine { char* admin_password; int port; int admin_port; + char* workdir; int read(const std::string& file); int getEnv(); diff --git a/test/tap/tap/utils.cpp b/test/tap/tap/utils.cpp index e31c47e5f7..33a0cbde2f 100644 --- a/test/tap/tap/utils.cpp +++ b/test/tap/tap/utils.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include "tap.h" #include "utils.h" @@ -102,3 +104,96 @@ int get_server_version(MYSQL *mysql, std::string& version) { return 0; } + +// Pipes definition +constexpr uint8_t NUM_PIPES = 3; +constexpr uint8_t PARENT_WRITE_PIPE = 0; +constexpr uint8_t PARENT_READ_PIPE = 1; +constexpr uint8_t PARENT_ERR_PIPE = 2; +int pipes[NUM_PIPES][2]; +// Pipe selection +constexpr uint8_t READ_FD = 0; +constexpr uint8_t WRITE_FD = 1; +// Parent pipes +const auto& PARENT_READ_FD = pipes[PARENT_READ_PIPE][READ_FD]; +const auto& PARENT_READ_ERR = pipes[PARENT_ERR_PIPE][READ_FD]; +const auto& PARENT_WRITE_FD = pipes[PARENT_WRITE_PIPE][WRITE_FD]; +// Child pipes +const auto& CHILD_READ_FD = pipes[PARENT_WRITE_PIPE][READ_FD]; +const auto& CHILD_WRITE_FD = pipes[PARENT_READ_PIPE][WRITE_FD]; +const auto& CHILD_WRITE_ERR = pipes[PARENT_ERR_PIPE][WRITE_FD]; + +int execvp(const std::string& cmd, const std::vector& argv, std::string& result) { + int err = 0; + std::string result_ = ""; + std::vector _argv = argv; + + // Append null to end of _argv for extra safety + _argv.push_back(nullptr); + + int outfd[2]; + int infd[2]; + + // Pipes for parent to write and read + pipe(pipes[PARENT_READ_PIPE]); + pipe(pipes[PARENT_WRITE_PIPE]); + pipe(pipes[PARENT_ERR_PIPE]); + + pid_t child_pid = fork(); + if(child_pid == 0) { + // Copy the pipe descriptors + dup2(CHILD_READ_FD, STDIN_FILENO); + dup2(CHILD_WRITE_FD, STDOUT_FILENO); + dup2(CHILD_WRITE_ERR, STDERR_FILENO); + + // Close no longer needed pipes + close(CHILD_READ_FD); + close(CHILD_WRITE_FD); + close(CHILD_WRITE_ERR); + + close(PARENT_READ_FD); + close(PARENT_READ_ERR); + close(PARENT_WRITE_FD); + + char** args = const_cast(_argv.data()); + err = execvp(cmd.c_str(), args); + + if (err) { + exit(errno); + } else { + exit(0); + } + } else { + char buffer[128]; + int count; + + // Close no longer needed pipes + close(CHILD_READ_FD); + close(CHILD_WRITE_FD); + close(CHILD_WRITE_ERR); + + if (err == 0) { + // Read from child’s stdout + count = read(PARENT_READ_FD, buffer, sizeof(buffer)); + while (count > 0) { + buffer[count] = 0; + result_ += buffer; + count = read(PARENT_READ_FD, buffer, sizeof(buffer)); + } + } else { + // Read from child’s stderr + count = read(PARENT_READ_ERR, buffer, sizeof(buffer)); + while (count > 0) { + buffer[count] = 0; + result_ += buffer; + count = read(PARENT_READ_ERR, buffer, sizeof(buffer)); + } + } + + waitpid(child_pid, &err, 0); + } + + result = result_; + + return err; +} diff --git a/test/tap/tap/utils.h b/test/tap/tap/utils.h index ac740ec45f..b79effb9df 100644 --- a/test/tap/tap/utils.h +++ b/test/tap/tap/utils.h @@ -3,6 +3,7 @@ #include #include +#include #define MYSQL_QUERY(mysql, query) \ do { \ @@ -27,5 +28,15 @@ int select_config_file(MYSQL* mysql, std::string& resultset); } #endif +/** + * @brief Execute the given comand, and stores it's output. + * + * @param file File to be executed. + * @param argv Arguments to be given to the executable. + * @param result The output of the file execution. If the execution succeed it contains `stdout` output, + * in case of failure `stderr` contents are returned. + * @return int Zero in case of success, or the errno returned by `execvp` in case of failure. + */ +int execvp(const std::string& file, const std::vector& argv, std::string& result); #endif // #define UTILS_H diff --git a/test/tap/tests/Makefile b/test/tap/tests/Makefile index 43124fcde0..2ad644bc4f 100644 --- a/test/tap/tests/Makefile +++ b/test/tap/tests/Makefile @@ -68,55 +68,37 @@ INCLUDEDIRS=-I../tap -I$(RE2_PATH) -I$(IDIR) -I$(JEMALLOC_IDIR) -I$(SQLITE3_DIR) LDIRS=-L$(TAP_LIBDIR) -L$(LDIR) -L$(JEMALLOC_LDIR) $(LIBCONFIG_LDIR) -L$(RE2_PATH)/obj -L$(MARIADB_LDIR) -L$(DAEMONPATH_LDIR) -L$(PCRE_LDIR) -L$(MICROHTTPD_LDIR) -L$(LIBHTTPSERVER_LDIR) -L$(LIBINJECTION_LDIR) -L$(CURL_LDIR) -L$(EV_LDIR) -L$(SSL_LDIR) MYLIBS=-Wl,--export-dynamic -Wl,-Bstatic -lconfig -lproxysql -ldaemon -ljemalloc -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lcurl -lssl -lcrypto -lev -Wl,-Bdynamic -lgnutls -lpthread -lm -lz -lrt $(EXTRALINK) -STATIC_LIBS=$(MARIADB_LDIR)/libmariadbclient.a +STATIC_LIBS= $(SSL_LDIR)/libssl.a $(SSL_LDIR)/libcrypto.a .PHONY: all all: tests .PHONY: clean clean: - rm -f basic-t set_character_set-t charset_unsigned_int-t select_config_file-t sqlite3-t galera_1_timeout_count galera_2_timeout_no_count aurora test_set_character_results-t test_ps_large_result-t set_testing-t test_firewall-t || true + rm -f *-t galera_1_timeout_count galera_2_timeout_no_count aurora || true -OPT=-O2 +OPT=-O2 -Wl,--no-as-needed +debug: OPT=-O0 -DDEBUG -ggdb -Wl,--no-as-needed +debug: tests -SRC=basic-t.cpp set_character_set-t.cpp charset_unsigned_int-t.cpp select_config_file-t.cpp sqlite3-t.cpp galera_1_timeout_count.cpp galera_2_timeout_no_count.cpp aurora.cpp test_set_character_results-t.cpp test_ps_large_result-t.cpp set_testing-t.cpp test_firewall-t.cpp - -tests: basic-t set_character_set-t charset_unsigned_int-t select_config_file-t sqlite3-t test_set_character_results-t test_ps_large_result-t set_testing-t test_firewall-t +tests: $(patsubst %.cpp,%,$(wildcard *-t.cpp)) testgalera: galera_1_timeout_count galera_2_timeout_no_count testaurora: aurora -sqlite3-t: $(TAP_LIBDIR)/libtap.a sqlite3-t.cpp - g++ sqlite3-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -lpthread -ldl -std=c++11 -ltap -o sqlite3-t - -basic-t: $(TAP_LIBDIR)/libtap.a - g++ basic-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 -ltap -o basic-t - -set_character_set-t: set_character_set-t.cpp $(TAP_LIBDIR)/libtap.a - g++ set_character_set-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 -ltap -ldl $(MYLIBS) -o set_character_set-t - -charset_unsigned_int-t: charset_unsigned_int-t.cpp $(TAP_LIBDIR)/libtap.a - g++ charset_unsigned_int-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 -ltap -ldl $(MYLIBS) -o charset_unsigned_int-t - -select_config_file-t: select_config_file-t.cpp $(TAP_LIBDIR)/libtap.a - g++ select_config_file-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 -ltap -ldl $(MYLIBS) -o select_config_file-t - -test_set_character_results-t: test_set_character_results-t.cpp $(TAP_LIBDIR)/libtap.a - g++ test_set_character_results-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -ltap -lssl -lcrypto -ldl -lpthread -o test_set_character_results-t - -test_ps_large_result-t: test_ps_large_result-t.cpp $(TAP_LIBDIR)/libtap.a - g++ test_ps_large_result-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -ltap -lssl -lcrypto -ldl -lpthread -o test_ps_large_result-t - -test_firewall-t: test_firewall-t.cpp $(TAP_LIBDIR)/libtap.a - g++ test_firewall-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -ltap -lssl -lcrypto -ldl -lpthread -o test_firewall-t +%-t:%-t.cpp $(TAP_LIBDIR)/libtap.a + $(CXX) $^ $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -lpthread -ldl -std=c++11 -ltap $(STATIC_LIBS) -o $@ galera_1_timeout_count: galera_1_timeout_count.cpp $(TAP_LIBDIR)/libtap.a - g++ -DTEST_GALERA -DDEBUG galera_1_timeout_count.cpp ../tap/SQLite3_Server.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) $(MYLIBS) -ltap -ldl -o galera_1_timeout_count -DGITVERSION=\"$(GIT_VERSION)\" + g++ -DTEST_GALERA -DDEBUG galera_1_timeout_count.cpp ../tap/SQLite3_Server.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) $(MYLIBS) -ltap -ldl $(STATIC_LIBS) -o galera_1_timeout_count -DGITVERSION=\"$(GIT_VERSION)\" galera_2_timeout_no_count: galera_2_timeout_no_count.cpp $(TAP_LIBDIR)/libtap.a - g++ -DTEST_GALERA -DDEBUG galera_2_timeout_no_count.cpp ../tap/SQLite3_Server.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) $(MYLIBS) -ltap -ldl -o galera_2_timeout_no_count -DGITVERSION=\"$(GIT_VERSION)\" + g++ -DTEST_GALERA -DDEBUG galera_2_timeout_no_count.cpp ../tap/SQLite3_Server.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) $(MYLIBS) -ltap -ldl $(STATIC_LIBS) -o galera_2_timeout_no_count -DGITVERSION=\"$(GIT_VERSION)\" aurora: aurora.cpp $(TAP_LIBDIR)/libtap.a - g++ -DTEST_AURORA -DDEBUG aurora.cpp ../tap/SQLite3_Server.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) $(MYLIBS) -ltap -ldl -o aurora -DGITVERSION=\"$(GIT_VERSION)\" + g++ -DTEST_AURORA -DDEBUG aurora.cpp ../tap/SQLite3_Server.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) $(MYLIBS) -ltap -ldl $(STATIC_LIBS) -o aurora -DGITVERSION=\"$(GIT_VERSION)\" + +1493_mixed_compression: reg_test_1493-mixed_compression-t.cpp + g++ -DTEST_AURORA -DDEBUG test_mixed_compression-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) $(MYLIBS) -ltap -ldl $(STATIC_LIBS) -o reg_test_1493-mixed_compression-t -DGITVERSION=\"$(GIT_VERSION)\" -set_testing-t: set_testing-t.cpp $(TAP_LIBDIR)/libtap.a - g++ set_testing-t.cpp -Wall $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -ltap -pthread -O0 -ggdb -ldl -lssl -lcrypto -o set_testing-t +2793_compression: reg_test_2793-compression-t.cpp + g++ -DTEST_AURORA -DDEBUG reg_test_2793-compression-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) $(MYLIBS) -ltap -ldl $(STATIC_LIBS) -o reg_test_2793-compression-t -DGITVERSION=\"$(GIT_VERSION)\" diff --git a/test/tap/tests/proxysql_reference_select_config_file.cnf b/test/tap/tests/proxysql_reference_select_config_file.cnf index 71f3bc75b0..8b209102fe 100644 --- a/test/tap/tests/proxysql_reference_select_config_file.cnf +++ b/test/tap/tests/proxysql_reference_select_config_file.cnf @@ -88,13 +88,21 @@ mysql_variables = connect_timeout_server_max="mysql" connection_delay_multiplex_ms="mysql" connection_max_age_ms="mysql" + connection_warming="mysql" connpoll_reset_queue_length="mysql" + default_CHARSET="mysql" + default_action="mysql" + default_character_set_client="mysql" + default_character_set_connection="mysql" + default_character_set_database="mysql" default_character_set_results="mysql" default_charset="mysql" default_collation_connection="mysql" + default_group_concat_max_len="mysql" default_isolation_level="mysql" default_max_join_size="mysql" default_max_latency_ms="mysql" + default_names="mysql" default_net_write_timeout="mysql" default_query_delay="mysql" default_query_timeout="mysql" @@ -102,12 +110,14 @@ mysql_variables = default_schema="mysql" default_session_track_gtids="mysql" default_sql_auto_is_null="mysql" + default_sql_log_bin="mysql" default_sql_mode="mysql" default_sql_safe_updates="mysql" default_sql_select_limit="mysql" default_time_zone="mysql" default_transaction_read="mysql" default_tx_isolation="mysql" + default_wsrep_sync_wait="mysql" enforce_autocommit_on_reads="mysql" eventslog_default_log="mysql" eventslog_filename="mysql" diff --git a/test/tap/tests/reg_test_1493-mixed_compression-t.cpp b/test/tap/tests/reg_test_1493-mixed_compression-t.cpp new file mode 100644 index 0000000000..75edafffba --- /dev/null +++ b/test/tap/tests/reg_test_1493-mixed_compression-t.cpp @@ -0,0 +1,85 @@ +/** + * @file reg_test_1493-mixed_compression-t.cpp + * @brief This test is a regression test for issue #1493. + * @date 2020-05-14 + */ + +#include +#include +#include + +#include +#include + +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +using std::string; + +int main(int argc, char** argv) { + CommandLine cl; + + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return -1; + } + + plan(2); + + MYSQL* proxysql_admin = mysql_init(NULL); + + // Initialize connections + if (!proxysql_admin) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); + return -1; + } + + // Connnect to local proxysql + if (!mysql_real_connect(proxysql_admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); + return -1; + } + + const char* disable_select_query_rules = + "UPDATE mysql_query_rules SET active=0 WHERE match_digest='^SELECT'"; + const char* enable_select_query_rules = + "UPDATE mysql_query_rules SET active=1 WHERE match_digest='^SELECT'"; + const char* update_mysql_query_rules = + "INSERT INTO mysql_query_rules (active, username, match_digest, destination_hostgroup, apply, cache_ttl, comment) " + "VALUES (1,'root','^SELECT.*', 1, 1, 1000000, 'test_mixed_compression_rule')"; + const char* delete_mysql_query_rule = + "DELETE FROM mysql_query_rules WHERE " + "comment='test_mixed_compression_rule'"; + const char* load_mysql_queries_runtime = + "LOAD MYSQL QUERY RULES TO RUNTIME"; + + // Setup config - query_rules + MYSQL_QUERY(proxysql_admin, disable_select_query_rules); + MYSQL_QUERY(proxysql_admin, update_mysql_query_rules); + MYSQL_QUERY(proxysql_admin, load_mysql_queries_runtime); + + // Mixed compressed / uncompressed queries test #1493 + const std::string mysql_client = "mysql"; + const std::string tg_port = std::string("-P") + std::to_string(cl.port); + const std::string name = std::string("-u") + cl.username; + const std::string pass = std::string("-p") + cl.password; + const std::vector n_auth_cargs = { "mysql", name.c_str(), pass.c_str(), "-h", cl.host, tg_port.c_str(), "-C", "-e", "select 1", "--default-auth=mysql_native_password" }; + const std::vector n_auth_args = { "mysql", name.c_str(), pass.c_str(), "-h", cl.host, tg_port.c_str(), "-e", "select 1", "--default-auth=mysql_native_password" }; + + // Query the mysql server in a compressed connection + std::string result = ""; + int query_res = execvp(mysql_client, n_auth_cargs, result); + ok(query_res == 0 && result != "", "Native auth compressed query should be executed correctly."); + + // Now query again using a uncompressed connection + query_res = execvp(mysql_client, n_auth_args, result); + ok(query_res == 0 && result != "", "Native auth uncompressed query should be executed correctly."); + + // Teardown config + MYSQL_QUERY(proxysql_admin, delete_mysql_query_rule); + MYSQL_QUERY(proxysql_admin, enable_select_query_rules); + MYSQL_QUERY(proxysql_admin, load_mysql_queries_runtime); + + return exit_status(); +} diff --git a/test/tap/tests/reg_test_2793-compression-t.cpp b/test/tap/tests/reg_test_2793-compression-t.cpp new file mode 100644 index 0000000000..5f493f0b7d --- /dev/null +++ b/test/tap/tests/reg_test_2793-compression-t.cpp @@ -0,0 +1,41 @@ +/** + * @file reg_test_2793-compression-t.cpp + * @brief This test is a regression test for issue #2793. + * @date 2020-05-14 + */ + +#include +#include +#include +#include +#include + +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +using std::string; + +int main(int argc, char** argv) { + CommandLine cl; + + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return -1; + } + + plan(1); + + const std::string mysql_client = "mysql"; + const std::string name = std::string("-u") + cl.username; + const std::string pass = std::string("-p") + cl.password; + const std::string tg_port = std::string("-P") + std::to_string(cl.port); + const std::vector cargs = { "mysql", name.c_str(), pass.c_str(), "-h", cl.host, tg_port.c_str(), "-C", "-e", "select 1" }; + + // Query the mysql server in a compressed connection + std::string result = ""; + int query_res = execvp(mysql_client, cargs, result); + ok(query_res == 0 && result != "", "Compressed query should be executed correctly."); + + return exit_status(); +} diff --git a/test/tap/tests/savepoint-948-t.cpp b/test/tap/tests/savepoint-948-t.cpp new file mode 100644 index 0000000000..981f5e4200 --- /dev/null +++ b/test/tap/tests/savepoint-948-t.cpp @@ -0,0 +1,401 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tap.h" +#include "utils.h" +#include "command_line.h" + + +unsigned long long monotonic_time() { + struct timespec ts; + //clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); // this is faster, but not precise + clock_gettime(CLOCK_MONOTONIC, &ts); + return (((unsigned long long) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000); +} + +struct cpu_timer +{ + cpu_timer() { + begin = monotonic_time(); + } + ~cpu_timer() + { + unsigned long long end = monotonic_time(); + std::cerr << double( end - begin ) / 1000000 << " secs.\n" ; + begin=end-begin; + }; + unsigned long long begin; +}; + +unsigned int num_threads=1; +int count=0; +char *username=NULL; +char *password=NULL; +char *host=(char *)"localhost"; +int port=3306; +char *schema=(char *)"information_schema"; +int silent = 0; +int sysbench = 0; +int local=0; +int transactions=0; +int uniquequeries=0; +int histograms=-1; + +unsigned int g_passed=0; +unsigned int g_failed=0; + +std::atomic cnt_transactions; +std::atomic cnt_SELECT_outside_transactions; + +unsigned int status_connections = 0; +unsigned int connect_phase_completed = 0; +unsigned int query_phase_completed = 0; + +__thread int g_seed; +std::mutex mtx_; + +inline int fastrand() { + g_seed = (214013*g_seed+2531011); + return (g_seed>>16)&0x7FFF; +} + +void gen_random(char *s, const int len) { + static const char alphanum[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + + for (int i = 0; i < len; ++i) { + s[i] = alphanum[fastrand() % (sizeof(alphanum) - 1)]; + } + + s[len] = 0; +} + +void * my_conn_thread(void *arg) { + g_seed = time(NULL) ^ getpid() ^ pthread_self(); + unsigned int select_OK=0; + unsigned int select_ERR=0; + int i, j; + MYSQL **mysqlconns=(MYSQL **)malloc(sizeof(MYSQL *)*count); + + if (mysqlconns==NULL) { + exit(EXIT_FAILURE); + } + + + for (i=0; i 7) { + q = "RELEASE SAVEPOINT "; + q += buf; + if (mysql_query(mysql, q.c_str())) { + fprintf(stderr,"Error running query: %s. Error: %s\n", q.c_str(), mysql_error(mysql)); + exit(EXIT_FAILURE); + } + sleepDelay = fastrand()%100; + usleep(sleepDelay * 100); + } + } + { + std::string q; + int f = fr%3; + if (f==0) { + q = "COMMIT"; + } else { + q = "ROLLBACK"; +/* + // FIXME: this code is currently commented because of another bug + if (explicit_transaction==false) { + if (f!=1) { + q = "SET AUTOCOMMIT=1"; + } + } +*/ + } + if (mysql_query(mysql, q.c_str())) { + fprintf(stderr,"Error running query: %s. Error: %s\n", q.c_str(), mysql_error(mysql)); + exit(EXIT_FAILURE); + } + cnt_transactions++; + sleepDelay = fastrand()%100; + usleep(sleepDelay * 100); + } +/* + // we do not log every single transaction, too verbose + bool testPassed = true; + { + std::lock_guard lock(mtx_); + ok(testPassed, "mysql connection [%p], thread_id [%lu], transaction completed", mysql, mysql->thread_id); + } +*/ + } + for (i=0; i= cnt_transactions+cnt_SELECT_outside_transactions-10) , "Number of transactions [%d] , Queries outside transaction [%d] , total connections returned [%d]", cnt_transactions.load(std::memory_order_relaxed), cnt_SELECT_outside_transactions.load(std::memory_order_relaxed), MyHGM_myconnpoll_push); + MYSQL_QUERY(mysqladmin, "DELETE FROM mysql_query_rules"); + MYSQL_QUERY(mysqladmin, "INSERT INTO mysql_query_rules SELECT * FROM mysql_query_rules_948"); + MYSQL_QUERY(mysqladmin, "LOAD MYSQL QUERY RULES TO RUNTIME"); + return exit_status(); +} diff --git a/test/tap/tests/select_config_file-t.cpp b/test/tap/tests/select_config_file-t.cpp deleted file mode 100644 index 276780b5a0..0000000000 --- a/test/tap/tests/select_config_file-t.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include "tap.h" -#include "command_line.h" -#include "utils.h" - -int select_config_file(MYSQL* mysql, std::string& resultset) { - if (mysql_query(mysql, "select config file")) { - fprintf(stderr, "File %s, line %d, Error: 2 %s\n", - __FILE__, __LINE__, mysql_error(mysql)); - return exit_status(); - } - - MYSQL_RES *result; - MYSQL_ROW row; - result = mysql_store_result(mysql); - if (result) { - row = mysql_fetch_row(result); - resultset = row[0]; - mysql_free_result(result); - } else { - fprintf(stderr, "error\n"); - } - -} - - -int main(int argc, char** argv) { - CommandLine cl; - - if(cl.getEnv()) - return exit_status(); - - plan(1); - diag("Testing SELECT CONFIG INTO OUTFILE"); - - MYSQL* mysql = mysql_init(NULL); - if (!mysql) - return exit_status(); - - if (!mysql_real_connect(mysql, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { - fprintf(stderr, "File %s, line %d, Error: %s\n", - __FILE__, __LINE__, mysql_error(mysql)); - return exit_status(); - } - - MYSQL_QUERY(mysql, "delete from global_variables"); - MYSQL_QUERY(mysql, "delete from mysql_users"); - MYSQL_QUERY(mysql, "delete from mysql_servers"); - MYSQL_QUERY(mysql, "delete from mysql_query_rules"); - MYSQL_QUERY(mysql, "delete from mysql_replication_hostgroups"); - MYSQL_QUERY(mysql, "delete from mysql_group_replication_hostgroups"); - MYSQL_QUERY(mysql, "delete from mysql_galera_hostgroups"); - MYSQL_QUERY(mysql, "delete from mysql_aws_aurora_hostgroups"); - MYSQL_QUERY(mysql, "delete from scheduler"); - MYSQL_QUERY(mysql, "delete from restapi_routes"); - MYSQL_QUERY(mysql, "delete from proxysql_servers"); - - MYSQL_QUERY(mysql, "insert into proxysql_servers (hostname, port, weight, comment) values ('hostname', 3333, 12, 'comment');"); - MYSQL_QUERY(mysql, "insert into scheduler (id, active, interval_ms, filename, arg1, arg2, arg3, arg4, arg5, comment) values " - " (1,1,1000,'filename','a1','a2','a3','a4','a5','comment');"); - MYSQL_QUERY(mysql, "insert into restapi_routes values " - " (1,1,1000,'GET', 'test','./scripts/script.py','comment');"); - MYSQL_QUERY(mysql, "insert into mysql_aws_aurora_hostgroups (writer_hostgroup, reader_hostgroup, active, aurora_port, " - " domain_name, max_lag_ms, check_interval_ms, check_timeout_ms, writer_is_also_reader, new_reader_weight, " - " add_lag_ms, min_lag_ms, lag_num_checks, comment) " - " values (1,2,1,3,'.domain.net',20,106,107,1,9,10,20,1,'comment');"); - MYSQL_QUERY(mysql, "insert into mysql_galera_hostgroups (writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, " - " active, max_writers, writer_is_also_reader, max_transactions_behind, comment) values (1,2,3,4,1,23,1,1,'comment');"); - MYSQL_QUERY(mysql, "insert into mysql_group_replication_hostgroups (writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, " - " active, max_writers, writer_is_also_reader, max_transactions_behind, comment) values (1,2,3,4,1,23,1,1,'comment');"); - MYSQL_QUERY(mysql, "insert into mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type, comment) " - " values (10,20,'read_only','Master / Slave App 1');"); - MYSQL_QUERY(mysql, "insert into mysql_servers (hostgroup_id, hostname, port, gtid_port, status, weight, compression, max_connections, " - " max_replication_lag, use_ssl, max_latency_ms, comment) values (0,'127.0.0.1',3306,0,'ONLINE',1,0,1000,0,0,0,'comment2');"); - MYSQL_QUERY(mysql, "insert into mysql_query_rules (rule_id, active, username, schemaname, flagIN, client_addr, proxy_addr, proxy_port, digest, " - " match_digest, match_pattern, negate_match_pattern, re_modifiers, flagOUT, replace_pattern, destination_hostgroup, cache_ttl, cache_empty_result, " - " cache_timeout, reconnect, timeout, retries, delay, next_query_flagIN, mirror_flagOUT, mirror_hostgroup, " - " error_msg, OK_msg, sticky_conn, multiplex, gtid_from_hostgroup, log, apply, comment) values " - " (1, 1, 'user', 'schema', 0, '.domain.com', '.proxy.com', 3333, 'ABC1', 'ABC', '^SELECT *', 0, 'CASE', 0, 1, 1, " - " 1, 1, 100, 1, 1, 100, 100, 0, 0, 1, 'Error', 'OK', 0, 0, 0, 0, 0, 'comm1')"); - MYSQL_QUERY(mysql, "insert into mysql_users (username, password, active, use_ssl, default_hostgroup, default_schema, schema_locked, " - " transaction_persistent, fast_forward, backend, frontend, max_connections, comment) values " - " ('user', 'password', 1, 0, 0, 'schema', 1, 0, 1, 1, 1, 10, 'comm1')"); - - MYSQL_QUERY(mysql, "update global_variables set variable_value='admin' where variable_name like 'admin-%'"); - MYSQL_QUERY(mysql, "update global_variables set variable_value='mysql' where variable_name like 'mysql-%'"); - - MYSQL_QUERY(mysql, "load mysql servers to runtime"); - - std::string resultset; - resultset.reserve(100000); - select_config_file(mysql, resultset); - - { - std::ifstream inFile; - inFile.open("./tests/proxysql_reference_select_config_file.cnf"); //open the input file - - std::stringstream strStream; - strStream << inFile.rdbuf(); //read the file - std::string str = strStream.str(); //str holds the content of the file - - ok(!str.compare(resultset), "Files are equal"); -#if 0 - std::ofstream f; - f.open("out.txt", std::ios::out); - f << resultset; - f.close(); -#endif - - } - -#if 0 - std::ofstream out("output.cnf"); - out << resultset; - out.close(); -#endif - - MYSQL_QUERY(mysql, "load mysql variables from disk"); - MYSQL_QUERY(mysql, "load admin variables from disk"); - MYSQL_QUERY(mysql, "load mysql users from disk"); - MYSQL_QUERY(mysql, "load mysql servers from disk"); - MYSQL_QUERY(mysql, "load scheduler from disk"); - MYSQL_QUERY(mysql, "load restapi from disk"); - MYSQL_QUERY(mysql, "load proxysql servers from disk"); - - mysql_close(mysql); - - return exit_status(); -} - diff --git a/test/tap/tests/set_testing-multi-t.cpp b/test/tap/tests/set_testing-multi-t.cpp new file mode 100644 index 0000000000..0ff0ef1aa8 --- /dev/null +++ b/test/tap/tests/set_testing-multi-t.cpp @@ -0,0 +1,659 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "json.hpp" + +#include "tap.h" +#include "utils.h" +#include "command_line.h" + +std::vector split(const std::string& s, char delimiter) +{ + std::vector tokens; + std::string token; + std::istringstream tokenStream(s); + while (std::getline(tokenStream, token, delimiter)) + { + tokens.push_back(token); + } + return tokens; +} + +using nlohmann::json; + +struct TestCase { + std::string command; + json expected_vars; +}; + +std::vector testCases; + +#define MAX_LINE 1024 + +int readTestCases(const std::string& fileName) { + FILE* fp = fopen(fileName.c_str(), "r"); + if (!fp) return 0; + + char buf[MAX_LINE], col1[MAX_LINE], col2[MAX_LINE]; + int n = 0; + for(;;) { + if (fgets(buf, sizeof(buf), fp) == NULL) break; + n = sscanf(buf, " \"%[^\"]\", \"%[^\"]\"", col1, col2); + if (n == 0) break; + + char *p = col2; + while(*p++) if(*p == '\'') *p = '\"'; + + json vars = json::parse(col2); + testCases.push_back({col1, vars}); + } + + fclose(fp); + return 1; +} + +unsigned long long monotonic_time() { + struct timespec ts; + //clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); // this is faster, but not precise + clock_gettime(CLOCK_MONOTONIC, &ts); + return (((unsigned long long) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000); +} + +struct cpu_timer +{ + cpu_timer() { + begin = monotonic_time(); + } + ~cpu_timer() + { + unsigned long long end = monotonic_time(); + std::cerr << double( end - begin ) / 1000000 << " secs.\n" ; + begin=end-begin; + }; + unsigned long long begin; +}; + +std::string bn = ""; +int queries_per_connections=1; +unsigned int num_threads=1; +int count=0; +char *username=NULL; +char *password=NULL; +char *host=(char *)"localhost"; +int port=3306; +int multiport=1; +char *schema=(char *)"information_schema"; +int silent = 0; +int sysbench = 0; +int local=0; +int queries=0; +int uniquequeries=0; +int histograms=-1; +int multi_users=0; + +bool is_mariadb = false; +bool is_cluster = false; +unsigned int g_connect_OK=0; +unsigned int g_connect_ERR=0; +unsigned int g_select_OK=0; +unsigned int g_select_ERR=0; + +unsigned int g_passed=0; +unsigned int g_failed=0; + +unsigned int status_connections = 0; +unsigned int connect_phase_completed = 0; +unsigned int query_phase_completed = 0; + +__thread int g_seed; +std::mutex mtx_; + +inline int fastrand() { + g_seed = (214013*g_seed+2531011); + return (g_seed>>16)&0x7FFF; +} + +void parseResultJsonColumn(MYSQL_RES *result, json& j) { + if(!result) return; + MYSQL_ROW row; + + while ((row = mysql_fetch_row(result))) + j = json::parse(row[0]); +} + +void parseResult(MYSQL_RES *result, json& j) { + if(!result) return; + MYSQL_ROW row; + + while ((row = mysql_fetch_row(result))) { + j[row[0]] = row[1]; + } +} + +void dumpResult(MYSQL_RES *result) { + if(!result) return; + MYSQL_ROW row; + + int num_fields = mysql_num_fields(result); + + while ((row = mysql_fetch_row(result))) + { + for(int i = 0; i < num_fields; i++) + { + printf("%s ", row[i] ? row[i] : "NULL"); + } + printf("\n"); + } +} + +void queryVariables(MYSQL *mysql, json& j) { + std::stringstream query; + if (is_mariadb) { + query << "SELECT /* mysql " << mysql << " */ lower(variable_name), variable_value FROM information_schema.session_variables WHERE variable_name IN " + " ('hostname', 'sql_log_bin', 'sql_mode', 'init_connect', 'time_zone', 'sql_auto_is_null', " + " 'sql_safe_updates', 'max_join_size', 'net_write_timeout', 'sql_select_limit', " + " 'sql_select_limit', 'character_set_results', 'tx_isolation', 'tx_read_only', " + " 'sql_auto_is_null', 'collation_connection', 'character_set_connection', 'character_set_client', 'character_set_database', 'group_concat_max_len');"; + } + if (is_cluster) { + query << "SELECT /* mysql " << mysql << " */ * FROM performance_schema.session_variables WHERE variable_name IN " + " ('hostname', 'sql_log_bin', 'sql_mode', 'init_connect', 'time_zone', 'sql_auto_is_null', " + " 'sql_safe_updates', 'session_track_gtids', 'max_join_size', 'net_write_timeout', 'sql_select_limit', " + " 'sql_select_limit', 'character_set_results', 'transaction_isolation', 'transaction_read_only', " + " 'sql_auto_is_null', 'collation_connection', 'character_set_connection', 'character_set_client', 'character_set_database', 'wsrep_sync_wait', 'group_concat_max_len');"; + } + if (!is_mariadb && !is_cluster) { + query << "SELECT /* mysql " << mysql << " */ * FROM performance_schema.session_variables WHERE variable_name IN " + " ('hostname', 'sql_log_bin', 'sql_mode', 'init_connect', 'time_zone', 'sql_auto_is_null', " + " 'sql_safe_updates', 'session_track_gtids', 'max_join_size', 'net_write_timeout', 'sql_select_limit', " + " 'sql_select_limit', 'character_set_results', 'transaction_isolation', 'transaction_read_only', " + " 'sql_auto_is_null', 'collation_connection', 'character_set_connection', 'character_set_client', 'character_set_database', 'group_concat_max_len');"; + } + //fprintf(stderr, "TRACE : QUERY 3 : variables %s\n", query.str().c_str()); + if (mysql_query(mysql, query.str().c_str())) { + if (silent==0) { + fprintf(stderr,"ERROR while running -- \"%s\" : (%d) %s\n", query.str().c_str(), mysql_errno(mysql), mysql_error(mysql)); + } + } else { + MYSQL_RES *result = mysql_store_result(mysql); + parseResult(result, j); + + mysql_free_result(result); + __sync_fetch_and_add(&g_select_OK,1); + } +} + +void queryInternalStatus(MYSQL *mysql, json& j) { + char *query = (char*)"PROXYSQL INTERNAL SESSION"; + + //fprintf(stderr, "TRACE : QUERY 4 : variables %s\n", query); + if (mysql_query(mysql, query)) { + if (silent==0) { + fprintf(stderr,"ERROR while running -- \"%s\" : (%d) %s\n", query, mysql_errno(mysql), mysql_error(mysql)); + } + } else { + MYSQL_RES *result = mysql_store_result(mysql); + parseResultJsonColumn(result, j); + + mysql_free_result(result); + __sync_fetch_and_add(&g_select_OK,1); + } + + // value types in mysql and in proxysql are different + // we should convert proxysql values to mysql format to compare + for (auto& el : j.items()) { + if (el.key() == "conn") { + std::string sql_log_bin_value; + + // sql_log_bin {0|1} + if (el.value()["sql_log_bin"] == 1) { + el.value().erase("sql_log_bin"); + j["conn"]["sql_log_bin"] = "ON"; + } + else if (el.value()["sql_log_bin"] == 0) { + el.value().erase("sql_log_bin"); + j["conn"]["sql_log_bin"] = "OFF"; + } + + // sql_auto_is_null {true|false} + if (!el.value()["sql_auto_is_null"].dump().compare("ON") || + !el.value()["sql_auto_is_null"].dump().compare("1") || + !el.value()["sql_auto_is_null"].dump().compare("on") || + el.value()["sql_auto_is_null"] == 1) { + el.value().erase("sql_auto_is_null"); + j["conn"]["sql_auto_is_null"] = "ON"; + } + else if (!el.value()["sql_auto_is_null"].dump().compare("OFF") || + !el.value()["sql_auto_is_null"].dump().compare("0") || + !el.value()["sql_auto_is_null"].dump().compare("off") || + el.value()["sql_auto_is_null"] == 0) { + el.value().erase("sql_auto_is_null"); + j["conn"]["sql_auto_is_null"] = "OFF"; + } + + // completely remove autocommit test +/* + // autocommit {true|false} + if (!el.value()["autocommit"].dump().compare("ON") || + !el.value()["autocommit"].dump().compare("1") || + !el.value()["autocommit"].dump().compare("on") || + el.value()["autocommit"] == 1) { + el.value().erase("autocommit"); + j["conn"]["autocommit"] = "ON"; + } + else if (!el.value()["autocommit"].dump().compare("OFF") || + !el.value()["autocommit"].dump().compare("0") || + !el.value()["autocommit"].dump().compare("off") || + el.value()["autocommit"] == 0) { + el.value().erase("autocommit"); + j["conn"]["autocommit"] = "OFF"; + } +*/ + // sql_safe_updates + if (!el.value()["sql_safe_updates"].dump().compare("\"ON\"") || + !el.value()["sql_safe_updates"].dump().compare("\"1\"") || + !el.value()["sql_safe_updates"].dump().compare("\"on\"") || + el.value()["sql_safe_updates"] == 1) { + el.value().erase("sql_safe_updates"); + j["conn"]["sql_safe_updates"] = "ON"; + } + else if (!el.value()["sql_safe_updates"].dump().compare("\"OFF\"") || + !el.value()["sql_safe_updates"].dump().compare("\"0\"") || + !el.value()["sql_safe_updates"].dump().compare("\"off\"") || + el.value()["sql_safe_updates"] == 0) { + el.value().erase("sql_safe_updates"); + j["conn"]["sql_safe_updates"] = "OFF"; + } + + std::stringstream ss; + ss << 0xFFFFFFFFFFFFFFFF; + // sql_select_limit + if (!el.value()["sql_select_limit"].dump().compare("\"DEFAULT\"")) { + el.value().erase("sql_select_limit"); + j["conn"]["sql_select_limit"] = strdup(ss.str().c_str()); + } + + if (!is_mariadb) { + // transaction_isolation (level) + if (!el.value()["isolation_level"].dump().compare("\"REPEATABLE READ\"")) { + el.value().erase("isolation_level"); + j["conn"]["transaction_isolation"] = "REPEATABLE-READ"; + } + else if (!el.value()["isolation_level"].dump().compare("\"READ COMMITTED\"")) { + el.value().erase("isolation_level"); + j["conn"]["transaction_isolation"] = "READ-COMMITTED"; + } + else if (!el.value()["isolation_level"].dump().compare("\"READ UNCOMMITTED\"")) { + el.value().erase("isolation_level"); + j["conn"]["transaction_isolation"] = "READ-UNCOMMITTED"; + } + else if (!el.value()["isolation_level"].dump().compare("\"SERIALIZABLE\"")) { + el.value().erase("isolation_level"); + j["conn"]["transaction_isolation"] = "SERIALIZABLE"; + } + } + else { + // transaction_isolation (level) + if (!el.value()["isolation_level"].dump().compare("\"REPEATABLE READ\"")) { + el.value().erase("isolation_level"); + j["conn"]["tx_isolation"] = "REPEATABLE-READ"; + } + else if (!el.value()["isolation_level"].dump().compare("\"READ COMMITTED\"")) { + el.value().erase("isolation_level"); + j["conn"]["tx_isolation"] = "READ-COMMITTED"; + } + else if (!el.value()["isolation_level"].dump().compare("\"READ UNCOMMITTED\"")) { + el.value().erase("isolation_level"); + j["conn"]["tx_isolation"] = "READ-UNCOMMITTED"; + } + else if (!el.value()["isolation_level"].dump().compare("\"SERIALIZABLE\"")) { + el.value().erase("isolation_level"); + j["conn"]["tx_isolation"] = "SERIALIZABLE"; + } + } + + if (!is_mariadb) { + // transaction_read (write|only) + if (!el.value()["transaction_read"].dump().compare("\"ONLY\"")) { + el.value().erase("transaction_read"); + j["conn"]["transaction_read_only"] = "ON"; + } + else if (!el.value()["transaction_read"].dump().compare("\"WRITE\"")) { + el.value().erase("transaction_read"); + j["conn"]["transaction_read_only"] = "OFF"; + } + } else { + // transaction_read (write|only) + if (!el.value()["transaction_read"].dump().compare("\"ONLY\"")) { + el.value().erase("transaction_read"); + j["conn"]["tx_read_only"] = "ON"; + } + else if (!el.value()["transaction_read"].dump().compare("\"WRITE\"")) { + el.value().erase("transaction_read"); + j["conn"]["tx_read_only"] = "OFF"; + } + } + + if (!is_mariadb) { + // session_track_gtids + if (!el.value()["session_track_gtids"].dump().compare("\"OFF\"")) { + el.value().erase("session_track_gtids"); + j["conn"]["session_track_gtids"] = "OFF"; + } + else if (!el.value()["session_track_gtids"].dump().compare("\"OWN_GTID\"")) { + el.value().erase("session_track_gtids"); + j["conn"]["session_track_gtids"] = "OWN_GTID"; + } + else if (!el.value()["session_track_gtids"].dump().compare("\"ALL_GTIDS\"")) { + el.value().erase("session_track_gtids"); + j["conn"]["session_track_gtids"] = "ALL_GTIDS"; + } + } + + } + } +} + +void * my_conn_thread(void *arg) { + g_seed = time(NULL) ^ getpid() ^ pthread_self(); + unsigned int select_OK=0; + unsigned int select_ERR=0; + int i, j; + MYSQL **mysqlconns=(MYSQL **)malloc(sizeof(MYSQL *)*count); + std::vector varsperconn(count); + + if (mysqlconns==NULL) { + exit(EXIT_FAILURE); + } + + std::vector cs = {"latin1", "utf8", "utf8mb4", "latin2", "latin7"}; + + for (i=0; i lock(mtx_); + skip(1, "mysql connection [%p], command [%s]", mysql, testCases[r2].command.c_str()); + continue; + } + if (strstr(testCases[r2].command.c_str(),"sql_log_bin")) { + std::lock_guard lock(mtx_); + skip(1, "mysql connection [%p], command [%s]", mysql, testCases[r2].command.c_str()); + continue; + } + } + std::vector commands = split(testCases[r2].command.c_str(), ';'); + for (auto c : commands) { + if (mysql_query(mysql, c.c_str())) { + if (silent==0) { + fprintf(stderr,"ERROR while running -- \"%s\" : (%d) %s\n", c.c_str(), mysql_errno(mysql), mysql_error(mysql)); + } + } else { + MYSQL_RES *result = mysql_store_result(mysql); + mysql_free_result(result); + select_OK++; + __sync_fetch_and_add(&g_select_OK,1); + } + } + + for (auto& el : testCases[r2].expected_vars.items()) { + if (el.key() == "transaction_isolation") { + if (is_mariadb) { + vars["tx_isolation"] = el.value(); + } + else { + vars[el.key()] = el.value(); + } + } + else if (el.key() == "session_track_gtids") { + if (!is_mariadb) { + vars[el.key()] = el.value(); + } + } + else if (el.key() == "wsrep_sync_wait") { + if (is_cluster) { + vars[el.key()] = el.value(); + } + } + else if (el.key() == "transaction_read_only") { + if (is_mariadb) { + vars["tx_read_only"] = el.value(); + } else { + vars[el.key()] = el.value(); + } + } + else { + vars[el.key()] = el.value(); + } + } + + int sleepDelay = fastrand()%100; + usleep(sleepDelay * 1000); + + char query[128]; + sprintf(query, "SELECT /* %p */ %d;", mysql, sleepDelay); + if (mysql_query(mysql,query)) { + select_ERR++; + __sync_fetch_and_add(&g_select_ERR,1); + } else { + MYSQL_RES *result = mysql_store_result(mysql); + mysql_free_result(result); + select_OK++; + __sync_fetch_and_add(&g_select_OK,1); + } + + json mysql_vars; + queryVariables(mysql, mysql_vars); + + json proxysql_vars; + queryInternalStatus(mysql, proxysql_vars); + + bool testPassed = true; + int variables_tested = 0; + for (auto& el : vars.items()) { + auto k = mysql_vars.find(el.key()); + auto s = proxysql_vars["conn"].find(el.key()); + + if (k == mysql_vars.end()) + fprintf(stderr, "Variable %s->%s in mysql resultset was not found.\nmysql data : %s\nproxysql data: %s\ncsv data %s\n", + el.value().dump().c_str(), el.key().c_str(), mysql_vars.dump().c_str(), proxysql_vars.dump().c_str(), vars.dump().c_str()); + + if (s == proxysql_vars["conn"].end()) + fprintf(stderr, "Variable %s->%s in proxysql resultset was not found.\nmysql data : %s\nproxysql data: %s\ncsv data %s\n", + el.value().dump().c_str(), el.key().c_str(), mysql_vars.dump().c_str(), proxysql_vars.dump().c_str(), vars.dump().c_str()); + + if (k.value() != el.value() || s.value() != el.value()) { + __sync_fetch_and_add(&g_failed, 1); + testPassed = false; + fprintf(stderr, "Test failed for this case %s->%s.\n\nmysql data %s\n\n proxysql data %s\n\n csv data %s\n\n\n", + el.value().dump().c_str(), el.key().c_str(), mysql_vars.dump().c_str(), proxysql_vars.dump().c_str(), vars.dump().c_str()); + ok(testPassed, "mysql connection [%p], thread_id [%lu], command [%s]", mysql, mysql->thread_id, testCases[r2].command.c_str()); + exit(0); + } else { + variables_tested++; + } + } + { + std::lock_guard lock(mtx_); + ok(testPassed, "mysql connection [%p], thread_id [%lu], variables_tested [%d], command [%s]", mysql, mysql->thread_id, variables_tested, testCases[r2].command.c_str()); + } + } + __sync_fetch_and_add(&query_phase_completed,1); + + return NULL; +} + + +int main(int argc, char *argv[]) { + CommandLine cl; + + if(cl.getEnv()) + return exit_status(); + + { + bn = basename(argv[0]); + std::string bn = basename(argv[0]); + std::cerr << "Filename: " << bn << std::endl; + if (bn == "set_testing-multi-t") { + multi_users=4; + } + } + + std::string fileName(std::string(cl.workdir) + "/set_testing-t.csv"); + + MYSQL* mysqladmin = mysql_init(NULL); + if (!mysqladmin) + return exit_status(); + + if (!mysql_real_connect(mysqladmin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", + __FILE__, __LINE__, mysql_error(mysqladmin)); + return exit_status(); + } +/* + MYSQL_QUERY(mysqladmin, "update global_variables set variable_value='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' where variable_name='mysql-default_sql_mode'"); + MYSQL_QUERY(mysqladmin, "update global_variables set variable_value='OFF' where variable_name='mysql-default_sql_safe_update'"); + MYSQL_QUERY(mysqladmin, "update global_variables set variable_value='UTF8' where variable_name='mysql-default_character_set_results'"); + MYSQL_QUERY(mysqladmin, "update global_variables set variable_value='REPEATABLE READ' where variable_name='mysql-default_isolation_level'"); + MYSQL_QUERY(mysqladmin, "update global_variables set variable_value='REPEATABLE READ' where variable_name='mysql-default_tx_isolation'"); + MYSQL_QUERY(mysqladmin, "update global_variables set variable_value='utf8_general_ci' where variable_name='mysql-default_collation_connection'"); + MYSQL_QUERY(mysqladmin, "update global_variables set variable_value='true' where variable_name='mysql-enforce_autocommit_on_reads'"); + MYSQL_QUERY(mysqladmin, "load mysql variables to runtime"); + +*/ + if (multi_users) { + for (int i=1; i<=multi_users; i++) { + std::string q = "INSERT OR IGNORE INTO mysql_users (username,password) VALUES ('sbtest" + std::to_string(i) + "','sbtest" + std::to_string(i) + "')"; + std::cerr << bn << ": " << q << std::endl; + MYSQL_QUERY(mysqladmin, q.c_str()); + } + std::string q = "LOAD MYSQL USERS TO RUNTIME"; + std::cerr << bn << ": " << q << std::endl; + MYSQL_QUERY(mysqladmin, q.c_str()); + q = "UPDATE mysql_servers SET max_connections=3 WHERE hostgroup_id=0;"; + std::cerr << bn << ": " << q << std::endl; + MYSQL_QUERY(mysqladmin, q.c_str()); + q = "LOAD MYSQL SERVERS TO RUNTIME"; + std::cerr << bn << ": " << q << std::endl; + MYSQL_QUERY(mysqladmin, q.c_str()); + } + MYSQL* mysql = mysql_init(NULL); + if (!mysql) + return exit_status(); + if (!mysql_real_connect(mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", + __FILE__, __LINE__, mysql_error(mysql)); + return exit_status(); + } + MYSQL_QUERY(mysql, "select @@version"); + MYSQL_RES *result = mysql_store_result(mysql); + MYSQL_ROW row; + while ((row = mysql_fetch_row(result))) + { + if (strstr(row[0], "Maria")) { + is_mariadb = true; + } + else { + is_mariadb = false; + } + + char* first_dash = strstr(row[0], "-"); + if (!first_dash || !strstr(first_dash+1, "-")) { + is_cluster = false; + } else { + is_cluster = true; + } + } + + mysql_free_result(result); + mysql_close(mysql); + + num_threads = 10; + queries = 1000; + queries_per_connections = 10; + count = 10; + username = cl.username; + password = cl.password; + host = cl.host; + port = cl.port; + + plan(queries * num_threads); + if (!readTestCases(fileName)) { + fprintf(stderr, "Cannot read %s\n", fileName.c_str()); + return exit_status(); + } + + if (strcmp(host,"localhost")==0) { + local = 1; + } + if (uniquequeries == 0) { + if (queries) uniquequeries=queries; + } + if (uniquequeries) { + uniquequeries=(int)sqrt(uniquequeries); + } + + pthread_t *thi=(pthread_t *)malloc(sizeof(pthread_t)*num_threads); + if (thi==NULL) + return exit_status(); + + for (unsigned int i=0; i lock(mtx_); + skip(1, "mysql connection [%p], command [%s]", mysql, testCases[r2].command.c_str()); + continue; + } + if (strstr(testCases[r2].command.c_str(),"sql_log_bin")) { + std::lock_guard lock(mtx_); + skip(1, "mysql connection [%p], command [%s]", mysql, testCases[r2].command.c_str()); + continue; + } + } std::vector commands = split(testCases[r2].command.c_str(), ';'); for (auto c : commands) { if (mysql_query(mysql, c.c_str())) { if (silent==0) { - fprintf(stderr,"%s\n", mysql_error(mysql)); + fprintf(stderr,"ERROR while running -- \"%s\" : (%d) %s\n", c.c_str(), mysql_errno(mysql), mysql_error(mysql)); } } else { MYSQL_RES *result = mysql_store_result(mysql); @@ -476,6 +489,7 @@ void * my_conn_thread(void *arg) { queryInternalStatus(mysql, proxysql_vars); bool testPassed = true; + int variables_tested = 0; for (auto& el : vars.items()) { auto k = mysql_vars.find(el.key()); auto s = proxysql_vars["conn"].find(el.key()); @@ -495,11 +509,13 @@ void * my_conn_thread(void *arg) { el.value().dump().c_str(), el.key().c_str(), mysql_vars.dump().c_str(), proxysql_vars.dump().c_str(), vars.dump().c_str()); ok(testPassed, "mysql connection [%p], thread_id [%lu], command [%s]", mysql, mysql->thread_id, testCases[r2].command.c_str()); exit(0); + } else { + variables_tested++; } } { std::lock_guard lock(mtx_); - ok(testPassed, "mysql connection [%p], thread_id [%lu], command [%s]", mysql, mysql->thread_id, testCases[r2].command.c_str()); + ok(testPassed, "mysql connection [%p], thread_id [%lu], variables_tested [%d], command [%s]", mysql, mysql->thread_id, variables_tested, testCases[r2].command.c_str()); } } __sync_fetch_and_add(&query_phase_completed,1); @@ -510,11 +526,13 @@ void * my_conn_thread(void *arg) { int main(int argc, char *argv[]) { CommandLine cl; - std::string fileName("./tests/set_testing-t.csv"); if(cl.getEnv()) return exit_status(); + std::string fileName(std::string(cl.workdir) + "/set_testing-t.csv"); +/* + // do not connect to admin at all MYSQL* mysqladmin = mysql_init(NULL); if (!mysqladmin) return exit_status(); @@ -534,8 +552,7 @@ int main(int argc, char *argv[]) { MYSQL_QUERY(mysqladmin, "update global_variables set variable_value='true' where variable_name='mysql-enforce_autocommit_on_reads'"); MYSQL_QUERY(mysqladmin, "load mysql variables to runtime"); - mysql_close(mysqladmin); - +*/ MYSQL* mysql = mysql_init(NULL); if (!mysql) return exit_status(); diff --git a/test/tap/tests/set_testing-t.csv b/test/tap/tests/set_testing-t.csv index db8114f171..beccbd8e34 100644 --- a/test/tap/tests/set_testing-t.csv +++ b/test/tap/tests/set_testing-t.csv @@ -31,7 +31,6 @@ "SET sql_mode='PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION'", "{'sql_mode':'PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION'}" "SET time_zone='+01:00'","{'time_zone':'+01:00'}" "SET time_zone='-03:00', sql_mode='ALLOW_INVALID_DATES'","{'time_zone':'-03:00', 'sql_mode':'ALLOW_INVALID_DATES'}" -"SET autocommit=0","{'autocommit':'OFF'}" "SET time_zone='+04:00', sql_mode='NO_ENGINE_SUBSTITUTION'", "{'time_zone':'+04:00','sql_mode':'NO_ENGINE_SUBSTITUTION'}" "SET sql_safe_updates='OFF'", "{'sql_safe_updates':'OFF'}" "SET sql_safe_updates='ON'", "{'sql_safe_updates':'ON'}" @@ -62,3 +61,5 @@ "set max_join_size=18446744073709551615", "{'max_join_size':'18446744073709551615'}" "set wsrep_sync_wait=1", "{'wsrep_sync_wait':'1'}" "set wsrep_sync_wait=0", "{'wsrep_sync_wait':'0'}" +"set group_concat_max_len=2048", "{'group_concat_max_len':'2048'}" +"set group_concat_max_len=4096", "{'group_concat_max_len':'4096'}" diff --git a/test/tap/tests/test_default_value_transaction_isolation-t.cpp b/test/tap/tests/test_default_value_transaction_isolation-t.cpp new file mode 100644 index 0000000000..cca5d20806 --- /dev/null +++ b/test/tap/tests/test_default_value_transaction_isolation-t.cpp @@ -0,0 +1,91 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +int main(int argc, char** argv) { + CommandLine cl; + + if(cl.getEnv()) + return exit_status(); + + plan(3); + diag("Testing default value for session varable transaction isolation"); + + MYSQL* mysqladmin = mysql_init(NULL); + if (!mysqladmin) + return exit_status(); + + if (!mysql_real_connect(mysqladmin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", + __FILE__, __LINE__, mysql_error(mysqladmin)); + return exit_status(); + } + + // Set default non-existing value for transaction isolation level + MYSQL_QUERY(mysqladmin, "set mysql-default_isolation_level='non-existing-value-1'"); + MYSQL_QUERY(mysqladmin, "load mysql variables to runtime"); + + MYSQL* mysql_1 = mysql_init(NULL); + if (!mysql_1) + return exit_status(); + + if (!mysql_real_connect(mysql_1, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { + fprintf(stderr, "Failed to connect to database: Error: %s\n", + mysql_error(mysql_1)); + return exit_status(); + } + MYSQL_QUERY(mysql_1, "select 1"); + MYSQL_RES* result = mysql_store_result(mysql_1); + ok(mysql_num_rows(result) == 1, "Select statement should be executed on connection 1"); + mysql_free_result(result); + + MYSQL* mysql_2 = mysql_init(NULL); + if (!mysql_2) + return exit_status(); + + if (!mysql_real_connect(mysql_2, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { + fprintf(stderr, "Failed to connect to database: Error: %s\n", + mysql_error(mysql_2)); + return exit_status(); + } + MYSQL_QUERY(mysql_2, "select 1"); + result = mysql_store_result(mysql_2); + ok(mysql_num_rows(result) == 1, "Select statement should be executed on connection 1"); + mysql_free_result(result); + + // Change default non-existing value for transaction isolation level + MYSQL_QUERY(mysqladmin, "set mysql-default_isolation_level='non-existing-value-2'"); + MYSQL_QUERY(mysqladmin, "load mysql variables to runtime"); + + // Try third connection with different default value of the session variable + MYSQL* mysql_3 = mysql_init(NULL); + if (!mysql_3) + return exit_status(); + + if (!mysql_real_connect(mysql_3, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { + fprintf(stderr, "Failed to connect to database: Error: %s\n", + mysql_error(mysql_3)); + return exit_status(); + } + MYSQL_QUERY(mysql_3, "select 1"); + result = mysql_store_result(mysql_3); + ok(mysql_num_rows(result) == 1, "Select statement should be executed on connection 1"); + mysql_free_result(result); + + mysql_close(mysql_3); + mysql_close(mysql_2); + mysql_close(mysql_1); + mysql_close(mysqladmin); + + return exit_status(); +} + diff --git a/test/tap/tests/test_firewall-t.cpp b/test/tap/tests/test_firewall-t.cpp index b15cf47d0d..70fbafbee6 100644 --- a/test/tap/tests/test_firewall-t.cpp +++ b/test/tap/tests/test_firewall-t.cpp @@ -17,7 +17,7 @@ int main(int argc, char** argv) { if(cl.getEnv()) return exit_status(); - plan(3); + plan(7); diag("Testing firewall whitelist functionality"); MYSQL* mysqladmin = mysql_init(NULL); @@ -55,7 +55,7 @@ int main(int argc, char** argv) { mysql_free_result(result); MYSQL_QUERY(mysqladmin, "update global_variables set variable_value=1 where variable_name='mysql-firewall_whitelist_enabled'"); - MYSQL_QUERY(mysqladmin, "load mysql users to runtime"); + MYSQL_QUERY(mysqladmin, "load mysql variables to runtime"); // Test that firewall initialized and blocks all queries if (mysql_query(mysql, "select @@version")) { @@ -138,6 +138,15 @@ int main(int argc, char** argv) { ok(false, "Query should be allowed by firewall, but it is blocked after active=1 update"); } + // Cleanup firewall rules + MYSQL_QUERY(mysqladmin, "load mysql firewall from disk"); + MYSQL_QUERY(mysqladmin, "load mysql firewall to runtime"); + + // Clean up variables + MYSQL_QUERY(mysqladmin, "load mysql variables from disk"); + MYSQL_QUERY(mysqladmin, "load mysql variables to runtime"); + + mysql_close(mysql); mysql_close(mysqladmin); diff --git a/test/tap/tests/test_mysql_query_rules_fast_routing-t.cpp b/test/tap/tests/test_mysql_query_rules_fast_routing-t.cpp new file mode 100644 index 0000000000..814263e671 --- /dev/null +++ b/test/tap/tests/test_mysql_query_rules_fast_routing-t.cpp @@ -0,0 +1,72 @@ +#include +#include +#include +#include + +#include + +#include +#include + +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +int main(int argc, char** argv) { + CommandLine cl; + + std::random_device rd; //Will be used to obtain a seed for the random number engine + std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd() + std::uniform_int_distribution<> dis(100000, 1000000); + + if(cl.getEnv()) + return exit_status(); + + plan(6); + diag("Testing query rules fast routing"); + + MYSQL* mysqlAdmin = mysql_init(NULL); + if (!mysqlAdmin) return exit_status(); + if (!mysql_real_connect(mysqlAdmin, cl.host, "admin", "admin", NULL, 6032, NULL, 0)) return exit_status(); + + const auto NUM_REPS=3; + char query[1024] = {0}; + for (auto i=0; i