Skip to content

Commit

Permalink
[RUN CI] PR changes
Browse files Browse the repository at this point in the history
  • Loading branch information
vineetg3 committed Feb 26, 2025
1 parent a386fb3 commit b7b9b90
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 84 deletions.
38 changes: 28 additions & 10 deletions documentation/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,16 +266,16 @@ For instance, `JOIN_STRINGS("; ", "Alpha", "Beta", "Gamma)` returns `"Alpha; Bet
<!-- TOC --><a name="lpad"></a>
### LPAD

The `LPAD` function pads an expression on the left side with a specified padding character and returns the padded expression till the desired length. It takes three arguments:
The `LPAD` function pads an expression on the left side with a specified padding character until it is the desired length. It takes three arguments:

1. The input expression to pad.
2. The desired final length of the expression.
3. The single character to use for padding.
1. The input string to pad.
2. The desired final length of the expression. It should be a positive integer literal.
3. The single character literal to use for padding.

The function behaves as follows:

- If the input expression is shorter than the desired length, it adds padding characters on the left until reaching the desired length.
- If the input expression is longer than the desired length, it truncates the expression from the left to match the desired length.
- If the input expression is longer than the desired length, it truncates the expression by removing characters from the right side until it matches the desired length.
- If the desired length is 0, it returns an empty string.
- If the desired length is negative, it raises an error.
- If the padding argument is not a single character, it raises an error.
Expand All @@ -284,19 +284,28 @@ The function behaves as follows:
Customers(left_padded_name = LPAD(name, 30, "*"))
```

For demonstration purposes here are examples on how it pads on string literals. Note that the first argument cannot be a string literal and this is only for demonstration purposes:
| Input | Output |
|-------|--------|
| `LPAD("123", 6, "0")` | `"000123"` |
| `LPAD("123", 5, "#")` | `"##123"` |
| `LPAD("123", 3, "0")` | `"123"` |
| `LPAD("123", 2, "0")` | `"12"` |
| `LPAD("123", 0, "0")` | `""` |

<!-- TOC --><a name="rpad"></a>
### RPAD

The `RPAD` function pads an expression on the right side with a specified padding character and returns the padded expression till the desired length. It takes three arguments:
The `RPAD` function pads an expression on the right side with a specified padding character until it is the desired length. It takes three arguments:

1. The input expression to pad.
2. The desired final length of the expression.
3. The single character to use for padding.
1. The input string to pad.
2. The desired final length of the expression. It should be a positive integer literal.
3. The single character literal to use for padding.

The function behaves as follows:

