Skip to content

Commit

Permalink
feat: add mysql client compatibility tests (apecloud#111)
Browse files Browse the repository at this point in the history
  • Loading branch information
NoyException committed Nov 28, 2024
1 parent be94e37 commit bca8fbb
Show file tree
Hide file tree
Showing 20 changed files with 1,824 additions and 66 deletions.
128 changes: 128 additions & 0 deletions .github/workflows/clients-compatibility.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
name: Compatibility Test for Clients

on:
push:
branches:
- main
- compatibility
- test
pull_request:
branches: [ "main" ]

jobs:
test-mysql:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '16'

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'

- name: Install dependencies
run: |
go get .
pip3 install "sqlglot[rs]"
curl -LJO https://github.com/duckdb/duckdb/releases/download/v1.1.3/duckdb_cli-linux-amd64.zip
unzip duckdb_cli-linux-amd64.zip
chmod +x duckdb
sudo mv duckdb /usr/local/bin
duckdb -c 'INSTALL json from core'
duckdb -c 'SELECT extension_name, loaded, install_path FROM duckdb_extensions() where installed'
sudo apt-get update
sudo apt-get install --yes --no-install-recommends bats cpanminus
cd compatibility/mysql/
sudo apt-get install --yes --no-install-recommends libmysqlclient-dev dotnet-sdk-8.0 dotnet-runtime-8.0
curl -L -o ./java/mysql-connector-java-8.0.30.jar https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.30/mysql-connector-java-8.0.30.jar
npm install mysql
sudo apt-get install --yes --no-install-recommends php-mysql
sudo cpanm DBD::mysql
pip3 install mysql-connector-python
sudo apt-get install --yes --no-install-recommends r-base-core
sudo R -e "install.packages('RMySQL', repos='http://cran.r-project.org')"
sudo gem install mysql2
- name: Build
run: go build -v

- name: Start MyDuck Server
run: |
./myduckserver &
sleep 5
- name: Run the Compatibility Test for MySQL Client
run: |
bats ./compatibility/mysql/test.bats
test-postgresql:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '16'

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'

- name: Install dependencies
run: |
go get .
pip3 install "sqlglot[rs]"
curl -LJO https://github.com/duckdb/duckdb/releases/download/v1.1.3/duckdb_cli-linux-amd64.zip
unzip duckdb_cli-linux-amd64.zip
chmod +x duckdb
sudo mv duckdb /usr/local/bin
duckdb -c 'INSTALL json from core'
duckdb -c 'SELECT extension_name, loaded, install_path FROM duckdb_extensions() where installed'
sudo apt-get update
sudo apt-get install --yes --no-install-recommends bats cpanminus
cd compatibility/pg/
sudo apt-get install --yes --no-install-recommends postgresql-client dotnet-sdk-8.0 dotnet-runtime-8.0
curl -L -o ./java/postgresql-42.7.4.jar https://jdbc.postgresql.org/download/postgresql-42.7.3.jar
npm install pg
sudo cpanm DBD::Pg
pip3 install psycopg2
sudo apt-get install --yes --no-install-recommends r-base-core
sudo R -e "install.packages('RPostgres', repos='http://cran.r-project.org')"
sudo gem install pg
- name: Build
run: go build -v

- name: Start MyDuck Server
run: |
./myduckserver &
sleep 5
- name: Run the Compatibility Test for PostgreSQL Client
run: |
bats ./compatibility/pg/test.bats
66 changes: 0 additions & 66 deletions .github/workflows/postgres-compatibility.yml

This file was deleted.

File renamed without changes.
9 changes: 9 additions & 0 deletions compatibility/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Client Compatibility Test

This contains a set of tests to verify the compatibility of the client libraries for the different databases.

You should not run `tests.bats` directly, instead use github actions (or act) to run the tests.

## TODO

Fixes tests that are commented out
154 changes: 154 additions & 0 deletions compatibility/mysql/c/mysql_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#include <stdio.h>
#include <stdlib.h>
#include <mysql/mysql.h>
#include <string.h>

typedef struct {
char *query;
char **expectedResults;
int expectedRows;
int expectedCols;
} Test;

typedef struct {
MYSQL *conn;
Test *tests;
int testCount;
} MySQLTest;

void connectDB(MySQLTest *mysqlTest, const char *ip, int port, const char *user, const char *password);
void disconnectDB(MySQLTest *mysqlTest);
void addTest(MySQLTest *mysqlTest, const char *query, char **expectedResults, int expectedRows, int expectedCols);
int runTests(MySQLTest *mysqlTest);
void readTestsFromFile(MySQLTest *mysqlTest, const char *filename);
size_t removeNewline(char *line);

void connectDB(MySQLTest *mysqlTest, const char *ip, int port, const char *user, const char *password) {
mysqlTest->conn = mysql_init(NULL);
if (mysqlTest->conn == NULL) {
printf("mysql_init() failed\n");
exit(1);
}

if (mysql_real_connect(mysqlTest->conn, ip, user, password, NULL, port, NULL, 0) == NULL) {
printf("mysql_real_connect() failed\n");
mysql_close(mysqlTest->conn);
exit(1);
}
}

void disconnectDB(MySQLTest *mysqlTest) {
mysql_close(mysqlTest->conn);
}

void addTest(MySQLTest *mysqlTest, const char *query, char **expectedResults, int expectedRows, int expectedCols) {
mysqlTest->tests = realloc(mysqlTest->tests, sizeof(Test) * (mysqlTest->testCount + 1));
mysqlTest->tests[mysqlTest->testCount].query = strdup(query);
mysqlTest->tests[mysqlTest->testCount].expectedResults = expectedResults;
mysqlTest->tests[mysqlTest->testCount].expectedRows = expectedRows;
mysqlTest->tests[mysqlTest->testCount].expectedCols = expectedCols;
mysqlTest->testCount++;
}

int runTests(MySQLTest *mysqlTest) {
for (int i = 0; i < mysqlTest->testCount; i++) {
Test *test = &mysqlTest->tests[i];
printf("Running test: %s\n", test->query);
if (mysql_query(mysqlTest->conn, test->query)) {
printf("Query failed: %s\n", mysql_error(mysqlTest->conn));
return 0;
}

MYSQL_RES *res = mysql_store_result(mysqlTest->conn);
if (res) {
int rows = mysql_num_rows(res);
int cols = mysql_num_fields(res);
if (cols != test->expectedCols) {
printf("Expected %d columns, got %d\n", test->expectedCols, cols);
mysql_free_result(res);
return 0;
}
MYSQL_ROW row;
int r = 0;
while ((row = mysql_fetch_row(res))) {
for (int c = 0; c < cols; c++) {
if (strcmp(row[c], test->expectedResults[r * cols + c]) != 0) {
printf("Expected: '%s', got: '%s'\n", test->expectedResults[r * cols + c], row[c]);
mysql_free_result(res);
return 0;
}
}
r++;
}
if (rows != test->expectedRows) {
printf("Expected %d rows, got %d\n", test->expectedRows, rows);
mysql_free_result(res);
return 0;
}
mysql_free_result(res);
}
}
return 1;
}

size_t removeNewline(char *line) {
size_t len = strlen(line);
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
line[--len] = '\0';
}
return len;
}

