Skip to content

Commit 50c52d3

Browse files
committed
SNOW-2272863: updated datatype mapper
1 parent ccc0c87 commit 50c52d3

6 files changed

Lines changed: 101 additions & 9 deletions

File tree

src/snowflake/snowpark/_internal/analyzer/datatype_mapper.py

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def str_to_sql_for_year_month_interval(
9494

9595
def str_to_sql_for_day_time_interval(value: str, datatype: DayTimeIntervalType) -> str:
9696
"""
97-
Converts "INTERVAL DD HH:MM:SS.ffffff [DAY TO SECOND | DAY | HOUR | MINUTE | SECOND]" to quoted format:
97+
Converts "INTERVAL DD HH:MM:SS.ffffff [DAY | HOUR | MINUTE | SECOND (TO) (DAY | HOUR | MINUTE | SECOND)]" to quoted format:
9898
- Same start/end field: extracts specific value (DAY, HOUR, MINUTE, or SECOND only)
9999
- Different fields: uses full range format (e.g., "DAY TO SECOND", "HOUR TO MINUTE")
100100
- Supports passthrough for already-quoted intervals
@@ -114,12 +114,53 @@ def str_to_sql_for_day_time_interval(value: str, datatype: DayTimeIntervalType)
114114
if len(parts[1]) > 1 and parts[1].startswith("'") and parts[1].endswith("'"):
115115
return value # passthrough
116116

117-
start_field = datatype.start_field if datatype.start_field is not None else 0
118-
end_field = datatype.end_field if datatype.end_field is not None else 1
117+
start_field = (
118+
datatype.start_field
119+
if datatype.start_field is not None
120+
else DayTimeIntervalType.DAY
121+
)
122+
end_field = (
123+
datatype.end_field
124+
if datatype.end_field is not None
125+
else DayTimeIntervalType.SECOND
126+
)
119127
if datatype.start_field == datatype.end_field:
120128
# When the start_field equals the end_field, it implies our DayTimeIntervalType is only
121129
# using a single field. This can be 1 of 4 choices. DAY for 0, HOUR for 1, MINUTE for 2, and SECOND for 3.
122-
extracted_value = parts[1]
130+
# We need to handle two cases:
131+
# 1. Simple format: "INTERVAL 23 HOUR" - parts[1] contains the value directly
132+
# 2. Complex format: "INTERVAL 1 01:01:01.7878 DAY TO SECOND" - need to parse the components
133+
134+
# Check if this is a simple format (e.g., "INTERVAL 23 HOUR")
135+
if len(parts) >= 3 and parts[2].upper() in ["DAY", "HOUR", "MINUTE", "SECOND"]:
136+
# Simple format: just use the value directly
137+
extracted_value = parts[1]
138+
elif len(parts) >= 3 and ":" in parts[2]:
139+
if datatype.start_field == 0:
140+
extracted_value = parts[1]
141+
else:
142+
time_part = parts[2]
143+
time_components = time_part.split(":")
144+
if datatype.start_field == 1:
145+
extracted_value = time_components[0]
146+
elif datatype.start_field == 2:
147+
extracted_value = (
148+
time_components[1] if len(time_components) > 1 else "0"
149+
)
150+
elif datatype.start_field == 3:
151+
if len(time_components) > 2:
152+
seconds_part = time_components[2]
153+
if "." in seconds_part:
154+
_, fractional = seconds_part.split(".", 1)
155+
extracted_value = fractional
156+
else:
157+
extracted_value = seconds_part
158+
else:
159+
extracted_value = "0"
160+
else:
161+
extracted_value = parts[1]
162+
else:
163+
extracted_value = parts[1]
123164
return (
124165
f"INTERVAL '{extracted_value}' {datatype._FIELD_NAMES[start_field].upper()}"
125166
)

src/snowflake/snowpark/_internal/type_utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,8 +1080,10 @@ def get_data_type_string_object_mappings(
10801080
)
10811081
DATA_TYPE_STRING_OBJECT_MAPPINGS["interval_year_to_month"] = YearMonthIntervalType
10821082
DATA_TYPE_STRING_OBJECT_MAPPINGS["intervalyeartomonth"] = YearMonthIntervalType
1083+
DATA_TYPE_STRING_OBJECT_MAPPINGS["interval_year_month"] = DayTimeIntervalType
10831084
DATA_TYPE_STRING_OBJECT_MAPPINGS["interval_day_to_second"] = DayTimeIntervalType
10841085
DATA_TYPE_STRING_OBJECT_MAPPINGS["intervaldaytosecond"] = DayTimeIntervalType
1086+
DATA_TYPE_STRING_OBJECT_MAPPINGS["interval_day_time"] = DayTimeIntervalType
10851087
DATA_TYPE_STRING_OBJECT_MAPPINGS["str"] = StringType
10861088
DATA_TYPE_STRING_OBJECT_MAPPINGS["varchar"] = StringType
10871089

src/snowflake/snowpark/functions.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10915,7 +10915,7 @@ def make_interval(
1091510915

1091610916
@private_preview(
1091710917
version="1.38.0",
10918-
extra_doc_string="Type YearMonthIntervalType is currently in private preview and needs to be enabled by setting parameter `FEATURE_INTERVAL_TYPES` to `ENABLED` and `ENABLE_INTERVAL_SUBTYPES` to `TRUE`.",
10918+
extra_doc_string="Type YearMonthIntervalType is currently in private preview and needs to be enabled by setting parameter `FEATURE_INTERVAL_TYPES` to `ENABLED`",
1091910919
)
1092010920
@publicapi
1092110921
def interval_year_month_from_parts(
@@ -10941,7 +10941,6 @@ def interval_year_month_from_parts(
1094110941
>>> from snowflake.snowpark.functions import interval_year_month_from_parts
1094210942
>>>
1094310943
>>> _ = session.sql("ALTER SESSION SET FEATURE_INTERVAL_TYPES=ENABLED;").collect()
10944-
>>> _ = session.sql("ALTER SESSION SET ENABLE_INTERVAL_SUBTYPES=TRUE;").collect()
1094510944
>>> df = session.create_dataframe([[1, 2]], ["years", "months"])
1094610945
>>> df.select(interval_year_month_from_parts(col("years"), col("months")).alias("interval")).show()
1094710946
--------------

src/snowflake/snowpark/types.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ class YearMonthIntervalType(_AnsiIntervalType):
248248
end_field: The end field of the interval (0=YEAR, 1=MONTH)
249249
250250
Notes:
251-
YearMonthIntervalType is currently in private preview since 1.38.0. It needs to be enabled by setting parameter `FEATURE_INTERVAL_TYPES` to `ENABLED` and `ENABLE_INTERVAL_SUBTYPES` to `TRUE`.
251+
YearMonthIntervalType is currently in private preview since 1.38.0. It needs to be enabled by setting parameter `FEATURE_INTERVAL_TYPES` to `ENABLED`.
252252
253253
YearMonthIntervalType is currently not supported in UDFs and Stored Procedures.
254254
"""
@@ -309,7 +309,7 @@ class DayTimeIntervalType(_AnsiIntervalType):
309309
end_field: The end field of the interval (0=DAY, 1=HOUR, 2=MINUTE, 3=SECOND)
310310
311311
Notes:
312-
DayTimeIntervalType is currently in private preview since 1.38.0. It needs to be enabled by setting parameters `FEATURE_INTERVAL_TYPES` to `ENABLED` and `ENABLE_INTERVAL_SUBTYPES` to `TRUE`.
312+
DayTimeIntervalType is currently in private preview since 1.38.0. It needs to be enabled by setting parameters `FEATURE_INTERVAL_TYPES` to `ENABLED`.
313313
314314
DayTimeIntervalType is currently not supported in UDFs and Stored Procedures.
315315
"""

tests/integ/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ def session(
274274
.config("local_testing", local_testing_mode)
275275
.config(
276276
"session_parameters",
277-
{"feature_interval_types": "ENABLED", "enable_interval_subtypes": "true"},
277+
{"feature_interval_types": "ENABLED"},
278278
)
279279
.create()
280280
)

tests/unit/test_datatype_mapper.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,30 @@ def test_to_sql():
233233
to_sql("INTERVAL 125.750 SECOND", DayTimeIntervalType(3))
234234
== "INTERVAL '125.750' SECOND :: INTERVAL SECOND"
235235
)
236+
assert (
237+
to_sql("INTERVAL 1 01:01:01.7878 DAY TO SECOND", DayTimeIntervalType(3))
238+
== "INTERVAL '7878' SECOND :: INTERVAL SECOND"
239+
)
240+
assert (
241+
to_sql("INTERVAL 5 12:30:45 DAY TO SECOND", DayTimeIntervalType(0))
242+
== "INTERVAL '5' DAY :: INTERVAL DAY"
243+
)
244+
assert (
245+
to_sql("INTERVAL 3 08:45:30 DAY TO SECOND", DayTimeIntervalType(1))
246+
== "INTERVAL '08' HOUR :: INTERVAL HOUR"
247+
)
248+
assert (
249+
to_sql("INTERVAL 2 14:37:22 DAY TO SECOND", DayTimeIntervalType(2))
250+
== "INTERVAL '37' MINUTE :: INTERVAL MINUTE"
251+
)
252+
assert (
253+
to_sql("INTERVAL 1 05:20:15.123456 DAY TO SECOND", DayTimeIntervalType(3))
254+
== "INTERVAL '123456' SECOND :: INTERVAL SECOND"
255+
)
256+
assert (
257+
to_sql("INTERVAL 4 16:42:58 DAY TO SECOND", DayTimeIntervalType(3))
258+
== "INTERVAL '58' SECOND :: INTERVAL SECOND"
259+
)
236260

237261

238262
def test_to_sql_system_function():
@@ -405,6 +429,32 @@ def test_to_sql_system_function():
405429
to_sql_no_cast("INTERVAL 125.750 SECOND", DayTimeIntervalType(3))
406430
== "INTERVAL '125.750' SECOND"
407431
)
432+
assert (
433+
to_sql_no_cast("INTERVAL 1 01:01:01.7878 DAY TO SECOND", DayTimeIntervalType(3))
434+
== "INTERVAL '7878' SECOND"
435+
)
436+
assert (
437+
to_sql_no_cast("INTERVAL 5 12:30:45 DAY TO SECOND", DayTimeIntervalType(0))
438+
== "INTERVAL '5' DAY"
439+
)
440+
assert (
441+
to_sql_no_cast("INTERVAL 3 08:45:30 DAY TO SECOND", DayTimeIntervalType(1))
442+
== "INTERVAL '08' HOUR"
443+
)
444+
assert (
445+
to_sql_no_cast("INTERVAL 2 14:37:22 DAY TO SECOND", DayTimeIntervalType(2))
446+
== "INTERVAL '37' MINUTE"
447+
)
448+
assert (
449+
to_sql_no_cast(
450+
"INTERVAL 1 05:20:15.123456 DAY TO SECOND", DayTimeIntervalType(3)
451+
)
452+
== "INTERVAL '123456' SECOND"
453+
)
454+
assert (
455+
to_sql_no_cast("INTERVAL 4 16:42:58 DAY TO SECOND", DayTimeIntervalType(3))
456+
== "INTERVAL '58' SECOND"
457+
)
408458

409459

410460
def test_generate_call_python_sp_sql():

0 commit comments

Comments
 (0)