diff --git a/.github/workflows/CI-3p-mariadb-connector-c.yml b/.github/workflows/CI-3p-mariadb-connector-c.yml new file mode 100644 index 0000000000..bfe5a7600e --- /dev/null +++ b/.github/workflows/CI-3p-mariadb-connector-c.yml @@ -0,0 +1,31 @@ +name: CI-3p-mariadb-connector-c + +on: + push: + branches: [ "v2.x" ] + paths-ignore: + - '.github/**' + - '**.md' + pull_request: + branches: [ "v2.x" ] + paths-ignore: + - '.github/**' + - '**.md' +# schedule: +# - cron: '15 13 * * 3' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref_name }} + +jobs: + cache: + uses: sysown/proxysql/.github/workflows/ci-builds.yml@GH-Actions + secrets: inherit + + run: + if: ${{ always() }} + needs: [ "cache" ] + uses: sysown/proxysql/.github/workflows/ci-3p-mariadb-connector-c.yml@GH-Actions + secrets: inherit + diff --git a/.github/workflows/CI-3p-mysql-connector-j.yml b/.github/workflows/CI-3p-mysql-connector-j.yml index 3d915cfee2..650e42107d 100644 --- a/.github/workflows/CI-3p-mysql-connector-j.yml +++ b/.github/workflows/CI-3p-mysql-connector-j.yml @@ -24,6 +24,7 @@ jobs: secrets: inherit run: + if: ${{ always() }} needs: [ "cache" ] uses: sysown/proxysql/.github/workflows/ci-3p-mysql-connector-j.yml@GH-Actions secrets: inherit diff --git a/.github/workflows/CI-3p-php-pdo-mysql.yml b/.github/workflows/CI-3p-php-pdo-mysql.yml index 8721d607cf..5a0bd67a41 100644 --- a/.github/workflows/CI-3p-php-pdo-mysql.yml +++ b/.github/workflows/CI-3p-php-pdo-mysql.yml @@ -18,13 +18,13 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} - jobs: cache: uses: sysown/proxysql/.github/workflows/ci-builds.yml@GH-Actions secrets: inherit run: + if: ${{ always() }} needs: [ "cache" ] uses: sysown/proxysql/.github/workflows/ci-3p-php-pdo-mysql.yml@GH-Actions secrets: inherit diff --git a/.github/workflows/CI-3p-sqlalchemy.yml b/.github/workflows/CI-3p-sqlalchemy.yml index 9be2100a3b..c1f1e96efb 100644 --- a/.github/workflows/CI-3p-sqlalchemy.yml +++ b/.github/workflows/CI-3p-sqlalchemy.yml @@ -24,6 +24,7 @@ jobs: secrets: inherit run: + if: ${{ always() }} needs: [ "cache" ] uses: sysown/proxysql/.github/workflows/ci-3p-sqlalchemy.yml@GH-Actions secrets: inherit diff --git a/.github/workflows/CI-basictests.yml b/.github/workflows/CI-basictests.yml index 134397c6d3..329e6ceb23 100644 --- a/.github/workflows/CI-basictests.yml +++ b/.github/workflows/CI-basictests.yml @@ -14,10 +14,6 @@ on: # schedule: # - cron: '15 13 * * 3' workflow_dispatch: -# workflow_run: -# workflows: ["CI-builds"] -# types: -# - completed concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} @@ -28,6 +24,7 @@ jobs: secrets: inherit run: + if: ${{ always() }} needs: [ "cache" ] uses: sysown/proxysql/.github/workflows/ci-basictests.yml@GH-Actions secrets: inherit diff --git a/.github/workflows/CI-cb_taptests.yml b/.github/workflows/CI-cb_taptests.yml deleted file mode 100644 index 986d5d8e4d..0000000000 --- a/.github/workflows/CI-cb_taptests.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: CI-cb_taptests - -on: -# push: -# branches: [ "v2.x" ] -# paths-ignore: -# - '.github/**' -# - '**.md' -# pull_request: -# branches: [ "v2.x" ] -# paths-ignore: -# - '.github/**' -# - '**.md' -# schedule: -# - cron: '15 13 * * 3' - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref_name }} - -jobs: - run: - uses: sysown/proxysql/.github/workflows/ci-cb_taptests.yml@GH-Actions - secrets: inherit - diff --git a/.github/workflows/CI-repltests.yml b/.github/workflows/CI-repltests.yml index 4716bdfe49..c7e6113953 100644 --- a/.github/workflows/CI-repltests.yml +++ b/.github/workflows/CI-repltests.yml @@ -24,6 +24,7 @@ jobs: secrets: inherit run: + if: ${{ always() }} needs: [ "cache" ] uses: sysown/proxysql/.github/workflows/ci-repltests.yml@GH-Actions secrets: inherit diff --git a/.github/workflows/CI-selftests.yml b/.github/workflows/CI-selftests.yml index 13461ed60f..ced298f0ac 100644 --- a/.github/workflows/CI-selftests.yml +++ b/.github/workflows/CI-selftests.yml @@ -24,6 +24,7 @@ jobs: secrets: inherit run: + if: ${{ always() }} needs: [ "cache" ] uses: sysown/proxysql/.github/workflows/ci-selftests.yml@GH-Actions secrets: inherit diff --git a/.github/workflows/CI-shuntest.yml b/.github/workflows/CI-shuntest.yml index cd4dfc7586..9ec86543a9 100644 --- a/.github/workflows/CI-shuntest.yml +++ b/.github/workflows/CI-shuntest.yml @@ -24,6 +24,7 @@ jobs: secrets: inherit run: + if: ${{ always() }} needs: [ "cache" ] uses: sysown/proxysql/.github/workflows/ci-shuntest.yml@GH-Actions secrets: inherit diff --git a/.github/workflows/CI-taptests-groups.yml b/.github/workflows/CI-taptests-groups.yml index b44dd79a66..ab07b29ae3 100644 --- a/.github/workflows/CI-taptests-groups.yml +++ b/.github/workflows/CI-taptests-groups.yml @@ -24,6 +24,7 @@ jobs: secrets: inherit run: + if: ${{ always() }} needs: [ "cache" ] uses: sysown/proxysql/.github/workflows/ci-taptests-groups.yml@GH-Actions secrets: inherit diff --git a/.github/workflows/CI-taptests-ssl.yml b/.github/workflows/CI-taptests-ssl.yml index 94812e613f..52f6a23121 100644 --- a/.github/workflows/CI-taptests-ssl.yml +++ b/.github/workflows/CI-taptests-ssl.yml @@ -24,6 +24,7 @@ jobs: secrets: inherit run: + if: ${{ always() }} needs: [ "cache" ] uses: sysown/proxysql/.github/workflows/ci-taptests-ssl.yml@GH-Actions secrets: inherit diff --git a/.github/workflows/CI-taptests.yml b/.github/workflows/CI-taptests.yml index 490f0d7298..6eab902818 100644 --- a/.github/workflows/CI-taptests.yml +++ b/.github/workflows/CI-taptests.yml @@ -24,6 +24,7 @@ jobs: secrets: inherit run: + if: ${{ always() }} needs: [ "cache" ] uses: sysown/proxysql/.github/workflows/ci-taptests.yml@GH-Actions secrets: inherit diff --git a/.gitignore b/.gitignore index 3647d93615..a047d63ea8 100644 --- a/.gitignore +++ b/.gitignore @@ -110,6 +110,10 @@ heaptrack.* #vscode .vscode/ +# test deps +test/deps/mariadb-connector-c/mariadb-connector-c-*/ +test/deps/mysql-connector-c/mysql-5.7.*/ + #tap tests test/tap/tap/cpp-dotenv/cpp-dotenv-* test/tap/tap/cpp-dotenv/static/cpp-dotenv-* diff --git a/Makefile b/Makefile index 34626ce6e2..b50b0b38d3 100644 --- a/Makefile +++ b/Makefile @@ -9,12 +9,10 @@ ### export GIT_VERSION=2.x-dev ### ``` -ifndef GIT_VERSION -GIT_VERSION := $(shell git describe --long --abbrev=7) +GIT_VERSION ?= $(shell git describe --long --abbrev=7) ifndef GIT_VERSION $(error GIT_VERSION is not set) endif -endif ### NOTES: ### to compile without jemalloc, set environment variable NOJEMALLOC=1 @@ -28,26 +26,39 @@ endif ### ### ** to use on-demand coredump generation feature, compile code without ASAN option (WITHASAN=0). -O0=-O0 -O2=-O2 -O1=-O1 -O3=-O3 -mtune=native -#OPTZ=$(O2) -EXTRALINK=#-pg -ALL_DEBUG=-ggdb -DDEBUG -NO_DEBUG= -DEBUG=${ALL_DEBUG} +O0 := -O0 +O2 := -O2 +O1 := -O1 +O3 := -O3 -mtune=native + +#EXTRALINK := #-pg +ALL_DEBUG := $(O0) -ggdb -DDEBUG +NO_DEBUG := $(O2) -ggdb +DEBUG := $(ALL_DEBUG) +CURVER ?= 2.6.0 #export DEBUG -#export OPTZ #export EXTRALINK export MAKE -export CURVER?=2.6.0 +export CURVER + +### detect compiler support for c++11/17 +CPLUSPLUS := $(shell ${CC} -std=c++17 -dM -E -x c++ /dev/null 2>/dev/null | grep -F __cplusplus | grep -Po '\d\d\d\d\d\dL') +ifneq ($(CPLUSPLUS),201703L) + CPLUSPLUS := $(shell ${CC} -std=c++11 -dM -E -x c++ /dev/null 2>/dev/null| grep -F __cplusplus | grep -Po '\d\d\d\d\d\dL') + LEGACY_BUILD := 1 +ifneq ($(CPLUSPLUS),201103L) + $(error Compiler must support at least c++11) +endif +endif +STDCPP := -std=c++$(shell echo $(CPLUSPLUS) | cut -c3-4) -DCXX$(shell echo $(CPLUSPLUS) | cut -c3-4) + +### detect distro +DISTRO := Unknown ifneq (,$(wildcard /etc/os-release)) DISTRO := $(shell awk -F= '/^NAME/{print $$2}' /etc/os-release) -else - DISTRO := Unknown endif +### multiprocessing NPROCS := 1 OS := $(shell uname -s) ifeq ($(OS),Linux) @@ -56,30 +67,35 @@ endif ifeq ($(OS),Darwin) NPROCS := $(shell sysctl -n hw.ncpu) endif +export MAKEOPT := -j${NPROCS} -export MAKEOPT=-j ${NPROCS} - +### systemd +SYSTEMD := 0 ifeq ($(wildcard /usr/lib/systemd/system), /usr/lib/systemd/system) - SYSTEMD=1 -else - SYSTEMD=0 + SYSTEMD := 1 endif + +### check user/group USERCHECK := $(shell getent passwd proxysql) GROUPCHECK := $(shell getent group proxysql) ### main targets +.DEFAULT: default .PHONY: default default: build_src .PHONY: debug debug: build_src_debug +.PHONY: testaurora_random +testaurora_random: build_src_testaurora_random + .PHONY: testaurora testaurora: build_src_testaurora - cd test/tap && OPTZ="${O0} -ggdb -DDEBUG -DTEST_AURORA" CC=${CC} CXX=${CXX} ${MAKE} - cd test/tap/tests && OPTZ="${O0} -ggdb -DDEBUG -DTEST_AURORA" CC=${CC} CXX=${CXX} ${MAKE} $(MAKECMDGOALS) + # cd test/tap && OPTZ="${O0} -ggdb -DDEBUG -DTEST_AURORA" CC=${CC} CXX=${CXX} ${MAKE} + # cd test/tap/tests && OPTZ="${O0} -ggdb -DDEBUG -DTEST_AURORA" CC=${CC} CXX=${CXX} ${MAKE} $(MAKECMDGOALS) .PHONY: testgalera testgalera: build_src_testgalera @@ -108,33 +124,65 @@ debug_clickhouse: build_src_debug_clickhouse ### helper targets .PHONY: build_deps -build_deps: - cd deps && OPTZ="${O2} -ggdb" CC=${CC} CXX=${CXX} ${MAKE} +build_deps: $(if $(LEGACY_BUILD),build_deps_legacy,build_deps_default) .PHONY: build_lib -build_lib: build_deps - cd lib && OPTZ="${O2} -ggdb" CC=${CC} CXX=${CXX} ${MAKE} +build_lib: $(if $(LEGACY_BUILD),build_lib_legacy,build_lib_default) .PHONY: build_src -build_src: build_lib - cd src && OPTZ="${O2} -ggdb" CC=${CC} CXX=${CXX} ${MAKE} +build_src: $(if $(LEGACY_BUILD),build_src_legacy,build_src_default) .PHONY: build_deps_debug -build_deps_debug: - cd deps && OPTZ="${O0} -ggdb -DDEBUG" PROXYDEBUG=1 CC=${CC} CXX=${CXX} ${MAKE} +build_deps_debug: $(if $(LEGACY_BUILD),build_deps_debug_legacy,build_deps_debug_default) .PHONY: build_lib_debug -build_lib_debug: build_deps_debug +build_lib_debug: $(if $(LEGACY_BUILD),build_lib_debug_legacy,build_lib_debug_default) + +.PHONY: build_src_debug +build_src_debug: $(if $(LEGACY_BUILD),build_src_debug_legacy,build_src_debug_default) + +# legacy build targets (pre c++17) +.PHONY: build_deps_legacy +build_deps_legacy: + cd deps && OPTZ="${O2} -ggdb" CC=${CC} CXX=${CXX} ${MAKE} + +.PHONY: build_lib_legacy +build_lib_legacy: build_deps_legacy + cd lib && OPTZ="${O2} -ggdb" CC=${CC} CXX=${CXX} ${MAKE} + +.PHONY: build_src_legacy +build_src_legacy: build_lib_legacy + cd src && OPTZ="${O2} -ggdb" CC=${CC} CXX=${CXX} ${MAKE} + +.PHONY: build_deps_debug_legacy +build_deps_debug_legacy: + cd deps && OPTZ="${O0} -ggdb -DDEBUG" PROXYDEBUG=1 CC=${CC} CXX=${CXX} ${MAKE} + +.PHONY: build_lib_debug_legacy +build_lib_debug_legacy: build_deps_debug_legacy cd lib && OPTZ="${O0} -ggdb -DDEBUG" CC=${CC} CXX=${CXX} ${MAKE} +.PHONY: build_src_debug_legacy +build_src_debug_legacy: build_lib_debug_legacy + cd src && OPTZ="${O0} -ggdb -DDEBUG" CC=${CC} CXX=${CXX} ${MAKE} +#-- + .PHONY: build_src_testaurora build_src_testaurora: build_lib_testaurora cd src && OPTZ="${O0} -ggdb -DDEBUG -DTEST_AURORA" CC=${CC} CXX=${CXX} ${MAKE} +.PHONY: build_src_testaurora_random +build_src_testaurora_random: build_lib_testaurora_random + cd src && OPTZ="${O0} -ggdb -DDEBUG -DTEST_AURORA -DTEST_AURORA_RANDOM" CC=${CC} CXX=${CXX} ${MAKE} + .PHONY: build_lib_testaurora build_lib_testaurora: build_deps_debug cd lib && OPTZ="${O0} -ggdb -DDEBUG -DTEST_AURORA" CC=${CC} CXX=${CXX} ${MAKE} +.PHONY: build_lib_testaurora_random +build_lib_testaurora_random: build_deps_debug + cd lib && OPTZ="${O0} -ggdb -DDEBUG -DTEST_AURORA -DTEST_AURORA_RANDOM" CC=${CC} CXX=${CXX} ${MAKE} + .PHONY: build_src_testgalera build_src_testgalera: build_lib_testgalera cd src && OPTZ="${O0} -ggdb -DDEBUG -DTEST_GALERA" CC=${CC} CXX=${CXX} ${MAKE} @@ -176,44 +224,67 @@ build_lib_testall: build_deps_debug cd lib && OPTZ="${O0} -ggdb -DDEBUG -DTEST_AURORA -DTEST_GALERA -DTEST_GROUPREP -DTEST_READONLY -DTEST_REPLICATIONLAG" CC=${CC} CXX=${CXX} ${MAKE} .PHONY: build_tap_test -build_tap_test: build_src - cd test/tap && OPTZ="${O0} -ggdb -DDEBUG" CC=${CC} CXX=${CXX} ${MAKE} +build_tap_test: build_tap_tests +.PHONY: build_tap_tests +build_tap_tests: build_src + cd test/tap && OPTZ="${O2} -ggdb" CC=${CC} CXX=${CXX} ${MAKE} .PHONY: build_tap_test_debug -build_tap_test_debug: build_src_debug +build_tap_test_debug: build_tap_tests_debug +.PHONY: build_tap_tests_debug +build_tap_tests_debug: build_src_debug cd test/tap && OPTZ="${O0} -ggdb -DDEBUG" CC=${CC} CXX=${CXX} ${MAKE} debug -.PHONY: build_src_debug -build_src_debug: build_lib_debug - cd src && OPTZ="${O0} -ggdb -DDEBUG" CC=${CC} CXX=${CXX} ${MAKE} - +# ClickHouse build targets are now default build targets. +# To maintain backward compatibility, ClickHouse targets are still available. .PHONY: build_deps_clickhouse -build_deps_clickhouse: - cd deps && OPTZ="${O2} -ggdb" PROXYSQLCLICKHOUSE=1 CC=${CC} CXX=${CXX} ${MAKE} +build_deps_clickhouse: build_deps_default .PHONY: build_deps_debug_clickhouse -build_deps_debug_clickhouse: - cd deps && OPTZ="${O0} -ggdb -DDEBUG" PROXYSQLCLICKHOUSE=1 PROXYDEBUG=1 CC=${CC} CXX=${CXX} ${MAKE} +build_deps_debug_clickhouse: build_deps_debug_default .PHONY: build_lib_clickhouse -build_lib_clickhouse: build_deps_clickhouse - cd lib && OPTZ="${O2} -ggdb" PROXYSQLCLICKHOUSE=1 CC=${CC} CXX=${CXX} ${MAKE} +build_lib_clickhouse: build_lib_default .PHONY: build_lib_debug_clickhouse -build_lib_debug_clickhouse: build_deps_debug_clickhouse - cd lib && OPTZ="${O0} -ggdb -DDEBUG" PROXYSQLCLICKHOUSE=1 CC=${CC} CXX=${CXX} ${MAKE} +build_lib_debug_clickhouse: build_lib_debug_default .PHONY: build_src_clickhouse -build_src_clickhouse: build_lib_clickhouse - cd src && OPTZ="${O2} -ggdb" PROXYSQLCLICKHOUSE=1 CC=${CC} CXX=${CXX} ${MAKE} +build_src_clickhouse: build_src_default .PHONY: build_src_debug_clickhouse -build_src_debug_clickhouse: build_lib_debug_clickhouse +build_src_debug_clickhouse: build_src_debug_default +#-- + +.PHONY: build_deps_default +build_deps_default: + cd deps && OPTZ="${O2} -ggdb" PROXYSQLCLICKHOUSE=1 CC=${CC} CXX=${CXX} ${MAKE} + +PHONY: build_deps_debug_default +build_deps_debug_default: + cd deps && OPTZ="${O0} -ggdb -DDEBUG" PROXYSQLCLICKHOUSE=1 PROXYDEBUG=1 CC=${CC} CXX=${CXX} ${MAKE} + +.PHONY: build_lib_default +build_lib_default: build_deps_default + cd lib && OPTZ="${O2} -ggdb" PROXYSQLCLICKHOUSE=1 CC=${CC} CXX=${CXX} ${MAKE} + +.PHONY: build_lib_debug_default +build_lib_debug_default: build_deps_debug_default + cd lib && OPTZ="${O0} -ggdb -DDEBUG" PROXYSQLCLICKHOUSE=1 CC=${CC} CXX=${CXX} ${MAKE} + +.PHONY: build_src_default +build_src_default: build_lib_default + cd src && OPTZ="${O2} -ggdb" PROXYSQLCLICKHOUSE=1 CC=${CC} CXX=${CXX} ${MAKE} + +.PHONY: build_src_debug_default +build_src_debug_default: build_lib_debug_default cd src && OPTZ="${O0} -ggdb -DDEBUG" PROXYSQLCLICKHOUSE=1 CC=${CC} CXX=${CXX} ${MAKE} ### packaging targets +SYS_KERN := $(shell uname -s) +#SYS_DIST := $(shell source /etc/os-release &>/dev/null; if [ -z ${NAME} ]; then head -1 /etc/redhat-release; else echo ${NAME}; fi | awk '{ print $1 }) SYS_ARCH := $(shell uname -m) REL_ARCH := $(subst x86_64,amd64,$(subst aarch64,arm64,$(SYS_ARCH))) RPM_ARCH := .$(SYS_ARCH) @@ -281,6 +352,8 @@ clean: cd lib && ${MAKE} clean cd src && ${MAKE} clean cd test/tap && ${MAKE} clean + cd test/deps && ${MAKE} clean + rm -f pkgroot || true .PHONY: cleanall cleanall: @@ -288,9 +361,11 @@ cleanall: cd lib && ${MAKE} clean cd src && ${MAKE} clean cd test/tap && ${MAKE} clean + cd test/deps && ${MAKE} cleanall rm -f binaries/*deb || true rm -f binaries/*rpm || true rm -f binaries/*id-hash || true + rm -rf pkgroot || true .PHONY: cleanbuild cleanbuild: @@ -331,7 +406,7 @@ ifeq ($(DISTRO),"Debian GNU/Linux") update-rc.d proxysql defaults else ifeq ($(DISTRO),"Unknown") - $(warning Not sure how to install proxysql service on this OS) + $(warning Not sure how to install proxysql service on this OS) endif endif endif @@ -368,7 +443,7 @@ ifeq ($(DISTRO),"Debian GNU/Linux") update-rc.d proxysql remove else ifeq ($(DISTRO),"Unknown") - $(warning Not sure how to uninstall proxysql service on this OS) +$(warning Not sure how to uninstall proxysql service on this OS) endif endif endif diff --git a/deps/Makefile b/deps/Makefile index 94b2e7627e..7fa0cec5b7 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -1,72 +1,57 @@ #!/bin/make -f -UNAME_S := $(shell uname -s) -UNAME_MS := $(word 2, $(shell uname -ms)) +SYS_KERN := $(shell uname -s) +SYS_ARCH := $(shell uname -m) +CENTOSVER := Unknown ifneq (,$(wildcard /etc/system-release)) CENTOSVER := $(shell rpm --eval %rhel) -else - CENTOSVER := Unknown endif -IS_ARM = $(if $(findstring aarch64, $(arch)),true,false) -IS_CENTOS = $(if $(findstring Unknown, $(CENTOSVER)),false,true) +IS_ARM := $(if $(findstring aarch64, $(SYS_ARCH)),true,false) +IS_CENTOS := $(if $(findstring Unknown, $(CENTOSVER)),false,true) # to compile libmariadb_client with support for valgrind enabled, run: # export USEVALGRIND=1 -# -USEVALGRIND := $(shell echo $(USEVALGRIND)) -PROXYDEBUG := $(shell echo $(PROXYDEBUG)) ifeq ($(PROXYDEBUG),1) -MYCFLAGS=-O0 -MYJEOPT=--enable-xmalloc --enable-prof --enable-fill --enable-debug - + MYCFLAGS := -O0 + MYJEOPT := --enable-xmalloc --enable-prof --enable-fill --enable-debug +else + MYCFLAGS := -O2 + MYJEOPT := --enable-xmalloc --enable-prof +endif ifeq ($(and $(IS_ARM),$(IS_CENTOS)),true) - MYJEOPT+=--with-lg-page=16 + MYJEOPT += --with-lg-page=16 endif -else -MYCFLAGS=-O2 -MYJEOPT=--enable-xmalloc --enable-prof -ifeq ($(and $(IS_ARM),$(IS_CENTOS)),true) - MYJEOPT+=--with-lg-page=16 +### detect compiler support for c++11/17 +CPLUSPLUS := $(shell ${CC} -std=c++17 -dM -E -x c++ /dev/null 2>/dev/null | grep -F __cplusplus | grep -Po '\d\d\d\d\d\dL') +ifneq ($(CPLUSPLUS),201703L) + CPLUSPLUS := $(shell ${CC} -std=c++11 -dM -E -x c++ /dev/null 2>/dev/null| grep -F __cplusplus | grep -Po '\d\d\d\d\d\dL') +ifneq ($(CPLUSPLUS),201103L) + $(error Compiler must support at least c++11) endif - endif +STDCPP := -std=c++$(shell echo $(CPLUSPLUS) | cut -c3-4) -DCXX$(shell echo $(CPLUSPLUS) | cut -c3-4) -# determine good compiler version for stdc++17 -#IS_CXX17 := 0 -#ifeq ($(CXX),clang++) -# CLANG_VERSION := $(shell clang -dumpversion | tr '.' ' ' | awk '{ printf("%04d.%04d.%04d", $$1, $$2, $$3) }') -# CLANG_MIN_VER := $(shell echo 14.0 | tr '.' ' ' | awk '{ printf("%04d.%04d.%04d", $$1, $$2, $$3) }') -#ifeq ($(CLANG_MIN_VER),$(firstword $(sort $(CLANG_VERSION) $(CLANG_MIN_VER)))) -# IS_CXX17 := 1 -#endif -#else -# GCC_VERSION := $(shell gcc -dumpfullversion 2>/dev/null || gcc -dumpversion | tr '.' ' ' | awk '{ printf("%04d.%04d.%04d", $$1, $$2, $$3) }') -# GCC_MIN_VER := $(shell echo 8.2.0 | tr '.' ' ' | awk '{ printf("%04d.%04d.%04d", $$1, $$2, $$3) }') -#ifeq ($(GCC_MIN_VER),$(firstword $(sort $(GCC_VERSION) $(GCC_MIN_VER)))) -# IS_CXX17 := 1 -#endif -#endif - - -PROXYSQLCLICKHOUSE := $(shell echo $(PROXYSQLCLICKHOUSE)) -tmpdefault=libconfig libdaemon jemalloc mariadb_client re2 sqlite3 pcre lz4 cityhash microhttpd curl ev libssl libhttpserver libinjection prometheus-cpp -ifeq ($(UNAME_S),Linux) - tmpdefault+= coredumper +targets := libconfig libdaemon jemalloc mariadb_client re2 sqlite3 pcre lz4 cityhash microhttpd curl ev libssl libhttpserver libinjection prometheus-cpp +ifeq ($(SYS_KERN),Linux) + targets += coredumper endif ifeq ($(PROXYSQLCLICKHOUSE),1) - tmpdefault+= clickhouse-cpp + targets += clickhouse-cpp endif -default: $(tmpdefault) + + +default: $(targets) .PHONY: default +### deps targets libinjection/libinjection/src/libinjection.a: cd libinjection && rm -rf libinjection-*/ || true @@ -75,7 +60,7 @@ ifneq ($(CENTOSVER),6) cd libinjection/libinjection && patch -p1 < ../update-build-py3.diff cd libinjection/libinjection && patch -p1 < ../libinjection_sqli.c.patch endif -ifeq ($(UNAME_S),Darwin) +ifeq ($(SYS_KERN),Darwin) sed -i '' 's/CC=/CC?=/' libinjection/libinjection/src/Makefile else sed -i -e 's/CC=/CC?=/' libinjection/libinjection/src/Makefile @@ -93,27 +78,16 @@ libssl/openssl/libssl.a: # cd libssl/openssl && patch crypto/asn1/a_time.c < ../a_time.c-multiplication-overflow.patch cd libssl/openssl && ./config no-ssl3 no-tests cd libssl/openssl && CC=${CC} CXX=${CXX} ${MAKE} - cd libssl/openssl && ln -s . lib # curl wants this path + cd libssl/openssl && ln -fsT . lib # curl wants this path libssl: libssl/openssl/libssl.a -MIN_VERSION := 4.9.0 -GCC_VERSION := $(shell gcc -dumpversion) -SORTED_VERSIONS := $(shell echo -e "$(GCC_VERSION)\n$(MIN_VERSION)" | sort -V) - -REQUIRE_PATCH = false -ifeq ($(MIN_VERSION),$(lastword $(SORTED_VERSIONS))) - REQUIRE_PATCH = true -endif - libhttpserver/libhttpserver/build/src/.libs/libhttpserver.a: libmicrohttpd/libmicrohttpd/src/microhttpd/.libs/libmicrohttpd.a re2/re2/obj/libre2.a cd libhttpserver && rm -rf libhttpserver-*/ || true cd libhttpserver && tar -zxf libhttpserver-*.tar.gz -#ifeq ($(REQUIRE_PATCH), true) cd libhttpserver/libhttpserver && patch -p1 < ../noexcept.patch cd libhttpserver/libhttpserver && patch -p1 < ../re2_regex.patch -#endif cd libhttpserver/libhttpserver && patch -p1 < ../final_val_post_process.patch cd libhttpserver/libhttpserver && patch -p1 < ../empty_uri_log_crash.patch cd libhttpserver/libhttpserver && patch -p0 < ../expose_raw_url.patch @@ -150,7 +124,7 @@ curl/curl/lib/.libs/libcurl.a: libssl/openssl/libssl.a cd curl && tar -zxf curl-*.tar.gz # cd curl/curl && ./configure --disable-debug --disable-ftp --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-manual --disable-ipv6 --disable-sspi --disable-crypto-auth --disable-ntlm-wb --disable-tls-srp --without-nghttp2 --without-libidn2 --without-libssh2 --without-brotli --with-ssl=$(shell pwd)/../../libssl/openssl/ && CC=${CC} CXX=${CXX} ${MAKE} cd curl/curl && autoreconf -fi -ifeq ($(UNAME_S),Darwin) +ifeq ($(SYS_KERN),Darwin) cd curl/curl && patch configure < ../configure.patch endif cd curl/curl && CFLAGS=-fPIC ./configure --disable-debug --disable-ftp --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-manual --disable-ipv6 --disable-sspi --disable-ntlm-wb --disable-tls-srp --without-nghttp2 --without-libidn2 --without-libssh2 --without-brotli --without-librtmp --without-libpsl --without-zstd --with-ssl=$(shell pwd)/libssl/openssl/ --enable-shared=yes && LD_LIBRARY_PATH=$(shell pwd)/libssl/openssl CC=${CC} CXX=${CXX} ${MAKE} @@ -186,17 +160,9 @@ lz4: lz4/lz4/lib/liblz4.a clickhouse-cpp/clickhouse-cpp/clickhouse/libclickhouse-cpp-lib-static.a: cd clickhouse-cpp && rm -rf clickhouse-cpp-*/ || true -#ifeq ($(IS_CXX17), 1) -# echo ">>> Clickhouse CXX17" - cd clickhouse-cpp && ln -fs clickhouse-cpp-2.3.0 clickhouse-cpp + cd clickhouse-cpp && ln -fsT clickhouse-cpp-2.3.0 clickhouse-cpp cd clickhouse-cpp && tar -zxf v2.3.0.tar.gz && sync cd clickhouse-cpp/clickhouse-cpp && patch clickhouse/base/wire_format.h < ../wire_format.patch -#else -# echo ">>> Clickhouse CXX11" -# cd clickhouse-cpp && ln -fs clickhouse-cpp-1.0.0 clickhouse-cpp -# cd clickhouse-cpp && tar -zxf v1.0.0.tar.gz && sync -# cd clickhouse-cpp && sed -i -e 's/SET (CMAKE_CXX_STANDARD_REQUIRED ON)//' clickhouse-cpp/cmake/cpp17.cmake -#endif cd clickhouse-cpp/clickhouse-cpp && cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo . cd clickhouse-cpp/clickhouse-cpp && CC=${CC} CXX=${CXX} ${MAKE} @@ -225,57 +191,49 @@ jemalloc/jemalloc/lib/libjemalloc.a: jemalloc: jemalloc/jemalloc/lib/libjemalloc.a -WITHASAN := $(shell echo $(WITHASAN)) - mariadb-client-library/mariadb_client/libmariadb/libmariadbclient.a: libssl/openssl/libssl.a cd mariadb-client-library && rm -rf mariadb-connector-c-*/ || true - cd mariadb-client-library && tar -zxf mariadb-connector-c-3.1.9-src.tar.gz - cd mariadb-client-library/mariadb_client && patch ./plugins/auth/CMakeLists.txt < ../plugin_auth_CMakeLists.txt.patch - cd mariadb-client-library/mariadb_client && patch -p0 < ../ConnectorName.patch + cd mariadb-client-library && tar -zxf mariadb-connector-c-3.3.8-src.tar.gz + cd mariadb-client-library/mariadb_client && patch -p0 < ../plugin_auth_CMakeLists.txt.patch cd mariadb-client-library/mariadb_client && cmake . -Wno-dev -DOPENSSL_ROOT_DIR=$(shell pwd)/libssl/openssl/ -DOPENSSL_LIBRARIES=$(shell pwd)/libssl/openssl/ -DICONV_LIBRARIES=$(brew --prefix libiconv)/lib -DICONV_INCLUDE=$(brew --prefix libiconv)/include . ifeq ($(PROXYDEBUG),1) - cd mariadb-client-library/mariadb_client && patch include/ma_context.h < ../ma_context.h.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../ma_context.h.patch else ifeq ($(USEVALGRIND),1) - cd mariadb-client-library/mariadb_client && patch include/ma_context.h < ../ma_context.h.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../ma_context.h.patch endif - cd mariadb-client-library/mariadb_client && patch libmariadb/mariadb_stmt.c < ../mariadb_stmt.c.patch - cd mariadb-client-library/mariadb_client && patch libmariadb/mariadb_lib.c < ../mariadb_lib.c.patch - cd mariadb-client-library/mariadb_client && patch libmariadb/mariadb_lib.c < ../mariadb_lib.c.collation.patch # make sure this path is applied after mariadb_lib.c.patch - cd mariadb-client-library/mariadb_client && patch libmariadb/mariadb_lib.c < ../mariadb_lib.c.ipv6_fix.patch # make sure this patch is applied after mariadb_lib.c.collation.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../mariadb_stmt.c.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../mariadb_lib.c.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../mariadb_lib.c.collation.patch # make sure this path is applied after mariadb_lib.c.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../mariadb_lib.c.ipv6_fix.patch # make sure this patch is applied after mariadb_lib.c.collation.patch # cd mariadb-client-library/mariadb_client && patch libmariadb/net.c < ../net.c.patch - cd mariadb-client-library/mariadb_client && patch libmariadb/mariadb_async.c < ../mariadb_async.c.patch - cd mariadb-client-library/mariadb_client && patch libmariadb/ma_password.c < ../ma_password.c.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../mariadb_async.c.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../ma_password.c.patch # cd mariadb-client-library/mariadb_client && patch libmariadb/ma_secure.c < ../ma_secure.c.patch - cd mariadb-client-library/mariadb_client && patch include/mysql.h < ../mysql.h.patch - cd mariadb-client-library/mariadb_client && patch include/mariadb_com.h < ../mariadb_com.h.patch - cd mariadb-client-library/mariadb_client && patch include/ma_priv.h < ../ma_priv.h.patch - cd mariadb-client-library/mariadb_client && patch libmariadb/ma_alloc.c < ../ma_alloc.c.patch - cd mariadb-client-library/mariadb_client && patch libmariadb/ma_charset.c < ../ma_charset.c.patch -# cd mariadb-client-library/mariadb_client && patch libmariadb/ma_pvio.c < ../ma_pvio.c.patch - cd mariadb-client-library/mariadb_client && patch unittest/libmariadb/basic-t.c < ../unittest_basic-t.c.patch - cd mariadb-client-library/mariadb_client && patch unittest/libmariadb/charset.c < ../unittest_charset.c.patch - cd mariadb-client-library/mariadb_client && patch -p0 < ../client_deprecate_eof.patch - cd mariadb-client-library/mariadb_client && patch -p0 < ../cr_new_stmt_metadata_removal.patch - cd mariadb-client-library/mariadb_client && patch -p0 < ../ps_buffer_stmt_read_all_rows.patch - cd mariadb-client-library/mariadb_client && patch -p0 < ../empty_split_compress_packet.patch - cd mariadb-client-library/mariadb_client && patch -p0 < ../mariadb_stmt_store_result_err.patch - + cd mariadb-client-library/mariadb_client && patch -p0 < ../mysql.h.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../ma_priv.h.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../ma_alloc.c.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../ma_charset.c.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../unittest_basic-t.c.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../unittest_charset.c.patch ifeq ($(WITHASAN),1) cd mariadb-client-library/mariadb_client && patch -p0 < ../mariadb_asan.patch endif - cd mariadb-client-library/mariadb_client && patch libmariadb/mariadb_dyncol.c < ../mariadb_dyncol.c-multiplication-overflow.patch - cd mariadb-client-library/mariadb_client && patch libmariadb/ma_array.c < ../ma_array.c-multiplication-overflow.patch - cd mariadb-client-library/mariadb_client && patch zlib/zutil.c < ../zutil.c-multiplication-overflow.patch - # patches for replication testing - cd mariadb-client-library/mariadb_client && patch libmariadb/mariadb_rpl.c < ../mariadb_rpl.c.patch - cd mariadb-client-library/mariadb_client && patch include/mariadb_rpl.h < ../mariadb_rpl.h.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../mariadb_dyncol.c-multiplication-overflow.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../ma_array.c-multiplication-overflow.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../zutil.c-multiplication-overflow.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../cr_new_stmt_metadata_removal.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../ps_buffer_stmt_read_all_rows.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../mariadb_stmt_store_result_err.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../empty_split_compress_packet.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../sslkeylogfile.patch + #cd mariadb-client-library/mariadb_client && patch libmariadb/ma_pvio.c < ../ma_pvio.c.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../client_deprecate_eof.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../mariadb_com.h.patch # patches for x509 cache . See https://github.com/sysown/proxysql/issues/4117 (Slow connection time with SSL and large CA file , relevant on Aurora) - cd mariadb-client-library/mariadb_client && patch libmariadb/mariadb_lib.c < ../mariadb_lib.c.x509cache.patch - cd mariadb-client-library/mariadb_client && patch libmariadb/secure/openssl.c < ../openssl.c.x509cache.patch - cd mariadb-client-library/mariadb_client && patch include/mysql.h < ../mysql.h.sslkeylogfile.patch - cd mariadb-client-library/mariadb_client && patch include/ma_common.h < ../ma_common.h.sslkeylogfile.patch - cd mariadb-client-library/mariadb_client && patch libmariadb/mariadb_lib.c < ../mariadb_lib.c.sslkeylogfile.patch - cd mariadb-client-library/mariadb_client && patch libmariadb/secure/openssl.c < ../openssl.c.sslkeylogfile.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../x509cache.patch + # patches for replication testing + cd mariadb-client-library/mariadb_client && patch -p0 < ../mariadb_rpl.patch + cd mariadb-client-library/mariadb_client && patch -p0 < ../cmakelists.txt.patch cd mariadb-client-library/mariadb_client && CC=${CC} CXX=${CXX} ${MAKE} mariadbclient # cd mariadb-client-library/mariadb_client/include && make my_config.h @@ -321,11 +279,11 @@ re2/re2/obj/libre2.a: # cd re2/re2 && sed -i -e 's/-O3 -g /-O3 -fPIC /' Makefile # cd re2/re2 && patch util/mutex.h < ../mutex.h.patch cd re2/re2 && patch re2/onepass.cc < ../onepass.cc-multiplication-overflow.patch -ifeq ($(UNAME_S),Darwin) - cd re2/re2 && sed -i '' -e 's/-O3 -g/-O3 -g -std=c++11 -fPIC -DMEMORY_SANITIZER -DRE2_ON_VALGRIND /' Makefile +ifeq ($(SYS_KERN),Darwin) + cd re2/re2 && sed -i '' -e 's/-O3 -g/-O3 -g -${STDCPP} -fPIC -DMEMORY_SANITIZER -DRE2_ON_VALGRIND /' Makefile # cd re2/re2 && sed -i '' -e 's/RE2_CXXFLAGS?=-std=c++11 /RE2_CXXFLAGS?=-std=c++11 -fPIC /' Makefile else - cd re2/re2 && sed -i -e 's/-O3 -g/-O3 -g -std=c++11 -fPIC -DMEMORY_SANITIZER -DRE2_ON_VALGRIND /' Makefile + cd re2/re2 && sed -i -e 's/-O3 -g/-O3 -g ${STDCPP} -fPIC -DMEMORY_SANITIZER -DRE2_ON_VALGRIND /' Makefile # cd re2/re2 && sed -i -e 's/RE2_CXXFLAGS?=-std=c++11 /RE2_CXXFLAGS?=-std=c++11 -fPIC /' Makefile endif cd re2/re2 && CC=${CC} CXX=${CXX} ${MAKE} @@ -343,6 +301,7 @@ pcre/pcre/.libs/libpcre.a: pcre: pcre/pcre/.libs/libpcre.a +### clean targets cleanpart: cd mariadb-client-library && rm -rf mariadb-connector-c-*/ || true diff --git a/deps/mariadb-client-library/ConnectorName.patch b/deps/mariadb-client-library/ConnectorName.patch deleted file mode 100644 index 3227bd1d7c..0000000000 --- a/deps/mariadb-client-library/ConnectorName.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git cmake/ConnectorName.cmake cmake/ConnectorName.cmake -index b7bbbad..357b8ac 100644 ---- cmake/ConnectorName.cmake -+++ cmake/ConnectorName.cmake -@@ -22,7 +22,7 @@ IF(CMAKE_SYSTEM_NAME MATCHES "Windows") - SET(MACHINE_NAME "x64") - ELSE() - SET(MACHINE_NAME "32") -- END() -+ ENDIF() - ENDIF() - - SET(product_name "mysql-connector-c-${CPACK_PACKAGE_VERSION}-${PLATFORM_NAME}${CONCAT_SIGN}${MACHINE_NAME}") diff --git a/deps/mariadb-client-library/client_deprecate_eof.patch b/deps/mariadb-client-library/client_deprecate_eof.patch index 2c7a611f50..bf8a89822e 100644 --- a/deps/mariadb-client-library/client_deprecate_eof.patch +++ b/deps/mariadb-client-library/client_deprecate_eof.patch @@ -1,10 +1,10 @@ diff --git include/ma_common.h include/ma_common.h -index 62cf71c..c05e9a3 100644 +index 0d3f39b3..112cd3b5 100644 --- include/ma_common.h +++ include/ma_common.h -@@ -23,6 +23,8 @@ - #include - #include +@@ -24,6 +24,8 @@ + #include + #include +#define MAX_PACKET_LENGTH (256L*256L*256L-1) + @@ -12,37 +12,36 @@ index 62cf71c..c05e9a3 100644 COM_MULTI_OFF= 0, COM_MULTI_CANCEL, diff --git include/mariadb_com.h include/mariadb_com.h -index 948d96e..225b2f5 100644 +index 67461b4f..96e6e04f 100644 --- include/mariadb_com.h +++ include/mariadb_com.h -@@ -160,6 +160,7 @@ enum enum_server_command - #define CLIENT_CONNECT_ATTRS (1UL << 20) +@@ -161,6 +161,7 @@ enum enum_server_command + #define CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA (1UL << 21) #define CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS (1UL << 22) #define CLIENT_SESSION_TRACKING (1UL << 23) +#define CLIENT_DEPRECATE_EOF (1UL << 24) + #define CLIENT_ZSTD_COMPRESSION (1UL << 26) #define CLIENT_PROGRESS (1UL << 29) /* client supports progress indicator */ #define CLIENT_PROGRESS_OBSOLETE CLIENT_PROGRESS - #define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30) -@@ -204,8 +205,14 @@ enum enum_server_command +@@ -209,7 +210,13 @@ enum enum_server_command CLIENT_REMEMBER_OPTIONS |\ CLIENT_PLUGIN_AUTH |\ CLIENT_SESSION_TRACKING |\ + CLIENT_DEPRECATE_EOF |\ CLIENT_CONNECT_ATTRS) - +/* Client capabilities *does no* longer holds 'CLIENT_DEPRECATE_EOF' flag + * by default. This is part of ProxySQL feature for conditionally disabling + * 'deprecate_eof' support in both client and backend connections. For more + * context see #3280. -+ */ - #define CLIENT_CAPABILITIES (CLIENT_MYSQL | \ ++ */ + #define CLIENT_CAPABILITIES (CLIENT_MYSQL | \ CLIENT_LONG_FLAG |\ CLIENT_TRANSACTIONS |\ diff --git include/mariadb_stmt.h include/mariadb_stmt.h -index d07540e..4930573 100644 +index 531c2181..1ae9cf99 100644 --- include/mariadb_stmt.h +++ include/mariadb_stmt.h -@@ -257,7 +257,7 @@ typedef struct st_mysql_perm_bind { +@@ -209,7 +209,7 @@ typedef struct st_mysql_perm_bind { } MYSQL_PS_CONVERSION; extern MYSQL_PS_CONVERSION mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY + 1]; @@ -52,7 +51,7 @@ index d07540e..4930573 100644 unsigned long net_field_length(unsigned char **packet); int ma_simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg, diff --git libmariadb/ma_net.c libmariadb/ma_net.c -index 15be4fc..a69e637 100644 +index 0b1d0cce..83bf729a 100644 --- libmariadb/ma_net.c +++ libmariadb/ma_net.c @@ -39,8 +39,6 @@ @@ -65,10 +64,10 @@ index 15be4fc..a69e637 100644 See bug conc-57 */ diff --git libmariadb/mariadb_lib.c libmariadb/mariadb_lib.c -index e475e25..1a04b2f 100644 +index 75e3c9a4..916024a8 100644 --- libmariadb/mariadb_lib.c +++ libmariadb/mariadb_lib.c -@@ -144,6 +144,7 @@ struct st_mariadb_methods MARIADB_DEFAULT_METHODS; +@@ -150,6 +150,7 @@ struct st_mariadb_methods MARIADB_DEFAULT_METHODS; #define IS_CONNHDLR_ACTIVE(mysql)\ ((mysql)->extension && (mysql)->extension->conn_hdlr) @@ -76,7 +75,7 @@ index e475e25..1a04b2f 100644 static void end_server(MYSQL *mysql); static void mysql_close_memory(MYSQL *mysql); void read_user_name(char *name); -@@ -190,11 +191,15 @@ void net_get_error(char *buf, size_t buf_len, +@@ -196,11 +197,15 @@ void net_get_error(char *buf, size_t buf_len, *****************************************************************************/ ulong @@ -93,45 +92,44 @@ index e475e25..1a04b2f 100644 restart: if (net->pvio != 0) len=ma_net_read(net); -@@ -251,6 +256,28 @@ restart: +@@ -263,6 +268,27 @@ restart: return(packet_error); } + else + { + if (is_data_packet) { -+ unsigned long deprecate_eof = -+ mysql->server_capabilities & CLIENT_DEPRECATE_EOF; -+ *is_data_packet = TRUE; -+ -+ // NOTE: -+ // The only possible alternative to an okay packet where the first byte is -+ // 0xfe, is a packet of int which is bigger than 0xffffff. This is -+ // the reason why a check 'len <= MAX_PACKET_LENGTH' guarantees that -+ // *by_exclusion* the current packet is an 'ok_packet'. -+ if (deprecate_eof && (net->read_pos[0] == 254 && len <= MAX_PACKET_LENGTH)) { -+ *is_data_packet = FALSE; -+ } -+ // A '< 9' check guarantees that it's an EOF packet. -+ else if (!deprecate_eof && (net->read_pos[0] == 254 && len < 9)) -+ { -+ *is_data_packet = FALSE; -+ } ++ unsigned long deprecate_eof = mysql->server_capabilities & CLIENT_DEPRECATE_EOF; ++ *is_data_packet = TRUE; ++ ++ // NOTE: ++ // The only possible alternative to an okay packet where the first byte is ++ // 0xfe, is a packet of int which is bigger than 0xffffff. This is ++ // the reason why a check 'len <= MAX_PACKET_LENGTH' guarantees that ++ // *by_exclusion* the current packet is an 'ok_packet'. ++ if (deprecate_eof && (net->read_pos[0] == 254 && len <= MAX_PACKET_LENGTH)) { ++ *is_data_packet = FALSE; ++ } ++ // A '< 9' check guarantees that it's an EOF packet. ++ else if (!deprecate_eof && (net->read_pos[0] == 254 && len < 9)) ++ { ++ *is_data_packet = FALSE; ++ } + } + } return len; } -@@ -424,7 +451,7 @@ mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg, +@@ -448,7 +474,7 @@ mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg, - if (!skipp_check) + if (!skip_check) { - result= ((mysql->packet_length=ma_net_safe_read(mysql)) == packet_error ? + result= ((mysql->packet_length=ma_net_safe_read(mysql, NULL)) == packet_error ? 1 : 0); } end: -@@ -565,12 +592,14 @@ end_server(MYSQL *mysql) +@@ -581,12 +607,14 @@ end_server(MYSQL *mysql) void mthd_my_skip_result(MYSQL *mysql) { ulong pkt_len; @@ -148,29 +146,29 @@ index e475e25..1a04b2f 100644 return; } -@@ -851,6 +880,95 @@ static size_t rset_field_offsets[]= { +@@ -1021,6 +1049,95 @@ static size_t rset_field_offsets[]= { OFFSET(MYSQL_FIELD, org_name_length) }; +/** -+ * @brief Helper function to unpack a particular field. -+ * -+ * NOTE: This function have been introduced due to the requirement -+ * of 'field unpacking' in mthd_my_read_metadata_ex. Extracting the -+ * logic from 'unpack_fields' function. -+ * -+ * @param mysql The mysql connection handler. -+ * @param alloc The mem_root allocator. -+ * @param default_value The default value to use for the field in case -+ * of not containing one. -+ * @param row The row containing the field description. -+ * @param field Output parameter holding the unpacked MYSQL_FIELD. -+ * -+ * @returns 0 on success. -+ */ ++ * @brief Helper function to unpack a particular field. ++ * ++ * NOTE: This function have been introduced due to the requirement ++ * of 'field unpacking' in mthd_my_read_metadata_ex. Extracting the ++ * logic from 'unpack_fields' function. ++ * ++ * @param mysql The mysql connection handler. ++ * @param alloc The mem_root allocator. ++ * @param default_value The default value to use for the field in case ++ * of not containing one. ++ * @param row The row containing the field description. ++ * @param field Output parameter holding the unpacked MYSQL_FIELD. ++ * ++ * @returns 0 on success. ++ */ +int +unpack_field(const MYSQL *mysql, MA_MEM_ROOT *alloc, my_bool default_value, -+ MYSQL_ROWS *row, MYSQL_FIELD *field) ++ MYSQL_ROWS *row, MYSQL_FIELD *field, ulong* field_length) +{ + unsigned int i, field_count = sizeof(rset_field_offsets) / sizeof(size_t) / 2; + char *p = NULL; @@ -181,15 +179,15 @@ index e475e25..1a04b2f 100644 + { + // Initialize in case of empty field + if (row->data[i][0] == 0) { -+ *(char **)(((char *)field) + rset_field_offsets[i*2]) = ma_strdup_root(alloc, ""); -+ *(unsigned int *)(((char *)field) + rset_field_offsets[i*2+1]) = 0; ++ *(char **)(((char *)field) + rset_field_offsets[i*2]) = ma_strdup_root(alloc, ""); ++ *(unsigned int *)(((char *)field) + rset_field_offsets[i*2+1]) = 0; + } + // Copy data in case of not empty + else + { -+ uint length = (uint)(row->data[i+1] - row->data[i] - 1); ++ uint length = ((field_length != NULL) ? (uint)field_length[i] : (uint)(row->data[i+1] - row->data[i] - 1)); + *(char **)(((char *)field) + rset_field_offsets[i*2]) = -+ ma_strdup_root(alloc, (char *)row->data[i]); ++ ma_strdup_root(alloc, (char *)row->data[i]); + *(unsigned int *)(((char *)field) + rset_field_offsets[i*2+1]) = + length; + } @@ -228,9 +226,9 @@ index e475e25..1a04b2f 100644 + field->flags|= NUM_FLAG; + + i++; -+ /* This is used by deprecated function mysql_list_fields only, -+ however the reported length is not correct, so we always zero it */ -+ if (default_value && row->data[i]) ++ /*This is used by deprecated function mysql_list_fields only, ++ however the reported length is not correct, so we always zero it */ ++ if (default_value && row->data[i]) + field->def = ma_strdup_root(alloc,(char*) row->data[i]); + else + field->def = 0; @@ -244,7 +242,7 @@ index e475e25..1a04b2f 100644 MYSQL_FIELD * unpack_fields(const MYSQL *mysql, MYSQL_DATA *data, MA_MEM_ROOT *alloc, uint fields, -@@ -858,71 +976,19 @@ unpack_fields(const MYSQL *mysql, +@@ -1028,71 +1145,19 @@ unpack_fields(const MYSQL *mysql, { MYSQL_ROWS *row; MYSQL_FIELD *field,*result; @@ -264,7 +262,7 @@ index e475e25..1a04b2f 100644 - for (i=0; i < field_count; i++) - { - uint length= (uint)(row->data[i+1] - row->data[i] - 1); -- if (!row->data[i] && row->data[i][length]) +- if (!row->data[i] || row->data[i][length]) - goto error; - - *(char **)(((char *)field) + rset_field_offsets[i*2])= @@ -314,12 +312,12 @@ index e475e25..1a04b2f 100644 - field->def_length= 0; - - field->max_length= 0; -+ if (unpack_field(mysql, alloc, default_value, row, field) != 0) ++ if (unpack_field(mysql, alloc, default_value, row, field, NULL) != 0) + goto error; } if (field < result + fields) goto error; -@@ -948,8 +1014,9 @@ MYSQL_DATA *mthd_my_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, +@@ -1118,8 +1183,9 @@ MYSQL_DATA *mthd_my_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, MYSQL_DATA *result; MYSQL_ROWS **prev_ptr,*cur; NET *net = &mysql->net; @@ -330,7 +328,7 @@ index e475e25..1a04b2f 100644 return(0); if (!(result=(MYSQL_DATA*) calloc(1, sizeof(MYSQL_DATA)))) { -@@ -962,7 +1029,7 @@ MYSQL_DATA *mthd_my_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, +@@ -1132,7 +1198,7 @@ MYSQL_DATA *mthd_my_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, result->rows=0; result->fields=fields; @@ -339,7 +337,7 @@ index e475e25..1a04b2f 100644 { result->rows++; if (!(cur= (MYSQL_ROWS*) ma_alloc_root(&result->alloc, -@@ -1005,7 +1072,7 @@ MYSQL_DATA *mthd_my_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, +@@ -1175,7 +1241,7 @@ MYSQL_DATA *mthd_my_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, } } cur->data[field]=to; /* End of last field */ @@ -348,10 +346,10 @@ index e475e25..1a04b2f 100644 { free_rows(result); return(0); -@@ -1015,10 +1082,14 @@ MYSQL_DATA *mthd_my_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, - /* save status */ +@@ -1186,10 +1252,14 @@ MYSQL_DATA *mthd_my_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, if (pkt_len > 1) { + unsigned int last_status= mysql->server_status; - cp++; - mysql->warning_count= uint2korr(cp); - cp+= 2; @@ -359,15 +357,15 @@ index e475e25..1a04b2f 100644 + // read the final EOF or OK packet + if (mysql->server_capabilities & CLIENT_DEPRECATE_EOF) + { -+ ma_read_ok_packet(mysql, cp + 1, pkt_len); ++ ma_read_ok_packet(mysql, cp + 1, pkt_len); + } else { -+ mysql->warning_count = uint2korr(cp + 1); -+ mysql->server_status = uint2korr(cp + 3); ++ mysql->warning_count = uint2korr(cp + 1); ++ mysql->server_status = uint2korr(cp + 3); + } + ma_status_callback(mysql, last_status) } return(result); - } -@@ -1035,15 +1106,21 @@ int mthd_my_read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths) +@@ -1207,15 +1277,21 @@ int mthd_my_read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths) uint field; ulong pkt_len,len; uchar *pos,*prev_pos, *end_pos; @@ -381,20 +379,19 @@ index e475e25..1a04b2f 100644 + // we are reading either final 'OK' or 'EOF' packet + if (mysql->net.read_pos[0] != 0 && !is_data_packet) { + unsigned int last_status= mysql->server_status; - mysql->warning_count= uint2korr(mysql->net.read_pos + 1); - mysql->server_status= uint2korr(mysql->net.read_pos + 3); -- return 1; /* End of data */ + if (mysql->server_capabilities & CLIENT_DEPRECATE_EOF) { + ma_read_ok_packet(mysql, mysql->net.read_pos + 1, pkt_len); + } else { + mysql->warning_count= uint2korr(mysql->net.read_pos + 1); + mysql->server_status= uint2korr(mysql->net.read_pos + 3); + } -+ return 1; + ma_status_callback(mysql, last_status); + return 1; /* End of data */ } - prev_pos= 0; /* allowed to write at packet[-1] */ - pos=mysql->net.read_pos; -@@ -1077,6 +1154,85 @@ int mthd_my_read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths) +@@ -1251,6 +1327,85 @@ int mthd_my_read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths) return 0; } @@ -438,27 +435,27 @@ index e475e25..1a04b2f 100644 + // read the columns info + for (f_i = 0; f_i < field_count; f_i++) + { -+ if (mthd_my_read_one_row(mysql, m_field_count, m_rows.data, m_len) == -1) -+ return NULL; -+ if (unpack_field(mysql, mem_root, 0, &m_rows, m_fields++)) -+ return NULL; ++ if (mthd_my_read_one_row(mysql, m_field_count, m_rows.data, m_len) == -1) ++ return NULL; ++ if (unpack_field(mysql, mem_root, 0, &m_rows, m_fields++, m_len)) ++ return NULL; + } + // read EOF packet in case of client not supporting 'CLIENT_DEPRECATE_EOF' + if (!(mysql->server_capabilities & CLIENT_DEPRECATE_EOF)) + { + // read the EOF packet + if (packet_error == ma_net_safe_read(mysql, NULL)) -+ return 0; ++ return 0; + -+ // verify it's actually an EOF packet -+ if (*(mysql->net.read_pos) == 254) -+ { -+ mysql->warning_count = uint2korr(mysql->net.read_pos + 1); -+ mysql->server_status = uint2korr(mysql->net.read_pos + 3); -+ } ++ // verify it's actually an EOF packet ++ if (*(mysql->net.read_pos) == 254) ++ { ++ mysql->warning_count = uint2korr(mysql->net.read_pos + 1); ++ mysql->server_status = uint2korr(mysql->net.read_pos + 3); + } ++ } + -+ return result; ++ return result; +} + +/** @@ -474,13 +471,13 @@ index e475e25..1a04b2f 100644 + */ +MYSQL_FIELD* mthd_my_read_metadata(MYSQL* mysql, ulong field_count, uint m_field_count) +{ -+ return mthd_my_read_metadata_ex(mysql, &mysql->field_alloc, field_count, m_field_count); ++ return mthd_my_read_metadata_ex(mysql, &mysql->field_alloc, field_count, m_field_count); +} + /**************************************************************************** ** Init MySQL structure or allocate one ****************************************************************************/ -@@ -1494,7 +1650,7 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, +@@ -1793,7 +1948,7 @@ restart: goto error; } */ @@ -489,7 +486,7 @@ index e475e25..1a04b2f 100644 { if (mysql->net.last_errno == CR_SERVER_LOST) my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN, -@@ -1596,6 +1752,16 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, +@@ -1894,6 +2049,16 @@ restart: } } @@ -506,14 +503,15 @@ index e475e25..1a04b2f 100644 /* pad 2 */ end+= 18; -@@ -2361,14 +2527,13 @@ int mthd_my_read_query_result(MYSQL *mysql) +@@ -2856,7 +3021,6 @@ int mthd_my_read_query_result(MYSQL *mysql) { uchar *pos; ulong field_count; - MYSQL_DATA *fields; ulong length; - my_bool can_local_infile= (mysql->options.extension) && (mysql->extension->auto_local_infile != WAIT_FOR_QUERY); - + const uchar *end; + uchar has_metadata; +@@ -2866,7 +3030,7 @@ int mthd_my_read_query_result(MYSQL *mysql) if (mysql->options.extension && mysql->extension->auto_local_infile == ACCEPT_FILE_REQUEST) mysql->extension->auto_local_infile= WAIT_FOR_QUERY; @@ -522,7 +520,7 @@ index e475e25..1a04b2f 100644 { return(1); } -@@ -2381,7 +2546,7 @@ get_info: +@@ -2880,7 +3044,7 @@ get_info: { int error=mysql_handle_local_infile(mysql, (char *)pos, can_local_infile); @@ -531,27 +529,34 @@ index e475e25..1a04b2f 100644 return(-1); goto get_info; /* Get info packet */ } -@@ -2389,12 +2554,13 @@ get_info: - mysql->server_status|= SERVER_STATUS_IN_TRANS; - - mysql->extra_info= net_field_length_ll(&pos); /* Maybe number of rec */ -- if (!(fields=mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0, -- ma_result_set_rows(mysql)))) -- return(-1); -- if (!(mysql->fields=unpack_fields(mysql, fields, &mysql->field_alloc, -- (uint) field_count, 1))) -- return(-1); -+ // read packet metadata -+ mysql->fields = -+ mthd_my_read_metadata(mysql, field_count, 7 + ma_extended_type_info_rows(mysql)); -+ if (!mysql->fields) { -+ ma_free_root(&mysql->field_alloc, MYF(0)); -+ return(1); -+ } - mysql->status=MYSQL_STATUS_GET_RESULT; - mysql->field_count=field_count; - return(0); -@@ -2775,21 +2941,17 @@ mysql_list_fields(MYSQL *mysql, const char *table, const char *wild) +@@ -2901,18 +3065,19 @@ get_info: + + if (has_metadata) + { +- if (!(fields= mysql->methods->db_read_rows(mysql, (MYSQL_FIELD *) 0, +- ma_result_set_rows(mysql)))) +- return (-1); +- if (!(mysql->fields= unpack_fields(mysql, fields, &mysql->field_alloc, +- (uint) field_count, 1))) +- return (-1); ++ // read packet metadata ++ mysql->fields = ++ mthd_my_read_metadata(mysql, field_count, 7 + ma_extended_type_info_rows(mysql)); ++ if (!mysql->fields) { ++ ma_free_root(&mysql->field_alloc, MYF(0)); ++ return(1); ++ } + } + else + { + unsigned int last_status= mysql->server_status; + /* Read EOF, to get the status and warning count. */ +- if ((length= ma_net_safe_read(mysql)) == packet_error) ++ if ((length= ma_net_safe_read(mysql, NULL)) == packet_error) + { + return -1; + } +@@ -3309,20 +3474,16 @@ mysql_list_fields(MYSQL *mysql, const char *table, const char *wild) MYSQL_RES * STDCALL mysql_list_processes(MYSQL *mysql) { @@ -569,14 +574,12 @@ index e475e25..1a04b2f 100644 - return(NULL); - if (!(mysql->fields= unpack_fields(mysql, fields, &mysql->field_alloc, - field_count, 0))) -- return(NULL); + mysql->fields= mthd_my_read_metadata(mysql, field_count, 7); + if (!mysql->fields) -+ return(NULL); + return(NULL); mysql->status=MYSQL_STATUS_GET_RESULT; mysql->field_count=field_count; - return(mysql_store_result(mysql)); -@@ -4251,7 +4413,7 @@ mysql_debug(const char *debug __attribute__((unused))) +@@ -4830,7 +4991,7 @@ mysql_debug(const char *debug __attribute__((unused))) *********************************************************************/ ulong STDCALL mysql_net_read_packet(MYSQL *mysql) { @@ -586,23 +589,23 @@ index e475e25..1a04b2f 100644 ulong STDCALL mysql_net_field_length(uchar **packet) diff --git libmariadb/mariadb_rpl.c libmariadb/mariadb_rpl.c -index 6a7e0cf..492727d 100644 +index 0019f246..daa6d242 100644 --- libmariadb/mariadb_rpl.c +++ libmariadb/mariadb_rpl.c -@@ -122,7 +122,7 @@ MARIADB_RPL_EVENT * STDCALL mariadb_rpl_fetch(MARIADB_RPL *rpl, MARIADB_RPL_EVEN - return 0; +@@ -1021,7 +1021,7 @@ MARIADB_RPL_EVENT * STDCALL mariadb_rpl_fetch(MARIADB_RPL *rpl, MARIADB_RPL_EVEN - while (1) { -- unsigned long pkt_len= ma_net_safe_read(rpl->mysql); -+ unsigned long pkt_len= ma_net_safe_read(rpl->mysql, NULL); - - if (pkt_len == packet_error) + if (rpl->mysql) { +- pkt_len= ma_net_safe_read(rpl->mysql); ++ pkt_len= ma_net_safe_read(rpl->mysql, NULL); + + if (pkt_len == packet_error) + { diff --git libmariadb/mariadb_stmt.c libmariadb/mariadb_stmt.c -index 0aaaf1a..229023b 100644 +index d6df0825..af442088 100644 --- libmariadb/mariadb_stmt.c +++ libmariadb/mariadb_stmt.c -@@ -78,6 +78,9 @@ typedef struct +@@ -80,6 +80,9 @@ typedef struct } MADB_STMT_EXTENSION; static my_bool net_stmt_close(MYSQL_STMT *stmt, my_bool remove); @@ -612,18 +615,19 @@ index 0aaaf1a..229023b 100644 static my_bool is_not_null= 0; static my_bool is_null= 1; -@@ -153,8 +156,9 @@ static int stmt_unbuffered_eof(MYSQL_STMT *stmt __attribute__((unused)), +@@ -171,8 +174,9 @@ static int stmt_unbuffered_eof(MYSQL_STMT *stmt __attribute__((unused)), static int stmt_unbuffered_fetch(MYSQL_STMT *stmt, uchar **row) { ulong pkt_len; -+ my_bool is_data_packet; - +- - pkt_len= ma_net_safe_read(stmt->mysql); ++ my_bool is_data_packet; ++ + pkt_len= ma_net_safe_read(stmt->mysql, &is_data_packet); if (pkt_len == packet_error) { -@@ -162,8 +166,10 @@ static int stmt_unbuffered_fetch(MYSQL_STMT *stmt, uchar **row) +@@ -180,8 +184,11 @@ static int stmt_unbuffered_fetch(MYSQL_STMT *stmt, uchar **row) return(1); } @@ -631,11 +635,12 @@ index 0aaaf1a..229023b 100644 + if (stmt->mysql->net.read_pos[0] != 0 && !is_data_packet) { + if (stmt->mysql->server_capabilities & CLIENT_DEPRECATE_EOF) -+ ma_read_ok_packet(stmt->mysql, stmt->mysql->net.read_pos + 1, pkt_len); ++ ma_read_ok_packet(stmt->mysql, stmt->mysql->net.read_pos + 1, pkt_len); ++ *row = NULL; stmt->fetch_row_func= stmt_unbuffered_eof; return(MYSQL_NO_DATA); -@@ -195,13 +201,15 @@ int mthd_stmt_read_all_rows(MYSQL_STMT *stmt) +@@ -213,10 +220,11 @@ int mthd_stmt_read_all_rows(MYSQL_STMT *stmt) MYSQL_ROWS *current, **pprevious; ulong packet_len; unsigned char *p; @@ -646,6 +651,11 @@ index 0aaaf1a..229023b 100644 - while ((packet_len = ma_net_safe_read(stmt->mysql)) != packet_error) + while ((packet_len = ma_net_safe_read(stmt->mysql, &is_data_packet)) != packet_error) { + // This change is required due to the new algorithm introduced in ProxySQL in #3295. + // ******************************************************************************** +@@ -237,7 +245,8 @@ int mthd_stmt_read_all_rows(MYSQL_STMT *stmt) + // ******************************************************************************** + p= stmt->mysql->net.read_pos; - if (packet_len > 7 || p[0] != 254) + // The check is by 'ma_net_safe_read' @@ -653,15 +663,14 @@ index 0aaaf1a..229023b 100644 { /* allocate space for rows */ if (!(current= (MYSQL_ROWS *)ma_alloc_root(&result->alloc, sizeof(MYSQL_ROWS) + packet_len))) -@@ -276,10 +284,16 @@ int mthd_stmt_read_all_rows(MYSQL_STMT *stmt) - { +@@ -324,10 +333,14 @@ int mthd_stmt_read_all_rows(MYSQL_STMT *stmt) + unsigned int last_status= stmt->mysql->server_status; *pprevious= 0; /* sace status info */ - p++; - stmt->upsert_status.warning_count= stmt->mysql->warning_count= uint2korr(p); - p+=2; - stmt->upsert_status.server_status= stmt->mysql->server_status= uint2korr(p); -+ + if (stmt->mysql->server_capabilities & CLIENT_DEPRECATE_EOF && !is_data_packet) { + ma_read_ok_packet(stmt->mysql, p + 1, packet_len); + stmt->upsert_status.warning_count= stmt->mysql->warning_count; @@ -670,11 +679,10 @@ index 0aaaf1a..229023b 100644 + stmt->upsert_status.warning_count= stmt->mysql->warning_count= uint2korr(p + 1); + stmt->upsert_status.server_status= stmt->mysql->server_status= uint2korr(p + 3); + } -+ + ma_status_callback(stmt->mysql, last_status); stmt->result_cursor= result->data; return(0); - } -@@ -336,30 +348,52 @@ static int stmt_cursor_fetch(MYSQL_STMT *stmt, uchar **row) +@@ -388,34 +401,57 @@ static int stmt_cursor_fetch(MYSQL_STMT *stmt, uchar **row) void mthd_stmt_flush_unbuffered(MYSQL_STMT *stmt) { ulong packet_len; @@ -684,7 +692,9 @@ index 0aaaf1a..229023b 100644 - while ((packet_len = ma_net_safe_read(stmt->mysql)) != packet_error) + while ((packet_len = ma_net_safe_read(stmt->mysql, &is_data_packet)) != packet_error) { + unsigned int last_status= stmt->mysql->server_status; uchar *pos= stmt->mysql->net.read_pos; + - if (!in_resultset && *pos == 0) /* OK */ + if (stmt->mysql->server_capabilities & CLIENT_DEPRECATE_EOF) { @@ -692,22 +702,23 @@ index 0aaaf1a..229023b 100644 - net_field_length(&pos); - net_field_length(&pos); - stmt->mysql->server_status= uint2korr(pos); -- goto end; + if (!in_resultset && *pos == 0) /* OK */ + { -+ // 'ma_read_ok_packet' now updates 'server_status' -+ ma_read_ok_packet(stmt->mysql, pos + 1, packet_len); -+ goto end; ++ // 'ma_read_ok_packet' now updates 'server_status' ++ ma_read_ok_packet(stmt->mysql, pos + 1, packet_len); ++ goto end; + } + + // We got the initial ok_packet, avoid extra checks. + in_resultset = 1; + + if (*pos != 0 && !is_data_packet) { /* Final OK */ -+ // 'ma_read_ok_packet' now updates 'server_status' -+ ma_read_ok_packet(stmt->mysql, pos + 1, packet_len); -+ goto end; ++ // 'ma_read_ok_packet' now updates 'server_status' ++ ma_read_ok_packet(stmt->mysql, pos + 1, packet_len); ++ goto end; + } + ma_status_callback(stmt->mysql, last_status); + goto end; } - if (packet_len < 8 && *pos == 254) /* EOF */ + else @@ -724,6 +735,7 @@ index 0aaaf1a..229023b 100644 + if (packet_len < 8 && *pos == 254) /* EOF */ { - stmt->mysql->server_status= uint2korr(pos + 3); + ma_status_callback(stmt->mysql, last_status); - if (in_resultset) + if (mariadb_connection(stmt->mysql)) + { @@ -741,7 +753,7 @@ index 0aaaf1a..229023b 100644 } } end: -@@ -1554,7 +1588,7 @@ my_bool mthd_stmt_read_prepare_response(MYSQL_STMT *stmt) +@@ -1675,7 +1711,7 @@ my_bool mthd_stmt_read_prepare_response(MYSQL_STMT *stmt) ulong packet_length; uchar *p; @@ -750,7 +762,7 @@ index 0aaaf1a..229023b 100644 return(1); p= (uchar *)stmt->mysql->net.read_pos; -@@ -1579,28 +1613,38 @@ my_bool mthd_stmt_read_prepare_response(MYSQL_STMT *stmt) +@@ -1747,28 +1783,37 @@ my_bool mthd_stmt_read_prepare_response(MYSQL_STMT *stmt) return(0); } @@ -775,12 +787,11 @@ index 0aaaf1a..229023b 100644 - if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0, - 7 + ma_extended_type_info_rows(stmt->mysql)))) + if (!(mthd_my_read_metadata_ex(stmt->mysql, &mt_alloc, stmt->param_count, -+ 7 + ma_extended_type_info_rows(stmt->mysql)))) ++ 7 + ma_extended_type_info_rows(stmt->mysql)))) return(1); - free_rows(result); + ma_free_root(&mt_alloc, MYF(0)); -+ return(0); } @@ -795,30 +806,29 @@ index 0aaaf1a..229023b 100644 - if (!(stmt->fields= unpack_fields(stmt->mysql, result, fields_ma_alloc_root, - stmt->field_count, 0))) + stmt->fields = -+ mthd_my_read_metadata_ex(stmt->mysql, fields_ma_alloc_root, stmt->field_count, -+ 7 + ma_extended_type_info_rows(stmt->mysql)); ++ mthd_my_read_metadata_ex(stmt->mysql, fields_ma_alloc_root, stmt->field_count, ++ 7 + ma_extended_type_info_rows(stmt->mysql)); + if (!(stmt->fields)) return(1); return(0); } -@@ -1871,6 +1915,12 @@ int stmt_read_execute_response(MYSQL_STMT *stmt) - if (!stmt->mysql) - return(1); - +@@ -2003,6 +2048,11 @@ int mthd_stmt_read_execute_response(MYSQL_STMT *stmt) + return (1); + } + } + /* + * NOTE: There is a relevant patch that should be applied in + * this section in case CURSOR support is added to ProxySQL, + * check: https://github.com/mysql/mysql-server/blob/ee4455a33b10f1b1886044322e4893f587b319ed/libmysql/libmysql.cc#L1994 + */ -+ + /* update affected rows, also if an error occurred */ stmt->upsert_status.affected_rows= stmt->mysql->affected_rows; - diff --git plugins/auth/my_auth.c plugins/auth/my_auth.c -index 0435b4f..c54c0e5 100644 +index 72773079..38e50b7a 100644 --- plugins/auth/my_auth.c +++ plugins/auth/my_auth.c -@@ -401,7 +401,7 @@ static int client_mpvio_read_packet(struct st_plugin_vio *mpv, uchar **buf) +@@ -463,7 +463,7 @@ static int client_mpvio_read_packet(struct st_plugin_vio *mpv, uchar **buf) } /* otherwise read the data */ @@ -827,7 +837,7 @@ index 0435b4f..c54c0e5 100644 return (int)packet_error; mpvio->last_read_packet_len= pkt_len; -@@ -617,7 +617,7 @@ retry: +@@ -691,7 +691,7 @@ retry: /* read the OK packet (or use the cached value in mysql->net.read_pos */ if (res == CR_OK) @@ -837,14 +847,14 @@ index 0435b4f..c54c0e5 100644 pkt_length= mpvio.last_read_packet_len; diff --git unittest/libmariadb/ps_bugs.c unittest/libmariadb/ps_bugs.c -index 25fd9f5..b1abcdd 100644 +index efe3d447..67effa26 100644 --- unittest/libmariadb/ps_bugs.c +++ unittest/libmariadb/ps_bugs.c -@@ -3931,6 +3931,10 @@ static int test_conc141(MYSQL *mysql) +@@ -3961,6 +3961,10 @@ static int test_conc141(MYSQL *mysql) rc= mysql_stmt_execute(stmt); check_stmt_rc(rc, stmt); -+ ++ + rc= mysql_stmt_free_result(stmt); + check_stmt_rc(rc, stmt); + @@ -852,20 +862,20 @@ index 25fd9f5..b1abcdd 100644 rc= mysql_stmt_next_result(stmt); FAIL_IF(rc==-1, "No more results and error expected"); diff --git unittest/libmariadb/ps_new.c unittest/libmariadb/ps_new.c -index 9b89d6f..39b93cb 100644 +index ea228f1c..38eaa0af 100644 --- unittest/libmariadb/ps_new.c +++ unittest/libmariadb/ps_new.c -@@ -105,6 +105,9 @@ static int test_multi_result(MYSQL *mysql) +@@ -108,6 +108,9 @@ static int test_multi_result(MYSQL *mysql) FAIL_IF(int_data[0] != 10 || int_data[1] != 20 || int_data[2] != 30, "expected 10 20 30"); + rc= mysql_stmt_free_result(stmt); // must call before next mysql_stmt_next_result + check_stmt_rc(rc, stmt); -+ ++ rc= mysql_stmt_next_result(stmt); check_stmt_rc(rc, stmt); rc= mysql_stmt_bind_result(stmt, rs_bind); -@@ -113,6 +116,8 @@ static int test_multi_result(MYSQL *mysql) +@@ -116,6 +119,8 @@ static int test_multi_result(MYSQL *mysql) FAIL_IF(mysql_stmt_field_count(stmt) != 3, "expected 3 fields"); FAIL_IF(int_data[0] != 100 || int_data[1] != 200 || int_data[2] != 300, "expected 100 200 300"); @@ -874,12 +884,11 @@ index 9b89d6f..39b93cb 100644 FAIL_IF(mysql_stmt_next_result(stmt) != 0, "expected more results"); rc= mysql_stmt_bind_result(stmt, rs_bind); -@@ -121,7 +126,10 @@ static int test_multi_result(MYSQL *mysql) +@@ -124,7 +129,9 @@ static int test_multi_result(MYSQL *mysql) FAIL_IF(mysql_stmt_field_count(stmt) != 2, "expected 2 fields"); FAIL_IF(int_data[0] != 200 || int_data[1] != 300, - "expected 100 200 300"); + "expected 200 300"); - -+ + rc= mysql_stmt_free_result(stmt); + check_stmt_rc(rc, stmt); + diff --git a/deps/mariadb-client-library/cmakelists.txt.patch b/deps/mariadb-client-library/cmakelists.txt.patch new file mode 100644 index 0000000000..32002b9158 --- /dev/null +++ b/deps/mariadb-client-library/cmakelists.txt.patch @@ -0,0 +1,22 @@ +diff --git CMakeLists.txt CMakeLists.txt +index d7150b4d..e8508f8f 100644 +--- CMakeLists.txt ++++ CMakeLists.txt +@@ -286,7 +286,7 @@ ELSEIF() + ENDIF() + + IF(NOT DEFAULT_CHARSET) +- SET(DEFAULT_CHARSET "utf8mb4") ++ SET(DEFAULT_CHARSET "latin1") + ENDIF() + + +@@ -405,7 +405,7 @@ IF ((NOT WIN32) AND (CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID + ELSEIF(CMAKE_C_COMPILER_ID MATCHES "MSVC") + SET(WARNING_AS_ERROR "/WX") + ENDIF() +-SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_AS_ERROR}") ++#SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_AS_ERROR}") + ENDIF() + + diff --git a/deps/mariadb-client-library/cr_new_stmt_metadata_removal.patch b/deps/mariadb-client-library/cr_new_stmt_metadata_removal.patch index 883ed84d2b..74f0b0bcf4 100644 --- a/deps/mariadb-client-library/cr_new_stmt_metadata_removal.patch +++ b/deps/mariadb-client-library/cr_new_stmt_metadata_removal.patch @@ -1,8 +1,8 @@ diff --git libmariadb/mariadb_stmt.c libmariadb/mariadb_stmt.c -index bbc2831..2168065 100644 +index 47f0f2b6..7c6482ac 100644 --- libmariadb/mariadb_stmt.c +++ libmariadb/mariadb_stmt.c -@@ -2013,7 +2013,61 @@ int stmt_read_execute_response(MYSQL_STMT *stmt) +@@ -2098,7 +2098,61 @@ int mthd_stmt_read_execute_response(MYSQL_STMT *stmt) stmt->state= MYSQL_STMT_WAITING_USE_OR_STORE; /* in certain cases parameter types can change: For example see bug 4026 (SELECT ?), so we need to update field information */ @@ -19,7 +19,7 @@ index bbc2831..2168065 100644 + if (mysql->field_count != stmt->field_count) + { + MA_MEM_ROOT *fields_ma_alloc_root= -+ &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; ++ &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; + uint i; + + // 'ma_free_root' will free all the allocated memory for 'fields_ma_alloc_root' @@ -27,11 +27,11 @@ index bbc2831..2168065 100644 + // the required space for 'MYSQL_BIND'. + ma_free_root(fields_ma_alloc_root, MYF(0)); + if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, -+ sizeof(MYSQL_BIND) * mysql->field_count)) || ++ sizeof(MYSQL_BIND) * mysql->field_count)) || + !(stmt->fields= (MYSQL_FIELD *)ma_alloc_root(fields_ma_alloc_root, -+ sizeof(MYSQL_FIELD) * mysql->field_count))) ++ sizeof(MYSQL_FIELD) * mysql->field_count))) + { -+ SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); ++ stmt_set_error(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return(1); + } + memset(stmt->bind, 0, sizeof(MYSQL_BIND) * mysql->field_count); @@ -65,14 +65,14 @@ index bbc2831..2168065 100644 { uint i; for (i=0; i < stmt->field_count; i++) -@@ -2025,12 +2079,8 @@ int stmt_read_execute_response(MYSQL_STMT *stmt) +@@ -2110,12 +2164,8 @@ int mthd_stmt_read_execute_response(MYSQL_STMT *stmt) stmt->fields[i].charsetnr= mysql->fields[i].charsetnr; stmt->fields[i].max_length= mysql->fields[i].max_length; } - } else - { - /* table was altered, see test_wl4166_2 */ -- SET_CLIENT_STMT_ERROR(stmt, CR_NEW_STMT_METADATA, SQLSTATE_UNKNOWN, 0); +- stmt_set_error(stmt, CR_NEW_STMT_METADATA, SQLSTATE_UNKNOWN, 0); - return(1); } + /*********************************************************************/ diff --git a/deps/mariadb-client-library/empty_split_compress_packet.patch b/deps/mariadb-client-library/empty_split_compress_packet.patch index 2d7aa187b6..e40d19b38d 100644 --- a/deps/mariadb-client-library/empty_split_compress_packet.patch +++ b/deps/mariadb-client-library/empty_split_compress_packet.patch @@ -1,8 +1,8 @@ diff --git libmariadb/ma_net.c libmariadb/ma_net.c -index 15be4fc..39a8138 100644 +index 2e932b17..0b1d0cce 100644 --- libmariadb/ma_net.c +++ libmariadb/ma_net.c -@@ -498,6 +498,7 @@ ulong ma_net_read(NET *net) +@@ -510,6 +510,7 @@ ulong ma_net_read(NET *net) { current+= 4; /* length + sequence_id, no more data will follow */ diff --git a/deps/mariadb-client-library/ma_alloc.c.patch b/deps/mariadb-client-library/ma_alloc.c.patch index 8464897ba9..f5fe53d07f 100644 --- a/deps/mariadb-client-library/ma_alloc.c.patch +++ b/deps/mariadb-client-library/ma_alloc.c.patch @@ -1,9 +1,13 @@ -@@ -85,7 +85,7 @@ +diff --git libmariadb/ma_alloc.c libmariadb/ma_alloc.c +index d6db279e..4526fef0 100644 +--- libmariadb/ma_alloc.c ++++ libmariadb/ma_alloc.c +@@ -86,7 +86,7 @@ void * ma_alloc_root(MA_MEM_ROOT *mem_root, size_t Size) if (! next) { /* Time to alloc new block */ get_size= MAX(Size+ALIGN_SIZE(sizeof(MA_USED_MEM)), - (mem_root->block_size & ~1) * (mem_root->block_num >> 2)); -+ (mem_root->block_size & ~1) * ( (mem_root->block_num >> 2) < 4 ? 4 : (mem_root->block_num >> 2) ) ); ++ (mem_root->block_size & ~1) * ((mem_root->block_num >> 2) < 4 ? 4 : (mem_root->block_num >> 2))); if (!(next = (MA_USED_MEM*) malloc(get_size))) { diff --git a/deps/mariadb-client-library/ma_array.c-multiplication-overflow.patch b/deps/mariadb-client-library/ma_array.c-multiplication-overflow.patch index 76bcb496ff..80e20be2bd 100644 --- a/deps/mariadb-client-library/ma_array.c-multiplication-overflow.patch +++ b/deps/mariadb-client-library/ma_array.c-multiplication-overflow.patch @@ -1,4 +1,8 @@ -@@ -46,7 +46,7 @@ +diff --git libmariadb/ma_array.c libmariadb/ma_array.c +index d067aa4a..81fc3c40 100644 +--- libmariadb/ma_array.c ++++ libmariadb/ma_array.c +@@ -46,7 +46,7 @@ my_bool ma_init_dynamic_array(DYNAMIC_ARRAY *array, uint element_size, array->max_element=init_alloc; array->alloc_increment=alloc_increment; array->size_of_element=element_size; @@ -7,7 +11,7 @@ { array->max_element=0; return(TRUE); -@@ -80,7 +80,7 @@ +@@ -80,7 +80,7 @@ unsigned char *ma_alloc_dynamic(DYNAMIC_ARRAY *array) if (array->elements == array->max_element) { char *new_ptr; @@ -16,7 +20,7 @@ array->alloc_increment)* array->size_of_element))) return 0; -@@ -111,14 +111,14 @@ +@@ -111,14 +111,14 @@ my_bool ma_set_dynamic(DYNAMIC_ARRAY *array, void * element, uint idx) char *new_ptr; size=(idx+array->alloc_increment)/array->alloc_increment; size*= array->alloc_increment; @@ -33,7 +37,7 @@ array->elements=idx+1; } memcpy(array->buffer+(idx * array->size_of_element),element, -@@ -155,7 +155,7 @@ +@@ -155,7 +155,7 @@ void ma_delete_dynamic_element(DYNAMIC_ARRAY *array, uint idx) char *ptr=array->buffer+array->size_of_element*idx; array->elements--; memmove(ptr,ptr+array->size_of_element, @@ -42,7 +46,7 @@ } -@@ -166,7 +166,7 @@ +@@ -166,7 +166,7 @@ void ma_freeze_size(DYNAMIC_ARRAY *array) if (array->buffer && array->max_element != elements) { array->buffer=(char*) realloc(array->buffer, diff --git a/deps/mariadb-client-library/ma_charset.c.patch b/deps/mariadb-client-library/ma_charset.c.patch index 765d47da6f..9892f4cb5f 100644 --- a/deps/mariadb-client-library/ma_charset.c.patch +++ b/deps/mariadb-client-library/ma_charset.c.patch @@ -1,4 +1,17 @@ -@@ -720,7 +720,7 @@ +diff --git libmariadb/ma_charset.c libmariadb/ma_charset.c +index f6ed6f80..4a1207f1 100644 +--- libmariadb/ma_charset.c ++++ libmariadb/ma_charset.c +@@ -542,7 +542,7 @@ static unsigned int check_mb_gb18030_valid(const char * start, const char * end) + */ + + #define UTF8_MB4 "utf8mb4" +-#define UTF8_MB3 "utf8mb3" ++#define UTF8_MB3 "utf8" + + /* {{{ mysql_charsets */ + const MARIADB_CHARSET_INFO mariadb_compiled_charsets[] = +@@ -722,7 +722,7 @@ const MARIADB_CHARSET_INFO mariadb_compiled_charsets[] = { 182, 1, "utf32", "utf32_unicode_520_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32}, { 183, 1, "utf32", "utf32_vietnamese_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32}, @@ -7,3 +20,13 @@ { 193, 1, UTF8_MB3, UTF8_MB3"_icelandic_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, { 194, 1, UTF8_MB3, UTF8_MB3"_latvian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, { 195, 1, UTF8_MB3, UTF8_MB3"_romanian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, +@@ -1083,9 +1083,6 @@ MARIADB_CHARSET_INFO * mysql_find_charset_name(const char *name) + else + csname= (char *)name; + +- if (!strcasecmp("utf8",csname)) +- csname= "utf8mb3"; +- + do { + if (!strcasecmp(c->csname, csname)) { + return(c); diff --git a/deps/mariadb-client-library/ma_common.h.sslkeylogfile.patch b/deps/mariadb-client-library/ma_common.h.sslkeylogfile.patch deleted file mode 100644 index af13a9449c..0000000000 --- a/deps/mariadb-client-library/ma_common.h.sslkeylogfile.patch +++ /dev/null @@ -1,14 +0,0 @@ -@@ -78,12 +78,13 @@ - my_bool (*set_option)(MYSQL *mysql, const char *config_option, const char *config_value); - HASH userdata; - char *server_public_key; - char *proxy_header; - size_t proxy_header_len; - int (*io_wait)(my_socket handle, my_bool is_read, int timeout); -+ void (*ssl_keylog_callback)(const void *ssl, const char *line); - }; - - typedef struct st_connection_handler - { - struct st_ma_connection_plugin *plugin; - void *data; diff --git a/deps/mariadb-client-library/ma_context.h.patch b/deps/mariadb-client-library/ma_context.h.patch index 2aae87324f..6ac91adb2e 100644 --- a/deps/mariadb-client-library/ma_context.h.patch +++ b/deps/mariadb-client-library/ma_context.h.patch @@ -1,4 +1,8 @@ -@@ -25,6 +25,8 @@ +diff --git include/ma_context.h include/ma_context.h +index 0ad7f68b..ad26a721 100644 +--- include/ma_context.h ++++ include/ma_context.h +@@ -26,6 +26,8 @@ (This particular implementation uses Posix ucontext swapcontext().) */ diff --git a/deps/mariadb-client-library/ma_password.c.patch b/deps/mariadb-client-library/ma_password.c.patch index d6300c0bb1..a1d30811ef 100644 --- a/deps/mariadb-client-library/ma_password.c.patch +++ b/deps/mariadb-client-library/ma_password.c.patch @@ -1,56 +1,56 @@ -@@ -98,6 +98,35 @@ +diff --git libmariadb/ma_password.c libmariadb/ma_password.c +index eb6fe6a8..b327bf61 100644 +--- libmariadb/ma_password.c ++++ libmariadb/ma_password.c +@@ -98,6 +98,33 @@ void my_crypt(unsigned char *buffer, const unsigned char *s1, const unsigned cha } } +unsigned char decode_char(char x) { -+ if (x >= '0' && x <= '9') -+ return (x - 0x30); -+ else if (x >= 'A' && x <= 'F') -+ return(x - 0x37); -+ else if (x >= 'a' && x <= 'f') -+ return(x - 0x57); -+ else { -+ fprintf(stderr,"%s:%d:%s(): [ERROR in libmariadbclient]: \n", __FILE__, __LINE__, __func__); -+ return 0; -+ } ++ if (x >= '0' && x <= '9') ++ return (x - 0x30); ++ else if (x >= 'A' && x <= 'F') ++ return(x - 0x37); ++ else if (x >= 'a' && x <= 'f') ++ return(x - 0x57); ++ else { ++ fprintf(stderr,"%s:%d:%s(): [ERROR in libmariadbclient]: \n", __FILE__, __LINE__, __func__); ++ return 0; ++ } +} + +void unhex_pass(unsigned char *out, const char *in) { -+ int i=0; -+ for (i=0;iextension->mariadb_server_capabilities) & - (MARIADB_CLIENT_EXTENDED_METADATA >> 32)) != 0; diff --git a/deps/mariadb-client-library/mariadb-connector-c-3.1.9-src.tar.gz b/deps/mariadb-client-library/mariadb-connector-c-3.1.9-src.tar.gz deleted file mode 100644 index 0d0e3e6e29..0000000000 Binary files a/deps/mariadb-client-library/mariadb-connector-c-3.1.9-src.tar.gz and /dev/null differ diff --git a/deps/mariadb-client-library/mariadb-connector-c-3.3.8-src.tar.gz b/deps/mariadb-client-library/mariadb-connector-c-3.3.8-src.tar.gz new file mode 100644 index 0000000000..9389ee0624 Binary files /dev/null and b/deps/mariadb-client-library/mariadb-connector-c-3.3.8-src.tar.gz differ diff --git a/deps/mariadb-client-library/mariadb_asan.patch b/deps/mariadb-client-library/mariadb_asan.patch index ce675f06b3..cb9c9a6440 100644 --- a/deps/mariadb-client-library/mariadb_asan.patch +++ b/deps/mariadb-client-library/mariadb_asan.patch @@ -1,8 +1,8 @@ diff --git CMakeLists.txt CMakeLists.txt -index 568e4f5..cfac85b 100644 +index 206e4e03..d7150b4d 100644 --- CMakeLists.txt +++ CMakeLists.txt -@@ -136,6 +136,10 @@ SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DDBUG_OFF") +@@ -131,6 +131,10 @@ SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DDBUG_OFF") SET(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -DDBUG_OFF") SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DDBUG_OFF") @@ -12,4 +12,4 @@ index 568e4f5..cfac85b 100644 + IF(CMAKE_COMPILER_IS_GNUCC) INCLUDE(CheckCCompilerFlag) - SET(GCC_FLAGS -Wunused -Wlogical-op -Wno-uninitialized -Wall -Wextra -Wformat-security -Wno-init-self -Wwrite-strings -Wshift-count-overflow -Wdeclaration-after-statement -Wno-undef -Wno-unknown-pragmas) + SET(GCC_FLAGS -Wunused -Wlogical-op -Wno-uninitialized -Wall -Wextra -Wformat-security -Wno-init-self -Wwrite-strings -Wshift-count-overflow -Wdeclaration-after-statement -Wno-undef -Wno-unknown-pragmas -Wno-stringop-truncation) diff --git a/deps/mariadb-client-library/mariadb_async.c.patch b/deps/mariadb-client-library/mariadb_async.c.patch index 09461aa752..12301dfbde 100644 --- a/deps/mariadb-client-library/mariadb_async.c.patch +++ b/deps/mariadb-client-library/mariadb_async.c.patch @@ -1,4 +1,8 @@ -@@ -479,19 +479,20 @@ +diff --git libmariadb/mariadb_async.c libmariadb/mariadb_async.c +index feb73329..f2fba5d4 100644 +--- libmariadb/mariadb_async.c ++++ libmariadb/mariadb_async.c +@@ -479,19 +479,20 @@ MK_ASYNC_CONT_BODY( struct mysql_set_character_set_params { MYSQL *mysql; const char *csname; @@ -21,7 +25,7 @@ { MK_ASYNC_START_BODY( mysql_set_character_set, -@@ -500,6 +501,7 @@ +@@ -500,6 +501,7 @@ MK_ASYNC_START_BODY( WIN_SET_NONBLOCKING(mysql) parms.mysql= mysql; parms.csname= csname; diff --git a/deps/mariadb-client-library/mariadb_client b/deps/mariadb-client-library/mariadb_client index 8ea53d8c52..718a3e0a19 120000 --- a/deps/mariadb-client-library/mariadb_client +++ b/deps/mariadb-client-library/mariadb_client @@ -1 +1 @@ -mariadb-connector-c-3.1.9-src \ No newline at end of file +mariadb-connector-c-3.3.8-src \ No newline at end of file diff --git a/deps/mariadb-client-library/mariadb_com.h.patch b/deps/mariadb-client-library/mariadb_com.h.patch index f24c5ad3b1..31c32fa015 100644 --- a/deps/mariadb-client-library/mariadb_com.h.patch +++ b/deps/mariadb-client-library/mariadb_com.h.patch @@ -1,15 +1,19 @@ -@@ -178,8 +178,8 @@ +diff --git include/mariadb_com.h include/mariadb_com.h +index 96e6e04f..7acc7403 100644 +--- include/mariadb_com.h ++++ include/mariadb_com.h +@@ -183,9 +183,7 @@ enum enum_server_command + (!(mysql->server_capabilities & CLIENT_MYSQL)) #define MARIADB_CLIENT_SUPPORTED_FLAGS (MARIADB_CLIENT_PROGRESS |\ - MARIADB_CLIENT_COM_MULTI |\ - MARIADB_CLIENT_STMT_BULK_OPERATIONS|\ -- MARIADB_CLIENT_EXTENDED_METADATA) -+ MARIADB_CLIENT_STMT_BULK_OPERATIONS\ -+ ) +- MARIADB_CLIENT_EXTENDED_METADATA|\ +- MARIADB_CLIENT_CACHE_METADATA) ++ MARIADB_CLIENT_STMT_BULK_OPERATIONS) #define CLIENT_SUPPORTED_FLAGS (CLIENT_MYSQL |\ CLIENT_FOUND_ROWS |\ -@@ -210,8 +210,6 @@ +@@ -221,8 +219,6 @@ enum enum_server_command CLIENT_LONG_FLAG |\ CLIENT_TRANSACTIONS |\ CLIENT_SECURE_CONNECTION |\ @@ -17,4 +21,4 @@ - CLIENT_PS_MULTI_RESULTS |\ CLIENT_PROTOCOL_41 |\ CLIENT_PLUGIN_AUTH |\ - CLIENT_SESSION_TRACKING |\ \ No newline at end of file + CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA | \ diff --git a/deps/mariadb-client-library/mariadb_dyncol.c-multiplication-overflow.patch b/deps/mariadb-client-library/mariadb_dyncol.c-multiplication-overflow.patch index 7df44d4e5a..8264af5d2a 100644 --- a/deps/mariadb-client-library/mariadb_dyncol.c-multiplication-overflow.patch +++ b/deps/mariadb-client-library/mariadb_dyncol.c-multiplication-overflow.patch @@ -1,4 +1,8 @@ -@@ -3999,13 +3999,13 @@ +diff --git libmariadb/mariadb_dyncol.c libmariadb/mariadb_dyncol.c +index 42de69ee..6b040d11 100644 +--- libmariadb/mariadb_dyncol.c ++++ libmariadb/mariadb_dyncol.c +@@ -4000,13 +4000,13 @@ mariadb_dyncol_val_long(longlong *ll, DYNAMIC_COLUMN_VALUE *val) (val->x.time_value.neg ? -1 : 1); break; case DYN_COL_DATE: diff --git a/deps/mariadb-client-library/mariadb_lib.c.collation.patch b/deps/mariadb-client-library/mariadb_lib.c.collation.patch index 3ee686ed63..6ae8090974 100644 --- a/deps/mariadb-client-library/mariadb_lib.c.collation.patch +++ b/deps/mariadb-client-library/mariadb_lib.c.collation.patch @@ -1,4 +1,8 @@ -@@ -1109,7 +1109,10 @@ mysql_init(MYSQL *mysql) +diff --git libmariadb/mariadb_lib.c libmariadb/mariadb_lib.c +index 66b44df4..c9aec200 100644 +--- libmariadb/mariadb_lib.c ++++ libmariadb/mariadb_lib.c +@@ -1283,7 +1283,10 @@ mysql_init(MYSQL *mysql) goto error; mysql->options.report_data_truncation= 1; mysql->options.connect_timeout=CONNECT_TIMEOUT; @@ -10,7 +14,7 @@ mysql->methods= &MARIADB_DEFAULT_METHODS; strcpy(mysql->net.sqlstate, "00000"); mysql->net.last_error[0]= mysql->net.last_errno= mysql->net.extension->extended_errno= 0; -@@ -1625,11 +1628,15 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql +@@ -1918,11 +1921,15 @@ restart: } } @@ -31,7 +35,7 @@ if (!mysql->charset) { -@@ -1892,10 +1899,16 @@ my_bool STDCALL mysql_change_user(MYSQL +@@ -2208,10 +2215,16 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, *s_db= mysql->db; int rc; @@ -52,7 +56,7 @@ mysql->user= strdup(user ? user : ""); mysql->passwd= strdup(passwd ? passwd : ""); -@@ -2963,6 +2976,12 @@ mysql_optionsv(MYSQL *mysql,enum mysql_o +@@ -3492,6 +3505,12 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) OPT_SET_VALUE_STR(&mysql->options, charset_dir, arg1); break; case MYSQL_SET_CHARSET_NAME: diff --git a/deps/mariadb-client-library/mariadb_lib.c.ipv6_fix.patch b/deps/mariadb-client-library/mariadb_lib.c.ipv6_fix.patch index b9ea4be5c9..8e76009de5 100644 --- a/deps/mariadb-client-library/mariadb_lib.c.ipv6_fix.patch +++ b/deps/mariadb-client-library/mariadb_lib.c.ipv6_fix.patch @@ -1,23 +1,15 @@ -@@ -1615,6 +1615,11 @@ - if (ma_pvio_connect(pvio, &cinfo) != 0) - { - ma_pvio_close(pvio); -+ if (mysql->options.extension && mysql->options.extension->async_context && -+ mysql->options.extension->async_context->pvio) { -+ /* async_context->pvio contains dangling pointer. Setting async_context->pvio to NULL*/ -+ mysql->options.extension->async_context->pvio = NULL; -+ } - goto error; - } - -@@ -1625,6 +1630,11 @@ +diff --git libmariadb/mariadb_lib.c libmariadb/mariadb_lib.c +index c9aec200..be4c91d1 100644 +--- libmariadb/mariadb_lib.c ++++ libmariadb/mariadb_lib.c +@@ -1752,6 +1752,11 @@ restart: if (ma_pvio_write(pvio, (unsigned char *)hdr, len) <= 0) { ma_pvio_close(pvio); + if (mysql->options.extension->async_context && -+ mysql->options.extension->async_context->pvio) { -+ /* async_context->pvio contains dangling pointer. Setting async_context->pvio to NULL*/ -+ mysql->options.extension->async_context->pvio = NULL; ++ mysql->options.extension->async_context->pvio) { ++ /* async_context->pvio contains dangling pointer. Invalidatng pvio pointer in the async context*/ ++ mysql->options.extension->async_context->pvio = NULL; + } goto error; } diff --git a/deps/mariadb-client-library/mariadb_lib.c.patch b/deps/mariadb-client-library/mariadb_lib.c.patch index 6db5e05a00..ab3bbaf2c3 100644 --- a/deps/mariadb-client-library/mariadb_lib.c.patch +++ b/deps/mariadb-client-library/mariadb_lib.c.patch @@ -1,41 +1,45 @@ diff --git libmariadb/mariadb_lib.c libmariadb/mariadb_lib.c -index 8c2a99b..cf6114a 100644 +index e8db51a0..684aff1a 100644 --- libmariadb/mariadb_lib.c +++ libmariadb/mariadb_lib.c -@@ -1643,9 +1643,16 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, +@@ -1936,10 +1936,16 @@ restart: mysql->client_flag= client_flag; -+ { -+ volatile my_bool net_blocking = ma_pvio_is_blocking(net->pvio); -+ if (!net_blocking) -+ ma_pvio_blocking(net->pvio, TRUE, 0); - if (run_plugin_auth(mysql, scramble_data, scramble_len, - scramble_plugin, db)) - goto error; -+ if (!net_blocking) -+ ma_pvio_blocking(net->pvio, FALSE, 0); -+ } - - if (mysql->client_flag & CLIENT_COMPRESS) - net->compress= 1; -@@ -1696,6 +1703,15 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, +- if (run_plugin_auth(mysql, scramble_data, scramble_len, +- scramble_plugin, db)) +- goto error; +- ++ { ++ volatile my_bool net_blocking = ma_pvio_is_blocking(net->pvio); ++ if (!net_blocking) ++ ma_pvio_blocking(net->pvio, TRUE, 0); ++ if (run_plugin_auth(mysql, scramble_data, scramble_len, ++ scramble_plugin, db)) ++ goto error; ++ if (!net_blocking) ++ ma_pvio_blocking(net->pvio, FALSE, 0); ++ } + if (mysql->client_flag & CLIENT_COMPRESS || + mysql->client_flag & CLIENT_ZSTD_COMPRESSION) + { +@@ -2006,6 +2012,15 @@ restart: return(mysql); error: + // fix for proxysql bug #2623 + if (mysql->net.extension) { -+ free(mysql->net.extension); -+ mysql->net.extension=NULL; ++ free(mysql->net.extension); ++ mysql->net.extension=NULL; + } + if (mysql->extension) { -+ free(mysql->extension); -+ mysql->extension=NULL; ++ free(mysql->extension); ++ mysql->extension=NULL; + } - /* Free alloced memory */ - end_server(mysql); - /* only free the allocated memory, user needs to call mysql_close */ -@@ -1775,7 +1791,7 @@ my_bool STDCALL mariadb_reconnect(MYSQL *mysql) + /* Free allocated memory */ + free(host_list); + free(host_copy); +@@ -2087,7 +2102,7 @@ my_bool STDCALL mariadb_reconnect(MYSQL *mysql) if (!mysql_real_connect(&tmp_mysql,mysql->host,mysql->user,mysql->passwd, mysql->db, mysql->port, mysql->unix_socket, mysql->client_flag | CLIENT_REMEMBER_OPTIONS) || @@ -44,23 +48,28 @@ index 8c2a99b..cf6114a 100644 { if (ctxt) my_context_install_suspend_resume_hook(ctxt, NULL, NULL); -@@ -1821,6 +1837,7 @@ void ma_invalidate_stmts(MYSQL *mysql, const char *function_name) +@@ -2134,10 +2149,11 @@ my_bool STDCALL mariadb_reconnect(MYSQL *mysql) + return(0); + } + +-void ma_invalidate_stmts(MYSQL *mysql, const char *function_name) ++void ma_invalidate_stmts(MYSQL *mysql, const char *function_name __attribute__((unused))) { if (mysql->stmts) { -+/* ++ /* LIST *li_stmt= mysql->stmts; for (; li_stmt; li_stmt= li_stmt->next) -@@ -1829,6 +1846,7 @@ void ma_invalidate_stmts(MYSQL *mysql, const char *function_name) +@@ -2146,6 +2162,7 @@ void ma_invalidate_stmts(MYSQL *mysql, const char *function_name) stmt->mysql= NULL; - SET_CLIENT_STMT_ERROR(stmt, CR_STMT_CLOSED, SQLSTATE_UNKNOWN, function_name); + stmt_set_error(stmt, CR_STMT_CLOSED, SQLSTATE_UNKNOWN, 0, function_name); } -+*/ ++ */ mysql->stmts= NULL; } } -@@ -2115,6 +2133,44 @@ mysql_close(MYSQL *mysql) +@@ -2458,6 +2475,42 @@ mysql_close(MYSQL *mysql) return; } @@ -69,28 +78,27 @@ index 8c2a99b..cf6114a 100644 +{ + if (mysql) /* Some simple safety */ + { -+ + if (mysql->methods) { -+ if (mysql->net.pvio) { -+ free_old_query(mysql); -+ mysql->status=MYSQL_STATUS_READY; /* Force command */ -+ mysql->options.reconnect=0; -+ end_server(mysql); -+ } -+ } ++ if (mysql->net.pvio) { ++ free_old_query(mysql); ++ mysql->status=MYSQL_STATUS_READY; /* Force command */ ++ mysql->options.reconnect=0; ++ end_server(mysql); ++ } ++ } + mysql_close_memory(mysql); + mysql_close_options(mysql); + mysql->host_info=mysql->user=mysql->passwd=mysql->db=0; + + // fix for proxysql bug #3204 + if (mysql->net.extension) { -+ free(mysql->net.extension); -+ mysql->net.extension=NULL; ++ free(mysql->net.extension); ++ mysql->net.extension=NULL; + } + + if (mysql->extension) { -+ free(mysql->extension); -+ mysql->extension=NULL; ++ free(mysql->extension); ++ mysql->extension=NULL; + } + + /* Clear pointers for better safety */ @@ -101,20 +109,10 @@ index 8c2a99b..cf6114a 100644 + } + return; +} -+ /************************************************************************** ** Do a query. If query returned rows, free old rows. -@@ -2189,6 +2245,8 @@ int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length) - char *data; - si_type= (enum enum_session_state_type)net_field_length(&pos); - switch(si_type) { -+ case SESSION_TRACK_GTIDS: -+ net_field_length(&pos); /* skip encoding */ - case SESSION_TRACK_SCHEMA: - case SESSION_TRACK_STATE_CHANGE: - case SESSION_TRACK_TRANSACTION_CHARACTERISTICS: -@@ -3670,18 +3728,27 @@ void STDCALL mysql_get_character_set_info(MYSQL *mysql, MY_CHARSET_INFO *cs) +@@ -4237,18 +4290,27 @@ void STDCALL mysql_get_character_set_info(MYSQL *mysql, MY_CHARSET_INFO *cs) mariadb_get_charset_info(mysql, cs); } @@ -147,11 +145,15 @@ index 8c2a99b..cf6114a 100644 if (!mysql_real_query(mysql, buff, (unsigned long)strlen(buff))) { mysql->charset= cs; -@@ -3691,6 +3758,7 @@ int STDCALL mysql_set_character_set(MYSQL *mysql, const char *csname) +@@ -4258,8 +4320,9 @@ int STDCALL mysql_set_character_set(MYSQL *mysql, const char *csname) } error: +- my_set_error(mysql, CR_CANT_READ_CHARSET, SQLSTATE_UNKNOWN, +- 0, csname, "compiled_in"); + if (!cs) - my_set_error(mysql, CR_CANT_READ_CHARSET, SQLSTATE_UNKNOWN, - 0, csname, "compiled_in"); ++ my_set_error(mysql, CR_CANT_READ_CHARSET, SQLSTATE_UNKNOWN, ++ 0, csname, "compiled_in"); return(mysql->net.last_errno); + } + diff --git a/deps/mariadb-client-library/mariadb_lib.c.sslkeylogfile.patch b/deps/mariadb-client-library/mariadb_lib.c.sslkeylogfile.patch deleted file mode 100644 index e0245b5828..0000000000 --- a/deps/mariadb-client-library/mariadb_lib.c.sslkeylogfile.patch +++ /dev/null @@ -1,32 +0,0 @@ -@@ -3277,12 +3277,15 @@ - case MYSQL_OPT_SSL_CRL: - OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crl, (char *)arg1); - break; - case MYSQL_OPT_SSL_CRLPATH: - OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crlpath, (char *)arg1); - break; -+ case MARIADB_OPT_SSL_KEYLOG_CALLBACK: -+ OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_keylog_callback, arg1); -+ break; - case MYSQL_OPT_CONNECT_ATTR_DELETE: - { - uchar *h; - CHECK_OPT_EXTENSION_SET(&mysql->options); - if (hash_inited(&mysql->options.extension->connect_attrs) && - (h= (uchar *)hash_search(&mysql->options.extension->connect_attrs, (uchar *)arg1, -@@ -3614,12 +3617,15 @@ - case MYSQL_OPT_SSL_CRL: - *((char **)arg)= mysql->options.extension ? mysql->options.ssl_cipher : NULL; - break; - case MYSQL_OPT_SSL_CRLPATH: - *((char **)arg)= mysql->options.extension ? mysql->options.extension->ssl_crlpath : NULL; - break; -+ case MARIADB_OPT_SSL_KEYLOG_CALLBACK: -+ *((void(**)(const void *, const char *))arg)= mysql->options.extension ? mysql->options.extension->ssl_keylog_callback : NULL; -+ break; - case MYSQL_OPT_CONNECT_ATTRS: - /* mysql_get_optionsv(mysql, MYSQL_OPT_CONNECT_ATTRS, keys, vals, elements) */ - { - unsigned int i, *elements; - char **key= NULL; - void *arg1; diff --git a/deps/mariadb-client-library/mariadb_lib.c.x509cache.patch b/deps/mariadb-client-library/mariadb_lib.c.x509cache.patch deleted file mode 100644 index 1cbae13819..0000000000 --- a/deps/mariadb-client-library/mariadb_lib.c.x509cache.patch +++ /dev/null @@ -1,32 +0,0 @@ -@@ -127,6 +127,11 @@ - uint mysql_port=0; - my_string mysql_unix_port=0; - -+#include -+extern __thread SSL_CTX * thread_ctx; -+extern __thread char **local_x509_files; -+extern __thread char **local_x509_sha1s; -+ - #define CONNECT_TIMEOUT 0 - - struct st_mariadb_methods MARIADB_DEFAULT_METHODS; -@@ -4078,6 +4083,19 @@ - - void STDCALL mysql_thread_end(void) - { -+ if (local_x509_files != NULL) { -+ int i; -+ for (i=0; local_x509_files[i] != NULL ; i++) { -+ free(local_x509_files[i]); -+ free(local_x509_sha1s[i]); -+ } -+ free(local_x509_files); -+ free(local_x509_sha1s); -+ } -+ if (thread_ctx != NULL) { -+ SSL_CTX_free(thread_ctx); -+ thread_ctx = NULL; -+ } - } - - int STDCALL mysql_set_server_option(MYSQL *mysql, diff --git a/deps/mariadb-client-library/mariadb_rpl.c.patch b/deps/mariadb-client-library/mariadb_rpl.c.patch deleted file mode 100644 index cbdd85c744..0000000000 --- a/deps/mariadb-client-library/mariadb_rpl.c.patch +++ /dev/null @@ -1,16 +0,0 @@ -@@ -386,6 +386,15 @@ - memcpy(rpl_event->event.rows.row_data, ev, rpl_event->event.rows.row_data_size); - } - break; -+ case PREVIOUS_GTIDS_LOG_EVENT: -+ case ANONYMOUS_GTID_LOG_EVENT: -+ case WRITE_ROWS_EVENT: -+ case UPDATE_ROWS_EVENT: -+ case DELETE_ROWS_EVENT: -+ case GTID_LOG_EVENT: -+ case HEARTBEAT_LOG_EVENT_V2: -+ case ROWS_QUERY_LOG_EVENT: -+ break; - default: - free(rpl_event); - return NULL; diff --git a/deps/mariadb-client-library/mariadb_rpl.h.patch b/deps/mariadb-client-library/mariadb_rpl.h.patch deleted file mode 100644 index 039536ef51..0000000000 --- a/deps/mariadb-client-library/mariadb_rpl.h.patch +++ /dev/null @@ -1,9 +0,0 @@ -@@ -94,6 +94,8 @@ - VIEW_CHANGE_EVENT= 37, - XA_PREPARE_LOG_EVENT= 38, - -+ HEARTBEAT_LOG_EVENT_V2 = 41, -+ - /* - Add new events here - right above this comment! - Existing events (except ENUM_END_EVENT) should never change their numbers diff --git a/deps/mariadb-client-library/mariadb_rpl.patch b/deps/mariadb-client-library/mariadb_rpl.patch new file mode 100644 index 0000000000..70c1eff114 --- /dev/null +++ b/deps/mariadb-client-library/mariadb_rpl.patch @@ -0,0 +1,29 @@ +diff --git include/mariadb_rpl.h include/mariadb_rpl.h +index ea0ca4db..697e5684 100644 +--- include/mariadb_rpl.h ++++ include/mariadb_rpl.h +@@ -134,7 +134,8 @@ enum mariadb_rpl_event { + VIEW_CHANGE_EVENT= 37, + XA_PREPARE_LOG_EVENT= 38, + PARTIAL_UPDATE_ROWS_EVENT = 39, +- ++ HEARTBEAT_LOG_EVENT_V2 = 41, ++ + /* + Add new events here - right above this comment! + Existing events (except ENUM_END_EVENT) should never change their numbers +diff --git libmariadb/mariadb_rpl.c libmariadb/mariadb_rpl.c +index daa6d242..dd3f905c 100644 +--- libmariadb/mariadb_rpl.c ++++ libmariadb/mariadb_rpl.c +@@ -1888,6 +1888,9 @@ MARIADB_RPL_EVENT * STDCALL mariadb_rpl_fetch(MARIADB_RPL *rpl, MARIADB_RPL_EVEN + } + break; + } ++ case ROWS_QUERY_LOG_EVENT: ++ case HEARTBEAT_LOG_EVENT_V2: ++ break; + default: + /* We need to report an error if this event can't be ignored */ + if (!(rpl_event->flags & LOG_EVENT_IGNORABLE_F)) + diff --git a/deps/mariadb-client-library/mariadb_stmt.c.patch b/deps/mariadb-client-library/mariadb_stmt.c.patch index e9fdf69487..cb4b4f86d7 100644 --- a/deps/mariadb-client-library/mariadb_stmt.c.patch +++ b/deps/mariadb-client-library/mariadb_stmt.c.patch @@ -1,4 +1,8 @@ -@@ -131,6 +131,7 @@ my_bool mthd_supported_buffer_type(enum +diff --git libmariadb/mariadb_stmt.c libmariadb/mariadb_stmt.c +index 5bcecfde..47f0f2b6 100644 +--- libmariadb/mariadb_stmt.c ++++ libmariadb/mariadb_stmt.c +@@ -149,6 +149,7 @@ my_bool mthd_supported_buffer_type(enum enum_field_types type) case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_TINY: case MYSQL_TYPE_TINY_BLOB: @@ -6,7 +10,7 @@ case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_YEAR: return 1; -@@ -1229,6 +1230,7 @@ my_bool STDCALL mysql_stmt_bind_param(MY +@@ -1332,6 +1333,7 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *bind) break; case MYSQL_TYPE_STRING: case MYSQL_TYPE_JSON: diff --git a/deps/mariadb-client-library/mariadb_stmt_store_result_err.patch b/deps/mariadb-client-library/mariadb_stmt_store_result_err.patch index ac493d3299..4b49c428e4 100644 --- a/deps/mariadb-client-library/mariadb_stmt_store_result_err.patch +++ b/deps/mariadb-client-library/mariadb_stmt_store_result_err.patch @@ -1,13 +1,13 @@ diff --git libmariadb/mariadb_stmt.c libmariadb/mariadb_stmt.c -index ba1617b..1c8d2e6 100644 +index 40a8ded1..d6df0825 100644 --- libmariadb/mariadb_stmt.c +++ libmariadb/mariadb_stmt.c -@@ -1838,6 +1838,9 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) +@@ -1903,6 +1903,9 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) stmt->result.data= NULL; stmt->result.rows= 0; stmt->mysql->status= MYSQL_STATUS_READY; + /* In case of error, stmt internal state needs to be reset. Otherwise state machine could be left in an -+ inconsistent state and further calls to 'mysql_stmt_execute' will halt. */ ++ inconsistent state and further calls to 'mysql_stmt_execute' will halt. */ + stmt->state= MYSQL_STMT_FETCH_DONE; return(1); } diff --git a/deps/mariadb-client-library/mysql.h.patch b/deps/mariadb-client-library/mysql.h.patch index ce8c6115da..60d293dc78 100644 --- a/deps/mariadb-client-library/mysql.h.patch +++ b/deps/mariadb-client-library/mysql.h.patch @@ -1,4 +1,8 @@ -@@ -517,7 +517,7 @@ +diff --git include/mysql.h include/mysql.h +index 76b16830..9ee86227 100644 +--- include/mysql.h ++++ include/mysql.h +@@ -531,7 +531,7 @@ const char * STDCALL mysql_info(MYSQL *mysql); unsigned long STDCALL mysql_thread_id(MYSQL *mysql); const char * STDCALL mysql_character_set_name(MYSQL *mysql); void STDCALL mysql_get_character_set_info(MYSQL *mysql, MY_CHARSET_INFO *cs); @@ -7,7 +11,7 @@ my_bool mariadb_get_infov(MYSQL *mysql, enum mariadb_value value, void *arg, ...); my_bool STDCALL mariadb_get_info(MYSQL *mysql, enum mariadb_value value, void *arg); -@@ -536,6 +536,7 @@ +@@ -550,6 +550,7 @@ MYSQL * STDCALL mysql_real_connect(MYSQL *mysql, const char *host, const char *unix_socket, unsigned long clientflag); void STDCALL mysql_close(MYSQL *sock); @@ -15,7 +19,7 @@ int STDCALL mysql_select_db(MYSQL *mysql, const char *db); int STDCALL mysql_query(MYSQL *mysql, const char *q); int STDCALL mysql_send_query(MYSQL *mysql, const char *q, -@@ -635,7 +636,7 @@ +@@ -649,7 +650,7 @@ int STDCALL mysql_stmt_next_result_start(int *ret, MYSQL_STMT *stmt); int STDCALL mysql_stmt_next_result_cont(int *ret, MYSQL_STMT *stmt, int status); int STDCALL mysql_set_character_set_start(int *ret, MYSQL *mysql, @@ -24,7 +28,7 @@ int STDCALL mysql_set_character_set_cont(int *ret, MYSQL *mysql, int status); int STDCALL mysql_change_user_start(my_bool *ret, MYSQL *mysql, -@@ -752,7 +752,7 @@ +@@ -765,7 +766,7 @@ struct st_mariadb_api { unsigned long (STDCALL *mysql_thread_id)(MYSQL *mysql); const char * (STDCALL *mysql_character_set_name)(MYSQL *mysql); void (STDCALL *mysql_get_character_set_info)(MYSQL *mysql, MY_CHARSET_INFO *cs); diff --git a/deps/mariadb-client-library/mysql.h.sslkeylogfile.patch b/deps/mariadb-client-library/mysql.h.sslkeylogfile.patch deleted file mode 100644 index 793d16f17d..0000000000 --- a/deps/mariadb-client-library/mysql.h.sslkeylogfile.patch +++ /dev/null @@ -1,16 +0,0 @@ -@@ -242,13 +242,14 @@ - MARIADB_OPT_DEBUG, - MARIADB_OPT_FOUND_ROWS, - MARIADB_OPT_MULTI_RESULTS, - MARIADB_OPT_MULTI_STATEMENTS, - MARIADB_OPT_INTERACTIVE, - MARIADB_OPT_PROXY_HEADER, -- MARIADB_OPT_IO_WAIT -+ MARIADB_OPT_IO_WAIT, -+ MARIADB_OPT_SSL_KEYLOG_CALLBACK - }; - - enum mariadb_value { - MARIADB_CHARSET_ID, - MARIADB_CHARSET_NAME, - MARIADB_CLIENT_ERRORS, diff --git a/deps/mariadb-client-library/openssl.c.sslkeylogfile.patch b/deps/mariadb-client-library/openssl.c.sslkeylogfile.patch deleted file mode 100644 index 7c53663f14..0000000000 --- a/deps/mariadb-client-library/openssl.c.sslkeylogfile.patch +++ /dev/null @@ -1,34 +0,0 @@ -@@ -529,12 +529,19 @@ - memset(buf, 0, size); - if (userdata) - strncpy(buf, (char *)userdata, size); - return (int)strlen(buf); - } - -+static void ma_tls_set_sslkeylog_callback(MYSQL *mysql, SSL_CTX *ssl_ctx) -+{ -+ if (mysql->options.extension && mysql->options.extension->ssl_keylog_callback) -+ { -+ SSL_CTX_set_keylog_callback(ssl_ctx, (void(*)(const SSL*, const char*))mysql->options.extension->ssl_keylog_callback); -+ } -+} - - static int ma_tls_set_certs(MYSQL *mysql, SSL *ssl) - { - char *certfile= mysql->options.ssl_cert, - *keyfile= mysql->options.ssl_key; - char *pw= (mysql->options.extension) ? -@@ -656,12 +663,13 @@ - if (!(ctx= SSL_CTX_new(SSLv23_client_method()))) - #endif - goto error; - if (mysql->options.extension) - options|= ma_tls_version_options(mysql->options.extension->tls_version); - SSL_CTX_set_options(ctx, options); -+ ma_tls_set_sslkeylog_callback(mysql, ctx); - #ifdef HAVE_TLS_SESSION_CACHE - SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT); - ma_tls_sessions= (MA_SSL_SESSION *)calloc(1, sizeof(struct st_ma_tls_session) * ma_tls_session_cache_size); - SSL_CTX_sess_set_new_cb(ctx, ma_tls_session_cb); - SSL_CTX_sess_set_remove_cb(ctx, ma_tls_remove_session_cb); - #endif diff --git a/deps/mariadb-client-library/plugin_auth_CMakeLists.txt.patch b/deps/mariadb-client-library/plugin_auth_CMakeLists.txt.patch index 989b9cf567..ed4aca87d1 100644 --- a/deps/mariadb-client-library/plugin_auth_CMakeLists.txt.patch +++ b/deps/mariadb-client-library/plugin_auth_CMakeLists.txt.patch @@ -1,4 +1,8 @@ -@@ -70,7 +70,7 @@ +diff --git plugins/auth/CMakeLists.txt plugins/auth/CMakeLists.txt +index 83e324b9..7c4ed019 100644 +--- plugins/auth/CMakeLists.txt ++++ plugins/auth/CMakeLists.txt +@@ -77,7 +77,7 @@ IF(CRYPTO_PLUGIN) REGISTER_PLUGIN(TARGET caching_sha2_password TYPE MARIADB_CLIENT_PLUGIN_AUTH CONFIGURATIONS DYNAMIC STATIC OFF @@ -7,7 +11,7 @@ SOURCES ${CC_SOURCE_DIR}/plugins/auth/caching_sha2_pw.c ${CRYPT_SOURCE} LIBRARIES ${CRYPT_LIBS}) -@@ -81,7 +81,7 @@ +@@ -88,7 +88,7 @@ IF(CRYPTO_PLUGIN) REGISTER_PLUGIN(TARGET sha256_password TYPE MARIADB_CLIENT_PLUGIN_AUTH CONFIGURATIONS DYNAMIC STATIC OFF @@ -16,7 +20,7 @@ SOURCES ${AUTH_DIR}/sha256_pw.c LIBRARIES ${CRYPT_LIBS}) ENDIF() -@@ -123,6 +123,6 @@ +@@ -137,6 +137,6 @@ REGISTER_PLUGIN(TARGET mysql_old_password REGISTER_PLUGIN(TARGET mysql_clear_password TYPE MARIADB_CLIENT_PLUGIN_AUTH CONFIGURATIONS DYNAMIC STATIC OFF diff --git a/deps/mariadb-client-library/ps_buffer_stmt_read_all_rows.patch b/deps/mariadb-client-library/ps_buffer_stmt_read_all_rows.patch index 373e4c3dd9..ee0f3c8a21 100644 --- a/deps/mariadb-client-library/ps_buffer_stmt_read_all_rows.patch +++ b/deps/mariadb-client-library/ps_buffer_stmt_read_all_rows.patch @@ -1,10 +1,10 @@ diff --git libmariadb/mariadb_stmt.c libmariadb/mariadb_stmt.c -index bbc2831..e66d810 100644 +index 7c6482ac..40a8ded1 100644 --- libmariadb/mariadb_stmt.c +++ libmariadb/mariadb_stmt.c -@@ -207,6 +207,24 @@ int mthd_stmt_read_all_rows(MYSQL_STMT *stmt) +@@ -218,6 +218,24 @@ int mthd_stmt_read_all_rows(MYSQL_STMT *stmt) - while ((packet_len = ma_net_safe_read(stmt->mysql, &is_data_packet)) != packet_error) + while ((packet_len = ma_net_safe_read(stmt->mysql)) != packet_error) { + // This change is required due to the new algorithm introduced in ProxySQL in #3295. + // ******************************************************************************** @@ -25,5 +25,5 @@ index bbc2831..e66d810 100644 + // ******************************************************************************** + p= stmt->mysql->net.read_pos; - // The check is by 'ma_net_safe_read' - if (p[0] == 0 || is_data_packet) + if (packet_len > 7 || p[0] != 254) + { diff --git a/deps/mariadb-client-library/sslkeylogfile.patch b/deps/mariadb-client-library/sslkeylogfile.patch new file mode 100644 index 0000000000..76766679dc --- /dev/null +++ b/deps/mariadb-client-library/sslkeylogfile.patch @@ -0,0 +1,77 @@ +diff --git include/ma_common.h include/ma_common.h +index 1ac0cb68..0d3f39b3 100644 +--- include/ma_common.h ++++ include/ma_common.h +@@ -80,6 +80,7 @@ struct st_mysql_options_extension { + char *proxy_header; + size_t proxy_header_len; + int (*io_wait)(my_socket handle, my_bool is_read, int timeout); ++ void (*ssl_keylog_callback)(const void *ssl, const char *line); + my_bool skip_read_response; + char *restricted_auth; + char *rpl_host; +diff --git include/mysql.h include/mysql.h +index 9ee86227..c07717c5 100644 +--- include/mysql.h ++++ include/mysql.h +@@ -257,7 +257,8 @@ extern const char *SQLSTATE_UNKNOWN; + MARIADB_OPT_RESTRICTED_AUTH, + MARIADB_OPT_RPL_REGISTER_REPLICA, + MARIADB_OPT_STATUS_CALLBACK, +- MARIADB_OPT_SERVER_PLUGINS ++ MARIADB_OPT_SERVER_PLUGINS, ++ MARIADB_OPT_SSL_KEYLOG_CALLBACK + }; + + enum mariadb_value { +diff --git libmariadb/mariadb_lib.c libmariadb/mariadb_lib.c +index be4c91d1..87dd7c3e 100644 +--- libmariadb/mariadb_lib.c ++++ libmariadb/mariadb_lib.c +@@ -3634,6 +3634,9 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) + case MYSQL_OPT_SSL_CRLPATH: + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crlpath, (char *)arg1); + break; ++ case MARIADB_OPT_SSL_KEYLOG_CALLBACK: ++ OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_keylog_callback, arg1); ++ break; + case MYSQL_OPT_CONNECT_ATTR_DELETE: + { + uchar *h; +@@ -4000,6 +4003,9 @@ mysql_get_optionv(MYSQL *mysql, enum mysql_option option, void *arg, ...) + case MYSQL_OPT_SSL_CRLPATH: + *((char **)arg)= mysql->options.extension ? mysql->options.extension->ssl_crlpath : NULL; + break; ++ case MARIADB_OPT_SSL_KEYLOG_CALLBACK: ++ *((void(**)(const void *, const char *))arg)= mysql->options.extension ? mysql->options.extension->ssl_keylog_callback : NULL; ++ break; + case MARIADB_OPT_TLS_VERSION: + case MYSQL_OPT_TLS_VERSION: + *((char **)arg)= mysql->options.extension ? mysql->options.extension->tls_version : NULL; +diff --git libmariadb/secure/openssl.c libmariadb/secure/openssl.c +index 2a272504..67d90c6a 100644 +--- libmariadb/secure/openssl.c ++++ libmariadb/secure/openssl.c +@@ -317,6 +317,13 @@ int ma_tls_get_password(char *buf, int size, + return (int)strlen(buf); + } + ++static void ma_tls_set_sslkeylog_callback(MYSQL *mysql, SSL_CTX *ctx) ++{ ++ if (mysql->options.extension && mysql->options.extension->ssl_keylog_callback) ++ { ++ SSL_CTX_set_keylog_callback(ctx, (void(*)(const SSL*, const char*))mysql->options.extension->ssl_keylog_callback); ++ } ++} + + static int ma_tls_set_certs(MYSQL *mysql, SSL_CTX *ctx) + { +@@ -433,7 +440,7 @@ void *ma_tls_init(MYSQL *mysql) + if (mysql->options.extension) + options= ma_tls_version_options(mysql->options.extension->tls_version); + SSL_CTX_set_options(ctx, options ? options : default_options); +- ++ ma_tls_set_sslkeylog_callback(mysql, ctx); + if (ma_tls_set_certs(mysql, ctx)) + { + goto error; diff --git a/deps/mariadb-client-library/unittest_basic-t.c.patch b/deps/mariadb-client-library/unittest_basic-t.c.patch index 3f0737041a..11b156e5f7 100644 --- a/deps/mariadb-client-library/unittest_basic-t.c.patch +++ b/deps/mariadb-client-library/unittest_basic-t.c.patch @@ -1,6 +1,8 @@ ---- /tmp/basic-t.c 2017-12-28 14:55:55.958512116 +0100 -+++ unittest/libmariadb/basic-t.c 2017-12-28 14:56:08.770490417 +0100 -@@ -48,7 +48,7 @@ +diff --git unittest/libmariadb/basic-t.c unittest/libmariadb/basic-t.c +index 40a44962..9d3219be 100644 +--- unittest/libmariadb/basic-t.c ++++ unittest/libmariadb/basic-t.c +@@ -52,7 +52,7 @@ static int test_conc75(MYSQL *my) rc= mysql_query(mysql, "CREATE TABLE a (a varchar(200))"); check_mysql_rc(rc, mysql); diff --git a/deps/mariadb-client-library/unittest_charset.c.patch b/deps/mariadb-client-library/unittest_charset.c.patch index 196218d8d0..8f2212078b 100644 --- a/deps/mariadb-client-library/unittest_charset.c.patch +++ b/deps/mariadb-client-library/unittest_charset.c.patch @@ -1,19 +1,24 @@ -@@ -74,12 +74,12 @@ - char *csname= (char*) "utf8"; +diff --git unittest/libmariadb/charset.c unittest/libmariadb/charset.c +index b438f8d9..0c7c5d7d 100644 +--- unittest/libmariadb/charset.c ++++ unittest/libmariadb/charset.c +@@ -75,13 +75,13 @@ int test_client_character_set(MYSQL *mysql) char *csdefault= (char*)mysql_character_set_name(mysql); + - FAIL_IF(mysql_set_character_set(mysql, csname), mysql_error(mysql)); + FAIL_IF(mysql_set_character_set(mysql, csname, 0), mysql_error(mysql)); mysql_get_character_set_info(mysql, &cs); - FAIL_IF(strcmp(cs.csname, "utf8") || strcmp(cs.name, "utf8_general_ci"), "Character set != UTF8"); + FAIL_IF(strcmp(cs.csname, "latin2") || strcmp(cs.name, "latin2_general_ci"), + "Character set != latin2"); - FAIL_IF(mysql_set_character_set(mysql, csdefault), mysql_error(mysql)); + FAIL_IF(mysql_set_character_set(mysql, csdefault, 0), mysql_error(mysql)); return OK; } -@@ -542,7 +542,7 @@ +@@ -546,7 +546,7 @@ static int test_bug30472(MYSQL *mysql) } /* Retrieve character set information. */ @@ -22,16 +27,16 @@ bug30472_retrieve_charset_info(mysql, character_set_name_1, character_set_client_1, -@@ -551,7 +551,7 @@ +@@ -555,7 +555,7 @@ static int test_bug30472(MYSQL *mysql) /* Switch client character set. */ -- FAIL_IF(mysql_set_character_set(mysql, "utf8"), "Setting cs to utf8 failed"); -+ FAIL_IF(mysql_set_character_set(mysql, "utf8", 0), "Setting cs to utf8 failed"); +- FAIL_IF(mysql_set_character_set(mysql, "ascii"), ++ FAIL_IF(mysql_set_character_set(mysql, "ascii", 0), + "Setting cs to ascii failed"); /* Retrieve character set information. */ - -@@ -581,7 +581,7 @@ +@@ -587,7 +587,7 @@ static int test_bug30472(MYSQL *mysql) /* Call mysql_change_user() with the same username, password, database. */ rc= mysql_change_user(mysql, username, password, (schema) ? schema : "test"); @@ -40,17 +45,17 @@ check_mysql_rc(rc, mysql); /* Retrieve character set information. */ -@@ -647,7 +647,7 @@ - /* ignore ucs2 */ - if (strcmp(row[0], "ucs2") && strcmp(row[0], "utf16le") && strcmp(row[0], "utf8mb4") && - strcmp(row[0], "utf16") && strcmp(row[0], "utf32")) { +@@ -658,7 +658,7 @@ static int test_bug_54100(MYSQL *mysql) + && (strcmp(row[0], "utf8") && mariadb_connection(mysql) && mysql_get_server_version(mysql) >= 100600) + && strcmp(row[0], "utf16") + && strcmp(row[0], "utf32")) { - rc= mysql_set_character_set(mysql, row[0]); + rc= mysql_set_character_set(mysql, row[0], 0); check_mysql_rc(rc, mysql); } } -@@ -748,7 +748,7 @@ - +@@ -765,14 +765,14 @@ static int charset_auto(MYSQL *my __attribute__((unused))) + "character set is not os character set"); if (strcmp(osname, "utf8")) { - rc= mysql_set_character_set(mysql, "utf8"); @@ -58,8 +63,7 @@ check_mysql_rc(rc, mysql); csname2= mysql_character_set_name(mysql); -@@ -756,7 +756,7 @@ - + diag("Character set: %s", csname2); FAIL_IF(!strcmp(csname2, csname1), "Wrong charset: expected utf8"); - rc= mysql_set_character_set(mysql, "auto"); diff --git a/deps/mariadb-client-library/openssl.c.x509cache.patch b/deps/mariadb-client-library/x509cache.patch similarity index 70% rename from deps/mariadb-client-library/openssl.c.x509cache.patch rename to deps/mariadb-client-library/x509cache.patch index 81109405c9..08233ad408 100644 --- a/deps/mariadb-client-library/openssl.c.x509cache.patch +++ b/deps/mariadb-client-library/x509cache.patch @@ -1,4 +1,44 @@ -@@ -30,6 +30,12 @@ +diff --git libmariadb/mariadb_lib.c libmariadb/mariadb_lib.c +index 916024a8..79564a10 100644 +--- libmariadb/mariadb_lib.c ++++ libmariadb/mariadb_lib.c +@@ -133,6 +133,11 @@ my_context_install_suspend_resume_hook(struct mysql_async_context *b, + uint mysql_port=0; + my_string mysql_unix_port=0; + ++#include ++extern __thread SSL_CTX * thread_ctx; ++extern __thread char **local_x509_files; ++extern __thread char **local_x509_sha1s; ++ + #define CONNECT_TIMEOUT 0 + + struct st_mariadb_methods MARIADB_DEFAULT_METHODS; +@@ -4650,6 +4655,19 @@ my_bool STDCALL mysql_thread_init(void) + + void STDCALL mysql_thread_end(void) + { ++ if (local_x509_files != NULL) { ++ int i; ++ for (i=0; local_x509_files[i] != NULL ; i++) { ++ free(local_x509_files[i]); ++ free(local_x509_sha1s[i]); ++ } ++ free(local_x509_files); ++ free(local_x509_sha1s); ++ } ++ if (thread_ctx != NULL) { ++ SSL_CTX_free(thread_ctx); ++ thread_ctx = NULL; ++ } + } + + int STDCALL mysql_set_server_option(MYSQL *mysql, +diff --git libmariadb/secure/openssl.c libmariadb/secure/openssl.c +index 67d90c6a..26938959 100644 +--- libmariadb/secure/openssl.c ++++ libmariadb/secure/openssl.c +@@ -30,6 +30,11 @@ #include #include @@ -6,12 +46,11 @@ +#include +#include +#include -+ + #if defined(_WIN32) && !defined(_OPENSSL_Applink) && defined(HAVE_OPENSSL_APPLINK_C) #include #endif -@@ -73,6 +79,84 @@ +@@ -70,6 +75,82 @@ extern my_bool ma_tls_initialized; extern unsigned int mariadb_deinitialize_ssl; @@ -21,10 +60,8 @@ +__thread char **local_x509_sha1s = NULL; + +static int proxysql_SSL_CTX_local_x509store_add_file(SSL_CTX *ctx, const char *CAfile, int add_in_store) { -+ int found = 0; -+ int i = 0; ++ int found=0, i=0, fd=-1; + -+ int fd = -1; + struct stat statbuf; + unsigned char temp[SHA_DIGEST_LENGTH]; + char file_sha1[SHA_DIGEST_LENGTH*2+2]; @@ -36,8 +73,8 @@ + if(fstat(fd, &statbuf) == 0) { + unsigned char *fb = (unsigned char *)mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (fb != MAP_FAILED) { ++ int i; + SHA1(fb, statbuf.st_size, temp); -+ int i; + for (i=0; i < SHA_DIGEST_LENGTH; i++) { + sprintf((char*)&(file_sha1[i*2]), "%02x", temp[i]); + } @@ -60,11 +97,11 @@ + } + + while (found == 0 && local_x509_files[i] != NULL) { -+ if (strcmp(CAfile, local_x509_files[i]) == 0) { -+ found = 1; -+ } else { -+ i++; -+ } ++ if (strcmp(CAfile, local_x509_files[i]) == 0) { ++ found = 1; ++ } else { ++ i++; ++ } + } + if (found == 1) { + if (strncmp(local_x509_sha1s[i],file_sha1,SHA_DIGEST_LENGTH*2)==0) { @@ -96,17 +133,17 @@ #define MAX_SSL_ERR_LEN 100 char tls_library_version[TLS_VERSION_LENGTH]; -@@ -456,7 +540,7 @@ +@@ -331,7 +412,7 @@ static int ma_tls_set_certs(MYSQL *mysql, SSL_CTX *ctx) + *keyfile= mysql->options.ssl_key; char *pw= (mysql->options.extension) ? mysql->options.extension->tls_pw : NULL; - SSL_CTX *ctx= SSL_get_SSL_CTX(ssl); - + int ssl_rc = 0; - /* add cipher */ if ((mysql->options.ssl_cipher && -@@ -467,16 +551,32 @@ - } + mysql->options.ssl_cipher[0] != 0)) +@@ -345,10 +426,27 @@ static int ma_tls_set_certs(MYSQL *mysql, SSL_CTX *ctx) + } /* ca_file and ca_path */ - if (!SSL_CTX_load_verify_locations(ctx, @@ -129,25 +166,11 @@ + if (mysql->options.ssl_ca != NULL) { + proxysql_SSL_CTX_local_x509store_add_file(ctx,mysql->options.ssl_ca, 0); + } -+ } ++ } + } + } + if (ssl_rc == 0) { if (mysql->options.ssl_ca || mysql->options.ssl_capath) goto error; if (SSL_CTX_set_default_verify_paths(ctx) == 0) - goto error; - } -- - if (keyfile && !certfile) - certfile= keyfile; - if (certfile && !keyfile) -@@ -566,6 +666,8 @@ - SSL_CTX_sess_set_remove_cb(ctx, ma_tls_remove_session_cb); - #endif - -+// if (local_x509store != NULL) -+// SSL_CTX_set1_cert_store(ctx, local_x509store); - if (!(ssl= SSL_new(ctx))) - goto error; - + diff --git a/deps/mariadb-client-library/zutil.c-multiplication-overflow.patch b/deps/mariadb-client-library/zutil.c-multiplication-overflow.patch index 524149f842..f1388ac893 100644 --- a/deps/mariadb-client-library/zutil.c-multiplication-overflow.patch +++ b/deps/mariadb-client-library/zutil.c-multiplication-overflow.patch @@ -1,7 +1,11 @@ -@@ -303,7 +303,7 @@ +diff --git external/zlib/zutil.c external/zlib/zutil.c +index 9543ae82..6a1ed031 100644 +--- external/zlib/zutil.c ++++ external/zlib/zutil.c +@@ -310,7 +310,7 @@ voidpf ZLIB_INTERNAL zcalloc(opaque, items, size) unsigned size; { - if (opaque) items += size - size; /* make compiler happy */ + (void)opaque; - return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + return sizeof(uInt) > 2 ? (voidpf)malloc((long) items * size) : (voidpf)calloc(items, size); diff --git a/doc/proxysql_cluster/proxysql_cluster_working.md b/doc/proxysql_cluster/proxysql_cluster_working.md new file mode 100644 index 0000000000..bb91a5089b --- /dev/null +++ b/doc/proxysql_cluster/proxysql_cluster_working.md @@ -0,0 +1,222 @@ +# Introduction +This documentation provides an in-depth look at the internal workings of the ProxySQL Cluster feature. It is intended for readers who are already familiar with the basic concepts and functionality of ProxySQL Cluster. + +# Prerequisites +Before reading this documentation, it is mandatory that the reader has gone through the official ProxySQL Cluster documentation available at [https://proxysql.com/documentation/proxysql-cluster/](https://proxysql.com/documentation/proxysql-cluster/). This will provide the necessary background knowledge and understanding of terminologies to understand the internal workings of the feature. + +# Important classes used by ProxySQL Cluster +This section describes the classes that are used by the ProxySQL Cluster feature. + +## ProxySQL_Cluster +The `ProxySQL_Cluster` class is the core component for managing ProxySQL clusters. It provides a wide range of operations and functionalities to handle and manage the cluster effectively. With `ProxySQL_Cluster`, you can add or remove nodes, manage node weights, monitor cluster status, and perform other essential tasks. + +## ProxySQL_Node_Entry +The `ProxySQL_Node_Entry` class represents an individual node within a ProxySQL cluster. It serves as a container for storing information and provides convenient methods to access node-specific details. It consists of important node attributes such as its status, weight, connection statistics, and contains global_checksum and checksum, epoch and version of individual modules of that node. + +## ProxySQL_Cluster_Nodes +The `ProxySQL_Cluster_Nodes` class serves as a centralized manager for all the nodes within a ProxySQL cluster. It provides a cohesive interface to handle operations related to the entire cluster's nodes efficiently. Through the `ProxySQL_Cluster_Nodes` class, you can easily manage and manipulate various node-specific actions within the cluster context. It consists of an unordered map that contains all the nodes. + +## ProxySQL_Node_Address +The `ProxySQL_Node_Address` class represents a ProxySQL node's address and encapsulates the hostname, port, node UUID, admin_mysql_ifaces, and optional IP address. + +## ProxySQL_GlobalVariables +The `ProxySQL_GlobalVariables` class contains checksum, epoch, and version of individual modules of the current ProxySQL instance. It provides a way to access and manage the global variables related to the ProxySQL Cluster. + +## ProxySQL_Checksum_Value +The `ProxySQL_Checksum_Value` class is used to store checksum values for modules. It includes member variables such as version and epoch to keep track of the version and epoch of the checksum value. + + +# Initializing Monitoring Threads +Each node within the ProxySQL cluster monitors core nodes to identify any updates in module configuration. This is achieved by dedicating a separate thread for each core node, for continuous monitoring for changes. + +## Load ProxySQL Servers +When the ProxySQL instance is started or the `LOAD PROXYSQL SERVERS TO RUNTIME` command is executed, the latest `proxysql_servers` table records are fetched from the database. This result set is passed to the ProxySQL Cluster's `load_servers_list` method. + +## Initialize ProxySQL Cluster Nodes +The retrieved result set is used to initialize the ProxySQL Cluster Nodes. The `load_servers_list` method of `ProxySQL_Cluster_Nodes` creates a map of all the nodes by utilizing the `ProxySQL_Node_Entry` objects. Each `ProxySQL_Node_Entry` object represents a node in the cluster and holds information such as the host, port, weight, and comment of the node. + +## Mark all nodes Inactive +Mark all the nodes in the `ProxySQL_Cluster_Nodes` map as inactive. + +## Check Node Existence and Update +For each node in the retrieved result set, the existence of the node is checked within the `ProxySQL_Cluster_Nodes` map. If the node already exists in the map, it is marked as active, and any changes in the weights and comments are updated accordingly. + +## Create New Node Entry +If a node does not exist in the map, a new `ProxySQL_Node_Entry` object is created for that node. The `ProxySQL_Node_Entry` is initialized with the host, port, weight, and comment of the node. This new entry is then inserted into the ProxySQL Cluster Nodes map. + +## Create Monitoring Thread +Subsequently, a monitoring thread is created for each node in the ProxySQL cluster. These monitoring threads are responsible for effectively managing and monitoring the status and performance of each node. The `ProxySQL_Node_Address`, containing the host and port of the node, is passed as an argument to the monitoring thread. + +## Remove Inactive Nodes +After iterating through all the entries in the result set and performing the necessary operations, any inactive nodes present in the `ProxySQL_Cluster_Nodes` map are deleted. This ensures that only active and relevant nodes remain within the cluster. + +## Flowchart: +```mermaid +flowchart TD + +subgraph Cluster Nodes Monitoring Threads + start[Start] --> load[LOAD PROXYSQL SERVERS TO RUNTIME] --> select[SELECT hostname, port, weight, comment FROM proxysql_servers ORDER BY hostname, port] + select -- ProxySQL_Cluster::load_servers_list -> ProxySQL_Cluster_Nodes::load_servers_list ->ProxySQL_Cluster_Nodes::set_all_inactive --> test + test[Set all nodes to inactive state] + test -- ProxySQL_Cluster::load_servers_list -> ProxySQL_Cluster_Nodes::load_servers_list --> check[Next node available in resultset?] + check -- NO --> preend[PREEND] + check -- YES
ProxySQL_Cluster_Nodes::umap_proxy_nodes --> check_map{Node exists in map?} + check_map -- YES --> update[Update node weight, comment, and set node state to active] + check_map -- NO --> create1[Create new ProxySQL_Node_Entry with node host, port, weight, and comment and set node state to active and insert it in the map
Create new thread for node and send ProxySQL_Node_Address with node host and port as argument to that thread] --> check + update --> check + preend[Remove all inactive nodes] --> END[END] + END[END] +end +``` + +# Monitoring Thread Working +This section describes how each monitoring thread representing a specific ProxySQL cluster node performs its operations. + +## Check ProxySQL Version +The thread compares the ProxySQL version of the node with the local instance's ProxySQL version by executing the `SELECT @@version` query. If the versions do not match, the connection with the remote peer is terminated. + +## Register Node +If the versions match, `PROXYSQL CLUSTER_NODE_UUID ProxySQL_GlobalVariables::uuid ProxySQL_Cluster::admin_mysql_ifaces` command is sent to the remote peer to register current node as a client. + +## Global Checksum +Global checksum is fetched from the remote peer by sending `SELECT GLOBAL_CHECKSUM()` query. The resultset is passed to the `ProxySQL_Cluster::Update_Global_Checksum` method, which finds the corresponding `ProxySQL_Node_Entry` in the `ProxySQL_Cluster_Nodes` map. If the new global checksum is different from the previously saved one, the global checksum is updated in the `ProxySQL_Node_Entry`. + +## Module Checksum +If the global checksum is updated, indicating changes in one or more modules. `SELECT * FROM runtime_checksums_values ORDER BY name` query is sent to the remote peer to fetch the latest checksums of all modules, including epoch and version. + +## Compare and Update Checksums +The resultset of the query is passed to the `ProxySQL_Cluster::Update_Node_Checksums` method, which finds the corresponding `ProxySQL_Node_Entry` in the `ProxySQL_Cluster_Nodes` map. The method then compares the checksum values of each module with the recently fetched resultset. If a checksum value is different, it is replaced with the latest one in the `ProxySQL_Node_Entry`, and the diff_check of that module is incremented. + +## Sync Configuration +For each module that has a `ProxySQL_Cluster::cluster_*module_name*_diffs_before_sync` value greater than zero, indicating that the module is enabled for syncing, the thread proceeds to check the node's version and epoch. If the node's version is greater than 1 and own_version is equal to 1 (means instance is just booted) or node's epoch is greater than own_epoch, then it checks diff_check. If diff_check is greater than `ProxySQL_Cluster::cluster_*module_name*_diffs_before_sync`, it fetches latest configuration of that module using `ProxySQL_Cluster::pull_*module_name*_from_peer` method. + +## Select Sync Source +The thread calls `ProxySQL_Cluster_Nodes::get_peer_to_sync_*module_name*` to iterate over `ProxySQL_Cluster_Nodes` map and find a node with a version greater than 1 and maximum epoch value among all nodes. This node is selected as source of truth for syncing. + +## Fetch and Apply Configuration +Connection is created to selected node using credentials obtained from `ProxySQL_Cluster::get_credentials` and fetches latest configuration for the module. After fetching configuration, it computes checksum locally and compares it with checksum received from node. If checksums match, changes are applied to runtime. Otherwise changes are discarded. + +## Save Configuration +If "save to disk" variable is set to true, configuration of that module is saved to disk ensuring persistence. + +## Flowchart (Simplified): +```mermaid + graph TB + A[Start] --> A1 + A1["Connect to remote peer and fetch ProxySQL version"] --> B + B{Compare ProxySQL version of remote peer with local instance} + B -->|Versions are not same| C[Close connection with the remote peer] + B -->|Versions are same| D[Register current node as a client] + D --> E[Fetch global checksum from remote peer] + E --> F{Compare global checksum with local global checksum} + F -->|Checksums are same| G[Do nothing] + F -->|Checksums are different| H[Override previous local global checksum with new one] + H --> K[Fetch checksums of all modules from peer node] + K --> N{Compare local module checksum_values with fetched remote peer resultset} + N -->|Checksums are same| O[Do nothing] + N -->|Checksums are different| P[Replace checksum and increment module diff_check] + P --> Q{Check if module cluster_*module_name*_diffs_before_sync > 0} + Q -->|Value is not greater than 0| R[Do nothing] + Q -->|Value is greater than 0| S{Own_version == 1 or local epoch > own_epoch?} + S -->|False| T[Do nothing] + S -->|True| U{diff_check >= cluster_*module_name*_diffs_before_sync?} + U -->|False| V[Do nothing] + U -->|True| X[Find node with version > 1 and max_epoch value] + X --> Y[Connect and fetch module's latest configuration from thet remote peer] + Y --> Y1[Compute checksum of fetched module configuration] + Y1 --> Z{Compare locally computed checksum with remote peer checksum} + Z -->|Checksums are not same| AA[Discard changes] + Z -->|Checksums are same| AB["Apply configuration changes to runtime"] + AB --> AC{Check cluster_*module_name*_save_to_disk == true?} + AC -->|False| AD[Do nothing] + AC -->|True| AE[Save configuration of the module to disk] + AE --> AF[End] +``` + +## Flowchart (Detailed): +```mermaid +graph TD + subgraph ProxySQL_Cluster_Monitor_thread[ProxySQL_Cluster_Monitor_thread] + direction TB + A[Start] -- "ProxySQL_Cluster_Monitor_thread
ProxySQL_Node_Address -> host, port" --> B + B[Connect to remote peer using host and port] --> C + C["Send `SELECT @@version`"] -- "resultset contains remote peer ProxySQL version" --> D + D{Remote peer ProxySQL version == Local PROXYSQL_VERSION?} + D -- "No" --> CLOSE_CONNECTION + D -- "Yes
Register yourself with a remote peer" --> E + E["Send `PROXYSQL CLUSTER_NODE_UUID ProxySQL_GlobalVariables::uuid ProxySQL_Cluster::admin_mysql_ifaces`"] --> F + F{ProxySQL_GlobalVariables::shutdown == 0} + F -- "Yes
Fetch Global Checksum from remote peer" --> G + F -- "No" --> CLOSE_CONNECTION + G["Send `SELECT GLOBAL_CHECKSUM()`"] -- "resultset contains global checksum of remote peer
ProxySQL_Cluster::Update_Global_Checksum" --> I + I{Local global checksum == Remote peer global checksum?} + I -- "Yes" --> AAA + I -- "No" --> J + J[Update local global checksum with remote peer global checksum value] -- "return checksum_updated = true
ProxySQL_Cluster_Monitor_thread

one or more module configuration had been changed. Fetching all the modules checksum, version and epoch from remote peer" --> K + K["Send `SELECT * FROM runtime_checksums_values ORDER BY name`"] -- "resultset contains module name, checksum, version and epoch
ProxySQL_Cluster_Nodes::Update_Node_Checksums
ProxySQL_Node_Entry::set_checksums" --> M + subgraph set_checksum[ProxySQL_Node_Entry::set_checksums] + direction TB + M[Load cluster_*module_name*_diffs_before_sync variables] --> N + N{resultset != NULL && record availabe in resultset?} -- "Yes

**logic is same for all module: module name == admin_variables
module name == mysql_query_rules
module name == mysql_servers
module name == mysql_users
module name == mysql_variables
module name == proxysql_servers
module name == ldap_variables**" --> O + O["Update local module version, epoch and last_updated value with the remote peer value"] --> P + P{Local module checksum == Remote peer module checksum?} + P -- "Yes" --> Q + P -- "No" --> S + Q[Set local module diff_check += 1] --> T + S[Update local module checksum with remote peer checksum value, last_changed to current time and diff_check = 1] --> T + T{Own module checksum == Local module checksum?} + T -- "Yes" --> U + T -- "No" --> N + U[Set local module diff_check = 0] --> N + N -- "No
cluster_*module_name*_diffs_before_sync variables" --> R + R{resultset == NULL?} + R -- "Yes
For every module" --> YY + YY[Set local module last_updated = current time] --> XX + XX{Local module checksum == Own module version?} + XX -- "Yes" --> WW + WW[Set local module diff_check = 0] --> V + XX -- "No" --> VV + VV[Set local module diff_check += 1] --> V + V{cluster_*module_name*_diffs_before_sync variables != 0?} + V -- "Yes" --> W + V -- "No" --> DELAY + W{Local module version > 1?} + W -- "Yes" --> X + W -- "No" --> V + X{"Own module version == 1 || Local module epoch > Own module epoch?"} + X -- "Yes" --> Y + X -- "No" --> V + Y{Local module diff_check >= cluster_*module_name*_diffs_before_sync?} + Y -- "No" --> V + Y -- "Yes
ProxySQL_Cluster_Nodes::umap_proxy_nodes" --> Y1 + Y1["Find local node with version > 1 and max_epoch"] -- "ProxySQL_Cluster::pull_*module_name*_from_peer" --> Z + Z["Connect to that remote peer and fetch latest module configuration"] --> AA + AA["Locally compute checksum of fetched configuration"] --> BB + BB{"Locally computed checksum == Remote peer checksum?"} + BB -- "Yes" --> DD + BB -- "No" --> DELAY + DD["Delete records in local module configuration table(s)"] --> EE + EE["Insert retrieved data from remote peer into local module configuration table(s)"] --> FF + FF["Issue internal `LOAD *module_name* TO RUNTIME`"] --> GG + GG{"Check cluster_*module_name*_save_to_disk == true?"} + GG -- "Yes" --> II + GG -- "No" --> AAA + II[Issue internal `SAVE *module_name* TO DISK`] + II --> AAA + end + AAA{"Local cluster_check_status_frequency_count >= cluster_check_status_frequency?"} + AAA -- "No" --> BBB + AAA -- Yes --> DDD + BBB[Set cluster_check_status_frequency_count += 1] --> DELAY + DDD[Set cluster_check_status_frequency_count = 0] --> EEE + EEE[Send `SELECT * FROM stats_mysql_global ORDER BY Variable_Name`] + EEE -- "resultset contains Client_Connections_connected, Client_Connections_created, ProxySQL_Uptime, Questions, Servers_table_version of remote peer
ProxySQL_Cluster_Nodes::Update_Node_Metrics
ProxySQL_Node_Entry::set_metrics" --> FFF + subgraph set_metrics[ProxySQL_Node_Entry::set_metrics] + direction TB + FFF[Update local metrices with values from resultset] --> DELAY + end + DELAY[Sleep cluster_check_interval_ms] --> F + CLOSE_CONNECTION[Close Connection] --> END + END[End] +end +``` \ No newline at end of file diff --git a/include/MySQL_HostGroups_Manager.h b/include/MySQL_HostGroups_Manager.h index 1f5c1d8dcc..83abc5c05a 100644 --- a/include/MySQL_HostGroups_Manager.h +++ b/include/MySQL_HostGroups_Manager.h @@ -66,6 +66,55 @@ using json = nlohmann::json; #define MYHGM_MYSQL_HOSTGROUP_ATTRIBUTES "CREATE TABLE mysql_hostgroup_attributes (hostgroup_id INT NOT NULL PRIMARY KEY , max_num_online_servers INT CHECK (max_num_online_servers>=0 AND max_num_online_servers <= 1000000) NOT NULL DEFAULT 1000000 , autocommit INT CHECK (autocommit IN (-1, 0, 1)) NOT NULL DEFAULT -1 , free_connections_pct INT CHECK (free_connections_pct >= 0 AND free_connections_pct <= 100) NOT NULL DEFAULT 10 , init_connect VARCHAR NOT NULL DEFAULT '' , multiplex INT CHECK (multiplex IN (0, 1)) NOT NULL DEFAULT 1 , connection_warming INT CHECK (connection_warming IN (0, 1)) NOT NULL DEFAULT 0 , throttle_connections_per_sec INT CHECK (throttle_connections_per_sec >= 1 AND throttle_connections_per_sec <= 1000000) NOT NULL DEFAULT 1000000 , ignore_session_variables VARCHAR CHECK (JSON_VALID(ignore_session_variables) OR ignore_session_variables = '') NOT NULL DEFAULT '' , hostgroup_settings VARCHAR CHECK (JSON_VALID(hostgroup_settings) OR hostgroup_settings = '') NOT NULL DEFAULT '' , servers_defaults VARCHAR CHECK (JSON_VALID(servers_defaults) OR servers_defaults = '') NOT NULL DEFAULT '' , comment VARCHAR NOT NULL DEFAULT '')" +/* + * @brief Generates the 'runtime_mysql_servers' resultset exposed to other ProxySQL cluster members. + * @details Makes 'SHUNNED' and 'SHUNNED_REPLICATION_LAG' statuses equivalent to 'ONLINE'. 'SHUNNED' states + * are by definition local transitory states, this is why a 'mysql_servers' table reconfiguration isn't + * normally performed when servers are internally imposed with these statuses. This means, that propagating + * this state to other cluster members is undesired behavior, and so it's generating a different checksum, + * due to a server having this particular state, that will result in extra unnecessary fetching operations. + * The query also filters out 'OFFLINE_HARD' servers, 'OFFLINE_HARD' is a local status which is equivalent to + * a server no longer being part of the table (DELETED state). And so, they shouldn't be propagated. + * + * For placing the query into a single line for debugging purposes: + * ``` + * sed 's/^\t\+"//g; s/"\s\\$//g; s/\\"/"/g' /tmp/select.sql | paste -sd '' + * ``` + */ +#define MYHGM_GEN_CLUSTER_ADMIN_RUNTIME_SERVERS \ + "SELECT " \ + "hostgroup_id, hostname, port, gtid_port," \ + "CASE status" \ + " WHEN 0 THEN \"ONLINE\"" \ + " WHEN 1 THEN \"ONLINE\"" \ + " WHEN 2 THEN \"OFFLINE_SOFT\"" \ + " WHEN 3 THEN \"OFFLINE_HARD\"" \ + " WHEN 4 THEN \"ONLINE\" " \ + "END status," \ + "weight, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment " \ + "FROM mysql_servers " \ + "WHERE status != 3 " \ + "ORDER BY hostgroup_id, hostname, port" \ + +/** + * @brief Generates the 'mysql_servers_v2' resultset exposed to other ProxySQL cluster members. + * @details The generated resultset is used for the checksum computation of the runtime ProxySQL config + * ('mysql_servers_v2' checksum), and it's also forwarded to other cluster members when querying the Admin + * interface with 'CLUSTER_QUERY_MYSQL_SERVERS_V2'. It makes 'SHUNNED' state equivalent to 'ONLINE', and also + * filters out any 'OFFLINE_HARD' entries. This is done because none of the statuses are valid configuration + * statuses, they are local, transient status that ProxySQL uses during operation. + */ +#define MYHGM_GEN_CLUSTER_ADMIN_MYSQL_SERVERS \ + "SELECT " \ + "hostgroup_id, hostname, port, gtid_port, " \ + "CASE" \ + " WHEN status=\"SHUNNED\" THEN \"ONLINE\"" \ + " ELSE status " \ + "END AS status, " \ + "weight, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment " \ + "FROM main.mysql_servers " \ + "WHERE status != \"OFFLINE_HARD\" " \ + "ORDER BY hostgroup_id, hostname, port" \ typedef std::unordered_map umap_mysql_errors; @@ -437,6 +486,27 @@ enum REPLICATION_LAG_SERVER_T { RLS__SIZE }; +/** + * @brief Contains the minimal info for server creation. + */ +struct srv_info_t { + /* @brief Server address */ + string addr; + /* @brief Server port */ + uint16_t port; + /* @brief Server type identifier, used for logging, e.g: 'Aurora AWS', 'GR', etc... */ + string kind; +}; + +/** + * @brief Contains options to be specified during server creation. + */ +struct srv_opts_t { + int64_t weigth; + int64_t max_conns; + int32_t use_ssl; +}; + class MySQL_HostGroups_Manager { private: SQLite3DB *admindb; @@ -451,12 +521,13 @@ class MySQL_HostGroups_Manager { #endif enum HGM_TABLES { - MYSQL_SERVERS = 0, + MYSQL_SERVERS_V2 = 0, MYSQL_REPLICATION_HOSTGROUPS, MYSQL_GROUP_REPLICATION_HOSTGROUPS, MYSQL_GALERA_HOSTGROUPS, MYSQL_AWS_AURORA_HOSTGROUPS, MYSQL_HOSTGROUP_ATTRIBUTES, + MYSQL_SERVERS, __HGM_TABLES_SIZE }; @@ -539,8 +610,26 @@ class MySQL_HostGroups_Manager { MySQL_HostGroups_Manager* myHGM; }; + /** + * @brief Used by 'MySQL_Monitor::read_only' to hold a mapping between servers and hostgroups. + * @details The hostgroup mapping holds the MySrvC for each of the hostgroups in which the servers is + * present, distinguishing between 'READER' and 'WRITER' hostgroups. + */ std::unordered_map> hostgroup_server_mapping; + /** + * @brief Holds the previous computed checksum for 'mysql_servers'. + * @details Used to check if the servers checksums has changed during 'commit', if a change is detected, + * the member 'hostgroup_server_mapping' is required to be regenerated. + * + * This is only updated during 'read_only_action_v2', since the action itself modifies + * 'hostgroup_server_mapping' in case any actions needs to be performed against the servers. + */ uint64_t hgsm_mysql_servers_checksum = 0; + /** + * @brief Holds the previous checksum for the 'MYSQL_REPLICATION_HOSTGROUPS'. + * @details Used during 'commit' to determine if config has changed for 'MYSQL_REPLICATION_HOSTGROUPS', + * and 'hostgroup_server_mapping' should be rebuild. + */ uint64_t hgsm_mysql_replication_hostgroups_checksum = 0; @@ -559,7 +648,7 @@ class MySQL_HostGroups_Manager { /** * @brief This resultset holds the current values for 'runtime_mysql_servers' computed by either latest * 'commit' or fetched from another Cluster node. It's also used by ProxySQL_Admin to respond to the - * intercepted query 'CLUSTER_QUERY_MYSQL_SERVERS'. + * intercepted query 'CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS'. * @details This resultset can't right now just contain the value for 'incoming_mysql_servers' as with the * rest of the intercepted resultset. This is due to 'runtime_mysql_servers' reconfigurations that can be * triggered by monitoring actions like 'Galera' currently performs. These actions not only trigger status @@ -590,6 +679,17 @@ class MySQL_HostGroups_Manager { SQLite3_result *incoming_replication_hostgroups; void generate_mysql_group_replication_hostgroups_table(); + /** + * @brief Regenerates the resultset used by 'MySQL_Monitor' containing the servers to be monitored. + * @details This function is required to be called after any action that results in the addition of a new + * server that 'MySQL_Monitor' should be aware of for 'group_replication', i.e. a server added to the + * hostgroups present in any entry of 'mysql_group_replication_hostgroups'. E.g: + * - Inside 'generate_mysql_group_replication_hostgroups_table'. + * - Autodiscovery. + * + * NOTE: This is a common pattern for all the clusters monitoring. + */ + void generate_mysql_group_replication_hostgroups_monitor_resultset(); SQLite3_result *incoming_group_replication_hostgroups; pthread_mutex_t Group_Replication_Info_mutex; @@ -610,6 +710,8 @@ class MySQL_HostGroups_Manager { void generate_mysql_hostgroup_attributes_table(); SQLite3_result *incoming_hostgroup_attributes; + SQLite3_result* incoming_mysql_servers_v2; + std::thread *HGCU_thread; std::thread *GTID_syncer_thread; @@ -753,16 +855,78 @@ class MySQL_HostGroups_Manager { void wrlock(); void wrunlock(); int servers_add(SQLite3_result *resultset); - bool commit(SQLite3_result* runtime_mysql_servers = nullptr, const std::string& checksum = "", const time_t epoch = 0); + /** + * @brief Generates a new global checksum for module 'mysql_servers_v2' using the provided hash. + * @param servers_v2_hash The 'raw_checksum' from 'MYHGM_GEN_CLUSTER_ADMIN_MYSQL_SERVERS' or peer node. + * @return Checksum computed using the provided hash, and 'mysql_servers' config tables hashes. + */ + std::string gen_global_mysql_servers_v2_checksum(uint64_t servers_v2_hash); + bool commit( + const peer_runtime_mysql_servers_t& peer_runtime_mysql_servers = {}, + const peer_mysql_servers_v2_t& peer_mysql_servers_v2 = {}, + bool only_commit_runtime_mysql_servers = true, + bool update_version = false + ); + /** + * @brief Extracted from 'commit'. Performs the following actions: + * 1. Re-generates the 'myhgm.mysql_servers' table. + * 2. If supplied 'runtime_mysql_servers' is 'nullptr': + * 1. Gets the contents of the table via 'MYHGM_GEN_CLUSTER_ADMIN_RUNTIME_SERVERS'. + * 2. Save the resultset into 'this->runtime_mysql_servers'. + * 3. If supplied 'runtime_mysql_servers' isn't 'nullptr': + * 1. Updates the 'this->runtime_mysql_servers' with it. + * 4. Updates 'HGM_TABLES::MYSQL_SERVERS' with raw checksum from 'this->runtime_mysql_servers'. + * @param runtime_mysql_servers If not 'nullptr', used to update 'this->runtime_mysql_servers'. + * @return The updated 'MySQL_HostGroups_Manager::runtime_mysql_servers'. + */ + uint64_t commit_update_checksum_from_mysql_servers(SQLite3_result* runtime_mysql_servers = nullptr); + /** + * @brief Analogous to 'commit_generate_mysql_servers_table' but for 'incoming_mysql_servers_v2'. + */ + uint64_t commit_update_checksum_from_mysql_servers_v2(SQLite3_result* incoming_mysql_servers_v2 = nullptr); + /** + * @brief Update all HGM_TABLES checksums and uses them to update the supplied SpookyHash. + * @details Checksums are the checksums for the following tables: + * - mysql_replication_hostgroups + * - mysql_group_replication_hostgroups + * - mysql_galera_hostgroups + * - mysql_aws_aurora_hostgroups + * - mysql_hostgroup_attributes + * + * These checksums are used to compute the global checksum for 'mysql_servers_v2'. + * @param myhash SpookyHash to be updated with all the computed checksums. + * @param init Indicates if the SpookyHash checksum is initialized. + */ void commit_update_checksums_from_tables(SpookyHash& myhash, bool& init); - void CUCFT1(SpookyHash& myhash, bool& init, const string& TableName, const string& ColumnName, uint64_t& raw_checksum); // used by commit_update_checksums_from_tables() - + /** + * @brief Performs the following actions: + * 1. Gets the current contents of table 'myhgm.TableName', using 'ColumnName' ordering. + * 2. Computes the checksum for that resultset. + * 3. Updates the supplied 'raw_checksum' and the supplied 'SpookyHash' with it. + * @details Stands for 'commit_update_checksum_from_table_1'. + * @param myhash Hash to be updated with the resultset checksum from the selected table. + * @param init If the supplied 'SpookyHash' has already being initialized. + * @param TableName The tablename from which to obtain the resultset for the 'raw_checksum' computation. + * @param ColumnName A column name to use for ordering in the supplied 'TableName'. + * @param raw_checksum A 'raw_checksum' to be updated with the obtained resultset. + */ + void CUCFT1( + SpookyHash& myhash, bool& init, const string& TableName, const string& ColumnName, uint64_t& raw_checksum + ); /** * @brief Store the resultset for the 'runtime_mysql_servers' table set that have been loaded to runtime. * The store configuration is later used by Cluster to propagate current config. * @param The resulset to be stored replacing the current one. */ void save_runtime_mysql_servers(SQLite3_result *); + + /** + * @brief Store the resultset for the 'mysql_servers_v2' table. + * The store configuration is later used by Cluster to propagate current config. + * @param The resulset to be stored replacing the current one. + */ + void save_mysql_servers_v2(SQLite3_result* s); + /** * @brief These setters/getter functions store and retrieve the currently hold resultset for the * 'incoming_*' table set that have been loaded to runtime. The store configuration is later used by @@ -774,7 +938,20 @@ class MySQL_HostGroups_Manager { SQLite3_result* get_current_mysql_table(const string& name); SQLite3_result * execute_query(char *query, char **error); - SQLite3_result *dump_table_mysql(const string&); + /** + * @brief Creates a resultset with the current full content of the target table. + * @param string The target table. Valid values are: + * - "mysql_aws_aurora_hostgroups" + * - "mysql_galera_hostgroups" + * - "mysql_group_replication_hostgroups" + * - "mysql_replication_hostgroups" + * - "mysql_hostgroup_attributes" + * - "mysql_servers" + * - "cluster_mysql_servers" + * When targeting 'mysql_servers' table is purged and regenerated. + * @return The generated resultset. + */ + SQLite3_result* dump_table_mysql(const string&); /** * @brief Update the public member resulset 'mysql_servers_to_monitor'. This resulset should contain the latest @@ -792,6 +969,34 @@ class MySQL_HostGroups_Manager { MyHGC * MyHGC_lookup(unsigned int); void MyConn_add_to_pool(MySQL_Connection *); + /** + * @brief Creates a new server in the target hostgroup if isn't already present. + * @details If the server is found already in the target hostgroup, no action is taken, unless its status + * is 'OFFLINE_HARD'. In case of finding it as 'OFFLINE_HARD': + * 1. Server hostgroup attributes are reset to known values, so they can be updated. + * 2. Server attributes are updated to either table definition values, or hostgroup 'servers_defaults'. + * 3. Server is bring back as 'ONLINE'. + * @param hid The hostgroup in which the server is to be created (or to bring it back as 'ONLINE'). + * @param srv_info Basic server info to be used during creation. + * @param srv_opts Server creation options. + * @return 0 in case of success, -1 in case of failure. + */ + int create_new_server_in_hg(uint32_t hid, const srv_info_t& srv_info, const srv_opts_t& srv_opts); + /** + * @brief Completely removes server from the target hostgroup if found. + * @details Several actions are taken if server is found: + * - Set the server as 'OFFLINE_HARD'. + * - Drop all current FREE connections to the server. + * - Delete the server from the 'myhgm.mysql_servers' table. + * + * This later step is not required if the caller is already going to perform a full deletion of the + * servers in the target hostgroup. Which is a common operation during table regeneration. + * @param hid Target hostgroup id. + * @param addr Target server address. + * @param port Target server port. + * @return 0 in case of success, -1 in case of failure. + */ + int remove_server_in_hg(uint32_t hid, const string& addr, uint16_t port); MySQL_Connection * get_MyConn_from_pool(unsigned int hid, MySQL_Session *sess, bool ff, char * gtid_uuid, uint64_t gtid_trxid, int max_lag_ms); @@ -817,6 +1022,33 @@ class MySQL_HostGroups_Manager { void update_group_replication_set_offline(char *_hostname, int _port, int _writer_hostgroup, char *error); void update_group_replication_set_read_only(char *_hostname, int _port, int _writer_hostgroup, char *error); void update_group_replication_set_writer(char *_hostname, int _port, int _writer_hostgroup); + /** + * @brief Tries to add a new server found during GR autodiscovery to the supplied hostgroup. + * @details For adding the new server, several actions are performed: + * 1. Lookup the target server in the corresponding MyHGC for the supplied hostgroup. + * 2. If server is found, and it's status isn't 'OFFLINE_HARD' do nothing. Otherwise: + * - If server is found as 'OFFLINE_HARD', reset the internal values corresponding to + * 'servers_defaults' values to '-1', update the defaulted values to the ones in its 'MyHGC', lastly + * re-enable the server and log the action. + * - If server isn't found, create it in the corresponding reader hostgroup of the supplied writer + * hostgroup, setting all 'servers_defaults' params as '-1', log the action. + * - After any of the two previous actions, always regenerate servers data structures. + * + * NOTE: Server data structures regeneration requires: + * 1. Purging the 'mysql_servers_table' (Lazy removal of 'OFFLINE_HARD' servers.) + * 2. Regenerate the actual 'myhgm::mysql_servers' table from memory structures. + * 3. Update the 'mysql_servers' resultset used for monitoring. This resultset is used for general + * monitoring actions like 'ping', 'connect'. + * 4. Regenerate the specific resultset for 'Group Replication' monitoring. This resultset is the way to + * communicate back to the main monitoring thread that servers config has changed, and a new thread + * shall be created with the new servers config. This same principle is used for Aurora. + * + * @param _host Server address. + * @param _port Server port. + * @param _wr_hg Writer hostgroup of the cluster being monitored. Autodiscovered servers are always added + * to the reader hostgroup by default, later monitoring actions will re-position the server is required. + */ + void update_group_replication_add_autodiscovered(const std::string& _host, int _port, int _wr_hg); void converge_group_replication_config(int _writer_hostgroup); /** * @brief Set the supplied server as SHUNNED, this function shall be called @@ -855,6 +1087,19 @@ class MySQL_HostGroups_Manager { bool aws_aurora_replication_lag_action(int _whid, int _rhid, char *server_id, float current_replication_lag_ms, bool enable, bool is_writer, bool verbose=true); void update_aws_aurora_set_writer(int _whid, int _rhid, char *server_id, bool verbose=true); void update_aws_aurora_set_reader(int _whid, int _rhid, char *server_id); + /** + * @brief Updates the resultset and corresponding checksum used by Monitor for AWS Aurora. + * @details This is required to be called when: + * - The 'mysql_aws_aurora_hostgroups' table is regenerated (via 'commit'). + * - When new servers are discovered, and created in already monitored Aurora clusters. + * + * The resultset holds the servers that are present in 'mysql_servers' table, and share hostgroups with + * the **active** clusters specified in 'mysql_aws_aurora_hostgroups'. See query + * 'SELECT_AWS_AURORA_SERVERS_FOR_MONITOR'. + * @param lock Wether if both 'AWS_Aurora_Info_mutex' and 'MySQL_Monitor::aws_aurora_mutex' mutexes should + * be taken or not. + */ + void update_aws_aurora_hosts_monitor_resultset(bool lock=false); SQLite3_result * get_stats_mysql_gtid_executed(); void generate_mysql_gtid_executed_tables(); @@ -869,6 +1114,54 @@ class MySQL_HostGroups_Manager { void shutdown(); void unshun_server_all_hostgroups(const char * address, uint16_t port, time_t t, int max_wait_sec, unsigned int *skip_hid); MySrvC* find_server_in_hg(unsigned int _hid, const std::string& addr, int port); + +private: + void update_hostgroup_manager_mappings(); + uint64_t get_mysql_servers_checksum(SQLite3_result* runtime_mysql_servers = nullptr); + uint64_t get_mysql_servers_v2_checksum(SQLite3_result* incoming_mysql_servers_v2 = nullptr); }; +/** + * @brief Helper function used to try to extract a value from the JSON field 'servers_defaults'. + * + * @param j JSON object constructed from 'servers_defaults' field. + * @param hid Hostgroup for which the 'servers_defaults' is defined in 'mysql_hostgroup_attributes'. Used for + * error logging. + * @param key The key for the value to be extracted. + * @param val_check A validation function, checks if the value is within a expected range. + * + * @return The value extracted from the supplied JSON. In case of error '-1', and error cause is logged. + */ +template ::value, bool>::type = true> +T j_get_srv_default_int_val( + const json& j, uint32_t hid, const string& key, const function& val_check +) { + if (j.find(key) != j.end()) { + const json::value_t val_type = j[key].type(); + const char* type_name = j[key].type_name(); + + if (val_type == json::value_t::number_integer || val_type == json::value_t::number_unsigned) { + T val = j[key].get(); + + if (val_check(val)) { + return val; + } else { + proxy_error( + "Invalid value %ld supplied for 'mysql_hostgroup_attributes.servers_defaults.%s' for hostgroup %d." + " Value NOT UPDATED.\n", + static_cast(val), key.c_str(), hid + ); + } + } else { + proxy_error( + "Invalid type '%s'(%hhu) supplied for 'mysql_hostgroup_attributes.servers_defaults.%s' for hostgroup %d." + " Value NOT UPDATED.\n", + type_name, static_cast(val_type), key.c_str(), hid + ); + } + } + + return static_cast(-1); +} + #endif /* __CLASS_MYSQL_HOSTGROUPS_MANAGER_H */ diff --git a/include/MySQL_Monitor.hpp b/include/MySQL_Monitor.hpp index 25388264f3..8454cf939e 100644 --- a/include/MySQL_Monitor.hpp +++ b/include/MySQL_Monitor.hpp @@ -70,6 +70,14 @@ A single AWS_Aurora_monitor_node will have a AWS_Aurora_status_entry per check. */ +#ifdef TEST_AURORA + +#define TEST_AURORA_MONITOR_BASE_QUERY \ + "SELECT SERVER_ID, SESSION_ID, LAST_UPDATE_TIMESTAMP, REPLICA_LAG_IN_MILLISECONDS, CPU"\ + " FROM REPLICA_HOST_STATUS ORDER BY SERVER_ID " + +#endif + class AWS_Aurora_replica_host_status_entry { public: char * server_id = NULL; @@ -200,6 +208,17 @@ enum class MySQL_Monitor_State_Data_Task_Result { TASK_RESULT_PENDING }; +/** + * @brief Holds the info from a GR server definition. + */ +struct gr_host_def_t { + string host; + int port; + int use_ssl; + bool writer_is_also_reader; + int max_transactions_behind; + int max_transactions_behind_count; +}; class MySQL_Monitor_State_Data { public: @@ -237,6 +256,11 @@ class MySQL_Monitor_State_Data { * @details Currently only used by 'group_replication'. */ uint64_t init_time = 0; + /** + * @brief Used by GroupReplication to determine if servers reported by cluster 'members' are already monitored. + * @details This way we avoid non-needed locking on 'MySQL_HostGroups_Manager' for server search. + */ + const std::vector* cur_monitored_gr_srvs = nullptr; MySQL_Monitor_State_Data(MySQL_Monitor_State_Data_Task_Type task_type, char* h, int p, bool _use_ssl = 0, int g = 0); ~MySQL_Monitor_State_Data(); diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index d2de482c79..22cf599717 100644 --- a/include/MySQL_Session.h +++ b/include/MySQL_Session.h @@ -146,9 +146,21 @@ class MySQL_Session bool handler_CommitRollback(PtrSize_t *); bool handler_SetAutocommit(PtrSize_t *); /** - * @brief Performs the cleanup of current session state, and the required operations to the supplied - * 'MySQL_Data_Stream' required for processing further queries. - * @param The 'MySQL_Data_Stream' which executed the previous query and which status should be updated. + * @brief Should execute most of the commands executed when a request is finalized. + * @details Cleanup of current session state, and required operations to the supplied 'MySQL_Data_Stream' + * for further queries processing. Takes care of the following actions: + * - Update the status of the backend connection (if supplied), with previous query actions. + * - Log the query for the required statuses. + * - Cleanup the previous Query_Processor output. + * - Free the resources of the backend connection (if supplied). + * - Reset all the required session status flags. E.g: + * + status + * + client_myds::DSS + * + started_sending_data_to_client + * + previous_hostgroup + * NOTE: Should become the place to hook other functions. + * @param myds If not null, should point to a MySQL_Data_Stream (backend connection) which connection status + * should be updated, and previous query resources cleanup. */ void RequestEnd(MySQL_Data_Stream *); void LogQuery(MySQL_Data_Stream *); diff --git a/include/ProxySQL_Cluster.hpp b/include/ProxySQL_Cluster.hpp index 2eaf88650a..75e2eed0e5 100644 --- a/include/ProxySQL_Cluster.hpp +++ b/include/ProxySQL_Cluster.hpp @@ -19,17 +19,20 @@ * the queries issued for generating the checksum for each of the target modules, for simpler reasoning, they should * also represent the actual resultset being received when issuing them, since this resultset is used for computing the * 'expected checksum' for the fetched config before loading it to runtime. This is done for the following modules: - * - 'runtime_mysql_servers': tables 'mysql_servers', 'mysql_replication_hostgroups', 'mysql_group_replication_hostroups', - * 'mysql_galera_hostgroups', 'mysql_aws_aurora_hostgroups', 'mysql_hostgroup_attributes'. + * - 'runtime_mysql_servers': tables 'mysql_servers' * - 'runtime_mysql_users'. * - 'runtime_mysql_query_rules'. - * + * - 'mysql_servers_v2': tables admin 'mysql_servers', 'mysql_replication_hostgroups', 'mysql_group_replication_hostroups', + * 'mysql_galera_hostgroups', 'mysql_aws_aurora_hostgroups', 'mysql_hostgroup_attributes'. * IMPORTANT: For further clarify this means that it's important that the actual resultset produced by the intercepted * query preserve the filtering and ordering expressed in this queries. */ /* @brief Query to be intercepted by 'ProxySQL_Admin' for 'runtime_mysql_servers'. See top comment for details. */ -#define CLUSTER_QUERY_MYSQL_SERVERS "PROXY_SELECT hostgroup_id, hostname, port, gtid_port, status, weight, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment FROM runtime_mysql_servers WHERE status<>'OFFLINE_HARD' ORDER BY hostgroup_id, hostname, port" +#define CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS "PROXY_SELECT hostgroup_id, hostname, port, gtid_port, status, weight, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment FROM runtime_mysql_servers WHERE status<>'OFFLINE_HARD' ORDER BY hostgroup_id, hostname, port" + +/* @brief Query to be intercepted by 'ProxySQL_Admin' for 'mysql_servers_v2'. See top comment for details. */ +#define CLUSTER_QUERY_MYSQL_SERVERS_V2 "PROXY_SELECT hostgroup_id, hostname, port, gtid_port, status, weight, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment FROM mysql_servers_v2 WHERE status<>'OFFLINE_HARD' ORDER BY hostgroup_id, hostname, port" /* @brief Query to be intercepted by 'ProxySQL_Admin' for 'runtime_mysql_replication_hostgroups'. See top comment for details. */ #define CLUSTER_QUERY_MYSQL_REPLICATION_HOSTGROUPS "PROXY_SELECT writer_hostgroup, reader_hostgroup, comment FROM runtime_mysql_replication_hostgroups ORDER BY writer_hostgroup" @@ -189,6 +192,7 @@ class ProxySQL_Node_Entry { ProxySQL_Checksum_Value_2 mysql_servers; ProxySQL_Checksum_Value_2 mysql_users; ProxySQL_Checksum_Value_2 proxysql_servers; + ProxySQL_Checksum_Value_2 mysql_servers_v2; } checksums_values; uint64_t global_checksum; }; @@ -279,7 +283,9 @@ class ProxySQL_Cluster_Nodes { SQLite3_result * stats_proxysql_servers_checksums(); SQLite3_result * stats_proxysql_servers_metrics(); void get_peer_to_sync_mysql_query_rules(char **host, uint16_t *port, char** ip_address); - void get_peer_to_sync_mysql_servers(char **host, uint16_t *port, char **peer_checksum, char** ip_address); + void get_peer_to_sync_runtime_mysql_servers(char **host, uint16_t *port, char **peer_checksum, char** ip_address); + void get_peer_to_sync_mysql_servers_v2(char** host, uint16_t* port, char** peer_mysql_servers_v2_checksum, + char** peer_runtime_mysql_servers_checksum, char** ip_address); void get_peer_to_sync_mysql_users(char **host, uint16_t *port, char** ip_address); void get_peer_to_sync_mysql_variables(char **host, uint16_t *port, char** ip_address); void get_peer_to_sync_admin_variables(char **host, uint16_t* port, char** ip_address); @@ -366,6 +372,31 @@ struct variable_type { }; }; +/** + * @brief Specifies the sync algorithm to use when pulling config from another ProxySQL cluster peer. + */ +enum class mysql_servers_sync_algorithm { + /** + * @brief Sync 'runtime_mysql_servers' and 'mysql_server_v2' from remote peer. + * @details This means that both config and runtime servers info are to be synced, in other words, both + * user promoted config and runtime changes performed by ProxySQL ('Monitor') in the remote peer will + * trigger the start of syncing operations. + */ + runtime_mysql_servers_and_mysql_servers_v2 = 1, + /** + * @brief Sync only mysql_server_v2 (config) from the remote peer. + * @details Since 'runtime_mysql_servers' isn't considered for fetching, only config changes promoted by + * the user in the remote peer will by acknowledge and trigger the start of a syncing operation. + */ + mysql_servers_v2 = 2, + /** + * @brief Dependent on whether ProxySQL has been started with the -M flag. + * @details If '-M' is not present, 'runtime_mysql_servers_and_mysql_servers_v2' is selected, otherwise + * 'mysql_servers_v2' is chosen. + */ + auto_select = 3 +}; + /** * @brief Simple struct for holding a query, and three messages to report * the progress of the query execution. @@ -378,21 +409,23 @@ struct fetch_query { }; class ProxySQL_Cluster { - private: +private: + SQLite3DB* mydb; pthread_mutex_t mutex; std::vector term_threads; ProxySQL_Cluster_Nodes nodes; - char *cluster_username; - char *cluster_password; + char* cluster_username; + char* cluster_password; struct { - std::array p_counter_array {}; - std::array p_gauge_array {}; + std::array p_counter_array{}; + std::array p_gauge_array{}; } metrics; int fetch_and_store(MYSQL* conn, const fetch_query& f_query, MYSQL_RES** result); friend class ProxySQL_Node_Entry; - public: +public: pthread_mutex_t update_mysql_query_rules_mutex; - pthread_mutex_t update_mysql_servers_mutex; + pthread_mutex_t update_runtime_mysql_servers_mutex; + pthread_mutex_t update_mysql_servers_v2_mutex; pthread_mutex_t update_mysql_users_mutex; pthread_mutex_t update_mysql_variables_mutex; pthread_mutex_t update_proxysql_servers_mutex; @@ -407,7 +440,7 @@ class ProxySQL_Cluster { */ SQLite3_result* proxysql_servers_to_monitor; - char *admin_mysql_ifaces; + char* admin_mysql_ifaces; int cluster_check_interval_ms; int cluster_check_status_frequency; int cluster_mysql_query_rules_diffs_before_sync; @@ -417,6 +450,7 @@ class ProxySQL_Cluster { int cluster_mysql_variables_diffs_before_sync; int cluster_ldap_variables_diffs_before_sync; int cluster_admin_variables_diffs_before_sync; + int cluster_mysql_servers_sync_algorithm; bool cluster_mysql_query_rules_save_to_disk; bool cluster_mysql_servers_save_to_disk; bool cluster_mysql_users_save_to_disk; @@ -428,7 +462,7 @@ class ProxySQL_Cluster { ~ProxySQL_Cluster(); void init() {}; void print_version(); - void load_servers_list(SQLite3_result *r, bool _lock = true) { + void load_servers_list(SQLite3_result* r, bool _lock = true) { nodes.load_servers_list(r, _lock); } void update_table_proxysql_servers_for_monitor(SQLite3_result* resultset) { @@ -440,17 +474,17 @@ class ProxySQL_Cluster { MySQL_Monitor::trigger_dns_cache_update(); } - void get_credentials(char **, char **); - void set_username(char *); - void set_password(char *); - void set_admin_mysql_ifaces(char *); - bool Update_Node_Metrics(char * _h, uint16_t _p, MYSQL_RES *_r, unsigned long long _response_time) { + void get_credentials(char**, char**); + void set_username(char*); + void set_password(char*); + void set_admin_mysql_ifaces(char*); + bool Update_Node_Metrics(char* _h, uint16_t _p, MYSQL_RES* _r, unsigned long long _response_time) { return nodes.Update_Node_Metrics(_h, _p, _r, _response_time); } - bool Update_Global_Checksum(char * _h, uint16_t _p, MYSQL_RES *_r) { + bool Update_Global_Checksum(char* _h, uint16_t _p, MYSQL_RES* _r) { return nodes.Update_Global_Checksum(_h, _p, _r); } - bool Update_Node_Checksums(char * _h, uint16_t _p, MYSQL_RES *_r = NULL) { + bool Update_Node_Checksums(char* _h, uint16_t _p, MYSQL_RES* _r = NULL) { return nodes.Update_Node_Checksums(_h, _p, _r); } void Reset_Global_Checksums(bool lock) { @@ -459,17 +493,19 @@ class ProxySQL_Cluster { SQLite3_result *dump_table_proxysql_servers() { return nodes.dump_table_proxysql_servers(); } - SQLite3_result * get_stats_proxysql_servers_checksums() { + SQLite3_result* get_stats_proxysql_servers_checksums() { return nodes.stats_proxysql_servers_checksums(); } - SQLite3_result * get_stats_proxysql_servers_metrics() { + SQLite3_result* get_stats_proxysql_servers_metrics() { return nodes.stats_proxysql_servers_metrics(); } void p_update_metrics(); void thread_ending(pthread_t); void join_term_thread(); void pull_mysql_query_rules_from_peer(const std::string& expected_checksum, const time_t epoch); - void pull_mysql_servers_from_peer(const std::string& expected_checksum, const time_t epoch); + void pull_runtime_mysql_servers_from_peer(const runtime_mysql_servers_checksum_t& peer_runtime_mysql_server); + void pull_mysql_servers_v2_from_peer(const mysql_servers_v2_checksum_t& peer_mysql_server_v2, + const runtime_mysql_servers_checksum_t& peer_runtime_mysql_server = {}, bool fetch_runtime_mysql_servers = false); void pull_mysql_users_from_peer(const std::string& expected_checksum, const time_t epoch); /** * @brief Pulls from peer the specified global variables by the type parameter. diff --git a/include/SQLite3_Server.h b/include/SQLite3_Server.h index a7a5dbf620..6b0526983e 100644 --- a/include/SQLite3_Server.h +++ b/include/SQLite3_Server.h @@ -5,6 +5,7 @@ #include "proxysql.h" #include "cpp.h" #include +#include class SQLite3_Session { public: @@ -14,7 +15,7 @@ class SQLite3_Session { }; #ifdef TEST_GROUPREP -using group_rep_status = std::tuple; +using group_rep_status = std::tuple; #endif class SQLite3_Server { @@ -70,7 +71,12 @@ class SQLite3_Server { unsigned int num_aurora_servers[3]; unsigned int max_num_aurora_servers; pthread_mutex_t aurora_mutex; - void populate_aws_aurora_table(MySQL_Session *sess); + /** + * @brief Handles queries to table 'REPLICA_HOST_STATUS'. + * @details This function needs to be called with lock on mutex aurora_mutex already acquired. + * @param sess The session which request is to be handled. + */ + void populate_aws_aurora_table(MySQL_Session *sess, uint32_t whg); void init_aurora_ifaces_string(std::string& s); #endif // TEST_AURORA #ifdef TEST_GALERA diff --git a/include/proxysql_admin.h b/include/proxysql_admin.h index 8d6629fc66..fda17886f1 100644 --- a/include/proxysql_admin.h +++ b/include/proxysql_admin.h @@ -1,9 +1,9 @@ #ifndef __CLASS_PROXYSQL_ADMIN_H #define __CLASS_PROXYSQL_ADMIN_H -#include -#include -#include +#include "prometheus/exposer.h" +#include "prometheus/counter.h" +#include "prometheus/gauge.h" #include "query_processor.h" #include "proxy_defines.h" @@ -116,10 +116,27 @@ struct admin_metrics_map_idx { }; }; +/** + * @brief Holds the retrieved info from the bootstrapping server. + * @details Used during ProxySQL_Admin initialization. + */ +struct bootstrap_info_t { + uint32_t server_language; + std::string server_version; + MYSQL_RES* servers; + MYSQL_RES* users; + std::string mon_user; + std::string mon_pass; + bool rand_gen_user; + + ~bootstrap_info_t(); +}; + // ProxySQL_Admin shared variables extern int admin__web_verbosity; struct incoming_servers_t { + SQLite3_result* incoming_mysql_servers_v2 = NULL; SQLite3_result* runtime_mysql_servers = NULL; SQLite3_result* incoming_replication_hostgroups = NULL; SQLite3_result* incoming_group_replication_hostgroups = NULL; @@ -128,7 +145,41 @@ struct incoming_servers_t { SQLite3_result* incoming_hostgroup_attributes = NULL; incoming_servers_t(); - incoming_servers_t(SQLite3_result*, SQLite3_result*, SQLite3_result*, SQLite3_result*, SQLite3_result*, SQLite3_result*); + incoming_servers_t(SQLite3_result*, SQLite3_result*, SQLite3_result*, SQLite3_result*, SQLite3_result*, SQLite3_result*, SQLite3_result*); +}; + +// Separate structs for runtime mysql server and mysql server v2 to avoid human error +struct runtime_mysql_servers_checksum_t { + std::string value; + time_t epoch; + + runtime_mysql_servers_checksum_t(); + runtime_mysql_servers_checksum_t(const std::string& value, time_t epoch); +}; + +struct mysql_servers_v2_checksum_t { + std::string value; + time_t epoch; + + mysql_servers_v2_checksum_t(); + mysql_servers_v2_checksum_t(const std::string& value, time_t epoch); +}; +// + +struct peer_runtime_mysql_servers_t { + SQLite3_result* resultset { nullptr }; + runtime_mysql_servers_checksum_t checksum {}; + + peer_runtime_mysql_servers_t(); + peer_runtime_mysql_servers_t(SQLite3_result*, const runtime_mysql_servers_checksum_t&); +}; + +struct peer_mysql_servers_v2_t { + SQLite3_result* resultset { nullptr }; + mysql_servers_v2_checksum_t checksum {}; + + peer_mysql_servers_v2_t(); + peer_mysql_servers_v2_t(SQLite3_result*, const mysql_servers_v2_checksum_t&); }; class ProxySQL_Admin { @@ -188,6 +239,7 @@ class ProxySQL_Admin { int cluster_mysql_variables_diffs_before_sync; int cluster_admin_variables_diffs_before_sync; int cluster_ldap_variables_diffs_before_sync; + int cluster_mysql_servers_sync_algorithm; bool cluster_mysql_query_rules_save_to_disk; bool cluster_mysql_servers_save_to_disk; bool cluster_mysql_users_save_to_disk; @@ -353,7 +405,14 @@ class ProxySQL_Admin { #endif int pipefd[2]; void print_version(); - bool init(); + /** + * @brief Initializes the module. + * @details Bootstrap info is only used for 'bootstrap mode', i.e. if 'GloVars.global.gr_bootstrap_mode' + * is detected to be 'true'. + * @param bootstrap_info Info used to create default config during initialization in bootstrap mode. + * @return Always true. + */ + bool init(const bootstrap_info_t& bootstrap_info); void init_ldap(); bool get_read_only() { return variables.admin_read_only; } bool set_read_only(bool ro) { variables.admin_read_only=ro; return variables.admin_read_only; } @@ -412,7 +471,8 @@ class ProxySQL_Admin { // void flush_admin_variables__from_disk_to_memory(); // commented in 2.3 because unused void flush_admin_variables__from_memory_to_disk(); void flush_ldap_variables__from_memory_to_disk(); - void load_mysql_servers_to_runtime(const incoming_servers_t& incoming_servers = {}, const std::string& checksum = "", const time_t epoch = 0); + void load_mysql_servers_to_runtime(const incoming_servers_t& incoming_servers = {}, const runtime_mysql_servers_checksum_t& peer_runtime_mysql_server = {}, + const mysql_servers_v2_checksum_t& peer_mysql_server_v2 = {}); void save_mysql_servers_from_runtime(); /** * @brief Performs the load to runtime of the current configuration in 'main' for 'mysql_query_rules' and @@ -546,6 +606,8 @@ class ProxySQL_Admin { #ifdef TEST_AURORA void enable_aurora_testing(); + void enable_aurora_testing_populate_mysql_servers(); + void enable_aurora_testing_populate_mysql_aurora_hostgroups(); #endif // TEST_AURORA #ifdef TEST_GALERA diff --git a/include/proxysql_glovars.hpp b/include/proxysql_glovars.hpp index fe23e756fd..b2b2066fc3 100644 --- a/include/proxysql_glovars.hpp +++ b/include/proxysql_glovars.hpp @@ -5,27 +5,17 @@ #define CLUSTER_SYNC_INTERFACES_MYSQL "('mysql-interfaces')" #include -#include +#include +#include "prometheus/registry.h" #include "configfile.hpp" #include "proxy_defines.h" +#include "proxysql_utils.h" namespace ez { class ezOptionParser; }; -/** - * @brief Helper function used to replace spaces and zeros by '0' char in the supplied checksum buffer. - * @param checksum Input buffer containing the checksum. - */ -inline void replace_checksum_zeros(char* checksum) { - for (int i=2; i<18; i++) { - if (checksum[i]==' ' || checksum[i]==0) { - checksum[i]='0'; - } - } -} - #ifndef ProxySQL_Checksum_Value_LENGTH #define ProxySQL_Checksum_Value_LENGTH 20 #endif @@ -126,6 +116,24 @@ class ProxySQL_GlobalVariables { #ifdef PROXYSQLCLICKHOUSE bool clickhouse_server; #endif /* PROXYSQLCLICKHOUSE */ + int gr_bootstrap_mode; + char* gr_bootstrap_uri; + char* gr_bootstrap_account; + char* gr_bootstrap_account_create; + char* gr_bootstrap_account_host; + uint64_t gr_bootstrap_password_retries; + char* gr_bootstrap_conf_bind_address; + uint64_t gr_bootstrap_conf_base_port; + bool gr_bootstrap_conf_use_sockets; + bool gr_bootstrap_conf_skip_tcp; + char* gr_bootstrap_ssl_ca; + char* gr_bootstrap_ssl_capath; + char* gr_bootstrap_ssl_cert; + char* gr_bootstrap_ssl_cipher; + char* gr_bootstrap_ssl_crl; + char* gr_bootstrap_ssl_crlpath; + char* gr_bootstrap_ssl_key; + char* gr_bootstrap_ssl_mode; pthread_mutex_t ext_glomth_mutex; bool ssl_keylog_enabled; @@ -149,6 +157,7 @@ class ProxySQL_GlobalVariables { ProxySQL_Checksum_Value mysql_variables; ProxySQL_Checksum_Value ldap_variables; ProxySQL_Checksum_Value proxysql_servers; + ProxySQL_Checksum_Value mysql_servers_v2; uint64_t global_checksum; unsigned long long updates_cnt; unsigned long long dumped_at; diff --git a/include/proxysql_structs.h b/include/proxysql_structs.h index 003d36a4a2..5b8270d7e8 100644 --- a/include/proxysql_structs.h +++ b/include/proxysql_structs.h @@ -140,6 +140,7 @@ enum debug_module { PROXY_DEBUG_QUERY_STATISTICS, PROXY_DEBUG_RESTAPI, PROXY_DEBUG_MONITOR, + PROXY_DEBUG_CLUSTER, PROXY_DEBUG_UNKNOWN // this module doesn't exist. It is used only to define the last possible module }; diff --git a/include/proxysql_utils.h b/include/proxysql_utils.h index ffe3fe1bba..2e127e57c4 100644 --- a/include/proxysql_utils.h +++ b/include/proxysql_utils.h @@ -2,6 +2,7 @@ #define __PROXYSQL_UTILS_H #include +#include #include #include #include @@ -11,6 +12,12 @@ #include #include +#include "sqlite3db.h" + +#ifndef ProxySQL_Checksum_Value_LENGTH +#define ProxySQL_Checksum_Value_LENGTH 20 +#endif + #ifndef ETIME // ETIME is not defined on FreeBSD // ETIME is used internaly to report API timer expired @@ -206,9 +213,42 @@ uint64_t get_timestamp_us(); */ std::string replace_str(const std::string& str, const std::string& match, const std::string& repl); +/** + * @brief Split a string into a vector of strings with the provided 'char' delimiter. + * @param s String to be split. + * @param delimiter Delimiter to be used. + * @return Vector with the string splits. Empty if none is found. + */ +std::vector split_str(const std::string& s, char delimiter); std::string generate_multi_rows_query(int rows, int params); +/** + * @brief Generates a random string of the length of the provider 'strSize' parameter. + * @param strSize The size of the string to be generated. + * @return A random string. + */ +std::string rand_str(std::size_t strSize); + +/** + * @brief Helper function used to replace spaces and zeros by '0' char in the supplied checksum buffer. + * @param checksum Input buffer containing the checksum. + */ +inline void replace_checksum_zeros(char* checksum) { + for (int i=2; i<18; i++) { + if (checksum[i]==' ' || checksum[i]==0) { + checksum[i]='0'; + } + } +} + +/** + * @brief Generates a ProxySQL checksum as a string from the supplied integer hash. + * @param hash The integer hash to be formated as a string. + * @return String representation of the supplied hash. + */ +std::string get_checksum_from_hash(uint64_t hash); + void close_all_non_term_fd(std::vector excludeFDs); /** diff --git a/include/sqlite3db.h b/include/sqlite3db.h index b4d3c52f3b..977a8cc296 100644 --- a/include/sqlite3db.h +++ b/include/sqlite3db.h @@ -1,9 +1,11 @@ #ifndef __CLASS_SQLITE3DB_H #define __CLASS_SQLITE3DB_H +#include #include "sqlite3.h" #undef swap #undef min #undef max +#include #include #define PROXYSQL_SQLITE3DB_PTHREAD_MUTEX diff --git a/lib/Makefile b/lib/Makefile index de61064fa8..c752ff254c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,154 +1,131 @@ #!/bin/make -f -ifndef GIT_VERSION - GIT_VERSION := $(shell git describe --long --abbrev=7) +GIT_VERSION ?= $(shell git describe --long --abbrev=7) ifndef GIT_VERSION $(error GIT_VERSION is not set) endif -endif -DEPS_PATH=../deps +DEPS_PATH := ../deps UNAME_S := $(shell uname -s) -MARIADB_PATH=$(DEPS_PATH)/mariadb-client-library/mariadb_client -MARIADB_IDIR=$(MARIADB_PATH)/include -JEMALLOC_PATH=$(DEPS_PATH)/jemalloc/jemalloc -JEMALLOC_IDIR=$(JEMALLOC_PATH)/include/jemalloc +MARIADB_PATH := $(DEPS_PATH)/mariadb-client-library/mariadb_client +MARIADB_IDIR := $(MARIADB_PATH)/include + +JEMALLOC_PATH := $(DEPS_PATH)/jemalloc/jemalloc +JEMALLOC_IDIR := $(JEMALLOC_PATH)/include/jemalloc -LIBCONFIG_PATH=$(DEPS_PATH)/libconfig/libconfig -LIBCONFIG_IDIR=-I$(LIBCONFIG_PATH)/lib +LIBCONFIG_PATH := $(DEPS_PATH)/libconfig/libconfig +LIBCONFIG_IDIR := -I$(LIBCONFIG_PATH)/lib -#INJECTION_PATH=$(DEPS_PATH)/libinjection -#INJECTION_IDIR=$(INJECTION_PATH) +#INJECTION_PATH := $(DEPS_PATH)/libinjection +#INJECTION_IDIR := $(INJECTION_PATH) -RE2_PATH=$(DEPS_PATH)/re2/re2 -RE2_IDIR=$(RE2_PATH) +RE2_PATH := $(DEPS_PATH)/re2/re2 +RE2_IDIR := $(RE2_PATH) -PCRE_PATH=$(DEPS_PATH)/pcre/pcre +PCRE_PATH := $(DEPS_PATH)/pcre/pcre -SQLITE3_DIR=$(DEPS_PATH)/sqlite3/sqlite3 +SQLITE3_DIR := $(DEPS_PATH)/sqlite3/sqlite3 -CLICKHOUSE_CPP_DIR=$(DEPS_PATH)/clickhouse-cpp/clickhouse-cpp +CLICKHOUSE_CPP_DIR := $(DEPS_PATH)/clickhouse-cpp/clickhouse-cpp -LIBINJECTION_DIR=$(DEPS_PATH)/libinjection/libinjection -LIBINJECTION_IDIR=-I$(LIBINJECTION_DIR)/src +LIBINJECTION_DIR := $(DEPS_PATH)/libinjection/libinjection +LIBINJECTION_IDIR := -I$(LIBINJECTION_DIR)/src -LIBHTTPSERVER_DIR=$(DEPS_PATH)/libhttpserver/libhttpserver -LIBHTTPSERVER_IDIR=-I$(LIBHTTPSERVER_DIR)/src +LIBHTTPSERVER_DIR := $(DEPS_PATH)/libhttpserver/libhttpserver +LIBHTTPSERVER_IDIR := -I$(LIBHTTPSERVER_DIR)/src -MICROHTTPD_DIR=$(DEPS_PATH)/libmicrohttpd/libmicrohttpd -MICROHTTPD_IDIR=-I$(MICROHTTPD_DIR) -I$(MICROHTTPD_DIR)/src/include +MICROHTTPD_DIR := $(DEPS_PATH)/libmicrohttpd/libmicrohttpd +MICROHTTPD_IDIR := -I$(MICROHTTPD_DIR) -I$(MICROHTTPD_DIR)/src/include -COREDUMPER_DIR=$(DEPS_PATH)/coredumper/coredumper -COREDUMPER_IDIR=$(COREDUMPER_DIR)/include +COREDUMPER_DIR := $(DEPS_PATH)/coredumper/coredumper +COREDUMPER_IDIR := $(COREDUMPER_DIR)/include -CURL_DIR=$(DEPS_PATH)/curl/curl -CURL_IDIR=$(CURL_DIR)/include +CURL_DIR := $(DEPS_PATH)/curl/curl +CURL_IDIR := $(CURL_DIR)/include -SSL_DIR=$(DEPS_PATH)/libssl/openssl/ -SSL_IDIR=$(SSL_DIR)/include +SSL_DIR := $(DEPS_PATH)/libssl/openssl/ +SSL_IDIR := $(SSL_DIR)/include +EV_DIR := $(DEPS_PATH)/libev/libev/ +EV_IDIR := $(EV_DIR) -EV_DIR=$(DEPS_PATH)/libev/libev/ -EV_IDIR=$(EV_DIR) +PROMETHEUS_PATH := $(DEPS_PATH)/prometheus-cpp/prometheus-cpp +PROMETHEUS_IDIR := $(PROMETHEUS_PATH)/pull/include -I$(PROMETHEUS_PATH)/core/include +PROMETHEUS_LDIR := $(PROMETHEUS_PATH)/lib -PROMETHEUS_PATH=$(DEPS_PATH)/prometheus-cpp/prometheus-cpp -PROMETHEUS_IDIR=$(PROMETHEUS_PATH)/pull/include -I$(PROMETHEUS_PATH)/core/include -PROMETHEUS_LDIR=$(PROMETHEUS_PATH)/lib -IDIR=../include +IDIR := ../include -IDIRS=-I$(IDIR) -I$(JEMALLOC_IDIR) -I$(MARIADB_IDIR) $(LIBCONFIG_IDIR) -I$(RE2_IDIR) -I$(SQLITE3_DIR) -I$(PCRE_PATH) -I/usr/local/include -I$(CLICKHOUSE_CPP_DIR) -I$(CLICKHOUSE_CPP_DIR)/contrib/ $(MICROHTTPD_IDIR) $(LIBHTTPSERVER_IDIR) $(LIBINJECTION_IDIR) -I$(CURL_IDIR) -I$(EV_DIR) -I$(SSL_IDIR) -I$(PROMETHEUS_IDIR) +IDIRS := -I$(IDIR) -I$(JEMALLOC_IDIR) -I$(MARIADB_IDIR) $(LIBCONFIG_IDIR) -I$(RE2_IDIR) -I$(SQLITE3_DIR) -I$(PCRE_PATH) -I/usr/local/include -I$(CLICKHOUSE_CPP_DIR) -I$(CLICKHOUSE_CPP_DIR)/contrib/ $(MICROHTTPD_IDIR) $(LIBHTTPSERVER_IDIR) $(LIBINJECTION_IDIR) -I$(CURL_IDIR) -I$(EV_DIR) -I$(SSL_IDIR) -I$(PROMETHEUS_IDIR) ifeq ($(UNAME_S),Linux) - IDIRS+= -I$(COREDUMPER_IDIR) + IDIRS += -I$(COREDUMPER_IDIR) endif -LDIRS=-L$(JEMALLOC_PATH)/lib -L$(RE2_PATH)/obj -L$(INJECTION_PATH) -L$(PROMETHEUS_LDIR) +LDIRS := -L$(JEMALLOC_PATH)/lib -L$(RE2_PATH)/obj -L$(INJECTION_PATH) -L$(PROMETHEUS_LDIR) -#LIBS=-rdynamic -Wl,-Bstatic -ljemalloc_pic -lre2 -Wl,-Bdynamic -ldl -lpthread -lm -lz -lrt $(EXTRALINK) -#LIBS=-Wl,--export-dynamic -Wl,-Bstatic -ljemalloc_pic -lre2 -Wl,-Bdynamic -ldl -lpthread -lm -lz -lrt $(EXTRALINK) +#LIBS := -rdynamic -Wl,-Bstatic -ljemalloc_pic -lre2 -Wl,-Bdynamic -ldl -lpthread -lm -lz -lrt $(EXTRALINK) +#LIBS := -Wl,--export-dynamic -Wl,-Bstatic -ljemalloc_pic -lre2 -Wl,-Bdynamic -ldl -lpthread -lm -lz -lrt $(EXTRALINK) -ODIR= obj +ODIR := obj -#CC=gcc -#CXX=g++ -#CC=clang -WITHASANVAR := $(shell echo $(WITHASAN)) -ifeq ($(WITHASANVAR),1) -WASAN=-fsanitize=address -# Force the disable of JEMALLOC, since ASAN isn't compatible. -export NOJEMALLOC = 1 -else -WASAN= +WASAN := +ifeq ($(WITHASAN),1) + WASAN := -fsanitize=address + # Force the disable of JEMALLOC, since ASAN isn't compatible. + export NOJEMALLOC=1 endif -#CFLAGS=$(IDIRS) $(OPTZ) $(DEBUG) -Wall #-lcrypto -#CXXFLAGS=-std=c++11 $(CFLAGS) $(LDIRS) $(LIBS) -NOJEMALLOC := $(shell echo $(NOJEMALLOC)) +NOJEM := ifeq ($(NOJEMALLOC),1) -NOJEM=-DNOJEM -else -NOJEM= + NOJEM := -DNOJEM endif -WITHGCOVVAR := $(shell echo $(WITHGCOV)) -ifeq ($(WITHGCOVVAR),1) -WGCOV=-DWITHGCOV --coverage -lgcov -else -WGCOV= +WGCOV := +ifeq ($(WITHGCOV),1) + WGCOV := -DWITHGCOV --coverage -lgcov endif -# determine good compiler version for stdc++17 -IS_CXX17 := 0 -ifeq ($(CXX),clang++) - CLANG_VERSION := $(shell clang -dumpversion | tr '.' ' ' | awk '{ printf("%04d.%04d.%04d", $$1, $$2, $$3) }') - CLANG_MIN_VER := $(shell echo 14.0 | tr '.' ' ' | awk '{ printf("%04d.%04d.%04d", $$1, $$2, $$3) }') -ifeq ($(CLANG_MIN_VER),$(firstword $(sort $(CLANG_VERSION) $(CLANG_MIN_VER)))) - IS_CXX17 := 1 -endif -else - GCC_VERSION := $(shell gcc -dumpfullversion 2>/dev/null || gcc -dumpversion | tr '.' ' ' | awk '{ printf("%04d.%04d.%04d", $$1, $$2, $$3) }') - GCC_MIN_VER := $(shell echo 8.2.0 | tr '.' ' ' | awk '{ printf("%04d.%04d.%04d", $$1, $$2, $$3) }') -ifeq ($(GCC_MIN_VER),$(firstword $(sort $(GCC_VERSION) $(GCC_MIN_VER)))) - IS_CXX17 := 1 +### detect compiler support for c++11/17 +CPLUSPLUS := $(shell ${CC} -std=c++17 -dM -E -x c++ /dev/null 2>/dev/null | grep -F __cplusplus | grep -Po '\d\d\d\d\d\dL') +ifneq ($(CPLUSPLUS),201703L) + CPLUSPLUS := $(shell ${CC} -std=c++11 -dM -E -x c++ /dev/null 2>/dev/null| grep -F __cplusplus | grep -Po '\d\d\d\d\d\dL') +ifneq ($(CPLUSPLUS),201103L) +$(error Compiler must support at least c++11) endif endif +STDCPP := -std=c++$(shell echo $(CPLUSPLUS) | cut -c3-4) -DCXX$(shell echo $(CPLUSPLUS) | cut -c3-4) -PROXYSQLCLICKHOUSE := $(shell echo $(PROXYSQLCLICKHOUSE)) -PSQLCH= +PSQLCH := ifeq ($(PROXYSQLCLICKHOUSE),1) - PSQLCH=-DPROXYSQLCLICKHOUSE -ifeq ($(IS_CXX17),1) - PSQLCH=-DPROXYSQLCLICKHOUSE -std=c++17 -DCXX17 -endif + PSQLCH := -DPROXYSQLCLICKHOUSE endif # 'libhttpserver': Add 'ENABLE_EPOLL' by default for all platforms except # for 'Darwin'. This is required when compiling 'libhttpserver' for avoiding # internal use of 'SELECT' in favor of 'EPOLL'. See #3591. +ENABLE_EPOLL := -DENABLE_EPOLL ifeq ($(UNAME_S),Darwin) -ENABLE_EPOLL= -else -ENABLE_EPOLL=-DENABLE_EPOLL + ENABLE_EPOLL := endif -MYCFLAGS=$(IDIRS) $(OPTZ) $(DEBUG) -Wall -DGITVERSION=\"$(GIT_VERSION)\" $(NOJEM) $(WGCOV) $(WASAN) -MYCXXFLAGS=-std=c++11 $(MYCFLAGS) $(PSQLCH) $(ENABLE_EPOLL) +MYCFLAGS := $(IDIRS) $(OPTZ) $(DEBUG) -Wall -DGITVERSION=\"$(GIT_VERSION)\" $(NOJEM) $(WGCOV) $(WASAN) +MYCXXFLAGS := $(STDCPP) $(MYCFLAGS) $(PSQLCH) $(ENABLE_EPOLL) default: libproxysql.a .PHONY: default -_OBJ_CXX = ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo SpookyV2.oo MySQL_Authentication.oo gen_utils.oo sqlite3db.oo mysql_connection.oo MySQL_HostGroups_Manager.oo mysql_data_stream.oo MySQL_Thread.oo MySQL_Session.oo MySQL_Protocol.oo mysql_backend.oo Query_Processor.oo ProxySQL_Admin.oo ProxySQL_Config.oo ProxySQL_Restapi.oo MySQL_Monitor.oo MySQL_Logger.oo thread.oo MySQL_PreparedStatement.oo ProxySQL_Cluster.oo ClickHouse_Authentication.oo ClickHouse_Server.oo ProxySQL_Statistics.oo Chart_bundle_js.oo ProxySQL_HTTP_Server.oo ProxySQL_RESTAPI_Server.oo font-awesome.min.css.oo main-bundle.min.css.oo set_parser.oo MySQL_Variables.oo c_tokenizer.oo proxysql_utils.oo proxysql_coredump.oo proxysql_sslkeylog.oo \ +_OBJ_CXX := ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo SpookyV2.oo MySQL_Authentication.oo gen_utils.oo sqlite3db.oo mysql_connection.oo MySQL_HostGroups_Manager.oo mysql_data_stream.oo MySQL_Thread.oo MySQL_Session.oo MySQL_Protocol.oo mysql_backend.oo Query_Processor.oo ProxySQL_Admin.oo ProxySQL_Config.oo ProxySQL_Restapi.oo MySQL_Monitor.oo MySQL_Logger.oo thread.oo MySQL_PreparedStatement.oo ProxySQL_Cluster.oo ClickHouse_Authentication.oo ClickHouse_Server.oo ProxySQL_Statistics.oo Chart_bundle_js.oo ProxySQL_HTTP_Server.oo ProxySQL_RESTAPI_Server.oo font-awesome.min.css.oo main-bundle.min.css.oo set_parser.oo MySQL_Variables.oo c_tokenizer.oo proxysql_utils.oo proxysql_coredump.oo proxysql_sslkeylog.oo \ sha256crypt.oo \ proxysql_find_charset.oo ProxySQL_Poll.oo -OBJ_CXX = $(patsubst %,$(ODIR)/%,$(_OBJ_CXX)) -HEADERS = ../include/*.h ../include/*.hpp +OBJ_CXX := $(patsubst %,$(ODIR)/%,$(_OBJ_CXX)) +HEADERS := ../include/*.h ../include/*.hpp %.ko: %.cpp $(HEADERS) $(CXX) -fPIC -c -o $@ $< $(MYCXXFLAGS) $(CXXFLAGS) diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index 80f3608271..e957e0f1dd 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -1360,6 +1360,7 @@ MySQL_HostGroups_Manager::MySQL_HostGroups_Manager() { incoming_galera_hostgroups=NULL; incoming_aws_aurora_hostgroups = NULL; incoming_hostgroup_attributes = NULL; + incoming_mysql_servers_v2 = NULL; pthread_rwlock_init(>id_rwlock, NULL); gtid_missing_nodes = false; gtid_ev_loop=NULL; @@ -1590,7 +1591,9 @@ SQLite3_result * MySQL_HostGroups_Manager::execute_query(char *query, char **err return resultset; } -void MySQL_HostGroups_Manager::CUCFT1(SpookyHash& myhash, bool& init, const string& TableName, const string& ColumnName, uint64_t& raw_checksum) { +void MySQL_HostGroups_Manager::CUCFT1( + SpookyHash& myhash, bool& init, const string& TableName, const string& ColumnName, uint64_t& raw_checksum +) { char *error=NULL; int cols=0; int affected_rows=0; @@ -1615,6 +1618,13 @@ void MySQL_HostGroups_Manager::CUCFT1(SpookyHash& myhash, bool& init, const stri } void MySQL_HostGroups_Manager::commit_update_checksums_from_tables(SpookyHash& myhash, bool& init) { + // Always reset the current table values before recomputing + for (size_t i = 0; i < table_resultset_checksum.size(); i++) { + if (i != HGM_TABLES::MYSQL_SERVERS && i != HGM_TABLES::MYSQL_SERVERS_V2) { + table_resultset_checksum[i] = 0; + } + } + CUCFT1(myhash,init,"mysql_replication_hostgroups","writer_hostgroup", table_resultset_checksum[HGM_TABLES::MYSQL_REPLICATION_HOSTGROUPS]); CUCFT1(myhash,init,"mysql_group_replication_hostgroups","writer_hostgroup", table_resultset_checksum[HGM_TABLES::MYSQL_GROUP_REPLICATION_HOSTGROUPS]); CUCFT1(myhash,init,"mysql_galera_hostgroups","writer_hostgroup", table_resultset_checksum[HGM_TABLES::MYSQL_GALERA_HOSTGROUPS]); @@ -1622,9 +1632,273 @@ void MySQL_HostGroups_Manager::commit_update_checksums_from_tables(SpookyHash& m CUCFT1(myhash,init,"mysql_hostgroup_attributes","hostgroup_id", table_resultset_checksum[HGM_TABLES::MYSQL_HOSTGROUP_ATTRIBUTES]); } +/** + * @brief This code updates the 'hostgroup_server_mapping' table with the most recent mysql_servers and mysql_replication_hostgroups + * records while utilizing checksums to prevent unnecessary updates. + * + * IMPORTANT: Make sure wrlock() is called before calling this method. + * +*/ +void MySQL_HostGroups_Manager::update_hostgroup_manager_mappings() { + + if (hgsm_mysql_servers_checksum != table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS] || + hgsm_mysql_replication_hostgroups_checksum != table_resultset_checksum[HGM_TABLES::MYSQL_REPLICATION_HOSTGROUPS]) + { + proxy_info("Rebuilding 'Hostgroup_Manager_Mapping' due to checksums change - mysql_servers { old: 0x%lX, new: 0x%lX }, mysql_replication_hostgroups { old:0x%lX, new:0x%lX }\n", + hgsm_mysql_servers_checksum, table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS], + hgsm_mysql_replication_hostgroups_checksum, table_resultset_checksum[HGM_TABLES::MYSQL_REPLICATION_HOSTGROUPS]); + + char* error = NULL; + int cols = 0; + int affected_rows = 0; + SQLite3_result* resultset = NULL; + + hostgroup_server_mapping.clear(); + + const char* query = "SELECT DISTINCT hostname, port, '1' is_writer, status, reader_hostgroup, writer_hostgroup, mem_pointer FROM mysql_replication_hostgroups JOIN mysql_servers ON hostgroup_id=writer_hostgroup WHERE status<>3 \ + UNION \ + SELECT DISTINCT hostname, port, '0' is_writer, status, reader_hostgroup, writer_hostgroup, mem_pointer FROM mysql_replication_hostgroups JOIN mysql_servers ON hostgroup_id=reader_hostgroup WHERE status<>3 \ + ORDER BY hostname, port"; + + mydb->execute_statement(query, &error, &cols, &affected_rows, &resultset); + + if (resultset && resultset->rows_count) { + std::string fetched_server_id; + HostGroup_Server_Mapping* fetched_server_mapping = NULL; + + for (std::vector::iterator it = resultset->rows.begin(); it != resultset->rows.end(); ++it) { + SQLite3_row* r = *it; + + const std::string& server_id = std::string(r->fields[0]) + ":::" + r->fields[1]; + + if (fetched_server_mapping == NULL || server_id != fetched_server_id) { + + auto itr = hostgroup_server_mapping.find(server_id); + + if (itr == hostgroup_server_mapping.end()) { + std::unique_ptr server_mapping(new HostGroup_Server_Mapping(this)); + fetched_server_mapping = server_mapping.get(); + hostgroup_server_mapping.insert( std::pair> { + server_id, std::move(server_mapping) + } ); + } else { + fetched_server_mapping = itr->second.get(); + } + + fetched_server_id = server_id; + } + + HostGroup_Server_Mapping::Node node; + //node.server_status = static_cast(atoi(r->fields[3])); + node.reader_hostgroup_id = atoi(r->fields[4]); + node.writer_hostgroup_id = atoi(r->fields[5]); + node.srv = reinterpret_cast(atoll(r->fields[6])); + + HostGroup_Server_Mapping::Type type = (r->fields[2] && r->fields[2][0] == '1') ? HostGroup_Server_Mapping::Type::WRITER : HostGroup_Server_Mapping::Type::READER; + fetched_server_mapping->add(type, node); + } + } + delete resultset; + + hgsm_mysql_servers_checksum = table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS]; + hgsm_mysql_replication_hostgroups_checksum = table_resultset_checksum[HGM_TABLES::MYSQL_REPLICATION_HOSTGROUPS]; + } +} + +/** + * @brief Generates a resultset holding the current Admin 'runtime_mysql_servers' as reported by Admin. + * @details Requires caller to hold the mutex 'MySQL_HostGroups_Manager::wrlock'. + * @param mydb The db in which to perform the query, typically 'MySQL_HostGroups_Manager::mydb'. + * @return An SQLite3 resultset for the query 'MYHGM_GEN_ADMIN_RUNTIME_SERVERS'. + */ +unique_ptr get_admin_runtime_mysql_servers(SQLite3DB* mydb) { + char* error = nullptr; + int cols = 0; + int affected_rows = 0; + SQLite3_result* resultset = nullptr; + + mydb->execute_statement(MYHGM_GEN_CLUSTER_ADMIN_RUNTIME_SERVERS, &error, &cols, &affected_rows, &resultset); + + if (error) { + proxy_error("SQLite3 query generating 'runtime_mysql_servers' resultset failed with error '%s'\n", error); + assert(0); + } + + return unique_ptr(resultset); +} + +/** + * @brief Generates a resultset with holding the current 'mysql_servers_v2' table. + * @details Requires caller to hold the mutex 'ProxySQL_Admin::mysql_servers_wrlock'. + * @return A resulset holding 'mysql_servers_v2'. + */ +unique_ptr get_mysql_servers_v2() { + char* error = nullptr; + int cols = 0; + int affected_rows = 0; + SQLite3_result* resultset = nullptr; + + if (GloAdmin && GloAdmin->admindb) { + GloAdmin->admindb->execute_statement( + MYHGM_GEN_CLUSTER_ADMIN_MYSQL_SERVERS, &error, &cols, &affected_rows, &resultset + ); + } + + return unique_ptr(resultset); +} + +void update_glovars_checksum_with_peers( + ProxySQL_Checksum_Value& module_checksum, + const string& new_checksum, + const string& peer_checksum_value, + time_t new_epoch, + time_t peer_checksum_epoch, + bool update_version +) { + module_checksum.set_checksum(const_cast(new_checksum.c_str())); + + if (update_version) + module_checksum.version++; + + bool computed_checksum_matches = + peer_checksum_value != "" && module_checksum.checksum == peer_checksum_value; + + if (peer_checksum_epoch != 0 && computed_checksum_matches) { + module_checksum.epoch = peer_checksum_epoch; + } else { + module_checksum.epoch = new_epoch; + } +} + +/** + * @brief Updates the global 'mysql_servers' module checksum. + * @details If the new computed checksum matches the supplied 'cluster_checksum', the epoch used for the + * checksum is the supplied epoch instead of current time. This way we ensure the preservation of the + * checksum and epoch fetched from the ProxySQL cluster peer node. + * + * IMPORTANT: This function also generates a new 'global_checksum'. This is because everytime + * 'runtime_mysql_servers' change, updating the global checksum is unconditional. + * @param new_checksum The new computed checksum for 'runtime_mysql_servers'. + * @param peer_checksum A checksum fetched from another ProxySQL cluster node, holds the checksum value + * and its epoch. Should be empty if no remote checksum is being considered. + * @param epoch The epoch to be preserved in case the supplied 'peer_checksum' matches the new computed + * checksum. + */ +void update_glovars_mysql_servers_checksum( + const string& new_checksum, + const runtime_mysql_servers_checksum_t& peer_checksum = {}, + bool update_version = false +) { + time_t new_epoch = time(NULL); + + update_glovars_checksum_with_peers( + GloVars.checksums_values.mysql_servers, + new_checksum, + peer_checksum.value, + new_epoch, + peer_checksum.epoch, + update_version + ); + + GloVars.checksums_values.updates_cnt++; + GloVars.generate_global_checksum(); + GloVars.epoch_version = new_epoch; +} + +/** + * @brief Updates the global 'mysql_servers_v2' module checksum. + * @details Unlike 'update_glovars_mysql_servers_checksum' this function doesn't generate a new + * 'global_checksum'. It's caller responsibility to ensure that 'global_checksum' is updated. + * @param new_checksum The new computed checksum for 'mysql_servers_v2'. + * @param peer_checksum A checksum fetched from another ProxySQL cluster node, holds the checksum value + * and its epoch. Should be empty if no remote checksum is being considered. + * @param epoch The epoch to be preserved in case the supplied 'peer_checksum' matches the new computed + * checksum. + */ +void update_glovars_mysql_servers_v2_checksum( + const string& new_checksum, + const mysql_servers_v2_checksum_t& peer_checksum = {}, + bool update_version = false +) { + time_t new_epoch = time(NULL); + + update_glovars_checksum_with_peers( + GloVars.checksums_values.mysql_servers_v2, + new_checksum, + peer_checksum.value, + new_epoch, + peer_checksum.epoch, + update_version + ); +} + +uint64_t MySQL_HostGroups_Manager::commit_update_checksum_from_mysql_servers(SQLite3_result* runtime_mysql_servers) { + mydb->execute("DELETE FROM mysql_servers"); + generate_mysql_servers_table(); + + if (runtime_mysql_servers == nullptr) { + unique_ptr resultset { get_admin_runtime_mysql_servers(mydb) }; + save_runtime_mysql_servers(resultset.release()); + } else { + save_runtime_mysql_servers(runtime_mysql_servers); + } + + uint64_t raw_checksum = this->runtime_mysql_servers ? this->runtime_mysql_servers->raw_checksum() : 0; + table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS] = raw_checksum; + + return raw_checksum; +} + +uint64_t MySQL_HostGroups_Manager::commit_update_checksum_from_mysql_servers_v2(SQLite3_result* mysql_servers_v2) { + if (mysql_servers_v2 == nullptr) { + unique_ptr resultset { get_mysql_servers_v2() }; + save_mysql_servers_v2(resultset.release()); + } else { + save_mysql_servers_v2(mysql_servers_v2); + } + + uint64_t raw_checksum = this->incoming_mysql_servers_v2 ? this->incoming_mysql_servers_v2->raw_checksum() : 0; + table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS_V2] = raw_checksum; + + return raw_checksum; +} + +std::string MySQL_HostGroups_Manager::gen_global_mysql_servers_v2_checksum(uint64_t servers_v2_hash) { + bool init = false; + SpookyHash global_hash {}; + + if (servers_v2_hash != 0) { + if (init == false) { + init = true; + global_hash.Init(19, 3); + } + + global_hash.Update(&servers_v2_hash, sizeof(servers_v2_hash)); + } + + commit_update_checksums_from_tables(global_hash, init); + + uint64_t hash_1 = 0, hash_2 = 0; + if (init) { + global_hash.Final(&hash_1,&hash_2); + } + + string mysrvs_checksum { get_checksum_from_hash(hash_1) }; + return mysrvs_checksum; +} + bool MySQL_HostGroups_Manager::commit( - SQLite3_result* runtime_mysql_servers, const std::string& checksum, const time_t epoch + const peer_runtime_mysql_servers_t& peer_runtime_mysql_servers, + const peer_mysql_servers_v2_t& peer_mysql_servers_v2, + bool only_commit_runtime_mysql_servers, + bool update_version ) { + // if only_commit_runtime_mysql_servers is true, mysql_servers_v2 resultset will not be entertained and will cause memory leak. + if (only_commit_runtime_mysql_servers) { + proxy_info("Generating runtime mysql servers records only.\n"); + } else { + proxy_info("Generating runtime mysql servers and mysql servers v2 records.\n"); + } unsigned long long curtime1=monotonic_time(); wrlock(); @@ -1653,9 +1927,9 @@ bool MySQL_HostGroups_Manager::commit( } if (resultset) { delete resultset; resultset=NULL; } } - char *query=NULL; + char *query=NULL; query=(char *)"SELECT mem_pointer, t1.hostgroup_id, t1.hostname, t1.port FROM mysql_servers t1 LEFT OUTER JOIN mysql_servers_incoming t2 ON (t1.hostgroup_id=t2.hostgroup_id AND t1.hostname=t2.hostname AND t1.port=t2.port) WHERE t2.hostgroup_id IS NULL"; - mydb->execute_statement(query, &error , &cols , &affected_rows , &resultset); + mydb->execute_statement(query, &error , &cols , &affected_rows , &resultset); if (error) { proxy_error("Error on %s : %s\n", query, error); } else { @@ -1683,15 +1957,12 @@ bool MySQL_HostGroups_Manager::commit( //mydb->execute("DELETE FROM mysql_servers"); //generate_mysql_servers_table(); -// INSERT OR IGNORE INTO mysql_servers SELECT ... FROM mysql_servers_incoming -// proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "INSERT OR IGNORE INTO mysql_servers(hostgroup_id, hostname, port, weight, status, compression, max_connections) SELECT hostgroup_id, hostname, port, weight, status, compression, max_connections FROM mysql_servers_incoming\n"); mydb->execute("INSERT OR IGNORE INTO mysql_servers(hostgroup_id, hostname, port, gtid_port, weight, status, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment) SELECT hostgroup_id, hostname, port, gtid_port, weight, status, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment FROM mysql_servers_incoming"); - // SELECT FROM mysql_servers whatever is not identical in mysql_servers_incoming, or where mem_pointer=0 (where there is no pointer yet) query=(char *)"SELECT t1.*, t2.gtid_port, t2.weight, t2.status, t2.compression, t2.max_connections, t2.max_replication_lag, t2.use_ssl, t2.max_latency_ms, t2.comment FROM mysql_servers t1 JOIN mysql_servers_incoming t2 ON (t1.hostgroup_id=t2.hostgroup_id AND t1.hostname=t2.hostname AND t1.port=t2.port) WHERE mem_pointer=0 OR t1.gtid_port<>t2.gtid_port OR t1.weight<>t2.weight OR t1.status<>t2.status OR t1.compression<>t2.compression OR t1.max_connections<>t2.max_connections OR t1.max_replication_lag<>t2.max_replication_lag OR t1.use_ssl<>t2.use_ssl OR t1.max_latency_ms<>t2.max_latency_ms or t1.comment<>t2.comment"; proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "%s\n", query); - mydb->execute_statement(query, &error , &cols , &affected_rows , &resultset); + mydb->execute_statement(query, &error , &cols , &affected_rows , &resultset); if (error) { proxy_error("Error on %s : %s\n", query, error); } else { @@ -1837,198 +2108,70 @@ bool MySQL_HostGroups_Manager::commit( proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "DELETE FROM mysql_servers_incoming\n"); mydb->execute("DELETE FROM mysql_servers_incoming"); - // replication - if (incoming_replication_hostgroups) { // this IF is extremely important, otherwise replication hostgroups may disappear - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "DELETE FROM mysql_replication_hostgroups\n"); - mydb->execute("DELETE FROM mysql_replication_hostgroups"); - generate_mysql_replication_hostgroups_table(); - } - - // group replication - if (incoming_group_replication_hostgroups) { - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "DELETE FROM mysql_group_replication_hostgroups\n"); - mydb->execute("DELETE FROM mysql_group_replication_hostgroups"); - generate_mysql_group_replication_hostgroups_table(); - } - - // galera - if (incoming_galera_hostgroups) { - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "DELETE FROM mysql_galera_hostgroups\n"); - mydb->execute("DELETE FROM mysql_galera_hostgroups"); - generate_mysql_galera_hostgroups_table(); - } - - // AWS Aurora - if (incoming_aws_aurora_hostgroups) { - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "DELETE FROM mysql_aws_aurora_hostgroups\n"); - mydb->execute("DELETE FROM mysql_aws_aurora_hostgroups"); - generate_mysql_aws_aurora_hostgroups_table(); - } - - // hostgroup attributes - if (incoming_hostgroup_attributes) { - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "DELETE FROM mysql_hostgroup_attributes\n"); - mydb->execute("DELETE FROM mysql_hostgroup_attributes"); - generate_mysql_hostgroup_attributes_table(); - } + string global_checksum_v2 {}; + if (only_commit_runtime_mysql_servers == false) { + // replication + if (incoming_replication_hostgroups) { // this IF is extremely important, otherwise replication hostgroups may disappear + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "DELETE FROM mysql_replication_hostgroups\n"); + mydb->execute("DELETE FROM mysql_replication_hostgroups"); + generate_mysql_replication_hostgroups_table(); + } - // Checksums are always generated - 'admin-checksum_*' deprecated - { - uint64_t hash1 = 0, hash2 = 0; - SpookyHash myhash; - char buf[ProxySQL_Checksum_Value_LENGTH]; - bool init = false; - { - mydb->execute("DELETE FROM mysql_servers"); - generate_mysql_servers_table(); - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - char *query=(char *)"SELECT hostgroup_id, hostname, port, gtid_port, CASE status WHEN 0 OR 1 OR 4 THEN 0 ELSE status END status, weight, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment FROM mysql_servers WHERE status<>3 ORDER BY hostgroup_id, hostname, port"; - mydb->execute_statement(query, &error , &cols , &affected_rows , &resultset); - if (runtime_mysql_servers == nullptr) { - char* error = NULL; - int cols = 0; - int affected_rows = 0; - SQLite3_result* resultset = NULL; - - mydb->execute_statement(MYHGM_GEN_ADMIN_RUNTIME_SERVERS, &error, &cols, &affected_rows, &resultset); - - // Remove 'OFFLINE_HARD' servers since they are not relevant to propagate to other Cluster - // nodes, or relevant for checksum computation. - const size_t init_row_count = resultset->rows_count; - size_t rm_rows_count = 0; - const auto is_offline_server = [&rm_rows_count] (SQLite3_row* row) { - if (strcasecmp(row->fields[4], "OFFLINE_HARD") == 0) { - rm_rows_count += 1; - return true; - } else { - return false; - } - }; - resultset->rows.erase( - std::remove_if(resultset->rows.begin(), resultset->rows.end(), is_offline_server), - resultset->rows.end() - ); - resultset->rows_count = init_row_count - rm_rows_count; + // group replication + if (incoming_group_replication_hostgroups) { + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "DELETE FROM mysql_group_replication_hostgroups\n"); + mydb->execute("DELETE FROM mysql_group_replication_hostgroups"); + generate_mysql_group_replication_hostgroups_table(); + } - save_runtime_mysql_servers(resultset); - } else { - save_runtime_mysql_servers(runtime_mysql_servers); - } + // galera + if (incoming_galera_hostgroups) { + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "DELETE FROM mysql_galera_hostgroups\n"); + mydb->execute("DELETE FROM mysql_galera_hostgroups"); + generate_mysql_galera_hostgroups_table(); + } - // reset all checksum - table_resultset_checksum.fill(0); + // AWS Aurora + if (incoming_aws_aurora_hostgroups) { + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "DELETE FROM mysql_aws_aurora_hostgroups\n"); + mydb->execute("DELETE FROM mysql_aws_aurora_hostgroups"); + generate_mysql_aws_aurora_hostgroups_table(); + } - if (resultset) { - if (resultset->rows_count) { - if (init == false) { - init = true; - myhash.Init(19,3); - } - uint64_t hash1_ = resultset->raw_checksum(); + // hostgroup attributes + if (incoming_hostgroup_attributes) { + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "DELETE FROM mysql_hostgroup_attributes\n"); + mydb->execute("DELETE FROM mysql_hostgroup_attributes"); + generate_mysql_hostgroup_attributes_table(); + } - table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS] = hash1_; + uint64_t new_hash = commit_update_checksum_from_mysql_servers_v2(peer_mysql_servers_v2.resultset); - myhash.Update(&hash1_, sizeof(hash1_)); - proxy_info("Checksum for table %s is 0x%lX\n", "mysql_servers", hash1_); - } - delete resultset; - } else { - proxy_info("Checksum for table %s is 0x%lX\n", "mysql_servers", (long unsigned int)0); - } + { + const string new_checksum { get_checksum_from_hash(new_hash) }; + proxy_info("Checksum for table %s is %s\n", "mysql_servers_v2", new_checksum.c_str()); } - commit_update_checksums_from_tables(myhash, init); + global_checksum_v2 = gen_global_mysql_servers_v2_checksum(new_hash); + proxy_info("New computed global checksum for 'mysql_servers_v2' is '%s'\n", global_checksum_v2.c_str()); + } + + // Update 'mysql_servers' and global checksums + { + uint64_t new_hash = commit_update_checksum_from_mysql_servers(peer_runtime_mysql_servers.resultset); + const string new_checksum { get_checksum_from_hash(new_hash) }; + proxy_info("Checksum for table %s is %s\n", "mysql_servers", new_checksum.c_str()); - if (init == true) { - myhash.Final(&hash1, &hash2); - } - uint32_t d32[2]; - memcpy(&d32,&hash1,sizeof(hash1)); - sprintf(buf,"0x%0X%0X", d32[0], d32[1]); pthread_mutex_lock(&GloVars.checksum_mutex); - GloVars.checksums_values.mysql_servers.set_checksum(buf); - GloVars.checksums_values.mysql_servers.version++; - //struct timespec ts; - //clock_gettime(CLOCK_REALTIME, &ts); - time_t t = time(NULL); - if (epoch != 0 && checksum != "" && GloVars.checksums_values.mysql_servers.checksum == checksum) { - GloVars.checksums_values.mysql_servers.epoch = epoch; - } else { - GloVars.checksums_values.mysql_servers.epoch = t; + if (only_commit_runtime_mysql_servers == false) { + update_glovars_mysql_servers_v2_checksum(global_checksum_v2, peer_mysql_servers_v2.checksum, true); } - GloVars.checksums_values.updates_cnt++; - GloVars.generate_global_checksum(); - GloVars.epoch_version = t; + update_glovars_mysql_servers_checksum(new_checksum, peer_runtime_mysql_servers.checksum, update_version); pthread_mutex_unlock(&GloVars.checksum_mutex); } // fill Hostgroup_Manager_Mapping with latest records - if (hgsm_mysql_servers_checksum != table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS] || - hgsm_mysql_replication_hostgroups_checksum != table_resultset_checksum[HGM_TABLES::MYSQL_REPLICATION_HOSTGROUPS]) - { - proxy_info("Rebuilding 'Hostgroup_Manager_Mapping' due to checksums change - mysql_servers { old: 0x%lX, new: 0x%lX }, mysql_replication_hostgroups { old:0x%lX, new:0x%lX }\n", - hgsm_mysql_servers_checksum, table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS], - hgsm_mysql_replication_hostgroups_checksum, table_resultset_checksum[HGM_TABLES::MYSQL_REPLICATION_HOSTGROUPS]); - - char* error = NULL; - int cols = 0; - int affected_rows = 0; - SQLite3_result* resultset = NULL; - - const char* query = "SELECT DISTINCT hostname, port, '1' is_writer, status, reader_hostgroup, writer_hostgroup, mem_pointer FROM mysql_replication_hostgroups JOIN mysql_servers ON hostgroup_id=writer_hostgroup WHERE status<>3 \ - UNION \ - SELECT DISTINCT hostname, port, '0' is_writer, status, reader_hostgroup, writer_hostgroup, mem_pointer FROM mysql_replication_hostgroups JOIN mysql_servers ON hostgroup_id=reader_hostgroup WHERE status<>3 \ - ORDER BY hostname, port"; - - mydb->execute_statement(query, &error, &cols, &affected_rows, &resultset); - - hostgroup_server_mapping.clear(); - - if (resultset && resultset->rows_count) { - std::string fetched_server_id; - HostGroup_Server_Mapping* fetched_server_mapping = NULL; - - for (std::vector::iterator it = resultset->rows.begin(); it != resultset->rows.end(); ++it) { - SQLite3_row* r = *it; - - const std::string& server_id = std::string(r->fields[0]) + ":::" + r->fields[1]; - - if (fetched_server_mapping == NULL || server_id != fetched_server_id) { - - auto itr = hostgroup_server_mapping.find(server_id); - - if (itr == hostgroup_server_mapping.end()) { - std::unique_ptr server_mapping(new HostGroup_Server_Mapping(this)); - fetched_server_mapping = server_mapping.get(); - hostgroup_server_mapping.insert( std::pair> { - server_id, std::move(server_mapping) - } ); - } - else { - fetched_server_mapping = itr->second.get(); - } - - fetched_server_id = server_id; - } - - HostGroup_Server_Mapping::Node node; - //node.server_status = static_cast(atoi(r->fields[3])); - node.reader_hostgroup_id = atoi(r->fields[4]); - node.writer_hostgroup_id = atoi(r->fields[5]); - node.srv = reinterpret_cast(atoll(r->fields[6])); - - HostGroup_Server_Mapping::Type type = (r->fields[2] && r->fields[2][0] == '1') ? HostGroup_Server_Mapping::Type::WRITER : HostGroup_Server_Mapping::Type::READER; - fetched_server_mapping->add(type, node); - } - } - delete resultset; - - hgsm_mysql_servers_checksum = table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS]; - hgsm_mysql_replication_hostgroups_checksum = table_resultset_checksum[HGM_TABLES::MYSQL_REPLICATION_HOSTGROUPS]; - } + update_hostgroup_manager_mappings(); ev_async_send(gtid_ev_loop, gtid_ev_async); @@ -2062,6 +2205,44 @@ bool MySQL_HostGroups_Manager::commit( return true; } +/** + * @brief Calculate the checksum for the runtime mysql_servers record, after excluding all the rows + * with the status OFFLINE_HARD from the result set + * + * @details The runtime mysql_servers is now considered as a distinct module and have a separate checksum calculation. + * This is because the records in the runtime module may differ from those in the admin mysql_servers module, which + * can cause synchronization issues within the cluster. + * + * @param runtime_mysql_servers resultset of runtime mysql_servers or can be a nullptr. +*/ +uint64_t MySQL_HostGroups_Manager::get_mysql_servers_checksum(SQLite3_result* runtime_mysql_servers) { + + //Note: GloVars.checksum_mutex needs to be locked + SQLite3_result* resultset = nullptr; + + if (runtime_mysql_servers == nullptr) { + char* error = NULL; + int cols = 0; + int affected_rows = 0; + + mydb->execute_statement(MYHGM_GEN_CLUSTER_ADMIN_RUNTIME_SERVERS, &error, &cols, &affected_rows, &resultset); + + if (resultset) { + save_runtime_mysql_servers(resultset); + } else { + proxy_info("Checksum for table %s is 0x%lX\n", "mysql_servers", (long unsigned int)0); + } + } else { + resultset = runtime_mysql_servers; + save_runtime_mysql_servers(runtime_mysql_servers); + } + + table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS] = resultset != nullptr ? resultset->raw_checksum() : 0; + proxy_info("Checksum for table %s is 0x%lX\n", "mysql_servers", table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS]); + + return table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS]; +} + bool MySQL_HostGroups_Manager::gtid_exists(MySrvC *mysrvc, char * gtid_uuid, uint64_t gtid_trxid) { bool ret = false; pthread_rwlock_rdlock(>id_rwlock); @@ -2428,6 +2609,11 @@ void MySQL_HostGroups_Manager::generate_mysql_group_replication_hostgroups_table // it is now time to build a new structure in Monitor + generate_mysql_group_replication_hostgroups_monitor_resultset(); + pthread_mutex_unlock(&Group_Replication_Info_mutex); +} + +void MySQL_HostGroups_Manager::generate_mysql_group_replication_hostgroups_monitor_resultset() { pthread_mutex_lock(&GloMyMon->group_replication_mutex); { char *error=NULL; @@ -2446,8 +2632,6 @@ void MySQL_HostGroups_Manager::generate_mysql_group_replication_hostgroups_table } } pthread_mutex_unlock(&GloMyMon->group_replication_mutex); - - pthread_mutex_unlock(&Group_Replication_Info_mutex); } void MySQL_HostGroups_Manager::generate_mysql_galera_hostgroups_table() { @@ -2594,7 +2778,9 @@ SQLite3_result * MySQL_HostGroups_Manager::dump_table_mysql(const string& name) } else if (name == "mysql_hostgroup_attributes") { query=(char *)"SELECT hostgroup_id, max_num_online_servers, autocommit, free_connections_pct, init_connect, multiplex, connection_warming, throttle_connections_per_sec, ignore_session_variables, hostgroup_settings, servers_defaults, comment FROM mysql_hostgroup_attributes ORDER BY hostgroup_id"; } else if (name == "mysql_servers") { - query = (char *)MYHGM_GEN_ADMIN_RUNTIME_SERVERS; + query = (char *)MYHGM_GEN_ADMIN_RUNTIME_SERVERS; + } else if (name == "cluster_mysql_servers") { + query = (char *)MYHGM_GEN_CLUSTER_ADMIN_RUNTIME_SERVERS; } else { assert(0); } @@ -3425,23 +3611,13 @@ inline double get_prometheus_counter_val( return current_val; } -void MySQL_HostGroups_Manager::add(MySrvC *mysrvc, unsigned int _hid) { - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Adding MySrvC %p (%s:%d) for hostgroup %d\n", mysrvc, mysrvc->address, mysrvc->port, _hid); - - // Since metrics for servers are stored per-endpoint; the metrics for a particular endpoint can live longer than the - // 'MySrvC' itself. For example, a failover or a server config change could remove the server from a particular - // hostgroup, and a subsequent one bring it back to the original hostgroup. For this reason, everytime a 'mysrvc' is - // created and added to a particular hostgroup, we update the endpoint metrics for it. - std::string endpoint_id { std::to_string(_hid) + ":" + string { mysrvc->address } + ":" + std::to_string(mysrvc->port) }; - - mysrvc->bytes_recv = get_prometheus_counter_val(this->status.p_conn_pool_bytes_data_recv_map, endpoint_id); - mysrvc->bytes_sent = get_prometheus_counter_val(this->status.p_conn_pool_bytes_data_sent_map, endpoint_id); - mysrvc->connect_ERR = get_prometheus_counter_val(this->status.p_connection_pool_conn_err_map, endpoint_id); - mysrvc->connect_OK = get_prometheus_counter_val(this->status.p_connection_pool_conn_ok_map, endpoint_id); - mysrvc->queries_sent = get_prometheus_counter_val(this->status.p_connection_pool_queries_map, endpoint_id); - - MyHGC *myhgc=MyHGC_lookup(_hid); +void reset_hg_attrs_server_defaults(MySrvC* mysrvc) { + mysrvc->weight = -1; + mysrvc->max_connections = -1; + mysrvc->use_ssl = -1; +} +void update_hg_attrs_server_defaults(MySrvC* mysrvc, MyHGC* myhgc) { if (mysrvc->weight == -1) { if (myhgc->servers_defaults.weight != -1) { mysrvc->weight = myhgc->servers_defaults.weight; @@ -3466,7 +3642,25 @@ void MySQL_HostGroups_Manager::add(MySrvC *mysrvc, unsigned int _hid) { mysrvc->use_ssl = 0; } } +} + +void MySQL_HostGroups_Manager::add(MySrvC *mysrvc, unsigned int _hid) { + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Adding MySrvC %p (%s:%d) for hostgroup %d\n", mysrvc, mysrvc->address, mysrvc->port, _hid); + + // Since metrics for servers are stored per-endpoint; the metrics for a particular endpoint can live longer than the + // 'MySrvC' itself. For example, a failover or a server config change could remove the server from a particular + // hostgroup, and a subsequent one bring it back to the original hostgroup. For this reason, everytime a 'mysrvc' is + // created and added to a particular hostgroup, we update the endpoint metrics for it. + std::string endpoint_id { std::to_string(_hid) + ":" + string { mysrvc->address } + ":" + std::to_string(mysrvc->port) }; + + mysrvc->bytes_recv = get_prometheus_counter_val(this->status.p_conn_pool_bytes_data_recv_map, endpoint_id); + mysrvc->bytes_sent = get_prometheus_counter_val(this->status.p_conn_pool_bytes_data_sent_map, endpoint_id); + mysrvc->connect_ERR = get_prometheus_counter_val(this->status.p_connection_pool_conn_err_map, endpoint_id); + mysrvc->connect_OK = get_prometheus_counter_val(this->status.p_connection_pool_conn_ok_map, endpoint_id); + mysrvc->queries_sent = get_prometheus_counter_val(this->status.p_connection_pool_queries_map, endpoint_id); + MyHGC *myhgc=MyHGC_lookup(_hid); + update_hg_attrs_server_defaults(mysrvc, myhgc); myhgc->mysrvs->add(mysrvc); } @@ -3880,6 +4074,14 @@ void MySQL_HostGroups_Manager::save_runtime_mysql_servers(SQLite3_result *s) { runtime_mysql_servers=s; } +void MySQL_HostGroups_Manager::save_mysql_servers_v2(SQLite3_result* s) { + if (incoming_mysql_servers_v2) { + delete incoming_mysql_servers_v2; + incoming_mysql_servers_v2 = nullptr; + } + incoming_mysql_servers_v2 = s; +} + SQLite3_result* MySQL_HostGroups_Manager::get_current_mysql_table(const string& name) { if (name == "mysql_aws_aurora_hostgroups") { return this->incoming_aws_aurora_hostgroups; @@ -3891,8 +4093,10 @@ SQLite3_result* MySQL_HostGroups_Manager::get_current_mysql_table(const string& return this->incoming_replication_hostgroups; } else if (name == "mysql_hostgroup_attributes") { return this->incoming_hostgroup_attributes; - } else if (name == "mysql_servers") { + } else if (name == "cluster_mysql_servers") { return this->runtime_mysql_servers; + } else if (name == "mysql_servers_v2") { + return this->incoming_mysql_servers_v2; } else { assert(0); } @@ -4753,70 +4957,21 @@ void MySQL_HostGroups_Manager::read_only_action_v2(const std::listexecute("DELETE FROM mysql_servers"); generate_mysql_servers_table(); - //if (GloAdmin && GloAdmin->checksum_variables.checksum_mysql_servers) + // Update the global checksums after 'mysql_servers' regeneration { - char* error = NULL; - int cols = 0; - int affected_rows = 0; - SQLite3_result* resultset = NULL; - mydb->execute_statement(MYHGM_GEN_ADMIN_RUNTIME_SERVERS, &error, &cols, &affected_rows, &resultset); - save_runtime_mysql_servers(resultset); // assigning runtime_mysql_servers with updated mysql server resultset - - resultset = NULL; - - // reset mysql_server checksum - table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS] = 0; - hgsm_mysql_servers_checksum = 0; - - char* query = (char*)"SELECT hostgroup_id, hostname, port, gtid_port, CASE status WHEN 0 OR 1 OR 4 THEN 0 ELSE status END status, weight, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment FROM mysql_servers WHERE status<>3 ORDER BY hostgroup_id, hostname, port"; - mydb->execute_statement(query, &error, &cols, &affected_rows, &resultset); + unique_ptr resultset { get_admin_runtime_mysql_servers(mydb) }; + uint64_t raw_checksum = resultset ? resultset->raw_checksum() : 0; - if (resultset) { - if (resultset->rows_count) { - table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS] = resultset->raw_checksum(); + // This is required to be updated to avoid extra rebuilding member 'hostgroup_server_mapping' + // during 'commit'. For extra details see 'hgsm_mysql_servers_checksum' @details. + hgsm_mysql_servers_checksum = raw_checksum; - hgsm_mysql_servers_checksum = table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS]; - } - delete resultset; - } else { - proxy_info("Checksum for table %s is 0x%lX\n", "mysql_servers", (long unsigned int)0); - } - - uint64_t hash = 0, hash2 = 0; - SpookyHash myhash; - bool init = false; - - for (uint64_t hash_val : table_resultset_checksum) { - if (hash_val) { - if (init == false) { - init = true; - myhash.Init(19, 3); - } - - myhash.Update(&hash_val, sizeof(hash_val)); - } - } - - if (init == true) { - myhash.Final(&hash, &hash2); - } + string mysrvs_checksum { get_checksum_from_hash(raw_checksum) }; + save_runtime_mysql_servers(resultset.release()); + proxy_info("Checksum for table %s is %s\n", "mysql_servers", mysrvs_checksum.c_str()); - char buf[ProxySQL_Checksum_Value_LENGTH]; - uint32_t d32[2]; - memcpy(&d32, &hash, sizeof(hash)); - sprintf(buf, "0x%0X%0X", d32[0], d32[1]); pthread_mutex_lock(&GloVars.checksum_mutex); - GloVars.checksums_values.mysql_servers.set_checksum(buf); - GloVars.checksums_values.mysql_servers.version++; - //struct timespec ts; - //clock_gettime(CLOCK_REALTIME, &ts); - time_t t = time(NULL); - - GloVars.checksums_values.mysql_servers.epoch = t; - - GloVars.checksums_values.updates_cnt++; - GloVars.generate_global_checksum(); - GloVars.epoch_version = t; + update_glovars_mysql_servers_checksum(mysrvs_checksum); pthread_mutex_unlock(&GloVars.checksum_mutex); } } @@ -5640,10 +5795,92 @@ void MySQL_HostGroups_Manager::converge_group_replication_config(int _writer_hos resultset=NULL; } } - } else { - // we couldn't find the cluster, exits + } else { + // we couldn't find the cluster, exits + } + pthread_mutex_unlock(&Group_Replication_Info_mutex); +} + +void MySQL_HostGroups_Manager::update_group_replication_add_autodiscovered( + const string& _host, int _port, int _wr_hg +) { + pthread_mutex_lock(&Group_Replication_Info_mutex); + const auto gr_info_map_it = this->Group_Replication_Info_Map.find(_wr_hg); + int32_t reader_hg = -1; + + if (gr_info_map_it == Group_Replication_Info_Map.end()) { + assert(0); + } else { + reader_hg = gr_info_map_it->second->reader_hostgroup; + } + pthread_mutex_unlock(&Group_Replication_Info_mutex); + + wrlock(); + + MyHGC *myhgc = MyHGC_lookup(reader_hg); + bool srv_found = false; + bool srv_found_offline = false; + + for (uint32_t j = 0; j < myhgc->mysrvs->cnt(); j++) { + MySrvC* mysrvc = static_cast(myhgc->mysrvs->servers->index(j)); + + // If the server is found as 'OFFLINE_HARD' we reset the 'MySrvC' values corresponding with the + // 'servers_defaults' (as in a new 'MySrvC' creation). We then later update these values with the + // 'servers_defaults' attributes from its corresponding 'MyHGC'. This way we ensure uniform behavior + // of new servers, and 'OFFLINE_HARD' ones when a user update 'servers_defaults' values, and reloads + // the servers to runtime. + if (strcmp(mysrvc->address,_host.c_str())==0 && mysrvc->port==_port) { + srv_found = true; + if (mysrvc->status == MYSQL_SERVER_STATUS_OFFLINE_HARD) { + reset_hg_attrs_server_defaults(mysrvc); + update_hg_attrs_server_defaults(mysrvc, mysrvc->myhgc); + proxy_info( + "Found healthy previously discovered GR node %s:%d as 'OFFLINE_HARD', setting back as 'ONLINE' with:" + " hostgroup=%d, weight=%ld, max_connections=%ld, use_ssl=%d\n", + _host.c_str(), _port, reader_hg, mysrvc->weight, mysrvc->max_connections, mysrvc->use_ssl + ); + mysrvc->status = MYSQL_SERVER_STATUS_ONLINE; + srv_found_offline = true; + } + } + } + + if (srv_found == false) { + MySrvC* mysrvc = new MySrvC( + const_cast(_host.c_str()), _port, 0, -1, MYSQL_SERVER_STATUS_ONLINE, 0, -1, 0, -1, 0, const_cast("") + ); + add(mysrvc, reader_hg); + proxy_info( + "Adding new discovered GR node %s:%d with: hostgroup=%d, weight=%ld, max_connections=%ld, use_ssl=%d\n", + _host.c_str(), _port, reader_hg, mysrvc->weight, mysrvc->max_connections, mysrvc->use_ssl + ); + } + + if (srv_found == false || srv_found_offline) { + purge_mysql_servers_table(); + + mydb->execute("DELETE FROM mysql_servers"); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "DELETE FROM mysql_servers\n"); + + generate_mysql_servers_table(); + + // Update the global checksums after 'mysql_servers' regeneration + { + unique_ptr resultset { get_admin_runtime_mysql_servers(mydb) }; + string mysrvs_checksum { get_checksum_from_hash(resultset ? resultset->raw_checksum() : 0) }; + save_runtime_mysql_servers(resultset.release()); + proxy_info("Checksum for table %s is %s\n", "mysql_servers", mysrvs_checksum.c_str()); + + pthread_mutex_lock(&GloVars.checksum_mutex); + update_glovars_mysql_servers_checksum(mysrvs_checksum); + pthread_mutex_unlock(&GloVars.checksum_mutex); + } + + update_table_mysql_servers_for_monitor(false); + generate_mysql_group_replication_hostgroups_monitor_resultset(); } - pthread_mutex_unlock(&Group_Replication_Info_mutex); + + wrunlock(); } Galera_Info::Galera_Info(int w, int b, int r, int o, int mw, int mtb, bool _a, int _w, char *c) { @@ -6886,49 +7123,6 @@ bool AWS_Aurora_Info::update(int r, int _port, char *_end_addr, int maxl, int al return ret; } -/** - * @brief Helper function used to try to extract a value from the JSON field 'servers_defaults'. - * - * @param j JSON object constructed from 'servers_defaults' field. - * @param hid Hostgroup for which the 'servers_defaults' is defined in 'mysql_hostgroup_attributes'. Used for - * error logging. - * @param key The key for the value to be extracted. - * @param val_check A validation function, checks if the value is within a expected range. - * - * @return The value extracted from the supplied JSON. In case of error '-1', and error cause is logged. - */ -template ::value, bool>::type = true> -T j_get_srv_default_int_val( - const json& j, uint32_t hid, const string& key, const function& val_check -) { - if (j.find(key) != j.end()) { - const json::value_t val_type = j[key].type(); - const char* type_name = j[key].type_name(); - - if (val_type == json::value_t::number_integer || val_type == json::value_t::number_unsigned) { - T val = j[key].get(); - - if (val_check(val)) { - return val; - } else { - proxy_error( - "Invalid value %ld supplied for 'mysql_hostgroup_attributes.servers_defaults.%s' for hostgroup %d." - " Value NOT UPDATED.\n", - static_cast(val), key.c_str(), hid - ); - } - } else { - proxy_error( - "Invalid type '%s'(%hhu) supplied for 'mysql_hostgroup_attributes.servers_defaults.%s' for hostgroup %d." - " Value NOT UPDATED.\n", - type_name, (int) val_type, key.c_str(), hid - ); - } - } - - return static_cast(-1); -} - /** * @brief Initializes the supplied 'MyHGC' with the specified 'hostgroup_settings'. * @details Input verification is performed in the supplied 'hostgroup_settings'. It's expected to be a valid @@ -7214,23 +7408,7 @@ void MySQL_HostGroups_Manager::generate_mysql_aws_aurora_hostgroups_table() { // it is now time to build a new structure in Monitor pthread_mutex_lock(&GloMyMon->aws_aurora_mutex); - { - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - char *query=(char *)"SELECT writer_hostgroup, reader_hostgroup, hostname, port, MAX(use_ssl) use_ssl , max_lag_ms , check_interval_ms , check_timeout_ms , " - "add_lag_ms , min_lag_ms , lag_num_checks FROM mysql_servers JOIN mysql_aws_aurora_hostgroups ON hostgroup_id=writer_hostgroup OR " - "hostgroup_id=reader_hostgroup WHERE active=1 AND status NOT IN (2,3) GROUP BY writer_hostgroup, hostname, port"; - mydb->execute_statement(query, &error , &cols , &affected_rows , &resultset); - if (resultset) { - if (GloMyMon->AWS_Aurora_Hosts_resultset) { - delete GloMyMon->AWS_Aurora_Hosts_resultset; - } - GloMyMon->AWS_Aurora_Hosts_resultset=resultset; - GloMyMon->AWS_Aurora_Hosts_resultset_checksum=resultset->raw_checksum(); - } - } + update_aws_aurora_hosts_monitor_resultset(false); pthread_mutex_unlock(&GloMyMon->aws_aurora_mutex); pthread_mutex_unlock(&AWS_Aurora_Info_mutex); @@ -7343,6 +7521,86 @@ bool MySQL_HostGroups_Manager::aws_aurora_replication_lag_action(int _whid, int return ret; } +int MySQL_HostGroups_Manager::create_new_server_in_hg( + uint32_t hid, const srv_info_t& srv_info, const srv_opts_t& srv_opts +) { + int32_t res = -1; + MySrvC* mysrvc = find_server_in_hg(hid, srv_info.addr, srv_info.port); + + if (mysrvc == nullptr) { + char* c_hostname { const_cast(srv_info.addr.c_str()) }; + MySrvC* mysrvc = new MySrvC( + c_hostname, srv_info.port, 0, srv_opts.weigth, MYSQL_SERVER_STATUS_ONLINE, 0, srv_opts.max_conns, 0, + srv_opts.use_ssl, 0, const_cast("") + ); + add(mysrvc,hid); + proxy_info( + "Adding new discovered %s node %s:%d with: hostgroup=%d, weight=%ld, max_connections=%ld, use_ssl=%d\n", + srv_info.kind.c_str(), c_hostname, srv_info.port, hid, mysrvc->weight, mysrvc->max_connections, + mysrvc->use_ssl + ); + + res = 0; + } else { + // If the server is found as 'OFFLINE_HARD' we reset the 'MySrvC' values corresponding with the + // 'servers_defaults' (as in a new 'MySrvC' creation). We then later update these values with the + // 'servers_defaults' attributes from its corresponding 'MyHGC'. This way we ensure uniform behavior + // of new servers, and 'OFFLINE_HARD' ones when a user update 'servers_defaults' values, and reloads + // the servers to runtime. + if (mysrvc && mysrvc->status == MYSQL_SERVER_STATUS_OFFLINE_HARD) { + reset_hg_attrs_server_defaults(mysrvc); + update_hg_attrs_server_defaults(mysrvc, mysrvc->myhgc); + mysrvc->status = MYSQL_SERVER_STATUS_ONLINE; + + proxy_info( + "Found healthy previously discovered %s node %s:%d as 'OFFLINE_HARD', setting back as 'ONLINE' with:" + " hostgroup=%d, weight=%ld, max_connections=%ld, use_ssl=%d\n", + srv_info.kind.c_str(), srv_info.addr.c_str(), srv_info.port, hid, mysrvc->weight, + mysrvc->max_connections, mysrvc->use_ssl + ); + + res = 0; + } + } + + return res; +} + +int MySQL_HostGroups_Manager::remove_server_in_hg(uint32_t hid, const string& addr, uint16_t port) { + MySrvC* mysrvc = find_server_in_hg(hid, addr, port); + if (mysrvc == nullptr) { + return -1; + } + + uint64_t mysrvc_addr = reinterpret_cast(mysrvc); + + proxy_warning( + "Removed server at address %ld, hostgroup %d, address %s port %d." + " Setting status OFFLINE HARD and immediately dropping all free connections." + " Used connections will be dropped when trying to use them\n", + mysrvc_addr, hid, mysrvc->address, mysrvc->port + ); + + // Set the server status + mysrvc->status=MYSQL_SERVER_STATUS_OFFLINE_HARD; + mysrvc->ConnectionsFree->drop_all_connections(); + + // TODO-NOTE: This is only required in case the caller isn't going to perform: + // - Full deletion of servers in the target 'hid'. + // - Table regeneration for the servers in the target 'hid'. + // This is a very common pattern when further operations have been performed over the + // servers, e.g. a set of servers additions and deletions over the target hostgroups. + // //////////////////////////////////////////////////////////////////////// + + // Remove the server from the table + const string del_srv_query { "DELETE FROM mysql_servers WHERE mem_pointer=" + std::to_string(mysrvc_addr) }; + mydb->execute(del_srv_query.c_str()); + + // //////////////////////////////////////////////////////////////////////// + + return 0; +} + // FIXME: complete this!! void MySQL_HostGroups_Manager::update_aws_aurora_set_writer(int _whid, int _rhid, char *_server_id, bool verbose) { int cols=0; @@ -7388,26 +7646,8 @@ void MySQL_HostGroups_Manager::update_aws_aurora_set_writer(int _whid, int _rhid free(error); error=NULL; } - //free(query); if (resultset) { -/* - // let's get info about this cluster - pthread_mutex_lock(&AWS_Aurora_Info_mutex); - std::map::iterator it2; - it2 = AWS_Aurora_Info_Map.find(_writer_hostgroup); - AWS_Aurora_Info *info=NULL; - if (it2!=AWS_Aurora_Info_Map.end()) { - info=it2->second; - writer_is_also_reader=info->writer_is_also_reader; - new_reader_weight = info->new_reader_weight; - read_HG = info->reader_hostgroup; - //need_converge=info->need_converge; - //info->need_converge=false; - //max_writers = info->max_writers; - } - pthread_mutex_unlock(&AWS_Aurora_Info_mutex); -*/ if (resultset->rows_count) { for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { SQLite3_row *r=*it; @@ -7422,41 +7662,17 @@ void MySQL_HostGroups_Manager::update_aws_aurora_set_writer(int _whid, int _rhid } } } -/* - if (need_converge == false) { - SQLite3_result *resultset2=NULL; - q = (char *)"SELECT COUNT(*) FROM mysql_servers WHERE hostgroup_id=%d AND status=0"; - query=(char *)malloc(strlen(q)+32); - sprintf(query,q,_writer_hostgroup); - mydb->execute_statement(query, &error, &cols , &affected_rows , &resultset2); - if (resultset2) { - if (resultset2->rows_count) { - for (std::vector::iterator it = resultset2->rows.begin() ; it != resultset2->rows.end(); ++it) { - SQLite3_row *r=*it; - int nwriters = atoi(r->fields[0]); - if (nwriters > max_writers) { - proxy_warning("Galera: too many writers in HG %d. Max=%d, current=%d\n", _writer_hostgroup, max_writers, nwriters); - need_converge = true; - } - } - } - delete resultset2; + + if (found_writer) { // maybe no-op + if ( + (writer_is_also_reader==0 && found_reader==false) + || + (writer_is_also_reader > 0 && found_reader==true) + ) { // either both true or both false + delete resultset; + resultset=NULL; } - free(query); } -*/ -// if (need_converge==false) { - if (found_writer) { // maybe no-op - if ( - (writer_is_also_reader==0 && found_reader==false) - || - (writer_is_also_reader > 0 && found_reader==true) - ) { // either both true or both false - delete resultset; - resultset=NULL; - } - } -// } } if (resultset) { @@ -7464,8 +7680,6 @@ void MySQL_HostGroups_Manager::update_aws_aurora_set_writer(int _whid, int _rhid // This should be the case most of the time, // because the calling function knows if an action is required. if (resultset->rows_count) { - //need_converge=false; - GloAdmin->mysql_servers_wrlock(); mydb->execute("DELETE FROM mysql_servers_incoming"); q=(char *)"INSERT INTO mysql_servers_incoming SELECT hostgroup_id, hostname, port, gtid_port, weight, status, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment FROM mysql_servers WHERE hostgroup_id=%d"; @@ -7475,17 +7689,12 @@ void MySQL_HostGroups_Manager::update_aws_aurora_set_writer(int _whid, int _rhid sprintf(query, q, _writer_hostgroup, _server_id, domain_name, aurora_port); mydb->execute(query); q=(char *)"UPDATE OR IGNORE mysql_servers_incoming SET hostgroup_id=%d WHERE hostname='%s%s' AND port=%d AND hostgroup_id<>%d"; - //query=(char *)malloc(strlen(q)+strlen(_hostname)+1024); // increased this buffer as it is used for other queries too sprintf(query, q, _writer_hostgroup, _server_id, domain_name, aurora_port, _writer_hostgroup); mydb->execute(query); - //free(query); q=(char *)"DELETE FROM mysql_servers_incoming WHERE hostname='%s%s' AND port=%d AND hostgroup_id<>%d"; - //query=(char *)malloc(strlen(q)+strlen(_hostname)+64); sprintf(query, q, _server_id, domain_name, aurora_port, _writer_hostgroup); mydb->execute(query); - //free(query); q=(char *)"UPDATE mysql_servers_incoming SET status=0 WHERE hostname='%s%s' AND port=%d AND hostgroup_id=%d"; - //query=(char *)malloc(strlen(q)+strlen(_hostname)+64); sprintf(query, q, _server_id, domain_name, aurora_port, _writer_hostgroup); mydb->execute(query); @@ -7497,13 +7706,13 @@ void MySQL_HostGroups_Manager::update_aws_aurora_set_writer(int _whid, int _rhid sprintf(query,q,_rhid, new_reader_weight, _whid); mydb->execute(query); - //free(query); if (writer_is_also_reader && read_HG>=0) { q=(char *)"INSERT OR IGNORE INTO mysql_servers_incoming (hostgroup_id,hostname,port,gtid_port,status,weight,compression,max_connections,max_replication_lag,use_ssl,max_latency_ms,comment) SELECT %d,hostname,port,gtid_port,status,weight,compression,max_connections,max_replication_lag,use_ssl,max_latency_ms,comment FROM mysql_servers_incoming WHERE hostgroup_id=%d AND hostname='%s%s' AND port=%d"; sprintf(query, q, read_HG, _writer_hostgroup, _server_id, domain_name, aurora_port); mydb->execute(query); q = (char *)"UPDATE mysql_servers_incoming SET weight=%d WHERE hostgroup_id=%d AND hostname='%s%s' AND port=%d"; sprintf(query, q, new_reader_weight, read_HG, _server_id, domain_name, aurora_port); + mydb->execute(query); } uint64_t checksum_current = 0; uint64_t checksum_incoming = 0; @@ -7549,30 +7758,11 @@ void MySQL_HostGroups_Manager::update_aws_aurora_set_writer(int _whid, int _rhid mydb->execute(query); commit(); wrlock(); -/* - SQLite3_result *resultset2=NULL; - q=(char *)"SELECT writer_hostgroup, reader_hostgroup FROM mysql_aws_aurora_hostgroups WHERE writer_hostgroup=%d"; - sprintf(query,q,_writer_hostgroup); - mydb->execute_statement(query, &error, &cols , &affected_rows , &resultset2); - if (resultset2) { - if (resultset2->rows_count) { - for (std::vector::iterator it = resultset2->rows.begin() ; it != resultset2->rows.end(); ++it) { - SQLite3_row *r=*it; - int writer_hostgroup=atoi(r->fields[0]); - int reader_hostgroup=atoi(r->fields[1]); -*/ - q=(char *)"DELETE FROM mysql_servers WHERE hostgroup_id IN (%d , %d)"; - sprintf(query,q,_whid,_rhid); - mydb->execute(query); - generate_mysql_servers_table(&_whid); - generate_mysql_servers_table(&_rhid); -/* - } - } - delete resultset2; - resultset2=NULL; - } -*/ + q=(char *)"DELETE FROM mysql_servers WHERE hostgroup_id IN (%d , %d)"; + sprintf(query,q,_whid,_rhid); + mydb->execute(query); + generate_mysql_servers_table(&_whid); + generate_mysql_servers_table(&_rhid); wrunlock(); } else { if (GloMTH->variables.hostgroup_manager_verbose > 1) { @@ -7583,45 +7773,57 @@ void MySQL_HostGroups_Manager::update_aws_aurora_set_writer(int _whid, int _rhid free(query); query = NULL; } else { + string full_hostname { string { _server_id } + string { domain_name } }; + GloAdmin->mysql_servers_wrlock(); - mydb->execute("DELETE FROM mysql_servers_incoming"); - q=(char *)"INSERT INTO mysql_servers_incoming SELECT hostgroup_id, hostname, port, gtid_port, weight, status, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment FROM mysql_servers WHERE hostname<>'%s%s'"; - sprintf(query,q, _server_id, domain_name); - mydb->execute(query); + wrlock(); - unsigned int max_max_connections = 1000; - unsigned int max_use_ssl = 0; - MyHGC *myhgc = MyHGC_lookup(_whid); - for (int j = 0; j < (int) myhgc->mysrvs->cnt(); j++) { - MySrvC *mysrvc = (MySrvC *) myhgc->mysrvs->servers->index(j); - if (mysrvc->max_connections > max_max_connections) { - max_max_connections = mysrvc->max_connections; - } - if (mysrvc->use_ssl > (int32_t) max_use_ssl) { - max_use_ssl = mysrvc->use_ssl; - } + srv_info_t srv_info { full_hostname, static_cast(aurora_port), "Aurora AWS" }; + srv_opts_t wr_srv_opts { -1, -1, -1 }; + + int wr_res = create_new_server_in_hg(_writer_hostgroup, srv_info, wr_srv_opts); + int rd_res = -1; + + // WRITER can also be placed as READER, or could previously be one + if (writer_is_also_reader && read_HG >= 0) { + srv_opts_t rd_srv_opts { new_reader_weight, -1, -1 }; + rd_res = create_new_server_in_hg(read_HG, srv_info, rd_srv_opts); } - q=(char *)"INSERT INTO mysql_servers_incoming (hostgroup_id, hostname, port, weight, max_connections, use_ssl) VALUES (%d, '%s%s', %d, %d, %d, %d)"; - sprintf(query,q, _writer_hostgroup, _server_id, domain_name, aurora_port, new_reader_weight, max_max_connections, max_use_ssl); - mydb->execute(query); - if (writer_is_also_reader && read_HG>=0) { - q=(char *)"INSERT INTO mysql_servers_incoming (hostgroup_id, hostname, port, weight, max_connections, use_ssl) VALUES (%d, '%s%s', %d, %d, %d, %d)"; - sprintf(query, q, read_HG, _server_id, domain_name, aurora_port, new_reader_weight, max_max_connections, max_use_ssl); - mydb->execute(query); + // A new server has been created, or an OFFLINE_HARD brought back as ONLINE + if (wr_res == 0 || rd_res == 0) { + proxy_info( + "AWS Aurora: setting new auto-discovered host %s:%d as writer\n", full_hostname.c_str(), aurora_port + ); + purge_mysql_servers_table(); + + const char del_srvs_query_t[] { "DELETE FROM mysql_servers WHERE hostgroup_id IN (%d , %d)" }; + const string del_srvs_query { cstr_format(del_srvs_query_t, _whid, _rhid).str }; + mydb->execute(del_srvs_query.c_str()); + + generate_mysql_servers_table(&_whid); + generate_mysql_servers_table(&_rhid); + + // Update the global checksums after 'mysql_servers' regeneration + { + unique_ptr resultset { get_admin_runtime_mysql_servers(mydb) }; + string mysrvs_checksum { get_checksum_from_hash(resultset ? resultset->raw_checksum() : 0) }; + save_runtime_mysql_servers(resultset.release()); + proxy_info("Checksum for table %s is %s\n", "mysql_servers", mysrvs_checksum.c_str()); + + pthread_mutex_lock(&GloVars.checksum_mutex); + update_glovars_mysql_servers_checksum(mysrvs_checksum); + pthread_mutex_unlock(&GloVars.checksum_mutex); + } + + // Because 'commit' isn't called, we are required to update 'mysql_servers_for_monitor'. + update_table_mysql_servers_for_monitor(false); + // Update AWS Aurora resultset used for monitoring + update_aws_aurora_hosts_monitor_resultset(true); } - proxy_info("AWS Aurora: setting new auto-discovered host %s%s:%d as writer\n", _server_id, domain_name, aurora_port); - commit(); - wrlock(); - q=(char *)"DELETE FROM mysql_servers WHERE hostgroup_id IN (%d , %d)"; - sprintf(query,q,_whid,_rhid); - mydb->execute(query); - generate_mysql_servers_table(&_whid); - generate_mysql_servers_table(&_rhid); + wrunlock(); GloAdmin->mysql_servers_wrunlock(); - free(query); - query = NULL; } } if (resultset) { @@ -7661,9 +7863,9 @@ void MySQL_HostGroups_Manager::update_aws_aurora_set_reader(int _whid, int _rhid } pthread_mutex_unlock(&AWS_Aurora_Info_mutex); } - q=(char *)"SELECT hostgroup_id FROM mysql_servers JOIN mysql_aws_aurora_hostgroups ON hostgroup_id=writer_hostgroup OR hostgroup_id=reader_hostgroup WHERE hostname='%s%s' AND port=%d AND status<>3"; - query=(char *)malloc(strlen(q)+strlen(_server_id)+strlen(domain_name)+32); - sprintf(query, q, _server_id, domain_name, aurora_port); + q = (char*)"SELECT hostgroup_id FROM mysql_servers JOIN mysql_aws_aurora_hostgroups ON hostgroup_id=writer_hostgroup OR hostgroup_id=reader_hostgroup WHERE hostname='%s%s' AND port=%d AND status<>3 AND hostgroup_id IN (%d,%d)"; + query=(char *)malloc(strlen(q)+strlen(_server_id)+strlen(domain_name)+32+32+32); + sprintf(query, q, _server_id, domain_name, aurora_port, _whid, _rhid); mydb->execute_statement(query, &error, &cols , &affected_rows , &resultset); if (error) { free(error); @@ -7676,124 +7878,73 @@ void MySQL_HostGroups_Manager::update_aws_aurora_set_reader(int _whid, int _rhid GloAdmin->mysql_servers_wrlock(); mydb->execute("DELETE FROM mysql_servers_incoming"); mydb->execute("INSERT INTO mysql_servers_incoming SELECT hostgroup_id, hostname, port, gtid_port, weight, status, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment FROM mysql_servers"); - q=(char *)"UPDATE OR IGNORE mysql_servers_incoming SET hostgroup_id=%d WHERE hostname='%s%s' AND port=%d AND hostgroup_id<>%d"; + // If server present as WRITER try moving it to 'reader_hostgroup'. + q=(char *)"UPDATE OR IGNORE mysql_servers_incoming SET hostgroup_id=%d WHERE hostname='%s%s' AND port=%d AND hostgroup_id=%d"; query=(char *)malloc(strlen(q)+strlen(_server_id)+strlen(domain_name)+512); - sprintf(query, q, _rhid, _server_id, domain_name, aurora_port, _rhid); + sprintf(query, q, _rhid, _server_id, domain_name, aurora_port, _whid); mydb->execute(query); - //free(query); - q=(char *)"DELETE FROM mysql_servers_incoming WHERE hostname='%s%s' AND port=%d AND hostgroup_id<>%d"; - //query=(char *)malloc(strlen(q)+strlen(_hostname)+64); - sprintf(query, q, _server_id, domain_name, aurora_port, _rhid); + // Reader could previously be also a reader, in which case previous operation 'UPDATE OR IGNORE' + // did nothing. If server is still in the 'writer_hostgroup', we should remove it. + q=(char *)"DELETE FROM mysql_servers_incoming WHERE hostname='%s%s' AND port=%d AND hostgroup_id=%d"; + sprintf(query, q, _server_id, domain_name, aurora_port, _whid); mydb->execute(query); - //free(query); q=(char *)"UPDATE mysql_servers_incoming SET status=0 WHERE hostname='%s%s' AND port=%d AND hostgroup_id=%d"; sprintf(query, q, _server_id, domain_name, aurora_port, _rhid); mydb->execute(query); - //free(query); - //converge_galera_config(_writer_hostgroup); commit(); wrlock(); -/* - SQLite3_result *resultset2=NULL; - q=(char *)"SELECT writer_hostgroup, reader_hostgroup FROM mysql_galera_hostgroups WHERE writer_hostgroup=%d"; - //query=(char *)malloc(strlen(q)+strlen(_hostname)+64); - sprintf(query,q,_writer_hostgroup); - mydb->execute_statement(query, &error, &cols , &affected_rows , &resultset2); - if (resultset2) { - if (resultset2->rows_count) { - for (std::vector::iterator it = resultset2->rows.begin() ; it != resultset2->rows.end(); ++it) { - SQLite3_row *r=*it; - int writer_hostgroup=atoi(r->fields[0]); - int backup_writer_hostgroup=atoi(r->fields[1]); - int reader_hostgroup=atoi(r->fields[2]); - int offline_hostgroup=atoi(r->fields[3]); -*/ - q=(char *)"DELETE FROM mysql_servers WHERE hostgroup_id IN (%d , %d)"; - sprintf(query,q,_whid,_rhid); - mydb->execute(query); - generate_mysql_servers_table(&_whid); - generate_mysql_servers_table(&_rhid); -/* - generate_mysql_servers_table(&writer_hostgroup); - generate_mysql_servers_table(&backup_writer_hostgroup); - generate_mysql_servers_table(&reader_hostgroup); - generate_mysql_servers_table(&offline_hostgroup); - } - } - delete resultset2; - resultset2=NULL; - } -*/ + + q=(char *)"DELETE FROM mysql_servers WHERE hostgroup_id IN (%d , %d)"; + sprintf(query,q,_whid,_rhid); + mydb->execute(query); + generate_mysql_servers_table(&_whid); + generate_mysql_servers_table(&_rhid); + wrunlock(); GloAdmin->mysql_servers_wrunlock(); free(query); } else { // we couldn't find the server // autodiscovery algorithm here - char *full_hostname=(char *)malloc(strlen(_server_id)+strlen(domain_name)+1); - sprintf(full_hostname, "%s%s", _server_id, domain_name); - bool found = false; + string full_hostname { string { _server_id } + string { domain_name } }; GloAdmin->mysql_servers_wrlock(); - unsigned int max_max_connections = 10; - unsigned int max_use_ssl = 0; wrlock(); - MyHGC *myhgc=MyHGC_lookup(_rhid); - { - for (int j=0; j<(int)myhgc->mysrvs->cnt(); j++) { - MySrvC *mysrvc=(MySrvC *)myhgc->mysrvs->servers->index(j); - if (mysrvc->max_connections > max_max_connections) { - max_max_connections = mysrvc->max_connections; - } - if (mysrvc->use_ssl > (int32_t) max_use_ssl) { - max_use_ssl = mysrvc->use_ssl; - } - if (strcmp(mysrvc->address,full_hostname)==0 && mysrvc->port==aurora_port) { - found = true; - // we found the server, we just configure it online if it was offline - if (mysrvc->status == MYSQL_SERVER_STATUS_OFFLINE_HARD) { - mysrvc->status = MYSQL_SERVER_STATUS_ONLINE; - } - } - } - if (found == false) { // the server doesn't exist - MySrvC *mysrvc=new MySrvC(full_hostname, aurora_port, 0, new_reader_weight, MYSQL_SERVER_STATUS_ONLINE, 0, max_max_connections, 0, max_use_ssl, 0, (char *)""); // add new fields here if adding more columns in mysql_servers - proxy_info("Adding new discovered AWS Aurora node %s:%d with: hostgroup=%d, weight=%d, max_connections=%d\n" , full_hostname, aurora_port, _rhid , new_reader_weight, max_max_connections); - add(mysrvc,_rhid); - } - q=(char *)"DELETE FROM mysql_servers WHERE hostgroup_id IN (%d , %d)"; - query = (char *)malloc(strlen(q)+64); - sprintf(query,q,_whid,_rhid); - mydb->execute(query); + + srv_info_t srv_info { full_hostname, static_cast(aurora_port), "Aurora AWS" }; + srv_opts_t srv_opts { new_reader_weight, -1, -1 }; + int wr_res = create_new_server_in_hg(_rhid, srv_info, srv_opts); + + // A new server has been created, or an OFFLINE_HARD brought back as ONLINE + if (wr_res == 0) { + purge_mysql_servers_table(); + + const char del_srvs_query_t[] { "DELETE FROM mysql_servers WHERE hostgroup_id IN (%d , %d)" }; + const string del_srvs_query { cstr_format(del_srvs_query_t, _whid, _rhid).str }; + mydb->execute(del_srvs_query.c_str()); + generate_mysql_servers_table(&_whid); generate_mysql_servers_table(&_rhid); - free(query); - } - // NOTE: Because 'commit' isn't called, we are required to update 'mysql_servers_for_monitor'. - // Also note that 'generate_mysql_servers' is previously called. - update_table_mysql_servers_for_monitor(false); - wrunlock(); - // it is now time to build a new structure in Monitor - pthread_mutex_lock(&AWS_Aurora_Info_mutex); - pthread_mutex_lock(&GloMyMon->aws_aurora_mutex); - { - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - char *query=(char *)"SELECT writer_hostgroup, reader_hostgroup, hostname, port, MAX(use_ssl) use_ssl , max_lag_ms , check_interval_ms , check_timeout_ms, add_lag_ms, min_lag_ms, lag_num_checks FROM mysql_servers JOIN mysql_aws_aurora_hostgroups ON hostgroup_id=writer_hostgroup OR hostgroup_id=reader_hostgroup WHERE active=1 AND status NOT IN (2,3) GROUP BY hostname, port"; - mydb->execute_statement(query, &error , &cols , &affected_rows , &resultset); - if (resultset) { - if (GloMyMon->AWS_Aurora_Hosts_resultset) { - delete GloMyMon->AWS_Aurora_Hosts_resultset; - } - GloMyMon->AWS_Aurora_Hosts_resultset=resultset; - GloMyMon->AWS_Aurora_Hosts_resultset_checksum=resultset->raw_checksum(); + + // Update the global checksums after 'mysql_servers' regeneration + { + unique_ptr resultset { get_admin_runtime_mysql_servers(mydb) }; + string mysrvs_checksum { get_checksum_from_hash(resultset ? resultset->raw_checksum() : 0) }; + save_runtime_mysql_servers(resultset.release()); + proxy_info("Checksum for table %s is %s\n", "mysql_servers", mysrvs_checksum.c_str()); + + pthread_mutex_lock(&GloVars.checksum_mutex); + update_glovars_mysql_servers_checksum(mysrvs_checksum); + pthread_mutex_unlock(&GloVars.checksum_mutex); } + + // Because 'commit' isn't called, we are required to update 'mysql_servers_for_monitor'. + update_table_mysql_servers_for_monitor(false); + // Update AWS Aurora resultset used for monitoring + update_aws_aurora_hosts_monitor_resultset(true); } - pthread_mutex_unlock(&GloMyMon->aws_aurora_mutex); - pthread_mutex_unlock(&AWS_Aurora_Info_mutex); + + wrunlock(); GloAdmin->mysql_servers_wrunlock(); - free(full_hostname); } } if (resultset) { @@ -7803,6 +7954,42 @@ void MySQL_HostGroups_Manager::update_aws_aurora_set_reader(int _whid, int _rhid free(domain_name); } +const char SELECT_AWS_AURORA_SERVERS_FOR_MONITOR[] { + "SELECT writer_hostgroup, reader_hostgroup, hostname, port, MAX(use_ssl) use_ssl, max_lag_ms, check_interval_ms," + " check_timeout_ms, add_lag_ms, min_lag_ms, lag_num_checks FROM mysql_servers" + " JOIN mysql_aws_aurora_hostgroups ON" + " hostgroup_id=writer_hostgroup OR hostgroup_id=reader_hostgroup WHERE active=1 AND status NOT IN (2,3)" + " GROUP BY writer_hostgroup, hostname, port" +}; + +void MySQL_HostGroups_Manager::update_aws_aurora_hosts_monitor_resultset(bool lock) { + if (lock) { + pthread_mutex_lock(&AWS_Aurora_Info_mutex); + pthread_mutex_lock(&GloMyMon->aws_aurora_mutex); + } + + SQLite3_result* resultset = nullptr; + { + char* error = nullptr; + int cols = 0; + int affected_rows = 0; + mydb->execute_statement(SELECT_AWS_AURORA_SERVERS_FOR_MONITOR, &error, &cols, &affected_rows, &resultset); + } + + if (resultset) { + if (GloMyMon->AWS_Aurora_Hosts_resultset) { + delete GloMyMon->AWS_Aurora_Hosts_resultset; + } + GloMyMon->AWS_Aurora_Hosts_resultset=resultset; + GloMyMon->AWS_Aurora_Hosts_resultset_checksum=resultset->raw_checksum(); + } + + if (lock) { + pthread_mutex_unlock(&GloMyMon->aws_aurora_mutex); + pthread_mutex_unlock(&AWS_Aurora_Info_mutex); + } +} + MySrvC* MySQL_HostGroups_Manager::find_server_in_hg(unsigned int _hid, const std::string& addr, int port) { MySrvC* f_server = nullptr; diff --git a/lib/MySQL_Monitor.cpp b/lib/MySQL_Monitor.cpp index 2ec9822433..747c8e2488 100644 --- a/lib/MySQL_Monitor.cpp +++ b/lib/MySQL_Monitor.cpp @@ -212,6 +212,9 @@ class MySQL_Monitor_Connection_Pool { __conn_register_label: for (unsigned int i=0; ilen; i++) { MYSQL *my1 = (MYSQL *)conns->index(i); + // 'my1' can be NULL due to connection cleanup + if (my1 == nullptr) continue; + assert(my!=my1); //assert(my->net.fd!=my1->net.fd); // FIXME: we changed this with the next section of code if (my->net.fd == my1->net.fd) { @@ -232,6 +235,20 @@ class MySQL_Monitor_Connection_Pool { #endif // DEBUG return; }; + /** + * @brief Unregister the conn from the supplied 'mmsd'. + * @details DEBUG only helper function useful for checking the get/put connection flow + * for 'MySQL_Monitor_Connection_Pool'. This function should be called whenever a monitoring action does + * no longer require the conn of it's 'MMSD' and the conn has been considered 'non-suited' for being + * returned to the conn pool. This can be due to a failure in the data querying from the server itself, + * or due to unexpected data retrieved from the server. Due to this, the flow for calling this function + * during 'async' monitoring actions is: + * - If an error has taken place during the fetching itself, this function shall be called as soon as + * the failure is detected by the async state machine. + * - In case no error has taken place (TASK_RESULT_SUCCESS), this function should be called by the + * task-handler if it determines that the retrieved data is malformed. See handle_mmsd_mysql_conn. + * @param mmsd The 'mmsd' which conn should be unregistered. + */ void conn_unregister(MySQL_Monitor_State_Data *mmsd) { #ifdef DEBUG std::lock_guard lock(mutex); @@ -322,6 +339,9 @@ MYSQL * MySQL_Monitor_Connection_Pool::get_connection(char *hostname, int port, if (my) { for (unsigned int j=0; jlen; j++) { MYSQL *my1 = (MYSQL *)conns->index(j); + // 'my1' can be NULL due to connection cleanup + if (!my1) continue; + assert(my!=my1); assert(my->net.fd!=my1->net.fd); } @@ -488,19 +508,23 @@ void MySQL_Monitor_Connection_Pool::put_connection(char *hostname, int port, MYS /** * @brief MySQL 8 status query for Group Replication members. * @details Since 'MySQL 8' we rely on 'COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE', deprecating the previously - * required 'sys.gr_member_routing_candidate_status' view. + * required 'sys.gr_member_routing_candidate_status' view. Another additions: + * - A new field 'members' has been added to the query, containing the current cluster members as seen by the + * queried node. This field is used for auto discovery. + * - Server state 'RECOVERING' is now also considered when detecting if a member is a 'viable' candidate. */ const char MYSQL_8_GR_QUERY[] { "SELECT (SELECT IF (" "MEMBER_STATE='ONLINE' AND (" - "(SELECT COUNT(*) FROM performance_schema.replication_group_members WHERE MEMBER_STATE != 'ONLINE') >=" + "(SELECT COUNT(*) FROM performance_schema.replication_group_members WHERE MEMBER_STATE NOT IN ('ONLINE', 'RECOVERING')) >=" " ((SELECT COUNT(*) FROM performance_schema.replication_group_members)/2) = 0)" ", 'YES', 'NO')) AS viable_candidate," " (SELECT IF (@@read_only, 'YES', 'NO')) as read_only," - " COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE AS transactions_behind " + " COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE AS transactions_behind, " + " (SELECT GROUP_CONCAT(CONCAT(member_host, \":\", member_port)) FROM performance_schema.replication_group_members) AS members " "FROM " "performance_schema.replication_group_members " - "JOIN performance_schema.replication_group_member_stats rgms USING(member_id)" + "JOIN performance_schema.replication_group_member_stats rgms USING(member_id) " "WHERE rgms.MEMBER_ID=@@SERVER_UUID" }; @@ -609,7 +633,7 @@ void MySQL_Monitor_State_Data::init_async() { async_state_machine_ = ASYNC_QUERY_START; #ifdef TEST_GROUPREP { - query_ = "SELECT viable_candidate,read_only,transactions_behind FROM GR_MEMBER_ROUTING_CANDIDATE_STATUS "; + query_ = "SELECT viable_candidate,read_only,transactions_behind,members FROM GR_MEMBER_ROUTING_CANDIDATE_STATUS "; query_ += std::string(hostname) + ":" + std::to_string(port); } #else @@ -1861,7 +1885,7 @@ void * monitor_group_replication_thread(void *arg) { mmsd->interr=0; // reset the value #ifdef TEST_GROUPREP { - std::string s { "SELECT viable_candidate,read_only,transactions_behind FROM GR_MEMBER_ROUTING_CANDIDATE_STATUS" }; + std::string s { "SELECT viable_candidate,read_only,transactions_behind,members FROM GR_MEMBER_ROUTING_CANDIDATE_STATUS" }; s += " " + std::string(mmsd->hostname) + ":" + std::to_string(mmsd->port); mmsd->async_exit_status=mysql_query_start(&mmsd->interr,mmsd->mysql,s.c_str()); } @@ -3367,18 +3391,6 @@ set extract_writer_hgs(SQLite3_result* Group_Replication_Hosts_results return writer_hgs; } -/** - * @brief Holds the info from a GR server definition. - */ -typedef struct _gr_host_def_t { - string host; - int port; - int use_ssl; - bool writer_is_also_reader; - int max_transactions_behind; - int max_transactions_behind_count; -} gr_host_def_t; - /** * @brief Extracts a 'MySQL_Monitor_State_Data' from the provided 'SQLite3_result*'. * @details The expected contents of the provided 'SQLite3_result*' are the ones generated by @@ -3499,13 +3511,67 @@ unique_ptr init_mmsd_with_conn( return mmsd; } +using gr_srv_addr_t = pair; + struct gr_srv_st_t { bool viable_candidate = false; bool read_only = true; int64_t transactions_behind = -1; bool inv_srv_state = false; + vector gr_members {}; }; +#define GR_MEMBER_ENTRY_ERR "%s '%s' in 'members' field from GR query to server '%s:%d'. Autodiscovery action aborted.\n" + +vector> parse_gr_members_addrs( + const MySQL_Monitor_State_Data* mmsd, const vector& gr_cluster_members +) { +#ifdef DEBUG + nlohmann::ordered_json members { gr_cluster_members }; + proxy_debug( + PROXY_DEBUG_MONITOR, 7, "Received 'members' field '%s' from GR query to server '%s:%d'\n", members.dump().c_str(), + mmsd->hostname, mmsd->port + ); +#endif + vector> result {}; + + for (const auto& cluster_member : gr_cluster_members) { + const vector gr_member_host_port { split_str(cluster_member, ':') }; + if (gr_member_host_port.size() != 2) { + proxy_error(GR_MEMBER_ENTRY_ERR, "Invalid server entry", cluster_member.c_str(), mmsd->hostname, mmsd->port); + break; + } + + const string srv_host { gr_member_host_port[0] }; + const char* c_str_port { gr_member_host_port[1].c_str() }; + + int32_t srv_port = -1; + + { + char* p_end = nullptr; + long port = std::strtol(c_str_port, &p_end, 10); + + if (c_str_port == p_end) { + proxy_error( + GR_MEMBER_ENTRY_ERR, "Failed to parse port for server entry", cluster_member.c_str(), mmsd->hostname, mmsd->port + ); + break; + } else { + srv_port = port; + } + } + + result.push_back({srv_host, srv_port}); + } + + // If any entry fails to parse, we invalidate the whole action + if (gr_cluster_members.size() != result.size()) { + return {}; + } else { + return result; + } +} + gr_srv_st_t extract_gr_srv_st(MySQL_Monitor_State_Data* mmsd) { gr_srv_st_t gr_srv_st {}; @@ -3516,14 +3582,21 @@ gr_srv_st_t extract_gr_srv_st(MySQL_Monitor_State_Data* mmsd) { num_fields = mysql_num_fields(mmsd->result); num_rows = mysql_num_rows(mmsd->result); - if (fields == NULL || num_fields!=3 || num_rows!=1) { - proxy_error( - "'mysql_fetch_fields' returns 'NULL', or 'mysql_num_fields(%d)', or 'mysql_num_rows(%d)' are incorrect." - " Server %s:%d. See bug #1994\n", - num_fields, num_rows, mmsd->hostname, mmsd->port - ); + if (fields == NULL || num_fields!=4 || num_rows!=1) { + if (num_rows == 0) { + proxy_error( + "Empty resultset for GR monitoring query from server %s:%d. Server is likely misconfigured\n", + mmsd->hostname, mmsd->port + ); + } else { + proxy_error( + "Invalid resultset for GR monitoring query from server %s:%d. Either 'mysql_fetch_fields=NULL' or unexpected 'mysql_num_fields=%d'." + " Please report this incident\n", + mmsd->hostname, mmsd->port, num_fields + ); + } if (mmsd->mysql_error_msg == NULL) { - mmsd->mysql_error_msg = strdup("Unknown error"); + mmsd->mysql_error_msg = strdup("Invalid or malformed resultset"); } gr_srv_st.inv_srv_state = true; } else { @@ -3537,11 +3610,17 @@ gr_srv_st_t extract_gr_srv_st(MySQL_Monitor_State_Data* mmsd) { if (row[2]) { gr_srv_st.transactions_behind=atol(row[2]); } + if (mmsd->cur_monitored_gr_srvs && row[3]) { + const string str_members_addrs { row[3] }; + const vector members_addrs { split_str(str_members_addrs, ',') }; + + gr_srv_st.gr_members = parse_gr_members_addrs(mmsd, members_addrs); + } } } proxy_debug( - PROXY_DEBUG_MONITOR, 4, + PROXY_DEBUG_MONITOR, 7, "Fetched %u:%s:%d info - interr: %d, error: %s, viable_candidate:'%d', read_only:'%d'," " transactions_behind:'%ld'\n", mmsd->hostgroup_id, mmsd->hostname, mmsd->port, mmsd->interr, mmsd->mysql_error_msg, @@ -3664,6 +3743,24 @@ void gr_mon_action_over_resp_srv(MySQL_Monitor_State_Data* mmsd, const gr_node_i MyHGM->group_replication_lag_action( mmsd->writer_hostgroup, mmsd->hostname, mmsd->port, node_info.lag_counts, node_info.srv_st.read_only, enable ); + + if (mmsd->cur_monitored_gr_srvs && node_info.srv_st.gr_members.empty() == false) { + for (const gr_srv_addr_t& gr_member : node_info.srv_st.gr_members) { + const string& srv_host { gr_member.first }; + const int32_t srv_port { gr_member.second }; + bool found = false; + + for (const gr_host_def_t& host_def : *mmsd->cur_monitored_gr_srvs) { + if (srv_host == host_def.host && srv_port == host_def.port) { + found = true; + } + } + + if (found == false) { + MyHGM->update_group_replication_add_autodiscovered(srv_host, srv_port, mmsd->writer_hostgroup); + } + } + } } } } @@ -3695,12 +3792,16 @@ void gr_handle_actions_over_unresp_srvs(const vector& hosts_defs, * before placing the connection back into the 'ConnectionPool', on failure, we discard the connection. * @param mmsd The mmsd wrapper holding all information for returning the connection. */ -void handle_mmsd_mysql_conn(MySQL_Monitor_State_Data* mmsd, bool unregister_conn_on_failure) { +void handle_mmsd_mysql_conn(MySQL_Monitor_State_Data* mmsd) { if (mmsd == nullptr) return; if (mmsd->mysql) { if (mmsd->interr || mmsd->mysql_error_msg) { - if (unregister_conn_on_failure) { + // If 'MySQL_Monitor_State_Data' reaches the end of a task_handler without 'TASK_RESULT_UNKNOWN': + // 1. Connection failed to be created, 'task_result' should be 'TASK_RESULT_UNKNOWN'. No + // unregister needed. + // 2. Fetching operation failed, the async fetching handler already handled the 'unregister'. + if (mmsd->get_task_result() == MySQL_Monitor_State_Data_Task_Result::TASK_RESULT_SUCCESS) { GloMyMon->My_Conn_Pool->conn_unregister(mmsd); } mysql_close(mmsd->mysql); @@ -3717,7 +3818,7 @@ void handle_mmsd_mysql_conn(MySQL_Monitor_State_Data* mmsd, bool unregister_conn MyHGM->p_update_mysql_error_counter( p_mysql_error_type::proxysql, mmsd->hostgroup_id, mmsd->hostname, mmsd->port, mysql_errno(mmsd->mysql) ); - if (unregister_conn_on_failure) { + if (mmsd->get_task_result() == MySQL_Monitor_State_Data_Task_Result::TASK_RESULT_SUCCESS) { GloMyMon->My_Conn_Pool->conn_unregister(mmsd); } mysql_close(mmsd->mysql); @@ -3766,9 +3867,9 @@ void gr_report_fetching_errs(MySQL_Monitor_State_Data* mmsd) { * @param mmsd The server 'MySQL_Monitor_State_Data' after the fetching is completed. It should either * hold a valid 'MYSQL_RES' or an error. */ -void async_gr_mon_actions_handler(MySQL_Monitor_State_Data* mmsd, bool unregister_conn_on_failure) { +void async_gr_mon_actions_handler(MySQL_Monitor_State_Data* mmsd) { // We base 'start_time' on the conn init for 'MySQL_Monitor_State_Data'. If a conn creation was - // required, we take into account this time into account, otherwise we asume that 'start_time=t1'. + // required, we take into account this time into account, otherwise we assume that 'start_time=t1'. uint64_t start_time = 0; if (mmsd->created_conn) { start_time = mmsd->init_time; @@ -3789,7 +3890,7 @@ void async_gr_mon_actions_handler(MySQL_Monitor_State_Data* mmsd, bool unregiste } // Handle 'mmsd' MySQL conn return to 'ConnectionPool' - handle_mmsd_mysql_conn(mmsd, unregister_conn_on_failure); + handle_mmsd_mysql_conn(mmsd); } /** @@ -3894,6 +3995,11 @@ void* monitor_GR_thread_HG(void *arg) { } } + int rnd_discoverer = conn_mmsds.size() == 0 ? -1 : rand() % conn_mmsds.size(); + if (rnd_discoverer != -1) { + conn_mmsds[rnd_discoverer]->cur_monitored_gr_srvs = &hosts_defs; + } + // TODO: This needs to be reworked once we change the way monitoring actions work on clusters, taking // the full cluster fetch data to avoid transient states. For now, since we perform the monitoring // actions independently, we workaround the limitation of 'Monitor_Poll' of only handling @@ -3905,7 +4011,7 @@ void* monitor_GR_thread_HG(void *arg) { // Handle 'mmsds' that failed to optain conns for (const unique_ptr& mmsd : fail_mmsds) { - async_gr_mon_actions_handler(mmsd.get(), true); + async_gr_mon_actions_handler(mmsd.get()); } // Update 't1' for subsequent fetch operations and reset errors @@ -3923,6 +4029,10 @@ void* monitor_GR_thread_HG(void *arg) { /////////////////////////////////////////////////////////////////////////////////////// + if (rnd_discoverer != -1) { + conn_mmsds[rnd_discoverer]->cur_monitored_gr_srvs = nullptr; + } + // Set the time for the next iteration next_check_time = curtime + mysql_thread___monitor_groupreplication_healthcheck_interval * 1000; } @@ -5780,12 +5890,12 @@ void * monitor_AWS_Aurora_thread_HG(void *arg) { rnd %= num_hosts; rc_ping = GloMyMon->server_responds_to_ping(hpa[rnd].host, hpa[rnd].port); //proxy_info("Looping Monitor thread for AWS Aurora writer HG %u\n", wHG); -#ifdef TEST_AURORA +#ifdef TEST_AURORA_RANDOM if (rand() % 100 < 30) { // we randomly fail 30% of the requests rc_ping = false; } -#endif // TEST_AURORA +#endif // TEST_AURORA_RANDOM if (rc_ping) { found_pingable_host = true; cur_host_idx = rnd; @@ -5808,12 +5918,12 @@ void * monitor_AWS_Aurora_thread_HG(void *arg) { } } -#ifdef TEST_AURORA +#ifdef TEST_AURORA_RANDOM if (rand() % 200 == 0) { // we randomly fail 0.5% of the requests found_pingable_host = false; } -#endif // TEST_AURORA +#endif // TEST_AURORA_RANDOM if (found_pingable_host == false) { proxy_error("No node is pingable for AWS Aurora cluster with writer HG %u\n", wHG); @@ -5867,7 +5977,10 @@ void * monitor_AWS_Aurora_thread_HG(void *arg) { mmsd->t1=monotonic_time(); mmsd->interr=0; // reset the value #ifdef TEST_AURORA - mmsd->async_exit_status = mysql_query_start(&mmsd->interr, mmsd->mysql, "SELECT SERVER_ID, SESSION_ID, LAST_UPDATE_TIMESTAMP, REPLICA_LAG_IN_MILLISECONDS, CPU FROM REPLICA_HOST_STATUS ORDER BY SERVER_ID"); + { + string query { TEST_AURORA_MONITOR_BASE_QUERY + std::to_string(wHG) }; + mmsd->async_exit_status = mysql_query_start(&mmsd->interr, mmsd->mysql, query.c_str()); + } #else // for reference we list the old queries. // original implementation: @@ -6075,34 +6188,6 @@ void * monitor_AWS_Aurora_thread_HG(void *arg) { } } } -/* - mmsd->writer_hostgroup=atoi(r->fields[0]); - mmsd->writer_is_also_reader=atoi(r->fields[4]); - mmsd->max_transactions_behind=atoi(r->fields[5]); - mmsd->mondb=monitordb; - WorkItem* item; - item=new WorkItem(mmsd,monitor_AWS_Aurora_thread); - GloMyMon->queue.add(item); - usleep(us); -*/ -// } - -/* - for - for (std::vector::iterator it = Galera_Hosts_resultset->rows.begin() ; it != Galera_Hosts_resultset->rows.end(); ++it) { - - } - SQLite3_row *r=*it; - bool rc_ping = true; - rc_ping = server_responds_to_ping(r->fields[1],atoi(r->fields[2])); - if (rc_ping) { // only if server is responding to pings - MySQL_Monitor_State_Data *mmsd=new MySQL_Monitor_State_Data(r->fields[1],atoi(r->fields[2]), NULL, atoi(r->fields[3])); - mmsd->writer_hostgroup=atoi(r->fields[0]); - mmsd->writer_is_also_reader=atoi(r->fields[4]); - mmsd->max_transactions_behind=atoi(r->fields[5]); - mmsd->mondb=monitordb; - -*/ } __exit_monitor_AWS_Aurora_thread_HG_now: if (mmsd) { @@ -6222,66 +6307,6 @@ void * MySQL_Monitor::monitor_aws_aurora() { pthread_mutex_unlock(&aws_aurora_mutex); } -/* - if (t1 < next_loop_at) { - goto __sleep_monitor_aws_aurora; - } - - if (next_loop_at == 0) { - // free the queue - - } - - next_loop_at=t1+1000*mysql_thread___monitor_galera_healthcheck_interval; - pthread_mutex_lock(&aws_aurora_mutex); - if (AWS_Aurora_Hosts_resultset==NULL) { - goto __end_monitor_aws_aurora_loop; - } else { - if (AWS_Aurora_Hosts_resultset->rows_count==0) { - goto __end_monitor_aws_aurora_loop; - } - int us=100; - if (AWS_Aurora_Hosts_resultset->rows_count) { - us=mysql_thread___monitor_read_only_interval/2/Galera_Hosts_resultset->rows_count; - } - for (std::vector::iterator it = Galera_Hosts_resultset->rows.begin() ; it != Galera_Hosts_resultset->rows.end(); ++it) { - SQLite3_row *r=*it; - bool rc_ping = true; - rc_ping = server_responds_to_ping(r->fields[1],atoi(r->fields[2])); - if (rc_ping) { // only if server is responding to pings - MySQL_Monitor_State_Data *mmsd=new MySQL_Monitor_State_Data(r->fields[1],atoi(r->fields[2]), NULL, atoi(r->fields[3])); - mmsd->writer_hostgroup=atoi(r->fields[0]); - mmsd->writer_is_also_reader=atoi(r->fields[4]); - mmsd->max_transactions_behind=atoi(r->fields[5]); - mmsd->mondb=monitordb; - WorkItem* item; - item=new WorkItem(mmsd,monitor_AWS_Aurora_thread); - GloMyMon->queue.add(item); - usleep(us); - } - if (GloMyMon->shutdown) { - pthread_mutex_unlock(&galera_mutex); - return NULL; - } - } - } - -__end_monitor_aws_aurora_loop: - pthread_mutex_unlock(&aws_aurora_mutex); - if (mysql_thread___monitor_enabled==true) { - } - -__sleep_monitor_aws_aurora: - t2=monotonic_time(); - if (t2 200000) { - st = 200000; - } - usleep(st); - } -*/ usleep(10000); } if (mysql_thr) { @@ -6323,6 +6348,18 @@ unsigned int MySQL_Monitor::estimate_lag(char* server_id, AWS_Aurora_status_entr return mlag; } +void print_aws_aurora_status_entry(AWS_Aurora_status_entry* aase) { + if (aase && aase->start_time) { + if (aase->host_statuses->size()) { + for (AWS_Aurora_replica_host_status_entry* hse : *aase->host_statuses) { + if (hse) { + fprintf(stderr,"%s %s %s %f %f\n", hse->server_id, hse->session_id, hse->last_update_timestamp, hse->replica_lag_ms , hse->cpu); + } + } + } + } +} + void MySQL_Monitor::evaluate_aws_aurora_results(unsigned int wHG, unsigned int rHG, AWS_Aurora_status_entry **lasts_ase, unsigned int ase_idx, unsigned int max_latency_ms, unsigned int add_lag_ms, unsigned int min_lag_ms, unsigned int lag_num_checks) { #ifdef TEST_AURORA unsigned int i = 0; @@ -6340,16 +6377,7 @@ void MySQL_Monitor::evaluate_aws_aurora_results(unsigned int wHG, unsigned int r for (i=0; i < N_L_ASE; i++) { AWS_Aurora_status_entry *aase = lasts_ase[i]; if (ev == true || i == ase_idx) { - if (aase && aase->start_time) { - if ( aase->host_statuses->size() ) { - for (std::vector::iterator it3 = aase->host_statuses->begin(); it3!=aase->host_statuses->end(); ++it3) { - AWS_Aurora_replica_host_status_entry *hse = *it3; - if (hse) { - fprintf(stderr,"%s %s %s %f %f\n", hse->server_id, hse->session_id, hse->last_update_timestamp, hse->replica_lag_ms , hse->cpu); - } - } - } - } + print_aws_aurora_status_entry(aase); } } } @@ -6383,7 +6411,7 @@ void MySQL_Monitor::evaluate_aws_aurora_results(unsigned int wHG, unsigned int r if (strcmp(prev_hse->server_id,hse->server_id)==0) { bool prev_enabled = true; - unsigned int prev_lag_ms = estimate_lag(hse->server_id, lasts_ase, ase_idx, add_lag_ms, min_lag_ms, lag_num_checks); + unsigned int prev_lag_ms = estimate_lag(hse->server_id, lasts_ase, prev_ase_idx, add_lag_ms, min_lag_ms, lag_num_checks); if (prev_lag_ms > max_latency_ms) { prev_enabled = false; } @@ -7566,7 +7594,7 @@ bool MySQL_Monitor::monitor_group_replication_process_ready_tasks_2( for (MySQL_Monitor_State_Data* mmsd : mmsds) { const MySQL_Monitor_State_Data_Task_Result task_result = mmsd->get_task_result(); assert(task_result != MySQL_Monitor_State_Data_Task_Result::TASK_RESULT_PENDING); - async_gr_mon_actions_handler(mmsd, false); + async_gr_mon_actions_handler(mmsd); } return true; diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index ed91654c05..78d632540f 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -933,10 +933,11 @@ bool MySQL_Session::handler_CommitRollback(PtrSize_t *pkt) { uint16_t setStatus = 0; if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; client_myds->myprot.generate_pkt_OK(true,NULL,NULL,1,0,0,setStatus,0,NULL); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; if (mirror==false) { RequestEnd(NULL); + } else { + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; } l_free(pkt->size,pkt->ptr); if (c=='c' || c=='C') { @@ -1079,10 +1080,11 @@ bool MySQL_Session::handler_SetAutocommit(PtrSize_t *pkt) { uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; client_myds->myprot.generate_pkt_OK(true,NULL,NULL,1,0,0,setStatus,0,NULL); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; if (mirror==false) { RequestEnd(NULL); + } else { + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; } __sync_fetch_and_add(&MyHGM->status.autocommit_cnt_filtered, 1); } @@ -1324,10 +1326,11 @@ void MySQL_Session::return_proxysql_internal(PtrSize_t *pkt) { // default client_myds->DSS=STATE_QUERY_SENT_NET; client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,1064,(char *)"42000",(char *)"Unknown PROXYSQL INTERNAL command",true); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; if (mirror==false) { RequestEnd(NULL); + } else { + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; } l_free(pkt->size,pkt->ptr); } @@ -1545,10 +1548,11 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; client_myds->myprot.generate_pkt_OK(true,NULL,NULL,1,0,0,setStatus,0,NULL); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; if (mirror==false) { RequestEnd(NULL); + } else { + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; } l_free(pkt->size,pkt->ptr); __sync_fetch_and_add(&MyHGM->status.frontend_set_names, 1); @@ -1565,10 +1569,11 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { resultset->add_column_definition(SQLITE_TEXT, "Message"); SQLite3_to_MySQL(resultset, NULL, 0, &client_myds->myprot, false, deprecate_eof_active); delete resultset; - client_myds->DSS = STATE_SLEEP; - status = WAITING_CLIENT_DATA; if (mirror == false) { RequestEnd(NULL); + } else { + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; } l_free(pkt->size, pkt->ptr); return true; @@ -1597,10 +1602,11 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { if (mysql_thread___enable_load_data_local_infile == false) { client_myds->DSS=STATE_QUERY_SENT_NET; client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,1047,(char *)"HY000",(char *)"Unsupported 'LOAD DATA LOCAL INFILE' command",true); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; if (mirror==false) { RequestEnd(NULL); + } else { + client_myds->DSS=STATE_SLEEP; + status=WAITING_CLIENT_DATA; } l_free(pkt->size,pkt->ptr); return true; @@ -2556,8 +2562,6 @@ bool MySQL_Session::handler_again___status_CHANGING_CHARSET(int *_rc) { 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; - status=WAITING_CLIENT_DATA; - client_myds->DSS=STATE_SLEEP; RequestEnd(myds); } } else { @@ -6707,8 +6711,6 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; client_myds->myprot.generate_pkt_OK(true,NULL,NULL,1,0,0,setStatus,0,NULL); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; RequestEnd(NULL); l_free(pkt->size,pkt->ptr); return true; @@ -6774,8 +6776,6 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; client_myds->myprot.generate_pkt_OK(true,NULL,NULL,1,0,0,setStatus,0,NULL); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; RequestEnd(NULL); l_free(pkt->size,pkt->ptr); return true; @@ -6814,8 +6814,6 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); if (autocommit) setStatus |= SERVER_STATUS_AUTOCOMMIT; client_myds->myprot.generate_pkt_OK(true,NULL,NULL,1,0,0,setStatus,0,NULL); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; RequestEnd(NULL); l_free(pkt->size,pkt->ptr); return true; @@ -7684,8 +7682,6 @@ void MySQL_Session::LogQuery(MySQL_Data_Stream *myds) { } } } -// this should execute most of the commands executed when a request is finalized -// this should become the place to hook other functions void MySQL_Session::RequestEnd(MySQL_Data_Stream *myds) { // check if multiplexing needs to be disabled char *qdt = NULL; @@ -7873,8 +7869,6 @@ bool MySQL_Session::handle_command_query_kill(PtrSize_t *pkt) { uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); if (autocommit) setStatus = SERVER_STATUS_AUTOCOMMIT; client_myds->myprot.generate_pkt_OK(true,NULL,NULL,1,0,0,setStatus,0,NULL); - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; RequestEnd(NULL); l_free(pkt->size,pkt->ptr); return true; diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 576837e47c..85f811b541 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -8,6 +8,7 @@ #include #include #include "MySQL_HostGroups_Manager.h" +#include "mysql.h" #include "proxysql_admin.h" #include "re2/re2.h" #include "re2/regexp.h" @@ -668,6 +669,7 @@ static char * admin_variables_names[]= { (char *)"cluster_mysql_variables_save_to_disk", (char *)"cluster_admin_variables_save_to_disk", (char *)"cluster_ldap_variables_save_to_disk", + (char *)"cluster_mysql_servers_sync_algorithm", (char *)"checksum_mysql_query_rules", (char *)"checksum_mysql_servers", (char *)"checksum_mysql_users", @@ -1068,7 +1070,7 @@ bool FlushCommandWrapper(MySQL_Session *sess, const std::vector& cm else assert(0); msg += "\n"; - proxy_debug(PROXY_DEBUG_ADMIN, 4, msg.c_str()); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "%s", msg.c_str()); #endif // DEBUG SPA->send_MySQL_OK(&sess->client_myds->myprot, NULL); return true; @@ -1089,19 +1091,54 @@ bool FlushCommandWrapper(MySQL_Session *sess, const string& modname, char *query incoming_servers_t::incoming_servers_t() {} incoming_servers_t::incoming_servers_t( - SQLite3_result* runtime_mysql_servers, + SQLite3_result* incoming_mysql_servers_v2, SQLite3_result* incoming_replication_hostgroups, SQLite3_result* incoming_group_replication_hostgroups, SQLite3_result* incoming_galera_hostgroups, SQLite3_result* incoming_aurora_hostgroups, - SQLite3_result* incoming_hostgroup_attributes + SQLite3_result* incoming_hostgroup_attributes, + SQLite3_result* runtime_mysql_servers ) : - runtime_mysql_servers(runtime_mysql_servers), + incoming_mysql_servers_v2(incoming_mysql_servers_v2), incoming_replication_hostgroups(incoming_replication_hostgroups), incoming_group_replication_hostgroups(incoming_group_replication_hostgroups), incoming_galera_hostgroups(incoming_galera_hostgroups), incoming_aurora_hostgroups(incoming_aurora_hostgroups), - incoming_hostgroup_attributes(incoming_hostgroup_attributes) + incoming_hostgroup_attributes(incoming_hostgroup_attributes), + runtime_mysql_servers(runtime_mysql_servers) +{} + +runtime_mysql_servers_checksum_t::runtime_mysql_servers_checksum_t() : epoch(0) {} + +runtime_mysql_servers_checksum_t::runtime_mysql_servers_checksum_t(const std::string& checksum, time_t epoch) : + value(checksum), epoch(epoch) {} + +mysql_servers_v2_checksum_t::mysql_servers_v2_checksum_t() : epoch(0) {} + +mysql_servers_v2_checksum_t::mysql_servers_v2_checksum_t(const std::string& checksum, time_t epoch) : + value(checksum), epoch(epoch) {} + +bootstrap_info_t::~bootstrap_info_t() { + if (servers != nullptr) { + mysql_free_result(servers); + } + if (users != nullptr) { + mysql_free_result(users); + } +} + +peer_runtime_mysql_servers_t::peer_runtime_mysql_servers_t() : resultset(nullptr), checksum() {} + +peer_runtime_mysql_servers_t::peer_runtime_mysql_servers_t( + SQLite3_result* resultset, const runtime_mysql_servers_checksum_t& checksum +) : resultset(resultset), checksum(checksum) +{} + +peer_mysql_servers_v2_t::peer_mysql_servers_v2_t() : resultset(nullptr), checksum() {} + +peer_mysql_servers_v2_t::peer_mysql_servers_v2_t( + SQLite3_result* resultset, const mysql_servers_v2_checksum_t& checksum +) : resultset(resultset), checksum(checksum) {} int ProxySQL_Test___GetDigestTable(bool reset, bool use_swap) { @@ -3762,8 +3799,8 @@ void admin_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *pkt) { if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats string tn = ""; - if (!strncasecmp(CLUSTER_QUERY_MYSQL_SERVERS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_SERVERS))) { - tn = "mysql_servers"; + if (!strncasecmp(CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS, query_no_space, strlen(CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS))) { + tn = "cluster_mysql_servers"; } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_REPLICATION_HOSTGROUPS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_REPLICATION_HOSTGROUPS))) { tn = "mysql_replication_hostgroups"; } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_GROUP_REPLICATION_HOSTGROUPS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_GROUP_REPLICATION_HOSTGROUPS))) { @@ -3774,6 +3811,8 @@ void admin_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *pkt) { tn = "mysql_aws_aurora_hostgroups"; } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_HOSTGROUP_ATTRIBUTES, query_no_space, strlen(CLUSTER_QUERY_MYSQL_HOSTGROUP_ATTRIBUTES))) { tn = "mysql_hostgroup_attributes"; + } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_SERVERS_V2, query_no_space, strlen(CLUSTER_QUERY_MYSQL_SERVERS_V2))) { + tn = "mysql_servers_v2"; } if (tn != "") { GloAdmin->mysql_servers_wrlock(); @@ -3781,7 +3820,28 @@ void admin_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *pkt) { GloAdmin->mysql_servers_wrunlock(); if (resultset == nullptr) { - resultset=MyHGM->dump_table_mysql(tn); + // 'mysql_servers_v2' is a virtual table that represents the latest 'main.mysql_servers' + // records promoted by the user. This section shouldn't be reached, since the initial resulset + // for this table ('MySQL_HostGroups_Manager::incoming_mysql_servers') is generated during + // initialization, and it's only updated in subsequent user config promotions. In case we + // reach here, an empty resultset should be replied, as it would mean that no user + // config has ever been promoted to runtime, and thus, this virtual table should remain empty. + if (tn == "mysql_servers_v2") { + const string query_empty_resultset { + string { MYHGM_GEN_CLUSTER_ADMIN_RUNTIME_SERVERS } + " LIMIT 0" + }; + + char *error=NULL; + int cols=0; + int affected_rows=0; + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "%s\n", query); + GloAdmin->mysql_servers_wrlock(); + GloAdmin->admindb->execute_statement(query_empty_resultset.c_str(), &error, &cols, &affected_rows, &resultset); + GloAdmin->mysql_servers_wrunlock(); + } else { + resultset = MyHGM->dump_table_mysql(tn); + } + if (resultset) { sess->SQLite3_to_MySQL(resultset, error, affected_rows, &sess->client_myds->myprot); delete resultset; @@ -5926,6 +5986,7 @@ ProxySQL_Admin::ProxySQL_Admin() : variables.cluster_mysql_variables_diffs_before_sync = 3; variables.cluster_admin_variables_diffs_before_sync = 3; variables.cluster_ldap_variables_diffs_before_sync = 3; + variables.cluster_mysql_servers_sync_algorithm = 1; checksum_variables.checksum_mysql_query_rules = true; checksum_variables.checksum_mysql_servers = true; checksum_variables.checksum_mysql_users = true; @@ -6048,7 +6109,269 @@ void ProxySQL_Admin::init_ldap() { } } -bool ProxySQL_Admin::init() { +struct boot_srv_info_t { + string member_id; + string member_host; + uint32_t member_port; + string member_state; + string member_role; + string member_version; +}; + +struct BOOT_SRV_INFO_T { + enum { + MEMBER_ID, + MEMBER_HOST, + MEMBER_PORT, + MEMBER_STATE, + MEMBER_ROLE, + MEMBER_VERSION + }; +}; + +struct boot_user_info_t { + string user; + string ssl_type; + string auth_string; + string auth_plugin; + bool password_expired; +}; + +struct BOOT_USER_INFO_T { + enum { + USER, + SSL_TYPE, + AUTH_STRING, + AUTH_PLUGIN, + PASSWORD_EXPIRED + }; +}; + +struct srv_defs_t { + int64_t weight; + int64_t max_conns; + int32_t use_ssl; +}; + +using boot_srv_cnf_t = pair; + +vector extract_boot_servers_info(MYSQL_RES* servers) { + vector servers_info {}; + + while (MYSQL_ROW row = mysql_fetch_row(servers)) { + servers_info.push_back({ + string { row[BOOT_SRV_INFO_T::MEMBER_ID] }, + string { row[BOOT_SRV_INFO_T::MEMBER_HOST] }, + static_cast(stoi(row[BOOT_SRV_INFO_T::MEMBER_PORT])), + string { row[BOOT_SRV_INFO_T::MEMBER_STATE] }, + string { row[BOOT_SRV_INFO_T::MEMBER_ROLE] }, + string { row[BOOT_SRV_INFO_T::MEMBER_VERSION] }, + }); + } + + return servers_info; +} + +string build_boot_servers_insert(const vector& srvs_info_defs) { + const string t_srvs_insert { + "INSERT INTO mysql_servers (hostgroup_id,hostname,port,status,weight,max_connections,use_ssl) VALUES " + }; + string t_srvs_values {}; + + for (const auto& info_defs : srvs_info_defs) { + const boot_srv_info_t& srv_info = info_defs.first; + const srv_defs_t& srv_defs = info_defs.second; + + const char t_values[] { "(%d, \"%s\", %d, \"%s\", %ld, %ld, %d)" }; + string srv_values = cstr_format( + t_values, + srv_info.member_role == "PRIMARY" ? 0 : 1, // HOSTGROUP_ID + srv_info.member_host.c_str(), // HOSTNAME + srv_info.member_port, // PORT + srv_info.member_state.c_str(), // STATUS + srv_defs.weight, // Weight + srv_defs.max_conns, // Max Connections + srv_defs.use_ssl // UseSSL + ).str; + + if (&info_defs != &srvs_info_defs.back()) { + srv_values += ","; + } + + t_srvs_values += srv_values; + } + + const string servers_insert { t_srvs_insert + t_srvs_values }; + + return servers_insert; +} + +string build_boot_users_insert(MYSQL_RES* users) { + vector users_info {}; + + while (MYSQL_ROW row = mysql_fetch_row(users)) { + users_info.push_back({ + string { row[BOOT_USER_INFO_T::USER] }, + string { row[BOOT_USER_INFO_T::SSL_TYPE] }, + string { row[BOOT_USER_INFO_T::AUTH_STRING] }, + string { row[BOOT_USER_INFO_T::AUTH_PLUGIN] }, + static_cast(atoi(row[BOOT_USER_INFO_T::PASSWORD_EXPIRED])) + }); + } + + // MySQL Users + const string t_users_insert { + "INSERT INTO mysql_users (username,password,active,use_ssl) VALUES " + }; + string t_users_values {}; + + for (const boot_user_info_t& user : users_info) { + uint32_t use_ssl = user.ssl_type.empty() ? 0 : 1; + const char t_values[] { "(\"%s\", \"%s\", %d, %d)" }; + + string srv_values = cstr_format( + t_values, + user.user.c_str(), // USERNAME + user.auth_string.c_str(), // HOSTNAME + 1, // ACTIVE: Always ON + use_ssl // USE_SSL: Dependent on backend user + ).str; + + if (&user != &users_info.back()) { + srv_values += ","; + } + + t_users_values += srv_values; + } + + const string users_insert { t_users_insert + t_users_values }; + + return users_insert; +} + +map get_cur_hg_attrs(SQLite3DB* admindb) { + map res {}; + + char* error = nullptr; + int cols = 0; + int affected_rows = 0; + SQLite3_result* resultset = NULL; + + admindb->execute_statement( + "SELECT hostgroup_id,servers_defaults FROM mysql_hostgroup_attributes", + &error, &cols, &affected_rows, &resultset + ); + + for (SQLite3_row* row : resultset->rows) { + const int32_t hid = atoi(row->fields[0]); + srv_defs_t srv_defs {}; + srv_defs.weight = 1; + srv_defs.max_conns = 512; + srv_defs.use_ssl = 1; + + nlohmann::json j_srv_defs = nlohmann::json::parse(row->fields[1]); + + const auto weight_check = [] (int64_t weight) -> bool { return weight >= 0; }; + srv_defs.weight = j_get_srv_default_int_val(j_srv_defs, hid, "weight", weight_check); + + const auto max_conns_check = [] (int64_t max_conns) -> bool { return max_conns >= 0; }; + srv_defs.max_conns = j_get_srv_default_int_val(j_srv_defs, hid, "max_connections", max_conns_check); + + const auto use_ssl_check = [] (int32_t use_ssl) -> bool { return use_ssl == 0 || use_ssl == 1; }; + srv_defs.use_ssl = j_get_srv_default_int_val(j_srv_defs, hid, "use_ssl", use_ssl_check); + + res.insert({ hid , srv_defs }); + } + + delete resultset; + + return res; +} + +vector build_srvs_info_with_defs( + const vector& srvs_info, + const map& hgid_defs, + const srv_defs_t global_defs +) { + vector res {}; + + for (const boot_srv_info_t& srv_info : srvs_info) { + if (srv_info.member_role == "PRIMARY") { + const auto hg_it = hgid_defs.find(0); + + if (hg_it != hgid_defs.end()) { + res.push_back({ srv_info, hg_it->second }); + } else { + res.push_back({ srv_info, global_defs }); + } + } else { + const auto hg_it = hgid_defs.find(1); + + if (hg_it != hgid_defs.end()) { + res.push_back({ srv_info, hg_it->second }); + } else { + res.push_back({ srv_info, global_defs }); + } + } + } + + return res; +} + +/** + * @brief Helper function used to check if tables are already filled with data. + * @details Handles the boilerplate operations of executing 'SELECT COUNT(*)' alike queries. + * @param admindb An already initialized instance of a SQLite3DB object to 'mem_admindb'. + * @param query The query to be executed, it's required to be 'SELECT COUNT(*)' alike. + * @return The resulting int of the 'COUNT(*)' in case of success, '-1' otherwise. In case of error, error + * cause are logged, and `assert` is called. + */ +int check_if_user_config(SQLite3DB* admindb, const char* query) { + char* error = nullptr; + int cols = 0; + int affected_rows = 0; + SQLite3_result* resultset = NULL; + + admindb->execute_statement(query, &error, &cols, &affected_rows, &resultset); + if (error) { + proxy_error( + "Aborting due to failed query over SQLite3 - db: '%s', query: '%s', err: %s", admindb->get_url(), query, error + ); + assert(0); + } + + int count = -1; + + if (resultset != nullptr && !resultset->rows.empty() && resultset->rows[0]->cnt >= 0) { + const char* s_count = resultset->rows[0]->fields[0]; + char* p_end = nullptr; + + count = std::strtol(s_count, &p_end, 10); + + if (p_end == s_count || errno == ERANGE) { + proxy_error( + "Aborting due to invalid query output, expected single INT (E.g. 'COUNT(*)') - query: '%s'", query + ); + count = -1; + } + } + + if (count == -1) { + assert(0); + } + + delete resultset; + return count; +}; + +/** + * @brief Definition of an auxiliary table used to store bootstrap variables. + * @details Table is used only to store in configdb bootstrap variables that are required to persist between + * executions. + */ +#define ADMIN_SQLITE_TABLE_BOOTSTRAP_VARIABLES "CREATE TABLE IF NOT EXISTS bootstrap_variables (variable_name VARCHAR NOT NULL PRIMARY KEY , variable_value VARCHAR NOT NULL)" + +bool ProxySQL_Admin::init(const bootstrap_info_t& bootstrap_info) { cpu_timer cpt; Admin_HTTP_Server = NULL; @@ -6333,8 +6656,201 @@ bool ProxySQL_Admin::init() { __insert_or_replace_disktable_select_maintable(); } } + + /** + * @brief Inserts a default 'mysql_group_replication_hostgroup'. + * @details Uses the following defaults: + * - writer_hostgroup: 0 + * - reader_hostgroup: 1 + * - backup_writer_hostgroup: 2 + * - offline_hostgroup: 3 + * - max_writers: 9 + * - writer_is_also_reader: 0 -> Keep hostgroups separated + * - max_transactions_behind: 0 + * + * The number of writers in 'multi_primary_mode' wont be restricted, user should tune this value to + * convenience. By default 'max_writers' is set to 9, as is the current member limitation for Group + * Replication. + */ + const char insert_def_gr_hgs[] { + "INSERT INTO mysql_group_replication_hostgroups (" + "writer_hostgroup,backup_writer_hostgroup,reader_hostgroup,offline_hostgroup,active,max_writers," + "writer_is_also_reader" + ") VALUES (0,2,1,3,1,9,0)" + }; + vector servers_info {}; + + if (GloVars.global.gr_bootstrap_mode) { + // Check if user config is present for 'mysql_group_replication_hostgroups' + bool user_gr_hg_cnf = check_if_user_config(admindb, "SELECT COUNT(*) FROM mysql_group_replication_hostgroups"); + if (user_gr_hg_cnf == false) { + admindb->execute(insert_def_gr_hgs); + } else { + proxy_info("Bootstrap config, found previous user 'mysql_group_replication_hostgroups' config, reusing...\n"); + } + + // Stores current user config for 'mysql_hostgroup_attributes::servers_defaults' + map hgid_defs {}; + // Check if user config is present for 'mysql_hostgroup_attributes' + bool user_gr_hg_attrs_cnf = check_if_user_config(admindb, "SELECT COUNT(*) FROM mysql_hostgroup_attributes"); + int32_t have_ssl = 1; + + // SSL explicitly disabled by user for backend connections + if (GloVars.global.gr_bootstrap_ssl_mode) { + if (strcasecmp(GloVars.global.gr_bootstrap_ssl_mode, "DISABLED") == 0) { + have_ssl = 0; + } + } + + const int64_t DEF_GR_SRV_WEIGHT = 1; + const int64_t DEF_GR_SRV_MAX_CONNS = 512; + const int32_t DEF_GR_SRV_USE_SSL = have_ssl; + + // Update 'mysql_hostgroup_attributes' with sensible defaults for the new discovered instances + if (user_gr_hg_attrs_cnf == false) { + const nlohmann::json j_def_attrs { + { "weight", DEF_GR_SRV_WEIGHT }, + { "max_connections", DEF_GR_SRV_MAX_CONNS }, + { "use_ssl", DEF_GR_SRV_USE_SSL } + }; + const string str_def_attrs { j_def_attrs.dump() }; + const string insert_def_hg_attrs { + "INSERT INTO mysql_hostgroup_attributes (hostgroup_id, servers_defaults) VALUES" + " (0,'"+ str_def_attrs + "'), (1,'" + str_def_attrs + "')" + }; + admindb->execute(insert_def_hg_attrs.c_str()); + } else { + proxy_info("Bootstrap config, found previous user 'mysql_hostgroup_attributes' config, reusing...\n"); + hgid_defs = get_cur_hg_attrs(admindb); + } + + // Define the 'global defaults'. Either pure defaults, or user specified (argument). These values are + // supersede if previous user config is found for 'mysql_hostgroup_attributes::servers_defaults'. + srv_defs_t global_srvs_defs {}; + global_srvs_defs.weight = DEF_GR_SRV_WEIGHT; + global_srvs_defs.max_conns = DEF_GR_SRV_MAX_CONNS; + global_srvs_defs.use_ssl = DEF_GR_SRV_USE_SSL; + + servers_info = extract_boot_servers_info(bootstrap_info.servers); + auto full_srvs_info = build_srvs_info_with_defs(servers_info, hgid_defs, global_srvs_defs); + const string servers_insert { build_boot_servers_insert(full_srvs_info) }; + + admindb->execute("DELETE FROM mysql_servers"); + admindb->execute(servers_insert.c_str()); + + const string users_insert { build_boot_users_insert(bootstrap_info.users) }; + admindb->execute("DELETE FROM mysql_users"); + admindb->execute(users_insert.c_str()); + + // Make the configuration persistent + flush_GENERIC__from_to("mysql_servers", "memory_to_disk"); + flush_mysql_users__from_memory_to_disk(); + } + + // Admin variables 'bootstrap' modifications + if (GloVars.global.gr_bootstrap_mode) { + // TODO-NOTE: This MUST go away; 'admin-hash_passwords' will be deprecated + admindb->execute("UPDATE global_variables SET variable_value='false' WHERE variable_name='admin-hash_passwords'"); + } flush_admin_variables___database_to_runtime(admindb,true); + + if (GloVars.global.gr_bootstrap_mode) { + flush_admin_variables___runtime_to_database(configdb, false, true, false); + } + + // MySQL variables / MySQL Query Rules 'bootstrap' modifications + if (GloVars.global.gr_bootstrap_mode && !servers_info.empty()) { + const uint64_t base_port { + GloVars.global.gr_bootstrap_conf_base_port == 0 ? 6446 : + GloVars.global.gr_bootstrap_conf_base_port + }; + const string bind_addr { + GloVars.global.gr_bootstrap_conf_bind_address == nullptr ? "0.0.0.0" : + string { GloVars.global.gr_bootstrap_conf_bind_address } + }; + const string s_rw_port { std::to_string(base_port) }; + const string s_ro_port { std::to_string(base_port + 1) }; + const string rw_addr { bind_addr + ":" + s_rw_port }; + const string ro_addr { bind_addr + ":" + s_ro_port }; + const string mysql_interfaces { rw_addr + ";" + ro_addr }; + + // Look for the default collation + const MARIADB_CHARSET_INFO* charset_info = proxysql_find_charset_nr(bootstrap_info.server_language); + const char* server_charset = charset_info == nullptr ? "" : charset_info->csname; + const char* server_collation = charset_info == nullptr ? "" : charset_info->name; + + // Holds user specified values, defaults, and implications of variables over others + const map bootstrap_mysql_vars { + { "mysql-server_version", bootstrap_info.server_version.c_str() }, + { "mysql-default_charset", server_charset }, + { "mysql-default_collation_connection", server_collation }, + { "mysql-interfaces", mysql_interfaces.c_str() }, + { "mysql-monitor_username", bootstrap_info.mon_user.c_str() }, + { "mysql-monitor_password", bootstrap_info.mon_pass.c_str() }, + { "mysql-have_ssl", "true" }, + { "mysql-ssl_p2s_ca", GloVars.global.gr_bootstrap_ssl_ca }, + { "mysql-ssl_p2s_capath", GloVars.global.gr_bootstrap_ssl_capath }, + { "mysql-ssl_p2s_cert", GloVars.global.gr_bootstrap_ssl_cert }, + { "mysql-ssl_p2s_cipher", GloVars.global.gr_bootstrap_ssl_cipher }, + { "mysql-ssl_p2s_crl", GloVars.global.gr_bootstrap_ssl_crl }, + { "mysql-ssl_p2s_crlpath", GloVars.global.gr_bootstrap_ssl_crlpath }, + { "mysql-ssl_p2s_key", GloVars.global.gr_bootstrap_ssl_key } + }; + + for (const pair& p_var_val : bootstrap_mysql_vars) { + if (p_var_val.second != nullptr) { + const string& name { p_var_val.first }; + const string& value { p_var_val.second }; + const string update_mysql_var { + "UPDATE global_variables SET variable_value='" + value + "' WHERE variable_name='" + name + "'" + }; + + admindb->execute(update_mysql_var.c_str()); + } + } + + // MySQL Query Rules - Port based RW split + { + // TODO: This should be able to contain in the future Unix socket based rules + const string insert_rw_split_rules { + "INSERT INTO mysql_query_rules (rule_id,active,proxy_port,destination_hostgroup,apply) VALUES " + " (0,1," + s_rw_port + ",0,1), (1,1," + s_ro_port + ",1,1)" + }; + + // Preserve previous user config targeting hostgroups 0/1 + bool user_qr_cnf = check_if_user_config(admindb, "SELECT COUNT(*) FROM mysql_query_rules"); + if (user_qr_cnf == false) { + admindb->execute(insert_rw_split_rules.c_str()); + } else { + proxy_info("Bootstrap config, found previous user 'mysql_query_rules' config, reusing...\n"); + } + + flush_GENERIC__from_to("mysql_query_rules", "memory_to_disk"); + } + + // Store the 'bootstrap_variables' + if (bootstrap_info.rand_gen_user) { + configdb->execute(ADMIN_SQLITE_TABLE_BOOTSTRAP_VARIABLES); + + const string insert_bootstrap_user { + "INSERT INTO bootstrap_variables (variable_name,variable_value) VALUES" + " ('bootstrap_username','" + string { bootstrap_info.mon_user } + "')" + }; + const string insert_bootstrap_pass { + "INSERT INTO bootstrap_variables (variable_name,variable_value) VALUES" + " ('bootstrap_password','" + string { bootstrap_info.mon_pass } + "')" + }; + + configdb->execute("DELETE FROM bootstrap_variables WHERE variable_name='bootstrap_username'"); + configdb->execute(insert_bootstrap_user.c_str()); + configdb->execute("DELETE FROM bootstrap_variables WHERE variable_name='bootstrap_password'"); + configdb->execute(insert_bootstrap_pass.c_str()); + } + } flush_mysql_variables___database_to_runtime(admindb,true); + if (GloVars.global.gr_bootstrap_mode) { + flush_mysql_variables___runtime_to_database(configdb, false, true, false); + } #ifdef PROXYSQLCLICKHOUSE flush_clickhouse_variables___database_to_runtime(admindb,true); #endif /* PROXYSQLCLICKHOUSE */ @@ -7554,7 +8070,7 @@ bool ProxySQL_Admin::ProxySQL_Test___Verify_mysql_query_rules_fast_routing( vector th_hashmaps {}; if (maps_per_thread) { - for (int i = 0; i < ths; i++) { + for (uint32_t i = 0; i < static_cast(ths); i++) { th_hashmaps.push_back(GloQPro->create_fast_routing_hashmap(resultset2)); } } @@ -8035,6 +8551,10 @@ char * ProxySQL_Admin::get_variable(char *name) { sprintf(intbuf,"%d",variables.cluster_ldap_variables_diffs_before_sync); return strdup(intbuf); } + if (!strcasecmp(name,"cluster_mysql_servers_sync_algorithm")) { + sprintf(intbuf, "%d", variables.cluster_mysql_servers_sync_algorithm); + return strdup(intbuf); + } if (!strcasecmp(name,"cluster_mysql_query_rules_save_to_disk")) { return strdup((variables.cluster_mysql_query_rules_save_to_disk ? "true" : "false")); } @@ -8512,6 +9032,22 @@ bool ProxySQL_Admin::set_variable(char *name, char *value, bool lock) { // this return false; } } + if (!strcasecmp(name,"cluster_mysql_servers_sync_algorithm")) { + int intv=atoi(value); + if (intv >= 1 && intv <= 3) { + + if (variables.cluster_mysql_servers_sync_algorithm != intv) { + proxy_info("'cluster_mysql_servers_sync_algorithm' updated. Resetting global checksums to force Cluster re-sync.\n"); + GloProxyCluster->Reset_Global_Checksums(lock); + } + + variables.cluster_mysql_servers_sync_algorithm=intv; + __sync_lock_test_and_set(&GloProxyCluster->cluster_mysql_servers_sync_algorithm, intv); + return true; + } else { + return false; + } + } if (!strcasecmp(name,"version")) { if (strcasecmp(value,(char *)PROXYSQL_VERSION)==0) { return true; @@ -9720,9 +10256,6 @@ void ProxySQL_Admin::stats___mysql_query_rules() { } void ProxySQL_Admin::stats___proxysql_servers_checksums() { - statsdb->execute("BEGIN"); - statsdb->execute("DELETE FROM stats_proxysql_servers_checksums"); - SQLite3_result *resultset=NULL; // NOTE: This mutex unlock is required due to a race condition created when: // - One Admin session has the following callstack: // + admin_session_handler -> locks on 'sql_query_global_mutex' @@ -9737,8 +10270,10 @@ void ProxySQL_Admin::stats___proxysql_servers_checksums() { // + ProxySQL_Cluster::pull_mysql_query_rules_from_peer -> tries to lock on 'sql_query_global_mutex' // Producing a deadlock scenario between the two threads. pthread_mutex_unlock(&this->sql_query_global_mutex); - resultset=GloProxyCluster->get_stats_proxysql_servers_checksums(); + SQLite3_result* resultset = GloProxyCluster->get_stats_proxysql_servers_checksums(); pthread_mutex_lock(&this->sql_query_global_mutex); + statsdb->execute("BEGIN"); + statsdb->execute("DELETE FROM stats_proxysql_servers_checksums"); if (resultset) { int rc; sqlite3_stmt *statement1=NULL; @@ -11634,6 +12169,14 @@ void ProxySQL_Admin::dump_checksums_values_table() { rc=(*proxy_sqlite3_clear_bindings)(statement1); ASSERT_SQLITE_OK(rc, admindb); rc=(*proxy_sqlite3_reset)(statement1); ASSERT_SQLITE_OK(rc, admindb); + rc = (*proxy_sqlite3_bind_text)(statement1, 1, "mysql_servers_v2", -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, admindb); + rc = (*proxy_sqlite3_bind_int64)(statement1, 2, GloVars.checksums_values.mysql_servers_v2.version); ASSERT_SQLITE_OK(rc, admindb); + rc = (*proxy_sqlite3_bind_int64)(statement1, 3, GloVars.checksums_values.mysql_servers_v2.epoch); ASSERT_SQLITE_OK(rc, admindb); + rc = (*proxy_sqlite3_bind_text)(statement1, 4, GloVars.checksums_values.mysql_servers_v2.checksum, -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, admindb); + SAFE_SQLITE3_STEP2(statement1); + rc = (*proxy_sqlite3_clear_bindings)(statement1); ASSERT_SQLITE_OK(rc, admindb); + rc = (*proxy_sqlite3_reset)(statement1); ASSERT_SQLITE_OK(rc, admindb); + if (GloMyLdapAuth) { rc=(*proxy_sqlite3_bind_text)(statement1, 1, "ldap_variables", -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, admindb); rc=(*proxy_sqlite3_bind_int64)(statement1, 2, GloVars.checksums_values.ldap_variables.version); ASSERT_SQLITE_OK(rc, admindb); @@ -12436,9 +12979,8 @@ void ProxySQL_Admin::load_scheduler_to_runtime() { resultset=NULL; } -void ProxySQL_Admin::load_mysql_servers_to_runtime( - const incoming_servers_t& incoming_servers, const std::string& checksum, const time_t epoch -) { +void ProxySQL_Admin::load_mysql_servers_to_runtime(const incoming_servers_t& incoming_servers, + const runtime_mysql_servers_checksum_t& peer_runtime_mysql_server, const mysql_servers_v2_checksum_t& peer_mysql_server_v2) { // make sure that the caller has called mysql_servers_wrlock() char *error=NULL; int cols=0; @@ -12457,11 +12999,12 @@ void ProxySQL_Admin::load_mysql_servers_to_runtime( SQLite3_result* incoming_galera_hostgroups = incoming_servers.incoming_galera_hostgroups; SQLite3_result* incoming_aurora_hostgroups = incoming_servers.incoming_aurora_hostgroups; SQLite3_result* incoming_hostgroup_attributes = incoming_servers.incoming_hostgroup_attributes; + SQLite3_result* incoming_mysql_servers_v2 = incoming_servers.incoming_mysql_servers_v2; - char *query=(char *)"SELECT hostgroup_id,hostname,port,gtid_port,status,weight,compression,max_connections,max_replication_lag,use_ssl,max_latency_ms,comment FROM main.mysql_servers ORDER BY hostgroup_id, hostname, port"; - proxy_debug(PROXY_DEBUG_ADMIN, 4, "%s\n", query); + const char *query=(char *)"SELECT hostgroup_id,hostname,port,gtid_port,status,weight,compression,max_connections,max_replication_lag,use_ssl,max_latency_ms,comment FROM main.mysql_servers ORDER BY hostgroup_id, hostname, port"; if (runtime_mysql_servers == nullptr) { - admindb->execute_statement(query, &error , &cols , &affected_rows , &resultset_servers); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "%s\n", query); + admindb->execute_statement(query, &error, &cols, &affected_rows, &resultset_servers); } else { resultset_servers = runtime_mysql_servers; } @@ -12625,7 +13168,11 @@ void ProxySQL_Admin::load_mysql_servers_to_runtime( } // commit all the changes - MyHGM->commit(runtime_mysql_servers, checksum, epoch); + MyHGM->commit( + { runtime_mysql_servers, peer_runtime_mysql_server }, + { incoming_mysql_servers_v2, peer_mysql_server_v2 }, + false, true + ); // quering runtime table will update and return latest records, so this is not needed. // GloAdmin->save_mysql_servers_runtime_to_database(true); @@ -14055,17 +14602,13 @@ void ProxySQL_Admin::enable_galera_testing() { } #endif // TEST_GALERA #ifdef TEST_AURORA -void ProxySQL_Admin::enable_aurora_testing() { - proxy_info("Admin is enabling AWS Aurora Testing using SQLite3 Server and HGs from 1271 to 1276\n"); + +void ProxySQL_Admin::enable_aurora_testing_populate_mysql_servers() { sqlite3_stmt *statement=NULL; - //sqlite3 *mydb3=admindb->get_db(); unsigned int num_aurora_servers = GloSQLite3Server->num_aurora_servers[0]; - int rc; - mysql_servers_wrlock(); admindb->execute("DELETE FROM mysql_servers WHERE hostgroup_id BETWEEN 1271 AND 1276"); char *query=(char *)"INSERT INTO mysql_servers (hostgroup_id,hostname,use_ssl,comment) VALUES (?1, ?2, ?3, ?4)"; - //rc=(*proxy_sqlite3_prepare_v2)(mydb3, query, -1, &statement, 0); - rc = admindb->prepare_v2(query, &statement); + int rc = admindb->prepare_v2(query, &statement); ASSERT_SQLITE_OK(rc, admindb); for (unsigned int j=1; j<4; j++) { proxy_info("Admin is enabling AWS Aurora Testing using SQLite3 Server and HGs 127%d and 127%d\n" , j*2-1 , j*2); @@ -14079,7 +14622,6 @@ void ProxySQL_Admin::enable_aurora_testing() { } else { if (j==3) { serverid = "host.1." + std::to_string(i+11) + ".aws-test.com"; - //serverid = "host." + std::to_string(j) + "." + std::to_string(i+11) + ".aws.3.test.com"; } } } @@ -14095,24 +14637,35 @@ void ProxySQL_Admin::enable_aurora_testing() { } } (*proxy_sqlite3_finalize)(statement); +} + +void ProxySQL_Admin::enable_aurora_testing_populate_mysql_aurora_hostgroups() { +#ifndef TEST_AURORA_RANDOM + admindb->execute("INSERT INTO mysql_aws_aurora_hostgroups (writer_hostgroup, reader_hostgroup, active, 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 (1271, 1272, 1, '.aws-test.com', 25, 1000, 90, 1, 1, 10, 20, 5, 'Automated Aurora Testing Cluster 1')"); + admindb->execute("INSERT INTO mysql_aws_aurora_hostgroups (writer_hostgroup, reader_hostgroup, active, 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 (1273, 1274, 1, '.cluster2.aws.test', 25, 1000, 90, 0, 1, 10, 20, 5, 'Automated Aurora Testing Cluster 2')"); + admindb->execute("INSERT INTO mysql_aws_aurora_hostgroups (writer_hostgroup, reader_hostgroup, active, 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 (1275, 1276, 1, '.aws-test.com', 25, 1000, 90, 0, 2, 10, 20, 5, 'Automated Aurora Testing Cluster 3')"); +#else admindb->execute("INSERT INTO mysql_aws_aurora_hostgroups (writer_hostgroup, reader_hostgroup, active, 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 (1271, 1272, 1, '.aws-test.com', 25, 120, 90, 1, 1, 10, 20, 5, 'Automated Aurora Testing Cluster 1')"); admindb->execute("INSERT INTO mysql_aws_aurora_hostgroups (writer_hostgroup, reader_hostgroup, active, 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 (1273, 1274, 1, '.cluster2.aws.test', 25, 120, 90, 0, 1, 10, 20, 5, 'Automated Aurora Testing Cluster 2')"); admindb->execute("INSERT INTO mysql_aws_aurora_hostgroups (writer_hostgroup, reader_hostgroup, active, 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 (1275, 1276, 1, '.aws-test.com', 25, 120, 90, 0, 2, 10, 20, 5, 'Automated Aurora Testing Cluster 3')"); - //admindb->execute("INSERT INTO mysql_aws_aurora_hostgroups (writer_hostgroup, reader_hostgroup, active, 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 (1275, 1276, 1, '.aws.3.test.com', 25, 120, 90, 0, 2, 10, 20, 5, 'Automated Aurora Testing Cluster 3')"); +#endif admindb->execute("UPDATE mysql_aws_aurora_hostgroups SET active=1"); - //admindb->execute("update mysql_servers set max_replication_lag=20"); +} + +void ProxySQL_Admin::enable_aurora_testing() { + proxy_info("Admin is enabling AWS Aurora Testing using SQLite3 Server and HGs from 1271 to 1276\n"); + mysql_servers_wrlock(); + enable_aurora_testing_populate_mysql_servers(); + enable_aurora_testing_populate_mysql_aurora_hostgroups(); load_mysql_servers_to_runtime(); mysql_servers_wrunlock(); - //admindb->execute("UPDATE global_variables SET variable_value=3000 WHERE variable_name='mysql-monitor_ping_interval'"); - //admindb->execute("UPDATE global_variables SET variable_value=1500 WHERE variable_name='mysql-monitor_ping_timeout'"); - //admindb->execute("UPDATE global_variables SET variable_value=3000 WHERE variable_name='mysql-monitor_replication_lag_interval'"); - //admindb->execute("UPDATE global_variables SET variable_value=1500 WHERE variable_name='mysql-monitor_replication_lag_timeout'"); - admindb->execute("UPDATE global_variables SET variable_value=200 WHERE variable_name='mysql-monitor_ping_interval'"); + admindb->execute("UPDATE global_variables SET variable_value=1000 WHERE variable_name='mysql-monitor_ping_interval'"); admindb->execute("UPDATE global_variables SET variable_value=3000 WHERE variable_name='mysql-monitor_ping_timeout'"); - admindb->execute("UPDATE global_variables SET variable_value=200 WHERE variable_name='mysql-monitor_replication_lag_interval'"); + admindb->execute("UPDATE global_variables SET variable_value=1000 WHERE variable_name='mysql-monitor_replication_lag_interval'"); admindb->execute("UPDATE global_variables SET variable_value=3000 WHERE variable_name='mysql-monitor_replication_lag_timeout'"); admindb->execute("UPDATE global_variables SET variable_value='percona.heartbeat' WHERE variable_name='mysql-monitor_replication_lag_use_percona_heartbeat'"); load_mysql_variables_to_runtime(); + admindb->execute("DELETE FROM mysql_users WHERE username LIKE '%aurora%'"); admindb->execute("INSERT INTO mysql_users (username,password,default_hostgroup) VALUES ('aurora1','pass1',1271), ('aurora2','pass2',1273), ('aurora3','pass3',1275)"); init_users(); admindb->execute("INSERT INTO mysql_query_rules (active, username, match_pattern, destination_hostgroup, apply) VALUES (1, 'aurora1', '^SELECT.*max_lag_ms', 1272, 1)"); diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index b60088f105..d06ee5114a 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -57,6 +57,8 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { mysql_thread_init(); pthread_detach(pthread_self()); + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Thread started for peer %s:%d\n", node->hostname, node->port); + proxy_info("Cluster: starting thread for peer %s:%d\n", node->hostname, node->port); char *query1 = (char *)"SELECT GLOBAL_CHECKSUM()"; // in future this will be used for "light check" char *query2 = (char *)"SELECT * FROM stats_mysql_global ORDER BY Variable_Name"; @@ -98,6 +100,7 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); } //rc_conn = mysql_real_connect(conn, node->hostname, username, password, NULL, node->port, NULL, CLIENT_COMPRESS); // FIXME: add optional support for compression + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Connecting to peer %s:%d\n", node->hostname, node->port); rc_conn = mysql_real_connect(conn, node->get_host_address(), username, password, NULL, node->port, NULL, 0); // if (rc_conn) { // } @@ -116,6 +119,7 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { if (row[0]) { const char* PROXYSQL_VERSION_ = GloMyLdapAuth == nullptr ? PROXYSQL_VERSION : PROXYSQL_VERSION"-Enterprise"; if (strcmp(row[0], PROXYSQL_VERSION_)==0) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Clustering with peer %s:%d . Remote version: %s . Self version: %s\n", node->hostname, node->port, row[0], PROXYSQL_VERSION_); proxy_info("Cluster: clustering with peer %s:%d . Remote version: %s . Self version: %s\n", node->hostname, node->port, row[0], PROXYSQL_VERSION_); same_version = true; std::string q = "PROXYSQL CLUSTER_NODE_UUID "; @@ -124,6 +128,7 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { pthread_mutex_lock(&GloProxyCluster->admin_mysql_ifaces_mutex); q += GloProxyCluster->admin_mysql_ifaces; pthread_mutex_unlock(&GloProxyCluster->admin_mysql_ifaces_mutex); + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Sending CLUSTER_NODE_UUID %s to peer %s:%d\n", GloVars.uuid, node->hostname, node->port); proxy_info("Cluster: sending CLUSTER_NODE_UUID %s to peer %s:%d\n", GloVars.uuid, node->hostname, node->port); rc_query = mysql_query(conn, q.c_str()); } else { @@ -133,6 +138,7 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { } mysql_free_result(result); if (same_version == false) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Remote peer %s:%d proxysql version is different. Closing connection\n", node->hostname, node->port); mysql_close(conn); conn = mysql_init(NULL); int exit_after_N_seconds = 30; // hardcoded sleep time @@ -338,8 +344,7 @@ ProxySQL_Node_Entry::ProxySQL_Node_Entry(char* _hostname, uint16_t _port, uint64 weight = _weight; if (_comment == NULL) { comment = strdup((char*)""); - } - else { + } else { comment = strdup(_comment); } @@ -421,13 +426,13 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { time_t now = time(NULL); // Fetch the cluster_*_diffs_before_sync variables to ensure consistency at local scope + unsigned int diff_av = (unsigned int)__sync_fetch_and_add(&GloProxyCluster->cluster_admin_variables_diffs_before_sync,0); unsigned int diff_mqr = (unsigned int)__sync_fetch_and_add(&GloProxyCluster->cluster_mysql_query_rules_diffs_before_sync,0); unsigned int diff_ms = (unsigned int)__sync_fetch_and_add(&GloProxyCluster->cluster_mysql_servers_diffs_before_sync,0); unsigned int diff_mu = (unsigned int)__sync_fetch_and_add(&GloProxyCluster->cluster_mysql_users_diffs_before_sync,0); unsigned int diff_ps = (unsigned int)__sync_fetch_and_add(&GloProxyCluster->cluster_proxysql_servers_diffs_before_sync,0); unsigned int diff_mv = (unsigned int)__sync_fetch_and_add(&GloProxyCluster->cluster_mysql_variables_diffs_before_sync,0); unsigned int diff_lv = (unsigned int)__sync_fetch_and_add(&GloProxyCluster->cluster_ldap_variables_diffs_before_sync,0); - unsigned int diff_av = (unsigned int)__sync_fetch_and_add(&GloProxyCluster->cluster_admin_variables_diffs_before_sync,0); pthread_mutex_lock(&GloVars.checksum_mutex); @@ -463,9 +468,12 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { } } else { checksums_values.admin_variables.diff_check++; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Checksum for admin_variables from peer %s:%d, version %llu, epoch %llu, checksum %s is different from local checksum %s. Incremented diff_check %d ...\n", hostname, port, checksums_values.admin_variables.version, checksums_values.admin_variables.epoch, + checksums_values.admin_variables.checksum, GloVars.checksums_values.admin_variables.checksum, checksums_values.admin_variables.diff_check); } if (strcmp(checksums_values.admin_variables.checksum, GloVars.checksums_values.admin_variables.checksum) == 0) { checksums_values.admin_variables.diff_check = 0; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Checksum for admin_variables from peer %s:%d matches with local checksum %s, reset diff_check to 0.\n", hostname, port, GloVars.checksums_values.admin_variables.checksum); } continue; } @@ -500,9 +508,12 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { } } else { checksums_values.mysql_query_rules.diff_check++; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Checksum for mysql_query_rules from peer %s:%d, version %llu, epoch %llu, checksum %s is different from local checksum %s. Incremented diff_check %d ...\n", hostname, port, checksums_values.mysql_query_rules.version, checksums_values.mysql_query_rules.epoch, + checksums_values.mysql_query_rules.checksum, GloVars.checksums_values.mysql_query_rules.checksum, checksums_values.mysql_query_rules.diff_check); } if (strcmp(checksums_values.mysql_query_rules.checksum, GloVars.checksums_values.mysql_query_rules.checksum) == 0) { checksums_values.mysql_query_rules.diff_check = 0; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Checksum for mysql_query_rules from peer %s:%d matches with local checksum %s, reset diff_check to 0.\n", hostname, port, GloVars.checksums_values.mysql_query_rules.checksum); } continue; } @@ -537,6 +548,8 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { } } else { checksums_values.mysql_servers.diff_check++; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Checksum for mysql_servers from peer %s:%d, version %llu, epoch %llu, checksum %s is different from local checksum %s. Incremented diff_check %d ...\n", hostname, port, checksums_values.mysql_servers.version, checksums_values.mysql_servers.epoch, + checksums_values.mysql_servers.checksum, GloVars.checksums_values.mysql_servers.checksum, checksums_values.mysql_servers.diff_check); } if (strcmp(checksums_values.mysql_servers.checksum, GloVars.checksums_values.mysql_servers.checksum) == 0) { // See LOGGING-NOTE at 'admin_variables' above. @@ -547,6 +560,54 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { ); } checksums_values.mysql_servers.diff_check = 0; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Checksum for mysql_servers from peer %s:%d matches with local checksum %s, reset diff_check to 0.\n", hostname, port, GloVars.checksums_values.mysql_servers.checksum); + } + continue; + } + if (strcmp(row[0], "mysql_servers_v2")==0) { + ProxySQL_Checksum_Value_2& checksum = checksums_values.mysql_servers_v2; + ProxySQL_Checksum_Value& global_checksum = GloVars.checksums_values.mysql_servers_v2; + checksums_values.mysql_servers_v2.version = atoll(row[1]); + checksums_values.mysql_servers_v2.epoch = atoll(row[2]); + checksums_values.mysql_servers_v2.last_updated = now; + if (strcmp(checksums_values.mysql_servers_v2.checksum, row[3])) { + strcpy(checksums_values.mysql_servers_v2.checksum, row[3]); + checksums_values.mysql_servers_v2.last_changed = now; + checksums_values.mysql_servers_v2.diff_check = 1; + const char* no_sync_message = NULL; + + if (diff_ms) { + no_sync_message = "Not syncing yet ...\n"; + } else { + no_sync_message = "Not syncing due to 'admin-cluster_mysql_servers_diffs_before_sync=0'.\n"; + } + + proxy_info( + "Cluster: detected a new checksum for %s from peer %s:%d, version %llu, epoch %llu, checksum %s . %s", + row[0], hostname, port, checksum.version, checksum.epoch, checksum.checksum, no_sync_message + ); + + if (strcmp(checksum.checksum, global_checksum.checksum) == 0) { + proxy_info( + "Cluster: checksum for %s from peer %s:%d matches with local checksum %s , we won't sync.\n", + row[0], hostname, port, global_checksum.checksum + ); + } + } else { + checksums_values.mysql_servers_v2.diff_check++; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Checksum for mysql_servers_v2 from peer %s:%d, version %llu, epoch %llu, checksum %s is different from local checksum %s. Incremented diff_check %d ...\n", hostname, port, checksums_values.mysql_servers_v2.version, checksums_values.mysql_servers_v2.epoch, + checksums_values.mysql_servers_v2.checksum, GloVars.checksums_values.mysql_servers_v2.checksum, checksums_values.mysql_servers_v2.diff_check); + } + if (strcmp(checksums_values.mysql_servers_v2.checksum, GloVars.checksums_values.mysql_servers_v2.checksum) == 0) { + // See LOGGING-NOTE at 'admin_variables' above. + if (checksums_values.mysql_servers_v2.last_changed == now) { + proxy_info( + "Cluster: checksum for mysql_servers_v2 from peer %s:%d matches with local checksum %s , we won't sync.\n", + hostname, port, GloVars.checksums_values.mysql_servers_v2.checksum + ); + } + checksums_values.mysql_servers_v2.diff_check = 0; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Checksum for mysql_servers_v2 from peer %s:%d matches with local checksum %s, reset diff_check to 0.\n", hostname, port, GloVars.checksums_values.mysql_servers.checksum); } continue; } @@ -581,9 +642,12 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { } } else { checksums_values.mysql_users.diff_check++; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Checksum for mysql_users from peer %s:%d, version %llu, epoch %llu, checksum %s is different from local checksum %s. Incremented diff_check %d ...\n", hostname, port, checksums_values.mysql_users.version, checksums_values.mysql_users.epoch, + checksums_values.mysql_users.checksum, GloVars.checksums_values.mysql_users.checksum, checksums_values.mysql_users.diff_check); } if (strcmp(checksums_values.mysql_users.checksum, GloVars.checksums_values.mysql_users.checksum) == 0) { checksums_values.mysql_users.diff_check = 0; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Checksum for mysql_users from peer %s:%d matches with local checksum %s, reset diff_check to 0.\n", hostname, port, GloVars.checksums_values.mysql_users.checksum); } continue; } @@ -618,9 +682,12 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { } } else { checksums_values.mysql_variables.diff_check++; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Checksum for mysql_variables from peer %s:%d, version %llu, epoch %llu, checksum %s is different from local checksum %s. Incremented diff_check %d ...\n", hostname, port, checksums_values.mysql_variables.version, checksums_values.mysql_variables.epoch, + checksums_values.mysql_variables.checksum, GloVars.checksums_values.mysql_variables.checksum, checksums_values.mysql_variables.diff_check); } if (strcmp(checksums_values.mysql_variables.checksum, GloVars.checksums_values.mysql_variables.checksum) == 0) { checksums_values.mysql_variables.diff_check = 0; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Checksum for mysql_variables from peer %s:%d matches with local checksum %s, reset diff_check to 0.\n", hostname, port, GloVars.checksums_values.mysql_variables.checksum); } continue; } @@ -655,9 +722,12 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { } } else { checksums_values.proxysql_servers.diff_check++; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Checksum for proxysql_servers from peer %s:%d, version %llu, epoch %llu, checksum %s is different from local checksum %s. Incremented diff_check %d ...\n", hostname, port, checksums_values.proxysql_servers.version, checksums_values.proxysql_servers.epoch, + checksums_values.proxysql_servers.checksum, GloVars.checksums_values.proxysql_servers.checksum, checksums_values.proxysql_servers.diff_check); } if (strcmp(checksums_values.proxysql_servers.checksum, GloVars.checksums_values.proxysql_servers.checksum) == 0) { checksums_values.proxysql_servers.diff_check = 0; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Checksum for proxysql_servers from peer %s:%d matches with local checksum %s, reset diff_check to 0.\n", hostname, port, GloVars.checksums_values.proxysql_servers.checksum); } continue; } @@ -692,9 +762,12 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { } } else { checksums_values.ldap_variables.diff_check++; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Checksum for ldap_variables from peer %s:%d, version %llu, epoch %llu, checksum %s is different from local checksum %s. Incremented diff_check %d ...\n", hostname, port, checksums_values.ldap_variables.version, checksums_values.ldap_variables.epoch, + checksums_values.ldap_variables.checksum, GloVars.checksums_values.ldap_variables.checksum, checksums_values.ldap_variables.diff_check); } if (strcmp(checksums_values.ldap_variables.checksum, GloVars.checksums_values.ldap_variables.checksum) == 0) { checksums_values.ldap_variables.diff_check = 0; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Checksum for ldap_variables from peer %s:%d matches with local checksum %s, reset diff_check to 0.\n", hostname, port, GloVars.checksums_values.ldap_variables.checksum); } continue; } @@ -720,6 +793,13 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { if (strcmp(v->checksum, GloVars.checksums_values.mysql_servers.checksum) == 0) { v->diff_check = 0; } + if (v->diff_check) + v->diff_check++; + v = &checksums_values.mysql_servers_v2; + v->last_updated = now; + if (strcmp(v->checksum, GloVars.checksums_values.mysql_servers_v2.checksum) == 0) { + v->diff_check = 0; + } if (v->diff_check) v->diff_check++; v = &checksums_values.mysql_users; @@ -756,6 +836,38 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { // note that this is done outside the critical section // as mutex on GloVars.checksum_mutex is already released ProxySQL_Checksum_Value_2 *v = NULL; + if (diff_av) { + v = &checksums_values.admin_variables; + unsigned long long own_version = __sync_fetch_and_add(&GloVars.checksums_values.admin_variables.version, 0); + unsigned long long own_epoch = __sync_fetch_and_add(&GloVars.checksums_values.admin_variables.epoch, 0); + char* own_checksum = __sync_fetch_and_add(&GloVars.checksums_values.admin_variables.checksum, 0); + const string expected_checksum { v->checksum }; + + if (v->version > 1) { + if ( + (own_version == 1) // we just booted + || + (v->epoch > own_epoch) // epoch is newer + ) { + if (v->diff_check >= diff_av) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with admin_variables version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); + proxy_info("Cluster: detected a peer %s:%d with admin_variables version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); + GloProxyCluster->pull_global_variables_from_peer("admin", expected_checksum, v->epoch); + } + } + if ((v->epoch == own_epoch) && v->diff_check && ((v->diff_check % (diff_av*10)) == 0)) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with admin_variables version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD ADMIN VARIABLES TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_av * 10)); + proxy_error("Cluster: detected a peer %s:%d with admin_variables version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD ADMIN VARIABLES TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_av*10)); + GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_conflict_admin_variables_share_epoch]->Increment(); + } + } else { + if (v->diff_check && (v->diff_check % (diff_av*10)) == 0) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with admin_variables version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD ADMIN VARIABLES TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_av * 10)); + proxy_warning("Cluster: detected a peer %s:%d with admin_variables version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD ADMIN VARIABLES TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_av*10)); + GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_delayed_admin_variables_version_one]->Increment(); + } + } + } if (diff_mqr) { unsigned long long own_version = __sync_fetch_and_add(&GloVars.checksums_values.mysql_query_rules.version,0); unsigned long long own_epoch = __sync_fetch_and_add(&GloVars.checksums_values.mysql_query_rules.epoch,0); @@ -770,27 +882,37 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { (v->epoch > own_epoch) // epoch is newer ) { if (v->diff_check >= diff_mqr) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_query_rules version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); proxy_info("Cluster: detected a peer %s:%d with mysql_query_rules version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); GloProxyCluster->pull_mysql_query_rules_from_peer(v_exp_checksum, v->epoch); } } if ((v->epoch == own_epoch) && v->diff_check && ((v->diff_check % (diff_mqr*10)) == 0)) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_query_rules version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_mqr * 10)); proxy_error("Cluster: detected a peer %s:%d with mysql_query_rules version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_mqr*10)); GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_conflict_mysql_query_rules_share_epoch]->Increment(); } } else { if (v->diff_check && (v->diff_check % (diff_mqr*10)) == 0) { - proxy_warning("Cluster: detected a peer %s:%d with mysql_query_rules version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD MYSQL QUERY RULES TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_mqr*10)); + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected a peer %s:%d with mysql_query_rules version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD MYSQL QUERY RULES TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_mqr * 10)); + proxy_warning("Cluster: detected a peer %s:%d with mysql_query_rules version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD MYSQL QUERY RULES TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_mqr*10)); GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_delayed_mysql_query_rules_version_one]->Increment(); } } } if (diff_ms) { - v = &checksums_values.mysql_servers; - unsigned long long own_version = __sync_fetch_and_add(&GloVars.checksums_values.mysql_servers.version,0); - unsigned long long own_epoch = __sync_fetch_and_add(&GloVars.checksums_values.mysql_servers.epoch,0); - char* own_checksum = __sync_fetch_and_add(&GloVars.checksums_values.mysql_servers.checksum,0); - const std::string v_exp_checksum { v->checksum }; + mysql_servers_sync_algorithm mysql_server_sync_algo = (mysql_servers_sync_algorithm)__sync_fetch_and_add(&GloProxyCluster->cluster_mysql_servers_sync_algorithm, 0); + + if (mysql_server_sync_algo == mysql_servers_sync_algorithm::auto_select) { + mysql_server_sync_algo = (GloVars.global.monitor == false) ? + mysql_servers_sync_algorithm::runtime_mysql_servers_and_mysql_servers_v2 : mysql_servers_sync_algorithm::mysql_servers_v2; + } + + v = &checksums_values.mysql_servers_v2; + const unsigned long long own_version = __sync_fetch_and_add(&GloVars.checksums_values.mysql_servers_v2.version, 0); + const unsigned long long own_epoch = __sync_fetch_and_add(&GloVars.checksums_values.mysql_servers_v2.epoch, 0); + const char* own_checksum = __sync_fetch_and_add(&GloVars.checksums_values.mysql_servers_v2.checksum, 0); + bool runtime_mysql_servers_already_loaded = false; if (v->version > 1) { if ( @@ -799,20 +921,69 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { (v->epoch > own_epoch) // epoch is newer ) { if (v->diff_check >= diff_ms) { - proxy_info("Cluster: detected a peer %s:%d with mysql_servers version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); - GloProxyCluster->pull_mysql_servers_from_peer(v_exp_checksum, v->epoch); + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_servers_v2 version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); + proxy_info("Cluster: detected a peer %s:%d with mysql_servers_v2 version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); + + ProxySQL_Checksum_Value_2* runtime_mysql_server_checksum = &checksums_values.mysql_servers; + + const bool fetch_runtime = (mysql_server_sync_algo == mysql_servers_sync_algorithm::runtime_mysql_servers_and_mysql_servers_v2); + + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetch mysql_servers_v2:'YES', mysql_servers:'%s' from peer %s:%d\n", (fetch_runtime ? "YES" : "NO"), + hostname, port); + proxy_info("Cluster: Fetch mysql_servers_v2:'YES', mysql_servers:'%s' from peer %s:%d\n", (fetch_runtime ? "YES" : "NO"), + hostname, port); + + GloProxyCluster->pull_mysql_servers_v2_from_peer({ v->checksum, static_cast(v->epoch) }, + { runtime_mysql_server_checksum->checksum, static_cast(runtime_mysql_server_checksum->epoch) }, fetch_runtime); + + runtime_mysql_servers_already_loaded = fetch_runtime; } } - if ((v->epoch == own_epoch) && v->diff_check && ((v->diff_check % (diff_ms*10)) == 0)) { - proxy_error("Cluster: detected a peer %s:%d with mysql_servers version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_ms*10)); + if ((v->epoch == own_epoch) && v->diff_check && ((v->diff_check % (diff_ms * 10)) == 0)) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_servers_v2 version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_ms * 10)); + proxy_error("Cluster: detected a peer %s:%d with mysql_servers_v2 version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_ms * 10)); GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_conflict_mysql_servers_share_epoch]->Increment(); } } else { - if (v->diff_check && (v->diff_check % (diff_ms*10)) == 0) { - proxy_warning("Cluster: detected a peer %s:%d with mysql_servers version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_ms*10)); + if (v->diff_check && (v->diff_check % (diff_ms * 10)) == 0) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_servers version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_ms * 10)); + proxy_warning("Cluster: detected a peer %s:%d with mysql_servers version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_ms * 10)); GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_delayed_mysql_servers_version_one]->Increment(); } } + + if (mysql_server_sync_algo == mysql_servers_sync_algorithm::runtime_mysql_servers_and_mysql_servers_v2 && + runtime_mysql_servers_already_loaded == false) { + v = &checksums_values.mysql_servers; + unsigned long long own_version = __sync_fetch_and_add(&GloVars.checksums_values.mysql_servers.version, 0); + unsigned long long own_epoch = __sync_fetch_and_add(&GloVars.checksums_values.mysql_servers.epoch, 0); + char* own_checksum = __sync_fetch_and_add(&GloVars.checksums_values.mysql_servers.checksum, 0); + + if (v->version > 1) { + if ( + (own_version == 1) // we just booted + || + (v->epoch > own_epoch) // epoch is newer + ) { + if (v->diff_check >= diff_ms) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_servers version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); + proxy_info("Cluster: detected a peer %s:%d with mysql_servers version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); + GloProxyCluster->pull_runtime_mysql_servers_from_peer({ v->checksum, static_cast(v->epoch) }); + } + } + if ((v->epoch == own_epoch) && v->diff_check && ((v->diff_check % (diff_ms * 10)) == 0)) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_servers version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_ms * 10)); + proxy_error("Cluster: detected a peer %s:%d with mysql_servers version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_ms * 10)); + GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_conflict_mysql_servers_share_epoch]->Increment(); + } + } else { + if (v->diff_check && (v->diff_check % (diff_ms * 10)) == 0) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_servers version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_ms * 10)); + proxy_warning("Cluster: detected a peer %s:%d with mysql_servers version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_ms * 10)); + GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_delayed_mysql_servers_version_one]->Increment(); + } + } + } } if (diff_mu) { v = &checksums_values.mysql_users; @@ -828,17 +999,20 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { (v->epoch > own_epoch) // epoch is newer ) { if (v->diff_check >= diff_mu) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_users version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); proxy_info("Cluster: detected a peer %s:%d with mysql_users version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); GloProxyCluster->pull_mysql_users_from_peer(v_exp_checksum, v->epoch); } } if ((v->epoch == own_epoch) && v->diff_check && ((v->diff_check % (diff_mu*10)) == 0)) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_users version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_mu * 10)); proxy_error("Cluster: detected a peer %s:%d with mysql_users version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_mu*10)); GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_conflict_mysql_users_share_epoch]->Increment(); } } else { if (v->diff_check && (v->diff_check % (diff_mu*10)) == 0) { - proxy_warning("Cluster: detected a peer %s:%d with mysql_users version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD MYSQL USERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_mu*10)); + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected a peer %s:%d with mysql_users version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD MYSQL USERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_mu * 10)); + proxy_warning("Cluster: detected a peer %s:%d with mysql_users version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD MYSQL USERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_mu*10)); GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_delayed_mysql_users_version_one]->Increment(); } } @@ -857,50 +1031,24 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { (v->epoch > own_epoch) // epoch is newer ) { if (v->diff_check >= diff_mv) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_variables version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); proxy_info("Cluster: detected a peer %s:%d with mysql_variables version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); GloProxyCluster->pull_global_variables_from_peer("mysql", expected_checksum, v->epoch); } } if ((v->epoch == own_epoch) && v->diff_check && ((v->diff_check % (diff_mv*10)) == 0)) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_variables version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD MYSQL VARIABLES TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_mv * 10)); proxy_error("Cluster: detected a peer %s:%d with mysql_variables version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD MYSQL VARIABLES TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_mv*10)); GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_conflict_mysql_variables_share_epoch]->Increment(); } } else { if (v->diff_check && (v->diff_check % (diff_mv*10)) == 0) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_variables version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD MYSQL VARIABLES TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_mv * 10)); proxy_warning("Cluster: detected a peer %s:%d with mysql_variables version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD MYSQL VARIABLES TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_mv*10)); GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_delayed_mysql_variables_version_one]->Increment(); } } } - if (diff_av) { - v = &checksums_values.admin_variables; - unsigned long long own_version = __sync_fetch_and_add(&GloVars.checksums_values.admin_variables.version, 0); - unsigned long long own_epoch = __sync_fetch_and_add(&GloVars.checksums_values.admin_variables.epoch, 0); - char* own_checksum = __sync_fetch_and_add(&GloVars.checksums_values.admin_variables.checksum, 0); - const string expected_checksum { v->checksum }; - - if (v->version > 1) { - if ( - (own_version == 1) // we just booted - || - (v->epoch > own_epoch) // epoch is newer - ) { - if (v->diff_check >= diff_av) { - proxy_info("Cluster: detected a peer %s:%d with admin_variables version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); - GloProxyCluster->pull_global_variables_from_peer("admin", expected_checksum, v->epoch); - } - } - if ((v->epoch == own_epoch) && v->diff_check && ((v->diff_check % (diff_av*10)) == 0)) { - proxy_error("Cluster: detected a peer %s:%d with admin_variables version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD ADMIN VARIABLES TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_av*10)); - GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_conflict_admin_variables_share_epoch]->Increment(); - } - } else { - if (v->diff_check && (v->diff_check % (diff_av*10)) == 0) { - proxy_warning("Cluster: detected a peer %s:%d with admin_variables version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD ADMIN VARIABLES TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_av*10)); - GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_delayed_admin_variables_version_one]->Increment(); - } - } - } if (GloMyLdapAuth && diff_lv) { v = &checksums_values.ldap_variables; unsigned long long own_version = __sync_fetch_and_add(&GloVars.checksums_values.ldap_variables.version, 0); @@ -915,16 +1063,19 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { (v->epoch > own_epoch) // epoch is newer ) { if (v->diff_check >= diff_lv) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with ldap_variables version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); proxy_info("Cluster: detected a peer %s:%d with ldap_variables version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); GloProxyCluster->pull_global_variables_from_peer("ldap", expected_checksum, v->epoch); } } if ((v->epoch == own_epoch) && v->diff_check && ((v->diff_check % (diff_lv*10)) == 0)) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with ldap_variables version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD LDAP VARIABLES is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_lv * 10)); proxy_error("Cluster: detected a peer %s:%d with ldap_variables version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD LDAP VARIABLES is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_lv*10)); GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_conflict_ldap_variables_share_epoch]->Increment(); } } else { if (v->diff_check && (v->diff_check % (diff_lv*10)) == 0) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with ldap_variables version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD LDAP VARIABLES TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_lv * 10)); proxy_warning("Cluster: detected a peer %s:%d with ldap_variables version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD LDAP VARIABLES TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_lv*10)); GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_delayed_ldap_variables_version_one]->Increment(); } @@ -950,34 +1101,26 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { (v->epoch > own_epoch) // epoch is newer ) { if (v->diff_check >= diff_ps) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with proxysql_servers version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); proxy_info("Cluster: detected a peer %s:%d with proxysql_servers version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); GloProxyCluster->pull_proxysql_servers_from_peer(v_exp_checksum, v->epoch); } } if ((v_epoch == own_epoch) && v_diff_check && ((v_diff_check % (diff_ps*10)) == 0)) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with proxysql_servers version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v_version, v_epoch, v_diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_ps * 10)); proxy_error("Cluster: detected a peer %s:%d with proxysql_servers version %llu, epoch %llu, diff_check %u, checksum %s. Own version: %llu, epoch: %llu, checksum %s. Sync conflict, epoch times are EQUAL, can't determine which server holds the latest config, we won't sync. This message will be repeated every %u checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v_version, v_epoch, v_diff_check, v->checksum, own_version, own_epoch, own_checksum, (diff_ps*10)); GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_conflict_proxysql_servers_share_epoch]->Increment(); } } else { if (v->diff_check && (v->diff_check % (diff_ps*10)) == 0) { - proxy_warning("Cluster: detected a peer %s:%d with proxysql_servers version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD PROXYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_ps*10)); + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with proxysql_servers version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD PROXYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_ps * 10)); + proxy_warning("Cluster: detected a peer %s:%d with proxysql_servers version %llu, epoch %llu, diff_check %u. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %u checks until LOAD PROXYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_ps*10)); GloProxyCluster->metrics.p_counter_array[p_cluster_counter::sync_delayed_proxysql_servers_version_one]->Increment(); } } } } -std::string get_checksum_from_hash(uint64_t hash) { - uint32_t d32[2] = { 0 }; - memcpy(&d32, &hash, sizeof(hash)); - - vector s_buf(20, 0); - sprintf(&s_buf[0],"0x%0X%0X", d32[0], d32[1]); - replace_checksum_zeros(&s_buf[0]); - - return string { &s_buf.front() }; -} - /** * @brief Computes the checksum from a MySQL resultset in the same we already do in 'SQLite3_result::raw_checksum'. * @details For each received column computing the field length via 'strlen' is required, this is because we @@ -1044,6 +1187,7 @@ void ProxySQL_Cluster::pull_mysql_query_rules_from_peer(const string& expected_c unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); } + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Query Rules from peer %s:%d started. Expected checksum: %s\n", hostname, port, expected_checksum.c_str()); proxy_info("Cluster: Fetching MySQL Query Rules from peer %s:%d started. Expected checksum: %s\n", hostname, port, expected_checksum.c_str()); rc_conn = mysql_real_connect(conn, ip_address ? ip_address : hostname, username, password, NULL, port, NULL, 0); if (rc_conn) { @@ -1058,6 +1202,7 @@ void ProxySQL_Cluster::pull_mysql_query_rules_from_peer(const string& expected_c rc_query = mysql_query(conn,CLUSTER_QUERY_MYSQL_QUERY_RULES_FAST_ROUTING); if ( rc_query == 0) { result2 = mysql_store_result(conn); + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Query Rules from peer %s:%d completed\n", hostname, port); proxy_info("Cluster: Fetching MySQL Query Rules from peer %s:%d completed\n", hostname, port); std::unique_ptr SQLite3_query_rules_resultset { get_SQLite3_resulset(result1) }; @@ -1066,10 +1211,11 @@ void ProxySQL_Cluster::pull_mysql_query_rules_from_peer(const string& expected_c const uint64_t query_rules_hash = SQLite3_query_rules_resultset->raw_checksum() + SQLite3_query_rules_fast_routing_resultset->raw_checksum(); const string computed_checksum { get_checksum_from_hash(query_rules_hash) }; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Computed checksum for MySQL Query Rules from peer %s:%d : %s\n", hostname, port, computed_checksum.c_str()); proxy_info("Cluster: Computed checksum for MySQL Query Rules from peer %s:%d : %s\n", hostname, port, computed_checksum.c_str()); if (expected_checksum == computed_checksum) { - + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Loading to runtime MySQL Query Rules from peer %s:%d\n", hostname, port); proxy_info("Cluster: Loading to runtime MySQL Query Rules from peer %s:%d\n", hostname, port); pthread_mutex_lock(&GloAdmin->sql_query_global_mutex); //GloAdmin->admindb->execute("PRAGMA quick_check"); @@ -1168,21 +1314,27 @@ void ProxySQL_Cluster::pull_mysql_query_rules_from_peer(const string& expected_c } //GloAdmin->admindb->execute("PRAGMA integrity_check"); GloAdmin->admindb->execute("COMMIT"); + + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Loading MySQL Query Rules to Runtime from peer %s:%d\n", hostname, port); // We release the ownership of the memory for 'SQLite3' resultsets here since now it's no longer // our responsability to free the memory, they should be directly passed to the 'Query Processor' GloAdmin->load_mysql_query_rules_to_runtime( SQLite3_query_rules_resultset.release(), SQLite3_query_rules_fast_routing_resultset.release(), expected_checksum, epoch ); if (GloProxyCluster->cluster_mysql_query_rules_save_to_disk == true) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Saving to disk MySQL Query Rules from peer %s:%d\n", hostname, port); proxy_info("Cluster: Saving to disk MySQL Query Rules from peer %s:%d\n", hostname, port); GloAdmin->flush_GENERIC__from_to("mysql_query_rules", "memory_to_disk"); } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "NOT saving to disk MySQL Query Rules from peer %s:%d\n", hostname, port); proxy_info("Cluster: NOT saving to disk MySQL Query Rules from peer %s:%d\n", hostname, port); } pthread_mutex_unlock(&GloAdmin->sql_query_global_mutex); metrics.p_counter_array[p_cluster_counter::pulled_mysql_query_rules_success]->Increment(); } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Query Rules from peer %s:%d failed because of mismatching checksum. Expected: %s , Computed: %s\n", + hostname, port, expected_checksum.c_str(), computed_checksum.c_str()); proxy_info( "Cluster: Fetching MySQL Query Rules from peer %s:%d failed because of mismatching checksum. Expected: %s , Computed: %s\n", hostname, port, expected_checksum.c_str(), computed_checksum.c_str() @@ -1190,10 +1342,12 @@ void ProxySQL_Cluster::pull_mysql_query_rules_from_peer(const string& expected_c metrics.p_counter_array[p_cluster_counter::pulled_mysql_query_rules_failure]->Increment(); } } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Query Rules from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); proxy_info("Cluster: Fetching MySQL Query Rules from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); metrics.p_counter_array[p_cluster_counter::pulled_mysql_query_rules_failure]->Increment(); } } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Query Rules from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); proxy_info("Cluster: Fetching MySQL Query Rules from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); metrics.p_counter_array[p_cluster_counter::pulled_mysql_query_rules_failure]->Increment(); } @@ -1204,6 +1358,7 @@ void ProxySQL_Cluster::pull_mysql_query_rules_from_peer(const string& expected_c mysql_free_result(result2); } } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Query Rules from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); proxy_info("Cluster: Fetching MySQL Query Rules from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); metrics.p_counter_array[p_cluster_counter::pulled_mysql_query_rules_failure]->Increment(); } @@ -1318,14 +1473,17 @@ void ProxySQL_Cluster::pull_mysql_users_from_peer(const string& expected_checksu unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); } + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Users from peer %s:%d started. Expected checksum: %s\n", hostname, port, expected_checksum.c_str()); proxy_info("Cluster: Fetching MySQL Users from peer %s:%d started. Expected checksum: %s\n", hostname, port, expected_checksum.c_str()); rc_conn = mysql_real_connect(conn, ip_address ? ip_address : hostname, username, password, NULL, port, NULL, 0); if (rc_conn == nullptr) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Users from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); proxy_info("Cluster: Fetching MySQL Users from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); metrics.p_counter_array[p_cluster_counter::pulled_mysql_users_failure]->Increment(); if (GloMyLdapAuth) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching LDAP Mappings from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); proxy_info("Cluster: Fetching LDAP Mappings from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); metrics.p_counter_array[p_cluster_counter::pulled_mysql_ldap_mapping_failure]->Increment(); } @@ -1340,9 +1498,11 @@ void ProxySQL_Cluster::pull_mysql_users_from_peer(const string& expected_checksu MYSQL_RES* mysql_users_result = mysql_store_result(conn); MYSQL_RES* ldap_mapping_result = nullptr; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Users from peer %s:%d completed\n", hostname, port); proxy_info("Cluster: Fetching MySQL Users from peer %s:%d completed\n", hostname, port); if (GloMyLdapAuth) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching LDAP Mappings from peer %s:%d.\n", hostname, port); proxy_info("Cluster: Fetching LDAP Mappings from peer %s:%d.\n", hostname, port); rc_query = mysql_query( @@ -1351,8 +1511,10 @@ void ProxySQL_Cluster::pull_mysql_users_from_peer(const string& expected_checksu if (rc_query == 0) { ldap_mapping_result = mysql_store_result(conn); + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching LDAP Mappings from peer %s:%d completed\n", hostname, port); proxy_info("Cluster: Fetching LDAP Mappings from peer %s:%d completed\n", hostname, port); } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching LDAP Mappings from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); proxy_info("Cluster: Fetching LDAP Mappings from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); metrics.p_counter_array[p_cluster_counter::pulled_mysql_ldap_mapping_failure]->Increment(); } @@ -1362,6 +1524,7 @@ void ProxySQL_Cluster::pull_mysql_users_from_peer(const string& expected_checksu const uint64_t users_raw_checksum = get_mysql_users_checksum(mysql_users_result, ldap_mapping_result, mysql_users_resultset); const string computed_checksum { get_checksum_from_hash(users_raw_checksum) }; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Computed checksum for MySQL Users from peer %s:%d : %s\n", hostname, port, computed_checksum.c_str()); proxy_info("Cluster: Computed checksum for MySQL Users from peer %s:%d : %s\n", hostname, port, computed_checksum.c_str()); if (expected_checksum == computed_checksum) { @@ -1373,22 +1536,28 @@ void ProxySQL_Cluster::pull_mysql_users_from_peer(const string& expected_checksu mysql_free_result(ldap_mapping_result); } + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Loading to runtime MySQL Users from peer %s:%d\n", hostname, port); proxy_info("Cluster: Loading to runtime MySQL Users from peer %s:%d\n", hostname, port); if (GloMyLdapAuth) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Loading to runtime LDAP Mappings from peer %s:%d\n", hostname, port); proxy_info("Cluster: Loading to runtime LDAP Mappings from peer %s:%d\n", hostname, port); } GloAdmin->init_users(std::move(mysql_users_resultset), expected_checksum, epoch); if (GloProxyCluster->cluster_mysql_users_save_to_disk == true) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Saving to disk MySQL Users from peer %s:%d\n", hostname, port); proxy_info("Cluster: Saving to disk MySQL Users from peer %s:%d\n", hostname, port); if (GloMyLdapAuth) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Saving to disk LDAP Mappings from peer %s:%d\n", hostname, port); proxy_info("Cluster: Saving to disk LDAP Mappings from peer %s:%d\n", hostname, port); } GloAdmin->flush_mysql_users__from_memory_to_disk(); } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "NOT saving to disk MySQL Users from peer %s:%d\n", hostname, port); proxy_info("Cluster: NOT saving to disk MySQL Users from peer %s:%d\n", hostname, port); if (GloMyLdapAuth) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "NOT Saving to disk LDAP Mappings from peer %s:%d\n", hostname, port); proxy_info("Cluster: NOT Saving to disk LDAP Mappings from peer %s:%d\n", hostname, port); } } @@ -1406,6 +1575,8 @@ void ProxySQL_Cluster::pull_mysql_users_from_peer(const string& expected_checksu mysql_free_result(ldap_mapping_result); } + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Users from peer %s:%d failed: Checksum changed from %s to %s\n", + hostname, port, expected_checksum.c_str(), computed_checksum.c_str()); proxy_info( "Cluster: Fetching MySQL Users from peer %s:%d failed: Checksum changed from %s to %s\n", hostname, port, expected_checksum.c_str(), computed_checksum.c_str() @@ -1417,10 +1588,12 @@ void ProxySQL_Cluster::pull_mysql_users_from_peer(const string& expected_checksu } } } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Users from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); proxy_info("Cluster: Fetching MySQL Users from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); metrics.p_counter_array[p_cluster_counter::pulled_mysql_users_failure]->Increment(); if (GloMyLdapAuth) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching LDAP Mappings from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); proxy_info("Cluster: Fetching LDAP Mappings from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); metrics.p_counter_array[p_cluster_counter::pulled_mysql_ldap_mapping_failure]->Increment(); } @@ -1497,68 +1670,10 @@ int ProxySQL_Cluster::fetch_and_store(MYSQL* conn, const fetch_query& f_query, M return query_res; } -/** - * @brief Generates a hash for the resulset received for the query 'CLUSTER_QUERY_MYSQL_SERVERS'. - * @details Remember that this query query is intercepted by 'ProxySQL_Admin' and always answered via - * 'MySQL_HostGroups_Manager::dump_table_proxysql_servers'. - * @param resultset The resulset resulting from the mentioned query. - * @return A hash representing the contents of the resulset. - */ -uint64_t mysql_servers_raw_checksum(MYSQL_RES* resultset) { - if (resultset == nullptr) { return 0; } - - uint64_t num_rows = mysql_num_rows(resultset); - if (num_rows == 0) { return 0; } - - MYSQL_FIELD* fields = mysql_fetch_fields(resultset); - uint32_t num_fields = mysql_num_fields(resultset); - uint32_t status_idx = 0; - - for (uint32_t i = 0; i < num_fields; i++) { - if (strcmp(fields[i].name, "status") == 0) { - status_idx = i; - } - } - - SpookyHash myhash {}; - myhash.Init(19,3); - - while (MYSQL_ROW row = mysql_fetch_row(resultset)) { - for (uint32_t i = 0; i < num_fields; i++) { - if (strcmp(row[status_idx], "OFFLINE_HARD") == 0) { - continue; - } - - if (row[i]) { - if (strcmp(fields[i].name, "status") == 0) { - if (strcmp(row[i], "ONLINE") == 0 || strcmp(row[i], "SHUNNED") == 0) { - myhash.Update("0", strlen("0")); - } else { - myhash.Update("2", strlen("1")); - } - } else { - // computing 'strlen' is required see @details - myhash.Update(row[i], strlen(row[i])); - } - } else { - myhash.Update("", 0); - } - } - } - - // restore the initial resulset index - mysql_data_seek(resultset, 0); - - uint64_t res_hash = 0, hash2 = 0; - myhash.Final(&res_hash, &hash2); - - return res_hash; -} - /** * @brief Generates a hash from the received resultsets from executing the following queries in the specified * order: - * - CLUSTER_QUERY_MYSQL_SERVERS. + * - CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS. * - CLUSTER_QUERY_MYSQL_REPLICATION_HOSTGROUPS. * - CLUSTER_QUERY_MYSQL_GROUP_REPLICATION_HOSTGROUPS. * - CLUSTER_QUERY_MYSQL_GALERA. @@ -1570,18 +1685,12 @@ uint64_t mysql_servers_raw_checksum(MYSQL_RES* resultset) { * @param results The resultsets from whose to compute the checksum. Previous described order is required. * @return Zero if the received resultset were empty, the computed hash otherwise. */ -uint64_t compute_servers_tables_raw_checksum(const vector& results) { +uint64_t compute_servers_tables_raw_checksum(const vector& results, size_t size) { bool init = false; SpookyHash myhash {}; - for (size_t i = 0; i < results.size(); i++) { - uint64_t raw_hash = 0; - - if (i == 0) { - raw_hash = mysql_servers_raw_checksum(results[i]); - } else { - raw_hash = mysql_raw_checksum(results[i]); - } + for (size_t i = 0; i < size; i++) { + uint64_t raw_hash = mysql_raw_checksum(results[i]); if (raw_hash != 0) { if (init == false) { @@ -1601,7 +1710,7 @@ uint64_t compute_servers_tables_raw_checksum(const vector& results) return servers_hash; } -incoming_servers_t convert_servers_resultsets(const std::vector& results) { +incoming_servers_t convert_mysql_servers_resultsets(const std::vector& results) { if (results.size() != sizeof(incoming_servers_t) / sizeof(void*)) { return incoming_servers_t {}; } else { @@ -1611,18 +1720,32 @@ incoming_servers_t convert_servers_resultsets(const std::vector& res get_SQLite3_resulset(results[2]).release(), get_SQLite3_resulset(results[3]).release(), get_SQLite3_resulset(results[4]).release(), - get_SQLite3_resulset(results[5]).release() + get_SQLite3_resulset(results[5]).release(), + get_SQLite3_resulset(results[6]).release(), }; } } -void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, const time_t epoch) { +/** + * @brief mysql_servers records will be fetched from remote peer and saved locally. + * + * @details This method involves fetching the mysql_servers records (also referred to as runtime_mysql_servers) from a remote peer + * and comparing their checksum to the remote peer's checksum. If the checksums match, the local mysql_servers (i.e., runtime_mysql_servers) + * will be updated and saved to disk, but only if the cluster_mysql_servers_save_to_disk variable is set to true. + * + * It's important to note that the runtime_mysql_servers module is distinct from the mysql_servers_v2 module. It has + * its own independent checksum (does not have dependent modules) and represents the current runtime state of the mysql_servers. + * + * @param peer_runtime_mysql_server checksum and epoch of mysql_servers from remote peer + */ +void ProxySQL_Cluster::pull_runtime_mysql_servers_from_peer(const runtime_mysql_servers_checksum_t& peer_runtime_mysql_server) { char * hostname = NULL; char * ip_address = NULL; uint16_t port = 0; char * peer_checksum = NULL; - pthread_mutex_lock(&GloProxyCluster->update_mysql_servers_mutex); - nodes.get_peer_to_sync_mysql_servers(&hostname, &port, &peer_checksum, &ip_address); + + pthread_mutex_lock(&GloProxyCluster->update_runtime_mysql_servers_mutex); + nodes.get_peer_to_sync_runtime_mysql_servers(&hostname, &port, &peer_checksum, &ip_address); if (hostname) { char *username = NULL; char *password = NULL; @@ -1634,6 +1757,157 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, goto __exit_pull_mysql_servers_from_peer; } GloProxyCluster->get_credentials(&username, &password); + if (strlen(username)) { // do not monitor if the username is empty + unsigned int timeout = 1; + mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + { + unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); + mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); + } + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching 'MySQL Servers' from peer %s:%d started. Expected checksum %s\n", hostname, port, peer_checksum); + proxy_info("Cluster: Fetching 'MySQL Servers' from peer %s:%d started. Expected checksum %s\n", hostname, port, peer_checksum); + rc_conn = mysql_real_connect(conn, ip_address ? ip_address : hostname, username, password, NULL, port, NULL, 0); + if (rc_conn) { + MySQL_Monitor::update_dns_cache_from_mysql_conn(conn); + + // servers messages + std::string fetch_servers_done; + string_format("Cluster: Fetching 'MySQL Servers' from peer %s:%d completed\n", fetch_servers_done, hostname, port); + std::string fetch_servers_err; + string_format("Cluster: Fetching 'MySQL Servers' from peer %s:%d failed: \n", fetch_servers_err, hostname, port); + + // Create fetching query + fetch_query query = { + CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS, + p_cluster_counter::pulled_mysql_servers_success, + p_cluster_counter::pulled_mysql_servers_failure, + { "", fetch_servers_done, fetch_servers_err } + }; + + MYSQL_RES* result = nullptr; + + if (fetch_and_store(conn, query, &result) != 0) { + if (result) { + mysql_free_result(result); + result = nullptr; + } + } + + if (result != nullptr) { + const uint64_t servers_hash = mysql_raw_checksum(result); + const string computed_checksum{ get_checksum_from_hash(servers_hash) }; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Computed checksum for MySQL Servers from peer %s:%d : %s\n", hostname, port, computed_checksum.c_str()); + proxy_info("Cluster: Computed checksum for MySQL Servers from peer %s:%d : %s\n", hostname, port, computed_checksum.c_str()); + + if (computed_checksum == peer_checksum) { + GloAdmin->mysql_servers_wrlock(); + std::unique_ptr runtime_mysql_servers_resultset = get_SQLite3_resulset(result); + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Loading runtime_mysql_servers from peer %s:%d into mysql_servers_incoming", hostname, port); + MyHGM->servers_add(runtime_mysql_servers_resultset.get()); + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Updating runtime_mysql_servers from peer %s:%d", hostname, port); + MyHGM->commit( + { runtime_mysql_servers_resultset.release(), peer_runtime_mysql_server }, + { nullptr, {} }, true, true + ); + + if (GloProxyCluster->cluster_mysql_servers_save_to_disk == true) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Saving Runtime MySQL Servers to Database\n"); + GloAdmin->save_mysql_servers_runtime_to_database(false); + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Saving to disk MySQL Servers v2 from peer %s:%d\n", hostname, port); + proxy_info("Cluster: Saving to disk MySQL Servers v2 from peer %s:%d\n", hostname, port); + GloAdmin->flush_GENERIC__from_to("mysql_servers", "memory_to_disk"); + } + GloAdmin->mysql_servers_wrunlock(); + + // free result + mysql_free_result(result); + + metrics.p_counter_array[p_cluster_counter::pulled_mysql_servers_success]->Increment(); + } + } + } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Servers from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); + proxy_info("Cluster: Fetching MySQL Servers from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); + metrics.p_counter_array[p_cluster_counter::pulled_mysql_servers_failure]->Increment(); + } + } + if (username) { + free(username); + } + if (password) { + free(password); + } +__exit_pull_mysql_servers_from_peer: + if (conn) { + if (conn->net.pvio) { + mysql_close(conn); + } + } + free(hostname); + + if (peer_checksum) + free(peer_checksum); + + if (ip_address) + free(ip_address); + } + pthread_mutex_unlock(&GloProxyCluster->update_runtime_mysql_servers_mutex); +} + +/** + * @brief mysql_servers_v2 records will be fetched from remote peer. mysql_servers records will be fetched if + * fetch_runtime_mysql_servers flag is true. + * + * @details The previous implementation of the "pull_mysql_servers_from_peer" method fetched data from "mysql_servers" (equivalent to runtime mysql_servers) + * and other dependent modules like "mysql_replication_hostgroups", "mysql_group_replication_hostgroups", "mysql_galera_hostgroups", + * "mysql_aws_aurora_hostgroups", and "mysql_hostgroup_attributes". It then computed an accumulated checksum and compares it with the + * peer checksum. If they matched, the configuration was loaded and saved to disk if "cluster_mysql_servers_save_to_disk" was set to true. + * + * The new implementation, "pull_mysql_servers_v2_from_peer", instead fetches data from "mysql_servers_v2" (equivalent to admin mysql_servers) + * and the same dependent modules. It then computes an accumulated checksum and compares it with the peer checksum. If they matched, the + * configuration was loaded and saved to disk if "cluster_mysql_servers_save_to_disk" was set to true. Additionally, if the "fetch_runtime_mysql_servers" + * option is enabled (if cluster_mysql_servers_sync_algorithm value is set to 1), the "mysql_servers" table will also be fetched and its checksum will be + * computed and matched with the peer checksum. If they match, the configuration will be loaded and saved to disk if the "cluster_mysql_servers_save_to_disk" + * option is true. + * + * Apart from separately fetching the runtime mysql_servers, the primary distinction between the previous and new implementations lies in the + * fetching of different tables (mysql_servers vs mysql_servers_v2) and computing of checksum. In the previous version, + * the checksum for "mysql_servers" was computed and added to the checksums of other dependent modules. In contrast, the new version + * calculates the checksum for "mysql_servers_v2" and combines it with the checksums of other dependent modules. + * + * IMPORTANT: This function performs both the fetching of config, and conditionally the 'runtime_mysql_servers', in + * order to avoid extra transitory states and checksums that would result if this operation was performed in multiple + * steps. When required by the sync algorithm ('mysql_servers_sync_algorithm'), these two fetches and configuration + * promotion should be performed in a single 'atomic' operation. + * + * @param peer_mysql_server_v2 checksum and epoch of mysql_servers_v2 from remote peer + * @param peer_runtime_mysql_server checksum and epoch of mysql_servers from remote peer + * @param fetch_runtime_mysql_servers fetch mysql_servers records if value is true + * + * NOTE: pull_mysql_servers_v2_from_peer will always be called irrespective of cluster_mysql_servers_sync_algorithm value. + */ +void ProxySQL_Cluster::pull_mysql_servers_v2_from_peer(const mysql_servers_v2_checksum_t& peer_mysql_server_v2, + const runtime_mysql_servers_checksum_t& peer_runtime_mysql_server, bool fetch_runtime_mysql_servers) { + char* hostname = NULL; + char* ip_address = NULL; + uint16_t port = 0; + char* peer_mysql_servers_v2_checksum = NULL; + char* peer_runtime_mysql_servers_checksum = NULL; + + pthread_mutex_lock(&GloProxyCluster->update_mysql_servers_v2_mutex); + nodes.get_peer_to_sync_mysql_servers_v2(&hostname, &port, &peer_mysql_servers_v2_checksum, + &peer_runtime_mysql_servers_checksum, &ip_address); + if (hostname) { + char* username = NULL; + char* password = NULL; + // bool rc_bool = true; + MYSQL* rc_conn; + MYSQL* conn = mysql_init(NULL); + if (conn == NULL) { + proxy_error("Unable to run mysql_init()\n"); + goto __exit_pull_mysql_servers_v2_from_peer; + } + GloProxyCluster->get_credentials(&username, &password); if (strlen(username)) { // do not monitor if the username is empty unsigned int timeout = 1; // unsigned int timeout_long = 60; @@ -1644,18 +1918,19 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); } - proxy_info("Cluster: Fetching MySQL Servers from peer %s:%d started. Expected checksum %s\n", hostname, port, peer_checksum); + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Servers v2 from peer %s:%d started. Expected checksum %s\n", hostname, port, peer_mysql_servers_v2_checksum); + proxy_info("Cluster: Fetching MySQL Servers v2 from peer %s:%d started. Expected checksum %s\n", hostname, port, peer_mysql_servers_v2_checksum); rc_conn = mysql_real_connect(conn, ip_address ? ip_address : hostname, username, password, NULL, port, NULL, 0); if (rc_conn) { MySQL_Monitor::update_dns_cache_from_mysql_conn(conn); - std::vector results {}; + std::vector results(7,nullptr); // servers messages std::string fetch_servers_done = ""; - string_format("Cluster: Fetching MySQL Servers from peer %s:%d completed\n", fetch_servers_done, hostname, port); + string_format("Cluster: Fetching 'MySQL Servers v2' from peer %s:%d completed\n", fetch_servers_done, hostname, port); std::string fetch_servers_err = ""; - string_format("Cluster: Fetching MySQL Servers from peer %s:%d failed: \n", fetch_servers_err, hostname, port); + string_format("Cluster: Fetching 'MySQL Servers v2' from peer %s:%d failed: \n", fetch_servers_err, hostname, port); // group_replication_hostgroups messages std::string fetch_group_replication_hostgroups = ""; @@ -1690,7 +1965,7 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, */ fetch_query queries[] = { { - CLUSTER_QUERY_MYSQL_SERVERS, + CLUSTER_QUERY_MYSQL_SERVERS_V2, p_cluster_counter::pulled_mysql_servers_success, p_cluster_counter::pulled_mysql_servers_failure, { "", fetch_servers_done, fetch_servers_err } @@ -1732,40 +2007,77 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, int it_err = fetch_and_store(conn, queries[i], &fetch_res); if (it_err == 0) { - results.push_back(fetch_res); + results[i] = fetch_res; } else { fetching_error = true; break; } } + // fetch_runtime_mysql_servers value depends on 'cluster_mysql_servers_sync_algorithm' + if (fetch_runtime_mysql_servers == true) { + // Fetching runtime mysql servers (mysql_servers) configuration from remote peer + std::string fetch_runtime_servers_done = ""; + string_format("Cluster: Fetching 'MySQL Servers' from peer %s:%d completed\n", fetch_runtime_servers_done, hostname, port); + std::string fetch_runtime_servers_err = ""; + string_format("Cluster: Fetching 'MySQL Servers' from peer %s:%d failed: \n", fetch_runtime_servers_err, hostname, port); + + // Query definition used to fetch data from a peer. + fetch_query query = { + CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS, + p_cluster_counter::pulled_mysql_servers_success, + p_cluster_counter::pulled_mysql_servers_failure, + { "", fetch_runtime_servers_done, fetch_runtime_servers_err } + }; + + MYSQL_RES* fetch_res = nullptr; + if (fetch_and_store(conn, query, &fetch_res) == 0) { + results[6] = fetch_res; + } else { + fetching_error = true; + } + } + if (fetching_error == false) { - const uint64_t servers_hash = compute_servers_tables_raw_checksum(results); - const string computed_checksum { get_checksum_from_hash(servers_hash) }; - proxy_info("Cluster: Computed checksum for MySQL Servers from peer %s:%d : %s\n", hostname, port, computed_checksum.c_str()); + const uint64_t servers_hash = compute_servers_tables_raw_checksum(results, 6); // ignore runtime_mysql_servers in checksum calculation + const string computed_checksum{ get_checksum_from_hash(servers_hash) }; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Computed checksum for MySQL Servers v2 from peer %s:%d : %s\n", hostname, port, computed_checksum.c_str()); + proxy_info("Cluster: Computed checksum for MySQL Servers v2 from peer %s:%d : %s\n", hostname, port, computed_checksum.c_str()); + + bool runtime_checksum_matches = true; + + if (results[6]) { + const uint64_t runtime_mysql_server_hash = mysql_raw_checksum(results[6]); + const std::string runtime_mysql_server_computed_checksum = get_checksum_from_hash(runtime_mysql_server_hash); + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Computed checksum for MySQL Servers from peer %s:%d : %s\n", hostname, port, runtime_mysql_server_computed_checksum.c_str()); + proxy_info("Cluster: Computed checksum for MySQL Servers from peer %s:%d : %s\n", hostname, port, runtime_mysql_server_computed_checksum.c_str()); + runtime_checksum_matches = (runtime_mysql_server_computed_checksum == peer_runtime_mysql_servers_checksum); + } - if (computed_checksum == peer_checksum) { + if (computed_checksum == peer_mysql_servers_v2_checksum && runtime_checksum_matches == true) { // No need to perform the conversion if checksums don't match - const incoming_servers_t incoming_servers { convert_servers_resultsets(results) }; + const incoming_servers_t incoming_servers{ convert_mysql_servers_resultsets(results) }; // we are OK to sync! + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching checksum for 'MySQL Servers' from peer %s:%d successful. Checksum: %s\n", hostname, port, computed_checksum.c_str()); proxy_info("Cluster: Fetching checksum for 'MySQL Servers' from peer %s:%d successful. Checksum: %s\n", hostname, port, computed_checksum.c_str()); // sync mysql_servers + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Writing mysql_servers table\n"); proxy_info("Cluster: Writing mysql_servers table\n"); GloAdmin->mysql_servers_wrlock(); GloAdmin->admindb->execute("DELETE FROM mysql_servers"); MYSQL_ROW row; - char *q=(char *)"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 (%s, \"%s\", %s, %s, \"%s\", %s, %s, %s, %s, %s, %s, '%s')"; + char* q = (char*)"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 (%s, \"%s\", %s, %s, \"%s\", %s, %s, %s, %s, %s, %s, '%s')"; while ((row = mysql_fetch_row(results[0]))) { int i; - int l=0; - for (i=0; i<11; i++) { - l+=strlen(row[i]); + int l = 0; + for (i = 0; i < 11; i++) { + l += strlen(row[i]); } - char *o=escape_string_single_quotes(row[11],false); - char *query = (char *)malloc(strlen(q)+i+strlen(o)+64); + char* o = escape_string_single_quotes(row[11], false); + char* query = (char*)malloc(strlen(q) + i + strlen(o) + 64); - sprintf(query,q,row[0],row[1],row[2],row[3], ( strcmp(row[4],"SHUNNED")==0 ? "ONLINE" : row[4] ), row[5], row[6],row[7],row[8],row[9],row[10],o); - if (o!=row[11]) { // there was a copy + sprintf(query, q, row[0], row[1], row[2], row[3], (strcmp(row[4], "SHUNNED") == 0 ? "ONLINE" : row[4]), row[5], row[6], row[7], row[8], row[9], row[10], o); + if (o != row[11]) { // there was a copy free(o); } GloAdmin->admindb->execute(query); @@ -1773,19 +2085,20 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, } // sync mysql_replication_hostgroups + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Writing mysql_replication_hostgroups table\n"); proxy_info("Cluster: Writing mysql_replication_hostgroups table\n"); GloAdmin->admindb->execute("DELETE FROM mysql_replication_hostgroups"); - q=(char *)"INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type, comment) VALUES (%s, %s, '%s', '%s')"; + q = (char*)"INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type, comment) VALUES (%s, %s, '%s', '%s')"; while ((row = mysql_fetch_row(results[1]))) { int i; - int l=0; - for (i=0; i<3; i++) { - l+=strlen(row[i]); + int l = 0; + for (i = 0; i < 3; i++) { + l += strlen(row[i]); } - char *o=escape_string_single_quotes(row[3],false); - char *query = (char *)malloc(strlen(q)+i+strlen(o)+64); - sprintf(query,q,row[0],row[1],row[2],o); - if (o!=row[3]) { // there was a copy + char* o = escape_string_single_quotes(row[3], false); + char* query = (char*)malloc(strlen(q) + i + strlen(o) + 64); + sprintf(query, q, row[0], row[1], row[2], o); + if (o != row[3]) { // there was a copy free(o); } GloAdmin->admindb->execute(query); @@ -1793,15 +2106,16 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, } // sync mysql_group_replication_hostgroups + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Writing mysql_group_replication_hostgroups table\n"); proxy_info("Cluster: Writing mysql_group_replication_hostgroups table\n"); GloAdmin->admindb->execute("DELETE FROM mysql_group_replication_hostgroups"); - q=(char*)"INSERT INTO mysql_group_replication_hostgroups ( " + q = (char*)"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) "; - char *error = NULL; + char* error = NULL; int cols = 0; int affected_rows = 0; - SQLite3_result *resultset = NULL; + SQLite3_result* resultset = NULL; while ((row = mysql_fetch_row(results[2]))) { int i; int l = 0; @@ -1815,7 +2129,7 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, if (row[8] != nullptr) { fqs += "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, '%s')"; o = escape_string_single_quotes(row[8], false); - query = (char *)malloc(strlen(fqs.c_str()) + i + strlen(o) + 64); + query = (char*)malloc(strlen(fqs.c_str()) + i + strlen(o) + 64); sprintf(query, fqs.c_str(), row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], o); // free in case of 'o' being a copy if (o != row[8]) { @@ -1825,22 +2139,24 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, // In case of comment being null, placeholder must not have '' fqs += "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)"; o = const_cast("NULL"); - query = (char *)malloc(strlen(fqs.c_str()) + strlen("NULL") + i + 64); + query = (char*)malloc(strlen(fqs.c_str()) + strlen("NULL") + i + 64); sprintf(query, fqs.c_str(), row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], o); } GloAdmin->admindb->execute(query); free(query); } + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Dumping fetched 'mysql_group_replication_hostgroups'\n"); proxy_info("Dumping fetched 'mysql_group_replication_hostgroups'\n"); - GloAdmin->admindb->execute_statement((char *)"SELECT * FROM mysql_group_replication_hostgroups", &error , &cols , &affected_rows , &resultset); + GloAdmin->admindb->execute_statement((char*)"SELECT * FROM mysql_group_replication_hostgroups", &error, &cols, &affected_rows, &resultset); resultset->dump_to_stderr(); delete resultset; // sync mysql_galera_hostgroups + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Writing mysql_galera_hostgroups table\n"); proxy_info("Cluster: Writing mysql_galera_hostgroups table\n"); GloAdmin->admindb->execute("DELETE FROM mysql_galera_hostgroups"); - q=(char *)"INSERT INTO mysql_galera_hostgroups ( " + q = (char*)"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) "; while ((row = mysql_fetch_row(results[3]))) { @@ -1856,7 +2172,7 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, if (row[8] != nullptr) { fqs += "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, '%s')"; o = escape_string_single_quotes(row[8], false); - query = (char *)malloc(strlen(fqs.c_str()) + i + strlen(o) + 64); + query = (char*)malloc(strlen(fqs.c_str()) + i + strlen(o) + 64); sprintf(query, fqs.c_str(), row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], o); // free in case of 'o' being a copy if (o != row[8]) { @@ -1866,22 +2182,24 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, // In case of comment being null, placeholder must not have '' fqs += "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)"; o = const_cast("NULL"); - query = (char *)malloc(strlen(fqs.c_str()) + i + strlen("NULL") + 64); + query = (char*)malloc(strlen(fqs.c_str()) + i + strlen("NULL") + 64); sprintf(query, fqs.c_str(), row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], o); } GloAdmin->admindb->execute(query); free(query); } + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Dumping fetched 'mysql_galera_hostgroups'\n"); proxy_info("Dumping fetched 'mysql_galera_hostgroups'\n"); - GloAdmin->admindb->execute_statement((char *)"SELECT * FROM mysql_galera_hostgroups", &error , &cols , &affected_rows , &resultset); + GloAdmin->admindb->execute_statement((char*)"SELECT * FROM mysql_galera_hostgroups", &error, &cols, &affected_rows, &resultset); resultset->dump_to_stderr(); delete resultset; // sync mysql_aws_aurora_hostgroups + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Writing mysql_aws_aurora_hostgroups table\n"); proxy_info("Cluster: Writing mysql_aws_aurora_hostgroups table\n"); GloAdmin->admindb->execute("DELETE FROM mysql_aws_aurora_hostgroups"); - q=(char *)"INSERT INTO mysql_aws_aurora_hostgroups ( " + q = (char*)"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) "; while ((row = mysql_fetch_row(results[4]))) { @@ -1897,7 +2215,7 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, if (row[13] != nullptr) { fqs += "VALUES (%s, %s, %s, %s, '%s', %s, %s, %s, %s, %s, %s, %s, %s, '%s')"; o = escape_string_single_quotes(row[13], false); - query = (char *)malloc(strlen(fqs.c_str()) + i + strlen(o) + 64); + query = (char*)malloc(strlen(fqs.c_str()) + i + strlen(o) + 64); sprintf(query, fqs.c_str(), row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], o); // free in case of 'o' being a copy if (o != row[13]) { @@ -1907,23 +2225,25 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, // In case of comment being null, placeholder must not have '' fqs += "VALUES (%s, %s, %s, %s, '%s', %s, %s, %s, %s, %s, %s, %s, %s, %s)"; o = const_cast("NULL"); - query = (char *)malloc(strlen(fqs.c_str()) + i + strlen("NULL") + 64); + query = (char*)malloc(strlen(fqs.c_str()) + i + strlen("NULL") + 64); sprintf(query, fqs.c_str(), row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], o); } GloAdmin->admindb->execute(query); free(query); } + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Dumping fetched 'mysql_aws_aurora_hostgroups'\n"); proxy_info("Dumping fetched 'mysql_aws_aurora_hostgroups'\n"); - GloAdmin->admindb->execute_statement((char *)"SELECT * FROM mysql_aws_aurora_hostgroups", &error , &cols , &affected_rows , &resultset); + GloAdmin->admindb->execute_statement((char*)"SELECT * FROM mysql_aws_aurora_hostgroups", &error, &cols, &affected_rows, &resultset); resultset->dump_to_stderr(); delete resultset; // sync mysql_hostgroup_attributes + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Writing mysql_hostgroup_attributes table\n"); proxy_info("Cluster: Writing mysql_hostgroup_attributes table\n"); GloAdmin->admindb->execute("DELETE FROM mysql_hostgroup_attributes"); { - const char * q=(const char *)"INSERT INTO mysql_hostgroup_attributes ( " + const char* q = (const char*)"INSERT INTO mysql_hostgroup_attributes ( " "hostgroup_id, max_num_online_servers, autocommit, free_connections_pct, " "init_connect, multiplex, connection_warming, throttle_connections_per_sec, " "ignore_session_variables, hostgroup_settings, servers_defaults, comment) VALUES " @@ -1946,30 +2266,41 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, rc=(*proxy_sqlite3_bind_text)(statement1, 11, row[10], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, GloAdmin->admindb); // servers_defaults rc=(*proxy_sqlite3_bind_text)(statement1, 12, row[11], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, GloAdmin->admindb); // comment SAFE_SQLITE3_STEP2(statement1); - rc=(*proxy_sqlite3_clear_bindings)(statement1); ASSERT_SQLITE_OK(rc, GloAdmin->admindb); - rc=(*proxy_sqlite3_reset)(statement1); ASSERT_SQLITE_OK(rc, GloAdmin->admindb); + rc = (*proxy_sqlite3_clear_bindings)(statement1); ASSERT_SQLITE_OK(rc, GloAdmin->admindb); + rc = (*proxy_sqlite3_reset)(statement1); ASSERT_SQLITE_OK(rc, GloAdmin->admindb); } (*proxy_sqlite3_finalize)(statement1); } + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Dumping fetched 'mysql_hostgroup_attributes'\n"); proxy_info("Dumping fetched 'mysql_hostgroup_attributes'\n"); - GloAdmin->admindb->execute_statement((char *)"SELECT * FROM mysql_hostgroup_attributes", &error , &cols , &affected_rows , &resultset); + GloAdmin->admindb->execute_statement((char*)"SELECT * FROM mysql_hostgroup_attributes", &error, &cols, &affected_rows, &resultset); resultset->dump_to_stderr(); delete resultset; - proxy_info("Cluster: Loading to runtime MySQL Servers from peer %s:%d\n", hostname, port); - GloAdmin->load_mysql_servers_to_runtime(incoming_servers, checksum, epoch); + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Loading to runtime MySQL Servers v2 from peer %s:%d\n", hostname, port); + proxy_info("Cluster: Loading to runtime MySQL Servers v2 from peer %s:%d\n", hostname, port); + GloAdmin->load_mysql_servers_to_runtime(incoming_servers, peer_runtime_mysql_server, peer_mysql_server_v2); + if (GloProxyCluster->cluster_mysql_servers_save_to_disk == true) { - proxy_info("Cluster: Saving to disk MySQL Servers from peer %s:%d\n", hostname, port); + if (fetch_runtime_mysql_servers == true) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Saving Runtime MySQL Servers to Database\n"); + GloAdmin->save_mysql_servers_runtime_to_database(false); + } + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Saving to disk MySQL Servers v2 from peer %s:%d\n", hostname, port); + proxy_info("Cluster: Saving to disk MySQL Servers v2 from peer %s:%d\n", hostname, port); GloAdmin->flush_GENERIC__from_to("mysql_servers", "memory_to_disk"); } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Not saving to disk MySQL Servers from peer %s:%d failed.\n", hostname, port); proxy_info("Cluster: Not saving to disk MySQL Servers from peer %s:%d failed.\n", hostname, port); } GloAdmin->mysql_servers_wrunlock(); } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Servers v2 from peer %s:%d failed: Checksum changed from %s to %s\n", + hostname, port, peer_mysql_servers_v2_checksum, computed_checksum.c_str()); proxy_info( - "Cluster: Fetching MySQL Servers from peer %s:%d failed: Checksum changed from %s to %s\n", - hostname, port, peer_checksum, computed_checksum.c_str() + "Cluster: Fetching MySQL Servers v2 from peer %s:%d failed: Checksum changed from %s to %s\n", + hostname, port, peer_mysql_servers_v2_checksum, computed_checksum.c_str() ); metrics.p_counter_array[p_cluster_counter::pulled_mysql_variables_failure]->Increment(); } @@ -1982,6 +2313,7 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, metrics.p_counter_array[p_cluster_counter::pulled_mysql_servers_success]->Increment(); } } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Servers from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); proxy_info("Cluster: Fetching MySQL Servers from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); metrics.p_counter_array[p_cluster_counter::pulled_mysql_servers_failure]->Increment(); } @@ -1992,7 +2324,7 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, if (password) { free(password); } -__exit_pull_mysql_servers_from_peer: + __exit_pull_mysql_servers_v2_from_peer: if (conn) { if (conn->net.pvio) { mysql_close(conn); @@ -2002,8 +2334,14 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, if (ip_address) free(ip_address); + + if (peer_mysql_servers_v2_checksum) + free (peer_mysql_servers_v2_checksum); + + if (peer_runtime_mysql_servers_checksum) + free(peer_runtime_mysql_servers_checksum); } - pthread_mutex_unlock(&GloProxyCluster->update_mysql_servers_mutex); + pthread_mutex_unlock(&GloProxyCluster->update_mysql_servers_v2_mutex); } void ProxySQL_Cluster::pull_global_variables_from_peer(const string& var_type, const string& expected_checksum, const time_t epoch) { @@ -2090,10 +2428,12 @@ void ProxySQL_Cluster::pull_global_variables_from_peer(const string& var_type, c if (rc_query == 0) { MYSQL_RES *result = mysql_store_result(conn); + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching %s Variables from peer %s:%d completed\n", vars_type_str, hostname, port); proxy_info("Cluster: Fetching %s Variables from peer %s:%d completed\n", vars_type_str, hostname, port); uint64_t glovars_hash = mysql_raw_checksum(result); string computed_checksum { get_checksum_from_hash(glovars_hash) }; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Computed checksum for %s Variables from peer %s:%d : %s\n", vars_type_str, hostname, port, computed_checksum.c_str()); proxy_info("Cluster: Computed checksum for %s Variables from peer %s:%d : %s\n", vars_type_str, hostname, port, computed_checksum.c_str()); if (expected_checksum == computed_checksum) { @@ -2129,12 +2469,14 @@ void ProxySQL_Cluster::pull_global_variables_from_peer(const string& var_type, c } mysql_free_result(result); + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Loading to runtime %s Variables from peer %s:%d\n", vars_type_str, hostname, port); proxy_info("Cluster: Loading to runtime %s Variables from peer %s:%d\n", vars_type_str, hostname, port); if (var_type == "mysql") { GloAdmin->load_mysql_variables_to_runtime(expected_checksum, epoch); if (GloProxyCluster->cluster_mysql_variables_save_to_disk == true) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Saving to disk MySQL Variables from peer %s:%d\n", hostname, port); proxy_info("Cluster: Saving to disk MySQL Variables from peer %s:%d\n", hostname, port); GloAdmin->flush_mysql_variables__from_memory_to_disk(); } @@ -2142,13 +2484,16 @@ void ProxySQL_Cluster::pull_global_variables_from_peer(const string& var_type, c GloAdmin->load_admin_variables_to_runtime(expected_checksum, epoch, false); if (GloProxyCluster->cluster_admin_variables_save_to_disk == true) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Saving to disk Admin Variables from peer %s:%d\n", hostname, port); proxy_info("Cluster: Saving to disk Admin Variables from peer %s:%d\n", hostname, port); GloAdmin->flush_admin_variables__from_memory_to_disk(); } + } else if (var_type == "ldap") { GloAdmin->load_ldap_variables_to_runtime(expected_checksum, epoch); if (GloProxyCluster->cluster_ldap_variables_save_to_disk == true) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Saving to disk LDAP Variables from peer %s:%d\n", hostname, port); proxy_info("Cluster: Saving to disk LDAP Variables from peer %s:%d\n", hostname, port); GloAdmin->flush_ldap_variables__from_memory_to_disk(); } @@ -2157,8 +2502,9 @@ void ProxySQL_Cluster::pull_global_variables_from_peer(const string& var_type, c assert(0); } metrics.p_counter_array[success_metric]->Increment(); - } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching %s Variables from peer %s:%d failed: Checksum changed from %s to %s\n", + vars_type_str, hostname, port, expected_checksum.c_str(), computed_checksum.c_str()); proxy_info( "Cluster: Fetching %s Variables from peer %s:%d failed: Checksum changed from %s to %s\n", vars_type_str, hostname, port, expected_checksum.c_str(), computed_checksum.c_str() @@ -2166,10 +2512,12 @@ void ProxySQL_Cluster::pull_global_variables_from_peer(const string& var_type, c metrics.p_counter_array[p_cluster_counter::pulled_mysql_variables_failure]->Increment(); } } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching %s Variables from peer %s:%d failed: %s\n", vars_type_str, hostname, port, mysql_error(conn)); proxy_info("Cluster: Fetching %s Variables from peer %s:%d failed: %s\n", vars_type_str, hostname, port, mysql_error(conn)); metrics.p_counter_array[failure_metric]->Increment(); } } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching %s Variables from peer %s:%d failed: %s\n", vars_type_str, hostname, port, mysql_error(conn)); proxy_info("Cluster: Fetching %s Variables from peer %s:%d failed: %s\n", vars_type_str, hostname, port, mysql_error(conn)); metrics.p_counter_array[failure_metric]->Increment(); } @@ -2222,6 +2570,8 @@ void ProxySQL_Cluster::pull_proxysql_servers_from_peer(const std::string& expect unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); } + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching ProxySQL Servers from peer %s:%d started. Expected checksum: %s\n", + hostname, port, expected_checksum.c_str()); proxy_info( "Cluster: Fetching ProxySQL Servers from peer %s:%d started. Expected checksum: %s\n", hostname, port, expected_checksum.c_str() @@ -2235,6 +2585,7 @@ void ProxySQL_Cluster::pull_proxysql_servers_from_peer(const std::string& expect MYSQL_RES* result = mysql_store_result(conn); uint64_t proxy_servers_hash = mysql_raw_checksum(result); const string computed_cks { get_checksum_from_hash(proxy_servers_hash) }; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching ProxySQL Servers from peer %s:%d completed. Computed checksum: %s\n", hostname, port, computed_cks.c_str()); proxy_info("Cluster: Fetching ProxySQL Servers from peer %s:%d completed. Computed checksum: %s\n", hostname, port, computed_cks.c_str()); if (computed_cks == expected_checksum) { @@ -2257,6 +2608,7 @@ void ProxySQL_Cluster::pull_proxysql_servers_from_peer(const std::string& expect free(query); } + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Dumping fetched 'proxysql_servers'\n"); proxy_info("Dumping fetched 'proxysql_servers'\n"); char *error = NULL; int cols = 0; @@ -2266,16 +2618,21 @@ void ProxySQL_Cluster::pull_proxysql_servers_from_peer(const std::string& expect resultset->dump_to_stderr(); delete resultset; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Loading to runtime ProxySQL Servers from peer %s:%d\n", hostname, port); proxy_info("Cluster: Loading to runtime ProxySQL Servers from peer %s:%d\n", hostname, port); GloAdmin->load_proxysql_servers_to_runtime(false, expected_checksum, epoch); if (GloProxyCluster->cluster_proxysql_servers_save_to_disk == true) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Saving to disk ProxySQL Servers from peer %s:%d\n", hostname, port); proxy_info("Cluster: Saving to disk ProxySQL Servers from peer %s:%d\n", hostname, port); GloAdmin->flush_GENERIC__from_to("proxysql_servers","memory_to_disk"); } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "NOT saving to disk ProxySQL Servers from peer %s:%d\n", hostname, port); proxy_info("Cluster: NOT saving to disk ProxySQL Servers from peer %s:%d\n", hostname, port); } metrics.p_counter_array[p_cluster_counter::pulled_proxysql_servers_success]->Increment(); } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching ProxySQL Servers from peer %s:%d failed: Checksum changed from %s to %s\n", + hostname, port, expected_checksum.c_str(), computed_cks.c_str()); proxy_info( "Cluster: Fetching ProxySQL Servers from peer %s:%d failed: Checksum changed from %s to %s\n", hostname, port, expected_checksum.c_str(), computed_cks.c_str() @@ -2284,10 +2641,12 @@ void ProxySQL_Cluster::pull_proxysql_servers_from_peer(const std::string& expect } mysql_free_result(result); } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching ProxySQL Servers from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); proxy_info("Cluster: Fetching ProxySQL Servers from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); metrics.p_counter_array[p_cluster_counter::pulled_proxysql_servers_failure]->Increment(); } } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching ProxySQL Servers from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); proxy_info("Cluster: Fetching ProxySQL Servers from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); metrics.p_counter_array[p_cluster_counter::pulled_proxysql_servers_failure]->Increment(); } @@ -2546,6 +2905,9 @@ void ProxySQL_Cluster_Nodes::load_servers_list(SQLite3_result *resultset, bool _ node = new ProxySQL_Node_Entry(h_, p_, w_ , c_); node->set_active(true); umap_proxy_nodes.insert(std::make_pair(hash_, node)); + + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Added new peer %s:%d\n", h_, p_); + ProxySQL_Node_Address * a = new ProxySQL_Node_Address(h_, p_, node->get_ipaddress()); pthread_attr_t attr; pthread_attr_init(&attr); @@ -2563,6 +2925,8 @@ void ProxySQL_Cluster_Nodes::load_servers_list(SQLite3_result *resultset, bool _ node->set_active(true); node->set_weight(w_); node->set_comment(c_); + + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Peer %s:%d already exists. Updating it\n", h_, p_); } } remove_inactives(); @@ -2598,8 +2962,10 @@ bool ProxySQL_Cluster_Nodes::Update_Global_Checksum(char * _h, uint16_t _p, MYSQ while ((row = mysql_fetch_row(_r))) { unsigned long long v = atoll(row[0]); if (v == node->global_checksum) { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Global checksum 0x%llX for peer %s:%d matches\n", v, node->get_hostname(), node->get_port()); ret = false; } else { + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Global checksum for peer %s:%d is different from fetched one. Local checksum:[0x%lX] Fetched checksum:[0x%llX]\n", node->get_hostname(), node->get_port(), node->global_checksum, v); node->global_checksum = v; } } @@ -2699,11 +3065,12 @@ void ProxySQL_Cluster_Nodes::get_peer_to_sync_mysql_query_rules(char **host, uin *host = hostname; *port = p; *ip_address = ip_addr; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_query_rules version %llu, epoch %llu\n", hostname, p, version, epoch); proxy_info("Cluster: detected peer %s:%d with mysql_query_rules version %llu, epoch %llu\n", hostname, p, version, epoch); } } -void ProxySQL_Cluster_Nodes::get_peer_to_sync_mysql_servers(char **host, uint16_t *port, char **peer_checksum, char** ip_address) { +void ProxySQL_Cluster_Nodes::get_peer_to_sync_runtime_mysql_servers(char **host, uint16_t *port, char **peer_checksum, char** ip_address) { unsigned long long version = 0; unsigned long long epoch = 0; unsigned long long max_epoch = 0; @@ -2765,8 +3132,88 @@ void ProxySQL_Cluster_Nodes::get_peer_to_sync_mysql_servers(char **host, uint16_ *host = hostname; *port = p; *ip_address = ip_addr; - proxy_info("Cluster: detected peer %s:%d with mysql_servers version %llu, epoch %llu\n", hostname, p, version, epoch); *peer_checksum = pc; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_servers version %llu, epoch %llu, checksum %s\n", hostname, p, version, epoch, pc); + proxy_info("Cluster: detected peer %s:%d with mysql_servers version %llu, epoch %llu\n", hostname, p, version, epoch); + } +} + +void ProxySQL_Cluster_Nodes::get_peer_to_sync_mysql_servers_v2(char** host, uint16_t* port, + char** peer_mysql_servers_v2_checksum, char** peer_runtime_mysql_servers_checksum, char** ip_address) { + unsigned long long version = 0; + unsigned long long epoch = 0; + unsigned long long max_epoch = 0; + char* hostname = NULL; + char* ip_addr = NULL; + uint16_t p = 0; + char* mysql_servers_v2_checksum = NULL; + char* runtime_mysql_servers_checksum = NULL; + //pthread_mutex_lock(&mutex); + //unsigned long long curtime = monotonic_time(); + unsigned int diff_ms = (unsigned int)__sync_fetch_and_add(&GloProxyCluster->cluster_mysql_servers_diffs_before_sync, 0); + for (std::unordered_map::iterator it = umap_proxy_nodes.begin(); it != umap_proxy_nodes.end(); ) { + ProxySQL_Node_Entry* node = it->second; + ProxySQL_Checksum_Value_2* v = &node->checksums_values.mysql_servers_v2; + if (v->version > 1) { + if (v->epoch > epoch) { + max_epoch = v->epoch; + if (v->diff_check >= diff_ms) { + epoch = v->epoch; + version = v->version; + if (mysql_servers_v2_checksum) { + free(mysql_servers_v2_checksum); + } + if (runtime_mysql_servers_checksum) { + free(runtime_mysql_servers_checksum); + } + if (hostname) { + free(hostname); + } + if (ip_addr) { + free(ip_addr); + } + mysql_servers_v2_checksum = strdup(v->checksum); + runtime_mysql_servers_checksum = strdup(node->checksums_values.mysql_servers.checksum); + hostname = strdup(node->get_hostname()); + const char* ip = node->get_ipaddress(); + if (ip) + ip_addr = strdup(ip); + p = node->get_port(); + } + } + } + it++; + } + // pthread_mutex_unlock(&mutex); + if (epoch) { + if (max_epoch > epoch) { + proxy_warning("Cluster: detected a peer with mysql_servers_v2 epoch %llu , but not enough diff_check. We won't sync from epoch %llu: temporarily skipping sync\n", max_epoch, epoch); + if (hostname) { + free(hostname); + hostname = NULL; + } + if (mysql_servers_v2_checksum) { + free(mysql_servers_v2_checksum); + mysql_servers_v2_checksum = NULL; + } + if (runtime_mysql_servers_checksum) { + free(runtime_mysql_servers_checksum); + runtime_mysql_servers_checksum = NULL; + } + if (ip_addr) { + free(ip_addr); + ip_addr = NULL; + } + } + } + if (hostname) { + *host = hostname; + *port = p; + *ip_address = ip_addr; + *peer_mysql_servers_v2_checksum = mysql_servers_v2_checksum; + *peer_runtime_mysql_servers_checksum = runtime_mysql_servers_checksum; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_servers_v2 version %llu, epoch %llu, mysql_servers_v2 checksum %s, runtime_mysql_servers %s\n", hostname, p, version, epoch, mysql_servers_v2_checksum, runtime_mysql_servers_checksum); + proxy_info("Cluster: detected peer %s:%d with mysql_servers_v2 version %llu, epoch %llu\n", hostname, p, version, epoch); } } @@ -2823,6 +3270,7 @@ void ProxySQL_Cluster_Nodes::get_peer_to_sync_mysql_users(char **host, uint16_t *host = hostname; *port = p; *ip_address = ip_addr; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_users version %llu, epoch %llu\n", hostname, p, version, epoch); proxy_info("Cluster: detected peer %s:%d with mysql_users version %llu, epoch %llu\n", hostname, p, version, epoch); } } @@ -2877,6 +3325,7 @@ void ProxySQL_Cluster_Nodes::get_peer_to_sync_mysql_variables(char **host, uint1 *host = hostname; *port = p; *ip_address = ip_addr; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with mysql_variables version %llu, epoch %llu\n", hostname, p, version, epoch); proxy_info("Cluster: detected peer %s:%d with mysql_variables version %llu, epoch %llu\n", hostname, p, version, epoch); } } @@ -2932,6 +3381,7 @@ void ProxySQL_Cluster_Nodes::get_peer_to_sync_admin_variables(char **host, uint1 *host = hostname; *port = p; *ip_address = ip_addr; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with admin_variables version %llu, epoch %llu\n", hostname, p, version, epoch); proxy_info("Cluster: detected peer %s:%d with admin_variables version %llu, epoch %llu\n", hostname, p, version, epoch); } } @@ -2986,6 +3436,7 @@ void ProxySQL_Cluster_Nodes::get_peer_to_sync_ldap_variables(char **host, uint16 *host = hostname; *port = p; *ip_address = ip_addr; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with ldap_variables version %llu, epoch %llu\n", hostname, p, version, epoch); proxy_info("Cluster: detected peer %s:%d with ldap_variables version %llu, epoch %llu\n", hostname, p, version, epoch); } } @@ -3043,6 +3494,7 @@ void ProxySQL_Cluster_Nodes::get_peer_to_sync_proxysql_servers(char **host, uint *host = hostname; *port = p; *ip_address = ip_addr; + proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Detected peer %s:%d with proxysql_servers version %llu, epoch %llu\n", hostname, p, version, epoch); proxy_info("Cluster: detected peer %s:%d with proxysql_servers version %llu, epoch %llu\n", hostname, p, version, epoch); } } @@ -3066,14 +3518,15 @@ SQLite3_result * ProxySQL_Cluster_Nodes::stats_proxysql_servers_checksums() { //unsigned long long curtime = monotonic_time(); for( std::unordered_map::iterator it = umap_proxy_nodes.begin(); it != umap_proxy_nodes.end(); ) { ProxySQL_Node_Entry * node = it->second; - ProxySQL_Checksum_Value_2 * vals[6]; + ProxySQL_Checksum_Value_2 * vals[7]; vals[0] = &node->checksums_values.admin_variables; vals[1] = &node->checksums_values.mysql_query_rules; vals[2] = &node->checksums_values.mysql_servers; vals[3] = &node->checksums_values.mysql_users; vals[4] = &node->checksums_values.mysql_variables; vals[5] = &node->checksums_values.proxysql_servers; - for (int i=0; i<6 ; i++) { + vals[6] = &node->checksums_values.mysql_servers_v2; + for (int i=0; i<7 ; i++) { ProxySQL_Checksum_Value_2 *v = vals[i]; char **pta=(char **)malloc(sizeof(char *)*colnum); pta[0]=strdup(node->get_hostname()); @@ -3099,6 +3552,9 @@ SQLite3_result * ProxySQL_Cluster_Nodes::stats_proxysql_servers_checksums() { case 5: pta[2]=strdup((char *)"proxysql_servers"); break; + case 6: + pta[2]=strdup((char*)"mysql_servers_v2"); + break; default: break; } @@ -3223,6 +3679,7 @@ vector> get_module_checksums(ProxySQL_N res.push_back({"mysql_users", &entry->checksums_values.mysql_users}); res.push_back({"mysql_variables", &entry->checksums_values.mysql_variables}); res.push_back({"proxysql_servers", &entry->checksums_values.proxysql_servers}); + res.push_back({"mysql_servers_v2", &entry->checksums_values.mysql_servers_v2}); return res; } @@ -3856,7 +4313,8 @@ cluster_metrics_map = std::make_tuple( ProxySQL_Cluster::ProxySQL_Cluster() : proxysql_servers_to_monitor(NULL) { pthread_mutex_init(&mutex,NULL); pthread_mutex_init(&update_mysql_query_rules_mutex,NULL); - pthread_mutex_init(&update_mysql_servers_mutex,NULL); + pthread_mutex_init(&update_runtime_mysql_servers_mutex,NULL); + pthread_mutex_init(&update_mysql_servers_v2_mutex, NULL); pthread_mutex_init(&update_mysql_users_mutex,NULL); pthread_mutex_init(&update_proxysql_servers_mutex,NULL); pthread_mutex_init(&update_mysql_variables_mutex,NULL); @@ -3874,6 +4332,7 @@ ProxySQL_Cluster::ProxySQL_Cluster() : proxysql_servers_to_monitor(NULL) { cluster_mysql_servers_save_to_disk = true; cluster_mysql_users_save_to_disk = true; cluster_proxysql_servers_save_to_disk = true; + cluster_mysql_servers_sync_algorithm = 1; init_prometheus_counter_array(cluster_metrics_map, this->metrics.p_counter_array); init_prometheus_gauge_array(cluster_metrics_map, this->metrics.p_gauge_array); } diff --git a/lib/ProxySQL_GloVars.cpp b/lib/ProxySQL_GloVars.cpp index d095361675..238624ea27 100644 --- a/lib/ProxySQL_GloVars.cpp +++ b/lib/ProxySQL_GloVars.cpp @@ -89,10 +89,63 @@ ProxySQL_GlobalVariables::~ProxySQL_GlobalVariables() { checksums_values.admin_variables.in_shutdown = true; checksums_values.mysql_query_rules.in_shutdown = true; checksums_values.mysql_servers.in_shutdown = true; + checksums_values.mysql_servers_v2.in_shutdown = true; checksums_values.mysql_users.in_shutdown = true; checksums_values.mysql_variables.in_shutdown = true; checksums_values.ldap_variables.in_shutdown = true; checksums_values.proxysql_servers.in_shutdown = true; + if (global.gr_bootstrap_uri) { + free(global.gr_bootstrap_uri); + global.gr_bootstrap_uri = nullptr; + } + if (global.gr_bootstrap_account) { + free(global.gr_bootstrap_account); + global.gr_bootstrap_account = nullptr; + } + if (global.gr_bootstrap_account_create) { + free(global.gr_bootstrap_account_create); + global.gr_bootstrap_account_create = nullptr; + } + if (global.gr_bootstrap_account_host) { + free(global.gr_bootstrap_account_host); + global.gr_bootstrap_account_host = nullptr; + } + if (global.gr_bootstrap_conf_bind_address) { + free(global.gr_bootstrap_conf_bind_address); + global.gr_bootstrap_conf_bind_address = nullptr; + } + if (global.gr_bootstrap_ssl_ca) { + free(global.gr_bootstrap_ssl_ca); + global.gr_bootstrap_ssl_ca = nullptr; + } + if (global.gr_bootstrap_ssl_capath) { + free(global.gr_bootstrap_ssl_capath); + global.gr_bootstrap_ssl_capath = nullptr; + } + if (global.gr_bootstrap_ssl_cert) { + free(global.gr_bootstrap_ssl_cert); + global.gr_bootstrap_ssl_cert = nullptr; + } + if (global.gr_bootstrap_ssl_cipher) { + free(global.gr_bootstrap_ssl_cipher); + global.gr_bootstrap_ssl_cipher = nullptr; + } + if (global.gr_bootstrap_ssl_crl) { + free(global.gr_bootstrap_ssl_crl); + global.gr_bootstrap_ssl_crl = nullptr; + } + if (global.gr_bootstrap_ssl_crlpath) { + free(global.gr_bootstrap_ssl_crlpath); + global.gr_bootstrap_ssl_crlpath = nullptr; + } + if (global.gr_bootstrap_ssl_key) { + free(global.gr_bootstrap_ssl_key); + global.gr_bootstrap_ssl_key = nullptr; + } + if (global.gr_bootstrap_ssl_mode) { + free(global.gr_bootstrap_ssl_mode); + global.gr_bootstrap_ssl_mode = nullptr; + } }; ProxySQL_GlobalVariables::ProxySQL_GlobalVariables() : @@ -148,6 +201,24 @@ ProxySQL_GlobalVariables::ProxySQL_GlobalVariables() : #ifdef PROXYSQLCLICKHOUSE global.clickhouse_server=false; #endif /* PROXYSQLCLICKHOUSE */ + global.gr_bootstrap_mode = 0; + global.gr_bootstrap_uri = nullptr; + global.gr_bootstrap_account = nullptr; + global.gr_bootstrap_account_create = nullptr; + global.gr_bootstrap_account_host = nullptr; + global.gr_bootstrap_password_retries = 20; + global.gr_bootstrap_conf_bind_address = nullptr; + global.gr_bootstrap_conf_base_port = 0; + global.gr_bootstrap_conf_use_sockets = false; + global.gr_bootstrap_conf_skip_tcp = false; + global.gr_bootstrap_ssl_ca = nullptr; + global.gr_bootstrap_ssl_capath = nullptr; + global.gr_bootstrap_ssl_cert = nullptr; + global.gr_bootstrap_ssl_cipher = nullptr; + global.gr_bootstrap_ssl_crl = nullptr; + global.gr_bootstrap_ssl_crlpath = nullptr; + global.gr_bootstrap_ssl_key = nullptr; + global.gr_bootstrap_ssl_mode = nullptr; global.ssl_keylog_enabled = false; opt=new ez::ezOptionParser(); opt->overview="High Performance Advanced Proxy for MySQL"; @@ -159,7 +230,8 @@ ProxySQL_GlobalVariables::ProxySQL_GlobalVariables() : opt->add((const char *)"",0,0,0,(const char *)"Display usage instructions.",(const char *)"-h",(const char *)"-help",(const char *)"--help",(const char *)"--usage"); opt->add((const char *)"",0,0,0,(const char *)"Print version",(const char *)"-V",(const char *)"--version"); #ifdef DEBUG - opt->add((const char *)"",0,1,0,(const char *)"Enable debugging messages with specific verbosity",(const char *)"-d",(const char *)"--debug"); + // NOTE: Temporal change for full 'bootstrap' compatibility, only '--debug' is allowed, '-d' is an alias for '-D' + opt->add((const char *)"",0,1,0,(const char *)"Enable debugging messages with specific verbosity",(const char *)"--debug"); #endif /* DEBUG */ opt->add((const char *)"",0,0,0,(const char *)"Starts only the admin service",(const char *)"-n",(const char *)"--no-start"); opt->add((const char *)"",0,0,0,(const char *)"Do not start Monitor Module",(const char *)"-M",(const char *)"--no-monitor"); @@ -170,6 +242,8 @@ ProxySQL_GlobalVariables::ProxySQL_GlobalVariables() : opt->add((const char *)"",0,0,0,(const char *)"Do not restart ProxySQL if crashes",(const char *)"-e",(const char *)"--exit-on-error"); opt->add((const char *)"~/proxysql.cnf",0,1,0,(const char *)"Configuration file",(const char *)"-c",(const char *)"--config"); opt->add((const char *)"",0,1,0,(const char *)"Datadir",(const char *)"-D",(const char *)"--datadir"); + // NOTE: Duplicated option for 'bootstrap' compatibility + opt->add((const char *)"",0,1,0,(const char *)"Datadir",(const char *)"-d",(const char *)"--directory"); opt->add((const char *)"",0,1,0,(const char *)"UUID",(const char *)"-U",(const char *)"--uuid"); opt->add((const char *)"",0,0,0,(const char *)"Rename/empty database file",(const char *)"--initial"); opt->add((const char *)"",0,0,0,(const char *)"Merge config file into database file",(const char *)"--reload"); @@ -180,6 +254,34 @@ ProxySQL_GlobalVariables::ProxySQL_GlobalVariables() : opt->add((const char *)"",0,1,0,(const char *)"Administration Unix Socket",(const char *)"-S",(const char *)"--admin-socket"); opt->add((const char *)"",0,0,0,(const char *)"Enable SQLite3 Server",(const char *)"--sqlite3-server"); + // Bootstrap General options + opt->add((const char *)"",0,1,0,(const char *)"Start ProxySQL in Group Replication bootstrap mode." + " An URI needs to be specified for creating a connection to the bootstrap server, if no URI is provided," + " a connection to the default local socket will be attempted.",(const char *)"-B", (const char *)"--bootstrap"); + opt->add((const char *)"",0,1,0, (const char *)"Account to use by monitoring after bootstrap, either reuses a specify account or creates a new one;" + " this behavior is controlled by related option '--acount-create'. When used, a password must be provided." ,(const char *)"--account"); + opt->add((const char *)"",0,1,0,(const char *)"Account creation policy for bootstrap. Possible values are:\n" + "- if-not-exists (default): If the account doesn't exist, create it, otherwise reuse it.\n" + "- always: Only bootstrap if the account isn't present and can be created.\n" + "- never: Only bootstrap if the account is already present.",(const char *)"--account-create"); + opt->add((const char *)"",0,1,0,(const char *)"Host pattern to be used for accounts created during bootstrap",(const char *)"--account-host"); + opt->add((const char *)"",0,1,0,(const char *)"Number of attempts for generating a password when creating an account during bootstrap",(const char *)"--password-retries"); + opt->add((const char *)"",0,1,0,(const char *)"Sets the default base port ('mysql-interfaces') for the default R/W split port based configuration",(const char *)"--conf-base-port"); + opt->add((const char *)"",0,1,0,(const char *)"Sets the default bind address ('mysql-interfaces'). Used in combination with '--conf-bind-port'",(const char *)"--conf-bind-address"); + // TODO: We should make query rules compatible with Unix socket domain addresses for routing + opt->add((const char *)"",0,1,0,(const char *)"bootstrap option, configures two Unix sockets with names 'mysql.sock' and 'mysqlro.sock'",(const char *)"--conf-use-sockets"); + opt->add((const char *)"",0,1,0,(const char *)"Sets the default base port for the default R/W split port based configuration",(const char *)"--conf-skip-tcp"); + // Bootstrap SSL options + opt->add((const char *)"",0,1,0,(const char *)"The path name of the Certificate Authority (CA) certificate file. Must specify the same certificate used by the server",(const char *)"--ssl-ca"); + opt->add((const char *)"",0,1,0,(const char *)"The path name of the directory that contains trusted SSL CA certificate files",(const char *)"--ssl-capath"); + opt->add((const char *)"",0,1,0,(const char *)"The path name of the client public key certificate file",(const char *)"--ssl-cert"); + opt->add((const char *)"",0,1,0,(const char *)"The list of permissible ciphers for SSL encryption",(const char *)"--ssl-cipher"); + opt->add((const char *)"",0,1,0,(const char *)"The path name of the file containing certificate revocation lists",(const char *)"--ssl-crl"); + opt->add((const char *)"",0,1,0,(const char *)"The path name of the directory that contains certificate revocation list files",(const char *)"--ssl-crlpath"); + opt->add((const char *)"",0,1,0,(const char *)"The path name of the client private key file",(const char *)"--ssl-key"); + // TODO: Complete information about this mode and it's relation with 'ssl-ca'. E.g: For 'VERIFY_CA' mode, + // MariaDB connector related options. Not direct option 'MYSQL_OPT_SSL_MODE'. + opt->add((const char *)"",0,1,0,(const char *)"SSL connection mode for using during bootstrap during normal operation with the backend servers. Only PREFERRED, and DISABLED are supported.",(const char *)"--ssl-mode"); #ifdef PROXYSQLCLICKHOUSE opt->add((const char *)"",0,0,0,(const char *)"Enable ClickHouse Server",(const char *)"--clickhouse-server"); #endif /* PROXYSQLCLICKHOUSE */ @@ -199,6 +301,21 @@ void ProxySQL_GlobalVariables::parse(int argc, const char * argv[]) { opt->parse(argc, argv); }; +void update_string_var_if_set(char** cur_val, ez::ezOptionParser* opt, const char* cmd_opt) { + if (opt->isSet(cmd_opt)) { + std::string val {}; + opt->get(cmd_opt)->getString(val); + if (*cur_val) { free(*cur_val); } + *cur_val = strdup(val.c_str()); + } +} + +void update_ulong_var_if_set(uint64_t& cur_val, ez::ezOptionParser* opt, const char* cmd_opt) { + if (opt->isSet(cmd_opt)) { + opt->get(cmd_opt)->getULong(cur_val); + } +} + void ProxySQL_GlobalVariables::process_opts_pre() { if (opt->isSet("-h")) { std::string usage; @@ -212,8 +329,8 @@ void ProxySQL_GlobalVariables::process_opts_pre() { exit(EXIT_SUCCESS); } - if (opt->isSet("-d")) { - opt->get("-d")->getInt(GloVars.__cmd_proxysql_gdbg); + if (opt->isSet("--debug")) { + opt->get("--debug")->getInt(GloVars.__cmd_proxysql_gdbg); global.gdbg=true; } @@ -237,6 +354,8 @@ void ProxySQL_GlobalVariables::process_opts_pre() { GloVars.__cmd_proxysql_datadir=strdup(datadir.c_str()); } + update_string_var_if_set(&GloVars.__cmd_proxysql_datadir, opt, "-d"); + if (opt->isSet("-U")) { std::string uuid; opt->get("-U")->getString(uuid); @@ -277,7 +396,24 @@ void ProxySQL_GlobalVariables::process_opts_pre() { global.clickhouse_server=true; } #endif /* PROXYSQLCLICKHOUSE */ - + update_string_var_if_set(&global.gr_bootstrap_uri, opt, "--bootstrap"); + global.gr_bootstrap_mode = opt->isSet("--bootstrap"); + update_ulong_var_if_set(global.gr_bootstrap_conf_base_port, opt, "--conf-base-port"); + update_string_var_if_set(&global.gr_bootstrap_conf_bind_address, opt, "--conf-bind-address"); + global.gr_bootstrap_conf_use_sockets = opt->isSet("--conf-use-sockets"); + global.gr_bootstrap_conf_skip_tcp = opt->isSet("--conf-skip-tcp"); + update_string_var_if_set(&global.gr_bootstrap_account, opt, "--account"); + update_string_var_if_set(&global.gr_bootstrap_account_create, opt, "--account-create"); + update_string_var_if_set(&global.gr_bootstrap_account_host, opt, "--account-host"); + update_ulong_var_if_set(global.gr_bootstrap_password_retries, opt, "--password-retries"); + update_string_var_if_set(&global.gr_bootstrap_ssl_ca, opt, "--ssl-ca"); + update_string_var_if_set(&global.gr_bootstrap_ssl_capath, opt, "--ssl-capath"); + update_string_var_if_set(&global.gr_bootstrap_ssl_cert, opt, "--ssl-cert"); + update_string_var_if_set(&global.gr_bootstrap_ssl_cipher, opt, "--ssl-cipher"); + update_string_var_if_set(&global.gr_bootstrap_ssl_crl, opt, "--ssl-crl"); + update_string_var_if_set(&global.gr_bootstrap_ssl_crlpath, opt, "--ssl-crlpath"); + update_string_var_if_set(&global.gr_bootstrap_ssl_key, opt, "--ssl-key"); + update_string_var_if_set(&global.gr_bootstrap_ssl_mode, opt, "--ssl-mode"); config_file=GloVars.__cmd_proxysql_config_file; @@ -405,6 +541,11 @@ uint64_t ProxySQL_GlobalVariables::generate_global_checksum() { myhash.Update(v->checksum,strlen(v->checksum)); myhash.Update(&v->version,sizeof(v->version)); } + v = &checksums_values.mysql_servers_v2; + if (v->version) { + myhash.Update(v->checksum, strlen(v->checksum)); + myhash.Update(&v->version, sizeof(v->version)); + } v = &checksums_values.mysql_users; if (v->version) { myhash.Update(v->checksum,strlen(v->checksum)); diff --git a/lib/Query_Processor.cpp b/lib/Query_Processor.cpp index 768dcc45d9..85f1b499db 100644 --- a/lib/Query_Processor.cpp +++ b/lib/Query_Processor.cpp @@ -1154,9 +1154,6 @@ unsigned long long Query_Processor::purge_query_digests(bool async_purge, bool p unsigned long long Query_Processor::purge_query_digests_async(char **msg) { unsigned long long ret = 0; - pthread_rwlock_wrlock(&digest_rwlock); - - umap_query_digest digest_umap_aux; umap_query_digest_text digest_text_umap_aux; pthread_rwlock_wrlock(&digest_rwlock); @@ -1399,7 +1396,7 @@ std::pair Query_Processor::get_query_digests_v2(const boo digest_umap_aux.insert(element); } } - digest_text_umap.insert(digest_text_umap_aux.begin(), digest_text_umap_aux.end()); + digest_text_umap_aux.insert(digest_text_umap_aux_2.begin(), digest_text_umap_aux_2.end()); digest_umap_aux_2.clear(); digest_text_umap_aux_2.clear(); @@ -1409,7 +1406,6 @@ std::pair Query_Processor::get_query_digests_v2(const boo // content of the auxiliary maps. pthread_rwlock_wrlock(&digest_rwlock); digest_umap_aux.swap(digest_umap); - digest_text_umap_aux.swap(digest_text_umap); for (const auto& element : digest_umap_aux) { uint64_t digest = element.first; QP_query_digest_stats *qds = (QP_query_digest_stats *)element.second; diff --git a/lib/debug.cpp b/lib/debug.cpp index 806cbea2bc..7d236f6a0f 100644 --- a/lib/debug.cpp +++ b/lib/debug.cpp @@ -474,6 +474,7 @@ void init_debug_struct() { GloVars.global.gdbg_lvl[PROXY_DEBUG_QUERY_STATISTICS].name=(char *)"debug_query_statistics"; GloVars.global.gdbg_lvl[PROXY_DEBUG_RESTAPI].name=(char *)"debug_restapi"; GloVars.global.gdbg_lvl[PROXY_DEBUG_MONITOR].name=(char *)"debug_monitor"; + GloVars.global.gdbg_lvl[PROXY_DEBUG_CLUSTER].name=(char *)"debug_cluster"; for (i=0;i +#include "openssl/ssl.h" +#include "proxysql_structs.h" +/////////////////////////////////////////////////////////////////////////////// + #include const MARIADB_CHARSET_INFO * proxysql_find_charset_nr(unsigned int nr) { @@ -12,21 +20,51 @@ const MARIADB_CHARSET_INFO * proxysql_find_charset_nr(unsigned int nr) { return NULL; } +/** + * @brief Finds the default (first) collation for the supplied 'charset name'. + * @details Previously, this function just returned the first collation found (default). Since v2.5.3, this + * function takes into consideration the thread variable 'SQL_COLLATION_CONNECTION' + * ('mysql-default_collation_connection'). This was introduced for being able to serve the same default + * collation as the server (for bootstrap mode) in case it's detected to be a MySQL 8 + * ('utf8mb4_0900_ai_ci'), instead of the retrocompatible default collation ('utf8mb4_general_ci'). This + * change also allows users to select the default collation that they please for a particular charset, if + * the collection specified via 'mysql-default_collation_connection', isn't found, the first found collation + * (original default) will be retrieved. + * @param name The 'charset name' for which to find the default collation. + * @return The collation found, NULL if none is find. + */ MARIADB_CHARSET_INFO * proxysql_find_charset_name(const char *name_) { + const char* default_collation = mysql_thread___default_variables[SQL_COLLATION_CONNECTION]; MARIADB_CHARSET_INFO *c = (MARIADB_CHARSET_INFO *)mariadb_compiled_charsets; + MARIADB_CHARSET_INFO* charset_collation = nullptr; + const char *name; if (strcasecmp(name_,(const char *)"utf8mb3")==0) { name = (const char *)"utf8"; } else { name = name_; } + do { if (!strcasecmp(c->csname, name)) { - return c; + if (charset_collation == nullptr) { + charset_collation = c; + } + + if (default_collation == nullptr) { + charset_collation = c; + break; + } else { + if (!strcmp(default_collation, c->name)) { + charset_collation = c; + break; + } + } } ++c; } while (c[0].nr != 0); - return NULL; + + return charset_collation; } MARIADB_CHARSET_INFO * proxysql_find_charset_collate_names(const char *csname_, const char *collatename_) { diff --git a/lib/proxysql_utils.cpp b/lib/proxysql_utils.cpp index 5475afb459..a5c31b7d52 100644 --- a/lib/proxysql_utils.cpp +++ b/lib/proxysql_utils.cpp @@ -1,12 +1,14 @@ #include "proxysql_utils.h" #include "mysqld_error.h" +#include #include #include #include #include #include +#include #include #include #include @@ -14,7 +16,9 @@ #include #include +using std::function; using std::string; +using std::unique_ptr; using std::vector; __attribute__((__format__ (__printf__, 1, 2))) @@ -368,6 +372,18 @@ int wexecvp( return child_err; } +std::vector split_str(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; +} + std::string replace_str(const std::string& str, const std::string& match, const std::string& repl) { if(match.empty()) { return str; @@ -404,6 +420,42 @@ std::string generate_multi_rows_query(int rows, int params) { return s; } +string rand_str(std::size_t strSize) { + string dic { "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" }; + std::random_device rd {}; + std::mt19937 gen { rd() }; + + std::shuffle(dic.begin(), dic.end(), gen); + + if (strSize < dic.size()) { + return dic.substr(0, strSize); + } else { + std::size_t req_modulus = static_cast(strSize / dic.size()); + std::size_t req_reminder = strSize % dic.size(); + string random_str {}; + + for (std::size_t i = 0; i < req_modulus; i++) { + std::shuffle(dic.begin(), dic.end(), gen); + random_str.append(dic); + } + + random_str.append(dic.substr(0, req_reminder)); + + return random_str; + } +} + +std::string get_checksum_from_hash(uint64_t hash) { + uint32_t d32[2] = { 0 }; + memcpy(&d32, &hash, sizeof(hash)); + + vector s_buf(ProxySQL_Checksum_Value_LENGTH, 0); + sprintf(&s_buf[0],"0x%0X%0X", d32[0], d32[1]); + replace_checksum_zeros(&s_buf[0]); + + return string { &s_buf.front() }; +} + void close_all_non_term_fd(std::vector excludeFDs) { DIR *d; struct dirent *dir; diff --git a/lib/set_parser.cpp b/lib/set_parser.cpp index 729dfa6c2c..521808d642 100644 --- a/lib/set_parser.cpp +++ b/lib/set_parser.cpp @@ -71,8 +71,9 @@ void SetParser::set_query(const std::string& nq) { #define VAR_VALUE_P1 "(" VAR_VALUE_P1_1 VAR_VALUE_P1_2 VAR_VALUE_P1_3 VAR_VALUE_P1_4 VAR_VALUE_P1_5 VAR_VALUE_P1_6 ")" std::map> SetParser::parse1() { - +#ifdef DEBUG proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 4, "Parsing query %s\n", query.c_str()); +#endif // DEBUG re2::RE2::Options *opt2=new re2::RE2::Options(RE2::Quiet); opt2->set_case_sensitive(false); opt2->set_longest_match(false); @@ -85,9 +86,13 @@ std::map> SetParser::parse1() { std::map> result; const std::string pattern="(?:" NAMES SPACES QUOTES NAME_VALUE QUOTES "(?: +COLLATE +" QUOTES NAME_VALUE QUOTES "|)" "|" SESSION_P1 VAR_P1 SPACES "(?:|:)=" SPACES QUOTES VAR_VALUE_P1 QUOTES ") *,? *"; +#ifdef DEBUG VALGRIND_DISABLE_ERROR_REPORTING; +#endif // DEBUG re2::RE2 re(pattern, *opt2); +#ifdef DEBUG VALGRIND_ENABLE_ERROR_REPORTING; +#endif // DEBUG std::string var; std::string value1, value2, value3, value4, value5; re2::StringPiece input(query); @@ -289,9 +294,10 @@ void SetParser::generateRE_parse1v2() { var_value += s; } var_value += ")"; - +#ifdef DEBUG proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 4, "Parsing query %s\n", query.c_str()); +#endif // DEBUG parse1v2_opt2 = new re2::RE2::Options(RE2::Quiet); parse1v2_opt2->set_case_sensitive(false); parse1v2_opt2->set_longest_match(false); @@ -339,7 +345,9 @@ void SetParser::generateRE_parse1v2() { //const std::string pattern="(?:" NAMES SPACES QUOTES NAME_VALUE QUOTES "(?: +COLLATE +" QUOTES NAME_VALUE QUOTES "|)" "|" SESSION_P1 VAR_P1 SPACES "(?:|:)=" SPACES QUOTES VAR_VALUE_P1 QUOTES ") *,? *"; const std::string pattern="(?:" NAMES SPACES + name_value + "(?: +COLLATE +" + name_value + "|)" "|" + var_1 + SPACES "(?:|:)=" SPACES + var_value + ") *,? *"; //const std::string pattern=var_1 + SPACES "(?:|:)=" SPACES + var_value; +#ifdef DEBUG VALGRIND_DISABLE_ERROR_REPORTING; +#endif // DEBUG #ifdef PARSERDEBUG if (verbosity > 0) { cout << pattern << endl; @@ -364,7 +372,9 @@ std::map> SetParser::parse1v2() { re2::RE2 re1("(\\s|;)+$", *parse1v2_opt2); // remove trailing spaces and semicolon re2::RE2::Replace(&query, re1, ""); +#ifdef DEBUG VALGRIND_ENABLE_ERROR_REPORTING; +#endif // DEBUG std::string var; std::string value1, value2, value3, value4, value5; re2::StringPiece input(query); @@ -420,7 +430,9 @@ VALGRIND_ENABLE_ERROR_REPORTING; std::map> SetParser::parse2() { +#ifdef DEBUG proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 4, "Parsing query %s\n", query.c_str()); +#endif // DEBUG re2::RE2::Options *opt2=new re2::RE2::Options(RE2::Quiet); opt2->set_case_sensitive(false); opt2->set_longest_match(false); @@ -471,7 +483,9 @@ std::map> SetParser::parse2() { } std::string SetParser::parse_character_set() { +#ifdef DEBUG proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 4, "Parsing query %s\n", query.c_str()); +#endif // DEBUG re2::RE2::Options *opt2=new re2::RE2::Options(RE2::Quiet); opt2->set_case_sensitive(false); opt2->set_longest_match(false); diff --git a/src/Makefile b/src/Makefile index 8e58b1c120..52a3d626ee 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,224 +1,232 @@ #!/bin/make -f -ifndef GIT_VERSION - GIT_VERSION := $(shell git describe --long --abbrev=7) +GIT_VERSION ?= $(shell git describe --long --abbrev=7) ifndef GIT_VERSION $(error GIT_VERSION is not set) endif -endif - -DEPS_PATH=../deps +CENTOSVER := Unknown ifneq (,$(wildcard /etc/system-release)) CENTOSVER := $(shell rpm --eval %rhel) -else - CENTOSVER := Unknown endif -MARIADB_PATH=$(DEPS_PATH)/mariadb-client-library/mariadb_client -MARIADB_IDIR=$(MARIADB_PATH)/include -MARIADB_LDIR=$(MARIADB_PATH)/libmariadb +PROXYSQL_PATH := .. +PROXYSQL_IDIR := $(PROXYSQL_PATH)/include +PROXYSQL_LDIR := $(PROXYSQL_PATH)/lib -DAEMONPATH=$(DEPS_PATH)/libdaemon/libdaemon -DAEMONPATH_IDIR=$(DAEMONPATH) -DAEMONPATH_LDIR=$(DAEMONPATH)/libdaemon/.libs +DEPS_PATH := $(PROXYSQL_PATH)/deps -JEMALLOC_PATH=$(DEPS_PATH)/jemalloc/jemalloc -JEMALLOC_IDIR=$(JEMALLOC_PATH)/include/jemalloc -JEMALLOC_LDIR=$(JEMALLOC_PATH)/lib +MARIADB_PATH := $(DEPS_PATH)/mariadb-client-library/mariadb_client +MARIADB_IDIR := $(MARIADB_PATH)/include +MARIADB_LDIR := $(MARIADB_PATH)/libmariadb -LIBCONFIG_PATH=$(DEPS_PATH)/libconfig/libconfig -LIBCONFIG_IDIR=-I$(LIBCONFIG_PATH)/lib -LIBCONFIG_LDIR=-L$(LIBCONFIG_PATH)/lib/.libs +LIBDAEMON_PATH := $(DEPS_PATH)/libdaemon/libdaemon +LIBDAEMON_IDIR := $(LIBDAEMON_PATH) +LIBDAEMON_LDIR := $(LIBDAEMON_PATH)/libdaemon/.libs -PROMETHEUS_PATH=$(DEPS_PATH)/prometheus-cpp/prometheus-cpp -PROMETHEUS_IDIR=$(PROMETHEUS_PATH)/pull/include -I$(PROMETHEUS_PATH)/core/include -PROMETHEUS_LDIR=$(PROMETHEUS_PATH)/lib +JEMALLOC_PATH := $(DEPS_PATH)/jemalloc/jemalloc +JEMALLOC_IDIR := $(JEMALLOC_PATH)/include/jemalloc +JEMALLOC_LDIR := $(JEMALLOC_PATH)/lib -RE2_PATH=$(DEPS_PATH)/re2/re2 -RE2_IDIR=$(RE2_PATH) +LIBCONFIG_PATH := $(DEPS_PATH)/libconfig/libconfig +LIBCONFIG_IDIR := $(LIBCONFIG_PATH)/lib +LIBCONFIG_LDIR := $(LIBCONFIG_PATH)/lib/.libs -PCRE_PATH=$(DEPS_PATH)/pcre/pcre -PCRE_LDIR=$(PCRE_PATH)/.libs +PROMETHEUS_PATH := $(DEPS_PATH)/prometheus-cpp/prometheus-cpp +PROMETHEUS_IDIR := $(PROMETHEUS_PATH)/pull/include -I$(PROMETHEUS_PATH)/core/include +PROMETHEUS_LDIR := $(PROMETHEUS_PATH)/lib -SQLITE3_DIR=$(DEPS_PATH)/sqlite3/sqlite3 +RE2_PATH := $(DEPS_PATH)/re2/re2 +RE2_IDIR := $(RE2_PATH) +RE2_LDIR := $(RE2_PATH)/obj -CITYHASH_DIR=$(DEPS_PATH)/cityhash/cityhash/src/.libs +PCRE_PATH := $(DEPS_PATH)/pcre/pcre +PCRE_IDIR := $(PCRE_PATH) +PCRE_LDIR := $(PCRE_PATH)/.libs -LZ4_DIR=$(DEPS_PATH)/lz4/lz4/lib +SQLITE3_PATH := $(DEPS_PATH)/sqlite3/sqlite3 +SQLITE3_IDIR := $(SQLITE3_PATH) +SQLITE3_LDIR := $(SQLITE3_PATH) -CLICKHOUSE_CPP_DIR=$(DEPS_PATH)/clickhouse-cpp/clickhouse-cpp +CITYHASH_PATH := $(DEPS_PATH)/cityhash/cityhash +CITYHASH_LDIR := $(CITYHASH_PATH)/src/.libs -LIBINJECTION_DIR=$(DEPS_PATH)/libinjection/libinjection -LIBINJECTION_IDIR=$(LIBINJECTION_DIR)/src -LIBINJECTION_LDIR=$(LIBINJECTION_DIR)/src +LZ4_PATH := $(DEPS_PATH)/lz4/lz4 +LZ4_LDIR := $(LZ4_PATH)/lib -LIBHTTPSERVER_DIR=$(DEPS_PATH)/libhttpserver/libhttpserver -LIBHTTPSERVER_IDIR=$(LIBHTTPSERVER_DIR)/src -LIBHTTPSERVER_LDIR=$(LIBHTTPSERVER_DIR)/build/src/.libs/ +CLICKHOUSE_CPP_PATH := $(DEPS_PATH)/clickhouse-cpp/clickhouse-cpp +CLICKHOUSE_CPP_CDIR := $(CLICKHOUSE_CPP_PATH)/contrib +CLICKHOUSE_CPP_IDIR := $(CLICKHOUSE_CPP_PATH) +CLICKHOUSE_CPP_LDIR := $(CLICKHOUSE_CPP_PATH)/clickhouse -MICROHTTPD_DIR=$(DEPS_PATH)/libmicrohttpd/libmicrohttpd/src -MICROHTTPD_IDIR=$(MICROHTTPD_DIR)/include -MICROHTTPD_LDIR=$(MICROHTTPD_DIR)/microhttpd/.libs +LIBINJECTION_PATH := $(DEPS_PATH)/libinjection/libinjection +LIBINJECTION_IDIR := $(LIBINJECTION_PATH)/src +LIBINJECTION_LDIR := $(LIBINJECTION_PATH)/src -COREDUMPER_DIR=$(DEPS_PATH)/coredumper/coredumper -COREDUMPER_IDIR=$(COREDUMPER_DIR)/include -COREDUMPER_LDIR=$(COREDUMPER_DIR)/src +LIBHTTPSERVER_PATH := $(DEPS_PATH)/libhttpserver/libhttpserver +LIBHTTPSERVER_IDIR := $(LIBHTTPSERVER_PATH)/src +LIBHTTPSERVER_LDIR := $(LIBHTTPSERVER_PATH)/build/src/.libs/ -CURL_DIR=$(DEPS_PATH)/curl/curl -CURL_IDIR=$(CURL_DIR)/include -CURL_LDIR=$(CURL_DIR)/lib/.libs +MICROHTTPD_PATH := $(DEPS_PATH)/libmicrohttpd/libmicrohttpd/src +MICROHTTPD_IDIR := $(MICROHTTPD_PATH)/include +MICROHTTPD_LDIR := $(MICROHTTPD_PATH)/microhttpd/.libs -SSL_DIR=$(DEPS_PATH)/libssl/openssl/ -SSL_IDIR=$(SSL_DIR)/include -SSL_LDIR=$(SSL_DIR) +COREDUMPER_PATH := $(DEPS_PATH)/coredumper/coredumper +COREDUMPER_IDIR := $(COREDUMPER_PATH)/include +COREDUMPER_LDIR := $(COREDUMPER_PATH)/src +CURL_PATH := $(DEPS_PATH)/curl/curl +CURL_IDIR := $(CURL_PATH)/include +CURL_LDIR := $(CURL_PATH)/lib/.libs -EV_DIR=$(DEPS_PATH)/libev/libev/ -EV_IDIR=$(EV_DIR) -EV_LDIR=$(EV_DIR)/.libs +SSL_PATH := $(DEPS_PATH)/libssl/openssl/ +SSL_IDIR := $(SSL_PATH)/include +SSL_LDIR := $(SSL_PATH) -IDIR=../include -LDIR=../lib -IDIRS=-I$(IDIR) -I$(JEMALLOC_IDIR) -I$(MARIADB_IDIR) $(LIBCONFIG_IDIR) -I$(DAEMONPATH_IDIR) -I$(SQLITE3_DIR) -I$(CLICKHOUSE_CPP_DIR) -I$(CLICKHOUSE_CPP_DIR)/contrib/ -I$(MICROHTTPD_IDIR) -I$(LIBHTTPSERVER_IDIR) -I$(LIBINJECTION_IDIR) -I$(CURL_IDIR) -I$(EV_IDIR) -I$(SSL_IDIR) -I$(RE2_IDIR) -I$(PROMETHEUS_IDIR) -LDIRS=-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) -L$(PROMETHEUS_LDIR) +EV_PATH := $(DEPS_PATH)/libev/libev/ +EV_IDIR := $(EV_PATH) +EV_LDIR := $(EV_PATH)/.libs -UNAME_S := $(shell uname -s) +IDIRS := -I$(PROXYSQL_IDIR) -I$(JEMALLOC_IDIR) -I$(MARIADB_IDIR) -I$(LIBCONFIG_IDIR) -I$(LIBDAEMON_IDIR) -I$(RE2_IDIR) -L$(PCRE_IDIR) -I$(MICROHTTPD_IDIR) -I$(LIBHTTPSERVER_IDIR) -I$(LIBINJECTION_IDIR) -I$(CURL_IDIR) -I$(EV_IDIR) -I$(SSL_IDIR) -I$(PROMETHEUS_IDIR) -I$(SQLITE3_IDIR) -I$(CLICKHOUSE_CPP_IDIR) -I$(CLICKHOUSE_CPP_CDIR) -I$(0) +LDIRS := -L$(PROXYSQL_LDIR) -L$(JEMALLOC_LDIR) -L$(MARIADB_LDIR) -L$(LIBCONFIG_LDIR) -L$(LIBDAEMON_LDIR) -L$(RE2_LDIR) -L$(PCRE_LDIR) -L$(MICROHTTPD_LDIR) -L$(LIBHTTPSERVER_LDIR) -L$(LIBINJECTION_LDIR) -L$(CURL_LDIR) -L$(EV_LDIR) -L$(SSL_LDIR) -L$(PROMETHEUS_LDIR) + + +UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Linux) - LDIRS+= -L$(COREDUMPER_LDIR) + LDIRS += -L$(COREDUMPER_LDIR) endif - ifeq ($(UNAME_S),Darwin) - IDIRS+= -I/usr/local/include - LDIRS+= -L/usr/local/lib -L/opt/homebrew/lib + IDIRS += -I/usr/local/include + LDIRS += -L/usr/local/lib -L/opt/homebrew/lib endif -# determine good compiler version for stdc++17 -IS_CXX17 := 0 -ifeq ($(CXX),clang++) - CLANG_VERSION := $(shell clang -dumpversion | tr '.' ' ' | awk '{ printf("%04d.%04d.%04d", $$1, $$2, $$3) }') - CLANG_MIN_VER := $(shell echo 14.0 | tr '.' ' ' | awk '{ printf("%04d.%04d.%04d", $$1, $$2, $$3) }') -ifeq ($(CLANG_MIN_VER),$(firstword $(sort $(CLANG_VERSION) $(CLANG_MIN_VER)))) - IS_CXX17 := 1 -endif -else - GCC_VERSION := $(shell gcc -dumpfullversion 2>/dev/null || gcc -dumpversion | tr '.' ' ' | awk '{ printf("%04d.%04d.%04d", $$1, $$2, $$3) }') - GCC_MIN_VER := $(shell echo 8.2.0 | tr '.' ' ' | awk '{ printf("%04d.%04d.%04d", $$1, $$2, $$3) }') -ifeq ($(GCC_MIN_VER),$(firstword $(sort $(GCC_VERSION) $(GCC_MIN_VER)))) - IS_CXX17 := 1 +### detect compiler support for c++11/17 +CPLUSPLUS := $(shell ${CC} -std=c++17 -dM -E -x c++ /dev/null 2>/dev/null | grep -F __cplusplus | grep -Po '\d\d\d\d\d\dL') +ifneq ($(CPLUSPLUS),201703L) + CPLUSPLUS := $(shell ${CC} -std=c++11 -dM -E -x c++ /dev/null 2>/dev/null| grep -F __cplusplus | grep -Po '\d\d\d\d\d\dL') +ifneq ($(CPLUSPLUS),201103L) +$(error Compiler must support at least c++11) endif endif +STDCPP := -std=c++$(shell echo $(CPLUSPLUS) | cut -c3-4) -DCXX$(shell echo $(CPLUSPLUS) | cut -c3-4) -PROXYSQLCLICKHOUSE := $(shell echo $(PROXYSQLCLICKHOUSE)) -PSQLCH= -ifeq ($(PROXYSQLCLICKHOUSE),1) - PSQLCH=-DPROXYSQLCLICKHOUSE -ifeq ($(IS_CXX17),1) - PSQLCH=-DPROXYSQLCLICKHOUSE -std=c++17 -DCXX17 +### handle clang issue https://bugs.llvm.org/show_bug.cgi?id=16404 +CLANGFIX := +ifeq ($(CXX),clang++) + CLANG_VERSION := $(shell clang -dumpversion | tr '.' ' ' | awk '{ printf("%04d.%04d.%04d", $$1, $$2, $$3) }') + CLANG_MIN_VER := $(shell echo 14.0 | tr '.' ' ' | awk '{ printf("%04d.%04d.%04d", $$1, $$2, $$3) }') +ifneq ($(CLANG_MIN_VER),$(firstword $(sort $(CLANG_VERSION) $(CLANG_MIN_VER)))) + CLANGFIX := --rtlib=compiler-rt --unwindlib=libgcc endif endif - -WITHGCOVVAR := $(shell echo $(WITHGCOV)) -ifeq ($(WITHGCOVVAR),1) -WGCOV=-DWITHGCOV --coverage -else -WGCOV= +PSQLCH := +ifeq ($(PROXYSQLCLICKHOUSE),1) + PSQLCH := -DPROXYSQLCLICKHOUSE endif -WITHASANVAR := $(shell echo $(WITHASAN)) -ifeq ($(WITHASANVAR),1) -WASAN= -fsanitize=address -# Force the disable of JEMALLOC, since ASAN isn't compatible. -export NOJEMALLOC = 1 -else -WASAN= +WGCOV := +ifeq ($(WITHGCOV),1) + WGCOV := -DWITHGCOV -lgcov --coverage endif -TEST_WITHASANVAR := $(shell echo $(TEST_WITHASAN)) -ifeq ($(TEST_WITHASANVAR),1) -WASAN+= -DTEST_WITHASAN +WASAN := +ifeq ($(WITHASAN),1) + WASAN := -fsanitize=address + # Force the disable of JEMALLOC, since ASAN isn't compatible. + export NOJEMALLOC=1 endif - -MYCXXFLAGS=-std=c++11 -ifeq ($(CXX),clang++) - MYCXXFLAGS+= -fuse-ld=lld +ifeq ($(TEST_WITHASAN),1) + WASAN += -DTEST_WITHASAN endif -MYCXXFLAGS+= $(IDIRS) $(OPTZ) $(DEBUG) $(PSQLCH) -DGITVERSION=\"$(GIT_VERSION)\" $(WGCOV) $(WASAN) -ifeq ($(WITHGCOVVAR),1) -LDFLAGS+= -lgcov --coverage +MYCXXFLAGS := $(STDCPP) +ifeq ($(CXX),clang++) + MYCXXFLAGS += -fuse-ld=lld endif +MYCXXFLAGS += $(IDIRS) $(OPTZ) $(DEBUG) $(PSQLCH) -DGITVERSION=\"$(GIT_VERSION)\" $(WGCOV) $(WASAN) -ifeq ($(WITHASANVAR),1) -LDFLAGS+= -fsanitize=address -endif -NOJEMALLOC := $(shell echo $(NOJEMALLOC)) -STATICMYLIBS=-Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lcurl -lssl -lcrypto -lev -ifeq ($(NOJEMALLOC),1) -else - STATICMYLIBS+= -ljemalloc +STATICMYLIBS := -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lcurl -lssl -lcrypto -lev +ifneq ($(NOJEMALLOC),1) + STATICMYLIBS += -ljemalloc endif ifeq ($(UNAME_S),Linux) - STATICMYLIBS+= -lcoredumper + STATICMYLIBS += -lcoredumper endif -MYLIBS=-Wl,--export-dynamic $(STATICMYLIBS) -Wl,-Bdynamic -lgnutls -lpthread -lm -lz -lrt -lprometheus-cpp-pull -lprometheus-cpp-core -luuid $(EXTRALINK) +MYLIBS := -Wl,--export-dynamic $(STATICMYLIBS) -Wl,-Bdynamic -lgnutls -lpthread -lm -lz -lrt -lprometheus-cpp-pull -lprometheus-cpp-core -luuid $(EXTRALINK) ifeq ($(UNAME_S),Darwin) - MYLIBS=-lre2 -lmariadbclient -lpthread -lm -lz -liconv -lgnutls -lprometheus-cpp-pull -lprometheus-cpp-core -luuid + MYLIBS :=-lre2 -lmariadbclient -lpthread -lm -lz -liconv -lgnutls -lprometheus-cpp-pull -lprometheus-cpp-core -luuid else - CURL_DIR=$(DEPS_PATH)/curl/curl - IDIRS+= -L$(CURL_DIR)/include - LDIRS+= -L$(CURL_DIR)/lib/.libs + CURL_DIR := $(DEPS_PATH)/curl/curl + IDIRS += -L$(CURL_DIR)/include + LDIRS += -L$(CURL_DIR)/lib/.libs endif ifeq ($(UNAME_S),Linux) - MYLIBS+= -ldl + MYLIBS += -ldl endif ifeq ($(UNAME_S),FreeBSD) - MYLIBS+= -lexecinfo + MYLIBS += -lexecinfo endif ifeq ($(CENTOSVER),6) - MYLIBS+= -lgcrypt + MYLIBS += -lgcrypt endif -LIBPROXYSQLAR=$(LDIR)/libproxysql.a +LIBPROXYSQLAR := $(PROXYSQL_LDIR)/libproxysql.a ifeq ($(UNAME_S),Darwin) - LIBPROXYSQLAR=$(LDIR)/libproxysql.a ../deps/jemalloc/jemalloc/lib/libjemalloc.a ../deps/libmicrohttpd/libmicrohttpd/src/microhttpd/.libs/libmicrohttpd.a ../deps/libhttpserver/libhttpserver/build/src/.libs/libhttpserver.a ../deps/pcre/pcre/.libs/libpcre.a ../deps/pcre/pcre/.libs/libpcrecpp.a ../deps/libdaemon/libdaemon/libdaemon/.libs/libdaemon.a ../deps/libconfig/libconfig/lib/.libs/libconfig++.a ../deps/libconfig/libconfig/lib/.libs/libconfig.a ../deps/curl/curl/lib/.libs/libcurl.a ../deps/sqlite3/sqlite3/sqlite3.o ../deps/libinjection/libinjection/src/libinjection.a ../deps/libhttpserver/libhttpserver/build/src/.libs/libhttpserver.a ../deps/libev/libev/.libs/libev.a -endif - -LIBPROXYSQLAR+= $(SSL_LDIR)/libssl.a $(SSL_LDIR)/libcrypto.a $(CITYHASH_DIR)/libcityhash.a -ODIR= obj - -EXECUTABLE=proxysql - -_OBJ = main.o proxysql_global.o SQLite3_Server.o proxy_tls.o -OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ)) + LIBPROXYSQLAR += $(JEMALLOC_LDIR)/libjemalloc.a + LIBPROXYSQLAR += $(MICROHTTPD_LDIR)/libmicrohttpd.a + LIBPROXYSQLAR += $(LIBHTTPSERVER_LDIR)/libhttpserver.a + LIBPROXYSQLAR += $(PCRE_LDIR)/libpcre.a + LIBPROXYSQLAR += $(PCRE_LDIR)/libpcrecpp.a + LIBPROXYSQLAR += $(LIBDAEMON_LDIR)/libdaemon.a + LIBPROXYSQLAR += $(LIBCONFIG_LDIR)/libconfig++.a + LIBPROXYSQLAR += $(LIBCONFIG_LDIR)/libconfig.a + LIBPROXYSQLAR += $(CURL_LDIR)/libcurl.a + LIBPROXYSQLAR += $(SQLITE3_LDIR)/sqlite3.o + LIBPROXYSQLAR += $(LIBINJECTION_LDIR)/libinjection.a + LIBPROXYSQLAR += $(EV_LDIR)/libev.a +endif +LIBPROXYSQLAR += $(SSL_LDIR)/libssl.a +LIBPROXYSQLAR += $(SSL_LDIR)/libcrypto.a +LIBPROXYSQLAR += $(CITYHASH_LDIR)/libcityhash.a + +ODIR := obj + +EXECUTABLE := proxysql + +_OBJ := main.o proxysql_global.o SQLite3_Server.o proxy_tls.o +OBJ := $(patsubst %,$(ODIR)/%,$(_OBJ)) $(ODIR)/%.o: %.cpp $(CXX) -c -o $@ $< $(MYCXXFLAGS) $(CXXFLAGS) -Wall $(EXECUTABLE): $(ODIR) $(OBJ) $(LIBPROXYSQLAR) ifeq ($(PROXYSQLCLICKHOUSE),1) - $(CXX) -o $@ $(OBJ) $(LIBPROXYSQLAR) $(CLICKHOUSE_CPP_DIR)/clickhouse/libclickhouse-cpp-lib-static.a $(LZ4_DIR)/liblz4.a $(MYCXXFLAGS) $(CXXFLAGS) $(LDIRS) $(LIBS) $(LDFLAGS) $(MYLIBS) + $(CXX) -o $@ $(OBJ) $(CLANGFIX) $(LIBPROXYSQLAR) $(CLICKHOUSE_CPP_LDIR)/libclickhouse-cpp-lib-static.a $(LZ4_LDIR)/liblz4.a $(MYCXXFLAGS) $(CXXFLAGS) $(LDIRS) $(LIBS) $(MYLIBS) else - $(CXX) -o $@ $(OBJ) $(LIBPROXYSQLAR) $(MYCXXFLAGS) $(CXXFLAGS) $(LDIRS) $(LIBS) $(LDFLAGS) $(MYLIBS) + $(CXX) -o $@ $(OBJ) $(CLANGFIX) $(LIBPROXYSQLAR) $(MYCXXFLAGS) $(CXXFLAGS) $(LDIRS) $(LIBS) $(MYLIBS) endif ifeq ($(UNAME_S),Darwin) shasum $(EXECUTABLE) > $(EXECUTABLE).sha1 else sha1sum $(EXECUTABLE) > $(EXECUTABLE).sha1 endif + $(ODIR): mkdir $(ODIR) $(LIBPROXYSQLAR): - cd $(LDIR) && ${MAKE} + cd $(PROXYSQL_LDIR) && ${MAKE} + + +### main targets default: $(EXECUTABLE) diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index 44566aa5b7..ad269a6078 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -33,6 +33,8 @@ #include #include +using std::string; + #define SELECT_VERSION_COMMENT "select @@version_comment limit 1" #define SELECT_VERSION_COMMENT_LEN 32 #define SELECT_DB_USER "select DATABASE(), USER() limit 1" @@ -317,6 +319,44 @@ bool match_monitor_query(const std::string& monitor_query, const std::string& qu } #endif // TEST_GROUPREP +#ifdef TEST_AURORA + +using std::vector; + +using aurora_hg_info_t = std::tuple; +enum AURORA_HG_INFO { + WRITER_HG, + READER_HG, + DOMAIN_NAME +}; + +vector get_hgs_info(SQLite3DB* db) { + vector whgs {}; + + char* error = NULL; + int cols = 0; + int affected_rows = 0; + SQLite3_result* resultset = NULL; + + GloAdmin->admindb->execute_statement( + "SELECT writer_hostgroup,reader_hostgroup,domain_name FROM mysql_aws_aurora_hostgroups", + &error, &cols, &affected_rows, &resultset + ); + + for (const SQLite3_row* r : resultset->rows) { + uint32_t writer_hg = atoi(r->fields[0]); + uint32_t reader_hg = atoi(r->fields[1]); + string domain_name { r->fields[2] }; + + whgs.push_back({writer_hg, reader_hg, domain_name}); + } + + return whgs; +} + + +#endif + void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *pkt) { char *error=NULL; @@ -735,7 +775,34 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p #ifdef TEST_AURORA if (strstr(query_no_space,(char *)"REPLICA_HOST_STATUS")) { pthread_mutex_lock(&GloSQLite3Server->aurora_mutex); - GloSQLite3Server->populate_aws_aurora_table(sess); + + if (strcasestr(query_no_space, TEST_AURORA_MONITOR_BASE_QUERY)) { + string s_whg { query_no_space + strlen(TEST_AURORA_MONITOR_BASE_QUERY) }; + uint32_t whg = atoi(s_whg.c_str()); + + GloSQLite3Server->populate_aws_aurora_table(sess, whg); + vector hgs_info { get_hgs_info(GloAdmin->admindb) }; + + const auto match_writer = [&whg](const aurora_hg_info_t& hg_info) { + return std::get(hg_info) == whg; + }; + const auto hg_info_it = std::find_if(hgs_info.begin(), hgs_info.end(), match_writer); + string select_query { + "SELECT SERVER_ID,SESSION_ID,LAST_UPDATE_TIMESTAMP,REPLICA_LAG_IN_MILLISECONDS,CPU" + " FROM REPLICA_HOST_STATUS " + }; + + if (hg_info_it == hgs_info.end()) { + select_query += " LIMIT 0"; + } else { + const string& domain_name { std::get(*hg_info_it) }; + select_query += " WHERE DOMAIN_NAME='" + domain_name + "' ORDER BY SERVER_ID"; + } + + free(query); + query = static_cast(malloc(select_query.length() + 1)); + strcpy(query, select_query.c_str()); + } } #endif // TEST_AURORA #ifdef TEST_GALERA @@ -751,7 +818,7 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p // NOTE: This query should be in one place that can be reused by // 'ProxySQL_Monitor' module. const std::string grouprep_monitor_test_query_start { - "SELECT viable_candidate,read_only,transactions_behind " + "SELECT viable_candidate,read_only,transactions_behind,members " "FROM GR_MEMBER_ROUTING_CANDIDATE_STATUS " }; @@ -769,14 +836,15 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p free(query); std::string t_select_as_query { - "SELECT '%s' AS viable_candidate, '%s' AS read_only, %d AS transactions_behind" + "SELECT '%s' AS viable_candidate, '%s' AS read_only, %d AS transactions_behind, '%s' AS members" }; std::string select_as_query {}; string_format( t_select_as_query, select_as_query, std::get<0>(gr_srv_status) ? "YES" : "NO", std::get<1>(gr_srv_status) ? "YES" : "NO", - std::get<2>(gr_srv_status) + std::get<2>(gr_srv_status), + std::get<3>(gr_srv_status).c_str() ); query = static_cast(malloc(select_as_query.length() + 1)); @@ -842,10 +910,12 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p #ifdef TEST_AURORA if (strstr(query_no_space,(char *)"REPLICA_HOST_STATUS")) { pthread_mutex_unlock(&GloSQLite3Server->aurora_mutex); +#ifdef TEST_AURORA_RANDOM if (rand() % 100 == 0) { // randomly add some latency on 1% of the traffic sleep(2); } +#endif } #endif // TEST_AURORA #ifdef TEST_GALERA @@ -913,7 +983,7 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p #ifdef TEST_GROUPREP group_rep_status SQLite3_Server::grouprep_test_value(const std::string& srv_addr) { - group_rep_status cur_srv_st { "YES", "YES", 0 }; + group_rep_status cur_srv_st { "YES", "YES", 0, "" }; auto it = grouprep_map.find(srv_addr); if (it != grouprep_map.end()) { @@ -1378,27 +1448,187 @@ void SQLite3_Server::populate_galera_table(MySQL_Session *sess) { #endif // TEST_GALERA #ifdef TEST_AURORA -void SQLite3_Server::populate_aws_aurora_table(MySQL_Session *sess) { - // this function needs to be called with lock on mutex aurora_mutex already acquired - sessdb->execute("DELETE FROM REPLICA_HOST_STATUS"); - sqlite3_stmt *statement=NULL; - //sqlite3 *mydb3=sessdb->get_db(); - int rc; - char *query=(char *)"INSERT INTO REPLICA_HOST_STATUS VALUES (?1, ?2, ?3, ?4, ?5)"; - //rc=sqlite3_prepare_v2(mydb3, query, -1, &statement, 0); - rc = sessdb->prepare_v2(query, &statement); - ASSERT_SQLITE_OK(rc, sessdb); + +float get_rand_cpu() { + int cpu_i = rand() % 10000; + float cpu = static_cast(cpu_i) / 100; + + return cpu; +} + +string get_curtime_str() { time_t __timer; char lut[30]; struct tm __tm_info; time(&__timer); localtime_r(&__timer, &__tm_info); strftime(lut, 25, "%Y-%m-%d %H:%M:%S", &__tm_info); + string s = string(lut); + return s; +} + +void bind_query_params( + SQLite3DB* db, + sqlite3_stmt* stmt, + const string& server_id, + const string& domain, + const string& session_id, + float cpu, + const string& lut, + int32_t lag_ms +) { + int rc = 0; + + rc=sqlite3_bind_text(stmt, 1, server_id.c_str(), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); + rc=sqlite3_bind_text(stmt, 2, domain.c_str(), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); + rc=sqlite3_bind_text(stmt, 3, session_id.c_str(), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); + rc=sqlite3_bind_double(stmt, 4, cpu); ASSERT_SQLITE_OK(rc, db); + rc=sqlite3_bind_text(stmt, 5, lut.c_str(), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, db); + rc=sqlite3_bind_double(stmt, 6, lag_ms); ASSERT_SQLITE_OK(rc, db); + SAFE_SQLITE3_STEP2(stmt); + rc=sqlite3_clear_bindings(stmt); ASSERT_SQLITE_OK(rc, db); + rc=sqlite3_reset(stmt); ASSERT_SQLITE_OK(rc, db); +} + +/** + * @brief Extracts SERVER_ID from the supplied hostname using DOMAIN_NAME. + * @param hostname The server hostname (SERVER_ID + DOMAIN_NAME)). + * @param domain_name The server DOMAIN_NAME as in 'mysql_aws_aurora_hostgroups' + * @return Either the SERVER_ID in the supplied hostname or empty if DOMAIN_NAME failed to match. + */ +string get_server_id(const string& hostname, const string& domain_name) { + string::size_type pos = hostname.find(domain_name); + + if (pos == string::npos) { + return {}; + } else { + return hostname.substr(0, pos); + } +} + +void SQLite3_Server::populate_aws_aurora_table(MySQL_Session *sess, uint32_t whg) { + int rc = 0; + sqlite3_stmt* stmt = NULL; + const char query[] { "INSERT INTO REPLICA_HOST_STATUS VALUES (?1, ?2, ?3, ?4, ?5, ?6)" }; + + rc = sessdb->prepare_v2(query, &stmt); + ASSERT_SQLITE_OK(rc, sessdb); + +#ifndef TEST_AURORA_RANDOM + SQLite3_result* host_status = NULL; + + { + char* error = NULL; + int cols = 0; + int affected_rows = 0; + + string query { + "SELECT SERVER_ID,DOMAIN_NAME,SESSION_ID,LAST_UPDATE_TIMESTAMP,REPLICA_LAG_IN_MILLISECONDS" + " FROM REPLICA_HOST_STATUS" + }; + sessdb->execute_statement(query.c_str(), &error, &cols, &affected_rows, &host_status); + } + + // If empty, we fill the map with sensible defaults for performing manual testing. + if (host_status->rows.empty()) { + vector hgs_info { get_hgs_info(GloAdmin->admindb) }; + SQLite3_result* resultset = nullptr; + + { + char* error = nullptr; + int cols = 0; + int affected_rows = 0; + + GloAdmin->admindb->execute_statement( + "SELECT hostname, hostgroup_id FROM mysql_servers WHERE hostgroup_id BETWEEN 1270 AND 1300" + " GROUP BY HOSTNAME", + &error, &cols, &affected_rows, &resultset + ); + } + + sessdb->execute("DELETE FROM REPLICA_HOST_STATUS"); + vector proc_srvs {}; + + for (const aurora_hg_info_t& hg_info : hgs_info) { + const auto match_writer = [&hg_info](const SQLite3_row* row) { + return atoi(row->fields[1]) == std::get(hg_info); + }; + const auto mysrv_it = std::find_if(resultset->rows.begin(), resultset->rows.end(), match_writer); + bool writer_set = false; + + for (const SQLite3_row* r : resultset->rows) { + const string srv_hostname { r->fields[0] }; + const uint32_t srv_hg_id = atoi(r->fields[1]); + const string& aurora_domain { std::get(hg_info) }; + + if ( + srv_hostname.find(aurora_domain) == string::npos || + std::find(proc_srvs.begin(), proc_srvs.end(), srv_hostname) != proc_srvs.end() + ) { + continue; + } + + const string server_id { + get_server_id(srv_hostname, std::get(hg_info)) + }; + + string session_id {}; + + if ( + (mysrv_it == resultset->rows.end() && writer_set == false) || + (srv_hg_id == std::get(hg_info) && writer_set == false) + ) { + session_id = "MASTER_SESSION_ID"; + writer_set = true; + } else { + session_id = "TESTID-" + server_id + aurora_domain + "-R"; + } + + const float cpu = get_rand_cpu(); + const string lut { get_curtime_str() }; + const int lag_ms = 0; + + bind_query_params(sessdb, stmt, server_id, aurora_domain, session_id, cpu, lut, lag_ms); + proc_srvs.push_back(srv_hostname); + } + } + + sqlite3_finalize(stmt); + delete resultset; + } else { + // We just re-generate deterministic 'SESSION_IDS', preserving 'MASTER_SESSION_ID' values: + // 'SESSION_IDS' are preserved, 'MASTER_SESSION_ID' or others. + for (SQLite3_row* row : host_status->rows) { + const char* server_id = row->fields[0]; + const char* domain_name = row->fields[1]; + + const char update_query_t[] { + "UPDATE REPLICA_HOST_STATUS SET SESSION_ID='%s',CPU=%f,LAST_UPDATE_TIMESTAMP='%s'" + " WHERE SERVER_ID='%s' AND DOMAIN_NAME='%s' AND SESSION_ID!='MASTER_SESSION_ID'" + }; + + const string session_id { "TESTID-" + string { server_id } + domain_name + "-R" }; + const float cpu = get_rand_cpu(); + const string lut { get_curtime_str() }; + + const string update_query { + cstr_format(update_query_t, session_id.c_str(), cpu, lut.c_str(), server_id, domain_name).str + }; + + sessdb->execute(update_query.c_str()); + } + } + + delete host_status; +#else + sessdb->execute("DELETE FROM REPLICA_HOST_STATUS"); + + string lut { get_curtime_str() }; string myip = string(sess->client_myds->proxy_addr.addr); string clu_id_s = myip.substr(6,1); unsigned int cluster_id = atoi(clu_id_s.c_str()); cluster_id--; - //if (rand() % 200 == 0) { + if (rand() % 20000 == 0) { // simulate a failover cur_aurora_writer[cluster_id] = rand() % num_aurora_servers[cluster_id]; @@ -1419,14 +1649,12 @@ void SQLite3_Server::populate_aws_aurora_table(MySQL_Session *sess) { } } for (unsigned int i=0; ifields[2] } == "YES" ? true : false, std::string { r->fields[3] } == "YES" ? true : false, - atoi(r->fields[4]) + atoi(r->fields[4]), + std::string { r->fields[5] } }; this->grouprep_map[srv_addr] = srv_status; @@ -1509,16 +1730,16 @@ void SQLite3_Server::populate_grouprep_table(MySQL_Session *sess, int txs_behind int hostgroup_id = atoi(r->fields[2]); const std::string t_insert_query { "INSERT INTO GR_MEMBER_ROUTING_CANDIDATE_STATUS" - " (hostname, port, viable_candidate, read_only, transactions_behind) VALUES" - " ('%s', %d, '%s', '%s', 0)" + " (hostname, port, viable_candidate, read_only, transactions_behind, members) VALUES" + " ('%s', %d, '%s', '%s', 0, '%s')" }; std::string insert_query {}; if (hostgroup_id % 4 == 0) { - string_format(t_insert_query, insert_query, hostname.c_str(), port, "YES", "NO"); + string_format(t_insert_query, insert_query, hostname.c_str(), port, "YES", "NO", ""); sessdb->execute(insert_query.c_str()); } else { - string_format(t_insert_query, insert_query, hostname.c_str(), port, "YES", "YES"); + string_format(t_insert_query, insert_query, hostname.c_str(), port, "YES", "YES", ""); sessdb->execute(insert_query.c_str()); } } @@ -1582,7 +1803,12 @@ bool SQLite3_Server::init() { tables_defs_aurora = new std::vector; insert_into_tables_defs(tables_defs_aurora, (const char *)"REPLICA_HOST_STATUS", - (const char *)"CREATE TABLE REPLICA_HOST_STATUS (SERVER_ID VARCHAR NOT NULL, SESSION_ID VARCHAR NOT NULL, CPU REAL NOT NULL, LAST_UPDATE_TIMESTAMP VARCHAR NOT NULL, REPLICA_LAG_IN_MILLISECONDS REAL NOT NULL)"); + "CREATE TABLE REPLICA_HOST_STATUS (" + " SERVER_ID VARCHAR NOT NULL , DOMAIN_NAME VARCHAR NOT NULL , SESSION_ID VARCHAR NOT NULL ," + " CPU REAL NOT NULL , LAST_UPDATE_TIMESTAMP VARCHAR NOT NULL , REPLICA_LAG_IN_MILLISECONDS REAL NOT NULL ," + " PRIMARY KEY (SERVER_ID, DOMAIN_NAME)" + ")" + ); check_and_build_standard_tables(sessdb, tables_defs_aurora); GloAdmin->enable_aurora_testing(); #endif // TEST_AURORA @@ -1599,7 +1825,7 @@ bool SQLite3_Server::init() { insert_into_tables_defs(tables_defs_grouprep, (const char *)"GR_MEMBER_ROUTING_CANDIDATE_STATUS", (const char*)"CREATE TABLE GR_MEMBER_ROUTING_CANDIDATE_STATUS (" - "hostname VARCHAR NOT NULL, port INT NOT NULL, viable_candidate varchar not null, read_only varchar not null, transactions_behind int not null, PRIMARY KEY (hostname, port)" + "hostname VARCHAR NOT NULL, port INT NOT NULL, viable_candidate varchar not null, read_only varchar not null, transactions_behind int not null, members VARCHAR NOT NULL, PRIMARY KEY (hostname, port)" ")" ); diff --git a/src/main.cpp b/src/main.cpp index 2fdf97e945..bd21a42d5c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,8 @@ #include "btree_map.h" #include "proxysql.h" +#include +#include #include #include #include @@ -11,6 +13,8 @@ //#define PROXYSQL_EXTERN #include "cpp.h" +#include "mysqld_error.h" + #include "ProxySQL_Statistics.hpp" #include "MySQL_PreparedStatement.h" #include "ProxySQL_Cluster.hpp" @@ -21,6 +25,7 @@ #include "MySQL_LDAP_Authentication.hpp" #include "proxysql_restapi.h" #include "Web_Interface.hpp" +#include "proxysql_utils.h" #include #include @@ -44,7 +49,9 @@ extern "C" MySQL_LDAP_Authentication * create_MySQL_LDAP_Authentication_func() { */ - +using std::map; +using std::string; +using std::vector; volatile create_MySQL_LDAP_Authentication_t * create_MySQL_LDAP_Authentication = NULL; @@ -563,6 +570,30 @@ void ProxySQL_Main_process_global_variables(int argc, const char **argv) { GloVars.ldap_auth_plugin=strdup(ldap_auth_plugin.c_str()); } } + const map varnames_globals_map { + { "mysql-ssl_p2s_ca", &GloVars.global.gr_bootstrap_ssl_ca }, + { "mysql-ssl_p2s_capath", &GloVars.global.gr_bootstrap_ssl_capath }, + { "mysql-ssl_p2s_cert", &GloVars.global.gr_bootstrap_ssl_cert }, + { "mysql-ssl_p2s_key", &GloVars.global.gr_bootstrap_ssl_key }, + { "mysql-ssl_p2s_cipher", &GloVars.global.gr_bootstrap_ssl_cipher }, + { "mysql-ssl_p2s_crl", &GloVars.global.gr_bootstrap_ssl_crl }, + { "mysql-ssl_p2s_crlpath", &GloVars.global.gr_bootstrap_ssl_crlpath } + }; + // Command line options always take precedence + if (GloVars.global.gr_bootstrap_mode && root.exists("mysql_variables")) { + const Setting& mysql_vars = root["mysql_variables"]; + + for (const pair& name_global : varnames_globals_map) { + for (const auto& setting_it : mysql_vars) { + if (*name_global.second == nullptr) { + if (setting_it.getName() == name_global.first && setting_it.isString()) { + const char* setting_val = setting_it.c_str(); + *name_global.second = strdup(setting_val); + } + } + } + } + } } else { proxy_warning("Unable to open config file %s\n", GloVars.config_file); // issue #705 if (GloVars.__cmd_proxysql_config_file) { @@ -672,7 +703,7 @@ void ProxySQL_Main_init_main_modules() { } -void ProxySQL_Main_init_Admin_module() { +void ProxySQL_Main_init_Admin_module(const bootstrap_info_t& bootstrap_info) { // cluster module needs to be initialized before GloProxyCluster = new ProxySQL_Cluster(); GloProxyCluster->init(); @@ -681,7 +712,7 @@ void ProxySQL_Main_init_Admin_module() { //GloProxyStats->init(); GloProxyStats->print_version(); GloAdmin = new ProxySQL_Admin(); - GloAdmin->init(); + GloAdmin->init(bootstrap_info); GloAdmin->print_version(); if (binary_sha1) { proxy_info("ProxySQL SHA1 checksum: %s\n", binary_sha1); @@ -934,7 +965,9 @@ void ProxySQL_Main_init() { static void LoadPlugins() { GloMyLdapAuth = NULL; - SQLite3DB::LoadPlugin(GloVars.sqlite3_plugin); + if (proxy_sqlite3_open_v2 == nullptr) { + SQLite3DB::LoadPlugin(GloVars.sqlite3_plugin); + } if (GloVars.web_interface_plugin) { dlerror(); char * dlsym_error = NULL; @@ -1017,14 +1050,14 @@ void UnloadPlugins() { } } -void ProxySQL_Main_init_phase2___not_started() { +void ProxySQL_Main_init_phase2___not_started(const bootstrap_info_t& boostrap_info) { std::string msg; ProxySQL_create_or_load_TLS(false, msg); LoadPlugins(); ProxySQL_Main_init_main_modules(); - ProxySQL_Main_init_Admin_module(); + ProxySQL_Main_init_Admin_module(boostrap_info); GloMTH->print_version(); { @@ -1327,6 +1360,351 @@ namespace { static const bool SET_TERMINATE = std::set_terminate(my_terminate); } +/** + * @brief Regex for parsing URI connections. + * @details Groups explanation: + * + HierPart doesn't hold '//' making previous non-capturing group optional. E.g: + * - '127.0.0.1:3306' + * - 'mysql-server-1:3306' + * + UserInfo is inside a non-capturing group to avoid matching '@'. + * + Host,Port groups are inside a non-capturing group to allow URI like: 'mysql://user:pass@' + * + RegName matches any valid Ipv4 or domain name. + * + Ipv6 matches Ipv6, it's NOT spec conforming, we don't verify the supplied Ip in the regex. + * + Port matches the supplied port. + * + Optionally match a supplied (/). + * + Ensure match termination in HierPart group, forcing conditional subgroups matching. + */ +const char CONN_URI_REGEX[] { + "^(:?(?P[a-z][a-z0-9\\+\\-\\.]*):\\/\\/)?" + "(?P" + "(?:(?P(?:\\%[0-9a-f][0-9a-f]|[a-z0-9\\-\\.\\_\\~]|[\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\=]|\\:)*)\\@)?" + "(:?" + "(?P" + "(?P(?:\\%[0-9a-f][0-9a-f]|[a-z0-9\\-\\.\\_\\~]|[\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\=]])*)|" + "(?P\\[(?:[0-9a-f]|[\\:])*\\]))" + "(?:\\:(?P[0-9]+)?)?" + ")?" + "(?:\\/)?" + ")?$" +}; + +/** + * @brief Holds each of the groups matched in a string by 'CONN_URI_REGEX'. + */ +struct conn_uri_t { + string scheme; + string hierpart; + string user; + string pass; + string host; + uint32_t port; +}; + +/** + * @brief Uses Regex 'CONN_URI_REGEX' to parse the supplied string. + * @details Tries to perform a 'PartialMatchN' over the 'CONN_URI_REGEX'. Right now doesn't perform a *full* + * check on the validity of the semantics of the URI itself. It does perform some checks. + * @param conn_uri A connection URI. + * @return On success `{EXIT_SUCCESS, conn_uri_t}`, otherwise `{EXIT_FAILURE, conn_uri_t{}}`. Error cause is + * logged. + */ +pair parse_conn_uri(const string& conn_uri) { + re2::RE2::Options opts(RE2::Quiet); + opts.set_case_sensitive(false); + + re2::RE2 re(CONN_URI_REGEX, opts);; + if (re.error_code()) { + proxy_error("Regex creation failed - %s\n", re.error().c_str()); + assert(0); + } + + const int num_groups = re.NumberOfCapturingGroups(); + std::vector str_args(num_groups, std::string {}); + std::vector re2_args {}; + + for (std::string& str_arg : str_args) { + re2_args.push_back(RE2::Arg(&str_arg)); + } + + std::vector matches {}; + for (RE2::Arg& re2_arg : re2_args) { + matches.push_back(&re2_arg); + } + + const re2::RE2::Arg* const* args = &matches[0]; + RE2::PartialMatchN(conn_uri, re, args, num_groups); + + const map& groups = re.NamedCapturingGroups(); + map::const_iterator group_it; + + string scheme {}; + string hierpart {}; + string userinfo {}; + string host {}; + uint32_t port = 0; + + if ((group_it = groups.find("Scheme")) != groups.end()) { scheme = str_args[group_it->second - 1]; } + if ((group_it = groups.find("HierPart")) != groups.end()) { hierpart = str_args[group_it->second - 1]; } + if ((group_it = groups.find("UserInfo")) != groups.end()) { userinfo = str_args[group_it->second - 1]; } + if ((group_it = groups.find("Host")) != groups.end()) { host = str_args[group_it->second - 1]; } + + // Remove the enclosing(`[]`) from IPv6 addresses + if (host.empty() == false && host.size() > 2) { + if (host[0] == '[') { + host = host.substr(1, host.size()-2); + } + } + + string user {}; + string pass {}; + + int32_t match_err = EXIT_SUCCESS; + + // Extract supplied info for user:pass + vector v_userinfo = split_str(userinfo, ':'); + if (v_userinfo.size() == 1) { + user = v_userinfo[0]; + } else if (v_userinfo.size() == 2) { + user = v_userinfo[0]; + pass = v_userinfo[1]; + } else if (v_userinfo.size() > 2) { + proxy_error( + "Invalid UserInfo '%s' supplied in connection URI. UserInfo should contain at max two fields 'user:pass'\n", + userinfo.c_str() + ); + match_err = EXIT_FAILURE; + } + + if ((group_it = groups.find("Port")) != groups.end()) { + const string s_port { str_args[group_it->second - 1] }; + + if (!s_port.empty()) { + char* p_end = nullptr; + port = std::strtol(s_port.c_str(), &p_end, 10); + + if (errno == ERANGE || p_end == s_port.c_str()) { + proxy_error("Invalid Port '%s' supplied in connection URI.\n", s_port.c_str()); + match_err = EXIT_FAILURE; + } + } + } + + struct conn_uri_t uri_data { scheme, hierpart, user, pass, host, port }; + + return { match_err, uri_data }; +} + +/** + * @brief Helper function to serialize 'conn_uri_t' for debugging purposes. + */ +string to_string(const conn_uri_t& conn_uri) { + nlohmann::ordered_json j; + + j["scheme"] = conn_uri.scheme; + j["user"] = conn_uri.user; +#ifdef DEBUG + j["pass"] = conn_uri.pass; +#endif + j["host"] = conn_uri.host; + j["port"] = conn_uri.port; + + return j.dump(); +} + +/** + * @brief Query for fetching MySQL users during bootstrapping. + * @details For security reasons, users matching the following names are excluded: + * - `mysql.%` + * - `root` + * - `bt_proxysql_%` + * Users starting with `bt_proxysql_` are considered `ProxySQL` created used during `bootstrap` for + * monitoring purposes. A user, could create it's own monitoring accounts under this prefix, to avoid + * ProxySQL fetching them as regular users. + */ +const char BOOTSTRAP_SELECT_USERS[] { + "SELECT DISTINCT user,ssl_type,authentication_string,plugin,password_expired FROM mysql.user" + " WHERE user NOT LIKE 'mysql.%' AND user NOT LIKE 'bt_proxysql_%'" +#ifndef DEBUG + " AND user != 'root'" +#endif +}; + +/** + * @brief Query for fetching MySQL servers during bootstrapping. + * @details As the regular GR monitoring queries, makes use of `replication_group_members` table. + */ +const char BOOTSTRAP_SELECT_SERVERS[] { + "SELECT MEMBER_ID,MEMBER_HOST,MEMBER_PORT,MEMBER_STATE,MEMBER_ROLE,MEMBER_VERSION FROM" + " performance_schema.replication_group_members" +}; + +/** + * @brief Stores credentials for monitoring created accounts during bootstrap. + */ +struct acct_creds_t { + string user; + string pass; +}; + +/** + * @brief Minimal set of permissions for a created GR monitoring account. + * @details Right now we **do not grant** permissions to `mysql_innodb_cluster_metadata` tables, since for now + * we don't make any use of them. + */ +const vector t_grant_perms_queries { + "GRANT USAGE ON *.* TO `%s`@`%%`", +// NOTE: For now, we don't make use of any `mysql_innodb_cluster_metadata` tables +// "GRANT SELECT, EXECUTE ON `mysql_innodb_cluster_metadata`.* TO `%s`@`%%`", +// NOTE: For now, we don't make use of 'routers' and 'v2_routers' table +// "GRANT INSERT, UPDATE, DELETE ON `mysql_innodb_cluster_metadata`.`routers` TO `%s`@`%%`", +// "GRANT INSERT, UPDATE, DELETE ON `mysql_innodb_cluster_metadata`.`v2_routers` TO `%s`@`%%`", + "GRANT SELECT ON `performance_schema`.`global_variables` TO `%s`@`%%`", + "GRANT SELECT ON `performance_schema`.`replication_group_member_stats` TO `%s`@`%%`", + "GRANT SELECT ON `performance_schema`.`replication_group_members` TO `%s`@`%%`" +}; + +/** + * @brief Grants the minimal set of permissions for GR monitoring to a supplies user. + * @details All permissions will be granted for host `%`. + * @param mysql An already opened MySQL connection. + * @param user The username to grant permissions to. + * @return Either `0` for success, or the corresponding `mysql_errno` for failure. + */ +int grant_user_perms(MYSQL* mysql, const string& user) { + for (const string& t_query : t_grant_perms_queries) { + const string query { cstr_format(t_query.c_str(), user.c_str()).str }; + + proxy_info("GRANT permissions '%s' to user\n", query.c_str()); + int myerr = mysql_query(mysql, query.c_str()); + if (myerr) { + return mysql_errno(mysql); + } + } + + return 0; +} + +/** + * @brief Generates a random password conforming with MySQL 'MEDIUM' policy. + * @param size The target password size. + * @return The random password generated. + */ +string gen_rand_password(std::size_t size) { + const string lowercase { "abcdefghijklmnopqrstuvwxyz" }; + const string uppercase { "ABCDEFGHIJKLMNOPQRSTUVWXYZ" }; + const string digits { "0123456789" }; + const string symbols { "~@#$^&*]}[{()|-=+;:.>,(remains / allphabet.size()); + std::size_t req_reminder = remains % allphabet.size(); + + for (std::size_t i = 0; i < req_modulus; i++) { + std::shuffle(allphabet.begin(), allphabet.end(), gen); + pass += allphabet; + } + + std::shuffle(allphabet.begin(), allphabet.end(), gen); + pass += allphabet.substr(0, req_reminder); + } + } + + return pass; +} + +/** + * @brief Creates a random monitoring account for bootstrap with a random generated password. + * @param mysql An already opened MySQL connection. + * @param max_retries Maximum number of attempts for creating the user. + * @return On success `{0, acct_creds_t}`, otherwise `{mysql_errno, acct_creds_t{}}`. Error cause is logged. + */ +pair create_random_bootstrap_account(MYSQL* mysql, uint32_t max_retries) { + // Random username + const string monitor_user { "bt_proxysql_" + rand_str(12) }; + string monitor_pass {}; + + int myerr = ER_NOT_VALID_PASSWORD; + uint32_t retries = 0; + + while (retries < max_retries && (myerr == ER_NOT_VALID_PASSWORD)) { + monitor_pass = gen_rand_password(16); + + const string user_create { + "CREATE USER IF NOT EXISTS '" + monitor_user + "'@'%' IDENTIFIED BY '" + monitor_pass + "'" + }; + + int myres = mysql_query(mysql, user_create.c_str()); + myerr = mysql_errno(mysql); + + if (myres || myerr) { + if (myerr != ER_NOT_VALID_PASSWORD) { + return { myerr, { "", "" } }; + } else { + proxy_info( + "Bootstrap config, failed to create password for user '%s'. Retrying...\n", monitor_user.c_str() + ); + retries += 1; + } + } else { + break; + } + } + + if (myerr == 0) { + myerr = grant_user_perms(mysql, monitor_user); + } + + return { myerr, { monitor_user, monitor_pass } }; +} + +/** + * @brief Creates a monitoring account for bootstrap with the supplied parameters. + * @param mysql An already opened MySQL connection. + * @param user The username for the new account. + * @param pass The password for the new account. + * @return On success `{0,acct_creds_t}`, otherwise `{mysql_errno,acct_creds_t}`. Doesn't log errors. + */ +pair create_bootstrap_account(MYSQL* mysql, const string& user, const string& pass) { + const string monitor_user { "'" + user + "'" }; + const string user_create { + "CREATE USER IF NOT EXISTS " + monitor_user + "@'%' IDENTIFIED BY '" + pass + "'" + }; + + int myerr = mysql_query(mysql, user_create.c_str()); + + if (myerr == 0) { + myerr = grant_user_perms(mysql, user); + } + + return { myerr, { user, pass } }; +} + int main(int argc, const char * argv[]) { { @@ -1376,6 +1754,287 @@ int main(int argc, const char * argv[]) { } } + bootstrap_info_t bootstrap_info {}; + // Try to connect to MySQL for performing the bootstrapping process: + // - If data isn't found we perform the bootstrap process. + // - If non-empty datadir is present, reconfiguration should be performed. + if (GloVars.global.gr_bootstrap_mode == 1) { + // Check the other required arguments for performing the bootstrapping process: + // - Username + // - Password - asked by prompt or supplied + // - Connection string parsing is required + const string conn_uri { GloVars.global.gr_bootstrap_uri }; + const pair parse_uri_res { parse_conn_uri(conn_uri) }; + const conn_uri_t uri_data = parse_uri_res.second; + + if (parse_uri_res.first == EXIT_FAILURE) { + proxy_info("Aborting bootstrap due to failed to parse or match URI - `%s`\n", to_string(uri_data).c_str()); + exit(parse_uri_res.first); + } else { + proxy_info("Bootstrap connection data supplied via URI - `%s`\n", to_string(uri_data).c_str()); + } + + const char* c_host = uri_data.host.c_str(); + const char* c_user = uri_data.user.empty() ? "root" : uri_data.user.c_str(); + const char* c_pass = nullptr; + uint32_t port = uri_data.port == 0 ? 3306 : uri_data.port; + uint32_t flags = CLIENT_SSL; + + nlohmann::ordered_json conn_data { { "host", c_host }, { "user", c_user }, { "port", port } }; + proxy_info("Performing bootstrap connection using URI data and defaults - `%s`\n", conn_data.dump().c_str()); + + if (uri_data.pass.empty()) { + c_pass = getpass("Enter password: "); + } else { + c_pass = uri_data.pass.c_str(); + } + + MYSQL* mysql = mysql_init(NULL); + + // SSL explicitly disabled by user for backend connections + if (GloVars.global.gr_bootstrap_ssl_mode) { + if (!strcasecmp(GloVars.global.gr_bootstrap_ssl_mode, "DISABLED")) { + flags = 0; + } + } + + if (flags == CLIENT_SSL) { + mysql_ssl_set( + mysql, + GloVars.global.gr_bootstrap_ssl_key, + GloVars.global.gr_bootstrap_ssl_cert, + GloVars.global.gr_bootstrap_ssl_ca, + GloVars.global.gr_bootstrap_ssl_capath, + GloVars.global.gr_bootstrap_ssl_cipher + ); + } + + if (!mysql_real_connect(mysql, c_host, c_user, c_pass, nullptr, port, NULL, flags)) { + proxy_error("Bootstrap failed, connection error '%s'\n", mysql_error(mysql)); + exit(EXIT_FAILURE); + } + + if (uri_data.pass.empty()) { + uint32_t passlen = strlen(c_pass); + memset(static_cast(const_cast(c_pass)), 0, passlen); + } + + // Get server default collation and version directly from initial handshake + bootstrap_info.server_language = mysql->server_language; + bootstrap_info.server_version = mysql->server_version; + + // Fetch all required data for Bootstrap + int myrc = mysql_query(mysql, BOOTSTRAP_SELECT_SERVERS); + + if (myrc) { + proxy_error("Bootstrap failed, query failed with error - %s\n", mysql_error(mysql)); + exit(EXIT_FAILURE); + } + + MYSQL_RES* myres_members = mysql_store_result(mysql); + + if (myres_members == nullptr || mysql_num_rows(myres_members) == 0) { + proxy_error("Bootstrap failed, expected server %s:%d to have Group Replication configured\n", c_host, port); + exit(EXIT_FAILURE); + } + + myrc = mysql_query(mysql, BOOTSTRAP_SELECT_USERS); + + if (myrc) { + proxy_error("Bootstrap failed, query failed with error - %s\n", mysql_error(mysql)); + exit(EXIT_FAILURE); + } + + MYSQL_RES* myres_users = mysql_store_result(mysql); + + if (myres_users == nullptr) { + proxy_error("Bootstrap failed, storing resultset failed with error - %s\n", mysql_error(mysql)); + exit(EXIT_FAILURE); + } + + // TODO-NOTE: Maybe further data verification should be performed here; bootstrap-info holding final types + bootstrap_info.servers = myres_members; + bootstrap_info.users = myres_users; + + // Setup a bootstrap account - monitoring + const string account_create_policy { + GloVars.global.gr_bootstrap_account_create == nullptr ? "if-not-exists" : + GloVars.global.gr_bootstrap_account_create + }; + + if (GloVars.global.gr_bootstrap_account == nullptr && GloVars.global.gr_bootstrap_account_create != nullptr) { + proxy_error("Bootstrap failed, option '--account-create' can only be used in combination with '--account'\n"); + exit(EXIT_FAILURE); + } + + const uint32_t password_retries = GloVars.global.gr_bootstrap_password_retries; + string new_mon_user {}; + string new_mon_pass {}; + + if (GloVars.global.gr_bootstrap_account != nullptr) { + const vector valid_policies { "if-not-exists", "always", "never" }; + if (std::find(valid_policies.begin(), valid_policies.end(), account_create_policy) == valid_policies.end()) { + proxy_error("Bootstrap failed, unknown '--account-create' option '%s'\n", account_create_policy.c_str()); + exit(EXIT_FAILURE); + } + + // Since an account has been specified, we require the password for the account + const string mon_user { GloVars.global.gr_bootstrap_account }; + const string get_acc_pass_msg { "Please enter MySQL password for " + mon_user + ": " }; + + // Get the account pass directly from user input + const string mon_pass = getpass(get_acc_pass_msg.c_str()); + + // 1. Check if account exists + const string get_user_cnt { "SELECT COUNT(*) FROM mysql.user WHERE user='" + mon_user + "'" }; + int cnt_err = mysql_query(mysql, get_user_cnt.c_str()); + MYSQL_RES* myres = mysql_store_result(mysql); + + if (cnt_err || myres == nullptr) { + proxy_error("Bootstrap failed, detecting count existence failed with error - %s\n", mysql_error(mysql)); + exit(EXIT_FAILURE); + } + + MYSQL_ROW myrow = mysql_fetch_row(myres); + uint32_t acc_exists = atoi(myrow[0]); + mysql_free_result(myres); + + if (account_create_policy == "if-not-exists") { + // 2. Account doesn't exists, create new account. Otherwise reuse current + if (acc_exists == 0) { + pair new_creds { create_bootstrap_account(mysql, mon_user, mon_pass) }; + + if (new_creds.first) { + proxy_error("Bootstrap failed, user creation failed with error - %s\n", mysql_error(mysql)); + exit(EXIT_FAILURE); + } { + // Store the credentials as the new 'monitor' ones. + new_mon_user = mon_user; + new_mon_pass = new_creds.second.pass; + } + } else { + new_mon_user = mon_user; + new_mon_pass = mon_pass; + } + } else if (account_create_policy == "always") { + if (acc_exists == 0) { + pair new_creds { create_bootstrap_account(mysql, mon_user, mon_pass) }; + + if (new_creds.first) { + proxy_error("Bootstrap failed, user creation failed with error - %s\n", mysql_error(mysql)); + exit(EXIT_FAILURE); + } + } else { + proxy_error( + "Bootstrap failed, account '%s' already exists but supplied option '--account-create=\"always\"'\n", + mon_user.c_str() + ); + exit(EXIT_FAILURE); + } + + new_mon_user = mon_user; + new_mon_pass = mon_pass; + } else if (account_create_policy == "never") { + if (acc_exists == 0) { + proxy_error( + "Bootstrap failed, account '%s' doesn't exists but supplied option '--account-create=\"never\"'\n", + mon_user.c_str() + ); + exit(EXIT_FAILURE); + } + + new_mon_user = mon_user; + new_mon_pass = mon_pass; + } else { + proxy_error("Bootstrap failed, unknown '--account-create' option '%s'\n", account_create_policy.c_str()); + exit(EXIT_FAILURE); + } + } else { + string prev_bootstrap_user {}; + string prev_bootstrap_pass {}; + + if (Proxy_file_exists(GloVars.admindb)) { + SQLite3DB::LoadPlugin(GloVars.sqlite3_plugin); + SQLite3DB* configdb = new SQLite3DB(); + configdb->open((char*)GloVars.admindb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX); + + { + const char check_table[] { + "SELECT count(*) FROM sqlite_master WHERE type='table' AND name='bootstrap_variables'" + }; + + int table_exists = 0; + + char* error = nullptr; + int cols = 0; + int affected_rows = 0; + SQLite3_result* resultset = NULL; + + configdb->execute_statement(check_table, &error , &cols , &affected_rows , &resultset); + + if (error == nullptr && resultset) { + table_exists = atoi(resultset->rows[0]->fields[0]); + delete resultset; + } else { + const char* err_msg = error != nullptr ? error : "Empty resultset"; + proxy_error("Bootstrap failed, query failed with error - %s", err_msg); + exit(EXIT_FAILURE); + } + + if (table_exists != 0) { + const char query_user_pass[] { + "SELECT variable_name,variable_value FROM bootstrap_variables" + " WHERE variable_name='bootstrap_username' OR variable_name='bootstrap_password'" + " ORDER BY variable_name" + }; + configdb->execute_statement(query_user_pass, &error, &cols, &affected_rows, &resultset); + + if (resultset->rows.size() != 0) { + prev_bootstrap_user = resultset->rows[1]->fields[1]; + prev_bootstrap_pass = resultset->rows[0]->fields[1]; + } + + if (resultset) { + delete resultset; + } + } + } + + delete configdb; + } + + if (!prev_bootstrap_pass.empty() && !prev_bootstrap_user.empty()) { + proxy_info( + "Bootstrap info, detected previous bootstrap user '%s' reusing account...\n", + prev_bootstrap_user.c_str() + ); + + new_mon_user = prev_bootstrap_user; + new_mon_pass = prev_bootstrap_pass; + } else { + // Create random account with random password + pair mon_creds { create_random_bootstrap_account(mysql, password_retries) }; + + if (mon_creds.first) { + proxy_error( + "Bootstrap failed, user creation '%s' failed with error - %s\n", + mon_creds.second.user.c_str(), mysql_error(mysql) + ); + exit(EXIT_FAILURE); + } else { + new_mon_user = mon_creds.second.user; + new_mon_pass = mon_creds.second.pass; + bootstrap_info.rand_gen_user = true; + } + } + } + + bootstrap_info.mon_user = new_mon_user; + bootstrap_info.mon_pass = new_mon_pass; + + mysql_close(mysql); + } + { cpu_timer t; ProxySQL_Main_init_SSL_module(); @@ -1508,7 +2167,7 @@ int main(int argc, const char * argv[]) { __start_label: { cpu_timer t; - ProxySQL_Main_init_phase2___not_started(); + ProxySQL_Main_init_phase2___not_started(bootstrap_info); #ifdef DEBUG std::cerr << "Main init phase2 completed in "; #endif diff --git a/test/Makefile b/test/Makefile index e50df910d9..9842ffd591 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,50 +1,73 @@ -DEPS_PATH=../deps +#!/bin/make -f -MARIADB_PATH=$(DEPS_PATH)/mariadb-client-library/mariadb_client -MARIADB_IDIR=$(MARIADB_PATH)/include +PROXYSQL_PATH := $(shell while [ ! -f ./src/proxysql_global.cpp ]; do cd ..; done; pwd) +PROXYSQL_IDIR := $(PROXYSQL_PATH)/include -JEMALLOC_PATH=$(DEPS_PATH)/jemalloc/jemalloc -JEMALLOC_IDIR=$(JEMALLOC_PATH)/include/jemalloc +DEPS_PATH := $(PROXYSQL_PATH)/deps -LIBCONFIG_PATH=$(DEPS_PATH)/libconfig/libconfig -LIBCONFIG_IDIR=-I$(LIBCONFIG_PATH)/lib -#INJECTION_PATH=$(DEPS_PATH)/libinjection -#INJECTION_IDIR=$(INJECTION_PATH) +MARIADB_PATH := $(DEPS_PATH)/mariadb-client-library/mariadb_client +MARIADB_IDIR := $(MARIADB_PATH)/include -RE2_PATH=$(DEPS_PATH)/re2/re2 -RE2_IDIR=$(RE2_PATH) +JEMALLOC_PATH := $(DEPS_PATH)/jemalloc/jemalloc +JEMALLOC_IDIR := $(JEMALLOC_PATH)/include/jemalloc -PCRE_PATH=$(DEPS_PATH)/pcre/pcre +LIBCONFIG_PATH := $(DEPS_PATH)/libconfig/libconfig +LIBCONFIG_IDIR := $(LIBCONFIG_PATH)/lib -SQLITE3_DIR=$(DEPS_PATH)/sqlite3/sqlite3 +#INJECTION_PATH := $(DEPS_PATH)/libinjection +#INJECTION_IDIR := $(INJECTION_PATH) -CLICKHOUSE_CPP_DIR=$(DEPS_PATH)/clickhouse-cpp/clickhouse-cpp +RE2_PATH := $(DEPS_PATH)/re2/re2 +RE2_IDIR := $(RE2_PATH) -LIBINJECTION_DIR=$(DEPS_PATH)/libinjection/libinjection -LIBINJECTION_IDIR=-I$(LIBINJECTION_DIR)/src +PCRE_PATH := $(DEPS_PATH)/pcre/pcre +PCRE_IDIR := $(PCRE_PATH) -LIBHTTPSERVER_DIR=$(DEPS_PATH)/libhttpserver/libhttpserver -LIBHTTPSERVER_IDIR=-I$(LIBHTTPSERVER_DIR)/src +SQLITE3_PATH := $(DEPS_PATH)/sqlite3/sqlite3 +SQLITE3_IDIR := $(SQLITE3_PATH) -MICROHTTPD_DIR=$(DEPS_PATH)/libmicrohttpd/libmicrohttpd -MICROHTTPD_IDIR=-I$(MICROHTTPD_DIR) -I$(MICROHTTPD_DIR)/src/include +CLICKHOUSE_CPP_PATH := $(DEPS_PATH)/clickhouse-cpp/clickhouse-cpp +CLICKHOUSE_CPP_IDIR := $(CLICKHOUSE_CPP_PATH) -CURL_DIR=$(DEPS_PATH)/curl/curl -CURL_IDIR=-I$(CURL_DIR)/include +LIBINJECTION_PATH := $(DEPS_PATH)/libinjection/libinjection +LIBINJECTION_IDIR := -I$(LIBINJECTION_PATH)/src -SSL_DIR=$(DEPS_PATH)/libssl/openssl/ -SSL_IDIR=$(SSL_DIR)/include +LIBHTTPSERVER_PATH := $(DEPS_PATH)/libhttpserver/libhttpserver +LIBHTTPSERVER_IDIR := -I$(LIBHTTPSERVER_PATH)/src +MICROHTTPD_PATH := $(DEPS_PATH)/libmicrohttpd/libmicrohttpd +MICROHTTPD_IDIR := $(MICROHTTPD_PATH)/src/include -EV_DIR=$(DEPS_PATH)/libev/libev/ -EV_IDIR=$(EV_DIR) +CURL_PATH := $(DEPS_PATH)/curl/curl +CURL_IDIR := -I$(CURL_PATH)/include -IDIR=../include +SSL_PATH := $(DEPS_PATH)/libssl/openssl/ +SSL_IDIR := $(SSL_PATH)/include -IDIRS=-I$(IDIR) -I$(JEMALLOC_IDIR) -I$(MARIADB_IDIR) $(LIBCONFIG_IDIR) -I$(RE2_IDIR) -I$(SQLITE3_DIR) -I$(PCRE_PATH) -I/usr/local/include -I$(CLICKHOUSE_CPP_DIR) $(MICROHTTPD_IDIR) $(LIBHTTPSERVER_IDIR) $(LIBINJECTION_IDIR) $(CURL_IDIR) -I$(EV_DIR) -I$(SSL_IDIR) +EV_PATH := $(DEPS_PATH)/libev/libev/ +EV_IDIR := $(EV_PATH) +LOCAL_IDIR := /usr/local/include + + +IDIRS := -I$(PROXYSQL_IDIR) +IDIRS += -I$(JEMALLOC_IDIR) +IDIRS += -I$(MARIADB_IDIR) +IDIRS += -I$(LIBCONFIG_IDIR) +IDIRS += -I$(RE2_IDIR) +IDIRS += -I$(SQLITE3_IDIR) +IDIRS += -I$(PCRE_IDIR) +IDIRS += -I$(LOCAL_IDIR) +IDIRS += -I$(CLICKHOUSE_CPP_DIR) +IDIRS += -I$(MICROHTTPD_PATH) +IDIRS += -I$(MICROHTTPD_IDIR) +IDIRS += -I$(LIBHTTPSERVER_IDIR) +IDIRS += -I$(LIBINJECTION_IDIR) +IDIRS += -I$(CURL_IDIR) +IDIRS += -I$(EV_DIR) +IDIRS += -I$(SSL_IDIR) sqlite_history_convert: sqlite_history_convert.cpp diff --git a/test/deps/Makefile b/test/deps/Makefile new file mode 100644 index 0000000000..d045a0212b --- /dev/null +++ b/test/deps/Makefile @@ -0,0 +1,66 @@ +#!/bin/make -f + + +PROXYSQL_PATH := $(shell while [ ! -f ./src/proxysql_global.cpp ]; do cd ..; done; pwd) +DEPS_PATH := $(PROXYSQL_PATH)/deps + + +.DEFAULT: default +.PHONY: default +default: all + +.PHONY: all +all: mariadb_client mysql_client + + +### test deps targets + +mariadb-connector-c/mariadb-connector-c/libmariadb/libmariadbclient.a: + cd mariadb-connector-c && rm -rf mariadb-connector-c-*/ || true + cd mariadb-connector-c && tar -zxf mariadb-connector-c-*.tar.gz + cd mariadb-connector-c/mariadb-connector-c && patch -p0 < ../CMakeLists.txt.patch + cd mariadb-connector-c/mariadb-connector-c && patch -p0 < ../ConnectorName.cmake.patch + cd mariadb-connector-c/mariadb-connector-c && cmake . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DOPENSSL_ROOT_DIR=$(DEPS_PATH)/libssl/openssl/ + cd mariadb-connector-c/mariadb-connector-c && CC=${CC} CXX=${CXX} ${MAKE} mariadbclient + +mariadb_client: mariadb-connector-c/mariadb-connector-c/libmariadb/libmariadbclient.a + + +#mysql-connector-c/mysql-connector-c/libmysql/libmysqlclient.a: +# cd mysql-connector-c && rm -rf mysql-connector-c-*-src/ || true +# cd mysql-connector-c && tar -zxf mysql-connector-c-*-src.tar.gz +# cd mysql-connector-c/mysql-connector-c && patch -p0 < ../CMakeLists.txt.patch +# cd mysql-connector-c/mysql-connector-c && patch -p0 < ../install_macros.cmake.patch +# cd mysql-connector-c/mysql-connector-c && cmake . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DOPENSSL_ROOT_DIR=$(DEPS_PATH)/libssl/openssl +# cd mysql-connector-c/mysql-connector-c && CC=${CC} CXX=${CXX} ${MAKE} mysqlclient mysql + +mysql-connector-c/mysql-connector-c/libmysql/libmysqlclient.a: + cd mysql-connector-c && curl -C - -O -s https://cdn.mysql.com//Downloads/MySQL-5.7/mysql-boost-5.7.44.tar.gz || true + cd mysql-connector-c && wget -nc -q https://cdn.mysql.com//Downloads/MySQL-5.7/mysql-boost-5.7.44.tar.gz || true + cd mysql-connector-c && rm -rf mysql-*/ || true + cd mysql-connector-c && tar -zxf mysql-boost-5.7.*.tar.gz + cd mysql-connector-c && ln -fsT $$(ls -1d mysql-5.7.*/) mysql-connector-c + cd mysql-connector-c/mysql-connector-c && cmake . -DWITH_BOOST=./boost -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_CXX_FLAGS_RELWITHDEBINFO="-O0 -ggdb -DNDEBUG -fPIC" -DOPENSSL_ROOT_DIR=$(DEPS_PATH)/libssl/openssl +# cd mysql-connector-c/mysql-connector-c && cmake . -DWITH_BOOST=./boost -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS_DEBUG="-O0 -ggdb -fPIC" -DOPENSSL_ROOT_DIR=$(DEPS_PATH)/libssl/openssl + cd mysql-connector-c/mysql-connector-c && CC=${CC} CXX=${CXX} ${MAKE} mysqlclient mysql + cd mysql-connector-c/mysql-connector-c && cp archive_output_directory/libmysqlclient.a libmysql/ + +mysql_client: mysql-connector-c/mysql-connector-c/libmysql/libmysqlclient.a + + +### clean targets + +.PHONY: cleanall +.SILENT: cleanall +cleanall: + cd mariadb-connector-c && rm -rf mariadb-connector-c-*/ || true + cd mysql-connector-c && rm -rf mysql-5.7.*/ || true + +.PHONY: clean +.SILENT: clean +clean: + cd mariadb-connector-c/mariadb-connector-c && $(MAKE) --no-print-directory clean || true + cd mariadb-connector-c/mariadb-connector-c && rm -f CMakeCache.txt || true + cd mysql-connector-c/mysql-connector-c && $(MAKE) --no-print-directory clean || true + cd mysql-connector-c/mysql-connector-c && rm -f CMakeCache.txt || true + cd mysql-connector-c/mysql-connector-c && rm -f libmysql/libmysqlclient.a || true diff --git a/test/deps/mariadb-connector-c/CMakeLists.txt.patch b/test/deps/mariadb-connector-c/CMakeLists.txt.patch new file mode 100644 index 0000000000..de55d09ad8 --- /dev/null +++ b/test/deps/mariadb-connector-c/CMakeLists.txt.patch @@ -0,0 +1,13 @@ +--- ./CMakeLists.txt 2020-02-27 15:01:10.993225716 +0100 ++++ ./CMakeLists.txt 2020-02-27 15:00:57.705257256 +0100 +@@ -132,8 +132,8 @@ + # Disable dbug information for release builds + SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DDBUG_OFF") + SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DDBUG_OFF") +-SET(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -DDBUG_OFF") +-SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DDBUG_OFF") ++SET(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -DDBUG_OFF -ggdb -O0") ++SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DDBUG_OFF -ggdb -O0") + + IF(CMAKE_COMPILER_IS_GNUCC) + INCLUDE(CheckCCompilerFlag) diff --git a/test/deps/mariadb-connector-c/ConnectorName.cmake.patch b/test/deps/mariadb-connector-c/ConnectorName.cmake.patch new file mode 100644 index 0000000000..5aa3e9666d --- /dev/null +++ b/test/deps/mariadb-connector-c/ConnectorName.cmake.patch @@ -0,0 +1,11 @@ +--- cmake/ConnectorName.cmake 2020-10-29 22:16:20.000000000 +0000 ++++ cmake/ConnectorName.cmake 2022-11-06 08:31:22.615513943 +0000 +@@ -22,7 +22,7 @@ + SET(MACHINE_NAME "x64") + ELSE() + SET(MACHINE_NAME "32") +- END() ++ ENDIF() + ENDIF() + + SET(product_name "mysql-connector-c-${CPACK_PACKAGE_VERSION}-${PLATFORM_NAME}${CONCAT_SIGN}${MACHINE_NAME}") diff --git a/test/deps/mariadb-connector-c/mariadb-connector-c b/test/deps/mariadb-connector-c/mariadb-connector-c new file mode 120000 index 0000000000..a92e60f909 --- /dev/null +++ b/test/deps/mariadb-connector-c/mariadb-connector-c @@ -0,0 +1 @@ +mariadb-connector-c-3.1.9 \ No newline at end of file diff --git a/test/deps/mariadb-connector-c/mariadb-connector-c-3.1.9.tar.gz b/test/deps/mariadb-connector-c/mariadb-connector-c-3.1.9.tar.gz new file mode 100644 index 0000000000..6af78adefc Binary files /dev/null and b/test/deps/mariadb-connector-c/mariadb-connector-c-3.1.9.tar.gz differ diff --git a/test/deps/mysql-connector-c/mysql-connector-c b/test/deps/mysql-connector-c/mysql-connector-c new file mode 120000 index 0000000000..7b32ab211a --- /dev/null +++ b/test/deps/mysql-connector-c/mysql-connector-c @@ -0,0 +1 @@ +mysql-5.7.44/ \ No newline at end of file diff --git a/test/tap/Makefile b/test/tap/Makefile index 39bab26d1c..ee93f10b31 100644 --- a/test/tap/Makefile +++ b/test/tap/Makefile @@ -1,20 +1,34 @@ +#!/bin/make -f + + +.DEFAULT: all .PHONY: all -all: tap tests tests_with_deps +all: tests tests_with_deps -.PHONY: clean -clean: - cd tap && ${MAKE} clean - cd tests && ${MAKE} clean - cd tests_with_deps && ${MAKE} clean +.PHONY: debug +debug: tests tests_with_deps -debug: tap tests tests_with_deps +.PHONY: test_deps +test_deps: + cd ../deps && CC=${CC} CXX=${CXX} ${MAKE} .PHONY: tap -tap: - cd tap && CC=${CC} CXX=${CXX} ${MAKE} $(MAKECMDGOALS) +tap: + cd tap && CC=${CC} CXX=${CXX} ${MAKE} -tests: tap +.PHONY: tests +tests: tap test_deps cd tests && CC=${CC} CXX=${CXX} ${MAKE} $(MAKECMDGOALS) -tests_with_deps: tap tests +.PHONY: tests_with_deps +tests_with_deps: tap test_deps cd tests_with_deps && CC=${CC} CXX=${CXX} ${MAKE} $(MAKECMDGOALS) + + +.PHONY: clean +.SILENT: clean +clean: + cd ../deps && ${MAKE} -s clean + cd tap && ${MAKE} -s clean + cd tests && ${MAKE} -s clean + cd tests_with_deps && ${MAKE} -s clean diff --git a/test/tap/tap/Makefile b/test/tap/tap/Makefile index 9e58496e42..45097bbe83 100644 --- a/test/tap/tap/Makefile +++ b/test/tap/tap/Makefile @@ -1,58 +1,91 @@ -DEPS_PATH=../../../deps +#!/bin/make -f -JSON_IDIR=$(DEPS_PATH)/json -MARIADB_PATH=$(DEPS_PATH)/mariadb-client-library/mariadb_client -MARIADB_IDIR=$(MARIADB_PATH)/include -MARIADB_LDIR=$(MARIADB_PATH)/libmariadb +GIT_VERSION ?= $(shell git describe --long --abbrev=7) +ifndef GIT_VERSION + $(error GIT_VERSION is not set) +endif -CURL_DIR=$(DEPS_PATH)/curl/curl -CURL_IDIR=$(CURL_DIR)/include -CURL_LDIR=$(CURL_DIR)/lib/.libs -SQLITE3_DIR=$(DEPS_PATH)/sqlite3/sqlite3 -SQLITE3_IDIR=$(SQLITE3_DIR) -SQLITE3_LDIR=$(SQLITE3_DIR) +PROXYSQL_PATH := ../../.. +PROXYSQL_IDIR := $(PROXYSQL_PATH)/include +PROXYSQL_LDIR := $(PROXYSQL_PATH)/lib -DOTENV_DIR=./cpp-dotenv/static/cpp-dotenv -DOTENV_IDIR=$(DOTENV_DIR)/include -DOTENV_LDIR=$(DOTENV_DIR) +DEPS_PATH := $(PROXYSQL_PATH)/deps -IDIR=../../../include -LDIR=../../../lib +JSON_PATH := $(DEPS_PATH)/json +JSON_IDIR := $(JSON_PATH) -LIBPROXYSQLAR=$(LDIR)/libproxysql.a -INCLUDEDIRS=-I$(IDIR) -I$(JSON_IDIR) -I$(MARIADB_IDIR) -I${CURL_IDIR} -I${SQLITE3_IDIR} -I$(DOTENV_IDIR) +MARIADB_PATH := $(DEPS_PATH)/mariadb-client-library/mariadb_client +MARIADB_IDIR := $(MARIADB_PATH)/include +MARIADB_LDIR := $(MARIADB_PATH)/libmariadb + +CURL_PATH := $(DEPS_PATH)/curl/curl +CURL_IDIR := $(CURL_PATH)/include +CURL_LDIR := $(CURL_PATH)/lib/.libs + +SQLITE3_PATH := $(DEPS_PATH)/sqlite3/sqlite3 +SQLITE3_IDIR := $(SQLITE3_PATH) +SQLITE3_LDIR := $(SQLITE3_PATH) + +DOTENV_PATH := ./cpp-dotenv/static/cpp-dotenv +DOTENV_IDIR := $(DOTENV_PATH)/include +DOTENV_LDIR := $(DOTENV_PATH) + + +LIBPROXYSQLAR := $(PROXYSQL_LDIR)/libproxysql.a + +IDIRS := -I$(PROXYSQL_IDIR) -I$(JSON_IDIR) -I$(MARIADB_IDIR) -I${CURL_IDIR} -I${SQLITE3_IDIR} -I$(DOTENV_IDIR) + +### detect compiler support for c++11/17 +CPLUSPLUS := $(shell ${CC} -std=c++17 -dM -E -x c++ /dev/null 2>/dev/null | grep -F __cplusplus | grep -Po '\d\d\d\d\d\dL') +ifneq ($(CPLUSPLUS),201703L) + CPLUSPLUS := $(shell ${CC} -std=c++11 -dM -E -x c++ /dev/null 2>/dev/null| grep -F __cplusplus | grep -Po '\d\d\d\d\d\dL') +ifneq ($(CPLUSPLUS),201103L) + $(error Compiler must support at least c++11) +endif +endif +STDCPP := -std=c++$(shell echo $(CPLUSPLUS) | cut -c3-4) -DCXX$(shell echo $(CPLUSPLUS) | cut -c3-4) + +WASAN := +ifeq ($(WITHASAN),1) + WASAN := -fsanitize=address +endif + +OPT := $(STDCPP) -O2 -ggdb -Wl,--no-as-needed $(WASAN) + + +### main targets + +.PHONY: default +default: all .PHONY: all all: libtap.a libtap.so libssl.so.3 libcrypto.so.3 libcpp_dotenv.so -.PHONY: clean -clean: - rm -f *.o libtap.a libtap.so || true - find . -name '*.so' -type f -delete || true - find cpp-dotenv/dynamic -name '*.o' -or -name '*.a' -delete || true - find cpp-dotenv/static -name '*.o' -or -name '*.a' -delete || true +debug: OPT := $(STDCPP) -O0 -DDEBUG -ggdb -Wl,--no-as-needed $(WASAN) +debug: libtap.a libtap.so -OPT=-O2 -debug: OPT = -O0 -DDEBUG -ggdb -debug: libtap.a libtap.so +### helper targets command_line.o: command_line.cpp cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a libcurl.so libssl.so.3 libcrypto.so.3 libcpp_dotenv.so - g++ -fPIC -c command_line.cpp -std=c++11 $(INCLUDEDIRS) $(OPT) + g++ -fPIC -c command_line.cpp $(IDIRS) $(OPT) utils.o: utils.cpp cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a libcurl.so libssl.so.3 libcrypto.so.3 libcpp_dotenv.so - g++ -fPIC -c utils.cpp -std=c++11 $(INCLUDEDIRS) $(OPT) + g++ -fPIC -c utils.cpp $(IDIRS) $(OPT) tap.o: tap.cpp cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a libcurl.so libssl.so.3 libcrypto.so.3 libcpp_dotenv.so - g++ -fPIC -c tap.cpp -std=c++11 $(INCLUDEDIRS) $(OPT) + g++ -fPIC -c tap.cpp $(IDIRS) $(OPT) -libtap.a: tap.cpp tap.h command_line.cpp command_line.h utils.cpp utils.h tap.o command_line.o utils.o cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a +libtap.a: tap.o command_line.o utils.o cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a ar rcs libtap.a tap.o command_line.o utils.o $(SQLITE3_LDIR)/sqlite3.o libtap.so: libtap.a cpp-dotenv/dynamic/cpp-dotenv/libcpp_dotenv.so - gcc -shared -o libtap.so -Wl,--whole-archive libtap.a ../../../deps/curl/curl/lib/.libs/libcurl.a ../../../deps/libssl/openssl/libcrypto.a ../../../deps/libssl/openssl/libssl.a -Wl,--no-whole-archive + gcc -shared -o libtap.so -Wl,--whole-archive libtap.a -Wl,--no-whole-archive + + +### tap deps targets libssl.so.3: $(DEPS_PATH)/libssl/openssl/libssl.so.3 cp -a $(DEPS_PATH)/libssl/openssl/libssl.so* . @@ -79,5 +112,16 @@ cpp-dotenv/dynamic/cpp-dotenv/libcpp_dotenv.so: cd cpp-dotenv/dynamic && tar -zxf ../cpp-dotenv-*.tar.gz cd cpp-dotenv/dynamic/cpp-dotenv && patch src/dotenv.cpp < ../../dotenv.cpp.patch cd cpp-dotenv/dynamic/cpp-dotenv && patch include/dotenv.h < ../../dotenv.h.patch - cd cpp-dotenv/dynamic/cpp-dotenv && cmake . -DBUILD_TESTING=OFF -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Debug + cd cpp-dotenv/dynamic/cpp-dotenv && cmake . -DBUILD_TESTING=OFF -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_RPATH="../tap:../../tap" -DCMAKE_BUILD_TYPE=Debug cd cpp-dotenv/dynamic/cpp-dotenv && CC=${CC} CXX=${CXX} ${MAKE} + + +### clean targets + +.SILENT: clean +.PHONY: clean +clean: + find . -name '*.a' -delete || true + find . -name '*.o' -delete || true + find . -name '*.so' -delete || true + find . -name '*.so.*' -delete || true diff --git a/test/tap/tests/Makefile b/test/tap/tests/Makefile index 5a4d57bc0c..422ead51cc 100644 --- a/test/tap/tests/Makefile +++ b/test/tap/tests/Makefile @@ -1,147 +1,210 @@ -DEPS_PATH=../../../deps +#!/bin/make -f -MARIADB_PATH=$(DEPS_PATH)/mariadb-client-library/mariadb_client -MARIADB_IDIR=$(MARIADB_PATH)/include -MARIADB_LDIR=$(MARIADB_PATH)/libmariadb -JEMALLOC_PATH=$(DEPS_PATH)/jemalloc/jemalloc -JEMALLOC_IDIR=$(JEMALLOC_PATH)/include/jemalloc -JEMALLOC_LDIR=$(JEMALLOC_PATH)/lib +GIT_VERSION ?= $(shell git describe --long --abbrev=7) +ifndef GIT_VERSION + $(error GIT_VERSION is not set) +endif + + +PROXYSQL_PATH := $(shell while [ ! -f ./src/proxysql_global.cpp ]; do cd ..; done; pwd) +PROXYSQL_IDIR := $(PROXYSQL_PATH)/include +PROXYSQL_LDIR := $(PROXYSQL_PATH)/lib + +DEPS_PATH := $(PROXYSQL_PATH)/deps + +MARIADB_PATH := $(DEPS_PATH)/mariadb-client-library/mariadb_client +MARIADB_IDIR := $(MARIADB_PATH)/include +MARIADB_LDIR := $(MARIADB_PATH)/libmariadb -PROXYSQL_PATH=../../.. -PROXYSQL_IDIR=$(PROXYSQL_PATH)/include +JEMALLOC_PATH := $(DEPS_PATH)/jemalloc/jemalloc +JEMALLOC_IDIR := $(JEMALLOC_PATH)/include/jemalloc +JEMALLOC_LDIR := $(JEMALLOC_PATH)/lib -JSON_IDIR=$(DEPS_PATH)/json +JSON_IDIR := $(DEPS_PATH)/json -RE2_PATH=$(DEPS_PATH)/re2/re2 -RE2_IDIR=$(RE2_PATH) +RE2_PATH := $(DEPS_PATH)/re2/re2 +RE2_IDIR := $(RE2_PATH) +RE2_LDIR := $(RE2_PATH)/obj -SQLITE3_DIR=$(DEPS_PATH)/sqlite3/sqlite3 +SQLITE3_PATH := $(DEPS_PATH)/sqlite3/sqlite3 +SQLITE3_IDIR := $(SQLITE3_PATH) +SQLITE3_LDIR := $(SQLITE3_PATH) -LIBHTTPSERVER_DIR=$(DEPS_PATH)/libhttpserver/libhttpserver -LIBHTTPSERVER_IDIR=$(LIBHTTPSERVER_DIR)/src -LIBHTTPSERVER_LDIR=$(LIBHTTPSERVER_DIR)/build/src/.libs/ +LIBHTTPSERVER_DIR := $(DEPS_PATH)/libhttpserver/libhttpserver +LIBHTTPSERVER_IDIR := $(LIBHTTPSERVER_DIR)/src +LIBHTTPSERVER_LDIR := $(LIBHTTPSERVER_DIR)/build/src/.libs/ -LIBCONFIG_PATH=$(DEPS_PATH)/libconfig/libconfig -LIBCONFIG_IDIR=$(LIBCONFIG_PATH)/lib -LIBCONFIG_LDIR=-L$(LIBCONFIG_PATH)/lib/.libs +LIBCONFIG_PATH := $(DEPS_PATH)/libconfig/libconfig +LIBCONFIG_IDIR := $(LIBCONFIG_PATH)/lib +LIBCONFIG_LDIR := $(LIBCONFIG_PATH)/lib/.libs -CURL_DIR=$(DEPS_PATH)/curl/curl -CURL_IDIR=$(CURL_DIR)/include -CURL_LDIR=$(CURL_DIR)/lib/.libs +CURL_DIR := $(DEPS_PATH)/curl/curl +CURL_IDIR := $(CURL_DIR)/include +CURL_LDIR := $(CURL_DIR)/lib/.libs -DAEMONPATH=$(DEPS_PATH)/libdaemon/libdaemon -DAEMONPATH_IDIR=$(DAEMONPATH) -DAEMONPATH_LDIR=$(DAEMONPATH)/libdaemon/.libs +DAEMONPATH := $(DEPS_PATH)/libdaemon/libdaemon +DAEMONPATH_IDIR := $(DAEMONPATH) +DAEMONPATH_LDIR := $(DAEMONPATH)/libdaemon/.libs -PCRE_PATH=$(DEPS_PATH)/pcre/pcre -PCRE_LDIR=$(PCRE_PATH)/.libs +PCRE_PATH := $(DEPS_PATH)/pcre/pcre +PCRE_LDIR := $(PCRE_PATH)/.libs -MICROHTTPD_DIR=$(DEPS_PATH)/libmicrohttpd/libmicrohttpd/src -MICROHTTPD_IDIR=$(MICROHTTPD_DIR)/include -MICROHTTPD_LDIR=$(MICROHTTPD_DIR)/microhttpd/.libs +MICROHTTPD_DIR := $(DEPS_PATH)/libmicrohttpd/libmicrohttpd/src +MICROHTTPD_IDIR := $(MICROHTTPD_DIR)/include +MICROHTTPD_LDIR := $(MICROHTTPD_DIR)/microhttpd/.libs -LIBINJECTION_DIR=$(DEPS_PATH)/libinjection/libinjection -LIBINJECTION_IDIR=$(LIBINJECTION_DIR)/src -LIBINJECTION_LDIR=$(LIBINJECTION_DIR)/src +LIBINJECTION_DIR := $(DEPS_PATH)/libinjection/libinjection +LIBINJECTION_IDIR := $(LIBINJECTION_DIR)/src +LIBINJECTION_LDIR := $(LIBINJECTION_DIR)/src -SSL_DIR=$(DEPS_PATH)/libssl/openssl/ -SSL_IDIR=$(SSL_DIR)/include -SSL_LDIR=$(SSL_DIR) +SSL_DIR := $(DEPS_PATH)/libssl/openssl/ +SSL_IDIR := $(SSL_DIR)/include +SSL_LDIR := $(SSL_DIR) -EV_DIR=$(DEPS_PATH)/libev/libev/ -EV_IDIR=$(EV_DIR) -EV_LDIR=$(EV_DIR)/.libs +EV_DIR := $(DEPS_PATH)/libev/libev/ +EV_IDIR := $(EV_DIR) +EV_LDIR := $(EV_DIR)/.libs -PROMETHEUS_PATH=$(DEPS_PATH)/prometheus-cpp/prometheus-cpp -PROMETHEUS_IDIR=$(PROMETHEUS_PATH)/pull/include -I$(PROMETHEUS_PATH)/core/include -PROMETHEUS_LDIR=$(PROMETHEUS_PATH)/lib +PROMETHEUS_PATH := $(DEPS_PATH)/prometheus-cpp/prometheus-cpp +PROMETHEUS_IDIR := $(PROMETHEUS_PATH)/pull/include -I$(PROMETHEUS_PATH)/core/include +PROMETHEUS_LDIR := $(PROMETHEUS_PATH)/lib -CITYHASH_DIR=$(DEPS_PATH)/cityhash/cityhash/ -CITYHASH_IDIR=$(CITYHASH_DIR) -CITYHASH_LDIR=$(CITYHASH_DIR)/src/.libs +CITYHASH_DIR := $(DEPS_PATH)/cityhash/cityhash/ +CITYHASH_IDIR := $(CITYHASH_DIR) +CITYHASH_LDIR := $(CITYHASH_DIR)/src/.libs -COREDUMPER_DIR=$(DEPS_PATH)/coredumper/coredumper -COREDUMPER_IDIR=$(COREDUMPER_DIR)/include -COREDUMPER_LDIR=$(COREDUMPER_DIR)/src +COREDUMPER_DIR := $(DEPS_PATH)/coredumper/coredumper +COREDUMPER_IDIR := $(COREDUMPER_DIR)/include +COREDUMPER_LDIR := $(COREDUMPER_DIR)/src -IDIR=../../../include -LDIR=../../../lib -TAP_LIBDIR=../tap +TAP_PATH := ../tap +TAP_IDIR := ../tap +TAP_LDIR := ../tap -DOTENV_DYN_DIR=../tap/cpp-dotenv/dynamic/cpp-dotenv -DOTENV_DYN_IDIR=$(DOTENV_DYN_DIR)/include -#DOTENV_DYN_LDIR=$(DOTENV_DYN_DIR) -DOTENV_DYN_LDIR=$(TAP_LIBDIR) +DOTENV_DYN_PATH := $(TAP_LDIR)/cpp-dotenv/dynamic/cpp-dotenv +DOTENV_DYN_IDIR := $(DOTENV_DYN_PATH)/include +#DOTENV_DYN_LDIR := $(DOTENV_DYN_PATH) +DOTENV_DYN_LDIR := $(TAP_LDIR) -LIBPROXYSQLAR=$(LDIR)/libproxysql.a +TEST_DEPS_PATH := $(PROXYSQL_PATH)/test/deps -ODIR=../../../obj +TEST_MARIADB_PATH := $(TEST_DEPS_PATH)/mariadb-connector-c/mariadb-connector-c +TEST_MARIADB_IDIR := $(TEST_MARIADB_PATH)/include +TEST_MARIADB_LDIR := $(TEST_MARIADB_PATH)/libmariadb -EXECUTABLE=proxysql +TEST_MYSQL_PATH := $(TEST_DEPS_PATH)/mysql-connector-c/mysql-connector-c +TEST_MYSQL_IDIR := $(TEST_MYSQL_PATH)/include +TEST_MYSQL_EDIR := $(TEST_MYSQL_PATH)/libbinlogevents/export/ +TEST_MYSQL_LDIR := $(TEST_MYSQL_PATH)/libmysql -OBJ=../../../src/obj/proxysql_global.o ../../../src/obj/main.o ../../../src/obj/proxy_tls.o +LIBPROXYSQLAR := $(PROXYSQL_LDIR)/libproxysql.a -INCLUDEDIRS=-I../tap -I$(RE2_PATH) -I$(IDIR) -I$(JEMALLOC_IDIR) -I$(SQLITE3_DIR) -I$(MICROHTTPD_IDIR) -I$(LIBHTTPSERVER_IDIR) -I$(CURL_IDIR) -I$(DAEMONPATH_IDIR) -I$(MARIADB_IDIR) -I$(SSL_IDIR) -I$(JSON_IDIR) -I$(LIBCONFIG_IDIR) -I$(PROMETHEUS_IDIR) -I$(EV_IDIR) -I$(DOTENV_DYN_IDIR) -LDIRS=-L$(TAP_LIBDIR) -L$(DOTENV_DYN_LDIR) -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$(PROMETHEUS_LDIR) +ODIR := $(PROXYSQL_PATH)/obj + +EXECUTABLE := proxysql + +OBJ := $(PROXYSQL_PATH)/src/obj/proxysql_global.o $(PROXYSQL_PATH)/src/obj/main.o $(PROXYSQL_PATH)/src/obj/proxy_tls.o + +IDIRS := -I$(TAP_IDIR) -I$(RE2_IDIR) -I$(PROXYSQL_IDIR) -I$(JEMALLOC_IDIR) -I$(LIBCONFIG_IDIR) -I$(MARIADB_IDIR) -I$(DAEMONPATH_IDIR) -I$(MICROHTTPD_IDIR) -I$(LIBHTTPSERVER_IDIR) -I$(CURL_IDIR) -I$(EV_IDIR) -I$(PROMETHEUS_IDIR) -I$(DOTENV_DYN_IDIR) -I$(SSL_IDIR) -I$(SQLITE3_IDIR) -I$(JSON_IDIR) +LDIRS := -L$(TAP_LDIR) -L$(RE2_LDIR) -L$(PROXYSQL_LDIR) -L$(JEMALLOC_LDIR) -L$(LIBCONFIG_LDIR) -L$(MARIADB_LDIR) -L$(DAEMONPATH_LDIR) -L$(MICROHTTPD_LDIR) -L$(LIBHTTPSERVER_LDIR) -L$(CURL_LDIR) -L$(EV_LDIR) -L$(PROMETHEUS_LDIR) -L$(DOTENV_DYN_LDIR) -L$(SSL_LDIR) -L$(PCRE_LDIR) -L$(LIBINJECTION_LDIR) UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Linux) - LDIRS+= -L$(COREDUMPER_LDIR) + LDIRS += -L$(COREDUMPER_LDIR) endif -MYLIBS=-Wl,--export-dynamic -Wl,-Bdynamic -lssl -lcrypto -lgnutls -ltap -lcpp_dotenv -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lcurl -lev -lprometheus-cpp-pull -lprometheus-cpp-core -luuid -Wl,-Bdynamic -lpthread -lm -lz -lrt -ldl $(EXTRALINK) -#MYLIBS=-Wl,--export-dynamic -Wl,-Bdynamic -lssl -lcrypto -lgnutls -ltap -lcpp_dotenv -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lev -lprometheus-cpp-pull -lprometheus-cpp-core -luuid -Wl,-Bdynamic -lpthread -lm -lz -lrt -ldl $(EXTRALINK) -MYLIBSJEMALLOC=-Wl,-Bstatic -ljemalloc -STATIC_LIBS= $(CITYHASH_LDIR)/libcityhash.a -#STATIC_LIBS= $(SSL_LDIR)/libssl.a $(SSL_LDIR)/libcrypto.a $(CITYHASH_LDIR)/libcityhash.a - -LIBCOREDUMPERAR= +MYLIBS := -Wl,--export-dynamic +MYLIBS += -Wl,-Bdynamic -lgnutls -ltap -lcpp_dotenv -lcurl -lssl -lcrypto +MYLIBS += -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lev -lprometheus-cpp-pull -lprometheus-cpp-core -luuid +MYLIBS += -Wl,-Bdynamic -lpthread -lm -lz -lrt -ldl $(EXTRALINK) +#MYLIBS := -Wl,--export-dynamic -Wl,-Bdynamic -lssl -lcrypto -lgnutls -ltap -lcpp_dotenv -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lev -lprometheus-cpp-pull -lprometheus-cpp-core -luuid -Wl,-Bdynamic -lpthread -lm -lz -lrt -ldl $(EXTRALINK) +MYLIBSJEMALLOC := -Wl,-Bstatic -ljemalloc +STATIC_LIBS := $(CITYHASH_LDIR)/libcityhash.a +#STATIC_LIBS := $(SSL_LDIR)/libssl.a $(SSL_LDIR)/libcrypto.a $(CITYHASH_LDIR)/libcityhash.a +LIBCOREDUMPERAR := ifeq ($(UNAME_S),Linux) - LIBCOREDUMPERAR=$(COREDUMPER_LDIR)/libcoredumper.a - STATIC_LIBS+= $(LIBCOREDUMPERAR) + LIBCOREDUMPERAR := $(COREDUMPER_LDIR)/libcoredumper.a + STATIC_LIBS += $(LIBCOREDUMPERAR) endif -.PHONY: all -all: tests - -.PHONY: clean -clean: - rm -f *-t galera_1_timeout_count galera_2_timeout_no_count aurora generate_set_session_csv set_testing-240.csv clickhouse_php_conn-t reg_test_3992_fast_forward_malformed_packet-pymysql-t || true +### detect compiler support for c++11/17 +CPLUSPLUS := $(shell ${CC} -std=c++17 -dM -E -x c++ /dev/null 2>/dev/null | grep -F __cplusplus | grep -Po '\d\d\d\d\d\dL') +ifneq ($(CPLUSPLUS),201703L) + CPLUSPLUS := $(shell ${CC} -std=c++11 -dM -E -x c++ /dev/null 2>/dev/null| grep -F __cplusplus | grep -Po '\d\d\d\d\d\dL') +ifneq ($(CPLUSPLUS),201103L) + $(error Compiler must support at least c++11) +endif +endif +STDCPP := -std=c++$(shell echo $(CPLUSPLUS) | cut -c3-4) -DCXX$(shell echo $(CPLUSPLUS) | cut -c3-4) -WITHGCOVVAR := $(shell echo $(WITHGCOV)) -ifeq ($(WITHGCOVVAR),1) -WGCOV=-DWITHGCOV --coverage -lgcov -else -WGCOV= +WGCOV := +ifeq ($(WITHGCOV),1) + WGCOV := -DWITHGCOV --coverage -lgcov endif -WITHASANVAR := $(shell echo $(WITHASAN)) -ifeq ($(WITHASANVAR),1) -WASAN=-fsanitize=address -DTEST_WITHASAN -else -WASAN= +WASAN := +ifeq ($(WITHASAN),1) + WASAN := -fsanitize=address -DTEST_WITHASAN endif -OPT=-O2 $(WGCOV) -Wl,--no-as-needed -debug: OPT=-O0 -DDEBUG -ggdb -Wl,--no-as-needed $(WGCOV) $(WASAN) +OPT := $(STDCPP) -O2 -ggdb -Wl,--no-as-needed -Wl,-rpath,$(TAP_LDIR) $(WGCOV) $(WASAN) -DGITVERSION=\"$(GIT_VERSION)\" + + +### main targets + +.DEFAULT: default +.PHONY: default +default: all + +.PHONY: all +all: tests + +debug: OPT := $(STDCPP) -O0 -DDEBUG -ggdb -Wl,--no-as-needed -Wl,-rpath,$(TAP_LDIR) $(WGCOV) $(WASAN) -DGITVERSION=\"$(GIT_VERSION)\" debug: tests -tests: tests-cpp tests-php tests-py \ - setparser_test reg_test_3504-change_user_libmariadb_helper reg_test_3504-change_user_libmysql_helper \ +#tests: build_test_deps +tests: tests-cpp \ + tests-php \ + tests-py \ + setparser_test \ + reg_test_3504-change_user_libmariadb_helper \ + reg_test_3504-change_user_libmysql_helper \ setparser_test2 setparser_test2-t \ setparser_test3 setparser_test3-t \ - set_testing-240.csv test_clickhouse_server_libmysql-t reg_test_stmt_resultset_err_no_rows_libmysql-t \ - prepare_statement_err3024_libmysql-t prepare_statement_err3024_async-t reg_test_mariadb_stmt_store_result_libmysql-t \ + set_testing-240.csv \ + test_clickhouse_server_libmysql-t \ + reg_test_stmt_resultset_err_no_rows_libmysql-t \ + prepare_statement_err3024_libmysql-t \ + prepare_statement_err3024_async-t \ + reg_test_mariadb_stmt_store_result_libmysql-t \ reg_test_mariadb_stmt_store_result_async-t + tests-cpp: $(patsubst %.cpp,%,$(wildcard *-t.cpp)) tests-php: $(patsubst %,php-%,$(wildcard *-t.php)) tests-py: $(patsubst %,py-%,$(wildcard *-t.py)) testgalera: galera_1_timeout_count galera_2_timeout_no_count testaurora: aurora + +### test deps targets + +#build_test_deps: $(TAP_LDIR)/libtap.so $(TEST_MARIADB_LDIR)/libmariadbclient.a $(TEST_MYSQL_LDIR)/libmysqlclient.a + +$(TAP_LDIR)/libtap.so: + cd $(TAP_PATH) && CC=${CC} CXX=${CXX} ${MAKE} + +$(TEST_MARIADB_LDIR)/libmariadbclient.a: + cd $(TEST_DEPS_PATH) && CC=${CC} CXX=${CXX} ${MAKE} mariadb_client + +$(TEST_MYSQL_LDIR)/libmysqlclient.a: + cd $(TEST_DEPS_PATH) && CC=${CC} CXX=${CXX} ${MAKE} mysql_client + + +### helper targets + set_testing-240.csv: generate_set_session_csv ./generate_set_session_csv > set_testing-240.csv @@ -153,82 +216,100 @@ py-%: cp $(patsubst py-%,%,$@) $(patsubst py-%.py,%,$@) chmod +x $(patsubst py-%.py,%,$@) -%-t: %-t.cpp $(TAP_LIBDIR)/libtap.so - $(CXX) -std=c++11 $< $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(STATIC_LIBS) -o $@ -# $(CXX) -std=c++11 $< $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -lpthread -ldl $(STATIC_LIBS) $(TAP_LIBDIR)/libtap.a -o $@ +%-t: %-t.cpp $(TAP_LDIR)/libtap.so + $(CXX) $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(STATIC_LIBS) -o $@ +# $(CXX) $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) -lpthread -ldl $(STATIC_LIBS) $(TAP_LDIR)/libtap.so -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) $(MYLIBSJEMALLOC) $(MYLIBS) $(STATIC_LIBS) -o galera_1_timeout_count -DGITVERSION=\"$(GIT_VERSION)\" +galera_1_timeout_count: galera_1_timeout_count.cpp $(TAP_LDIR)/libtap.so + g++ -DTEST_GALERA $< ../tap/SQLite3_Server.cpp $(IDIRS) $(LDIRS) $(OPT) $(OBJ) $(MYLIBSJEMALLOC) $(MYLIBS) $(STATIC_LIBS) -o $@ -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) $(MYLIBSJEMALLOC) $(MYLIBS) $(STATIC_LIBS) -o galera_2_timeout_no_count -DGITVERSION=\"$(GIT_VERSION)\" +galera_2_timeout_no_count: galera_2_timeout_no_count.cpp $(TAP_LDIR)/libtap.so + g++ -DTEST_GALERA $< ../tap/SQLite3_Server.cpp $(IDIRS) $(LDIRS) $(OPT) $(OBJ) $(MYLIBSJEMALLOC) $(MYLIBS) $(STATIC_LIBS) -o $@ generate_set_session_csv: generate_set_session_csv.cpp - g++ -o generate_set_session_csv generate_set_session_csv.cpp -O0 -ggdb + g++ $< $(OPT) -o $@ -aurora: aurora.cpp $(TAP_LIBDIR)/libtap.a - g++ -DTEST_AURORA -DDEBUG aurora.cpp ../tap/SQLite3_Server.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) $(MYLIBSJEMALLOC) $(MYLIBS) $(STATIC_LIBS) -o aurora -DGITVERSION=\"$(GIT_VERSION)\" +aurora: aurora.cpp $(TAP_LDIR)/libtap.so + g++ -DTEST_AURORA $< ../tap/SQLite3_Server.cpp $(IDIRS) $(LDIRS) $(OPT) $(OBJ) $(MYLIBSJEMALLOC) $(MYLIBS) $(STATIC_LIBS) -o $@ -test_tokenizer-t: test_tokenizer-t.cpp $(TAP_LIBDIR)/libtap.a - g++ test_tokenizer-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -o test_tokenizer-t -DGITVERSION=\"$(GIT_VERSION)\" +test_tokenizer-t: test_tokenizer-t.cpp $(TAP_LDIR)/libtap.so + g++ $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) -o $@ -test_mysql_query_digests_stages-t: test_mysql_query_digests_stages-t.cpp $(TAP_LIBDIR)/libtap.a - g++ test_mysql_query_digests_stages-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -o test_mysql_query_digests_stages-t -DGITVERSION=\"$(GIT_VERSION)\" +test_mysql_query_digests_stages-t: test_mysql_query_digests_stages-t.cpp $(TAP_LDIR)/libtap.so + g++ $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) -o $@ -sqlite3-t: sqlite3-t.cpp $(TAP_LIBDIR)/libtap.a - g++ sqlite3-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) $(LIBCOREDUMPERAR) -o sqlite3-t -DGITVERSION=\"$(GIT_VERSION)\" +sqlite3-t: sqlite3-t.cpp $(TAP_LDIR)/libtap.so + g++ $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(LIBCOREDUMPERAR) -o $@ -test_gtid_forwarding-t: test_gtid_forwarding-t.cpp $(TAP_LIBDIR)/libtap.a - g++ test_gtid_forwarding-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -o test_gtid_forwarding-t -DGITVERSION=\"$(GIT_VERSION)\" +test_gtid_forwarding-t: test_gtid_forwarding-t.cpp $(TAP_LDIR)/libtap.so + g++ $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) -o $@ -test_admin_prometheus_metrics_dump-t: test_admin_prometheus_metrics_dump-t.cpp $(TAP_LIBDIR)/libtap.a - g++ test_admin_prometheus_metrics_dump-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -o test_admin_prometheus_metrics_dump-t -DGITVERSION=\"$(GIT_VERSION)\" +test_admin_prometheus_metrics_dump-t: test_admin_prometheus_metrics_dump-t.cpp $(TAP_LDIR)/libtap.so + g++ $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) -o $@ -create_connection_annotation: test_connection_annotation-t.cpp - g++ -DTEST_AURORA -DDEBUG test_connection_annotation-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) $(MYLIBS) $(STATIC_LIBS) -o test_connection_annotation-t -DGITVERSION=\"$(GIT_VERSION)\" +create_connection_annotation: test_connection_annotation-t.cpp $(TAP_LDIR)/libtap.so + g++ -DTEST_AURORA $< $(IDIRS) $(LDIRS) $(OPT) $(OBJ) $(MYLIBS) $(STATIC_LIBS) -o $@ -setparser_test: setparser_test.cpp $(TAP_LIBDIR)/libtap.a $(RE2_PATH)/util/test.cc $(LDIR)/set_parser.cpp $(LIBPROXYSQLAR) $(LIBCOREDUMPERAR) - g++ -DDEBUG setparser_test.cpp $(RE2_PATH)/util/test.cc $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) $(WASAN) $(LIBCOREDUMPERAR) -o setparser_test -DGITVERSION=\"$(GIT_VERSION)\" +setparser_test: setparser_test.cpp $(TAP_LDIR)/libtap.so $(RE2_PATH)/util/test.cc $(PROXYSQL_LDIR)/set_parser.cpp $(LIBPROXYSQLAR) $(LIBCOREDUMPERAR) + g++ $< $(RE2_PATH)/util/test.cc $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(LIBCOREDUMPERAR) -o $@ setparser_test2-t: setparser_test2 rm setparser_test2-t || true - ln -s setparser_test2 setparser_test2-t + ln -fs setparser_test2 setparser_test2-t -setparser_test2: setparser_test2.cpp $(TAP_LIBDIR)/libtap.a $(LDIR)/set_parser.cpp setparser_test_common.h $(LIBCOREDUMPERAR) - g++ -DDEBUG setparser_test2.cpp $(LDIR)/set_parser.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) $(WASAN) $(LIBCOREDUMPERAR) -o setparser_test2 -DGITVERSION=\"$(GIT_VERSION)\" +setparser_test2: setparser_test2.cpp $(TAP_LDIR)/libtap.so $(PROXYSQL_LDIR)/set_parser.cpp setparser_test_common.h $(LIBCOREDUMPERAR) + g++ $< $(PROXYSQL_LDIR)/set_parser.cpp $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(LIBCOREDUMPERAR) -o $@ setparser_test3-t: setparser_test3 rm setparser_test3-t || true - ln -s setparser_test3 setparser_test3-t + ln -fs setparser_test3 setparser_test3-t + +setparser_test3: setparser_test3.cpp $(TAP_LDIR)/libtap.so $(PROXYSQL_LDIR)/set_parser.cpp setparser_test_common.h $(LIBCOREDUMPERAR) + g++ -DPARSERDEBUG $< $(PROXYSQL_LDIR)/set_parser.cpp $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(LIBCOREDUMPERAR) -o $@ + -setparser_test3: setparser_test3.cpp $(TAP_LIBDIR)/libtap.a $(LDIR)/set_parser.cpp setparser_test_common.h $(LIBCOREDUMPERAR) - g++ -DDEBUG -DPARSERDEBUG setparser_test3.cpp $(LDIR)/set_parser.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) $(WASAN) $(LIBCOREDUMPERAR) -o setparser_test3 -DGITVERSION=\"$(GIT_VERSION)\" +CUSTOMARGS := -I$(TAP_IDIR) -I$(CURL_IDIR) -I$(SQLITE3_IDIR) -I$(PROXYSQL_IDIR) -I$(JSON_IDIR) -I$(SSL_IDIR) +CUSTOMARGS += -L$(TAP_LDIR) -L$(CURL_LDIR) -L$(SSL_LDIR) +#CUSTOMARGS += -Wl,-Bstatic -lcurl +CUSTOMARGS += -Wl,-Bdynamic -ltap -lcpp_dotenv -lcurl -lssl -lcrypto -lpthread -lz -ldl +CUSTOMARGS += $(OPT) -CUSTOMARGS=-DGITVERSION=\"$(GIT_VERSION)\" -I$(SQLITE3_DIR) -I$(IDIR) -I$(CURL_IDIR) -I$(JSON_IDIR) -I../tap -L$(TAP_LIBDIR) -L$(CURL_LDIR) -Wl,-Bstatic -lcurl -Wl,-Bdynamic -ltap -lcpp_dotenv -lpthread -std=c++11 -lz -ldl +reg_test_3504-change_user_libmariadb_helper: reg_test_3504-change_user_helper.cpp $(TAP_LDIR)/libtap.so + $(CXX) -DDISABLE_WARNING_COUNT_LOGGING $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(STATIC_LIBS) -o $@ -reg_test_3504-change_user_libmariadb_helper: reg_test_3504-change_user_helper.cpp $(TAP_LIBDIR)/libtap.so - $(CXX) -DDISABLE_WARNING_COUNT_LOGGING -DDEBUG reg_test_3504-change_user_helper.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -std=c++11 $(STATIC_LIBS) -o reg_test_3504-change_user_libmariadb_helper -DGITVERSION=\"$(GIT_VERSION)\" +reg_test_3504-change_user_libmysql_helper: reg_test_3504-change_user_helper.cpp $(TAP_LDIR)/libtap.so + $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient $(CUSTOMARGS) -o $@ -reg_test_3504-change_user_libmysql_helper: reg_test_3504-change_user_helper.cpp - $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING reg_test_3504-change_user_helper.cpp -I/usr/include/mysql $(CUSTOMARGS) -lmysqlclient -o reg_test_3504-change_user_libmysql_helper +test_clickhouse_server_libmysql-t: test_clickhouse_server-t.cpp $(TAP_LDIR)/libtap.so + $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient $(CUSTOMARGS) -o $@ -test_clickhouse_server_libmysql-t: test_clickhouse_server-t.cpp - $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING -DDEBUG test_clickhouse_server-t.cpp -I/usr/include/mysql $(CUSTOMARGS) -lmysqlclient -o test_clickhouse_server_libmysql-t +reg_test_stmt_resultset_err_no_rows_libmysql-t: reg_test_stmt_resultset_err_no_rows-t.cpp $(TAP_LDIR)/libtap.so + $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient $(CUSTOMARGS) -o $@ -reg_test_stmt_resultset_err_no_rows_libmysql-t: reg_test_stmt_resultset_err_no_rows-t.cpp - $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING reg_test_stmt_resultset_err_no_rows-t.cpp -I/usr/include/mysql $(CUSTOMARGS) -lmysqlclient -o reg_test_stmt_resultset_err_no_rows_libmysql-t +reg_test_mariadb_stmt_store_result_libmysql-t: reg_test_mariadb_stmt_store_result-t.cpp $(TAP_LDIR)/libtap.so + $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient $(CUSTOMARGS) -o $@ -reg_test_mariadb_stmt_store_result_libmysql-t: reg_test_mariadb_stmt_store_result-t.cpp $(TAP_LIBDIR)/libtap.a - $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING reg_test_mariadb_stmt_store_result-t.cpp -I/usr/include/mysql $(CUSTOMARGS) -lmysqlclient -o reg_test_mariadb_stmt_store_result_libmysql-t +reg_test_mariadb_stmt_store_result_async-t: reg_test_mariadb_stmt_store_result-t.cpp $(TAP_LDIR)/libtap.so + $(CXX) -DASYNC_API $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(STATIC_LIBS) -o $@ -reg_test_mariadb_stmt_store_result_async-t: reg_test_mariadb_stmt_store_result-t.cpp $(TAP_LIBDIR)/libtap.a - $(CXX) -DASYNC_API reg_test_mariadb_stmt_store_result-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -lpthread -ldl -std=c++11 -ltap $(STATIC_LIBS) -o reg_test_mariadb_stmt_store_result_async-t +prepare_statement_err3024_libmysql-t: prepare_statement_err3024-t.cpp $(TAP_LDIR)/libtap.so + $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient $(CUSTOMARGS) -o $@ -prepare_statement_err3024_libmysql-t: prepare_statement_err3024-t.cpp $(TAP_LIBDIR)/libtap.a - $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING prepare_statement_err3024-t.cpp -I/usr/include/mysql $(CUSTOMARGS) -lmysqlclient -o prepare_statement_err3024_libmysql-t +prepare_statement_err3024_async-t: prepare_statement_err3024-t.cpp $(TAP_LDIR)/libtap.so + $(CXX) -DASYNC_API $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(STATIC_LIBS) -o $@ -prepare_statement_err3024_async-t: prepare_statement_err3024-t.cpp $(TAP_LIBDIR)/libtap.a - $(CXX) -DASYNC_API prepare_statement_err3024-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -lpthread -ldl -std=c++11 -ltap $(STATIC_LIBS) -o prepare_statement_err3024_async-t -DGITVERSION=\"$(GIT_VERSION)\" +test_wexecvp_syscall_failures-t: test_wexecvp_syscall_failures-t.cpp $(TAP_LDIR)/libtap.so + $(CXX) $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) -Wl,--wrap=pipe,--wrap=fcntl,--wrap=read,--wrap=poll $(STATIC_LIBS) -o $@ -test_wexecvp_syscall_failures-t: test_wexecvp_syscall_failures-t.cpp $(TAP_LIBDIR)/libtap.a - $(CXX) test_wexecvp_syscall_failures-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -std=c++11 -Wl,--wrap=pipe,--wrap=fcntl,--wrap=read,--wrap=poll -lpthread -ldl -ltap $(STATIC_LIBS) -o $@ + +### clean targets + +.SILENT: clean +.PHONY: clean +clean: + rm -f *-t || true + rm -f galera_1_timeout_count galera_2_timeout_no_count aurora || true + rm -f generate_set_session_csv set_testing-240.csv || true + rm -f setparser_test setparser_test2 setparser_test3 || true + rm -f reg_test_3504-change_user_libmariadb_helper reg_test_3504-change_user_libmysql_helper || true + rm -f *.gcda *.gcno || true diff --git a/test/tap/tests/admin-listen_on_unix-t.cpp b/test/tap/tests/admin-listen_on_unix-t.cpp index b5917a5d7c..94c6d0f2dd 100644 --- a/test/tap/tests/admin-listen_on_unix-t.cpp +++ b/test/tap/tests/admin-listen_on_unix-t.cpp @@ -4,8 +4,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "proxysql_utils.h" #include "tap.h" diff --git a/test/tap/tests/admin_show_create_table-t.cpp b/test/tap/tests/admin_show_create_table-t.cpp index 7647e59891..5a995b1a6b 100644 --- a/test/tap/tests/admin_show_create_table-t.cpp +++ b/test/tap/tests/admin_show_create_table-t.cpp @@ -6,8 +6,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/admin_show_fields_from-t.cpp b/test/tap/tests/admin_show_fields_from-t.cpp index 6ace382f26..8252bafd46 100644 --- a/test/tap/tests/admin_show_fields_from-t.cpp +++ b/test/tap/tests/admin_show_fields_from-t.cpp @@ -6,8 +6,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/admin_show_table_status-t.cpp b/test/tap/tests/admin_show_table_status-t.cpp index 42c02b7d07..1e48e78471 100644 --- a/test/tap/tests/admin_show_table_status-t.cpp +++ b/test/tap/tests/admin_show_table_status-t.cpp @@ -6,8 +6,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/admin_various_commands-t.cpp b/test/tap/tests/admin_various_commands-t.cpp index db683ba859..cbc2ea3c9b 100644 --- a/test/tap/tests/admin_various_commands-t.cpp +++ b/test/tap/tests/admin_various_commands-t.cpp @@ -6,8 +6,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/admin_various_commands2-t.cpp b/test/tap/tests/admin_various_commands2-t.cpp index d38c2cc409..94081b1e67 100644 --- a/test/tap/tests/admin_various_commands2-t.cpp +++ b/test/tap/tests/admin_various_commands2-t.cpp @@ -6,8 +6,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/admin_various_commands3-t.cpp b/test/tap/tests/admin_various_commands3-t.cpp index 4a80f16b08..db86720d81 100644 --- a/test/tap/tests/admin_various_commands3-t.cpp +++ b/test/tap/tests/admin_various_commands3-t.cpp @@ -6,8 +6,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/charset_unsigned_int-t.cpp b/test/tap/tests/charset_unsigned_int-t.cpp index 62fd4cdac6..fafc557d48 100644 --- a/test/tap/tests/charset_unsigned_int-t.cpp +++ b/test/tap/tests/charset_unsigned_int-t.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/firewall_commands1-t.cpp b/test/tap/tests/firewall_commands1-t.cpp index f3bfd1a631..6aa56cf61d 100644 --- a/test/tap/tests/firewall_commands1-t.cpp +++ b/test/tap/tests/firewall_commands1-t.cpp @@ -6,8 +6,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/generate_set_session_csv.cpp b/test/tap/tests/generate_set_session_csv.cpp index 900a85e525..95eed9e68f 100644 --- a/test/tap/tests/generate_set_session_csv.cpp +++ b/test/tap/tests/generate_set_session_csv.cpp @@ -5,6 +5,7 @@ #include #include #include +#include std::vector bool_values = { "0", "1", @@ -64,7 +65,7 @@ class variable { }; -std::unordered_map vars; +std::unordered_map> vars; void add_value_j(std::string& j, const std::string& s, variable *v) { @@ -125,44 +126,44 @@ void add_values_and_quotes(const std::string& name, const std::vector("sql_log_bin", false, false, true); vars["sql_log_bin"]->add(bool_values); - vars["sql_safe_updates"] = new variable("sql_safe_updates", true, false, true); + vars["sql_safe_updates"] = std::make_unique("sql_safe_updates", true, false, true); vars["sql_safe_updates"]->add(bool_values); - vars["sql_big_selects"] = new variable("sql_big_selects", true, false, true); + vars["sql_big_selects"] = std::make_unique("sql_big_selects", true, false, true); vars["sql_big_selects"]->add(bool_values); -// vars["wsrep_sync_wait"] = new variable("wsrep_sync_wait", true, false); +// vars["wsrep_sync_wait"] = std::make_unique("wsrep_sync_wait", true, false); // vars["wsrep_sync_wait"]->add(bool_values); - vars["sql_auto_is_null"] = new variable("sql_auto_is_null", true, false, true); + vars["sql_auto_is_null"] = std::make_unique("sql_auto_is_null", true, false, true); vars["sql_auto_is_null"]->add(bool_values); - vars["foreign_key_checks"] = new variable("foreign_key_checks", true, false, true); + vars["foreign_key_checks"] = std::make_unique("foreign_key_checks", true, false, true); vars["foreign_key_checks"]->add(bool_values); - vars["unique_checks"] = new variable("unique_checks", true, false, true); + vars["unique_checks"] = std::make_unique("unique_checks", true, false, true); vars["unique_checks"]->add(bool_values); - vars["innodb_lock_wait_timeout"] = new variable("innodb_lock_wait_timeout", true, true, false); + vars["innodb_lock_wait_timeout"] = std::make_unique("innodb_lock_wait_timeout", true, true, false); vars["innodb_lock_wait_timeout"]->add(int_values_small, 34); vars["innodb_lock_wait_timeout"]->add(int_values, 117); - vars["innodb_strict_mode"] = new variable("innodb_strict_mode", true, false, true); + vars["innodb_strict_mode"] = std::make_unique("innodb_strict_mode", true, false, true); vars["innodb_strict_mode"]->add(bool_values); - vars["innodb_table_locks"] = new variable("innodb_table_locks", true, false, true); + vars["innodb_table_locks"] = std::make_unique("innodb_table_locks", true, false, true); vars["innodb_table_locks"]->add(bool_values); - vars["aurora_read_replica_read_committed"] = new variable("aurora_read_replica_read_committed", true, false, true); + vars["aurora_read_replica_read_committed"] = std::make_unique("aurora_read_replica_read_committed", true, false, true); vars["aurora_read_replica_read_committed"]->add(bool_values); - //vars[""] = new variable(""); + //vars[""] = std::make_unique(""); //vars[""]->add(bool_values); - vars["auto_increment_increment"] = new variable("auto_increment_increment", true, true, false); + vars["auto_increment_increment"] = std::make_unique("auto_increment_increment", true, true, false); vars["auto_increment_increment"]->add(int_values_small, 10); - vars["auto_increment_offset"] = new variable("auto_increment_offset", true, true, false); + vars["auto_increment_offset"] = std::make_unique("auto_increment_offset", true, true, false); vars["auto_increment_offset"]->add(int_values_small, 20); - vars["sql_select_limit"] = new variable("sql_select_limit", true, true, false); + vars["sql_select_limit"] = std::make_unique("sql_select_limit", true, true, false); vars["sql_select_limit"]->add(int_values_small, 80); vars["sql_select_limit"]->add(int_values, 80); - vars["group_concat_max_len"] = new variable("group_concat_max_len", true, true, false); + vars["group_concat_max_len"] = std::make_unique("group_concat_max_len", true, true, false); vars["group_concat_max_len"]->add(int_values_small, 123); vars["group_concat_max_len"]->add(int_values, 123); - vars["join_buffer_size"] = new variable("join_buffer_size", true, true, false); + vars["join_buffer_size"] = std::make_unique("join_buffer_size", true, true, false); vars["join_buffer_size"]->add(int_values, 1028); { // join_buffer_size uses blocks of 128 , so we need to round it @@ -175,21 +176,21 @@ int main() { } } - vars["query_cache_type"] = new variable("query_cache_type", true, true, false); + vars["query_cache_type"] = std::make_unique("query_cache_type", true, true, false); vars["query_cache_type"]->add(bool_values); vars["query_cache_type"]->add("2"); add_values_and_quotes("query_cache_type", {"DeMaNd"}); - vars["lock_wait_timeout"] = new variable("lock_wait_timeout", true, true, false); + vars["lock_wait_timeout"] = std::make_unique("lock_wait_timeout", true, true, false); vars["lock_wait_timeout"]->add(int_values_small, 321); - vars["max_join_size"] = new variable("max_join_size", true, true, false); + vars["max_join_size"] = std::make_unique("max_join_size", true, true, false); vars["max_join_size"]->add(int_values, 1000); vars["max_join_size"]->add("18446744073709551615"); vars["max_join_size"]->add("DEFAULT"); - vars["tmp_table_size"] = new variable("tmp_table_size", true, true, false); + vars["tmp_table_size"] = std::make_unique("tmp_table_size", true, true, false); vars["tmp_table_size"]->add(int_values, 10050); vars["tmp_table_size"]->add("18446744000000051615"); - vars["max_heap_table_size"] = new variable("max_heap_table_size", true, true, false); + vars["max_heap_table_size"] = std::make_unique("max_heap_table_size", true, true, false); vars["max_heap_table_size"]->add(int_values, 20031); vars["max_heap_table_size"]->add("8446744073709547520"); { @@ -202,67 +203,67 @@ int main() { *it = std::to_string(a); } } - vars["sort_buffer_size"] = new variable("sort_buffer_size", true, true, false); + vars["sort_buffer_size"] = std::make_unique("sort_buffer_size", true, true, false); vars["sort_buffer_size"]->add(int_values, 40123); vars["sort_buffer_size"]->add("18446744073709551615"); - vars["max_statement_time"] = new variable("max_statement_time", true, true, false); + vars["max_statement_time"] = std::make_unique("max_statement_time", true, true, false); vars["max_statement_time"]->add(int_values, 12012); vars["max_statement_time"]->add("0"); - vars["max_execution_time"] = new variable("max_execution_time", true, true, false); + vars["max_execution_time"] = std::make_unique("max_execution_time", true, true, false); vars["max_execution_time"]->add(int_values, 12930); vars["max_execution_time"]->add("0"); - vars["long_query_time"] = new variable("long_query_time", true, true, false); + vars["long_query_time"] = std::make_unique("long_query_time", true, true, false); vars["long_query_time"]->add(int_values, 2951); vars["long_query_time"]->add(int_values_small, 498); vars["long_query_time"]->add("0"); - vars["max_sort_length"] = new variable("max_sort_length", true, true, false); + vars["max_sort_length"] = std::make_unique("max_sort_length", true, true, false); vars["max_sort_length"]->add(int_values, 1123); - vars["optimizer_prune_level"] = new variable("optimizer_prune_level", true, true, false); + vars["optimizer_prune_level"] = std::make_unique("optimizer_prune_level", true, true, false); vars["optimizer_prune_level"]->add({0, 1}, 0); - vars["optimizer_search_depth"] = new variable("optimizer_search_depth", true, true, false); + vars["optimizer_search_depth"] = std::make_unique("optimizer_search_depth", true, true, false); vars["optimizer_search_depth"]->add({0, 1, 12, 30, 62}, 0); - vars["timestamp"] = new variable("timestamp", true, true, false); + vars["timestamp"] = std::make_unique("timestamp", true, true, false); vars["timestamp"]->add("1640011196"); for (unsigned long long i = 0 ; i < 10 ; i++) { unsigned long long ts = 1640011196 + i*3600*24; vars["timestamp"]->add(std::to_string(ts)); } - vars["session_track_gtids"] = new variable("session_track_gtids", true, true, false); + vars["session_track_gtids"] = std::make_unique("session_track_gtids", true, true, false); vars["session_track_gtids"]->add("OWN_GTID"); // vars["session_track_gtids"]->add("OFF"); // vars["session_track_gtids"]->add("ALL_GTID"); - vars["optimizer_switch"] = new variable("optimizer_switch", true, false, false); + vars["optimizer_switch"] = std::make_unique("optimizer_switch", true, false, false); vars["optimizer_switch"]->add(std::vector {"'materialization=off'", "`materialization=on`", "\"materialization=off\""}); vars["optimizer_switch"]->add(std::vector {"'index_merge_union=off'", "`index_merge_union=on`", "\"index_merge_union=off\""}); vars["optimizer_switch"]->add(std::vector {"'semijoin=off'", "`semijoin=on`", "\"semijoin=off\""}); - vars["optimizer_use_condition_selectivity"] = new variable("optimizer_use_condition_selectivity", true, true, false); + vars["optimizer_use_condition_selectivity"] = std::make_unique("optimizer_use_condition_selectivity", true, true, false); vars["optimizer_use_condition_selectivity"]->add({1, 2, 3, 4, 5}, 0); - vars["lc_time_names"] = new variable("lc_time_names", true, false, false); + vars["lc_time_names"] = std::make_unique("lc_time_names", true, false, false); vars["lc_time_names"]->add(std::vector {"en_US", "'en_US'", "`en_US`", "\"en_US\""}); vars["lc_time_names"]->add(std::vector {"en_GB", "'en_GB'", "`en_GB`", "\"en_GB\""}); //vars["lc_time_names"]->add(std::vector {"ja_JP", "'ja_JP'", "`ja_JP`", "\"ja_JP\""}); vars["lc_time_names"]->add(std::vector {"pt_BR", "'pt_BR'", "`pt_BR`", "\"pt_BR\""}); - vars["lc_messages"] = new variable("lc_messages", true, false, false); + vars["lc_messages"] = std::make_unique("lc_messages", true, false, false); vars["lc_messages"]->add(std::vector {"it_IT", "'it_IT'", "`it_IT`", "\"it_IT\""}); vars["lc_messages"]->add(std::vector {"es_ES", "'es_ES'", "`es_ES`", "\"es_ES\""}); vars["lc_messages"]->add(std::vector {"fr_FR", "'fr_FR'", "`fr_FR`", "\"fr_FR\""}); - vars["log_slow_filter"] = new variable("log_slow_filter", true, false, false); + vars["log_slow_filter"] = std::make_unique("log_slow_filter", true, false, false); vars["log_slow_filter"]->add(std::vector {"'not_using_index'", "`not_using_index`", "\"not_using_index\""}); vars["log_slow_filter"]->add(std::vector {"'admin,filesort,filesort_on_disk,full_join'", "`admin,filesort,filesort_on_disk,full_join`", "\"admin,filesort,filesort_on_disk,full_join\""}); - vars["time_zone"] = new variable("time_zone", true, false, false); + vars["time_zone"] = std::make_unique("time_zone", true, false, false); vars["time_zone"]->add(std::vector {"'+01:00'", "`+02:15`", "\"+03:30\""}); vars["time_zone"]->add(std::vector {"'+04:45'", "`+05:00`", "\"+06:10\""}); vars["time_zone"]->add(std::vector {"'-1:10'", "`-03:33`", "\"-04:56\""}); vars["time_zone"]->add(std::vector {"'+02:45'", "`+7:00`", "\"+06:10\""}); vars["time_zone"]->add(std::vector {"'-11:10'", "`-11:33`", "\"-04:56\""}); - vars["sql_mode"] = new variable("sql_mode", true, false, false); + vars["sql_mode"] = std::make_unique("sql_mode", true, false, false); vars["sql_mode"]->add(std::vector {"'traditional'", "TRADITIONAL", "''"}); vars["sql_mode"]->add(std::vector {"'NO_BACKSLASH_ESCAPES'", "NO_BACKSLASH_ESCAPES"}); vars["sql_mode"]->add(std::vector {"'TRADITIONAL,NO_BACKSLASH_ESCAPES'", "\"NO_BACKSLASH_ESCAPES,TRADITIONAL\""}); @@ -274,24 +275,24 @@ int main() { vars["sql_mode"]->add(std::vector {"''", "\"\"", "' '", "\" \"", "' '", "\" \"",}); - vars["default_storage_engine"] = new variable("default_storage_engine", true, false, false); + vars["default_storage_engine"] = std::make_unique("default_storage_engine", true, false, false); add_values_and_quotes("default_storage_engine", {"InnoDB", "MEMORY", "MyISAM", "BLACKHOLE"}); - vars["default_tmp_storage_engine"] = new variable("default_tmp_storage_engine", true, false, false); + vars["default_tmp_storage_engine"] = std::make_unique("default_tmp_storage_engine", true, false, false); vars["default_tmp_storage_engine"]->add(vars["default_storage_engine"]->values); - vars["group_replication_consistency"] = new variable("group_replication_consistency", true, false, false); + vars["group_replication_consistency"] = std::make_unique("group_replication_consistency", true, false, false); add_values_and_quotes("group_replication_consistency", {"EVENTUAL", "BEFORE_ON_PRIMARY_FAILOVER", "BEFORE", "AFTER", "BEFORE_AND_AFTER"}); - vars["wsrep_osu_method"] = new variable("wsrep_osu_method", true, false, false); + vars["wsrep_osu_method"] = std::make_unique("wsrep_osu_method", true, false, false); add_values_and_quotes("wsrep_osu_method", {"TOI","RSU"}); - vars["sql_quote_show_create"] = new variable("sql_quote_show_create", true, false, true); + vars["sql_quote_show_create"] = std::make_unique("sql_quote_show_create", true, false, true); vars["sql_quote_show_create"]->add(bool_values); - vars["sql_require_primary_key"] = new variable("sql_require_primary_key", true, false, true); + vars["sql_require_primary_key"] = std::make_unique("sql_require_primary_key", true, false, true); vars["sql_require_primary_key"]->add(bool_values); - vars["sql_generate_invisible_primary_key"] = new variable("sql_generate_invisible_primary_key", true, false, true); + vars["sql_generate_invisible_primary_key"] = std::make_unique("sql_generate_invisible_primary_key", true, false, true); vars["sql_generate_invisible_primary_key"]->add(bool_values); /* example: @@ -301,8 +302,8 @@ int main() { variable * vararray[vars.size()]; { unsigned int i = 0; - for (std::unordered_map::iterator it = vars.begin() ; it != vars.end() ; it++) { - vararray[i] = it->second; + for (std::unordered_map>::iterator it = vars.begin() ; it != vars.end() ; it++) { + vararray[i] = it->second.get(); i++; } } @@ -360,9 +361,9 @@ int main() { j += "sql_big_selects"; j += "\":\""; if (strcasecmp(s2.c_str(),(char *)"default")==0 || s2 == "18446744073709551615") { - add_value_j(j,"ON",vars["sql_big_selects"]); + add_value_j(j,"ON",vars["sql_big_selects"].get()); } else { - add_value_j(j,"OFF",vars["sql_big_selects"]); + add_value_j(j,"OFF",vars["sql_big_selects"].get()); } } if (ine != ne -1) { diff --git a/test/tap/tests/kill_connection-t.cpp b/test/tap/tests/kill_connection-t.cpp index ade613df06..5f964a959d 100644 --- a/test/tap/tests/kill_connection-t.cpp +++ b/test/tap/tests/kill_connection-t.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/kill_connection2-t.cpp b/test/tap/tests/kill_connection2-t.cpp index 3b89032e98..947c9d9cf5 100644 --- a/test/tap/tests/kill_connection2-t.cpp +++ b/test/tap/tests/kill_connection2-t.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/kill_connection3-t.cpp b/test/tap/tests/kill_connection3-t.cpp index 3fd31bf98a..ef28ac6cb6 100644 --- a/test/tap/tests/kill_connection3-t.cpp +++ b/test/tap/tests/kill_connection3-t.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/max_connections_ff-t.cpp b/test/tap/tests/max_connections_ff-t.cpp index 22f1285abb..f85a66f330 100644 --- a/test/tap/tests/max_connections_ff-t.cpp +++ b/test/tap/tests/max_connections_ff-t.cpp @@ -24,8 +24,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "json.hpp" diff --git a/test/tap/tests/multiple_prepared_statements-t.cpp b/test/tap/tests/multiple_prepared_statements-t.cpp index 8a4560437c..ea3bed6035 100644 --- a/test/tap/tests/multiple_prepared_statements-t.cpp +++ b/test/tap/tests/multiple_prepared_statements-t.cpp @@ -21,8 +21,8 @@ It stresses how proxysql managers prepared statements, specifically: #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "command_line.h" #include "proxysql_utils.h" diff --git a/test/tap/tests/mysql-fast_forward-t.cpp b/test/tap/tests/mysql-fast_forward-t.cpp index 7b43a29276..6fa29c4eb0 100644 --- a/test/tap/tests/mysql-fast_forward-t.cpp +++ b/test/tap/tests/mysql-fast_forward-t.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/mysql-init_connect-1-t.cpp b/test/tap/tests/mysql-init_connect-1-t.cpp index b0643b14a4..65dc2202b4 100644 --- a/test/tap/tests/mysql-init_connect-1-t.cpp +++ b/test/tap/tests/mysql-init_connect-1-t.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/mysql-init_connect-2-t.cpp b/test/tap/tests/mysql-init_connect-2-t.cpp index cc9aeea1ed..705869c70c 100644 --- a/test/tap/tests/mysql-init_connect-2-t.cpp +++ b/test/tap/tests/mysql-init_connect-2-t.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/mysql-last_insert_id-t.cpp b/test/tap/tests/mysql-last_insert_id-t.cpp index 4d8acd3acc..1e8e9d18b1 100644 --- a/test/tap/tests/mysql-last_insert_id-t.cpp +++ b/test/tap/tests/mysql-last_insert_id-t.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/mysql-mirror1-t.cpp b/test/tap/tests/mysql-mirror1-t.cpp index 42e00c8726..54e63488f7 100644 --- a/test/tap/tests/mysql-mirror1-t.cpp +++ b/test/tap/tests/mysql-mirror1-t.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/mysql-set_transaction-t.cpp b/test/tap/tests/mysql-set_transaction-t.cpp index 5472e97dd2..1948bafb40 100644 --- a/test/tap/tests/mysql-set_transaction-t.cpp +++ b/test/tap/tests/mysql-set_transaction-t.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/mysql-sql_log_bin-error-t.cpp b/test/tap/tests/mysql-sql_log_bin-error-t.cpp index 9e1e4ad792..8311372a6b 100644 --- a/test/tap/tests/mysql-sql_log_bin-error-t.cpp +++ b/test/tap/tests/mysql-sql_log_bin-error-t.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/mysql-test_ssl_CA-t.cpp b/test/tap/tests/mysql-test_ssl_CA-t.cpp index 7ded1443e9..b09d659e03 100644 --- a/test/tap/tests/mysql-test_ssl_CA-t.cpp +++ b/test/tap/tests/mysql-test_ssl_CA-t.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include "mysql.h" #include #include "tap.h" diff --git a/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp b/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp index 06a1907214..51d5300f96 100644 --- a/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp +++ b/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp @@ -15,8 +15,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "json.hpp" diff --git a/test/tap/tests/mysql_stmt_send_long_data-t.cpp b/test/tap/tests/mysql_stmt_send_long_data-t.cpp index d14a7e102e..92722c5bf8 100644 --- a/test/tap/tests/mysql_stmt_send_long_data-t.cpp +++ b/test/tap/tests/mysql_stmt_send_long_data-t.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/mysql_stmt_send_long_data_large-t.cpp b/test/tap/tests/mysql_stmt_send_long_data_large-t.cpp index 15486cfa64..e0e504e5bf 100644 --- a/test/tap/tests/mysql_stmt_send_long_data_large-t.cpp +++ b/test/tap/tests/mysql_stmt_send_long_data_large-t.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/prepare_statement_err3024-t.cpp b/test/tap/tests/prepare_statement_err3024-t.cpp index 51eea03e22..3938d4cf1e 100644 --- a/test/tap/tests/prepare_statement_err3024-t.cpp +++ b/test/tap/tests/prepare_statement_err3024-t.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_1493-mixed_compression-t.cpp b/test/tap/tests/reg_test_1493-mixed_compression-t.cpp index 75edafffba..473d3288cf 100644 --- a/test/tap/tests/reg_test_1493-mixed_compression-t.cpp +++ b/test/tap/tests/reg_test_1493-mixed_compression-t.cpp @@ -8,8 +8,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_1574-mariadb_read_stmt_execute_response-t.cpp b/test/tap/tests/reg_test_1574-mariadb_read_stmt_execute_response-t.cpp index a803d0d2d6..0a78523c7c 100644 --- a/test/tap/tests/reg_test_1574-mariadb_read_stmt_execute_response-t.cpp +++ b/test/tap/tests/reg_test_1574-mariadb_read_stmt_execute_response-t.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_1574-stmt_metadata-t.cpp b/test/tap/tests/reg_test_1574-stmt_metadata-t.cpp index 742ee3aed7..fb61d6a474 100644 --- a/test/tap/tests/reg_test_1574-stmt_metadata-t.cpp +++ b/test/tap/tests/reg_test_1574-stmt_metadata-t.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_2793-compression-t.cpp b/test/tap/tests/reg_test_2793-compression-t.cpp index 5f493f0b7d..7256094746 100644 --- a/test/tap/tests/reg_test_2793-compression-t.cpp +++ b/test/tap/tests/reg_test_2793-compression-t.cpp @@ -7,8 +7,8 @@ #include #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_3184-set_wait_timeout-t.cpp b/test/tap/tests/reg_test_3184-set_wait_timeout-t.cpp index ff5b5cdcc5..d3eddc8d40 100644 --- a/test/tap/tests/reg_test_3184-set_wait_timeout-t.cpp +++ b/test/tap/tests/reg_test_3184-set_wait_timeout-t.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp b/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp index 440e5c64f9..6eac9c9d73 100644 --- a/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp +++ b/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp @@ -18,7 +18,7 @@ #include "curl/curl.h" #include "json.hpp" #include "mysql.h" -#include "mysql/mysqld_error.h" +#include "mysqld_error.h" #include "command_line.h" #include "proxysql_utils.h" diff --git a/test/tap/tests/reg_test_3247-mycli_support-t.cpp b/test/tap/tests/reg_test_3247-mycli_support-t.cpp index 0cb9505c73..4116bd621d 100644 --- a/test/tap/tests/reg_test_3247-mycli_support-t.cpp +++ b/test/tap/tests/reg_test_3247-mycli_support-t.cpp @@ -8,8 +8,8 @@ #include #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_3273_ssl_con-t.cpp b/test/tap/tests/reg_test_3273_ssl_con-t.cpp index 32d187e15c..1b0c8b55cb 100644 --- a/test/tap/tests/reg_test_3273_ssl_con-t.cpp +++ b/test/tap/tests/reg_test_3273_ssl_con-t.cpp @@ -28,7 +28,7 @@ #include #include -#include +#include "mysql.h" #include #include "tap.h" diff --git a/test/tap/tests/reg_test_3317-lock_hostgroup_special_queries-t.cpp b/test/tap/tests/reg_test_3317-lock_hostgroup_special_queries-t.cpp index 46264349c1..5eadc264fb 100644 --- a/test/tap/tests/reg_test_3317-lock_hostgroup_special_queries-t.cpp +++ b/test/tap/tests/reg_test_3317-lock_hostgroup_special_queries-t.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_3327-process_query_set_status_flags-t.cpp b/test/tap/tests/reg_test_3327-process_query_set_status_flags-t.cpp index 8f6c0506ed..98a0983dbc 100644 --- a/test/tap/tests/reg_test_3327-process_query_set_status_flags-t.cpp +++ b/test/tap/tests/reg_test_3327-process_query_set_status_flags-t.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_3427-stmt_first_comment1-t.cpp b/test/tap/tests/reg_test_3427-stmt_first_comment1-t.cpp index c4b6f9e077..a4eec522b3 100644 --- a/test/tap/tests/reg_test_3427-stmt_first_comment1-t.cpp +++ b/test/tap/tests/reg_test_3427-stmt_first_comment1-t.cpp @@ -29,8 +29,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "proxysql_utils.h" #include "tap.h" diff --git a/test/tap/tests/reg_test_3434-text_stmt_mix-t.cpp b/test/tap/tests/reg_test_3434-text_stmt_mix-t.cpp index b232ab5c27..6fc4bdac3d 100644 --- a/test/tap/tests/reg_test_3434-text_stmt_mix-t.cpp +++ b/test/tap/tests/reg_test_3434-text_stmt_mix-t.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include "mysql.h" #include "proxysql_utils.h" #include "tap.h" diff --git a/test/tap/tests/reg_test_3493-USE_with_comment-t.cpp b/test/tap/tests/reg_test_3493-USE_with_comment-t.cpp index 16600783e2..66f509296c 100644 --- a/test/tap/tests/reg_test_3493-USE_with_comment-t.cpp +++ b/test/tap/tests/reg_test_3493-USE_with_comment-t.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_3504-change_user-t.cpp b/test/tap/tests/reg_test_3504-change_user-t.cpp index 8251c958ac..29b0bcb1fb 100644 --- a/test/tap/tests/reg_test_3504-change_user-t.cpp +++ b/test/tap/tests/reg_test_3504-change_user-t.cpp @@ -21,8 +21,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "command_line.h" #include "proxysql_utils.h" diff --git a/test/tap/tests/reg_test_3504-change_user_helper.cpp b/test/tap/tests/reg_test_3504-change_user_helper.cpp index 1efb7a0c3f..ee6ba80dbb 100644 --- a/test/tap/tests/reg_test_3504-change_user_helper.cpp +++ b/test/tap/tests/reg_test_3504-change_user_helper.cpp @@ -31,8 +31,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "proxysql_utils.h" #include "json.hpp" diff --git a/test/tap/tests/reg_test_3546-stmt_empty_params-t.cpp b/test/tap/tests/reg_test_3546-stmt_empty_params-t.cpp index 82c0e3323f..86a148cd4f 100644 --- a/test/tap/tests/reg_test_3546-stmt_empty_params-t.cpp +++ b/test/tap/tests/reg_test_3546-stmt_empty_params-t.cpp @@ -20,8 +20,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "proxysql_utils.h" #include "tap.h" diff --git a/test/tap/tests/reg_test_3549-autocommit_tracking-t.cpp b/test/tap/tests/reg_test_3549-autocommit_tracking-t.cpp index 65656a4b7f..42980c22b5 100644 --- a/test/tap/tests/reg_test_3549-autocommit_tracking-t.cpp +++ b/test/tap/tests/reg_test_3549-autocommit_tracking-t.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include "mysql.h" #include "proxysql_utils.h" #include "tap.h" diff --git a/test/tap/tests/reg_test_3585-stmt_metadata-t.cpp b/test/tap/tests/reg_test_3585-stmt_metadata-t.cpp index c6a19cb872..f40e959a39 100644 --- a/test/tap/tests/reg_test_3585-stmt_metadata-t.cpp +++ b/test/tap/tests/reg_test_3585-stmt_metadata-t.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_3591-restapi_num_fds-t.cpp b/test/tap/tests/reg_test_3591-restapi_num_fds-t.cpp index f859517183..13bb29142e 100644 --- a/test/tap/tests/reg_test_3591-restapi_num_fds-t.cpp +++ b/test/tap/tests/reg_test_3591-restapi_num_fds-t.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp b/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp index 1f4eb5b693..8afc9a8614 100644 --- a/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp +++ b/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp @@ -29,7 +29,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_3606-mysql_warnings-t.cpp b/test/tap/tests/reg_test_3606-mysql_warnings-t.cpp index 412bc68b61..b5a1fc3166 100644 --- a/test/tap/tests/reg_test_3606-mysql_warnings-t.cpp +++ b/test/tap/tests/reg_test_3606-mysql_warnings-t.cpp @@ -18,15 +18,14 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" -#include +#include "proxysql_utils.h" #include "tap.h" #include "command_line.h" #include "utils.h" -#include "utils.h" using std::vector; using std::tuple; diff --git a/test/tap/tests/reg_test_3625-sqlite3_session_client_error_limit-t.cpp b/test/tap/tests/reg_test_3625-sqlite3_session_client_error_limit-t.cpp index 72536eabc3..70d6106819 100644 --- a/test/tap/tests/reg_test_3625-sqlite3_session_client_error_limit-t.cpp +++ b/test/tap/tests/reg_test_3625-sqlite3_session_client_error_limit-t.cpp @@ -15,8 +15,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_3690-admin_large_pkts-t.cpp b/test/tap/tests/reg_test_3690-admin_large_pkts-t.cpp index 486c46bd54..83687245fc 100644 --- a/test/tap/tests/reg_test_3690-admin_large_pkts-t.cpp +++ b/test/tap/tests/reg_test_3690-admin_large_pkts-t.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include "mysql.h" #include "json.hpp" #include "tap.h" diff --git a/test/tap/tests/reg_test_3765_ssl_pollout-t.cpp b/test/tap/tests/reg_test_3765_ssl_pollout-t.cpp index 7570c4642e..e559c758f8 100644 --- a/test/tap/tests/reg_test_3765_ssl_pollout-t.cpp +++ b/test/tap/tests/reg_test_3765_ssl_pollout-t.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include "mysql.h" #include #include "tap.h" diff --git a/test/tap/tests/reg_test_3838-restapi_eintr-t.cpp b/test/tap/tests/reg_test_3838-restapi_eintr-t.cpp index cd081cd0b1..b890a43d8c 100644 --- a/test/tap/tests/reg_test_3838-restapi_eintr-t.cpp +++ b/test/tap/tests/reg_test_3838-restapi_eintr-t.cpp @@ -22,8 +22,8 @@ #include "curl/curl.h" -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "json.hpp" #include "tap.h" diff --git a/test/tap/tests/reg_test_3847_admin_lock-t.cpp b/test/tap/tests/reg_test_3847_admin_lock-t.cpp index 8fad233ef9..e110a7874b 100644 --- a/test/tap/tests/reg_test_3847_admin_lock-t.cpp +++ b/test/tap/tests/reg_test_3847_admin_lock-t.cpp @@ -10,8 +10,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "proxysql_utils.h" #include "tap.h" diff --git a/test/tap/tests/reg_test_3992_fast_forward_malformed_packet-mysqlsh-t.cpp b/test/tap/tests/reg_test_3992_fast_forward_malformed_packet-mysqlsh-t.cpp index 51fd91de47..b19e38edc2 100644 --- a/test/tap/tests/reg_test_3992_fast_forward_malformed_packet-mysqlsh-t.cpp +++ b/test/tap/tests/reg_test_3992_fast_forward_malformed_packet-mysqlsh-t.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_3992_fast_forward_malformed_packet-t.cpp b/test/tap/tests/reg_test_3992_fast_forward_malformed_packet-t.cpp index 7335ed7b67..128fdec549 100644 --- a/test/tap/tests/reg_test_3992_fast_forward_malformed_packet-t.cpp +++ b/test/tap/tests/reg_test_3992_fast_forward_malformed_packet-t.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_4001-restapi_scripts_num_fds-t.cpp b/test/tap/tests/reg_test_4001-restapi_scripts_num_fds-t.cpp index e8c162c88e..b035d8c40d 100644 --- a/test/tap/tests/reg_test_4001-restapi_scripts_num_fds-t.cpp +++ b/test/tap/tests/reg_test_4001-restapi_scripts_num_fds-t.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include "mysql.h" #include "command_line.h" #include "proxysql_utils.h" diff --git a/test/tap/tests/reg_test_4072-show-warnings-t.cpp b/test/tap/tests/reg_test_4072-show-warnings-t.cpp index d0bcf6dbb4..3e22e70e19 100644 --- a/test/tap/tests/reg_test_4072-show-warnings-t.cpp +++ b/test/tap/tests/reg_test_4072-show-warnings-t.cpp @@ -7,8 +7,8 @@ #include #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" #include "utils.h" diff --git a/test/tap/tests/reg_test_4264-commit_rollback-t.cpp b/test/tap/tests/reg_test_4264-commit_rollback-t.cpp index 15369d8895..1c5c6585ab 100644 --- a/test/tap/tests/reg_test_4264-commit_rollback-t.cpp +++ b/test/tap/tests/reg_test_4264-commit_rollback-t.cpp @@ -41,8 +41,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "utils.h" diff --git a/test/tap/tests/reg_test_4300-dollar_quote_check-t.cpp b/test/tap/tests/reg_test_4300-dollar_quote_check-t.cpp index 72ecc5b70c..9e6828e621 100644 --- a/test/tap/tests/reg_test_4300-dollar_quote_check-t.cpp +++ b/test/tap/tests/reg_test_4300-dollar_quote_check-t.cpp @@ -15,8 +15,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "utils.h" diff --git a/test/tap/tests/reg_test_4399-stats_mysql_query_digest-t.cpp b/test/tap/tests/reg_test_4399-stats_mysql_query_digest-t.cpp new file mode 100644 index 0000000000..4dbba2cb85 --- /dev/null +++ b/test/tap/tests/reg_test_4399-stats_mysql_query_digest-t.cpp @@ -0,0 +1,93 @@ +/** + * @file reg_test_4399-stats_mysql_query_digest-t.cpp + * @brief This test verifies stability of ProxySQL by checking if it remains operational when + * stats_mysql_query_digest table is queried frequently while actively serving traffic. + */ + +#include +#include +#include +#include "mysql.h" +#include "mysqld_error.h" +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +CommandLine cl; + +const unsigned int QUERY_COUNT = 1000; + +int main(int argc, char** argv) { + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return -1; + } + + plan(2); + + // Initialize Admin connection + MYSQL* proxysql_admin = mysql_init(NULL); + if (!proxysql_admin) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); + return -1; + } + // Connnect to ProxySQL Admin + 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; + } + + MYSQL_QUERY(proxysql_admin, "SET mysql-query_digests='true'"); + MYSQL_QUERY(proxysql_admin, "SET mysql-query_digests_keep_comment='true'"); + MYSQL_QUERY(proxysql_admin, "SET mysql-query_digests_normalize_digest_text='true'"); + MYSQL_QUERY(proxysql_admin, "SET mysql-query_digests_max_digest_length=2048"); + MYSQL_QUERY(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME"); + + // clearing previously stored digests + MYSQL_QUERY(proxysql_admin, "SELECT COUNT(*) FROM stats_mysql_query_digest_reset"); + mysql_free_result(mysql_store_result(proxysql_admin)); + + // Initialize ProxySQL connection + MYSQL* proxysql = mysql_init(NULL); + if (!proxysql) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); + return -1; + } + + // Connect to ProxySQL + if (!mysql_real_connect(proxysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); + return exit_status(); + } + + auto handle = std::async(std::launch::async, [&]() -> int { + char query[128]{}; + diag("Generating simulated traffic..."); + for (unsigned int i=0; i < QUERY_COUNT; i++) { + sprintf(query, "DO /*#%d#*/ %d", i, i); + MYSQL_QUERY(proxysql, query); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + return EXIT_SUCCESS; + } + ); + + bool result = true; + diag("Querying stats_mysql_query_digest table..."); + for (unsigned int i=0; i < QUERY_COUNT; i++) { + if (mysql_query(proxysql_admin, "SELECT COUNT(*) FROM stats_mysql_query_digest")) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); + result = false; + break; + } + mysql_free_result(mysql_store_result(proxysql_admin)); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + ok(result == true, "All queries on stats_mysql_query_digest table were executed successfully"); + ok(handle.get() == EXIT_SUCCESS, "Successfully ran a set of dummy queries to simulate traffic"); + + mysql_close(proxysql); + mysql_close(proxysql_admin); + + return exit_status(); +} diff --git a/test/tap/tests/reg_test_4402-mysql_fields-t.cpp b/test/tap/tests/reg_test_4402-mysql_fields-t.cpp new file mode 100644 index 0000000000..7ab0a0bbdb --- /dev/null +++ b/test/tap/tests/reg_test_4402-mysql_fields-t.cpp @@ -0,0 +1,129 @@ +/** + * @file reg_test_4402-mysql-fields-t.cpp + * @brief This TAP test checks if the length of the column alias and table alias surpasses 250 characters, + * should not impact MySQL field name length (MySQL_FIELD::name_length) and the MySQL field database length (MySQL_FIELD::db_length) + */ + +#include +#include +#include +#include +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +#define MYSQL_QUERY__(mysql, query) \ + do { \ + if (mysql_query(mysql, query)) { \ + fprintf(stderr, "File %s, line %d, Error: %s\n", \ + __FILE__, __LINE__, mysql_error(mysql)); \ + goto cleanup; \ + } \ + } while(0) + +CommandLine cl; + +std::string generate_random_string(size_t length) { + std::srand(static_cast(std::time(nullptr))); + static const char characters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + static const int numCharacters = (sizeof(characters) - 1)/ sizeof(char); + + std::string randomString; + randomString.reserve(length); + + for (size_t i = 0; i < length; ++i) { + char randomChar = characters[std::rand() % numCharacters]; + randomString.push_back(randomChar); + } + + return randomString; +} + +int main(int argc, char** argv) { + + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return -1; + } + + plan(256*2); + + // Initialize ProxySQL connection + MYSQL* proxysql = mysql_init(NULL); + if (!proxysql) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); + return -1; + } + + // Connect to ProxySQL + if (!mysql_real_connect(proxysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); + return exit_status(); + } + + MYSQL_QUERY__(proxysql, "DROP DATABASE IF EXISTS testdb"); + MYSQL_QUERY__(proxysql, "CREATE DATABASE testdb"); + + diag("Creating echo_int function..."); + MYSQL_QUERY__(proxysql, "CREATE FUNCTION testdb.echo_int(N INT) RETURNS INT DETERMINISTIC RETURN N;"); + + diag("Creating dummy_table..."); + MYSQL_QUERY__(proxysql, "CREATE TABLE testdb.dummy_table(data VARCHAR(10))"); + + // wait for replication + std::this_thread::sleep_for(std::chrono::seconds(2)); + + // alias maximum length is 256. + // https://dev.mysql.com/doc/refman/8.2/en/identifier-length.html + // https://mariadb.com/kb/en/identifier-names/#maximum-length + for (unsigned int length = 1; length <= 256; length++) { + + // to check column alias issue: + { + const std::string& query = "SELECT testdb.echo_int(1) AS " + generate_random_string(length); + MYSQL_QUERY__(proxysql, query.c_str()); + + MYSQL_RES* res = mysql_use_result(proxysql); + if (!res) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); + return exit_status(); + } + + MYSQL_FIELD* field = mysql_fetch_fields(res); + + ok(field->name_length == length, "name_length: '%u'. Expected length: '%u'", field->name_length, length); + + if (res) { + mysql_free_result(res); + res = NULL; + } + } + + // to check table alias issue: + { + const std::string& query = "SELECT data FROM testdb.dummy_table AS " + generate_random_string(length); + MYSQL_QUERY__(proxysql, query.c_str()); + + MYSQL_RES* res = mysql_use_result(proxysql); + if (!res) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); + return exit_status(); + } + + MYSQL_FIELD* field = mysql_fetch_fields(res); + + ok(field->db_length == (sizeof("testdb")-1), "db_length: '%u'. Expected length: '%u'", + field->db_length, (unsigned int)(sizeof("testdb")-1)); + + if (res) { + mysql_free_result(res); + res = NULL; + } + } + } +cleanup: + mysql_query(proxysql, "DROP DATABASE IF EXISTS testdb"); + mysql_close(proxysql); + + return exit_status(); +} diff --git a/test/tap/tests/reg_test_fast_forward_split_packet-t.cpp b/test/tap/tests/reg_test_fast_forward_split_packet-t.cpp index a302543202..626545055b 100644 --- a/test/tap/tests/reg_test_fast_forward_split_packet-t.cpp +++ b/test/tap/tests/reg_test_fast_forward_split_packet-t.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_mariadb_stmt_store_result-t.cpp b/test/tap/tests/reg_test_mariadb_stmt_store_result-t.cpp index aa21489f1f..292f98f137 100644 --- a/test/tap/tests/reg_test_mariadb_stmt_store_result-t.cpp +++ b/test/tap/tests/reg_test_mariadb_stmt_store_result-t.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/reg_test_sql_calc_found_rows-t.cpp b/test/tap/tests/reg_test_sql_calc_found_rows-t.cpp index 10aef61c33..674a1f9b74 100644 --- a/test/tap/tests/reg_test_sql_calc_found_rows-t.cpp +++ b/test/tap/tests/reg_test_sql_calc_found_rows-t.cpp @@ -12,8 +12,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "json.hpp" diff --git a/test/tap/tests/reg_test_stmt_resultset_err_no_rows-t.cpp b/test/tap/tests/reg_test_stmt_resultset_err_no_rows-t.cpp index 514b5d627e..f02fc7fd95 100644 --- a/test/tap/tests/reg_test_stmt_resultset_err_no_rows-t.cpp +++ b/test/tap/tests/reg_test_stmt_resultset_err_no_rows-t.cpp @@ -16,8 +16,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "proxysql_utils.h" #include "tap.h" @@ -140,9 +140,9 @@ int main(int argc, char** argv) { bind.buffer_type = MYSQL_TYPE_STRING; bind.buffer = (char *)&data_c2; bind.buffer_length = STRING_SIZE; - bind.is_null = &is_null[0]; + bind.is_null = (my_bool *)&is_null[0]; bind.length = &length[0]; - bind.error = &error[0]; + bind.error = (my_bool *)&error[0]; if (mysql_stmt_bind_result(stmt, &bind)) { diag("mysql_stmt_bind_result at line %d failed: %s", __LINE__, mysql_stmt_error(stmt)); diff --git a/test/tap/tests/reg_test_stmt_resultset_err_no_rows_php-t.cpp b/test/tap/tests/reg_test_stmt_resultset_err_no_rows_php-t.cpp index 03a1f0143e..bd7a0cf19d 100644 --- a/test/tap/tests/reg_test_stmt_resultset_err_no_rows_php-t.cpp +++ b/test/tap/tests/reg_test_stmt_resultset_err_no_rows_php-t.cpp @@ -8,8 +8,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "proxysql_utils.h" #include "tap.h" diff --git a/test/tap/tests/repro_3404-mysql_close_fd_leak.cpp b/test/tap/tests/repro_3404-mysql_close_fd_leak.cpp index 83b1e28770..5e0ec91332 100644 --- a/test/tap/tests/repro_3404-mysql_close_fd_leak.cpp +++ b/test/tap/tests/repro_3404-mysql_close_fd_leak.cpp @@ -56,7 +56,7 @@ #include #include -#include +#include "mysql.h" #include #include "tap.h" diff --git a/test/tap/tests/repro_test_leak_3350.cpp b/test/tap/tests/repro_test_leak_3350.cpp index 6d51b66367..bc0644f6e2 100644 --- a/test/tap/tests/repro_test_leak_3350.cpp +++ b/test/tap/tests/repro_test_leak_3350.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/repro_test_leak_3525.cpp b/test/tap/tests/repro_test_leak_3525.cpp index 5ac66cbe4a..c4b50932cd 100644 --- a/test/tap/tests/repro_test_leak_3525.cpp +++ b/test/tap/tests/repro_test_leak_3525.cpp @@ -7,7 +7,7 @@ */ #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/savepoint-3749-t.cpp b/test/tap/tests/savepoint-3749-t.cpp index 9606863af3..e8202d900d 100644 --- a/test/tap/tests/savepoint-3749-t.cpp +++ b/test/tap/tests/savepoint-3749-t.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include "mysql.h" #include #include #include diff --git a/test/tap/tests/savepoint-948-t.cpp b/test/tap/tests/savepoint-948-t.cpp index 3ec1a2168c..a174ed2fc2 100644 --- a/test/tap/tests/savepoint-948-t.cpp +++ b/test/tap/tests/savepoint-948-t.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include "mysql.h" #include #include #include diff --git a/test/tap/tests/set_character_set-t.cpp b/test/tap/tests/set_character_set-t.cpp index b0fdce464b..f208e532f9 100644 --- a/test/tap/tests/set_character_set-t.cpp +++ b/test/tap/tests/set_character_set-t.cpp @@ -36,7 +36,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/set_testing-240-t.cpp b/test/tap/tests/set_testing-240-t.cpp index 5021676c57..cacf8c0052 100644 --- a/test/tap/tests/set_testing-240-t.cpp +++ b/test/tap/tests/set_testing-240-t.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include "mysql.h" #include #include #include @@ -456,6 +456,11 @@ void * my_conn_thread(void *arg) { } __sync_fetch_and_add(&query_phase_completed,1); + // close all connections + for (i=0; i rows_res { extract_mysql_rows(result) }; + mysql_free_result(result); + + for (const auto& act_row : rows_res) { + diag("Found hostgroup: %s '%s'", act_row[0].c_str(), act_row[1].c_str()); + } //queries = 3000; //queries = testCases.size(); + queries = queries / rows_res.size(); // keep test duration constant unsigned int p = queries * num_threads; - p *= 2; // number of algorithms + p *= 2; // number of algorithms + p *= rows_res.size(); // number of host groups plan(p); - if (strcmp(host,"localhost")==0) { - local = 1; - } - if (uniquequeries == 0) { - if (queries) uniquequeries=queries; - } - if (uniquequeries) { - uniquequeries=(int)sqrt(uniquequeries); - } + for (const auto& act_row : rows_res) { + diag("Using hostgroup: %s '%s'", act_row[0].c_str(), act_row[1].c_str()); - for (int algo = 1; algo <= 2; algo++ ) { - connect_phase_completed = 0; - query_phase_completed = 0; - std::string qu = "SET mysql-set_parser_algorithm=" + std::to_string(algo); - diag("Setting: %s", qu.c_str()); - MYSQL_QUERY(proxysql_admin, qu.c_str()); - MYSQL_QUERY(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME"); - pthread_t *thi=(pthread_t *)malloc(sizeof(pthread_t)*num_threads); - if (thi==NULL) + diag("Creating new hostgroup 101: DELETE FROM mysql_servers WHERE hostgroup_id = 101"); + MYSQL_QUERY(proxysql_admin, "DELETE FROM mysql_servers WHERE hostgroup_id = 101"); + + const std::string insert = "INSERT INTO mysql_servers (hostgroup_id, hostname, port, max_connections, max_replication_lag, comment) SELECT DISTINCT 101, hostname, port, 100, 0, comment FROM mysql_servers WHERE hostgroup_id = '" + act_row[0] + "'"; + diag("Creating new hostgroup 101: %s" , insert.c_str()); + MYSQL_QUERY(proxysql_admin, insert.c_str()); + MYSQL_QUERY(proxysql_admin, "LOAD MYSQL SERVERS TO RUNTIME"); + + const std::string update = "UPDATE mysql_query_rules SET destination_hostgroup=101 WHERE destination_hostgroup=" + act_row[0]; + diag("Changing read traffic to hostgroup 101: %s", update.c_str()); + MYSQL_QUERY(proxysql_admin, update.c_str()); + MYSQL_QUERY(proxysql_admin, "LOAD MYSQL QUERY RULES TO RUNTIME"); + + if (detect_version(cl, is_mariadb) != 0) { + diag("Cannot detect MySQL version"); return exit_status(); + } - for (unsigned int i=0; i::iterator it = vars_counters.begin(); it!=vars_counters.end(); it++) { - diag("Unknown variable %s:\t Count: %d , unknown: %d", it->first.c_str(), it->second.count, it->second.unknown); + if (uniquequeries) { + uniquequeries=(int)sqrt(uniquequeries); + } + + for (int algo = 1; algo <= 2; algo++ ) { + connect_phase_completed = 0; + query_phase_completed = 0; + std::string qu = "SET mysql-set_parser_algorithm=" + std::to_string(algo); + diag("Setting %s", qu.c_str()); + MYSQL_QUERY(proxysql_admin, qu.c_str()); + MYSQL_QUERY(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME"); + pthread_t *thi=(pthread_t *)malloc(sizeof(pthread_t)*num_threads); + if (thi==NULL) + return exit_status(); + + for (unsigned int i=0; i::iterator it = vars_counters.begin(); it!=vars_counters.end(); it++) { + diag("Unknown variable %s:\t Count: %d , unknown: %d", it->first.c_str(), it->second.count, it->second.unknown); + } + sleep(10); + } return exit_status(); } diff --git a/test/tap/tests/set_testing-multi-t.cpp b/test/tap/tests/set_testing-multi-t.cpp index 02d87f9f26..1f85d6a95b 100644 --- a/test/tap/tests/set_testing-multi-t.cpp +++ b/test/tap/tests/set_testing-multi-t.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include "mysql.h" #include #include #include diff --git a/test/tap/tests/set_testing-t.cpp b/test/tap/tests/set_testing-t.cpp index 18f336bd56..0604669075 100644 --- a/test/tap/tests/set_testing-t.cpp +++ b/test/tap/tests/set_testing-t.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include "mysql.h" #include #include #include diff --git a/test/tap/tests/sqlite3-t.cpp b/test/tap/tests/sqlite3-t.cpp index e41f0068e3..c9c1a421c7 100644 --- a/test/tap/tests/sqlite3-t.cpp +++ b/test/tap/tests/sqlite3-t.cpp @@ -5,7 +5,8 @@ #include #include #include -#include + +#include "openssl/ssl.h" #include "mysql.h" #include "proxysql_structs.h" diff --git a/test/tap/tests/sqlite_autocommit-t.cpp b/test/tap/tests/sqlite_autocommit-t.cpp index 0fb858d56e..e5a15e0726 100644 --- a/test/tap/tests/sqlite_autocommit-t.cpp +++ b/test/tap/tests/sqlite_autocommit-t.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_admin_prometheus_metrics_dump-t_disabled_gh3571.cpp b/test/tap/tests/test_admin_prometheus_metrics_dump-t_disabled_gh3571.cpp index 4b3ba54325..f979b772e0 100644 --- a/test/tap/tests/test_admin_prometheus_metrics_dump-t_disabled_gh3571.cpp +++ b/test/tap/tests/test_admin_prometheus_metrics_dump-t_disabled_gh3571.cpp @@ -10,8 +10,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_admin_stats-t.cpp b/test/tap/tests/test_admin_stats-t.cpp index 7128767250..29ac4d0e40 100644 --- a/test/tap/tests/test_admin_stats-t.cpp +++ b/test/tap/tests/test_admin_stats-t.cpp @@ -27,8 +27,8 @@ DELETE FROM history_mysql_status_variables; #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_auto_increment_delay_multiplex-t.cpp b/test/tap/tests/test_auto_increment_delay_multiplex-t.cpp index ad6f04a258..ee50da7211 100644 --- a/test/tap/tests/test_auto_increment_delay_multiplex-t.cpp +++ b/test/tap/tests/test_auto_increment_delay_multiplex-t.cpp @@ -28,8 +28,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "json.hpp" diff --git a/test/tap/tests/test_binlog_fast_forward-t.cpp b/test/tap/tests/test_binlog_fast_forward-t.cpp index a80247fb7a..6f9c98befb 100644 --- a/test/tap/tests/test_binlog_fast_forward-t.cpp +++ b/test/tap/tests/test_binlog_fast_forward-t.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" @@ -38,13 +38,17 @@ std::vector adminQ_set1 = { int pull_replication(MYSQL *mysql, int server_id) { MARIADB_RPL_EVENT *event= NULL; MARIADB_RPL *rpl= mariadb_rpl_init(mysql); + if (!rpl) { + fprintf(stderr, "File %s, line %d, Error: mariadb_rpl_init failed\n", __FILE__, __LINE__); + return exit_status(); + } rpl->server_id= server_id; rpl->start_position= 4; rpl->flags= MARIADB_RPL_BINLOG_SEND_ANNOTATE_ROWS; - - if (mariadb_rpl_open(rpl)) + if (mariadb_rpl_open(rpl)) { + fprintf(stderr, "File %s, line %d, Error: '%d':%s\n", __FILE__, __LINE__, mysql_errno(rpl->mysql), mysql_error(rpl->mysql)); return exit_status(); - + } int num_heartbeats = 0; int num_events = 0; @@ -149,6 +153,16 @@ int main(int argc, char** argv) { __FILE__, __LINE__, mysql_error(mysqladmin)); return exit_status(); } + + const std::vector query_rules = { "INSERT OR IGNORE INTO mysql_query_rules (rule_id,active,match_digest,destination_hostgroup,multiplex,apply) VALUES\ + (-1,1,'^(SELECT @rpl_semi_sync_slave=\\?.*|SET @rpl_semi_sync_slave=\\?.*)$',0,0,1)", + "LOAD MYSQL QUERY RULES TO RUNTIME" }; + + for (const auto& query : query_rules) { + diag("Running on Admin: %s", query.c_str()); + MYSQL_QUERY(mysqladmin, query.c_str()); + } + // we now test various combination setup_replication(11, false, false, repl_queries_set1); setup_replication(12, true, false, repl_queries_set1); diff --git a/test/tap/tests/test_binlog_reader-t.cpp b/test/tap/tests/test_binlog_reader-t.cpp index 432d1244ef..82eaf7f619 100644 --- a/test/tap/tests/test_binlog_reader-t.cpp +++ b/test/tap/tests/test_binlog_reader-t.cpp @@ -28,8 +28,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include #include "tap.h" diff --git a/test/tap/tests/test_binlog_reader_uses_previous_hostgroup-t.cpp b/test/tap/tests/test_binlog_reader_uses_previous_hostgroup-t.cpp index 45e214f296..2015e47163 100644 --- a/test/tap/tests/test_binlog_reader_uses_previous_hostgroup-t.cpp +++ b/test/tap/tests/test_binlog_reader_uses_previous_hostgroup-t.cpp @@ -10,7 +10,7 @@ */ #include -#include +#include "mysql.h" #include #include diff --git a/test/tap/tests/test_clickhouse_server-t.cpp b/test/tap/tests/test_clickhouse_server-t.cpp index 1fe6a08665..d943442e97 100644 --- a/test/tap/tests/test_clickhouse_server-t.cpp +++ b/test/tap/tests/test_clickhouse_server-t.cpp @@ -28,8 +28,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_client_limit_error-t.cpp b/test/tap/tests/test_client_limit_error-t.cpp index 8ae4618d2e..6d84f245a7 100644 --- a/test/tap/tests/test_client_limit_error-t.cpp +++ b/test/tap/tests/test_client_limit_error-t.cpp @@ -48,8 +48,8 @@ #include #include -#include -#include +#include "proxysql_utils.h" +#include "mysql.h" #include "json.hpp" #include "tap.h" diff --git a/test/tap/tests/test_cluster1-t.cpp b/test/tap/tests/test_cluster1-t.cpp index b3a4526cef..089c4bd82a 100644 --- a/test/tap/tests/test_cluster1-t.cpp +++ b/test/tap/tests/test_cluster1-t.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_cluster_sync-t.cpp b/test/tap/tests/test_cluster_sync-t.cpp index fc5b15d0f0..8c57c7be2c 100644 --- a/test/tap/tests/test_cluster_sync-t.cpp +++ b/test/tap/tests/test_cluster_sync-t.cpp @@ -77,9 +77,9 @@ #include -#include +#include "proxysql_utils.h" -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" @@ -534,7 +534,7 @@ int update_proxysql_servers(const conn_opts_t& conn_opts, MYSQL* admin) { const vector module_sync_payloads { { update_mysql_servers, - "mysql_servers", + "mysql_servers_v2", "admin-cluster_mysql_servers_diffs_before_sync", "admin-checksum_mysql_servers", }, @@ -2449,6 +2449,7 @@ int main(int, char**) { std::make_tuple("admin-cluster_mysql_variables_save_to_disk" , "true" ), std::make_tuple("admin-cluster_proxysql_servers_diffs_before_sync" , "4" ), std::make_tuple("admin-cluster_proxysql_servers_save_to_disk" , "true" ), + std::make_tuple("admin-cluster_mysql_servers_sync_algorithm" , "1" ), // std::make_tuple("admin-cluster_username" , "" ), Known issue, can't clear // std::make_tuple("admin-cluster_password" , "" ), Known issue, can't clear // std::make_tuple("admin-debug" , "false" ), Should not be synced diff --git a/test/tap/tests/test_cluster_sync_config/test_cluster_sync-t.cnf b/test/tap/tests/test_cluster_sync_config/test_cluster_sync-t.cnf index 8a51f47dc9..51b163f34e 100644 --- a/test/tap/tests/test_cluster_sync_config/test_cluster_sync-t.cnf +++ b/test/tap/tests/test_cluster_sync_config/test_cluster_sync-t.cnf @@ -17,6 +17,7 @@ admin_variables= cluster_mysql_users_diffs_before_sync=3 cluster_admin_variables_diffs_before_sync=3 cluster_proxysql_servers_diffs_before_sync=3 + cluster_sync_interfaces=false } mysql_variables= diff --git a/test/tap/tests/test_cluster_sync_config/test_cluster_sync_nomonitor/.gitignore b/test/tap/tests/test_cluster_sync_config/test_cluster_sync_nomonitor/.gitignore new file mode 100644 index 0000000000..25cc06f021 --- /dev/null +++ b/test/tap/tests/test_cluster_sync_config/test_cluster_sync_nomonitor/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory + +# Except this file +!.gitignore \ No newline at end of file diff --git a/test/tap/tests/test_cluster_sync_config/test_cluster_sync_withmonitor/.gitignore b/test/tap/tests/test_cluster_sync_config/test_cluster_sync_withmonitor/.gitignore new file mode 100644 index 0000000000..25cc06f021 --- /dev/null +++ b/test/tap/tests/test_cluster_sync_config/test_cluster_sync_withmonitor/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory + +# Except this file +!.gitignore \ No newline at end of file diff --git a/test/tap/tests/test_cluster_sync_mysql_servers-t.cpp b/test/tap/tests/test_cluster_sync_mysql_servers-t.cpp new file mode 100644 index 0000000000..a1338a4a8d --- /dev/null +++ b/test/tap/tests/test_cluster_sync_mysql_servers-t.cpp @@ -0,0 +1,1100 @@ +/** + * @file test_cluster_sync_mysql_servers-t.cpp + * @brief Checks that ProxySQL mysql_server and mysql_server_v2 is properly syncing. + * @details Checks the sync of the following tables: + * - 'mysql_servers_v2' + * - 'mysql_servers' + * + * + * Test Cluster Isolation: + * ---------------------- + * For guaranteeing that this test doesn't invalidate the configuration of a running ProxySQL cluster and + * that after the test, the previous valid configuration is restored, the following actions are performed: + * + * 1. The Core nodes from the current cluster configuration are backup. + * 2. Primary (currently tested instance) is removed from the Core nodes. + * 3. A sync wait until all core nodes have performed the removal of primary is executed. + * 4. Now Primary is isolated from the previous cluster, tests can proceed. Primary is setup to hold itself + * in its 'proxysql_servers' as well as the target spawned replica. + * 5. After the tests recover the primary configuration and add it back to the Core nodes from Cluster: + * - Recover the previous 'mysql_servers' from disk, and load them to runtime, discarding any previous + * config performed during the test. + * - Insert the primary back into a Core node from cluster and wait for all nodes to sync including it. + * - Insert into the primary the previous backup Core nodes from Cluster and load to runtime. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#ifndef SPOOKYV2 +#include "SpookyV2.h" +#define SPOOKYV2 +#endif +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +using std::string; + +#define MYSQL_QUERY__(mysql, query) \ + do { \ + if (mysql_query(mysql, query)) { \ + fprintf(stderr, "File %s, line %d, Error: %s\n", \ + __FILE__, __LINE__, mysql_error(mysql)); \ + goto cleanup; \ + } \ + } while(0) + +// GLOBAL TEST PARAMETERS +const uint32_t SYNC_TIMEOUT = 10; +const uint32_t CONNECT_TIMEOUT = 10; +const uint32_t R_NOMONITOR_PORT = 96061; +const uint32_t R_WITHMONITOR_PORT = 96062; + +const std::string t_debug_query = "mysql -u%s -p%s -h %s -P%d -C -e \"%s\""; + +using mysql_server_tuple = std::tuple; +using replication_hostgroups_tuple = std::tuple; + +/** + * @brief Computes the checksum for the resultset, excluding records labeled as 'OFFLINE_HARD', instead of checking each row individually. + * + * @param resultset mysql_servers + * + */ +uint64_t mysql_servers_raw_checksum(MYSQL_RES* resultset) { + if (resultset == nullptr) { return 0; } + + uint64_t num_rows = mysql_num_rows(resultset); + if (num_rows == 0) { return 0; } + + MYSQL_FIELD* fields = mysql_fetch_fields(resultset); + uint32_t num_fields = mysql_num_fields(resultset); + uint32_t status_idx = 0; + + for (uint32_t i = 0; i < num_fields; i++) { + if (strcmp(fields[i].name, "status") == 0) { + status_idx = i; + } + } + + SpookyHash myhash {}; + myhash.Init(19,3); + + while (MYSQL_ROW row = mysql_fetch_row(resultset)) { + for (uint32_t i = 0; i < num_fields; i++) { + if (strcmp(row[status_idx], "OFFLINE_HARD") == 0) { + continue; + } + + if (row[i]) { + if (strcmp(fields[i].name, "status") == 0) { + if (strcmp(row[i], "ONLINE") == 0 || strcmp(row[i], "SHUNNED") == 0) { + myhash.Update("0", strlen("0")); + } else { + myhash.Update("2", strlen("1")); + } + } else { + // computing 'strlen' is required see @details + myhash.Update(row[i], strlen(row[i])); + } + } else { + myhash.Update("", 0); + } + } + } + + // restore the initial resulset index + mysql_data_seek(resultset, 0); + + uint64_t res_hash = 0, hash2 = 0; + myhash.Final(&res_hash, &hash2); + + return res_hash; +} + + +/** + * @brief Helper function to verify that the sync of a table (or variable) have been performed. + * + * @param r_proxy_admin An already opened connection to ProxySQL. + * @param queries Queries to be executed that should return a **non-zero** value after the sync has taken place. + * @param sync_timeout Timeout for the sync to happen. + * + * @return EXIT_SUCCESS in case of success, otherwise: + * - '-1' if a query against Admin fails to be performed (failure is logged). + * - '-2' if timeout expired without sync being completed. + */ +int sync_checker(MYSQL* r_proxy_admin, const std::vector& queries, uint32_t sync_timeout) { + bool not_synced_query = false; + uint waited = 0; + + while (waited < sync_timeout) { + not_synced_query = false; + + // Check that all the entries have been synced + for (const auto& query : queries) { + int q_res = mysql_query(r_proxy_admin, query.c_str()); + if (q_res != EXIT_SUCCESS) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(r_proxy_admin)); + return -1; + } + + MYSQL_RES* proxysql_servers_res = mysql_store_result(r_proxy_admin); + MYSQL_ROW row = mysql_fetch_row(proxysql_servers_res); + int row_value = atoi(row[0]); + mysql_free_result(proxysql_servers_res); + + if (row_value == 0) { + not_synced_query = true; + break; + } + } + + if (not_synced_query) { + waited += 1; + sleep(1); + } else { + break; + } + } + + if (not_synced_query) { + return -2; + } else { + return EXIT_SUCCESS; + } +} + +int check_nodes_sync( + const CommandLine& cl, const std::vector& core_nodes, const std::string& check_query, uint32_t sync_timeout +) { + for (const auto& node : core_nodes) { + const std::string host { node[0] }; + const int port = std::stol(node[1]); + + MYSQL* c_node_admin = mysql_init(NULL); + if (!mysql_real_connect(c_node_admin, host.c_str(), cl.admin_username, cl.admin_password, NULL, port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(c_node_admin)); + return EXIT_FAILURE; + } + + int not_synced = sync_checker(c_node_admin, { check_query }, sync_timeout); + if (not_synced != EXIT_SUCCESS) { + const std::string err_msg { "Node '" + host + ":" + std::to_string(port) + "' sync timed out" }; + fprintf(stderr, "File %s, line %d, Error: `%s`\n", __FILE__, __LINE__, err_msg.c_str()); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +int insert_mysql_servers_records(MYSQL* proxy_admin, const std::vector& insert_mysql_servers_values, + const std::vector& insert_replication_hostgroups_values) { + + MYSQL_QUERY(proxy_admin, "DELETE FROM mysql_servers"); + MYSQL_QUERY(proxy_admin, "DELETE FROM mysql_replication_hostgroups"); + + // Configure 'mysql_servers' and check sync with NULL comments + const char* t_insert_mysql_servers = + "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 (%d, '%s', %d, %d, '%s', %d, %d, %d, %d, %d, %d, '%s')"; + + const char* t_mysql_replication_hostgroups = + "INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type) VALUES (%d,%d,'%s')"; + + for (auto const& values : insert_mysql_servers_values) { + std::string insert_mysql_servers_hostgroup_query; + string_format( + t_insert_mysql_servers, + insert_mysql_servers_hostgroup_query, + std::get<0>(values), + std::get<1>(values).c_str(), + std::get<2>(values), + std::get<3>(values), + std::get<4>(values).c_str(), + std::get<5>(values), + std::get<6>(values), + std::get<7>(values), + std::get<8>(values), + std::get<9>(values), + std::get<10>(values), + std::get<11>(values).c_str() + ); + + // Insert the new mysql_servers hostgroups values + MYSQL_QUERY(proxy_admin, insert_mysql_servers_hostgroup_query.c_str()); + } + + for (auto const& values : insert_replication_hostgroups_values) { + std::string insert_mysql_replication_hostgroups_query; + string_format( + t_mysql_replication_hostgroups, + insert_mysql_replication_hostgroups_query, + std::get<0>(values), + std::get<1>(values), + std::get<2>(values).c_str() + ); + + // Insert the new mysql_replication_hostgroups values + MYSQL_QUERY(proxy_admin, insert_mysql_replication_hostgroups_query.c_str()); + } + + MYSQL_QUERY(proxy_admin, "LOAD MYSQL SERVERS TO RUNTIME"); + + return EXIT_SUCCESS; +} + +int wait_for_node_sync(MYSQL* r_proxy_admin, uint64_t master_checksum, const std::string& table) { + uint waited = 0; + bool not_synced = false; + const std::string& query = "SELECT * FROM " + table; + + while (waited < SYNC_TIMEOUT) { + not_synced = false; + + MYSQL_QUERY(r_proxy_admin, query.c_str()); + MYSQL_RES* mysql_servers_res = mysql_store_result(r_proxy_admin); + auto replica_checksum = mysql_servers_raw_checksum(mysql_servers_res); + mysql_free_result(mysql_servers_res); + + if (replica_checksum != master_checksum) { + not_synced = true; + diag("Waiting for '%s' to be synced", table.c_str()); + } + + if (not_synced) { + waited += 1; + sleep(1); + } else { + break; + } + } + + if (not_synced) { + diag("'wait_for_node_sync' timeout for query '%s'", query.c_str()); + } + + return not_synced; +}; + +int check_mysql_servers_sync( + const CommandLine& cl, MYSQL* proxy_admin, MYSQL* r_proxy_withmonitor_admin, MYSQL* r_proxy_nomonitor_admin +) { + std::string print_master_mysql_servers_hostgroups; + string_format(t_debug_query, print_master_mysql_servers_hostgroups, cl.admin_username, cl.admin_password, cl.host, cl.admin_port, "SELECT * FROM mysql_servers"); + std::string print_master_runtime_mysql_servers_hostgroups; + string_format(t_debug_query, print_master_runtime_mysql_servers_hostgroups, cl.admin_username, cl.admin_password, cl.host, cl.admin_port, "SELECT * FROM runtime_mysql_servers"); + + std::string print_nomonitor_replica_mysql_servers_hostgroups; + string_format(t_debug_query, print_nomonitor_replica_mysql_servers_hostgroups, "radmin", "radmin", cl.host, R_NOMONITOR_PORT, "SELECT * FROM mysql_servers"); + std::string print_nomonitor_replica_runtime_mysql_servers_hostgroups; + string_format(t_debug_query, print_nomonitor_replica_runtime_mysql_servers_hostgroups, "radmin", "radmin", cl.host, R_NOMONITOR_PORT, "SELECT * FROM runtime_mysql_servers"); + std::string print_nomonitor_replica_disk_mysql_servers_hostgroups; + string_format(t_debug_query, print_nomonitor_replica_disk_mysql_servers_hostgroups, "radmin", "radmin", cl.host, R_NOMONITOR_PORT, "SELECT * FROM disk.mysql_servers"); + + std::string print_withmonitor_replica_mysql_servers_hostgroups; + string_format(t_debug_query, print_withmonitor_replica_mysql_servers_hostgroups, "radmin", "radmin", cl.host, R_WITHMONITOR_PORT, "SELECT * FROM mysql_servers"); + std::string print_withmonitor_replica_runtime_mysql_servers_hostgroups; + string_format(t_debug_query, print_withmonitor_replica_runtime_mysql_servers_hostgroups, "radmin", "radmin", cl.host, R_WITHMONITOR_PORT, "SELECT * FROM runtime_mysql_servers"); + std::string print_withmonitor_replica_disk_mysql_servers_hostgroups; + string_format(t_debug_query, print_withmonitor_replica_disk_mysql_servers_hostgroups, "radmin", "radmin", cl.host, R_WITHMONITOR_PORT, "SELECT * FROM disk.mysql_servers"); + + + std::string variable_val; + + // get mysql_servers sync algorithm value + int g_err = get_variable_value(proxy_admin, "admin-cluster_mysql_servers_sync_algorithm", variable_val); + if (g_err) { return EXIT_FAILURE; } + const int cluster_sync_mysql_servers_algorithm = atoi(variable_val.c_str()); + + // get monitor enabled variable value + g_err = get_variable_value(proxy_admin, "mysql-monitor_enabled", variable_val); + if (g_err) { return EXIT_FAILURE; } + + bool monitor_enabled = false; + if (strcasecmp(variable_val.c_str(), "true") == 0 || strcasecmp(variable_val.c_str(), "1") == 0) { + monitor_enabled = true; + } + + // get save-to-disk variable value + g_err = get_variable_value(proxy_admin, "admin-cluster_mysql_servers_save_to_disk", variable_val); + if (g_err) { return EXIT_FAILURE; } + bool save_to_disk_value = false; + if (strcasecmp(variable_val.c_str(), "true") == 0 || strcasecmp(variable_val.c_str(), "1") == 0) { + save_to_disk_value = true; + } + + // get read_only interval variable value + g_err = get_variable_value(proxy_admin, "mysql-monitor_read_only_interval", variable_val); + if (g_err) { return EXIT_FAILURE; } + const long monitor_read_only_interval = std::stol(variable_val); + + // get read_only timeout variable value + g_err = get_variable_value(proxy_admin, "mysql-monitor_read_only_timeout", variable_val); + if (g_err) { return EXIT_FAILURE; } + const long monitor_read_only_timeout = std::stol(variable_val); + + diag("Checking mysql_servers_sync status " + "[admin-cluster_mysql_servers_sync_algorithm:'%d', " + "mysql-monitor_enabled:'%s', " + "admin-cluster_mysql_servers_save_to_disk:'%s'" + "]...", cluster_sync_mysql_servers_algorithm, (monitor_enabled ? "true" : "false"), (save_to_disk_value ? "true" : "false")); + + std::cout << "MASTER 'MYSQL SERVERS' TABLE BEFORE SYNC:" << std::endl; + system(print_master_mysql_servers_hostgroups.c_str()); + std::cout << std::endl; + + // Wait till read_only actions have been performed + uint64_t wait = monitor_read_only_interval + monitor_read_only_timeout; + usleep((wait * 1000) * 2); + + std::cout << "MASTER 'RUNTIME MYSQL SERVERS' TABLE BEFORE SYNC:" << std::endl; + system(print_master_runtime_mysql_servers_hostgroups.c_str()); + std::cout << std::endl; + + uint64_t master_mysql_servers_checksum = 0; + uint64_t master_runtime_mysql_servers_checksum = 0; + + // fetch master mysql_servers resultset and compute it's hash + { + MYSQL_QUERY(proxy_admin, "SELECT * FROM mysql_servers"); + MYSQL_RES* mysql_servers_res = mysql_store_result(proxy_admin); + master_mysql_servers_checksum = mysql_servers_raw_checksum(mysql_servers_res); + mysql_free_result(mysql_servers_res); + } + + // fetch master runtime_mysql_servers resultset and compute it's hash + { + MYSQL_QUERY(proxy_admin, "SELECT * FROM runtime_mysql_servers"); + MYSQL_RES* mysql_servers_res = mysql_store_result(proxy_admin); + master_runtime_mysql_servers_checksum = mysql_servers_raw_checksum(mysql_servers_res); + mysql_free_result(mysql_servers_res); + } + + // This comment is exclusively for this TAP test + // If monitor is enabled, records of runtime_mysql_servers and mysql_servers should not match + if (monitor_enabled == true) { + ok(master_mysql_servers_checksum != master_runtime_mysql_servers_checksum, "'runtime_mysql_servers' and 'mysql_servers' should not match."); + } else { + ok(master_mysql_servers_checksum == master_runtime_mysql_servers_checksum, "'runtime_mysql_servers' and 'mysql_servers' should match."); + } + // + + // SYNCH CHECK + bool not_synced_query = false; + + if (save_to_disk_value == false) { + + if (cluster_sync_mysql_servers_algorithm == 1) { + // Algo: 1 [Sync mysql_servers_v2 and runtime_mysql_servers] + + // Replica [WITHMONITOR] mysql_servers for both the replica and master will be identical. + not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_mysql_servers_checksum, "mysql_servers"); + std::cout << "REPLICA [WITHMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_withmonitor_replica_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'mysql_servers' with NULL comments should be synced."); + std::cout << std::endl; + + // Replica [NOMONITOR] mysql_servers for both the replica and master will be identical. + not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_mysql_servers_checksum, "mysql_servers"); + std::cout << "REPLICA [NOMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_nomonitor_replica_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'mysql_servers' with NULL comments should be synced."); + std::cout << std::endl; + + // Replica [WITHMONITOR] runtime_mysql_servers for both the replica and master will be identical. + not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_runtime_mysql_servers_checksum, "runtime_mysql_servers"); + std::cout << "REPLICA [WITHMONITOR] 'RUNTIME MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_withmonitor_replica_runtime_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'runtime_mysql_servers' with NULL comments should be synced."); + std::cout << std::endl; + + // Replica [NOMONITOR] runtime_mysql_servers for both the replica and master will be identical. + not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_runtime_mysql_servers_checksum, "runtime_mysql_servers"); + std::cout << "REPLICA [NOMONITOR] 'RUNTIME MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_nomonitor_replica_runtime_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'runtime_mysql_servers' with NULL comments should be synced."); + std::cout << std::endl; + } else if (cluster_sync_mysql_servers_algorithm == 2) { + // Algo: 2 [Sync mysql_servers_v2 only] + + // Replica [WITHMONITOR] mysql_servers for both the replica and master will be identical. + not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_mysql_servers_checksum, "mysql_servers"); + std::cout << "REPLICA [WITHMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_withmonitor_replica_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'mysql_servers' with NULL comments should be synced."); + std::cout << std::endl; + + // Replica [NOMONITOR] mysql_servers for both the replica and master will be identical. + not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_mysql_servers_checksum, "mysql_servers"); + std::cout << "REPLICA [NOMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_nomonitor_replica_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'mysql_servers' with NULL comments should be synced."); + std::cout << std::endl; + + // Replica [WITHMONITOR] runtime_mysql_servers for both the replica and master will be identical. + // Reason: Replica [WITHMONITOR] has monitoring checks enabled and will generate identical runtime_mysql_servers records. + not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_runtime_mysql_servers_checksum, "runtime_mysql_servers"); + std::cout << "REPLICA [WITHMONITOR] 'RUNTIME MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_withmonitor_replica_runtime_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'runtime_mysql_servers' with NULL comments should be synced."); + std::cout << std::endl; + + // Replica [NOMONITOR] runtime_mysql_servers will be identical to master mysql_servers. + // Reason: Replica [NOMONITOR] has monitoring checks disabled, so runtime_mysql_servers will be identical to mysql_servers records. + not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_mysql_servers_checksum, "runtime_mysql_servers"); + std::cout << "REPLICA [NOMONITOR] 'RUNTIME MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_nomonitor_replica_runtime_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'runtime_mysql_servers' with NULL comments should be synced."); + std::cout << std::endl; + } else if (cluster_sync_mysql_servers_algorithm == 3) { + // Algo: 3 [If the command line includes the argument "-M", Algorithm 1 will be selected. + // If "-M" is not provided, then Algorithm 2 will be chosen.] + + // Replica [WITHMONITOR] mysql_servers for both the replica and master will be identical. + not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_mysql_servers_checksum, "mysql_servers"); + std::cout << "REPLICA [WITHMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_withmonitor_replica_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'mysql_servers' with NULL comments should be synced."); + std::cout << std::endl; + + // Replica [NOMONITOR] mysql_servers for both the replica and master will be identical. + not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_mysql_servers_checksum, "mysql_servers"); + std::cout << "REPLICA [NOMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_nomonitor_replica_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'mysql_servers' with NULL comments should be synced."); + std::cout << std::endl; + + // Replica [WITHMONITOR] runtime_mysql_servers for both the replica and master will be identical. + // Reason: Algorithm 2 will be selected [Sync mysql_servers_v2]. After read_only_action, + // the runtime_mysql_servers for replica becomes identical to master. + not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_runtime_mysql_servers_checksum, "runtime_mysql_servers"); + std::cout << "REPLICA [WITHMONITOR] 'RUNTIME MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_withmonitor_replica_runtime_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'runtime_mysql_servers' with NULL comments should be synced."); + std::cout << std::endl; + + // Replica [NOMONITOR] runtime_mysql_servers for both the replica and master will be identical. + // Reason: Algorithm 1 will be selected [Sync mysql_servers_v2 and runtime_mysql_servers] + not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_runtime_mysql_servers_checksum, "runtime_mysql_servers"); + std::cout << "REPLICA [NOMONITOR] 'RUNTIME MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_nomonitor_replica_runtime_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'runtime_mysql_servers' with NULL comments should be synced."); + std::cout << std::endl; + } + } else { //save_to_disk_value is true + + // If Algorithm 1 is selected, the runtime_mysql_servers data will be saved to disk. + // If Algorithm 2 is selected, the mysql_servers data will be saved to disk. + // However, for Algorithm 3, the data saved to disk will depend on the algorithm that is chosen based on the -M argument. + + if (cluster_sync_mysql_servers_algorithm == 1) { + + // Replica [WITHMONITOR] disk.mysql_servers and master runtime_mysql_servers will be identical. + not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_runtime_mysql_servers_checksum, "disk.mysql_servers"); + std::cout << "REPLICA [WITHMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_withmonitor_replica_disk_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'disk.mysql_servers' should be identical to 'runtime_mysql_servers'."); + std::cout << std::endl; + + // Replica [NOMONITOR] disk.mysql_servers and master runtime_mysql_servers will be identical. + not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_runtime_mysql_servers_checksum, "disk.mysql_servers"); + std::cout << "REPLICA [NOMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_nomonitor_replica_disk_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'disk.mysql_servers' should be identical to 'runtime_mysql_servers'."); + std::cout << std::endl; + + } else if (cluster_sync_mysql_servers_algorithm == 2) { + + // Replica [WITHMONITOR] disk.mysql_servers and master mysql_servers will be identical. + not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_mysql_servers_checksum, "disk.mysql_servers"); + std::cout << "REPLICA [WITHMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_withmonitor_replica_disk_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'disk.mysql_servers' with NULL comments should be synced."); + std::cout << std::endl; + + // Replica [NOMONITOR] disk.mysql_servers and master mysql_servers will be identical. + not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_mysql_servers_checksum, "disk.mysql_servers"); + std::cout << "REPLICA [NOMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_nomonitor_replica_disk_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'disk.mysql_servers' with NULL comments should be synced."); + std::cout << std::endl; + } else if (cluster_sync_mysql_servers_algorithm == 3) { + + // Replica [WITHMONITOR] disk.mysql_servers and master mysql_servers will be identical. + not_synced_query = wait_for_node_sync(r_proxy_withmonitor_admin, master_mysql_servers_checksum, "disk.mysql_servers"); + std::cout << "REPLICA [WITHMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_withmonitor_replica_disk_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'disk.disk.mysql_servers' with NULL comments should be synced."); + std::cout << std::endl; + + // Replica [NOMONITOR] disk.mysql_servers and master runtime_mysql_servers will be identical. + not_synced_query = wait_for_node_sync(r_proxy_nomonitor_admin, master_runtime_mysql_servers_checksum, "disk.mysql_servers"); + std::cout << "REPLICA [NOMONITOR] 'MYSQL SERVERS' TABLE AFTER SYNC:" << std::endl; + system(print_nomonitor_replica_disk_mysql_servers_hostgroups.c_str()); + ok(not_synced_query == false, "'disk.mysql_servers' with NULL comments should be synced."); + } + } + + diag("Checking mysql_servers_sync status " + "[admin-cluster_mysql_servers_sync_algorithm:'%d', " + "mysql-monitor_enabled:'%s', " + "admin-cluster_mysql_servers_save_to_disk:'%s'" + "]... Done", cluster_sync_mysql_servers_algorithm, (monitor_enabled ? "true" : "false"), (save_to_disk_value ? "true" : "false")); + + return EXIT_SUCCESS; +} + +/** + * @brief Assumes that 'proxysql_servers' holds at least the one entry required for this test. + * @details It's assumed that primary ProxySQL is part of a Cluster. + */ +int update_proxysql_servers(const CommandLine& cl, MYSQL* admin) { + const char update_proxysql_servers_t[] { + "UPDATE proxysql_servers SET comment='%s' WHERE hostname='%s' and port=%d" + }; + + cfmt_t update_servers { + cstr_format(update_proxysql_servers_t, std::to_string(time(NULL)).c_str(), cl.host, cl.admin_port) + }; + MYSQL_QUERY_T(admin, update_servers.str.c_str()); + MYSQL_QUERY_T(admin, "LOAD PROXYSQL SERVERS TO RUNTIME"); + + return EXIT_SUCCESS; +} + +int setup_config_file(const CommandLine& cl, uint32_t r_port, const std::string& config_filename) { + const std::string& workdir = std::string(cl.workdir); + const std::string& t_fmt_config_file = workdir + "test_cluster_sync_config/test_cluster_sync-t.cnf"; + const std::string& fmt_config_file = workdir + "test_cluster_sync_config/test_cluster_sync_" + config_filename + "/test_cluster_sync.cnf"; + const std::string& datadir_path = workdir + "test_cluster_sync_config/test_cluster_sync_" + config_filename; + + // Prepare the configuration file + config_t cfg {}; + + config_init(&cfg); + + if (!config_read_file(&cfg, t_fmt_config_file.c_str())) { + fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg)); + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - Error reading config file."); + config_destroy(&cfg); + return -1; + } + + config_setting_t* r_datadir = config_lookup(&cfg, "datadir"); + if (r_datadir == nullptr) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - 'datadir' setting not found."); + return -1; + } + + config_setting_t* r_admin_vars = config_lookup(&cfg, "admin_variables"); + if (r_admin_vars == nullptr) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - 'admin_variables' setting not found."); + return -1; + } + + config_setting_t* r_mysql_ifaces = config_setting_get_member(r_admin_vars, "mysql_ifaces"); + if (r_mysql_ifaces == nullptr) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - 'mysql_ifaces' setting not found."); + return -1; + } + + int r_ifaces_res = config_setting_set_string(r_mysql_ifaces, std::string { "0.0.0.0:" + std::to_string(r_port) }.c_str()); + if (r_ifaces_res == CONFIG_FALSE) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - Error while trying to set the values for 'mysql_ifaces'."); + return -1; + } + + config_setting_t* p_servers = config_lookup(&cfg, "proxysql_servers"); + if (p_servers == nullptr) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - 'proxysql_servers' setting not found."); + return -1; + } + + int r_datadir_res = config_setting_set_string(r_datadir, datadir_path.c_str()); + if (r_datadir_res == CONFIG_FALSE) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - Error while trying to set the 'datadir' value."); + return -1; + } + + // Get first group settings + config_setting_t* r_pserver_group = config_setting_get_elem(p_servers, 0); + if (r_pserver_group == nullptr) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - 'proxysql_servers' doesn't contains first group."); + return -1; + } + config_setting_t* r_pserver_hostname = config_setting_get_member(r_pserver_group, "hostname"); + config_setting_t* r_pserver_port = config_setting_get_member(r_pserver_group, "port"); + + // Check the group members + if (r_pserver_hostname == nullptr || r_pserver_port == nullptr) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - 'proxysql_servers' doesn't contains the necessary group members."); + return -1; + } + + int fhost_res = config_setting_set_string(r_pserver_hostname, cl.host); + int fport_res = config_setting_set_int(r_pserver_port, cl.admin_port); + + if (fhost_res == CONFIG_FALSE || fport_res == CONFIG_FALSE) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid config file - Error while trying to set the values from env variables."); + return -1; + } + + // Write the new config file + if (config_write_file(&cfg, fmt_config_file.c_str()) == CONFIG_FALSE) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Config file - Error while trying to write the new config file."); + return -1; + } + + config_destroy(&cfg); + + return 0; +} + +int launch_proxysql_replica(const CommandLine& cl, uint32_t r_port, const std::string config_filename, bool monitor_enabled, + const std::atomic& save_proxy_stderr) { + + const std::string& workdir = std::string(cl.workdir); + const std::string& replica_stderr = workdir + "test_cluster_sync_config/test_cluster_sync_" + config_filename + "/cluster_sync_node_stderr.txt"; + const std::string& proxysql_db = workdir + "test_cluster_sync_config/test_cluster_sync_" + config_filename + "/proxysql.db"; + const std::string& stats_db = workdir + "test_cluster_sync_config/test_cluster_sync_" + config_filename + "/proxysql_stats.db"; + const std::string& fmt_config_file = workdir + "test_cluster_sync_config/test_cluster_sync_" + config_filename + "/test_cluster_sync.cnf"; + + // Setup the config file using the env variables in 'CommandLine' + if (setup_config_file(cl, r_port, config_filename)) { + return EXIT_FAILURE; + } + + const std::string& proxy_binary_path = workdir + "../../../src/proxysql"; + const std::string& proxy_command = proxy_binary_path + " -f " + (monitor_enabled == false ? "-M" : "") + " -c " + fmt_config_file + " > " + replica_stderr + " 2>&1"; + + diag("Launching replica ProxySQL [%s] via 'system' with command : `%s`", config_filename.c_str(), proxy_command.c_str()); + int exec_res = system(proxy_command.c_str()); + + ok(exec_res == 0, "proxysql cluster node [%s] should execute and shutdown nicely. 'system' result was: %d", config_filename.c_str(), exec_res); + + // In case of error place in log the reason + if (exec_res || save_proxy_stderr.load()) { + if (exec_res) { + diag("LOG: Proxysql cluster node [%s] execution failed, logging stderr into 'test_cluster_sync_node_stderr_%s.txt", config_filename.c_str(), config_filename.c_str()); + } else { + diag("LOG: One of the tests failed to pass, logging stderr 'test_cluster_sync_node_stderr_%s.txt", config_filename.c_str()); + } + } + + remove(proxysql_db.c_str()); + remove(stats_db.c_str()); + + return EXIT_SUCCESS; +} + +int get_read_only_value(const std::string& host, uint16_t port, const std::string& username, const std::string& password, + int* read_only_val) { + + // check is mysql server has read_only value 0 + MYSQL* mysqldb = mysql_init(NULL); + + // Initialize connections + if (!mysqldb) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysqldb)); + return EXIT_FAILURE; + } + + // Connnect to local proxysql + if (!mysql_real_connect(mysqldb, host.c_str(), username.c_str(), password.c_str(), NULL, port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysqldb)); + mysql_close(mysqldb); + return EXIT_FAILURE; + } + + const int rc_query = mysql_query(mysqldb,"SELECT @@global.read_only read_only"); + + if (rc_query == 0) { + MYSQL_RES *result = mysql_store_result(mysqldb); + MYSQL_ROW row; + + while ((row = mysql_fetch_row(result))) { + + if (row[0]) { + *read_only_val = static_cast(std::strtoul(row[0], NULL, 10)); + } + } + + mysql_free_result(result); + } + + mysql_close(mysqldb); + + return EXIT_SUCCESS; +} + +std::vector> queries = { + { + "SET mysql-monitor_read_only_interval=200", // setting read_only variables + "SET mysql-monitor_read_only_timeout=100", + "SET mysql-monitor_enabled='true'", // enabling monitor + "LOAD MYSQL VARIABLES TO RUNTIME", + "UPDATE global_variables SET variable_value='1' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'",// setting admin-cluster_mysql_servers_sync_algorithm to 1 -> fetch mysql_servers_v2 and runtime_mysql_servers + "LOAD ADMIN VARIABLES TO RUNTIME" + }, + { + "UPDATE global_variables SET variable_value='2' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'", + "LOAD ADMIN VARIABLES TO RUNTIME", + "INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type) VALUES (999,998,'read_only')", // adding dummy data so replica nodes can sync after algorithm change from 1 to 2. + "LOAD MYSQL SERVERS TO RUNTIME" + }, + { + "UPDATE global_variables SET variable_value='3' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'", + "LOAD ADMIN VARIABLES TO RUNTIME", + "DELETE FROM mysql_replication_hostgroups WHERE writer_hostgroup=999 AND reader_hostgroup=998 AND check_type='read_only'", // deleting dummy data so replica nodes can sync after algorithm change from 2 to 3. + "LOAD MYSQL SERVERS TO RUNTIME" + }, + { + "SET mysql-monitor_enabled='false'", + "LOAD MYSQL VARIABLES TO RUNTIME", + "LOAD MYSQL SERVERS TO RUNTIME", // to regenerate runtime_mysql_servers + "UPDATE global_variables SET variable_value='1' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'", + "LOAD ADMIN VARIABLES TO RUNTIME" + }, + { + "UPDATE global_variables SET variable_value='2' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'", + "LOAD ADMIN VARIABLES TO RUNTIME", + "INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type) VALUES (999,998,'read_only')",// adding dummy data so replica nodes can sync after algorithm change from 1 to 2. + "LOAD MYSQL SERVERS TO RUNTIME" + }, + { + "UPDATE global_variables SET variable_value='3' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'", + "LOAD ADMIN VARIABLES TO RUNTIME", + "DELETE FROM mysql_replication_hostgroups WHERE writer_hostgroup=999 AND reader_hostgroup=998 AND check_type='read_only'", // deleting dummy data so replica nodes can sync after algorithm change from 2 to 3. + "LOAD MYSQL SERVERS TO RUNTIME" + }, + { + // save to disk + "SET mysql-monitor_enabled='true'", + "LOAD MYSQL VARIABLES TO RUNTIME", + "UPDATE global_variables SET variable_value='true' WHERE variable_name='admin-cluster_mysql_servers_save_to_disk'", // setting admin-cluster_mysql_servers_save_to_disk to true + "UPDATE global_variables SET variable_value='1' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'", + "LOAD ADMIN VARIABLES TO RUNTIME", + "INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type) VALUES (997,996,'read_only')", // adding dummy data so replica nodes can sync after algorithm change from 1 to 2. + "LOAD MYSQL SERVERS TO RUNTIME" + }, + { + "UPDATE global_variables SET variable_value='2' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'", + "LOAD ADMIN VARIABLES TO RUNTIME", + "DELETE FROM mysql_replication_hostgroups WHERE writer_hostgroup=997 AND reader_hostgroup=996 AND check_type='read_only'", // deleting dummy data so replica nodes can sync after algorithm change from 2 to 3. + "LOAD MYSQL SERVERS TO RUNTIME" + }, + { + "UPDATE global_variables SET variable_value='3' WHERE variable_name='admin-cluster_mysql_servers_sync_algorithm'", + "LOAD ADMIN VARIABLES TO RUNTIME", + "INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type) VALUES (997,996,'read_only')",// adding dummy data so replica nodes can sync after algorithm change from 1 to 2. + "LOAD MYSQL SERVERS TO RUNTIME" + } +}; + +int main(int, char**) { + + CommandLine cl; + std::atomic save_proxy_stderr(false); + + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return EXIT_FAILURE; + } + + plan( 1 + 1 // replica instances + + 1 // confirming mysql server 127.0.0.1:13306 is a writer + + (6 * 5) // calling check_mysql_servers_sync 7 times, 5 differnt checks in each call + + (3 * 3) + + 1 + 1 // shutting down replica instances + ); + + MYSQL* proxy_admin = mysql_init(NULL); + + // Initialize connections + if (!proxy_admin) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy_admin)); + return EXIT_FAILURE; + } + + // Connnect to local proxysql + if (!mysql_real_connect(proxy_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(proxy_admin)); + return EXIT_FAILURE; + } + + const std::string t_update_proxysql_servers { + "INSERT INTO proxysql_servers (hostname, port, weight, comment) VALUES ('%s', %d, 0, 'proxysql')" + }; + + std::string update_proxysql_servers; + string_format(t_update_proxysql_servers, update_proxysql_servers, cl.host, cl.admin_port); + + // 1. Backup the Core nodes from current cluster configuration + MYSQL_QUERY(proxy_admin, "DROP TABLE IF EXISTS proxysql_servers_sync_test_backup_2687"); + MYSQL_QUERY(proxy_admin, "CREATE TABLE proxysql_servers_sync_test_backup_2687 AS SELECT * FROM proxysql_servers"); + + // 2. Remove primary from Core nodes + MYSQL_QUERY(proxy_admin, "DELETE FROM proxysql_servers WHERE hostname=='127.0.0.1' AND PORT==6032"); + MYSQL_QUERY(proxy_admin, "LOAD PROXYSQL SERVERS TO RUNTIME"); + MYSQL_QUERY(proxy_admin, "SELECT hostname,port FROM proxysql_servers"); + MYSQL_RES* my_res = mysql_store_result(proxy_admin); + std::vector core_nodes { extract_mysql_rows(my_res) }; + mysql_free_result(my_res); + + // 2.1 If core nodes are not reachable, assume no cluster is running; make test gracefully exit + if (core_nodes.size()) { + const string host { core_nodes[0][0] }; + const int port = std::stol(core_nodes[0][1]); + MYSQL* c_node_admin = mysql_init(NULL); + + if (!mysql_real_connect(c_node_admin, host.c_str(), cl.admin_username, cl.admin_password, NULL, port, NULL, 0)) { + int myerrno = mysql_errno(c_node_admin); + + if (myerrno == 2002) { + diag("Unable to connect to cluster Core nodes; required environment not met, gracefully exiting..."); + plan(0); + return exit_status(); + } else { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy_admin)); + return EXIT_FAILURE; + } + } + + mysql_close(c_node_admin); + } + + // 3. Wait for all Core nodes to sync (confirm primary out of core nodes) + std::string check_no_primary_query {}; + string_format( + "SELECT CASE COUNT(*) WHEN 0 THEN 1 ELSE 0 END FROM proxysql_servers WHERE hostname=='%s' AND port==%d", + check_no_primary_query, cl.host, cl.admin_port + ); + + int check_res = check_nodes_sync(cl, core_nodes, check_no_primary_query, SYNC_TIMEOUT); + if (check_res != EXIT_SUCCESS) { return EXIT_FAILURE; } + + // 4. Remove all current servers from primary instance (only secondary sync matters) + MYSQL_QUERY(proxy_admin, "DELETE FROM proxysql_servers"); + MYSQL_QUERY(proxy_admin, update_proxysql_servers.c_str()); + MYSQL_QUERY(proxy_admin, "LOAD PROXYSQL SERVERS TO RUNTIME"); + + // disable admin-cluster_mysql_servers_save_to_disk before executing replicas + MYSQL_QUERY(proxy_admin, "UPDATE global_variables SET variable_value='false' WHERE variable_name='admin-cluster_mysql_servers_save_to_disk'"); // setting admin-cluster_mysql_servers_save_to_disk to false + MYSQL_QUERY(proxy_admin, "LOAD ADMIN VARIABLES TO RUNTIME"); + + // cleaning old records + MYSQL_QUERY(proxy_admin, "DELETE FROM mysql_servers"); + MYSQL_QUERY(proxy_admin, "DELETE FROM mysql_replication_hostgroups"); + MYSQL_QUERY(proxy_admin, "LOAD MYSQL SERVERS TO RUNTIME"); + + // Launch proxysql with cluster config and monitor feature disabled + std::thread proxysql_replica_nomonitor_thd(launch_proxysql_replica, std::ref(cl), R_NOMONITOR_PORT, "nomonitor", false, std::ref(save_proxy_stderr)); + + // Launch proxysql with cluster config - with -M commandline + std::thread proxysql_replica_withmonitor_thd(launch_proxysql_replica, std::ref(cl), R_WITHMONITOR_PORT, "withmonitor", true, std::ref(save_proxy_stderr)); + + MYSQL* r_proxysql_nomonitor_admin = NULL; + MYSQL* r_proxysql_withmonitor_admin = NULL; + { + // Waiting for proxysql to be ready + conn_opts_t conn_opts_nomonitor {}; + conn_opts_nomonitor.host = cl.host; + conn_opts_nomonitor.user = "radmin"; + conn_opts_nomonitor.pass = "radmin"; + conn_opts_nomonitor.port = R_NOMONITOR_PORT; + + // connect to proxsqyl replica [nomonitor] + r_proxysql_nomonitor_admin = wait_for_proxysql(conn_opts_nomonitor, CONNECT_TIMEOUT); + + // Once the thread is spanwed we should always go to cleanup to wait + ok(r_proxysql_nomonitor_admin != nullptr, "New instance of proxysql [nomonitor] with cluster config should be properly spawned."); + + if (r_proxysql_nomonitor_admin == nullptr) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(r_proxysql_nomonitor_admin)); + goto cleanup; + } + + conn_opts_t conn_opts_withmonitor {}; + conn_opts_withmonitor.host = cl.host; + conn_opts_withmonitor.user = "radmin"; + conn_opts_withmonitor.pass = "radmin"; + conn_opts_withmonitor.port = R_WITHMONITOR_PORT; + + // connect to proxsqyl replica [nomonitor] + r_proxysql_withmonitor_admin = wait_for_proxysql(conn_opts_withmonitor, CONNECT_TIMEOUT); + + // Once the thread is spanwed we should always go to cleanup to wait + ok(r_proxysql_withmonitor_admin != nullptr, "New instance of proxysql [withmonitor] with cluster config should be properly spawned."); + + if (r_proxysql_withmonitor_admin == nullptr) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(r_proxysql_withmonitor_admin)); + goto cleanup; + } + + int read_only_val = -1; + int result = get_read_only_value("127.0.0.1", 13306, "root", "root", &read_only_val); + if (result != EXIT_SUCCESS) { + fprintf(stderr, "File %s, line %d, Error: `%s`\n", __FILE__, __LINE__, "Fetching read_only value from mysql server failed."); + goto cleanup; + } + + // For thorough testing of synchronization under all possible scenarios, it is necessary for + // the MySQL server at 127.0.0.1:13306 to function as a writer. + ok(read_only_val == 0, "MySQL Server '127.0.0.1:13306' should function as a writer"); + + const std::vector insert_mysql_servers_values { + std::make_tuple(1, "127.0.0.1", 13306, 12, "ONLINE", 1, 1, 1000, 300, 1, 200, ""), // this server has read_only value 0 (writer) + std::make_tuple(2, "127.0.0.1", 13307, 13, "OFFLINE_SOFT", 2, 1, 500, 300, 1, 200, ""), + std::make_tuple(3, "127.0.0.1", 13308, 14, "OFFLINE_HARD", 2, 1, 500, 300, 1, 200, ""), + std::make_tuple(4, "127.0.0.1", 13309, 15, "SHUNNED", 1, 0, 500, 300, 1, 200, "") + }; + + const std::vector insert_replication_hostgroups_values { + std::make_tuple(0, 1, "read_only") // Here we are assigning the hostgroup to the reader, and read-only actions will creating a new entry in hostgroup 0. + }; + + // Inserting new records into 'mysql_servers' and 'mysql_replication_hostgroups'. + result = insert_mysql_servers_records(proxy_admin, insert_mysql_servers_values, insert_replication_hostgroups_values); + + if (result != EXIT_SUCCESS) { + fprintf(stderr, "File %s, line %d, Error: `%s`\n", __FILE__, __LINE__, "Failed to insert records in mysql_servers table."); + goto cleanup; + } + + for (const auto& pre_queries : queries) { + + for (const std::string& query : pre_queries) { + MYSQL_QUERY__(proxy_admin, query.c_str()); + usleep(1000000); + } + sleep(2); + + result = check_mysql_servers_sync(cl, proxy_admin, r_proxysql_withmonitor_admin, r_proxysql_nomonitor_admin); + if (result != EXIT_SUCCESS) { + fprintf(stderr, "File %s, line %d, Error: `%s`\n", __FILE__, __LINE__, "Checking mysql servers sync records failed."); + goto cleanup; + } + } + } + +cleanup: + // In case of test failing, save the stderr output from the spawned proxysql instance + if (tests_failed() != 0) { + save_proxy_stderr.store(true); + } + + if (r_proxysql_nomonitor_admin) { + int mysql_timeout = 2; + + mysql_options(r_proxysql_nomonitor_admin, MYSQL_OPT_CONNECT_TIMEOUT, &mysql_timeout); + mysql_options(r_proxysql_nomonitor_admin, MYSQL_OPT_READ_TIMEOUT, &mysql_timeout); + mysql_options(r_proxysql_nomonitor_admin, MYSQL_OPT_WRITE_TIMEOUT, &mysql_timeout); + mysql_query(r_proxysql_nomonitor_admin, "PROXYSQL SHUTDOWN"); + mysql_close(r_proxysql_nomonitor_admin); + } + + if (r_proxysql_withmonitor_admin) { + int mysql_timeout = 2; + + mysql_options(r_proxysql_withmonitor_admin, MYSQL_OPT_CONNECT_TIMEOUT, &mysql_timeout); + mysql_options(r_proxysql_withmonitor_admin, MYSQL_OPT_READ_TIMEOUT, &mysql_timeout); + mysql_options(r_proxysql_withmonitor_admin, MYSQL_OPT_WRITE_TIMEOUT, &mysql_timeout); + mysql_query(r_proxysql_withmonitor_admin, "PROXYSQL SHUTDOWN"); + mysql_close(r_proxysql_withmonitor_admin); + } + + proxysql_replica_nomonitor_thd.join(); + proxysql_replica_withmonitor_thd.join(); + + // Recover primary ProxySQL MySQL and ProxySQL servers + diag("RESTORING: Recovering primary configuration..."); + + { + // Recover previous MySQL servers and generate a newer checksum for primary + MYSQL_QUERY(proxy_admin, "LOAD MYSQL SERVERS FROM DISK"); + MYSQL_QUERY(proxy_admin, "LOAD MYSQL SERVERS TO RUNTIME"); + + // Insert primary into another Core node config and wait for replication + diag("RESTORING: Inserting primary back into Core nodes"); + bool recovered_servers_st = false; + + std::string insert_query {}; + string_format( + "INSERT INTO proxysql_servers (hostname,port,weight,comment) VALUES ('%s',%d,0,'proxysql')", + insert_query, cl.host, cl.admin_port + ); + + for (const auto& row : core_nodes) { + const std::string host { row[0] }; + const int port = std::stol(row[1]); + MYSQL* c_node_admin = mysql_init(NULL); + + diag("RESTORING: Inserting into node '%s:%d'", host.c_str(), port); + + if (!mysql_real_connect(c_node_admin, host.c_str(), cl.admin_username, cl.admin_password, NULL, port, NULL, 0)) { + const std::string err_msg { + "Connection to core node failed with '" + std::string { mysql_error(c_node_admin) } + "'. Retrying..." + }; + fprintf(stderr, "File %s, line %d, Error: `%s`\n", __FILE__, __LINE__, err_msg.c_str()); + mysql_close(c_node_admin); + continue; + } + + int my_rc = mysql_query(c_node_admin, insert_query.c_str()); + if (my_rc == EXIT_SUCCESS) { + mysql_query(c_node_admin, "LOAD PROXYSQL SERVERS TO RUNTIME"); + break; + } else { + const std::string err_msg { + "Insert primary into node failed with: '" + std::string { mysql_error(c_node_admin) } + "'" + }; + fprintf(stderr, "File %s, line %d, Error: `%s`\n", __FILE__, __LINE__, err_msg.c_str()); + } + } + + // Wait for sync after primary insertion into Core node + std::string check_for_primary {}; + string_format( + "SELECT COUNT(*) FROM proxysql_servers WHERE hostname=='%s' AND port==%d", check_no_primary_query, + cl.host, cl.admin_port + ); + + // Wait for the other nodes to sync ProxySQL servers to include Primary + int check_res = check_nodes_sync(cl, core_nodes, check_no_primary_query, SYNC_TIMEOUT); + if (check_res != EXIT_SUCCESS) { return EXIT_FAILURE; } + + // Recover the old ProxySQL servers from backup in primary + MYSQL_QUERY(proxy_admin, "DELETE FROM proxysql_servers"); + MYSQL_QUERY(proxy_admin, "INSERT INTO proxysql_servers SELECT * FROM proxysql_servers_sync_test_backup_2687"); + MYSQL_QUERY(proxy_admin, "DROP TABLE proxysql_servers_sync_test_backup_2687"); + MYSQL_QUERY(proxy_admin, "LOAD PROXYSQL SERVERS TO RUNTIME"); + } + + mysql_close(proxy_admin); + + return exit_status(); +} diff --git a/test/tap/tests/test_com_reset_connection_com_change_user-t.cpp b/test/tap/tests/test_com_reset_connection_com_change_user-t.cpp index e02981b2c3..ed81bd5abb 100644 --- a/test/tap/tests/test_com_reset_connection_com_change_user-t.cpp +++ b/test/tap/tests/test_com_reset_connection_com_change_user-t.cpp @@ -19,8 +19,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "command_line.h" #include "proxysql_utils.h" diff --git a/test/tap/tests/test_connection_annotation-t.cpp b/test/tap/tests/test_connection_annotation-t.cpp index 93b09f28fe..915e20c122 100644 --- a/test/tap/tests/test_connection_annotation-t.cpp +++ b/test/tap/tests/test_connection_annotation-t.cpp @@ -10,8 +10,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_csharp_connector_support-t.cpp b/test/tap/tests/test_csharp_connector_support-t.cpp index 6c4856f079..a6a1168fe7 100644 --- a/test/tap/tests/test_csharp_connector_support-t.cpp +++ b/test/tap/tests/test_csharp_connector_support-t.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_debug_filters-t.cpp b/test/tap/tests/test_debug_filters-t.cpp index c3b17bc2df..559f73b653 100644 --- a/test/tap/tests/test_debug_filters-t.cpp +++ b/test/tap/tests/test_debug_filters-t.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_default_conn_collation-t.cpp b/test/tap/tests/test_default_conn_collation-t.cpp index fa8a29ef8d..a130ac1307 100644 --- a/test/tap/tests/test_default_conn_collation-t.cpp +++ b/test/tap/tests/test_default_conn_collation-t.cpp @@ -10,7 +10,7 @@ #include #include "mysql.h" -#include "mysql/mysqld_error.h" +#include "mysqld_error.h" #include "tap.h" #include "utils.h" diff --git a/test/tap/tests/test_default_value_transaction_isolation-t.cpp b/test/tap/tests/test_default_value_transaction_isolation-t.cpp index cca5d20806..972b9a3ac0 100644 --- a/test/tap/tests/test_default_value_transaction_isolation-t.cpp +++ b/test/tap/tests/test_default_value_transaction_isolation-t.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_default_value_transaction_isolation_attr-t.cpp b/test/tap/tests/test_default_value_transaction_isolation_attr-t.cpp index e709c24635..c90541e200 100644 --- a/test/tap/tests/test_default_value_transaction_isolation_attr-t.cpp +++ b/test/tap/tests/test_default_value_transaction_isolation_attr-t.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_digest_umap_aux-t.cpp b/test/tap/tests/test_digest_umap_aux-t.cpp index f68be97350..227f5af08d 100644 --- a/test/tap/tests/test_digest_umap_aux-t.cpp +++ b/test/tap/tests/test_digest_umap_aux-t.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include "mysql.h" #include #include #include diff --git a/test/tap/tests/test_dns_cache-t.cpp b/test/tap/tests/test_dns_cache-t.cpp index c1b5f14c75..652efa2425 100644 --- a/test/tap/tests/test_dns_cache-t.cpp +++ b/test/tap/tests/test_dns_cache-t.cpp @@ -14,8 +14,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_enforce_autocommit_on_reads-t.cpp b/test/tap/tests/test_enforce_autocommit_on_reads-t.cpp index 1dbc62af7c..0d7550c4a9 100644 --- a/test/tap/tests/test_enforce_autocommit_on_reads-t.cpp +++ b/test/tap/tests/test_enforce_autocommit_on_reads-t.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_filtered_set_statements-t.cpp b/test/tap/tests/test_filtered_set_statements-t.cpp index 726050ac62..5e27cb55fc 100644 --- a/test/tap/tests/test_filtered_set_statements-t.cpp +++ b/test/tap/tests/test_filtered_set_statements-t.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include "mysql.h" #include "proxysql_utils.h" #include "tap.h" diff --git a/test/tap/tests/test_firewall-t.cpp b/test/tap/tests/test_firewall-t.cpp index 39ccf4bd6d..a9b8cc6bab 100644 --- a/test/tap/tests/test_firewall-t.cpp +++ b/test/tap/tests/test_firewall-t.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_flagOUT_weight-t.cpp b/test/tap/tests/test_flagOUT_weight-t.cpp index df6aa718c5..f07da9f2cc 100644 --- a/test/tap/tests/test_flagOUT_weight-t.cpp +++ b/test/tap/tests/test_flagOUT_weight-t.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_gtid_forwarding-t.cpp b/test/tap/tests/test_gtid_forwarding-t.cpp index 4be02a5c72..dddc65206a 100644 --- a/test/tap/tests/test_gtid_forwarding-t.cpp +++ b/test/tap/tests/test_gtid_forwarding-t.cpp @@ -14,8 +14,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_keep_multiplexing_variables-t.cpp b/test/tap/tests/test_keep_multiplexing_variables-t.cpp index ef4995bc91..56e283c93b 100644 --- a/test/tap/tests/test_keep_multiplexing_variables-t.cpp +++ b/test/tap/tests/test_keep_multiplexing_variables-t.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_log_last_insert_id-t.cpp b/test/tap/tests/test_log_last_insert_id-t.cpp index 8fa074c153..2443a85d1d 100644 --- a/test/tap/tests/test_log_last_insert_id-t.cpp +++ b/test/tap/tests/test_log_last_insert_id-t.cpp @@ -7,8 +7,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_mysql_connect_retries_delay-t.cpp b/test/tap/tests/test_mysql_connect_retries_delay-t.cpp index e08596bb9a..014c2eff6b 100644 --- a/test/tap/tests/test_mysql_connect_retries_delay-t.cpp +++ b/test/tap/tests/test_mysql_connect_retries_delay-t.cpp @@ -18,8 +18,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_mysql_hostgroup_attributes-1-t.cpp b/test/tap/tests/test_mysql_hostgroup_attributes-1-t.cpp index 31adbb1f4d..8825126eb8 100644 --- a/test/tap/tests/test_mysql_hostgroup_attributes-1-t.cpp +++ b/test/tap/tests/test_mysql_hostgroup_attributes-1-t.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" 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 index 765366165c..cecfd8dfcc 100644 --- a/test/tap/tests/test_mysql_query_rules_fast_routing-t.cpp +++ b/test/tap/tests/test_mysql_query_rules_fast_routing-t.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_mysqlsh-t.cpp b/test/tap/tests/test_mysqlsh-t.cpp index bb1324398f..a809b172b5 100644 --- a/test/tap/tests/test_mysqlsh-t.cpp +++ b/test/tap/tests/test_mysqlsh-t.cpp @@ -8,8 +8,8 @@ #include #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_prometheus_metrics-t.cpp b/test/tap/tests/test_prometheus_metrics-t.cpp index 1b52e23934..a9bc27df6a 100644 --- a/test/tap/tests/test_prometheus_metrics-t.cpp +++ b/test/tap/tests/test_prometheus_metrics-t.cpp @@ -16,8 +16,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "json.hpp" #include "tap.h" diff --git a/test/tap/tests/test_ps_async-t.cpp b/test/tap/tests/test_ps_async-t.cpp index 5674b532c1..707d880eaf 100644 --- a/test/tap/tests/test_ps_async-t.cpp +++ b/test/tap/tests/test_ps_async-t.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_ps_hg_routing-t.cpp b/test/tap/tests/test_ps_hg_routing-t.cpp index ad4da4000c..02a514f111 100644 --- a/test/tap/tests/test_ps_hg_routing-t.cpp +++ b/test/tap/tests/test_ps_hg_routing-t.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_ps_large_result-t.cpp b/test/tap/tests/test_ps_large_result-t.cpp index 5f2b47c70d..52e6caaba9 100644 --- a/test/tap/tests/test_ps_large_result-t.cpp +++ b/test/tap/tests/test_ps_large_result-t.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_ps_no_store-t.cpp b/test/tap/tests/test_ps_no_store-t.cpp index f2aad57199..a64b507bf9 100644 --- a/test/tap/tests/test_ps_no_store-t.cpp +++ b/test/tap/tests/test_ps_no_store-t.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_query_cache_soft_ttl_pct-t.cpp b/test/tap/tests/test_query_cache_soft_ttl_pct-t.cpp index 4594477c4f..68bce7954f 100644 --- a/test/tap/tests/test_query_cache_soft_ttl_pct-t.cpp +++ b/test/tap/tests/test_query_cache_soft_ttl_pct-t.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include "mysql.h" #include #include #include diff --git a/test/tap/tests/test_query_rules_fast_routing_algorithm-t.cpp b/test/tap/tests/test_query_rules_fast_routing_algorithm-t.cpp index b0171f9c37..81b8c39ee4 100644 --- a/test/tap/tests/test_query_rules_fast_routing_algorithm-t.cpp +++ b/test/tap/tests/test_query_rules_fast_routing_algorithm-t.cpp @@ -13,8 +13,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "utils.h" diff --git a/test/tap/tests/test_query_rules_routing-t.cpp b/test/tap/tests/test_query_rules_routing-t.cpp index 165e08543e..792ef90ae9 100644 --- a/test/tap/tests/test_query_rules_routing-t.cpp +++ b/test/tap/tests/test_query_rules_routing-t.cpp @@ -19,8 +19,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "command_line.h" #include "proxysql_utils.h" diff --git a/test/tap/tests/test_query_timeout-t.cpp b/test/tap/tests/test_query_timeout-t.cpp index 32ae524c4c..9f4af5f6b5 100644 --- a/test/tap/tests/test_query_timeout-t.cpp +++ b/test/tap/tests/test_query_timeout-t.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_read_only_actions_offline_hard_servers-t.cpp b/test/tap/tests/test_read_only_actions_offline_hard_servers-t.cpp index 3ee3e9a42e..90380e20f9 100644 --- a/test/tap/tests/test_read_only_actions_offline_hard_servers-t.cpp +++ b/test/tap/tests/test_read_only_actions_offline_hard_servers-t.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" #include "utils.h" diff --git a/test/tap/tests/test_rw_binary_data-t.cpp b/test/tap/tests/test_rw_binary_data-t.cpp index a61f9834b6..748d62aedd 100644 --- a/test/tap/tests/test_rw_binary_data-t.cpp +++ b/test/tap/tests/test_rw_binary_data-t.cpp @@ -24,8 +24,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "command_line.h" #include "proxysql_utils.h" diff --git a/test/tap/tests/test_set_character_results-t.cpp b/test/tap/tests/test_set_character_results-t.cpp index 5bd927353f..a4499ac04c 100644 --- a/test/tap/tests/test_set_character_results-t.cpp +++ b/test/tap/tests/test_set_character_results-t.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_set_collation-t.cpp b/test/tap/tests/test_set_collation-t.cpp index 563f6af61c..cc3a6df4b9 100644 --- a/test/tap/tests/test_set_collation-t.cpp +++ b/test/tap/tests/test_set_collation-t.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include "mysql.h" #include #include "tap.h" diff --git a/test/tap/tests/test_simple_embedded_HTTP_server-t.cpp b/test/tap/tests/test_simple_embedded_HTTP_server-t.cpp index 86d9328bb1..4f2860a729 100644 --- a/test/tap/tests/test_simple_embedded_HTTP_server-t.cpp +++ b/test/tap/tests/test_simple_embedded_HTTP_server-t.cpp @@ -8,8 +8,8 @@ #include "curl/curl.h" -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_sqlite3_server-t.cpp b/test/tap/tests/test_sqlite3_server-t.cpp index 36d6982ef8..a9e2b06f93 100644 --- a/test/tap/tests/test_sqlite3_server-t.cpp +++ b/test/tap/tests/test_sqlite3_server-t.cpp @@ -35,8 +35,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_sqlite3_server_and_fast_routing-t.cpp b/test/tap/tests/test_sqlite3_server_and_fast_routing-t.cpp index 84f74042e6..c53732373d 100644 --- a/test/tap/tests/test_sqlite3_server_and_fast_routing-t.cpp +++ b/test/tap/tests/test_sqlite3_server_and_fast_routing-t.cpp @@ -7,8 +7,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_ssl_connect-t.cpp b/test/tap/tests/test_ssl_connect-t.cpp index f517c5a82d..daa9d02f66 100644 --- a/test/tap/tests/test_ssl_connect-t.cpp +++ b/test/tap/tests/test_ssl_connect-t.cpp @@ -6,8 +6,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_ssl_fast_forward-1-t.cpp b/test/tap/tests/test_ssl_fast_forward-1-t.cpp index b075ef632f..f8c5022c8d 100644 --- a/test/tap/tests/test_ssl_fast_forward-1-t.cpp +++ b/test/tap/tests/test_ssl_fast_forward-1-t.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_ssl_fast_forward-2-t.cpp b/test/tap/tests/test_ssl_fast_forward-2-t.cpp index dd5b0b9a3b..e90f4b8d94 100644 --- a/test/tap/tests/test_ssl_fast_forward-2-t.cpp +++ b/test/tap/tests/test_ssl_fast_forward-2-t.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_ssl_fast_forward-3-t.cpp b/test/tap/tests/test_ssl_fast_forward-3-t.cpp index af48b854d4..24d36e3842 100644 --- a/test/tap/tests/test_ssl_fast_forward-3-t.cpp +++ b/test/tap/tests/test_ssl_fast_forward-3-t.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_ssl_large_query-1-t.cpp b/test/tap/tests/test_ssl_large_query-1-t.cpp index fd9ef63a9e..896a2d955d 100644 --- a/test/tap/tests/test_ssl_large_query-1-t.cpp +++ b/test/tap/tests/test_ssl_large_query-1-t.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_ssl_large_query-2-t.cpp b/test/tap/tests/test_ssl_large_query-2-t.cpp index eb780cf2b4..eaaaada5db 100644 --- a/test/tap/tests/test_ssl_large_query-2-t.cpp +++ b/test/tap/tests/test_ssl_large_query-2-t.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_stats_proxysql_message_metrics-t.cpp b/test/tap/tests/test_stats_proxysql_message_metrics-t.cpp index 173dff40b4..8970596977 100644 --- a/test/tap/tests/test_stats_proxysql_message_metrics-t.cpp +++ b/test/tap/tests/test_stats_proxysql_message_metrics-t.cpp @@ -8,8 +8,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "tap.h" #include "utils.h" diff --git a/test/tap/tests/test_throttle_max_bytes_per_second_to_client-t.cpp b/test/tap/tests/test_throttle_max_bytes_per_second_to_client-t.cpp index 3382d1fa21..d031f3c561 100644 --- a/test/tap/tests/test_throttle_max_bytes_per_second_to_client-t.cpp +++ b/test/tap/tests/test_throttle_max_bytes_per_second_to_client-t.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include "mysql.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_unshun_algorithm-t.cpp b/test/tap/tests/test_unshun_algorithm-t.cpp index 6e57c5f8a0..4a5714eaaf 100644 --- a/test/tap/tests/test_unshun_algorithm-t.cpp +++ b/test/tap/tests/test_unshun_algorithm-t.cpp @@ -63,8 +63,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "proxysql_utils.h" #include "tap.h" diff --git a/test/tap/tests/test_unsupported_queries-t.cpp b/test/tap/tests/test_unsupported_queries-t.cpp index 35acce78ab..07afbb7884 100644 --- a/test/tap/tests/test_unsupported_queries-t.cpp +++ b/test/tap/tests/test_unsupported_queries-t.cpp @@ -12,8 +12,8 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" #include "command_line.h" #include "json.hpp" diff --git a/test/tap/tests_with_deps/Makefile b/test/tap/tests_with_deps/Makefile index 8c99712ab9..7e9665fea3 100644 --- a/test/tap/tests_with_deps/Makefile +++ b/test/tap/tests_with_deps/Makefile @@ -1,13 +1,20 @@ +#!/bin/make -f + + +.DEFAULT: all .PHONY: all all: deprecate_eof_support -.PHONY: clean -clean: - cd deprecate_eof_support && ${MAKE} clean - .PHONY: debug debug: deprecate_eof_support + .PHONY: deprecate_eof_support deprecate_eof_support: cd deprecate_eof_support && CC=${CC} CXX=${CXX} ${MAKE} $(MAKECMDGOALS) + + +.PHONY: clean +.SILENT: clean +clean: + cd deprecate_eof_support && ${MAKE} clean diff --git a/test/tap/tests_with_deps/deprecate_eof_support/Makefile b/test/tap/tests_with_deps/deprecate_eof_support/Makefile index 4db69b67f1..e800475998 100644 --- a/test/tap/tests_with_deps/deprecate_eof_support/Makefile +++ b/test/tap/tests_with_deps/deprecate_eof_support/Makefile @@ -1,106 +1,194 @@ -include ../common_defs.Makefile +#!/bin/make -f -MARIADB_BASE_FOLDER=$(TEST_DEPS)/mariadb-client-library-3.1.9/ -MARIADB_TEST_DEP=$(TEST_DEPS)/mariadb-client-library-3.1.9/mariadb-connector-c -BASE_DIR_EXIST := $(shell if [ -d $(MARIADB_BASE_FOLDER) ]; then echo 1; else echo 0; fi) -DEP_FOLDER_EMPTY := $(shell if [ -f $(MARIADB_TEST_DEP)/CMakeLists.txt ]; then echo 1; else echo 0; fi) +GIT_VERSION ?= $(shell git describe --long --abbrev=7) +ifndef GIT_VERSION + $(error GIT_VERSION is not set) +endif -ifeq ($(BASE_DIR_EXIST), 1) -ifeq ($(DEP_FOLDER_EMPTY), 0) - $(error "Dependency folder '$(MARIADB_TEST_DEP)' is empty. Make sure folder is initialized") -endif +PROXYSQL_PATH := $(shell while [ ! -f ./src/proxysql_global.cpp ]; do cd ..; done; pwd) +PROXYSQL_IDIR := $(PROXYSQL_PATH)/include +PROXYSQL_LDIR := $(PROXYSQL_PATH)/lib -.PHONY: all -all: build_deps tests +DEPS_PATH := $(PROXYSQL_PATH)/deps -OPT=-O2 $(WGCOV) -Wl,--no-as-needed +MARIADB_PATH := $(DEPS_PATH)/mariadb-client-library/mariadb_client +MARIADB_IDIR := $(MARIADB_PATH)/include +MARIADB_LDIR := $(MARIADB_PATH)/libmariadb -.PHONY: debug -debug: OPT=-O0 -DDEBUG -ggdb -Wl,--no-as-needed $(WGCOV) $(WASAN) -debug: build_deps tests +JEMALLOC_PATH := $(DEPS_PATH)/jemalloc/jemalloc +JEMALLOC_IDIR := $(JEMALLOC_PATH)/include/jemalloc +JEMALLOC_LDIR := $(JEMALLOC_PATH)/lib -LLP=$(shell pwd)/../../tap -$(MARIADB_TEST_DEP)/libmariadb/libmariadbclient.a: - cd $(MARIADB_BASE_FOLDER) && CC=${CC} CXX=${CXX} ${MAKE} mariadb_client +JSON_IDIR := $(DEPS_PATH)/json -.PHONY: build_deps -build_deps: $(MARIADB_TEST_DEP)/libmariadb/libmariadbclient.a +RE2_PATH := $(DEPS_PATH)/re2/re2 +RE2_IDIR := $(RE2_PATH) +RE2_LDIR := $(RE2_PATH)/obj -.PHONY: clean -clean: - rm -f *-t - rm -f fwd_eof_query - rm -f fwd_eof_ok_query - rm -f *.a *.o - cd $(MARIADB_BASE_FOLDER) && ${MAKE} clean - -WITHGCOVVAR := $(shell echo $(WITHGCOV)) -ifeq ($(WITHGCOVVAR),1) -WGCOV=-DWITHGCOV --coverage -lgcov -else -WGCOV= +SQLITE3_PATH := $(DEPS_PATH)/sqlite3/sqlite3 +SQLITE3_IDIR := $(SQLITE3_PATH) +SQLITE3_LDIR := $(SQLITE3_PATH) + +LIBHTTPSERVER_DIR := $(DEPS_PATH)/libhttpserver/libhttpserver +LIBHTTPSERVER_IDIR := $(LIBHTTPSERVER_DIR)/src +LIBHTTPSERVER_LDIR := $(LIBHTTPSERVER_DIR)/build/src/.libs/ + +LIBCONFIG_PATH := $(DEPS_PATH)/libconfig/libconfig +LIBCONFIG_IDIR := $(LIBCONFIG_PATH)/lib +LIBCONFIG_LDIR := $(LIBCONFIG_PATH)/lib/.libs + +CURL_DIR := $(DEPS_PATH)/curl/curl +CURL_IDIR := $(CURL_DIR)/include +CURL_LDIR := $(CURL_DIR)/lib/.libs + +PCRE_PATH := $(DEPS_PATH)/pcre/pcre +PCRE_LDIR := $(PCRE_PATH)/.libs + +MICROHTTPD_DIR := $(DEPS_PATH)/libmicrohttpd/libmicrohttpd/src +MICROHTTPD_IDIR := $(MICROHTTPD_DIR)/include +MICROHTTPD_LDIR := $(MICROHTTPD_DIR)/microhttpd/.libs + +SSL_DIR := $(DEPS_PATH)/libssl/openssl/ +SSL_IDIR := $(SSL_DIR)/include +SSL_LDIR := $(SSL_DIR) + +EV_DIR := $(DEPS_PATH)/libev/libev/ +EV_IDIR := $(EV_DIR) +EV_LDIR := $(EV_DIR)/.libs + +PROMETHEUS_PATH := $(DEPS_PATH)/prometheus-cpp/prometheus-cpp +PROMETHEUS_IDIR := $(PROMETHEUS_PATH)/pull/include -I$(PROMETHEUS_PATH)/core/include +PROMETHEUS_LDIR := $(PROMETHEUS_PATH)/lib + +CITYHASH_DIR := $(DEPS_PATH)/cityhash/cityhash/ +CITYHASH_IDIR := $(CITYHASH_DIR) +CITYHASH_LDIR := $(CITYHASH_DIR)/src/.libs + +COREDUMPER_DIR := $(DEPS_PATH)/coredumper/coredumper +COREDUMPER_IDIR := $(COREDUMPER_DIR)/include +COREDUMPER_LDIR := $(COREDUMPER_DIR)/src + +TAP_PATH := $(PROXYSQL_PATH)/test/tap/tap +TAP_IDIR := $(TAP_PATH) +TAP_LDIR := $(TAP_PATH) + +TEST_DEPS_PATH := $(PROXYSQL_PATH)/test/deps + +TEST_MARIADB_PATH := $(TEST_DEPS_PATH)/mariadb-connector-c/mariadb-connector-c +TEST_MARIADB_IDIR := $(TEST_MARIADB_PATH)/include +TEST_MARIADB_LDIR := $(TEST_MARIADB_PATH)/libmariadb + +TEST_MYSQL_PATH := $(TEST_DEPS_PATH)/mysql-connector-c/mysql-connector-c +TEST_MYSQL_IDIR := $(TEST_MYSQL_PATH)/include +TEST_MYSQL_EDIR := $(TEST_MYSQL_PATH)/libbinlogevents/export +TEST_MYSQL_LDIR := $(TEST_MYSQL_PATH)/libmysql + +### detect compiler support for c++11/17 +CPLUSPLUS := $(shell ${CC} -std=c++17 -dM -E -x c++ /dev/null 2>/dev/null | grep -F __cplusplus | grep -Po '\d\d\d\d\d\dL') +ifneq ($(CPLUSPLUS),201703L) + CPLUSPLUS := $(shell ${CC} -std=c++11 -dM -E -x c++ /dev/null 2>/dev/null| grep -F __cplusplus | grep -Po '\d\d\d\d\d\dL') +ifneq ($(CPLUSPLUS),201103L) + $(error Compiler must support at least c++11) +endif +endif +STDCPP := -std=c++$(shell echo $(CPLUSPLUS) | cut -c3-4) -DCXX$(shell echo $(CPLUSPLUS) | cut -c3-4) + +WGCOV := +ifeq ($(WITHGCOV),1) + WGCOV := -DWITHGCOV --coverage -lgcov endif -WITHASANVAR := $(shell echo $(WITHASAN)) -ifeq ($(WITHASANVAR),1) -WASAN=-fsanitize=address -else -WASAN= +WASAN := +ifeq ($(WITHASAN),1) + WASAN := -fsanitize=address endif -TESTS_DEPS=$(TAP_LIBDIR)/libtap.a $(MARIADB_TEST_DEP)/libmariadb/libmariadbclient.a +OPT := $(STDCPP) -O2 -ggdb -Wl,--no-as-needed -Wl,-rpath,"../../tap" $(WGCOV) $(WASAN) + +IDIRS := -I$(TAP_IDIR) -I$(RE2_IDIR) -I$(PROXYSQL_IDIR) -I$(JEMALLOC_IDIR) -I$(LIBCONFIG_IDIR) -I$(MICROHTTPD_IDIR) -I$(LIBHTTPSERVER_IDIR) -I$(CURL_IDIR) -I$(EV_IDIR) -I$(PROMETHEUS_IDIR) -I$(DOTENV_DYN_IDIR) -I$(SSL_IDIR) -I$(SQLITE3_IDIR) -I$(JSON_IDIR) +LDIRS := -L$(TAP_LDIR) -L$(RE2_LDIR) -L$(PROXYSQL_LDIR) -L$(JEMALLOC_LDIR) -L$(LIBCONFIG_LDIR) -L$(MICROHTTPD_LDIR) -L$(LIBHTTPSERVER_LDIR) -L$(CURL_LDIR) -L$(EV_LDIR) -L$(PROMETHEUS_LDIR) -L$(DOTENV_DYN_LDIR) -L$(SSL_LDIR) -L$(SQLITE3_LDIR) -L$(PCRE_LDIR) + +### main targets + +.DEFAULT: default +.PHONY: default +default: all + +.PHONY: all +all: tests + +.PHONY: debug +debug: DEBUG := -DDEBUG +debug: OPT := $(STDCPP) -O0 -DDEBUG -ggdb -Wl,--no-as-needed -Wl,-rpath,"../../tap" $(WGCOV) $(WASAN) +debug: tests + -tests: $(patsubst %.cpp,%,$(wildcard *-t.cpp)) fwd_eof_query fwd_eof_ok_query +### test deps targets -COMMONARGS1=$(OPT) -std=c++11 -I$(TAP_LIBDIR) -L$(TAP_LIBDIR) -ltap -lcpp_dotenv -Wl,--no-as-needed -lz -ldl -lpthread -DGITVERSION=\"$(GIT_VERSION)\" -DDEBUG -I$(JSON_IDIR) -COMMONARGS=$(COMMONARGS1) $(MYLIBS) +#.PHONY: build_test_deps +#build_test_deps: $(TAP_LDIR)/libtap.so $(TEST_MARIADB_LDIR)/libmariadbclient.a $(TEST_MYSQL_LDIR)/libmysqlclient.a -ok_packet_mixed_queries-t: eof_packet_mixed_queries-t.cpp $(TESTS_DEPS) - LD_LIBRARY_PATH=$(LLP) $(CXX) $< $(INCLUDEDIRS) $(LDIRS) $(COMMONARGS) -o $@ +$(TAP_LDIR)/libtap.so: + cd $(TAP_PATH) && CC=${CC} CXX=${CXX} ${MAKE} -eof_packet_mixed_queries-t: eof_packet_mixed_queries-t.cpp ok_packet_mixed_queries-t $(TESTS_DEPS) - LD_LIBRARY_PATH=$(LLP) $(CXX) -DNON_EOF_SUPPORT $< $(INCLUDEDIRS) -I$(MARIADB_TEST_DEP)/include/ -L$(MARIADB_TEST_DEP)/libmariadb $(COMMONARGS) -o $@ +$(TEST_MARIADB_LDIR)/libmariadbclient.a: + cd $(TEST_DEPS_PATH) && CC=${CC} CXX=${CXX} ${MAKE} mariadb_client -fwd_eof_query: fwd_eof_query.cpp $(TESTS_DEPS) - LD_LIBRARY_PATH=$(LLP) $(CXX) -DNON_EOF_SUPPORT $< $(INCLUDEDIRS) -L$(MARIADB_TEST_DEP)/libmariadb $(COMMONARGS) -o $@ +$(TEST_MYSQL_LDIR)/libmysqlclient.a: + cd $(TEST_DEPS_PATH) && CC=${CC} CXX=${CXX} ${MAKE} mysql_client -#fwd_eof_ok_query: fwd_eof_query.cpp $(TESTS_DEPS) -# $(CXX) -DDEBUG fwd_eof_query.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 -I$(TAP_LIBDIR) -L$(TAP_LIBDIR) $(MYLIBS) -L$(SSL_LDIR) -ltap -Wl,--no-as-needed -ldl -lpthread -o fwd_eof_ok_query -DGITVERSION=\"$(GIT_VERSION)\" + +### test and helper targets + +#tests: build_test_deps +tests: $(patsubst %.cpp,%,$(wildcard *-t.cpp)) ok_packet_mixed_queries-t fwd_eof_query fwd_eof_ok_query + +COMMONARGS = $(OPT) -Wl,-Bdynamic -ltap -lcpp_dotenv -lcurl -lssl -lcrypto -lz -ldl -lpthread -DGITVERSION=\"$(GIT_VERSION)\" + +ok_packet_mixed_queries-t: eof_packet_mixed_queries-t.cpp + $(CXX) $< $(IDIRS) $(LDIRS) -I$(MARIADB_IDIR) -L$(MARIADB_LDIR) -lmariadbclient $(COMMONARGS) -o $@ + +eof_packet_mixed_queries-t: eof_packet_mixed_queries-t.cpp + $(CXX) -DNON_EOF_SUPPORT $< $(IDIRS) $(LDIRS) -I$(TEST_MARIADB_IDIR) -L$(TEST_MARIADB_LDIR) -lmariadbclient $(COMMONARGS) -o $@ + +fwd_eof_query: fwd_eof_query.cpp + $(CXX) -DNON_EOF_SUPPORT $< $(IDIRS) $(LDIRS) -I$(TEST_MARIADB_IDIR) -L$(TEST_MARIADB_LDIR) -lmariadbclient $(COMMONARGS) -o $@ # NOTE: Compilation with 'libmysql' instead of 'libmariadb' client to confirm packet sequence id isn't check by 'libmariadb' -fwd_eof_ok_query: fwd_eof_query.cpp $(TAP_LIBDIR)/libtap.a - LD_LIBRARY_PATH=$(LLP) $(CXX) $< -I/usr/include/mysql/ -I$(IDIR) -L/usr/lib/x86_64-linux-gnu/ -lmysqlclient $(COMMONARGS1) -o $@ +fwd_eof_ok_query: fwd_eof_query.cpp + $(CXX) $< $(IDIRS) $(LDIRS) -I$(TEST_MARIADB_IDIR) -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient $(COMMONARGS) -o $@ +# NOTE end -deprecate_eof_cache-t: deprecate_eof_cache-t.cpp $(TESTS_DEPS) - LD_LIBRARY_PATH=$(LLP) $(CXX) $^ $(INCLUDEDIRS) -L$(MARIADB_TEST_DEP)/libmariadb -L$(PROXYLDIR) -lproxysql $(COMMONARGS) -o $@ +deprecate_eof_cache-t: deprecate_eof_cache-t.cpp + $(CXX) $< $(IDIRS) $(LDIRS) $(PROXYSQL_LDIR)/proxysql_utils.cpp -I$(TEST_MARIADB_IDIR) -L$(TEST_MARIADB_LDIR) -lmariadbclient $(COMMONARGS) -o $@ -eof_cache_mixed_flags-t: eof_cache_mixed_flags-t.cpp $(TESTS_DEPS) - LD_LIBRARY_PATH=$(LLP) $(CXX) $^ $(INCLUDEDIRS) -L$(MARIADB_TEST_DEP)/libmariadb $(COMMONARGS) -o $@ +eof_cache_mixed_flags-t: eof_cache_mixed_flags-t.cpp + $(CXX) $< $(IDIRS) $(LDIRS) -I$(TEST_MARIADB_IDIR) -L$(TEST_MARIADB_LDIR) -lmariadbclient $(COMMONARGS) -o $@ -eof_mixed_flags_queries-t: eof_mixed_flags_queries-t.cpp $(TESTS_DEPS) - LD_LIBRARY_PATH=$(LLP) $(CXX) $^ $(INCLUDEDIRS) -L$(MARIADB_TEST_DEP)/libmariadb $(COMMONARGS) -o $@ +eof_mixed_flags_queries-t: eof_mixed_flags_queries-t.cpp + $(CXX) $< $(IDIRS) $(LDIRS) -I$(TEST_MARIADB_IDIR) -L$(TEST_MARIADB_LDIR) -lmariadbclient $(COMMONARGS) -o $@ -eof_conn_options_check-t: eof_conn_options_check-t.cpp $(TESTS_DEPS) - LD_LIBRARY_PATH=$(LLP) $(CXX) $< $(INCLUDEDIRS) $(LDIRS) $(COMMONARGS) -o $@ +eof_conn_options_check-t: eof_conn_options_check-t.cpp + $(CXX) $< $(IDIRS) $(LDIRS) -I$(MARIADB_IDIR) -L$(MARIADB_LDIR) -lmariadbclient $(COMMONARGS) -o $@ -eof_fast_forward-t: eof_fast_forward-t.cpp $(TESTS_DEPS) - LD_LIBRARY_PATH=$(LLP) $(CXX) $< $(INCLUDEDIRS) $(LDIRS) $(COMMONARGS) -o $@ +eof_fast_forward-t: eof_fast_forward-t.cpp + $(CXX) $< $(IDIRS) $(LDIRS) -I$(MARIADB_IDIR) -L$(MARIADB_LDIR) -lmariadbclient $(COMMONARGS) -o $@ -else -.PHONY: all -all: - $(warning The required deps were not found. Tests 'deprecate_eof_support' wont be build) -.PHONY: debug -debug: - $(warning The required deps were not found. Tests 'deprecate_eof_support' wont be build) +### clean targets +.SILENT: cleanall +.PHONY: cleanall +cleanall: clean + cd $(TEST_DEPS_PATH) && ${MAKE} cleanall + +.SILENT: clean .PHONY: clean clean: - rm -f *-t - rm -f fwd_eof_query - rm -f fwd_eof_ok_query - rm -f *.a *.o -endif + rm -f *-t || true + rm -f fwd_eof_query || true + rm -f fwd_eof_ok_query || true + rm -f *.a *.o || true + rm -f *.gcda *.gcno || true diff --git a/test/tap/tests_with_deps/deprecate_eof_support/deprecate_eof_cache-t.cpp b/test/tap/tests_with_deps/deprecate_eof_support/deprecate_eof_cache-t.cpp index 66c4e8b8c1..e46e2d3f94 100644 --- a/test/tap/tests_with_deps/deprecate_eof_support/deprecate_eof_cache-t.cpp +++ b/test/tap/tests_with_deps/deprecate_eof_support/deprecate_eof_cache-t.cpp @@ -3,13 +3,14 @@ #include #include #include +#include #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" -#include +#include "proxysql_utils.h" #include "tap.h" #include "command_line.h" @@ -17,6 +18,7 @@ #include "json.hpp" + using std::vector; using std::string; using std::pair; diff --git a/test/tap/tests_with_deps/deprecate_eof_support/eof_cache_mixed_flags-t.cpp b/test/tap/tests_with_deps/deprecate_eof_support/eof_cache_mixed_flags-t.cpp index 22f6ee9ffc..f7f95a694d 100644 --- a/test/tap/tests_with_deps/deprecate_eof_support/eof_cache_mixed_flags-t.cpp +++ b/test/tap/tests_with_deps/deprecate_eof_support/eof_cache_mixed_flags-t.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests_with_deps/deprecate_eof_support/eof_conn_options_check-t.cpp b/test/tap/tests_with_deps/deprecate_eof_support/eof_conn_options_check-t.cpp index bb9cbfe999..ea64d518c8 100644 --- a/test/tap/tests_with_deps/deprecate_eof_support/eof_conn_options_check-t.cpp +++ b/test/tap/tests_with_deps/deprecate_eof_support/eof_conn_options_check-t.cpp @@ -17,13 +17,14 @@ #include #include -#include +#include "mysqld_error.h" #include "json.hpp" #include "tap.h" #include "command_line.h" #include "utils.h" + using nlohmann::json; /** diff --git a/test/tap/tests_with_deps/deprecate_eof_support/eof_fast_forward-t.cpp b/test/tap/tests_with_deps/deprecate_eof_support/eof_fast_forward-t.cpp index 976c4931c5..e6260d0a7d 100644 --- a/test/tap/tests_with_deps/deprecate_eof_support/eof_fast_forward-t.cpp +++ b/test/tap/tests_with_deps/deprecate_eof_support/eof_fast_forward-t.cpp @@ -17,15 +17,16 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" -#include +#include "proxysql_utils.h" #include "tap.h" #include "command_line.h" #include "utils.h" + using std::vector; using std::string; using std::pair; diff --git a/test/tap/tests_with_deps/deprecate_eof_support/eof_mixed_flags_queries-t.cpp b/test/tap/tests_with_deps/deprecate_eof_support/eof_mixed_flags_queries-t.cpp index d860ed25c3..2222e6a19c 100644 --- a/test/tap/tests_with_deps/deprecate_eof_support/eof_mixed_flags_queries-t.cpp +++ b/test/tap/tests_with_deps/deprecate_eof_support/eof_mixed_flags_queries-t.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests_with_deps/deprecate_eof_support/eof_packet_mixed_queries-t.cpp b/test/tap/tests_with_deps/deprecate_eof_support/eof_packet_mixed_queries-t.cpp index 6584afb920..d55fa7f2f6 100644 --- a/test/tap/tests_with_deps/deprecate_eof_support/eof_packet_mixed_queries-t.cpp +++ b/test/tap/tests_with_deps/deprecate_eof_support/eof_packet_mixed_queries-t.cpp @@ -19,9 +19,9 @@ #include #include "mysql.h" -#include "mysql/mysqld_error.h" +#include "mysqld_error.h" -#include +#include "proxysql_utils.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests_with_deps/deprecate_eof_support/fwd_eof_query.cpp b/test/tap/tests_with_deps/deprecate_eof_support/fwd_eof_query.cpp index faf18c8599..5eb35f4c61 100644 --- a/test/tap/tests_with_deps/deprecate_eof_support/fwd_eof_query.cpp +++ b/test/tap/tests_with_deps/deprecate_eof_support/fwd_eof_query.cpp @@ -5,16 +5,18 @@ #include #include -#include -#include +#include "mysql.h" +#include "mysqld_error.h" -#include +#include "proxysql_utils.h" #include "tap.h" #include "command_line.h" #include "json.hpp" +#include "MySQL_Protocol.h" + using namespace nlohmann; using std::string;