void readTestsFromFile(MySQLTest *mysqlTest, const char *filename) {
FILE *file = fopen(filename, "r");
if (!file) {
perror("Failed to open test data file");
exit(1);
}

char line[1024];
while (fgets(line, sizeof(line), file)) {
size_t len = removeNewline(line);
if (len == 0) continue;

char *query = strdup(line);
char **expectedResults = NULL;
int expectedRows = 0, expectedCols = 0;

while (fgets(line, sizeof(line), file)) {
len = removeNewline(line);
if (len == 0) break;

char *token = strtok(line, ",");
int col = 0;
while (token) {
expectedResults = realloc(expectedResults, sizeof(char*) * (expectedRows * expectedCols + col + 1));
expectedResults[expectedRows * expectedCols + col] = strdup(token);
token = strtok(NULL, ",");
col++;
}
expectedRows++;
if (expectedCols == 0) expectedCols = col;
}
addTest(mysqlTest, query, expectedResults, expectedRows, expectedCols);
}

fclose(file);
}

int main(int argc, char *argv[]) {
if (argc != 6) {
printf("Usage: %s <ip> <port> <user> <password> <testFile>\n", argv[0]);
return 1;
}

MySQLTest mysqlTest = {0};
connectDB(&mysqlTest, argv[1], atoi(argv[2]), argv[3], argv[4]);

readTestsFromFile(&mysqlTest, argv[5]);

int result = runTests(&mysqlTest);
disconnectDB(&mysqlTest);
free(mysqlTest.tests);
return result ? 0 : 1;
}
7 changes: 7 additions & 0 deletions compatibility/mysql/clean.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

rm -rf ./c/mysql_test \
./csharp/bin ./csharp/obj \
./go/mysql \
./java/*.class \
./rust/target ./rust/Cargo.lock
Loading

0 comments on commit bca8fbb

Please sign in to comment.