diff --git a/cider/exec/plan/parser/SubstraitToAnalyzerExpr.cpp b/cider/exec/plan/parser/SubstraitToAnalyzerExpr.cpp index 600551574..f44aafd12 100644 --- a/cider/exec/plan/parser/SubstraitToAnalyzerExpr.cpp +++ b/cider/exec/plan/parser/SubstraitToAnalyzerExpr.cpp @@ -36,8 +36,8 @@ bool getExprUpdatable(std::unordered_map, bool> return map.find(expr) == map.end() || !map.find(expr)->second; } -bool isStringFunction(std::string function_name) { - std::unordered_set supportedStrFunctionSet{"substring"}; +bool isStringFunction(const std::string& function_name) { + std::unordered_set supportedStrFunctionSet{"substring", "lower", "upper"}; return supportedStrFunctionSet.find(function_name) != supportedStrFunctionSet.end(); } @@ -799,10 +799,10 @@ std::shared_ptr Substrait2AnalyzerExprConverter::buildStrExpr( function_name.begin(), function_name.end(), function_name.begin(), ::toupper); auto string_op_kind = name_to_string_op_kind(function_name); switch (string_op_kind) { - // case SqlStringOpKind::LOWER: - // return makeExpr(args); - // case SqlStringOpKind::UPPER: - // return makeExpr(args); + case SqlStringOpKind::LOWER: + return makeExpr(args); + case SqlStringOpKind::UPPER: + return makeExpr(args); // case SqlStringOpKind::INITCAP: // return makeExpr(args); // case SqlStringOpKind::REVERSE: diff --git a/cider/tests/functionality/CiderStringTest.cpp b/cider/tests/functionality/CiderStringTest.cpp index cd62bfe27..17c5463ef 100644 --- a/cider/tests/functionality/CiderStringTest.cpp +++ b/cider/tests/functionality/CiderStringTest.cpp @@ -82,7 +82,7 @@ class CiderStringTestArrow : public CiderTestBase { QueryArrowDataGenerator::generateBatchByTypes( schema_, array_, - 30, + 50, {"col_1", "col_2"}, {CREATE_SUBSTRAIT_TYPE(I32), CREATE_SUBSTRAIT_TYPE(Varchar)}, {0, 0}, @@ -120,7 +120,7 @@ class CiderStringNullableTestArrow : public CiderTestBase { QueryArrowDataGenerator::generateBatchByTypes( schema_, array_, - 30, + 50, {"col_1", "col_2"}, {CREATE_SUBSTRAIT_TYPE(I32), CREATE_SUBSTRAIT_TYPE(Varchar)}, {2, 2}, @@ -350,7 +350,6 @@ TEST_F(CiderStringTestArrow, ArrowSubstringNestTest) { assertQueryArrow("SELECT * FROM test WHERE SUBSTRING(col_2, 1, 3) = 'aaa'"); assertQueryArrow("SELECT * FROM test WHERE SUBSTRING(col_2, 1, 3) <> 'bbb'"); assertQueryArrow("SELECT * FROM test WHERE SUBSTRING(col_2, 1, 3) > 'aaa'"); - assertQueryArrow("SELECT SUBSTRING(SUBSTRING(col_2, 1, 8), 1, 4) FROM test "); } @@ -358,10 +357,81 @@ TEST_F(CiderStringRandomTestArrow, ArrowSubstringNestTest) { assertQueryArrow("SELECT * FROM test WHERE SUBSTRING(col_2, 1, 3) = 'aaa'"); assertQueryArrow("SELECT * FROM test WHERE SUBSTRING(col_2, 1, 3) <> 'bbb'"); assertQueryArrow("SELECT * FROM test WHERE SUBSTRING(col_2, 1, 3) > 'aaa'"); - assertQueryArrow("SELECT SUBSTRING(SUBSTRING(col_2, 1, 8), 1, 4) FROM test "); } +TEST_F(CiderStringNullableTestArrow, ArrowBasicStringTest) { + assertQueryArrow("SELECT col_2 FROM test "); + assertQueryArrow("SELECT col_1, col_2 FROM test "); + assertQueryArrow("SELECT * FROM test "); + assertQueryArrow("SELECT col_2 FROM test where col_2 = 'aaaa'"); + assertQueryArrow("SELECT col_2 FROM test where col_2 = '0000000000'"); + assertQueryArrow("SELECT col_2 FROM test where col_2 <> '0000000000'"); + assertQueryArrow("SELECT col_1 FROM test where col_2 <> '1111111111'"); + assertQueryArrow("SELECT col_1, col_2 FROM test where col_2 <> '2222222222'"); + assertQueryArrow("SELECT col_2 FROM test where col_2 <> 'aaaaaaaaaaa'"); + assertQueryArrow("SELECT * FROM test where col_2 <> 'abcdefghijklmn'"); + assertQueryArrow("SELECT col_2 FROM test where col_2 IS NOT NULL"); + assertQueryArrow("SELECT col_2 FROM test where col_2 < 'uuu'"); +} + +TEST_F(CiderStringNullableTestArrow, ArrowBasicStringLikeTest) { + assertQueryArrow("SELECT col_2 FROM test where col_2 LIKE '%1111'"); + assertQueryArrow("SELECT col_2 FROM test where col_2 LIKE '1111%'"); + assertQueryArrow("SELECT col_2 FROM test where col_2 LIKE '%1111%'"); + assertQueryArrow("SELECT col_2 FROM test where col_2 LIKE '%1234%'"); + assertQueryArrow("SELECT col_2 FROM test where col_2 LIKE '22%22'"); + assertQueryArrow("SELECT col_2 FROM test where col_2 LIKE '_33%'"); + assertQueryArrow("SELECT col_2 FROM test where col_2 LIKE '44_%'"); + + assertQueryArrow( + "SELECT col_2 FROM test where col_2 LIKE '5555%' OR col_2 LIKE '%6666'"); + assertQueryArrow( + "SELECT col_2 FROM test where col_2 LIKE '7777%' AND col_2 LIKE '%8888'"); + assertQueryArrow("SELECT col_2 FROM test where col_2 LIKE '%1111'", + "like_wo_cast.json"); + assertQueryArrow("SELECT col_2 FROM test where col_2 NOT LIKE '1111%'"); + assertQueryArrow("SELECT col_2 FROM test where col_2 NOT LIKE '44_4444444'"); + assertQueryArrow( + "SELECT col_2 FROM test where col_2 NOT LIKE '44_4%' and col_2 NOT LIKE '%111%'"); +} + +TEST_F(CiderStringNullableTestArrow, ArrowSubstringTest) { + // variable source string + assertQueryArrow("SELECT SUBSTRING(col_2, 1, 10) FROM test "); + assertQueryArrow("SELECT SUBSTRING(col_2, 1, 5) FROM test "); + + // out of range + assertQueryArrow("SELECT SUBSTRING(col_2, 4, 8) FROM test "); + assertQueryArrow("SELECT SUBSTRING(col_2, 0, 12) FROM test "); + assertQueryArrow("SELECT SUBSTRING(col_2, 12, 0) FROM test "); + assertQueryArrow("SELECT SUBSTRING(col_2, 12, 2) FROM test "); + + // from for + assertQueryArrow("SELECT SUBSTRING(col_2 from 2 for 8) FROM test "); + + // zero length + assertQueryArrow("SELECT SUBSTRING(col_2, 4, 0) FROM test "); + + // negative wrap + assertQueryArrow("SELECT SUBSTRING(col_2, -4, 2) FROM test "); +} + +TEST_F(CiderStringTestArrow, ArrowBasicStringTest) { + assertQueryArrow("SELECT col_2 FROM test "); + assertQueryArrow("SELECT col_1, col_2 FROM test "); + assertQueryArrow("SELECT * FROM test "); + assertQueryArrow("SELECT col_2 FROM test where col_2 = 'aaaa'"); + assertQueryArrow("SELECT col_2 FROM test where col_2 = '0000000000'"); + assertQueryArrow("SELECT col_2 FROM test where col_2 <> '0000000000'"); + assertQueryArrow("SELECT col_1 FROM test where col_2 <> '1111111111'"); + assertQueryArrow("SELECT col_1, col_2 FROM test where col_2 <> '2222222222'"); + assertQueryArrow("SELECT * FROM test where col_2 <> 'aaaaaaaaaaa'"); + assertQueryArrow("SELECT * FROM test where col_2 <> 'abcdefghijklmn'"); + assertQueryArrow("SELECT col_2 FROM test where col_2 IS NOT NULL"); + assertQueryArrow("SELECT col_2 FROM test where col_2 < 'uuu'"); +} + TEST_F(CiderStringNullableTestArrow, ArrowSubstringNestTest) { assertQueryArrow("SELECT * FROM test WHERE SUBSTRING(col_2, 1, 3) = 'aaa'"); assertQueryArrow("SELECT * FROM test WHERE SUBSTRING(col_2, 1, 3) <> 'bbb'"); @@ -370,6 +440,54 @@ TEST_F(CiderStringNullableTestArrow, ArrowSubstringNestTest) { assertQueryArrow("SELECT SUBSTRING(SUBSTRING(col_2, 1, 8), 1, 4) FROM test "); } +TEST_F(CiderStringTestArrow, ArrowCaseConvertionTest) { + // select column from table + assertQueryArrow("SELECT col_2, LOWER(col_2) FROM test;", "stringop_lower.json"); + assertQueryArrow("SELECT col_2, UPPER(col_2) FROM test;", "stringop_upper.json"); + + // select literal from table + assertQueryArrow("SELECT LOWER('aAbBcCdD12') FROM test;", + "stringop_lower_literal.json"); + assertQueryArrow("SELECT UPPER('aAbBcCdD12') FROM test;", + "stringop_upper_literal.json"); + + // string op on filter clause + assertQueryArrow("SELECT col_2 FROM test WHERE LOWER(col_2) = 'aaaaaaaaaa'", + "stringop_lower_condition.json"); + assertQueryArrow("SELECT col_2 FROM test WHERE UPPER(col_2) = 'AAAAAAAAAA'", + "stringop_upper_condition.json"); + + /// NOTE: (YBRua) Skipped for now because we dont expect queries without FROM clauses. + /// 1. Behaviors of Cider and DuckDb are different w.r.t. this query. + /// DuckDb produces only 1 row, while Cider produces input_row_num rows. + /// Because the compiled row_func IR always runs for input_row_num times + /// at runtime in current implementation of Cider. + /// 2. If no input table (no FROM clause) is given, the generated Substrait plan will + /// have a "virtualTable" (instead of a "namedTable") as a placeholder input. + /// + // select literal + // assertQueryArrow("SELECT LOWER('ABCDEFG');", "stringop_lower_constexpr_null.json"); + // assertQueryArrow("SELECT UPPER('abcdefg');", "stringop_upper_constexpr_null.json"); +} + +TEST_F(CiderStringNullableTestArrow, ArrowCaseConvertionTest) { + // select column from table + assertQueryArrow("SELECT col_2, LOWER(col_2) FROM test;", "stringop_lower_null.json"); + assertQueryArrow("SELECT col_2, UPPER(col_2) FROM test;", "stringop_upper_null.json"); + + // select literal from table + assertQueryArrow("SELECT LOWER('aAbBcCdD12') FROM test;", + "stringop_lower_literal_null.json"); + assertQueryArrow("SELECT UPPER('aAbBcCdD12') FROM test;", + "stringop_upper_literal_null.json"); + + // string op on filter clause + assertQueryArrow("SELECT col_2 FROM test WHERE LOWER(col_2) = 'aaaaaaaaaa'", + "stringop_lower_condition_null.json"); + assertQueryArrow("SELECT col_2 FROM test WHERE UPPER(col_2) = 'AAAAAAAAAA'", + "stringop_upper_condition_null.json"); +} + class CiderConstantStringTest : public CiderTestBase { public: CiderConstantStringTest() { diff --git a/cider/tests/substrait_plan_files/stringop_lower.json b/cider/tests/substrait_plan_files/stringop_lower.json new file mode 100644 index 000000000..3dad837f9 --- /dev/null +++ b/cider/tests/substrait_plan_files/stringop_lower.json @@ -0,0 +1,116 @@ +{ + "extensionUris": [ + { + "extensionUriAnchor": 1, + "uri": "/functions_string.yaml" + } + ], + "extensions": [ + { + "extensionFunction": { + "extensionUriReference": 1, + "functionAnchor": 0, + "name": "lower:opt_vchar" + } + } + ], + "relations": [ + { + "root": { + "input": { + "project": { + "common": { + "emit": { + "outputMapping": [ + 2, + 3 + ] + } + }, + "input": { + "read": { + "common": { + "direct": {} + }, + "baseSchema": { + "names": [ + "COL_1", + "COL_2" + ], + "struct": { + "types": [ + { + "i32": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + } + ], + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "namedTable": { + "names": [ + "TEST" + ] + } + } + }, + "expressions": [ + { + "selection": { + "directReference": { + "structField": { + "field": 1 + } + }, + "rootReference": {} + } + }, + { + "scalarFunction": { + "functionReference": 0, + "args": [], + "outputType": { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "arguments": [ + { + "value": { + "selection": { + "directReference": { + "structField": { + "field": 1 + } + }, + "rootReference": {} + } + } + } + ] + } + } + ] + } + }, + "names": [ + "COL_2", + "EXPR$1" + ] + } + } + ], + "expectedTypeUrls": [] +} \ No newline at end of file diff --git a/cider/tests/substrait_plan_files/stringop_lower_condition.json b/cider/tests/substrait_plan_files/stringop_lower_condition.json new file mode 100644 index 000000000..99f48b4e3 --- /dev/null +++ b/cider/tests/substrait_plan_files/stringop_lower_condition.json @@ -0,0 +1,160 @@ +{ + "extensionUris": [ + { + "extensionUriAnchor": 2, + "uri": "/functions_string.yaml" + }, + { + "extensionUriAnchor": 1, + "uri": "/functions_comparison.yaml" + } + ], + "extensions": [ + { + "extensionFunction": { + "extensionUriReference": 1, + "functionAnchor": 0, + "name": "equal:any_any" + } + }, + { + "extensionFunction": { + "extensionUriReference": 2, + "functionAnchor": 1, + "name": "lower:opt_vchar" + } + } + ], + "relations": [ + { + "root": { + "input": { + "project": { + "common": { + "emit": { + "outputMapping": [ + 2 + ] + } + }, + "input": { + "filter": { + "common": { + "direct": {} + }, + "input": { + "read": { + "common": { + "direct": {} + }, + "baseSchema": { + "names": [ + "COL_1", + "COL_2" + ], + "struct": { + "types": [ + { + "i32": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + } + ], + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "namedTable": { + "names": [ + "TEST" + ] + } + } + }, + "condition": { + "scalarFunction": { + "functionReference": 0, + "args": [], + "outputType": { + "bool": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "arguments": [ + { + "value": { + "scalarFunction": { + "functionReference": 1, + "args": [], + "outputType": { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "arguments": [ + { + "value": { + "selection": { + "directReference": { + "structField": { + "field": 1 + } + }, + "rootReference": {} + } + } + } + ] + } + } + }, + { + "value": { + "literal": { + "varChar": { + "value": "aaaaaaaaaa", + "length": 10 + }, + "nullable": false, + "typeVariationReference": 0 + } + } + } + ] + } + } + } + }, + "expressions": [ + { + "selection": { + "directReference": { + "structField": { + "field": 1 + } + }, + "rootReference": {} + } + } + ] + } + }, + "names": [ + "COL_2" + ] + } + } + ], + "expectedTypeUrls": [] +} \ No newline at end of file diff --git a/cider/tests/substrait_plan_files/stringop_lower_condition_null.json b/cider/tests/substrait_plan_files/stringop_lower_condition_null.json new file mode 100644 index 000000000..4ef586003 --- /dev/null +++ b/cider/tests/substrait_plan_files/stringop_lower_condition_null.json @@ -0,0 +1,160 @@ +{ + "extensionUris": [ + { + "extensionUriAnchor": 2, + "uri": "/functions_string.yaml" + }, + { + "extensionUriAnchor": 1, + "uri": "/functions_comparison.yaml" + } + ], + "extensions": [ + { + "extensionFunction": { + "extensionUriReference": 1, + "functionAnchor": 0, + "name": "equal:any_any" + } + }, + { + "extensionFunction": { + "extensionUriReference": 2, + "functionAnchor": 1, + "name": "lower:opt_vchar" + } + } + ], + "relations": [ + { + "root": { + "input": { + "project": { + "common": { + "emit": { + "outputMapping": [ + 2 + ] + } + }, + "input": { + "filter": { + "common": { + "direct": {} + }, + "input": { + "read": { + "common": { + "direct": {} + }, + "baseSchema": { + "names": [ + "COL_1", + "COL_2" + ], + "struct": { + "types": [ + { + "i32": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + }, + { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + } + ], + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "namedTable": { + "names": [ + "TEST" + ] + } + } + }, + "condition": { + "scalarFunction": { + "functionReference": 0, + "args": [], + "outputType": { + "bool": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + }, + "arguments": [ + { + "value": { + "scalarFunction": { + "functionReference": 1, + "args": [], + "outputType": { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + }, + "arguments": [ + { + "value": { + "selection": { + "directReference": { + "structField": { + "field": 1 + } + }, + "rootReference": {} + } + } + } + ] + } + } + }, + { + "value": { + "literal": { + "varChar": { + "value": "aaaaaaaaaa", + "length": 10 + }, + "nullable": false, + "typeVariationReference": 0 + } + } + } + ] + } + } + } + }, + "expressions": [ + { + "selection": { + "directReference": { + "structField": { + "field": 1 + } + }, + "rootReference": {} + } + } + ] + } + }, + "names": [ + "COL_2" + ] + } + } + ], + "expectedTypeUrls": [] +} \ No newline at end of file diff --git a/cider/tests/substrait_plan_files/stringop_lower_constexpr_null.json b/cider/tests/substrait_plan_files/stringop_lower_constexpr_null.json new file mode 100644 index 000000000..0d4e6a45d --- /dev/null +++ b/cider/tests/substrait_plan_files/stringop_lower_constexpr_null.json @@ -0,0 +1,101 @@ +{ + "extensionUris": [ + { + "extensionUriAnchor": 1, + "uri": "/functions_string.yaml" + } + ], + "extensions": [ + { + "extensionFunction": { + "extensionUriReference": 1, + "functionAnchor": 0, + "name": "lower:opt_fchar" + } + } + ], + "relations": [ + { + "root": { + "input": { + "project": { + "common": { + "emit": { + "outputMapping": [ + 1 + ] + } + }, + "input": { + "read": { + "common": { + "direct": {} + }, + "baseSchema": { + "names": [ + "ZERO" + ], + "struct": { + "types": [ + { + "i32": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + } + ], + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "virtualTable": { + "values": [ + { + "fields": [ + { + "i32": 0, + "nullable": false, + "typeVariationReference": 0 + } + ] + } + ] + } + } + }, + "expressions": [ + { + "scalarFunction": { + "functionReference": 0, + "args": [], + "outputType": { + "fixedChar": { + "length": 7, + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "arguments": [ + { + "value": { + "literal": { + "fixedChar": "ABCDEFG", + "nullable": false, + "typeVariationReference": 0 + } + } + } + ] + } + } + ] + } + }, + "names": [ + "EXPR$0" + ] + } + } + ], + "expectedTypeUrls": [] +} \ No newline at end of file diff --git a/cider/tests/substrait_plan_files/stringop_lower_literal.json b/cider/tests/substrait_plan_files/stringop_lower_literal.json new file mode 100644 index 000000000..eabcf3c01 --- /dev/null +++ b/cider/tests/substrait_plan_files/stringop_lower_literal.json @@ -0,0 +1,101 @@ +{ + "extensionUris": [ + { + "extensionUriAnchor": 1, + "uri": "/functions_string.yaml" + } + ], + "extensions": [ + { + "extensionFunction": { + "extensionUriReference": 1, + "functionAnchor": 0, + "name": "lower:opt_fchar" + } + } + ], + "relations": [ + { + "root": { + "input": { + "project": { + "common": { + "emit": { + "outputMapping": [ + 2 + ] + } + }, + "input": { + "read": { + "common": { + "direct": {} + }, + "baseSchema": { + "names": [ + "COL_1", + "COL_2" + ], + "struct": { + "types": [ + { + "i32": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + } + ], + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "namedTable": { + "names": [ + "TEST" + ] + } + } + }, + "expressions": [ + { + "scalarFunction": { + "functionReference": 0, + "args": [], + "outputType": { + "fixedChar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "arguments": [ + { + "value": { + "literal": { + "fixedChar": "aAbBcCdD12", + "nullable": false, + "typeVariationReference": 0 + } + } + } + ] + } + } + ] + } + }, + "names": [ + "EXPR$0" + ] + } + } + ], + "expectedTypeUrls": [] +} \ No newline at end of file diff --git a/cider/tests/substrait_plan_files/stringop_lower_literal_null.json b/cider/tests/substrait_plan_files/stringop_lower_literal_null.json new file mode 100644 index 000000000..622386d61 --- /dev/null +++ b/cider/tests/substrait_plan_files/stringop_lower_literal_null.json @@ -0,0 +1,101 @@ +{ + "extensionUris": [ + { + "extensionUriAnchor": 1, + "uri": "/functions_string.yaml" + } + ], + "extensions": [ + { + "extensionFunction": { + "extensionUriReference": 1, + "functionAnchor": 0, + "name": "lower:opt_fchar" + } + } + ], + "relations": [ + { + "root": { + "input": { + "project": { + "common": { + "emit": { + "outputMapping": [ + 2 + ] + } + }, + "input": { + "read": { + "common": { + "direct": {} + }, + "baseSchema": { + "names": [ + "COL_1", + "COL_2" + ], + "struct": { + "types": [ + { + "i32": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + }, + { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + } + ], + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "namedTable": { + "names": [ + "TEST" + ] + } + } + }, + "expressions": [ + { + "scalarFunction": { + "functionReference": 0, + "args": [], + "outputType": { + "fixedChar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "arguments": [ + { + "value": { + "literal": { + "fixedChar": "aAbBcCdD12", + "nullable": false, + "typeVariationReference": 0 + } + } + } + ] + } + } + ] + } + }, + "names": [ + "EXPR$0" + ] + } + } + ], + "expectedTypeUrls": [] +} \ No newline at end of file diff --git a/cider/tests/substrait_plan_files/stringop_lower_null.json b/cider/tests/substrait_plan_files/stringop_lower_null.json new file mode 100644 index 000000000..10287c03b --- /dev/null +++ b/cider/tests/substrait_plan_files/stringop_lower_null.json @@ -0,0 +1,116 @@ +{ + "extensionUris": [ + { + "extensionUriAnchor": 1, + "uri": "/functions_string.yaml" + } + ], + "extensions": [ + { + "extensionFunction": { + "extensionUriReference": 1, + "functionAnchor": 0, + "name": "lower:opt_vchar" + } + } + ], + "relations": [ + { + "root": { + "input": { + "project": { + "common": { + "emit": { + "outputMapping": [ + 2, + 3 + ] + } + }, + "input": { + "read": { + "common": { + "direct": {} + }, + "baseSchema": { + "names": [ + "COL_1", + "COL_2" + ], + "struct": { + "types": [ + { + "i32": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + }, + { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + } + ], + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "namedTable": { + "names": [ + "TEST" + ] + } + } + }, + "expressions": [ + { + "selection": { + "directReference": { + "structField": { + "field": 1 + } + }, + "rootReference": {} + } + }, + { + "scalarFunction": { + "functionReference": 0, + "args": [], + "outputType": { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + }, + "arguments": [ + { + "value": { + "selection": { + "directReference": { + "structField": { + "field": 1 + } + }, + "rootReference": {} + } + } + } + ] + } + } + ] + } + }, + "names": [ + "COL_2", + "EXPR$1" + ] + } + } + ], + "expectedTypeUrls": [] +} \ No newline at end of file diff --git a/cider/tests/substrait_plan_files/stringop_upper.json b/cider/tests/substrait_plan_files/stringop_upper.json new file mode 100644 index 000000000..b7c7558c9 --- /dev/null +++ b/cider/tests/substrait_plan_files/stringop_upper.json @@ -0,0 +1,116 @@ +{ + "extensionUris": [ + { + "extensionUriAnchor": 1, + "uri": "/functions_string.yaml" + } + ], + "extensions": [ + { + "extensionFunction": { + "extensionUriReference": 1, + "functionAnchor": 0, + "name": "upper:opt_vchar" + } + } + ], + "relations": [ + { + "root": { + "input": { + "project": { + "common": { + "emit": { + "outputMapping": [ + 2, + 3 + ] + } + }, + "input": { + "read": { + "common": { + "direct": {} + }, + "baseSchema": { + "names": [ + "COL_1", + "COL_2" + ], + "struct": { + "types": [ + { + "i32": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + } + ], + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "namedTable": { + "names": [ + "TEST" + ] + } + } + }, + "expressions": [ + { + "selection": { + "directReference": { + "structField": { + "field": 1 + } + }, + "rootReference": {} + } + }, + { + "scalarFunction": { + "functionReference": 0, + "args": [], + "outputType": { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "arguments": [ + { + "value": { + "selection": { + "directReference": { + "structField": { + "field": 1 + } + }, + "rootReference": {} + } + } + } + ] + } + } + ] + } + }, + "names": [ + "COL_2", + "EXPR$1" + ] + } + } + ], + "expectedTypeUrls": [] +} \ No newline at end of file diff --git a/cider/tests/substrait_plan_files/stringop_upper_condition.json b/cider/tests/substrait_plan_files/stringop_upper_condition.json new file mode 100644 index 000000000..e065067b7 --- /dev/null +++ b/cider/tests/substrait_plan_files/stringop_upper_condition.json @@ -0,0 +1,160 @@ +{ + "extensionUris": [ + { + "extensionUriAnchor": 2, + "uri": "/functions_string.yaml" + }, + { + "extensionUriAnchor": 1, + "uri": "/functions_comparison.yaml" + } + ], + "extensions": [ + { + "extensionFunction": { + "extensionUriReference": 1, + "functionAnchor": 0, + "name": "equal:any_any" + } + }, + { + "extensionFunction": { + "extensionUriReference": 2, + "functionAnchor": 1, + "name": "upper:opt_vchar" + } + } + ], + "relations": [ + { + "root": { + "input": { + "project": { + "common": { + "emit": { + "outputMapping": [ + 2 + ] + } + }, + "input": { + "filter": { + "common": { + "direct": {} + }, + "input": { + "read": { + "common": { + "direct": {} + }, + "baseSchema": { + "names": [ + "COL_1", + "COL_2" + ], + "struct": { + "types": [ + { + "i32": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + } + ], + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "namedTable": { + "names": [ + "TEST" + ] + } + } + }, + "condition": { + "scalarFunction": { + "functionReference": 0, + "args": [], + "outputType": { + "bool": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "arguments": [ + { + "value": { + "scalarFunction": { + "functionReference": 1, + "args": [], + "outputType": { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "arguments": [ + { + "value": { + "selection": { + "directReference": { + "structField": { + "field": 1 + } + }, + "rootReference": {} + } + } + } + ] + } + } + }, + { + "value": { + "literal": { + "varChar": { + "value": "AAAAAAAAAA", + "length": 10 + }, + "nullable": false, + "typeVariationReference": 0 + } + } + } + ] + } + } + } + }, + "expressions": [ + { + "selection": { + "directReference": { + "structField": { + "field": 1 + } + }, + "rootReference": {} + } + } + ] + } + }, + "names": [ + "COL_2" + ] + } + } + ], + "expectedTypeUrls": [] +} \ No newline at end of file diff --git a/cider/tests/substrait_plan_files/stringop_upper_condition_null.json b/cider/tests/substrait_plan_files/stringop_upper_condition_null.json new file mode 100644 index 000000000..18dc8de94 --- /dev/null +++ b/cider/tests/substrait_plan_files/stringop_upper_condition_null.json @@ -0,0 +1,160 @@ +{ + "extensionUris": [ + { + "extensionUriAnchor": 2, + "uri": "/functions_string.yaml" + }, + { + "extensionUriAnchor": 1, + "uri": "/functions_comparison.yaml" + } + ], + "extensions": [ + { + "extensionFunction": { + "extensionUriReference": 1, + "functionAnchor": 0, + "name": "equal:any_any" + } + }, + { + "extensionFunction": { + "extensionUriReference": 2, + "functionAnchor": 1, + "name": "upper:opt_vchar" + } + } + ], + "relations": [ + { + "root": { + "input": { + "project": { + "common": { + "emit": { + "outputMapping": [ + 2 + ] + } + }, + "input": { + "filter": { + "common": { + "direct": {} + }, + "input": { + "read": { + "common": { + "direct": {} + }, + "baseSchema": { + "names": [ + "COL_1", + "COL_2" + ], + "struct": { + "types": [ + { + "i32": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + }, + { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + } + ], + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "namedTable": { + "names": [ + "TEST" + ] + } + } + }, + "condition": { + "scalarFunction": { + "functionReference": 0, + "args": [], + "outputType": { + "bool": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + }, + "arguments": [ + { + "value": { + "scalarFunction": { + "functionReference": 1, + "args": [], + "outputType": { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + }, + "arguments": [ + { + "value": { + "selection": { + "directReference": { + "structField": { + "field": 1 + } + }, + "rootReference": {} + } + } + } + ] + } + } + }, + { + "value": { + "literal": { + "varChar": { + "value": "AAAAAAAAAA", + "length": 10 + }, + "nullable": false, + "typeVariationReference": 0 + } + } + } + ] + } + } + } + }, + "expressions": [ + { + "selection": { + "directReference": { + "structField": { + "field": 1 + } + }, + "rootReference": {} + } + } + ] + } + }, + "names": [ + "COL_2" + ] + } + } + ], + "expectedTypeUrls": [] +} \ No newline at end of file diff --git a/cider/tests/substrait_plan_files/stringop_upper_constexpr_null.json b/cider/tests/substrait_plan_files/stringop_upper_constexpr_null.json new file mode 100644 index 000000000..0ceec66ab --- /dev/null +++ b/cider/tests/substrait_plan_files/stringop_upper_constexpr_null.json @@ -0,0 +1,101 @@ +{ + "extensionUris": [ + { + "extensionUriAnchor": 1, + "uri": "/functions_string.yaml" + } + ], + "extensions": [ + { + "extensionFunction": { + "extensionUriReference": 1, + "functionAnchor": 0, + "name": "upper:opt_fchar" + } + } + ], + "relations": [ + { + "root": { + "input": { + "project": { + "common": { + "emit": { + "outputMapping": [ + 1 + ] + } + }, + "input": { + "read": { + "common": { + "direct": {} + }, + "baseSchema": { + "names": [ + "ZERO" + ], + "struct": { + "types": [ + { + "i32": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + } + ], + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "virtualTable": { + "values": [ + { + "fields": [ + { + "i32": 0, + "nullable": false, + "typeVariationReference": 0 + } + ] + } + ] + } + } + }, + "expressions": [ + { + "scalarFunction": { + "functionReference": 0, + "args": [], + "outputType": { + "fixedChar": { + "length": 7, + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "arguments": [ + { + "value": { + "literal": { + "fixedChar": "abcdefg", + "nullable": false, + "typeVariationReference": 0 + } + } + } + ] + } + } + ] + } + }, + "names": [ + "EXPR$0" + ] + } + } + ], + "expectedTypeUrls": [] +} \ No newline at end of file diff --git a/cider/tests/substrait_plan_files/stringop_upper_literal.json b/cider/tests/substrait_plan_files/stringop_upper_literal.json new file mode 100644 index 000000000..b9de82845 --- /dev/null +++ b/cider/tests/substrait_plan_files/stringop_upper_literal.json @@ -0,0 +1,101 @@ +{ + "extensionUris": [ + { + "extensionUriAnchor": 1, + "uri": "/functions_string.yaml" + } + ], + "extensions": [ + { + "extensionFunction": { + "extensionUriReference": 1, + "functionAnchor": 0, + "name": "upper:opt_fchar" + } + } + ], + "relations": [ + { + "root": { + "input": { + "project": { + "common": { + "emit": { + "outputMapping": [ + 2 + ] + } + }, + "input": { + "read": { + "common": { + "direct": {} + }, + "baseSchema": { + "names": [ + "COL_1", + "COL_2" + ], + "struct": { + "types": [ + { + "i32": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + } + ], + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "namedTable": { + "names": [ + "TEST" + ] + } + } + }, + "expressions": [ + { + "scalarFunction": { + "functionReference": 0, + "args": [], + "outputType": { + "fixedChar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "arguments": [ + { + "value": { + "literal": { + "fixedChar": "aAbBcCdD12", + "nullable": false, + "typeVariationReference": 0 + } + } + } + ] + } + } + ] + } + }, + "names": [ + "EXPR$0" + ] + } + } + ], + "expectedTypeUrls": [] +} \ No newline at end of file diff --git a/cider/tests/substrait_plan_files/stringop_upper_literal_null.json b/cider/tests/substrait_plan_files/stringop_upper_literal_null.json new file mode 100644 index 000000000..b32217992 --- /dev/null +++ b/cider/tests/substrait_plan_files/stringop_upper_literal_null.json @@ -0,0 +1,101 @@ +{ + "extensionUris": [ + { + "extensionUriAnchor": 1, + "uri": "/functions_string.yaml" + } + ], + "extensions": [ + { + "extensionFunction": { + "extensionUriReference": 1, + "functionAnchor": 0, + "name": "upper:opt_fchar" + } + } + ], + "relations": [ + { + "root": { + "input": { + "project": { + "common": { + "emit": { + "outputMapping": [ + 2 + ] + } + }, + "input": { + "read": { + "common": { + "direct": {} + }, + "baseSchema": { + "names": [ + "COL_1", + "COL_2" + ], + "struct": { + "types": [ + { + "i32": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + }, + { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + } + ], + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "namedTable": { + "names": [ + "TEST" + ] + } + } + }, + "expressions": [ + { + "scalarFunction": { + "functionReference": 0, + "args": [], + "outputType": { + "fixedChar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "arguments": [ + { + "value": { + "literal": { + "fixedChar": "aAbBcCdD12", + "nullable": false, + "typeVariationReference": 0 + } + } + } + ] + } + } + ] + } + }, + "names": [ + "EXPR$0" + ] + } + } + ], + "expectedTypeUrls": [] +} \ No newline at end of file diff --git a/cider/tests/substrait_plan_files/stringop_upper_null.json b/cider/tests/substrait_plan_files/stringop_upper_null.json new file mode 100644 index 000000000..cd1e876b8 --- /dev/null +++ b/cider/tests/substrait_plan_files/stringop_upper_null.json @@ -0,0 +1,116 @@ +{ + "extensionUris": [ + { + "extensionUriAnchor": 1, + "uri": "/functions_string.yaml" + } + ], + "extensions": [ + { + "extensionFunction": { + "extensionUriReference": 1, + "functionAnchor": 0, + "name": "upper:opt_vchar" + } + } + ], + "relations": [ + { + "root": { + "input": { + "project": { + "common": { + "emit": { + "outputMapping": [ + 2, + 3 + ] + } + }, + "input": { + "read": { + "common": { + "direct": {} + }, + "baseSchema": { + "names": [ + "COL_1", + "COL_2" + ], + "struct": { + "types": [ + { + "i32": { + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + }, + { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + } + ], + "typeVariationReference": 0, + "nullability": "NULLABILITY_REQUIRED" + } + }, + "namedTable": { + "names": [ + "TEST" + ] + } + } + }, + "expressions": [ + { + "selection": { + "directReference": { + "structField": { + "field": 1 + } + }, + "rootReference": {} + } + }, + { + "scalarFunction": { + "functionReference": 0, + "args": [], + "outputType": { + "varchar": { + "length": 10, + "typeVariationReference": 0, + "nullability": "NULLABILITY_NULLABLE" + } + }, + "arguments": [ + { + "value": { + "selection": { + "directReference": { + "structField": { + "field": 1 + } + }, + "rootReference": {} + } + } + } + ] + } + } + ] + } + }, + "names": [ + "COL_2", + "EXPR$1" + ] + } + } + ], + "expectedTypeUrls": [] +} \ No newline at end of file diff --git a/cider/type/plan/Analyzer.cpp b/cider/type/plan/Analyzer.cpp index 138a5d52c..3c5c0757a 100644 --- a/cider/type/plan/Analyzer.cpp +++ b/cider/type/plan/Analyzer.cpp @@ -392,6 +392,16 @@ void StringOper::check_operand_types( } } +std::shared_ptr LowerStringOper::deep_copy() const { + return makeExpr( + std::dynamic_pointer_cast(StringOper::deep_copy())); +} + +std::shared_ptr UpperStringOper::deep_copy() const { + return makeExpr( + std::dynamic_pointer_cast(StringOper::deep_copy())); +} + std::shared_ptr SubstringStringOper::deep_copy() const { return makeExpr( std::dynamic_pointer_cast(StringOper::deep_copy())); diff --git a/cider/type/plan/Analyzer.h b/cider/type/plan/Analyzer.h index 662f14088..ed8aea390 100644 --- a/cider/type/plan/Analyzer.h +++ b/cider/type/plan/Analyzer.h @@ -1784,6 +1784,36 @@ class LowerStringOper : public StringOper { std::vector getArgNames() const override { return {"operand"}; } }; +class UpperStringOper : public StringOper { + public: + UpperStringOper(const std::shared_ptr& operand) + : StringOper(SqlStringOpKind::UPPER, + {operand}, + getMinArgs(), + getExpectedTypeFamilies(), + getArgNames()) {} + + UpperStringOper(const std::vector>& operands) + : StringOper(SqlStringOpKind::UPPER, + operands, + getMinArgs(), + getExpectedTypeFamilies(), + getArgNames()) {} + + UpperStringOper(const std::shared_ptr& string_oper) + : StringOper(string_oper) {} + + std::shared_ptr deep_copy() const override; + + size_t getMinArgs() const override { return 1UL; } + + std::vector getExpectedTypeFamilies() const override { + return {OperandTypeFamily::STRING_FAMILY}; + } + + std::vector getArgNames() const override { return {"operand"}; } +}; + class SubstringStringOper : public StringOper { public: SubstringStringOper(const std::shared_ptr& operand,