Skip to content

Commit 46c6ce0

Browse files
author
peco-engineer-bot[bot]
committed
Cursor.description collapses TIMESTAMP_NTZ to 'timestamp' on the SELECT path (#786)
Signed-off-by: peco-engineer-bot[bot] <3815206+peco-engineer-bot[bot]@users.noreply.github.com>
1 parent 84ab9b1 commit 46c6ce0

5 files changed

Lines changed: 49 additions & 8 deletions

File tree

src/databricks/sql/backend/thrift_backend.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -777,8 +777,10 @@ def _col_to_description(col, field=None, host_url=None):
777777
sql_type = field.metadata.get(b"Spark:DataType:SqlName")
778778
if sql_type == b"VARIANT":
779779
cleaned_type = "variant"
780+
elif sql_type == b"TIMESTAMP_NTZ":
781+
cleaned_type = "timestamp_ntz"
780782
except Exception as e:
781-
logger.debug(f"Could not extract variant type from field: {e}")
783+
logger.debug(f"Could not extract type from field metadata: {e}")
782784

783785
return col.columnName, cleaned_type, None, None, precision, scale, None
784786

src/databricks/sql/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,7 @@ def convert_to_assigned_datatypes_in_column_table(column_table, description):
771771
converted_column_table.append(
772772
tuple(v if v is None else datetime.date.fromisoformat(v) for v in col)
773773
)
774-
elif description[i][1] == "timestamp":
774+
elif description[i][1] in ("timestamp", "timestamp_ntz"):
775775
converted_column_table.append(
776776
tuple((v if v is None else parser.parse(v)) for v in col)
777777
)

tests/e2e/test_driver.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,22 @@ def test_timezone_with_timestamp(self):
10431043
assert arrow_result_table.field(0).type == ts_type
10441044
assert arrow_result_value == expected.timestamp() * 1000000
10451045

1046+
def test_timestamp_ntz_description_type_code(self):
1047+
# See issue #786: cursor.description must distinguish TIMESTAMP_NTZ
1048+
# from TIMESTAMP. Both arrive over Thrift as TTypeId.TIMESTAMP_TYPE,
1049+
# so the type_code must be recovered from the Arrow field metadata.
1050+
with self.cursor() as cursor:
1051+
cursor.execute(
1052+
"SELECT "
1053+
" CAST('2024-10-07 12:00:00' AS TIMESTAMP) AS tz_aware, "
1054+
" CAST('2024-10-07 12:00:00' AS TIMESTAMP_NTZ) AS tz_naive"
1055+
)
1056+
description = cursor.description
1057+
assert description[0][0] == "tz_aware"
1058+
assert description[0][1] == "timestamp"
1059+
assert description[1][0] == "tz_naive"
1060+
assert description[1][1] == "timestamp_ntz"
1061+
10461062
@skipUnless(pysql_supports_arrow(), "arrow test needs arrow support")
10471063
def test_can_flip_compression(self):
10481064
with self.cursor() as cursor:

tests/unit/test_thrift_backend.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2454,21 +2454,44 @@ def test_execute_command_sets_complex_type_fields_correctly(
24542454
@unittest.skipIf(pyarrow is None, "Requires pyarrow")
24552455
def test_col_to_description(self):
24562456
test_cases = [
2457-
("variant_col", {b"Spark:DataType:SqlName": b"VARIANT"}, "variant"),
2458-
("normal_col", {}, "string"),
2457+
(
2458+
"variant_col",
2459+
ttypes.TTypeId.STRING_TYPE,
2460+
{b"Spark:DataType:SqlName": b"VARIANT"},
2461+
"variant",
2462+
),
2463+
(
2464+
"timestamp_ntz_col",
2465+
ttypes.TTypeId.TIMESTAMP_TYPE,
2466+
{b"Spark:DataType:SqlName": b"TIMESTAMP_NTZ"},
2467+
"timestamp_ntz",
2468+
),
2469+
(
2470+
"timestamp_col",
2471+
ttypes.TTypeId.TIMESTAMP_TYPE,
2472+
{b"Spark:DataType:SqlName": b"TIMESTAMP"},
2473+
"timestamp",
2474+
),
2475+
("normal_col", ttypes.TTypeId.STRING_TYPE, {}, "string"),
24592476
(
24602477
"weird_field",
2478+
ttypes.TTypeId.STRING_TYPE,
24612479
{b"Spark:DataType:SqlName": b"Some unexpected value"},
24622480
"string",
24632481
),
2464-
("missing_field", None, "string"), # None field case
2482+
(
2483+
"missing_field",
2484+
ttypes.TTypeId.STRING_TYPE,
2485+
None,
2486+
"string",
2487+
), # None field case
24652488
]
24662489

2467-
for column_name, field_metadata, expected_type in test_cases:
2490+
for column_name, primitive_type, field_metadata, expected_type in test_cases:
24682491
with self.subTest(column_name=column_name, expected_type=expected_type):
24692492
col = ttypes.TColumnDesc(
24702493
columnName=column_name,
2471-
typeDesc=self._make_type_desc(ttypes.TTypeId.STRING_TYPE),
2494+
typeDesc=self._make_type_desc(primitive_type),
24722495
)
24732496

24742497
field = (

tests/unit/test_util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def get_column_table_and_description(self):
3030
("decimal_column", "decimal", None, None, 10, 2, None),
3131
("date_column", "date", None, None, None, None, None),
3232
("timestamp_column", "timestamp", None, None, None, None, None),
33-
("timestamp_ntz_column", "timestamp", None, None, None, None, None),
33+
("timestamp_ntz_column", "timestamp_ntz", None, None, None, None, None),
3434
("timestamp_column_2", "timestamp", None, None, None, None, None),
3535
("timestamp_column_3", "timestamp", None, None, None, None, None),
3636
("timestamp_column_4", "timestamp", None, None, None, None, None),

0 commit comments

Comments
 (0)