diff --git a/.gitignore b/.gitignore index e8015040..a63be433 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ # Keep empty directories: >> .gitignore/.git* /target/ .idea - +/r2dbc-mariadb.iml diff --git a/.travis.yml b/.travis.yml index 831a8d2a..a024870e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,55 +1,56 @@ -#dist: trusty -sudo: false +os: linux language: java services: docker +jdk: openjdk11 addons: hosts: - mariadb.example.com -jdk: openjdk11 before_install: - - chmod +x .travis/script.sh .travis/build/build.sh .travis/gen-ssl.sh .travis/build/docker-entrypoint.sh .travis/sql/* - - chmod 777 .travis/build/ - - echo "MAVEN_OPTS='-Xmx384m'" > ~/.mavenrc + - git clone https://github.com/mariadb-corporation/connector-test-machine.git install: - - wget -qO- 'https://github.com/tianon/pgp-happy-eyeballs/raw/master/hack-my-builds.sh' | bash - - mkdir tmp - - .travis/gen-ssl.sh mariadb.example.com tmp - - export PROJ_PATH=`pwd` - - export SSLCERT=$PROJ_PATH/tmp - - export ENTRYPOINT=$PROJ_PATH/.travis/sql - - export TEST_SERVER_SSL_CERT=$SSLCERT/server.crt - - export TEST_CLIENT_SSL_CERT=$SSLCERT/client.crt - - export TEST_CLIENT_KEY=$SSLCERT/client.key + - |- + case $TRAVIS_OS_NAME in + windows) + choco install openjdk11 maven + export PATH=$(cmd.exe //c "refreshenv > nul & C:\Progra~1\Git\bin\bash -c 'echo \$PATH' ") + connector-test-machine/launch.bat -t "$srv" -v "$v" -d testr2 + ;; + linux) + source connector-test-machine/launch.sh -t "$srv" -v "$v" -d testr2 + ;; + esac -cache: - directories: - - $HOME/.m2 - -matrix: +jobs: + fast_finish: true allow_failures: - - env: DB=build - - env: DB=mysql:5.6 - - env: DB=mysql:5.7 - - env: DB=mysql:8.0 ADDITIONAL_CONF=--default-authentication-plugin=mysql_native_password --caching_sha2_password_private_key_path=/etc/sslcert/server.key --caching_sha2_password_public_key_path=/etc/sslcert/public.key --sha256_password_public_key_path=/etc/sslcert/public.key --sha256_password_private_key_path=/etc/sslcert/server.key + - env: srv=skysql + - env: srv=skysql-ha + - env: srv=mariadb-es v=10.5 include: - - env: DB=mariadb:10.2 - - env: DB=mariadb:10.3 - - env: DB=mariadb:10.4 - - env: DB=mariadb:10.5 - - env: DB=mariadb:10.5 NO_BACKSLASH_ESCAPES=true - - env: DB=mariadb:10.5 BENCHMARK=1 - - env: DB=mariadb:10.5 MAXSCALE_VERSION=2.5.3 SSLPORT=4009 - - env: DB=build - - env: DB=mysql:5.6 - - env: DB=mysql:5.7 - - env: DB=mysql:8.0 ADDITIONAL_CONF=--default-authentication-plugin=mysql_native_password --caching_sha2_password_private_key_path=/etc/sslcert/server.key --caching_sha2_password_public_key_path=/etc/sslcert/public.key --sha256_password_public_key_path=/etc/sslcert/public.key --sha256_password_private_key_path=/etc/sslcert/server.key + - env: srv=mariadb v=10.5 + os: windows + language: shell + - env: srv=mariadb v=10.2 + - env: srv=mariadb v=10.3 + - env: srv=mariadb v=10.4 + - env: srv=mariadb v=10.5 + - env: srv=mariadb v=10.5 NO_BACKSLASH_ESCAPES=true + - env: srv=mariadb v=10.5 BENCH=1 + - env: srv=maxscale + - env: srv=skysql + - env: srv=skysql-ha + - env: srv=build v=10.6 + - env: srv=mysql v=5.7 + - env: srv=mysql v=8.0 script: - - if [ "$DB" = "build" ] ; then .travis/build/build.sh; fi - - if [ "$DB" = "build" ] ; then docker build -t build:latest --label build .travis/build/; fi - - .travis/script.sh + - mvn clean -Dmaven.test.skip > /dev/null + - if [ -n "$BENCH" ] ; then mvn package -P bench -Dmaven.test.skip; fi + - if [ -n "$BENCH" ] ; then java -Duser.country=US -Duser.language=en -DTEST_PORT=$TEST_DB_PORT -DTEST_HOST=$TEST_DB_HOST -DTEST_USERNAME=$TEST_DB_USER -DTEST_PASSWORD=$TEST_DB_PASSWORD -jar target/benchmarks.jar; fi + - if [ -z "$BENCH" ] ; then MAVEN_SKIP_RC=true MAVEN_OPTS="-Xmx2g -XX:MaxPermSize=512m" mvn test -DjobId=${TRAVIS_JOB_ID}; fi + after_success: - bash <(curl -s https://codecov.io/bash) diff --git a/.travis/build/Dockerfile b/.travis/build/Dockerfile deleted file mode 100644 index f152504b..00000000 --- a/.travis/build/Dockerfile +++ /dev/null @@ -1,99 +0,0 @@ -# vim:set ft=dockerfile: -FROM ubuntu:bionic - -# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added -RUN groupadd -r mysql && useradd -r -g mysql mysql - -# https://bugs.debian.org/830696 (apt uses gpgv by default in newer releases, rather than gpg) -RUN set -ex; \ - apt-get update; \ - if ! which gpg; then \ - apt-get install -y --no-install-recommends gnupg; \ - fi; \ -# Ubuntu includes "gnupg" (not "gnupg2", but still 2.x), but not dirmngr, and gnupg 2.x requires dirmngr -# so, if we're not running gnupg 1.x, explicitly install dirmngr too - if ! gpg --version | grep -q '^gpg (GnuPG) 1\.'; then \ - apt-get install -y --no-install-recommends dirmngr; \ - fi; \ - rm -rf /var/lib/apt/lists/* - -# add gosu for easy step-down from root -ENV GOSU_VERSION 1.10 -RUN set -ex; \ - \ - fetchDeps=' \ - ca-certificates \ - wget \ - '; \ - apt-get update; \ - apt-get install -y --no-install-recommends $fetchDeps; \ - rm -rf /var/lib/apt/lists/*; \ - \ - dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ - wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ - wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ - \ -# verify the signature - export GNUPGHOME="$(mktemp -d)"; \ - gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ - gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ - command -v gpgconf > /dev/null && gpgconf --kill all || :; \ - rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc; \ - \ - chmod +x /usr/local/bin/gosu; \ -# verify that the binary works - gosu nobody true; \ - \ - apt-get purge -y --auto-remove $fetchDeps - -RUN mkdir /docker-entrypoint-initdb.d - -# install "pwgen" for randomizing passwords -# install "apt-transport-https" for Percona's repo (switched to https-only) -RUN apt-get update && apt-get install -y --no-install-recommends \ - apt-transport-https ca-certificates \ - tzdata \ - pwgen \ - && rm -rf /var/lib/apt/lists/* - -RUN { \ - echo "mariadb-server-10.5" mysql-server/root_password password 'unused'; \ - echo "mariadb-server-10.5" mysql-server/root_password_again password 'unused'; \ - } | debconf-set-selections - -RUN apt-get update -y -RUN apt-get install -y software-properties-common wget -RUN apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 0xcbcb082a1bb943db -RUN apt-key adv --recv-keys --keyserver ha.pool.sks-keyservers.net F1656F24C74CD1D8 -RUN echo 'deb http://yum.mariadb.org/galera/repo/deb bionic main' > /etc/apt/sources.list.d/galera-test-repo.list -RUN apt-get update -y - -RUN apt-get install -y curl libdbi-perl rsync socat galera3 libnuma1 libaio1 zlib1g-dev libreadline5 libjemalloc1 libsnappy1v5 libcrack2 - -COPY *.deb /root/ -RUN chmod 777 /root/* - -RUN dpkg --install /root/mysql-common* -RUN dpkg --install /root/mariadb-common* -RUN dpkg -R --unpack /root/ -RUN apt-get install -f -y - -RUN rm -rf /var/lib/apt/lists/* \ - && sed -ri 's/^user\s/#&/' /etc/mysql/my.cnf /etc/mysql/conf.d/* \ - && rm -rf /var/lib/mysql && mkdir -p /var/lib/mysql /var/run/mysqld \ - && chown -R mysql:mysql /var/lib/mysql /var/run/mysqld \ - && chmod 777 /var/run/mysqld \ - && find /etc/mysql/ -name '*.cnf' -print0 \ - | xargs -0 grep -lZE '^(bind-address|log)' \ - | xargs -rt -0 sed -Ei 's/^(bind-address|log)/#&/' \ - && echo '[mysqld]\nskip-host-cache\nskip-name-resolve' > /etc/mysql/conf.d/docker.cnf - -VOLUME /var/lib/mysql - -COPY docker-entrypoint.sh /usr/local/bin/ -RUN ln -s usr/local/bin/docker-entrypoint.sh / # backwards compat -ENTRYPOINT ["docker-entrypoint.sh"] - -EXPOSE 3306 -CMD ["mysqld"] - diff --git a/.travis/build/build.sh b/.travis/build/build.sh deleted file mode 100644 index f4d4c85d..00000000 --- a/.travis/build/build.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash - -echo "**************************************************************************" -echo "* searching for last complete build" -echo "**************************************************************************" - -wget -q -o /dev/null index.html http://hasky.askmonty.org/archive/10.5/ -grep -o ">build-[0-9]*" index.html | grep -o "[0-9]*" | tac | while read -r line ; do - - curl -s --head http://hasky.askmonty.org/archive/10.5/build-$line/kvm-deb-bionic-amd64/md5sums.txt | head -n 1 | grep "HTTP/1.[01] [23].." > /dev/null - if [ $? = "0" ]; then - echo "**************************************************************************" - echo "* Processing $line" - echo "**************************************************************************" - wget -q -o /dev/null -O $line.html http://hasky.askmonty.org/archive/10.5/build-$line/kvm-deb-bionic-amd64/debs/binary/ - grep -o ">[^\"]*\.deb" $line.html | grep -o "[^>]*\.deb" | while read -r file ; do - if [[ "$file" =~ ^mariadb-plugin.* ]] ; - then - echo "skipped file: $file" - else - echo "download file: $file" - wget -q -o /dev/null -O .travis/build/$file http://hasky.askmonty.org/archive/10.5/build-$line/kvm-deb-bionic-amd64/debs/binary/$file - fi - done - - exit - else - echo "skip build $line" - fi -done - - - diff --git a/.travis/build/docker-entrypoint.sh b/.travis/build/docker-entrypoint.sh deleted file mode 100644 index a3ee049c..00000000 --- a/.travis/build/docker-entrypoint.sh +++ /dev/null @@ -1,196 +0,0 @@ -#!/bin/bash -set -eo pipefail -shopt -s nullglob - -# if command starts with an option, prepend mysqld -if [ "${1:0:1}" = '-' ]; then - set -- mysqld "$@" -fi - -# skip setup if they want an option that stops mysqld -wantHelp= -for arg; do - case "$arg" in - -'?'|--help|--print-defaults|-V|--version) - wantHelp=1 - break - ;; - esac -done - -# usage: file_env VAR [DEFAULT] -# ie: file_env 'XYZ_DB_PASSWORD' 'example' -# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of -# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) -file_env() { - local var="$1" - local fileVar="${var}_FILE" - local def="${2:-}" - if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then - echo >&2 "error: both $var and $fileVar are set (but are exclusive)" - exit 1 - fi - local val="$def" - if [ "${!var:-}" ]; then - val="${!var}" - elif [ "${!fileVar:-}" ]; then - val="$(< "${!fileVar}")" - fi - export "$var"="$val" - unset "$fileVar" -} - -_check_config() { - toRun=( "$@" --verbose --help --log-bin-index="$(mktemp -u)" ) - if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then - cat >&2 <<-EOM - ERROR: mysqld failed while attempting to check config - command was: "${toRun[*]}" - $errors - EOM - exit 1 - fi -} - -# Fetch value from server config -# We use mysqld --verbose --help instead of my_print_defaults because the -# latter only show values present in config files, and not server defaults -_get_config() { - local conf="$1"; shift - "$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null \ - | awk '$1 == "'"$conf"'" && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }' - # match "datadir /some/path with/spaces in/it here" but not "--xyz=abc\n datadir (xyz)" -} - -# allow the container to be started with `--user` -if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then - _check_config "$@" - DATADIR="$(_get_config 'datadir' "$@")" - mkdir -p "$DATADIR" - find "$DATADIR" \! -user mysql -exec chown mysql '{}' + - exec gosu mysql "$BASH_SOURCE" "$@" -fi - -if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then - # still need to check config, container may have started with --user - _check_config "$@" - # Get config - DATADIR="$(_get_config 'datadir' "$@")" - - if [ ! -d "$DATADIR/mysql" ]; then - file_env 'MYSQL_ROOT_PASSWORD' - if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - echo >&2 'error: database is uninitialized and password option is not specified ' - echo >&2 ' You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD' - exit 1 - fi - - mkdir -p "$DATADIR" - - echo 'Initializing database' - installArgs=( --datadir="$DATADIR" --rpm ) - if { mysql_install_db --help || :; } | grep -q -- '--auth-root-authentication-method'; then - # beginning in 10.4.3, install_db uses "socket" which only allows system user root to connect, switch back to "normal" to allow mysql root without a password - # see https://github.com/mariadb-corporation/server/commit/b9f3f06857ac6f9105dc65caae19782f09b47fb3 - # (this flag doesn't exist in 10.0 and below) - installArgs+=( --auth-root-authentication-method=normal ) - fi - # "Other options are passed to mysqld." (so we pass all "mysqld" arguments directly here) - mysql_install_db "${installArgs[@]}" "${@:2}" - echo 'Database initialized' - - SOCKET="$(_get_config 'socket' "$@")" - "$@" --skip-networking --socket="${SOCKET}" & - pid="$!" - - mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) - - for i in {30..0}; do - if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then - break - fi - echo 'MySQL init process in progress...' - sleep 1 - done - if [ "$i" = 0 ]; then - echo >&2 'MySQL init process failed.' - exit 1 - fi - - if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then - # sed is for https://bugs.mysql.com/bug.php?id=20545 - mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql - fi - - if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" - echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" - fi - - rootCreate= - # default root to listen for connections from anywhere - file_env 'MYSQL_ROOT_HOST' '%' - if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then - # no, we don't care if read finds a terminating character in this heredoc - # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 - read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ; - GRANT ALL ON *.* TO 'root'@'${MYSQL_ROOT_HOST}' WITH GRANT OPTION ; - EOSQL - fi - - "${mysql[@]}" <<-EOSQL - -- What's done in this file shouldn't be replicated - -- or products like mysql-fabric won't work - SET @@SESSION.SQL_LOG_BIN=0; - DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ; - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MYSQL_ROOT_PASSWORD}') ; - GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ; - ${rootCreate} - DROP DATABASE IF EXISTS test ; - FLUSH PRIVILEGES ; - EOSQL - - if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then - mysql+=( -p"${MYSQL_ROOT_PASSWORD}" ) - fi - - file_env 'MYSQL_DATABASE' - if [ "$MYSQL_DATABASE" ]; then - echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}" - mysql+=( "$MYSQL_DATABASE" ) - fi - - file_env 'MYSQL_USER' - file_env 'MYSQL_PASSWORD' - if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then - echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}" - - if [ "$MYSQL_DATABASE" ]; then - echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}" - fi - fi - - echo - for f in /docker-entrypoint-initdb.d/*; do - case "$f" in - *.sh) echo "$0: running $f"; . "$f" ;; - *.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;; - *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;; - *) echo "$0: ignoring $f" ;; - esac - echo - done - - if ! kill -s TERM "$pid" || ! wait "$pid"; then - echo >&2 'MySQL init process failed.' - exit 1 - fi - - echo - echo 'MySQL init process done. Ready for start up.' - echo - fi -fi - -exec "$@" \ No newline at end of file diff --git a/.travis/docker-compose.yml b/.travis/docker-compose.yml deleted file mode 100644 index a4184f92..00000000 --- a/.travis/docker-compose.yml +++ /dev/null @@ -1,13 +0,0 @@ -version: '2.1' -services: - db: - image: $DB - command: --max-connections=500 --max-allowed-packet=20M --innodb-log-file-size=200M --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --ssl-ca=/etc/sslcert/ca.crt --ssl-cert=/etc/sslcert/server.crt --ssl-key=/etc/sslcert/server.key --bind-address=0.0.0.0 --performance-schema=ON $ADDITIONAL_CONF - ports: - - 3305:3306 - volumes: - - $SSLCERT:/etc/sslcert - - $ENTRYPOINT:/docker-entrypoint-initdb.d - environment: - MYSQL_DATABASE: testr2 - MYSQL_ALLOW_EMPTY_PASSWORD: 1 diff --git a/.travis/gen-ssl.sh b/.travis/gen-ssl.sh deleted file mode 100644 index 8e5875b6..00000000 --- a/.travis/gen-ssl.sh +++ /dev/null @@ -1,160 +0,0 @@ -#!/bin/bash -set -e - -log() { - echo "$@" 1>&2 -} - -print_error() { - echo "$@" 1>&2 - exit 1 -} - -print_usage() { - print_error "Usage: gen-ssl-cert-key " -} - -gen_cert_subject() { - local fqdn="$1" - [[ "${fqdn}" != "" ]] || print_error "FQDN cannot be blank" - echo "/C=XX/ST=X/O=X/localityName=X/CN=${fqdn}/organizationalUnitName=X/emailAddress=X/" -} - -main() { - local fqdn="$1" - local sslDir="$2" - [[ "${fqdn}" != "" ]] || print_usage - [[ -d "${sslDir}" ]] || print_error "Directory does not exist: ${sslDir}" - - local caCertFile="${sslDir}/ca.crt" - local caKeyFile="${sslDir}/ca.key" - local certFile="${sslDir}/server.crt" - local keyFile="${sslDir}/server.key" - local pubkeyFile="${sslDir}/public.key" - local csrFile=$(mktemp) - local clientCertFile="${sslDir}/client.crt" - local clientKeyFile="${sslDir}/client.key" - local clientKeystoreFile="${sslDir}/client-keystore.jks" - local fullClientKeystoreFile="${sslDir}/fullclient-keystore.jks" - local tmpKeystoreFile=$(mktemp) - local pcks12FullKeystoreFile="${sslDir}/fullclient-keystore.p12" - local clientReqFile=$(mktemp) - - log "Generating CA key" - openssl genrsa -out "${caKeyFile}" 2048 - - log "Generating CA certificate" - openssl req \ - -sha1 \ - -new \ - -x509 \ - -nodes \ - -days 3650 \ - -subj "$(gen_cert_subject ca.example.com)" \ - -key "${caKeyFile}" \ - -out "${caCertFile}" - - log "Generating private key" - openssl genrsa -out "${keyFile}" 2048 - - log "Generating public key" - openssl rsa -in "${keyFile}" -pubout -out "${pubkeyFile}" - - log "Generating certificate signing request" - openssl req \ - -new \ - -batch \ - -sha1 \ - -subj "$(gen_cert_subject "$fqdn")" \ - -set_serial 01 \ - -key "${keyFile}" \ - -out "${csrFile}" \ - -nodes - - log "Generating X509 certificate" - openssl x509 \ - -req \ - -sha1 \ - -set_serial 01 \ - -CA "${caCertFile}" \ - -CAkey "${caKeyFile}" \ - -days 3650 \ - -in "${csrFile}" \ - -signkey "${keyFile}" \ - -out "${certFile}" - - log "Generating client certificate" - openssl req \ - -batch \ - -newkey rsa:2048 \ - -days 3600 \ - -subj "$(gen_cert_subject "$fqdn")" \ - -nodes \ - -keyout "${clientKeyFile}" \ - -out "${clientReqFile}" - - openssl x509 \ - -req \ - -in "${clientReqFile}" \ - -days 3600 \ - -CA "${caCertFile}" \ - -CAkey "${caKeyFile}" \ - -set_serial 01 \ - -out "${clientCertFile}" - - # Now generate a keystore with the client cert & key - log "Generating client keystore" - openssl pkcs12 \ - -export \ - -in "${clientCertFile}" \ - -inkey "${clientKeyFile}" \ - -out "${tmpKeystoreFile}" \ - -name "mysqlAlias" \ - -passout pass:kspass - - # convert PKSC12 to JKS - keytool \ - -importkeystore \ - -deststorepass kspass \ - -destkeypass kspass \ - -destkeystore "${clientKeystoreFile}" \ - -srckeystore ${tmpKeystoreFile} \ - -srcstoretype PKCS12 \ - -srcstorepass kspass \ - -alias "mysqlAlias" - - # Now generate a full keystore with the client cert & key + trust certificates - log "Generating full client keystore" - openssl pkcs12 \ - -export \ - -in "${clientCertFile}" \ - -inkey "${clientKeyFile}" \ - -out "${pcks12FullKeystoreFile}" \ - -name "mysqlAlias" \ - -passout pass:kspass - - # convert PKSC12 to JKS - keytool \ - -importkeystore \ - -deststorepass kspass \ - -destkeypass kspasskey \ - -deststoretype JKS \ - -destkeystore "${fullClientKeystoreFile}" \ - -srckeystore ${pcks12FullKeystoreFile} \ - -srcstoretype PKCS12 \ - -srcstorepass kspass \ - -alias "mysqlAlias" - - log "Generating trustStore" - keytool -import -file "${certFile}" -alias CA -keystore "${fullClientKeystoreFile}" -storepass kspass -keypass kspasskey -noprompt - - # Clean up CSR file: - rm "$csrFile" - rm "$clientReqFile" - rm "$tmpKeystoreFile" - - log "Generated key file and certificate in: ${sslDir}" - ls -l "${sslDir}" -} - -main "$@" diff --git a/.travis/maxscale-compose.yml b/.travis/maxscale-compose.yml deleted file mode 100644 index dc104d2b..00000000 --- a/.travis/maxscale-compose.yml +++ /dev/null @@ -1,35 +0,0 @@ -version: '2.1' -services: - db: - image: $DB - command: --max-connections=500 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --ssl-ca=/etc/sslcert/ca.crt --ssl-cert=/etc/sslcert/server.crt --ssl-key=/etc/sslcert/server.key --bind-address=0.0.0.0 - ports: - - 3305:3306 - volumes: - - $SSLCERT:/etc/sslcert - - $ENTRYPOINT:/docker-entrypoint-initdb.d - environment: - MYSQL_DATABASE: testr2 - MYSQL_ALLOW_EMPTY_PASSWORD: 1 - healthcheck: - test: ["CMD", "mysql", "--protocol=tcp", "-ubob", "-h127.0.0.1", "-ubob"] - timeout: 20s - retries: 10 - - maxscale: - depends_on: - db: - condition: service_healthy - links: - - "db:database" - ports: - - 4006:4006 - - 4008:4008 - - 4009:4009 - volumes: - - $SSLCERT:/etc/sslcert - build: - context: . - dockerfile: maxscale/Dockerfile - args: - MAXSCALE_VERSION: $MAXSCALE_VERSION diff --git a/.travis/maxscale/Dockerfile b/.travis/maxscale/Dockerfile deleted file mode 100644 index 0a60b4e7..00000000 --- a/.travis/maxscale/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -FROM centos:7 - -ARG MAXSCALE_VERSION -ENV MAXSCALE_VERSION ${MAXSCALE_VERSION:-2.5.3} - -COPY maxscale/mariadb.repo /etc/yum.repos.d/ - -RUN rpm --import https://yum.mariadb.org/RPM-GPG-KEY-MariaDB \ - && yum -y install https://downloads.mariadb.com/MaxScale/${MAXSCALE_VERSION}/centos/7/x86_64/maxscale-${MAXSCALE_VERSION}-2.rhel.7.x86_64.rpm \ - && yum -y update - -RUN yum -y install maxscale-${MAXSCALE_VERSION} MariaDB-client \ - && yum clean all \ - && rm -rf /tmp/* - -COPY maxscale/docker-entrypoint.sh / -COPY maxscale/maxscale.cnf /etc/ -RUN chmod 777 /etc/maxscale.cnf -RUN chmod 777 /docker-entrypoint.sh - - -EXPOSE 4006 4007 4008 - -ENTRYPOINT ["/docker-entrypoint.sh"] \ No newline at end of file diff --git a/.travis/maxscale/docker-entrypoint.sh b/.travis/maxscale/docker-entrypoint.sh deleted file mode 100644 index 1f2d02c9..00000000 --- a/.travis/maxscale/docker-entrypoint.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash - -set -e - -echo 'creating configuration done' - -sleep 15 - -################################################################################################# -# wait for db availability for 60s -################################################################################################# -mysql=( mysql --protocol=tcp -ubob -hdb --port=3306 ) -for i in {60..0}; do - if echo 'use test2' | "${mysql[@]}" &> /dev/null; then - break - fi - echo 'DB init process in progress...' - sleep 1 -done - -echo 'use test2' | "${mysql[@]}" -if [ "$i" = 0 ]; then - echo 'DB init process failed.' - exit 1 -fi - -echo 'maxscale launching ...' - -tail -n 500 /etc/maxscale.cnf - -/usr/bin/maxscale --user=root --nodaemon - -cd /var/log/maxscale -ls -lrt -tail -n 500 /var/log/maxscale/maxscale.log diff --git a/.travis/maxscale/mariadb.repo b/.travis/maxscale/mariadb.repo deleted file mode 100644 index 055f3b5c..00000000 --- a/.travis/maxscale/mariadb.repo +++ /dev/null @@ -1,7 +0,0 @@ -# MariaDB 10.2 CentOS repository list - created 2017-06-05 08:06 UTC -# http://downloads.mariadb.org/mariadb/repositories/ -[mariadb] -name = MariaDB -baseurl = http://yum.mariadb.org/10.2/centos7-amd64 -gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB -gpgcheck=1 \ No newline at end of file diff --git a/.travis/maxscale/maxscale.cnf b/.travis/maxscale/maxscale.cnf deleted file mode 100644 index 8a85c9e8..00000000 --- a/.travis/maxscale/maxscale.cnf +++ /dev/null @@ -1,121 +0,0 @@ -# MaxScale documentation: -# https://mariadb.com/kb/en/mariadb-maxscale-24/ - -# Global parameters -# -# Complete list of configuration options: -# https://mariadb.com/kb/en/mariadb-maxscale-24-mariadb-maxscale-configuration-guide/ - -[maxscale] -threads=auto - -# Server definitions -# -# Set the address of the server to the network -# address of a MariaDB server. -# - -[server2] -type=server -address=database -port=3306 -protocol=MariaDBBackend -ssl=true -ssl_ca_cert=/etc/sslcert/server.crt -ssl_cert=/etc/sslcert/client.crt -ssl_key=/etc/sslcert/client.key - - -[server1] -type=server -address=db -port=3306 -protocol=MariaDBBackend - - -# Monitor for the servers -# -# This will keep MaxScale aware of the state of the servers. -# MariaDB Monitor documentation: -# https://mariadb.com/kb/en/mariadb-maxscale-24-mariadb-monitor/ - -[MariaDB-Monitor] -type=monitor -module=mariadbmon -servers=server1 -user=boby -password=hey -monitor_interval=2000 - -[MariaDB-Monitor2] -type=monitor -module=mariadbmon -servers=server2 -user=boby -password=hey -monitor_interval=2000 - -# Service definitions -# -# Service Definition for a read-only service and -# a read/write splitting service. -# - -# ReadConnRoute documentation: -# https://mariadb.com/kb/en/mariadb-maxscale-24-readconnroute/ - -[Read-Only-Service] -type=service -router=readconnroute -servers=server1 -user=boby -password=hey -router_options=slave - -# ReadWriteSplit documentation: -# https://mariadb.com/kb/en/mariadb-maxscale-24-readwritesplit/ - -[Read-Write-Service] -type=service -router=readwritesplit -servers=server1 -version_string=10.5.99-MariaDB-maxScale -user=boby -password=hey - -[Read-Write-Service2] -type=service -router=readwritesplit -version_string=10.5.99-MariaDB-maxScale -servers=server2 -user=boby -password=hey - -# Listener definitions for the services -# -# These listeners represent the ports the -# services will listen on. -# - -[Read-Only-Listener] -type=listener -service=Read-Only-Service -protocol=MariaDBClient -port=4008 - -[Read-Write-Listener] -type=listener -service=Read-Write-Service -protocol=MariaDBClient -port=4006 - - -[Read-Write-Listener2] -type=listener -service=Read-Write-Service2 -protocol=MariaDBClient -port=4009 -ssl=true -ssl_ca_cert=/etc/sslcert/ca.crt -ssl_cert=/etc/sslcert/server.crt -ssl_key=/etc/sslcert/server.key diff --git a/.travis/script.sh b/.travis/script.sh deleted file mode 100644 index 514e3c97..00000000 --- a/.travis/script.sh +++ /dev/null @@ -1,131 +0,0 @@ -#!/bin/bash - -set -x -set -e - -################################################################################################################### -# test different type of configuration -################################################################################################################### -if [ -z "NO_BACKSLASH_ESCAPES" ]; then - export NO_BACKSLASH_ESCAPES=false -fi - -if [ -n "$BENCHMARK" ]; then - cmd=(mvn clean package -P bench -Dmaven.test.skip) -else - mvn clean - cmd=(mvn clean verify $ADDITIONNAL_VARIABLES -DjobId=${TRAVIS_JOB_ID} \ - -DkeystorePath="$SSLCERT/client-keystore.jks" \ - -DTEST_HOST=mariadb.example.com \ - -DTEST_PORT=3305 \ - -DTEST_USERNAME=bob \ - -DTEST_DATABASE=test2 \ - -DRUN_LONG_TEST=false \ - -DkeystorePassword="kspass" \ - -DserverCertificatePath="$SSLCERT/server.crt" \ - -DNO_BACKSLASH_ESCAPES="$NO_BACKSLASH_ESCAPES" - -Dkeystore2Path="$SSLCERT/fullclient-keystore.jks" \ - -Dkeystore2Password="kspass" -DkeyPassword="kspasskey" \ - -Dkeystore2PathP12="$SSLCERT/fullclient-keystore.p12" \ - -DrunLongTest=true \ - -DserverPublicKey="$SSLCERT/public.key" \ - -DsslPort="$SSLPORT") -fi - -if [ -n "$MAXSCALE_VERSION" ]; then - ################################################################################################################### - # launch Maxscale with one server - ################################################################################################################### - export TEST_PORT=4006 - mysql=(mysql --protocol=tcp -ubob -h127.0.0.1 --port=4006) - export COMPOSE_FILE=.travis/maxscale-compose.yml - docker-compose -f ${COMPOSE_FILE} build -else - ################################################################################################################### - # launch docker server - ################################################################################################################### - mysql=(mysql --protocol=tcp -ubob -h127.0.0.1 --port=3305) - export COMPOSE_FILE=.travis/docker-compose.yml -fi - -docker-compose -f ${COMPOSE_FILE} up -d - -################################################################################################################### -# wait for docker initialisation -################################################################################################################### - -for i in {15..0}; do - if echo 'SELECT 1' | "${mysql[@]}" ; then - break - fi - echo 'data server still not active' - sleep 2 -done - -docker-compose -f ${COMPOSE_FILE} logs - -if [ "$i" = 0 ]; then - - if echo 'SELECT 1' | "${mysql[@]}" ; then - break - fi - if [ -n "$MAXSCALE_VERSION" ] ; then - docker-compose -f $COMPOSE_FILE exec maxscale tail -n 500 /var/log/maxscale/maxscale.log - fi - echo >&2 'data server init process failed.' - exit 1 -fi - -################################################################################################################### -# create PAM user -################################################################################################################### - -if [ -z "$MAXSCALE_VERSION" ] ; then - docker-compose -f ${COMPOSE_FILE} exec -u root db bash /docker-entrypoint-initdb.d/pam/pam.sh - sleep 2 - docker-compose -f ${COMPOSE_FILE} stop db - sleep 2 - docker-compose -f ${COMPOSE_FILE} up -d - docker-compose -f ${COMPOSE_FILE} logs db - - - ################################################################################################################### - # wait for docker initialisation - ################################################################################################################### - - for i in {15..0}; do - if echo 'SELECT 1' | "${mysql[@]}" ; then - break - fi - echo 'data server still not active' - sleep 2 - done - - docker-compose -f ${COMPOSE_FILE} logs - - if [ "$i" = 0 ]; then - - if echo 'SELECT 1' | "${mysql[@]}" ; then - break - fi - echo >&2 'data server init process failed.' - exit 1 - fi -fi - - -################################################################################################################### -# run test suite -################################################################################################################### -echo "Running coveralls for JDK version: $TRAVIS_JDK_VERSION" - -echo ${cmd} -"${cmd[@]}" - -if [ -n "$BENCHMARK" ]; then - java -DTEST_HOST=mariadb.example.com \ - -DTEST_PORT=3305 \ - -DTEST_USERNAME=bob \ - -DTEST_DATABASE=test2 \ - -jar target/benchmarks.jar -fi diff --git a/.travis/sql/dbinit.sql b/.travis/sql/dbinit.sql deleted file mode 100644 index 2cf6df15..00000000 --- a/.travis/sql/dbinit.sql +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2020 MariaDB Ab. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CREATE USER 'bob'@'%'; -GRANT ALL ON *.* TO 'bob'@'%' with grant option; - -CREATE USER 'boby'@'%' identified by 'hey'; -GRANT ALL ON *.* TO 'boby'@'%' with grant option; - -FLUSH PRIVILEGES; - -CREATE DATABASE test2; \ No newline at end of file diff --git a/.travis/sql/pam/pam.sh b/.travis/sql/pam/pam.sh deleted file mode 100644 index 4a879a0e..00000000 --- a/.travis/sql/pam/pam.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -tee /etc/pam.d/mariadb << EOF -auth required pam_unix.so audit -auth required pam_unix.so audit -account required pam_unix.so audit -EOF - -useradd testPam -chpasswd << EOF -testPam:myPwd -EOF - -usermod -a -G shadow mysql - -echo "pam configuration done" \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 27f1b772..084a6e85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,14 @@ # Change Log +## [1.0.1](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/tree/1.0.1) (09 Mar 2021) +[Full Changelog](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/compare/1.0.0...1.0.1) + +Changes: +* [R2DBC-16] Ensure connection autocommit initialisation and new option autocommit + +Corrections: +* [R2DBC-17] Transactions in query flux might not be persisted +* [R2DBC-19] Data bigger than 16Mb correction. + ## [1.0.0](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/tree/1.0.0) (08 Dec 2020) [Full Changelog](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/compare/0.8.4...1.0.0) diff --git a/README.md b/README.md index 833c7190..ebe95ce0 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,7 @@ # MariaDB R2DBC connector [![Maven Central][maven-image]][maven-url] -[![Linux Build][travis-image]][travis-url] -[![Build status][appveyor-image]][appveyor-url] +[![Test Build][travis-image]][travis-url] [![License][license-image]][license-url] @@ -18,7 +17,7 @@ MariaDB and MySQL client, 100% Java, compatible with Java8+, apache 2.0 licensed - Driver permits ed25519, PAM authentication that comes with MariaDB. - use MariaDB 10.5 returning fonction to permit Statement.returnGeneratedValues -Driver follow [R2DBC 0.8.3 specifications](https://r2dbc.io/spec/0.8.3.RELEASE/spec/html/) +Driver follow [R2DBC 0.8.4 specifications](https://r2dbc.io/spec/0.8.4.RELEASE/spec/html/) ## Documentation @@ -106,6 +105,7 @@ Basic example: | **`useServerPrepStmts`** | Permit to indicate to use text or binary protocol for query with parameter |*boolean* | false | | **`prepareCacheSize`** | if useServerPrepStmts = true, cache the prepared informations in a LRU cache to avoid re-preparation of command. Next use of that command, only prepared identifier and parameters (if any) will be sent to server. This mainly permit for server to avoid reparsing query. |*int* |256 | | **`pamOtherPwd`** | Permit to provide additional password for PAM authentication with multiple authentication step. If multiple passwords, value must be URL encoded.|*string* | | +| **`autocommit`** | Set default autocommit value on connection initialization" |*boolean* | true | ## Roadmap @@ -124,7 +124,5 @@ To file an issue or follow the development, see [JIRA](https://jira.mariadb.org/ [travis-url]:https://travis-ci.com/mariadb-corporation/mariadb-connector-r2dbc [maven-image]:https://maven-badges.herokuapp.com/maven-central/org.mariadb/r2dbc-mariadb/badge.svg [maven-url]:https://maven-badges.herokuapp.com/maven-central/org.mariadb/r2dbc-mariadb -[appveyor-image]:https://ci.appveyor.com/api/projects/status/ikt87wc77r2v4032/branch/master?svg=true -[appveyor-url]:https://ci.appveyor.com/project/mariadb/mariadb-connector-r2dbc/branch/master [license-image]:https://img.shields.io/badge/License-Apache%202.0-blue.svg [license-url]:https://opensource.org/licenses/Apache-2.0 diff --git a/appveyor-download.bat b/appveyor-download.bat deleted file mode 100644 index 5cc2dbbb..00000000 --- a/appveyor-download.bat +++ /dev/null @@ -1,16 +0,0 @@ -@echo off -set archive=http://ftp.hosteurope.de/mirror/archive.mariadb.org//mariadb-%DB%/winx64-packages/mariadb-%DB%-winx64.msi -set last=http://mirror.i3d.net/pub/mariadb//mariadb-%DB%/winx64-packages/mariadb-%DB%-winx64.msi - -curl -fLsS -o server.msi %archive% - -if %ERRORLEVEL% == 0 goto end - -curl -fLsS -o server.msi %last% -if %ERRORLEVEL% == 0 goto end - -echo Failure Reason Given is %errorlevel% -exit /b %errorlevel% - -:end -echo "File found". diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index da4b3a70..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,46 +0,0 @@ -version: '{build}' -environment: - matrix: - - DB: '10.2.36' - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - CMAKE_PARAM: 'Visual Studio 15 2017 Win64' - JAVA_HOME: C:\Program Files\Java\jdk1.8.0 - - - DB: '10.3.27' - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - CMAKE_PARAM: 'Visual Studio 15 2017 Win64' - JAVA_HOME: C:\Program Files\Java\jdk1.8.0 - - - DB: '10.4.17' - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - CMAKE_PARAM: 'Visual Studio 15 2017 Win64' - JAVA_HOME: C:\Program Files\Java\jdk1.8.0 - - - DB: '10.5.8' - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - CMAKE_PARAM: 'Visual Studio 15 2017 Win64' - JAVA_HOME: C:\Program Files\Java\jdk1.8.0 - -clone_folder: c:\projects\r2dbc-mariadb -install: - - ps: | - Add-Type -AssemblyName System.IO.Compression.FileSystem - if (!(Test-Path -Path "C:\maven" )) { - (new-object System.Net.WebClient).DownloadFile('http://www.us.apache.org/dist/maven/maven-3/3.5.4/binaries/apache-maven-3.5.4-bin.zip', 'C:\maven-bin.zip') - [System.IO.Compression.ZipFile]::ExtractToDirectory("C:\maven-bin.zip", "C:\maven") - } - - cmd: appveyor-download.bat - - cmd: msiexec /i server.msi INSTALLDIR=c:\projects\server SERVICENAME=mariadb ALLOWREMOTEROOTACCESS=true /qn - - cmd: "\"c:\\projects\\server\\bin\\mysql.exe\" -e \"create database testj\" --user=root" - - cmd: SET PATH=C:\maven\apache-maven-3.5.4\bin;%JAVA_HOME%\bin;%PATH:C:\Ruby193\bin;=%; - - cmd: SET MAVEN_OPTS=-Xms512m -Xms768m - - cmd: SET JAVA_OPTS=-Xms512m -Xmx768m - - cmd: SET M2_HOME=C:\maven\apache-maven-3.5.4 - - cmd: SET M - - cmd: cd c:\projects\r2dbc-mariadb - - cmd: mvn --version - - cmd: java -version -build_script: - - mvn clean package -Dmaven.test.skip=true -test_script: - - mvn test -DRUN_LONG_TEST=false diff --git a/pom.xml b/pom.xml index a3964403..a19dbe87 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.mariadb r2dbc-mariadb - 1.0.0 + 1.0.1 jar https://github.com/mariadb-corporation/mariadb-connector-r2dbc @@ -32,13 +32,13 @@ 1.8 3.0.2 - 5.7.0 - 1.26 + 5.7.1 + 1.27 1.2.3 - 4.1.54.Final + 4.1.59.Final UTF-8 - 0.8.3.RELEASE - 2020.0.1 + 0.8.4.RELEASE + Dysprosium-SR17 2.6.1 0.8.2.RELEASE benchmarks @@ -264,6 +264,7 @@ maven-surefire-plugin 2.22.2 + -Xmx1024m paranoid @@ -371,7 +372,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M4 + 3.0.0-M5 DISABLED diff --git a/r2dbc-mariadb.iml b/r2dbc-mariadb.iml deleted file mode 100644 index 74f3f131..00000000 --- a/r2dbc-mariadb.iml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/src/benchmark/README.md b/src/benchmark/README.md index db782d81..cb50d688 100644 --- a/src/benchmark/README.md +++ b/src/benchmark/README.md @@ -22,7 +22,7 @@ Configuration by system properties : * TEST_PORT: 3306 * TEST_USERNAME: root * TEST_PASSWORD: "" -* TEST_DATABASE: "testj" +* TEST_DATABASE: "testr2" example: ```script diff --git a/src/benchmark/java/org/mariadb/r2dbc/Common.java b/src/benchmark/java/org/mariadb/r2dbc/Common.java index ab5ff1dc..7af4c494 100644 --- a/src/benchmark/java/org/mariadb/r2dbc/Common.java +++ b/src/benchmark/java/org/mariadb/r2dbc/Common.java @@ -42,7 +42,7 @@ public static class MyState { public final int port = Integer.parseInt(System.getProperty("TEST_PORT", "3306")); public final String username = System.getProperty("TEST_USERNAME", "root"); public final String password = System.getProperty("TEST_PASSWORD", ""); - public final String database = System.getProperty("TEST_DATABASE", "testj"); + public final String database = System.getProperty("TEST_DATABASE", "testr2"); // connections protected Connection jdbc; diff --git a/src/main/java/org/mariadb/r2dbc/MariadbClientParameterizedQueryStatement.java b/src/main/java/org/mariadb/r2dbc/MariadbClientParameterizedQueryStatement.java index fc7621d2..c4aa08b5 100644 --- a/src/main/java/org/mariadb/r2dbc/MariadbClientParameterizedQueryStatement.java +++ b/src/main/java/org/mariadb/r2dbc/MariadbClientParameterizedQueryStatement.java @@ -164,7 +164,7 @@ public Flux execute() { .windowUntil(it -> it.resultSetEnd()) .map( dataRow -> - new org.mariadb.r2dbc.MariadbResult( + new MariadbResult( true, dataRow, ExceptionFactory.INSTANCE, diff --git a/src/main/java/org/mariadb/r2dbc/MariadbConnection.java b/src/main/java/org/mariadb/r2dbc/MariadbConnection.java index 5752aa53..b9cce9f1 100644 --- a/src/main/java/org/mariadb/r2dbc/MariadbConnection.java +++ b/src/main/java/org/mariadb/r2dbc/MariadbConnection.java @@ -20,7 +20,6 @@ import io.r2dbc.spi.ValidationDepth; import org.mariadb.r2dbc.api.MariadbStatement; import org.mariadb.r2dbc.client.Client; -import org.mariadb.r2dbc.client.ClientBase; import org.mariadb.r2dbc.message.client.PingPacket; import org.mariadb.r2dbc.message.client.QueryPacket; import org.mariadb.r2dbc.util.Assert; @@ -50,9 +49,7 @@ final class MariadbConnection implements org.mariadb.r2dbc.api.MariadbConnection @Override public Mono beginTransaction() { - try (ClientBase.LockAction lockAction = this.client.getLockAction()) { - return lockAction.beginTransaction(); - } + return this.client.beginTransaction(); } @Override @@ -62,9 +59,7 @@ public Mono close() { @Override public Mono commitTransaction() { - try (ClientBase.LockAction lockAction = this.client.getLockAction()) { - return lockAction.commitTransaction(); - } + return this.client.commitTransaction(); } @Override @@ -75,9 +70,7 @@ public MariadbBatch createBatch() { @Override public Mono createSavepoint(String name) { Assert.requireNonNull(name, "name must not be null"); - try (ClientBase.LockAction lockAction = this.client.getLockAction()) { - return lockAction.createSavepoint(name); - } + return this.client.createSavepoint(name); } @Override @@ -114,31 +107,23 @@ public boolean isAutoCommit() { @Override public Mono releaseSavepoint(String name) { Assert.requireNonNull(name, "name must not be null"); - try (ClientBase.LockAction lockAction = this.client.getLockAction()) { - return lockAction.releaseSavepoint(name); - } + return this.client.releaseSavepoint(name); } @Override public Mono rollbackTransaction() { - try (ClientBase.LockAction lockAction = this.client.getLockAction()) { - return lockAction.rollbackTransaction(); - } + return this.client.rollbackTransaction(); } @Override public Mono rollbackTransactionToSavepoint(String name) { Assert.requireNonNull(name, "name must not be null"); - try (ClientBase.LockAction lockAction = this.client.getLockAction()) { - return lockAction.rollbackTransactionToSavepoint(name); - } + return this.client.rollbackTransactionToSavepoint(name); } @Override public Mono setAutoCommit(boolean autoCommit) { - try (ClientBase.LockAction lockAction = this.client.getLockAction()) { - return lockAction.setAutoCommit(autoCommit); - } + return client.setAutoCommit(autoCommit); } @Override diff --git a/src/main/java/org/mariadb/r2dbc/MariadbConnectionConfiguration.java b/src/main/java/org/mariadb/r2dbc/MariadbConnectionConfiguration.java index acd55505..c3101f65 100644 --- a/src/main/java/org/mariadb/r2dbc/MariadbConnectionConfiguration.java +++ b/src/main/java/org/mariadb/r2dbc/MariadbConnectionConfiguration.java @@ -55,6 +55,7 @@ public final class MariadbConnectionConfiguration { private final boolean allowPublicKeyRetrieval; private IsolationLevel isolationLevel; private final boolean useServerPrepStmts; + private final boolean autocommit; private MariadbConnectionConfiguration( @Nullable Duration connectTimeout, @@ -81,6 +82,7 @@ private MariadbConnectionConfiguration( @Nullable String cachingRsaPublicKey, boolean allowPublicKeyRetrieval, boolean useServerPrepStmts, + boolean autocommit, @Nullable Integer prepareCacheSize, @Nullable CharSequence[] pamOtherPwd) { this.connectTimeout = connectTimeout == null ? Duration.ofSeconds(10) : connectTimeout; @@ -110,6 +112,7 @@ private MariadbConnectionConfiguration( this.useServerPrepStmts = useServerPrepStmts; this.prepareCacheSize = (prepareCacheSize == null) ? 250 : prepareCacheSize.intValue(); this.pamOtherPwd = pamOtherPwd; + this.autocommit = autocommit; } static boolean boolValue(Object value) { @@ -198,6 +201,11 @@ public static Builder fromOptions(ConnectionFactoryOptions connectionFactoryOpti connectionFactoryOptions.getValue( MariadbConnectionFactoryProvider.USE_SERVER_PREPARE))); } + if (connectionFactoryOptions.hasOption(MariadbConnectionFactoryProvider.AUTO_COMMIT)) { + builder.autocommit( + boolValue( + connectionFactoryOptions.getValue(MariadbConnectionFactoryProvider.AUTO_COMMIT))); + } if (connectionFactoryOptions.hasOption( MariadbConnectionFactoryProvider.CONNECTION_ATTRIBUTES)) { Map myMap = new HashMap<>(); @@ -221,8 +229,7 @@ public static Builder fromOptions(ConnectionFactoryOptions connectionFactoryOpti if (connectionFactoryOptions.hasOption(MariadbConnectionFactoryProvider.SSL_MODE)) { builder.sslMode( - Enum.valueOf( - SslMode.class, + SslMode.from( connectionFactoryOptions.getValue(MariadbConnectionFactoryProvider.SSL_MODE))); } builder.serverSslCert( @@ -347,6 +354,10 @@ public boolean useServerPrepStmts() { return useServerPrepStmts; } + public boolean autocommit() { + return autocommit; + } + public int getPrepareCacheSize() { return prepareCacheSize; } @@ -431,6 +442,8 @@ public String toString() { + isolationLevel + ", useServerPrepStmts=" + useServerPrepStmts + + ", autocommit=" + + autocommit + ", pamOtherPwd=" + hiddenPamPwd + '}'; @@ -461,6 +474,7 @@ public static final class Builder implements Cloneable { private boolean allowMultiQueries = false; private boolean allowPipelining = true; private boolean useServerPrepStmts = false; + private boolean autocommit = true; @Nullable Integer prepareCacheSize; @Nullable private List tlsProtocol; @Nullable private String serverSslCert; @@ -517,6 +531,7 @@ public MariadbConnectionConfiguration build() { this.cachingRsaPublicKey, this.allowPublicKeyRetrieval, this.useServerPrepStmts, + this.autocommit, this.prepareCacheSize, this.pamOtherPwd); } @@ -742,6 +757,17 @@ public Builder useServerPrepStmts(boolean useServerPrepStmts) { return this; } + /** + * Permit to indicate default autocommit value. Default value True. + * + * @param autocommit use autocommit + * @return this {@link Builder} + */ + public Builder autocommit(boolean autocommit) { + this.autocommit = autocommit; + return this; + } + /** * Permit pipelining (sending request before resolution of previous one). * diff --git a/src/main/java/org/mariadb/r2dbc/MariadbConnectionFactory.java b/src/main/java/org/mariadb/r2dbc/MariadbConnectionFactory.java index e0882696..7e2cd83e 100644 --- a/src/main/java/org/mariadb/r2dbc/MariadbConnectionFactory.java +++ b/src/main/java/org/mariadb/r2dbc/MariadbConnectionFactory.java @@ -76,13 +76,16 @@ private Mono doCreateConnection() { .flatMap( client -> { Mono waiting = Mono.empty(); - if (configuration.getSessionVariables() != null - && configuration.getSessionVariables().size() > 0) { + // only execute SET command if needed : + // - autocommit default value differ than option + // - session variable set + if ((configuration.getSessionVariables() != null + && configuration.getSessionVariables().size() > 0) + || client.isAutoCommit() != configuration.autocommit()) { waiting = setSessionVariables(client); } if (configuration.getIsolationLevel() == null) { - Mono isolationLevelMono = waiting.then(getIsolationLevel(client)); return isolationLevelMono .map(it -> new MariadbConnection(client, it, configuration)) @@ -124,18 +127,20 @@ public String toString() { } private Mono setSessionVariables(Client client) { - StringBuilder sql = new StringBuilder("SET "); - - Map sessionVariable = configuration.getSessionVariables(); - Iterator keys = sessionVariable.keySet().iterator(); - for (int i = 0; i < sessionVariable.size(); i++) { - if (i > 0) sql.append(","); - String key = keys.next(); - String value = sessionVariable.get(key); - if (value == null) - throw new IllegalArgumentException( - String.format("Session variable '%s' has no value", key)); - sql.append(key).append("=").append(value); + StringBuilder sql = + new StringBuilder("SET autocommit=" + (configuration.autocommit() ? "1" : "0")); + if (configuration.getSessionVariables() != null + && configuration.getSessionVariables().size() > 0) { + Map sessionVariable = configuration.getSessionVariables(); + Iterator keys = sessionVariable.keySet().iterator(); + for (int i = 0; i < sessionVariable.size(); i++) { + String key = keys.next(); + String value = sessionVariable.get(key); + if (value == null) + throw new IllegalArgumentException( + String.format("Session variable '%s' has no value", key)); + sql.append(",").append(key).append("=").append(value); + } } return new MariadbSimpleQueryStatement(client, sql.toString()).execute().last().then(); diff --git a/src/main/java/org/mariadb/r2dbc/MariadbConnectionFactoryProvider.java b/src/main/java/org/mariadb/r2dbc/MariadbConnectionFactoryProvider.java index 1db39d42..8d0d47fc 100644 --- a/src/main/java/org/mariadb/r2dbc/MariadbConnectionFactoryProvider.java +++ b/src/main/java/org/mariadb/r2dbc/MariadbConnectionFactoryProvider.java @@ -33,6 +33,7 @@ public final class MariadbConnectionFactoryProvider implements ConnectionFactory public static final Option CLIENT_SSL_CERT = Option.valueOf("clientSslCert"); public static final Option ALLOW_PIPELINING = Option.valueOf("allowPipelining"); public static final Option USE_SERVER_PREPARE = Option.valueOf("useServerPrepStmts"); + public static final Option AUTO_COMMIT = Option.valueOf("autoCommit"); public static final Option PREPARE_CACHE_SIZE = Option.valueOf("prepareCacheSize"); public static final Option SSL_MODE = Option.valueOf("sslMode"); public static final Option CONNECTION_ATTRIBUTES = Option.valueOf("connectionAttributes"); diff --git a/src/main/java/org/mariadb/r2dbc/SslMode.java b/src/main/java/org/mariadb/r2dbc/SslMode.java index 442e4278..0a13c4fd 100644 --- a/src/main/java/org/mariadb/r2dbc/SslMode.java +++ b/src/main/java/org/mariadb/r2dbc/SslMode.java @@ -21,5 +21,15 @@ public enum SslMode { ENABLE_TRUST, // Encryption, but no certificate and hostname validation (DEVELOPMENT ONLY) ENABLE_WITHOUT_HOSTNAME_VERIFICATION, // Encryption, certificates validation, BUT no hostname // validation - ENABLE, // Standard SSL use: Encryption, certificate validation and hostname validation + ENABLE; // Standard SSL use: Encryption, certificate validation and hostname validation + + public static SslMode from(String value) { + for (SslMode sslMode : values()) { + if (sslMode.name().equalsIgnoreCase(value)) { + return sslMode; + } + } + throw new IllegalArgumentException( + String.format("Wrong argument value '%s' for SslMode", value)); + } } diff --git a/src/main/java/org/mariadb/r2dbc/client/Client.java b/src/main/java/org/mariadb/r2dbc/client/Client.java index 2f0dc3b3..7bbe46b1 100644 --- a/src/main/java/org/mariadb/r2dbc/client/Client.java +++ b/src/main/java/org/mariadb/r2dbc/client/Client.java @@ -46,8 +46,6 @@ public interface Client { Mono sendSslRequest( SslRequestPacket sslRequest, MariadbConnectionConfiguration configuration); - ClientBase.LockAction getLockAction(); - boolean isAutoCommit(); boolean noBackslashEscapes(); @@ -61,4 +59,18 @@ Mono sendSslRequest( void sendNext(); PrepareCache getPrepareCache(); + + Mono beginTransaction(); + + Mono commitTransaction(); + + Mono rollbackTransaction(); + + Mono setAutoCommit(boolean autoCommit); + + Mono rollbackTransactionToSavepoint(String name); + + Mono releaseSavepoint(String name); + + Mono createSavepoint(String name); } diff --git a/src/main/java/org/mariadb/r2dbc/client/ClientBase.java b/src/main/java/org/mariadb/r2dbc/client/ClientBase.java index 10a55f5c..3acfd57f 100644 --- a/src/main/java/org/mariadb/r2dbc/client/ClientBase.java +++ b/src/main/java/org/mariadb/r2dbc/client/ClientBase.java @@ -29,6 +29,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Consumer; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import org.mariadb.r2dbc.ExceptionFactory; @@ -42,6 +43,7 @@ import org.mariadb.r2dbc.util.PrepareCache; import org.mariadb.r2dbc.util.constants.ServerStatus; import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSink; import reactor.core.publisher.Mono; import reactor.netty.Connection; import reactor.netty.tcp.TcpClient; @@ -59,7 +61,7 @@ public abstract class ClientBase implements Client { private final AtomicBoolean isClosed = new AtomicBoolean(false); private final MariadbPacketDecoder mariadbPacketDecoder; private final MariadbPacketEncoder mariadbPacketEncoder = new MariadbPacketEncoder(); - private volatile Context context; + protected volatile Context context; private final PrepareCache prepareCache; protected ClientBase(Connection connection, MariadbConnectionConfiguration configuration) { @@ -190,6 +192,138 @@ public Flux sendCommand(ClientMessage message, DecoderState initi public abstract Flux sendCommand( ClientMessage message, DecoderState initialState, String sql); + private Flux execute(Consumer> s) { + AtomicBoolean atomicBoolean = new AtomicBoolean(); + return Flux.create( + sink -> { + if (!isConnected()) { + sink.error( + new R2dbcNonTransientResourceException( + "Connection is close. Cannot send anything")); + return; + } + if (atomicBoolean.compareAndSet(false, true)) { + try { + lock.lock(); + s.accept(sink); + } finally { + lock.unlock(); + } + } + }); + } + + abstract void begin(FluxSink sink); + + abstract void executeWhenTransaction(FluxSink sink, String cmd); + + abstract void executeAutoCommit(FluxSink sink, boolean autoCommit); + + /** + * Specific implementation, to avoid executing BEGIN if already in transaction + * + * @return publisher + */ + public Mono beginTransaction() { + try { + lock.lock(); + return execute(sink -> begin(sink)) + .handle(ExceptionFactory.withSql("BEGIN")::handleErrorResponse) + .then(); + } finally { + lock.unlock(); + } + } + + /** + * Specific implementation, to avoid executing COMMIT if no transaction + * + * @return publisher + */ + public Mono commitTransaction() { + try { + lock.lock(); + return execute(sink -> executeWhenTransaction(sink, "COMMIT")) + .handle(ExceptionFactory.withSql("COMMIT")::handleErrorResponse) + .then(); + } finally { + lock.unlock(); + } + } + + /** + * Specific implementation, to avoid executing ROLLBACK if no transaction + * + * @return publisher + */ + public Mono rollbackTransaction() { + try { + lock.lock(); + return execute(sink -> executeWhenTransaction(sink, "ROLLBACK")) + .handle(ExceptionFactory.withSql("ROLLBACK")::handleErrorResponse) + .then(); + } finally { + lock.unlock(); + } + } + + /** + * Specific implementation, to avoid executing ROLLBACK TO TRANSACTION if no transaction + * + * @return publisher + */ + public Mono rollbackTransactionToSavepoint(String name) { + try { + lock.lock(); + String cmd = String.format("ROLLBACK TO SAVEPOINT `%s`", name.replace("`", "``")); + return execute(sink -> executeWhenTransaction(sink, cmd)) + .handle(ExceptionFactory.withSql(cmd)::handleErrorResponse) + .then(); + } finally { + lock.unlock(); + } + } + + public Mono releaseSavepoint(String name) { + try { + lock.lock(); + String cmd = String.format("RELEASE SAVEPOINT `%s`", name.replace("`", "``")); + return sendCommand(new QueryPacket(cmd)) + .handle(ExceptionFactory.withSql(cmd)::handleErrorResponse) + .then(); + } finally { + lock.unlock(); + } + } + + public Mono createSavepoint(String name) { + try { + lock.lock(); + String cmd = String.format("SAVEPOINT `%s`", name.replace("`", "``")); + return sendCommand(new QueryPacket(cmd)) + .handle(ExceptionFactory.withSql(cmd)::handleErrorResponse) + .then(); + } finally { + lock.unlock(); + } + } + + /** + * Specific implementation, to avoid changing autocommit mode if already in this autocommit mode + * + * @return publisher + */ + public Mono setAutoCommit(boolean autoCommit) { + try { + lock.lock(); + return execute(sink -> executeAutoCommit(sink, autoCommit)) + .handle(ExceptionFactory.withSql(null)::handleErrorResponse) + .then(); + } finally { + lock.unlock(); + } + } + @Override public Flux receive(DecoderState initialState) { return Flux.create( @@ -211,10 +345,6 @@ public void setContext(InitialHandshakePacket handshake) { mariadbPacketEncoder.setContext(context); } - public LockAction getLockAction() { - return new LockAction(); - } - /** * Get current server autocommit. * @@ -271,76 +401,4 @@ public PrepareCache getPrepareCache() { public String toString() { return "Client{isClosed=" + isClosed + ", context=" + context + '}'; } - - public class LockAction implements AutoCloseable { - public LockAction() { - lock.lock(); - } - - public Mono rollbackTransaction() { - if (!responseReceivers.isEmpty() - || (context.getServerStatus() & ServerStatus.IN_TRANSACTION) > 0) { - return exchange("ROLLBACK").then(); - } else { - logger.debug("Skipping savepoint release because no active transaction"); - return Mono.empty(); - } - } - - public Mono releaseSavepoint(String name) { - return exchange(String.format("RELEASE SAVEPOINT `%s`", name.replace("`", "``"))).then(); - } - - public Mono beginTransaction() { - if (!responseReceivers.isEmpty() - || (context.getServerStatus() & ServerStatus.IN_TRANSACTION) == 0) { - return exchange("BEGIN").then(); - } else { - logger.debug("Skipping begin transaction because already in transaction"); - return Mono.empty(); - } - } - - public Mono commitTransaction() { - if (!responseReceivers.isEmpty() - || (context.getServerStatus() & ServerStatus.IN_TRANSACTION) > 0) { - return exchange("COMMIT").then(); - } else { - logger.debug("Skipping commit transaction because no active transaction"); - return Mono.empty(); - } - } - - private Flux exchange(String sql) { - ExceptionFactory exceptionFactory = ExceptionFactory.withSql(sql); - return sendCommand(new QueryPacket(sql)).handle(exceptionFactory::handleErrorResponse); - } - - public Mono createSavepoint(String name) { - return exchange(String.format("SAVEPOINT `%s`", name.replace("`", "``"))).then(); - } - - public Mono rollbackTransactionToSavepoint(String name) { - if (!responseReceivers.isEmpty() - || (context.getServerStatus() & ServerStatus.IN_TRANSACTION) > 0) { - return exchange(String.format("ROLLBACK TO SAVEPOINT `%s`", name.replace("`", "``"))) - .then(); - } else { - logger.debug("Skipping rollback to savepoint: no active transaction"); - return Mono.empty(); - } - } - - public Mono setAutoCommit(boolean autoCommit) { - if (!responseReceivers.isEmpty() || autoCommit != isAutoCommit()) { - return exchange("SET autocommit=" + (autoCommit ? '1' : '0')).then(); - } - return Mono.empty(); - } - - @Override - public void close() { - lock.unlock(); - } - } } diff --git a/src/main/java/org/mariadb/r2dbc/client/ClientImpl.java b/src/main/java/org/mariadb/r2dbc/client/ClientImpl.java index 81e048ad..9464644b 100644 --- a/src/main/java/org/mariadb/r2dbc/client/ClientImpl.java +++ b/src/main/java/org/mariadb/r2dbc/client/ClientImpl.java @@ -24,16 +24,23 @@ import org.mariadb.r2dbc.message.client.ClientMessage; import org.mariadb.r2dbc.message.client.ExecutePacket; import org.mariadb.r2dbc.message.client.PreparePacket; +import org.mariadb.r2dbc.message.client.QueryPacket; import org.mariadb.r2dbc.message.server.ServerMessage; +import org.mariadb.r2dbc.util.constants.ServerStatus; import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSink; import reactor.core.publisher.Mono; import reactor.netty.Connection; import reactor.netty.resources.ConnectionProvider; import reactor.netty.tcp.TcpClient; +import reactor.util.Logger; +import reactor.util.Loggers; import reactor.util.concurrent.Queues; /** Client that only send query one by one. */ public final class ClientImpl extends ClientBase { + private static final Logger logger = Loggers.getLogger(ClientImpl.class); + public ClientImpl(Connection connection, MariadbConnectionConfiguration configuration) { super(connection, configuration); } @@ -95,6 +102,52 @@ public Flux sendCommand( }); } + protected void begin(FluxSink sink) { + if (this.responseReceivers.isEmpty()) { + if ((context.getServerStatus() & ServerStatus.IN_TRANSACTION) == 0) { + this.responseReceivers.add(new CmdElement(sink, DecoderState.QUERY_RESPONSE, "BEGIN")); + connection.channel().writeAndFlush(new QueryPacket("BEGIN")); + } else { + logger.debug("Skipping begin transaction because already in transaction"); + sink.complete(); + } + } else { + this.responseReceivers.add(new CmdElement(sink, DecoderState.QUERY_RESPONSE, "BEGIN")); + sendingQueue.add(new QueryPacket("BEGIN")); + } + } + + protected void executeAutoCommit(FluxSink sink, boolean autoCommit) { + String cmd = "SET autocommit=" + (autoCommit ? '1' : '0'); + if (this.responseReceivers.isEmpty()) { + if (autoCommit != isAutoCommit()) { + this.responseReceivers.add(new CmdElement(sink, DecoderState.QUERY_RESPONSE, cmd)); + connection.channel().writeAndFlush(new QueryPacket(cmd)); + } else { + logger.debug("Skipping autocommit since already in that state"); + sink.complete(); + } + } else { + this.responseReceivers.add(new CmdElement(sink, DecoderState.QUERY_RESPONSE, cmd)); + sendingQueue.add(new QueryPacket(cmd)); + } + } + + protected void executeWhenTransaction(FluxSink sink, String cmd) { + if (this.responseReceivers.isEmpty()) { + if ((context.getServerStatus() & ServerStatus.IN_TRANSACTION) > 0) { + this.responseReceivers.add(new CmdElement(sink, DecoderState.QUERY_RESPONSE, cmd)); + connection.channel().writeAndFlush(new QueryPacket(cmd)); + } else { + logger.debug(String.format("Skipping '%s' because no active transaction", cmd)); + sink.complete(); + } + } else { + this.responseReceivers.add(new CmdElement(sink, DecoderState.QUERY_RESPONSE, cmd)); + sendingQueue.add(new QueryPacket(cmd)); + } + } + public void sendNext() { lock.lock(); try { diff --git a/src/main/java/org/mariadb/r2dbc/client/ClientPipelineImpl.java b/src/main/java/org/mariadb/r2dbc/client/ClientPipelineImpl.java index 30d0fc56..27f57066 100644 --- a/src/main/java/org/mariadb/r2dbc/client/ClientPipelineImpl.java +++ b/src/main/java/org/mariadb/r2dbc/client/ClientPipelineImpl.java @@ -23,15 +23,22 @@ import org.mariadb.r2dbc.message.client.ClientMessage; import org.mariadb.r2dbc.message.client.ExecutePacket; import org.mariadb.r2dbc.message.client.PreparePacket; +import org.mariadb.r2dbc.message.client.QueryPacket; import org.mariadb.r2dbc.message.server.ServerMessage; +import org.mariadb.r2dbc.util.constants.ServerStatus; import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSink; import reactor.core.publisher.Mono; import reactor.netty.Connection; import reactor.netty.resources.ConnectionProvider; import reactor.netty.tcp.TcpClient; +import reactor.util.Logger; +import reactor.util.Loggers; /** Client that send queries pipelining (without waiting for result). */ public final class ClientPipelineImpl extends ClientBase { + private static final Logger logger = Loggers.getLogger(ClientPipelineImpl.class); + public ClientPipelineImpl(Connection connection, MariadbConnectionConfiguration configuration) { super(connection, configuration); } @@ -103,5 +110,38 @@ public Flux sendCommand( }); } + protected void begin(FluxSink sink) { + if (!responseReceivers.isEmpty() + || (context.getServerStatus() & ServerStatus.IN_TRANSACTION) == 0) { + this.responseReceivers.add(new CmdElement(sink, DecoderState.QUERY_RESPONSE, "BEGIN")); + connection.channel().writeAndFlush(new QueryPacket("BEGIN")); + } else { + logger.debug("Skipping begin transaction because already in transaction"); + sink.complete(); + } + } + + protected void executeAutoCommit(FluxSink sink, boolean autoCommit) { + String cmd = "SET autocommit=" + (autoCommit ? '1' : '0'); + if (this.responseReceivers.isEmpty() || autoCommit != isAutoCommit()) { + this.responseReceivers.add(new CmdElement(sink, DecoderState.QUERY_RESPONSE, cmd)); + connection.channel().writeAndFlush(new QueryPacket(cmd)); + } else { + logger.debug("Skipping autocommit since already in that state"); + sink.complete(); + } + } + + protected void executeWhenTransaction(FluxSink sink, String cmd) { + if (!responseReceivers.isEmpty() + || (context.getServerStatus() & ServerStatus.IN_TRANSACTION) > 0) { + this.responseReceivers.add(new CmdElement(sink, DecoderState.QUERY_RESPONSE, cmd)); + connection.channel().writeAndFlush(new QueryPacket(cmd)); + } else { + logger.debug(String.format("Skipping '%s' because no active transaction", cmd)); + sink.complete(); + } + } + public void sendNext() {} } diff --git a/src/main/java/org/mariadb/r2dbc/client/DecoderState.java b/src/main/java/org/mariadb/r2dbc/client/DecoderState.java index ebc88239..0f9291fc 100644 --- a/src/main/java/org/mariadb/r2dbc/client/DecoderState.java +++ b/src/main/java/org/mariadb/r2dbc/client/DecoderState.java @@ -222,7 +222,7 @@ public DecoderState next(MariadbPacketDecoder decoder) { public DecoderState decoder(short val, int len, long serverCapabilities) { switch (val) { case 254: - if ((serverCapabilities & Capabilities.CLIENT_DEPRECATE_EOF) == 0) { + if ((serverCapabilities & Capabilities.CLIENT_DEPRECATE_EOF) == 0 && len < 0xffffff) { return EOF_END; } else if (len < 0xffffff) { return OK_PACKET; @@ -255,7 +255,12 @@ public DecoderState next(MariadbPacketDecoder decoder) { PrepareResultPacket packet; public DecoderState decoder(short val, int len, long serverCapabilities) { - return this; + switch (val) { + case 255: // 0xFF + return ERROR; + default: + return this; + } } @Override diff --git a/src/main/java/org/mariadb/r2dbc/client/MariadbPacketDecoder.java b/src/main/java/org/mariadb/r2dbc/client/MariadbPacketDecoder.java index 495ef79e..68b5d810 100644 --- a/src/main/java/org/mariadb/r2dbc/client/MariadbPacketDecoder.java +++ b/src/main/java/org/mariadb/r2dbc/client/MariadbPacketDecoder.java @@ -89,8 +89,16 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List out) private void handleBuffer(ByteBuf packet, Sequencer sequencer) { if (cmdElement == null && !loadNextResponse()) { + char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); + char[] hexChars = new char[packet.readerIndex() * 2]; + for (int j = 0; j < packet.readerIndex(); j++) { + int v = packet.getByte(j) & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } throw new R2dbcNonTransientResourceException( - "unexpected message received when no command was send"); + String.format( + "unexpected message received when no command was send: 0x%s", new String(hexChars))); } state = diff --git a/src/main/java/org/mariadb/r2dbc/codec/list/BlobCodec.java b/src/main/java/org/mariadb/r2dbc/codec/list/BlobCodec.java index d4736e02..05ccc600 100644 --- a/src/main/java/org/mariadb/r2dbc/codec/list/BlobCodec.java +++ b/src/main/java/org/mariadb/r2dbc/codec/list/BlobCodec.java @@ -51,11 +51,11 @@ public boolean canDecode(ColumnDefinitionPacket column, Class type) { } @Override - public io.r2dbc.spi.Blob decodeText( + public Blob decodeText( ByteBuf buf, int length, ColumnDefinitionPacket column, - Class type) { + Class type) { switch (column.getType()) { case STRING: case VARCHAR: diff --git a/src/main/java/org/mariadb/r2dbc/util/DefaultHostnameVerifier.java b/src/main/java/org/mariadb/r2dbc/util/DefaultHostnameVerifier.java index c4903ff7..5a3ec605 100644 --- a/src/main/java/org/mariadb/r2dbc/util/DefaultHostnameVerifier.java +++ b/src/main/java/org/mariadb/r2dbc/util/DefaultHostnameVerifier.java @@ -153,11 +153,11 @@ private static String normalizedHostMsg(String normalizedHost) { return msg.toString(); } - private DefaultHostnameVerifier.SubjectAltNames getSubjectAltNames(X509Certificate cert) + private SubjectAltNames getSubjectAltNames(X509Certificate cert) throws CertificateParsingException { Collection> entries = cert.getSubjectAlternativeNames(); - DefaultHostnameVerifier.SubjectAltNames subjectAltNames = - new DefaultHostnameVerifier.SubjectAltNames(); + SubjectAltNames subjectAltNames = + new SubjectAltNames(); if (entries != null) { for (List entry : entries) { if (entry.size() >= 2) { @@ -168,8 +168,8 @@ private DefaultHostnameVerifier.SubjectAltNames getSubjectAltNames(X509Certifica if (altNameDns != null) { String normalizedSubjectAlt = altNameDns.toLowerCase(Locale.ROOT); subjectAltNames.add( - new DefaultHostnameVerifier.GeneralName( - normalizedSubjectAlt, DefaultHostnameVerifier.Extension.DNS)); + new GeneralName( + normalizedSubjectAlt, Extension.DNS)); } } @@ -177,8 +177,8 @@ private DefaultHostnameVerifier.SubjectAltNames getSubjectAltNames(X509Certifica String altNameIp = (String) entry.get(1); if (altNameIp != null) { subjectAltNames.add( - new DefaultHostnameVerifier.GeneralName( - altNameIp, DefaultHostnameVerifier.Extension.IP)); + new GeneralName( + altNameIp, Extension.IP)); } } } @@ -232,14 +232,14 @@ public void verify(String host, X509Certificate cert, long serverThreadId) throw // *********************************************************** // RFC 6125 : check Subject Alternative Name (SAN) // *********************************************************** - DefaultHostnameVerifier.SubjectAltNames subjectAltNames = getSubjectAltNames(cert); + SubjectAltNames subjectAltNames = getSubjectAltNames(cert); if (!subjectAltNames.isEmpty()) { // *********************************************************** // Host is IPv4 : Check corresponding entries in subject alternative names // *********************************************************** if (Utility.isIPv4(lowerCaseHost)) { - for (DefaultHostnameVerifier.GeneralName entry : subjectAltNames.getGeneralNames()) { + for (GeneralName entry : subjectAltNames.getGeneralNames()) { if (logger.isTraceEnabled()) { logger.trace( "Conn={}. IPv4 verification of hostname : type={} value={} to {}", @@ -249,7 +249,7 @@ public void verify(String host, X509Certificate cert, long serverThreadId) throw lowerCaseHost); } - if (entry.extension == DefaultHostnameVerifier.Extension.IP + if (entry.extension == Extension.IP && lowerCaseHost.equals(entry.value)) { return; } @@ -259,7 +259,7 @@ public void verify(String host, X509Certificate cert, long serverThreadId) throw // Host is IPv6 : Check corresponding entries in subject alternative names // *********************************************************** String normalisedHost = normaliseAddress(lowerCaseHost); - for (DefaultHostnameVerifier.GeneralName entry : subjectAltNames.getGeneralNames()) { + for (GeneralName entry : subjectAltNames.getGeneralNames()) { if (logger.isTraceEnabled()) { logger.trace( "Conn={}. IPv6 verification of hostname : type={} value={} to {}", @@ -269,7 +269,7 @@ public void verify(String host, X509Certificate cert, long serverThreadId) throw lowerCaseHost); } - if (entry.extension == DefaultHostnameVerifier.Extension.IP + if (entry.extension == Extension.IP && !Utility.isIPv4(entry.value) && normalisedHost.equals(normaliseAddress(entry.value))) { return; @@ -279,7 +279,7 @@ public void verify(String host, X509Certificate cert, long serverThreadId) throw // *********************************************************** // Host is not IP = DNS : Check corresponding entries in alternative subject names // *********************************************************** - for (DefaultHostnameVerifier.GeneralName entry : subjectAltNames.getGeneralNames()) { + for (GeneralName entry : subjectAltNames.getGeneralNames()) { if (logger.isTraceEnabled()) { logger.trace( "Conn={}. DNS verification of hostname : type={} value={} to {}", @@ -289,7 +289,7 @@ public void verify(String host, X509Certificate cert, long serverThreadId) throw lowerCaseHost); } - if (entry.extension == DefaultHostnameVerifier.Extension.DNS + if (entry.extension == Extension.DNS && matchDns(lowerCaseHost, entry.value.toLowerCase(Locale.ROOT))) { return; } @@ -353,9 +353,9 @@ private enum Extension { private class GeneralName { private final String value; - private final DefaultHostnameVerifier.Extension extension; + private final Extension extension; - public GeneralName(String value, DefaultHostnameVerifier.Extension extension) { + public GeneralName(String value, Extension extension) { this.value = value; this.extension = extension; } @@ -368,7 +368,7 @@ public String toString() { private class SubjectAltNames { - private final List generalNames = new ArrayList<>(); + private final List generalNames = new ArrayList<>(); @Override public String toString() { @@ -379,7 +379,7 @@ public String toString() { StringBuilder sb = new StringBuilder("SAN["); boolean first = true; - for (DefaultHostnameVerifier.GeneralName generalName : generalNames) { + for (GeneralName generalName : generalNames) { if (!first) { sb.append(","); } @@ -390,11 +390,11 @@ public String toString() { return sb.toString(); } - public List getGeneralNames() { + public List getGeneralNames() { return generalNames; } - public void add(DefaultHostnameVerifier.GeneralName generalName) { + public void add(GeneralName generalName) { generalNames.add(generalName); } diff --git a/src/test/java/org/mariadb/r2dbc/BaseConnectionTest.java b/src/test/java/org/mariadb/r2dbc/BaseConnectionTest.java index fea05132..24ee0abb 100644 --- a/src/test/java/org/mariadb/r2dbc/BaseConnectionTest.java +++ b/src/test/java/org/mariadb/r2dbc/BaseConnectionTest.java @@ -36,7 +36,9 @@ public class BaseConnectionTest extends BaseTest { public static TcpProxy proxy; private static Random rand = new Random(); public static final Boolean backslashEscape = - Boolean.valueOf(System.getProperty("NO_BACKSLASH_ESCAPES", "false")); + System.getenv("NO_BACKSLASH_ESCAPES") != null + ? Boolean.valueOf(System.getenv("NO_BACKSLASH_ESCAPES")) + : false; @BeforeAll public static void beforeAll() throws Exception { diff --git a/src/test/java/org/mariadb/r2dbc/BaseTest.java b/src/test/java/org/mariadb/r2dbc/BaseTest.java index 80cd7f70..d3e9dc06 100644 --- a/src/test/java/org/mariadb/r2dbc/BaseTest.java +++ b/src/test/java/org/mariadb/r2dbc/BaseTest.java @@ -47,6 +47,8 @@ public void afterEach(ExtensionContext extensionContext) throws Exception { @Override public void beforeEach(ExtensionContext extensionContext) throws Exception { initialTest = Instant.now(); + System.out.println( + " test : " + extensionContext.getTestMethod().get().getName() + " begin"); } } } diff --git a/src/test/java/org/mariadb/r2dbc/TestConfiguration.java b/src/test/java/org/mariadb/r2dbc/TestConfiguration.java index df251aa2..f22d224c 100644 --- a/src/test/java/org/mariadb/r2dbc/TestConfiguration.java +++ b/src/test/java/org/mariadb/r2dbc/TestConfiguration.java @@ -16,8 +16,12 @@ package org.mariadb.r2dbc; +import io.r2dbc.spi.ConnectionFactoryOptions; import java.io.IOException; import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.Properties; public class TestConfiguration { @@ -27,43 +31,73 @@ public class TestConfiguration { public static final String username; public static final String password; public static final String database; + public static final String other; + public static final MariadbConnectionConfiguration.Builder defaultBuilder; static { String defaultHost = "localhost"; String defaultPort = "3306"; - String defaultDatabase = "testj"; + String defaultDatabase = "testr2"; String defaultPassword = ""; String defaultUser = "root"; + String defaultOther = null; try (InputStream inputStream = BaseTest.class.getClassLoader().getResourceAsStream("conf.properties")) { Properties prop = new Properties(); prop.load(inputStream); - defaultHost = prop.getProperty("DB_HOST"); - defaultPort = prop.getProperty("DB_PORT"); - defaultDatabase = prop.getProperty("DB_DATABASE"); - defaultPassword = prop.getProperty("DB_PASSWORD"); - defaultUser = prop.getProperty("DB_USER"); + defaultHost = get("DB_HOST", prop); + defaultPort = get("DB_PORT", prop); + defaultDatabase = get("DB_DATABASE", prop); + defaultPassword = get("DB_PASSWORD", prop); + defaultUser = get("DB_USER", prop); + String val = System.getenv("TEST_REQUIRE_TLS"); + if ("1".equals(val)) { + String cert = System.getenv("TEST_DB_SERVER_CERT"); + defaultOther = "sslMode=enable&serverSslCert=" + cert; + } else { + defaultOther = get("DB_OTHER", prop); + } } catch (IOException io) { io.printStackTrace(); } + host = defaultHost; + port = Integer.parseInt(defaultPort); + database = defaultDatabase; + password = defaultPassword; + username = defaultUser; + other = defaultOther; + String encodedUser; + String encodedPwd; + try { + encodedUser = URLEncoder.encode(username, StandardCharsets.UTF_8.toString()); + encodedPwd = URLEncoder.encode(password, StandardCharsets.UTF_8.toString()); + } catch (UnsupportedEncodingException e) { + encodedUser = username; + encodedPwd = password; + } + String connString = + String.format( + "r2dbc:mariadb://%s:%s@%s:%s/%s%s", + encodedUser, + encodedPwd, + host, + port, + database, + other == null ? "" : "?" + other.replace("\n", "\\n")); - host = System.getProperty("TEST_HOST", defaultHost); - port = Integer.parseInt(System.getProperty("TEST_PORT", defaultPort)); - database = System.getProperty("TEST_DATABASE", defaultDatabase); - password = System.getProperty("TEST_PASSWORD", defaultPassword); - username = System.getProperty("TEST_USERNAME", defaultUser); + ConnectionFactoryOptions options = ConnectionFactoryOptions.parse(connString); + defaultBuilder = MariadbConnectionConfiguration.fromOptions(options); } - public static final MariadbConnectionConfiguration.Builder defaultBuilder = - MariadbConnectionConfiguration.builder() - .host(host) - .port(port) - .username(username) - .password(password) - .database(database); + private static String get(String name, Properties prop) { + String val = System.getenv("TEST_" + name); + if (val == null) val = System.getProperty("TEST_" + name); + if (val == null) val = prop.getProperty(name); + return val; + } public static final MariadbConnectionConfiguration defaultConf = defaultBuilder.build(); public static final MariadbConnectionFactory defaultFactory = diff --git a/src/test/java/org/mariadb/r2dbc/integration/BigResultSetTest.java b/src/test/java/org/mariadb/r2dbc/integration/BigResultSetTest.java index 112063d1..35c6e365 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/BigResultSetTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/BigResultSetTest.java @@ -52,7 +52,9 @@ public void beforeEach() { @Test void BigResultSet() { - Assumptions.assumeTrue(Boolean.parseBoolean(System.getProperty("RUN_LONG_TEST", "true"))); + Assumptions.assumeTrue( + System.getenv("RUN_LONG_TEST") == null + || !Boolean.parseBoolean(System.getenv("RUN_LONG_TEST"))); MariadbConnectionMetadata meta = sharedConn.getMetadata(); // sequence table requirement Assumptions.assumeTrue(meta.isMariaDBServer() && minVersion(10, 1, 0)); @@ -111,7 +113,7 @@ void multiPacketRow(MariadbConnection connection) { for (int i = 0; i < array19m.length; i++) { array19m[i] = (char) (0x30 + (i % 10)); } - + connection.beginTransaction().block(); connection .createStatement("INSERT INTO multiPacketRow VALUES (?, ?)") .bind(0, new String(array19m)) @@ -134,6 +136,7 @@ void multiPacketRow(MariadbConnection connection) { })) .blockLast() .toCharArray()); + connection.rollbackTransaction().block(); } public boolean checkMaxAllowedPacketMore20m(MariadbConnection connection) { diff --git a/src/test/java/org/mariadb/r2dbc/integration/ConfigurationTest.java b/src/test/java/org/mariadb/r2dbc/integration/ConfigurationTest.java index f5ba7d51..9b8b345d 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/ConfigurationTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/ConfigurationTest.java @@ -17,26 +17,47 @@ package org.mariadb.r2dbc.integration; import io.r2dbc.spi.*; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.time.Duration; +import java.util.HashMap; +import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mariadb.r2dbc.*; +import org.mariadb.r2dbc.api.MariadbConnection; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; public class ConfigurationTest extends BaseTest { @Test void usingOption() { + String encodedUser; + String encodedPwd; + try { + encodedUser = + URLEncoder.encode(TestConfiguration.username, StandardCharsets.UTF_8.toString()); + encodedPwd = URLEncoder.encode(TestConfiguration.password, StandardCharsets.UTF_8.toString()); + } catch (UnsupportedEncodingException e) { + encodedUser = TestConfiguration.username; + encodedPwd = TestConfiguration.password; + } + ConnectionFactory factory = ConnectionFactories.get( String.format( - "r2dbc:mariadb://%s:%s@%s:%s/%s", - TestConfiguration.username, - TestConfiguration.password, + "r2dbc:mariadb://%s:%s@%s:%s/%s%s", + encodedUser, + encodedPwd, TestConfiguration.host, TestConfiguration.port, - TestConfiguration.database)); + TestConfiguration.database, + TestConfiguration.other == null + ? "" + : "?" + TestConfiguration.other.replace("\n", "\\n"))); Connection connection = Mono.from(factory.create()).block(); Flux.from(connection.createStatement("SELECT * FROM myTable").execute()) .flatMap(r -> r.map((row, metadata) -> row.get(0, String.class))); @@ -176,6 +197,53 @@ void checkOptionsPerOption() { "No value found for user"); } + @Test + void autocommitValue() throws Exception { + MariadbConnectionConfiguration conf = TestConfiguration.defaultBuilder.clone().build(); + + Assertions.assertTrue(conf.autocommit()); + + MariadbConnection sharedConn = new MariadbConnectionFactory(conf).create().block(); + sharedConn + .createStatement("SELECT @@autocommit") + .execute() + .flatMap(r -> r.map((row, metadata) -> row.get(0, Integer.class))) + .as(StepVerifier::create) + .expectNext(1) + .verifyComplete(); + Assertions.assertTrue(sharedConn.isAutoCommit()); + sharedConn.close().block(); + + conf = TestConfiguration.defaultBuilder.clone().autocommit(false).build(); + Assertions.assertFalse(conf.autocommit()); + sharedConn = new MariadbConnectionFactory(conf).create().block(); + Assertions.assertFalse(sharedConn.isAutoCommit()); + sharedConn.createStatement("SET @@autocommit=0"); + sharedConn.close().block(); + + conf = TestConfiguration.defaultBuilder.clone().build(); + Assertions.assertTrue(conf.autocommit()); + sharedConn = new MariadbConnectionFactory(conf).create().block(); + Assertions.assertTrue(sharedConn.isAutoCommit()); + sharedConn.createStatement("SET @@autocommit=1"); + sharedConn.close().block(); + + Map sessionVariables = new HashMap<>(); + sessionVariables.put("net_read_timeout", "60"); + sessionVariables.put("wait_timeout", "2147483"); + + conf = + TestConfiguration.defaultBuilder + .clone() + .autocommit(false) + .sessionVariables(sessionVariables) + .build(); + Assertions.assertFalse(conf.autocommit()); + sharedConn = new MariadbConnectionFactory(conf).create().block(); + Assertions.assertFalse(sharedConn.isAutoCommit()); + sharedConn.close().block(); + } + @Test void confMinOption() { assertThrows( diff --git a/src/test/java/org/mariadb/r2dbc/integration/ConnectionTest.java b/src/test/java/org/mariadb/r2dbc/integration/ConnectionTest.java index be28f7da..6e3b22ae 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/ConnectionTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/ConnectionTest.java @@ -26,6 +26,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.mariadb.r2dbc.BaseConnectionTest; import org.mariadb.r2dbc.MariadbConnectionConfiguration; @@ -84,6 +85,11 @@ private void reInitLog() { @Test void connectionError() throws Exception { + Assumptions.assumeTrue( + !"maxscale".equals(System.getenv("srv")) + && !"skysql".equals(System.getenv("srv")) + && !"skysql-ha".equals(System.getenv("srv"))); + disableLog(); MariadbConnection connection = createProxyCon(); try { @@ -105,6 +111,11 @@ void connectionError() throws Exception { @Test void multipleCommandStack() throws Exception { + Assumptions.assumeTrue( + !"maxscale".equals(System.getenv("srv")) + && !"skysql".equals(System.getenv("srv")) + && !"skysql-ha".equals(System.getenv("srv"))); + disableLog(); MariadbConnection connection = createProxyCon(); Runnable runnable = () -> proxy.stop(); @@ -131,11 +142,16 @@ void multipleCommandStack() throws Exception { } finally { Thread.sleep(100); reInitLog(); + proxy.forceClose(); } } @Test void connectionWithoutErrorOnClose() throws Exception { + Assumptions.assumeTrue( + !"maxscale".equals(System.getenv("srv")) + && !"skysql".equals(System.getenv("srv")) + && !"skysql-ha".equals(System.getenv("srv"))); disableLog(); MariadbConnection connection = createProxyCon(); proxy.stop(); @@ -145,11 +161,15 @@ void connectionWithoutErrorOnClose() throws Exception { @Test void connectionDuringError() throws Exception { + Assumptions.assumeTrue( + !"maxscale".equals(System.getenv("srv")) + && !"skysql".equals(System.getenv("srv")) + && !"skysql-ha".equals(System.getenv("srv"))); disableLog(); MariadbConnection connection = createProxyCon(); - new java.util.Timer() + new Timer() .schedule( - new java.util.TimerTask() { + new TimerTask() { @Override public void run() { proxy.stop(); @@ -374,22 +394,11 @@ void queryAfterClose() throws Exception { .verify(); } - private void consume(io.r2dbc.spi.Connection connection) { + private void consume(Connection connection) { int loop = 100; int numberOfUserCol = 41; - io.r2dbc.spi.Statement statement = - connection.createStatement("select * FROM mysql.user LIMIT 1"); - - Flux lastOne; - lastOne = stat(statement, numberOfUserCol); - while (loop-- > 0) { - lastOne = lastOne.thenMany(stat(statement, numberOfUserCol)); - } - Object[] obj = lastOne.blockLast(); - } - - private Flux stat(io.r2dbc.spi.Statement statement, int numberOfUserCol) { - return Flux.from(statement.execute()) + Statement statement = connection.createStatement("select * FROM mysql.user LIMIT 1"); + Flux.from(statement.execute()) .flatMap( it -> it.map( @@ -399,7 +408,8 @@ private Flux stat(io.r2dbc.spi.Statement statement, int numberOfUserCo objs[i] = row.get(i); } return objs; - })); + })) + .blockLast(); } @Test @@ -504,7 +514,7 @@ public void run() { try { connection = factory.create().block(); int rnd = (int) (Math.random() * 1000); - io.r2dbc.spi.Statement statement = connection.createStatement("select " + rnd); + Statement statement = connection.createStatement("select " + rnd); BigInteger val = Flux.from(statement.execute()) .flatMap(it -> it.map((row, rowMetadata) -> row.get(0, BigInteger.class))) @@ -532,7 +542,7 @@ public ExecuteQueriesOnSameConnection(AtomicInteger i, MariadbConnection connect public void run() { try { int rnd = (int) (Math.random() * 1000); - io.r2dbc.spi.Statement statement = connection.createStatement("select " + rnd); + Statement statement = connection.createStatement("select " + rnd); BigInteger val = Flux.from(statement.execute()) .flatMap(it -> it.map((row, rowMetadata) -> row.get(0, BigInteger.class))) @@ -548,12 +558,23 @@ public void run() { @Test void getTransactionIsolationLevel() { - Assertions.assertEquals( - IsolationLevel.REPEATABLE_READ, sharedConn.getTransactionIsolationLevel()); - sharedConn.setTransactionIsolationLevel(IsolationLevel.READ_UNCOMMITTED).block(); - Assertions.assertEquals( - IsolationLevel.READ_UNCOMMITTED, sharedConn.getTransactionIsolationLevel()); - sharedConn.setTransactionIsolationLevel(IsolationLevel.REPEATABLE_READ).block(); + MariadbConnection connection = + new MariadbConnectionFactory(TestConfiguration.defaultBuilder.build()).create().block(); + try { + IsolationLevel defaultValue = IsolationLevel.REPEATABLE_READ; + + if ("skysql".equals(System.getenv("srv")) || "skysql-ha".equals(System.getenv("srv"))) { + defaultValue = IsolationLevel.READ_COMMITTED; + } + + Assertions.assertEquals(defaultValue, connection.getTransactionIsolationLevel()); + connection.setTransactionIsolationLevel(IsolationLevel.READ_UNCOMMITTED).block(); + Assertions.assertEquals( + IsolationLevel.READ_UNCOMMITTED, connection.getTransactionIsolationLevel()); + connection.setTransactionIsolationLevel(defaultValue).block(); + } finally { + connection.close().block(); + } } @Test @@ -644,14 +665,24 @@ void useSavePoint() { @Test void toStringTest() { - Assertions.assertTrue( - sharedConn - .toString() - .contains( - "MariadbConnection{client=Client{isClosed=false, " - + "context=ConnectionContext{") - && sharedConn + MariadbConnection connection = + new MariadbConnectionFactory(TestConfiguration.defaultBuilder.build()).create().block(); + try { + Assertions.assertTrue( + connection + .toString() + .contains( + "MariadbConnection{client=Client{isClosed=false, " + + "context=ConnectionContext{")); + if (!"skysql".equals(System.getenv("srv")) && !"skysql-ha".equals(System.getenv("srv"))) { + + Assertions.assertTrue( + connection .toString() .contains(", isolationLevel=IsolationLevel{sql='REPEATABLE READ'}}")); + } + } finally { + connection.close().block(); + } } } diff --git a/src/test/java/org/mariadb/r2dbc/integration/ErrorTest.java b/src/test/java/org/mariadb/r2dbc/integration/ErrorTest.java index bac4594e..874af8ef 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/ErrorTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/ErrorTest.java @@ -56,9 +56,13 @@ void queryTimeout() throws Exception { @Test void permissionDenied() throws Exception { sharedConn.createStatement("CREATE USER userWithoutRight").execute().blockLast(); - MariadbConnectionConfiguration conf = - TestConfiguration.defaultBuilder.clone().username("userWithoutRight").password("").build(); + TestConfiguration.defaultBuilder + .clone() + .allowPublicKeyRetrieval(true) + .username("userWithoutRight") + .password("") + .build(); new MariadbConnectionFactory(conf) .create() .as(StepVerifier::create) @@ -67,12 +71,13 @@ void permissionDenied() throws Exception { throwable instanceof R2dbcNonTransientResourceException && (throwable .getMessage() - .contains("Access denied for user 'userWithoutRight'@'%' to database"))) + .contains("Access denied for user 'userWithoutRight'"))) .verify(); conf = TestConfiguration.defaultBuilder .clone() + .allowPublicKeyRetrieval(true) .username("userWithoutRight") .password("wrongpassword") .build(); @@ -108,6 +113,9 @@ void dataIntegrity() throws Exception { @Test void rollbackException() { + Assumptions.assumeTrue( + !"skysql".equals(System.getenv("srv")) && !"skysql-ha".equals(System.getenv("srv"))); + MariadbConnection connection = null; MariadbConnection connection2 = null; try { diff --git a/src/test/java/org/mariadb/r2dbc/integration/MultiQueriesTest.java b/src/test/java/org/mariadb/r2dbc/integration/MultiQueriesTest.java index 2ebbf899..474a27a6 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/MultiQueriesTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/MultiQueriesTest.java @@ -18,6 +18,7 @@ import io.r2dbc.spi.R2dbcBadGrammarException; import java.util.Optional; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.mariadb.r2dbc.BaseConnectionTest; import org.mariadb.r2dbc.MariadbConnectionConfiguration; @@ -30,6 +31,8 @@ public class MultiQueriesTest extends BaseConnectionTest { @Test void multiQueryDefault() { + Assumptions.assumeTrue( + !"maxscale".equals(System.getenv("srv")) && !"skysql-ha".equals(System.getenv("srv"))); sharedConn .createStatement("SELECT 1; SELECT 'a'") .execute() @@ -59,6 +62,9 @@ void multiQueryEnable() throws Exception { @Test void multiQueryDisable() throws Exception { + Assumptions.assumeTrue( + !"maxscale".equals(System.getenv("srv")) && !"skysql-ha".equals(System.getenv("srv"))); + MariadbConnectionConfiguration conf = TestConfiguration.defaultBuilder.clone().allowMultiQueries(false).build(); MariadbConnection connection = new MariadbConnectionFactory(conf).create().block(); @@ -77,6 +83,9 @@ void multiQueryDisable() throws Exception { @Test void multiQueryWithParameterDefault() { + Assumptions.assumeTrue( + !"maxscale".equals(System.getenv("srv")) && !"skysql-ha".equals(System.getenv("srv"))); + sharedConn .createStatement("SELECT CAST(? as CHAR); SELECT ?") .bind(0, 1) @@ -110,6 +119,9 @@ void multiQueryWithParameterEnable() throws Exception { @Test void multiQueryWithParameterDisable() throws Exception { + Assumptions.assumeTrue( + !"maxscale".equals(System.getenv("srv")) && !"skysql-ha".equals(System.getenv("srv"))); + MariadbConnectionConfiguration conf = TestConfiguration.defaultBuilder.clone().allowMultiQueries(false).build(); MariadbConnection connection = new MariadbConnectionFactory(conf).create().block(); diff --git a/src/test/java/org/mariadb/r2dbc/integration/PrepareResultSetTest.java b/src/test/java/org/mariadb/r2dbc/integration/PrepareResultSetTest.java index 6834c06d..8dcc2abd 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/PrepareResultSetTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/PrepareResultSetTest.java @@ -100,7 +100,7 @@ void parameterLengthEncoded() { arr1024[i] = (char) ('a' + (i % 10)); } - char[] arr = new char[16000000]; + char[] arr = new char[16_000_000]; for (int i = 0; i < arr.length; i++) { arr[i] = (char) ('a' + (i % 10)); } @@ -142,7 +142,7 @@ void parameterLengthEncodedLong() { Assumptions.assumeFalse( "mariadb:10.1".equals(System.getenv("DB")) || "mysql:5.6".equals(System.getenv("DB"))); - char[] arr = new char[20000000]; + char[] arr = new char[20_000_000]; for (int i = 0; i < arr.length; i++) { arr[i] = (char) ('a' + (i % 10)); } @@ -154,6 +154,7 @@ void parameterLengthEncodedLong() { + "(t0 LONGTEXT) DEFAULT CHARSET=utf8mb4") .execute() .blockLast(); + sharedConnPrepare.beginTransaction().block(); sharedConnPrepare .createStatement("INSERT INTO parameterLengthEncodedLong VALUES (?)") .bind(0, val) @@ -162,10 +163,11 @@ void parameterLengthEncodedLong() { sharedConnPrepare .createStatement("SELECT * FROM parameterLengthEncodedLong") .execute() - .flatMap(r -> r.map((row, metadata) -> row.get(0, String.class))) + .flatMap(r -> r.map((row, metadata) -> row.get(0, String.class).length())) .as(StepVerifier::create) - .expectNext(val) + .expectNext(val.length()) .verifyComplete(); + sharedConnPrepare.commitTransaction().block(); } @Test @@ -506,54 +508,30 @@ void cacheReuse() throws Throwable { Object[] entriesArr = cache.entrySet().toArray(); switch ((int) i) { case 0: - Assertions.assertEquals( - "SELECT 0, ?=ServerPrepareResult{statementId=1, numColumns=2, numParams=1, closing=false, use=0, cached=true}", - entriesArr[0].toString()); + Assertions.assertTrue(entriesArr[0].toString().startsWith("SELECT 0")); prepareResults[0] = ((Map.Entry) entriesArr[0]).getValue(); break; case 1: - Assertions.assertEquals( - "SELECT 0, ?=ServerPrepareResult{statementId=1, numColumns=2, numParams=1, closing=false, use=0, cached=true}", - entriesArr[0].toString()); - Assertions.assertEquals( - "SELECT 1, ?=ServerPrepareResult{statementId=2, numColumns=2, numParams=1, closing=false, use=0, cached=true}", - entriesArr[1].toString()); + Assertions.assertTrue(entriesArr[0].toString().startsWith("SELECT 0")); + Assertions.assertTrue(entriesArr[1].toString().startsWith("SELECT 1")); prepareResults[1] = ((Map.Entry) entriesArr[1]).getValue(); break; case 2: - Assertions.assertEquals( - "SELECT 0, ?=ServerPrepareResult{statementId=1, numColumns=2, numParams=1, closing=false, use=0, cached=true}", - entriesArr[0].toString()); - Assertions.assertEquals( - "SELECT 1, ?=ServerPrepareResult{statementId=2, numColumns=2, numParams=1, closing=false, use=0, cached=true}", - entriesArr[1].toString()); - Assertions.assertEquals( - "SELECT 2, ?=ServerPrepareResult{statementId=3, numColumns=2, numParams=1, closing=false, use=0, cached=true}", - entriesArr[2].toString()); + Assertions.assertTrue(entriesArr[0].toString().startsWith("SELECT 0")); + Assertions.assertTrue(entriesArr[1].toString().startsWith("SELECT 1")); + Assertions.assertTrue(entriesArr[2].toString().startsWith("SELECT 2")); prepareResults[2] = ((Map.Entry) entriesArr[2]).getValue(); break; case 3: - Assertions.assertEquals( - "SELECT 2, ?=ServerPrepareResult{statementId=3, numColumns=2, numParams=1, closing=false, use=0, cached=true}", - entriesArr[0].toString()); - Assertions.assertEquals( - "SELECT 1, ?=ServerPrepareResult{statementId=2, numColumns=2, numParams=1, closing=false, use=0, cached=true}", - entriesArr[1].toString()); - Assertions.assertEquals( - "SELECT 3, ?=ServerPrepareResult{statementId=4, numColumns=2, numParams=1, closing=false, use=0, cached=true}", - entriesArr[2].toString()); + Assertions.assertTrue(entriesArr[0].toString().startsWith("SELECT 2")); + Assertions.assertTrue(entriesArr[1].toString().startsWith("SELECT 1")); + Assertions.assertTrue(entriesArr[2].toString().startsWith("SELECT 3")); prepareResults[3] = ((Map.Entry) entriesArr[2]).getValue(); break; case 4: - Assertions.assertEquals( - "SELECT 1, ?=ServerPrepareResult{statementId=2, numColumns=2, numParams=1, closing=false, use=0, cached=true}", - entriesArr[0].toString()); - Assertions.assertEquals( - "SELECT 3, ?=ServerPrepareResult{statementId=4, numColumns=2, numParams=1, closing=false, use=0, cached=true}", - entriesArr[1].toString()); - Assertions.assertEquals( - "SELECT 4, ?=ServerPrepareResult{statementId=5, numColumns=2, numParams=1, closing=false, use=0, cached=true}", - entriesArr[2].toString()); + Assertions.assertTrue(entriesArr[0].toString().startsWith("SELECT 1")); + Assertions.assertTrue(entriesArr[1].toString().startsWith("SELECT 3")); + Assertions.assertTrue(entriesArr[2].toString().startsWith("SELECT 4")); prepareResults[4] = ((Map.Entry) entriesArr[2]).getValue(); break; } @@ -563,25 +541,24 @@ void cacheReuse() throws Throwable { } } - Assertions.assertEquals( - "ServerPrepareResult{statementId=1, numColumns=2, numParams=1, closing=true, use=0, cached=false}", - prepareResults[0].toString()); - Assertions.assertEquals( - "ServerPrepareResult{statementId=2, numColumns=2, numParams=1, closing=false, use=0, cached=true}", - prepareResults[1].toString()); - Assertions.assertEquals( - "ServerPrepareResult{statementId=3, numColumns=2, numParams=1, closing=true, use=0, cached=false}", - prepareResults[2].toString()); - Assertions.assertEquals( - "ServerPrepareResult{statementId=4, numColumns=2, numParams=1, closing=false, use=0, cached=true}", - prepareResults[3].toString()); - Assertions.assertEquals( - "ServerPrepareResult{statementId=5, numColumns=2, numParams=1, closing=false, use=0, cached=true}", - prepareResults[4].toString()); + Assertions.assertTrue( + prepareResults[0].toString().contains("closing=true, use=0, cached=false}")); + Assertions.assertTrue( + prepareResults[1].toString().contains("closing=false, use=0, cached=true}")); + Assertions.assertTrue( + prepareResults[2].toString().contains("closing=true, use=0, cached=false}")); + Assertions.assertTrue( + prepareResults[3].toString().contains("closing=false, use=0, cached=true}")); + Assertions.assertTrue( + prepareResults[4].toString().contains("closing=false, use=0, cached=true}")); List endingStatus = prepareInfo(connection); // Com_stmt_prepare - Assertions.assertEquals("5", endingStatus.get(1), endingStatus.get(1)); + if (!"maxscale".equals(System.getenv("srv")) + && !"skysql-ha".equals(System.getenv("srv")) + && (isMariaDBServer() || !minVersion(8, 0, 0))) { + Assertions.assertEquals("5", endingStatus.get(1), endingStatus.get(1)); + } } finally { connection.close().block(); diff --git a/src/test/java/org/mariadb/r2dbc/integration/RowMetadataTest.java b/src/test/java/org/mariadb/r2dbc/integration/RowMetadataTest.java index 65265f8f..a1d37106 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/RowMetadataTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/RowMetadataTest.java @@ -97,12 +97,12 @@ void rowMeta() { ColumnDefinitionPacket t1Meta = (ColumnDefinitionPacket) colMeta.getNativeTypeMetadata(); assertEquals( - System.getProperty("TEST_DATABASE", "testj"), t1Meta.getSchema()); + System.getProperty("TEST_DATABASE", "testr2"), t1Meta.getSchema()); assertEquals("t1Alias", t1Meta.getColumnAlias()); assertEquals("t1", t1Meta.getColumn()); assertEquals("rowmeta", t1Meta.getTable()); assertEquals("rowMetaAlias", t1Meta.getTableAlias()); - assertEquals(224, t1Meta.getCharset()); + assertTrue(t1Meta.getCharset() == 224 || t1Meta.getCharset() == 45); assertEquals(256, t1Meta.getDisplaySize()); assertFalse(t1Meta.isBinary()); assertFalse(t1Meta.isBlob()); @@ -132,7 +132,7 @@ void rowMeta() { ColumnDefinitionPacket t2Meta = (ColumnDefinitionPacket) colMeta.getNativeTypeMetadata(); assertEquals( - System.getProperty("TEST_DATABASE", "testj"), t2Meta.getSchema()); + System.getProperty("TEST_DATABASE", "testr2"), t2Meta.getSchema()); assertEquals("t2", t2Meta.getColumnAlias()); assertEquals("t2", t2Meta.getColumn()); assertEquals("rowmeta", t2Meta.getTable()); diff --git a/src/test/java/org/mariadb/r2dbc/integration/TlsTest.java b/src/test/java/org/mariadb/r2dbc/integration/TlsTest.java index 279b9435..a1acfda0 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/TlsTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/TlsTest.java @@ -47,9 +47,9 @@ public static void before2() { clientSslCert = System.getenv("TEST_CLIENT_SSL_CERT"); clientSslKey = System.getenv("TEST_CLIENT_KEY"); sslPort = - System.getProperty("sslPort") == null || System.getProperty("sslPort").isEmpty() + System.getenv("SSLPORT") == null || System.getenv("SSLPORT").isEmpty() ? TestConfiguration.port - : Integer.valueOf(System.getProperty("sslPort")); + : Integer.valueOf(System.getenv("SSLPORT")); // try default if not present if (serverSslCert == null) { File sslDir = new File(System.getProperty("user.dir") + "/../ssl"); @@ -91,6 +91,10 @@ public static void before2() { @Test void defaultHasNoSSL() throws Exception { + Assumptions.assumeTrue( + !"maxscale".equals(System.getenv("srv")) + && !"skysql".equals(System.getenv("srv")) + && !"skysql-ha".equals(System.getenv("srv"))); Assumptions.assumeTrue(haveSsl(sharedConn)); sharedConn .createStatement("SHOW STATUS like 'Ssl_version'") @@ -107,6 +111,8 @@ void defaultHasNoSSL() throws Exception { @Test void trustValidation() throws Exception { + Assumptions.assumeTrue( + !"maxscale".equals(System.getenv("srv")) && !"skysql-ha".equals(System.getenv("srv"))); Assumptions.assumeTrue(haveSsl(sharedConn)); MariadbConnectionConfiguration conf = TestConfiguration.defaultBuilder @@ -174,6 +180,10 @@ void wrongCertificateFiles() throws Exception { @Test void trustForceProtocol() throws Exception { + Assumptions.assumeTrue( + !"maxscale".equals(System.getenv("srv")) + && !"skysql".equals(System.getenv("srv")) + && !"skysql-ha".equals(System.getenv("srv"))); String trustProtocol = minVersion(8, 0, 0) ? "TLSv1.2" : "TLSv1.1"; Assumptions.assumeTrue(haveSsl(sharedConn)); MariadbConnectionConfiguration conf = @@ -239,6 +249,10 @@ private static String readLine(String filePath) throws IOException { @Test void fullWithoutServerCert() throws Exception { + Assumptions.assumeTrue( + !"maxscale".equals(System.getenv("srv")) + && !"skysql".equals(System.getenv("srv")) + && !"skysql-ha".equals(System.getenv("srv"))); Assumptions.assumeTrue(haveSsl(sharedConn)); assertThrows( R2dbcTransientResourceException.class, @@ -313,7 +327,9 @@ void fullValidation() throws Exception { @Test void fullMutualWithoutClientCerts() throws Exception { Assumptions.assumeTrue( - System.getenv("TRAVIS") != null && System.getenv("MAXSCALE_VERSION") == null); + System.getenv("TRAVIS") != null + && !"maxscale".equals(System.getenv("srv")) + && !"skysql-ha".equals(System.getenv("srv"))); Assumptions.assumeTrue(haveSsl(sharedConn)); Assumptions.assumeTrue(serverSslCert != null && clientSslCert != null & clientSslKey != null); MariadbConnectionConfiguration conf = diff --git a/src/test/java/org/mariadb/r2dbc/integration/TransactionTest.java b/src/test/java/org/mariadb/r2dbc/integration/TransactionTest.java new file mode 100644 index 00000000..088bf4bc --- /dev/null +++ b/src/test/java/org/mariadb/r2dbc/integration/TransactionTest.java @@ -0,0 +1,236 @@ +/* + * Copyright 2020 MariaDB Ab. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mariadb.r2dbc.integration; + +import io.r2dbc.spi.*; +import java.net.URL; +import java.util.*; +import org.junit.jupiter.api.*; +import org.mariadb.r2dbc.BaseConnectionTest; +import org.mariadb.r2dbc.api.MariadbConnection; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +public class TransactionTest extends BaseConnectionTest { + private static String insertCmd = + "INSERT INTO `users` (`first_name`, `last_name`, `email`) VALUES ('MariaDB', 'Row', 'mariadb@test.com')"; + + @BeforeAll + public static void before2() { + drop(); + sharedConn + .createStatement( + "CREATE TABLE `users` (\n" + + " `id` int(11) NOT NULL AUTO_INCREMENT,\n" + + " `first_name` varchar(255) COLLATE utf16_slovak_ci NOT NULL,\n" + + " `last_name` varchar(255) COLLATE utf16_slovak_ci NOT NULL,\n" + + " `email` varchar(255) COLLATE utf16_slovak_ci NOT NULL,\n" + + " PRIMARY KEY (`id`)\n" + + ")") + .execute() + .blockLast(); + } + + @BeforeEach + public void beforeEch() { + sharedConn.createStatement("TRUNCATE TABLE `users`").execute().blockLast(); + } + + @AfterAll + public static void drop() { + sharedConn.createStatement("DROP TABLE IF EXISTS `users`").execute().blockLast(); + } + + @Test + void commit() { + MariadbConnection conn = factory.create().block(); + + conn.beginTransaction() + .thenMany(conn.createStatement(insertCmd).execute()) + .concatWith(Flux.from(conn.commitTransaction()).then(Mono.empty())) + .onErrorResume(err -> Flux.from(conn.rollbackTransaction()).then(Mono.empty())) + .blockLast(); + checkInserted(conn, 1); + conn.close(); + } + + @Test + void multipleBegin() { + MariadbConnection conn = factory.create().block(); + // must issue only one begin command + conn.beginTransaction().thenMany(conn.beginTransaction()).blockLast(); + conn.beginTransaction().block(); + conn.close(); + } + + @Test + void commitWithoutTransaction() { + // must issue no commit command + sharedConn.commitTransaction().thenMany(sharedConn.commitTransaction()).blockLast(); + sharedConn.commitTransaction().block(); + } + + @Test + void rollbackWithoutTransaction() { + // must issue no commit command + sharedConn.rollbackTransaction().thenMany(sharedConn.rollbackTransaction()).blockLast(); + sharedConn.rollbackTransaction().block(); + } + + @Test + void createSavepoint() { + // must issue multiple savepoints + sharedConn.createSavepoint("t").thenMany(sharedConn.createSavepoint("t")).blockLast(); + sharedConn.createSavepoint("t").block(); + } + + @Test + void rollback() { + MariadbConnection conn = factory.create().block(); + + conn.beginTransaction() + .thenMany(conn.createStatement(insertCmd).execute()) + .onErrorResume(err -> Flux.from(conn.rollbackTransaction()).then(Mono.empty())) + .blockLast(); + conn.rollbackTransaction().block(); + checkInserted(conn, 0); + conn.close(); + } + + @Test + void rollbackPipelining() { + MariadbConnection conn = factory.create().block(); + + conn.beginTransaction() + .thenMany(conn.createStatement(insertCmd).execute()) + .concatWith(Flux.from(conn.rollbackTransaction()).then(Mono.empty())) + .onErrorResume(err -> Flux.from(conn.rollbackTransaction()).then(Mono.empty())) + .blockLast(); + checkInserted(conn, 0); + conn.close(); + } + + @Test + void releaseSavepoint() throws Exception { + String url = + "http://secure:pwd@hist:3306/testr2?sslMode=ENABLE&serverSslCert=-----BEGIN CERTIFICATE-----\n" + + "MIIELzCCAxegAwIBAgIUNz1kWFjbLVDWVCBnEAsf95UPMS8wDQYJKoZIhvcNAQEL\n" + + "BQAwQTEQMA4GA1UEChMHTWFyaWFEQjEPMA0GA1UECxMGU2t5U1FMMRwwGgYDVQQD\n" + + "ExNyb290LXBraS5za3lzcWwubmV0MB4XDTE5MDkwNTE4MjMxMVoXDTI5MDkwMjE4\n" + + "MjM0MFowQTEQMA4GA1UEChMHTWFyaWFEQjEPMA0GA1UECxMGU2t5U1FMMRwwGgYD\n" + + "VQQDExNyb290LXBraS5za3lzcWwubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" + + "MIIBCgKCAQEA0orsl93gB0MZuiby/4QmcmK2OrS/hEcDI/AN+Dpn9c3JKeEVp2OD\n" + + "sft47OHGwdgyagBtNV6zZgOc6IOnwt+rGDmrmiuxHkf/XWV+y66skWAtMyM7ycCL\n" + + "J3z5dO6xaZvYKJyhPcnx2NROEAJrkdVfoyJCtCElDDdRrknXWLPfZrph8E7I2mDP\n" + + "SV8ZF4wdxbU7oHKM4CoTRgXQnCDq2Wv8OLZr4Mq224nSmEJK+cXRwKqbFUvuiSco\n" + + "bTBnJjyeKldqJ/lCRwu9fU6fBHFuBNUEvZBzavt0B8SYi/l22wYHxlpOslowTaG4\n" + + "Lh8Nj79PP7rsy44hHvOBGc/ZsKIGCDOIMwIDAQABo4IBHTCCARkwDgYDVR0PAQH/\n" + + "BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHAIHx8QSWENuYb/xmuh\n" + + "17S2nnXWMB8GA1UdIwQYMBaAFHAIHx8QSWENuYb/xmuh17S2nnXWMEAGCCsGAQUF\n" + + "BwEBBDQwMjAwBggrBgEFBQcwAoYkaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3Br\n" + + "aV9yb290L2NhMB4GA1UdEQQXMBWCE3Jvb3QtcGtpLnNreXNxbC5uZXQwHAYDVR0e\n" + + "AQH/BBIwEKAOMAyCCnNreXNxbC5uZXQwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDov\n" + + "LzEyNy4wLjAuMTo4MjAwL3YxL3BraV9yb290L2NybDANBgkqhkiG9w0BAQsFAAOC\n" + + "AQEAfwo8ZW666UjHJ4DY+M9tDgRwFwFd7v3EBhLrGvkD+CWaiJIS9RnWwE0gn9SU\n" + + "syBvRn3PrjsveCR3cIjqAzUplOyMMvvJ77E8rzfQEwOhHbATyKNQG32KaitCdEBP\n" + + "v0XDb7SBw2eKQxdahMcT5yxh9DkCizTXE8usZIiW+V9FVcEPPNia4d9ZMlmLWMcP\n" + + "pZlxE4W5ngU6iCN7PJ3aeKrk4Y1PM36XJ11f5pouMULUvqbjepa/R1KJt27OSbrJ\n" + + "RjHDa+s0AljgPZDl7KqQOOA5hrNT1Om+5IVs+uAbY7mWQC2GwYlFsg5laqWf7SC0\n" + + "hPvVLDb8GaRK6LA4PCROZwiM9g==\n" + + "-----END CERTIFICATE-----\n" + + "-----BEGIN CERTIFICATE-----\n" + + "MIID4jCCAsqgAwIBAgIUB9abD+hn9+dtYuqwkA3Rj5ZrPgUwDQYJKoZIhvcNAQEL\n" + + "BQAwQTEQMA4GA1UEChMHTWFyaWFEQjEPMA0GA1UECxMGU2t5U1FMMRwwGgYDVQQD\n" + + "ExNyb290LXBraS5za3lzcWwubmV0MB4XDTE5MDkwNTE4MjMxOFoXDTI5MDkwMjE4\n" + + "MjM0OFowGTEXMBUGA1UEAxMOcGtpLnNreXNxbC5uZXQwggEiMA0GCSqGSIb3DQEB\n" + + "AQUAA4IBDwAwggEKAoIBAQDYov+F2ijXdIiZ0AuX4fAJ6KQ16zb4mQ2qgsrO02yW\n" + + "kF3EJV6/XQO0WqGok4SjcvLBLuSsQBFahtgB70d/YZ+PBUrwzzmgWa3Ga+GuzKl6\n" + + "O2QI8vu7l8D0esJe7mY4KsAwNIvMUAdqUUCgB01KmCIwWoVqN1h65dX1qOf1N6qk\n" + + "f+rXeFKBGoDq/DM6zR90irpYBt2guE5iZd5r63JMXANlmh44IWxEswBqAa4B+GlY\n" + + "7m7Psk9E+i4rexN45+815SLnHr86y3PNUlFfzgfQwXCLVPqSPGfNzyEz8MZUVYQv\n" + + "zg+hZmrTe9nCekMi3hk97Dh1x40Y1rSTtbQjUu2SvJepAgMBAAGjgfkwgfYwDgYD\n" + + "VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLwrAxEtsiHF\n" + + "Oz/dRW65RxwIHHVmMB8GA1UdIwQYMBaAFHAIHx8QSWENuYb/xmuh17S2nnXWMEAG\n" + + "CCsGAQUFBwEBBDQwMjAwBggrBgEFBQcwAoYkaHR0cDovLzEyNy4wLjAuMTo4MjAw\n" + + "L3YxL3BraV9yb290L2NhMBkGA1UdEQQSMBCCDnBraS5za3lzcWwubmV0MDYGA1Ud\n" + + "HwQvMC0wK6ApoCeGJWh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2lfcm9vdC9j\n" + + "cmwwDQYJKoZIhvcNAQELBQADggEBAAZZKyFT+mVwuafkBOBYqXb/dCPdqbUnGBic\n" + + "E2dRK0sxKYbeq7I3lo95UXrtfNBEMY740ZJzUwi6whDUMGNMoV0yFRPHPYvmopC5\n" + + "wCUA62pPuvHEqwo7HSuO3TBmt5x0b2e9R0gJ535GZSTQI+ArseUwn5IJ2v/BUIRJ\n" + + "xjAMwRmM9TOWcK6VLEBZoHXEzENrBLHr0fKhVyJhhuQV+xeEVY28odwzwH85AyUk\n" + + "1lrAIzuz3YCDHtjL449U+hdz/2tytI1KXJscm/mrAhtgUjQnKCY0fFkJESL+TDwX\n" + + "Sdb13rs8ZQvwanpcOt+Kg86O/vz2P5JLC8fK1L4aUilt0X5b8gc=\n" + + "-----END CERTIFICATE-----"; + URL r = new URL(url); + MariadbConnection conn = factory.create().block(); + conn.setAutoCommit(false).block(); + conn.createStatement(insertCmd) + .execute() + .thenMany(conn.createSavepoint("mySavePoint")) + .thenMany(conn.createStatement(insertCmd).execute()) + .concatWith(Flux.from(conn.releaseSavepoint("mySavePoint")).then(Mono.empty())) + .onErrorResume(err -> Flux.from(conn.rollbackTransaction()).then(Mono.empty())) + .blockLast(); + checkInserted(conn, 2); + conn.rollbackTransaction().block(); + conn.close(); + } + + @Test + void rollbackSavepoint() { + MariadbConnection conn = factory.create().block(); + conn.setAutoCommit(false).block(); + conn.createStatement(insertCmd) + .execute() + .thenMany(conn.createSavepoint("mySavePoint")) + .thenMany(conn.createStatement(insertCmd).execute()) + .onErrorResume(err -> Flux.from(conn.rollbackTransaction()).then(Mono.empty())) + .blockLast(); + conn.rollbackTransactionToSavepoint("mySavePoint").block(); + checkInserted(conn, 1); + conn.rollbackTransaction().block(); + conn.close(); + } + + @Test + void rollbackSavepointPipelining() { + MariadbConnection conn = factory.create().block(); + conn.setAutoCommit(false).block(); + conn.createStatement(insertCmd) + .execute() + .thenMany(conn.createSavepoint("mySavePoint")) + .thenMany(conn.createStatement(insertCmd).execute()) + .concatWith( + Flux.from(conn.rollbackTransactionToSavepoint("mySavePoint")).then(Mono.empty())) + .onErrorResume(err -> Flux.from(conn.rollbackTransaction()).then(Mono.empty())) + .blockLast(); + checkInserted(conn, 1); + conn.rollbackTransaction().block(); + conn.close(); + } + + private Mono checkInserted(MariadbConnection conn, int expectedValue) { + conn.createStatement("SELECT count(*) FROM `users`") + .execute() + .flatMap(r -> r.map((row, metadata) -> row.get(0, Integer.class))) + .as(StepVerifier::create) + .expectNext(expectedValue) + .verifyComplete(); + return Mono.empty(); + } +} diff --git a/src/test/java/org/mariadb/r2dbc/integration/authentication/Ed25519PluginTest.java b/src/test/java/org/mariadb/r2dbc/integration/authentication/Ed25519PluginTest.java index f8d28fa3..3f8d0107 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/authentication/Ed25519PluginTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/authentication/Ed25519PluginTest.java @@ -56,9 +56,13 @@ public static void before2() { .blockLast(); } sharedConn - .createStatement("GRANT ALL on *.* to verificationEd25519AuthPlugin") + .createStatement( + "GRANT SELECT on `" + + TestConfiguration.database + + "`.* to verificationEd25519AuthPlugin") .execute() .blockLast(); + sharedConn.createStatement("FLUSH PRIVILEGES").execute().blockLast(); } } @@ -77,6 +81,8 @@ public static void after2() { @Test public void verificationEd25519AuthPlugin() throws Throwable { + Assumptions.assumeTrue( + !"maxscale".equals(System.getenv("srv")) && !"skysql-ha".equals(System.getenv("srv"))); MariadbConnectionMetadata meta = sharedConn.getMetadata(); Assumptions.assumeTrue(meta.isMariaDBServer() && meta.minVersion(10, 2, 0)); diff --git a/src/test/java/org/mariadb/r2dbc/integration/authentication/PamPluginTest.java b/src/test/java/org/mariadb/r2dbc/integration/authentication/PamPluginTest.java index 1d0ae13a..3d81f175 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/authentication/PamPluginTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/authentication/PamPluginTest.java @@ -32,23 +32,32 @@ public void pamAuthPlugin() throws Throwable { // only test on travis, because only work on Unix-like operating systems. // /etc/pam.d/mariadb pam configuration is created beforehand Assumptions.assumeTrue( - System.getenv("TRAVIS") != null && System.getenv("MAXSCALE_VERSION") == null); + System.getenv("TRAVIS") != null + && System.getenv("TEST_PAM_USER") != null + && !"maxscale".equals(System.getenv("srv")) + && !"skysql".equals(System.getenv("srv")) + && !"mariadb-es".equals(System.getenv("srv")) + && !"skysql-ha".equals(System.getenv("srv"))); Assumptions.assumeTrue(isMariaDBServer()); - + String pamUser = System.getenv("TEST_PAM_USER"); sharedConn.createStatement("INSTALL PLUGIN pam SONAME 'auth_pam'").execute().blockLast(); - sharedConn.createStatement("DROP USER IF EXISTS 'testPam'@'%'").execute().blockLast(); + sharedConn.createStatement("DROP USER IF EXISTS '" + pamUser + "'@'%'").execute().blockLast(); sharedConn - .createStatement("CREATE USER 'testPam'@'%' IDENTIFIED VIA pam USING 'mariadb'") + .createStatement("CREATE USER '" + pamUser + "'@'%' IDENTIFIED VIA pam USING 'mariadb'") .execute() .blockLast(); sharedConn - .createStatement("GRANT SELECT ON *.* TO 'testPam'@'%' IDENTIFIED VIA pam") + .createStatement("GRANT SELECT ON *.* TO '" + pamUser + "'@'%' IDENTIFIED VIA pam") .execute() .blockLast(); sharedConn.createStatement("FLUSH PRIVILEGES").execute().blockLast(); MariadbConnectionConfiguration conf = - TestConfiguration.defaultBuilder.clone().username("testPam").password("myPwd").build(); + TestConfiguration.defaultBuilder + .clone() + .username(System.getenv("TEST_PAM_USER")) + .password(System.getenv("TEST_PAM_PWD")) + .build(); MariadbConnection connection = new MariadbConnectionFactory(conf).create().block(); connection.close().block(); } diff --git a/src/test/java/org/mariadb/r2dbc/integration/authentication/Sha256PluginTest.java b/src/test/java/org/mariadb/r2dbc/integration/authentication/Sha256PluginTest.java index 9bf5dba9..8d1c029d 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/authentication/Sha256PluginTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/authentication/Sha256PluginTest.java @@ -17,6 +17,7 @@ package org.mariadb.r2dbc.integration.authentication; import io.r2dbc.spi.R2dbcNonTransientResourceException; +import java.io.File; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; @@ -32,28 +33,57 @@ public class Sha256PluginTest extends BaseConnectionTest { private static String cachingRsaPublicKey; private static boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win"); + private static boolean validPath(String path) { + if (path == null) return false; + try { + File f = new File(path); + return f.exists(); + } catch (Exception e) { + // eat + } + return false; + } + @BeforeAll public static void init() throws Exception { Assumptions.assumeTrue(!isMariaDBServer() && minVersion(5, 7, 0)); rsaPublicKey = System.getProperty("rsaPublicKey"); - if (rsaPublicKey == null && minVersion(8, 0, 0)) { + if (!validPath(rsaPublicKey) && minVersion(8, 0, 0)) { rsaPublicKey = sharedConn .createStatement("SELECT @@caching_sha2_password_public_key_path") .execute() .flatMap(r -> r.map((row, metadata) -> row.get(0, String.class))) .blockLast(); + if (!validPath(rsaPublicKey)) { + rsaPublicKey = System.getenv("TEST_DB_RSA_PUBLIC_KEY"); + if (!validPath(rsaPublicKey)) { + File sslDir = new File(System.getProperty("user.dir") + "/ssl"); + if (sslDir.exists() && sslDir.isDirectory()) { + rsaPublicKey = System.getProperty("user.dir") + "/ssl/public.key"; + } else rsaPublicKey = null; + } + } } cachingRsaPublicKey = System.getProperty("cachingRsaPublicKey"); - if (cachingRsaPublicKey == null) { + if (!validPath(cachingRsaPublicKey)) { cachingRsaPublicKey = sharedConn .createStatement("SELECT @@sha256_password_public_key_path") .execute() .flatMap(r -> r.map((row, metadata) -> row.get(0, String.class))) .blockLast(); + if (!validPath(cachingRsaPublicKey)) { + cachingRsaPublicKey = System.getenv("TEST_DB_RSA_PUBLIC_KEY"); + if (!validPath(cachingRsaPublicKey)) { + File sslDir = new File(System.getProperty("user.dir") + "/ssl"); + if (sslDir.exists() && sslDir.isDirectory()) { + cachingRsaPublicKey = System.getProperty("user.dir") + "/ssl/public.key"; + } else cachingRsaPublicKey = null; + } + } } sharedConn diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/BigIntegerParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/BigIntegerParseTest.java index 40234f25..80b5874f 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/BigIntegerParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/BigIntegerParseTest.java @@ -49,6 +49,7 @@ public static void before2() { "INSERT INTO BigIntUnsignedTable VALUES (0), (1), (18446744073709551615), (null)") .execute() .blockLast(); + sharedConn.createStatement("FLUSH TABLES").execute().blockLast(); } @AfterAll diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/BitParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/BitParseTest.java index 2d438a61..9a0a92b6 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/BitParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/BitParseTest.java @@ -40,6 +40,7 @@ public static void before2() { "INSERT INTO BitTable VALUES (b'0000', 1, b'0'), (b'0000000100000000', 2, b'1'),(b'0000111100000000', 3, b'10'),(b'1010', 4, b'11'), (null, 5, b'100')") .execute() .blockLast(); + sharedConn.createStatement("FLUSH TABLES").execute().blockLast(); } @AfterAll diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/BlobParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/BlobParseTest.java index 66971ef2..043ecaa4 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/BlobParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/BlobParseTest.java @@ -45,6 +45,7 @@ public static void before2() { "INSERT INTO BlobTable VALUES ('diego🤘💪',1),('georg',2),('lawrin',3), (null,4)") .execute() .blockLast(); + sharedConn.createStatement("FLUSH TABLES").execute().blockLast(); } @AfterAll diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/DateParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/DateParseTest.java index 16577a82..a32f602d 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/DateParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/DateParseTest.java @@ -38,6 +38,7 @@ public static void before2() { .createStatement("INSERT INTO DateTable VALUES('2010-01-12',1), ('2011-2-28',2), (null,3)") .execute() .blockLast(); + sharedConn.createStatement("FLUSH TABLES").execute().blockLast(); } @AfterAll diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/DateTimeParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/DateTimeParseTest.java index aa3cef49..d4aa731b 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/DateTimeParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/DateTimeParseTest.java @@ -42,6 +42,7 @@ public static void before2() { "INSERT INTO DateTimeTable VALUES('2013-07-22 12:50:05.01230'), ('2035-01-31 10:45:01'), (null)") .execute() .blockLast(); + sharedConn.createStatement("FLUSH TABLES").execute().blockLast(); } @AfterAll diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/DecimalParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/DecimalParseTest.java index 8dabb156..b907290c 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/DecimalParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/DecimalParseTest.java @@ -42,6 +42,7 @@ public static void before2() { + " (null), (19223372036854775807.9223372036854775807)") .execute() .blockLast(); + sharedConn.createStatement("FLUSH TABLES").execute().blockLast(); } @AfterAll diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/DoubleParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/DoubleParseTest.java index 8eb926fc..55f914aa 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/DoubleParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/DoubleParseTest.java @@ -37,6 +37,7 @@ public static void before2() { .createStatement("INSERT INTO DoubleTable VALUES (0.1),(1),(922.92233), (null)") .execute() .blockLast(); + sharedConn.createStatement("FLUSH TABLES").execute().blockLast(); } @AfterAll diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/FloatParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/FloatParseTest.java index 5c0e4425..5aec9de1 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/FloatParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/FloatParseTest.java @@ -37,6 +37,7 @@ public static void before2() { .createStatement("INSERT INTO FloatTable VALUES (0.1),(1),(922.92233), (null)") .execute() .blockLast(); + sharedConn.createStatement("FLUSH TABLES").execute().blockLast(); } @AfterAll diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/IntParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/IntParseTest.java index 1d71b908..5c44de19 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/IntParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/IntParseTest.java @@ -49,6 +49,7 @@ public static void before2() { .createStatement("INSERT INTO IntUnsignedTable VALUES (0), (1), (4294967295), (null)") .execute() .blockLast(); + sharedConn.createStatement("FLUSH TABLES").execute().blockLast(); } @AfterAll diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/MediumIntParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/MediumIntParseTest.java index 7589ee00..1731266b 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/MediumIntParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/MediumIntParseTest.java @@ -47,6 +47,7 @@ public static void before2() { .createStatement("INSERT INTO MediumIntUnsignedTable VALUES (0), (1), (16777215), (null)") .execute() .blockLast(); + sharedConn.createStatement("FLUSH TABLES").execute().blockLast(); } @AfterAll diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/ShortParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/ShortParseTest.java index 3dccdbf5..04e54ebd 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/ShortParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/ShortParseTest.java @@ -47,6 +47,7 @@ public static void before2() { .createStatement("INSERT INTO ShortUnsignedTable VALUES (0), (1), (65535), (null)") .execute() .blockLast(); + sharedConn.createStatement("FLUSH TABLES").execute().blockLast(); } @AfterAll diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/StringParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/StringParseTest.java index d4604336..208ad7f6 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/StringParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/StringParseTest.java @@ -50,6 +50,7 @@ public static void before2() { .createStatement("INSERT INTO StringTable VALUES ('some🌟'),('1'),('0'), (null)") .execute() .blockLast(); + sharedConn.createStatement("FLUSH TABLES").execute().blockLast(); } @AfterAll diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/TimeParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/TimeParseTest.java index 5abf6a7d..9aebdf4c 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/TimeParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/TimeParseTest.java @@ -46,6 +46,7 @@ public static void before2() { + ", (null, null)") .execute() .blockLast(); + sharedConn.createStatement("FLUSH TABLES").execute().blockLast(); } @AfterAll diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/TimestampParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/TimestampParseTest.java index 61b4c7d7..a910afa6 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/TimestampParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/TimestampParseTest.java @@ -52,6 +52,7 @@ public static void before2() { "INSERT INTO TimestampTable2 VALUES('1970-01-02 12:50:05.01230'), ('1970-01-01 10:45:01'), (null)") .execute() .blockLast(); + sharedConn.createStatement("FLUSH TABLES").execute().blockLast(); } @AfterAll diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/TinyIntParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/TinyIntParseTest.java index 0d66104d..a8b72489 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/TinyIntParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/TinyIntParseTest.java @@ -47,6 +47,7 @@ public static void before2() { .createStatement("INSERT INTO tinyIntUnsignedTable VALUES (0), (1), (255), (null)") .execute() .blockLast(); + sharedConn.createStatement("FLUSH TABLES").execute().blockLast(); } @AfterAll diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/YearParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/YearParseTest.java index 37135337..a5aa4d38 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/YearParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/YearParseTest.java @@ -46,6 +46,7 @@ public static void before2() { sharedConn.createStatement(sqlCreate).execute().blockLast(); sharedConn.createStatement(sqlInsert).execute().blockLast(); + sharedConn.createStatement("FLUSH TABLES").execute().blockLast(); } @AfterAll diff --git a/src/test/java/org/mariadb/r2dbc/tools/TcpProxySocket.java b/src/test/java/org/mariadb/r2dbc/tools/TcpProxySocket.java index e6c2043a..f4656346 100644 --- a/src/test/java/org/mariadb/r2dbc/tools/TcpProxySocket.java +++ b/src/test/java/org/mariadb/r2dbc/tools/TcpProxySocket.java @@ -80,7 +80,6 @@ public void sendRst() { } } catch (IOException e) { // eat Exception - e.printStackTrace(); } try { if (server != null) { diff --git a/src/test/resources/conf.properties b/src/test/resources/conf.properties index d764d02a..68f3b352 100644 --- a/src/test/resources/conf.properties +++ b/src/test/resources/conf.properties @@ -1,6 +1,6 @@ DB_HOST=localhost DB_PORT=3306 -DB_DATABASE=testj +DB_DATABASE=testr2 DB_USER=root DB_PASSWORD= DB_OTHER= \ No newline at end of file