From 7e4f0af065617c4dbf36d98ff25ebb64a0475783 Mon Sep 17 00:00:00 2001 From: dennis zhuang Date: Tue, 25 Nov 2025 00:26:50 -0800 Subject: [PATCH] fix: mysql binary date type and multi-lang ci tests (#7291) * fix: mysql binary date type Signed-off-by: Dennis Zhuang * test: add unit test Signed-off-by: Dennis Zhuang * fix: typo Signed-off-by: Dennis Zhuang * ci: add multi lang integration tests ci Signed-off-by: Dennis Zhuang * fix: path and branch Signed-off-by: Dennis Zhuang * ci: prevent resuse runner Signed-off-by: Dennis Zhuang * fix: ci Signed-off-by: Dennis Zhuang * ci: Multi-language Integration Tests trigged only when pushing to main Signed-off-by: Dennis Zhuang --------- Signed-off-by: Dennis Zhuang --- .github/workflows/multi-lang-tests.yml | 172 +++++++++++++++++++++++++ src/servers/src/mysql/helper.rs | 40 +++++- 2 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/multi-lang-tests.yml diff --git a/.github/workflows/multi-lang-tests.yml b/.github/workflows/multi-lang-tests.yml new file mode 100644 index 0000000000..792ed36b75 --- /dev/null +++ b/.github/workflows/multi-lang-tests.yml @@ -0,0 +1,172 @@ +name: Multi-language Integration Tests + +on: + push: + branches: + - main + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + build-greptimedb: + if: ${{ github.repository == 'GreptimeTeam/greptimedb' }} + name: Build GreptimeDB binary + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: arduino/setup-protoc@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + - uses: actions-rust-lang/setup-rust-toolchain@v1 + - uses: Swatinem/rust-cache@v2 + with: + shared-key: "multi-lang-build" + cache-all-crates: "true" + save-if: ${{ github.ref == 'refs/heads/main' }} + - name: Install cargo-gc-bin + shell: bash + run: cargo install cargo-gc-bin --force + - name: Build greptime binary + shell: bash + run: cargo gc -- --bin greptime --features "pg_kvbackend,mysql_kvbackend" + - name: Pack greptime binary + shell: bash + run: | + mkdir bin && \ + mv ./target/debug/greptime bin + - name: Print greptime binary info + run: ls -lh bin + - name: Upload greptime binary + uses: actions/upload-artifact@v4 + with: + name: greptime-bin + path: bin/ + retention-days: 1 + + run-multi-lang-tests: + name: Run Multi-language SDK Tests + needs: build-greptimedb + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout greptimedb-tests repository + uses: actions/checkout@v4 + with: + repository: GreptimeTeam/greptimedb-tests + persist-credentials: false + + - name: Download pre-built greptime binary + uses: actions/download-artifact@v4 + with: + name: greptime-bin + path: bin + + - name: Setup greptime binary + run: | + chmod +x ./bin/greptime + ls -lh ./bin/greptime + ./bin/greptime --version + + - name: Setup Java 17 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + cache: 'maven' + + - name: Setup Python 3.8 + uses: actions/setup-python@v5 + with: + python-version: '3.8' + + - name: Setup Go 1.24 + uses: actions/setup-go@v5 + with: + go-version: '1.24' + cache: true + cache-dependency-path: go-tests/go.sum + + - name: Install Python dependencies + run: | + pip install mysql-connector-python psycopg2-binary + python3 -c "import mysql.connector; print(f'mysql-connector-python {mysql.connector.__version__}')" + python3 -c "import psycopg2; print(f'psycopg2 {psycopg2.__version__}')" + + - name: Install Go dependencies + working-directory: go-tests + run: | + go mod download + go mod verify + go version + + - name: Kill existing GreptimeDB processes + run: | + pkill -f greptime || true + sleep 2 + + - name: Start GreptimeDB standalone + run: | + ./bin/greptime standalone start \ + --http-addr 0.0.0.0:4000 \ + --rpc-addr 0.0.0.0:4001 \ + --mysql-addr 0.0.0.0:4002 \ + --postgres-addr 0.0.0.0:4003 \ + --user-provider=static_user_provider:cmd:greptime_user=greptime_pwd > /tmp/greptimedb.log 2>&1 & + + - name: Wait for GreptimeDB to be ready + run: | + echo "Waiting for GreptimeDB..." + for i in {1..60}; do + if curl -sf http://localhost:4000/health > /dev/null; then + echo "✅ GreptimeDB is ready" + exit 0 + fi + sleep 2 + done + echo "❌ GreptimeDB failed to start" + cat /tmp/greptimedb.log + exit 1 + + - name: Run multi-language tests + env: + DB_NAME: test_db + MYSQL_HOST: localhost + MYSQL_PORT: 4002 + POSTGRES_HOST: localhost + POSTGRES_PORT: 4003 + HTTP_PORT: 4000 + GREPTIME_USERNAME: greptime_user + GREPTIME_PASSWORD: greptime_pwd + run: | + chmod +x ./run_tests.sh + ./run_tests.sh + + - name: Collect logs on failure + if: failure() + run: | + echo "=== GreptimeDB Logs ===" + cat /tmp/greptimedb.log || true + + - name: Upload test logs on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: test-logs + path: | + /tmp/greptimedb.log + java-tests/target/surefire-reports/ + python-tests/.pytest_cache/ + go-tests/*.log + **/test-output/ + retention-days: 7 + + - name: Cleanup + if: always() + run: | + pkill -f greptime || true diff --git a/src/servers/src/mysql/helper.rs b/src/servers/src/mysql/helper.rs index cf92741bea..f765fba2d4 100644 --- a/src/servers/src/mysql/helper.rs +++ b/src/servers/src/mysql/helper.rs @@ -18,7 +18,7 @@ use std::time::Duration; use chrono::NaiveDate; use common_query::prelude::ScalarValue; use common_sql::convert::sql_value_to_value; -use common_time::Timestamp; +use common_time::{Date, Timestamp}; use datafusion_common::tree_node::{Transformed, TreeNode}; use datafusion_expr::LogicalPlan; use datatypes::prelude::ConcreteDataType; @@ -210,7 +210,8 @@ pub fn convert_value(param: &ParamValue, t: &ConcreteDataType) -> Result Ok(ScalarValue::Binary(Some(b.to_vec()))), - ConcreteDataType::Timestamp(ts_type) => covert_bytes_to_timestamp(b, ts_type), + ConcreteDataType::Timestamp(ts_type) => convert_bytes_to_timestamp(b, ts_type), + ConcreteDataType::Date(_) => convert_bytes_to_date(b), _ => error::PreparedStmtTypeMismatchSnafu { expected: t, actual: param.coltype, @@ -285,7 +286,7 @@ pub fn convert_expr_to_scalar_value(param: &Expr, t: &ConcreteDataType) -> Resul } } -fn covert_bytes_to_timestamp(bytes: &[u8], ts_type: &TimestampType) -> Result { +fn convert_bytes_to_timestamp(bytes: &[u8], ts_type: &TimestampType) -> Result { let ts = Timestamp::from_str_utc(&String::from_utf8_lossy(bytes)) .map_err(|e| { error::MysqlValueConversionSnafu { @@ -314,6 +315,17 @@ fn covert_bytes_to_timestamp(bytes: &[u8], ts_type: &TimestampType) -> Result Result { + let date = Date::from_str_utc(&String::from_utf8_lossy(bytes)).map_err(|e| { + error::MysqlValueConversionSnafu { + err_msg: e.to_string(), + } + .build() + })?; + + Ok(ScalarValue::Date32(Some(date.val()))) +} + #[cfg(test)] mod tests { use datatypes::types::{ @@ -512,8 +524,28 @@ mod tests { ]; for (input, ts_type, expected) in test_cases { - let result = covert_bytes_to_timestamp(input.as_bytes(), &ts_type).unwrap(); + let result = convert_bytes_to_timestamp(input.as_bytes(), &ts_type).unwrap(); assert_eq!(result, expected); } } + + #[test] + fn test_convert_bytes_to_date() { + let test_cases = vec![ + // Standard date format: YYYY-MM-DD + ("1970-01-01", ScalarValue::Date32(Some(0))), + ("1969-12-31", ScalarValue::Date32(Some(-1))), + ("2024-02-29", ScalarValue::Date32(Some(19782))), + ("2024-01-01", ScalarValue::Date32(Some(19723))), + ("2024-12-31", ScalarValue::Date32(Some(20088))), + ("2001-01-02", ScalarValue::Date32(Some(11324))), + ("2050-06-14", ScalarValue::Date32(Some(29384))), + ("2020-03-15", ScalarValue::Date32(Some(18336))), + ]; + + for (input, expected) in test_cases { + let result = convert_bytes_to_date(input.as_bytes()).unwrap(); + assert_eq!(result, expected, "Failed for input: {}", input); + } + } }