-
Notifications
You must be signed in to change notification settings - Fork 120
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
SNOW-1043123: Add greatest/least support to local testing #1247
Conversation
|
||
|
||
@patch("least") | ||
def mock_least(*exprs: ColumnEmulator): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first argument determines the return type:
If the first type is numeric, then the return type is ‘widened’ according to the numeric types in the list of all arguments.
If the first type is not numeric, then all other arguments must be convertible to the first type.
If any of the argument values is NULL, the result is NULL.
from https://docs.snowflake.com/en/sql-reference/functions/least#usage-notes
The first two cases since those require coercion (implicit type conversion, "123" can be implicitly evaluated to 123
) for this PR, we should leave a TODO in the docstring.
Does the current implementation support the third case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've modified logic and added test cases to show that coercion works correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I had a typo in the comment above, I meant that coercion is not supported in Local Testing in general so we could ignore the first two cases in this PR and simply leave a TODO in the docstring (including a JIRA ticket)
|
||
|
||
@patch("greatest") | ||
def mock_greatest(*exprs: ColumnEmulator): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a TODO comment for type coercion in the docstrings and check the edge case when the input column (only) has null values.
def _compare(x: Any, y: Any) -> Tuple[Any, Any]: | ||
""" | ||
Compares two values based on the rules described for greatest/least | ||
https://docs.snowflake.com/en/sql-reference/functions/least#usage-notes | ||
""" | ||
if x is None or y is None: | ||
return (None, None) | ||
|
||
_x = x if isinstance(x, Number) else float(x) | ||
_y = y if isinstance(y, Number) else float(y) | ||
|
||
if _x > _y: | ||
return (_x, _y) | ||
else: | ||
return (_y, _x) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we consider non-numeric types here?
I see in the description https://docs.snowflake.com/en/sql-reference/functions/least:
LEAST supports all data types, including VARIANT.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can add some more test cases to be sure. From what I can tell anything non numeric gets cast to a numeric type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interesting, if I compare a string "abc" with a date "2024-01-01", both would be cast to numeric first?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That case actually generates an exception:
SELECT greatest("A", "B") FROM ( SELECT "A", to_timestamp("B") AS "B" FROM ( SELECT $1 AS "A", $2 AS "B" FROM VALUES ('abc' :: STRING, '2024-01-01 00:00:00' :: STRING)));
Timestamp 'abc' is not recognized
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although if they are all strings it does look like they aren't converted to numerics so the _compare function is incorrect. I'll fix it up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks! do you know what exception class it's raising?
I'm thinking of raising the same error to align the error experience in live mode.
for example, I noticed that in the code type(_x)(y)
, this might raise ValueError
, I'm thinking if we should wrap this into a snowpark client exception
@@ -933,6 +933,45 @@ def mock_to_variant(expr: ColumnEmulator): | |||
return res | |||
|
|||
|
|||
def _compare(x: Any, y: Any) -> Tuple[Any, Any]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use TypeVar
here to indicate that input and output needs to be the same type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have left a comment about the error experience, otherwise the change LGTM
def _compare(x: Any, y: Any) -> Tuple[Any, Any]: | ||
""" | ||
Compares two values based on the rules described for greatest/least | ||
https://docs.snowflake.com/en/sql-reference/functions/least#usage-notes | ||
""" | ||
if x is None or y is None: | ||
return (None, None) | ||
|
||
_x = x if isinstance(x, Number) else float(x) | ||
_y = y if isinstance(y, Number) else float(y) | ||
|
||
if _x > _y: | ||
return (_x, _y) | ||
else: | ||
return (_y, _x) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks! do you know what exception class it's raising?
I'm thinking of raising the same error to align the error experience in live mode.
for example, I noticed that in the code type(_x)(y)
, this might raise ValueError
, I'm thinking if we should wrap this into a snowpark client exception
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, @sfc-gh-jrose can you help create a JIRA to track the error experience?
Please answer these questions before submitting your pull requests. Thanks!
What GitHub issue is this PR addressing? Make sure that there is an accompanying issue to your PR.
Fixes #1043123
Fill out the following pre-review checklist:
Please describe how your code solves the related issue.
This commit adds support for
greatest
andleast
in local testing. There are also a bunch of format changes that pre-commit made as well.