You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/10/02 20:53:21 UTC

(superset) branch fix/crate-timestamp-testcontainers created (now c0d1dc51451)

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

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


      at c0d1dc51451 test(cratedb): cover epoch-ms timestamp decoding against a real instance

This branch includes the following new commits:

     new c0d1dc51451 test(cratedb): cover epoch-ms timestamp decoding against a real instance

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(superset) 01/01: test(cratedb): cover epoch-ms timestamp decoding against a real instance

Posted by ru...@apache.org.
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 <ev...@rusackas.com>
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 <ev...@preset.io>
    Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
 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))]