mirror of
https://github.com/dolthub/dolt.git
synced 2025-12-21 11:59:41 -06:00
Merge pull request #9952 from dolthub/elian/mariadb
dolthub/dolt#9904: Add MariaDB connectors integration tests
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
name: 'Dolt MySQL client integration tests'
|
||||
description: 'Smoke tests for mysql client integrations'
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: '../../../integration-tests/MySQLDockerfile'
|
||||
48
.github/workflows/ci-mysql-client-tests.yaml
vendored
48
.github/workflows/ci-mysql-client-tests.yaml
vendored
@@ -11,14 +11,50 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
mysql_client_integrations_job:
|
||||
mysql_client_integrations:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
name: Run tests
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Copy go package
|
||||
run: cp -r ./go ./integration-tests/go
|
||||
- name: Test mysql client integrations
|
||||
uses: ./.github/actions/mysql-client-tests
|
||||
with:
|
||||
path: dolt
|
||||
|
||||
- name: Free disk space
|
||||
run: |
|
||||
NAME="DISK-CLEANUP"
|
||||
echo "[${NAME}] Starting background cleanup..."
|
||||
[ -d "$AGENT_TOOLSDIRECTORY" ] && sudo rm -rf "$AGENT_TOOLSDIRECTORY" &
|
||||
[ -d /usr/share/dotnet ] && sudo rm -rf /usr/share/dotnet &
|
||||
[ -d /usr/local/lib/android ] && sudo rm -rf /usr/local/lib/android &
|
||||
[ -d /opt/ghc ] && sudo rm -rf /opt/ghc &
|
||||
[ -d /usr/local/share/boost ] && sudo rm -rf /usr/local/share/boost &
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Ensure cache directory exists
|
||||
run: |
|
||||
sudo mkdir -p /mnt/.buildx-cache
|
||||
sudo chown $USER:$USER /mnt/.buildx-cache
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /mnt/.buildx-cache
|
||||
key: ${{ runner.os }}-docker-mysql-client-integrations
|
||||
restore-keys: |
|
||||
${{ runner.os }}-docker
|
||||
|
||||
- name: Build MySQL test image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: dolt/integration-tests/mysql-client-tests/Dockerfile
|
||||
tags: mysql-client-tests:latest
|
||||
load: true
|
||||
cache-from: type=local,src=/mnt/.buildx-cache
|
||||
cache-to: type=local,dest=/mnt/.buildx-cache
|
||||
|
||||
- name: Test MySQL client integrations
|
||||
run: docker run --rm mysql-client-tests:latest
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
FROM --platform=linux/amd64 ubuntu:20.04
|
||||
|
||||
# install python, libmysqlclient-dev, java, bats, git ruby, perl, cpan
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt update -y && \
|
||||
apt install -y \
|
||||
curl \
|
||||
gnupg \
|
||||
libwxbase3.0-0v5 \
|
||||
libwxgtk3.0-gtk3-0v5 \
|
||||
libncurses5 \
|
||||
libsctp1 \
|
||||
software-properties-common && \
|
||||
curl -sL https://deb.nodesource.com/setup_22.x | bash - && \
|
||||
add-apt-repository ppa:deadsnakes/ppa -y && \
|
||||
curl -OL https://binaries2.erlang-solutions.com/ubuntu/pool/contrib/e/esl-erlang/esl-erlang_25.0-1~ubuntu~focal_amd64.deb && \
|
||||
dpkg -i esl-erlang_25.0-1~ubuntu~focal_amd64.deb && \
|
||||
curl -LO https://binaries2.erlang-solutions.com/ubuntu/pool/contrib/e/elixir/elixir_1.14.3_1_otp_25.3~ubuntu~focal_all.deb && \
|
||||
dpkg -i elixir_1.14.3_1_otp_25.3~ubuntu~focal_all.deb
|
||||
RUN apt update -y && \
|
||||
apt install -y \
|
||||
python3.9 \
|
||||
python3-pip \
|
||||
curl \
|
||||
wget \
|
||||
pkg-config \
|
||||
mysql-client \
|
||||
libmysqlclient-dev \
|
||||
openjdk-17-jdk \
|
||||
ant \
|
||||
ca-certificates-java \
|
||||
bats \
|
||||
perl \
|
||||
php \
|
||||
php-mysqli \
|
||||
cpanminus \
|
||||
cmake \
|
||||
g++ \
|
||||
libmysqlcppconn-dev \
|
||||
git \
|
||||
ruby \
|
||||
ruby-dev \
|
||||
gem \
|
||||
libc6 \
|
||||
libgcc1 \
|
||||
libgssapi-krb5-2 \
|
||||
libicu66 \
|
||||
libssl1.1 \
|
||||
libstdc++6 \
|
||||
zlib1g \
|
||||
r-base \
|
||||
postgresql \
|
||||
postgresql-contrib \
|
||||
libpq-dev \
|
||||
nodejs \
|
||||
lsof \
|
||||
postgresql-server-dev-all && \
|
||||
update-ca-certificates -f
|
||||
|
||||
# install go
|
||||
WORKDIR /root
|
||||
ENV GO_VERSION=1.25.0
|
||||
ENV GOPATH=$HOME/go
|
||||
ENV PATH=$PATH:$GOPATH/bin
|
||||
ENV PATH=$PATH:$GOPATH/bin:/usr/local/go/bin
|
||||
RUN curl -O "https://dl.google.com/go/go${GO_VERSION}.linux-amd64.tar.gz" && \
|
||||
sha256sum "go${GO_VERSION}.linux-amd64.tar.gz" && \
|
||||
tar -xvf "go${GO_VERSION}.linux-amd64.tar.gz" -C /usr/local && \
|
||||
chown -R root:root /usr/local/go && \
|
||||
mkdir -p $HOME/go/{bin,src} && \
|
||||
go version
|
||||
|
||||
# install MySQL dependency from source
|
||||
RUN git clone https://github.com/go-sql-driver/mysql.git
|
||||
WORKDIR mysql
|
||||
RUN git checkout tags/v1.6.0 -b v1.6.0
|
||||
RUN go install .
|
||||
WORKDIR /
|
||||
|
||||
# install dotnet
|
||||
RUN curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 8.0.412 --install-dir /usr/local/bin --no-path && \
|
||||
dotnet --version
|
||||
|
||||
# install pip for python3.9
|
||||
RUN curl -LO https://bootstrap.pypa.io/get-pip.py && \
|
||||
python3.9 get-pip.py && \
|
||||
pip --version
|
||||
|
||||
# install mysql connector and pymsql
|
||||
RUN pip install mysql-connector-python==8.0.32
|
||||
RUN pip install PyMySQL==1.0.2
|
||||
RUN pip install sqlalchemy==1.4.46
|
||||
|
||||
# Setup JAVA_HOME -- useful for docker commandline
|
||||
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64/
|
||||
|
||||
# install mysql connector java
|
||||
RUN mkdir -p /mysql-client-tests/java
|
||||
RUN curl -L -o /mysql-client-tests/java/mysql-connector-java-8.0.21.jar \
|
||||
https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.21/mysql-connector-java-8.0.21.jar
|
||||
|
||||
# install node deps
|
||||
COPY mysql-client-tests/node/package.json /mysql-client-tests/node/
|
||||
COPY mysql-client-tests/node/package-lock.json /mysql-client-tests/node/
|
||||
WORKDIR /mysql-client-tests/node
|
||||
RUN npm install
|
||||
|
||||
# install cpan dependencies
|
||||
RUN cpanm --force DBD::mysql
|
||||
|
||||
# install ruby dependencies
|
||||
COPY mysql-client-tests/ruby/Gemfile /mysql-client-tests/ruby/
|
||||
COPY mysql-client-tests/ruby/Gemfile.lock /mysql-client-tests/ruby/
|
||||
WORKDIR /mysql-client-tests/ruby
|
||||
RUN gem install bundler -v 2.1.4 && bundle install
|
||||
|
||||
# install R packages
|
||||
RUN Rscript -e 'install.packages(c("DBI", "RMySQL", "RMariaDB"), \
|
||||
repos = c(RSPM="https://packagemanager.rstudio.com/cran/__linux__/focal/latest"))'
|
||||
|
||||
# install rust
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain stable -y
|
||||
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
|
||||
# install postgres and psql
|
||||
RUN service postgresql start
|
||||
|
||||
# install mysql_fdw
|
||||
WORKDIR /mysql-client-tests/mysql_fdw
|
||||
RUN git clone https://github.com/EnterpriseDB/mysql_fdw --branch REL-2_9_0
|
||||
WORKDIR /mysql-client-tests/mysql_fdw/mysql_fdw
|
||||
RUN make USE_PGXS=1 && \
|
||||
make USE_PGXS=1 install
|
||||
|
||||
# install dolt from source
|
||||
WORKDIR /root/building
|
||||
COPY ./go .
|
||||
ENV GOFLAGS="-mod=readonly"
|
||||
RUN go build -o /usr/local/bin/dolt ./cmd/dolt
|
||||
|
||||
COPY mysql-client-tests /mysql-client-tests
|
||||
COPY mysql-client-tests/mysql-client-tests-entrypoint.sh /mysql-client-tests/entrypoint.sh
|
||||
|
||||
# install rust dependencies
|
||||
WORKDIR /mysql-client-tests/rust
|
||||
RUN cargo build --config net.git-fetch-with-cli=true
|
||||
|
||||
WORKDIR /mysql-client-tests
|
||||
ENTRYPOINT ["/mysql-client-tests/entrypoint.sh"]
|
||||
@@ -1,5 +0,0 @@
|
||||
mysql-client-tests/c/mysql-client-tests
|
||||
mysql-client-tests/java/MySQLConnectorTest.class
|
||||
mysql-client-tests/cpp/_build/**/*
|
||||
mysql-client-tests/cpp/_build
|
||||
bats
|
||||
186
integration-tests/mysql-client-tests/Dockerfile
Normal file
186
integration-tests/mysql-client-tests/Dockerfile
Normal file
@@ -0,0 +1,186 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM golang:1.25-alpine AS golang_cgo125
|
||||
ENV CGO_ENABLED=1
|
||||
ENV GO_LDFLAGS="-linkmode external -extldflags '-static'"
|
||||
RUN apk add --no-cache build-base
|
||||
|
||||
FROM golang_cgo125 AS dolt_build
|
||||
RUN apk add --no-cache icu-dev icu-static
|
||||
COPY dolt/go/go.mod /build/dolt/go/
|
||||
WORKDIR /build/dolt/go/
|
||||
RUN go mod download
|
||||
COPY dolt/go/ /build/dolt/go/
|
||||
RUN go build -tags icu_static -ldflags "$GO_LDFLAGS" -o /build/bin/dolt ./cmd/dolt
|
||||
|
||||
FROM golang_cgo125 AS go_clients_build
|
||||
COPY dolt/integration-tests/mysql-client-tests/go /build/go/
|
||||
WORKDIR /build/go/
|
||||
RUN go build -ldflags "$GO_LDFLAGS" -o /build/bin/mysql-client-test
|
||||
|
||||
COPY dolt/integration-tests/mysql-client-tests/go-mysql/ /build/go-mysql/
|
||||
WORKDIR /build/go-mysql/
|
||||
RUN go build -ldflags "$GO_LDFLAGS" -o /build/bin/sql-driver-mysql-test
|
||||
|
||||
FROM rust:1.90-alpine3.22 AS rust_clients_build
|
||||
RUN apk add --no-cache musl-dev
|
||||
COPY dolt/integration-tests/mysql-client-tests/rust/ /build/rust/
|
||||
WORKDIR /build/rust/
|
||||
RUN cargo build --release --target-dir /build/bin/ && cargo clean # exe is in release/
|
||||
|
||||
FROM debian:bookworm-slim AS dotnet_clients_build
|
||||
RUN apt-get update && apt-get install -y wget gnupg ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||
RUN wget https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \
|
||||
&& dpkg -i packages-microsoft-prod.deb \
|
||||
&& rm packages-microsoft-prod.deb
|
||||
RUN apt-get update && apt-get install -y dotnet-sdk-9.0 && rm -rf /var/lib/apt/lists/*
|
||||
COPY dolt/integration-tests/mysql-client-tests/dotnet/MySqlClient/*.csproj /build/dotnet/MySqlClient/
|
||||
WORKDIR /build/dotnet/MySqlClient/
|
||||
RUN dotnet restore
|
||||
COPY dolt/integration-tests/mysql-client-tests/dotnet/MySqlClient/ /build/dotnet/MySqlClient/
|
||||
RUN dotnet publish -c Release -o /build/bin --no-restore
|
||||
|
||||
COPY dolt/integration-tests/mysql-client-tests/dotnet/MySqlConnector/*.csproj /build/dotnet/MySqlConnector/
|
||||
WORKDIR /build/dotnet/MySqlConnector/
|
||||
RUN dotnet restore
|
||||
COPY dolt/integration-tests/mysql-client-tests/dotnet/MySqlConnector/ /build/dotnet/MySqlConnector/
|
||||
RUN dotnet publish -c Release -o /build/bin --no-restore
|
||||
# devart dotconnect reqs a license so we've skipped it here
|
||||
|
||||
FROM debian:bookworm-slim AS c_clients_build
|
||||
# default-libmysqlclient-dev uses libmariadb under the hood but here we test both header interfaces
|
||||
RUN apt-get update && apt-get install -y default-libmysqlclient-dev libmariadb-dev unixodbc-dev odbcinst wget gcc make && rm -rf /var/lib/apt/lists/*
|
||||
RUN wget --progress=dot:giga https://dlm.mariadb.com/4465891/Connectors/odbc/connector-odbc-3.2.7/mariadb-connector-odbc_3.2.7-1+maria~bookworm_amd64.deb \
|
||||
&& dpkg -i mariadb-connector-odbc_3.2.7-1+maria~bookworm_amd64.deb || apt-get install -y -f \
|
||||
&& rm mariadb-connector-odbc_3.2.7-1+maria~bookworm_amd64.deb \
|
||||
&& odbcinst -i -d -f /dev/stdin <<EOF
|
||||
[MariaDB ODBC 3.2 Driver]
|
||||
Description=MariaDB Connector/ODBC v.3.2
|
||||
Driver=/usr/lib/x86_64-linux-gnu/libmaodbc.so
|
||||
EOF
|
||||
COPY dolt/integration-tests/mysql-client-tests/c/ /build/c/
|
||||
WORKDIR /build/c/
|
||||
RUN make
|
||||
|
||||
FROM debian:bookworm-slim AS cpp_clients_build
|
||||
RUN apt-get update && apt-get install -y libmysqlcppconn-dev wget g++ make && rm -rf /var/lib/apt/lists/*
|
||||
RUN wget --progress=dot:giga https://dlm.mariadb.com/4464866/Connectors/cpp/connector-cpp-1.1.7/mariadb-connector-cpp_1.1.7-1+maria~bookworm_amd64.deb \
|
||||
&& dpkg -i mariadb-connector-cpp_1.1.7-1+maria~bookworm_amd64.deb || apt-get install -y -f \
|
||||
&& rm mariadb-connector-cpp_1.1.7-1+maria~bookworm_amd64.deb
|
||||
COPY dolt/integration-tests/mysql-client-tests/cpp/ /build/cpp/
|
||||
WORKDIR /build/cpp/
|
||||
RUN make
|
||||
|
||||
FROM python:3.14-slim-bookworm AS python_clients_build
|
||||
RUN apt-get update && apt-get install -y binutils libmariadb-dev gcc && rm -rf /var/lib/apt/lists/*
|
||||
RUN pip install --no-cache-dir mysql-connector-python==8.0.33 PyMySQL==1.0.2 sqlalchemy==1.4.46 mariadb pyinstaller
|
||||
COPY dolt/integration-tests/mysql-client-tests/python/ /build/python/
|
||||
WORKDIR /build/python/
|
||||
RUN pyinstaller --onefile pymysql-test.py
|
||||
RUN pyinstaller --onefile --collect-all mysql.connector sqlalchemy-test.py
|
||||
RUN pyinstaller --onefile --collect-all mysql.connector mysql-connector-test.py
|
||||
RUN pyinstaller --onefile mariadb-connector-test.py
|
||||
|
||||
FROM elixir:1.18-slim AS elixir_clients_build
|
||||
RUN apt-get update && apt-get install -y ca-certificates xz-utils curl && rm -rf /var/lib/apt/lists/*
|
||||
RUN curl -sSL https://ziglang.org/download/0.14.1/zig-x86_64-linux-0.14.1.tar.xz | tar -xJ
|
||||
ENV PATH="/zig-x86_64-linux-0.14.1:${PATH}"
|
||||
COPY dolt/integration-tests/mysql-client-tests/elixir/myxql/mix.exs /build/elixir/myxql/
|
||||
WORKDIR /build/elixir/myxql/
|
||||
RUN mix local.hex --force && mix local.rebar --force
|
||||
RUN mix deps.get
|
||||
COPY dolt/integration-tests/mysql-client-tests/elixir/myxql/ /build/elixir/myxql/
|
||||
RUN MIX_ENV=prod mix release simple
|
||||
COPY dolt/integration-tests/mysql-client-tests/elixir/mysql/mix.exs /build/elixir/mysql/
|
||||
WORKDIR /build/elixir/mysql/
|
||||
RUN mix deps.get
|
||||
COPY dolt/integration-tests/mysql-client-tests/elixir/mysql/ /build/elixir/mysql/
|
||||
RUN MIX_ENV=prod mix release mysql_otp
|
||||
RUN mkdir -p /build/bin && \
|
||||
cp /build/elixir/myxql/burrito_out/simple_linux /build/bin/myxql-driver-test && \
|
||||
cp /build/elixir/mysql/burrito_out/mysql_otp_linux /build/bin/mysql-otp-test
|
||||
|
||||
FROM maven:3.8-openjdk-17-slim AS java_clients_build
|
||||
RUN apt-get update && apt-get install -y binutils && rm -rf /var/lib/apt/lists/*
|
||||
COPY dolt/integration-tests/mysql-client-tests/java/ /build/java/
|
||||
WORKDIR /build/java/
|
||||
RUN mvn package
|
||||
RUN jlink \
|
||||
--add-modules java.base,java.sql,java.naming,java.xml,java.logging,java.management \
|
||||
--strip-debug \
|
||||
--no-man-pages \
|
||||
--no-header-files \
|
||||
--output /build/jre
|
||||
|
||||
FROM node:22-bookworm-slim AS node_clients_build
|
||||
COPY dolt/integration-tests/mysql-client-tests/node/package.json /build/bin/
|
||||
WORKDIR /build/bin/
|
||||
RUN npm install --omit=dev
|
||||
COPY dolt/integration-tests/mysql-client-tests/node/ /build/bin/
|
||||
|
||||
FROM ruby:3.4-bookworm AS ruby_clients_build
|
||||
RUN apt-get update && apt-get install -y default-libmysqlclient-dev ruby3.1-dev bundler && rm -rf /var/lib/apt/lists/*
|
||||
COPY dolt/integration-tests/mysql-client-tests/ruby/Gemfile /build/ruby/
|
||||
WORKDIR /build/ruby/
|
||||
RUN bundle install
|
||||
COPY dolt/integration-tests/mysql-client-tests/ruby/ /build/bin/
|
||||
|
||||
FROM swift:5.10-bookworm AS swift_clients_build
|
||||
RUN apt-get update && apt-get install -y libmariadb-dev pkg-config libstdc++-12-dev && rm -rf /var/lib/apt/lists/*
|
||||
COPY dolt/integration-tests/mysql-client-tests/swift/ /build/swift/
|
||||
WORKDIR /build/swift/
|
||||
RUN swift build -c release --static-swift-stdlib
|
||||
RUN mkdir -p /build/bin && cp .build/release/MariaDBTest /build/bin/mariadb-swift-test
|
||||
|
||||
FROM php:8.3-bookworm AS runtime
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libmysqlcppconn-dev \
|
||||
libdbd-mysql-perl \
|
||||
libdbd-mariadb-perl \
|
||||
default-mysql-client \
|
||||
postgresql-15-mysql-fdw \
|
||||
r-base-core \
|
||||
lsof \
|
||||
bats \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Runtime Libraries
|
||||
COPY --from=cpp_clients_build /usr/lib/x86_64-linux-gnu/libmariadb* /usr/lib/x86_64-linux-gnu/
|
||||
COPY --from=c_clients_build /usr/lib/x86_64-linux-gnu/ /usr/lib/x86_64-linux-gnu/
|
||||
COPY --from=c_clients_build /etc/odbcinst.ini /etc/odbcinst.ini
|
||||
COPY --from=java_clients_build /build/jre/ /opt/jre/
|
||||
COPY --from=node_clients_build /usr/local/bin/node /usr/local/bin/
|
||||
COPY --from=ruby_clients_build /usr/local/bin/ruby /usr/local/bin/
|
||||
COPY --from=ruby_clients_build /usr/local/lib/ /usr/local/lib/
|
||||
COPY --from=ruby_clients_build /usr/local/bundle/ /usr/local/bundle/
|
||||
|
||||
RUN docker-php-ext-install mysqli pdo_mysql && \
|
||||
R -e "install.packages('DBI', repos='https://cloud.r-project.org/')" && \
|
||||
R -e "install.packages('RMySQL', repos='https://cloud.r-project.org/')" && \
|
||||
R -e "install.packages('RMariaDB', repos='https://cloud.r-project.org/')"
|
||||
|
||||
ENV PATH="/opt/jre/bin:${PATH}" GEM_HOME="/usr/local/bundle"
|
||||
RUN ldconfig
|
||||
|
||||
COPY --from=go_clients_build /build/bin/ /build/bin/go/
|
||||
COPY --from=rust_clients_build /build/bin/release/ /build/bin/rust/
|
||||
COPY --from=dotnet_clients_build /build/bin/ /build/bin/dotnet/
|
||||
COPY --from=c_clients_build /build/bin/ /build/bin/c/
|
||||
COPY --from=cpp_clients_build /build/bin/ /build/bin/cpp/
|
||||
COPY --from=python_clients_build /build/python/dist/ /build/bin/python/
|
||||
COPY --from=elixir_clients_build /build/bin/ /build/bin/elixir/
|
||||
COPY --from=java_clients_build /build/java/target/ /build/bin/java/
|
||||
COPY --from=node_clients_build /build/bin/ /build/bin/node/
|
||||
COPY --from=swift_clients_build /build/bin/ /build/bin/swift/
|
||||
COPY dolt/integration-tests/mysql-client-tests/php/ /build/bin/php/
|
||||
COPY --from=ruby_clients_build /build/bin/ /build/bin/ruby/
|
||||
COPY dolt/integration-tests/mysql-client-tests/r/ /build/bin/r/
|
||||
COPY dolt/integration-tests/mysql-client-tests/perl/ /build/bin/perl/
|
||||
|
||||
COPY dolt/integration-tests/mysql-client-tests/mysql-client-tests.bats /build/bin/
|
||||
COPY dolt/integration-tests/mysql-client-tests/helpers.bash /build/bin/
|
||||
COPY dolt/integration-tests/mysql-client-tests/mysql-client-tests-entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
COPY --from=dolt_build /build/bin/ /usr/local/bin/
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
15
integration-tests/mysql-client-tests/Dockerfile.dockerignore
Normal file
15
integration-tests/mysql-client-tests/Dockerfile.dockerignore
Normal file
@@ -0,0 +1,15 @@
|
||||
**/.git/
|
||||
**/.github/
|
||||
**/docker/
|
||||
**/images/
|
||||
**/node_modules/
|
||||
**/*Dockerfile
|
||||
**/transactions/
|
||||
**/orm-tests/
|
||||
**/out/
|
||||
**/bats/archive-test-repos/
|
||||
**/bats/performance-repo/
|
||||
**/bats/corrupt_dbs/
|
||||
**/integration-tests/data-dump-loading-tests/
|
||||
**/.dolt/
|
||||
**/.doltcfg/
|
||||
@@ -1,11 +1,34 @@
|
||||
CFLAGS := $(shell pkg-config --cflags mysqlclient)
|
||||
LDFLAGS := $(shell pkg-config --libs mysqlclient)
|
||||
CC = gcc
|
||||
|
||||
all: mysql-connector-c-test
|
||||
CFLAGS_MYSQL = -I/usr/include/mysql -Wall -O2
|
||||
CFLAGS_MARIADB = -I/usr/include/mariadb -Wall -O2
|
||||
CFLAGS_ODBC = -Wall -O2
|
||||
|
||||
mysql-connector-c-test: mysql-connector-c-test.c
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
LDFLAGS_MYSQL = -lmysqlclient
|
||||
LDFLAGS_MARIADB = -lmariadb
|
||||
LDFLAGS_ODBC = -lodbc
|
||||
|
||||
TARGET_MYSQL = /build/bin/mysql-client-test
|
||||
TARGET_MARIADB = /build/bin/mariadb-client-test
|
||||
TARGET_ODBC = /build/bin/mariadb-odbc-test
|
||||
|
||||
SRCS_MYSQL = mysql-connector-test.c
|
||||
SRCS_MARIADB = mariadb-connector-test.c
|
||||
SRCS_ODBC = mariadb-odbc-test.c
|
||||
|
||||
all: $(TARGET_MYSQL) $(TARGET_MARIADB) $(TARGET_ODBC)
|
||||
|
||||
$(TARGET_MYSQL): $(SRCS_MYSQL)
|
||||
@mkdir -p /build/bin
|
||||
$(CC) $(CFLAGS_MYSQL) -o $@ $^ $(LDFLAGS_MYSQL)
|
||||
|
||||
$(TARGET_MARIADB): $(SRCS_MARIADB)
|
||||
@mkdir -p /build/bin
|
||||
$(CC) $(CFLAGS_MARIADB) -o $@ $^ $(LDFLAGS_MARIADB)
|
||||
|
||||
$(TARGET_ODBC): $(SRCS_ODBC)
|
||||
@mkdir -p /build/bin
|
||||
$(CC) $(CFLAGS_ODBC) -o $@ $^ $(LDFLAGS_ODBC)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f mysql-connector-c-test
|
||||
rm -f $(TARGET_MYSQL) $(TARGET_MARIADB) $(TARGET_ODBC)
|
||||
|
||||
199
integration-tests/mysql-client-tests/c/mariadb-connector-test.c
Normal file
199
integration-tests/mysql-client-tests/c/mariadb-connector-test.c
Normal file
@@ -0,0 +1,199 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <memory.h>
|
||||
#include <mariadb/mysql.h>
|
||||
|
||||
#define QUERIES_SIZE 14
|
||||
|
||||
char *queries[QUERIES_SIZE] =
|
||||
{
|
||||
"create table test (pk int, `value` int, primary key(pk))",
|
||||
"describe test",
|
||||
"select * from test",
|
||||
"insert into test (pk, `value`) values (0,0)",
|
||||
"select * from test",
|
||||
"call dolt_add('-A');",
|
||||
"call dolt_commit('-m', 'my commit')",
|
||||
"select COUNT(*) FROM dolt_log",
|
||||
"call dolt_checkout('-b', 'mybranch')",
|
||||
"insert into test (pk, `value`) values (10,10)",
|
||||
"call dolt_commit('-a', '-m', 'my commit2')",
|
||||
"call dolt_checkout('main')",
|
||||
"call dolt_merge('mybranch')",
|
||||
"select COUNT(*) FROM dolt_log",
|
||||
};
|
||||
|
||||
typedef struct statement_t {
|
||||
char *query;
|
||||
MYSQL_BIND bind[10];
|
||||
int expect_prepare_error;
|
||||
int expect_exec_error;
|
||||
int expect_result_metadata;
|
||||
} statement;
|
||||
|
||||
void test_statement(MYSQL *con, statement *stmt) {
|
||||
MYSQL_STMT *mstmt = mysql_stmt_init(con);
|
||||
if (!mstmt) {
|
||||
fprintf(stderr, "failed to init stmt: %s\n", mysql_error(con));
|
||||
exit(1);
|
||||
}
|
||||
if ( mysql_stmt_prepare(mstmt, stmt->query, strlen(stmt->query)) ) {
|
||||
if ( !stmt->expect_prepare_error) {
|
||||
fprintf(stderr, "failed to prepare stmt: %s: %s\n", stmt->query, mysql_stmt_error(mstmt));
|
||||
exit(1);
|
||||
} else {
|
||||
goto close;
|
||||
}
|
||||
}
|
||||
if ( mysql_stmt_bind_param(mstmt, stmt->bind) ) {
|
||||
fprintf(stderr, "failed to bind stmt: %s: %s\n", stmt->query, mysql_stmt_error(mstmt));
|
||||
exit(1);
|
||||
}
|
||||
MYSQL_RES *metadata = mysql_stmt_result_metadata(mstmt);
|
||||
if (stmt->expect_result_metadata && metadata == NULL) {
|
||||
fprintf(stderr, "result metadata was unexpectedly NULL: %s\n", stmt->query);
|
||||
exit(1);
|
||||
} else if (!stmt->expect_result_metadata && metadata != NULL) {
|
||||
fprintf(stderr, "result metadata was unexpectedly non-NULL: %s\n", stmt->query);
|
||||
exit(1);
|
||||
}
|
||||
if ( mysql_stmt_execute(mstmt) ) {
|
||||
if ( !stmt->expect_exec_error) {
|
||||
fprintf(stderr, "failed to execute stmt: %s: %s\n", stmt->query, mysql_stmt_error(mstmt));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
// TODO: Add test for mysql_stmt_store_result when supported
|
||||
close:
|
||||
if ( mysql_stmt_close(mstmt) ) {
|
||||
fprintf(stderr, "failed to close stmt: %s: %s\n", stmt->query, mysql_error(con));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
statement LAST_STATEMENT = {
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
char* user = argv[1];
|
||||
int port = atoi(argv[2]);
|
||||
char* db = argv[3];
|
||||
|
||||
MYSQL *con = mysql_init(NULL);
|
||||
|
||||
if ( con == NULL ) {
|
||||
fprintf(stderr, "%s\n", mysql_error(con));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#ifndef MARIADB_CLIENT_VERSION_STR
|
||||
fprintf(stderr, "Error: Not using MariaDB connector!\n");
|
||||
exit(1);
|
||||
#endif
|
||||
|
||||
if ( mysql_real_connect(con,
|
||||
"127.0.0.1",
|
||||
user,
|
||||
"",
|
||||
db,
|
||||
port,
|
||||
NULL,
|
||||
0 ) == NULL) {
|
||||
fprintf(stderr, "%s\n", mysql_error(con));
|
||||
mysql_close(con);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for ( int i = 0; i < QUERIES_SIZE; i++ ) {
|
||||
if ( mysql_query(con, queries[i]) ) {
|
||||
printf("QUERY FAILED: %s\n", queries[i]);
|
||||
fprintf(stderr, "%s\n", mysql_error(con));
|
||||
mysql_close(con);
|
||||
exit(1);
|
||||
} else {
|
||||
// Not checking validity of results for now
|
||||
MYSQL_RES* result = mysql_use_result(con);
|
||||
mysql_free_result(result);
|
||||
}
|
||||
}
|
||||
|
||||
int pk = 1;
|
||||
int value = 12;
|
||||
unsigned long string_len = 16;
|
||||
statement statements[] = {
|
||||
{
|
||||
.query = "select * from test where pk = ?",
|
||||
.bind = {
|
||||
[0] = {
|
||||
.buffer_type = MYSQL_TYPE_LONG,
|
||||
.buffer = (void *)(&pk),
|
||||
.buffer_length = sizeof(pk),
|
||||
},
|
||||
},
|
||||
.expect_result_metadata = 1,
|
||||
},
|
||||
{
|
||||
.query = "select * from test where pk = ?",
|
||||
.bind = {
|
||||
[0] = {
|
||||
.buffer_type = MYSQL_TYPE_LONG,
|
||||
.buffer = (void *)(&pk),
|
||||
.buffer_length = sizeof(pk),
|
||||
.is_unsigned = 1,
|
||||
},
|
||||
},
|
||||
.expect_result_metadata = 1,
|
||||
},
|
||||
{
|
||||
.query = "insert into test values (?, ?)",
|
||||
.bind = {
|
||||
[0] = {
|
||||
.buffer_type = MYSQL_TYPE_LONG,
|
||||
.buffer = (void *)(&pk),
|
||||
.buffer_length = sizeof(pk),
|
||||
},
|
||||
[1] = {
|
||||
.buffer_type = MYSQL_TYPE_LONG,
|
||||
.buffer = (void *)(&value),
|
||||
.buffer_length = sizeof(value),
|
||||
},
|
||||
},
|
||||
.expect_result_metadata = 0,
|
||||
},
|
||||
{
|
||||
.query = "update test set `value` = ?",
|
||||
.bind = {
|
||||
[0] = {
|
||||
.buffer_type = MYSQL_TYPE_STRING,
|
||||
.buffer = (void *)"test string here",
|
||||
.buffer_length = string_len,
|
||||
.length = &string_len,
|
||||
},
|
||||
},
|
||||
.expect_exec_error = 1,
|
||||
.expect_result_metadata = 0,
|
||||
},
|
||||
{
|
||||
.query = "select * from test SYNTAX ERROR where pk = ?",
|
||||
.bind = {
|
||||
[0] = {
|
||||
.buffer_type = MYSQL_TYPE_LONG,
|
||||
.buffer = (void *)(&pk),
|
||||
.buffer_length = sizeof(pk),
|
||||
},
|
||||
},
|
||||
.expect_prepare_error = 1,
|
||||
},
|
||||
LAST_STATEMENT,
|
||||
};
|
||||
|
||||
for (int i = 0; statements[i].query; i++) {
|
||||
test_statement(con, &statements[i]);
|
||||
}
|
||||
|
||||
mysql_close(con);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
186
integration-tests/mysql-client-tests/c/mariadb-odbc-test.c
Normal file
186
integration-tests/mysql-client-tests/c/mariadb-odbc-test.c
Normal file
@@ -0,0 +1,186 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sql.h>
|
||||
#include <sqlext.h>
|
||||
|
||||
#define QUERIES_SIZE 14
|
||||
|
||||
char *queries[QUERIES_SIZE] = {
|
||||
"create table test (pk int, `value` int, primary key(pk))",
|
||||
"describe test",
|
||||
"select * from test",
|
||||
"insert into test (pk, `value`) values (0,0)",
|
||||
"select * from test",
|
||||
"call dolt_add('-A')",
|
||||
"call dolt_commit('-m', 'my commit')",
|
||||
"select COUNT(*) FROM dolt_log",
|
||||
"call dolt_checkout('-b', 'mybranch')",
|
||||
"insert into test (pk, `value`) values (10,10)",
|
||||
"call dolt_commit('-a', '-m', 'my commit2')",
|
||||
"call dolt_checkout('main')",
|
||||
"call dolt_merge('mybranch')",
|
||||
"select COUNT(*) FROM dolt_log",
|
||||
};
|
||||
|
||||
void check_error(SQLRETURN ret, SQLHANDLE handle, SQLSMALLINT type, const char *msg) {
|
||||
if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
|
||||
SQLCHAR sqlstate[6];
|
||||
SQLCHAR message[SQL_MAX_MESSAGE_LENGTH];
|
||||
SQLINTEGER native_error;
|
||||
SQLSMALLINT text_length;
|
||||
|
||||
SQLGetDiagRec(type, handle, 1, sqlstate, &native_error, message, sizeof(message), &text_length);
|
||||
fprintf(stderr, "%s\nSQLSTATE: %s\nMessage: %s\n", msg, sqlstate, message);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct prepared_statement_t {
|
||||
char *query;
|
||||
int num_params;
|
||||
int pk_param;
|
||||
int value_param;
|
||||
int expect_prepare_error;
|
||||
int expect_exec_error;
|
||||
} prepared_statement;
|
||||
|
||||
void test_prepared_statement(SQLHDBC dbc, prepared_statement *pstmt) {
|
||||
SQLHSTMT stmt;
|
||||
SQLRETURN ret;
|
||||
|
||||
ret = SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
|
||||
check_error(ret, dbc, SQL_HANDLE_DBC, "Failed to allocate statement handle");
|
||||
|
||||
ret = SQLPrepare(stmt, (SQLCHAR *)pstmt->query, SQL_NTS);
|
||||
if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
|
||||
if (!pstmt->expect_prepare_error) {
|
||||
check_error(ret, stmt, SQL_HANDLE_STMT, "Failed to prepare statement");
|
||||
} else {
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (pstmt->num_params > 0) {
|
||||
if (pstmt->pk_param) {
|
||||
SQLINTEGER pk = 1;
|
||||
ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &pk, 0, NULL);
|
||||
check_error(ret, stmt, SQL_HANDLE_STMT, "Failed to bind pk parameter");
|
||||
}
|
||||
if (pstmt->num_params > 1 && pstmt->value_param) {
|
||||
SQLINTEGER value = 12;
|
||||
ret = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &value, 0, NULL);
|
||||
check_error(ret, stmt, SQL_HANDLE_STMT, "Failed to bind value parameter");
|
||||
}
|
||||
}
|
||||
|
||||
ret = SQLExecute(stmt);
|
||||
if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
|
||||
if (!pstmt->expect_exec_error) {
|
||||
check_error(ret, stmt, SQL_HANDLE_STMT, "Failed to execute statement");
|
||||
}
|
||||
}
|
||||
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 4) {
|
||||
fprintf(stderr, "Usage: %s <user> <port> <database>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *user = argv[1];
|
||||
int port = atoi(argv[2]);
|
||||
char *db = argv[3];
|
||||
|
||||
SQLHENV env;
|
||||
SQLHDBC dbc;
|
||||
SQLHSTMT stmt;
|
||||
SQLRETURN ret;
|
||||
|
||||
// Allocate environment handle
|
||||
ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
|
||||
check_error(ret, env, SQL_HANDLE_ENV, "Failed to allocate environment handle");
|
||||
|
||||
// Set ODBC version
|
||||
ret = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
|
||||
check_error(ret, env, SQL_HANDLE_ENV, "Failed to set ODBC version");
|
||||
|
||||
// Allocate connection handle
|
||||
ret = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
|
||||
check_error(ret, env, SQL_HANDLE_ENV, "Failed to allocate connection handle");
|
||||
|
||||
// Build connection string
|
||||
char connStr[512];
|
||||
snprintf(connStr, sizeof(connStr),
|
||||
"DRIVER=MariaDB ODBC 3.2 Driver;SERVER=127.0.0.1;PORT=%d;DATABASE=%s;UID=%s;PWD=;",
|
||||
port, db, user);
|
||||
|
||||
// Connect to database
|
||||
ret = SQLDriverConnect(dbc, NULL, (SQLCHAR *)connStr, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);
|
||||
check_error(ret, dbc, SQL_HANDLE_DBC, "Failed to connect to database");
|
||||
|
||||
printf("Connected to database successfully\n");
|
||||
|
||||
// Execute test queries
|
||||
for (int i = 0; i < QUERIES_SIZE; i++) {
|
||||
ret = SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
|
||||
check_error(ret, dbc, SQL_HANDLE_DBC, "Failed to allocate statement handle");
|
||||
|
||||
ret = SQLExecDirect(stmt, (SQLCHAR *)queries[i], SQL_NTS);
|
||||
if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
|
||||
printf("QUERY FAILED: %s\n", queries[i]);
|
||||
check_error(ret, stmt, SQL_HANDLE_STMT, "Query execution failed");
|
||||
}
|
||||
|
||||
// Fetch and discard results
|
||||
while (SQLFetch(stmt) == SQL_SUCCESS) {
|
||||
// Just consume the results
|
||||
}
|
||||
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
}
|
||||
|
||||
// Test prepared statements
|
||||
prepared_statement statements[] = {
|
||||
{
|
||||
.query = "select * from test where pk = ?",
|
||||
.num_params = 1,
|
||||
.pk_param = 1,
|
||||
.expect_prepare_error = 0,
|
||||
.expect_exec_error = 0,
|
||||
},
|
||||
{
|
||||
.query = "insert into test values (?, ?)",
|
||||
.num_params = 2,
|
||||
.pk_param = 1,
|
||||
.value_param = 1,
|
||||
.expect_prepare_error = 0,
|
||||
.expect_exec_error = 0,
|
||||
},
|
||||
{
|
||||
.query = "select * from test SYNTAX ERROR where pk = ?",
|
||||
.num_params = 1,
|
||||
.pk_param = 1,
|
||||
.expect_prepare_error = 1,
|
||||
.expect_exec_error = 0,
|
||||
},
|
||||
{NULL, 0, 0, 0, 0, 0}, // Sentinel
|
||||
};
|
||||
|
||||
for (int i = 0; statements[i].query; i++) {
|
||||
test_prepared_statement(dbc, &statements[i]);
|
||||
}
|
||||
|
||||
printf("All tests passed\n");
|
||||
|
||||
// Cleanup
|
||||
SQLDisconnect(dbc);
|
||||
SQLFreeHandle(SQL_HANDLE_DBC, dbc);
|
||||
SQLFreeHandle(SQL_HANDLE_ENV, env);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(DoltCxxConnectorTest
|
||||
VERSION 0.1
|
||||
DESCRIPTION "A smoke test for mysql-connector-c++ connecting to Dolt"
|
||||
LANGUAGES CXX)
|
||||
|
||||
add_executable(test_mysql_connector_cxx mysql-connector-cpp-test.cpp)
|
||||
set_property(TARGET test_mysql_connector_cxx PROPERTY CXX_STANDARD 11)
|
||||
|
||||
if(WITH_JDBC)
|
||||
add_subdirectory(third_party/mysql-connector-cpp EXCLUDE_FROM_ALL)
|
||||
target_link_libraries(test_mysql_connector_cxx connector-jdbc)
|
||||
else()
|
||||
find_library(LIBMYSQLCPPCONN "mysqlcppconn")
|
||||
target_link_libraries(test_mysql_connector_cxx "${LIBMYSQLCPPCONN}")
|
||||
endif()
|
||||
@@ -1,14 +1,26 @@
|
||||
MYSQL_CONCPP_DIR = /usr/local/Cellar/mysql-connector-c++/8.0.21
|
||||
CPPFLAGS = -I $(MYSQL_CONCPP_DIR)/include -L $(MYSQL_CONCPP_DIR)/lib64
|
||||
LDLIBS = -lmysqlcppconn8
|
||||
CXX = clang++ -stdlib=libc++
|
||||
CXXFLAGS = -std=c++11
|
||||
CXX = g++
|
||||
|
||||
all: mysql-connector-cpp-test
|
||||
CXXFLAGS_MYSQL = -I/usr/include/cppconn -std=c++11 -Wall -O2
|
||||
CXXFLAGS_MARIADB = -I/usr/include/mariadb -std=c++11 -Wall -O2
|
||||
|
||||
mysql-connector-cpp-test: mysql-connector-cpp-test.cpp
|
||||
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $@ $^ $(LDLIBS)
|
||||
LDFLAGS_MYSQL = -lmysqlcppconn
|
||||
LDFLAGS_MARIADB = -lmariadbcpp
|
||||
|
||||
TARGET_MYSQL = /build/bin/mysql-connector-test
|
||||
TARGET_MARIADB = /build/bin/mariadb-connector-test
|
||||
|
||||
SRCS_MYSQL = mysql-connector-test.cpp
|
||||
SRCS_MARIADB = mariadb-connector-test.cpp
|
||||
|
||||
all: $(TARGET_MYSQL) $(TARGET_MARIADB)
|
||||
|
||||
$(TARGET_MYSQL): $(SRCS_MYSQL)
|
||||
@mkdir -p /build/bin
|
||||
$(CXX) $(CXXFLAGS_MYSQL) -o $@ $^ $(LDFLAGS_MYSQL)
|
||||
|
||||
$(TARGET_MARIADB): $(SRCS_MARIADB)
|
||||
@mkdir -p /build/bin
|
||||
$(CXX) $(CXXFLAGS_MARIADB) -o $@ $^ $(LDFLAGS_MARIADB)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f mysql-connector-cpp-test
|
||||
rm -f $(TARGET_MYSQL) $(TARGET_MARIADB)
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
# General
|
||||
|
||||
This code uses git submodules. You need to recursively pull all the submodules
|
||||
in order for it to build.
|
||||
|
||||
# Building on OS X
|
||||
|
||||
```sh
|
||||
$ brew install cmake openssl mysql-client boost
|
||||
$ export PATH=/usr/local/Cellar/mysql-client/8.0.21/bin/:"$PATH"
|
||||
$ mkdir _build
|
||||
$ cd _build
|
||||
$ cmake .. -DWITH_SSL=/usr/local/Cellar/openssl@1.1/1.1.1g/ -DWITH_JDBC=yes
|
||||
$ make -j 10
|
||||
```
|
||||
|
||||
TODO: These instructions are coupled to openssl and mysql-client version that
|
||||
happen to be installed...
|
||||
|
||||
# Build on Ubuntu / Debian
|
||||
|
||||
```sh
|
||||
$ apt-get install g++ cmake libmysqlcppconn-dev
|
||||
$ mkdir _build
|
||||
$ cd _build
|
||||
$ cmake ..
|
||||
$ make -j 10
|
||||
```
|
||||
@@ -0,0 +1,88 @@
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
#include <mariadb/conncpp.hpp>
|
||||
|
||||
#define QUERIES_SIZE 14
|
||||
|
||||
std::string queries[QUERIES_SIZE] =
|
||||
{
|
||||
"create table test (pk int, `value` int, primary key(pk))",
|
||||
"describe test",
|
||||
"select * from test",
|
||||
"insert into test (pk, `value`) values (0,0)",
|
||||
"select * from test",
|
||||
"call dolt_add('-A');",
|
||||
"call dolt_commit('-m', 'my commit')",
|
||||
"select COUNT(*) FROM dolt_log",
|
||||
"call dolt_checkout('-b', 'mybranch')",
|
||||
"insert into test (pk, `value`) values (1,1)",
|
||||
"call dolt_commit('-a', '-m', 'my commit2')",
|
||||
"call dolt_checkout('main')",
|
||||
"call dolt_merge('mybranch')",
|
||||
"select COUNT(*) FROM dolt_log",
|
||||
};
|
||||
|
||||
int is_update[QUERIES_SIZE] = {1,0,0,1,0,0,0,0,0,1,0,0,0,0};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 4) {
|
||||
std::cerr << "Usage: " << argv[0] << " <user> <port> <database>" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string user = argv[1];
|
||||
std::string port = argv[2];
|
||||
std::string db = argv[3];
|
||||
|
||||
try {
|
||||
// Get the driver instance
|
||||
sql::Driver* driver = sql::mariadb::get_driver_instance();
|
||||
|
||||
// Create connection properties
|
||||
sql::SQLString url("jdbc:mariadb://127.0.0.1:" + port + "/" + db);
|
||||
sql::Properties properties({{"user", user}, {"password", ""}});
|
||||
|
||||
// Establish connection
|
||||
std::unique_ptr<sql::Connection> con(driver->connect(url, properties));
|
||||
|
||||
for ( int i = 0; i < QUERIES_SIZE; i++ ) {
|
||||
try {
|
||||
std::unique_ptr<sql::Statement> stmt(con->createStatement());
|
||||
|
||||
if ( is_update[i] ) {
|
||||
stmt->executeUpdate(queries[i]);
|
||||
} else {
|
||||
std::unique_ptr<sql::ResultSet> res(stmt->executeQuery(queries[i]));
|
||||
|
||||
// Assert that all columns have column name metadata populated
|
||||
sql::ResultSetMetaData* metadata = res->getMetaData();
|
||||
const uint32_t columnCount = metadata->getColumnCount();
|
||||
for (uint32_t columnIndex = 1; columnIndex <= columnCount; ++columnIndex) {
|
||||
sql::SQLString columnName = metadata->getColumnName(columnIndex);
|
||||
if (columnName.length() == 0) {
|
||||
std::cerr << "Column name is empty at index " << columnIndex << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (sql::SQLException &e) {
|
||||
std::cout << "QUERY: " << queries[i] << std::endl;
|
||||
std::cout << "# ERR: " << e.what();
|
||||
std::cout << " (MariaDB error code: " << e.getErrorCode();
|
||||
std::cout << ", SQLState: " << e.getSQLState() << " )" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
} catch (sql::SQLException &e) {
|
||||
std::cerr << "Connection error: " << e.what() << std::endl;
|
||||
std::cerr << " (MariaDB error code: " << e.getErrorCode();
|
||||
std::cerr << ", SQLState: " << e.getSQLState() << " )" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,11 @@ std::string queries[QUERIES_SIZE] =
|
||||
int is_update[QUERIES_SIZE] = {1,0,0,1,0,0,0,0,0,1,0,0,0,0};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 4) {
|
||||
std::cerr << "Usage: " << argv[0] << " <user> <port> <database>" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string user = argv[1];
|
||||
std::string port = argv[2];
|
||||
std::string db = argv[3];
|
||||
@@ -63,7 +68,7 @@ int main(int argc, char **argv) {
|
||||
sql::Statement *stmt = con->createStatement();
|
||||
|
||||
if ( is_update[i] ) {
|
||||
int affected_rows = stmt->executeUpdate(queries[i]);
|
||||
stmt->executeUpdate(queries[i]);
|
||||
} else {
|
||||
sql::ResultSet *res = stmt->executeQuery(queries[i]);
|
||||
|
||||
Submodule integration-tests/mysql-client-tests/cpp/third_party/mysql-connector-cpp deleted from 857a8d63d8
@@ -1,136 +0,0 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
.vs
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.log
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.Publish.xml
|
||||
*.pubxml
|
||||
*.azurePubxml
|
||||
|
||||
# NuGet Packages Directory
|
||||
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
|
||||
packages/
|
||||
## TODO: If the tool you use requires repositories.config, also uncomment the next line
|
||||
!packages/repositories.config
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
![Ss]tyle[Cc]op.targets
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.[Pp]ublish.xml
|
||||
|
||||
*.publishsettings
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
App_Data/*.mdf
|
||||
App_Data/*.ldf
|
||||
|
||||
# =========================
|
||||
# Windows detritus
|
||||
# =========================
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Mac desktop service store files
|
||||
.DS_Store
|
||||
|
||||
_NCrunch*
|
||||
ZZ
|
||||
@@ -3,6 +3,9 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AssemblyName>mysql-client-test</AssemblyName>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<SelfContained>true</SelfContained>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AssemblyName>mysql-connector-test</AssemblyName>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<SelfContained>true</SelfContained>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
defmodule Simple.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :simple,
|
||||
version: "0.1.0",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
{:myxql, "~> 0.5.0"},
|
||||
]
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
defmodule MySQLOTP.Application do
|
||||
use Application
|
||||
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
IO.puts("MySQLOTP.Application.start/2 called")
|
||||
|
||||
# This is a CLI app, so we run the command and exit
|
||||
# Start the application supervisor first
|
||||
children = []
|
||||
opts = [strategy: :one_for_one, name: MySQLOTP.Supervisor]
|
||||
|
||||
{:ok, pid} = Supervisor.start_link(children, opts)
|
||||
IO.puts("Supervisor started")
|
||||
|
||||
# Spawn a task to run the CLI command
|
||||
IO.puts("Spawning MySQLOTPTest.main/1")
|
||||
spawn(fn ->
|
||||
MySQLOTPTest.main([])
|
||||
end)
|
||||
|
||||
{:ok, pid}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
defmodule MySQLOTPTest do
|
||||
@moduledoc """
|
||||
Test for MySQL/OTP (Erlang native MySQL client)
|
||||
Uses the :mysql Erlang library
|
||||
"""
|
||||
|
||||
def main(_args \\ []) do
|
||||
IO.puts("Starting MySQL/OTP Test")
|
||||
|
||||
cli_args = Burrito.Util.Args.get_arguments()
|
||||
IO.puts("Received CLI args: #{inspect(cli_args)}")
|
||||
|
||||
result = run(cli_args)
|
||||
System.halt(0)
|
||||
result
|
||||
end
|
||||
|
||||
defp run(args) do
|
||||
if length(args) < 3 do
|
||||
IO.puts("Usage: mysql-otp-test <user> <port> <database>")
|
||||
System.halt(1)
|
||||
end
|
||||
|
||||
user = Enum.at(args, 0)
|
||||
port_str = Enum.at(args, 1)
|
||||
database = Enum.at(args, 2)
|
||||
|
||||
{port, _} = Integer.parse(port_str)
|
||||
|
||||
# Start MySQL/OTP connection using Erlang :mysql module
|
||||
{:ok, pid} = :mysql.start_link([
|
||||
host: '127.0.0.1',
|
||||
port: port,
|
||||
user: String.to_charlist(user),
|
||||
password: '',
|
||||
database: String.to_charlist(database)
|
||||
])
|
||||
|
||||
IO.puts("Connected using MySQL/OTP (Erlang connector)")
|
||||
|
||||
# Test queries
|
||||
queries = [
|
||||
"create table test (pk int, `value` int, primary key(pk))",
|
||||
"describe test",
|
||||
"select * from test",
|
||||
"insert into test (pk, `value`) values (0,0)",
|
||||
"select * from test",
|
||||
"call dolt_add('-A')",
|
||||
"call dolt_commit('-m', 'my commit')",
|
||||
"select COUNT(*) FROM dolt_log",
|
||||
"call dolt_checkout('-b', 'mybranch')",
|
||||
"insert into test (pk, `value`) values (1,1)",
|
||||
"call dolt_commit('-a', '-m', 'my commit2')",
|
||||
"call dolt_checkout('main')",
|
||||
"call dolt_merge('mybranch')",
|
||||
"select COUNT(*) FROM dolt_log"
|
||||
]
|
||||
|
||||
# Execute each query
|
||||
Enum.each(queries, fn query ->
|
||||
IO.puts("Executing: #{query}")
|
||||
|
||||
case :mysql.query(pid, query) do
|
||||
:ok ->
|
||||
IO.puts(" → OK")
|
||||
|
||||
{:ok, column_names, rows} ->
|
||||
IO.puts(" → #{length(rows)} row(s) returned with #{length(column_names)} column(s)")
|
||||
|
||||
{:error, reason} ->
|
||||
IO.puts("Query failed: #{query}")
|
||||
IO.puts("Error: #{inspect(reason)}")
|
||||
:mysql.stop(pid)
|
||||
System.halt(1)
|
||||
end
|
||||
end)
|
||||
|
||||
# Verify final log count
|
||||
{:ok, _columns, rows} = :mysql.query(pid, "select COUNT(*) FROM dolt_log")
|
||||
[[count]] = rows
|
||||
|
||||
if count != 3 do
|
||||
IO.puts("Expected 3 commits in dolt_log, got #{count}")
|
||||
:mysql.stop(pid)
|
||||
System.halt(1)
|
||||
end
|
||||
|
||||
:mysql.stop(pid)
|
||||
IO.puts("\nAll MySQL/OTP tests passed!")
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
43
integration-tests/mysql-client-tests/elixir/mysql/mix.exs
Normal file
43
integration-tests/mysql-client-tests/elixir/mysql/mix.exs
Normal file
@@ -0,0 +1,43 @@
|
||||
defmodule MySQLOTP.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :mysql_otp_test,
|
||||
version: "0.1.0",
|
||||
elixir: "~> 1.18",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps(),
|
||||
releases: releases()
|
||||
]
|
||||
end
|
||||
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger, :crypto, :public_key, :ssl],
|
||||
mod: {MySQLOTP.Application, []}
|
||||
]
|
||||
end
|
||||
|
||||
defp releases do
|
||||
[
|
||||
mysql_otp: [
|
||||
steps: [:assemble, &Burrito.wrap/1],
|
||||
burrito: [
|
||||
targets: [
|
||||
linux: [os: :linux, cpu: :x86_64]
|
||||
],
|
||||
no_native_archivers: true
|
||||
]
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
defp deps do
|
||||
[
|
||||
{:mysql, "~> 1.9.0"},
|
||||
{:burrito, "~> 1.4.0"}
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
defmodule SmokeTest do
|
||||
def myTestFunc(arg1, arg2) do
|
||||
if arg1 != arg2 do
|
||||
raise "Test error"
|
||||
end
|
||||
def main(_args \\ []) do
|
||||
IO.puts("Starting SmokeTest.main/1")
|
||||
|
||||
cli_args = Burrito.Util.Args.get_arguments()
|
||||
IO.puts("Received CLI args: #{inspect(cli_args)}")
|
||||
|
||||
result = run(cli_args)
|
||||
System.halt(0)
|
||||
result
|
||||
end
|
||||
|
||||
@spec run :: nil
|
||||
def run do
|
||||
args = System.argv()
|
||||
defp run(args) do
|
||||
if length(args) < 3 do
|
||||
IO.puts("Usage: simple <user> <port> <database>")
|
||||
System.halt(1)
|
||||
end
|
||||
|
||||
user = Enum.at(args, 0)
|
||||
{port, _} = Integer.parse(Enum.at(args, 1))
|
||||
port_str = Enum.at(args, 1)
|
||||
database = Enum.at(args, 2)
|
||||
|
||||
{port, _} = Integer.parse(port_str)
|
||||
|
||||
{:ok, pid} = MyXQL.start_link(username: user, port: port, database: database)
|
||||
{:ok, _} = MyXQL.query(pid, "drop table if exists test")
|
||||
@@ -21,8 +31,6 @@ defmodule SmokeTest do
|
||||
myTestFunc(result.num_rows, 0)
|
||||
|
||||
{:ok, _} = MyXQL.query(pid, "insert into test (pk, `value`) values (0,0)")
|
||||
|
||||
# MyXQL uses the CLIENT_FOUND_ROWS flag so we should return the number of rows matched
|
||||
{:ok, result} = MyXQL.query(pid, "UPDATE test SET pk = pk where pk = 0")
|
||||
myTestFunc(result.num_rows, 1)
|
||||
|
||||
@@ -31,7 +39,7 @@ defmodule SmokeTest do
|
||||
|
||||
{:ok, result} = MyXQL.query(pid, "SELECT * FROM test")
|
||||
myTestFunc(result.num_rows, 1)
|
||||
myTestFunc(result.rows, [[0,0]])
|
||||
myTestFunc(result.rows, [[0, 0]])
|
||||
|
||||
{:ok, _} = MyXQL.query(pid, "call dolt_add('-A');")
|
||||
{:ok, _} = MyXQL.query(pid, "call dolt_commit('-m', 'my commit')")
|
||||
@@ -45,5 +53,12 @@ defmodule SmokeTest do
|
||||
{:ok, result} = MyXQL.query(pid, "select COUNT(*) FROM dolt_log")
|
||||
myTestFunc(result.num_rows, 1)
|
||||
myTestFunc(result.rows, [[3]])
|
||||
:ok
|
||||
end
|
||||
|
||||
defp myTestFunc(arg1, arg2) do
|
||||
if arg1 != arg2 do
|
||||
raise "Test error: expected #{inspect(arg2)}, got #{inspect(arg1)}"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
defmodule Simple.Application do
|
||||
use Application
|
||||
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
IO.puts("Simple.Application.start/2 called")
|
||||
|
||||
# This is a CLI app, so we run the command and exit
|
||||
# Start the application supervisor first
|
||||
children = []
|
||||
opts = [strategy: :one_for_one, name: Simple.Supervisor]
|
||||
|
||||
{:ok, pid} = Supervisor.start_link(children, opts)
|
||||
IO.puts("Supervisor started")
|
||||
|
||||
# Spawn a task to run the CLI command
|
||||
IO.puts("Spawning SmokeTest.main/1")
|
||||
spawn(fn ->
|
||||
SmokeTest.main([])
|
||||
end)
|
||||
|
||||
{:ok, pid}
|
||||
end
|
||||
end
|
||||
|
||||
43
integration-tests/mysql-client-tests/elixir/myxql/mix.exs
Normal file
43
integration-tests/mysql-client-tests/elixir/myxql/mix.exs
Normal file
@@ -0,0 +1,43 @@
|
||||
defmodule Simple.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :simple,
|
||||
version: "0.1.0",
|
||||
elixir: "~> 1.18",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps(),
|
||||
releases: releases()
|
||||
]
|
||||
end
|
||||
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger, :crypto, :public_key, :ssl],
|
||||
mod: {Simple.Application, []}
|
||||
]
|
||||
end
|
||||
|
||||
defp releases do
|
||||
[
|
||||
simple: [
|
||||
steps: [:assemble, &Burrito.wrap/1],
|
||||
burrito: [
|
||||
targets: [
|
||||
linux: [os: :linux, cpu: :x86_64]
|
||||
],
|
||||
no_native_archivers: true
|
||||
]
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
defp deps do
|
||||
[
|
||||
{:myxql, "~> 0.5.0"},
|
||||
{:burrito, "~> 1.4.0"}
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
117
integration-tests/mysql-client-tests/java/pom.xml
Normal file
117
integration-tests/mysql-client-tests/java/pom.xml
Normal file
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.dolthub</groupId>
|
||||
<artifactId>j</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>8.0.33</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mariadb.jdbc</groupId>
|
||||
<artifactId>mariadb-java-client</artifactId>
|
||||
<version>3.5.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mariadb</groupId>
|
||||
<artifactId>r2dbc-mariadb</artifactId>
|
||||
<version>1.2.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>mysql-connector-test</id>
|
||||
<phase>package</phase>
|
||||
<goals><goal>shade</goal></goals>
|
||||
<configuration>
|
||||
<finalName>mysql-connector-test</finalName>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>MySQLConnectorTest</mainClass>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
<execution>
|
||||
<id>mysql-connector-test-collation</id>
|
||||
<phase>package</phase>
|
||||
<goals><goal>shade</goal></goals>
|
||||
<configuration>
|
||||
<finalName>mysql-connector-test-collation</finalName>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>MySQLConnectorTest_Collation</mainClass>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
<execution>
|
||||
<id>mariadb-connector-test</id>
|
||||
<phase>package</phase>
|
||||
<goals><goal>shade</goal></goals>
|
||||
<configuration>
|
||||
<finalName>mariadb-connector-test</finalName>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>MariaDBConnectorTest</mainClass>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
<execution>
|
||||
<id>mariadb-R2DBC-test</id>
|
||||
<phase>package</phase>
|
||||
<goals><goal>shade</goal></goals>
|
||||
<configuration>
|
||||
<finalName>mariadb-R2DBC-test</finalName>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>MariaDBR2DBCTest</mainClass>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,179 @@
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MariaDBConnectorTest {
|
||||
|
||||
// TestCase represents a single query test case
|
||||
static class TestCase {
|
||||
public String query;
|
||||
public String expectedResult;
|
||||
public Object fieldAccessor; // String (column name) or Integer (field position)
|
||||
|
||||
public TestCase(String query, String expectedResult, Object fieldAccessor) {
|
||||
this.query = query;
|
||||
this.expectedResult = expectedResult;
|
||||
this.fieldAccessor = fieldAccessor;
|
||||
}
|
||||
}
|
||||
|
||||
// test queries to be run against Dolt
|
||||
private static final TestCase[] testCases = {
|
||||
new TestCase("create table test (pk int, `value` int, primary key(pk))", "0", 1),
|
||||
new TestCase("describe test", "pk", 1),
|
||||
new TestCase("select * from test", null, "pk"),
|
||||
new TestCase("insert into test (pk, `value`) values (0,0)", "1", 1),
|
||||
new TestCase("select * from test", "0", "test.pk"),
|
||||
new TestCase("call dolt_add('-A')", "0", 1),
|
||||
new TestCase("call dolt_commit('-m', 'my commit')", "0", 1),
|
||||
new TestCase("select COUNT(*) FROM dolt_log", "2", 1),
|
||||
new TestCase("call dolt_checkout('-b', 'mybranch')", "0", 1),
|
||||
new TestCase("insert into test (pk, `value`) values (1,1)", "1", 1),
|
||||
new TestCase("call dolt_commit('-a', '-m', 'my commit2')", "1", 1),
|
||||
new TestCase("call dolt_checkout('main')", "0", 1),
|
||||
new TestCase("call dolt_merge('mybranch')", "", 1),
|
||||
new TestCase("select COUNT(*) FROM dolt_log", "3", "COUNT(*)"),
|
||||
};
|
||||
|
||||
public static void main(String[] args) {
|
||||
testStatements(args);
|
||||
testServerSideCursors(args);
|
||||
testCollation(args);
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
// testServerSideCursors does a simple smoke test with server-side cursors to make sure
|
||||
// results can be read. Note that we don't test results here; this is just a high level
|
||||
// smoke test that we can execute server-side cursors logic without the server erroring out.
|
||||
// This test was added for a regression where server-side cursor logic was getting
|
||||
// corrupted result set memory and sending invalid data to the client, which caused the
|
||||
// server to error out and crash the connection. If any errors are encountered, a stack trace
|
||||
// is printed and this function exits with a non-zero code.
|
||||
// For more details, see: https://github.com/dolthub/dolt/issues/9125
|
||||
private static void testServerSideCursors(String[] args) {
|
||||
String user = args[0];
|
||||
String port = args[1];
|
||||
String db = args[2];
|
||||
|
||||
try {
|
||||
// MariaDB JDBC URL format
|
||||
String url = "jdbc:mariadb://127.0.0.1:" + port + "/" + db +
|
||||
"?useCursorFetch=true";
|
||||
Connection conn = DriverManager.getConnection(url, user, "");
|
||||
|
||||
executePreparedQuery(conn, "SELECT 1;");
|
||||
executePreparedQuery(conn, "SELECT database();");
|
||||
executePreparedQuery(conn, "SHOW COLLATION;");
|
||||
executePreparedQuery(conn, "SHOW COLLATION;");
|
||||
executePreparedQuery(conn, "SHOW COLLATION;");
|
||||
} catch (SQLException ex) {
|
||||
System.out.println("An error occurred.");
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// executePreparedQuery executes the specified |query| using |conn| as a prepared statement,
|
||||
// and uses server-side cursor to fetch results. This method does not do any validation of
|
||||
// results from the query. It is simply a smoke test to ensure the connection doesn't crash.
|
||||
private static void executePreparedQuery(Connection conn, String query) throws SQLException {
|
||||
PreparedStatement stmt = conn.prepareStatement(query, ResultSet.TYPE_FORWARD_ONLY);
|
||||
stmt.setFetchSize(25); // needed to ensure a server-side cursor is used
|
||||
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
while (rs.next()) {}
|
||||
|
||||
rs.close();
|
||||
stmt.close();
|
||||
}
|
||||
|
||||
// testStatements executes the queries from |queries| and asserts their results from
|
||||
// |expectedResults|. If any errors are encountered, a stack trace is printed and this
|
||||
// function exits with a non-zero code.
|
||||
private static void testStatements(String[] args) {
|
||||
Connection conn = null;
|
||||
|
||||
String user = args[0];
|
||||
String port = args[1];
|
||||
String db = args[2];
|
||||
|
||||
try {
|
||||
// MariaDB JDBC URL format
|
||||
String url = "jdbc:mariadb://127.0.0.1:" + port + "/" + db;
|
||||
String password = "";
|
||||
|
||||
conn = DriverManager.getConnection(url, user, password);
|
||||
Statement st = conn.createStatement();
|
||||
|
||||
for (TestCase test : testCases) {
|
||||
if ( st.execute(test.query) ) {
|
||||
ResultSet rs = st.getResultSet();
|
||||
if (rs.next()) {
|
||||
String result = "";
|
||||
if (test.fieldAccessor instanceof String) {
|
||||
result = rs.getString((String)test.fieldAccessor);
|
||||
} else if (test.fieldAccessor instanceof Integer) {
|
||||
result = rs.getString((Integer)test.fieldAccessor);
|
||||
} else {
|
||||
System.out.println("Unsupported field accessor value: " + test.fieldAccessor);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (!Objects.equals(test.expectedResult, result) &&
|
||||
!(test.query.contains("dolt_commit")) &&
|
||||
!(test.query.contains("dolt_merge"))) {
|
||||
System.out.println("Query: \n" + test.query);
|
||||
System.out.println("Expected:\n" + test.expectedResult);
|
||||
System.out.println("Result:\n" + result);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String result = Integer.toString(st.getUpdateCount());
|
||||
if ( !Objects.equals(test.expectedResult, result) ) {
|
||||
System.out.println("Query: \n" + test.query);
|
||||
System.out.println("Expected:\n" + test.expectedResult);
|
||||
System.out.println("Rows Updated:\n" + result);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
System.out.println("An error occurred.");
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// testCollation tests that metadata queries work properly with collations.
|
||||
// This is a regression test for https://github.com/dolthub/dolt/issues/9890
|
||||
private static void testCollation(String[] args) {
|
||||
String user = args[0];
|
||||
String port = args[1];
|
||||
String db = args[2];
|
||||
|
||||
try {
|
||||
// MariaDB JDBC URL format
|
||||
String url = "jdbc:mariadb://127.0.0.1:" + port + "/" + db;
|
||||
Connection conn = DriverManager.getConnection(url, user, "");
|
||||
|
||||
// This should not throw an exception
|
||||
ResultSet result = conn.getMetaData().getColumns(null, null, null, null);
|
||||
|
||||
// Close the result set
|
||||
if (result != null) {
|
||||
result.close();
|
||||
}
|
||||
conn.close();
|
||||
} catch (SQLException ex) {
|
||||
System.out.println("Collation test failed.");
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import io.r2dbc.spi.ConnectionFactoryOptions;
|
||||
import io.r2dbc.spi.ConnectionFactories;
|
||||
import io.r2dbc.spi.Result;
|
||||
import org.mariadb.r2dbc.MariadbConnectionConfiguration;
|
||||
import org.mariadb.r2dbc.MariadbConnectionFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public class MariaDBR2DBCTest {
|
||||
|
||||
// TestCase represents a single query test case
|
||||
static class TestCase {
|
||||
public String query;
|
||||
public boolean isUpdate;
|
||||
|
||||
public TestCase(String query, boolean isUpdate) {
|
||||
this.query = query;
|
||||
this.isUpdate = isUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
// test queries to be run against Dolt
|
||||
private static final TestCase[] testCases = {
|
||||
new TestCase("create table test (pk int, `value` int, primary key(pk))", true),
|
||||
new TestCase("insert into test (pk, `value`) values (0,0)", true),
|
||||
new TestCase("select * from test", false),
|
||||
new TestCase("call dolt_add('-A')", false),
|
||||
new TestCase("call dolt_commit('-m', 'my commit')", false),
|
||||
new TestCase("select COUNT(*) FROM dolt_log", false),
|
||||
new TestCase("call dolt_checkout('-b', 'mybranch')", false),
|
||||
new TestCase("insert into test (pk, `value`) values (1,1)", true),
|
||||
new TestCase("call dolt_commit('-a', '-m', 'my commit2')", false),
|
||||
new TestCase("call dolt_checkout('main')", false),
|
||||
new TestCase("call dolt_merge('mybranch')", false),
|
||||
new TestCase("select COUNT(*) FROM dolt_log", false),
|
||||
};
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length < 3) {
|
||||
System.err.println("Usage: MariaDBR2DBCTest <user> <port> <database>");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
String user = args[0];
|
||||
int port = Integer.parseInt(args[1]);
|
||||
String database = args[2];
|
||||
|
||||
try {
|
||||
runTests(user, port, database);
|
||||
System.out.println("All R2DBC tests passed!");
|
||||
} catch (Exception e) {
|
||||
System.err.println("R2DBC test failed: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void runTests(String user, int port, String database) {
|
||||
// Create connection configuration
|
||||
MariadbConnectionConfiguration config = MariadbConnectionConfiguration.builder()
|
||||
.host("127.0.0.1")
|
||||
.port(port)
|
||||
.username(user)
|
||||
.password("")
|
||||
.database(database)
|
||||
.build();
|
||||
|
||||
ConnectionFactory connectionFactory = new MariadbConnectionFactory(config);
|
||||
|
||||
// Run tests reactively - block at the end for the test
|
||||
Mono.from(connectionFactory.create())
|
||||
.flatMapMany(connection ->
|
||||
Flux.fromArray(testCases)
|
||||
.concatMap(testCase -> executeTest(connection, testCase))
|
||||
.doFinally(signalType ->
|
||||
Mono.from(connection.close()).subscribe()
|
||||
)
|
||||
)
|
||||
.blockLast(Duration.ofSeconds(30));
|
||||
}
|
||||
|
||||
private static Mono<Void> executeTest(Connection connection, TestCase testCase) {
|
||||
System.out.println("Executing: " + testCase.query);
|
||||
|
||||
return Mono.from(connection.createStatement(testCase.query).execute())
|
||||
.flatMap(result -> {
|
||||
if (testCase.isUpdate) {
|
||||
// For updates, just get the rows affected
|
||||
return Mono.from(result.getRowsUpdated()).then();
|
||||
} else {
|
||||
// For selects, consume all rows
|
||||
return Flux.from(result.map((row, metadata) -> row))
|
||||
.then();
|
||||
}
|
||||
})
|
||||
.onErrorResume(e -> {
|
||||
System.err.println("Error executing query: " + testCase.query);
|
||||
System.err.println("Error: " + e.getMessage());
|
||||
return Mono.error(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,173 +1,173 @@
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
|
||||
public class MySQLConnectorTest {
|
||||
|
||||
// test queries to be run against Dolt
|
||||
private static final String[] queries = {
|
||||
"create table test (pk int, `value` int, primary key(pk))",
|
||||
"describe test",
|
||||
"select * from test",
|
||||
"insert into test (pk, `value`) values (0,0)",
|
||||
"select * from test",
|
||||
"call dolt_add('-A')",
|
||||
"call dolt_commit('-m', 'my commit')",
|
||||
"select COUNT(*) FROM dolt_log",
|
||||
"call dolt_checkout('-b', 'mybranch')",
|
||||
"insert into test (pk, `value`) values (1,1)",
|
||||
"call dolt_commit('-a', '-m', 'my commit2')",
|
||||
"call dolt_checkout('main')",
|
||||
"call dolt_merge('mybranch')",
|
||||
"select COUNT(*) FROM dolt_log",
|
||||
};
|
||||
|
||||
// We currently only test a single field value in the first row
|
||||
private static final String[] expectedResults = {
|
||||
"0",
|
||||
"pk",
|
||||
null,
|
||||
"1",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"2",
|
||||
"0",
|
||||
"1",
|
||||
"1",
|
||||
"0",
|
||||
"",
|
||||
"3"
|
||||
};
|
||||
|
||||
// fieldAccessors are the value used to access a field in a row in a result set. Currently, only
|
||||
// String (i.e column name) and Integer (i.e. field position) values are supported.
|
||||
private static final Object[] fieldAccessors = {
|
||||
1,
|
||||
1,
|
||||
"pk",
|
||||
1,
|
||||
"test.pk",
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
"COUNT(*)",
|
||||
};
|
||||
|
||||
public static void main(String[] args) {
|
||||
testStatements(args);
|
||||
testServerSideCursors(args);
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
// testServerSideCursors does a simple smoke test with server-side cursors to make sure
|
||||
// results can be read. Note that we don't test results here; this is just a high level
|
||||
// smoke test that we can execute server-side cursors logic without the server erroring out.
|
||||
// This test was added for a regression where server-side cursor logic was getting
|
||||
// corrupted result set memory and sending invalid data to the client, which caused the
|
||||
// server to error out and crash the connection. If any errors are encountered, a stack trace
|
||||
// is printed and this function exits with a non-zero code.
|
||||
// For more details, see: https://github.com/dolthub/dolt/issues/9125
|
||||
private static void testServerSideCursors(String[] args) {
|
||||
String user = args[0];
|
||||
String port = args[1];
|
||||
String db = args[2];
|
||||
|
||||
try {
|
||||
String url = "jdbc:mysql://127.0.0.1:" + port + "/" + db +
|
||||
"?useServerPrepStmts=true&useCursorFetch=true";
|
||||
Connection conn = DriverManager.getConnection(url, user, "");
|
||||
|
||||
executePreparedQuery(conn, "SELECT 1;");
|
||||
executePreparedQuery(conn, "SELECT database();");
|
||||
executePreparedQuery(conn, "SHOW COLLATION;");
|
||||
executePreparedQuery(conn, "SHOW COLLATION;");
|
||||
executePreparedQuery(conn, "SHOW COLLATION;");
|
||||
} catch (SQLException ex) {
|
||||
System.out.println("An error occurred.");
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// executePreparedQuery executes the specified |query| using |conn| as a prepared statement,
|
||||
// and uses server-side cursor to fetch results. This method does not do any validation of
|
||||
// results from the query. It is simply a smoke test to ensure the connection doesn't crash.
|
||||
private static void executePreparedQuery(Connection conn, String query) throws SQLException {
|
||||
PreparedStatement stmt = conn.prepareStatement(query, ResultSet.TYPE_FORWARD_ONLY);
|
||||
stmt.setFetchSize(25); // needed to ensure a server-side cursor is used
|
||||
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
while (rs.next()) {}
|
||||
|
||||
rs.close();
|
||||
stmt.close();
|
||||
}
|
||||
|
||||
// testStatements executes the queries from |queries| and asserts their results from
|
||||
// |expectedResults|. If any errors are encountered, a stack trace is printed and this
|
||||
// function exits with a non-zero code.
|
||||
private static void testStatements(String[] args) {
|
||||
Connection conn = null;
|
||||
|
||||
String user = args[0];
|
||||
String port = args[1];
|
||||
String db = args[2];
|
||||
|
||||
try {
|
||||
String url = "jdbc:mysql://127.0.0.1:" + port + "/" + db;
|
||||
String password = "";
|
||||
|
||||
conn = DriverManager.getConnection(url, user, password);
|
||||
Statement st = conn.createStatement();
|
||||
|
||||
for (int i = 0; i < queries.length; i++) {
|
||||
String query = queries[i];
|
||||
String expected = expectedResults[i];
|
||||
if ( st.execute(query) ) {
|
||||
ResultSet rs = st.getResultSet();
|
||||
if (rs.next()) {
|
||||
String result = "";
|
||||
Object fieldAccessor = fieldAccessors[i];
|
||||
if (fieldAccessor instanceof String) {
|
||||
result = rs.getString((String)fieldAccessor);
|
||||
} else if (fieldAccessor instanceof Integer) {
|
||||
result = rs.getString((Integer)fieldAccessor);
|
||||
} else {
|
||||
System.out.println("Unsupported field accessor value: " + fieldAccessor);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (!expected.equals(result) && !(query.contains("dolt_commit")) && !(query.contains("dolt_merge"))) {
|
||||
System.out.println("Query: \n" + query);
|
||||
System.out.println("Expected:\n" + expected);
|
||||
System.out.println("Result:\n" + result);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String result = Integer.toString(st.getUpdateCount());
|
||||
if ( !expected.equals(result) ) {
|
||||
System.out.println("Query: \n" + query);
|
||||
System.out.println("Expected:\n" + expected);
|
||||
System.out.println("Rows Updated:\n" + result);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
System.out.println("An error occurred.");
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
|
||||
public class MySQLConnectorTest {
|
||||
|
||||
// test queries to be run against Dolt
|
||||
private static final String[] queries = {
|
||||
"create table test (pk int, `value` int, primary key(pk))",
|
||||
"describe test",
|
||||
"select * from test",
|
||||
"insert into test (pk, `value`) values (0,0)",
|
||||
"select * from test",
|
||||
"call dolt_add('-A')",
|
||||
"call dolt_commit('-m', 'my commit')",
|
||||
"select COUNT(*) FROM dolt_log",
|
||||
"call dolt_checkout('-b', 'mybranch')",
|
||||
"insert into test (pk, `value`) values (1,1)",
|
||||
"call dolt_commit('-a', '-m', 'my commit2')",
|
||||
"call dolt_checkout('main')",
|
||||
"call dolt_merge('mybranch')",
|
||||
"select COUNT(*) FROM dolt_log",
|
||||
};
|
||||
|
||||
// We currently only test a single field value in the first row
|
||||
private static final String[] expectedResults = {
|
||||
"0",
|
||||
"pk",
|
||||
null,
|
||||
"1",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"2",
|
||||
"0",
|
||||
"1",
|
||||
"1",
|
||||
"0",
|
||||
"",
|
||||
"3"
|
||||
};
|
||||
|
||||
// fieldAccessors are the value used to access a field in a row in a result set. Currently, only
|
||||
// String (i.e column name) and Integer (i.e. field position) values are supported.
|
||||
private static final Object[] fieldAccessors = {
|
||||
1,
|
||||
1,
|
||||
"pk",
|
||||
1,
|
||||
"test.pk",
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
"COUNT(*)",
|
||||
};
|
||||
|
||||
public static void main(String[] args) {
|
||||
testStatements(args);
|
||||
testServerSideCursors(args);
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
// testServerSideCursors does a simple smoke test with server-side cursors to make sure
|
||||
// results can be read. Note that we don't test results here; this is just a high level
|
||||
// smoke test that we can execute server-side cursors logic without the server erroring out.
|
||||
// This test was added for a regression where server-side cursor logic was getting
|
||||
// corrupted result set memory and sending invalid data to the client, which caused the
|
||||
// server to error out and crash the connection. If any errors are encountered, a stack trace
|
||||
// is printed and this function exits with a non-zero code.
|
||||
// For more details, see: https://github.com/dolthub/dolt/issues/9125
|
||||
private static void testServerSideCursors(String[] args) {
|
||||
String user = args[0];
|
||||
String port = args[1];
|
||||
String db = args[2];
|
||||
|
||||
try {
|
||||
String url = "jdbc:mysql://127.0.0.1:" + port + "/" + db +
|
||||
"?useServerPrepStmts=true&useCursorFetch=true";
|
||||
Connection conn = DriverManager.getConnection(url, user, "");
|
||||
|
||||
executePreparedQuery(conn, "SELECT 1;");
|
||||
executePreparedQuery(conn, "SELECT database();");
|
||||
executePreparedQuery(conn, "SHOW COLLATION;");
|
||||
executePreparedQuery(conn, "SHOW COLLATION;");
|
||||
executePreparedQuery(conn, "SHOW COLLATION;");
|
||||
} catch (SQLException ex) {
|
||||
System.out.println("An error occurred.");
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// executePreparedQuery executes the specified |query| using |conn| as a prepared statement,
|
||||
// and uses server-side cursor to fetch results. This method does not do any validation of
|
||||
// results from the query. It is simply a smoke test to ensure the connection doesn't crash.
|
||||
private static void executePreparedQuery(Connection conn, String query) throws SQLException {
|
||||
PreparedStatement stmt = conn.prepareStatement(query, ResultSet.TYPE_FORWARD_ONLY);
|
||||
stmt.setFetchSize(25); // needed to ensure a server-side cursor is used
|
||||
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
while (rs.next()) {}
|
||||
|
||||
rs.close();
|
||||
stmt.close();
|
||||
}
|
||||
|
||||
// testStatements executes the queries from |queries| and asserts their results from
|
||||
// |expectedResults|. If any errors are encountered, a stack trace is printed and this
|
||||
// function exits with a non-zero code.
|
||||
private static void testStatements(String[] args) {
|
||||
Connection conn = null;
|
||||
|
||||
String user = args[0];
|
||||
String port = args[1];
|
||||
String db = args[2];
|
||||
|
||||
try {
|
||||
String url = "jdbc:mysql://127.0.0.1:" + port + "/" + db;
|
||||
String password = "";
|
||||
|
||||
conn = DriverManager.getConnection(url, user, password);
|
||||
Statement st = conn.createStatement();
|
||||
|
||||
for (int i = 0; i < queries.length; i++) {
|
||||
String query = queries[i];
|
||||
String expected = expectedResults[i];
|
||||
if ( st.execute(query) ) {
|
||||
ResultSet rs = st.getResultSet();
|
||||
if (rs.next()) {
|
||||
String result = "";
|
||||
Object fieldAccessor = fieldAccessors[i];
|
||||
if (fieldAccessor instanceof String) {
|
||||
result = rs.getString((String)fieldAccessor);
|
||||
} else if (fieldAccessor instanceof Integer) {
|
||||
result = rs.getString((Integer)fieldAccessor);
|
||||
} else {
|
||||
System.out.println("Unsupported field accessor value: " + fieldAccessor);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (!expected.equals(result) && !(query.contains("dolt_commit")) && !(query.contains("dolt_merge"))) {
|
||||
System.out.println("Query: \n" + query);
|
||||
System.out.println("Expected:\n" + expected);
|
||||
System.out.println("Result:\n" + result);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String result = Integer.toString(st.getUpdateCount());
|
||||
if ( !expected.equals(result) ) {
|
||||
System.out.println("Query: \n" + query);
|
||||
System.out.println("Expected:\n" + expected);
|
||||
System.out.println("Rows Updated:\n" + result);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
System.out.println("An error occurred.");
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,4 +7,4 @@ dolt config --global --add user.name mysql-test-runner
|
||||
dolt config --global --add user.email mysql-test-runner@liquidata.co
|
||||
|
||||
echo "Running mysql-client-tests:"
|
||||
bats /mysql-client-tests/mysql-client-tests.bats
|
||||
bats /build/bin/mysql-client-tests.bats
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bats
|
||||
load $BATS_TEST_DIRNAME/helpers.bash
|
||||
load /build/bin/helpers.bash
|
||||
|
||||
# MySQL client tests are set up to test Dolt as a MySQL server and
|
||||
# standard MySQL Clients in a wide array of languages. I used BATS because
|
||||
@@ -25,107 +25,112 @@ teardown() {
|
||||
fi
|
||||
}
|
||||
|
||||
@test "go go-sql-drive/mysql test" {
|
||||
(cd $BATS_TEST_DIRNAME/go; go build .)
|
||||
$BATS_TEST_DIRNAME/go/go $USER $PORT $REPO_NAME
|
||||
@test "go go-sql-driver/mysql" {
|
||||
/build/bin/go/sql-driver-mysql-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "go go-mysql test" {
|
||||
(cd $BATS_TEST_DIRNAME/go-mysql; go build .)
|
||||
$BATS_TEST_DIRNAME/go-mysql/go $USER $PORT $REPO_NAME
|
||||
@test "go go-mysql" {
|
||||
/build/bin/go/mysql-client-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "python mysql.connector client" {
|
||||
python3.9 $BATS_TEST_DIRNAME/python/mysql.connector-test.py $USER $PORT $REPO_NAME
|
||||
@test "python mysql.connector" {
|
||||
/build/bin/python/mysql-connector-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "python mariadb connector" {
|
||||
/build/bin/python/mariadb-connector-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "python pymysql client" {
|
||||
python3.9 $BATS_TEST_DIRNAME/python/pymysql-test.py $USER $PORT $REPO_NAME
|
||||
/build/bin/python/pymysql-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "python sqlachemy client" {
|
||||
python3.9 $BATS_TEST_DIRNAME/python/sqlalchemy-test.py $USER $PORT $REPO_NAME
|
||||
/build/bin/python/sqlalchemy-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "mysql-connector-java client" {
|
||||
javac $BATS_TEST_DIRNAME/java/MySQLConnectorTest.java
|
||||
java -cp $BATS_TEST_DIRNAME/java:$BATS_TEST_DIRNAME/java/mysql-connector-java-8.0.21.jar MySQLConnectorTest $USER $PORT $REPO_NAME
|
||||
@test "java mysql-connector-j" {
|
||||
java -jar /build/bin/java/mysql-connector-test.jar $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "mysql-connector-java client collations" {
|
||||
javac $BATS_TEST_DIRNAME/java/MySQLConnectorTest_Collation.java
|
||||
java -cp $BATS_TEST_DIRNAME/java:$BATS_TEST_DIRNAME/java/mysql-connector-java-8.0.21.jar MySQLConnectorTest_Collation $USER $PORT $REPO_NAME
|
||||
@test "java mysql-connector-j collation" {
|
||||
java -jar /build/bin/java/mysql-connector-test-collation.jar $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "java mariadb-java-client" {
|
||||
java -jar /build/bin/java/mariadb-connector-test.jar $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "java r2dbc-mariadb connector" {
|
||||
java -jar /build/bin/java/mariadb-R2DBC-test.jar $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "node mysql client" {
|
||||
node $BATS_TEST_DIRNAME/node/index.js $USER $PORT $REPO_NAME
|
||||
node $BATS_TEST_DIRNAME/node/knex.js $USER $PORT $REPO_NAME
|
||||
node /build/bin/node/index.js $USER $PORT $REPO_NAME
|
||||
node /build/bin/node/knex.js $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "node mariadb connector" {
|
||||
node /build/bin/node/mariadb-connector.js $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "node mysql client, hosted workbench stability" {
|
||||
node $BATS_TEST_DIRNAME/node/workbench.js $USER $PORT $REPO_NAME $BATS_TEST_DIRNAME/node/testdata
|
||||
node /build/bin/node/workbench.js $USER $PORT $REPO_NAME /build/bin/node/testdata
|
||||
}
|
||||
|
||||
@test "c mysql connector" {
|
||||
(cd $BATS_TEST_DIRNAME/c; make clean; make)
|
||||
$BATS_TEST_DIRNAME/c/mysql-connector-c-test $USER $PORT $REPO_NAME
|
||||
@test "c mysql client" {
|
||||
/build/bin/c/mysql-client-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "c mariadb client" {
|
||||
/build/bin/c/mariadb-client-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "c mariadb odbc connector" {
|
||||
/build/bin/c/mariadb-odbc-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "cpp mysql connector" {
|
||||
if [ -d $BATS_TEST_DIRNAME/cpp/_build ]
|
||||
then
|
||||
rm -rf $BATS_TEST_DIRNAME/cpp/_build/*
|
||||
else
|
||||
mkdir $BATS_TEST_DIRNAME/cpp/_build
|
||||
fi
|
||||
cd $BATS_TEST_DIRNAME/cpp/_build
|
||||
if [[ `uname` = "Darwin" ]]; then
|
||||
PATH=/usr/local/Cellar/mysql-client/8.0.21/bin/:"$PATH" cmake .. -DWITH_SSL=/usr/local/Cellar/openssl@1.1/1.1.1g/ -DWITH_JDBC=yes;
|
||||
else
|
||||
cmake ..
|
||||
fi
|
||||
cmake ..
|
||||
make -j 10
|
||||
$BATS_TEST_DIRNAME/cpp/_build/test_mysql_connector_cxx $USER $PORT $REPO_NAME
|
||||
cd -
|
||||
/build/bin/cpp/mysql-connector-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "cpp mariadb connector" {
|
||||
/build/bin/cpp/mariadb-connector-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "dotnet mysql connector" {
|
||||
cd $BATS_TEST_DIRNAME/dotnet/MySqlConnector
|
||||
# dotnet run uses output channel 3 which conflicts with bats so we pipe it to null
|
||||
dotnet run -- $USER $PORT $REPO_NAME 3>&-
|
||||
/build/bin/dotnet/mysql-connector-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "dotnet mysql client" {
|
||||
cd $BATS_TEST_DIRNAME/dotnet/MySqlClient
|
||||
# dotnet run uses output channel 3 which conflicts with bats so we pipe it to null
|
||||
dotnet run -- $USER $PORT $REPO_NAME 3>&-
|
||||
/build/bin/dotnet/mysql-client-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "perl DBD:mysql client" {
|
||||
perl $BATS_TEST_DIRNAME/perl/dbd-mysql-test.pl $USER $PORT $REPO_NAME
|
||||
perl /build/bin/perl/dbd-mysql-test.pl $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "ruby ruby/mysql test" {
|
||||
ruby $BATS_TEST_DIRNAME/ruby/ruby-mysql-test.rb $USER $PORT $REPO_NAME
|
||||
@test "perl DBD:MariaDB client" {
|
||||
perl /build/bin/perl/dbd-mariadb-test.pl $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "ruby mysql2 test" {
|
||||
ruby $BATS_TEST_DIRNAME/ruby/mysql2-test.rb $USER $PORT $REPO_NAME
|
||||
@test "ruby ruby/mysql client" {
|
||||
ruby /build/bin/ruby/mysql-client-test.rb $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "elixir myxql test" {
|
||||
cd $BATS_TEST_DIRNAME/elixir/
|
||||
# install some mix dependencies
|
||||
mix local.hex --force
|
||||
mix local.rebar --force
|
||||
mix deps.get
|
||||
|
||||
# run the test
|
||||
mix run -e "IO.puts(SmokeTest.run())" $USER $PORT $REPO_NAME
|
||||
@test "ruby mysql2" {
|
||||
ruby /build/bin/ruby/mysql2-test.rb $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "mysqldump works" {
|
||||
@test "elixir myxql" {
|
||||
/build/bin/elixir/myxql-driver-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "elixir mysql-otp" {
|
||||
/build/bin/elixir/mysql-otp-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "mysqldump" {
|
||||
mysqldump $REPO_NAME -P $PORT -h 0.0.0.0 -u $USER
|
||||
}
|
||||
|
||||
@@ -163,27 +168,26 @@ EOF" -m "postgres"
|
||||
}
|
||||
|
||||
@test "R RMySQL client" {
|
||||
Rscript $BATS_TEST_DIRNAME/r/rmysql-test.r $USER $PORT $REPO_NAME
|
||||
Rscript /build/bin/r/rmysql-test.r $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "R RMariaDB client" {
|
||||
skip "Error loading RMariaDB library"
|
||||
# ex: https://github.com/dolthub/dolt/actions/runs/4428743682/jobs/7770282852
|
||||
Rscript $BATS_TEST_DIRNAME/r/rmariadb-test.r $USER $PORT $REPO_NAME
|
||||
Rscript /build/bin/r/rmariadb-test.r $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "rust mysql client" {
|
||||
cd $BATS_TEST_DIRNAME/rust
|
||||
./target/debug/mysql_connector_test $USER $PORT $REPO_NAME
|
||||
/build/bin/rust/mysql-client-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "swift perfect-mariadb client" {
|
||||
/build/bin/swift/mariadb-swift-test $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "php mysqli mysql client" {
|
||||
cd $BATS_TEST_DIRNAME/php
|
||||
php mysqli_connector_test.php $USER $PORT $REPO_NAME
|
||||
php /build/bin/php/mysqli_connector_test.php $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
@test "php pdo mysql client" {
|
||||
cd $BATS_TEST_DIRNAME/php
|
||||
php pdo_connector_test.php $USER $PORT $REPO_NAME
|
||||
php /build/bin/php/pdo_connector_test.php $USER $PORT $REPO_NAME
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import mariadb from "mariadb";
|
||||
import { getArgs } from "./helpers.js";
|
||||
|
||||
const tests = [
|
||||
{ q: "create table test (pk int, `value` int, primary key(pk))", isQuery: false },
|
||||
{ q: "describe test", isQuery: true },
|
||||
{ q: "select * from test", isQuery: true },
|
||||
{ q: "insert into test (pk, `value`) values (0,0)", isQuery: false },
|
||||
{ q: "select * from test", isQuery: true },
|
||||
{ q: "call dolt_add('-A');", isQuery: true },
|
||||
{ q: "call dolt_commit('-m', 'my commit')", isQuery: true },
|
||||
{ q: "select COUNT(*) FROM dolt_log", isQuery: true },
|
||||
{ q: "call dolt_checkout('-b', 'mybranch')", isQuery: true },
|
||||
{ q: "insert into test (pk, `value`) values (1,1)", isQuery: false },
|
||||
{ q: "call dolt_commit('-a', '-m', 'my commit2')", isQuery: true },
|
||||
{ q: "call dolt_checkout('main')", isQuery: true },
|
||||
{ q: "call dolt_merge('mybranch')", isQuery: true },
|
||||
{ q: "select COUNT(*) FROM dolt_log", isQuery: true },
|
||||
];
|
||||
|
||||
async function main() {
|
||||
const { user, port, dbName } = getArgs();
|
||||
|
||||
let conn;
|
||||
try {
|
||||
// Create connection pool
|
||||
const pool = mariadb.createPool({
|
||||
host: "127.0.0.1",
|
||||
port: port,
|
||||
user: user,
|
||||
database: dbName,
|
||||
connectionLimit: 5,
|
||||
});
|
||||
|
||||
// Get a connection from the pool
|
||||
conn = await pool.getConnection();
|
||||
console.log("Connected to MariaDB!");
|
||||
|
||||
// Run all tests
|
||||
for (const test of tests) {
|
||||
console.log(`Executing: ${test.q}`);
|
||||
|
||||
if (test.isQuery) {
|
||||
// Execute query and fetch results
|
||||
const rows = await conn.query(test.q);
|
||||
console.log(` → ${rows.length} row(s) returned`);
|
||||
} else {
|
||||
// Execute update/insert
|
||||
const result = await conn.query(test.q);
|
||||
console.log(` → ${result.affectedRows} row(s) affected`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("\nAll MariaDB connector tests passed!");
|
||||
|
||||
// Close connection and pool
|
||||
if (conn) conn.release();
|
||||
await pool.end();
|
||||
|
||||
process.exit(0);
|
||||
} catch (err) {
|
||||
console.error("MariaDB connector test failed:");
|
||||
console.error(err);
|
||||
if (conn) conn.release();
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"knex": "^2.4.0",
|
||||
"mariadb": "^3.4.5",
|
||||
"mysql": "^2.18.1",
|
||||
"mysql2": "^3.9.8",
|
||||
"wtfnode": "^0.9.1",
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
use strict;
|
||||
|
||||
use DBI;
|
||||
|
||||
my $QUERY_RESPONSE = [
|
||||
{ "create table test (pk int, `value` int, primary key(pk))" => '0E0' },
|
||||
{ "describe test" => 2 },
|
||||
{ "insert into test (pk, `value`) values (0,0)" => 1 },
|
||||
{ "select * from test" => 1 },
|
||||
{"call dolt_add('-A');" => 1 },
|
||||
{"call dolt_commit('-m', 'my commit')" => 1},
|
||||
{"call dolt_checkout('-b', 'mybranch')" => 1 },
|
||||
{"insert into test (pk, `value`) values (1,1)" => 1 },
|
||||
{"call dolt_commit('-a', '-m', 'my commit2')" => 1 },
|
||||
{"call dolt_checkout('main')" => 1 },
|
||||
{"call dolt_merge('mybranch')" => 1 },
|
||||
{"select COUNT(*) FROM dolt_log" => 1 },
|
||||
];
|
||||
|
||||
my $user = $ARGV[0];
|
||||
my $port = $ARGV[1];
|
||||
my $db = $ARGV[2];
|
||||
|
||||
# Use DBD::MariaDB driver
|
||||
my $dsn = "DBI:MariaDB:database=$db;host=127.0.0.1;port=$port";
|
||||
my $dbh = DBI->connect($dsn, $user, "", {PrintError => 1, RaiseError => 1});
|
||||
|
||||
print "Connected using DBD::MariaDB\n";
|
||||
|
||||
foreach my $query_response ( @{$QUERY_RESPONSE} ) {
|
||||
my @query_keys = keys %{$query_response};
|
||||
my $query = $query_keys[0];
|
||||
my $exp_result = $query_response->{$query};
|
||||
|
||||
my $result = $dbh->do($query);
|
||||
if ( $result != $exp_result ) {
|
||||
print "QUERY: $query\n";
|
||||
print "EXPECTED: $exp_result\n";
|
||||
print "RESULT: $result\n";
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
print "All DBD::MariaDB tests passed!\n";
|
||||
exit 0;
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import mariadb
|
||||
import sys
|
||||
|
||||
QUERY_RESPONSE = [
|
||||
{"create table test (pk int, `value` int, primary key(pk))": ()},
|
||||
{"describe test": (
|
||||
('pk', 'int', 'NO', 'PRI', None, ''),
|
||||
('value', 'int', 'YES', '', None, '')
|
||||
)},
|
||||
{"insert into test (pk, `value`) values (0,0)": ()},
|
||||
{"select * from test": ((0, 0),)},
|
||||
{"call dolt_add('-A');": ((0,),)},
|
||||
{"call dolt_commit('-m', 'my commit')": (('',),)},
|
||||
{"select COUNT(*) FROM dolt_log": ((2,),)},
|
||||
{"call dolt_checkout('-b', 'mybranch')": ((0, "Switched to branch 'mybranch'"),)},
|
||||
{"insert into test (pk, `value`) values (1,1)": ()},
|
||||
{"call dolt_commit('-a', '-m', 'my commit2')": (('',),)},
|
||||
{"call dolt_checkout('main')": ((0, "Switched to branch 'main'"),)},
|
||||
{"call dolt_merge('mybranch')": (('',1,0,),)},
|
||||
{"select COUNT(*) FROM dolt_log": ((3,),)},
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
user = sys.argv[1]
|
||||
port = int(sys.argv[2])
|
||||
db = sys.argv[3]
|
||||
|
||||
try:
|
||||
# Connect using MariaDB Connector/Python
|
||||
connection = mariadb.connect(
|
||||
user=user,
|
||||
host="127.0.0.1",
|
||||
port=port,
|
||||
database=db
|
||||
)
|
||||
|
||||
print(f"Connected to MariaDB using MariaDB Connector/Python v{mariadb.__version__}")
|
||||
|
||||
cursor = connection.cursor()
|
||||
|
||||
for query_response in QUERY_RESPONSE:
|
||||
query = list(query_response.keys())[0]
|
||||
exp_results = query_response[query]
|
||||
|
||||
cursor.execute(query)
|
||||
|
||||
try:
|
||||
results = cursor.fetchall()
|
||||
|
||||
# MariaDB Connector/Python returns lists, convert to tuples for comparison
|
||||
results = tuple(tuple(row) if isinstance(row, list) else row for row in results)
|
||||
|
||||
# Skip validation for dolt_commit and dolt_merge as their results vary
|
||||
if ("dolt_commit" not in query) and ("dolt_merge" not in query):
|
||||
if results != exp_results:
|
||||
print("Query:")
|
||||
print(query)
|
||||
print("Expected:")
|
||||
print(exp_results)
|
||||
print("Received:")
|
||||
print(results)
|
||||
sys.exit(1)
|
||||
except mariadb.Error:
|
||||
# This is a write query with no results
|
||||
pass
|
||||
|
||||
cursor.close()
|
||||
connection.close()
|
||||
|
||||
print("All MariaDB Connector/Python tests passed!")
|
||||
sys.exit(0)
|
||||
|
||||
except mariadb.Error as e:
|
||||
print(f"Error connecting to MariaDB: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,70 +1,70 @@
|
||||
import mysql.connector
|
||||
import sys
|
||||
|
||||
QUERY_RESPONSE = [
|
||||
{"create table test (pk int, `value` int, primary key(pk))": []},
|
||||
{"describe test": [
|
||||
('pk', 'int', 'NO', 'PRI', None, ''),
|
||||
('value', 'int', 'YES', '', None, '')
|
||||
]},
|
||||
{"insert into test (pk, `value`) values (0,0)": []},
|
||||
{"select * from test": [(0, 0)]},
|
||||
# We used to have a bug where spaces after a semicolon in a query
|
||||
# would cause a client/server disconnect.
|
||||
# https://github.com/dolthub/vitess/pull/65
|
||||
# The following regression tests it.
|
||||
{"select * from test; ": [(0, 0)]},
|
||||
{"select * from test; ": [(0, 0)]},
|
||||
# Test the Dolt SQL functions
|
||||
{"call dolt_add('-A');": [(0,)]},
|
||||
{"call dolt_commit('-m', 'my commit')": [('',)]},
|
||||
{"select COUNT(*) FROM dolt_log": [(2,)]},
|
||||
{"call dolt_checkout('-b', 'mybranch')": [(0, "Switched to branch 'mybranch'")]},
|
||||
{"insert into test (pk, `value`) values (1,1)": []},
|
||||
{"call dolt_commit('-a', '-m', 'my commit2')": [('',)]},
|
||||
{"call dolt_checkout('main')": [(0, "Switched to branch 'main'")]},
|
||||
{"call dolt_merge('mybranch')": [('',1,0,)]},
|
||||
{"select COUNT(*) FROM dolt_log": [(3,)]},
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
user = sys.argv[1]
|
||||
port = sys.argv[2]
|
||||
db = sys.argv[3]
|
||||
|
||||
connection = mysql.connector.connect(user=user,
|
||||
host="127.0.0.1",
|
||||
port=port,
|
||||
database=db)
|
||||
|
||||
for query_response in QUERY_RESPONSE:
|
||||
query = list(query_response.keys())[0]
|
||||
exp_results = query_response[query]
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(query)
|
||||
try:
|
||||
results = cursor.fetchall()
|
||||
print(exp_results)
|
||||
print(results)
|
||||
if (results != exp_results) and ("dolt_commit" not in query) and ("dolt_merge" not in query):
|
||||
print("Query:")
|
||||
print(query)
|
||||
print("Expected:")
|
||||
print(exp_results)
|
||||
print("Received:")
|
||||
print(results)
|
||||
exit(1)
|
||||
except mysql.connector.errors.InterfaceError:
|
||||
|
||||
# This is a write query
|
||||
pass
|
||||
|
||||
cursor.close()
|
||||
|
||||
connection.close()
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
main()
|
||||
import mysql.connector
|
||||
import sys
|
||||
|
||||
QUERY_RESPONSE = [
|
||||
{"create table test (pk int, `value` int, primary key(pk))": []},
|
||||
{"describe test": [
|
||||
('pk', 'int', 'NO', 'PRI', None, ''),
|
||||
('value', 'int', 'YES', '', None, '')
|
||||
]},
|
||||
{"insert into test (pk, `value`) values (0,0)": []},
|
||||
{"select * from test": [(0, 0)]},
|
||||
# We used to have a bug where spaces after a semicolon in a query
|
||||
# would cause a client/server disconnect.
|
||||
# https://github.com/dolthub/vitess/pull/65
|
||||
# The following regression tests it.
|
||||
{"select * from test; ": [(0, 0)]},
|
||||
{"select * from test; ": [(0, 0)]},
|
||||
# Test the Dolt SQL functions
|
||||
{"call dolt_add('-A');": [(0,)]},
|
||||
{"call dolt_commit('-m', 'my commit')": [('',)]},
|
||||
{"select COUNT(*) FROM dolt_log": [(2,)]},
|
||||
{"call dolt_checkout('-b', 'mybranch')": [(0, "Switched to branch 'mybranch'")]},
|
||||
{"insert into test (pk, `value`) values (1,1)": []},
|
||||
{"call dolt_commit('-a', '-m', 'my commit2')": [('',)]},
|
||||
{"call dolt_checkout('main')": [(0, "Switched to branch 'main'")]},
|
||||
{"call dolt_merge('mybranch')": [('',1,0,)]},
|
||||
{"select COUNT(*) FROM dolt_log": [(3,)]},
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
user = sys.argv[1]
|
||||
port = sys.argv[2]
|
||||
db = sys.argv[3]
|
||||
|
||||
connection = mysql.connector.connect(user=user,
|
||||
host="127.0.0.1",
|
||||
port=port,
|
||||
database=db)
|
||||
|
||||
for query_response in QUERY_RESPONSE:
|
||||
query = list(query_response.keys())[0]
|
||||
exp_results = query_response[query]
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(query)
|
||||
try:
|
||||
results = cursor.fetchall()
|
||||
print(exp_results)
|
||||
print(results)
|
||||
if (results != exp_results) and ("dolt_commit" not in query) and ("dolt_merge" not in query):
|
||||
print("Query:")
|
||||
print(query)
|
||||
print("Expected:")
|
||||
print(exp_results)
|
||||
print("Received:")
|
||||
print(results)
|
||||
sys.exit(1)
|
||||
except mysql.connector.errors.InterfaceError:
|
||||
|
||||
# This is a write query
|
||||
pass
|
||||
|
||||
cursor.close()
|
||||
|
||||
connection.close()
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
main()
|
||||
@@ -44,11 +44,11 @@ def main():
|
||||
print(exp_results)
|
||||
print("Received:")
|
||||
print(results)
|
||||
exit(1)
|
||||
sys.exit(1)
|
||||
|
||||
connection.close()
|
||||
|
||||
exit(0)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
main()
|
||||
|
||||
@@ -1,67 +1,67 @@
|
||||
import sqlalchemy
|
||||
|
||||
from sqlalchemy.engine import Engine
|
||||
from sqlalchemy import create_engine
|
||||
|
||||
import sys
|
||||
|
||||
QUERY_RESPONSE = [
|
||||
{"create table test (pk int, `value` int, primary key(pk))": []},
|
||||
{"describe test": [
|
||||
('pk', 'int', 'NO', 'PRI', None, ''),
|
||||
('value', 'int', 'YES', '', None, '')
|
||||
]},
|
||||
{"insert into test (pk, `value`) values (0,0)": ()},
|
||||
{"select * from test": [(0, 0)]},
|
||||
{"call dolt_add('-A');": [(0,)]},
|
||||
{"call dolt_commit('-m', 'my commit')": [('',)]},
|
||||
{"select COUNT(*) FROM dolt_log": [(2,)]},
|
||||
{"call dolt_checkout('-b', 'mybranch')": [(0, "Switched to branch 'mybranch'")]},
|
||||
{"insert into test (pk, `value`) values (1,1)": []},
|
||||
{"call dolt_commit('-a', '-m', 'my commit2')": [('',)]},
|
||||
{"call dolt_checkout('main')": [(0, "Switched to branch 'main'")]},
|
||||
{"call dolt_merge('mybranch')": [('',1,0,)]},
|
||||
{"select COUNT(*) FROM dolt_log": [(3,)]},
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
user = sys.argv[1]
|
||||
port = int(sys.argv[2])
|
||||
db = sys.argv[3]
|
||||
|
||||
conn_string_base = "mysql+mysqlconnector://"
|
||||
|
||||
engine = create_engine(conn_string_base +
|
||||
"{user}@127.0.0.1:{port}/{db}".format(user=user,
|
||||
port=port,
|
||||
db=db)
|
||||
)
|
||||
|
||||
with engine.connect() as con:
|
||||
for query_response in QUERY_RESPONSE:
|
||||
query = list(query_response.keys())[0]
|
||||
exp_results = query_response[query]
|
||||
|
||||
result_proxy = con.execute(query)
|
||||
|
||||
try:
|
||||
results = result_proxy.fetchall()
|
||||
if (results != exp_results) and ("dolt_commit" not in query) and ("dolt_merge" not in query):
|
||||
print("Query:")
|
||||
print(query)
|
||||
print("Expected:")
|
||||
print(exp_results)
|
||||
print("Received:")
|
||||
print(results)
|
||||
exit(1)
|
||||
# You can't call fetchall on an insert
|
||||
# so we'll just ignore the exception
|
||||
except sqlalchemy.exc.ResourceClosedError:
|
||||
pass
|
||||
|
||||
con.close()
|
||||
exit(0)
|
||||
|
||||
|
||||
main()
|
||||
import sqlalchemy
|
||||
|
||||
from sqlalchemy.engine import Engine
|
||||
from sqlalchemy import create_engine
|
||||
|
||||
import sys
|
||||
|
||||
QUERY_RESPONSE = [
|
||||
{"create table test (pk int, `value` int, primary key(pk))": []},
|
||||
{"describe test": [
|
||||
('pk', 'int', 'NO', 'PRI', None, ''),
|
||||
('value', 'int', 'YES', '', None, '')
|
||||
]},
|
||||
{"insert into test (pk, `value`) values (0,0)": ()},
|
||||
{"select * from test": [(0, 0)]},
|
||||
{"call dolt_add('-A');": [(0,)]},
|
||||
{"call dolt_commit('-m', 'my commit')": [('',)]},
|
||||
{"select COUNT(*) FROM dolt_log": [(2,)]},
|
||||
{"call dolt_checkout('-b', 'mybranch')": [(0, "Switched to branch 'mybranch'")]},
|
||||
{"insert into test (pk, `value`) values (1,1)": []},
|
||||
{"call dolt_commit('-a', '-m', 'my commit2')": [('',)]},
|
||||
{"call dolt_checkout('main')": [(0, "Switched to branch 'main'")]},
|
||||
{"call dolt_merge('mybranch')": [('',1,0,)]},
|
||||
{"select COUNT(*) FROM dolt_log": [(3,)]},
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
user = sys.argv[1]
|
||||
port = int(sys.argv[2])
|
||||
db = sys.argv[3]
|
||||
|
||||
conn_string_base = "mysql+mysqlconnector://"
|
||||
|
||||
engine = create_engine(conn_string_base +
|
||||
"{user}@127.0.0.1:{port}/{db}".format(user=user,
|
||||
port=port,
|
||||
db=db)
|
||||
)
|
||||
|
||||
with engine.connect() as con:
|
||||
for query_response in QUERY_RESPONSE:
|
||||
query = list(query_response.keys())[0]
|
||||
exp_results = query_response[query]
|
||||
|
||||
result_proxy = con.execute(query)
|
||||
|
||||
try:
|
||||
results = result_proxy.fetchall()
|
||||
if (results != exp_results) and ("dolt_commit" not in query) and ("dolt_merge" not in query):
|
||||
print("Query:")
|
||||
print(query)
|
||||
print("Expected:")
|
||||
print(exp_results)
|
||||
print("Received:")
|
||||
print(results)
|
||||
sys.exit(1)
|
||||
# You can't call fetchall on an insert
|
||||
# so we'll just ignore the exception
|
||||
except sqlalchemy.exc.ResourceClosedError:
|
||||
pass
|
||||
|
||||
con.close()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
main()
|
||||
|
||||
@@ -1,89 +1,89 @@
|
||||
library(RMariaDB)
|
||||
library(DBI)
|
||||
|
||||
args = commandArgs(trailingOnly=TRUE)
|
||||
|
||||
user = args[1]
|
||||
port = args[2]
|
||||
db = args[3]
|
||||
|
||||
conn = dbConnect(RMariaDB::MariaDB(), host="127.0.0.1", port = port,
|
||||
username = user, dbname = db)
|
||||
|
||||
# check standard queries
|
||||
queries = list("create table test (pk int, value int, primary key(pk))",
|
||||
"describe test",
|
||||
"insert into test (pk, `value`) values (0,0)",
|
||||
"select * from test")
|
||||
|
||||
responses = list(NULL,
|
||||
data.frame(Field = c("pk", "value"), Type = c("int", "int"), Null = c("NO", "YES"), Key = c("PRI", ""), Default = c(NA, NA), Extra = c("", ""), stringsAsFactors = FALSE),
|
||||
NULL,
|
||||
data.frame(pk = c(0), value = c(0), stringsAsFactors = FALSE))
|
||||
|
||||
for(i in 1:length(queries)) {
|
||||
q = queries[[i]]
|
||||
want = responses[[i]]
|
||||
if (!is.null(want)) {
|
||||
got <- dbGetQuery(conn, q)
|
||||
if (!isTRUE(all.equal(want, got))) {
|
||||
print(q)
|
||||
print(want)
|
||||
print(got)
|
||||
quit(1)
|
||||
}
|
||||
} else {
|
||||
dbExecute(conn, q)
|
||||
}
|
||||
}
|
||||
|
||||
# check prepared statements
|
||||
stmt <- dbSendStatement(conn, "INSERT INTO test values (?, ?)")
|
||||
rs <- dbBind(stmt, list(1,1))
|
||||
rowsAff <- dbGetRowsAffected(rs)
|
||||
dbClearResult(rs)
|
||||
|
||||
if (rowsAff != 1) {
|
||||
print("failed to execute prepared statement")
|
||||
quit(1)
|
||||
}
|
||||
|
||||
got <- dbGetQuery(conn, "select * from test where pk = 1")
|
||||
want = data.frame(pk = c(1), value = c(1))
|
||||
if (!isTRUE(all.equal(want, got))) {
|
||||
print("unexpected prepared statement result")
|
||||
print(got)
|
||||
quit(1)
|
||||
}
|
||||
|
||||
dolt_queries = list("call DOLT_ADD('-A')",
|
||||
"call dolt_commit('-m', 'my commit')",
|
||||
"call dolt_checkout('-b', 'mybranch')",
|
||||
"insert into test (pk, `value`) values (2,2)",
|
||||
"call dolt_commit('-a', '-m', 'my commit2')",
|
||||
"call dolt_checkout('main')",
|
||||
"call dolt_merge('mybranch')")
|
||||
|
||||
for(i in 1:length(dolt_queries)) {
|
||||
q = dolt_queries[[i]]
|
||||
dbExecute(conn, q)
|
||||
}
|
||||
|
||||
count <- dbGetQuery(conn, "select COUNT(*) as c from dolt_log")
|
||||
want <- data.frame(c = c(3))
|
||||
ret <- all.equal(count, want)
|
||||
if (!ret) {
|
||||
print("Number of commits is incorrect")
|
||||
quit(1)
|
||||
}
|
||||
|
||||
# Add a failing query and ensure that the connection does not quit.
|
||||
# cc. https://github.com/dolthub/dolt/issues/3418
|
||||
try(dbExecute(conn, "insert into test values (0, 1)"), silent = TRUE)
|
||||
one <- dbGetQuery(conn, "select 1 as pk")
|
||||
ret <- one == data.frame(pk=1)
|
||||
if (!ret) {
|
||||
print("Number of commits is incorrect")
|
||||
quit(1)
|
||||
}
|
||||
|
||||
library(RMariaDB)
|
||||
library(DBI)
|
||||
|
||||
args = commandArgs(trailingOnly=TRUE)
|
||||
|
||||
user = args[1]
|
||||
port = args[2]
|
||||
db = args[3]
|
||||
|
||||
conn = dbConnect(RMariaDB::MariaDB(), host="127.0.0.1", port = port,
|
||||
username = user, dbname = db)
|
||||
|
||||
# check standard queries
|
||||
queries = list("create table test (pk int, value int, primary key(pk))",
|
||||
"describe test",
|
||||
"insert into test (pk, `value`) values (0,0)",
|
||||
"select * from test")
|
||||
|
||||
responses = list(NULL,
|
||||
data.frame(Field = c("pk", "value"), Type = c("int", "int"), Null = c("NO", "YES"), Key = c("PRI", ""), Default = c(NA_character_, NA_character_), Extra = c("", ""), stringsAsFactors = FALSE),
|
||||
NULL,
|
||||
data.frame(pk = c(0), value = c(0), stringsAsFactors = FALSE))
|
||||
|
||||
for(i in 1:length(queries)) {
|
||||
q = queries[[i]]
|
||||
want = responses[[i]]
|
||||
if (!is.null(want)) {
|
||||
got <- dbGetQuery(conn, q)
|
||||
if (!isTRUE(all.equal(want, got))) {
|
||||
print(q)
|
||||
print(want)
|
||||
print(got)
|
||||
quit(save="no", status=1)
|
||||
}
|
||||
} else {
|
||||
dbExecute(conn, q)
|
||||
}
|
||||
}
|
||||
|
||||
# check prepared statements
|
||||
stmt <- dbSendStatement(conn, "INSERT INTO test values (?, ?)")
|
||||
rs <- dbBind(stmt, list(1,1))
|
||||
rowsAff <- dbGetRowsAffected(rs)
|
||||
dbClearResult(rs)
|
||||
|
||||
if (rowsAff != 1) {
|
||||
print("failed to execute prepared statement")
|
||||
quit(save="no", status=1)
|
||||
}
|
||||
|
||||
got <- dbGetQuery(conn, "select * from test where pk = 1")
|
||||
want = data.frame(pk = c(1), value = c(1))
|
||||
if (!isTRUE(all.equal(want, got))) {
|
||||
print("unexpected prepared statement result")
|
||||
print(got)
|
||||
quit(save="no", status=1)
|
||||
}
|
||||
|
||||
dolt_queries = list("call DOLT_ADD('-A')",
|
||||
"call dolt_commit('-m', 'my commit')",
|
||||
"call dolt_checkout('-b', 'mybranch')",
|
||||
"insert into test (pk, `value`) values (2,2)",
|
||||
"call dolt_commit('-a', '-m', 'my commit2')",
|
||||
"call dolt_checkout('main')",
|
||||
"call dolt_merge('mybranch')")
|
||||
|
||||
for(i in 1:length(dolt_queries)) {
|
||||
q = dolt_queries[[i]]
|
||||
dbExecute(conn, q)
|
||||
}
|
||||
|
||||
count <- dbGetQuery(conn, "select COUNT(*) as c from dolt_log")
|
||||
want <- data.frame(c = c(3))
|
||||
ret <- all.equal(count, want)
|
||||
if (!ret) {
|
||||
print("Number of commits is incorrect")
|
||||
quit(save="no", status=1)
|
||||
}
|
||||
|
||||
# Add a failing query and ensure that the connection does not quit.
|
||||
# cc. https://github.com/dolthub/dolt/issues/3418
|
||||
try(dbExecute(conn, "insert into test values (0, 1)"), silent = TRUE)
|
||||
one <- dbGetQuery(conn, "select 1 as pk")
|
||||
ret <- one == data.frame(pk=1)
|
||||
if (!ret) {
|
||||
print("Number of commits is incorrect")
|
||||
quit(save="no", status=1)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,5 +8,5 @@ edition = "2018"
|
||||
mysql = "*"
|
||||
|
||||
[[bin]]
|
||||
name = "mysql_connector_test"
|
||||
name = "mysql-client-test"
|
||||
path = "src/mysql_connector_test.rs"
|
||||
|
||||
25
integration-tests/mysql-client-tests/swift/Package.swift
Normal file
25
integration-tests/mysql-client-tests/swift/Package.swift
Normal file
@@ -0,0 +1,25 @@
|
||||
// swift-tools-version:5.5
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "MariaDBTest",
|
||||
platforms: [
|
||||
.macOS(.v10_15)
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/PerfectlySoft/Perfect-MariaDB.git", from: "3.0.0")
|
||||
],
|
||||
targets: [
|
||||
.executableTarget(
|
||||
name: "MariaDBTest",
|
||||
dependencies: [
|
||||
.product(name: "MariaDB", package: "Perfect-MariaDB")
|
||||
],
|
||||
path: "Sources",
|
||||
linkerSettings: [
|
||||
.linkedLibrary("mariadb")
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
127
integration-tests/mysql-client-tests/swift/Sources/main.swift
Normal file
127
integration-tests/mysql-client-tests/swift/Sources/main.swift
Normal file
@@ -0,0 +1,127 @@
|
||||
import Foundation
|
||||
import MariaDB
|
||||
|
||||
func main() throws {
|
||||
// Get connection parameters from command line
|
||||
guard CommandLine.arguments.count >= 4 else {
|
||||
print("Usage: MariaDBTest <user> <port> <database>")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
let user = CommandLine.arguments[1]
|
||||
let port = UInt32(CommandLine.arguments[2]) ?? 3306
|
||||
let database = CommandLine.arguments[3]
|
||||
|
||||
// Connect to the database
|
||||
let mysql = MySQL()
|
||||
|
||||
guard mysql.connect(
|
||||
host: "127.0.0.1",
|
||||
user: user,
|
||||
password: "",
|
||||
db: database,
|
||||
port: port
|
||||
) else {
|
||||
print("Connection failed: \(mysql.errorMessage())")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
print("Connected to MariaDB successfully")
|
||||
|
||||
// Test queries
|
||||
let queries = [
|
||||
"CREATE TABLE test (pk INT, value INT, PRIMARY KEY(pk))",
|
||||
"INSERT INTO test (pk, value) VALUES (0, 0)",
|
||||
"SELECT * FROM test",
|
||||
"CALL dolt_add('-A')",
|
||||
"CALL dolt_commit('-m', 'my commit')",
|
||||
"SELECT COUNT(*) FROM dolt_log",
|
||||
"CALL dolt_checkout('-b', 'mybranch')",
|
||||
"INSERT INTO test (pk, value) VALUES (10, 10)",
|
||||
"CALL dolt_commit('-a', '-m', 'my commit2')",
|
||||
"CALL dolt_checkout('main')",
|
||||
"CALL dolt_merge('mybranch')",
|
||||
"SELECT COUNT(*) FROM dolt_log"
|
||||
]
|
||||
|
||||
for query in queries {
|
||||
guard mysql.query(statement: query) else {
|
||||
print("Query failed: \(query)")
|
||||
print("Error: \(mysql.errorMessage())")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Consume results if any
|
||||
let results = mysql.storeResults()
|
||||
results?.close()
|
||||
}
|
||||
|
||||
// Test prepared statements
|
||||
print("Testing prepared statements...")
|
||||
|
||||
let stmt = MySQLStmt(mysql)
|
||||
|
||||
// Test SELECT with prepared statement
|
||||
let selectQuery = "SELECT * FROM test WHERE pk = ?"
|
||||
guard stmt.prepare(statement: selectQuery) else {
|
||||
print("Failed to prepare SELECT statement: \(stmt.errorMessage())")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
stmt.bindParam(1)
|
||||
|
||||
guard stmt.execute() else {
|
||||
print("Failed to execute SELECT statement: \(stmt.errorMessage())")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
let resultSet = stmt.results()
|
||||
resultSet.close()
|
||||
stmt.close()
|
||||
|
||||
// Test INSERT with prepared statement
|
||||
let insertStmt = MySQLStmt(mysql)
|
||||
|
||||
let insertQuery = "INSERT INTO test VALUES (?, ?)"
|
||||
guard insertStmt.prepare(statement: insertQuery) else {
|
||||
print("Failed to prepare INSERT statement: \(insertStmt.errorMessage())")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
insertStmt.bindParam(2)
|
||||
insertStmt.bindParam(20)
|
||||
|
||||
guard insertStmt.execute() else {
|
||||
print("Failed to execute INSERT statement: \(insertStmt.errorMessage())")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
insertStmt.close()
|
||||
|
||||
// Verify the insert
|
||||
guard mysql.query(statement: "SELECT * FROM test WHERE pk = 2") else {
|
||||
print("Failed to verify insert")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
if let verifyResults = mysql.storeResults() {
|
||||
let row = verifyResults.next()
|
||||
guard row != nil else {
|
||||
print("Expected row not found after insert")
|
||||
exit(1)
|
||||
}
|
||||
verifyResults.close()
|
||||
}
|
||||
|
||||
print("All Swift tests passed!")
|
||||
|
||||
mysql.close()
|
||||
}
|
||||
|
||||
do {
|
||||
try main()
|
||||
} catch {
|
||||
print("Error: \(error)")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user