- If the input expression is shorter than the desired length, it adds padding characters on the right until reaching the desired length.
- If the input expression is longer than the desired length, it truncates the expression from the left to match the desired length.
- If the input expression is longer than the desired length, it truncates the expression by removing characters from the right side until it matches the desired length.
- If the desired length is 0, it returns an empty string
- If the desired length is negative, it raises an error
- If the padding argument is not a single character, it raises an error
Expand All @@ -305,6 +314,15 @@ The function behaves as follows:
Customers(right_padded_name = RPAD(name, 30, "*"))
```

For demonstration purposes here are examples on how it pads on string literals. Please note the first argument cannot be a string literal and this is only for demonstration purposes:
| Input | Output |
|-------|--------|
| `RPAD("123", 6, "0")` | `"123000"` |
| `RPAD("123", 5, "#")` | `"123##"` |
| `RPAD("123", 3, "0")` | `"123"` |
| `RPAD("123", 2, "0")` | `"12"` |
| `RPAD("123", 0, "0")` | `""` |

<!-- TOC --><a name="datetime-functions"></a>

## Datetime Functions
Expand Down
113 changes: 58 additions & 55 deletions pydough/sqlglot/transform_bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -729,15 +729,13 @@ def convert_contains(
return convert_like(None, [column, pattern])


def convert_lpad(
def pad_helper(
raw_args: Sequence[RelationalExpression] | None,
sql_glot_args: Sequence[SQLGlotExpression],
pad_func: str,
) -> SQLGlotExpression:
"""
Converts and pads the string to the left till the string is the specified length.
If length is 0, return an empty string.
If length is negative, raise an error.
If length is positive, pad the string on the left to the specified length.
Helper function for LPAD and RPAD.
Expects sqlglot_args[0] to be the column to pad.
Expects sqlglot_args[1] and sqlglot_args[2] to be literals.
Expects sqlglot_args[1] to be the returned length of the padded string.
Expand All @@ -748,45 +746,80 @@ def convert_lpad(
SQLGlot expressions. (Not actively used in this implementation.)
`sql_glot_args`: The operands passed to the function after they were converted
to SQLGlot expressions. The first operand is expected to be a string.
`pad_func`: The name of the padding function to use.
Returns:
The SQLGlot expression matching the functionality of
`LPAD(string, length, padding)`. With the caveat that if length is 0,
it will return an empty string.
A tuple of sqlglot expressions for the column to pad, the length of the column,
the required length, padding string and the integer literal of the required length.
"""
assert pad_func in ["LPAD", "RPAD"]
assert len(sql_glot_args) == 3

if (
not isinstance(sql_glot_args[1], sqlglot_expressions.Literal)
or sql_glot_args[1].is_string
isinstance(sql_glot_args[1], sqlglot_expressions.Literal)
and not sql_glot_args[1].is_string
):
raise ValueError("LPAD function requires the length argument to be an integer.")
try:
required_len = int(sql_glot_args[1].this)
if required_len < 0:
raise ValueError()
except ValueError:
raise ValueError(
f"{pad_func} function requires the length argument to be a non-negative integer literal."
)
else:
raise ValueError(
f"{pad_func} function requires the length argument to be a non-negative integer literal."
)

if (
not isinstance(sql_glot_args[2], sqlglot_expressions.Literal)
or not sql_glot_args[2].is_string
):
raise ValueError("LPAD function requires the padding argument to be a string.")
raise ValueError(
f"{pad_func} function requires the padding argument to be a string literal of length 1."
)
if len(str(sql_glot_args[2].this)) != 1:
raise ValueError(
"LPAD function requires the padding argument to be of length 1."
f"{pad_func} function requires the padding argument to be a string literal of length 1."
)

try:
required_len = int(sql_glot_args[1].this)
except ValueError:
raise ValueError("LPAD function requires the length argument to be an integer.")
if required_len < 0:
raise ValueError("LPAD function requires a non-negative length.")
if required_len == 0:
return sqlglot_expressions.convert("")

col_glot = sql_glot_args[0]
col_len_glot = sqlglot_expressions.Length(this=sql_glot_args[0])
required_len_glot = sqlglot_expressions.convert(required_len)
pad_string_glot = sqlglot_expressions.convert(
str(sql_glot_args[2].this) * required_len
)
return col_glot, col_len_glot, required_len_glot, pad_string_glot, required_len


def convert_lpad(
raw_args: Sequence[RelationalExpression] | None,
sql_glot_args: Sequence[SQLGlotExpression],
) -> SQLGlotExpression:
"""
Converts and pads the string to the left till the string is the specified length.
If length is 0, return an empty string.
If length is negative, raise an error.
If length is positive, pad the string on the left to the specified length.
Args:
`raw_args`: The operands passed to the function before they were converted to
SQLGlot expressions. (Not actively used in this implementation.)
`sql_glot_args`: The operands passed to the function after they were converted
to SQLGlot expressions. The first operand is expected to be a string.
Returns:
The SQLGlot expression matching the functionality of
`LPAD(string, length, padding)`. With the caveat that if length is 0,
it will return an empty string.
"""
col_glot, col_len_glot, required_len_glot, pad_string_glot, required_len = (
pad_helper(raw_args, sql_glot_args, "LPAD")
)
if required_len == 0:
return sqlglot_expressions.convert("")

answer = convert_iff_case(
None,
[
Expand Down Expand Up @@ -819,10 +852,6 @@ def convert_rpad(
If length is 0, return an empty string.
If length is negative, raise an error.
If length is positive, pad the string on the right to the specified length.
Expects sqlglot_args[0] to be the column to pad.
Expects sqlglot_args[1] and sqlglot_args[2] to be literals.
Expects sqlglot_args[1] to be the returned length of the padded string.
Expects sqlglot_args[2] to be the string to pad with.
Args:
`raw_args`: The operands passed to the function before they were converted to
Expand All @@ -835,38 +864,12 @@ def convert_rpad(
`RPAD(string, length, padding)`. With the caveat that if length is 0,
it will return an empty string.
"""
assert len(sql_glot_args) == 3

if (
not isinstance(sql_glot_args[1], sqlglot_expressions.Literal)
or sql_glot_args[1].is_string
):
raise ValueError("RPAD function requires the length argument to be an integer.")

if (
not isinstance(sql_glot_args[2], sqlglot_expressions.Literal)
or not sql_glot_args[2].is_string
):
raise ValueError("RPAD function requires the padding argument to be a string")
if len(str(sql_glot_args[2].this)) != 1:
raise ValueError(
"RPAD function requires the padding argument to be of length 1."
)

try:
required_len = int(sql_glot_args[1].this)
except ValueError:
raise ValueError("RPAD function requires the length argument to be an integer.")
if required_len < 0:
raise ValueError("RPAD function requires a non-negative length")
col_glot, _, required_len_glot, pad_string_glot, required_len = pad_helper(
raw_args, sql_glot_args, "RPAD"
)
if required_len == 0:
return sqlglot_expressions.convert("")

col_glot = sql_glot_args[0]
required_len_glot = sqlglot_expressions.convert(required_len)
pad_string_glot = sqlglot_expressions.convert(
str(sql_glot_args[2].this) * required_len
)
answer = sqlglot_expressions.Substring(
this=convert_concat(None, [col_glot, pad_string_glot]),
start=sqlglot_expressions.convert(1),
Expand Down
20 changes: 20 additions & 0 deletions tests/bad_pydough_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@ def bad_lpad_6():
return Customers(padded_name=LPAD(name, datetime.datetime.now(), "*"))


def bad_lpad_7():
# Non-literal length
return Customers(padded_name=LPAD(name, LENGTH(phone), "*"))


def bad_lpad_8():
# Non-literal padding string
return Customers(padded_name=LPAD(name, 20, LENGTH(phone)))


def bad_rpad_1():
# String length argument
return Customers(padded_name=RPAD(name, "20", "*"))
Expand Down Expand Up @@ -138,6 +148,16 @@ def bad_rpad_6():
return Customers(padded_name=RPAD(name, datetime.datetime.now(), "*"))


def bad_rpad_7():
# Non-literal length
return Customers(padded_name=RPAD(name, LENGTH(phone), "*"))


def bad_rpad_8():
# Non-literal padding string
return Customers(padded_name=RPAD(name, 20, LENGTH(phone)))


def bad_floor():
# Using `math.floor` (calls __floor__)
return Customer(age=math.floor(order.total_price))
Expand Down
Loading

0 comments on commit b7b9b90

Please sign in to comment.