Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Interval expression to Snowpark API #1191

Merged
merged 11 commits into from
Jan 11, 2024
4 changes: 4 additions & 0 deletions src/snowflake/snowpark/_internal/analyzer/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
Expression,
FunctionExpression,
InExpression,
Interval,
Like,
ListAgg,
Literal,
Expand Down Expand Up @@ -339,6 +340,9 @@ def analyze(
sql = sql.upper()
return sql

if isinstance(expr, Interval):
sfc-gh-nkrishna marked this conversation as resolved.
Show resolved Hide resolved
return expr.sql()

if isinstance(expr, Attribute):
assert self.alias_maps_to_use is not None
name = self.alias_maps_to_use.get(expr.expr_id, expr.name)
Expand Down
40 changes: 40 additions & 0 deletions src/snowflake/snowpark/_internal/analyzer/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,46 @@ def __init__(self, value: Any, datatype: Optional[DataType] = None) -> None:
self.datatype = infer_type(value)


class Interval(Expression):
def __init__(
self,
year: int = 0,
quarter: int = 0,
month: int = 0,
week: int = 0,
day: int = 0,
hour: int = 0,
minute: int = 0,
second: int = 0,
millisecond: int = 0,
microsecond: int = 0,
nanosecond: int = 0,
) -> None:
super().__init__()
self.year = year
self.quarter = quarter
self.month = month
self.week = week
self.day = day
self.hour = hour
self.minute = minute
self.second = second
self.millisecond = millisecond
self.microsecond = microsecond
self.nanosecond = nanosecond

def sql(self) -> str:
return (
f"INTERVAL '{self.year} year, {self.quarter} quarter, {self.month} month, "
f"{self.week} week, {self.day} day, {self.hour} hour, {self.minute} minute, "
f"{self.second} second, {self.millisecond} millisecond, {self.microsecond} microsecond, "
f"{self.nanosecond} nanosecond'"
sfc-gh-nkrishna marked this conversation as resolved.
Show resolved Hide resolved
)

def __str__(self) -> str:
return self.sql()


class Like(Expression):
def __init__(self, expr: Expression, pattern: Expression) -> None:
super().__init__(expr)
Expand Down
28 changes: 27 additions & 1 deletion tests/integ/test_dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from snowflake.connector import IntegrityError
from snowflake.snowpark import Column, Row, Window
from snowflake.snowpark._internal.analyzer.analyzer_utils import result_scan_statement
from snowflake.snowpark._internal.analyzer.expression import Attribute, Star
from snowflake.snowpark._internal.analyzer.expression import Attribute, Interval, Star
from snowflake.snowpark._internal.utils import TempObjectType, warning_dict
from snowflake.snowpark.exceptions import (
SnowparkColumnException,
Expand Down Expand Up @@ -3440,3 +3440,29 @@ def test_drop_columns_special_names(session):
Utils.check_answer(df2, [Row(1), Row(2)])
finally:
Utils.drop_table(session, table_name)


def test_dataframe_interval_operation(session):
sfc-gh-nkrishna marked this conversation as resolved.
Show resolved Hide resolved
df = session.create_dataframe(
[
[datetime.datetime(2010, 1, 1), datetime.datetime(2011, 1, 1)],
[datetime.datetime(2012, 1, 1), datetime.datetime(2013, 1, 1)],
],
schema=["a", "b"],
)
df2 = df.with_column("TWO_DAYS_AHEAD", df["a"] + Column(Interval(day=2)))
Utils.check_answer(
df2,
[
Row(
datetime.datetime(2010, 1, 1),
datetime.datetime(2011, 1, 1),
datetime.datetime(2010, 1, 3),
),
Row(
datetime.datetime(2012, 1, 1),
datetime.datetime(2013, 1, 1),
datetime.datetime(2012, 1, 3),
),
],
)
Loading