This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch fix/crate-timestamp-testcontainers
in repository https://gitbox.apache.org/repos/asf/superset.git

commit c0d1dc51451ae63d40310e332009aad90c8fd238
Author: Evan Rusackas <[email protected]>
AuthorDate: Fri Oct 2 13:52:35 2026 -0700

    test(cratedb): cover epoch-ms timestamp decoding against a real instance
    
    #44720 fixed CrateEngineSpec.fetch_data to decode TIMESTAMP columns via
    cursor._result["col_types"] (crate-python's DBAPI cursor.description
    always reports type_code=None, confirmed by reading
    crate.client.cursor.Cursor.description directly), but only unit tests
    with a mocked cursor._result ever exercised it. Those would never
    notice a future crate-python release renaming or restructuring that
    private attribute.
    
    Adds a testcontainers case that creates a real table with both
    TIMESTAMP WITHOUT TIME ZONE and TIMESTAMP WITH TIME ZONE columns
    (type codes 15 and 11 per crate.client.converter.DataType, confirmed
    against the installed driver), inserts epoch-millisecond values, and
    runs fetch_data against the real raw DBAPI cursor from a live query.
    
    Co-Authored-By: Evan Rusackas <[email protected]>
    Co-Authored-By: Claude Sonnet 5 <[email protected]>
---
 tests/testcontainers/db_engine_specs/test_crate.py | 44 ++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/tests/testcontainers/db_engine_specs/test_crate.py 
b/tests/testcontainers/db_engine_specs/test_crate.py
index 6ad7fb3cc25..245fb1b5d11 100644
--- a/tests/testcontainers/db_engine_specs/test_crate.py
+++ b/tests/testcontainers/db_engine_specs/test_crate.py
@@ -26,6 +26,7 @@ runs natively on GitHub Actions' x86_64 runners.
 """
 
 from collections.abc import Iterator
+from datetime import datetime
 
 import pytest
 from sqlalchemy import (
@@ -101,3 +102,46 @@ def test_get_columns_maps_native_types(engine: Engine) -> 
None:
     for col in by_name.values():
         spec = CrateEngineSpec.get_column_spec(str(col["type"]))
         assert spec is not None
+
+
+def test_fetch_data_decodes_epoch_ms_timestamps(engine: Engine) -> None:
+    """
+    Regression for apache/superset#44720: crate-python's real DBAPI cursor
+    always reports ``type_code=None`` in ``cursor.description`` (confirmed
+    by reading ``crate.client.cursor.Cursor.description`` directly -- it
+    hardcodes ``None`` for every description slot but the column name), so
+    ``CrateEngineSpec.fetch_data`` has to find TIMESTAMP columns via the
+    driver's private ``cursor._result["col_types"]`` instead. The existing
+    unit tests mock that attribute directly and would never notice if a
+    future crate-python release renamed or restructured it; only a real
+    cursor, from a real query against a real server, exercises the actual
+    attribute this code depends on.
+
+    CrateDB's wire protocol reports both ``TIMESTAMP WITHOUT TIME ZONE`` and
+    ``TIMESTAMP WITH TIME ZONE`` as epoch-millisecond integers (type codes
+    15 and 11 respectively -- see ``crate.client.converter.DataType``);
+    both must decode to the same real ``datetime``.
+    """
+    with engine.begin() as conn:
+        conn.exec_driver_sql(
+            "CREATE TABLE pilot_timestamps ("
+            "id INTEGER PRIMARY KEY, "
+            "ts_no_tz TIMESTAMP WITHOUT TIME ZONE, "
+            "ts_with_tz TIMESTAMP WITH TIME ZONE"
+            ")"
+        )
+        conn.exec_driver_sql(
+            "INSERT INTO pilot_timestamps (id, ts_no_tz, ts_with_tz) "
+            "VALUES (1, 1704067200000, 1704067200000)"
+        )
+        conn.exec_driver_sql("REFRESH TABLE pilot_timestamps")
+
+    raw_conn = engine.raw_connection()
+    try:
+        cursor = raw_conn.cursor()
+        cursor.execute("SELECT ts_no_tz, ts_with_tz FROM pilot_timestamps 
WHERE id = 1")
+        rows = CrateEngineSpec.fetch_data(cursor)
+    finally:
+        raw_conn.close()
+
+    assert rows == [(datetime(2024, 1, 1), datetime(2024, 1, 1))]

Reply via email to