diff --git a/backends-clickhouse/src/test/scala/org/apache/gluten/execution/GlutenClickHouseTPCHSaltNullParquetSuite.scala b/backends-clickhouse/src/test/scala/org/apache/gluten/execution/GlutenClickHouseTPCHSaltNullParquetSuite.scala index 748bd5a7f7f6..de5ad36adbc3 100644 --- a/backends-clickhouse/src/test/scala/org/apache/gluten/execution/GlutenClickHouseTPCHSaltNullParquetSuite.scala +++ b/backends-clickhouse/src/test/scala/org/apache/gluten/execution/GlutenClickHouseTPCHSaltNullParquetSuite.scala @@ -2563,5 +2563,18 @@ class GlutenClickHouseTPCHSaltNullParquetSuite extends GlutenClickHouseTPCHAbstr compareResultsAgainstVanillaSpark(select_sql, true, { _ => }) spark.sql("drop table test_tbl_5896") } + + test("test left with len -1") { + val tbl_create_sql = + "create table test_left(col string) using parquet" + val tbl_insert_sql = + "insert into test_left values('test1'), ('test2')" + spark.sql(tbl_create_sql) + spark.sql(tbl_insert_sql) + compareResultsAgainstVanillaSpark("select left(col, -1) from test_left", true, { _ => }) + compareResultsAgainstVanillaSpark("select left(col, -2) from test_left", true, { _ => }) + compareResultsAgainstVanillaSpark("select substring(col, 0, -1) from test_left", true, { _ => }) + spark.sql("drop table test_left") + } } // scalastyle:on line.size.limit diff --git a/cpp-ch/local-engine/Parser/scalar_function_parser/substring.cpp b/cpp-ch/local-engine/Parser/scalar_function_parser/substring.cpp index 64c97da80581..550e77344ddf 100644 --- a/cpp-ch/local-engine/Parser/scalar_function_parser/substring.cpp +++ b/cpp-ch/local-engine/Parser/scalar_function_parser/substring.cpp @@ -59,7 +59,9 @@ class FunctionParserSubstring : public FunctionParser const auto * const_one_node = addColumnToActionsDAG(actions_dag, index_type, 1); const auto * equals_zero_node = toFunctionNode(actions_dag, "equals", {index_arg, const_zero_node}); const auto * if_node = toFunctionNode(actions_dag, "if", {equals_zero_node, const_one_node, index_arg}); - const auto * substring_func_node = toFunctionNode(actions_dag, "substringUTF8", {str_arg, if_node, length_arg}); + const auto * less_zero_node = toFunctionNode(actions_dag, "less", {length_arg, const_zero_node}); + const auto * if_len_node = toFunctionNode(actions_dag, "if", {less_zero_node, const_zero_node, length_arg}); + const auto * substring_func_node = toFunctionNode(actions_dag, "substringUTF8", {str_arg, if_node, if_len_node}); return convertNodeTypeIfNeeded(substrait_func, substring_func_node, actions_dag); } protected: