Skip to content

Commit

Permalink
[CALCITE-5989] Type inference for RPAD and LPAD functions (BIGQUERY) …
Browse files Browse the repository at this point in the history
…is incorrect

Signed-off-by: Mihai Budiu <[email protected]>
  • Loading branch information
mihaibudiu committed Sep 29, 2023
1 parent be21466 commit 0dd60c8
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -275,12 +275,18 @@ private static SqlCall transformConvert(SqlValidator validator, SqlCall call) {
public static final SqlFunction LENGTH =
SqlStdOperatorTable.CHAR_LENGTH.withName("LENGTH");

// Helper function for deriving types for the *PAD functions
private static RelDataType deriveTypePad(SqlOperatorBinding binding, RelDataType type) {
SqlTypeName result = SqlTypeUtil.isBinary(type) ? SqlTypeName.VARBINARY : SqlTypeName.VARCHAR;
return binding.getTypeFactory().createSqlType(result);
}

/** The "LPAD(original_value, return_length[, pattern])" function. */
@LibraryOperator(libraries = {BIG_QUERY, ORACLE})
public static final SqlFunction LPAD =
SqlBasicFunction.create(
"LPAD",
ReturnTypes.ARG0_NULLABLE_VARYING,
ReturnTypes.ARG0.andThen(SqlLibraryOperators::deriveTypePad),
OperandTypes.STRING_NUMERIC_OPTIONAL_STRING,
SqlFunctionCategory.STRING);

Expand All @@ -289,7 +295,7 @@ private static SqlCall transformConvert(SqlValidator validator, SqlCall call) {
public static final SqlFunction RPAD =
SqlBasicFunction.create(
"RPAD",
ReturnTypes.ARG0_NULLABLE_VARYING,
ReturnTypes.ARG0.andThen(SqlLibraryOperators::deriveTypePad),
OperandTypes.STRING_NUMERIC_OPTIONAL_STRING,
SqlFunctionCategory.STRING);

Expand Down
40 changes: 40 additions & 0 deletions core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,46 @@ private static boolean skipItem(RexNode expr) {
.check();
}

/**
* Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5989">[CALCITE-5989]
* Type inference for RPAD and LPAD functions (BIGQUERY) is incorrect</a>. */
@Test void testRpad() {
HepProgramBuilder builder = new HepProgramBuilder();
builder.addRuleClass(ReduceExpressionsRule.class);
HepPlanner hepPlanner = new HepPlanner(builder.build());
hepPlanner.addRule(CoreRules.PROJECT_REDUCE_EXPRESSIONS);

final String sql = "select RPAD('abc', 8, 'A')";
fixture()
.withFactory(
t -> t.withOperatorTable(opTab ->
SqlLibraryOperatorTableFactory.INSTANCE.getOperatorTable(
SqlLibrary.BIG_QUERY))) // needed for RPAD function
.sql(sql)
.withPlanner(hepPlanner)
.check();
}

/**
* Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5989">[CALCITE-5989]
* Type inference for RPAD and LPAD functions (BIGQUERY) is incorrect</a>. */
@Test void testLpad() {
HepProgramBuilder builder = new HepProgramBuilder();
builder.addRuleClass(ReduceExpressionsRule.class);
HepPlanner hepPlanner = new HepPlanner(builder.build());
hepPlanner.addRule(CoreRules.PROJECT_REDUCE_EXPRESSIONS);

final String sql = "select LPAD('abc', 8, 'A')";
fixture()
.withFactory(
t -> t.withOperatorTable(opTab ->
SqlLibraryOperatorTableFactory.INSTANCE.getOperatorTable(
SqlLibrary.BIG_QUERY))) // needed for LPAD function
.sql(sql)
.withPlanner(hepPlanner)
.check();
}

/**
* Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5971">[CALCITE-5971]
* Add the RelRule to rewrite the bernoulli sample as Filter</a>. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6221,6 +6221,23 @@ EnumerableLimitSort(sort0=[$0], dir0=[ASC], offset=[5], fetch=[10])
EnumerableTableScan(table=[[CATALOG, SALES, EMP]])
EnumerableProject(MGR=[$3])
EnumerableTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
</TestCase>
<TestCase name="testLpad">
<Resource name="sql">
<![CDATA[select LPAD('abc', 8, 'A')]]>
</Resource>
<Resource name="planBefore">
<![CDATA[
LogicalProject(EXPR$0=[LPAD('abc', 8, 'A')])
LogicalValues(tuples=[[{ 0 }]])
]]>
</Resource>
<Resource name="planAfter">
<![CDATA[
LogicalProject(EXPR$0=['AAAAAabc':VARCHAR])
LogicalValues(tuples=[[{ 0 }]])
]]>
</Resource>
</TestCase>
Expand Down Expand Up @@ -12876,6 +12893,23 @@ LogicalProject(EXPR$0=[1])
LogicalFilter(condition=[=($1, 'Charlie')])
LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
</TestCase>
<TestCase name="testRpad">
<Resource name="sql">
<![CDATA[select RPAD('abc', 8, 'A')]]>
</Resource>
<Resource name="planBefore">
<![CDATA[
LogicalProject(EXPR$0=[RPAD('abc', 8, 'A')])
LogicalValues(tuples=[[{ 0 }]])
]]>
</Resource>
<Resource name="planAfter">
<![CDATA[
LogicalProject(EXPR$0=['abcAAAAA':VARCHAR])
LogicalValues(tuples=[[{ 0 }]])
]]>
</Resource>
</TestCase>
Expand Down
32 changes: 16 additions & 16 deletions testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8225,20 +8225,20 @@ private void testCurrentDateFunc(Pair<String, Hook.Closeable> pair) {
@Test void testLpadFunction() {
final SqlOperatorFixture f = fixture().withLibrary(SqlLibrary.BIG_QUERY);
f.setFor(SqlLibraryOperators.LPAD);
f.check("select lpad('12345', 8, 'a')", "VARCHAR(5) NOT NULL", "aaa12345");
f.checkString("lpad('12345', 8)", " 12345", "VARCHAR(5) NOT NULL");
f.checkString("lpad('12345', 8, 'ab')", "aba12345", "VARCHAR(5) NOT NULL");
f.checkString("lpad('12345', 3, 'a')", "123", "VARCHAR(5) NOT NULL");
f.check("select lpad('12345', 8, 'a')", "VARCHAR NOT NULL", "aaa12345");
f.checkString("lpad('12345', 8)", " 12345", "VARCHAR NOT NULL");
f.checkString("lpad('12345', 8, 'ab')", "aba12345", "VARCHAR NOT NULL");
f.checkString("lpad('12345', 3, 'a')", "123", "VARCHAR NOT NULL");
f.checkFails("lpad('12345', -3, 'a')",
"Second argument for LPAD/RPAD must not be negative", true);
f.checkFails("lpad('12345', -3)",
"Second argument for LPAD/RPAD must not be negative", true);
f.checkFails("lpad('12345', 3, '')",
"Third argument (pad pattern) for LPAD/RPAD must not be empty", true);
f.checkString("lpad(x'aa', 4, x'bb')", "bbbbbbaa", "VARBINARY(1) NOT NULL");
f.checkString("lpad(x'aa', 4)", "202020aa", "VARBINARY(1) NOT NULL");
f.checkString("lpad(x'aaaaaa', 2)", "aaaa", "VARBINARY(3) NOT NULL");
f.checkString("lpad(x'aaaaaa', 2, x'bb')", "aaaa", "VARBINARY(3) NOT NULL");
f.checkString("lpad(x'aa', 4, x'bb')", "bbbbbbaa", "VARBINARY NOT NULL");
f.checkString("lpad(x'aa', 4)", "202020aa", "VARBINARY NOT NULL");
f.checkString("lpad(x'aaaaaa', 2)", "aaaa", "VARBINARY NOT NULL");
f.checkString("lpad(x'aaaaaa', 2, x'bb')", "aaaa", "VARBINARY NOT NULL");
f.checkFails("lpad(x'aa', -3, x'bb')",
"Second argument for LPAD/RPAD must not be negative", true);
f.checkFails("lpad(x'aa', -3)",
Expand All @@ -8250,21 +8250,21 @@ private void testCurrentDateFunc(Pair<String, Hook.Closeable> pair) {
@Test void testRpadFunction() {
final SqlOperatorFixture f = fixture().withLibrary(SqlLibrary.BIG_QUERY);
f.setFor(SqlLibraryOperators.RPAD);
f.check("select rpad('12345', 8, 'a')", "VARCHAR(5) NOT NULL", "12345aaa");
f.checkString("rpad('12345', 8)", "12345 ", "VARCHAR(5) NOT NULL");
f.checkString("rpad('12345', 8, 'ab')", "12345aba", "VARCHAR(5) NOT NULL");
f.checkString("rpad('12345', 3, 'a')", "123", "VARCHAR(5) NOT NULL");
f.check("select rpad('12345', 8, 'a')", "VARCHAR NOT NULL", "12345aaa");
f.checkString("rpad('12345', 8)", "12345 ", "VARCHAR NOT NULL");
f.checkString("rpad('12345', 8, 'ab')", "12345aba", "VARCHAR NOT NULL");
f.checkString("rpad('12345', 3, 'a')", "123", "VARCHAR NOT NULL");
f.checkFails("rpad('12345', -3, 'a')",
"Second argument for LPAD/RPAD must not be negative", true);
f.checkFails("rpad('12345', -3)",
"Second argument for LPAD/RPAD must not be negative", true);
f.checkFails("rpad('12345', 3, '')",
"Third argument (pad pattern) for LPAD/RPAD must not be empty", true);

f.checkString("rpad(x'aa', 4, x'bb')", "aabbbbbb", "VARBINARY(1) NOT NULL");
f.checkString("rpad(x'aa', 4)", "aa202020", "VARBINARY(1) NOT NULL");
f.checkString("rpad(x'aaaaaa', 2)", "aaaa", "VARBINARY(3) NOT NULL");
f.checkString("rpad(x'aaaaaa', 2, x'bb')", "aaaa", "VARBINARY(3) NOT NULL");
f.checkString("rpad(x'aa', 4, x'bb')", "aabbbbbb", "VARBINARY NOT NULL");
f.checkString("rpad(x'aa', 4)", "aa202020", "VARBINARY NOT NULL");
f.checkString("rpad(x'aaaaaa', 2)", "aaaa", "VARBINARY NOT NULL");
f.checkString("rpad(x'aaaaaa', 2, x'bb')", "aaaa", "VARBINARY NOT NULL");
f.checkFails("rpad(x'aa', -3, x'bb')",
"Second argument for LPAD/RPAD must not be negative", true);
f.checkFails("rpad(x'aa', -3)",
Expand Down

0 comments on commit 0dd60c8

Please sign in to comment.