From b348eb46bc59103947dd33cdf95359b7de91d7f2 Mon Sep 17 00:00:00 2001 From: Fredrik Fornwall Date: Mon, 13 Jul 2026 18:58:07 +0200 Subject: [PATCH 1/2] test(statement): cover dictionary-encoded bind parameter columns Adds test_parameter_dictionary_encoded, gated on statement_bind: binds a dictionary-encoded string column (what pandas categoricals produce over the C data interface) and asserts the decoded values, including a null, round-trip via insert + read-back through the existing sample_table fixture and its query_override hook. Dictionary encoding is an encoding of the same logical values, not a different logical type (Arrow columnar format, Dictionary-encoded Layout), so a driver that binds plain string columns should accept the dictionary-encoded equivalent, decoding it if the database has no native counterpart. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01LGf8PVEe2tYkw8Q6Pd95tq Signed-off-by: Fredrik Fornwall --- adbc_drivers_validation/tests/statement.py | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/adbc_drivers_validation/tests/statement.py b/adbc_drivers_validation/tests/statement.py index d521b59..811d344 100644 --- a/adbc_drivers_validation/tests/statement.py +++ b/adbc_drivers_validation/tests/statement.py @@ -121,6 +121,43 @@ def test_parameter_execute( result = pyarrow.RecordBatchReader._import_from_c(handle.address).read_all() assert result[0].to_pylist() == [2, 3, 4, 5] + @pytest.mark.requires_features(["statement_bind"]) + def test_parameter_dictionary_encoded( + self, + driver: model.DriverQuirks, + conn: adbc_driver_manager.dbapi.Connection, + sample_table: str, + ) -> None: + # Dictionary encoding is an encoding of the same logical values, not + # a different logical type (Arrow columnar format, "Dictionary-encoded + # Layout"). A driver that binds string parameters should also accept + # a dictionary-encoded string column (what pandas produces for + # categoricals), decoding it if the database has no equivalent. + id_ = driver.quote_identifier("id") + value = driver.quote_identifier("value") + ids = pyarrow.array([7101, 7102, 7103], type=pyarrow.int64()) + values = pyarrow.array( + ["apple", "banana", None], type=pyarrow.string() + ).dictionary_encode() + parameters = pyarrow.RecordBatch.from_arrays([ids, values], names=["0", "1"]) + with conn.cursor() as cursor: + cursor.adbc_statement.set_sql_query( + f"INSERT INTO {sample_table} ({id_}, {value}) " + f"VALUES ({driver.bind_parameter(1)}, {driver.bind_parameter(2)})" + ) + cursor.adbc_statement.bind(parameters) + cursor.adbc_statement.prepare() + cursor.adbc_statement.execute_update() + + with conn.cursor() as cursor: + cursor.adbc_statement.set_sql_query( + f"SELECT {value} FROM {sample_table} " + f"WHERE {id_} IN (7101, 7102, 7103) ORDER BY {id_}" + ) + handle, _ = cursor.adbc_statement.execute_query() + result = pyarrow.RecordBatchReader._import_from_c(handle.address).read_all() + assert result[0].to_pylist() == ["apple", "banana", None] + def test_parameter_schema( self, driver: model.DriverQuirks, conn: adbc_driver_manager.dbapi.Connection ) -> None: From 79b2de6c2f9a643c4592d2d7231af4097dbf19b9 Mon Sep 17 00:00:00 2001 From: Fredrik Fornwall Date: Tue, 14 Jul 2026 11:27:31 +0200 Subject: [PATCH 2/2] test(statement): prepare before bind for driver compatibility Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01VsDgjiYuJYrp59oto64m9D --- adbc_drivers_validation/tests/statement.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/adbc_drivers_validation/tests/statement.py b/adbc_drivers_validation/tests/statement.py index 811d344..65273c8 100644 --- a/adbc_drivers_validation/tests/statement.py +++ b/adbc_drivers_validation/tests/statement.py @@ -145,8 +145,10 @@ def test_parameter_dictionary_encoded( f"INSERT INTO {sample_table} ({id_}, {value}) " f"VALUES ({driver.bind_parameter(1)}, {driver.bind_parameter(2)})" ) - cursor.adbc_statement.bind(parameters) + # Prepare before Bind: some drivers (e.g. the Go FlightSQL + # driver) require it, and prepare-first works everywhere. cursor.adbc_statement.prepare() + cursor.adbc_statement.bind(parameters) cursor.adbc_statement.execute_update() with conn.cursor() as cursor: