add odbc and swift test

This commit is contained in:
elianddb
2025-10-13 13:23:44 -07:00
parent 1b05f9bc3a
commit 17ab809bd1
8 changed files with 413 additions and 28 deletions

View File

@@ -15,11 +15,14 @@ jobs:
runs-on: ubuntu-22.04
timeout-minutes: 45
steps:
- name: Clean up preinstalled .NET and agent tools
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
- name: Maximize build space
uses: easimon/maximize-build-space@v10
with:
remove-dotnet: 'true'
remove-codeql: 'true'
remove-haskell: 'true'
- name: Checkout
uses: actions/checkout@v4
with:
@@ -29,14 +32,14 @@ jobs:
uses: docker/setup-buildx-action@v2
- name: Build MySQL test image
uses: docker/build-push-action@v4
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=gha
cache-to: type=gha,mode=max
cache-to: type=gha,mode=min
- name: Test MySQL client integrations
run: docker run --rm mysql-client-tests:latest

View File

@@ -5,10 +5,10 @@ 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
RUN apk add --no-cache icu-dev icu-static
COPY dolt/go/ /build/dolt/go/
RUN go build -tags icu_static -ldflags "$GO_LDFLAGS" -o /build/bin/dolt ./cmd/dolt
@@ -22,31 +22,43 @@ 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 apk add --no-cache musl-dev
RUN cargo build --release --target-dir /build/bin/ # exe is in release/
FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim AS dotnet_clients_build
COPY dolt/integration-tests/mysql-client-tests/dotnet/MySqlClient/ /build/dotnet/MySqlClient/
COPY dolt/integration-tests/mysql-client-tests/dotnet/MySqlClient/*.csproj /build/dotnet/MySqlClient/
WORKDIR /build/dotnet/MySqlClient/
RUN dotnet publish -c Release -o /build/bin
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/ /build/dotnet/MySqlConnector/
COPY dolt/integration-tests/mysql-client-tests/dotnet/MySqlConnector/*.csproj /build/dotnet/MySqlConnector/
WORKDIR /build/dotnet/MySqlConnector/
RUN dotnet publish -c Release -o /build/bin
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 gcc:12.5-bookworm 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 && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y default-libmysqlclient-dev libmariadb-dev unixodbc-dev odbcinst wget && 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 gcc:12.5-bookworm AS cpp_clients_build
RUN apt-get update && apt-get install -y libmysqlcppconn-dev wget && rm -rf /var/lib/apt/lists/*
RUN wget https://dlm.mariadb.com/4464866/Connectors/cpp/connector-cpp-1.1.7/mariadb-connector-cpp_1.1.7-1+maria~bookworm_amd64.deb \
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/
@@ -75,7 +87,6 @@ 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 local.hex --force && mix local.rebar --force
RUN mix deps.get
COPY dolt/integration-tests/mysql-client-tests/elixir/mysql/ /build/elixir/mysql/
RUN MIX_ENV=prod mix release mysql_otp
@@ -111,6 +122,13 @@ COPY dolt/integration-tests/mysql-client-tests/ruby/ /build/bin/
FROM php:8.3-bookworm AS php_deps
RUN docker-php-ext-install mysqli pdo_mysql
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 debian:bookworm-slim AS r_deps
RUN apt-get update && apt-get install -y r-base-core libmariadb-dev && rm -rf /var/lib/apt/lists/*
WORKDIR /build/r/
@@ -130,24 +148,24 @@ RUN apt-get update && apt-get install -y \
bats \
&& rm -rf /var/lib/apt/lists/*
# Runtime
COPY --from=dolt_build /build/bin/ /usr/local/bin/
# Runtime Libraries
COPY --from=cpp_clients_build /usr/lib/x86_64-linux-gnu/libmariadb* /usr/lib/x86_64-linux-gnu/
COPY --from=cpp_clients_build /usr/include/mariadb/ /usr/include/mariadb/
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/node
COPY --from=node_clients_build /usr/local/bin/node /usr/local/bin/
COPY --from=php_deps /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
COPY --from=php_deps /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/
COPY --from=ruby_clients_build /usr/local/bin/ruby /usr/local/bin/ruby
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/
COPY --from=r_deps /build/r/*.tar.gz /tmp/r-packages/
RUN R CMD INSTALL /tmp/r-packages/*.tar.gz && rm -rf /tmp/r-packages/
ENV PATH="/opt/jre/bin:/usr/local/bundle/bin:${PATH}"
ENV GEM_HOME="/usr/local/bundle"
ENV BUNDLE_APP_CONFIG="/usr/local/bundle"
# Binaries
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/
@@ -157,6 +175,7 @@ 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/
@@ -167,4 +186,6 @@ 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"]

View File

@@ -5,4 +5,11 @@
**/node_modules/
**/*Dockerfile
**/transactions/
**/orm-tests/
**/orm-tests/
**/out/
**/bats/archive-test-repos/
**/bats/performance-repo/
**/bats/corrupt_dbs/
**/integration-tests/data-dump-loading-tests/
**/.dolt/
**/.doltcfg/

View File

@@ -2,17 +2,21 @@ CC = gcc
CFLAGS_MYSQL = -I/usr/include/mysql -Wall -O2
CFLAGS_MARIADB = -I/usr/include/mariadb -Wall -O2
CFLAGS_ODBC = -Wall -O2
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)
all: $(TARGET_MYSQL) $(TARGET_MARIADB) $(TARGET_ODBC)
$(TARGET_MYSQL): $(SRCS_MYSQL)
@mkdir -p /build/bin
@@ -22,5 +26,9 @@ $(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)
clean:
rm -f $(TARGET_MYSQL) $(TARGET_MARIADB)
rm -f $(TARGET_MYSQL) $(TARGET_MARIADB) $(TARGET_ODBC)

View 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;
}

View File

@@ -86,6 +86,10 @@ teardown() {
/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" {
/build/bin/cpp/mysql-connector-test $USER $PORT $REPO_NAME
}
@@ -175,6 +179,10 @@ EOF" -m "postgres"
/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" {
php /build/bin/php/mysqli_connector_test.php $USER $PORT $REPO_NAME
}

View 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")
]
)
]
)

View 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)
}