From e15ea44b483acd455cccd8bd3e21995afc86b817 Mon Sep 17 00:00:00 2001 From: amory Date: Tue, 18 Mar 2025 14:13:36 +0800 Subject: [PATCH] [fix](named_struct) fix named_struct signature which deduce wrong for nested decimal precision (#48964) before this pr , we deduce the precision of the function named_struct containing the decimal type is same with the precision of the decimal type in select . In this case we can not make right named_struct for multiple decimal value like : ``` mysql> create table t01 (a decimal(6,3), d struct) properties ("replication_num"="1"); Query OK, 0 rows affected (0.14 sec) mysql> insert into t01 values (123.321, named_struct('col', 1, 'col1', 345.24)); Query OK, 1 row affected (0.26 sec) {'label':'label_a2dc895ee7834701_841639ad6d93923f', 'status':'VISIBLE', 'txnId':'9010'} mysql> select named_struct("col_11", a, "col_12", d) from t01; +--------------------------------------------------------+ | named_struct("col_11", a, "col_12", d) | +--------------------------------------------------------+ | {"col_11":123.321, "col_12":{"col":1, "col1":345.240}} | +--------------------------------------------------------+ 1 row in set (0.17 sec) mysql> insert into t01 values (123.321, named_struct('col', 1, 'col1', 12345.24)); Query OK, 1 row affected (0.17 sec) {'label':'label_77ed6047f6a6494d_810c26becfd0535c', 'status':'VISIBLE', 'txnId':'9011'} mysql> select named_struct("col_11", a, "col_12", d) from t01; ERROR 1105 (HY000): errCode = 2, detailMessage = (10.16.10.6)[E-255]Arithmetic overflow when converting value 12345.24 from type Decimal(7, 2) to type Decimal(6, 3) mysql> desc verbose select named_struct("col_11", a, "col_12", d) from t01; +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Explain String(Nereids Planner) | +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | PLAN FRAGMENT 0 | | OUTPUT EXPRS: | | named_struct("col_11", a, "col_12", d)[#2] | | PARTITION: RANDOM | | | | HAS_COLO_PLAN_NODE: false | | | | VRESULT SINK | | MYSQL_PROTOCAL | | | | 0:VOlapScanNode(50) | | TABLE: tt.t01(t01), PREAGGREGATION: ON | | partitions=1/1 (t01) | | tablets=10/10, tabletList=1741751856342,1741751856344,1741751856346 ... | | cardinality=2, avgRowSize=2955.0, numNodes=1 | | pushAggOp=NONE | | final projections: named_struct('col_11', a[#0], 'col_12', CAST(d[#1] AS struct)) | | final project output tuple id: 1 | | tuple ids: 0 | | | | Tuples: | | TupleDescriptor{id=0, tbl=t01} | | SlotDescriptor{id=0, col=a, colUniqueId=0, type=decimalv3(6,3), nullable=true, isAutoIncrement=false, subColPath=null} | | SlotDescriptor{id=1, col=d, colUniqueId=1, type=struct, nullable=true, isAutoIncrement=false, subColPath=null} | | | | TupleDescriptor{id=1, tbl=t01} | | SlotDescriptor{id=2, col=null, colUniqueId=null, type=struct>, nullable=false, isAutoIncrement=false, subColPath=null} | | | | | | ``` --- .../functions/scalar/CreateNamedStruct.java | 28 +++++++++++-------- .../test_struct_functions.out | 7 +++++ .../test_struct_functions_by_literal.out | 6 ++++ .../test_struct_functions.groovy | 8 ++++++ .../test_struct_functions_by_literal.groovy | 4 +++ 5 files changed, 41 insertions(+), 12 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CreateNamedStruct.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CreateNamedStruct.java index 0184fed65a6e52..7b1e8a31d673c5 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CreateNamedStruct.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CreateNamedStruct.java @@ -21,7 +21,8 @@ import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.functions.AlwaysNotNullable; -import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; +import org.apache.doris.nereids.trees.expressions.functions.ComputePrecision; +import org.apache.doris.nereids.trees.expressions.functions.CustomSignature; import org.apache.doris.nereids.trees.expressions.functions.ExpressionTrait; import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; @@ -38,12 +39,9 @@ /** * ScalarFunction 'named_struct'. */ -public class CreateNamedStruct extends ScalarFunction - implements ExplicitlyCastableSignature, AlwaysNotNullable { +public class CreateNamedStruct extends ScalarFunction implements CustomSignature, ComputePrecision, AlwaysNotNullable { - public static final List SIGNATURES = ImmutableList.of( - FunctionSignature.ret(StructType.SYSTEM_DEFAULT).args() - ); + public static final FunctionSignature SIGNATURE = FunctionSignature.ret(StructType.SYSTEM_DEFAULT).args(); /** * constructor with 0 or more arguments. @@ -83,14 +81,14 @@ public CreateNamedStruct withChildren(List children) { } @Override - public R accept(ExpressionVisitor visitor, C context) { - return visitor.visitCreateNamedStruct(this, context); + public FunctionSignature computePrecision(FunctionSignature signature) { + return signature; } @Override - public List getSignatures() { + public FunctionSignature customSignature() { if (arity() == 0) { - return SIGNATURES; + return SIGNATURE; } else { ImmutableList.Builder structFields = ImmutableList.builder(); for (int i = 0; i < arity(); i = i + 2) { @@ -98,8 +96,14 @@ public List getSignatures() { structFields.add(new StructField(nameLiteral.getStringValue(), children.get(i + 1).getDataType(), true, "")); } - return ImmutableList.of(FunctionSignature.ret(new StructType(structFields.build())) - .args(children.stream().map(ExpressionTrait::getDataType).toArray(DataType[]::new))); + return FunctionSignature.ret(new StructType(structFields.build())) + .args(children.stream().map(ExpressionTrait::getDataType).toArray(DataType[]::new)); } } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitCreateNamedStruct(this, context); + } + } diff --git a/regression-test/data/query_p0/sql_functions/struct_functions/test_struct_functions.out b/regression-test/data/query_p0/sql_functions/struct_functions/test_struct_functions.out index 1d6a691cf0f717..7c4078faaf2d71 100644 --- a/regression-test/data/query_p0/sql_functions/struct_functions/test_struct_functions.out +++ b/regression-test/data/query_p0/sql_functions/struct_functions/test_struct_functions.out @@ -39,3 +39,10 @@ a \N abc \N \N \N NULL null \N +-- !sql_before -- +{"col_11":123.321, "col_12":{"col":1, "col1":345.24}} + +-- !sql_after -- +{"col_11":123.321, "col_12":{"col":1, "col1":345.24}} +{"col_11":123.321, "col_12":{"col":1, "col1":12345.24}} + diff --git a/regression-test/data/query_p0/sql_functions/struct_functions/test_struct_functions_by_literal.out b/regression-test/data/query_p0/sql_functions/struct_functions/test_struct_functions_by_literal.out index 3a6e5847e45f06..4e28ba7e4b7519 100644 --- a/regression-test/data/query_p0/sql_functions/struct_functions/test_struct_functions_by_literal.out +++ b/regression-test/data/query_p0/sql_functions/struct_functions/test_struct_functions_by_literal.out @@ -35,3 +35,9 @@ -- !sql -- 10000000000 +-- !sql -- +123.321 {"col":1, "col1":12345.24} + +-- !sql -- +123.321 {"col":1, "col1":12345.24} + diff --git a/regression-test/suites/query_p0/sql_functions/struct_functions/test_struct_functions.groovy b/regression-test/suites/query_p0/sql_functions/struct_functions/test_struct_functions.groovy index fefa7a6aa04c52..64cccd81d09658 100644 --- a/regression-test/suites/query_p0/sql_functions/struct_functions/test_struct_functions.groovy +++ b/regression-test/suites/query_p0/sql_functions/struct_functions/test_struct_functions.groovy @@ -47,4 +47,12 @@ suite("test_struct_functions") { qt_select_struct_element_2 "SELECT struct_element(k3,'f1'),struct_element(k3,'f2'),struct_element(k3,'f3') FROM ${tableName} ORDER BY k1" qt_select_struct_element_3 "SELECT struct_element(k4,1),struct_element(k4,2),struct_element(k4,3),struct_element(k4,4) FROM ${tableName} ORDER BY k1" qt_select_struct_element_4 "SELECT struct_element(k5,1),struct_element(k5,2),struct_element(k5,3) FROM ${tableName} ORDER BY k1" + + //The precision of the decimal type in the test select is inconsistent with the precision of the function named_struct containing the decimal type. + sql """ drop table if exists t01 --force """; + sql """ create table if not exists t01 (a decimal(6,3), d struct) properties ("replication_num"="1");""" + sql """ insert into t01 values (123.321, named_struct('col', 1, 'col1', 345.24));""" + qt_sql_before """ select named_struct("col_11", a, "col_12", d) from t01; """ + sql """ insert into t01 values (123.321, named_struct('col', 1, 'col1', 12345.24));""" + qt_sql_after """ select named_struct("col_11", a, "col_12", d) from t01; """ } diff --git a/regression-test/suites/query_p0/sql_functions/struct_functions/test_struct_functions_by_literal.groovy b/regression-test/suites/query_p0/sql_functions/struct_functions/test_struct_functions_by_literal.groovy index d394b3cbd4219f..8cdc0cbdabb311 100644 --- a/regression-test/suites/query_p0/sql_functions/struct_functions/test_struct_functions_by_literal.groovy +++ b/regression-test/suites/query_p0/sql_functions/struct_functions/test_struct_functions_by_literal.groovy @@ -33,4 +33,8 @@ suite("test_struct_functions_by_literal") { qt_sql "select struct_element(named_struct('f1', 1, 'f2', 2, 'f3', 3), 'f1')" qt_sql "select struct_element(named_struct('f1', 1, 'f2', 1000, 'f3', 10000000000), 3)" + + //The precision of the decimal type in the test select is inconsistent with the precision of the function named_struct containing the decimal type. + qt_sql "select cast(123.321 as decimal(6,3)), named_struct('col', 1, 'col1', 12345.24)" + qt_sql "select cast(123.321 as decimal(6,3)), named_struct('col', 1, 'col1', cast(12345.24 as decimal(7,2)))" }