Skip to content

Commit bebb7e5

Browse files
authored
ci(python): expand driver compatibility coverage (#795)
* ci(python): expand driver compatibility coverage * ci(python): resolve latest driver dynamically
1 parent e117a1a commit bebb7e5

6 files changed

Lines changed: 66 additions & 11 deletions

File tree

.github/workflows/bindings.python.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,15 @@ jobs:
193193
with:
194194
python-version: "3.13"
195195

196+
- name: Resolve Databend version
197+
id: databend-version
198+
env:
199+
GH_TOKEN: ${{ github.token }}
200+
run: |
201+
version=$(gh api repos/databendlabs/databend/releases --jq '[.[] | select(.draft | not) | select(.tag_name | endswith("-nightly"))][0].tag_name')
202+
echo "version=$version" >> "$GITHUB_OUTPUT"
203+
echo "Running compatibility tests with Databend version: **$version**" >> "$GITHUB_STEP_SUMMARY"
204+
196205
- uses: wntrblm/nox@2025.05.01
197206
- name: Download artifact
198207
uses: actions/download-artifact@v4
@@ -201,3 +210,6 @@ jobs:
201210
path: bindings/python/dist
202211
- name: Run Nox
203212
run: nox -f tests/nox/noxfile.py
213+
env:
214+
DATABEND_META_VERSION: ${{ steps.databend-version.outputs.version }}
215+
DATABEND_QUERY_VERSION: ${{ steps.databend-version.outputs.version }}

AGENTS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ This repository is a Rust workspace for BendSQL plus a separate frontend, langua
2929
Format: `type(scope): description` or `type: description`.
3030
Allowed types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`.
3131

32+
## Python Binding Compatibility Tests
33+
34+
- Databend CI can run the latest Python binding tests against older driver versions, and BendSQL keeps a similar compatibility matrix so failures are caught here without blocking the main Databend repository.
35+
- When adding or changing tests in `bindings/python/tests/**/steps/binding.py`, guard queries, API usage, and assertions that depend on a particular driver or server version with the existing `DRIVER_VERSION` and/or `DB_VERSION` checks. Refer to the existing uses of these variables in the `binding.py` files: execute only the version-dependent portion when the tested version supports it, while leaving version-independent coverage unconditional.
36+
- Keep the `latest` entry in the `new_test_with_old_drivers` matrix in `tests/nox/noxfile.py`; it installs the unpinned `databend-driver` release and resolves its concrete version for test gating. Representative older versions may be rotated, but `latest` must not be omitted or replaced with a pinned version.
37+
- Set each version boundary to the first release that supports the behavior. Do not make the older-driver/newer-test compatibility jobs require behavior that is only available in newer driver or server releases.
38+
3239
## Task Routing
3340

3441
- Rust behavior, CLI flags, REPL, output, or public Rust API changes: read `agents/rust-workspace.md`

bindings/python/tests/asyncio/steps/binding.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@
2424
tc = unittest.TestCase()
2525

2626
os.environ["DATABEND_DRIVER_HEARTBEAT_INTERVAL_SECONDS"] = "1"
27-
os.environ["RUST_LOG"] = "warn,databend_driver=debug,databend_client=debug"
27+
os.environ.setdefault(
28+
"RUST_LOG",
29+
"warn",
30+
)
31+
os.environ.setdefault("RUST_BACKTRACE", "1")
2832
import databend_driver
2933

3034
NOW = int(time.time())
@@ -265,6 +269,11 @@ async def _(context):
265269

266270

267271
async def test_load_file(context, load_method):
272+
# The load method argument replaced format_options/copy_options in v0.28.0.
273+
if DRIVER_VERSION < (0, 28, 0):
274+
print("SKIP: load_file method requires driver >= 0.28.0")
275+
return
276+
268277
if DRIVER_VERSION >= (0, 28, 3) and DB_VERSION >= (1, 2, 792):
269278
await context.conn.exec("CREATE OR REPLACE DATABASE db1")
270279
await context.conn.exec("use db1")

bindings/python/tests/blocking/steps/binding.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@
2525
from behave import given, when, then
2626

2727
os.environ["DATABEND_DRIVER_HEARTBEAT_INTERVAL_SECONDS"] = "1"
28-
os.environ["RUST_LOG"] = "warn,databend_driver=debug,databend_client=debug"
28+
os.environ.setdefault(
29+
"RUST_LOG",
30+
"warn",
31+
)
32+
os.environ.setdefault("RUST_BACKTRACE", "1")
2933
import databend_driver
3034

3135
NOW = int(time.time())
@@ -115,9 +119,11 @@ def _(context):
115119
row = context.conn.query_row("select to_binary('xyz')")
116120
assert row.values() == (b"xyz",), f"Binary: {row.values()}"
117121

118-
# Interval
119-
row = context.conn.query_row("select to_interval('1 microseconds')")
120-
assert row.values() == (timedelta(microseconds=1),), f"Interval: {row.values()}"
122+
# Older drivers decode a one-microsecond interval as one millisecond.
123+
if DRIVER_VERSION >= (0, 28, 0):
124+
# Interval
125+
row = context.conn.query_row("select to_interval('1 microseconds')")
126+
assert row.values() == (timedelta(microseconds=1),), f"Interval: {row.values()}"
121127

122128
# Decimal
123129
row = context.conn.query_row("SELECT 15.7563::Decimal(8,4), 2.0+3.0", params=[8, 4])
@@ -310,6 +316,11 @@ def _(context):
310316

311317

312318
def test_load_file(context, load_method):
319+
# The load method argument replaced format_options/copy_options in v0.28.0.
320+
if DRIVER_VERSION < (0, 28, 0):
321+
print("SKIP: load_file method requires driver >= 0.28.0")
322+
return
323+
313324
if DRIVER_VERSION >= (0, 28, 3) and DB_VERSION >= (1, 2, 792):
314325
context.conn.exec("CREATE OR REPLACE DATABASE db1")
315326
context.conn.exec("use db1")

bindings/python/tests/cursor/steps/binding.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@
2121
from behave import given, when, then
2222

2323
os.environ["DATABEND_DRIVER_HEARTBEAT_INTERVAL_SECONDS"] = "1"
24-
os.environ["RUST_LOG"] = "warn,databend_driver=debug,databend_client=debug"
24+
os.environ.setdefault(
25+
"RUST_LOG",
26+
"warn",
27+
)
28+
os.environ.setdefault("RUST_BACKTRACE", "1")
2529
import databend_driver
2630

2731
NOW = int(time.time())

tests/nox/noxfile.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import nox
1616
import os
1717

18+
LATEST_DRIVER = "latest"
19+
1820

1921
def generate_params1():
2022
for db_version in ["1.2.803", "1.2.791"]:
@@ -49,19 +51,29 @@ def new_driver_with_old_servers(session, db_version, query_result_format):
4951

5052

5153
def generate_params2():
52-
for driver_version in ["0.28.2", "0.28.1"]:
54+
for driver_version in ["0.27.6", "0.33.6", "0.34.0", LATEST_DRIVER]:
5355
for query_result_format in ["arrow", "json"]:
54-
v = tuple(map(int, driver_version.split(".")))
55-
if query_result_format == "arrow" and v <= (0, 30, 3):
56-
continue
56+
if driver_version != LATEST_DRIVER:
57+
v = tuple(map(int, driver_version.split(".")))
58+
if query_result_format == "arrow" and v <= (0, 30, 3):
59+
continue
5760
yield nox.param(driver_version, query_result_format)
5861

5962

6063
@nox.session
6164
@nox.parametrize(["driver_version", "query_result_format"], generate_params2())
6265
def new_test_with_old_drivers(session, driver_version, query_result_format):
6366
session.install("behave")
64-
session.install(f"databend-driver=={driver_version}")
67+
if driver_version == LATEST_DRIVER:
68+
session.install("databend-driver")
69+
driver_version = session.run(
70+
"python",
71+
"-c",
72+
"from importlib.metadata import version; print(version('databend-driver'))",
73+
silent=True,
74+
).strip()
75+
else:
76+
session.install(f"databend-driver=={driver_version}")
6577
with session.chdir(".."):
6678
env = {
6779
"DRIVER_VERSION": driver_version,

0 commit comments

Comments
 (0)