From 99755920e26a242aa10044c62438d6f2eaa27a97 Mon Sep 17 00:00:00 2001 From: starocean999 <40539150+starocean999@users.noreply.github.com> Date: Mon, 25 Dec 2023 11:23:00 +0800 Subject: [PATCH 001/299] [fix](nereids)subquery unnest need handle subquery in Not expr correnctly (#28713) --- .../rules/analysis/SubqueryToApply.java | 35 ++++++- .../nereids_p0/subquery/test_subquery.out | 4 + .../nereids_p0/subquery/test_subquery.groovy | 93 +++++++++++++++++++ 3 files changed, 127 insertions(+), 5 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SubqueryToApply.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SubqueryToApply.java index c236e1b325d206..5f55f86cc91257 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SubqueryToApply.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SubqueryToApply.java @@ -23,6 +23,7 @@ import org.apache.doris.nereids.rules.RuleType; import org.apache.doris.nereids.trees.TreeNode; import org.apache.doris.nereids.trees.expressions.Alias; +import org.apache.doris.nereids.trees.expressions.And; import org.apache.doris.nereids.trees.expressions.BinaryOperator; import org.apache.doris.nereids.trees.expressions.Exists; import org.apache.doris.nereids.trees.expressions.Expression; @@ -30,6 +31,7 @@ import org.apache.doris.nereids.trees.expressions.ListQuery; import org.apache.doris.nereids.trees.expressions.MarkJoinSlotReference; import org.apache.doris.nereids.trees.expressions.NamedExpression; +import org.apache.doris.nereids.trees.expressions.Not; import org.apache.doris.nereids.trees.expressions.Or; import org.apache.doris.nereids.trees.expressions.ScalarSubquery; import org.apache.doris.nereids.trees.expressions.Slot; @@ -38,7 +40,6 @@ import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral; import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter; import org.apache.doris.nereids.trees.plans.Plan; -import org.apache.doris.nereids.trees.plans.algebra.Aggregate; import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate; import org.apache.doris.nereids.trees.plans.logical.LogicalApply; import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; @@ -73,7 +74,8 @@ public List buildRules() { RuleType.FILTER_SUBQUERY_TO_APPLY.build( logicalFilter().thenApply(ctx -> { LogicalFilter filter = ctx.root; - + boolean shouldOutputMarkJoinSlot = filter.getConjuncts().stream() + .anyMatch(expr -> shouldOutputMarkJoinSlot(expr, SearchState.SearchNot)); ImmutableList> subqueryExprsList = filter.getConjuncts().stream() .>map(e -> e.collect(SubqueryToApply::canConvertToSupply)) .collect(ImmutableList.toImmutableList()); @@ -98,7 +100,7 @@ public List buildRules() { // first step: Replace the subquery of predicate in LogicalFilter // second step: Replace subquery with LogicalApply ReplaceSubquery replaceSubquery = new ReplaceSubquery( - ctx.statementContext, false); + ctx.statementContext, shouldOutputMarkJoinSlot); SubqueryContext context = new SubqueryContext(subqueryExprs); Expression conjunct = replaceSubquery.replace(oldConjuncts.get(i), context); @@ -343,7 +345,7 @@ private boolean nonMarkJoinExistsWithAgg(SubqueryExpr exists, && hasTopLevelAggWithoutGroupBy(exists.getQueryPlan()); } - private boolean hasTopLevelAggWithoutGroupBy(Plan plan) { + private static boolean hasTopLevelAggWithoutGroupBy(Plan plan) { if (plan instanceof LogicalAggregate) { return ((LogicalAggregate) plan).getGroupByExpressions().isEmpty(); } else if (plan instanceof LogicalProject || plan instanceof LogicalSort) { @@ -427,7 +429,7 @@ public Expression visitExistsSubquery(Exists exists, SubqueryContext context) { // it will always consider the returned result to be true boolean needCreateMarkJoinSlot = isMarkJoin || shouldOutputMarkJoinSlot; MarkJoinSlotReference markJoinSlotReference = null; - if (exists.getQueryPlan().anyMatch(Aggregate.class::isInstance) && needCreateMarkJoinSlot) { + if (hasTopLevelAggWithoutGroupBy(exists.getQueryPlan()) && needCreateMarkJoinSlot) { markJoinSlotReference = new MarkJoinSlotReference(statementContext.generateColumnName(), true); } else if (needCreateMarkJoinSlot) { @@ -505,4 +507,27 @@ private void setSubqueryToMarkJoinSlot(SubqueryExpr subquery, } + private enum SearchState { + SearchNot, + SearchAnd, + SearchExistsOrInSubquery + } + + private boolean shouldOutputMarkJoinSlot(Expression expr, SearchState searchState) { + if (searchState == SearchState.SearchNot && expr instanceof Not) { + if (shouldOutputMarkJoinSlot(((Not) expr).child(), SearchState.SearchAnd)) { + return true; + } + } else if (searchState == SearchState.SearchAnd && expr instanceof And) { + for (Expression child : expr.children()) { + if (shouldOutputMarkJoinSlot(child, SearchState.SearchExistsOrInSubquery)) { + return true; + } + } + } else if (searchState == SearchState.SearchExistsOrInSubquery + && (expr instanceof InSubquery || expr instanceof Exists)) { + return true; + } + return false; + } } diff --git a/regression-test/data/nereids_p0/subquery/test_subquery.out b/regression-test/data/nereids_p0/subquery/test_subquery.out index 2344cedddd5077..004aa0bcad05bf 100644 --- a/regression-test/data/nereids_p0/subquery/test_subquery.out +++ b/regression-test/data/nereids_p0/subquery/test_subquery.out @@ -26,3 +26,7 @@ true 15 1992 3021 11011920 0.000 true 9999-12-12 2015-04-02T00:00 3.141592653 2 -- !sql_mark_join -- 1 +-- !select_sub -- +1 9 +2 \N + diff --git a/regression-test/suites/nereids_p0/subquery/test_subquery.groovy b/regression-test/suites/nereids_p0/subquery/test_subquery.groovy index dfb79c771d8eac..36a863a211d351 100644 --- a/regression-test/suites/nereids_p0/subquery/test_subquery.groovy +++ b/regression-test/suites/nereids_p0/subquery/test_subquery.groovy @@ -155,4 +155,97 @@ suite("test_subquery") { qt_sql_mark_join """with A as (select count(*) n1 from test_one_row_relation where exists (select 1 from test_one_row_relation t where t.user_id = test_one_row_relation.user_id) or 1 = 1) select * from A;""" sql """drop table if exists test_one_row_relation;""" + + sql """drop table if exists subquery_test_t1;""" + sql """drop table if exists subquery_test_t2;""" + sql """create table subquery_test_t1 ( + id int + ) + UNIQUE KEY (`id`) + DISTRIBUTED BY HASH(`id`) BUCKETS 1 + PROPERTIES ("replication_allocation" = "tag.location.default: 1");""" + sql """create table subquery_test_t2 ( + id int + ) + UNIQUE KEY (`id`) + DISTRIBUTED BY HASH(`id`) BUCKETS 1 + PROPERTIES ("replication_allocation" = "tag.location.default: 1");""" + + explain { + sql("""analyzed plan select subquery_test_t1.id from subquery_test_t1 + where + not ( + exists(select 1 from subquery_test_t2 where subquery_test_t1.id = subquery_test_t2.id and subquery_test_t2.id = 5) + and + exists(select 1 from subquery_test_t2 where subquery_test_t1.id = subquery_test_t2.id and subquery_test_t2.id = 6) + ); """) + contains("isMarkJoin=true") + } + explain { + sql("""analyzed plan select subquery_test_t1.id from subquery_test_t1 + where + not ( + subquery_test_t1.id > 10 + and + exists(select 1 from subquery_test_t2 where subquery_test_t1.id = subquery_test_t2.id and subquery_test_t2.id = 6) + );""") + contains("isMarkJoin=true") + } + explain { + sql("""analyzed plan select subquery_test_t1.id from subquery_test_t1 + where + not ( + subquery_test_t1.id > 10 + and + subquery_test_t1.id in (select 1 from subquery_test_t2 where subquery_test_t1.id = subquery_test_t2.id and subquery_test_t2.id = 6) + ); """) + contains("isMarkJoin=true") + } + explain { + sql("""analyzed plan select subquery_test_t1.id from subquery_test_t1 + where + not ( + subquery_test_t1.id > 10 + and + subquery_test_t1.id in (select 1 from subquery_test_t2 where subquery_test_t1.id = subquery_test_t2.id and subquery_test_t2.id = 6) + ); """) + contains("isMarkJoin=true") + } + explain { + sql("""analyzed plan select subquery_test_t1.id from subquery_test_t1 + where + not ( + subquery_test_t1.id > 10 + and + ( subquery_test_t1.id < 100 or subquery_test_t1.id in (select 1 from subquery_test_t2 where subquery_test_t1.id = subquery_test_t2.id and subquery_test_t2.id = 6) ) + ); """) + contains("isMarkJoin=true") + } + explain { + sql("""analyzed plan select subquery_test_t1.id from subquery_test_t1 + where + not ( + subquery_test_t1.id > 10 + and + ( subquery_test_t1.id < 100 or case when subquery_test_t1.id in (select 1 from subquery_test_t2 where subquery_test_t1.id = subquery_test_t2.id and subquery_test_t2.id = 6) then 1 else 0 end ) + );""") + contains("isMarkJoin=true") + } + + sql """drop table if exists table_23_undef_undef""" + sql """create table table_23_undef_undef (`pk` int,`col_int_undef_signed` int ,`col_varchar_10__undef_signed` varchar(10) ,`col_varchar_1024__undef_signed` varchar(1024) ) engine=olap distributed by hash(pk) buckets 10 properties( 'replication_num' = '1');""" + sql """drop table if exists table_20_undef_undef""" + sql """create table table_20_undef_undef (`pk` int,`col_int_undef_signed` int ,`col_varchar_10__undef_signed` varchar(10) ,`col_varchar_1024__undef_signed` varchar(1024) ) engine=olap distributed by hash(pk) buckets 10 properties( 'replication_num' = '1');""" + sql """drop table if exists table_9_undef_undef""" + sql """create table table_9_undef_undef (`pk` int,`col_int_undef_signed` int ,`col_varchar_10__undef_signed` varchar(10) ,`col_varchar_1024__undef_signed` varchar(1024) ) engine=olap distributed by hash(pk) buckets 10 properties( 'replication_num' = '1');""" + + sql """insert into table_23_undef_undef values (0,0,'t','p'),(1,6,'q',"really"),(2,3,'p',"of"),(3,null,"he",'k'),(4,8,"this","don't"),(5,6,"see","this"),(6,5,'s','q'),(7,null,'o','j'),(8,9,'l',"could"),(9,null,"one",'l'),(10,7,"can't",'f'),(11,2,"going","not"),(12,null,'g','r'),(13,3,"ok",'s'),(14,6,"she",'k'),(15,null,"she",'p'),(16,8,"what","him"),(17,null,"from","to"),(18,5,"so","up"),(19,null,"my","is"),(20,null,'h',"see"),(21,null,"as","to"),(22,0,"know","the");""" + sql """insert into table_20_undef_undef values (0,null,'r','x'),(1,null,'m',"say"),(2,2,"mean",'h'),(3,null,'n','b'),(4,8,"do","do"),(5,9,'h',"were"),(6,null,"was","one"),(7,2,'o',"she"),(8,0,"who","me"),(9,null,'n',"that"),(10,null,"will",'l'),(11,4,'m',"if"),(12,5,"the","got"),(13,null,"why",'f'),(14,0,"of","for"),(15,null,"or","ok"),(16,null,'c','u'),(17,3,'f','c'),(18,null,"see",'f'),(19,2,'f','z');""" + sql """insert into table_9_undef_undef values (0,3,"his",'g'),(1,8,'p','n'),(2,null,"get","got"),(3,3,'r','r'),(4,null,"or","get"),(5,0,'j',"yeah"),(6,null,'w','x'),(7,8,'q',"for"),(8,3,'p',"that");""" + + qt_select_sub"""SELECT DISTINCT alias1.`pk` AS field1, alias2.`col_int_undef_signed` AS field2 FROM table_23_undef_undef AS alias1, table_20_undef_undef AS alias2 WHERE ( EXISTS ( SELECT DISTINCT SQ1_alias1.`col_varchar_10__undef_signed` AS SQ1_field1 FROM table_9_undef_undef AS SQ1_alias1 WHERE SQ1_alias1.`col_varchar_10__undef_signed` = alias1.`col_varchar_10__undef_signed` ) ) OR alias1.`col_varchar_1024__undef_signed` = "TmxRwcNZHC" AND ( alias1.`col_varchar_10__undef_signed` <> "rnZeukOcuM" AND alias2.`col_varchar_10__undef_signed` != "dbPAEpzstk" ) ORDER BY alias1.`pk`, field1, field2 LIMIT 2 OFFSET 7; """ + sql """drop table if exists table_23_undef_undef""" + sql """drop table if exists table_20_undef_undef""" + sql """drop table if exists table_9_undef_undef""" + } From c53611dcb3906637648b7e4bd77e01e3b5399388 Mon Sep 17 00:00:00 2001 From: starocean999 <40539150+starocean999@users.noreply.github.com> Date: Mon, 25 Dec 2023 11:23:27 +0800 Subject: [PATCH 002/299] [feature](nereids)support decimalv2 (#28726) --- .../executable/NumericArithmetic.java | 3 + .../expressions/literal/DecimalLiteral.java | 5 +- .../expressions/literal/DecimalV3Literal.java | 28 +++++- .../apache/doris/nereids/types/DataType.java | 18 ++++ .../doris/nereids/types/DecimalV2Type.java | 52 +++++++---- .../nereids/parser/NereidsParserTest.java | 8 ++ .../nereids_p0/datatype/test_decimalv2.out | 52 +++++++++++ .../decimalv2/test_decimalv2_overflow.groovy | 2 +- .../nereids_p0/datatype/test_decimalv2.groovy | 88 +++++++++++++++++++ 9 files changed, 237 insertions(+), 19 deletions(-) create mode 100644 regression-test/data/nereids_p0/datatype/test_decimalv2.out create mode 100644 regression-test/suites/nereids_p0/datatype/test_decimalv2.groovy diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmetic.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmetic.java index 228c8be3d09d0d..0aad2849823ed6 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmetic.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmetic.java @@ -570,6 +570,9 @@ public static Expression divideDouble(DoubleLiteral first, DoubleLiteral second) return new DoubleLiteral(result); } + /** + * Executable arithmetic functions divide + */ @ExecFunction(name = "divide", argTypes = {"DECIMAL", "DECIMAL"}, returnType = "DECIMAL") public static Expression divideDecimal(DecimalLiteral first, DecimalLiteral second) { if (first.getValue().compareTo(BigDecimal.ZERO) == 0) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalLiteral.java index 472ea3a5dc7a7c..711673cc2ff672 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalLiteral.java @@ -74,13 +74,14 @@ public double getDouble() { /** * check precision and scale is enough for value. */ - public static void checkPrecisionAndScale(int precision, int scale, BigDecimal value) throws AnalysisException { + private static void checkPrecisionAndScale(int precision, int scale, BigDecimal value) throws AnalysisException { Preconditions.checkNotNull(value); int realPrecision = value.precision(); int realScale = value.scale(); boolean valid = true; if (precision != -1 && scale != -1) { - if (precision < realPrecision || scale < realScale) { + if (precision < realPrecision || scale < realScale + || realPrecision - realScale > DecimalV2Type.MAX_PRECISION - DecimalV2Type.MAX_SCALE) { valid = false; } } else { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalV3Literal.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalV3Literal.java index 741d2c3c4c11c5..65296b4f79b7f3 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalV3Literal.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalV3Literal.java @@ -18,9 +18,12 @@ package org.apache.doris.nereids.trees.expressions.literal; import org.apache.doris.analysis.LiteralExpr; +import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.DecimalV3Type; +import com.google.common.base.Preconditions; + import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Objects; @@ -43,7 +46,7 @@ public DecimalV3Literal(BigDecimal value) { public DecimalV3Literal(DecimalV3Type dataType, BigDecimal value) { super(DecimalV3Type.createDecimalV3TypeLooseCheck(dataType.getPrecision(), dataType.getScale())); Objects.requireNonNull(value, "value not be null"); - DecimalLiteral.checkPrecisionAndScale(dataType.getPrecision(), dataType.getScale(), value); + checkPrecisionAndScale(dataType.getPrecision(), dataType.getScale(), value); BigDecimal adjustedValue = value.scale() < 0 ? value : value.setScale(dataType.getScale(), RoundingMode.HALF_UP); this.value = Objects.requireNonNull(adjustedValue); @@ -80,4 +83,27 @@ public DecimalV3Literal roundFloor(int newScale) { .createDecimalV3Type(((DecimalV3Type) dataType).getPrecision(), newScale), value.setScale(newScale, RoundingMode.FLOOR)); } + + /** + * check precision and scale is enough for value. + */ + private static void checkPrecisionAndScale(int precision, int scale, BigDecimal value) throws AnalysisException { + Preconditions.checkNotNull(value); + int realPrecision = value.precision(); + int realScale = value.scale(); + boolean valid = true; + if (precision != -1 && scale != -1) { + if (precision < realPrecision || scale < realScale) { + valid = false; + } + } else { + valid = false; + } + + if (!valid) { + throw new AnalysisException( + String.format("Invalid precision and scale - expect (%d, %d), but (%d, %d)", + precision, scale, realPrecision, realScale)); + } + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java index b22c6093f9a75c..999e8881569736 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java @@ -156,6 +156,24 @@ public static DataType convertPrimitiveFromStrings(List types, boolean u throw new AnalysisException("Nereids do not support type: " + type); } break; + case "decimalv2": + // NOTICE, maybe convert to decimalv3, so do not truc here. + switch (types.size()) { + case 1: + dataType = DecimalV2Type.CATALOG_DEFAULT_NOT_CONVERSION; + break; + case 2: + dataType = DecimalV2Type.createDecimalV2TypeWithoutTruncate( + Integer.parseInt(types.get(1)), 0, false); + break; + case 3: + dataType = DecimalV2Type.createDecimalV2TypeWithoutTruncate( + Integer.parseInt(types.get(1)), Integer.parseInt(types.get(2)), false); + break; + default: + throw new AnalysisException("Nereids do not support type: " + type); + } + break; case "decimalv3": switch (types.size()) { case 1: diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DecimalV2Type.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DecimalV2Type.java index 00b043db7a67f3..523f113d996541 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DecimalV2Type.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DecimalV2Type.java @@ -37,17 +37,21 @@ public class DecimalV2Type extends FractionalType { public static int MAX_PRECISION = 27; public static int MAX_SCALE = 9; - public static final DecimalV2Type SYSTEM_DEFAULT = new DecimalV2Type(MAX_PRECISION, MAX_SCALE); - public static final DecimalV2Type CATALOG_DEFAULT = new DecimalV2Type(DEFAULT_PRECISION, DEFAULT_SCALE); - - private static final DecimalV2Type BOOLEAN_DECIMAL = new DecimalV2Type(1, 0); - private static final DecimalV2Type TINYINT_DECIMAL = new DecimalV2Type(3, 0); - private static final DecimalV2Type SMALLINT_DECIMAL = new DecimalV2Type(5, 0); - private static final DecimalV2Type INTEGER_DECIMAL = new DecimalV2Type(10, 0); - private static final DecimalV2Type BIGINT_DECIMAL = new DecimalV2Type(20, 0); - private static final DecimalV2Type LARGEINT_DECIMAL = new DecimalV2Type(27, 0); - private static final DecimalV2Type FLOAT_DECIMAL = new DecimalV2Type(14, 7); - private static final DecimalV2Type DOUBLE_DECIMAL = new DecimalV2Type(27, 9); + public static final DecimalV2Type SYSTEM_DEFAULT = new DecimalV2Type(MAX_PRECISION, MAX_SCALE, true); + public static final DecimalV2Type SYSTEM_DEFAULT_NOT_CONVERSION = + new DecimalV2Type(MAX_PRECISION, MAX_SCALE, false); + public static final DecimalV2Type CATALOG_DEFAULT = new DecimalV2Type(DEFAULT_PRECISION, DEFAULT_SCALE, true); + public static final DecimalV2Type CATALOG_DEFAULT_NOT_CONVERSION = + new DecimalV2Type(DEFAULT_PRECISION, DEFAULT_SCALE, false); + + private static final DecimalV2Type BOOLEAN_DECIMAL = new DecimalV2Type(1, 0, true); + private static final DecimalV2Type TINYINT_DECIMAL = new DecimalV2Type(3, 0, true); + private static final DecimalV2Type SMALLINT_DECIMAL = new DecimalV2Type(5, 0, true); + private static final DecimalV2Type INTEGER_DECIMAL = new DecimalV2Type(10, 0, true); + private static final DecimalV2Type BIGINT_DECIMAL = new DecimalV2Type(20, 0, true); + private static final DecimalV2Type LARGEINT_DECIMAL = new DecimalV2Type(27, 0, true); + private static final DecimalV2Type FLOAT_DECIMAL = new DecimalV2Type(14, 7, true); + private static final DecimalV2Type DOUBLE_DECIMAL = new DecimalV2Type(27, 9, true); private static final int WIDTH = 16; @@ -68,14 +72,17 @@ public class DecimalV2Type extends FractionalType { private final int precision; private final int scale; + private final boolean shouldConversion; + /** * constructors. */ - private DecimalV2Type(int precision, int scale) { + private DecimalV2Type(int precision, int scale, boolean shouldConversion) { Preconditions.checkArgument(precision >= scale, "precision should not smaller than scale," + " but precision is " + precision, ", scale is " + scale); this.precision = precision; this.scale = scale; + this.shouldConversion = shouldConversion; } /** createDecimalV2Type. */ @@ -86,7 +93,7 @@ public static DecimalV2Type createDecimalV2Type(int precision, int scale) { if (precision == CATALOG_DEFAULT.precision && scale == CATALOG_DEFAULT.scale) { return CATALOG_DEFAULT; } - return new DecimalV2Type(Math.min(precision, MAX_PRECISION), Math.min(scale, MAX_SCALE)); + return new DecimalV2Type(Math.min(precision, MAX_PRECISION), Math.min(scale, MAX_SCALE), true); } public static DecimalV2Type createDecimalV2Type(BigDecimal bigDecimal) { @@ -105,7 +112,22 @@ public static DecimalV2Type createDecimalV2TypeWithoutTruncate(int precision, in if (precision == CATALOG_DEFAULT.precision && scale == CATALOG_DEFAULT.scale) { return CATALOG_DEFAULT; } - return new DecimalV2Type(precision, scale); + return new DecimalV2Type(precision, scale, true); + } + + /** + * create DecimalV2Type with appropriate scale, precision and shouldConversion flag, + * not truncate to MAX_PRECISION, MAX_SCALE. + */ + public static DecimalV2Type createDecimalV2TypeWithoutTruncate(int precision, int scale, + boolean shouldConversion) { + if (precision == SYSTEM_DEFAULT.precision && scale == SYSTEM_DEFAULT.scale) { + return shouldConversion ? SYSTEM_DEFAULT : SYSTEM_DEFAULT_NOT_CONVERSION; + } + if (precision == CATALOG_DEFAULT.precision && scale == CATALOG_DEFAULT.scale) { + return shouldConversion ? CATALOG_DEFAULT : CATALOG_DEFAULT_NOT_CONVERSION; + } + return new DecimalV2Type(precision, scale, shouldConversion); } /** @@ -153,7 +175,7 @@ public int getScale() { @Override public DataType conversion() { - if (Config.enable_decimal_conversion) { + if (Config.enable_decimal_conversion && shouldConversion) { return DecimalV3Type.createDecimalV3Type(precision, scale); } Preconditions.checkArgument(precision > 0 && precision <= MAX_PRECISION, diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java index bae4d7459ce9f0..9baee503cfc491 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java @@ -331,6 +331,14 @@ public void testDatetimev1() { } + @Test + public void testDecimalv2() { + String decv2 = "SELECT CAST('1.234' AS decimalv2(10,5))"; + NereidsParser nereidsParser = new NereidsParser(); + LogicalPlan logicalPlan = (LogicalPlan) nereidsParser.parseSingle(decv2).child(0); + Assertions.assertTrue(logicalPlan.getExpressions().get(0).getDataType().isDecimalV2Type()); + } + @Test public void parseSetOperation() { String union = "select * from t1 union select * from t2 union all select * from t3"; diff --git a/regression-test/data/nereids_p0/datatype/test_decimalv2.out b/regression-test/data/nereids_p0/datatype/test_decimalv2.out new file mode 100644 index 00000000000000..70c7ed37cbbf78 --- /dev/null +++ b/regression-test/data/nereids_p0/datatype/test_decimalv2.out @@ -0,0 +1,52 @@ +-- This file is automatically generated. You should know what you did if you want to edit this +-- !sql1 -- +1.230 + +-- !sql2 -- +1.230 + +-- !sql1 -- +1 1.230 +2 2.340 +3 3.450 + +-- !sql2 -- +1 1.230 1 1.230 +2 2.340 2 2.340 +3 3.450 3 3.450 + +-- !sql2 -- +1 1.230 1 1.230 +2 2.340 2 2.340 +3 3.450 3 3.450 + +-- !sql2 -- +1 1.230 1 1.230 +2 2.340 2 2.340 +3 3.450 3 3.450 + +-- !sql2 -- +1 1.230 1 1.230 +2 2.340 2 2.340 +3 3.450 3 3.450 + +-- !sql2 -- +1 1.230 1 1.230 +2 2.340 2 2.340 +3 3.450 3 3.450 + +-- !sql2 -- +1 1.230 1 1.230 +2 2.340 2 2.340 +3 3.450 3 3.450 + +-- !sql2 -- +1 1.230 1 1.230 +2 2.340 2 2.340 +3 3.450 3 3.450 + +-- !sql2 -- +1 1.230 1 1.230 +2 2.340 2 2.340 +3 3.450 3 3.450 + diff --git a/regression-test/suites/datatype_p0/decimalv2/test_decimalv2_overflow.groovy b/regression-test/suites/datatype_p0/decimalv2/test_decimalv2_overflow.groovy index 2b23bf2d5dce7d..53cd87fac48478 100644 --- a/regression-test/suites/datatype_p0/decimalv2/test_decimalv2_overflow.groovy +++ b/regression-test/suites/datatype_p0/decimalv2/test_decimalv2_overflow.groovy @@ -36,7 +36,7 @@ suite("test_decimalv2_overflow", "nonConcurrent") { def tblName2 = "test_decimalv2_overflow2" sql "drop table if exists ${tblName2}" sql """ CREATE TABLE ${tblName2} ( - `c2` decimalv2(20, 2), + `c2` decimalv2(20, 2) ) ENGINE=OLAP UNIQUE KEY(`c2`) DISTRIBUTED BY HASH(`c2`) BUCKETS 10 diff --git a/regression-test/suites/nereids_p0/datatype/test_decimalv2.groovy b/regression-test/suites/nereids_p0/datatype/test_decimalv2.groovy new file mode 100644 index 00000000000000..3bdab1ccd51045 --- /dev/null +++ b/regression-test/suites/nereids_p0/datatype/test_decimalv2.groovy @@ -0,0 +1,88 @@ + +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +suite("test_decimalv2") { + def tbName = "test_decimalv2_exprs" + + sql """set enable_nereids_planner=true""" + sql """set enable_fallback_to_original_planner=false""" + sql """set enable_nereids_dml=true""" + + sql "DROP TABLE IF EXISTS ${tbName}" + sql """ + create table ${tbName}(k1 decimalv2(10,3), k2 int) distributed by hash(k1) buckets 1 properties("replication_num" = "1"); + """ + sql """ insert into ${tbName} values("1.23", 1); """ + + qt_sql1 """ select dt + from + ( + select cast(k1 as decimalv2(5,3)) as dt + from ${tbName} + ) r; """ + qt_sql2 """ select dt + from + ( + select cast(k1 as decimal(5,3)) as dt + from ${tbName} + ) r; """ + sql "DROP TABLE ${tbName}" + + tbName = "test_decimalv2_runtime_filter" + sql "DROP TABLE IF EXISTS ${tbName}" + sql """ + CREATE TABLE IF NOT EXISTS ${tbName} ( + c0 int, + c2 decimalv2(10, 3) + ) + DISTRIBUTED BY HASH(c0) BUCKETS 5 properties("replication_num" = "1"); + """ + sql "insert into ${tbName} values(1, '1.23')" + sql "insert into ${tbName} values(2, '2.34')" + sql "insert into ${tbName} values(3, '3.45')" + + qt_sql1 "select * from ${tbName} ORDER BY c0" + + sql " set runtime_filter_type = 1; " + qt_sql2 "select * from ${tbName} a, ${tbName} b WHERE a.c2 = b.c2 ORDER BY a.c0" + + sql " set runtime_filter_type = 2; " + qt_sql2 "select * from ${tbName} a, ${tbName} b WHERE a.c2 = b.c2 ORDER BY a.c0" + + sql " set runtime_filter_type = 4; " + qt_sql2 "select * from ${tbName} a, ${tbName} b WHERE a.c2 = b.c2 ORDER BY a.c0" + + sql " set runtime_filter_type = 8; " + qt_sql2 "select * from ${tbName} a, ${tbName} b WHERE a.c2 = b.c2 ORDER BY a.c0" + + sql " set runtime_filter_wait_time_ms = 0; " + + sql " set runtime_filter_type = 1; " + qt_sql2 "select * from ${tbName} a, ${tbName} b WHERE a.c2 = b.c2 ORDER BY a.c0" + + sql " set runtime_filter_type = 2; " + qt_sql2 "select * from ${tbName} a, ${tbName} b WHERE a.c2 = b.c2 ORDER BY a.c0" + + sql " set runtime_filter_type = 4; " + qt_sql2 "select * from ${tbName} a, ${tbName} b WHERE a.c2 = b.c2 ORDER BY a.c0" + + sql " set runtime_filter_type = 8; " + qt_sql2 "select * from ${tbName} a, ${tbName} b WHERE a.c2 = b.c2 ORDER BY a.c0" + + sql "DROP TABLE ${tbName}" +} From 48f8f8f2ad7e8d71b08b0d9a3a861f2dbe98095e Mon Sep 17 00:00:00 2001 From: starocean999 <40539150+starocean999@users.noreply.github.com> Date: Mon, 25 Dec 2023 11:23:44 +0800 Subject: [PATCH 003/299] [fix](nereids)group by expr may be bound twice in bind agg slot (#28771) --- .../rules/analysis/BindExpression.java | 38 +++++++++++++++---- .../nereids_syntax_p0/analyze_agg.groovy | 11 ++++++ 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java index 0437f7cdcdac3f..83b0eb1ea7924e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java @@ -292,10 +292,22 @@ protected boolean condition(Rule rule, Plan plan) { if (alias.child().anyMatch(expr -> expr instanceof AggregateFunction)) { continue; } - // NOTICE: must use unbound expressions, because we will bind them in binding group by expr. - childOutputsToExpr.putIfAbsent(alias.getName(), agg.getOutputExpressions().get(i).child(0)); + /* + Alias(x) has been bound by binding agg's output + we add x to childOutputsToExpr, so when binding group by exprs later, we can use x directly + and won't bind it again + select + p_cycle_time / (select max(p_cycle_time) from log_event_8) as 'x', + count(distinct case_id) as 'y' + from + log_event_8 + group by + x + */ + childOutputsToExpr.putIfAbsent(alias.getName(), output.get(i).child(0)); } + Set boundedGroupByExpressions = Sets.newHashSet(); List replacedGroupBy = agg.getGroupByExpressions().stream() .map(groupBy -> { if (groupBy instanceof UnboundSlot) { @@ -303,7 +315,9 @@ protected boolean condition(Rule rule, Plan plan) { if (unboundSlot.getNameParts().size() == 1) { String name = unboundSlot.getNameParts().get(0); if (childOutputsToExpr.containsKey(name)) { - return childOutputsToExpr.get(name); + Expression expression = childOutputsToExpr.get(name); + boundedGroupByExpressions.add(expression); + return expression; } } } @@ -336,16 +350,24 @@ protected boolean condition(Rule rule, Plan plan) { List groupBy = replacedGroupBy.stream() .map(expression -> { - Expression e = binder.bind(expression); - if (e instanceof UnboundSlot) { - return childBinder.bind(e); + if (boundedGroupByExpressions.contains(expression)) { + // expr has been bound by binding agg's output + return expression; + } else { + // bind slot for unbound exprs + Expression e = binder.bind(expression); + if (e instanceof UnboundSlot) { + return childBinder.bind(e); + } + return e; } - return e; }) .collect(Collectors.toList()); groupBy.forEach(expression -> checkBoundExceptLambda(expression, ctx.root)); groupBy = groupBy.stream() - .map(expr -> bindFunction(expr, ctx.root, ctx.cascadesContext)) + // bind function for unbound exprs or return old expr if it's bound by binding agg's output + .map(expr -> boundedGroupByExpressions.contains(expr) ? expr + : bindFunction(expr, ctx.root, ctx.cascadesContext)) .collect(ImmutableList.toImmutableList()); checkIfOutputAliasNameDuplicatedForGroupBy(groupBy, output); return agg.withGroupByAndOutput(groupBy, output); diff --git a/regression-test/suites/nereids_syntax_p0/analyze_agg.groovy b/regression-test/suites/nereids_syntax_p0/analyze_agg.groovy index 9f426e23e3d133..9a79df6bad524b 100644 --- a/regression-test/suites/nereids_syntax_p0/analyze_agg.groovy +++ b/regression-test/suites/nereids_syntax_p0/analyze_agg.groovy @@ -77,4 +77,15 @@ suite("analyze_agg") { // should not bind g /g in group by again, otherwise will throw exception sql "select g / g as nu, sum(c) from t2 group by nu" + sql """ + select + 1, + id / (select max(id) from t2) as 'x', + count(distinct c) as 'y' + from + t2 + group by + 1, + x + """ } \ No newline at end of file From 6de797844d10dd55f4c0cc58d5fd89078ab55e0d Mon Sep 17 00:00:00 2001 From: meiyi Date: Mon, 25 Dec 2023 11:34:11 +0800 Subject: [PATCH 004/299] [doc](insert) Add group commit docs (#25949) --- docs/en/docs/admin-manual/config/be-config.md | 13 + .../import/import-way/group-commit-manual.md | 415 ++++++++++++++++++ docs/sidebars.json | 3 +- .../docs/admin-manual/config/be-config.md | 13 + .../import/import-way/group-commit-manual.md | 413 +++++++++++++++++ 5 files changed, 856 insertions(+), 1 deletion(-) create mode 100644 docs/en/docs/data-operate/import/import-way/group-commit-manual.md create mode 100644 docs/zh-CN/docs/data-operate/import/import-way/group-commit-manual.md diff --git a/docs/en/docs/admin-manual/config/be-config.md b/docs/en/docs/admin-manual/config/be-config.md index 3c72ac3a82aacf..1b8ee9423dc76e 100644 --- a/docs/en/docs/admin-manual/config/be-config.md +++ b/docs/en/docs/admin-manual/config/be-config.md @@ -1504,3 +1504,16 @@ Indicates how many tablets failed to load in the data directory. At the same tim * Description: BE Whether to enable the use of java-jni. When enabled, mutual calls between c++ and java are allowed. Currently supports hudi, java-udf, jdbc, max-compute, paimon, preload, avro * Default value: true + +#### `group_commit_wal_path` + +* The `WAL` directory of group commit. +* Default: A directory named `wal` is created under each directory of the `storage_root_path`. Configuration examples: + ``` + group_commit_wal_path=/data1/storage/wal;/data2/storage/wal;/data3/storage/wal + ``` + +#### `group_commit_memory_rows_for_max_filter_ratio` + +* Description: The `max_filter_ratio` limit can only work if the total rows of `group commit` is less than this value. See [Group Commit](../../data-operate/import/import-way/group-commit-manual.md) for more details +* Default: 10000 diff --git a/docs/en/docs/data-operate/import/import-way/group-commit-manual.md b/docs/en/docs/data-operate/import/import-way/group-commit-manual.md new file mode 100644 index 00000000000000..9b0e007e15b6c0 --- /dev/null +++ b/docs/en/docs/data-operate/import/import-way/group-commit-manual.md @@ -0,0 +1,415 @@ +--- +{ + "title": "Group Commit", + "language": "en" +} +--- + + + +# Group Commit + +Group commit load does not introduce a new data import method, but an extension of `INSERT INTO tbl VALUS(...)`, `Stream Load` and `Http Stream`. It is a way to improve the write performance of Doris with high-concurrency and small-data writes. Your application can directly use JDBC to do high-concurrency insert operation into Doris, at the same time, combining PreparedStatement can get even higher performance. In logging scenarios, you can also do high-concurrency Stream Load or Http Stream into Doris. + +## Group Commit Mode + +Group Commit provides 3 modes: + +* `off_mode` + +Disable group commit, keep the original behavior for `INSERT INTO VALUES`, `Stream Load` and `Http Stream`. + +* `sync_mode` + +Doris groups multiple loads into one transaction commit based on the `group_commit_interval` table property. The load is returned after the transaction commit. This mode is suitable for high-concurrency writing scenarios and requires immediate data visibility after the load is finished. + +* `async_mode` + +Doris writes data to the Write Ahead Log (WAL) firstly, then the load is returned. Doris groups multiple loads into one transaction commit based on the `group_commit_interval` table property, and the data is visible after the commit. To prevent excessive disk space usage by the WAL, it automatically switches to `sync_mode`. This is suitable for latency-sensitive and high-frequency writing. + +## Basic operations + +If the table schema is: +```sql +CREATE TABLE `dt` ( + `id` int(11) NOT NULL, + `name` varchar(50) NULL, + `score` int(11) NULL +) ENGINE=OLAP +DUPLICATE KEY(`id`) +DISTRIBUTED BY HASH(`id`) BUCKETS 1 +PROPERTIES ( + "replication_num" = "1" +); +``` + +### INSERT INTO VALUES + +* async_mode +```sql +# Config session variable to enable the async group commit, the default value is off_mode +mysql> set group_commit = async_mode; + +# The retured label is start with 'group_commit', which is the label of the real load job +mysql> insert into dt values(1, 'Bob', 90), (2, 'Alice', 99); +Query OK, 2 rows affected (0.05 sec) +{'label':'group_commit_a145ce07f1c972fc-bd2c54597052a9ad', 'status':'PREPARE', 'txnId':'181508'} + +# The returned label and txn_id are the same as the above, which means they are handled in on load job +mysql> insert into dt(id, name) values(3, 'John'); +Query OK, 1 row affected (0.01 sec) +{'label':'group_commit_a145ce07f1c972fc-bd2c54597052a9ad', 'status':'PREPARE', 'txnId':'181508'} + +# The data is not visible +mysql> select * from dt; +Empty set (0.01 sec) + +# After about 10 seconds, the data is visible +mysql> select * from dt; ++------+-------+-------+ +| id | name | score | ++------+-------+-------+ +| 1 | Bob | 90 | +| 2 | Alice | 99 | +| 3 | John | NULL | ++------+-------+-------+ +3 rows in set (0.02 sec) +``` + +* sync_mode +```sql +# Config session variable to enable the sync group commit +mysql> set group_commit = sync_mode; + +# The retured label is start with 'group_commit', which is the label of the real load job. +# The insert costs at least the group_commit_interval_ms of table property. +mysql> insert into dt values(4, 'Bob', 90), (5, 'Alice', 99); +Query OK, 2 rows affected (10.06 sec) +{'label':'group_commit_d84ab96c09b60587_ec455a33cb0e9e87', 'status':'PREPARE', 'txnId':'3007', 'query_id':'fc6b94085d704a94-a69bfc9a202e66e2'} + +# The data is visible after the insert is returned +mysql> select * from dt; ++------+-------+-------+ +| id | name | score | ++------+-------+-------+ +| 1 | Bob | 90 | +| 2 | Alice | 99 | +| 3 | John | NULL | +| 4 | Bob | 90 | +| 5 | Alice | 99 | ++------+-------+-------+ +5 rows in set (0.03 sec) +``` + +* off_mode +```sql +mysql> set group_commit = off_mode; +``` + +### Stream Load + +If the content of `data.csv` is: +```sql +6,Amy,60 +7,Ross,98 +``` + +* async_mode +```sql +# Add 'group_commit:async_mode' configuration in the http header + +curl --location-trusted -u {user}:{passwd} -T data.csv -H "group_commit:async_mode" -H "column_separator:," http://{fe_host}:{http_port}/api/db/dt/_stream_load +{ + "TxnId": 7009, + "Label": "group_commit_c84d2099208436ab_96e33fda01eddba8", + "Comment": "", + "GroupCommit": true, + "Status": "Success", + "Message": "OK", + "NumberTotalRows": 2, + "NumberLoadedRows": 2, + "NumberFilteredRows": 0, + "NumberUnselectedRows": 0, + "LoadBytes": 19, + "LoadTimeMs": 35, + "StreamLoadPutTimeMs": 5, + "ReadDataTimeMs": 0, + "WriteDataTimeMs": 26 +} + +# The returned 'GroupCommit' is 'true', which means this is a group commit load +# The retured label is start with 'group_commit', which is the label of the real load job +``` + +* sync_mode +```sql +# Add 'group_commit:sync_mode' configuration in the http header + +curl --location-trusted -u {user}:{passwd} -T data.csv -H "group_commit:sync_mode" -H "column_separator:," http://{fe_host}:{http_port}/api/db/dt/_stream_load +{ + "TxnId": 3009, + "Label": "group_commit_d941bf17f6efcc80_ccf4afdde9881293", + "Comment": "", + "GroupCommit": true, + "Status": "Success", + "Message": "OK", + "NumberTotalRows": 2, + "NumberLoadedRows": 2, + "NumberFilteredRows": 0, + "NumberUnselectedRows": 0, + "LoadBytes": 19, + "LoadTimeMs": 10044, + "StreamLoadPutTimeMs": 4, + "ReadDataTimeMs": 0, + "WriteDataTimeMs": 10038 +} + +# The returned 'GroupCommit' is 'true', which means this is a group commit load +# The retured label is start with 'group_commit', which is the label of the real load job +``` + +See [Stream Load](stream-load-manual.md) for more detailed syntax used by **Stream Load**. + +### Http Stream + +* async_mode +```sql +# Add 'group_commit:async_mode' configuration in the http header + +curl --location-trusted -u {user}:{passwd} -T data.csv -H "group_commit:async_mode" -H "sql:insert into db.dt select * from http_stream('column_separator'=',', 'format' = 'CSV')" http://{fe_host}:{http_port}/api/_http_stream +{ + "TxnId": 7011, + "Label": "group_commit_3b45c5750d5f15e5_703428e462e1ebb0", + "Comment": "", + "GroupCommit": true, + "Status": "Success", + "Message": "OK", + "NumberTotalRows": 2, + "NumberLoadedRows": 2, + "NumberFilteredRows": 0, + "NumberUnselectedRows": 0, + "LoadBytes": 19, + "LoadTimeMs": 65, + "StreamLoadPutTimeMs": 41, + "ReadDataTimeMs": 47, + "WriteDataTimeMs": 23 +} + +# The returned 'GroupCommit' is 'true', which means this is a group commit load +# The retured label is start with 'group_commit', which is the label of the real load job +``` + +* sync_mode +```sql +# Add 'group_commit:sync_mode' configuration in the http header + +curl --location-trusted -u {user}:{passwd} -T data.csv -H "group_commit:sync_mode" -H "sql:insert into db.dt select * from http_stream('column_separator'=',', 'format' = 'CSV')" http://{fe_host}:{http_port}/api/_http_stream +{ + "TxnId": 3011, + "Label": "group_commit_fe470e6752aadbe6_a8f3ac328b02ea91", + "Comment": "", + "GroupCommit": true, + "Status": "Success", + "Message": "OK", + "NumberTotalRows": 2, + "NumberLoadedRows": 2, + "NumberFilteredRows": 0, + "NumberUnselectedRows": 0, + "LoadBytes": 19, + "LoadTimeMs": 10066, + "StreamLoadPutTimeMs": 31, + "ReadDataTimeMs": 32, + "WriteDataTimeMs": 10034 +} + +# The returned 'GroupCommit' is 'true', which means this is a group commit load +# The retured label is start with 'group_commit', which is the label of the real load job +``` + +See [Stream Load](stream-load-manual.md) for more detailed syntax used by **Http Stream**. + +### Use `PreparedStatement` + +To reduce the CPU cost of SQL parsing and query planning, we provide the `PreparedStatement` in the FE. When using `PreparedStatement`, the SQL and its plan will be cached in the session level memory cache and will be reused later on, which reduces the CPU cost of FE. The following is an example of using PreparedStatement in JDBC: + +1. Setup JDBC url and enable server side prepared statement + +``` +url = jdbc:mysql://127.0.0.1:9030/db?useServerPrepStmts=true +``` + +2. Set `group_commit` session variable, there are two ways to do it: + +* Add `sessionVariables=group_commit=async_mode` in JDBC url + +``` +url = jdbc:mysql://127.0.0.1:9030/db?useServerPrepStmts=true&sessionVariables=group_commit=async_mode +``` + +* Use `SET group_commit = async_mode;` command + +``` +try (Statement statement = conn.createStatement()) { + statement.execute("SET group_commit = async_mode;"); +} +``` + +3. Using `PreparedStatement` + +```java +private static final String JDBC_DRIVER = "com.mysql.jdbc.Driver"; +private static final String URL_PATTERN = "jdbc:mysql://%s:%d/%s?useServerPrepStmts=true"; +private static final String HOST = "127.0.0.1"; +private static final int PORT = 9087; +private static final String DB = "db"; +private static final String TBL = "dt"; +private static final String USER = "root"; +private static final String PASSWD = ""; +private static final int INSERT_BATCH_SIZE = 10; + +private static void groupCommitInsert() throws Exception { + Class.forName(JDBC_DRIVER); + try (Connection conn = DriverManager.getConnection(String.format(URL_PATTERN, HOST, PORT, DB), USER, PASSWD)) { + // set session variable 'group_commit' + try (Statement statement = conn.createStatement()) { + statement.execute("SET group_commit = async_mode;"); + } + + String query = "insert into " + TBL + " values(?, ?, ?)"; + try (PreparedStatement stmt = conn.prepareStatement(query)) { + for (int i = 0; i < INSERT_BATCH_SIZE; i++) { + stmt.setInt(1, i); + stmt.setString(2, "name" + i); + stmt.setInt(3, i + 10); + int result = stmt.executeUpdate(); + System.out.println("rows: " + result); + } + } + } catch (Exception e) { + e.printStackTrace(); + } +} + +private static void groupCommitInsertBatch() throws Exception { + Class.forName(JDBC_DRIVER); + // add rewriteBatchedStatements=true and cachePrepStmts=true in JDBC url + // set session variables by sessionVariables=group_commit=async_mode in JDBC url + try (Connection conn = DriverManager.getConnection( + String.format(URL_PATTERN + "&rewriteBatchedStatements=true&cachePrepStmts=true&sessionVariables=group_commit=async_mode", HOST, PORT, DB), USER, PASSWD)) { + + String query = "insert into " + TBL + " values(?, ?, ?)"; + try (PreparedStatement stmt = conn.prepareStatement(query)) { + for (int j = 0; j < 5; j++) { + // 10 rows per insert + for (int i = 0; i < INSERT_BATCH_SIZE; i++) { + stmt.setInt(1, i); + stmt.setString(2, "name" + i); + stmt.setInt(3, i + 10); + stmt.addBatch(); + } + int[] result = stmt.executeBatch(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } +} +``` + +See [Synchronize Data Using Insert Method](../import-scenes/jdbc-load.md) for more details about **JDBC**. + +## Modify the group commit interval + +The default group commit interval is 10 seconds. Users can modify the configuration of the table: + +```sql +# Modify the group commit interval to 2 seconds +ALTER TABLE dt SET ("group_commit_interval_ms"="2000"); +``` + +## Limitations + +* When the group commit is enabled, some `INSERT INTO VALUES` sqls are not executed in the group commit way if they meet the following conditions: + + * Transaction insert, such as `BEGIN`, `INSERT INTO VALUES`, `COMMIT` + + * Specify the label, such as `INSERT INTO dt WITH LABEL {label} VALUES` + + * Expressions within VALUES, such as `INSERT INTO dt VALUES (1 + 100)` + + * Column update + + * Tables that do not support light schema changes + +* When the group commit is enabled, some `Stream Load` and `Http Stream` are not executed in the group commit way if they meet the following conditions: + + * Two phase commit + + * Specify the label + + * Column update + + * Tables that do not support light schema changes + +* For unique table, because the group commit can not guarantee the commit order, users can use sequence column to ensure the data consistency. + +* The limit of `max_filter_ratio` + + * For non group commit load, filter_ratio is calculated by the failed rows and total rows when load is finished. If the filter_ratio does not match, the transaction will not commit + + * In the group commit mode, multiple user loads are executed by one internal load. The internal load will commit all user loads. + + * Currently, group commit supports a certain degree of max_filter_ratio semantics. When the total number of rows does not exceed group_commit_memory_rows_for_max_filter_ratio (configured in `be.conf`, defaulting to `10000` rows), max_filter_ratio will work. + +* The limit of WAL + + * For async_mode group commit, data is written to the Write Ahead Log (WAL). If the internal load succeeds, the WAL is immediately deleted. If the internal load fails, data is recovery by importing the WAL. + + * Currently, WAL files are stored only on one disk of one BE. If the BE's disk is damaged or the file is mistakenly deleted, it may result in data loss. + + * When decommissioning a BE node, please use the [`DECOMMISSION`](../../../sql-manual/sql-reference/Cluster-Management-Statements/ALTER-SYSTEM-DECOMMISSION-BACKEND.md) command to safely decommission the node. This prevents potential data loss if the WAL files are not processed before the node is taken offline. + + * For async_mode group commit writes, to protect disk space, it switches to sync_mode under the following conditions: + + * For an import with large amount of data: exceeding 80% of the disk space of a WAL directory. + + * Chunked stream loads with an unknown data amount. + + * Insufficient disk space, even with it is an import with small amount of data. + + * During hard weight schema changes (adding or dropping columns, modifying varchar length, and renaming columns are lightweight schema changes, others are hard weight), to ensure WAL file is compatibility with the table's schema, the final stage of metadata modification in FE will reject group commit writes. Clients get `insert table ${table_name} is blocked on schema change` exception and can retry the import. + +## Relevant system configuration + +### BE configuration + +#### `group_commit_wal_path` + +* The `WAL` directory of group commit. +* Default: A directory named `wal` is created under each directory of the `storage_root_path`. Configuration examples: + ``` + group_commit_wal_path=/data1/storage/wal;/data2/storage/wal;/data3/storage/wal + ``` + +#### `group_commit_memory_rows_for_max_filter_ratio` + +* Description: The `max_filter_ratio` limit can only work if the total rows of `group commit` is less than this value. +* Default: 10000 + diff --git a/docs/sidebars.json b/docs/sidebars.json index da910610f08645..7e5286f7a4c73a 100644 --- a/docs/sidebars.json +++ b/docs/sidebars.json @@ -91,7 +91,8 @@ "data-operate/import/import-way/mysql-load-manual", "data-operate/import/import-way/s3-load-manual", "data-operate/import/import-way/insert-into-manual", - "data-operate/import/import-way/load-json-format" + "data-operate/import/import-way/load-json-format", + "data-operate/import/import-way/group-commit-manual" ] }, { diff --git a/docs/zh-CN/docs/admin-manual/config/be-config.md b/docs/zh-CN/docs/admin-manual/config/be-config.md index 04d74f49d60632..7b99c1f2cb9068 100644 --- a/docs/zh-CN/docs/admin-manual/config/be-config.md +++ b/docs/zh-CN/docs/admin-manual/config/be-config.md @@ -1533,3 +1533,16 @@ load tablets from header failed, failed tablets size: xxx, path=xxx * 描述: BE 是否开启使用java-jni,开启后允许 c++ 与 java 之间的相互调用。目前已经支持hudi、java-udf、jdbc、max-compute、paimon、preload、avro * 默认值: true + +#### `group_commit_wal_path` + +* 描述: group commit 存放 WAL 文件的目录,请参考 [Group Commit](../../data-operate/import/import-way/group-commit-manual.md) +* 默认值: 默认在用户配置的`storage_root_path`的各个目录下创建一个名为`wal`的目录。配置示例: + ``` + group_commit_wal_path=/data1/storage/wal;/data2/storage/wal;/data3/storage/wal + ``` + +#### `group_commit_memory_rows_for_max_filter_ratio` + +* 描述: 当 group commit 导入的总行数不高于该值,`max_filter_ratio` 正常工作,否则不工作,请参考 [Group Commit](../../data-operate/import/import-way/group-commit-manual.md) +* 默认值: 10000 diff --git a/docs/zh-CN/docs/data-operate/import/import-way/group-commit-manual.md b/docs/zh-CN/docs/data-operate/import/import-way/group-commit-manual.md new file mode 100644 index 00000000000000..e657b6a6199ae6 --- /dev/null +++ b/docs/zh-CN/docs/data-operate/import/import-way/group-commit-manual.md @@ -0,0 +1,413 @@ +--- +{ + "title": "Group Commit", + "language": "zh-CN" +} +--- + + + +# Group Commit + +Group Commit 不是一种新的导入方式,而是对`INSERT INTO tbl VALUES(...)`、`Stream Load`、`Http Stream`的扩展,大幅提升了高并发小写入的性能。您的应用程序可以直接使用 JDBC 将数据高频写入 Doris,同时通过使用 PreparedStatement 可以获得更高的性能。在日志场景下,您也可以利用 Stream Load 或者 Http Stream 将数据高频写入 Doris。 + +## Group Commit 模式 + +Group Commit 写入有三种模式,分别是: + +* 关闭模式(`off_mode`) + +不开启 Group Commit,保持以上三种导入方式的默认行为。 + +* 同步模式(`sync_mode`) + +Doris 根据负载和表的 `group_commit_interval`属性将多个导入在一个事务提交,事务提交后导入返回。这适用于高并发写入场景,且在导入完成后要求数据立即可见。 + +* 异步模式(`async_mode`) + +Doris 首先将数据写入 WAL (`Write Ahead Log`),然后导入立即返回。Doris 会根据负载和表的`group_commit_interval`属性异步提交数据,提交之后数据可见。为了防止 WAL 占用较大的磁盘空间,单次导入数据量较大时,会自动切换为`sync_mode`。这适用于写入延迟敏感以及高频写入的场景。 + +## Group Commit 使用方式 + +假如表的结构为: +```sql +CREATE TABLE `dt` ( + `id` int(11) NOT NULL, + `name` varchar(50) NULL, + `score` int(11) NULL +) ENGINE=OLAP +DUPLICATE KEY(`id`) +DISTRIBUTED BY HASH(`id`) BUCKETS 1 +PROPERTIES ( + "replication_num" = "1" +); +``` + +### INSERT INTO VALUES + +* 异步模式 +```sql +# 配置session变量开启 group commit (默认为off_mode),开启异步模式 +mysql> set group_commit = async_mode; + +# 这里返回的label是 group_commit 开头的,可以区分出是否使用了 group commit +mysql> insert into dt values(1, 'Bob', 90), (2, 'Alice', 99); +Query OK, 2 rows affected (0.05 sec) +{'label':'group_commit_a145ce07f1c972fc-bd2c54597052a9ad', 'status':'PREPARE', 'txnId':'181508'} + +# 可以看出这个 label, txn_id 和上一个相同,说明是攒到了同一个导入任务中 +mysql> insert into dt(id, name) values(3, 'John'); +Query OK, 1 row affected (0.01 sec) +{'label':'group_commit_a145ce07f1c972fc-bd2c54597052a9ad', 'status':'PREPARE', 'txnId':'181508'} + +# 不能立刻查询到 +mysql> select * from dt; +Empty set (0.01 sec) + +# 10秒后可以查询到,可以通过表属性 group_commit_interval 控制数据可见延迟。 +mysql> select * from dt; ++------+-------+-------+ +| id | name | score | ++------+-------+-------+ +| 1 | Bob | 90 | +| 2 | Alice | 99 | +| 3 | John | NULL | ++------+-------+-------+ +3 rows in set (0.02 sec) +``` + +* 同步模式 +```sql +# 配置session变量开启 group commit (默认为off_mode),开启同步模式 +mysql> set group_commit = sync_mode; + +# 这里返回的 label 是 group_commit 开头的,可以区分出是否谁用了 group commit,导入耗时至少是表属性 group_commit_interval。 +mysql> insert into dt values(4, 'Bob', 90), (5, 'Alice', 99); +Query OK, 2 rows affected (10.06 sec) +{'label':'group_commit_d84ab96c09b60587_ec455a33cb0e9e87', 'status':'PREPARE', 'txnId':'3007', 'query_id':'fc6b94085d704a94-a69bfc9a202e66e2'} + +# 数据可以立刻读出 +mysql> select * from dt; ++------+-------+-------+ +| id | name | score | ++------+-------+-------+ +| 1 | Bob | 90 | +| 2 | Alice | 99 | +| 3 | John | NULL | +| 4 | Bob | 90 | +| 5 | Alice | 99 | ++------+-------+-------+ +5 rows in set (0.03 sec) +``` + +* 关闭模式 +```sql +mysql> set group_commit = off_mode; +``` + +### Stream Load + +假如`data.csv`的内容为: +```sql +6,Amy,60 +7,Ross,98 +``` + +* 异步模式 +```sql +# 导入时在header中增加"group_commit:async_mode"配置 + +curl --location-trusted -u {user}:{passwd} -T data.csv -H "group_commit:async_mode" -H "column_separator:," http://{fe_host}:{http_port}/api/db/dt/_stream_load +{ + "TxnId": 7009, + "Label": "group_commit_c84d2099208436ab_96e33fda01eddba8", + "Comment": "", + "GroupCommit": true, + "Status": "Success", + "Message": "OK", + "NumberTotalRows": 2, + "NumberLoadedRows": 2, + "NumberFilteredRows": 0, + "NumberUnselectedRows": 0, + "LoadBytes": 19, + "LoadTimeMs": 35, + "StreamLoadPutTimeMs": 5, + "ReadDataTimeMs": 0, + "WriteDataTimeMs": 26 +} + +# 返回的GroupCommit为true,说明进入了group commit的流程 +# 返回的Label是group_commit开头的,是真正消费数据的导入关联的label +``` + +* 同步模式 +```sql +# 导入时在header中增加"group_commit:sync_mode"配置 + +curl --location-trusted -u {user}:{passwd} -T data.csv -H "group_commit:sync_mode" -H "column_separator:," http://{fe_host}:{http_port}/api/db/dt/_stream_load +{ + "TxnId": 3009, + "Label": "group_commit_d941bf17f6efcc80_ccf4afdde9881293", + "Comment": "", + "GroupCommit": true, + "Status": "Success", + "Message": "OK", + "NumberTotalRows": 2, + "NumberLoadedRows": 2, + "NumberFilteredRows": 0, + "NumberUnselectedRows": 0, + "LoadBytes": 19, + "LoadTimeMs": 10044, + "StreamLoadPutTimeMs": 4, + "ReadDataTimeMs": 0, + "WriteDataTimeMs": 10038 +} + +# 返回的GroupCommit为true,说明进入了group commit的流程 +# 返回的Label是group_commit开头的,是真正消费数据的导入关联的label +``` + +关于 Stream Load 使用的更多详细语法及最佳实践,请参阅 [Stream Load](stream-load-manual.md)。 + +### Http Stream + +* 异步模式 +```sql +# 导入时在header中增加"group_commit:async_mode"配置 + +curl --location-trusted -u {user}:{passwd} -T data.csv -H "group_commit:async_mode" -H "sql:insert into db.dt select * from http_stream('column_separator'=',', 'format' = 'CSV')" http://{fe_host}:{http_port}/api/_http_stream +{ + "TxnId": 7011, + "Label": "group_commit_3b45c5750d5f15e5_703428e462e1ebb0", + "Comment": "", + "GroupCommit": true, + "Status": "Success", + "Message": "OK", + "NumberTotalRows": 2, + "NumberLoadedRows": 2, + "NumberFilteredRows": 0, + "NumberUnselectedRows": 0, + "LoadBytes": 19, + "LoadTimeMs": 65, + "StreamLoadPutTimeMs": 41, + "ReadDataTimeMs": 47, + "WriteDataTimeMs": 23 +} + +# 返回的GroupCommit为true,说明进入了group commit的流程 +# 返回的Label是group_commit开头的,是真正消费数据的导入关联的label +``` + +* 同步模式 +```sql +# 导入时在header中增加"group_commit:sync_mode"配置 + +curl --location-trusted -u {user}:{passwd} -T data.csv -H "group_commit:sync_mode" -H "sql:insert into db.dt select * from http_stream('column_separator'=',', 'format' = 'CSV')" http://{fe_host}:{http_port}/api/_http_stream +{ + "TxnId": 3011, + "Label": "group_commit_fe470e6752aadbe6_a8f3ac328b02ea91", + "Comment": "", + "GroupCommit": true, + "Status": "Success", + "Message": "OK", + "NumberTotalRows": 2, + "NumberLoadedRows": 2, + "NumberFilteredRows": 0, + "NumberUnselectedRows": 0, + "LoadBytes": 19, + "LoadTimeMs": 10066, + "StreamLoadPutTimeMs": 31, + "ReadDataTimeMs": 32, + "WriteDataTimeMs": 10034 +} + +# 返回的GroupCommit为true,说明进入了group commit的流程 +# 返回的Label是group_commit开头的,是真正消费数据的导入关联的label +``` + +关于 Http Stream 使用的更多详细语法及最佳实践,请参阅 [Stream Load](stream-load-manual.md)。 + +### 使用`PreparedStatement` + +当用户使用 JDBC `insert into values`方式写入时,为了减少 SQL 解析和生成规划的开销, 我们在 FE 端支持了 MySQL 协议的`PreparedStatement`特性。当使用`PreparedStatement`时,SQL 和其导入规划将被缓存到 Session 级别的内存缓存中,后续的导入直接使用缓存对象,降低了 FE 的 CPU 压力。下面是在 JDBC 中使用 PreparedStatement 的例子: + +1. 设置 JDBC url 并在 Server 端开启 prepared statement + +``` +url = jdbc:mysql://127.0.0.1:9030/db?useServerPrepStmts=true +``` + +2. 配置 `group_commit` session变量,有如下两种方式: + +* 通过 JDBC url 设置,增加`sessionVariables=group_commit=async_mode` + +``` +url = jdbc:mysql://127.0.0.1:9030/db?useServerPrepStmts=true&sessionVariables=group_commit=async_mode +``` + +* 通过执行 SQL 设置 + +``` +try (Statement statement = conn.createStatement()) { + statement.execute("SET group_commit = async_mode;"); +} +``` + +3. 使用 `PreparedStatement` + +```java +private static final String JDBC_DRIVER = "com.mysql.jdbc.Driver"; +private static final String URL_PATTERN = "jdbc:mysql://%s:%d/%s?useServerPrepStmts=true"; +private static final String HOST = "127.0.0.1"; +private static final int PORT = 9087; +private static final String DB = "db"; +private static final String TBL = "dt"; +private static final String USER = "root"; +private static final String PASSWD = ""; +private static final int INSERT_BATCH_SIZE = 10; + +private static void groupCommitInsert() throws Exception { + Class.forName(JDBC_DRIVER); + try (Connection conn = DriverManager.getConnection(String.format(URL_PATTERN, HOST, PORT, DB), USER, PASSWD)) { + // set session variable 'group_commit' + try (Statement statement = conn.createStatement()) { + statement.execute("SET group_commit = async_mode;"); + } + + String query = "insert into " + TBL + " values(?, ?, ?)"; + try (PreparedStatement stmt = conn.prepareStatement(query)) { + for (int i = 0; i < INSERT_BATCH_SIZE; i++) { + stmt.setInt(1, i); + stmt.setString(2, "name" + i); + stmt.setInt(3, i + 10); + int result = stmt.executeUpdate(); + System.out.println("rows: " + result); + } + } + } catch (Exception e) { + e.printStackTrace(); + } +} + +private static void groupCommitInsertBatch() throws Exception { + Class.forName(JDBC_DRIVER); + // add rewriteBatchedStatements=true and cachePrepStmts=true in JDBC url + // set session variables by sessionVariables=group_commit=async_mode in JDBC url + try (Connection conn = DriverManager.getConnection( + String.format(URL_PATTERN + "&rewriteBatchedStatements=true&cachePrepStmts=true&sessionVariables=group_commit=async_mode", HOST, PORT, DB), USER, PASSWD)) { + + String query = "insert into " + TBL + " values(?, ?, ?)"; + try (PreparedStatement stmt = conn.prepareStatement(query)) { + for (int j = 0; j < 5; j++) { + // 10 rows per insert + for (int i = 0; i < INSERT_BATCH_SIZE; i++) { + stmt.setInt(1, i); + stmt.setString(2, "name" + i); + stmt.setInt(3, i + 10); + stmt.addBatch(); + } + int[] result = stmt.executeBatch(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } +} +``` + +关于**JDBC**的更多用法,参考[使用Insert方式同步数据](../import-scenes/jdbc-load.md)。 + +## 修改group commit默认提交间隔 + +group commit 的默认提交间隔为 10 秒,用户可以通过修改表的配置,调整 group commit 的提交间隔: + +```sql +# 修改提交间隔为 2 秒 +ALTER TABLE dt SET ("group_commit_interval_ms"="2000"); +``` + +## 使用限制 + +* 当开启了 group commit 模式,系统会判断用户发起的`INSERT INTO VALUES`语句是否符合 group commit 的条件,如果符合,该语句的执行会进入到 group commit 写入中。符合以下条件会自动退化为非 group commit 方式: + + + 事务写入,即`Begin`; `INSERT INTO VALUES`; `COMMIT`方式 + + + 指定 label,即`INSERT INTO dt WITH LABEL {label} VALUES` + + + VALUES 中包含表达式,即`INSERT INTO dt VALUES (1 + 100)` + + + 列更新写入 + + + 表不支持 light schema change + +* 当开启了 group commit 模式,系统会判断用户发起的`Stream Load`和`Http Stream`是否符合 group commit 的条件,如果符合,该导入的执行会进入到 group commit 写入中。符合以下条件的会自动退化为非 group commit 方式: + + + 两阶段提交 + + + 指定 label + + + 列更新写入 + + + 表不支持 light schema change + ++ 对于 unique 模型,由于 group commit 不能保证提交顺序,用户可以配合 sequence 列使用来保证数据一致性 + +* 对`max_filter_ratio`语义的支持 + + * 在默认的导入中,`filter_ratio`是导入完成后,通过失败的行数和总行数计算,决定是否提交本次写入 + + * 在 group commit 模式下,由于多个用户发起的导入会被一个内部导入执行,虽然可以计算出每个导入的`filter_ratio`,但是数据一旦进入内部导入,就只能 commit transaction + + * group commit 模式支持了一定程度的`max_filter_ratio`语义,当导入的总行数不高于`group_commit_memory_rows_for_max_filter_ratio`(配置在`be.conf`中,默认为`10000`行),`max_filter_ratio` 工作 + +* WAL 限制 + + * 对于`async_mode`的 group commit 写入,会把数据写入 WAL。如果内部导入成功,则 WAL 被立刻删除;如果内部导入失败,通过导入 WAL 的方法来恢复数据 + + * 目前 WAL 文件只存储在一个 BE 上,如果这个 BE 磁盘损坏或文件误删等,可能导入丢失部分数据 + + * 当下线 BE 节点时,请使用[`DECOMMISSION`](../../../sql-manual/sql-reference/Cluster-Management-Statements/ALTER-SYSTEM-DECOMMISSION-BACKEND.md)命令,安全下线节点,防止该节点下线前 WAL 文件还没有全部处理完成,导致部分数据丢失 + + * 对于`async_mode`的 group commit 写入,为了保护磁盘空间,当遇到以下情况时,会切换成`sync_mode` + + * 导入数据量过大,即超过 WAL 单目录的80%空间 + + * 不知道数据量的 chunked stream load + + * 导入数据量不大,但磁盘可用空间不足 + + * 当发生重量级 schema change(目前加减列、修改 varchar 长度和重命名列是轻量级 schema change,其它的是重量级 schema change) 时,为了保证 WAL 能够适配表的 schema,在 schema change 最后的 fe 修改元数据阶段,会拒绝 group commit 写入,客户端收到`insert table ${table_name} is blocked on schema change`异常,客户端重试即可 + +## 相关系统配置 + +### BE 配置 + +#### `group_commit_wal_path` + +* 描述: group commit 存放 WAL 文件的目录 +* 默认值: 默认在用户配置的`storage_root_path`的各个目录下创建一个名为`wal`的目录。配置示例: + ``` + group_commit_wal_path=/data1/storage/wal;/data2/storage/wal;/data3/storage/wal + ``` + +#### `group_commit_memory_rows_for_max_filter_ratio` + +* 描述: 当 group commit 导入的总行数不高于该值,`max_filter_ratio` 正常工作,否则不工作 +* 默认值: 10000 From e9e1e2894bfa729613d4e3c48a543dbb54b72452 Mon Sep 17 00:00:00 2001 From: lihangyu <15605149486@163.com> Date: Mon, 25 Dec 2023 11:50:41 +0800 Subject: [PATCH 005/299] [performance](variant) support topn 2phase read for variant column (#28318) [performance](variant) support topn 2phase read for variant column --- .../segment_v2/hierarchical_data_reader.h | 2 +- be/src/olap/rowset/segment_v2/segment.cpp | 67 ++++++++++++- be/src/olap/rowset/segment_v2/segment.h | 14 ++- .../rowset/segment_v2/segment_iterator.cpp | 3 +- .../olap/rowset/segment_v2/segment_iterator.h | 4 +- be/src/olap/tablet_schema.cpp | 14 +++ be/src/olap/tablet_schema.h | 6 ++ be/src/runtime/descriptors.cpp | 4 + be/src/service/internal_service.cpp | 94 ++++++++++++------- be/src/vec/columns/column_object.cpp | 9 +- be/src/vec/exec/scan/new_olap_scanner.cpp | 30 ++---- be/src/vec/exec/scan/new_olap_scanner.h | 1 - be/src/vec/json/path_in_data.cpp | 11 +++ be/src/vec/json/path_in_data.h | 1 + .../org/apache/doris/analysis/SelectStmt.java | 3 +- gensrc/proto/descriptors.proto | 1 + regression-test/data/variant_p0/load.out | 22 ++--- .../data/variant_p0/sql/gh_data.out | 68 ++++++++++---- regression-test/suites/variant_p0/load.groovy | 8 +- .../suites/variant_p0/sql/gh_data.sql | 9 +- 20 files changed, 261 insertions(+), 110 deletions(-) diff --git a/be/src/olap/rowset/segment_v2/hierarchical_data_reader.h b/be/src/olap/rowset/segment_v2/hierarchical_data_reader.h index 58da2a43435ae5..99f926f44cf5e6 100644 --- a/be/src/olap/rowset/segment_v2/hierarchical_data_reader.h +++ b/be/src/olap/rowset/segment_v2/hierarchical_data_reader.h @@ -229,7 +229,7 @@ class ExtractReader : public ColumnIterator { private: Status extract_to(vectorized::MutableColumnPtr& dst, size_t nrows); - const TabletColumn& _col; + TabletColumn _col; // may shared among different column iterators std::unique_ptr _root_reader; }; diff --git a/be/src/olap/rowset/segment_v2/segment.cpp b/be/src/olap/rowset/segment_v2/segment.cpp index e3d1ce0c599523..de2593447c5175 100644 --- a/be/src/olap/rowset/segment_v2/segment.cpp +++ b/be/src/olap/rowset/segment_v2/segment.cpp @@ -61,10 +61,12 @@ #include "util/slice.h" // Slice #include "vec/columns/column.h" #include "vec/common/string_ref.h" +#include "vec/core/field.h" #include "vec/data_types/data_type.h" #include "vec/data_types/data_type_factory.hpp" #include "vec/data_types/data_type_nullable.h" #include "vec/data_types/data_type_object.h" +#include "vec/json/path_in_data.h" #include "vec/olap/vgeneric_iterators.h" namespace doris { @@ -332,17 +334,18 @@ Status Segment::_load_index_impl() { // Return the storage datatype of related column to field. // Return nullptr meaning no such storage infomation for this column -vectorized::DataTypePtr Segment::get_data_type_of(const Field& field, bool ignore_children) const { +vectorized::DataTypePtr Segment::get_data_type_of(vectorized::PathInData path, bool is_nullable, + bool ignore_children) const { // Path has higher priority - if (!field.path().empty()) { - auto node = _sub_column_tree.find_leaf(field.path()); + if (!path.empty()) { + auto node = _sub_column_tree.find_leaf(path); if (node) { if (ignore_children || node->children.empty()) { return node->data.file_column_type; } } // it contains children or column missing in storage, so treat it as variant - return field.is_nullable() + return is_nullable ? vectorized::make_nullable(std::make_shared()) : std::make_shared(); } @@ -686,7 +689,8 @@ Status Segment::read_key_by_rowid(uint32_t row_id, std::string* key) { bool Segment::same_with_storage_type(int32_t cid, const Schema& schema, bool ignore_children) const { - auto file_column_type = get_data_type_of(*schema.column(cid), ignore_children); + auto file_column_type = get_data_type_of(schema.column(cid)->path(), + schema.column(cid)->is_nullable(), ignore_children); auto expected_type = Schema::get_data_type_ptr(*schema.column(cid)); #ifndef NDEBUG if (file_column_type && !file_column_type->equals(*expected_type)) { @@ -700,5 +704,58 @@ bool Segment::same_with_storage_type(int32_t cid, const Schema& schema, return same; } +Status Segment::seek_and_read_by_rowid(const TabletSchema& schema, SlotDescriptor* slot, + uint32_t row_id, vectorized::MutableColumnPtr& result, + OlapReaderStatistics& stats, + std::unique_ptr& iterator_hint) { + StorageReadOptions storage_read_opt; + storage_read_opt.io_ctx.reader_type = ReaderType::READER_QUERY; + segment_v2::ColumnIteratorOptions opt { + .use_page_cache = !config::disable_storage_page_cache, + .file_reader = file_reader().get(), + .stats = &stats, + .io_ctx = io::IOContext {.reader_type = ReaderType::READER_QUERY}, + }; + std::vector single_row_loc {row_id}; + if (!slot->column_paths().empty()) { + vectorized::PathInData path(schema.column_by_uid(slot->col_unique_id()).name_lower_case(), + slot->column_paths()); + auto storage_type = get_data_type_of(path, slot->is_nullable(), false); + vectorized::MutableColumnPtr file_storage_column = storage_type->create_column(); + DCHECK(storage_type != nullptr); + TabletColumn column = TabletColumn::create_materialized_variant_column( + schema.column_by_uid(slot->col_unique_id()).name_lower_case(), slot->column_paths(), + slot->col_unique_id()); + if (iterator_hint == nullptr) { + RETURN_IF_ERROR(new_column_iterator(column, &iterator_hint, &storage_read_opt)); + RETURN_IF_ERROR(iterator_hint->init(opt)); + } + RETURN_IF_ERROR( + iterator_hint->read_by_rowids(single_row_loc.data(), 1, file_storage_column)); + // iterator_hint.reset(nullptr); + // Get it's inner field, for JSONB case + vectorized::Field field = remove_nullable(storage_type)->get_default(); + file_storage_column->get(0, field); + result->insert(field); + } else { + int index = (slot->col_unique_id() >= 0) ? schema.field_index(slot->col_unique_id()) + : schema.field_index(slot->col_name()); + if (index < 0) { + std::stringstream ss; + ss << "field name is invalid. field=" << slot->col_name() + << ", field_name_to_index=" << schema.get_all_field_names(); + return Status::InternalError(ss.str()); + } + storage_read_opt.io_ctx.reader_type = ReaderType::READER_QUERY; + if (iterator_hint == nullptr) { + RETURN_IF_ERROR( + new_column_iterator(schema.column(index), &iterator_hint, &storage_read_opt)); + RETURN_IF_ERROR(iterator_hint->init(opt)); + } + RETURN_IF_ERROR(iterator_hint->read_by_rowids(single_row_loc.data(), 1, result)); + } + return Status::OK(); +} + } // namespace segment_v2 } // namespace doris diff --git a/be/src/olap/rowset/segment_v2/segment.h b/be/src/olap/rowset/segment_v2/segment.h index 42b7fc83ac0cae..47ccab71be68a2 100644 --- a/be/src/olap/rowset/segment_v2/segment.h +++ b/be/src/olap/rowset/segment_v2/segment.h @@ -39,11 +39,14 @@ #include "olap/rowset/segment_v2/page_handle.h" #include "olap/schema.h" #include "olap/tablet_schema.h" +#include "runtime/descriptors.h" #include "util/once.h" #include "util/slice.h" +#include "vec/columns/column.h" #include "vec/columns/subcolumn_tree.h" #include "vec/data_types/data_type.h" #include "vec/data_types/data_type_nullable.h" +#include "vec/json/path_in_data.h" namespace doris { namespace vectorized { @@ -123,6 +126,10 @@ class Segment : public std::enable_shared_from_this { Status read_key_by_rowid(uint32_t row_id, std::string* key); + Status seek_and_read_by_rowid(const TabletSchema& schema, SlotDescriptor* slot, uint32_t row_id, + vectorized::MutableColumnPtr& result, OlapReaderStatistics& stats, + std::unique_ptr& iterator_hint); + Status load_index(); Status load_pk_index_and_bf(); @@ -146,7 +153,8 @@ class Segment : public std::enable_shared_from_this { // ignore_chidren set to false will treat field as variant // when it contains children with field paths. // nullptr will returned if storage type does not contains such column - std::shared_ptr get_data_type_of(const Field& filed, + std::shared_ptr get_data_type_of(vectorized::PathInData path, + bool is_nullable, bool ignore_children) const; // Check is schema read type equals storage column type @@ -157,8 +165,8 @@ class Segment : public std::enable_shared_from_this { bool can_apply_predicate_safely(int cid, Predicate* pred, const Schema& schema, ReaderType read_type) const { const Field* col = schema.column(cid); - vectorized::DataTypePtr storage_column_type = - get_data_type_of(*col, read_type != ReaderType::READER_QUERY); + vectorized::DataTypePtr storage_column_type = get_data_type_of( + col->path(), col->is_nullable(), read_type != ReaderType::READER_QUERY); if (storage_column_type == nullptr) { // Default column iterator return true; diff --git a/be/src/olap/rowset/segment_v2/segment_iterator.cpp b/be/src/olap/rowset/segment_v2/segment_iterator.cpp index 483e0e502df8da..f85ed5abce5982 100644 --- a/be/src/olap/rowset/segment_v2/segment_iterator.cpp +++ b/be/src/olap/rowset/segment_v2/segment_iterator.cpp @@ -342,7 +342,8 @@ Status SegmentIterator::_init_impl(const StorageReadOptions& opts) { const Field* col = _schema->column(i); if (col) { auto storage_type = _segment->get_data_type_of( - *col, _opts.io_ctx.reader_type != ReaderType::READER_QUERY); + col->path(), col->is_nullable(), + _opts.io_ctx.reader_type != ReaderType::READER_QUERY); if (storage_type == nullptr) { storage_type = vectorized::DataTypeFactory::instance().create_data_type(*col); } diff --git a/be/src/olap/rowset/segment_v2/segment_iterator.h b/be/src/olap/rowset/segment_v2/segment_iterator.h index 3fca034a18fd39..c953e0b9bcc8ba 100644 --- a/be/src/olap/rowset/segment_v2/segment_iterator.h +++ b/be/src/olap/rowset/segment_v2/segment_iterator.h @@ -251,8 +251,8 @@ class SegmentIterator : public RowwiseIterator { if (block_cid >= block->columns()) { continue; } - vectorized::DataTypePtr storage_type = - _segment->get_data_type_of(*_schema->column(cid), false); + vectorized::DataTypePtr storage_type = _segment->get_data_type_of( + _schema->column(cid)->path(), _schema->column(cid)->is_nullable(), false); if (storage_type && !storage_type->equals(*block->get_by_position(block_cid).type)) { // Do additional cast vectorized::MutableColumnPtr tmp = storage_type->create_column(); diff --git a/be/src/olap/tablet_schema.cpp b/be/src/olap/tablet_schema.cpp index 63682d8c7128f8..78e7e938caae5f 100644 --- a/be/src/olap/tablet_schema.cpp +++ b/be/src/olap/tablet_schema.cpp @@ -554,6 +554,20 @@ void TabletColumn::init_from_pb(const ColumnPB& column) { } } +TabletColumn TabletColumn::create_materialized_variant_column(const std::string& root, + const std::vector& paths, + int32_t parent_unique_id) { + TabletColumn subcol; + subcol.set_type(FieldType::OLAP_FIELD_TYPE_VARIANT); + subcol.set_is_nullable(true); + subcol.set_unique_id(-1); + subcol.set_parent_unique_id(parent_unique_id); + vectorized::PathInData path(root, paths); + subcol.set_path_info(path); + subcol.set_name(path.get_path()); + return subcol; +} + void TabletColumn::to_schema_pb(ColumnPB* column) const { column->set_unique_id(_unique_id); column->set_name(_col_name); diff --git a/be/src/olap/tablet_schema.h b/be/src/olap/tablet_schema.h index 3f56568080019b..21970b5cbac5eb 100644 --- a/be/src/olap/tablet_schema.h +++ b/be/src/olap/tablet_schema.h @@ -36,6 +36,7 @@ #include "gutil/stringprintf.h" #include "olap/olap_common.h" #include "runtime/define_primitive_type.h" +#include "runtime/descriptors.h" #include "util/string_util.h" #include "vec/aggregate_functions/aggregate_function.h" #include "vec/common/string_utils/string_utils.h" @@ -91,6 +92,11 @@ class TabletColumn { _type == FieldType::OLAP_FIELD_TYPE_QUANTILE_STATE || _type == FieldType::OLAP_FIELD_TYPE_AGG_STATE; } + // Such columns are not exist in frontend schema info, so we need to + // add them into tablet_schema for later column indexing. + static TabletColumn create_materialized_variant_column(const std::string& root, + const std::vector& paths, + int32_t parent_unique_id); bool has_default_value() const { return _has_default_value; } std::string default_value() const { return _default_value; } size_t length() const { return _length; } diff --git a/be/src/runtime/descriptors.cpp b/be/src/runtime/descriptors.cpp index 721950abbee741..09113f85eea1d3 100644 --- a/be/src/runtime/descriptors.cpp +++ b/be/src/runtime/descriptors.cpp @@ -85,6 +85,7 @@ SlotDescriptor::SlotDescriptor(const PSlotDescriptor& pdesc) _is_materialized(pdesc.is_materialized()), _is_key(pdesc.is_key()), _need_materialize(true), + _column_paths(pdesc.column_paths().begin(), pdesc.column_paths().end()), _is_auto_increment(pdesc.is_auto_increment()) {} void SlotDescriptor::to_protobuf(PSlotDescriptor* pslot) const { @@ -103,6 +104,9 @@ void SlotDescriptor::to_protobuf(PSlotDescriptor* pslot) const { pslot->set_is_key(_is_key); pslot->set_is_auto_increment(_is_auto_increment); pslot->set_col_type(_col_type); + for (const std::string& path : _column_paths) { + pslot->add_column_paths(path); + } } vectorized::MutableColumnPtr SlotDescriptor::get_empty_mutable_column() const { diff --git a/be/src/service/internal_service.cpp b/be/src/service/internal_service.cpp index 8d352ba1d53186..bca48737bf508a 100644 --- a/be/src/service/internal_service.cpp +++ b/be/src/service/internal_service.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,7 @@ #include #include #include +#include #include #include @@ -73,6 +75,7 @@ #include "olap/rowset/segment_v2/common.h" #include "olap/rowset/segment_v2/inverted_index_desc.h" #include "olap/rowset/segment_v2/segment.h" +#include "olap/rowset/segment_v2/segment_iterator.h" #include "olap/segment_loader.h" #include "olap/storage_engine.h" #include "olap/tablet.h" @@ -1711,6 +1714,38 @@ auto scope_timer_run(Func fn, int64_t* cost) -> decltype(fn()) { return res; } +struct IteratorKey { + int64_t tablet_id; + RowsetId rowset_id; + uint64_t segment_id; + int slot_id; + + // unordered map std::equal_to + bool operator==(const IteratorKey& rhs) const { + return tablet_id == rhs.tablet_id && rowset_id == rhs.rowset_id && + segment_id == rhs.segment_id && slot_id == rhs.slot_id; + } +}; + +struct HashOfIteratorKey { + size_t operator()(const IteratorKey& key) const { + size_t seed = 0; + seed = HashUtil::hash64(&key.tablet_id, sizeof(key.tablet_id), seed); + seed = HashUtil::hash64(&key.rowset_id.hi, sizeof(key.rowset_id.hi), seed); + seed = HashUtil::hash64(&key.rowset_id.mi, sizeof(key.rowset_id.mi), seed); + seed = HashUtil::hash64(&key.rowset_id.lo, sizeof(key.rowset_id.lo), seed); + seed = HashUtil::hash64(&key.segment_id, sizeof(key.segment_id), seed); + seed = HashUtil::hash64(&key.slot_id, sizeof(key.slot_id), seed); + return seed; + } +}; + +struct IteratorItem { + std::unique_ptr iterator; + // for holding the reference of segment to avoid use after release + SegmentSharedPtr segment; +}; + Status PInternalServiceImpl::_multi_get(const PMultiGetRequest& request, PMultiGetResponse* response) { OlapReaderStatistics stats; @@ -1735,6 +1770,7 @@ Status PInternalServiceImpl::_multi_get(const PMultiGetRequest& request, full_read_schema.append_column(TabletColumn(column_pb)); } + std::unordered_map iterator_map; // read row by row for (size_t i = 0; i < request.row_locs_size(); ++i) { const auto& row_loc = request.row_locs(i); @@ -1773,8 +1809,8 @@ Status PInternalServiceImpl::_multi_get(const PMultiGetRequest& request, }, &acquire_segments_ms)); // find segment - auto it = std::find_if(segment_cache.get_segments().begin(), - segment_cache.get_segments().end(), + auto it = std::find_if(segment_cache.get_segments().cbegin(), + segment_cache.get_segments().cend(), [&row_loc](const segment_v2::SegmentSharedPtr& seg) { return seg->id() == row_loc.segment_id(); }); @@ -1802,37 +1838,28 @@ Status PInternalServiceImpl::_multi_get(const PMultiGetRequest& request, if (result_block.is_empty_column()) { result_block = vectorized::Block(desc.slots(), request.row_locs().size()); } + VLOG_DEBUG << "Read row location " + << fmt::format("{}, {}, {}, {}", row_location.tablet_id, + row_location.row_location.rowset_id.to_string(), + row_location.row_location.segment_id, + row_location.row_location.row_id); for (int x = 0; x < desc.slots().size(); ++x) { - int index = -1; - if (desc.slots()[x]->col_unique_id() >= 0) { - // light sc enabled - index = full_read_schema.field_index(desc.slots()[x]->col_unique_id()); - } else { - index = full_read_schema.field_index(desc.slots()[x]->col_name()); - } - if (index < 0) { - std::stringstream ss; - ss << "field name is invalid. field=" << desc.slots()[x]->col_name() - << ", field_name_to_index=" << full_read_schema.get_all_field_names(); - return Status::InternalError(ss.str()); - } - std::unique_ptr column_iterator; + auto row_id = static_cast(row_loc.ordinal_id()); vectorized::MutableColumnPtr column = result_block.get_by_position(x).column->assume_mutable(); - StorageReadOptions storage_read_opt; - storage_read_opt.io_ctx.reader_type = ReaderType::READER_QUERY; - RETURN_IF_ERROR(segment->new_column_iterator(full_read_schema.column(index), - &column_iterator, &storage_read_opt)); - segment_v2::ColumnIteratorOptions opt { - .use_page_cache = !config::disable_storage_page_cache, - .file_reader = segment->file_reader().get(), - .stats = &stats, - .io_ctx = io::IOContext {.reader_type = ReaderType::READER_QUERY}, - }; - static_cast(column_iterator->init(opt)); - std::vector single_row_loc { - static_cast(row_loc.ordinal_id())}; - RETURN_IF_ERROR(column_iterator->read_by_rowids(single_row_loc.data(), 1, column)); + IteratorKey iterator_key {.tablet_id = tablet->tablet_id(), + .rowset_id = rowset_id, + .segment_id = row_loc.segment_id(), + .slot_id = desc.slots()[x]->id()}; + IteratorItem& iterator_item = iterator_map[iterator_key]; + if (iterator_item.segment == nullptr) { + // hold the reference + iterator_map[iterator_key].segment = segment; + } + segment = iterator_item.segment; + RETURN_IF_ERROR(segment->seek_and_read_by_rowid(full_read_schema, desc.slots()[x], + row_id, column, stats, + iterator_item.iterator)); } } // serialize block if not empty @@ -1852,11 +1879,13 @@ Status PInternalServiceImpl::_multi_get(const PMultiGetRequest& request, "hit_cached_pages:{}, total_pages_read:{}, compressed_bytes_read:{}, " "io_latency:{}ns, " "uncompressed_bytes_read:{}," + "bytes_read:{}," "acquire_tablet_ms:{}, acquire_rowsets_ms:{}, acquire_segments_ms:{}, " "lookup_row_data_ms:{}", stats.cached_pages_num, stats.total_pages_num, stats.compressed_bytes_read, - stats.io_ns, stats.uncompressed_bytes_read, acquire_tablet_ms, - acquire_rowsets_ms, acquire_segments_ms, lookup_row_data_ms); + stats.io_ns, stats.uncompressed_bytes_read, stats.bytes_read, + acquire_tablet_ms, acquire_rowsets_ms, acquire_segments_ms, + lookup_row_data_ms); return Status::OK(); } @@ -1865,6 +1894,7 @@ void PInternalServiceImpl::multiget_data(google::protobuf::RpcController* contro PMultiGetResponse* response, google::protobuf::Closure* done) { bool ret = _light_work_pool.try_offer([request, response, done, this]() { + signal::set_signal_task_id(request->query_id()); // multi get data by rowid MonotonicStopWatch watch; watch.start(); diff --git a/be/src/vec/columns/column_object.cpp b/be/src/vec/columns/column_object.cpp index f44fc62ed982ea..782a2967f03230 100644 --- a/be/src/vec/columns/column_object.cpp +++ b/be/src/vec/columns/column_object.cpp @@ -645,11 +645,12 @@ void ColumnObject::for_each_subcolumn(ColumnCallback callback) { } void ColumnObject::insert_from(const IColumn& src, size_t n) { - const auto& src_v = assert_cast(src); + const auto* src_v = check_and_get_column(src); // optimize when src and this column are scalar variant, since try_insert is inefficiency - if (src_v.is_scalar_variant() && is_scalar_variant() && - src_v.get_root_type()->equals(*get_root_type()) && src_v.is_finalized() && is_finalized()) { - assert_cast(*get_root()).insert_from(*src_v.get_root(), n); + if (src_v != nullptr && src_v->is_scalar_variant() && is_scalar_variant() && + src_v->get_root_type()->equals(*get_root_type()) && src_v->is_finalized() && + is_finalized()) { + assert_cast(*get_root()).insert_from(*src_v->get_root(), n); ++num_rows; return; } diff --git a/be/src/vec/exec/scan/new_olap_scanner.cpp b/be/src/vec/exec/scan/new_olap_scanner.cpp index c6e401f3f5484b..5566abec3ab383 100644 --- a/be/src/vec/exec/scan/new_olap_scanner.cpp +++ b/be/src/vec/exec/scan/new_olap_scanner.cpp @@ -58,6 +58,7 @@ #include "vec/exec/scan/new_olap_scan_node.h" #include "vec/exec/scan/vscan_node.h" #include "vec/exprs/vexpr_context.h" +#include "vec/json/path_in_data.h" #include "vec/olap/block_reader.h" namespace doris::vectorized { @@ -411,16 +412,6 @@ Status NewOlapScanner::_init_tablet_reader_params( return Status::OK(); } -vectorized::PathInData NewOlapScanner::_build_path(SlotDescriptor* slot, - const std::string& root_name) { - PathInDataBuilder path_builder; - path_builder.append(root_name, false); - for (const std::string& path : slot->column_paths()) { - path_builder.append(path, false); - } - return path_builder.build(); -} - Status NewOlapScanner::_init_variant_columns() { auto& tablet_schema = _tablet_reader_params.tablet_schema; // Parent column has path info to distinction from each other @@ -434,16 +425,10 @@ Status NewOlapScanner::_init_variant_columns() { if (slot->type().is_variant_type()) { // Such columns are not exist in frontend schema info, so we need to // add them into tablet_schema for later column indexing. - TabletColumn subcol; - subcol.set_type(FieldType::OLAP_FIELD_TYPE_VARIANT); - subcol.set_is_nullable(true); - subcol.set_unique_id(-1); - subcol.set_parent_unique_id(slot->col_unique_id()); - PathInData path = _build_path( - slot, tablet_schema->column_by_uid(slot->col_unique_id()).name_lower_case()); - subcol.set_path_info(path); - subcol.set_name(path.get_path()); - if (tablet_schema->field_index(path) < 0) { + TabletColumn subcol = TabletColumn::create_materialized_variant_column( + tablet_schema->column_by_uid(slot->col_unique_id()).name_lower_case(), + slot->column_paths(), slot->col_unique_id()); + if (tablet_schema->field_index(subcol.path_info()) < 0) { tablet_schema->append_column(subcol, TabletSchema::ColumnType::VARIANT); } } @@ -465,8 +450,9 @@ Status NewOlapScanner::_init_return_columns() { int32_t index = 0; auto& tablet_schema = _tablet_reader_params.tablet_schema; if (slot->type().is_variant_type()) { - index = tablet_schema->field_index(_build_path( - slot, tablet_schema->column_by_uid(slot->col_unique_id()).name_lower_case())); + index = tablet_schema->field_index(PathInData( + tablet_schema->column_by_uid(slot->col_unique_id()).name_lower_case(), + slot->column_paths())); } else { index = slot->col_unique_id() >= 0 ? tablet_schema->field_index(slot->col_unique_id()) : tablet_schema->field_index(slot->col_name()); diff --git a/be/src/vec/exec/scan/new_olap_scanner.h b/be/src/vec/exec/scan/new_olap_scanner.h index ae094b84f0390e..9eab71f3c34eb2 100644 --- a/be/src/vec/exec/scan/new_olap_scanner.h +++ b/be/src/vec/exec/scan/new_olap_scanner.h @@ -93,7 +93,6 @@ class NewOlapScanner : public VScanner { const std::vector& function_filters); [[nodiscard]] Status _init_return_columns(); - vectorized::PathInData _build_path(SlotDescriptor* slot, const std::string& root_name); [[nodiscard]] Status _init_variant_columns(); std::vector _key_ranges; diff --git a/be/src/vec/json/path_in_data.cpp b/be/src/vec/json/path_in_data.cpp index 1c02febd446946..ae91b44499493c 100644 --- a/be/src/vec/json/path_in_data.cpp +++ b/be/src/vec/json/path_in_data.cpp @@ -46,11 +46,22 @@ PathInData::PathInData(const PathInData& other) : path(other.path) { build_parts(other.get_parts()); } +PathInData::PathInData(const std::string& root, const std::vector& paths) { + PathInDataBuilder path_builder; + path_builder.append(root, false); + for (const std::string& path : paths) { + path_builder.append(path, false); + } + build_path(path_builder.get_parts()); + build_parts(path_builder.get_parts()); +} + PathInData::PathInData(const std::vector& paths) { PathInDataBuilder path_builder; for (size_t i = 0; i < paths.size(); ++i) { path_builder.append(paths[i], false); } + build_path(path_builder.get_parts()); build_parts(path_builder.get_parts()); } diff --git a/be/src/vec/json/path_in_data.h b/be/src/vec/json/path_in_data.h index 6531a8bfc6a8a3..267ab1fab3f761 100644 --- a/be/src/vec/json/path_in_data.h +++ b/be/src/vec/json/path_in_data.h @@ -61,6 +61,7 @@ class PathInData { explicit PathInData(std::string_view path_); explicit PathInData(const Parts& parts_); explicit PathInData(const std::vector& paths); + explicit PathInData(const std::string& root, const std::vector& paths); PathInData(const PathInData& other); PathInData& operator=(const PathInData& other); static UInt128 get_parts_hash(const Parts& parts_); diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java index 893d4c93239f77..dc3c2f52d812ca 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java @@ -829,8 +829,7 @@ public boolean checkEnableTwoPhaseRead(Analyzer analyzer) { LOG.debug("only support duplicate key or MOW model"); return false; } - if (!olapTable.getEnableLightSchemaChange() || !Strings.isNullOrEmpty(olapTable.getStoragePolicy()) - || olapTable.hasVariantColumns()) { + if (!olapTable.getEnableLightSchemaChange()) { return false; } if (getOrderByElements() != null) { diff --git a/gensrc/proto/descriptors.proto b/gensrc/proto/descriptors.proto index 270199cc0204c7..8762a78c0f4ed1 100644 --- a/gensrc/proto/descriptors.proto +++ b/gensrc/proto/descriptors.proto @@ -38,6 +38,7 @@ message PSlotDescriptor { optional bool is_key = 12; optional bool is_auto_increment = 13; optional int32 col_type = 14 [default = 0]; + repeated string column_paths = 15; }; message PTupleDescriptor { diff --git a/regression-test/data/variant_p0/load.out b/regression-test/data/variant_p0/load.out index e48aafe2e0d423..7def94d2fadeef 100644 --- a/regression-test/data/variant_p0/load.out +++ b/regression-test/data/variant_p0/load.out @@ -143,7 +143,7 @@ [123] -- !sql_25 -- -50000 55000.00000000013 6150000 +50000 55000.000000002256 6150000 -- !sql_26 -- 5000 @@ -227,16 +227,16 @@ \N \N \N -- !sql_36_2 -- -7702 {"payload":{"commits":[{"sha":"348743fdce27d3f3c97e366381b1b7b371fc4510","author":{"email":"9b7a0973fc99779f7e1822eb7336ff5d28bd2653@users.noreply.github.com","name":"Łukasz Magiera"},"message":"Create README.md","distinct":true,"url":"https://api.github.com/repos/magik6k/BitBuffer/commits/348743fdce27d3f3c97e366381b1b7b371fc4510"}],"before":"4e150694dacd35e7d5cda4e9f6a2aedb1d35db36","head":"348743fdce27d3f3c97e366381b1b7b371fc4510","size":1,"push_id":536752118,"ref":"refs/heads/master","distinct_size":1},"created_at":"2015-01-01T00:59:58Z","id":"2489395761","public":1,"actor":{"gravatar_id":"","url":"https://api.github.com/users/magik6k","id":3867941,"login":"magik6k","avatar_url":"https://avatars.githubusercontent.com/u/3867941?"},"repo":{"url":"https://api.github.com/repos/magik6k/BitBuffer","id":28677864,"name":"magik6k/BitBuffer"},"type":"PushEvent"} -7701 {"payload":{"pages":[{"page_name":"Android Development Basics","title":"Android Development Basics","summary":null,"action":"edited","sha":"e4e947a4f29b1a06f560ac1e62bd3bf183e434b6","html_url":"https://github.com/wllmtrng/wllmtrng.github.io/wiki/Android-Development-Basics"}]},"created_at":"2015-01-01T00:59:58Z","id":"2489395760","public":1,"actor":{"gravatar_id":"","url":"https://api.github.com/users/wllmtrng","id":1335855,"login":"wllmtrng","avatar_url":"https://avatars.githubusercontent.com/u/1335855?"},"repo":{"url":"https://api.github.com/repos/wllmtrng/wllmtrng.github.io","id":18089434,"name":"wllmtrng/wllmtrng.github.io"},"type":"GollumEvent"} -7700 {"payload":{"forkee":{"svn_url":"https://github.com/WangXYZ/TBC","pushed_at":"2014-12-24T18:26:11Z","issues_url":"https://api.github.com/repos/WangXYZ/TBC/issues{/number}","events_url":"https://api.github.com/repos/WangXYZ/TBC/events","labels_url":"https://api.github.com/repos/WangXYZ/TBC/labels{/name}","releases_url":"https://api.github.com/repos/WangXYZ/TBC/releases{/id}","keys_url":"https://api.github.com/repos/WangXYZ/TBC/keys{/key_id}","stargazers_url":"https://api.github.com/repos/WangXYZ/TBC/stargazers","has_downloads":1,"commits_url":"https://api.github.com/repos/WangXYZ/TBC/commits{/sha}","downloads_url":"https://api.github.com/repos/WangXYZ/TBC/downloads","default_branch":"master","open_issues":0,"size":9207,"forks_count":0,"id":28678211,"has_wiki":0,"owner":{"starred_url":"https://api.github.com/users/WangXYZ/starred{/owner}{/repo}","url":"https://api.github.com/users/WangXYZ","repos_url":"https://api.github.com/users/WangXYZ/repos","events_url":"https://api.github.com/users/WangXYZ/events{/privacy}","login":"WangXYZ","avatar_url":"https://avatars.githubusercontent.com/u/8252171?v=3","gravatar_id":"","site_admin":0,"html_url":"https://github.com/WangXYZ","received_events_url":"https://api.github.com/users/WangXYZ/received_events","followers_url":"https://api.github.com/users/WangXYZ/followers","following_url":"https://api.github.com/users/WangXYZ/following{/other_user}","gists_url":"https://api.github.com/users/WangXYZ/gists{/gist_id}","type":"Organization","subscriptions_url":"https://api.github.com/users/WangXYZ/subscriptions","organizations_url":"https://api.github.com/users/WangXYZ/orgs","id":8252171},"languages_url":"https://api.github.com/repos/WangXYZ/TBC/languages","git_tags_url":"https://api.github.com/repos/WangXYZ/TBC/git/tags{/sha}","archive_url":"https://api.github.com/repos/WangXYZ/TBC/{archive_format}{/ref}","git_refs_url":"https://api.github.com/repos/WangXYZ/TBC/git/refs{/sha}","trees_url":"https://api.github.com/repos/WangXYZ/TBC/git/trees{/sha}","updated_at":"2014-09-10T17:41:40Z","description":"ACID Scripts for CMaNGOS TBC","forks_url":"https://api.github.com/repos/WangXYZ/TBC/forks","hooks_url":"https://api.github.com/repos/WangXYZ/TBC/hooks","created_at":"2015-01-01T00:59:57Z","fork":1,"forks":0,"subscription_url":"https://api.github.com/repos/WangXYZ/TBC/subscription","compare_url":"https://api.github.com/repos/WangXYZ/TBC/compare/{base}...{head}","url":"https://api.github.com/repos/WangXYZ/TBC","collaborators_url":"https://api.github.com/repos/WangXYZ/TBC/collaborators{/collaborator}","statuses_url":"https://api.github.com/repos/WangXYZ/TBC/statuses/{sha}","comments_url":"https://api.github.com/repos/WangXYZ/TBC/comments{/number}","blobs_url":"https://api.github.com/repos/WangXYZ/TBC/git/blobs{/sha}","html_url":"https://github.com/WangXYZ/TBC","watchers_count":0,"has_issues":0,"has_pages":0,"contents_url":"https://api.github.com/repos/WangXYZ/TBC/contents/{+path}","issue_events_url":"https://api.github.com/repos/WangXYZ/TBC/issues/events{/number}","ssh_url":"git@github.com:WangXYZ/TBC.git","tags_url":"https://api.github.com/repos/WangXYZ/TBC/tags","name":"TBC","issue_comment_url":"https://api.github.com/repos/WangXYZ/TBC/issues/comments/{number}","git_url":"git://github.com/WangXYZ/TBC.git","subscribers_url":"https://api.github.com/repos/WangXYZ/TBC/subscribers","clone_url":"https://github.com/WangXYZ/TBC.git","notifications_url":"https://api.github.com/repos/WangXYZ/TBC/notifications{?since,all,participating}","full_name":"WangXYZ/TBC","private":0,"teams_url":"https://api.github.com/repos/WangXYZ/TBC/teams","milestones_url":"https://api.github.com/repos/WangXYZ/TBC/milestones{/number}","public":1,"git_commits_url":"https://api.github.com/repos/WangXYZ/TBC/git/commits{/sha}","open_issues_count":0,"watchers":0,"contributors_url":"https://api.github.com/repos/WangXYZ/TBC/contributors","branches_url":"https://api.github.com/repos/WangXYZ/TBC/branches{/branch}","stargazers_count":0,"pulls_url":"https://api.github.com/repos/WangXYZ/TBC/pulls{/number}","merges_url":"https://api.github.com/repos/WangXYZ/TBC/merges","assignees_url":"https://api.github.com/repos/WangXYZ/TBC/assignees{/user}"}},"created_at":"2015-01-01T00:59:57Z","id":"2489395755","org":{"gravatar_id":"","url":"https://api.github.com/orgs/ACID-Scripts","id":8674587,"login":"ACID-Scripts","avatar_url":"https://avatars.githubusercontent.com/u/8674587?"},"public":1,"actor":{"gravatar_id":"","url":"https://api.github.com/users/WangXYZ","id":8252171,"login":"WangXYZ","avatar_url":"https://avatars.githubusercontent.com/u/8252171?"},"repo":{"url":"https://api.github.com/repos/ACID-Scripts/TBC","id":23724137,"name":"ACID-Scripts/TBC"},"type":"ForkEvent"} -7699 {"payload":{"description":"Game using the MS Kinect that scans the user and creates a 3D version of them.","ref":"master","ref_type":"branch","pusher_type":"user","master_branch":"master"},"created_at":"2015-01-01T00:59:57Z","id":"2489395752","public":1,"actor":{"gravatar_id":"","url":"https://api.github.com/users/chrisjimenez","id":3580593,"login":"chrisjimenez","avatar_url":"https://avatars.githubusercontent.com/u/3580593?"},"repo":{"url":"https://api.github.com/repos/chrisjimenez/IpsePuppet","id":28678128,"name":"chrisjimenez/IpsePuppet"},"type":"CreateEvent"} -7698 {"payload":{"issue":{"url":"https://api.github.com/repos/antonioortegajr/beerfind.me/issues/12","created_at":"2015-01-01T00:59:56Z","body":"The bee that are being searched for currently should also have logos and descriptions from the API.","events_url":"https://api.github.com/repos/antonioortegajr/beerfind.me/issues/12/events","labels_url":"https://api.github.com/repos/antonioortegajr/beerfind.me/issues/12/labels{/name}","locked":0,"state":"open","comments_url":"https://api.github.com/repos/antonioortegajr/beerfind.me/issues/12/comments","user":{"starred_url":"https://api.github.com/users/antonioortegajr/starred{/owner}{/repo}","url":"https://api.github.com/users/antonioortegajr","repos_url":"https://api.github.com/users/antonioortegajr/repos","events_url":"https://api.github.com/users/antonioortegajr/events{/privacy}","login":"antonioortegajr","avatar_url":"https://avatars.githubusercontent.com/u/6744175?v=3","gravatar_id":"","site_admin":0,"html_url":"https://github.com/antonioortegajr","received_events_url":"https://api.github.com/users/antonioortegajr/received_events","followers_url":"https://api.github.com/users/antonioortegajr/followers","following_url":"https://api.github.com/users/antonioortegajr/following{/other_user}","gists_url":"https://api.github.com/users/antonioortegajr/gists{/gist_id}","type":"User","subscriptions_url":"https://api.github.com/users/antonioortegajr/subscriptions","organizations_url":"https://api.github.com/users/antonioortegajr/orgs","id":6744175},"title":"add logos and descriptions to beers being searched","id":53210166,"number":12,"comments":0,"updated_at":"2015-01-01T00:59:56Z","html_url":"https://github.com/antonioortegajr/beerfind.me/issues/12"},"action":"opened"},"created_at":"2015-01-01T00:59:56Z","id":"2489395749","public":1,"actor":{"gravatar_id":"","url":"https://api.github.com/users/antonioortegajr","id":6744175,"login":"antonioortegajr","avatar_url":"https://avatars.githubusercontent.com/u/6744175?"},"repo":{"url":"https://api.github.com/repos/antonioortegajr/beerfind.me","id":28573267,"name":"antonioortegajr/beerfind.me"},"type":"IssuesEvent"} -7697 {"payload":{"action":"closed","pull_request":{"review_comments_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/pulls/534/comments","url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/pulls/534","body":"","patch_url":"https://github.com/XLabs/Xamarin-Forms-Labs/pull/534.patch","head":{"ref":"master","user":{"starred_url":"https://api.github.com/users/bokmadsen/starred{/owner}{/repo}","url":"https://api.github.com/users/bokmadsen","repos_url":"https://api.github.com/users/bokmadsen/repos","events_url":"https://api.github.com/users/bokmadsen/events{/privacy}","login":"bokmadsen","avatar_url":"https://avatars.githubusercontent.com/u/790740?v=3","gravatar_id":"","site_admin":0,"html_url":"https://github.com/bokmadsen","received_events_url":"https://api.github.com/users/bokmadsen/received_events","followers_url":"https://api.github.com/users/bokmadsen/followers","following_url":"https://api.github.com/users/bokmadsen/following{/other_user}","gists_url":"https://api.github.com/users/bokmadsen/gists{/gist_id}","type":"User","subscriptions_url":"https://api.github.com/users/bokmadsen/subscriptions","organizations_url":"https://api.github.com/users/bokmadsen/orgs","id":790740},"sha":"2a664fd4ac7e8ff340893ad5e4c2f50127ee52c6","label":"bokmadsen:master","repo":{"svn_url":"https://github.com/bokmadsen/Xamarin-Forms-Labs","pushed_at":"2014-12-21T16:59:46Z","url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs","issues_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/issues{/number}","collaborators_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/collaborators{/collaborator}","events_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/events","has_downloads":1,"labels_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/labels{/name}","keys_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/keys{/key_id}","releases_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/releases{/id}","stargazers_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/stargazers","statuses_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/statuses/{sha}","comments_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/comments{/number}","blobs_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/git/blobs{/sha}","html_url":"https://github.com/bokmadsen/Xamarin-Forms-Labs","has_pages":1,"commits_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/commits{/sha}","downloads_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/downloads","contents_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/contents/{+path}","has_issues":0,"issue_events_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/issues/events{/number}","ssh_url":"git@github.com:bokmadsen/Xamarin-Forms-Labs.git","watchers_count":0,"default_branch":"master","open_issues":0,"size":96455,"forks_count":0,"tags_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/tags","id":28305437,"name":"Xamarin-Forms-Labs","has_wiki":1,"issue_comment_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/issues/comments/{number}","owner":{"starred_url":"https://api.github.com/users/bokmadsen/starred{/owner}{/repo}","url":"https://api.github.com/users/bokmadsen","repos_url":"https://api.github.com/users/bokmadsen/repos","events_url":"https://api.github.com/users/bokmadsen/events{/privacy}","login":"bokmadsen","avatar_url":"https://avatars.githubusercontent.com/u/790740?v=3","gravatar_id":"","site_admin":0,"html_url":"https://github.com/bokmadsen","received_events_url":"https://api.github.com/users/bokmadsen/received_events","followers_url":"https://api.github.com/users/bokmadsen/followers","following_url":"https://api.github.com/users/bokmadsen/following{/other_user}","gists_url":"https://api.github.com/users/bokmadsen/gists{/gist_id}","type":"User","subscriptions_url":"https://api.github.com/users/bokmadsen/subscriptions","organizations_url":"https://api.github.com/users/bokmadsen/orgs","id":790740},"languages_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/languages","git_url":"git://github.com/bokmadsen/Xamarin-Forms-Labs.git","subscribers_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/subscribers","clone_url":"https://github.com/bokmadsen/Xamarin-Forms-Labs.git","notifications_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/notifications{?since,all,participating}","git_tags_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/git/tags{/sha}","archive_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/{archive_format}{/ref}","full_name":"bokmadsen/Xamarin-Forms-Labs","private":0,"teams_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/teams","milestones_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/milestones{/number}","git_refs_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/git/refs{/sha}","git_commits_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/git/commits{/sha}","trees_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/git/trees{/sha}","updated_at":"2014-12-21T16:59:46Z","description":"Xamarin Forms Labs is a open source project that aims to provide a powerful and cross platform set of controls and helpers tailored to work with Xamarin Forms.","forks_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/forks","open_issues_count":0,"hooks_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/hooks","watchers":0,"created_at":"2014-12-21T16:32:52Z","contributors_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/contributors","branches_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/branches{/branch}","stargazers_count":0,"fork":1,"forks":0,"compare_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/compare/{base}...{head}","pulls_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/pulls{/number}","subscription_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/subscription","merges_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/merges","assignees_url":"https://api.github.com/repos/bokmadsen/Xamarin-Forms-Labs/assignees{/user}","language":"C#","homepage":""}},"issue_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/issues/534","statuses_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/statuses/2a664fd4ac7e8ff340893ad5e4c2f50127ee52c6","comments_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/issues/534/comments","diff_url":"https://github.com/XLabs/Xamarin-Forms-Labs/pull/534.diff","_links":{"self":{"href":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/pulls/534"},"commits":{"href":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/pulls/534/commits"},"review_comments":{"href":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/pulls/534/comments"},"statuses":{"href":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/statuses/2a664fd4ac7e8ff340893ad5e4c2f50127ee52c6"},"comments":{"href":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/issues/534/comments"},"html":{"href":"https://github.com/XLabs/Xamarin-Forms-Labs/pull/534"},"issue":{"href":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/issues/534"},"review_comment":{"href":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/pulls/comments/{number}"}},"title":"Added null check in HybridWebView","base":{"ref":"master","user":{"starred_url":"https://api.github.com/users/XLabs/starred{/owner}{/repo}","url":"https://api.github.com/users/XLabs","repos_url":"https://api.github.com/users/XLabs/repos","events_url":"https://api.github.com/users/XLabs/events{/privacy}","login":"XLabs","avatar_url":"https://avatars.githubusercontent.com/u/7787062?v=3","gravatar_id":"","site_admin":0,"html_url":"https://github.com/XLabs","received_events_url":"https://api.github.com/users/XLabs/received_events","followers_url":"https://api.github.com/users/XLabs/followers","following_url":"https://api.github.com/users/XLabs/following{/other_user}","gists_url":"https://api.github.com/users/XLabs/gists{/gist_id}","type":"Organization","subscriptions_url":"https://api.github.com/users/XLabs/subscriptions","organizations_url":"https://api.github.com/users/XLabs/orgs","id":7787062},"sha":"6906dd38ff69debcc304cb05b6877fae71747acd","label":"XLabs:master","repo":{"svn_url":"https://github.com/XLabs/Xamarin-Forms-Labs","pushed_at":"2015-01-01T00:59:12Z","url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs","issues_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/issues{/number}","collaborators_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/collaborators{/collaborator}","events_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/events","has_downloads":1,"labels_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/labels{/name}","keys_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/keys{/key_id}","releases_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/releases{/id}","stargazers_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/stargazers","statuses_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/statuses/{sha}","comments_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/comments{/number}","blobs_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/git/blobs{/sha}","html_url":"https://github.com/XLabs/Xamarin-Forms-Labs","has_pages":1,"commits_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/commits{/sha}","downloads_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/downloads","contents_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/contents/{+path}","has_issues":1,"issue_events_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/issues/events{/number}","ssh_url":"git@github.com:XLabs/Xamarin-Forms-Labs.git","watchers_count":340,"default_branch":"master","open_issues":92,"size":104805,"forks_count":210,"tags_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/tags","id":20463939,"name":"Xamarin-Forms-Labs","has_wiki":1,"issue_comment_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/issues/comments/{number}","owner":{"starred_url":"https://api.github.com/users/XLabs/starred{/owner}{/repo}","url":"https://api.github.com/users/XLabs","repos_url":"https://api.github.com/users/XLabs/repos","events_url":"https://api.github.com/users/XLabs/events{/privacy}","login":"XLabs","avatar_url":"https://avatars.githubusercontent.com/u/7787062?v=3","gravatar_id":"","site_admin":0,"html_url":"https://github.com/XLabs","received_events_url":"https://api.github.com/users/XLabs/received_events","followers_url":"https://api.github.com/users/XLabs/followers","following_url":"https://api.github.com/users/XLabs/following{/other_user}","gists_url":"https://api.github.com/users/XLabs/gists{/gist_id}","type":"Organization","subscriptions_url":"https://api.github.com/users/XLabs/subscriptions","organizations_url":"https://api.github.com/users/XLabs/orgs","id":7787062},"languages_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/languages","git_url":"git://github.com/XLabs/Xamarin-Forms-Labs.git","subscribers_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/subscribers","clone_url":"https://github.com/XLabs/Xamarin-Forms-Labs.git","notifications_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/notifications{?since,all,participating}","git_tags_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/git/tags{/sha}","archive_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/{archive_format}{/ref}","full_name":"XLabs/Xamarin-Forms-Labs","private":0,"teams_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/teams","milestones_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/milestones{/number}","git_refs_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/git/refs{/sha}","git_commits_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/git/commits{/sha}","trees_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/git/trees{/sha}","updated_at":"2015-01-01T00:59:13Z","description":"Xamarin Forms Labs is a open source project that aims to provide a powerful and cross platform set of controls and helpers tailored to work with Xamarin Forms.","forks_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/forks","open_issues_count":92,"hooks_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/hooks","watchers":340,"created_at":"2014-06-03T23:53:11Z","contributors_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/contributors","branches_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/branches{/branch}","stargazers_count":340,"fork":0,"forks":210,"compare_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/compare/{base}...{head}","pulls_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/pulls{/number}","subscription_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/subscription","merges_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/merges","assignees_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/assignees{/user}","language":"C#","homepage":""}},"updated_at":"2015-01-01T00:59:55Z","html_url":"https://github.com/XLabs/Xamarin-Forms-Labs/pull/534","commits_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/pulls/534/commits","created_at":"2014-12-21T17:00:20Z","locked":0,"state":"closed","user":{"starred_url":"https://api.github.com/users/bokmadsen/starred{/owner}{/repo}","url":"https://api.github.com/users/bokmadsen","repos_url":"https://api.github.com/users/bokmadsen/repos","events_url":"https://api.github.com/users/bokmadsen/events{/privacy}","login":"bokmadsen","avatar_url":"https://avatars.githubusercontent.com/u/790740?v=3","gravatar_id":"","site_admin":0,"html_url":"https://github.com/bokmadsen","received_events_url":"https://api.github.com/users/bokmadsen/received_events","followers_url":"https://api.github.com/users/bokmadsen/followers","following_url":"https://api.github.com/users/bokmadsen/following{/other_user}","gists_url":"https://api.github.com/users/bokmadsen/gists{/gist_id}","type":"User","subscriptions_url":"https://api.github.com/users/bokmadsen/subscriptions","organizations_url":"https://api.github.com/users/bokmadsen/orgs","id":790740},"id":26429779,"number":534,"review_comment_url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs/pulls/comments/{number}","additions":16,"changed_files":1,"mergeable_state":"clean","review_comments":0,"deletions":14,"merged":0,"merge_commit_sha":"9cda5a97584b1ad549fff2dcaaf304adeb27ae07","commits":1,"closed_at":"2015-01-01T00:59:55Z","mergeable":1,"comments":0},"number":534},"created_at":"2015-01-01T00:59:55Z","id":"2489395745","org":{"gravatar_id":"","url":"https://api.github.com/orgs/XLabs","id":7787062,"login":"XLabs","avatar_url":"https://avatars.githubusercontent.com/u/7787062?"},"public":1,"actor":{"gravatar_id":"","url":"https://api.github.com/users/rmarinho","id":1235097,"login":"rmarinho","avatar_url":"https://avatars.githubusercontent.com/u/1235097?"},"repo":{"url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs","id":20463939,"name":"XLabs/Xamarin-Forms-Labs"},"type":"PullRequestEvent"} -7696 {"payload":{"commits":[{"sha":"c356eac8fa4409a1aa794ab07244250a862da03b","author":{"email":"de8898f6c55e335aa0a2b937fae65fb756ee038f@gmail.com","name":"Zaryafaraj"},"message":"reserve modal styles","distinct":true,"url":"https://api.github.com/repos/Fathalian/Guild/commits/c356eac8fa4409a1aa794ab07244250a862da03b"},{"sha":"9a773fc648910c7a2499401f44a6e5f71eb30460","author":{"email":"de8898f6c55e335aa0a2b937fae65fb756ee038f@gmail.com","name":"Zaryafaraj"},"message":"Merge branch 'master' of https://github.com/Fathalian/Guild\\n\\nConflicts:\\n\\tapp/templates/reserveModal.html","distinct":true,"url":"https://api.github.com/repos/Fathalian/Guild/commits/9a773fc648910c7a2499401f44a6e5f71eb30460"}],"before":"da6d28eba11c89836e132bbba032c22d92e4f233","head":"9a773fc648910c7a2499401f44a6e5f71eb30460","size":2,"push_id":536752114,"ref":"refs/heads/master","distinct_size":2},"created_at":"2015-01-01T00:59:55Z","id":"2489395744","public":1,"actor":{"gravatar_id":"","url":"https://api.github.com/users/Zaryafaraj","id":1356088,"login":"Zaryafaraj","avatar_url":"https://avatars.githubusercontent.com/u/1356088?"},"repo":{"url":"https://api.github.com/repos/Fathalian/Guild","id":26995510,"name":"Fathalian/Guild"},"type":"PushEvent"} -7695 {"payload":{"commits":[{"sha":"17cf9ea07662d74b2d5bac1cf976f5853a63920d","author":{"email":"af59d1d6805404937849f05dafd5a911888fd7a4@gmail.com","name":"Kevin Hofmaenner"},"message":"minor UI tweak","distinct":true,"url":"https://api.github.com/repos/kevinhofmaenner/blackjack/commits/17cf9ea07662d74b2d5bac1cf976f5853a63920d"}],"before":"6b4f5af0a2a70bbe00cd88281823b436d506ff2d","head":"17cf9ea07662d74b2d5bac1cf976f5853a63920d","size":1,"push_id":536752112,"ref":"refs/heads/master","distinct_size":1},"created_at":"2015-01-01T00:59:55Z","id":"2489395742","public":1,"actor":{"gravatar_id":"","url":"https://api.github.com/users/kevinhofmaenner","id":10161858,"login":"kevinhofmaenner","avatar_url":"https://avatars.githubusercontent.com/u/10161858?"},"repo":{"url":"https://api.github.com/repos/kevinhofmaenner/blackjack","id":28652857,"name":"kevinhofmaenner/blackjack"},"type":"PushEvent"} -7694 {"payload":{"commits":[{"sha":"aa8ec0de017c8003758776739facc819e33ac7c9","author":{"email":"e0e04a2320844b42511db0376599e166ab5bda54@gmail.com","name":"Runhang Li"},"message":"finish all hamms test","distinct":true,"url":"https://api.github.com/repos/marklrh/ocaml-cohttp-test/commits/aa8ec0de017c8003758776739facc819e33ac7c9"}],"before":"2bb795fc30fc15ab85bcc10f894bfcfa118d69bc","head":"aa8ec0de017c8003758776739facc819e33ac7c9","size":1,"push_id":536752109,"ref":"refs/heads/master","distinct_size":1},"created_at":"2015-01-01T00:59:53Z","id":"2489395735","public":1,"actor":{"gravatar_id":"","url":"https://api.github.com/users/marklrh","id":3656079,"login":"marklrh","avatar_url":"https://avatars.githubusercontent.com/u/3656079?"},"repo":{"url":"https://api.github.com/repos/marklrh/ocaml-cohttp-test","id":27470715,"name":"marklrh/ocaml-cohttp-test"},"type":"PushEvent"} -7693 {"payload":{"commits":[{"sha":"0b27989723feb4b183d5f87813fef146b670b1d1","author":{"email":"7c5a0c567b5584a13fde407456875318a5bec977@gmail.com","name":"Raphaël Benitte"},"message":"Add time clock widget + Improve stylus theming","distinct":true,"url":"https://api.github.com/repos/plouc/mozaik/commits/0b27989723feb4b183d5f87813fef146b670b1d1"}],"before":"7ddf17eb74fff5adad6e2feb72bcb627d4644800","head":"0b27989723feb4b183d5f87813fef146b670b1d1","size":1,"push_id":536752104,"ref":"refs/heads/master","distinct_size":1},"created_at":"2015-01-01T00:59:51Z","id":"2489395728","public":1,"actor":{"gravatar_id":"","url":"https://api.github.com/users/plouc","id":501642,"login":"plouc","avatar_url":"https://avatars.githubusercontent.com/u/501642?"},"repo":{"url":"https://api.github.com/repos/plouc/mozaik","id":28498113,"name":"plouc/mozaik"},"type":"PushEvent"} +7702 {"url":"https://api.github.com/repos/magik6k/BitBuffer","id":28677864,"name":"magik6k/BitBuffer"} +7701 {"url":"https://api.github.com/repos/wllmtrng/wllmtrng.github.io","id":18089434,"name":"wllmtrng/wllmtrng.github.io"} +7700 {"url":"https://api.github.com/repos/ACID-Scripts/TBC","id":23724137,"name":"ACID-Scripts/TBC"} +7699 {"url":"https://api.github.com/repos/chrisjimenez/IpsePuppet","id":28678128,"name":"chrisjimenez/IpsePuppet"} +7698 {"url":"https://api.github.com/repos/antonioortegajr/beerfind.me","id":28573267,"name":"antonioortegajr/beerfind.me"} +7697 {"url":"https://api.github.com/repos/XLabs/Xamarin-Forms-Labs","id":20463939,"name":"XLabs/Xamarin-Forms-Labs"} +7696 {"url":"https://api.github.com/repos/Fathalian/Guild","id":26995510,"name":"Fathalian/Guild"} +7695 {"url":"https://api.github.com/repos/kevinhofmaenner/blackjack","id":28652857,"name":"kevinhofmaenner/blackjack"} +7694 {"url":"https://api.github.com/repos/marklrh/ocaml-cohttp-test","id":27470715,"name":"marklrh/ocaml-cohttp-test"} +7693 {"url":"https://api.github.com/repos/plouc/mozaik","id":28498113,"name":"plouc/mozaik"} -- !sql_36_3 -- 2 {"updated_value":10} diff --git a/regression-test/data/variant_p0/sql/gh_data.out b/regression-test/data/variant_p0/sql/gh_data.out index 7fc5e10dbe180d..4c34edabe48205 100644 --- a/regression-test/data/variant_p0/sql/gh_data.out +++ b/regression-test/data/variant_p0/sql/gh_data.out @@ -3,31 +3,37 @@ 0 -- !gh_data_2 -- -5000 +0 -- !gh_data_3 -- +0 + +-- !gh_data_4 -- +5000 + +-- !gh_data_5 -- leonardomso/33-js-concepts 3 ytdl-org/youtube-dl 3 Bogdanp/neko 2 bminossi/AllVideoPocsFromHackerOne 2 disclose/diodata 2 --- !gh_data_4 -- +-- !gh_data_6 -- 14690758274 --- !gh_data_5 -- +-- !gh_data_7 -- 73453762334584 --- !gh_data_6 -- +-- !gh_data_8 -- 457806339 --- !gh_data_7 -- +-- !gh_data_9 -- 0 --- !gh_data_8 -- +-- !gh_data_10 -- 19829 --- !gh_data_9 -- +-- !gh_data_11 -- 49390617 64890096 10696700 @@ -39,19 +45,19 @@ disclose/diodata 2 42386044 73801003 --- !gh_data_10 -- -27 {"payload":{"action":"started"},"created_at":"2021-01-02T16:37:27Z","id":"14690746717","public":1,"actor":{"gravatar_id":"","display_login":"sergdudnik","url":"https://api.github.com/users/sergdudnik","id":16341546,"login":"sergdudnik","avatar_url":"https://avatars.githubusercontent.com/u/16341546?"},"repo":{"url":"https://api.github.com/repos/leonardomso/33-js-concepts","id":147350463,"name":"leonardomso/33-js-concepts"},"type":"WatchEvent"} -36 {"payload":{"action":"started"},"created_at":"2021-01-02T16:37:27Z","id":"14690746732","public":1,"actor":{"gravatar_id":"","display_login":"juliusHuelsmann","url":"https://api.github.com/users/juliusHuelsmann","id":9212314,"login":"juliusHuelsmann","avatar_url":"https://avatars.githubusercontent.com/u/9212314?"},"repo":{"url":"https://api.github.com/repos/odeke-em/drive","id":26109545,"name":"odeke-em/drive"},"type":"WatchEvent"} -46 {"payload":{"action":"started"},"created_at":"2021-01-02T16:37:27Z","id":"14690746749","org":{"gravatar_id":"","url":"https://api.github.com/orgs/GO-LiFE","id":38434522,"login":"GO-LiFE","avatar_url":"https://avatars.githubusercontent.com/u/38434522?"},"public":1,"actor":{"gravatar_id":"","display_login":"okbean","url":"https://api.github.com/users/okbean","id":75969386,"login":"okbean","avatar_url":"https://avatars.githubusercontent.com/u/75969386?"},"repo":{"url":"https://api.github.com/repos/GO-LiFE/GoFIT_SDK_Android","id":141905736,"name":"GO-LiFE/GoFIT_SDK_Android"},"type":"WatchEvent"} -56 {"payload":{"action":"started"},"created_at":"2021-01-02T16:37:28Z","id":"14690746773","public":1,"actor":{"gravatar_id":"","display_login":"PWDream","url":"https://api.github.com/users/PWDream","id":4903755,"login":"PWDream","avatar_url":"https://avatars.githubusercontent.com/u/4903755?"},"repo":{"url":"https://api.github.com/repos/MrXujiang/h5-Dooring","id":289417971,"name":"MrXujiang/h5-Dooring"},"type":"WatchEvent"} -86 {"payload":{"action":"started"},"created_at":"2021-01-02T16:37:29Z","id":"14690746843","public":1,"actor":{"gravatar_id":"","display_login":"Gui-Yom","url":"https://api.github.com/users/Gui-Yom","id":25181283,"login":"Gui-Yom","avatar_url":"https://avatars.githubusercontent.com/u/25181283?"},"repo":{"url":"https://api.github.com/repos/redsaph/cleartext","id":106453399,"name":"redsaph/cleartext"},"type":"WatchEvent"} -98 {"payload":{"action":"started"},"created_at":"2021-01-02T16:37:29Z","id":"14690746866","org":{"gravatar_id":"","url":"https://api.github.com/orgs/sherlock-project","id":48293496,"login":"sherlock-project","avatar_url":"https://avatars.githubusercontent.com/u/48293496?"},"public":1,"actor":{"gravatar_id":"","display_login":"humaidk2","url":"https://api.github.com/users/humaidk2","id":12982026,"login":"humaidk2","avatar_url":"https://avatars.githubusercontent.com/u/12982026?"},"repo":{"url":"https://api.github.com/repos/sherlock-project/sherlock","id":162998479,"name":"sherlock-project/sherlock"},"type":"WatchEvent"} -101 {"payload":{"action":"started"},"created_at":"2021-01-02T16:37:29Z","id":"14690746870","public":1,"actor":{"gravatar_id":"","display_login":"hasantezcan","url":"https://api.github.com/users/hasantezcan","id":32804505,"login":"hasantezcan","avatar_url":"https://avatars.githubusercontent.com/u/32804505?"},"repo":{"url":"https://api.github.com/repos/okandavut/react-spotify-nowplaying","id":326215605,"name":"okandavut/react-spotify-nowplaying"},"type":"WatchEvent"} -112 {"payload":{"action":"started"},"created_at":"2021-01-02T16:37:30Z","id":"14690746899","public":1,"actor":{"gravatar_id":"","display_login":"nicholas-robertson","url":"https://api.github.com/users/nicholas-robertson","id":17681331,"login":"nicholas-robertson","avatar_url":"https://avatars.githubusercontent.com/u/17681331?"},"repo":{"url":"https://api.github.com/repos/sentriz/gonic","id":178435468,"name":"sentriz/gonic"},"type":"WatchEvent"} -122 {"payload":{"action":"started"},"created_at":"2021-01-02T16:37:30Z","id":"14690746914","org":{"gravatar_id":"","url":"https://api.github.com/orgs/netlify-labs","id":47546088,"login":"netlify-labs","avatar_url":"https://avatars.githubusercontent.com/u/47546088?"},"public":1,"actor":{"gravatar_id":"","display_login":"javaniecampbell","url":"https://api.github.com/users/javaniecampbell","id":1676496,"login":"javaniecampbell","avatar_url":"https://avatars.githubusercontent.com/u/1676496?"},"repo":{"url":"https://api.github.com/repos/netlify-labs/react-netlify-identity-widget","id":182606378,"name":"netlify-labs/react-netlify-identity-widget"},"type":"WatchEvent"} -169 {"payload":{"action":"started"},"created_at":"2021-01-02T16:37:32Z","id":"14690747028","org":{"gravatar_id":"","url":"https://api.github.com/orgs/microsoft","id":6154722,"login":"microsoft","avatar_url":"https://avatars.githubusercontent.com/u/6154722?"},"public":1,"actor":{"gravatar_id":"","display_login":"Yxnt","url":"https://api.github.com/users/Yxnt","id":10323352,"login":"Yxnt","avatar_url":"https://avatars.githubusercontent.com/u/10323352?"},"repo":{"url":"https://api.github.com/repos/microsoft/BotBuilder-Samples","id":68730444,"name":"microsoft/BotBuilder-Samples"},"type":"WatchEvent"} +-- !gh_data_12 -- +27 {"url":"https://api.github.com/repos/leonardomso/33-js-concepts","id":147350463,"name":"leonardomso/33-js-concepts"} +36 {"url":"https://api.github.com/repos/odeke-em/drive","id":26109545,"name":"odeke-em/drive"} +46 {"url":"https://api.github.com/repos/GO-LiFE/GoFIT_SDK_Android","id":141905736,"name":"GO-LiFE/GoFIT_SDK_Android"} +56 {"url":"https://api.github.com/repos/MrXujiang/h5-Dooring","id":289417971,"name":"MrXujiang/h5-Dooring"} +86 {"url":"https://api.github.com/repos/redsaph/cleartext","id":106453399,"name":"redsaph/cleartext"} +98 {"url":"https://api.github.com/repos/sherlock-project/sherlock","id":162998479,"name":"sherlock-project/sherlock"} +101 {"url":"https://api.github.com/repos/okandavut/react-spotify-nowplaying","id":326215605,"name":"okandavut/react-spotify-nowplaying"} +112 {"url":"https://api.github.com/repos/sentriz/gonic","id":178435468,"name":"sentriz/gonic"} +122 {"url":"https://api.github.com/repos/netlify-labs/react-netlify-identity-widget","id":182606378,"name":"netlify-labs/react-netlify-identity-widget"} +169 {"url":"https://api.github.com/repos/microsoft/BotBuilder-Samples","id":68730444,"name":"microsoft/BotBuilder-Samples"} --- !gh_data_11 -- +-- !gh_data_13 -- 2051941 1 10696700 1 32271952 2 @@ -63,3 +69,27 @@ disclose/diodata 2 64890096 1 73801003 1 +-- !gh_data_14 -- +27 14690746717 WatchEvent leonardomso/33-js-concepts +36 14690746732 WatchEvent odeke-em/drive +46 14690746749 WatchEvent GO-LiFE/GoFIT_SDK_Android +56 14690746773 WatchEvent MrXujiang/h5-Dooring +86 14690746843 WatchEvent redsaph/cleartext +98 14690746866 WatchEvent sherlock-project/sherlock +101 14690746870 WatchEvent okandavut/react-spotify-nowplaying +112 14690746899 WatchEvent sentriz/gonic +122 14690746914 WatchEvent netlify-labs/react-netlify-identity-widget +169 14690747028 WatchEvent microsoft/BotBuilder-Samples + +-- !gh_data_15 -- +user +user +user +user +user +user +user +user +user +user + diff --git a/regression-test/suites/variant_p0/load.groovy b/regression-test/suites/variant_p0/load.groovy index 0501d3ce52fa5a..b23ae19d12f1a2 100644 --- a/regression-test/suites/variant_p0/load.groovy +++ b/regression-test/suites/variant_p0/load.groovy @@ -287,7 +287,7 @@ suite("regression_test_variant", "variant_type"){ // 12. streamload remote file table_name = "logdata" create_table.call(table_name, "4") - sql "set enable_two_phase_read_opt = false;" + // sql "set enable_two_phase_read_opt = false;" // no sparse columns set_be_config.call("variant_ratio_of_defaults_as_sparse_column", "1") load_json_data.call(table_name, """${getS3Url() + '/load/logdata.json'}""") @@ -340,7 +340,7 @@ suite("regression_test_variant", "variant_type"){ qt_sql_36_1 "select cast(v:a as int), cast(v:b as int), cast(v:c as int) from ${table_name} order by k limit 10" sql "DELETE FROM ${table_name} WHERE k=1" sql "select * from ${table_name}" - qt_sql_36_2 "select * from ${table_name} where k > 3 order by k desc limit 10" + qt_sql_36_2 """select k, json_extract(cast(v as text), "\$.repo") from ${table_name} where k > 3 order by k desc limit 10""" sql "insert into ${table_name} select * from ${table_name}" sql """UPDATE ${table_name} set v = '{"updated_value" : 10}' where k = 2""" qt_sql_36_3 """select * from ${table_name} where k = 2""" @@ -386,13 +386,13 @@ suite("regression_test_variant", "variant_type"){ sql """insert into ${table_name} values (2, "abe", '{"c" : 1}')""" sql """insert into ${table_name} values (3, "abd", '{"d" : 1}')""" sql "delete from ${table_name} where k in (select k from variant_mow where k in (1, 2))" - qt_sql_38 "select * from ${table_name} order by k" + qt_sql_38 "select * from ${table_name} order by k limit 10" // read text from sparse col set_be_config.call("variant_ratio_of_defaults_as_sparse_column", "0.95") sql """insert into sparse_columns select 0, '{"a": 1123, "b" : [123, {"xx" : 1}], "c" : {"c" : 456, "d" : null, "e" : 7.111}, "zzz" : null, "oooo" : {"akakaka" : null, "xxxx" : {"xxx" : 123}}}' as json_str union all select 0, '{"a" : 1234, "xxxx" : "kaana", "ddd" : {"aaa" : 123, "mxmxm" : [456, "789"]}}' as json_str from numbers("number" = "4096") limit 4096 ;""" - qt_sql_31 """select cast(v:xxxx as string) from sparse_columns where cast(v:xxxx as string) != 'null' limit 1;""" + qt_sql_31 """select cast(v:xxxx as string) from sparse_columns where cast(v:xxxx as string) != 'null' order by k limit 1;""" sql "truncate table sparse_columns" set_be_config.call("variant_ratio_of_defaults_as_sparse_column", "0.95") } finally { diff --git a/regression-test/suites/variant_p0/sql/gh_data.sql b/regression-test/suites/variant_p0/sql/gh_data.sql index 72a3adb01cff43..2fc0ccac14a629 100644 --- a/regression-test/suites/variant_p0/sql/gh_data.sql +++ b/regression-test/suites/variant_p0/sql/gh_data.sql @@ -1,4 +1,6 @@ set exec_mem_limit=8G; +set enable_two_phase_read_opt = true; +set topn_opt_limit_threshold = 1024; SELECT count() from ghdata; SELECT cast(v:repo.name as string), count() AS stars FROM ghdata WHERE cast(v:type as string) = 'WatchEvent' GROUP BY cast(v:repo.name as string) ORDER BY stars DESC, cast(v:repo.name as string) LIMIT 5; SELECT max(cast(cast(v:`id` as string) as bigint)) FROM ghdata; @@ -6,8 +8,9 @@ SELECT sum(cast(cast(v:`id` as string) as bigint)) FROM ghdata; SELECT sum(cast(v:payload.member.id as bigint)) FROM ghdata; SELECT sum(cast(v:payload.pull_request.milestone.creator.site_admin as bigint)) FROM ghdata; SELECT sum(length(v:payload.pull_request.base.repo.html_url)) FROM ghdata; --- SELECT v:payload.commits.author.name FROM ghdata ORDER BY k LIMIT 10; SELECT v:payload.member.id FROM ghdata where cast(v:payload.member.id as string) is not null ORDER BY k LIMIT 10; -- select k, v:payload.commits.author.name AS name, e FROM ghdata as t lateral view explode(cast(v:payload.commits.author.name as array)) tm1 as e order by k limit 5; -select k, v from ghdata WHERE cast(v:type as string) = 'WatchEvent' order by k limit 10; -SELECT cast(v:payload.member.id as bigint), count() FROM ghdata where cast(v:payload.member.id as bigint) is not null group by cast(v:payload.member.id as bigint) order by 1, 2 desc LIMIT 10; \ No newline at end of file +select k, json_extract(v, '$.repo') from ghdata WHERE cast(v:type as string) = 'WatchEvent' order by k limit 10; +SELECT cast(v:payload.member.id as bigint), count() FROM ghdata where cast(v:payload.member.id as bigint) is not null group by cast(v:payload.member.id as bigint) order by 1, 2 desc LIMIT 10; +select k, cast(v:`id` as string), cast(v:type as string), cast(v:repo.name as string) from ghdata WHERE cast(v:type as string) = 'WatchEvent' order by k limit 10; +SELECT cast(v:payload.pusher_type as text) FROM ghdata where cast(v:payload.pusher_type as text) is not null ORDER BY k LIMIT 10; \ No newline at end of file From 1d984e0ebb1010f58b860c3089254053566d5b13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E5=81=A5?= Date: Mon, 25 Dec 2023 12:53:14 +0800 Subject: [PATCH 006/299] return residual expr of join (#28760) --- .../jobs/joinorder/hypergraph/HyperGraph.java | 158 +++++++++++------- .../jobs/joinorder/hypergraph/edge/Edge.java | 22 +++ .../joinorder/hypergraph/edge/FilterEdge.java | 15 -- .../mv/AbstractMaterializedViewRule.java | 6 +- .../exploration/mv/ComparisonResult.java | 94 +++++++++++ .../rules/exploration/mv/StructInfo.java | 2 +- .../hypergraph/CompareOuterJoinTest.java | 13 +- .../hypergraph/PullupExpressionTest.java | 151 +++++++++++++++++ .../exploration/mv/BuildStructInfoTest.java | 4 +- 9 files changed, 380 insertions(+), 85 deletions(-) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/ComparisonResult.java create mode 100644 fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/PullupExpressionTest.java diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/HyperGraph.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/HyperGraph.java index e534fc1fa84624..7bd33c64b3c593 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/HyperGraph.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/HyperGraph.java @@ -27,6 +27,7 @@ import org.apache.doris.nereids.jobs.joinorder.hypergraph.node.StructInfoNode; import org.apache.doris.nereids.memo.Group; import org.apache.doris.nereids.memo.GroupExpression; +import org.apache.doris.nereids.rules.exploration.mv.ComparisonResult; import org.apache.doris.nereids.rules.exploration.mv.LogicalCompatibilityContext; import org.apache.doris.nereids.rules.rewrite.PushDownFilterThroughJoin; import org.apache.doris.nereids.trees.expressions.Alias; @@ -44,18 +45,21 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.BitSet; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; +import java.util.Map.Entry; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nullable; /** * The graph is a join graph, whose node is the leaf plan and edge is a join operator. @@ -268,11 +272,11 @@ private void makeFilterConflictRules(JoinEdge joinEdge) { filterEdges.forEach(e -> { if (LongBitmap.isSubset(e.getReferenceNodes(), leftSubNodes) && !PushDownFilterThroughJoin.COULD_PUSH_THROUGH_LEFT.contains(joinEdge.getJoinType())) { - e.addRejectJoin(joinEdge); + e.addRejectEdge(joinEdge); } if (LongBitmap.isSubset(e.getReferenceNodes(), rightSubNodes) && !PushDownFilterThroughJoin.COULD_PUSH_THROUGH_RIGHT.contains(joinEdge.getJoinType())) { - e.addRejectJoin(joinEdge); + e.addRejectEdge(joinEdge); } }); } @@ -289,9 +293,11 @@ private void makeJoinConflictRules(JoinEdge edgeB) { JoinEdge childA = joinEdges.get(i); if (!JoinType.isAssoc(childA.getJoinType(), edgeB.getJoinType())) { leftRequired = LongBitmap.newBitmapUnion(leftRequired, childA.getLeftSubNodes(joinEdges)); + childA.addRejectEdge(edgeB); } if (!JoinType.isLAssoc(childA.getJoinType(), edgeB.getJoinType())) { leftRequired = LongBitmap.newBitmapUnion(leftRequired, childA.getRightSubNodes(joinEdges)); + childA.addRejectEdge(edgeB); } } @@ -299,9 +305,11 @@ private void makeJoinConflictRules(JoinEdge edgeB) { JoinEdge childA = joinEdges.get(i); if (!JoinType.isAssoc(edgeB.getJoinType(), childA.getJoinType())) { rightRequired = LongBitmap.newBitmapUnion(rightRequired, childA.getRightSubNodes(joinEdges)); + childA.addRejectEdge(edgeB); } if (!JoinType.isRAssoc(edgeB.getJoinType(), childA.getJoinType())) { rightRequired = LongBitmap.newBitmapUnion(rightRequired, childA.getLeftSubNodes(joinEdges)); + childA.addRejectEdge(edgeB); } } edgeB.setLeftExtendedNodes(leftRequired); @@ -593,57 +601,75 @@ public int edgeSize() { * compare hypergraph * * @param viewHG the compared hyper graph - * @return null represents not compatible, or return some expression which can - * be pull up from this hyper graph + * @return Comparison result */ - public @Nullable List isLogicCompatible(HyperGraph viewHG, LogicalCompatibilityContext ctx) { - Map queryToView = constructEdgeMap(viewHG, ctx.getQueryToViewEdgeExpressionMapping()); + public ComparisonResult isLogicCompatible(HyperGraph viewHG, LogicalCompatibilityContext ctx) { + // 1 try to construct a map which can be mapped from edge to edge + Map queryToView = constructMapWithNode(viewHG, ctx.getQueryToViewNodeIDMapping()); - // All edge in view must have a mapped edge in query - if (queryToView.size() != viewHG.edgeSize()) { - return null; + // 2. compare them by expression and extract residual expr + ComparisonResult.Builder builder = new ComparisonResult.Builder(); + ComparisonResult edgeCompareRes = compareEdgesWithExpr(queryToView, ctx.getQueryToViewEdgeExpressionMapping()); + if (edgeCompareRes.isInvalid()) { + return ComparisonResult.INVALID; } + builder.addComparisonResult(edgeCompareRes); - boolean allMatch = queryToView.entrySet().stream() - .allMatch(entry -> - compareEdgeWithNode(entry.getKey(), entry.getValue(), ctx.getQueryToViewNodeIDMapping())); - if (!allMatch) { - return null; + // 3. pull join edge of view is no sense, so reject them + if (!queryToView.values().containsAll(viewHG.joinEdges)) { + return ComparisonResult.INVALID; } - // join edges must be identical - boolean isJoinIdentical = joinEdges.stream() - .allMatch(queryToView::containsKey); - if (!isJoinIdentical) { - return null; + // 4. process residual edges + List residualQueryJoin = + processOrphanEdges(Sets.difference(Sets.newHashSet(joinEdges), queryToView.keySet())); + if (residualQueryJoin == null) { + return ComparisonResult.INVALID; } + builder.addQueryExpressions(residualQueryJoin); - // extract all top filters - List residualFilterEdges = filterEdges.stream() - .filter(e -> !queryToView.containsKey(e)) - .collect(ImmutableList.toImmutableList()); - if (residualFilterEdges.stream().anyMatch(e -> !e.isTopFilter())) { - return null; + List residualQueryFilter = + processOrphanEdges(Sets.difference(Sets.newHashSet(filterEdges), queryToView.keySet())); + if (residualQueryFilter == null) { + return ComparisonResult.INVALID; } - return residualFilterEdges.stream() - .flatMap(e -> e.getExpressions().stream()) - .collect(ImmutableList.toImmutableList()); + builder.addQueryExpressions(residualQueryFilter); + + List residualViewFilter = + processOrphanEdges( + Sets.difference(Sets.newHashSet(viewHG.filterEdges), Sets.newHashSet(queryToView.values()))); + if (residualViewFilter == null) { + return ComparisonResult.INVALID; + } + builder.addViewExpressions(residualViewFilter); + + return builder.build(); } - private Map constructEdgeMap(HyperGraph viewHG, Map exprMap) { - Map exprToEdge = constructExprMap(viewHG); - Map queryToView = new HashMap<>(); - joinEdges.stream() - .filter(e -> !e.getExpressions().isEmpty() - && exprMap.containsKey(e.getExpression(0)) - && compareEdgeWithExpr(e, exprToEdge.get(exprMap.get(e.getExpression(0))), exprMap)) - .forEach(e -> queryToView.put(e, exprToEdge.get(exprMap.get(e.getExpression(0))))); - filterEdges.stream() - .filter(e -> !e.getExpressions().isEmpty() - && exprMap.containsKey(e.getExpression(0)) - && compareEdgeWithExpr(e, exprToEdge.get(exprMap.get(e.getExpression(0))), exprMap)) - .forEach(e -> queryToView.put(e, exprToEdge.get(exprMap.get(e.getExpression(0))))); - return queryToView; + private List processOrphanEdges(Set edges) { + List expressions = new ArrayList<>(); + for (Edge edge : edges) { + if (!edge.canPullUp()) { + return null; + } + expressions.addAll(edge.getExpressions()); + } + return expressions; + } + + private Map constructMapWithNode(HyperGraph viewHG, Map nodeMap) { + // TODO use hash map to reduce loop + Map joinEdgeMap = joinEdges.stream().map(qe -> { + Optional viewEdge = viewHG.joinEdges.stream() + .filter(ve -> compareEdgeWithNode(qe, ve, nodeMap)).findFirst(); + return Pair.of(qe, viewEdge); + }).filter(e -> e.second.isPresent()).collect(ImmutableMap.toImmutableMap(p -> p.first, p -> p.second.get())); + Map filterEdgeMap = filterEdges.stream().map(qe -> { + Optional viewEdge = viewHG.filterEdges.stream() + .filter(ve -> compareEdgeWithNode(qe, ve, nodeMap)).findFirst(); + return Pair.of(qe, viewEdge); + }).filter(e -> e.second.isPresent()).collect(ImmutableMap.toImmutableMap(p -> p.first, p -> p.second.get())); + return ImmutableMap.builder().putAll(joinEdgeMap).putAll(filterEdgeMap).build(); } private boolean compareEdgeWithNode(Edge t, Edge o, Map nodeMap) { @@ -686,24 +712,40 @@ private boolean compareNodeMap(long bitmap1, long bitmap2, Map return bitmap2 == newBitmap1; } - private boolean compareEdgeWithExpr(Edge t, Edge o, Map expressionMap) { - if (t.getExpressions().size() != o.getExpressions().size()) { - return false; - } - int size = t.getExpressions().size(); - for (int i = 0; i < size; i++) { - if (!Objects.equals(expressionMap.get(t.getExpression(i)), o.getExpression(i))) { - return false; + private ComparisonResult compareEdgesWithExpr(Map queryToViewedgeMap, + Map queryToView) { + ComparisonResult.Builder builder = new ComparisonResult.Builder(); + for (Entry e : queryToViewedgeMap.entrySet()) { + ComparisonResult res = compareEdgeWithExpr(e.getKey(), e.getValue(), queryToView); + if (res.isInvalid()) { + return ComparisonResult.INVALID; } + builder.addComparisonResult(res); } - return true; + return builder.build(); } - private Map constructExprMap(HyperGraph hyperGraph) { - Map exprToEdge = new HashMap<>(); - hyperGraph.joinEdges.forEach(edge -> edge.getExpressions().forEach(expr -> exprToEdge.put(expr, edge))); - hyperGraph.filterEdges.forEach(edge -> edge.getExpressions().forEach(expr -> exprToEdge.put(expr, edge))); - return exprToEdge; + private ComparisonResult compareEdgeWithExpr(Edge query, Edge view, Map queryToView) { + Set queryExprSet = query.getExpressionSet(); + Set viewExprSet = view.getExpressionSet(); + + Set equalViewExpr = new HashSet<>(); + List residualQueryExpr = new ArrayList<>(); + for (Expression queryExpr : queryExprSet) { + if (queryToView.containsKey(queryExpr) && viewExprSet.contains(queryToView.get(queryExpr))) { + equalViewExpr.add(queryToView.get(queryExpr)); + } else { + residualQueryExpr.add(queryExpr); + } + } + List residualViewExpr = ImmutableList.copyOf(Sets.difference(viewExprSet, equalViewExpr)); + if (!residualViewExpr.isEmpty() && !view.canPullUp()) { + return ComparisonResult.INVALID; + } + if (!residualQueryExpr.isEmpty() && !query.canPullUp()) { + return ComparisonResult.INVALID; + } + return new ComparisonResult(residualQueryExpr, residualViewExpr); } /** diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/edge/Edge.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/edge/Edge.java index 4b88ce9f60dc5c..c47300922b5191 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/edge/Edge.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/edge/Edge.java @@ -22,6 +22,8 @@ import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.Slot; +import com.google.common.collect.ImmutableSet; + import java.util.BitSet; import java.util.List; import java.util.Set; @@ -51,6 +53,8 @@ public abstract class Edge { // record all sub nodes behind in this operator. It's T function in paper private final long subTreeNodes; + private long rejectNodes = 0; + /** * Create simple edge. */ @@ -71,6 +75,10 @@ public boolean isSimple() { return LongBitmap.getCardinality(leftExtendedNodes) == 1 && LongBitmap.getCardinality(rightExtendedNodes) == 1; } + public void addRejectEdge(Edge edge) { + rejectNodes = LongBitmap.newBitmapUnion(edge.getReferenceNodes(), rejectNodes); + } + public void addLeftExtendNode(long left) { this.leftExtendedNodes = LongBitmap.or(this.leftExtendedNodes, left); } @@ -171,6 +179,20 @@ public double getSelectivity() { public abstract List getExpressions(); + public Set getExpressionSet() { + return ImmutableSet.copyOf(getExpressions()); + } + + public boolean canPullUp() { + // Only inner join and filter with none rejectNodes can be pull up + return rejectNodes == 0 + && !(this instanceof JoinEdge && !((JoinEdge) this).getJoinType().isInnerJoin()); + } + + public long getRejectNodes() { + return rejectNodes; + } + public Expression getExpression(int i) { return getExpressions().get(i); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/edge/FilterEdge.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/edge/FilterEdge.java index ec037871025cf6..57c6d9660d089c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/edge/FilterEdge.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/edge/FilterEdge.java @@ -22,7 +22,6 @@ import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; -import java.util.ArrayList; import java.util.BitSet; import java.util.List; import java.util.Set; @@ -32,25 +31,11 @@ */ public class FilterEdge extends Edge { private final LogicalFilter filter; - private final List rejectEdges; public FilterEdge(LogicalFilter filter, int index, BitSet childEdges, long subTreeNodes, long childRequireNodes) { super(index, childEdges, new BitSet(), subTreeNodes, childRequireNodes, 0L); this.filter = filter; - rejectEdges = new ArrayList<>(); - } - - public void addRejectJoin(JoinEdge joinEdge) { - rejectEdges.add(joinEdge.getIndex()); - } - - public List getRejectEdges() { - return rejectEdges; - } - - public boolean isTopFilter() { - return rejectEdges.isEmpty(); } @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java index 90ebe567c4628e..c63e2d85af35ab 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java @@ -136,12 +136,14 @@ protected List rewrite(Plan queryPlan, CascadesContext cascadesContext) { LogicalCompatibilityContext compatibilityContext = LogicalCompatibilityContext.from(queryToViewTableMapping, queryToViewSlotMapping, queryStructInfo, viewStructInfo); - List pulledUpExpressions = StructInfo.isGraphLogicalEquals(queryStructInfo, viewStructInfo, + ComparisonResult comparisonResult = StructInfo.isGraphLogicalEquals(queryStructInfo, viewStructInfo, compatibilityContext); - if (pulledUpExpressions == null) { + if (comparisonResult.isInvalid()) { logger.debug(currentClassName + " graph logical is not equals so continue"); continue; } + // TODO: Use set of list? And consider view expr + List pulledUpExpressions = ImmutableList.copyOf(comparisonResult.getQueryExpressions()); // set pulled up expression to queryStructInfo predicates and update related predicates if (!pulledUpExpressions.isEmpty()) { queryStructInfo.addPredicates(pulledUpExpressions); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/ComparisonResult.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/ComparisonResult.java new file mode 100644 index 00000000000000..cc8284f33e6824 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/ComparisonResult.java @@ -0,0 +1,94 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.rules.exploration.mv; + +import org.apache.doris.nereids.trees.expressions.Expression; + +import com.google.common.collect.ImmutableList; + +import java.util.Collection; +import java.util.List; + +/** + * comparison result of view and query + */ +public class ComparisonResult { + public static final ComparisonResult INVALID = new ComparisonResult(ImmutableList.of(), ImmutableList.of(), false); + public static final ComparisonResult EMPTY = new ComparisonResult(ImmutableList.of(), ImmutableList.of(), true); + private final boolean valid; + private final List viewExpressions; + private final List queryExpressions; + + public ComparisonResult(List queryExpressions, List viewExpressions) { + this(queryExpressions, viewExpressions, true); + } + + ComparisonResult(List queryExpressions, List viewExpressions, boolean valid) { + this.viewExpressions = ImmutableList.copyOf(viewExpressions); + this.queryExpressions = ImmutableList.copyOf(queryExpressions); + this.valid = valid; + } + + public List getViewExpressions() { + return viewExpressions; + } + + public List getQueryExpressions() { + return queryExpressions; + } + + public boolean isInvalid() { + return !valid; + } + + /** + * Builder + */ + public static class Builder { + ImmutableList.Builder queryBuilder = new ImmutableList.Builder<>(); + ImmutableList.Builder viewBuilder = new ImmutableList.Builder<>(); + boolean valid = true; + + /** + * add comparisonResult + */ + public Builder addComparisonResult(ComparisonResult comparisonResult) { + if (comparisonResult.isInvalid()) { + valid = false; + return this; + } + queryBuilder.addAll(comparisonResult.getQueryExpressions()); + viewBuilder.addAll(comparisonResult.getViewExpressions()); + return this; + } + + public Builder addQueryExpressions(Collection expressions) { + queryBuilder.addAll(expressions); + return this; + } + + public Builder addViewExpressions(Collection expressions) { + viewBuilder.addAll(expressions); + return this; + } + + public ComparisonResult build() { + return new ComparisonResult(queryBuilder.build(), viewBuilder.build(), valid); + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java index 3c9814cdce31c5..20da9ee12fdd69 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java @@ -263,7 +263,7 @@ public List getExpressions() { * For inner join should judge only the join tables, * for other join type should also judge the join direction, it's input filter that can not be pulled up etc. */ - public static @Nullable List isGraphLogicalEquals(StructInfo queryStructInfo, StructInfo viewStructInfo, + public static ComparisonResult isGraphLogicalEquals(StructInfo queryStructInfo, StructInfo viewStructInfo, LogicalCompatibilityContext compatibilityContext) { return queryStructInfo.hyperGraph.isLogicCompatible(viewStructInfo.hyperGraph, compatibilityContext); } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/CompareOuterJoinTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/CompareOuterJoinTest.java index d0e5084a1c16cf..cfc88b2aa3cef2 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/CompareOuterJoinTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/CompareOuterJoinTest.java @@ -62,7 +62,7 @@ void testStarGraphWithInnerJoin() { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - Assertions.assertTrue(h1.isLogicCompatible(h2, constructContext(p1, p2)) != null); + Assertions.assertFalse(h1.isLogicCompatible(h2, constructContext(p1, p2)).isInvalid()); } @Test @@ -79,7 +79,7 @@ void testRandomQuery() { .getAllPlan().get(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - Assertions.assertTrue(h1.isLogicCompatible(h2, constructContext(p1, p2)) != null); + Assertions.assertFalse(h1.isLogicCompatible(h2, constructContext(p1, p2)).isInvalid()); } @Test @@ -104,7 +104,7 @@ void testInnerJoinWithFilter() { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - List exprList = h1.isLogicCompatible(h2, constructContext(p1, p2)); + List exprList = h1.isLogicCompatible(h2, constructContext(p1, p2)).getQueryExpressions(); Assertions.assertEquals(1, exprList.size()); Assertions.assertEquals("(id = 0)", exprList.get(0).toSql()); } @@ -132,7 +132,7 @@ void testInnerJoinWithFilter2() { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - List exprList = h1.isLogicCompatible(h2, constructContext(p1, p2)); + List exprList = h1.isLogicCompatible(h2, constructContext(p1, p2)).getQueryExpressions(); Assertions.assertEquals(0, exprList.size()); } @@ -159,7 +159,7 @@ void testLeftOuterJoinWithLeftFilter() { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - List exprList = h1.isLogicCompatible(h2, constructContext(p1, p2)); + List exprList = h1.isLogicCompatible(h2, constructContext(p1, p2)).getQueryExpressions(); Assertions.assertEquals(1, exprList.size()); Assertions.assertEquals("(id = 0)", exprList.get(0).toSql()); } @@ -187,8 +187,7 @@ void testLeftOuterJoinWithRightFilter() { .getAllPlan().get(0).child(0); HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); - List exprList = h1.isLogicCompatible(h2, constructContext(p1, p2)); - Assertions.assertEquals(null, exprList); + Assertions.assertTrue(h1.isLogicCompatible(h2, constructContext(p1, p2)).isInvalid()); } LogicalCompatibilityContext constructContext(Plan p1, Plan p2) { diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/PullupExpressionTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/PullupExpressionTest.java new file mode 100644 index 00000000000000..d4818a844e80ab --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/PullupExpressionTest.java @@ -0,0 +1,151 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.jobs.joinorder.hypergraph; + +import org.apache.doris.nereids.CascadesContext; +import org.apache.doris.nereids.rules.RuleSet; +import org.apache.doris.nereids.rules.exploration.mv.AbstractMaterializedViewRule; +import org.apache.doris.nereids.rules.exploration.mv.ComparisonResult; +import org.apache.doris.nereids.rules.exploration.mv.LogicalCompatibilityContext; +import org.apache.doris.nereids.rules.exploration.mv.StructInfo; +import org.apache.doris.nereids.rules.exploration.mv.mapping.RelationMapping; +import org.apache.doris.nereids.rules.exploration.mv.mapping.SlotMapping; +import org.apache.doris.nereids.sqltest.SqlTestBase; +import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.util.PlanChecker; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class PullupExpressionTest extends SqlTestBase { + @Test + void testPullUpQueryFilter() { + CascadesContext c1 = createCascadesContext( + "select * from T1 join T2 on T1.id = T2.id where T1.id = 1", + connectContext + ); + Plan p1 = PlanChecker.from(c1) + .analyze() + .rewrite() + .getPlan().child(0); + CascadesContext c2 = createCascadesContext( + "select * from T1 join T2 on T1.id = T2.id", + connectContext + ); + Plan p2 = PlanChecker.from(c2) + .analyze() + .rewrite() + .applyExploration(RuleSet.BUSHY_TREE_JOIN_REORDER) + .getAllPlan().get(0).child(0); + HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); + HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); + ComparisonResult res = h1.isLogicCompatible(h2, constructContext(p1, p2)); + Assertions.assertEquals(2, res.getQueryExpressions().size()); + Assertions.assertEquals("(id = 1)", res.getQueryExpressions().get(0).toSql()); + Assertions.assertEquals("(id = 1)", res.getQueryExpressions().get(1).toSql()); + } + + @Test + void testPullUpQueryJoinCondition() { + CascadesContext c1 = createCascadesContext( + "select * from T1 join T2 on T1.id = T2.id and T1.score = T2.score", + connectContext + ); + Plan p1 = PlanChecker.from(c1) + .analyze() + .rewrite() + .getPlan().child(0); + CascadesContext c2 = createCascadesContext( + "select * from T1 join T2 on T1.id = T2.id", + connectContext + ); + Plan p2 = PlanChecker.from(c2) + .analyze() + .rewrite() + .applyExploration(RuleSet.BUSHY_TREE_JOIN_REORDER) + .getAllPlan().get(0).child(0); + HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); + HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); + ComparisonResult res = h1.isLogicCompatible(h2, constructContext(p1, p2)); + Assertions.assertEquals(1, res.getQueryExpressions().size()); + Assertions.assertEquals("(score = score)", res.getQueryExpressions().get(0).toSql()); + } + + @Test + void testPullUpViewFilter() { + CascadesContext c1 = createCascadesContext( + "select * from T1 join T2 on T1.id = T2.id", + connectContext + ); + Plan p1 = PlanChecker.from(c1) + .analyze() + .rewrite() + .getPlan().child(0); + CascadesContext c2 = createCascadesContext( + "select * from T1 join T2 on T1.id = T2.id where T1.id = 1 and T2.id = 1", + connectContext + ); + Plan p2 = PlanChecker.from(c2) + .analyze() + .rewrite() + .applyExploration(RuleSet.BUSHY_TREE_JOIN_REORDER) + .getAllPlan().get(0).child(0); + HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); + HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); + ComparisonResult res = h1.isLogicCompatible(h2, constructContext(p1, p2)); + Assertions.assertEquals(2, res.getViewExpressions().size()); + Assertions.assertEquals("(id = 1)", res.getViewExpressions().get(0).toSql()); + Assertions.assertEquals("(id = 1)", res.getViewExpressions().get(1).toSql()); + } + + @Test + void testPullUpViewJoinCondition() { + CascadesContext c1 = createCascadesContext( + "select * from T1 join T2 on T1.id = T2.id ", + connectContext + ); + Plan p1 = PlanChecker.from(c1) + .analyze() + .rewrite() + .getPlan().child(0); + CascadesContext c2 = createCascadesContext( + "select * from T1 join T2 on T1.id = T2.id and T1.score = T2.score", + connectContext + ); + Plan p2 = PlanChecker.from(c2) + .analyze() + .rewrite() + .applyExploration(RuleSet.BUSHY_TREE_JOIN_REORDER) + .getAllPlan().get(0).child(0); + HyperGraph h1 = HyperGraph.toStructInfo(p1).get(0); + HyperGraph h2 = HyperGraph.toStructInfo(p2).get(0); + ComparisonResult res = h1.isLogicCompatible(h2, constructContext(p1, p2)); + Assertions.assertEquals(1, res.getViewExpressions().size()); + Assertions.assertEquals("(score = score)", res.getViewExpressions().get(0).toSql()); + } + + LogicalCompatibilityContext constructContext(Plan p1, Plan p2) { + StructInfo st1 = AbstractMaterializedViewRule.extractStructInfo(p1, + null).get(0); + StructInfo st2 = AbstractMaterializedViewRule.extractStructInfo(p2, + null).get(0); + RelationMapping rm = RelationMapping.generate(st1.getRelations(), st2.getRelations()).get(0); + SlotMapping sm = SlotMapping.generate(rm); + return LogicalCompatibilityContext.from(rm, sm, st1, st2); + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/BuildStructInfoTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/BuildStructInfoTest.java index 6ac41a49812f44..f68365f6708b37 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/BuildStructInfoTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/BuildStructInfoTest.java @@ -79,7 +79,7 @@ void testFilter() { .when(j -> { HyperGraph structInfo = HyperGraph.toStructInfo(j).get(0); Assertions.assertTrue(structInfo.getJoinEdge(0).getJoinType().isLeftOuterJoin()); - Assertions.assertEquals(0, (int) structInfo.getFilterEdge(0).getRejectEdges().get(0)); + Assertions.assertEquals(3L, structInfo.getFilterEdge(0).getRejectNodes()); return true; })); @@ -92,7 +92,7 @@ void testFilter() { .when(j -> { HyperGraph structInfo = HyperGraph.toStructInfo(j).get(0); Assertions.assertTrue(structInfo.getJoinEdge(0).getJoinType().isLeftOuterJoin()); - Assertions.assertTrue(structInfo.getFilterEdge(0).getRejectEdges().isEmpty()); + Assertions.assertEquals(0, structInfo.getFilterEdge(0).getRejectNodes()); return true; })); } From c2eabbd441021732560d1e81365fb6e29fb812fd Mon Sep 17 00:00:00 2001 From: Kaijie Chen Date: Mon, 25 Dec 2023 13:49:18 +0800 Subject: [PATCH 007/299] [fix](load) fix nullptr when getting memtable flush running count (#28942) * [fix](load) fix nullptr when getting memtable flush running count * style --- be/src/olap/delta_writer.cpp | 3 +-- be/src/olap/delta_writer_v2.cpp | 4 +--- be/src/olap/memtable_writer.cpp | 4 ++++ be/src/olap/memtable_writer.h | 2 ++ 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/be/src/olap/delta_writer.cpp b/be/src/olap/delta_writer.cpp index 1c2f24ae6d94d3..f49a68985d8606 100644 --- a/be/src/olap/delta_writer.cpp +++ b/be/src/olap/delta_writer.cpp @@ -126,8 +126,7 @@ Status BaseDeltaWriter::write(const vectorized::Block* block, const std::vector< if (!_is_init && !_is_cancelled) { RETURN_IF_ERROR(init()); } - while (_memtable_writer->get_flush_token_stats().flush_running_count >= - config::memtable_flush_running_count_limit) { + while (_memtable_writer->flush_running_count() >= config::memtable_flush_running_count_limit) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } return _memtable_writer->write(block, row_idxs, is_append); diff --git a/be/src/olap/delta_writer_v2.cpp b/be/src/olap/delta_writer_v2.cpp index cfe059f1890c89..bca1213b047480 100644 --- a/be/src/olap/delta_writer_v2.cpp +++ b/be/src/olap/delta_writer_v2.cpp @@ -37,7 +37,6 @@ #include "gutil/strings/numbers.h" #include "io/fs/file_writer.h" // IWYU pragma: keep #include "olap/data_dir.h" -#include "olap/memtable_flush_executor.h" #include "olap/olap_define.h" #include "olap/rowset/beta_rowset.h" #include "olap/rowset/beta_rowset_writer_v2.h" @@ -153,8 +152,7 @@ Status DeltaWriterV2::write(const vectorized::Block* block, const std::vectorget_flush_token_stats().flush_running_count >= - config::memtable_flush_running_count_limit) { + while (_memtable_writer->flush_running_count() >= config::memtable_flush_running_count_limit) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } SCOPED_RAW_TIMER(&_write_memtable_time); diff --git a/be/src/olap/memtable_writer.cpp b/be/src/olap/memtable_writer.cpp index 1faed6c328b167..c967cdbd483968 100644 --- a/be/src/olap/memtable_writer.cpp +++ b/be/src/olap/memtable_writer.cpp @@ -345,6 +345,10 @@ const FlushStatistic& MemTableWriter::get_flush_token_stats() { return _flush_token->get_stats(); } +uint64_t MemTableWriter::flush_running_count() const { + return _flush_token == nullptr ? 0 : _flush_token->get_stats().flush_running_count.load(); +} + int64_t MemTableWriter::mem_consumption(MemType mem) { if (!_is_init) { // This method may be called before this writer is initialized. diff --git a/be/src/olap/memtable_writer.h b/be/src/olap/memtable_writer.h index c5459c09065cda..a2687d9402cdca 100644 --- a/be/src/olap/memtable_writer.h +++ b/be/src/olap/memtable_writer.h @@ -109,6 +109,8 @@ class MemTableWriter { const FlushStatistic& get_flush_token_stats(); + uint64_t flush_running_count() const; + private: // push a full memtable to flush executor Status _flush_memtable_async(); From f2cdf150781c7e7b9c5c05aee47f5be2b24d7ce5 Mon Sep 17 00:00:00 2001 From: deardeng <565620795@qq.com> Date: Mon, 25 Dec 2023 14:06:48 +0800 Subject: [PATCH 008/299] (enhance)(regression) Support `force_olap_table_replication_num=3` run test_insert_random_distribution_table case (#28903) --- ...st_insert_random_distribution_table.groovy | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/regression-test/suites/load_p0/insert/test_insert_random_distribution_table.groovy b/regression-test/suites/load_p0/insert/test_insert_random_distribution_table.groovy index 4630264ab41353..ae9f81400a96fb 100644 --- a/regression-test/suites/load_p0/insert/test_insert_random_distribution_table.groovy +++ b/regression-test/suites/load_p0/insert/test_insert_random_distribution_table.groovy @@ -44,11 +44,25 @@ suite("test_insert_random_distribution_table", "p0") { def totalCount = sql "select count() from ${tableName}" assertEquals(totalCount[0][0], 4) def res = sql "show tablets from ${tableName}" - def tabletId1 = res[0][0] - def tabletId2 = res[1][0] - def tabletId3 = res[2][0] - def tabletId4 = res[3][0] - def tabletId5 = res[4][0] + def getTablets = { result -> + def map = [:] + def tablets = [] + for (int i = 0; i < result.size(); i++) { + if (!map.get(result[i][0])) { + tablets.add(result[i][0]) + map.put(result[i][0], result[i]) + } + } + return tablets + } + + def tablets = getTablets.call(res) + + def tabletId1 = tablets[0] + def tabletId2 = tablets[1] + def tabletId3 = tablets[2] + def tabletId4 = tablets[3] + def tabletId5 = tablets[4] def rowCount1 = sql "select count() from ${tableName} tablet(${tabletId1})" def rowCount2 = sql "select count() from ${tableName} tablet(${tabletId2})" @@ -132,25 +146,28 @@ suite("test_insert_random_distribution_table", "p0") { def partition3 = "p20231013" assertEquals(totalCount[0][0], 5) res = sql "show tablets from ${tableName} partition ${partition1}" - def tabletId11 = res[0][0] - def tabletId12 = res[1][0] - def tabletId13 = res[2][0] - def tabletId14 = res[3][0] - def tabletId15 = res[4][0] + tablets = getTablets.call(res) + def tabletId11 = tablets[0] + def tabletId12 = tablets[1] + def tabletId13 = tablets[2] + def tabletId14 = tablets[3] + def tabletId15 = tablets[4] res = sql "show tablets from ${tableName} partition ${partition2}" - def tabletId21 = res[0][0] - def tabletId22 = res[1][0] - def tabletId23 = res[2][0] - def tabletId24 = res[3][0] - def tabletId25 = res[4][0] + tablets = getTablets.call(res) + def tabletId21 = tablets[0] + def tabletId22 = tablets[1] + def tabletId23 = tablets[2] + def tabletId24 = tablets[3] + def tabletId25 = tablets[4] res = sql "show tablets from ${tableName} partition ${partition3}" - def tabletId31 = res[0][0] - def tabletId32 = res[1][0] - def tabletId33 = res[2][0] - def tabletId34 = res[3][0] - def tabletId35 = res[4][0] + tablets = getTablets.call(res) + def tabletId31 = tablets[0] + def tabletId32 = tablets[1] + def tabletId33 = tablets[2] + def tabletId34 = tablets[3] + def tabletId35 = tablets[4] def rowCount11 = sql "select count() from ${tableName} tablet(${tabletId11})" def rowCount12 = sql "select count() from ${tableName} tablet(${tabletId12})" From 29d3d5e433bdb55f3692ddd3d5956cf9ba4399a4 Mon Sep 17 00:00:00 2001 From: Calvin Kirs Date: Mon, 25 Dec 2023 15:12:14 +0800 Subject: [PATCH 009/299] [Test](Job)Add test case (#28481) --- .../apache/doris/analysis/CreateJobStmt.java | 22 ++-- .../suites/job_p0/test_base_insert_job.groovy | 108 ++++++++++++++++-- 2 files changed, 104 insertions(+), 26 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateJobStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateJobStmt.java index dc2083debc2bfc..ececccc3169715 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateJobStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateJobStmt.java @@ -40,7 +40,6 @@ import org.apache.commons.lang3.StringUtils; import java.util.HashSet; -import java.util.Set; /** * syntax: @@ -86,15 +85,11 @@ public class CreateJobStmt extends DdlStmt { private JobExecuteType executeType; // exclude job name prefix, which is used by inner job - private final Set excludeJobNamePrefix = new HashSet<>(); - - { - excludeJobNamePrefix.add("inner_mtmv_"); - } + private final String excludeJobNamePrefix = "inner_"; private static final ImmutableSet> supportStmtSuperClass = new ImmutableSet.Builder>().add(InsertStmt.class) - .add(UpdateStmt.class).build(); + .build(); private static final HashSet supportStmtClassNamesCache = new HashSet<>(16); @@ -164,16 +159,15 @@ public void analyze(Analyzer analyzer) throws UserException { String originStmt = getOrigStmt().originStmt; String executeSql = parseExecuteSql(originStmt); job.setExecuteSql(executeSql); - - //job.checkJobParams(); jobInstance = job; } private void checkJobName(String jobName) throws AnalysisException { - for (String prefix : excludeJobNamePrefix) { - if (jobName.startsWith(prefix)) { - throw new AnalysisException("job name can not start with " + prefix); - } + if (StringUtils.isBlank(jobName)) { + throw new AnalysisException("job name can not be null"); + } + if (jobName.startsWith(excludeJobNamePrefix)) { + throw new AnalysisException("job name can not start with " + excludeJobNamePrefix); } } @@ -193,7 +187,7 @@ private void checkStmtSupport() throws AnalysisException { return; } } - throw new AnalysisException("Not support this stmt type"); + throw new AnalysisException("Not support " + doStmt.getClass().getSimpleName() + " type in job"); } private void analyzerSqlStmt() throws UserException { diff --git a/regression-test/suites/job_p0/test_base_insert_job.groovy b/regression-test/suites/job_p0/test_base_insert_job.groovy index 035def7a2d1424..a6f151141b921f 100644 --- a/regression-test/suites/job_p0/test_base_insert_job.groovy +++ b/regression-test/suites/job_p0/test_base_insert_job.groovy @@ -23,10 +23,32 @@ import java.time.ZoneId; suite("test_base_insert_job") { def tableName = "t_test_BASE_inSert_job" def jobName = "insert_recovery_test_base_insert_job" + def jobMixedName = "Insert_recovery_Test_base_insert_job" sql """drop table if exists `${tableName}` force""" sql """ DROP JOB where jobname = '${jobName}' """ + sql """ + DROP JOB where jobname = 'JOB' + """ + sql """ + DROP JOB where jobname = 'DO' + """ + sql """ + DROP JOB where jobname = 'AT' + """ + sql """ + DROP JOB where jobname = 'SCHEDULE' + """ + sql """ + DROP JOB where jobname = 'STARTS' + """ + sql """ + DROP JOB where jobname = 'ENDS' + """ + sql """ + DROP JOB where jobname = '${jobMixedName}' + """ sql """ CREATE TABLE IF NOT EXISTS `${tableName}` @@ -48,9 +70,20 @@ suite("test_base_insert_job") { def jobs = sql """select * from ${tableName}""" println jobs assert 3>=jobs.size() >= (2 as Boolean) //at least 2 records, some times 3 records + sql """ + CREATE JOB ${jobMixedName} ON SCHEDULE every 1 second DO insert into ${tableName} (timestamp, type, user_id) values ('2023-03-18','1','12213'); + """ + def mixedNameJobs = sql """select name,comment from jobs("type"="insert") where Name='${jobMixedName}'""" + println mixedNameJobs + assert mixedNameJobs.size() == 1 && mixedNameJobs.get(0).get(0) == jobMixedName + assert mixedNameJobs.get(0).get(1) == '' sql """ DROP JOB where jobname = '${jobName}' """ + sql """ + DROP JOB where jobname = '${jobMixedName}' + """ + sql """drop table if exists `${tableName}` force """ sql """ CREATE TABLE IF NOT EXISTS `${tableName}` @@ -84,7 +117,6 @@ suite("test_base_insert_job") { def onceJobSql= onceJob.get(0).get(1); println onceJobSql def assertSql = "insert into ${tableName} values (\'2023-07-19\', sleep(10000), 1001);" - println 'hhh' println assertSql assert onceJobSql == assertSql // test cancel task @@ -108,7 +140,34 @@ suite("test_base_insert_job") { assert oncejob.get(0).get(0) == "FINISHED" //assert comment assert oncejob.get(0).get(1) == "test for test&68686781jbjbhj//ncsa" - + + // assert same job name + try { + sql """ + CREATE JOB ${jobName} ON SCHEDULE EVERY 10 second comment 'test for test&68686781jbjbhj//ncsa' DO insert into ${tableName} values ('2023-07-19', sleep(10000), 1001); + """ + }catch (Exception e) { + assert e.getMessage().contains("job name exist, jobName:insert_recovery_test_base_insert_job") + } + def errorTblName="${tableName}qwertyuioppoiuyte" + sql """drop table if exists `${errorTblName}` force""" + // assert error table name + try { + sql """ + CREATE JOB ${jobName} ON SCHEDULE EVERY 10 second comment 'test for test&68686781jbjbhj//ncsa' DO insert into ${errorTblName} values ('2023-07-19', sleep(10000), 1001); + """ + }catch (Exception e) { + assert e.getMessage().contains("Unknown table 't_test_BASE_inSert_jobqwertyuioppoiuyte'") + } + // assert not support stmt + try{ + sql """ + CREATE JOB ${jobName} ON SCHEDULE at '${startTime}' comment 'test' DO update ${tableName} set type=2 where type=1; + """ + } catch (Exception e) { + assert e.getMessage().contains("Not support UpdateStmt type in job") + } + // assert start time greater than current time try{ sql """ CREATE JOB ${jobName} ON SCHEDULE at '${startTime}' comment 'test' DO insert into ${tableName} (timestamp, type, user_id) values ('2023-03-18','1','12213'); @@ -116,9 +175,7 @@ suite("test_base_insert_job") { } catch (Exception e) { assert e.getMessage().contains("startTimeMs must be greater than current time") } - sql """ - DROP JOB where jobname = 'test_one_time_error_starts' - """ + // assert end time less than start time try{ sql """ CREATE JOB test_one_time_error_starts ON SCHEDULE at '2023-11-13 14:18:07' comment 'test' DO insert into ${tableName} (timestamp, type, user_id) values ('2023-03-18','1','12213'); @@ -126,9 +183,14 @@ suite("test_base_insert_job") { } catch (Exception e) { assert e.getMessage().contains("startTimeMs must be greater than current time") } - sql """ - DROP JOB where jobname = 'test_error_starts' - """ + try{ + sql """ + CREATE JOB inner_test ON SCHEDULE at '2023-11-13 14:18:07' comment 'test' DO insert into ${tableName} (timestamp, type, user_id) values ('2023-03-18','1','12213'); + """ + } catch (Exception e) { + assert e.getMessage().contains("job name can not start with inner_") + } + // assert end time less than start time try{ sql """ CREATE JOB test_error_starts ON SCHEDULE every 1 second ends '2023-11-13 14:18:07' comment 'test' DO insert into ${tableName} (timestamp, type, user_id) values ('2023-03-18','1','12213'); @@ -136,10 +198,7 @@ suite("test_base_insert_job") { } catch (Exception e) { assert e.getMessage().contains("end time cannot be less than start time") } - - sql """ - DROP JOB where jobname = 'test_error_starts' - """ + // assert interval time unit can not be years try{ sql """ CREATE JOB test_error_starts ON SCHEDULE every 1 years ends '2023-11-13 14:18:07' comment 'test' DO insert into ${tableName} (timestamp, type, user_id) values ('2023-03-18','1','12213'); @@ -147,5 +206,30 @@ suite("test_base_insert_job") { } catch (Exception e) { assert e.getMessage().contains("interval time unit can not be years") } + + // test keyword as job name + sql """ + CREATE JOB JOB ON SCHEDULE every 20 second comment 'test' DO insert into ${tableName} (timestamp, type, user_id) values ('2023-03-18','1','12213'); + """ + sql """ + CREATE JOB SCHEDULE ON SCHEDULE every 20 second comment 'test' DO insert into ${tableName} (timestamp, type, user_id) values ('2023-03-18','1','12213'); + """ + sql """ + CREATE JOB DO ON SCHEDULE every 20 second comment 'test' DO insert into ${tableName} (timestamp, type, user_id) values ('2023-03-18','1','12213'); + """ + sql """ + CREATE JOB AT ON SCHEDULE every 20 second comment 'test' DO insert into ${tableName} (timestamp, type, user_id) values ('2023-03-18','1','12213'); + """ + + sql """ + CREATE JOB STARTS ON SCHEDULE every 20 second comment 'test' DO insert into ${tableName} (timestamp, type, user_id) values ('2023-03-18','1','12213'); + """ + + sql """ + CREATE JOB ENDS ON SCHEDULE every 20 second comment 'test' DO insert into ${tableName} (timestamp, type, user_id) values ('2023-03-18','1','12213'); + """ + + def jobCountRsp = sql"""select count(1) from jobs("type"="insert") where name in ('JOB','DO','SCHEDULE','AT','STARTS','ENDS')""" + assert jobCountRsp.get(0).get(0) == 6 } From 8e47fd28e2fad52abb90fe0dd6319f1c90958565 Mon Sep 17 00:00:00 2001 From: Nitin-Kashyap <66766227+Nitin-Kashyap@users.noreply.github.com> Date: Mon, 25 Dec 2023 14:49:18 +0700 Subject: [PATCH 010/299] [fix](doc) typo fix in auto-partition page (#28512) --- docs/en/docs/advanced/partition/auto-partition.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/docs/advanced/partition/auto-partition.md b/docs/en/docs/advanced/partition/auto-partition.md index 4117a0d90a29ba..398135ecd91dec 100644 --- a/docs/en/docs/advanced/partition/auto-partition.md +++ b/docs/en/docs/advanced/partition/auto-partition.md @@ -81,7 +81,7 @@ PROPERTIES ( The table stores a large amount of business history data, partitioned based on the date the transaction occurred. As you can see when building the table, we need to manually create the partitions in advance. If the data range of the partitioned columns changes, for example, 2022 is added to the above table, we need to create a partition by [ALTER-TABLE-PARTITION](../../sql-manual/sql-reference/Data-Definition-Statements/Alter/ALTER-TABLE-PARTITION) to make changes to the table partition. If such partitions need to be changed, or subdivided at a finer level of granularity, it is very tedious to modify them. At this point we can rewrite the table DDL using AUTO PARTITION. -## Grammer +## Grammar When building a table, use the following syntax to populate [CREATE-TABLE](../../sql-manual/sql-reference/Data-Definition-Statements/Create/CREATE-TABLE) with the `partition_info` section: @@ -143,7 +143,7 @@ When building a table, use the following syntax to populate [CREATE-TABLE](../.. 1. Currently the AUTO RANGE PARTITION function supports only one partition column; 2. In AUTO RANGE PARTITION, the partition function supports only `date_trunc` and the partition column supports only `DATEV2` or `DATETIMEV2` format; -3. In AUTO LIST PARTITION, function calls are not supported. Partitioned columns support `BOOLEAN`, `TINYINT`, `SMALLINT`, `INT`, `BIGINT`, `LARGEINT`, `DATE`, `DATETIME`, `CHAR`, `VARCHAR` datatypes, and partitioned values are enum values. +3. In AUTO LIST PARTITION, function calls are not supported. Partitioned columns support `BOOLEAN`, `TINYINT`, `SMALLINT`, `INT`, `BIGINT`, `LARGEINT`, `DATE`, `DATETIME`, `CHAR`, `VARCHAR` data-types, and partitioned values are enum values. 4. In AUTO LIST PARTITION, a separate new PARTITION is created for each fetch of a partition column for which the corresponding partition does not currently exist. ## Sample Scenarios From 17917a0adb57ac337dfd41e8853b1aa87c9d5acd Mon Sep 17 00:00:00 2001 From: Kaijie Chen Date: Mon, 25 Dec 2023 16:27:16 +0800 Subject: [PATCH 011/299] [feature](load) enable memtable on sink node by default (#28963) --- docs/en/docs/advanced/variables.md | 2 +- docs/zh-CN/docs/advanced/variables.md | 2 +- .../src/main/java/org/apache/doris/qe/SessionVariable.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/docs/advanced/variables.md b/docs/en/docs/advanced/variables.md index 030e228f5234aa..e339b1893dc789 100644 --- a/docs/en/docs/advanced/variables.md +++ b/docs/en/docs/advanced/variables.md @@ -689,7 +689,7 @@ Note that the comment must start with /*+ and can only follow the SELECT. * `enable_memtable_on_sink_node` - Whether to enable MemTable on DataSink node when loading data, default is false. + Whether to enable MemTable on DataSink node when loading data, default is true. Build MemTable on DataSink node, and send segments to other backends through brpc streaming. diff --git a/docs/zh-CN/docs/advanced/variables.md b/docs/zh-CN/docs/advanced/variables.md index 2a697b01bf7de4..510ad85c36e9ff 100644 --- a/docs/zh-CN/docs/advanced/variables.md +++ b/docs/zh-CN/docs/advanced/variables.md @@ -677,7 +677,7 @@ try (Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:9030/ * `enable_memtable_on_sink_node` - 是否在数据导入中启用 MemTable 前移,默认为 false + 是否在数据导入中启用 MemTable 前移,默认为 true 在 DataSink 节点上构建 MemTable,并通过 brpc streaming 发送 segment 到其他 BE。 diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java index b9658bf34a5802..332e0f5bfaa8f7 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java @@ -1322,7 +1322,7 @@ public void setEnableLeftZigZag(boolean enableLeftZigZag) { public boolean truncateCharOrVarcharColumns = false; @VariableMgr.VarAttr(name = ENABLE_MEMTABLE_ON_SINK_NODE, needForward = true) - public boolean enableMemtableOnSinkNode = false; + public boolean enableMemtableOnSinkNode = true; @VariableMgr.VarAttr(name = LOAD_STREAM_PER_NODE) public int loadStreamPerNode = 20; From 2dc7d82affa6a4ca47f749cfdcba8b2db6ef8c9a Mon Sep 17 00:00:00 2001 From: Calvin Kirs Date: Mon, 25 Dec 2023 16:55:59 +0800 Subject: [PATCH 012/299] [Chore](Job)print log before task execute (#28962) --- .../apache/doris/job/executor/DefaultTaskExecutorHandler.java | 3 ++- .../org/apache/doris/job/executor/DispatchTaskHandler.java | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/job/executor/DefaultTaskExecutorHandler.java b/fe/fe-core/src/main/java/org/apache/doris/job/executor/DefaultTaskExecutorHandler.java index a07e248af6d593..befa8cc35fcbcc 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/job/executor/DefaultTaskExecutorHandler.java +++ b/fe/fe-core/src/main/java/org/apache/doris/job/executor/DefaultTaskExecutorHandler.java @@ -42,9 +42,10 @@ public void onEvent(ExecuteTaskEvent executeTaskEvent) { return; } if (task.isCancelled()) { - log.info("task is canceled, ignore"); + log.info("task is canceled, ignore. task id is {}", task.getTaskId()); return; } + log.info("start to execute task, task id is {}", task.getTaskId()); try { task.runTask(); } catch (Exception e) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/job/executor/DispatchTaskHandler.java b/fe/fe-core/src/main/java/org/apache/doris/job/executor/DispatchTaskHandler.java index cb0a393d9175c3..e5933d133cbd61 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/job/executor/DispatchTaskHandler.java +++ b/fe/fe-core/src/main/java/org/apache/doris/job/executor/DispatchTaskHandler.java @@ -67,6 +67,8 @@ public void onEvent(TimerJobEvent event) { JobType jobType = event.getJob().getJobType(); for (AbstractTask task : tasks) { disruptorMap.get(jobType).publishEvent(task, event.getJob().getJobConfig()); + log.info("dispatch timer job success, job id is {}, task id is {}", + event.getJob().getJobId(), task.getTaskId()); } } } catch (Exception e) { From 91e5b47439692068a74f84ad80dc543d34fd561e Mon Sep 17 00:00:00 2001 From: walter Date: Mon, 25 Dec 2023 19:18:01 +0800 Subject: [PATCH 013/299] [fix](hdfs) Fix HdfsFileSystem::exists_impl crash (#28952) Calling hdfsGetLastExceptionRootCause without initializing ThreadLocalState will crash. This PR modifies the condition for determining the existence of a hdfs file, because hdfsExists will set errno to ENOENT when the file does not exist, we can use this condition to check whether a file existence rather than check the existence of the root cause. --- be/src/io/fs/hdfs_file_system.cpp | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/be/src/io/fs/hdfs_file_system.cpp b/be/src/io/fs/hdfs_file_system.cpp index 7e0062d2430c7f..be508aa23a922f 100644 --- a/be/src/io/fs/hdfs_file_system.cpp +++ b/be/src/io/fs/hdfs_file_system.cpp @@ -17,6 +17,7 @@ #include "io/fs/hdfs_file_system.h" +#include #include #include #include @@ -227,28 +228,20 @@ Status HdfsFileSystem::delete_internal(const Path& path, int is_recursive) { Status HdfsFileSystem::exists_impl(const Path& path, bool* res) const { CHECK_HDFS_HANDLE(_fs_handle); Path real_path = convert_path(path, _fs_name); -#ifdef USE_HADOOP_HDFS - // HACK: the HDFS native client won't clear the last exception as expected so - // `hdfsGetLastExceptionRootCause` might return a staled root cause. Save the - // last root cause here and verify after hdfsExists returns a non-zero code. - // - // See details: - // https://github.com/apache/hadoop/blob/5cda162a804fb0cfc2a5ac0058ab407662c5fb00/ - // hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfs/jni_helper.c#L795 - char* former_root_cause = hdfsGetLastExceptionRootCause(); -#endif int is_exists = hdfsExists(_fs_handle->hdfs_fs, real_path.string().c_str()); #ifdef USE_HADOOP_HDFS // when calling hdfsExists() and return non-zero code, - // if root_cause is nullptr, which means the file does not exist. - // if root_cause is not nullptr, which means it encounter other error, should return. + // if errno is ENOENT, which means the file does not exist. + // if errno is not ENOENT, which means it encounter other error, should return. // NOTE: not for libhdfs3 since it only runs on MaxOS, don't have to support it. - if (is_exists != 0) { + // + // See details: + // https://github.com/apache/hadoop/blob/5cda162a804fb0cfc2a5ac0058ab407662c5fb00/ + // hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfs/hdfs.c#L1923-L1924 + if (is_exists != 0 && errno != ENOENT) { char* root_cause = hdfsGetLastExceptionRootCause(); - if (root_cause != nullptr && root_cause != former_root_cause) { - return Status::IOError("failed to check path existence {}: {}", path.native(), - root_cause); - } + return Status::IOError("failed to check path existence {}: {}", path.native(), + (root_cause ? root_cause : "unknown")); } #endif *res = (is_exists == 0); From 7081139bdce53e3536c8e25e45a9b4cb92e4f43d Mon Sep 17 00:00:00 2001 From: caiconghui <55968745+caiconghui@users.noreply.github.com> Date: Mon, 25 Dec 2023 20:35:22 +0800 Subject: [PATCH 014/299] [fix](block) fix be core while mutable block merge may cause different row size between columns in origin block (#27943) --- be/src/exec/exec_node.cpp | 1 + .../olap/rowset/segment_v2/segment_writer.cpp | 4 +- be/src/olap/tablet.cpp | 2 + be/src/pipeline/exec/join_probe_operator.cpp | 5 +- .../exec/nested_loop_join_probe_operator.cpp | 32 ++++++----- .../exec/nested_loop_join_probe_operator.h | 6 +-- be/src/pipeline/pipeline_x/operator.cpp | 1 + be/src/vec/common/sort/sorter.cpp | 1 + be/src/vec/core/block.cpp | 9 ++-- be/src/vec/exec/format/csv/csv_reader.cpp | 6 ++- be/src/vec/exec/format/orc/vorc_reader.cpp | 6 +-- .../exec/format/parquet/vparquet_reader.cpp | 5 +- .../vec/exec/format/table/iceberg_reader.cpp | 4 +- be/src/vec/exec/join/vjoin_node_base.cpp | 5 +- .../vec/exec/join/vnested_loop_join_node.cpp | 15 +++--- be/src/vec/exec/join/vnested_loop_join_node.h | 22 ++++---- be/src/vec/exec/scan/pip_scanner_context.h | 1 + be/src/vec/exec/scan/scanner_context.cpp | 1 + be/src/vec/exec/scan/scanner_scheduler.cpp | 4 +- be/src/vec/exec/vpartition_sort_node.h | 1 + be/src/vec/exec/vrepeat_node.cpp | 2 +- be/src/vec/exec/vtable_function_node.cpp | 2 +- be/src/vec/exec/vunion_node.cpp | 4 ++ be/src/vec/olap/block_reader.cpp | 8 ++- be/src/vec/olap/vcollect_iterator.cpp | 4 +- be/src/vec/olap/vertical_block_reader.cpp | 2 + be/src/vec/olap/vgeneric_iterators.cpp | 5 +- be/src/vec/runtime/vsorted_run_merger.cpp | 1 + be/test/vec/core/block_test.cpp | 53 +++++++++++++++++++ 29 files changed, 146 insertions(+), 66 deletions(-) diff --git a/be/src/exec/exec_node.cpp b/be/src/exec/exec_node.cpp index 4681218dcae824..7d38ee5e651611 100644 --- a/be/src/exec/exec_node.cpp +++ b/be/src/exec/exec_node.cpp @@ -561,6 +561,7 @@ Status ExecNode::do_projections(vectorized::Block* origin_block, vectorized::Blo } } DCHECK(mutable_block.rows() == rows); + output_block->set_columns(std::move(mutable_columns)); } return Status::OK(); diff --git a/be/src/olap/rowset/segment_v2/segment_writer.cpp b/be/src/olap/rowset/segment_v2/segment_writer.cpp index 713448e1aaee11..a36b83f23b9f1d 100644 --- a/be/src/olap/rowset/segment_v2/segment_writer.cpp +++ b/be/src/olap/rowset/segment_v2/segment_writer.cpp @@ -519,6 +519,7 @@ Status SegmentWriter::append_block_with_partial_content(const vectorized::Block* auto mutable_full_columns = full_block.mutate_columns(); RETURN_IF_ERROR(fill_missing_columns(mutable_full_columns, use_default_or_null_flag, has_default_or_nullable, segment_start_pos)); + full_block.set_columns(std::move(mutable_full_columns)); // row column should be filled here if (_tablet_schema->store_row_column()) { // convert block to row store format @@ -586,7 +587,6 @@ Status SegmentWriter::fill_missing_columns(vectorized::MutableColumns& mutable_f const auto& cids_missing = _opts.rowset_ctx->partial_update_info->missing_cids; auto old_value_block = _tablet_schema->create_block_by_cids(cids_missing); CHECK(cids_missing.size() == old_value_block.columns()); - auto mutable_old_columns = old_value_block.mutate_columns(); bool has_row_column = _tablet_schema->store_row_column(); // record real pos, key is input line num, value is old_block line num std::map read_index; @@ -609,6 +609,7 @@ Status SegmentWriter::fill_missing_columns(vectorized::MutableColumns& mutable_f } continue; } + auto mutable_old_columns = old_value_block.mutate_columns(); for (size_t cid = 0; cid < mutable_old_columns.size(); ++cid) { TabletColumn tablet_column = _tablet_schema->column(cids_missing[cid]); auto st = tablet->fetch_value_by_rowids(rowset, seg_it.first, rids, tablet_column, @@ -619,6 +620,7 @@ Status SegmentWriter::fill_missing_columns(vectorized::MutableColumns& mutable_f return st; } } + old_value_block.set_columns(std::move(mutable_old_columns)); } } // build default value columns diff --git a/be/src/olap/tablet.cpp b/be/src/olap/tablet.cpp index a5f34633d79240..5f915ad1de16c9 100644 --- a/be/src/olap/tablet.cpp +++ b/be/src/olap/tablet.cpp @@ -3199,6 +3199,7 @@ Status Tablet::generate_new_block_for_partial_update( read_index_update[idx]); } } + output_block->set_columns(std::move(full_mutable_columns)); VLOG_DEBUG << "full block when publish: " << output_block->dump_data(); return Status::OK(); } @@ -3244,6 +3245,7 @@ Status Tablet::read_columns_by_plan(TabletSchemaSPtr tablet_schema, } } } + block.set_columns(std::move(mutable_columns)); return Status::OK(); } diff --git a/be/src/pipeline/exec/join_probe_operator.cpp b/be/src/pipeline/exec/join_probe_operator.cpp index 6b6796767e9505..4e94b4a206be51 100644 --- a/be/src/pipeline/exec/join_probe_operator.cpp +++ b/be/src/pipeline/exec/join_probe_operator.cpp @@ -134,9 +134,8 @@ Status JoinProbeLocalState::_build_output_block( } } - if (!is_mem_reuse || !keep_origin) { - output_block->swap(mutable_block.to_block()); - } + output_block->swap(mutable_block.to_block()); + DCHECK(output_block->rows() == rows); } diff --git a/be/src/pipeline/exec/nested_loop_join_probe_operator.cpp b/be/src/pipeline/exec/nested_loop_join_probe_operator.cpp index b7477d0b4f8755..1e3de36e92062f 100644 --- a/be/src/pipeline/exec/nested_loop_join_probe_operator.cpp +++ b/be/src/pipeline/exec/nested_loop_join_probe_operator.cpp @@ -130,7 +130,6 @@ Status NestedLoopJoinProbeLocalState::generate_join_block_data(RuntimeState* sta _left_side_process_count = 0; DCHECK(!_need_more_input_data || !_matched_rows_done); - vectorized::MutableBlock mutable_join_block(&_join_block); if (!_matched_rows_done && !_need_more_input_data) { // We should try to join rows if there still are some rows from probe side. while (_join_block.rows() < state->batch_size()) { @@ -144,7 +143,7 @@ Status NestedLoopJoinProbeLocalState::generate_join_block_data(RuntimeState* sta _reset_with_next_probe_row(); if (_left_block_pos < _child_block->rows()) { if constexpr (set_probe_side_flag) { - _probe_offset_stack.push(mutable_join_block.rows()); + _probe_offset_stack.push(_join_block.rows()); } } else { if (_shared_state->left_side_eos) { @@ -163,9 +162,9 @@ Status NestedLoopJoinProbeLocalState::generate_join_block_data(RuntimeState* sta const auto& now_process_build_block = _shared_state->build_blocks[_current_build_pos++]; if constexpr (set_build_side_flag) { - _build_offset_stack.push(mutable_join_block.rows()); + _build_offset_stack.push(_join_block.rows()); } - _process_left_child_block(mutable_join_block, now_process_build_block); + _process_left_child_block(_join_block, now_process_build_block); } if constexpr (set_probe_side_flag) { @@ -178,21 +177,20 @@ Status NestedLoopJoinProbeLocalState::generate_join_block_data(RuntimeState* sta if (!status.ok()) { return status; } - mutable_join_block = vectorized::MutableBlock(&_join_block); // If this join operation is left outer join or full outer join, when // `_left_side_process_count`, means all rows from build // side have been joined with _left_side_process_count, we should output current // probe row with null from build side. if (_left_side_process_count) { _finalize_current_phase( - mutable_join_block, state->batch_size()); + _join_block, state->batch_size()); } } if (_left_side_process_count) { if (p._is_mark_join && _shared_state->build_blocks.empty()) { DCHECK_EQ(JoinOpType::value, TJoinOp::CROSS_JOIN); - _append_left_data_with_null(mutable_join_block); + _append_left_data_with_null(_join_block); } } } @@ -204,7 +202,6 @@ Status NestedLoopJoinProbeLocalState::generate_join_block_data(RuntimeState* sta set_probe_side_flag, ignore_null>( &_join_block, !p._is_right_semi_anti))); _update_additional_flags(&_join_block); - mutable_join_block = vectorized::MutableBlock(&_join_block); if (!status.ok()) { return status; } @@ -214,7 +211,7 @@ Status NestedLoopJoinProbeLocalState::generate_join_block_data(RuntimeState* sta if (_matched_rows_done && _output_null_idx_build_side < _shared_state->build_blocks.size()) { _finalize_current_phase( - mutable_join_block, state->batch_size()); + _join_block, state->batch_size()); } } return Status::OK(); @@ -235,10 +232,10 @@ void NestedLoopJoinProbeLocalState::_resize_fill_tuple_is_null_column(size_t new } template -void NestedLoopJoinProbeLocalState::_finalize_current_phase(vectorized::MutableBlock& mutable_block, +void NestedLoopJoinProbeLocalState::_finalize_current_phase(vectorized::Block& block, size_t batch_size) { auto& p = _parent->cast(); - auto& dst_columns = mutable_block.mutable_columns(); + auto dst_columns = block.mutate_columns(); DCHECK_GT(dst_columns.size(), 0); auto column_size = dst_columns[0]->size(); if constexpr (BuildSide) { @@ -355,12 +352,12 @@ void NestedLoopJoinProbeLocalState::_finalize_current_phase(vectorized::MutableB _resize_fill_tuple_is_null_column(_left_side_process_count, 0, 1); } } + block.set_columns(std::move(dst_columns)); } -void NestedLoopJoinProbeLocalState::_append_left_data_with_null( - vectorized::MutableBlock& mutable_block) const { +void NestedLoopJoinProbeLocalState::_append_left_data_with_null(vectorized::Block& block) const { auto& p = _parent->cast(); - auto& dst_columns = mutable_block.mutable_columns(); + auto dst_columns = block.mutate_columns(); DCHECK(p._is_mark_join); for (size_t i = 0; i < p._num_probe_side_columns; ++i) { const vectorized::ColumnWithTypeAndName& src_column = _child_block->get_by_position(i); @@ -387,13 +384,13 @@ void NestedLoopJoinProbeLocalState::_append_left_data_with_null( auto& mark_column = *dst_columns[dst_columns.size() - 1]; vectorized::ColumnFilterHelper(mark_column) .resize_fill(mark_column.size() + _left_side_process_count, 0); + block.set_columns(std::move(dst_columns)); } void NestedLoopJoinProbeLocalState::_process_left_child_block( - vectorized::MutableBlock& mutable_block, - const vectorized::Block& now_process_build_block) const { + vectorized::Block& block, const vectorized::Block& now_process_build_block) const { auto& p = _parent->cast(); - auto& dst_columns = mutable_block.mutable_columns(); + auto dst_columns = block.mutate_columns(); const int max_added_rows = now_process_build_block.rows(); for (size_t i = 0; i < p._num_probe_side_columns; ++i) { const vectorized::ColumnWithTypeAndName& src_column = _child_block->get_by_position(i); @@ -434,6 +431,7 @@ void NestedLoopJoinProbeLocalState::_process_left_child_block( 0, max_added_rows); } } + block.set_columns(std::move(dst_columns)); } NestedLoopJoinProbeOperatorX::NestedLoopJoinProbeOperatorX(ObjectPool* pool, const TPlanNode& tnode, diff --git a/be/src/pipeline/exec/nested_loop_join_probe_operator.h b/be/src/pipeline/exec/nested_loop_join_probe_operator.h index bc8913f5d08c7e..525640beb48519 100644 --- a/be/src/pipeline/exec/nested_loop_join_probe_operator.h +++ b/be/src/pipeline/exec/nested_loop_join_probe_operator.h @@ -82,11 +82,11 @@ class NestedLoopJoinProbeLocalState final friend class NestedLoopJoinProbeOperatorX; void _update_additional_flags(vectorized::Block* block); template - void _finalize_current_phase(vectorized::MutableBlock& mutable_block, size_t batch_size); + void _finalize_current_phase(vectorized::Block& block, size_t batch_size); void _resize_fill_tuple_is_null_column(size_t new_size, int left_flag, int right_flag); void _reset_with_next_probe_row(); - void _append_left_data_with_null(vectorized::MutableBlock& mutable_block) const; - void _process_left_child_block(vectorized::MutableBlock& mutable_block, + void _append_left_data_with_null(vectorized::Block& block) const; + void _process_left_child_block(vectorized::Block& block, const vectorized::Block& now_process_build_block) const; template void _do_filtering_and_update_visited_flags_impl(vectorized::Block* block, int column_to_keep, diff --git a/be/src/pipeline/pipeline_x/operator.cpp b/be/src/pipeline/pipeline_x/operator.cpp index 39598dadbd8325..b90c9551426f64 100644 --- a/be/src/pipeline/pipeline_x/operator.cpp +++ b/be/src/pipeline/pipeline_x/operator.cpp @@ -187,6 +187,7 @@ Status OperatorXBase::do_projections(RuntimeState* state, vectorized::Block* ori } } DCHECK(mutable_block.rows() == rows); + output_block->set_columns(std::move(mutable_columns)); } return Status::OK(); diff --git a/be/src/vec/common/sort/sorter.cpp b/be/src/vec/common/sort/sorter.cpp index 40e51ce20ce357..bd095e1afc561c 100644 --- a/be/src/vec/common/sort/sorter.cpp +++ b/be/src/vec/common/sort/sorter.cpp @@ -186,6 +186,7 @@ Status MergeSorterState::_merge_sort_read_not_spilled(int batch_size, if (merged_rows == batch_size) break; } + block->set_columns(std::move(merged_columns)); if (merged_rows == 0) { *eos = true; diff --git a/be/src/vec/core/block.cpp b/be/src/vec/core/block.cpp index 544a558afc01ba..c2594a2e35a3e2 100644 --- a/be/src/vec/core/block.cpp +++ b/be/src/vec/core/block.cpp @@ -577,7 +577,7 @@ MutableColumns Block::mutate_columns() { size_t num_columns = data.size(); MutableColumns columns(num_columns); for (size_t i = 0; i < num_columns; ++i) { - columns[i] = data[i].column ? (*std::move(data[i].column)).assume_mutable() + columns[i] = data[i].column ? (*std::move(data[i].column)).mutate() : data[i].type->create_column(); } return columns; @@ -716,7 +716,7 @@ void Block::swap(Block& other) noexcept { void Block::swap(Block&& other) noexcept { clear(); data = std::move(other.data); - initialize_index_by_name(); + index_by_name = std::move(other.index_by_name); row_same_bit = std::move(other.row_same_bit); } @@ -936,7 +936,7 @@ void MutableBlock::swap(MutableBlock& another) noexcept { _columns.swap(another._columns); _data_types.swap(another._data_types); _names.swap(another._names); - initialize_index_by_name(); + index_by_name.swap(another.index_by_name); } void MutableBlock::swap(MutableBlock&& another) noexcept { @@ -944,7 +944,7 @@ void MutableBlock::swap(MutableBlock&& another) noexcept { _columns = std::move(another._columns); _data_types = std::move(another._data_types); _names = std::move(another._names); - initialize_index_by_name(); + index_by_name = std::move(another.index_by_name); } void MutableBlock::add_row(const Block* block, int row) { @@ -1027,6 +1027,7 @@ Block MutableBlock::to_block(int start_column) { Block MutableBlock::to_block(int start_column, int end_column) { ColumnsWithTypeAndName columns_with_schema; + columns_with_schema.reserve(end_column - start_column); for (size_t i = start_column; i < end_column; ++i) { columns_with_schema.emplace_back(std::move(_columns[i]), _data_types[i], _names[i]); } diff --git a/be/src/vec/exec/format/csv/csv_reader.cpp b/be/src/vec/exec/format/csv/csv_reader.cpp index 0d93fcd648c1db..9f8c01b10dbac9 100644 --- a/be/src/vec/exec/format/csv/csv_reader.cpp +++ b/be/src/vec/exec/format/csv/csv_reader.cpp @@ -479,10 +479,11 @@ Status CsvReader::get_next_block(Block* block, size_t* read_rows, bool* eof) { RETURN_IF_ERROR(_validate_line(Slice(ptr, size), &success)); ++rows; } - for (auto& col : block->mutate_columns()) { + auto mutate_columns = block->mutate_columns(); + for (auto& col : mutate_columns) { col->resize(rows); } - + block->set_columns(std::move(mutate_columns)); } else { auto columns = block->mutate_columns(); while (rows < batch_size && !_line_reader_eof) { @@ -504,6 +505,7 @@ Status CsvReader::get_next_block(Block* block, size_t* read_rows, bool* eof) { } RETURN_IF_ERROR(_fill_dest_columns(Slice(ptr, size), block, columns, &rows)); } + block->set_columns(std::move(columns)); } *eof = (rows == 0); diff --git a/be/src/vec/exec/format/orc/vorc_reader.cpp b/be/src/vec/exec/format/orc/vorc_reader.cpp index c57d38076241c2..12a889ec23ed2f 100644 --- a/be/src/vec/exec/format/orc/vorc_reader.cpp +++ b/be/src/vec/exec/format/orc/vorc_reader.cpp @@ -1412,11 +1412,11 @@ Status OrcReader::get_next_block(Block* block, size_t* read_rows, bool* eof) { auto rows = std::min(get_remaining_rows(), (int64_t)_batch_size); set_remaining_rows(get_remaining_rows() - rows); - - for (auto& col : block->mutate_columns()) { + auto mutate_columns = block->mutate_columns(); + for (auto& col : mutate_columns) { col->resize(rows); } - + block->set_columns(std::move(mutate_columns)); *read_rows = rows; if (get_remaining_rows() == 0) { *eof = true; diff --git a/be/src/vec/exec/format/parquet/vparquet_reader.cpp b/be/src/vec/exec/format/parquet/vparquet_reader.cpp index 87555f6ee86cda..6ae4ea2f5bc69e 100644 --- a/be/src/vec/exec/format/parquet/vparquet_reader.cpp +++ b/be/src/vec/exec/format/parquet/vparquet_reader.cpp @@ -528,10 +528,11 @@ Status ParquetReader::get_next_block(Block* block, size_t* read_rows, bool* eof) _current_group_reader->set_remaining_rows(_current_group_reader->get_remaining_rows() - rows); - - for (auto& col : block->mutate_columns()) { + auto mutate_columns = block->mutate_columns(); + for (auto& col : mutate_columns) { col->resize(rows); } + block->set_columns(std::move(mutate_columns)); *read_rows = rows; if (_current_group_reader->get_remaining_rows() == 0) { diff --git a/be/src/vec/exec/format/table/iceberg_reader.cpp b/be/src/vec/exec/format/table/iceberg_reader.cpp index 5ad1bdd315df56..8c05b8c2a08e70 100644 --- a/be/src/vec/exec/format/table/iceberg_reader.cpp +++ b/be/src/vec/exec/format/table/iceberg_reader.cpp @@ -143,9 +143,11 @@ Status IcebergTableReader::get_next_block(Block* block, size_t* read_rows, bool* auto rows = std::min(_remaining_push_down_count, (int64_t)_state->query_options().batch_size); _remaining_push_down_count -= rows; - for (auto& col : block->mutate_columns()) { + auto mutate_columns = block->mutate_columns(); + for (auto& col : mutate_columns) { col->resize(rows); } + block->set_columns(std::move(mutate_columns)); *read_rows = rows; if (_remaining_push_down_count == 0) { *eof = true; diff --git a/be/src/vec/exec/join/vjoin_node_base.cpp b/be/src/vec/exec/join/vjoin_node_base.cpp index 0077fe2a7bfbaa..656810ba7bd39d 100644 --- a/be/src/vec/exec/join/vjoin_node_base.cpp +++ b/be/src/vec/exec/join/vjoin_node_base.cpp @@ -177,6 +177,7 @@ Status VJoinNodeBase::_build_output_block(Block* origin_block, Block* output_blo } } }; + if (rows != 0) { auto& mutable_columns = mutable_block.mutable_columns(); if (_output_expr_ctxs.empty()) { @@ -207,9 +208,7 @@ Status VJoinNodeBase::_build_output_block(Block* origin_block, Block* output_blo } } - if (!is_mem_reuse || !keep_origin) { - output_block->swap(mutable_block.to_block()); - } + output_block->swap(mutable_block.to_block()); DCHECK(output_block->rows() == rows); } diff --git a/be/src/vec/exec/join/vnested_loop_join_node.cpp b/be/src/vec/exec/join/vnested_loop_join_node.cpp index 7d8100aa6caa18..08d4c7d4487c37 100644 --- a/be/src/vec/exec/join/vnested_loop_join_node.cpp +++ b/be/src/vec/exec/join/vnested_loop_join_node.cpp @@ -278,8 +278,8 @@ Status VNestedLoopJoinNode::get_next(RuntimeState* state, Block* block, bool* eo return pull(state, block, eos); } -void VNestedLoopJoinNode::_append_left_data_with_null(MutableBlock& mutable_block) const { - auto& dst_columns = mutable_block.mutable_columns(); +void VNestedLoopJoinNode::_append_left_data_with_null(Block& block) const { + auto dst_columns = block.mutate_columns(); DCHECK(_is_mark_join); for (size_t i = 0; i < _num_probe_side_columns; ++i) { const ColumnWithTypeAndName& src_column = _left_block->get_by_position(i); @@ -305,11 +305,12 @@ void VNestedLoopJoinNode::_append_left_data_with_null(MutableBlock& mutable_bloc auto& mark_column = *dst_columns[dst_columns.size() - 1]; ColumnFilterHelper(mark_column).resize_fill(mark_column.size() + _left_side_process_count, 0); + block.set_columns(std::move(dst_columns)); } -void VNestedLoopJoinNode::_process_left_child_block(MutableBlock& mutable_block, +void VNestedLoopJoinNode::_process_left_child_block(Block& block, const Block& now_process_build_block) const { - auto& dst_columns = mutable_block.mutable_columns(); + auto dst_columns = block.mutate_columns(); const int max_added_rows = now_process_build_block.rows(); for (size_t i = 0; i < _num_probe_side_columns; ++i) { const ColumnWithTypeAndName& src_column = _left_block->get_by_position(i); @@ -345,6 +346,7 @@ void VNestedLoopJoinNode::_process_left_child_block(MutableBlock& mutable_block, max_added_rows); } } + block.set_columns(std::move(dst_columns)); } void VNestedLoopJoinNode::_update_additional_flags(Block* block) { @@ -395,8 +397,8 @@ void VNestedLoopJoinNode::_add_tuple_is_null_column(Block* block) { } template -void VNestedLoopJoinNode::_finalize_current_phase(MutableBlock& mutable_block, size_t batch_size) { - auto& dst_columns = mutable_block.mutable_columns(); +void VNestedLoopJoinNode::_finalize_current_phase(Block& block, size_t batch_size) { + auto dst_columns = block.mutate_columns(); DCHECK_GT(dst_columns.size(), 0); auto column_size = dst_columns[0]->size(); if constexpr (BuildSide) { @@ -508,6 +510,7 @@ void VNestedLoopJoinNode::_finalize_current_phase(MutableBlock& mutable_block, s _resize_fill_tuple_is_null_column(_left_side_process_count, 0, 1); } } + block.set_columns(std::move(dst_columns)); } void VNestedLoopJoinNode::_reset_with_next_probe_row() { diff --git a/be/src/vec/exec/join/vnested_loop_join_node.h b/be/src/vec/exec/join/vnested_loop_join_node.h index 810bf57e7f5e8b..732648592745c9 100644 --- a/be/src/vec/exec/join/vnested_loop_join_node.h +++ b/be/src/vec/exec/join/vnested_loop_join_node.h @@ -115,7 +115,6 @@ class VNestedLoopJoinNode final : public VJoinNodeBase { _left_side_process_count = 0; DCHECK(!_need_more_input_data || !_matched_rows_done); - MutableBlock mutable_join_block(&_join_block); if (!_matched_rows_done && !_need_more_input_data) { // We should try to join rows if there still are some rows from probe side. while (_join_block.rows() < state->batch_size()) { @@ -129,7 +128,7 @@ class VNestedLoopJoinNode final : public VJoinNodeBase { _reset_with_next_probe_row(); if (_left_block_pos < _left_block->rows()) { if constexpr (set_probe_side_flag) { - _probe_offset_stack.push(mutable_join_block.rows()); + _probe_offset_stack.push(_join_block.rows()); } } else { if (_left_side_eos) { @@ -148,9 +147,9 @@ class VNestedLoopJoinNode final : public VJoinNodeBase { const auto& now_process_build_block = _build_blocks[_current_build_pos++]; if constexpr (set_build_side_flag) { - _build_offset_stack.push(mutable_join_block.rows()); + _build_offset_stack.push(_join_block.rows()); } - _process_left_child_block(mutable_join_block, now_process_build_block); + _process_left_child_block(_join_block, now_process_build_block); } if constexpr (set_probe_side_flag) { @@ -163,21 +162,20 @@ class VNestedLoopJoinNode final : public VJoinNodeBase { if (!status.ok()) { return status; } - mutable_join_block = MutableBlock(&_join_block); // If this join operation is left outer join or full outer join, when // `_left_side_process_count`, means all rows from build // side have been joined with _left_side_process_count, we should output current // probe row with null from build side. if (_left_side_process_count) { _finalize_current_phase( - mutable_join_block, state->batch_size()); + _join_block, state->batch_size()); } } if (_left_side_process_count) { if (_is_mark_join && _build_blocks.empty()) { DCHECK_EQ(JoinOpType::value, TJoinOp::CROSS_JOIN); - _append_left_data_with_null(mutable_join_block); + _append_left_data_with_null(_join_block); } } } @@ -189,7 +187,6 @@ class VNestedLoopJoinNode final : public VJoinNodeBase { set_build_side_flag, set_probe_side_flag, ignore_null>( &_join_block, !_is_right_semi_anti))); _update_additional_flags(&_join_block); - mutable_join_block = MutableBlock(&_join_block); if (!status.ok()) { return status; } @@ -198,7 +195,7 @@ class VNestedLoopJoinNode final : public VJoinNodeBase { if constexpr (set_build_side_flag) { if (_matched_rows_done && _output_null_idx_build_side < _build_blocks.size()) { _finalize_current_phase( - mutable_join_block, state->batch_size()); + _join_block, state->batch_size()); } } return Status::OK(); @@ -208,8 +205,7 @@ class VNestedLoopJoinNode final : public VJoinNodeBase { // Processes a block from the left child. // dst_columns: left_child_row and now_process_build_block to construct a bundle column of new block // now_process_build_block: right child block now to process - void _process_left_child_block(MutableBlock& mutable_block, - const Block& now_process_build_block) const; + void _process_left_child_block(Block& block, const Block& now_process_build_block) const; template Status _do_filtering_and_update_visited_flags(Block* block, bool materialize); @@ -221,7 +217,7 @@ class VNestedLoopJoinNode final : public VJoinNodeBase { bool materialize, Filter& filter); template - void _finalize_current_phase(MutableBlock& mutable_block, size_t batch_size); + void _finalize_current_phase(Block& block, size_t batch_size); void _reset_with_next_probe_row(); @@ -238,7 +234,7 @@ class VNestedLoopJoinNode final : public VJoinNodeBase { // For mark join, if the relation from right side is empty, we should construct intermediate // block with data from left side and filled with null for right side - void _append_left_data_with_null(MutableBlock& mutable_block) const; + void _append_left_data_with_null(Block& block) const; // List of build blocks, constructed in prepare() Blocks _build_blocks; diff --git a/be/src/vec/exec/scan/pip_scanner_context.h b/be/src/vec/exec/scan/pip_scanner_context.h index 309aed96a8cf7f..42726cb17fe0f0 100644 --- a/be/src/vec/exec/scan/pip_scanner_context.h +++ b/be/src/vec/exec/scan/pip_scanner_context.h @@ -106,6 +106,7 @@ class PipScannerContext : public vectorized::ScannerContext { static_cast(m.merge(*merge_block)); return_free_block(std::move(merge_block)); } + (*block)->set_columns(std::move(m.mutable_columns())); } return Status::OK(); diff --git a/be/src/vec/exec/scan/scanner_context.cpp b/be/src/vec/exec/scan/scanner_context.cpp index 5ad2dbec5b69fa..4d3820d7634074 100644 --- a/be/src/vec/exec/scan/scanner_context.cpp +++ b/be/src/vec/exec/scan/scanner_context.cpp @@ -339,6 +339,7 @@ Status ScannerContext::get_block_from_queue(RuntimeState* state, vectorized::Blo static_cast(m.merge(*merge_block)); return_free_block(std::move(merge_block)); } + (*block)->set_columns(std::move(m.mutable_columns())); } return Status::OK(); diff --git a/be/src/vec/exec/scan/scanner_scheduler.cpp b/be/src/vec/exec/scan/scanner_scheduler.cpp index e8d7f8a7139a6d..42285f1d22a854 100644 --- a/be/src/vec/exec/scan/scanner_scheduler.cpp +++ b/be/src/vec/exec/scan/scanner_scheduler.cpp @@ -374,7 +374,9 @@ void ScannerScheduler::_scanner_scan(ScannerScheduler* scheduler, ctx->return_free_block(std::move(block)); } else { if (!blocks.empty() && blocks.back()->rows() + block->rows() <= state->batch_size()) { - static_cast(vectorized::MutableBlock(blocks.back().get()).merge(*block)); + vectorized::MutableBlock mutable_block(blocks.back().get()); + static_cast(mutable_block.merge(*block)); + blocks.back().get()->set_columns(std::move(mutable_block.mutable_columns())); ctx->return_free_block(std::move(block)); } else { blocks.push_back(std::move(block)); diff --git a/be/src/vec/exec/vpartition_sort_node.h b/be/src/vec/exec/vpartition_sort_node.h index acf18bad7b4ba0..d369846df5926c 100644 --- a/be/src/vec/exec/vpartition_sort_node.h +++ b/be/src/vec/exec/vpartition_sort_node.h @@ -58,6 +58,7 @@ struct PartitionBlocks { for (int i = 0; i < mutable_columns.size(); ++i) { columns[i]->append_data_by_selector(mutable_columns[i], selector); } + blocks.back()->set_columns(std::move(mutable_columns)); init_rows = init_rows - selector.size(); total_rows = total_rows + selector.size(); selector.clear(); diff --git a/be/src/vec/exec/vrepeat_node.cpp b/be/src/vec/exec/vrepeat_node.cpp index 1765c1dcf7f44c..717c0c28d08b67 100644 --- a/be/src/vec/exec/vrepeat_node.cpp +++ b/be/src/vec/exec/vrepeat_node.cpp @@ -167,7 +167,7 @@ Status VRepeatNode::get_repeated_block(Block* child_block, int repeat_id_idx, Bl } cur_col++; } - + output_block->set_columns(std::move(columns)); DCHECK_EQ(cur_col, column_size); return Status::OK(); diff --git a/be/src/vec/exec/vtable_function_node.cpp b/be/src/vec/exec/vtable_function_node.cpp index b7302aedcdc747..8affd4cbe7e265 100644 --- a/be/src/vec/exec/vtable_function_node.cpp +++ b/be/src/vec/exec/vtable_function_node.cpp @@ -218,7 +218,7 @@ Status VTableFunctionNode::_get_expanded_block(RuntimeState* state, Block* outpu for (auto index : _useless_slot_indexs) { columns[index]->insert_many_defaults(row_size - columns[index]->size()); } - + output_block->set_columns(std::move(columns)); // 3. eval conjuncts RETURN_IF_ERROR(VExprContext::filter_block(_conjuncts, output_block, output_block->columns())); diff --git a/be/src/vec/exec/vunion_node.cpp b/be/src/vec/exec/vunion_node.cpp index 884b66347b5c13..8ee258ca05678e 100644 --- a/be/src/vec/exec/vunion_node.cpp +++ b/be/src/vec/exec/vunion_node.cpp @@ -205,6 +205,7 @@ Status VUnionNode::get_next_materialized(RuntimeState* state, Block* block) { ++_child_idx; } } + block->set_columns(std::move(mblock.mutable_columns())); DCHECK_LE(_child_idx, _children.size()); return Status::OK(); @@ -233,6 +234,7 @@ Status VUnionNode::get_next_const(RuntimeState* state, Block* block) { tmp_block.clear(); } } + block->set_columns(std::move(mblock.mutable_columns())); // some insert query like "insert into string_test select 1, repeat('a', 1024 * 1024);" // the const expr will be in output expr cause the union node return a empty block. so here we @@ -256,6 +258,8 @@ Status VUnionNode::materialize_child_block(RuntimeState* state, int child_id, Block res; RETURN_IF_ERROR(materialize_block(input_block, child_id, &res)); RETURN_IF_ERROR(mblock.merge(res)); + + output_block->set_columns(std::move(mblock.mutable_columns())); } return Status::OK(); } diff --git a/be/src/vec/olap/block_reader.cpp b/be/src/vec/olap/block_reader.cpp index 9c15302af6c0b4..82604ac4083307 100644 --- a/be/src/vec/olap/block_reader.cpp +++ b/be/src/vec/olap/block_reader.cpp @@ -337,6 +337,7 @@ Status BlockReader::_agg_key_next_block(Block* block, bool* eof) { _agg_data_counters.push_back(_last_agg_data_counter); _last_agg_data_counter = 0; _update_agg_data(target_columns); + block->set_columns(std::move(target_columns)); _merged_rows += merged_row; return Status::OK(); @@ -410,17 +411,20 @@ Status BlockReader::_unique_key_next_block(Block* block, bool* eof) { } } } - + auto target_columns_size = target_columns.size(); ColumnWithTypeAndName column_with_type_and_name {_delete_filter_column, std::make_shared(), "__DORIS_COMPACTION_FILTER__"}; + block->set_columns(std::move(target_columns)); block->insert(column_with_type_and_name); - RETURN_IF_ERROR(Block::filter_block(block, target_columns.size(), target_columns.size())); + RETURN_IF_ERROR(Block::filter_block(block, target_columns_size, target_columns_size)); _stats.rows_del_filtered += target_block_row - block->rows(); DCHECK(block->try_get_by_name("__DORIS_COMPACTION_FILTER__") == nullptr); if (UNLIKELY(_reader_context.record_rowids)) { DCHECK_EQ(_block_row_locations.size(), block->rows() + delete_count); } + } else { + block->set_columns(std::move(target_columns)); } return Status::OK(); } diff --git a/be/src/vec/olap/vcollect_iterator.cpp b/be/src/vec/olap/vcollect_iterator.cpp index 921e89c3c8a4e2..ca9e2b729671d9 100644 --- a/be/src/vec/olap/vcollect_iterator.cpp +++ b/be/src/vec/olap/vcollect_iterator.cpp @@ -770,7 +770,7 @@ Status VCollectIterator::Level1Iterator::_normal_next(IteratorRowRef* ref) { Status VCollectIterator::Level1Iterator::_merge_next(Block* block) { int target_block_row = 0; auto target_columns = block->mutate_columns(); - size_t column_count = block->columns(); + size_t column_count = target_columns.size(); IteratorRowRef cur_row = _ref; IteratorRowRef pre_row_ref = _ref; @@ -809,6 +809,7 @@ Status VCollectIterator::Level1Iterator::_merge_next(Block* block) { if (UNLIKELY(_reader->_reader_context.record_rowids)) { _block_row_locations.resize(target_block_row); } + block->set_columns(std::move(target_columns)); return res; } @@ -825,6 +826,7 @@ Status VCollectIterator::Level1Iterator::_merge_next(Block* block) { continuous_row_in_block); } } + block->set_columns(std::move(target_columns)); return Status::OK(); } if (continuous_row_in_block == 0) { diff --git a/be/src/vec/olap/vertical_block_reader.cpp b/be/src/vec/olap/vertical_block_reader.cpp index 3f4a0cd3a1299b..5f2d856fe794bd 100644 --- a/be/src/vec/olap/vertical_block_reader.cpp +++ b/be/src/vec/olap/vertical_block_reader.cpp @@ -405,6 +405,7 @@ Status VerticalBlockReader::_agg_key_next_block(Block* block, bool* eof) { _agg_data_counters.push_back(_last_agg_data_counter); _last_agg_data_counter = 0; _update_agg_data(target_columns); + block->set_columns(std::move(target_columns)); return Status::OK(); } @@ -545,6 +546,7 @@ Status VerticalBlockReader::_unique_key_next_block(Block* block, bool* eof) { }); ++target_block_row; } while (target_block_row < _reader_context.batch_size); + block->set_columns(std::move(target_columns)); return Status::OK(); } diff --git a/be/src/vec/olap/vgeneric_iterators.cpp b/be/src/vec/olap/vgeneric_iterators.cpp index 65dc8e3e0dc6f3..155bb4ee076be8 100644 --- a/be/src/vec/olap/vgeneric_iterators.cpp +++ b/be/src/vec/olap/vgeneric_iterators.cpp @@ -75,14 +75,15 @@ Status VStatisticsIterator::next_batch(Block* block) { : std::min(_target_rows - _output_rows, MAX_ROW_SIZE_IN_COUNT); if (_push_down_agg_type_opt == TPushAggOp::COUNT) { size = std::min(_target_rows - _output_rows, MAX_ROW_SIZE_IN_COUNT); - for (int i = 0; i < block->columns(); ++i) { + for (int i = 0; i < columns.size(); ++i) { columns[i]->resize(size); } } else { - for (int i = 0; i < block->columns(); ++i) { + for (int i = 0; i < columns.size(); ++i) { RETURN_IF_ERROR(_column_iterators[i]->next_batch_of_zone_map(&size, columns[i])); } } + block->set_columns(std::move(columns)); _output_rows += size; return Status::OK(); } diff --git a/be/src/vec/runtime/vsorted_run_merger.cpp b/be/src/vec/runtime/vsorted_run_merger.cpp index 1bdd82ba8e4f68..6d2f68db6287df 100644 --- a/be/src/vec/runtime/vsorted_run_merger.cpp +++ b/be/src/vec/runtime/vsorted_run_merger.cpp @@ -194,6 +194,7 @@ Status VSortedRunMerger::get_next(Block* output_block, bool* eos) { break; } } + output_block->set_columns(std::move(merged_columns)); if (merged_rows == 0) { *eos = true; diff --git a/be/test/vec/core/block_test.cpp b/be/test/vec/core/block_test.cpp index 020c4f2e923cd6..7e8a26ce438a2c 100644 --- a/be/test/vec/core/block_test.cpp +++ b/be/test/vec/core/block_test.cpp @@ -390,4 +390,57 @@ TEST(BlockTest, dump_data) { vectorized::Block::filter_block_internal(&block1, filter, block1.columns()); EXPECT_EQ(size, block1.rows()); } + +TEST(BlockTest, merge_with_shared_columns) { + auto vec = vectorized::ColumnVector::create(); + auto& int32_data = vec->get_data(); + for (int i = 0; i < 1024; ++i) { + int32_data.push_back(i); + } + vectorized::DataTypePtr int32_type(std::make_shared()); + vectorized::ColumnWithTypeAndName test_k1(vec->get_ptr(), int32_type, "k1"); + + auto strcol = vectorized::ColumnString::create(); + for (int i = 0; i < 1024; ++i) { + std::string is = std::to_string(i); + strcol->insert_data(is.c_str(), is.size()); + } + vectorized::DataTypePtr string_type(std::make_shared()); + vectorized::ColumnWithTypeAndName test_v1(strcol->get_ptr(), string_type, "v1"); + + vectorized::ColumnWithTypeAndName test_v2(strcol->get_ptr(), string_type, "v2"); + + vectorized::Block src_block({test_k1, test_v1, test_v2}); + + auto vec_temp = vectorized::ColumnVector::create(); + auto& int32_data_temp = vec_temp->get_data(); + for (int i = 0; i < 10; ++i) { + int32_data_temp.push_back(i); + } + + vectorized::ColumnWithTypeAndName test_k1_temp(vec_temp->get_ptr(), int32_type, "k1"); + + auto strcol_temp = vectorized::ColumnString::create(); + for (int i = 0; i < 10; ++i) { + std::string is = std::to_string(i); + strcol_temp->insert_data(is.c_str(), is.size()); + } + + vectorized::ColumnWithTypeAndName test_v1_temp(strcol_temp->get_ptr(), string_type, "v1"); + vectorized::ColumnWithTypeAndName test_v2_temp(strcol_temp->get_ptr(), string_type, "v2"); + + vectorized::Block temp_block({test_k1_temp, test_v1_temp, test_v2_temp}); + + vectorized::MutableBlock mutable_block(&src_block); + auto status = mutable_block.merge(temp_block); + ASSERT_TRUE(status.ok()); + + src_block.set_columns(std::move(mutable_block.mutable_columns())); + + for (auto& column : src_block.get_columns()) { + EXPECT_EQ(1034, column->size()); + } + EXPECT_EQ(1034, src_block.rows()); +} + } // namespace doris From 8921b313b43172c002821ef14650df8ae8bc2891 Mon Sep 17 00:00:00 2001 From: Nitin-Kashyap <66766227+Nitin-Kashyap@users.noreply.github.com> Date: Mon, 25 Dec 2023 20:02:37 +0700 Subject: [PATCH 015/299] [fix](doc) typo fix in dynamic-partition page (#28511) --- docs/en/docs/advanced/partition/dynamic-partition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/docs/advanced/partition/dynamic-partition.md b/docs/en/docs/advanced/partition/dynamic-partition.md index 99ab594cdd809a..037caa28805b22 100644 --- a/docs/en/docs/advanced/partition/dynamic-partition.md +++ b/docs/en/docs/advanced/partition/dynamic-partition.md @@ -206,7 +206,7 @@ Assuming the number of history partitions to be created is `expect_create_partit `expect_create_partition_num` = `end` - `start`; - `dynamic_partition.history_partition_num` is set - `expect_create_partition_num` = `end` - max(`start`, `-histoty_partition_num`); + `expect_create_partition_num` = `end` - max(`start`, `-history_partition_num`); 2. `create_history_partition` = `false` No history partition will be created, `expect_create_partition_num` = `end` - 0; From 0af9371a96dccd7ddbfcad13cffc25a46b858158 Mon Sep 17 00:00:00 2001 From: TengJianPing <18241664+jacktengg@users.noreply.github.com> Date: Mon, 25 Dec 2023 22:19:01 +0800 Subject: [PATCH 016/299] [fix](hash join) fix column ref DCHECK failure of hash join node block mem reuse (#28991) Introduced by #28851, after evaluating build side expr, some columns in resulting block may be referenced more than once in the same block. e.g. coalesce(col_a, 'string') if col_a is nullable but actually contains no null values, in this case funcition coalesce will insert a new nullable column which references the original col_a. --- be/src/vec/exec/join/vhash_join_node.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/be/src/vec/exec/join/vhash_join_node.cpp b/be/src/vec/exec/join/vhash_join_node.cpp index 30f2450f458c11..60391716e28af6 100644 --- a/be/src/vec/exec/join/vhash_join_node.cpp +++ b/be/src/vec/exec/join/vhash_join_node.cpp @@ -692,7 +692,7 @@ Status HashJoinNode::_materialize_build_side(RuntimeState* state) { // data from data. while (!eos && (!_short_circuit_for_null_in_build_side || !_has_null_in_build_side) && (!_probe_open_finish || !_is_hash_join_early_start_probe_eos(state))) { - block.clear_column_data(); + release_block_memory(block, 1); RETURN_IF_CANCELLED(state); { SCOPED_TIMER(_build_get_next_timer); From c2c5df9341a310c442bf0b3f489ef30a702a2982 Mon Sep 17 00:00:00 2001 From: zhiqiang Date: Mon, 25 Dec 2023 22:47:23 +0800 Subject: [PATCH 017/299] [opt](assert_num_rows) support filter in AssertNumRows operator and fix some explain (#28935) * NEED * Update pipeline x * fix pipelinex compile --- .../exec/assert_num_rows_operator.cpp | 3 + be/src/vec/exec/vassert_num_rows_node.cpp | 2 + .../doris/planner/AssertNumRowsNode.java | 5 + .../apache/doris/planner/DataGenScanNode.java | 20 ++++ .../doris/planner/PartitionSortNode.java | 4 + .../correctness_p0/test_assert_row_num.out | 17 ++++ .../correctness_p0/test_assert_row_num.groovy | 91 +++++++++++++++++++ 7 files changed, 142 insertions(+) create mode 100644 regression-test/data/correctness_p0/test_assert_row_num.out create mode 100644 regression-test/suites/correctness_p0/test_assert_row_num.groovy diff --git a/be/src/pipeline/exec/assert_num_rows_operator.cpp b/be/src/pipeline/exec/assert_num_rows_operator.cpp index baddce5a94d538..d08f60cce74218 100644 --- a/be/src/pipeline/exec/assert_num_rows_operator.cpp +++ b/be/src/pipeline/exec/assert_num_rows_operator.cpp @@ -17,6 +17,8 @@ #include "assert_num_rows_operator.h" +#include "vec/exprs/vexpr_context.h" + namespace doris::pipeline { OperatorPtr AssertNumRowsOperatorBuilder::build_operator() { @@ -83,6 +85,7 @@ Status AssertNumRowsOperatorX::pull(doris::RuntimeState* state, vectorized::Bloc } COUNTER_SET(local_state.rows_returned_counter(), local_state.num_rows_returned()); COUNTER_UPDATE(local_state.blocks_returned_counter(), 1); + RETURN_IF_ERROR(vectorized::VExprContext::filter_block(_conjuncts, block, block->columns())); return Status::OK(); } diff --git a/be/src/vec/exec/vassert_num_rows_node.cpp b/be/src/vec/exec/vassert_num_rows_node.cpp index f01c4a254b0802..9ef1166e2fb29e 100644 --- a/be/src/vec/exec/vassert_num_rows_node.cpp +++ b/be/src/vec/exec/vassert_num_rows_node.cpp @@ -29,6 +29,7 @@ #include "runtime/runtime_state.h" #include "vec/core/block.h" +#include "vec/exprs/vexpr_context.h" namespace doris { class DescriptorTbl; @@ -100,6 +101,7 @@ Status VAssertNumRowsNode::pull(doris::RuntimeState* state, vectorized::Block* b to_string_lambda(_assertion), _desired_num_rows, _subquery_string); } COUNTER_SET(_rows_returned_counter, _num_rows_returned); + RETURN_IF_ERROR(VExprContext::filter_block(_conjuncts, block, block->columns())); return Status::OK(); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/planner/AssertNumRowsNode.java b/fe/fe-core/src/main/java/org/apache/doris/planner/AssertNumRowsNode.java index 84ac5520ee294f..0ae50ce0e4c756 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/planner/AssertNumRowsNode.java +++ b/fe/fe-core/src/main/java/org/apache/doris/planner/AssertNumRowsNode.java @@ -79,6 +79,11 @@ public String getNodeExplainString(String prefix, TExplainLevel detailLevel) { StringBuilder output = new StringBuilder() .append(prefix).append("assert number of rows: ") .append(assertion).append(" ").append(desiredNumOfRows).append("\n"); + + if (!conjuncts.isEmpty()) { + output.append(prefix).append("predicates: ").append(getExplainString(conjuncts)).append("\n"); + } + return output.toString(); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/planner/DataGenScanNode.java b/fe/fe-core/src/main/java/org/apache/doris/planner/DataGenScanNode.java index d00641d135c86f..457c643182ece5 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/planner/DataGenScanNode.java +++ b/fe/fe-core/src/main/java/org/apache/doris/planner/DataGenScanNode.java @@ -26,6 +26,7 @@ import org.apache.doris.tablefunction.DataGenTableValuedFunction; import org.apache.doris.tablefunction.TableValuedFunctionTask; import org.apache.doris.thrift.TDataGenScanNode; +import org.apache.doris.thrift.TExplainLevel; import org.apache.doris.thrift.TNetworkAddress; import org.apache.doris.thrift.TPlanNode; import org.apache.doris.thrift.TPlanNodeType; @@ -117,4 +118,23 @@ public boolean needToCheckColumnPriv() { public int getNumInstances() { return 1; } + + @Override + public String getNodeExplainString(String prefix, TExplainLevel detailLevel) { + if (detailLevel == TExplainLevel.BRIEF) { + return ""; + } + + StringBuilder output = new StringBuilder(); + + if (!conjuncts.isEmpty()) { + output.append(prefix).append("predicates: ").append(getExplainString(conjuncts)).append("\n"); + } + + output.append(prefix).append("table value function: ").append(tvf.getDataGenFunctionName()).append("\n"); + + + + return output.toString(); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/planner/PartitionSortNode.java b/fe/fe-core/src/main/java/org/apache/doris/planner/PartitionSortNode.java index fc63af3b6c1cce..20142e380ce710 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/planner/PartitionSortNode.java +++ b/fe/fe-core/src/main/java/org/apache/doris/planner/PartitionSortNode.java @@ -120,6 +120,10 @@ public String getNodeExplainString(String prefix, TExplainLevel detailLevel) { } output.append("\n"); + if (!conjuncts.isEmpty()) { + output.append(prefix).append("predicates: ").append(getExplainString(conjuncts)).append("\n"); + } + // Add the limit information; output.append(prefix).append("has global limit: ").append(hasGlobalLimit).append("\n"); output.append(prefix).append("partition limit: ").append(partitionLimit).append("\n"); diff --git a/regression-test/data/correctness_p0/test_assert_row_num.out b/regression-test/data/correctness_p0/test_assert_row_num.out new file mode 100644 index 00000000000000..0ac1221fb698a7 --- /dev/null +++ b/regression-test/data/correctness_p0/test_assert_row_num.out @@ -0,0 +1,17 @@ +-- This file is automatically generated. You should know what you did if you want to edit this +-- !sql_1 -- + +-- !sql_2 -- +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 + +-- !sql_3 -- + diff --git a/regression-test/suites/correctness_p0/test_assert_row_num.groovy b/regression-test/suites/correctness_p0/test_assert_row_num.groovy new file mode 100644 index 00000000000000..818213f56fee89 --- /dev/null +++ b/regression-test/suites/correctness_p0/test_assert_row_num.groovy @@ -0,0 +1,91 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +suite("test_assert_num_rows") { + qt_sql_1 """ + SELECT * from numbers("number"="10") WHERE ( SELECT * FROM (SELECT 3) __DORIS_DUAL__ ) IS NULL + """ + + qt_sql_2 """ + SELECT * from numbers("number"="10") WHERE ( SELECT * FROM (SELECT 3) __DORIS_DUAL__ ) IS NOT NULL + """ + sql """ + DROP TABLE IF EXISTS table_9_undef_undef; + """ + sql """ + DROP TABLE IF EXISTS table_10_undef_undef; + """ + sql """ + CREATE TABLE table_10_undef_undef ( + `pk` int, `col_int_undef_signed` int , + `col_varchar_10__undef_signed` varchar(10), + `col_varchar_1024__undef_signed` varchar(1024)) + ENGINE=olap distributed BY hash(pk) buckets 10 properties('replication_num'='1'); + """ + + sql """ + CREATE TABLE table_9_undef_undef ( + `pk` int,`col_int_undef_signed` int , + `col_varchar_10__undef_signed` varchar(10) , + `col_varchar_1024__undef_signed` varchar(1024)) + ENGINE=olap distributed BY hash(pk) buckets 10 properties('replication_num' = '1'); + """ + + sql """ + INSERT INTO table_9_undef_undef + VALUES (0,NULL,"get",'r'), + (1,NULL,'q','i'), + (2,2,"about","yes"), + (3,NULL,"see","see"), + (4,NULL,"was","been"), + (5,NULL,"yes",'p'), + (6,6,"you",'u'), + (7,0,"me",'v'), + (8,5,"something",'f'); + """ + sql """ + INSERT INTO table_10_undef_undef + VALUES (0,NULL,"it's","time"), + (1,NULL,"right",'o'), + (2,5,'y','k'), + (3,1,'r',"I'll"), + (4,2,'e',"time"), + (5,8,'v',"from"), + (6,NULL,"you",'v'), + (7,NULL,'r','a'), + (8,1,'d',"didn't"), + (9,NULL,'r',"go"); + """ + + qt_sql_3 """ + SELECT alias1 . `pk` AS field1, + alias1 . `col_int_undef_signed` AS field2 + FROM table_10_undef_undef AS alias1, + table_9_undef_undef AS alias2 + WHERE + (SELECT * + FROM + (SELECT 3) __DORIS_DUAL__) IS NULL + HAVING field2 < 2 + ORDER BY alias1 . `pk`, + alias1 .`pk` ASC, + field1, + field2 + LIMIT 2 + OFFSET 6; + """ +} \ No newline at end of file From 137f785698023640eddf598014c67579b43826af Mon Sep 17 00:00:00 2001 From: py023 <137761033+py023@users.noreply.github.com> Date: Mon, 25 Dec 2023 22:58:08 +0800 Subject: [PATCH 018/299] [fix](parquet_reader) misused bool pointer (#28986) Signed-off-by: pengyu --- be/src/vec/exec/format/parquet/vparquet_column_reader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/be/src/vec/exec/format/parquet/vparquet_column_reader.cpp b/be/src/vec/exec/format/parquet/vparquet_column_reader.cpp index 99d6f6b4e85849..a675046e1fd92b 100644 --- a/be/src/vec/exec/format/parquet/vparquet_column_reader.cpp +++ b/be/src/vec/exec/format/parquet/vparquet_column_reader.cpp @@ -447,7 +447,7 @@ Status ScalarColumnReader::read_dict_values_to_column(MutableColumnPtr& doris_co bool* has_dict) { bool loaded; RETURN_IF_ERROR(_try_load_dict_page(&loaded, has_dict)); - if (loaded && has_dict) { + if (loaded && *has_dict) { return _chunk_reader->read_dict_values_to_column(doris_column); } return Status::OK(); From 2411dbe0e6d9e78931535472d8ff0d8ab0af5b8a Mon Sep 17 00:00:00 2001 From: slothever <18522955+wsjz@users.noreply.github.com> Date: Mon, 25 Dec 2023 23:05:44 +0800 Subject: [PATCH 019/299] [doc](multi-catalog)add krb and some ertificates FAQs (#28858) add some security docs --- docs/en/docs/lakehouse/faq.md | 18 ++++++++++++++++++ docs/en/docs/lakehouse/multi-catalog/hive.md | 2 ++ .../docs/lakehouse/multi-catalog/iceberg.md | 2 ++ docs/zh-CN/docs/lakehouse/faq.md | 19 +++++++++++++++++++ .../docs/lakehouse/multi-catalog/hive.md | 2 ++ .../docs/lakehouse/multi-catalog/iceberg.md | 2 ++ 6 files changed, 45 insertions(+) diff --git a/docs/en/docs/lakehouse/faq.md b/docs/en/docs/lakehouse/faq.md index ee441961c963cd..3b20fa878c2005 100644 --- a/docs/en/docs/lakehouse/faq.md +++ b/docs/en/docs/lakehouse/faq.md @@ -27,6 +27,19 @@ under the License. # FAQ +## Certificates + +1. If an error is reported: `curl 77: Problem with the SSL CA cert.`, need update your certificate. + - Download the latest certificate from `https://curl.haxx.se/docs/caextract.html`. + - Place the downloaded cacert-xxx.pem in the `/etc/ssl/certs/` directory. For example: `sudo cp cacert-xxx.pem /etc/ssl/certs/ca-certificates.crt`. + +2. If an error is reported: `ERROR 1105 (HY000): errCode = 2, detailMessage = (x.x.x.x)[CANCELLED][INTERNAL_ERROR]error setting certificate verify locations: CAfile: /etc/ssl/certs/ca-certificates.crt CApath: none`. + +``` +yum install -y ca-certificates +ln -s /etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt /etc/ssl/certs/ca-certificates.crt +``` + ## Kerberos @@ -58,6 +71,11 @@ under the License. - The principal used must exist in the klist, use `klist -kt your.keytab` to check. - Ensure the catalog configuration correct, such as missing the `yarn.resourcemanager.principal`. - If the preceding checks are correct, the JDK version installed by yum or other package-management utility in the current system maybe have an unsupported encryption algorithm. It is recommended to install JDK by yourself and set `JAVA_HOME` environment variable. + - Kerberos uses AES-256 by default for encryption. If you use Oracle JDK, you must install JCE. In the case of OpenJDK, some distributions of OpenJDK automatically provide the JCE Unlimited Strength Jurisdiction Policy Files, so it's not need to install JCE. + - The JCE version corresponds to the JDK version. You need to select the JCE according to the JDK version. Download the JCE zip package and decompress it into `$JAVA_HOME/jre/lib/security`: + - JDK6:[JCE6](http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html) + - JDK7:[JCE7](http://www.oracle.com/technetwork/java/embedded/embedded-se/downloads/jce-7-download-432124.html) + - JDK8:[JCE8](http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html) 5. An error is reported when using KMS to access HDFS: `java.security.InvalidKeyException: Illegal key size` diff --git a/docs/en/docs/lakehouse/multi-catalog/hive.md b/docs/en/docs/lakehouse/multi-catalog/hive.md index 754c3954f5268e..25fddea125064d 100644 --- a/docs/en/docs/lakehouse/multi-catalog/hive.md +++ b/docs/en/docs/lakehouse/multi-catalog/hive.md @@ -168,6 +168,8 @@ CREATE CATALOG hive PROPERTIES ( ### Hive With Glue +> When connecting Glue, if it's not on the EC2 environment, need copy the `~/.aws` from the EC2 environment to the current environment. And can also download and configure the [AWS Cli tools](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html), which also creates the `.aws` directory under the current user directory. + ```sql CREATE CATALOG hive PROPERTIES ( "type"="hms", diff --git a/docs/en/docs/lakehouse/multi-catalog/iceberg.md b/docs/en/docs/lakehouse/multi-catalog/iceberg.md index 2baa05770ff135..07325114ca1d56 100644 --- a/docs/en/docs/lakehouse/multi-catalog/iceberg.md +++ b/docs/en/docs/lakehouse/multi-catalog/iceberg.md @@ -96,6 +96,8 @@ CREATE CATALOG iceberg PROPERTIES ( #### AWS Glue +> When connecting Glue, if it's not on the EC2 environment, need copy the `~/.aws` from the EC2 environment to the current environment. And can also download and configure the [AWS Cli tools](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html), which also creates the `.aws` directory under the current user directory. + ```sql CREATE CATALOG glue PROPERTIES ( "type"="iceberg", diff --git a/docs/zh-CN/docs/lakehouse/faq.md b/docs/zh-CN/docs/lakehouse/faq.md index 8651784e2e81b0..7c608eb61ae234 100644 --- a/docs/zh-CN/docs/lakehouse/faq.md +++ b/docs/zh-CN/docs/lakehouse/faq.md @@ -27,6 +27,19 @@ under the License. # 常见问题 +## 证书问题 + +1. 查询时报错 `curl 77: Problem with the SSL CA cert.`。说明当前系统证书过旧,需要更新本地证书。 + - 可以从 `https://curl.haxx.se/docs/caextract.html` 下载最新的CA证书。 + - 将下载后的cacert-xxx.pem放到`/etc/ssl/certs/`目录,例如:`sudo cp cacert-xxx.pem /etc/ssl/certs/ca-certificates.crt`。 + +2. 查询时报错:`ERROR 1105 (HY000): errCode = 2, detailMessage = (x.x.x.x)[CANCELLED][INTERNAL_ERROR]error setting certificate verify locations: CAfile: /etc/ssl/certs/ca-certificates.crt CApath: none`. + +``` +yum install -y ca-certificates +ln -s /etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt /etc/ssl/certs/ca-certificates.crt +``` + ## Kerberos 1. 连接 Kerberos 认证的 Hive Metastore 报错:`GSS initiate failed` @@ -58,6 +71,12 @@ under the License. - 用到的principal必须在klist中存在,使用`klist -kt your.keytab`检查。 - 检查catalog配置是否正确,比如漏配`yarn.resourcemanager.principal`。 - 若上述检查没问题,则当前系统yum或者其他包管理软件安装的JDK版本存在不支持的加密算法,建议自行安装JDK并设置`JAVA_HOME`环境变量。 + - Kerberos默认使用AES-256来进行加密。如果使用Oracle JDK,则必须安装JCE。如果是OpenJDK,OpenJDK的某些发行版会自动提供无限强度的JCE,因此不需要安装JCE。 + - JCE与JDK版本是对应的,需要根据JDK的版本来选择JCE版本,下载JCE的zip包并解压到`$JAVA_HOME/jre/lib/security`目录下: + - JDK6:[JCE6](http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html) + - JDK7:[JCE7](http://www.oracle.com/technetwork/java/embedded/embedded-se/downloads/jce-7-download-432124.html) + - JDK8:[JCE8](http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html) + 5. 使用 KMS 访问 HDFS 时报错:`java.security.InvalidKeyException: Illegal key size` diff --git a/docs/zh-CN/docs/lakehouse/multi-catalog/hive.md b/docs/zh-CN/docs/lakehouse/multi-catalog/hive.md index 16f4565522a300..e75977c25f4766 100644 --- a/docs/zh-CN/docs/lakehouse/multi-catalog/hive.md +++ b/docs/zh-CN/docs/lakehouse/multi-catalog/hive.md @@ -168,6 +168,8 @@ CREATE CATALOG hive PROPERTIES ( ### Hive With Glue +> 连接Glue时,如果是在非EC2环境,需要将EC2环境里的 `~/.aws` 目录拷贝到当前环境里。也可以下载[AWS Cli](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)工具进行配置,这种方式也会在当前用户目录下创建`.aws`目录。 + ```sql CREATE CATALOG hive PROPERTIES ( "type"="hms", diff --git a/docs/zh-CN/docs/lakehouse/multi-catalog/iceberg.md b/docs/zh-CN/docs/lakehouse/multi-catalog/iceberg.md index 3e6a4826d02049..9bf61f6d9f7646 100644 --- a/docs/zh-CN/docs/lakehouse/multi-catalog/iceberg.md +++ b/docs/zh-CN/docs/lakehouse/multi-catalog/iceberg.md @@ -96,6 +96,8 @@ CREATE CATALOG iceberg PROPERTIES ( #### AWS Glue +> 连接Glue时,如果是在非EC2环境,需要将EC2环境里的 `~/.aws` 目录拷贝到当前环境里。也可以下载[AWS Cli](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)工具进行配置,这种方式也会在当前用户目录下创建`.aws`目录。 + ```sql CREATE CATALOG glue PROPERTIES ( "type"="iceberg", From 17f3ca7349d05e10c851d35838836a2ac2235da4 Mon Sep 17 00:00:00 2001 From: starocean999 <40539150+starocean999@users.noreply.github.com> Date: Mon, 25 Dec 2023 23:06:45 +0800 Subject: [PATCH 020/299] [fix](planner)should save original select list item before analyze (#28187) * [fix](planner)should save original select list item before analyze * fix test case * fix failed case --- .../org/apache/doris/catalog/ScalarType.java | 2 +- .../org/apache/doris/analysis/SelectStmt.java | 30 ++++++++++++++----- .../data/ddl_p0/test_create_view.out | 2 +- regression-test/suites/view_p0/view_p0.groovy | 28 +++++++++++++++++ 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java b/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java index a4972d9ced7002..57876d08d721af 100644 --- a/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java +++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java @@ -616,7 +616,7 @@ public String toSql(int depth) { StringBuilder stringBuilder = new StringBuilder(); switch (type) { case CHAR: - if (isWildcardVarchar()) { + if (isWildcardChar()) { stringBuilder.append("CHARACTER"); } else if (Strings.isNullOrEmpty(lenStr)) { stringBuilder.append("CHAR").append("(").append(len).append(")"); diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java index dc3c2f52d812ca..d316a07f093069 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java @@ -561,8 +561,14 @@ public void analyze(Analyzer analyzer) throws UserException { // remove excepted columns resultExprs.removeIf(expr -> exceptCols.contains(expr.toColumnLabel())); colLabels.removeIf(exceptCols::contains); + if (needToSql) { + originalExpr = Expr.cloneList(resultExprs); + } } else { + if (needToSql) { + originalExpr = new ArrayList<>(); + } List items = selectList.getItems(); for (int i = 0; i < items.size(); i++) { SelectListItem item = items.get(i); @@ -574,6 +580,11 @@ public void analyze(Analyzer analyzer) throws UserException { expandStar(analyzer, tblName); } } else { + // save originalExpr before being analyzed + // because analyze may change the expr by adding cast or some other stuff + if (needToSql) { + originalExpr.add(item.getExpr().clone()); + } // Analyze the resultExpr before generating a label to ensure enforcement // of expr child and depth limits (toColumn() label may call toSql()). item.getExpr().analyze(analyzer); @@ -647,10 +658,6 @@ public void analyze(Analyzer analyzer) throws UserException { subColPath.add(expr.toSubColumnLabel()); } } - // analyze valueList if exists - if (needToSql) { - originalExpr = Expr.cloneList(resultExprs); - } // analyze selectListExprs Expr.analyze(resultExprs, analyzer); @@ -1252,6 +1259,9 @@ private void expandStar(TableName tblName, TupleDescriptor desc) throws Analysis slot.setTable(desc.getTable()); slot.setTupleId(desc.getId()); resultExprs.add(rewriteQueryExprByMvColumnExpr(slot, analyzer)); + if (needToSql) { + originalExpr.add(slot); + } colLabels.add(col.getName()); // empty sub lables subColPath.add(Lists.newArrayList()); @@ -2601,6 +2611,9 @@ public void substituteSelectList(Analyzer analyzer, List newColLabels) tblRef.analyze(analyzer); leftTblRef = tblRef; } + if (needToSql) { + originalExpr = new ArrayList<>(); + } // populate selectListExprs, aliasSMap, and colNames for (SelectListItem item : selectList.getItems()) { if (item.isStar()) { @@ -2611,6 +2624,11 @@ public void substituteSelectList(Analyzer analyzer, List newColLabels) expandStar(analyzer, tblName); } } else { + if (needToSql) { + // save originalExpr before being analyzed + // because analyze may change the expr by adding cast or some other stuff + originalExpr.add(item.getExpr().clone()); + } // to make sure the sortinfo's AnalyticExpr and resultExprs's AnalyticExpr analytic once if (item.getExpr() instanceof AnalyticExpr) { item.getExpr().analyze(analyzer); @@ -2624,9 +2642,7 @@ public void substituteSelectList(Analyzer analyzer, List newColLabels) resultExprs.add(rewriteQueryExprByMvColumnExpr(item.getExpr(), analyzer)); } } - if (needToSql) { - originalExpr = Expr.cloneList(resultExprs); - } + // substitute group by if (groupByClause != null) { boolean aliasFirst = false; diff --git a/regression-test/data/ddl_p0/test_create_view.out b/regression-test/data/ddl_p0/test_create_view.out index a84d667d0acd14..239fafc025ef3a 100644 --- a/regression-test/data/ddl_p0/test_create_view.out +++ b/regression-test/data/ddl_p0/test_create_view.out @@ -25,5 +25,5 @@ 3 [-1, 20, 0] [0, 1, 0] -- !test_view_6 -- -v1 CREATE VIEW `v1` COMMENT 'VIEW' AS SELECT `error_code` AS `error_code`, 1 AS `__literal_1`, 'string' AS `__literal_2`, now() AS `__now_3`, dayofyear(`op_time`) AS `__dayofyear_4`, CAST(`source` AS BIGINT) AS `__cast_expr_5`, min(`timestamp`) OVER (ORDER BY `op_time` DESC NULLS LAST ROWS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING) AS `__min_6`, 1 > 2 AS `__binary_predicate_7`, (2 + 3) AS `__arithmetic_expr_8`, 1 IN (1, 2, 3, 4) AS `__in_predicate_9`, `remark` LIKE '%like' AS `__like_predicate_10`, CASE WHEN `remark` = 's' THEN 1 ELSE 2 END AS `__case_expr_11`, (CAST(TRUE AS BIGINT) | CAST(FALSE AS BIGINT)) AS `__arithmetic_expr_12` FROM `regression_test_ddl_p0`.`view_column_name_test`; +v1 CREATE VIEW `v1` COMMENT 'VIEW' AS SELECT `error_code` AS `error_code`, 1 AS `__literal_1`, 'string' AS `__literal_2`, now() AS `__now_3`, dayofyear(`op_time`) AS `__dayofyear_4`, CAST(`source` AS BIGINT) AS `__cast_expr_5`, min(`timestamp`) OVER (ORDER BY `op_time` DESC NULLS LAST ROWS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING) AS `__min_6`, 1 > 2 AS `__binary_predicate_7`, (2 + 3) AS `__arithmetic_expr_8`, 1 IN (1, 2, 3, 4) AS `__in_predicate_9`, `remark` LIKE '%like' AS `__like_predicate_10`, CASE WHEN `remark` = 's' THEN 1 ELSE 2 END AS `__case_expr_11`, (TRUE | FALSE) AS `__arithmetic_expr_12` FROM `regression_test_ddl_p0`.`view_column_name_test`; diff --git a/regression-test/suites/view_p0/view_p0.groovy b/regression-test/suites/view_p0/view_p0.groovy index a1df7e07cf4d20..82c9e3987ce03e 100644 --- a/regression-test/suites/view_p0/view_p0.groovy +++ b/regression-test/suites/view_p0/view_p0.groovy @@ -136,4 +136,32 @@ suite("view_p0") { sql """CREATE VIEW IF NOT EXISTS `test_view_abc`(`a`) AS WITH T1 AS (SELECT 1 AS 'a'), T2 AS (SELECT 2 AS 'a') SELECT T1.a FROM T1 UNION ALL SELECT T2.a FROM T2;""" sql "drop view if exists test_view_abc;" + + sql """DROP TABLE IF EXISTS test_view_table2""" + + sql """ + CREATE TABLE test_view_table2 ( + c_date varchar(50) + ) + ENGINE=OLAP + UNIQUE KEY(`c_date`) + distributed by hash(c_date) properties('replication_num'='1'); + """ + + sql """ drop view if exists test_view_table2_view;""" + sql """CREATE VIEW `test_view_table2_view` + AS + SELECT + date_format(c_date,'%Y-%m-%d') AS `CREATE_DATE` + FROM + test_view_table2 + GROUP BY + date_format(c_date, '%Y-%m-%d'); + """ + sql """set enable_nereids_planner=false;""" + sql """select * from test_view_table2_view;""" + sql """set enable_nereids_planner=true;""" + sql """select * from test_view_table2_view;""" + sql """ drop view if exists test_view_table2_view;""" + sql """DROP TABLE IF EXISTS test_view_table2""" } From cefae3dc90a4d59bba7b4ca94219e98e6062402a Mon Sep 17 00:00:00 2001 From: plat1ko Date: Tue, 26 Dec 2023 00:29:03 +0800 Subject: [PATCH 021/299] [bug](storage) Fix gc rowset bug (#28979) --- be/src/olap/data_dir.cpp | 76 ++++++++++++++++--------- be/test/agent/task_worker_pool_test.cpp | 2 + 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/be/src/olap/data_dir.cpp b/be/src/olap/data_dir.cpp index 8aa07791ebc837..80a0f7131fdad1 100644 --- a/be/src/olap/data_dir.cpp +++ b/be/src/olap/data_dir.cpp @@ -681,14 +681,17 @@ void DataDir::_perform_path_gc_by_tablet(std::vector& tablet_paths) } void DataDir::_perform_path_gc_by_rowset(const std::vector& tablet_paths) { - if (_stop_bg_worker) return; - if (tablet_paths.empty()) { + if (_stop_bg_worker || tablet_paths.empty()) { return; } + LOG(INFO) << "start to path gc by rowset"; int counter = 0; for (const auto& path : tablet_paths) { - if (_stop_bg_worker) break; + if (_stop_bg_worker) { + break; + } + ++counter; if (config::path_gc_check_step > 0 && counter % config::path_gc_check_step == 0) { std::this_thread::sleep_for( @@ -696,31 +699,50 @@ void DataDir::_perform_path_gc_by_rowset(const std::vector& tablet_ } TTabletId tablet_id = -1; TSchemaHash schema_hash = -1; - bool is_valid = _tablet_manager->get_tablet_id_and_schema_hash_from_path(path, &tablet_id, - &schema_hash); + bool is_valid = doris::TabletManager::get_tablet_id_and_schema_hash_from_path( + path, &tablet_id, &schema_hash); if (!is_valid || tablet_id < 1 || schema_hash < 1) [[unlikely]] { LOG(WARNING) << "unknown path:" << path; continue; } + + auto tablet = _tablet_manager->get_tablet(tablet_id); + if (!tablet) { + // Could not found the tablet, maybe it's a dropped tablet, will be reclaimed + // in the next time `_perform_path_gc_by_tablet` + continue; + } + bool exists; std::vector files; - if (_stop_bg_worker) break; auto st = io::global_local_filesystem()->list(path, true, &files, &exists); if (!st.ok()) [[unlikely]] { LOG(WARNING) << "fail to list tablet path " << path << " : " << st; continue; } - RowsetIdUnorderedSet useful_rowsets; - auto tablet = _tablet_manager->get_tablet(tablet_id); - if (!tablet) { - // Could not found the tablet, maybe it's a dropped tablet, will be reclaimed - // in the next time `_perform_path_gc_by_tablet` - continue; + + // Rowset files excluding pending rowsets + std::vector> rowsets_not_pending; + for (auto&& file : files) { + auto rowset_id = extract_rowset_id(file.file_name); + if (rowset_id.hi == 0) { + continue; // Not a rowset + } + + if (StorageEngine::instance()->pending_local_rowsets().contains(rowset_id)) { + continue; // Pending rowset file + } + + rowsets_not_pending.emplace_back(rowset_id, std::move(file.file_name)); } + + RowsetIdUnorderedSet rowsets_in_version_map; tablet->traverse_rowsets( - [&useful_rowsets](auto& rs) { useful_rowsets.insert(rs->rowset_id()); }, true); - // rowset_id -> is_garbage - std::unordered_map checked_rowsets; + [&rowsets_in_version_map](auto& rs) { + rowsets_in_version_map.insert(rs->rowset_id()); + }, + true); + auto reclaim_rowset_file = [](const std::string& path) { auto st = io::global_local_filesystem()->delete_file(path); if (!st.ok()) [[unlikely]] { @@ -729,28 +751,26 @@ void DataDir::_perform_path_gc_by_rowset(const std::vector& tablet_ } LOG(INFO) << "delete garbage path: " << path; // Audit log }; + auto should_reclaim = [&, this](const RowsetId& rowset_id) { - return !useful_rowsets.contains(rowset_id) && - !StorageEngine::instance()->pending_local_rowsets().contains(rowset_id) && + return !rowsets_in_version_map.contains(rowset_id) && !StorageEngine::instance()->check_rowset_id_in_unused_rowsets(rowset_id) && RowsetMetaManager::exists(get_meta(), tablet->tablet_uid(), rowset_id) .is(); }; - for (auto&& file : files) { - auto rowset_id = extract_rowset_id(file.file_name); - if (rowset_id.hi == 0) { - continue; // Not a rowset - } - auto find_it = checked_rowsets.find(rowset_id); - if (find_it != checked_rowsets.end()) { - if (find_it->second) { - reclaim_rowset_file(path + '/' + file.file_name); + + // rowset_id -> is_garbage + std::unordered_map checked_rowsets; + for (auto&& [rowset_id, filename] : rowsets_not_pending) { + if (auto it = checked_rowsets.find(rowset_id); it != checked_rowsets.end()) { + if (it->second) { // Is checked garbage rowset + reclaim_rowset_file(path + '/' + filename); } continue; } - // Check rowset useful + if (should_reclaim(rowset_id)) { - reclaim_rowset_file(path + '/' + file.file_name); + reclaim_rowset_file(path + '/' + filename); checked_rowsets.emplace(rowset_id, true); } else { checked_rowsets.emplace(rowset_id, false); diff --git a/be/test/agent/task_worker_pool_test.cpp b/be/test/agent/task_worker_pool_test.cpp index 3ea591c805840f..8fd78cc3fd225a 100644 --- a/be/test/agent/task_worker_pool_test.cpp +++ b/be/test/agent/task_worker_pool_test.cpp @@ -44,6 +44,7 @@ TEST(TaskWorkerPoolTest, TaskWorkerPool) { workers.submit_task(task); workers.submit_task(task); // Pending and ignored when stop + std::this_thread::sleep_for(200ms); workers.stop(); workers.submit_task(task); // Ignore @@ -92,6 +93,7 @@ TEST(TaskWorkerPoolTest, PriorTaskWorkerPool) { workers.submit_task(task); workers.submit_task(task); workers.submit_task(task); // Pending and ignored when stop + std::this_thread::sleep_for(100ms); workers.stop(); EXPECT_EQ(normal_count.load(), 2); From 2aea47c0a9383c1c981cbf2cb7dd72bce2bfb3ec Mon Sep 17 00:00:00 2001 From: zhangdong <493738387@qq.com> Date: Tue, 26 Dec 2023 00:29:28 +0800 Subject: [PATCH 022/299] [fix](mtmv)add log for resolve pending task (#28999) * add lock for resolve pending task * add lock for resolve pending task --- .../org/apache/doris/job/extensions/mtmv/MTMVTask.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fe/fe-core/src/main/java/org/apache/doris/job/extensions/mtmv/MTMVTask.java b/fe/fe-core/src/main/java/org/apache/doris/job/extensions/mtmv/MTMVTask.java index ab9b6f9fa94211..7c11f0549ad859 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/job/extensions/mtmv/MTMVTask.java +++ b/fe/fe-core/src/main/java/org/apache/doris/job/extensions/mtmv/MTMVTask.java @@ -135,6 +135,7 @@ public MTMVTask(long dbId, long mtmvId, MTMVTaskContext taskContext) { @Override public void run() throws JobException { + LOG.info("mtmv task run, taskId: {}", super.getTaskId()); try { ConnectContext ctx = MTMVPlanUtil.createMTMVContext(mtmv); // Every time a task is run, the relation is regenerated because baseTables and baseViews may change, @@ -185,18 +186,21 @@ private void exec(ConnectContext ctx, Set refreshPartitionIds, Map Date: Tue, 26 Dec 2023 00:29:42 +0800 Subject: [PATCH 023/299] [improvement](nereids) Get partition related table disable nullable field and complete agg matched pattern mv rules. (#28973) * [improvement] (nereids) Get partition related table disable nullable field and modify regression test, complete agg mv rules. * make filed not null to create partition mv --- .../apache/doris/nereids/rules/RuleSet.java | 6 +++ .../MaterializedViewFilterAggregateRule.java | 45 +++++++++++++++++ ...ializedViewFilterProjectAggregateRule.java | 47 ++++++++++++++++++ ...ializedViewProjectFilterAggregateRule.java | 47 ++++++++++++++++++ .../exploration/mv/MaterializedViewUtils.java | 1 + .../mv/MaterializedViewUtilsTest.java | 48 +++++++++++++++++++ .../aggregate_with_roll_up.out | 2 + .../aggregate_without_roll_up.out | 14 ++++++ .../mv/join/inner/inner_join.out | 20 ++++++++ .../mv/join/left_outer/outer_join.out | 16 +++++++ .../test_partition_refresh_mtmv.groovy | 12 ++--- .../aggregate_with_roll_up.groovy | 2 +- .../aggregate_without_roll_up.groovy | 8 ++-- .../mv/join/inner/inner_join.groovy | 10 ++-- .../mv/join/left_outer/outer_join.groovy | 2 +- 15 files changed, 263 insertions(+), 17 deletions(-) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewFilterAggregateRule.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewFilterProjectAggregateRule.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewProjectFilterAggregateRule.java diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java index a9ce2cce17412d..b7d07aa134df74 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java @@ -40,10 +40,13 @@ import org.apache.doris.nereids.rules.exploration.join.SemiJoinSemiJoinTranspose; import org.apache.doris.nereids.rules.exploration.join.SemiJoinSemiJoinTransposeProject; import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewAggregateRule; +import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewFilterAggregateRule; import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewFilterJoinRule; +import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewFilterProjectAggregateRule; import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewFilterProjectJoinRule; import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewOnlyJoinRule; import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewProjectAggregateRule; +import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewProjectFilterAggregateRule; import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewProjectFilterJoinRule; import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewProjectJoinRule; import org.apache.doris.nereids.rules.implementation.AggregateStrategies; @@ -233,6 +236,9 @@ public class RuleSet { .add(MaterializedViewProjectFilterJoinRule.INSTANCE) .add(MaterializedViewAggregateRule.INSTANCE) .add(MaterializedViewProjectAggregateRule.INSTANCE) + .add(MaterializedViewFilterAggregateRule.INSTANCE) + .add(MaterializedViewProjectFilterAggregateRule.INSTANCE) + .add(MaterializedViewFilterProjectAggregateRule.INSTANCE) .build(); public List getDPHypReorderRules() { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewFilterAggregateRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewFilterAggregateRule.java new file mode 100644 index 00000000000000..40c8b6d681b3ea --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewFilterAggregateRule.java @@ -0,0 +1,45 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.rules.exploration.mv; + +import org.apache.doris.nereids.rules.Rule; +import org.apache.doris.nereids.rules.RuleType; +import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate; +import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; + +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * MaterializedViewFilterAggregateRule + */ +public class MaterializedViewFilterAggregateRule extends AbstractMaterializedViewAggregateRule { + + public static final MaterializedViewFilterAggregateRule INSTANCE = new MaterializedViewFilterAggregateRule(); + + @Override + public List buildRules() { + return ImmutableList.of( + logicalFilter(logicalAggregate(any())).thenApplyMultiNoThrow(ctx -> { + LogicalFilter> root = ctx.root; + return rewrite(root, ctx.cascadesContext); + }).toRule(RuleType.MATERIALIZED_VIEW_FILTER_AGGREGATE)); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewFilterProjectAggregateRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewFilterProjectAggregateRule.java new file mode 100644 index 00000000000000..40655e97394463 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewFilterProjectAggregateRule.java @@ -0,0 +1,47 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.rules.exploration.mv; + +import org.apache.doris.nereids.rules.Rule; +import org.apache.doris.nereids.rules.RuleType; +import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate; +import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; +import org.apache.doris.nereids.trees.plans.logical.LogicalProject; + +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * MaterializedViewFilterProjectAggregateRule + */ +public class MaterializedViewFilterProjectAggregateRule extends AbstractMaterializedViewAggregateRule { + + public static final MaterializedViewFilterProjectAggregateRule + INSTANCE = new MaterializedViewFilterProjectAggregateRule(); + + @Override + public List buildRules() { + return ImmutableList.of( + logicalFilter(logicalProject(logicalAggregate(any()))).thenApplyMultiNoThrow(ctx -> { + LogicalFilter>> root = ctx.root; + return rewrite(root, ctx.cascadesContext); + }).toRule(RuleType.MATERIALIZED_VIEW_FILTER_AGGREGATE)); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewProjectFilterAggregateRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewProjectFilterAggregateRule.java new file mode 100644 index 00000000000000..b841862d5b2505 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewProjectFilterAggregateRule.java @@ -0,0 +1,47 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.rules.exploration.mv; + +import org.apache.doris.nereids.rules.Rule; +import org.apache.doris.nereids.rules.RuleType; +import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate; +import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; +import org.apache.doris.nereids.trees.plans.logical.LogicalProject; + +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * MaterializedViewProjectFilterAggregateRule + */ +public class MaterializedViewProjectFilterAggregateRule extends AbstractMaterializedViewAggregateRule { + + public static final MaterializedViewProjectFilterAggregateRule + INSTANCE = new MaterializedViewProjectFilterAggregateRule(); + + @Override + public List buildRules() { + return ImmutableList.of( + logicalProject(logicalFilter(logicalAggregate(any()))).thenApplyMultiNoThrow(ctx -> { + LogicalProject>> root = ctx.root; + return rewrite(root, ctx.cascadesContext); + }).toRule(RuleType.MATERIALIZED_VIEW_FILTER_AGGREGATE)); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java index ca54c3768318cc..3a8da2c752bc49 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java @@ -222,6 +222,7 @@ public Void visitLogicalRelation(LogicalRelation relation, IncrementCheckerConte if (partitionColumnSet.contains(mvReferenceColumn)) { context.setRelatedTable(table); context.setRelatedTableColumn(mvReferenceColumn); + context.setPctPossible(!mvReferenceColumn.isAllowNull()); } return visit(relation, context); } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtilsTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtilsTest.java index c2af1dbdf4aa17..3b78d88c39b05b 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtilsTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtilsTest.java @@ -93,6 +93,31 @@ protected void runBeforeAll() throws Exception { + "PROPERTIES (\n" + " \"replication_num\" = \"1\"\n" + ")"); + + createTable("CREATE TABLE IF NOT EXISTS lineitem_null (\n" + + " L_ORDERKEY INTEGER NOT NULL,\n" + + " L_PARTKEY INTEGER NOT NULL,\n" + + " L_SUPPKEY INTEGER NOT NULL,\n" + + " L_LINENUMBER INTEGER NOT NULL,\n" + + " L_QUANTITY DECIMALV3(15,2) NOT NULL,\n" + + " L_EXTENDEDPRICE DECIMALV3(15,2) NOT NULL,\n" + + " L_DISCOUNT DECIMALV3(15,2) NOT NULL,\n" + + " L_TAX DECIMALV3(15,2) NOT NULL,\n" + + " L_RETURNFLAG CHAR(1) NOT NULL,\n" + + " L_LINESTATUS CHAR(1) NOT NULL,\n" + + " L_SHIPDATE DATE NULL,\n" + + " L_COMMITDATE DATE NULL,\n" + + " L_RECEIPTDATE DATE NULL,\n" + + " L_SHIPINSTRUCT CHAR(25) NOT NULL,\n" + + " L_SHIPMODE CHAR(10) NOT NULL,\n" + + " L_COMMENT VARCHAR(44) NOT NULL\n" + + ")\n" + + "DUPLICATE KEY(L_ORDERKEY, L_PARTKEY, L_SUPPKEY, L_LINENUMBER)\n" + + "PARTITION BY RANGE(L_SHIPDATE) (PARTITION `day_1` VALUES LESS THAN ('2017-02-01'))\n" + + "DISTRIBUTED BY HASH(L_ORDERKEY) BUCKETS 3\n" + + "PROPERTIES (\n" + + " \"replication_num\" = \"1\"\n" + + ")"); } @Test @@ -121,6 +146,29 @@ public void getRelatedTableInfoTestWithoutGroupTest() { }); } + @Test + public void getRelatedTableInfoTestWithoutGroupNullTest() { + PlanChecker.from(connectContext) + .checkExplain("SELECT (o.c1_abs + ps.c2_abs) as add_alias, l.L_SHIPDATE, l.L_ORDERKEY, o.O_ORDERDATE, " + + "ps.PS_AVAILQTY " + + "FROM " + + "lineitem_null as l " + + "LEFT JOIN " + + "(SELECT abs(O_TOTALPRICE + 10) as c1_abs, O_CUSTKEY, O_ORDERDATE, O_ORDERKEY " + + "FROM orders) as o " + + "ON l.L_ORDERKEY = o.O_ORDERKEY " + + "JOIN " + + "(SELECT abs(sqrt(PS_SUPPLYCOST)) as c2_abs, PS_AVAILQTY, PS_PARTKEY, PS_SUPPKEY " + + "FROM partsupp) as ps " + + "ON l.L_PARTKEY = ps.PS_PARTKEY and l.L_SUPPKEY = ps.PS_SUPPKEY", + nereidsPlanner -> { + Plan rewrittenPlan = nereidsPlanner.getRewrittenPlan(); + Optional relatedTableInfo = + MaterializedViewUtils.getRelatedTableInfo("l_shipdate", rewrittenPlan); + Assertions.assertFalse(relatedTableInfo.isPresent()); + }); + } + @Test public void getRelatedTableInfoTestWithSubqueryTest() { PlanChecker.from(connectContext) diff --git a/regression-test/data/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.out b/regression-test/data/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.out index e6facb7b89026f..fb223bc661b9b1 100644 --- a/regression-test/data/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.out +++ b/regression-test/data/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.out @@ -38,8 +38,10 @@ 3 3 2023-12-11 43.20 43.20 43.20 1 0 -- !query18_0_before -- +3 2023-12-11 43.20 43.20 43.20 1 0 -- !query18_0_after -- +3 2023-12-11 43.20 43.20 43.20 1 0 -- !query19_0_before -- 2 3 2023-12-08 20.00 diff --git a/regression-test/data/nereids_rules_p0/mv/agg_without_roll_up/aggregate_without_roll_up.out b/regression-test/data/nereids_rules_p0/mv/agg_without_roll_up/aggregate_without_roll_up.out index cb924d69bbbfb7..8bf8d7d4f9fa76 100644 --- a/regression-test/data/nereids_rules_p0/mv/agg_without_roll_up/aggregate_without_roll_up.out +++ b/regression-test/data/nereids_rules_p0/mv/agg_without_roll_up/aggregate_without_roll_up.out @@ -1,7 +1,13 @@ -- This file is automatically generated. You should know what you did if you want to edit this -- !query1_0_before -- +1 yy 0 0 77.50 33.50 9.50 5 +2 mi 0 0 57.40 56.20 1.20 2 +2 mm 0 0 43.20 43.20 43.20 1 -- !query1_0_after -- +1 yy 0 0 77.50 33.50 9.50 5 +2 mi 0 0 57.40 56.20 1.20 2 +2 mm 0 0 43.20 43.20 43.20 1 -- !query1_1_before -- 2023-12-08 1 yy 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 @@ -114,16 +120,24 @@ 5 2 mi 0 0 0 0 0 0 0 0 0 0 0 0 0 -- !query18_0_before -- +3 2023-12-11 43.20 43.20 43.20 1 0 -- !query18_0_after -- +3 2023-12-11 43.20 43.20 43.20 1 0 -- !query18_1_before -- +4 43.20 -- !query18_1_after -- +4 43.20 -- !query18_2_before -- +4 43.20 +6 57.40 -- !query18_2_after -- +4 43.20 +6 57.40 -- !query19_0_before -- 2 3 2023-12-08 20.00 10.50 9.50 2 diff --git a/regression-test/data/nereids_rules_p0/mv/join/inner/inner_join.out b/regression-test/data/nereids_rules_p0/mv/join/inner/inner_join.out index df3e20dc64ce0b..abf0eb188e6b25 100644 --- a/regression-test/data/nereids_rules_p0/mv/join/inner/inner_join.out +++ b/regression-test/data/nereids_rules_p0/mv/join/inner/inner_join.out @@ -100,8 +100,24 @@ 6 -- !query2_0_before -- +4 +4 +4 +4 +4 +4 +6 +6 -- !query2_0_after -- +4 +4 +4 +4 +4 +4 +6 +6 -- !query2_1_before -- 4 @@ -268,8 +284,12 @@ 2 3 2023-12-08 -- !query7_0_before -- +2 3 2023-12-08 +2 3 2023-12-08 -- !query7_0_after -- +2 3 2023-12-08 +2 3 2023-12-08 -- !query10_0_before -- diff --git a/regression-test/data/nereids_rules_p0/mv/join/left_outer/outer_join.out b/regression-test/data/nereids_rules_p0/mv/join/left_outer/outer_join.out index 94143e800ada97..e8665ccb9484fc 100644 --- a/regression-test/data/nereids_rules_p0/mv/join/left_outer/outer_join.out +++ b/regression-test/data/nereids_rules_p0/mv/join/left_outer/outer_join.out @@ -88,8 +88,24 @@ 2 2 -- !query2_0_before -- +4 +4 +4 +4 +4 +4 +6 +6 -- !query2_0_after -- +4 +4 +4 +4 +4 +4 +6 +6 -- !query2_1_before -- 4 diff --git a/regression-test/suites/mtmv_p0/test_partition_refresh_mtmv.groovy b/regression-test/suites/mtmv_p0/test_partition_refresh_mtmv.groovy index 8688cb00b6857c..f2a55d43472d38 100644 --- a/regression-test/suites/mtmv_p0/test_partition_refresh_mtmv.groovy +++ b/regression-test/suites/mtmv_p0/test_partition_refresh_mtmv.groovy @@ -30,7 +30,7 @@ suite("test_partition_refresh_mtmv") { CREATE TABLE `${tableNameNum}` ( `user_id` LARGEINT NOT NULL COMMENT '\"用户id\"', `date` DATE NOT NULL COMMENT '\"数据灌入日期时间\"', - `num` SMALLINT NULL COMMENT '\"数量\"' + `num` SMALLINT NOT NULL COMMENT '\"数量\"' ) ENGINE=OLAP DUPLICATE KEY(`user_id`, `date`, `num`) COMMENT 'OLAP' @@ -64,7 +64,7 @@ suite("test_partition_refresh_mtmv") { CREATE TABLE `${tableNameNum}` ( `user_id` LARGEINT NOT NULL COMMENT '\"用户id\"', `date` DATE NOT NULL COMMENT '\"数据灌入日期时间\"', - `num` SMALLINT NULL COMMENT '\"数量\"' + `num` SMALLINT NOT NULL COMMENT '\"数量\"' ) ENGINE=OLAP DUPLICATE KEY(`user_id`, `date`, `num`) COMMENT 'OLAP' @@ -98,7 +98,7 @@ suite("test_partition_refresh_mtmv") { CREATE TABLE `${tableNameNum}` ( `user_id` LARGEINT NOT NULL COMMENT '\"用户id\"', `date` DATE NOT NULL COMMENT '\"数据灌入日期时间\"', - `num` SMALLINT NULL COMMENT '\"数量\"' + `num` SMALLINT NOT NULL COMMENT '\"数量\"' ) ENGINE=OLAP DUPLICATE KEY(`user_id`, `date`, `num`) COMMENT 'OLAP' @@ -144,7 +144,7 @@ suite("test_partition_refresh_mtmv") { CREATE TABLE `${tableNameNum}` ( `user_id` LARGEINT NOT NULL COMMENT '\"用户id\"', `date` DATE NOT NULL COMMENT '\"数据灌入日期时间\"', - `num` SMALLINT NULL COMMENT '\"数量\"' + `num` SMALLINT NOT NULL COMMENT '\"数量\"' ) ENGINE=OLAP DUPLICATE KEY(`user_id`, `date`, `num`) COMMENT 'OLAP' @@ -236,7 +236,7 @@ suite("test_partition_refresh_mtmv") { CREATE TABLE `${tableNameNum}` ( `user_id` LARGEINT NOT NULL COMMENT '\"用户id\"', `date` DATE NOT NULL COMMENT '\"数据灌入日期时间\"', - `num` SMALLINT NULL COMMENT '\"数量\"' + `num` SMALLINT NOT NULL COMMENT '\"数量\"' ) ENGINE=OLAP DUPLICATE KEY(`user_id`, `date`, `num`) COMMENT 'OLAP' @@ -337,7 +337,7 @@ suite("test_partition_refresh_mtmv") { CREATE TABLE `${tableNameNum}` ( `user_id` LARGEINT NOT NULL COMMENT '\"用户id\"', `date` DATE NOT NULL COMMENT '\"数据灌入日期时间\"', - `num` SMALLINT NULL COMMENT '\"数量\"' + `num` SMALLINT NOT NULL COMMENT '\"数量\"' ) ENGINE=OLAP DUPLICATE KEY(`user_id`, `date`, `num`) COMMENT 'OLAP' diff --git a/regression-test/suites/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.groovy b/regression-test/suites/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.groovy index b56922d5db6470..d6b5c37e59c095 100644 --- a/regression-test/suites/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.groovy +++ b/regression-test/suites/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.groovy @@ -402,7 +402,7 @@ suite("aggregate_with_roll_up") { "count(distinct case when o_shippriority > 1 and o_orderkey IN (1, 3) then o_custkey else null end) " + "from lineitem t1 " + "left join orders on t1.l_orderkey = orders.o_orderkey and t1.l_shipdate = o_orderdate " + - "where o_orderdate = '2023-12-11' and l_partkey = 2 " + + "where o_orderdate = '2023-12-11' and l_partkey = 3 " + "group by " + "l_shipdate, " + "l_suppkey" diff --git a/regression-test/suites/nereids_rules_p0/mv/agg_without_roll_up/aggregate_without_roll_up.groovy b/regression-test/suites/nereids_rules_p0/mv/agg_without_roll_up/aggregate_without_roll_up.groovy index 72e80eadbf93f0..5d40aa03138a6d 100644 --- a/regression-test/suites/nereids_rules_p0/mv/agg_without_roll_up/aggregate_without_roll_up.groovy +++ b/regression-test/suites/nereids_rules_p0/mv/agg_without_roll_up/aggregate_without_roll_up.groovy @@ -194,7 +194,7 @@ suite("aggregate_without_roll_up") { "min(o_totalprice), " + "count(*) " + "from orders " + - "where o_shippriority in (9.5, 10.5)" + + "where o_shippriority in (1, 2)" + "group by " + "o_shippriority, " + "o_comment " @@ -645,7 +645,7 @@ suite("aggregate_without_roll_up") { "count(distinct case when o_shippriority > 1 and o_orderkey IN (1, 3) then o_custkey else null end) " + "from lineitem t1 " + "left join orders on t1.l_orderkey = orders.o_orderkey and t1.l_shipdate = o_orderdate " + - "where l_shipdate = '2023-12-11' and l_suppkey = 2 " + + "where l_shipdate = '2023-12-11' and l_suppkey = 3 " + "group by " + "l_shipdate, " + "l_suppkey" @@ -662,7 +662,7 @@ suite("aggregate_without_roll_up") { def query18_1 = "select l_linenumber, sum(o_totalprice) as sum_alias " + "from lineitem " + "inner join orders on l_orderkey = o_orderkey " + - "where o_custkey = 2 and l_linenumber = 3 " + + "where o_custkey = 2 and l_linenumber = 4 " + "group by l_linenumber, o_custkey " order_qt_query18_1_before "${query18_1}" check_rewrite(mv18_1, query18_1, "mv18_1") @@ -677,7 +677,7 @@ suite("aggregate_without_roll_up") { def query18_2 = "select lineitem.l_linenumber, sum(o_totalprice) as sum_alias " + "from lineitem " + "inner join orders on lineitem.l_orderkey = orders.o_orderkey " + - "where o_custkey = 2 and l_suppkey= 4 " + + "where o_custkey = 2 and l_suppkey= 3 " + "group by lineitem.l_linenumber, orders.o_custkey " order_qt_query18_2_before "${query18_2}" check_not_match(mv18_2, query18_2, "mv18_2") diff --git a/regression-test/suites/nereids_rules_p0/mv/join/inner/inner_join.groovy b/regression-test/suites/nereids_rules_p0/mv/join/inner/inner_join.groovy index 0dcf33a9d87eb2..ccf6a83f2c610d 100644 --- a/regression-test/suites/nereids_rules_p0/mv/join/inner/inner_join.groovy +++ b/regression-test/suites/nereids_rules_p0/mv/join/inner/inner_join.groovy @@ -239,7 +239,7 @@ suite("inner_join") { def query2_0 = "select lineitem.L_LINENUMBER " + "from lineitem " + "inner join orders on lineitem.L_ORDERKEY = orders.O_ORDERKEY " + - "where lineitem.L_LINENUMBER > 10" + "where lineitem.L_LINENUMBER > 0" order_qt_query2_0_before "${query2_0}" check_rewrite(mv2_0, query2_0, "mv2_0") order_qt_query2_0_after "${query2_0}" @@ -400,10 +400,10 @@ suite("inner_join") { "on lineitem.l_orderkey = o_orderkey and l_shipdate = o_orderdate " def query7_0 = "select l_partkey, l_suppkey, l_shipdate " + "from (select l_shipdate, l_orderkey, l_partkey, l_suppkey " + - "from lineitem where l_partkey in (3, 4)) t1 " + + "from lineitem where l_partkey in (2, 3, 4)) t1 " + "inner join (select * from orders where o_orderdate = '2023-12-08') t2 " + "on t1.l_orderkey = o_orderkey and t1.l_shipdate = o_orderdate " + - "where l_partkey = 3" + "where l_partkey = 2" order_qt_query7_0_before "${query7_0}" check_rewrite(mv7_0, query7_0, "mv7_0") order_qt_query7_0_after "${query7_0}" @@ -417,8 +417,8 @@ suite("inner_join") { def query10_0 = "select orders.O_CUSTKEY " + "from orders " + "inner join lineitem on orders.O_ORDERKEY = lineitem.L_ORDERKEY " + - "WHERE lineitem.L_LINENUMBER > 10 AND orders.O_CUSTKEY = 5 AND " + - "orders.O_SHIPPRIORITY = 1" + "WHERE lineitem.L_LINENUMBER > 0 AND orders.O_CUSTKEY = 1 AND " + + "orders.O_SHIPPRIORITY = 2" order_qt_query10_0_before "${query10_0}" check_not_match(mv10_0, query10_0, "mv10_0") order_qt_query10_0_after "${query10_0}" diff --git a/regression-test/suites/nereids_rules_p0/mv/join/left_outer/outer_join.groovy b/regression-test/suites/nereids_rules_p0/mv/join/left_outer/outer_join.groovy index d7ec67189ac871..e8b1da2572a22d 100644 --- a/regression-test/suites/nereids_rules_p0/mv/join/left_outer/outer_join.groovy +++ b/regression-test/suites/nereids_rules_p0/mv/join/left_outer/outer_join.groovy @@ -224,7 +224,7 @@ suite("outer_join") { def query2_0 = "select lineitem.L_LINENUMBER " + "from lineitem " + "left join orders on lineitem.L_ORDERKEY = orders.O_ORDERKEY " + - "where lineitem.L_LINENUMBER > 10" + "where lineitem.L_LINENUMBER > 0" order_qt_query2_0_before "${query2_0}" check_not_match(mv2_0, query2_0, "mv2_0") order_qt_query2_0_after "${query2_0}" From 75a45484b6d7864ee3415b3d2351d326db48bf86 Mon Sep 17 00:00:00 2001 From: lihangyu <15605149486@163.com> Date: Tue, 26 Dec 2023 00:34:58 +0800 Subject: [PATCH 024/299] [chore](config) modify `tablet_schema_cache_recycle_interval` from 24h to 1h (#28980) To prevent from too many tablet schema cache in memory and lead to performance issue when hold lock to erase item --- be/src/common/config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/be/src/common/config.cpp b/be/src/common/config.cpp index 8153e5aca5f86b..2f12cea1fa3470 100644 --- a/be/src/common/config.cpp +++ b/be/src/common/config.cpp @@ -1100,7 +1100,7 @@ DEFINE_Int32(group_commit_memory_rows_for_max_filter_ratio, "10000"); DEFINE_Bool(wait_internal_group_commit_finish, "false"); DEFINE_mInt32(scan_thread_nice_value, "0"); -DEFINE_mInt32(tablet_schema_cache_recycle_interval, "86400"); +DEFINE_mInt32(tablet_schema_cache_recycle_interval, "3600"); DEFINE_Bool(exit_on_exception, "false"); // This config controls whether the s3 file writer would flush cache asynchronously From f30e50676ec93aa647ef96f78996616686f92c12 Mon Sep 17 00:00:00 2001 From: Ashin Gau Date: Tue, 26 Dec 2023 10:24:12 +0800 Subject: [PATCH 025/299] [opt](scanner) optimize the number of threads of scanners (#28640) 1. Remove `doris_max_remote_scanner_thread_pool_thread_num`, use `doris_scanner_thread_pool_thread_num` only. 2. Set the default value `doris_scanner_thread_pool_thread_num` as `std::max(48, CpuInfo::num_cores() * 4)` --- be/src/common/config.cpp | 12 +++++++++++- be/src/common/config.h | 6 +++++- be/src/io/fs/buffered_reader.h | 7 +++---- be/src/vec/exec/scan/scanner_scheduler.cpp | 2 ++ .../java/org/apache/doris/hudi/HudiJniScanner.java | 4 ++-- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/be/src/common/config.cpp b/be/src/common/config.cpp index 2f12cea1fa3470..919b83f0c7d706 100644 --- a/be/src/common/config.cpp +++ b/be/src/common/config.cpp @@ -41,6 +41,7 @@ #include "common/status.h" #include "io/fs/file_writer.h" #include "io/fs/local_file_system.h" +#include "util/cpu_info.h" namespace doris::config { @@ -228,7 +229,14 @@ DEFINE_Bool(doris_enable_scanner_thread_pool_per_disk, "true"); DEFINE_mInt64(doris_blocking_priority_queue_wait_timeout_ms, "500"); // number of scanner thread pool size for olap table // and the min thread num of remote scanner thread pool -DEFINE_Int32(doris_scanner_thread_pool_thread_num, "48"); +DEFINE_Int32(doris_scanner_thread_pool_thread_num, "-1"); +DEFINE_Validator(doris_scanner_thread_pool_thread_num, [](const int config) -> bool { + if (config == -1) { + CpuInfo::init(); + doris_scanner_thread_pool_thread_num = std::max(48, CpuInfo::num_cores() * 4); + } + return true; +}); DEFINE_Int32(doris_max_remote_scanner_thread_pool_thread_num, "-1"); // number of olap scanner thread pool queue size DEFINE_Int32(doris_scanner_thread_pool_queue_size, "102400"); @@ -864,6 +872,8 @@ DEFINE_mInt32(parquet_rowgroup_max_buffer_mb, "128"); // Max buffer size for parquet chunk column DEFINE_mInt32(parquet_column_max_buffer_mb, "8"); DEFINE_mDouble(max_amplified_read_ratio, "0.8"); +DEFINE_mInt32(merged_oss_min_io_size, "1048576"); +DEFINE_mInt32(merged_hdfs_min_io_size, "8192"); // OrcReader DEFINE_mInt32(orc_natural_read_size_mb, "8"); diff --git a/be/src/common/config.h b/be/src/common/config.h index abdf1c67a2e06f..affe47851fb97a 100644 --- a/be/src/common/config.h +++ b/be/src/common/config.h @@ -276,7 +276,7 @@ DECLARE_Bool(doris_enable_scanner_thread_pool_per_disk); DECLARE_mInt64(doris_blocking_priority_queue_wait_timeout_ms); // number of scanner thread pool size for olap table // and the min thread num of remote scanner thread pool -DECLARE_Int32(doris_scanner_thread_pool_thread_num); +DECLARE_mInt32(doris_scanner_thread_pool_thread_num); // max number of remote scanner thread pool size // if equal to -1, value is std::max(512, CpuInfo::num_cores() * 10) DECLARE_Int32(doris_max_remote_scanner_thread_pool_thread_num); @@ -921,6 +921,10 @@ DECLARE_mInt32(parquet_rowgroup_max_buffer_mb); DECLARE_mInt32(parquet_column_max_buffer_mb); // Merge small IO, the max amplified read ratio DECLARE_mDouble(max_amplified_read_ratio); +// Equivalent min size of each IO that can reach the maximum storage speed limit +// 1MB for oss, 8KB for hdfs +DECLARE_mInt32(merged_oss_min_io_size); +DECLARE_mInt32(merged_hdfs_min_io_size); // OrcReader DECLARE_mInt32(orc_natural_read_size_mb); diff --git a/be/src/io/fs/buffered_reader.h b/be/src/io/fs/buffered_reader.h index e78c1c79251146..2f8eb465cdfc54 100644 --- a/be/src/io/fs/buffered_reader.h +++ b/be/src/io/fs/buffered_reader.h @@ -131,8 +131,6 @@ class MergeRangeFileReader : public io::FileReader { static constexpr size_t READ_SLICE_SIZE = 8 * 1024 * 1024; // 8MB static constexpr size_t BOX_SIZE = 1 * 1024 * 1024; // 1MB static constexpr size_t SMALL_IO = 2 * 1024 * 1024; // 2MB - static constexpr size_t HDFS_MIN_IO_SIZE = 4 * 1024; // 4KB - static constexpr size_t OSS_MIN_IO_SIZE = 512 * 1024; // 512KB static constexpr size_t NUM_BOX = TOTAL_BUFFER_SIZE / BOX_SIZE; // 128 MergeRangeFileReader(RuntimeProfile* profile, io::FileReaderSPtr reader, @@ -146,8 +144,9 @@ class MergeRangeFileReader : public io::FileReader { _is_oss = typeid_cast(_reader.get()) != nullptr; _max_amplified_ratio = config::max_amplified_read_ratio; // Equivalent min size of each IO that can reach the maximum storage speed limit: - // 512KB for oss, 4KB for hdfs - _equivalent_io_size = _is_oss ? OSS_MIN_IO_SIZE : HDFS_MIN_IO_SIZE; + // 1MB for oss, 8KB for hdfs + _equivalent_io_size = + _is_oss ? config::merged_oss_min_io_size : config::merged_hdfs_min_io_size; for (const PrefetchRange& range : _random_access_ranges) { _statistics.apply_bytes += range.end_offset - range.start_offset; } diff --git a/be/src/vec/exec/scan/scanner_scheduler.cpp b/be/src/vec/exec/scan/scanner_scheduler.cpp index 42285f1d22a854..84f56813c34a9e 100644 --- a/be/src/vec/exec/scan/scanner_scheduler.cpp +++ b/be/src/vec/exec/scan/scanner_scheduler.cpp @@ -122,6 +122,8 @@ Status ScannerScheduler::init(ExecEnv* env) { _remote_thread_pool_max_size = config::doris_max_remote_scanner_thread_pool_thread_num != -1 ? config::doris_max_remote_scanner_thread_pool_thread_num : std::max(512, CpuInfo::num_cores() * 10); + _remote_thread_pool_max_size = + std::max(_remote_thread_pool_max_size, config::doris_scanner_thread_pool_thread_num); _remote_scan_thread_pool = std::make_unique( _remote_thread_pool_max_size, config::doris_remote_scanner_thread_pool_queue_size, "RemoteScanThreadPool"); diff --git a/fe/be-java-extensions/hudi-scanner/src/main/java/org/apache/doris/hudi/HudiJniScanner.java b/fe/be-java-extensions/hudi-scanner/src/main/java/org/apache/doris/hudi/HudiJniScanner.java index daf8d4a21fa87b..17ca650675de3c 100644 --- a/fe/be-java-extensions/hudi-scanner/src/main/java/org/apache/doris/hudi/HudiJniScanner.java +++ b/fe/be-java-extensions/hudi-scanner/src/main/java/org/apache/doris/hudi/HudiJniScanner.java @@ -85,8 +85,8 @@ public class HudiJniScanner extends JniScanner { private static final ScheduledExecutorService cleanResolverService = Executors.newScheduledThreadPool(1); static { - int numThreads = Math.max(Runtime.getRuntime().availableProcessors() * 2 + 1, 4); - if (numThreads > 32) { + int numThreads = Math.max(Runtime.getRuntime().availableProcessors() * 2, 4); + if (numThreads > 48) { numThreads = Runtime.getRuntime().availableProcessors(); } avroReadPool = Executors.newFixedThreadPool(numThreads, From 92660bb1b273ab562bb8ebc4885686ba055534b9 Mon Sep 17 00:00:00 2001 From: lihangyu <15605149486@163.com> Date: Tue, 26 Dec 2023 10:24:43 +0800 Subject: [PATCH 026/299] [chore](config) modify `variant_ratio_of_defaults_as_sparse_column` from 0.95 to 1 (#28984) since sparse column is not stable at present --- be/src/common/config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/be/src/common/config.cpp b/be/src/common/config.cpp index 919b83f0c7d706..1d8178ceec2e86 100644 --- a/be/src/common/config.cpp +++ b/be/src/common/config.cpp @@ -969,7 +969,7 @@ DEFINE_mInt64(workload_group_scan_task_wait_timeout_ms, "10000"); DEFINE_Bool(enable_index_apply_preds_except_leafnode_of_andnode, "true"); DEFINE_mBool(variant_enable_flatten_nested, "false"); -DEFINE_mDouble(variant_ratio_of_defaults_as_sparse_column, "0.95"); +DEFINE_mDouble(variant_ratio_of_defaults_as_sparse_column, "1"); DEFINE_mInt64(variant_threshold_rows_to_estimate_sparse_column, "1000"); // block file cache From 065eb9a72b599d5ca0058fa80135ea37240de96f Mon Sep 17 00:00:00 2001 From: starocean999 <40539150+starocean999@users.noreply.github.com> Date: Tue, 26 Dec 2023 11:19:04 +0800 Subject: [PATCH 027/299] [feature](nereids)support partition property in nereids (#28982) --- .../org/apache/doris/nereids/DorisParser.g4 | 2 +- fe/fe-core/src/main/cup/sql_parser.cup | 26 -- .../doris/analysis/SinglePartitionDesc.java | 19 +- .../nereids/parser/LogicalPlanBuilder.java | 27 +- .../plans/commands/info/ColumnDefinition.java | 260 +++++++++++++++++- .../commands/info/FixedRangePartition.java | 21 +- .../plans/commands/info/InPartition.java | 19 +- .../commands/info/LessThanPartition.java | 31 ++- .../commands/info/PartitionDefinition.java | 88 +++++- .../plans/commands/info/StepPartition.java | 11 +- .../rules/exploration/mv/MappingTest.java | 2 +- .../test_agg_materialize.groovy | 2 +- .../datatype_p0/date/test_date_key.groovy | 2 +- .../datatype_p0/date/test_datetime_key.groovy | 2 +- .../datetimev1/test_datetimev1_calc.groovy | 2 +- .../datev1/test_datev1_calc.groovy | 2 +- .../decimalv2/test_decimalv2_calc.groovy | 2 +- ...test_decimalv2_calc_with_conversion.groovy | 2 +- .../decimalv3/test_decimalv3_overflow.groovy | 2 +- .../decimalv3/test_uniq_tab_decimalv2.groovy | 2 +- ...te_nestedtypes_with_unsupport_types.groovy | 8 +- .../ddl/dup_tbl_basic_multi_table_create.sql | 2 +- ...te_or_datetime_computation_negative.groovy | 2 +- .../test_default_limit.groovy | 4 +- ...count_distinct_with_collection_type.groovy | 2 +- .../test_array_functions.groovy | 2 +- .../size_funciton/test_size_function.groovy | 2 +- 27 files changed, 444 insertions(+), 102 deletions(-) diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 index abef9ffa6d7555..a2f90ea21667c8 100644 --- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 +++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 @@ -502,7 +502,7 @@ partitionsDef ; partitionDef - : (lessThanPartitionDef | fixedPartitionDef | stepPartitionDef | inPartitionDef) properties=propertyClause? + : (lessThanPartitionDef | fixedPartitionDef | stepPartitionDef | inPartitionDef) (LEFT_PAREN partitionProperties=propertyItemList RIGHT_PAREN)? ; lessThanPartitionDef diff --git a/fe/fe-core/src/main/cup/sql_parser.cup b/fe/fe-core/src/main/cup/sql_parser.cup index 26b221c128c6e3..7e8f4dd96b569a 100644 --- a/fe/fe-core/src/main/cup/sql_parser.cup +++ b/fe/fe-core/src/main/cup/sql_parser.cup @@ -1800,19 +1800,6 @@ create_stmt ::= RESULT = new CreateTableStmt(ifNotExists, isExternal, name, columns, engineName, keys, partition, distribution, tblProperties, extProperties, tableComment, index); :} - | KW_CREATE opt_external:isExternal KW_TABLE opt_if_not_exists:ifNotExists table_name:name - LPAREN column_definition_list:columns COMMA RPAREN opt_engine:engineName - opt_keys:keys - opt_comment:tableComment - opt_partition:partition - opt_distribution:distribution - opt_rollup:index - opt_properties:tblProperties - opt_ext_properties:extProperties - {: - RESULT = new CreateTableStmt(ifNotExists, isExternal, name, columns, null, engineName, keys, partition, - distribution, tblProperties, extProperties, tableComment, index); - :} | KW_CREATE opt_external:isExternal KW_TABLE opt_if_not_exists:ifNotExists table_name:name LPAREN column_definition_list:columns COMMA index_definition_list:indexes RPAREN opt_engine:engineName opt_keys:keys @@ -1826,19 +1813,6 @@ create_stmt ::= RESULT = new CreateTableStmt(ifNotExists, isExternal, name, columns, indexes, engineName, keys, partition, distribution, tblProperties, extProperties, tableComment, index); :} - | KW_CREATE opt_external:isExternal KW_TABLE opt_if_not_exists:ifNotExists table_name:name - LPAREN column_definition_list:columns COMMA index_definition_list:indexes COMMA RPAREN opt_engine:engineName - opt_keys:keys - opt_comment:tableComment - opt_partition:partition - opt_distribution:distribution - opt_rollup:index - opt_properties:tblProperties - opt_ext_properties:extProperties - {: - RESULT = new CreateTableStmt(ifNotExists, isExternal, name, columns, indexes, engineName, keys, partition, - distribution, tblProperties, extProperties, tableComment, index); - :} | KW_CREATE opt_external:isExternal KW_TABLE opt_if_not_exists:ifNotExists table_name:name opt_col_list:columns opt_engine:engineName diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/SinglePartitionDesc.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/SinglePartitionDesc.java index ac6cad493ef73c..e25adbaba0ffba 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/SinglePartitionDesc.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/SinglePartitionDesc.java @@ -68,19 +68,22 @@ public SinglePartitionDesc(boolean ifNotExists, String partName, PartitionKeyDes /** * for Nereids */ - public SinglePartitionDesc(boolean ifNotExists, String partName, PartitionKeyDesc partitionKeyDesc, - ReplicaAllocation replicaAlloc, Map properties) { - this.ifNotExists = ifNotExists; - + public SinglePartitionDesc(boolean ifNotExists, String partName, + PartitionKeyDesc partitionKeyDesc, ReplicaAllocation replicaAlloc, + Map properties, DataProperty partitionDataProperty, boolean isInMemory, + TTabletType tabletType, Long versionInfo, String storagePolicy, boolean isMutable) { this.isAnalyzed = true; - + this.ifNotExists = ifNotExists; this.partName = partName; this.partitionKeyDesc = partitionKeyDesc; this.properties = properties; - - this.partitionDataProperty = new DataProperty(DataProperty.DEFAULT_STORAGE_MEDIUM); + this.partitionDataProperty = partitionDataProperty; this.replicaAlloc = replicaAlloc; - this.storagePolicy = ""; + this.isInMemory = isInMemory; + this.tabletType = tabletType; + this.versionInfo = versionInfo; + this.storagePolicy = storagePolicy; + this.isMutable = isMutable; } public boolean isSetIfNotExists() { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java index 37b42c7123f75c..0d24a8b9377048 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java @@ -2430,7 +2430,7 @@ public IndexDefinition visitIndexDef(IndexDefContext ctx) { String indexName = ctx.indexName.getText(); List indexCols = visitIdentifierList(ctx.cols); Map properties = visitPropertyItemList(ctx.properties); - String indexType = ctx.indexType.getText(); + String indexType = ctx.indexType != null ? ctx.indexType.getText() : null; String comment = ctx.comment != null ? ctx.comment.getText() : ""; return new IndexDefinition(indexName, indexCols, indexType, properties, comment); } @@ -2438,8 +2438,16 @@ public IndexDefinition visitIndexDef(IndexDefContext ctx) { @Override public List visitPartitionsDef(PartitionsDefContext ctx) { return ctx.partitions.stream() - .map(p -> ((PartitionDefinition) visit(p)).withProperties(visitPropertyClause(p.properties))) - .collect(Collectors.toList()); + .map(p -> ((PartitionDefinition) visit(p))).collect(Collectors.toList()); + } + + @Override + public PartitionDefinition visitPartitionDef(DorisParser.PartitionDefContext ctx) { + PartitionDefinition partitionDefinition = (PartitionDefinition) visit(ctx.getChild(0)); + if (ctx.partitionProperties != null) { + partitionDefinition.withProperties(visitPropertyItemList(ctx.partitionProperties)); + } + return partitionDefinition; } @Override @@ -2447,9 +2455,10 @@ public PartitionDefinition visitLessThanPartitionDef(LessThanPartitionDefContext String partitionName = ctx.partitionName.getText(); if (ctx.MAXVALUE() == null) { List lessThanValues = visitConstantSeq(ctx.constantSeq()); - return new LessThanPartition(partitionName, lessThanValues); + return new LessThanPartition(ctx.EXISTS() != null, partitionName, lessThanValues); } else { - return new LessThanPartition(partitionName, ImmutableList.of(MaxValue.INSTANCE)); + return new LessThanPartition(ctx.EXISTS() != null, partitionName, + ImmutableList.of(MaxValue.INSTANCE)); } } @@ -2458,15 +2467,15 @@ public PartitionDefinition visitFixedPartitionDef(FixedPartitionDefContext ctx) String partitionName = ctx.partitionName.getText(); List lowerBounds = visitConstantSeq(ctx.lower); List upperBounds = visitConstantSeq(ctx.upper); - return new FixedRangePartition(partitionName, lowerBounds, upperBounds); + return new FixedRangePartition(ctx.EXISTS() != null, partitionName, lowerBounds, upperBounds); } @Override public PartitionDefinition visitStepPartitionDef(StepPartitionDefContext ctx) { List fromExpression = visitConstantSeq(ctx.from); List toExpression = visitConstantSeq(ctx.to); - return new StepPartition(fromExpression, toExpression, Long.parseLong(ctx.unitsAmount.getText()), - ctx.unit != null ? ctx.unit.getText() : null); + return new StepPartition(false, null, fromExpression, toExpression, + Long.parseLong(ctx.unitsAmount.getText()), ctx.unit != null ? ctx.unit.getText() : null); } @Override @@ -2479,7 +2488,7 @@ public PartitionDefinition visitInPartitionDef(InPartitionDefContext ctx) { values = visitConstantSeq(ctx.constants).stream().map(ImmutableList::of) .collect(Collectors.toList()); } - return new InPartition(ctx.partitionName.getText(), values); + return new InPartition(ctx.EXISTS() != null, ctx.partitionName.getText(), values); } @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/ColumnDefinition.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/ColumnDefinition.java index 2736d71a50c0bc..5832a0534c0b8a 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/ColumnDefinition.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/ColumnDefinition.java @@ -21,6 +21,9 @@ import org.apache.doris.catalog.AggregateType; import org.apache.doris.catalog.Column; import org.apache.doris.catalog.KeysType; +import org.apache.doris.catalog.PrimitiveType; +import org.apache.doris.catalog.ScalarType; +import org.apache.doris.catalog.Type; import org.apache.doris.common.Config; import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.types.ArrayType; @@ -33,10 +36,13 @@ import org.apache.doris.nereids.types.StructType; import org.apache.doris.nereids.types.TinyIntType; import org.apache.doris.nereids.types.VarcharType; +import org.apache.doris.qe.SessionVariable; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; @@ -176,9 +182,7 @@ private DataType updateCharacterTypeLength(DataType dataType) { * validate column definition and analyze */ public void validate(boolean isOlap, Set keysSet, boolean isEnableMergeOnWrite, KeysType keysType) { - if (Config.disable_nested_complex_type && isNestedComplexType(type)) { - throw new AnalysisException("Unsupported data type: " + type.toSql()); - } + validateDataType(type.toCatalogDataType()); type = updateCharacterTypeLength(type); if (type.isArrayType()) { int depth = 0; @@ -369,6 +373,256 @@ private boolean isNestedComplexType(DataType dataType) { return false; } + // from TypeDef.java analyze() + private void validateDataType(Type catalogType) { + if (catalogType.exceedsMaxNestingDepth()) { + throw new AnalysisException( + String.format("Type exceeds the maximum nesting depth of %s:\n%s", + Type.MAX_NESTING_DEPTH, catalogType.toSql())); + } + if (!catalogType.isSupported()) { + throw new AnalysisException("Unsupported data type: " + catalogType.toSql()); + } + + if (catalogType.isScalarType()) { + validateScalarType((ScalarType) catalogType); + } else if (catalogType.isComplexType()) { + // now we not support array / map / struct nesting complex type + if (catalogType.isArrayType()) { + Type itemType = ((org.apache.doris.catalog.ArrayType) catalogType).getItemType(); + if (itemType instanceof ScalarType) { + validateNestedType(catalogType, (ScalarType) itemType); + } else if (Config.disable_nested_complex_type + && !(itemType instanceof org.apache.doris.catalog.ArrayType)) { + // now we can array nesting array + throw new AnalysisException( + "Unsupported data type: ARRAY<" + itemType.toSql() + ">"); + } + } + if (catalogType.isMapType()) { + org.apache.doris.catalog.MapType mt = + (org.apache.doris.catalog.MapType) catalogType; + if (Config.disable_nested_complex_type && (!(mt.getKeyType() instanceof ScalarType) + || !(mt.getValueType() instanceof ScalarType))) { + throw new AnalysisException("Unsupported data type: MAP<" + + mt.getKeyType().toSql() + "," + mt.getValueType().toSql() + ">"); + } + if (mt.getKeyType() instanceof ScalarType) { + validateNestedType(catalogType, (ScalarType) mt.getKeyType()); + } + if (mt.getValueType() instanceof ScalarType) { + validateNestedType(catalogType, (ScalarType) mt.getValueType()); + } + } + if (catalogType.isStructType()) { + ArrayList fields = + ((org.apache.doris.catalog.StructType) catalogType).getFields(); + Set fieldNames = new HashSet<>(); + for (org.apache.doris.catalog.StructField field : fields) { + Type fieldType = field.getType(); + if (fieldType instanceof ScalarType) { + validateNestedType(catalogType, (ScalarType) fieldType); + if (!fieldNames.add(field.getName())) { + throw new AnalysisException("Duplicate field name " + field.getName() + + " in struct " + catalogType.toSql()); + } + } else if (Config.disable_nested_complex_type) { + throw new AnalysisException( + "Unsupported field type: " + fieldType.toSql() + " for STRUCT"); + } + } + } + } + } + + private void validateScalarType(ScalarType scalarType) { + PrimitiveType type = scalarType.getPrimitiveType(); + // When string type length is not assigned, it needs to be assigned to 1. + if (scalarType.getPrimitiveType().isStringType() && !scalarType.isLengthSet()) { + if (scalarType.getPrimitiveType() == PrimitiveType.VARCHAR) { + // always set varchar length MAX_VARCHAR_LENGTH + scalarType.setLength(ScalarType.MAX_VARCHAR_LENGTH); + } else if (scalarType.getPrimitiveType() == PrimitiveType.STRING) { + // always set text length MAX_STRING_LENGTH + scalarType.setLength(ScalarType.MAX_STRING_LENGTH); + } else { + scalarType.setLength(1); + } + } + switch (type) { + case CHAR: + case VARCHAR: { + String name; + int maxLen; + if (type == PrimitiveType.VARCHAR) { + name = "VARCHAR"; + maxLen = ScalarType.MAX_VARCHAR_LENGTH; + } else { + name = "CHAR"; + maxLen = ScalarType.MAX_CHAR_LENGTH; + return; + } + int len = scalarType.getLength(); + // len is decided by child, when it is -1. + + if (len <= 0) { + throw new AnalysisException(name + " size must be > 0: " + len); + } + if (scalarType.getLength() > maxLen) { + throw new AnalysisException(name + " size must be <= " + maxLen + ": " + len); + } + break; + } + case DECIMALV2: { + int precision = scalarType.decimalPrecision(); + int scale = scalarType.decimalScale(); + // precision: [1, 27] + if (precision < 1 || precision > ScalarType.MAX_DECIMALV2_PRECISION) { + throw new AnalysisException("Precision of decimal must between 1 and 27." + + " Precision was set to: " + precision + "."); + } + // scale: [0, 9] + if (scale < 0 || scale > ScalarType.MAX_DECIMALV2_SCALE) { + throw new AnalysisException("Scale of decimal must between 0 and 9." + + " Scale was set to: " + scale + "."); + } + if (precision - scale > ScalarType.MAX_DECIMALV2_PRECISION + - ScalarType.MAX_DECIMALV2_SCALE) { + throw new AnalysisException("Invalid decimal type with precision = " + precision + + ", scale = " + scale); + } + // scale < precision + if (scale > precision) { + throw new AnalysisException("Scale of decimal must be smaller than precision." + + " Scale is " + scale + " and precision is " + precision); + } + break; + } + case DECIMAL32: { + int decimal32Precision = scalarType.decimalPrecision(); + int decimal32Scale = scalarType.decimalScale(); + if (decimal32Precision < 1 + || decimal32Precision > ScalarType.MAX_DECIMAL32_PRECISION) { + throw new AnalysisException("Precision of decimal must between 1 and 9." + + " Precision was set to: " + decimal32Precision + "."); + } + // scale >= 0 + if (decimal32Scale < 0) { + throw new AnalysisException("Scale of decimal must not be less than 0." + + " Scale was set to: " + decimal32Scale + "."); + } + // scale < precision + if (decimal32Scale > decimal32Precision) { + throw new AnalysisException( + "Scale of decimal must be smaller than precision." + " Scale is " + + decimal32Scale + " and precision is " + decimal32Precision); + } + break; + } + case DECIMAL64: { + int decimal64Precision = scalarType.decimalPrecision(); + int decimal64Scale = scalarType.decimalScale(); + if (decimal64Precision < 1 + || decimal64Precision > ScalarType.MAX_DECIMAL64_PRECISION) { + throw new AnalysisException("Precision of decimal64 must between 1 and 18." + + " Precision was set to: " + decimal64Precision + "."); + } + // scale >= 0 + if (decimal64Scale < 0) { + throw new AnalysisException("Scale of decimal must not be less than 0." + + " Scale was set to: " + decimal64Scale + "."); + } + // scale < precision + if (decimal64Scale > decimal64Precision) { + throw new AnalysisException( + "Scale of decimal must be smaller than precision." + " Scale is " + + decimal64Scale + " and precision is " + decimal64Precision); + } + break; + } + case DECIMAL128: { + int decimal128Precision = scalarType.decimalPrecision(); + int decimal128Scale = scalarType.decimalScale(); + if (decimal128Precision < 1 + || decimal128Precision > ScalarType.MAX_DECIMAL128_PRECISION) { + throw new AnalysisException("Precision of decimal128 must between 1 and 38." + + " Precision was set to: " + decimal128Precision + "."); + } + // scale >= 0 + if (decimal128Scale < 0) { + throw new AnalysisException("Scale of decimal must not be less than 0." + + " Scale was set to: " + decimal128Scale + "."); + } + // scale < precision + if (decimal128Scale > decimal128Precision) { + throw new AnalysisException( + "Scale of decimal must be smaller than precision." + " Scale is " + + decimal128Scale + " and precision is " + decimal128Precision); + } + break; + } + case DECIMAL256: { + if (SessionVariable.getEnableDecimal256()) { + int precision = scalarType.decimalPrecision(); + int scale = scalarType.decimalScale(); + if (precision < 1 || precision > ScalarType.MAX_DECIMAL256_PRECISION) { + throw new AnalysisException("Precision of decimal256 must between 1 and 76." + + " Precision was set to: " + precision + "."); + } + // scale >= 0 + if (scale < 0) { + throw new AnalysisException("Scale of decimal must not be less than 0." + + " Scale was set to: " + scale + "."); + } + // scale < precision + if (scale > precision) { + throw new AnalysisException( + "Scale of decimal must be smaller than precision." + " Scale is " + + scale + " and precision is " + precision); + } + break; + } else { + int precision = scalarType.decimalPrecision(); + throw new AnalysisException("Column of type Decimal256 with precision " + + precision + " in not supported."); + } + } + case TIMEV2: + case DATETIMEV2: { + int precision = scalarType.decimalPrecision(); + int scale = scalarType.decimalScale(); + // precision: [1, 27] + if (precision != ScalarType.DATETIME_PRECISION) { + throw new AnalysisException( + "Precision of Datetime/Time must be " + ScalarType.DATETIME_PRECISION + + "." + " Precision was set to: " + precision + "."); + } + // scale: [0, 9] + if (scale < 0 || scale > 6) { + throw new AnalysisException("Scale of Datetime/Time must between 0 and 6." + + " Scale was set to: " + scale + "."); + } + break; + } + case INVALID_TYPE: + throw new AnalysisException("Invalid type."); + default: + break; + } + } + + private void validateNestedType(Type parent, Type child) throws AnalysisException { + if (child.isNull()) { + throw new AnalysisException("Unsupported data type: " + child.toSql()); + } + // check whether the sub-type is supported + if (!parent.supportSubType(child)) { + throw new AnalysisException( + parent.getPrimitiveType() + " unsupported sub-type: " + child.toSql()); + } + validateDataType(child); + } + /** * translate to catalog create table stmt */ diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/FixedRangePartition.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/FixedRangePartition.java index 7655c6b32857c0..104ea0a3eba845 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/FixedRangePartition.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/FixedRangePartition.java @@ -20,10 +20,10 @@ import org.apache.doris.analysis.PartitionKeyDesc; import org.apache.doris.analysis.PartitionValue; import org.apache.doris.analysis.SinglePartitionDesc; +import org.apache.doris.common.FeNameFormat; +import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.trees.expressions.Expression; -import com.google.common.collect.Maps; - import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -33,12 +33,12 @@ * represent fixed range partition */ public class FixedRangePartition extends PartitionDefinition { - private final String partitionName; private List lowerBounds; private List upperBounds; - public FixedRangePartition(String partitionName, List lowerBounds, List upperBounds) { - this.partitionName = partitionName; + public FixedRangePartition(boolean ifNotExists, String partitionName, + List lowerBounds, List upperBounds) { + super(ifNotExists, partitionName); this.lowerBounds = lowerBounds; this.upperBounds = upperBounds; } @@ -46,6 +46,11 @@ public FixedRangePartition(String partitionName, List lowerBounds, L @Override public void validate(Map properties) { super.validate(properties); + try { + FeNameFormat.checkPartitionName(partitionName); + } catch (Exception e) { + throw new AnalysisException(e.getMessage(), e.getCause()); + } List newLowerBounds = new ArrayList<>(); List newUpperBounds = new ArrayList<>(); for (int i = 0; i < partitionTypes.size(); ++i) { @@ -72,7 +77,9 @@ public SinglePartitionDesc translateToCatalogStyle() { .collect(Collectors.toList()); List upperValues = upperBounds.stream().map(this::toLegacyPartitionValueStmt) .collect(Collectors.toList()); - return new SinglePartitionDesc(false, partitionName, - PartitionKeyDesc.createFixed(lowerValues, upperValues), replicaAllocation, Maps.newHashMap()); + return new SinglePartitionDesc(ifNotExists, partitionName, + PartitionKeyDesc.createFixed(lowerValues, upperValues), replicaAllocation, + properties, partitionDataProperty, isInMemory, tabletType, versionInfo, + storagePolicy, isMutable); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/InPartition.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/InPartition.java index 30dfcb9efcecbb..611da907e15eed 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/InPartition.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/InPartition.java @@ -21,11 +21,10 @@ import org.apache.doris.analysis.PartitionKeyDesc; import org.apache.doris.analysis.PartitionValue; import org.apache.doris.analysis.SinglePartitionDesc; +import org.apache.doris.common.FeNameFormat; import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.trees.expressions.Expression; -import com.google.common.collect.Maps; - import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -35,17 +34,21 @@ * represent in partition */ public class InPartition extends PartitionDefinition { - private final String partitionName; private final List> values; - public InPartition(String partitionName, List> values) { - this.partitionName = partitionName; + public InPartition(boolean ifNotExists, String partitionName, List> values) { + super(ifNotExists, partitionName); this.values = values; } @Override public void validate(Map properties) { super.validate(properties); + try { + FeNameFormat.checkPartitionName(partitionName); + } catch (Exception e) { + throw new AnalysisException(e.getMessage(), e.getCause()); + } if (values.stream().anyMatch(l -> l.stream().anyMatch(MaxValue.class::isInstance))) { throw new AnalysisException("MAXVALUE cannot be used in 'in partition'"); } @@ -65,7 +68,9 @@ public AllPartitionDesc translateToCatalogStyle() { List> catalogValues = values.stream().map(l -> l.stream() .map(this::toLegacyPartitionValueStmt) .collect(Collectors.toList())).collect(Collectors.toList()); - return new SinglePartitionDesc(false, partitionName, PartitionKeyDesc.createIn(catalogValues), - replicaAllocation, Maps.newHashMap()); + return new SinglePartitionDesc(ifNotExists, partitionName, + PartitionKeyDesc.createIn(catalogValues), replicaAllocation, properties, + partitionDataProperty, isInMemory, tabletType, versionInfo, storagePolicy, + isMutable); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/LessThanPartition.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/LessThanPartition.java index 11a6f4f50da7f2..627c514a550b3c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/LessThanPartition.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/LessThanPartition.java @@ -20,10 +20,10 @@ import org.apache.doris.analysis.PartitionKeyDesc; import org.apache.doris.analysis.PartitionValue; import org.apache.doris.analysis.SinglePartitionDesc; +import org.apache.doris.common.FeNameFormat; +import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.trees.expressions.Expression; -import com.google.common.collect.Maps; - import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -32,17 +32,21 @@ * represent less than partition */ public class LessThanPartition extends PartitionDefinition { - private final String partitionName; private final List values; - public LessThanPartition(String partitionName, List values) { - this.partitionName = partitionName; + public LessThanPartition(boolean ifNotExists, String partitionName, List values) { + super(ifNotExists, partitionName); this.values = values; } @Override public void validate(Map properties) { super.validate(properties); + try { + FeNameFormat.checkPartitionName(partitionName); + } catch (Exception e) { + throw new AnalysisException(e.getMessage(), e.getCause()); + } } public String getPartitionName() { @@ -54,13 +58,16 @@ public String getPartitionName() { */ public SinglePartitionDesc translateToCatalogStyle() { if (values.get(0) instanceof MaxValue) { - return new SinglePartitionDesc(false, partitionName, PartitionKeyDesc.createMaxKeyDesc(), - replicaAllocation, Maps.newHashMap()); + return new SinglePartitionDesc(ifNotExists, partitionName, + PartitionKeyDesc.createMaxKeyDesc(), replicaAllocation, properties, + partitionDataProperty, isInMemory, tabletType, versionInfo, storagePolicy, + isMutable); } - List partitionValues = values.stream() - .map(this::toLegacyPartitionValueStmt) - .collect(Collectors.toList()); - return new SinglePartitionDesc(false, partitionName, - PartitionKeyDesc.createLessThan(partitionValues), replicaAllocation, Maps.newHashMap()); + List partitionValues = + values.stream().map(this::toLegacyPartitionValueStmt).collect(Collectors.toList()); + return new SinglePartitionDesc(ifNotExists, partitionName, + PartitionKeyDesc.createLessThan(partitionValues), replicaAllocation, properties, + partitionDataProperty, isInMemory, tabletType, versionInfo, storagePolicy, + isMutable); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/PartitionDefinition.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/PartitionDefinition.java index 2d89ed94909267..81ddd7b2ac2199 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/PartitionDefinition.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/PartitionDefinition.java @@ -19,6 +19,7 @@ import org.apache.doris.analysis.AllPartitionDesc; import org.apache.doris.analysis.PartitionValue; +import org.apache.doris.catalog.DataProperty; import org.apache.doris.catalog.ReplicaAllocation; import org.apache.doris.common.util.PropertyAnalyzer; import org.apache.doris.nereids.exceptions.AnalysisException; @@ -27,6 +28,11 @@ import org.apache.doris.nereids.trees.expressions.shape.LeafExpression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.DataType; +import org.apache.doris.thrift.TTabletType; + +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; import java.util.List; import java.util.Map; @@ -35,9 +41,25 @@ * abstract class for partition definition */ public abstract class PartitionDefinition { - protected List partitionTypes; + + protected boolean ifNotExists; + protected String partitionName; protected Map properties; + + protected List partitionTypes; + protected DataProperty partitionDataProperty = + new DataProperty(DataProperty.DEFAULT_STORAGE_MEDIUM); protected ReplicaAllocation replicaAllocation = ReplicaAllocation.DEFAULT_ALLOCATION; + protected boolean isInMemory = false; + protected TTabletType tabletType = TTabletType.TABLET_TYPE_DISK; + protected Long versionInfo; + protected String storagePolicy = ""; + protected boolean isMutable; + + public PartitionDefinition(boolean ifNotExists, String partName) { + this.ifNotExists = ifNotExists; + this.partitionName = partName; + } public PartitionDefinition withProperties(Map properties) { this.properties = properties; @@ -50,12 +72,74 @@ public PartitionDefinition withProperties(Map properties) { * Validate the properties. * Derived class can override this method to do more validation. */ - public void validate(Map properties) { + public void validate(Map otherProperties) { try { + if (PropertyAnalyzer.analyzeUniqueKeyMergeOnWrite(otherProperties)) { + String storagePolicy = PropertyAnalyzer.analyzeStoragePolicy(properties); + if (!storagePolicy.isEmpty()) { + throw new AnalysisException( + "Can not create UNIQUE KEY table that enables Merge-On-write" + + " with storage policy(" + storagePolicy + ")"); + } + } + boolean hasStoragePolicy = false; + if (properties != null) { + hasStoragePolicy = properties.keySet().stream().anyMatch(iter -> { + boolean equal = iter + .compareToIgnoreCase(PropertyAnalyzer.PROPERTIES_STORAGE_POLICY) == 0; + // when find has storage policy properties, here will set it in partition + if (equal) { + storagePolicy = properties.get(iter); + } + return equal; + }); + } + + Map mergedMap = Maps.newHashMap(); + // Should putAll `otherProperties` before `this.properties`, + // because the priority of partition is higher than table + if (otherProperties != null) { + mergedMap.putAll(otherProperties); + } + if (this.properties != null) { + mergedMap.putAll(this.properties); + } + this.properties = mergedMap; + + // analyze data property + partitionDataProperty = PropertyAnalyzer.analyzeDataProperty(properties, + new DataProperty(DataProperty.DEFAULT_STORAGE_MEDIUM)); + Preconditions.checkNotNull(partitionDataProperty); replicaAllocation = PropertyAnalyzer.analyzeReplicaAllocation(properties, ""); if (replicaAllocation.isNotSet()) { replicaAllocation = ReplicaAllocation.DEFAULT_ALLOCATION; } + // analyze version info + versionInfo = PropertyAnalyzer.analyzeVersionInfo(properties); + + // analyze in memory + isInMemory = PropertyAnalyzer.analyzeBooleanProp(properties, + PropertyAnalyzer.PROPERTIES_INMEMORY, false); + if (isInMemory == true) { + throw new AnalysisException("Not support set 'in_memory'='true' now!"); + } + + // analyze is mutable + isMutable = PropertyAnalyzer.analyzeBooleanProp(properties, + PropertyAnalyzer.PROPERTIES_MUTABLE, true); + + tabletType = PropertyAnalyzer.analyzeTabletType(properties); + + if (otherProperties == null) { + // check unknown properties + if (properties != null && !properties.isEmpty()) { + if (!hasStoragePolicy) { + Joiner.MapJoiner mapJoiner = Joiner.on(", ").withKeyValueSeparator(" = "); + throw new AnalysisException( + "Unknown properties: " + mapJoiner.join(properties)); + } + } + } } catch (Exception e) { throw new AnalysisException(e.getMessage(), e.getCause()); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/StepPartition.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/StepPartition.java index a02013d377049c..1de00369913676 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/StepPartition.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/StepPartition.java @@ -23,8 +23,6 @@ import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.trees.expressions.Expression; -import com.google.common.collect.Maps; - import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -38,8 +36,9 @@ public class StepPartition extends PartitionDefinition { private final long unit; private final String unitString; - public StepPartition(List fromExpression, List toExpression, long unit, - String unitString) { + public StepPartition(boolean ifNotExists, String partitionName, List fromExpression, + List toExpression, long unit, String unitString) { + super(ifNotExists, partitionName); this.fromExpression = fromExpression; this.toExpression = toExpression; this.unit = unit; @@ -68,10 +67,10 @@ public MultiPartitionDesc translateToCatalogStyle() { try { if (unitString == null) { return new MultiPartitionDesc(PartitionKeyDesc.createMultiFixed(fromValues, toValues, unit), - replicaAllocation, Maps.newHashMap()); + replicaAllocation, properties); } return new MultiPartitionDesc(PartitionKeyDesc.createMultiFixed(fromValues, toValues, unit, unitString), - replicaAllocation, Maps.newHashMap()); + replicaAllocation, properties); } catch (Exception e) { throw new AnalysisException(e.getMessage(), e.getCause()); } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MappingTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MappingTest.java index e03228524331b1..d818c15b4b9df7 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MappingTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MappingTest.java @@ -64,7 +64,7 @@ protected void runBeforeAll() throws Exception { + ")"); createTable("CREATE TABLE IF NOT EXISTS customer (\n" + " C_CUSTKEY INTEGER NOT NULL,\n" - + " C_NAME VARCHAR(25) NOT NULL,\n" + + " C_NAME VARCHAR(25) NOT NULL\n" + ")\n" + "DUPLICATE KEY(C_CUSTKEY, C_NAME)\n" + "DISTRIBUTED BY HASH(C_CUSTKEY) BUCKETS 3\n" diff --git a/regression-test/suites/correctness_p0/test_agg_materialize.groovy b/regression-test/suites/correctness_p0/test_agg_materialize.groovy index 3f79013ea01712..eb44b85fafc522 100644 --- a/regression-test/suites/correctness_p0/test_agg_materialize.groovy +++ b/regression-test/suites/correctness_p0/test_agg_materialize.groovy @@ -34,7 +34,7 @@ suite("test_agg_materialize") { sql """drop table if exists c5749_bug_t;""" sql """CREATE TABLE `c5749_bug_t` ( `org_code` varchar(255) NULL , - `cash_amt` decimal(27, 9) NULL , + `cash_amt` decimal(27, 9) NULL ) ENGINE=OLAP DUPLICATE KEY(`org_code`) diff --git a/regression-test/suites/datatype_p0/date/test_date_key.groovy b/regression-test/suites/datatype_p0/date/test_date_key.groovy index 22c9a018dd393a..dbbe7f6870516b 100644 --- a/regression-test/suites/datatype_p0/date/test_date_key.groovy +++ b/regression-test/suites/datatype_p0/date/test_date_key.groovy @@ -20,7 +20,7 @@ suite("test_date_key") { sql """ create table `test_date_key` ( `k1` datev1, `k2` int, - INDEX idx_k1 (`k1`) USING BITMAP, + INDEX idx_k1 (`k1`) USING BITMAP ) duplicate key(`k1`) distributed by hash(k2) buckets 3 properties("replication_num" = "1"); diff --git a/regression-test/suites/datatype_p0/date/test_datetime_key.groovy b/regression-test/suites/datatype_p0/date/test_datetime_key.groovy index 848cdc1a4c58af..e349244adc4cd2 100644 --- a/regression-test/suites/datatype_p0/date/test_datetime_key.groovy +++ b/regression-test/suites/datatype_p0/date/test_datetime_key.groovy @@ -20,7 +20,7 @@ suite("test_datetime_key") { sql """ create table `test_datetime_key` ( `k1` datetimev1, `k2` int, - INDEX idx_k1 (`k1`) USING BITMAP, + INDEX idx_k1 (`k1`) USING BITMAP ) duplicate key(`k1`) distributed by hash(k2) buckets 3 properties("replication_num" = "1"); diff --git a/regression-test/suites/datatype_p0/datetimev1/test_datetimev1_calc.groovy b/regression-test/suites/datatype_p0/datetimev1/test_datetimev1_calc.groovy index 8b404db4e694ba..399d49bc94a11c 100644 --- a/regression-test/suites/datatype_p0/datetimev1/test_datetimev1_calc.groovy +++ b/regression-test/suites/datatype_p0/datetimev1/test_datetimev1_calc.groovy @@ -29,7 +29,7 @@ suite("test_datetimev1_calc", "nonConcurrent") { CREATE TABLE IF NOT EXISTS `${table1}` ( `id` int, `value1` datetimev1 NULL COMMENT "", - `value2` datetimev1 NULL COMMENT "", + `value2` datetimev1 NULL COMMENT "" ) ENGINE=OLAP DUPLICATE KEY(`id`) COMMENT "OLAP" diff --git a/regression-test/suites/datatype_p0/datev1/test_datev1_calc.groovy b/regression-test/suites/datatype_p0/datev1/test_datev1_calc.groovy index 3c875633e9423e..ec7a2738e45bdf 100644 --- a/regression-test/suites/datatype_p0/datev1/test_datev1_calc.groovy +++ b/regression-test/suites/datatype_p0/datev1/test_datev1_calc.groovy @@ -29,7 +29,7 @@ suite("test_datev1_calc", "nonConcurrent") { CREATE TABLE IF NOT EXISTS `${table1}` ( `id` int, `value1` datev1 NULL COMMENT "", - `value2` datev1 NULL COMMENT "", + `value2` datev1 NULL COMMENT "" ) ENGINE=OLAP DUPLICATE KEY(`id`) COMMENT "OLAP" diff --git a/regression-test/suites/datatype_p0/decimalv2/test_decimalv2_calc.groovy b/regression-test/suites/datatype_p0/decimalv2/test_decimalv2_calc.groovy index e2439c6229dc09..30aa98c9fb6e46 100644 --- a/regression-test/suites/datatype_p0/decimalv2/test_decimalv2_calc.groovy +++ b/regression-test/suites/datatype_p0/decimalv2/test_decimalv2_calc.groovy @@ -31,7 +31,7 @@ suite("test_decimalv2_calc", "nonConcurrent") { `decimal_key1` decimalv2(8, 5) NULL COMMENT "", `decimal_key2` decimalv2(16, 5) NULL COMMENT "", `decimal_value1` decimalv2(8, 5) NULL COMMENT "", - `decimal_value2` decimalv2(16, 5) NULL COMMENT "", + `decimal_value2` decimalv2(16, 5) NULL COMMENT "" ) ENGINE=OLAP DUPLICATE KEY(`decimal_key1`, `decimal_key2`) COMMENT "OLAP" diff --git a/regression-test/suites/datatype_p0/decimalv2/test_decimalv2_calc_with_conversion.groovy b/regression-test/suites/datatype_p0/decimalv2/test_decimalv2_calc_with_conversion.groovy index f500c195b26d3e..c8c196311f45db 100644 --- a/regression-test/suites/datatype_p0/decimalv2/test_decimalv2_calc_with_conversion.groovy +++ b/regression-test/suites/datatype_p0/decimalv2/test_decimalv2_calc_with_conversion.groovy @@ -31,7 +31,7 @@ suite("test_decimalv2_calc_with_conversion", "nonConcurrent") { `decimal_key1` decimalv2(8, 5) NULL COMMENT "", `decimal_key2` decimalv2(16, 5) NULL COMMENT "", `decimal_value1` decimalv2(8, 5) NULL COMMENT "", - `decimal_value2` decimalv2(16, 5) NULL COMMENT "", + `decimal_value2` decimalv2(16, 5) NULL COMMENT "" ) ENGINE=OLAP DUPLICATE KEY(`decimal_key1`, `decimal_key2`) COMMENT "OLAP" diff --git a/regression-test/suites/datatype_p0/decimalv3/test_decimalv3_overflow.groovy b/regression-test/suites/datatype_p0/decimalv3/test_decimalv3_overflow.groovy index f371c4c812fe97..384ba1fd8c367b 100644 --- a/regression-test/suites/datatype_p0/decimalv3/test_decimalv3_overflow.groovy +++ b/regression-test/suites/datatype_p0/decimalv3/test_decimalv3_overflow.groovy @@ -32,7 +32,7 @@ suite("test_decimalv3_overflow") { def tblName2 = "test_decimalv3_overflow2" sql "drop table if exists ${tblName2}" sql """ CREATE TABLE ${tblName2} ( - `c2` decimalv3(20, 2), + `c2` decimalv3(20, 2) ) ENGINE=OLAP UNIQUE KEY(`c2`) DISTRIBUTED BY HASH(`c2`) BUCKETS 10 diff --git a/regression-test/suites/datatype_p0/decimalv3/test_uniq_tab_decimalv2.groovy b/regression-test/suites/datatype_p0/decimalv3/test_uniq_tab_decimalv2.groovy index 90efdd3a216180..ca2ea138d6b577 100644 --- a/regression-test/suites/datatype_p0/decimalv3/test_uniq_tab_decimalv2.groovy +++ b/regression-test/suites/datatype_p0/decimalv3/test_uniq_tab_decimalv2.groovy @@ -26,7 +26,7 @@ suite("test_uniq_tab_decimalv2") { `decimal_key1` decimalv2(8, 5) NULL COMMENT "", `decimal_key2` decimalv2(16, 5) NULL COMMENT "", `decimal_value1` decimalv2(8, 5) NULL COMMENT "", - `decimal_value2` decimalv2(16, 5) NULL COMMENT "", + `decimal_value2` decimalv2(16, 5) NULL COMMENT "" ) ENGINE=OLAP UNIQUE KEY(`decimal_key1`, `decimal_key2`) COMMENT "OLAP" diff --git a/regression-test/suites/datatype_p0/nested_types/ddl/create_nestedtypes_with_unsupport_types.groovy b/regression-test/suites/datatype_p0/nested_types/ddl/create_nestedtypes_with_unsupport_types.groovy index 45b4176c0ba699..7a6b1218b7df7c 100644 --- a/regression-test/suites/datatype_p0/nested_types/ddl/create_nestedtypes_with_unsupport_types.groovy +++ b/regression-test/suites/datatype_p0/nested_types/ddl/create_nestedtypes_with_unsupport_types.groovy @@ -25,7 +25,7 @@ suite("create_nestedtypes_with_unsupport_types") { sql """ CREATE TABLE IF NOT EXISTS ${testTablex} ( `k1` INT(11) NULL, - `k2` array<${columnType}> NULL, + `k2` array<${columnType}> NULL ) DUPLICATE KEY(`k1`) DISTRIBUTED BY HASH(`k1`) BUCKETS 1 @@ -48,7 +48,7 @@ suite("create_nestedtypes_with_unsupport_types") { sql """ CREATE TABLE IF NOT EXISTS ${testTablex} ( `k1` INT(11) NULL, - `k2` map<${columnType}, String> NULL, + `k2` map<${columnType}, String> NULL ) DUPLICATE KEY(`k1`) DISTRIBUTED BY HASH(`k1`) BUCKETS 1 @@ -60,7 +60,7 @@ suite("create_nestedtypes_with_unsupport_types") { sql """ CREATE TABLE IF NOT EXISTS ${testTablex} ( `k1` INT(11) NULL, - `k2` map NULL, + `k2` map NULL ) DUPLICATE KEY(`k1`) DISTRIBUTED BY HASH(`k1`) BUCKETS 1 @@ -84,7 +84,7 @@ suite("create_nestedtypes_with_unsupport_types") { sql """ CREATE TABLE IF NOT EXISTS ${testTablex} ( `k1` INT(11) NULL, - `k2` STRUCT NULL, + `k2` STRUCT NULL ) DUPLICATE KEY(`k1`) DISTRIBUTED BY HASH(`k1`) BUCKETS 1 diff --git a/regression-test/suites/load_p0/routine_load/ddl/dup_tbl_basic_multi_table_create.sql b/regression-test/suites/load_p0/routine_load/ddl/dup_tbl_basic_multi_table_create.sql index ea0a1fe63d6e53..63ab9c268a51eb 100644 --- a/regression-test/suites/load_p0/routine_load/ddl/dup_tbl_basic_multi_table_create.sql +++ b/regression-test/suites/load_p0/routine_load/ddl/dup_tbl_basic_multi_table_create.sql @@ -29,7 +29,7 @@ CREATE TABLE routine_load_dup_tbl_basic_multi_table INDEX idx_ngrambf_k116 (`k16`) USING NGRAM_BF PROPERTIES("gram_size"="3", "bf_size"="256"), INDEX idx_ngrambf_k117 (`k17`) USING NGRAM_BF PROPERTIES("gram_size"="3", "bf_size"="256"), - INDEX idx_bitmap_k104 (`k02`) USING BITMAP, + INDEX idx_bitmap_k104 (`k02`) USING BITMAP ) DUPLICATE KEY(k00) diff --git a/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_or_datetime_computation_negative.groovy b/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_or_datetime_computation_negative.groovy index 83431ede6ab55d..1a5d3ca7a0acfb 100644 --- a/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_or_datetime_computation_negative.groovy +++ b/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_or_datetime_computation_negative.groovy @@ -23,7 +23,7 @@ suite("test_date_or_datetime_computation_negative") { `dateV2` DATEV2 NOT NULL, `dateV2_null` DATEV2 NULL, `datetime` DATETIME NOT NULL, - `datetime_null` DATETIME NULL, ) + `datetime_null` DATETIME NULL ) DUPLICATE KEY(`row_id`) DISTRIBUTED BY HASH(`row_id`) BUCKETS 1 PROPERTIES ( diff --git a/regression-test/suites/query_p0/session_variable/test_default_limit.groovy b/regression-test/suites/query_p0/session_variable/test_default_limit.groovy index ad28f98aa39ccd..4133ccc33976be 100644 --- a/regression-test/suites/query_p0/session_variable/test_default_limit.groovy +++ b/regression-test/suites/query_p0/session_variable/test_default_limit.groovy @@ -23,7 +23,7 @@ suite('test_default_limit', "arrow_flight_sql") { create table baseall ( k0 int, k1 int, - k2 int, + k2 int ) distributed by hash(k0) buckets 16 properties( @@ -35,7 +35,7 @@ suite('test_default_limit', "arrow_flight_sql") { create table bigtable ( k0 int, k1 int, - k2 int, + k2 int ) distributed by hash(k0) buckets 16 properties( diff --git a/regression-test/suites/query_p0/sql_functions/aggregate_functions/test_count_distinct_with_collection_type.groovy b/regression-test/suites/query_p0/sql_functions/aggregate_functions/test_count_distinct_with_collection_type.groovy index 96cdc229de8f9e..b062920aa44991 100644 --- a/regression-test/suites/query_p0/sql_functions/aggregate_functions/test_count_distinct_with_collection_type.groovy +++ b/regression-test/suites/query_p0/sql_functions/aggregate_functions/test_count_distinct_with_collection_type.groovy @@ -24,7 +24,7 @@ suite("test_count_distinct_with_collection_type", "p0") { `id` int(11) NULL, `a` array NULL, `m` map NULL, - `s` STRUCT NULL, + `s` STRUCT NULL ) ENGINE=OLAP DUPLICATE KEY(`id`) COMMENT 'OLAP' diff --git a/regression-test/suites/query_p0/sql_functions/array_functions/test_array_functions.groovy b/regression-test/suites/query_p0/sql_functions/array_functions/test_array_functions.groovy index c043ada07af502..858a4bdbd37ef3 100644 --- a/regression-test/suites/query_p0/sql_functions/array_functions/test_array_functions.groovy +++ b/regression-test/suites/query_p0/sql_functions/array_functions/test_array_functions.groovy @@ -314,7 +314,7 @@ suite("test_array_functions") { `kdcmlv3s1` decimalv3(9, 3) null, `kdcmlv3s2` decimalv3(15, 5) null, `kdcmlv3s3` decimalv3(27, 9) null, - `kadcml` array null, + `kadcml` array null ) engine=olap DISTRIBUTED BY HASH(`id`) BUCKETS 1 properties("replication_num" = "1") diff --git a/regression-test/suites/query_p0/sql_functions/size_funciton/test_size_function.groovy b/regression-test/suites/query_p0/sql_functions/size_funciton/test_size_function.groovy index fe2607ecb12065..93f1b58158746c 100644 --- a/regression-test/suites/query_p0/sql_functions/size_funciton/test_size_function.groovy +++ b/regression-test/suites/query_p0/sql_functions/size_funciton/test_size_function.groovy @@ -37,7 +37,7 @@ suite("test_size_function") { `id` int(11) NULL, `c_array1` array NULL, `c_array2` array NULL, - `c_map` map NULL, + `c_map` map NULL ) ENGINE=OLAP DUPLICATE KEY(`id`) DISTRIBUTED BY HASH(`id`) BUCKETS 1 From b8fd55b0cf0f128b67ab44fb895f3a331fdc66ac Mon Sep 17 00:00:00 2001 From: morrySnow <101034200+morrySnow@users.noreply.github.com> Date: Tue, 26 Dec 2023 11:47:04 +0800 Subject: [PATCH 028/299] [feature](Nereids) support table valued function http_stream (#29004) --- .../catalog/BuiltinTableValuedFunctions.java | 2 + .../functions/table/HttpStream.java | 56 +++++++++++++++++++ .../visitor/TableValuedFunctionVisitor.java | 5 ++ 3 files changed, 63 insertions(+) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/HttpStream.java diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinTableValuedFunctions.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinTableValuedFunctions.java index 66b0fb49de6691..d1b63fe6237019 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinTableValuedFunctions.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinTableValuedFunctions.java @@ -23,6 +23,7 @@ import org.apache.doris.nereids.trees.expressions.functions.table.FrontendsDisks; import org.apache.doris.nereids.trees.expressions.functions.table.GroupCommit; import org.apache.doris.nereids.trees.expressions.functions.table.Hdfs; +import org.apache.doris.nereids.trees.expressions.functions.table.HttpStream; import org.apache.doris.nereids.trees.expressions.functions.table.IcebergMeta; import org.apache.doris.nereids.trees.expressions.functions.table.Jobs; import org.apache.doris.nereids.trees.expressions.functions.table.Local; @@ -51,6 +52,7 @@ public class BuiltinTableValuedFunctions implements FunctionHelper { tableValued(Local.class, "local"), tableValued(IcebergMeta.class, "iceberg_meta"), tableValued(Hdfs.class, "hdfs"), + tableValued(HttpStream.class, "http_stream"), tableValued(Numbers.class, "numbers"), tableValued(Queries.class, "queries"), tableValued(S3.class, "s3"), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/HttpStream.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/HttpStream.java new file mode 100644 index 00000000000000..de052b078db43e --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/HttpStream.java @@ -0,0 +1,56 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.functions.table; + +import org.apache.doris.catalog.FunctionSignature; +import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.trees.expressions.Properties; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.coercion.AnyDataType; +import org.apache.doris.tablefunction.HttpStreamTableValuedFunction; +import org.apache.doris.tablefunction.TableValuedFunctionIf; + +import java.util.Map; + +/** http_stream */ +public class HttpStream extends TableValuedFunction { + public HttpStream(Properties properties) { + super("http_stream", properties); + } + + @Override + public FunctionSignature customSignature() { + return FunctionSignature.of(AnyDataType.INSTANCE_WITHOUT_INDEX, getArgumentsTypes()); + } + + @Override + protected TableValuedFunctionIf toCatalogFunction() { + try { + Map arguments = getTVFProperties().getMap(); + return new HttpStreamTableValuedFunction(arguments); + } catch (Throwable t) { + throw new AnalysisException("Can not build HttpStreamTableValuedFunction by " + + this + ": " + t.getMessage(), t); + } + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitHttpStream(this, context); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/TableValuedFunctionVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/TableValuedFunctionVisitor.java index 9967e472694db7..fe07097bc81fca 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/TableValuedFunctionVisitor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/TableValuedFunctionVisitor.java @@ -23,6 +23,7 @@ import org.apache.doris.nereids.trees.expressions.functions.table.FrontendsDisks; import org.apache.doris.nereids.trees.expressions.functions.table.GroupCommit; import org.apache.doris.nereids.trees.expressions.functions.table.Hdfs; +import org.apache.doris.nereids.trees.expressions.functions.table.HttpStream; import org.apache.doris.nereids.trees.expressions.functions.table.IcebergMeta; import org.apache.doris.nereids.trees.expressions.functions.table.Jobs; import org.apache.doris.nereids.trees.expressions.functions.table.Local; @@ -74,6 +75,10 @@ default R visitHdfs(Hdfs hdfs, C context) { return visitTableValuedFunction(hdfs, context); } + default R visitHttpStream(HttpStream httpStream, C context) { + return visitTableValuedFunction(httpStream, context); + } + default R visitIcebergMeta(IcebergMeta icebergMeta, C context) { return visitTableValuedFunction(icebergMeta, context); } From 509cfea99ad55fa9d45fe7237fe2cbe5aa9d5a1a Mon Sep 17 00:00:00 2001 From: slothever <18522955+wsjz@users.noreply.github.com> Date: Tue, 26 Dec 2023 12:29:05 +0800 Subject: [PATCH 029/299] [feature](Load)(step2)support nereids load job schedule (#26356) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We will Integrate new load job manager into new job scheduling framework so that the insert into task can be scheduled after the broker load sql is converted to insert into TVF(table value function) sql. issue: https://github.com/apache/doris/issues/24221 Now support: 1. load data by tvf insert into sql, but just for simple load(columns need to be defined in the table) 2. show load stmt - job id, label name, job state, time info - simple progress 3. cancel load from db 4. support that enable new load through Config.enable_nereids_load 5. can replay job after restarting doris TODO: - support partition insert job - support show statistics from BE - support multiple task and collect task statistic - support transactional task - need add ut case --- .../java/org/apache/doris/common/Config.java | 12 + .../org/apache/doris/nereids/DorisParser.g4 | 15 +- .../apache/doris/analysis/CreateJobStmt.java | 22 +- .../apache/doris/analysis/ShowLoadStmt.java | 7 + .../java/org/apache/doris/catalog/Env.java | 9 + .../apache/doris/job/base/AbstractJob.java | 98 +++- .../java/org/apache/doris/job/base/Job.java | 25 +- .../apache/doris/job/base/JobExecuteType.java | 2 +- .../apache/doris/job/common/JobStatus.java | 1 - .../org/apache/doris/job/common/JobType.java | 2 +- .../job/extensions/insert/InsertJob.java | 496 +++++++++++++++--- .../job/extensions/insert/InsertTask.java | 128 +++-- .../doris/job/extensions/mtmv/MTMVJob.java | 2 +- .../apache/doris/job/manager/JobManager.java | 236 ++++++++- .../apache/doris/job/task/AbstractTask.java | 10 + .../java/org/apache/doris/load/ExportJob.java | 9 +- .../java/org/apache/doris/load/ExportMgr.java | 26 +- .../apache/doris/load/ExportTaskExecutor.java | 6 + .../org/apache/doris/load/loadv2/LoadJob.java | 105 ---- .../apache/doris/load/loadv2/LoadManager.java | 8 +- .../doris/load/loadv2/LoadStatistic.java | 142 +++++ .../nereids/jobs/load/LabelProcessor.java | 181 +++++++ .../nereids/parser/LogicalPlanBuilder.java | 14 +- .../trees/plans/commands/InsertExecutor.java | 13 +- .../commands/InsertIntoTableCommand.java | 15 + .../trees/plans/commands/LoadCommand.java | 51 +- .../org/apache/doris/persist/EditLog.java | 30 +- .../java/org/apache/doris/qe/DdlExecutor.java | 5 +- .../org/apache/doris/qe/ShowExecutor.java | 17 +- .../executor/TransientTaskExecutor.java | 2 + .../manager/TransientTaskManager.java | 5 +- .../registry/ExportTaskRegister.java | 2 +- .../suites/job_p0/test_base_insert_job.groovy | 2 +- 33 files changed, 1324 insertions(+), 374 deletions(-) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/load/loadv2/LoadStatistic.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/load/LabelProcessor.java diff --git a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java index 04d33fd4cc0bd0..c038a9f1780ceb 100644 --- a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java +++ b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java @@ -1931,6 +1931,18 @@ public class Config extends ConfigBase { @ConfField(masterOnly = true) public static boolean enable_hms_events_incremental_sync = false; + /** + * If set to true, doris will try to parse the ddl of a hive view and try to execute the query + * otherwise it will throw an AnalysisException. + */ + @ConfField(mutable = true, varType = VariableAnnotation.EXPERIMENTAL, description = { + "当前默认设置为 false,开启后支持使用新优化器的load语句导入数据,失败后会降级旧的load语句。", + "Now default set to true, After this function is enabled, the load statement of " + + "the new optimizer can be used to import data. If this function fails, " + + "the old load statement will be degraded."}) + public static boolean enable_nereids_load = false; + + /** * Maximum number of events to poll in each RPC. */ diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 index a2f90ea21667c8..dc202fef7431c0 100644 --- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 +++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 @@ -71,11 +71,6 @@ statement (withRemoteStorageSystem)? (PROPERTIES LEFT_PAREN properties=propertyItemList RIGHT_PAREN)? (commentSpec)? #load - | LOAD LABEL lableName=identifier - LEFT_PAREN dataDescs+=dataDesc (COMMA dataDescs+=dataDesc)* RIGHT_PAREN - resourceDesc - (PROPERTIES LEFT_PAREN properties=propertyItemList RIGHT_PAREN)? - (commentSpec)? #resourceLoad | LOAD mysqlDataDesc (PROPERTIES LEFT_PAREN properties=propertyItemList RIGHT_PAREN)? (commentSpec)? #mysqlLoad @@ -131,7 +126,7 @@ dataDesc (PARTITION partition=identifierList)? (COLUMNS TERMINATED BY comma=STRING_LITERAL)? (LINES TERMINATED BY separator=STRING_LITERAL)? - (FORMAT AS format=identifier)? + (FORMAT AS format=identifierOrStringLiteral)? (columns=identifierList)? (columnsFromPath=colFromPath)? (columnMapping=colMappingList)? @@ -167,6 +162,11 @@ refreshMethod : COMPLETE | AUTO ; +identifierOrStringLiteral + : identifier + | STRING_LITERAL + ; + identifierOrText : errorCapturingIdentifier | STRING_LITERAL @@ -224,7 +224,8 @@ mappingExpr ; withRemoteStorageSystem - : WITH S3 LEFT_PAREN + : resourceDesc + | WITH S3 LEFT_PAREN brokerProperties=propertyItemList RIGHT_PAREN | WITH HDFS LEFT_PAREN diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateJobStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateJobStmt.java index ececccc3169715..ef76aedba2b400 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateJobStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateJobStmt.java @@ -117,10 +117,8 @@ public void analyze(Analyzer analyzer) throws UserException { analyzerSqlStmt(); // check its insert stmt,currently only support insert stmt //todo when support other stmt,need to check stmt type and generate jobInstance - InsertJob job = new InsertJob(); JobExecutionConfiguration jobExecutionConfiguration = new JobExecutionConfiguration(); jobExecutionConfiguration.setExecuteType(executeType); - job.setCreateTimeMs(System.currentTimeMillis()); TimerDefinition timerDefinition = new TimerDefinition(); if (null != onceJobStartTimestamp) { @@ -148,17 +146,19 @@ public void analyze(Analyzer analyzer) throws UserException { } checkJobName(labelName.getLabelName()); jobExecutionConfiguration.setTimerDefinition(timerDefinition); - job.setJobConfig(jobExecutionConfiguration); - - job.setComment(comment); - job.setCurrentDbName(labelName.getDbName()); - job.setJobName(labelName.getLabelName()); - job.setCreateUser(ConnectContext.get().getCurrentUserIdentity()); - job.setJobStatus(JobStatus.RUNNING); - job.setJobId(Env.getCurrentEnv().getNextId()); String originStmt = getOrigStmt().originStmt; String executeSql = parseExecuteSql(originStmt); - job.setExecuteSql(executeSql); + // create job use label name as its job name + String jobName = labelName.getLabelName(); + InsertJob job = new InsertJob(jobName, + JobStatus.RUNNING, + labelName.getDbName(), + comment, + ConnectContext.get().getCurrentUserIdentity(), + jobExecutionConfiguration, + System.currentTimeMillis(), + executeSql); + //job.checkJobParams(); jobInstance = job; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowLoadStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowLoadStmt.java index ae8283f6ddfa72..1e00b5d8897ca6 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowLoadStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowLoadStmt.java @@ -108,6 +108,13 @@ public Set getStates() { return states; } + public org.apache.doris.load.loadv2.JobState getStateV2() { + if (Strings.isNullOrEmpty(stateValue)) { + return null; + } + return org.apache.doris.load.loadv2.JobState.valueOf(stateValue); + } + public boolean isAccurateMatch() { return isAccurateMatch; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java index 9ae7d4f1617660..cd77f70f59c4fa 100755 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java @@ -182,6 +182,7 @@ import org.apache.doris.mysql.privilege.AccessControllerManager; import org.apache.doris.mysql.privilege.Auth; import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.nereids.jobs.load.LabelProcessor; import org.apache.doris.nereids.trees.plans.commands.info.AlterMTMVPropertyInfo; import org.apache.doris.nereids.trees.plans.commands.info.AlterMTMVRefreshInfo; import org.apache.doris.nereids.trees.plans.commands.info.TableNameInfo; @@ -362,6 +363,7 @@ public class Env { private ExportTaskRegister exportTaskRegister; private JobManager, ?> jobManager; + private LabelProcessor labelProcessor; private TransientTaskManager transientTaskManager; private MasterDaemon labelCleaner; // To clean old LabelInfo, ExportJobInfos @@ -641,8 +643,11 @@ private Env(boolean isCheckpointCatalog) { } this.metastoreEventsProcessor = new MetastoreEventsProcessor(); this.jobManager = new JobManager<>(); + this.labelProcessor = new LabelProcessor(); this.transientTaskManager = new TransientTaskManager(); this.exportTaskRegister = new ExportTaskRegister(transientTaskManager); + this.transientTaskManager = new TransientTaskManager(); + this.replayedJournalId = new AtomicLong(0L); this.stmtIdCounter = new AtomicLong(0L); this.isElectable = false; @@ -3907,6 +3912,10 @@ public JobManager getJobManager() { return jobManager; } + public LabelProcessor getLabelProcessor() { + return labelProcessor; + } + public TransientTaskManager getTransientTaskManager() { return transientTaskManager; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/job/base/AbstractJob.java b/fe/fe-core/src/main/java/org/apache/doris/job/base/AbstractJob.java index 83f02326d82f26..6e9cb48da1ca44 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/job/base/AbstractJob.java +++ b/fe/fe-core/src/main/java/org/apache/doris/job/base/AbstractJob.java @@ -23,6 +23,8 @@ import org.apache.doris.catalog.ScalarType; import org.apache.doris.common.io.Text; import org.apache.doris.common.io.Writable; +import org.apache.doris.common.util.LogBuilder; +import org.apache.doris.common.util.LogKey; import org.apache.doris.common.util.TimeUtils; import org.apache.doris.job.common.JobStatus; import org.apache.doris.job.common.TaskStatus; @@ -76,14 +78,55 @@ public abstract class AbstractJob implements Job runningTasks) { + this.jobId = jobId; + this.jobName = jobName; + this.jobStatus = jobStatus; + this.currentDbName = currentDbName; + this.comment = comment; + this.createUser = createUser; + this.jobConfig = jobConfig; + this.createTimeMs = createTimeMs; + this.executeSql = executeSql; + this.runningTasks = runningTasks; + } + private List runningTasks = new ArrayList<>(); @Override @@ -109,6 +152,10 @@ public void cancelAllTasks() throws JobException { .add("Comment") .build(); + protected static long getNextJobId() { + return System.nanoTime() + RandomUtils.nextInt(); + } + @Override public void cancelTaskById(long taskId) throws JobException { if (CollectionUtils.isEmpty(runningTasks)) { @@ -154,17 +201,18 @@ public List commonCreateTasks(TaskType taskType, C taskContext) { return createTasks(taskType, taskContext); } - public void initTasks(List tasks) { + public void initTasks(Collection tasks, TaskType taskType) { + if (CollectionUtils.isEmpty(getRunningTasks())) { + runningTasks = new ArrayList<>(); + } tasks.forEach(task -> { - task.setJobId(jobId); - task.setTaskId(getNextId()); + task.setTaskType(taskType); + task.setJobId(getJobId()); task.setCreateTimeMs(System.currentTimeMillis()); task.setStatus(TaskStatus.PENDING); }); - if (CollectionUtils.isEmpty(getRunningTasks())) { - setRunningTasks(new ArrayList<>()); - } - getRunningTasks().addAll((Collection) tasks); + getRunningTasks().addAll(tasks); + this.startTimeMs = System.currentTimeMillis(); } public void checkJobParams() { @@ -208,10 +256,22 @@ public void updateJobStatus(JobStatus newJobStatus) throws JobException { public static AbstractJob readFields(DataInput in) throws IOException { String jsonJob = Text.readString(in); AbstractJob job = GsonUtils.GSON.fromJson(jsonJob, AbstractJob.class); - job.setRunningTasks(new ArrayList<>()); + job.runningTasks = new ArrayList<>(); return job; } + public void logCreateOperation() { + Env.getCurrentEnv().getEditLog().logCreateJob(this); + } + + public void logFinalOperation() { + Env.getCurrentEnv().getEditLog().logEndJob(this); + } + + public void logUpdateOperation() { + Env.getCurrentEnv().getEditLog().logUpdateJob(this); + } + @Override public void onTaskFail(T task) throws JobException { updateJobStatusIfEnd(); @@ -303,7 +363,19 @@ public ShowResultSetMetaData getJobMetaData() { return builder.build(); } - private static long getNextId() { - return System.nanoTime() + RandomUtils.nextInt(); + @Override + public void onRegister() throws JobException {} + + @Override + public void onUnRegister() throws JobException {} + + @Override + public void onReplayCreate() throws JobException { + log.info(new LogBuilder(LogKey.SCHEDULER_JOB, getJobId()).add("msg", "replay create scheduler job").build()); + } + + @Override + public void onReplayEnd(AbstractJob replayJob) throws JobException { + log.info(new LogBuilder(LogKey.SCHEDULER_JOB, getJobId()).add("msg", "replay delete scheduler job").build()); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/job/base/Job.java b/fe/fe-core/src/main/java/org/apache/doris/job/base/Job.java index ee352a0f417667..1124e7f2d28028 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/job/base/Job.java +++ b/fe/fe-core/src/main/java/org/apache/doris/job/base/Job.java @@ -99,11 +99,34 @@ public interface Job { /** * Cancels all running tasks of this job. - * * @throws JobException If cancelling a running task fails. */ void cancelAllTasks() throws JobException; + /** + * register job + * @throws JobException If register job failed. + */ + void onRegister() throws JobException; + + /** + * register job failed + * @throws JobException If failed. + */ + void onUnRegister() throws JobException; + + /** + * replay create job + * @throws JobException If replay create failed. + */ + void onReplayCreate() throws JobException; + + /** + * replay finished or cancelled job + * @throws JobException If replay end failed. + */ + void onReplayEnd(AbstractJob replayJob) throws JobException; + /** * Notifies the job when a task execution fails. * diff --git a/fe/fe-core/src/main/java/org/apache/doris/job/base/JobExecuteType.java b/fe/fe-core/src/main/java/org/apache/doris/job/base/JobExecuteType.java index ea9ddb3b0203d4..3529a2efeff7da 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/job/base/JobExecuteType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/job/base/JobExecuteType.java @@ -37,7 +37,7 @@ public enum JobExecuteType { */ MANUAL, /** - * The job will be executed immediately. + * The job will be executed only once and immediately. */ INSTANT, } diff --git a/fe/fe-core/src/main/java/org/apache/doris/job/common/JobStatus.java b/fe/fe-core/src/main/java/org/apache/doris/job/common/JobStatus.java index 2df65e4654d414..22b799225d1a06 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/job/common/JobStatus.java +++ b/fe/fe-core/src/main/java/org/apache/doris/job/common/JobStatus.java @@ -36,7 +36,6 @@ public enum JobStatus { * The stop state cannot be resumed */ STOPPED, - /** * When the task is finished, the finished state will be triggered. */ diff --git a/fe/fe-core/src/main/java/org/apache/doris/job/common/JobType.java b/fe/fe-core/src/main/java/org/apache/doris/job/common/JobType.java index 1beb4e0a3840f3..084a39ddf096bd 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/job/common/JobType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/job/common/JobType.java @@ -19,5 +19,5 @@ public enum JobType { INSERT, - MV + MV, } diff --git a/fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/InsertJob.java b/fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/InsertJob.java index 74581b8f1b0b49..9256864efcacc1 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/InsertJob.java +++ b/fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/InsertJob.java @@ -17,43 +17,74 @@ package org.apache.doris.job.extensions.insert; +import org.apache.doris.analysis.LoadStmt; +import org.apache.doris.analysis.UserIdentity; +import org.apache.doris.catalog.AuthorizationInfo; import org.apache.doris.catalog.Column; +import org.apache.doris.catalog.Database; import org.apache.doris.catalog.Env; import org.apache.doris.catalog.ScalarType; import org.apache.doris.common.Config; -import org.apache.doris.common.MetaNotFoundException; +import org.apache.doris.common.DdlException; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.common.FeConstants; +import org.apache.doris.common.LabelAlreadyUsedException; import org.apache.doris.common.io.Text; +import org.apache.doris.common.util.LogBuilder; +import org.apache.doris.common.util.LogKey; +import org.apache.doris.common.util.TimeUtils; import org.apache.doris.job.base.AbstractJob; import org.apache.doris.job.base.JobExecuteType; +import org.apache.doris.job.base.JobExecutionConfiguration; +import org.apache.doris.job.common.JobStatus; import org.apache.doris.job.common.JobType; import org.apache.doris.job.common.TaskType; import org.apache.doris.job.exception.JobException; -import org.apache.doris.load.loadv2.LoadJob; +import org.apache.doris.load.FailMsg; +import org.apache.doris.load.loadv2.LoadStatistic; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.mysql.privilege.Privilege; import org.apache.doris.nereids.trees.plans.commands.InsertIntoTableCommand; import org.apache.doris.persist.gson.GsonUtils; import org.apache.doris.qe.ConnectContext; import org.apache.doris.qe.ShowResultSetMetaData; import org.apache.doris.qe.StmtExecutor; +import org.apache.doris.thrift.TUniqueId; +import org.apache.doris.transaction.ErrorTabletInfo; +import org.apache.doris.transaction.TabletCommitInfo; +import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.annotations.SerializedName; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.stream.Collectors; +@EqualsAndHashCode(callSuper = true) @Data @Slf4j -public class InsertJob extends AbstractJob { +public class InsertJob extends AbstractJob> { public static final ImmutableList SCHEMA = ImmutableList.of( new Column("Id", ScalarType.createStringType()), @@ -66,40 +97,145 @@ public class InsertJob extends AbstractJob { new Column("CreateTime", ScalarType.createStringType()), new Column("Comment", ScalarType.createStringType())); + private static final ShowResultSetMetaData TASK_META_DATA = + ShowResultSetMetaData.builder() + .addColumn(new Column("TaskId", ScalarType.createVarchar(80))) + .addColumn(new Column("Label", ScalarType.createVarchar(80))) + .addColumn(new Column("Status", ScalarType.createVarchar(20))) + .addColumn(new Column("EtlInfo", ScalarType.createVarchar(100))) + .addColumn(new Column("TaskInfo", ScalarType.createVarchar(100))) + .addColumn(new Column("ErrorMsg", ScalarType.createVarchar(100))) + + .addColumn(new Column("CreateTimeMs", ScalarType.createVarchar(20))) + .addColumn(new Column("FinishTimeMs", ScalarType.createVarchar(20))) + .addColumn(new Column("TrackingUrl", ScalarType.createVarchar(200))) + .addColumn(new Column("LoadStatistic", ScalarType.createVarchar(200))) + .addColumn(new Column("User", ScalarType.createVarchar(50))) + .build(); + public static final ImmutableMap COLUMN_TO_INDEX; static { - ImmutableMap.Builder builder = new ImmutableMap.Builder(); + ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); for (int i = 0; i < SCHEMA.size(); i++) { builder.put(SCHEMA.get(i).getName().toLowerCase(), i); } COLUMN_TO_INDEX = builder.build(); } - @SerializedName(value = "lp") - String labelPrefix; + @SerializedName("tis") + ConcurrentLinkedQueue historyTaskIdList; + @SerializedName("did") + private final long dbId; + @SerializedName("ln") + private String labelName; + @SerializedName("lt") + private InsertJob.LoadType loadType; + // 0: the job status is pending + // n/100: n is the number of task which has been finished + // 99: all tasks have been finished + // 100: txn status is visible and load has been finished + @SerializedName("pg") + private int progress; + @SerializedName("fm") + private FailMsg failMsg; + @SerializedName("plans") + private List plans = new ArrayList<>(); + private LoadStatistic loadStatistic = new LoadStatistic(); + private Set finishedTaskIds = new HashSet<>(); + private ConcurrentHashMap idToTasks = new ConcurrentHashMap<>(); + private Map properties = new HashMap<>(); + private Set tableNames; + private AuthorizationInfo authorizationInfo; - InsertIntoTableCommand command; + private ConnectContext ctx; + private StmtExecutor stmtExecutor; + private List errorTabletInfos = new ArrayList<>(); + private List commitInfos = new ArrayList<>(); - StmtExecutor stmtExecutor; + // max save task num, do we need to config it? + private static final int MAX_SAVE_TASK_NUM = 100; - ConnectContext ctx; + /** + * load job type + */ + public enum LoadType { + BULK, + SPARK, + LOCAL_FILE, + UNKNOWN - @SerializedName("tis") - ConcurrentLinkedQueue historyTaskIdList; + } + + public enum Priority { + HIGH(0), + NORMAL(1), + LOW(2); + + Priority(int value) { + this.value = value; + } + + private final int value; + + public int getValue() { + return value; + } + } + + public InsertJob(String jobName, + JobStatus jobStatus, + String dbName, + String comment, + UserIdentity createUser, + JobExecutionConfiguration jobConfig, + Long createTimeMs, + String executeSql) { + super(getNextJobId(), jobName, jobStatus, dbName, comment, createUser, + jobConfig, createTimeMs, executeSql, null); + this.dbId = ConnectContext.get().getCurrentDbId(); + } + + public InsertJob(ConnectContext ctx, + StmtExecutor executor, + String labelName, + List plans, + Set sinkTableNames, + Map properties, + String comment, + JobExecutionConfiguration jobConfig) { + super(getNextJobId(), labelName, JobStatus.RUNNING, null, + comment, ctx.getCurrentUserIdentity(), jobConfig); + this.ctx = ctx; + this.plans = plans; + this.stmtExecutor = executor; + this.dbId = ctx.getCurrentDbId(); + this.labelName = labelName; + this.tableNames = sinkTableNames; + this.properties = properties; + // TODO: not support other type yet + this.loadType = InsertJob.LoadType.BULK; + } @Override - public List createTasks(TaskType taskType, Map taskContext) { - //nothing need to do in insert job - InsertTask task = new InsertTask(null, getCurrentDbName(), getExecuteSql(), getCreateUser()); - task.setJobId(getJobId()); - task.setTaskType(taskType); - task.setTaskId(Env.getCurrentEnv().getNextId()); - ArrayList tasks = new ArrayList<>(); - tasks.add(task); - super.initTasks(tasks); - recordTask(task.getTaskId()); - return tasks; + public List createTasks(TaskType taskType, Map taskContext) { + if (plans.isEmpty()) { + InsertTask task = new InsertTask(labelName, getCurrentDbName(), getExecuteSql(), getCreateUser()); + idToTasks.put(task.getTaskId(), task); + recordTask(task.getTaskId()); + } else { + // use for load stmt + for (InsertIntoTableCommand logicalPlan : plans) { + if (!logicalPlan.getLabelName().isPresent()) { + throw new IllegalArgumentException("Load plan need label name."); + } + InsertTask task = new InsertTask(logicalPlan, ctx, stmtExecutor, loadStatistic); + idToTasks.put(task.getTaskId(), task); + recordTask(task.getTaskId()); + } + } + initTasks(idToTasks.values(), taskType); + return new ArrayList<>(idToTasks.values()); } public void recordTask(long id) { @@ -116,7 +252,6 @@ public void recordTask(long id) { if (historyTaskIdList.size() >= Config.max_persistence_task_count) { historyTaskIdList.poll(); } - Env.getCurrentEnv().getEditLog().logUpdateJob(this); } @Override @@ -125,23 +260,27 @@ public void cancelTaskById(long taskId) throws JobException { } @Override - public boolean isReadyForScheduling(Map taskContext) { - return CollectionUtils.isEmpty(getRunningTasks()); + public void cancelAllTasks() throws JobException { + try { + checkAuth("CANCEL LOAD"); + super.cancelAllTasks(); + this.failMsg = new FailMsg(FailMsg.CancelType.USER_CANCEL, "user cancel"); + } catch (DdlException e) { + throw new JobException(e); + } } - @Override - public void cancelAllTasks() throws JobException { - super.cancelAllTasks(); + public boolean isReadyForScheduling(Map taskContext) { + return CollectionUtils.isEmpty(getRunningTasks()); } - @Override protected void checkJobParamsInternal() { - if (command == null && StringUtils.isBlank(getExecuteSql())) { + if (plans.isEmpty() && StringUtils.isBlank(getExecuteSql())) { throw new IllegalArgumentException("command or sql is null,must be set"); } - if (null != command && !getJobConfig().getExecuteType().equals(JobExecuteType.INSTANT)) { + if (!plans.isEmpty() && !getJobConfig().getExecuteType().equals(JobExecuteType.INSTANT)) { throw new IllegalArgumentException("command must be null when executeType is not instant"); } } @@ -153,27 +292,22 @@ public List queryTasks() { } //TODO it's will be refactor, we will storage task info in job inner and query from it List taskIdList = new ArrayList<>(this.historyTaskIdList); + Collections.reverse(taskIdList); - List loadJobs = Env.getCurrentEnv().getLoadManager().queryLoadJobsByJobIds(taskIdList); - if (CollectionUtils.isEmpty(loadJobs)) { + return queryLoadTasksByTaskIds(taskIdList); + } + + public List queryLoadTasksByTaskIds(List taskIdList) { + if (taskIdList.isEmpty()) { return new ArrayList<>(); } - List tasks = new ArrayList<>(); - loadJobs.forEach(loadJob -> { - InsertTask task; - try { - task = new InsertTask(loadJob.getLabel(), loadJob.getDb().getFullName(), null, getCreateUser()); - task.setCreateTimeMs(loadJob.getCreateTimestamp()); - } catch (MetaNotFoundException e) { - log.warn("load job not found, job id is {}", loadJob.getId()); - return; + List jobs = new ArrayList<>(); + taskIdList.forEach(id -> { + if (null != idToTasks.get(id)) { + jobs.add(idToTasks.get(id)); } - task.setJobId(getJobId()); - task.setTaskId(loadJob.getId()); - task.setLoadJob(loadJob); - tasks.add(task); }); - return tasks; + return jobs; } @Override @@ -193,6 +327,12 @@ public ShowResultSetMetaData getTaskMetaData() { @Override public void onTaskFail(InsertTask task) { + try { + updateJobStatus(JobStatus.STOPPED); + this.failMsg = new FailMsg(FailMsg.CancelType.LOAD_RUN_FAIL, task.getErrMsg()); + } catch (JobException e) { + throw new RuntimeException(e); + } getRunningTasks().remove(task); } @@ -203,7 +343,129 @@ public void onTaskSuccess(InsertTask task) throws JobException { @Override public List getShowInfo() { - return super.getCommonShowInfo(); + try { + // check auth + checkAuth("SHOW LOAD"); + List jobInfo = Lists.newArrayList(); + // jobId + jobInfo.add(getJobId().toString()); + // label + if (StringUtils.isEmpty(getLabelName())) { + jobInfo.add(FeConstants.null_string); + } else { + jobInfo.add(getLabelName()); + } + // state + if (getJobStatus() == JobStatus.STOPPED) { + jobInfo.add("CANCELLED"); + } else { + jobInfo.add(getJobStatus().name()); + } + + // progress + String progress = Env.getCurrentProgressManager().getProgressInfo(String.valueOf(getJobId())); + switch (getJobStatus()) { + case RUNNING: + if (isPending()) { + jobInfo.add("ETL:0%; LOAD:0%"); + } else { + jobInfo.add("ETL:100%; LOAD:" + progress + "%"); + } + break; + case FINISHED: + jobInfo.add("ETL:100%; LOAD:100%"); + break; + case STOPPED: + default: + jobInfo.add("ETL:N/A; LOAD:N/A"); + break; + } + // type + jobInfo.add(loadType.name()); + + // etl info + if (loadStatistic.getCounters().size() == 0) { + jobInfo.add(FeConstants.null_string); + } else { + jobInfo.add(Joiner.on("; ").withKeyValueSeparator("=").join(loadStatistic.getCounters())); + } + + // task info + jobInfo.add("cluster:" + getResourceName() + "; timeout(s):" + getTimeout() + + "; max_filter_ratio:" + getMaxFilterRatio() + "; priority:" + getPriority()); + // error msg + if (failMsg == null) { + jobInfo.add(FeConstants.null_string); + } else { + jobInfo.add("type:" + failMsg.getCancelType() + "; msg:" + failMsg.getMsg()); + } + + // create time + jobInfo.add(TimeUtils.longToTimeString(getCreateTimeMs())); + // etl start time + jobInfo.add(TimeUtils.longToTimeString(getStartTimeMs())); + // etl end time + jobInfo.add(TimeUtils.longToTimeString(getStartTimeMs())); + // load start time + jobInfo.add(TimeUtils.longToTimeString(getStartTimeMs())); + // load end time + jobInfo.add(TimeUtils.longToTimeString(getFinishTimeMs())); + // tracking urls + List trackingUrl = idToTasks.values().stream() + .map(task -> { + if (StringUtils.isNotEmpty(task.getTrackingUrl())) { + return task.getTrackingUrl(); + } else { + return FeConstants.null_string; + } + }) + .collect(Collectors.toList()); + if (trackingUrl.isEmpty()) { + jobInfo.add(FeConstants.null_string); + } else { + jobInfo.add(trackingUrl.toString()); + } + // job details + jobInfo.add(loadStatistic.toJson()); + // transaction id + jobInfo.add(String.valueOf(0)); + // error tablets + jobInfo.add(errorTabletsToJson()); + // user, some load job may not have user info + if (getCreateUser() == null || getCreateUser().getQualifiedUser() == null) { + jobInfo.add(FeConstants.null_string); + } else { + jobInfo.add(getCreateUser().getQualifiedUser()); + } + // comment + jobInfo.add(getComment()); + return jobInfo; + } catch (DdlException e) { + throw new RuntimeException(e); + } + } + + private String getPriority() { + return properties.getOrDefault(LoadStmt.PRIORITY, Priority.NORMAL.name()); + } + + public double getMaxFilterRatio() { + return Double.parseDouble(properties.getOrDefault(LoadStmt.MAX_FILTER_RATIO_PROPERTY, "0.0")); + } + + public long getTimeout() { + if (properties.containsKey(LoadStmt.TIMEOUT_PROPERTY)) { + return Long.parseLong(properties.get(LoadStmt.TIMEOUT_PROPERTY)); + } + return Config.broker_load_default_timeout_second; + } + + + public static InsertJob readFields(DataInput in) throws IOException { + String jsonJob = Text.readString(in); + InsertJob job = GsonUtils.GSON.fromJson(jsonJob, InsertJob.class); + job.setRunningTasks(new ArrayList<>()); + return job; } @Override @@ -211,19 +473,129 @@ public void write(DataOutput out) throws IOException { Text.writeString(out, GsonUtils.GSON.toJson(this)); } - private static final ShowResultSetMetaData TASK_META_DATA = - ShowResultSetMetaData.builder() - .addColumn(new Column("TaskId", ScalarType.createVarchar(20))) - .addColumn(new Column("Label", ScalarType.createVarchar(20))) - .addColumn(new Column("Status", ScalarType.createVarchar(20))) - .addColumn(new Column("EtlInfo", ScalarType.createVarchar(20))) - .addColumn(new Column("TaskInfo", ScalarType.createVarchar(20))) - .addColumn(new Column("ErrorMsg", ScalarType.createVarchar(20))) + public String errorTabletsToJson() { + Map map = new HashMap<>(); + errorTabletInfos.stream().limit(Config.max_error_tablet_of_broker_load) + .forEach(p -> map.put(p.getTabletId(), p.getMsg())); + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + return gson.toJson(map); + } - .addColumn(new Column("CreateTimeMs", ScalarType.createVarchar(20))) - .addColumn(new Column("FinishTimeMs", ScalarType.createVarchar(20))) - .addColumn(new Column("TrackingUrl", ScalarType.createVarchar(20))) - .addColumn(new Column("LoadStatistic", ScalarType.createVarchar(20))) - .addColumn(new Column("User", ScalarType.createVarchar(20))) - .build(); + public void updateLoadingStatus(Long beId, TUniqueId loadId, TUniqueId fragmentId, long scannedRows, + long scannedBytes, boolean isDone) { + loadStatistic.updateLoadProgress(beId, loadId, fragmentId, scannedRows, scannedBytes, isDone); + progress = (int) ((double) finishedTaskIds.size() / idToTasks.size() * 100); + if (progress == 100) { + progress = 99; + } + } + + private void checkAuth(String command) throws DdlException { + if (authorizationInfo == null) { + // use the old method to check priv + checkAuthWithoutAuthInfo(command); + return; + } + if (!Env.getCurrentEnv().getAccessManager().checkPrivByAuthInfo(ConnectContext.get(), authorizationInfo, + PrivPredicate.LOAD)) { + ErrorReport.reportDdlException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, + Privilege.LOAD_PRIV); + } + } + + /** + * This method is compatible with old load job without authorization info + * If db or table name could not be found by id, it will throw the NOT_EXISTS_ERROR + * + * @throws DdlException + */ + private void checkAuthWithoutAuthInfo(String command) throws DdlException { + Database db = Env.getCurrentInternalCatalog().getDbOrDdlException(dbId); + // check auth + if (tableNames == null || tableNames.isEmpty()) { + // forward compatibility + if (!Env.getCurrentEnv().getAccessManager().checkDbPriv(ConnectContext.get(), db.getFullName(), + PrivPredicate.LOAD)) { + ErrorReport.reportDdlException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, + Privilege.LOAD_PRIV); + } + } else { + for (String tblName : tableNames) { + if (!Env.getCurrentEnv().getAccessManager().checkTblPriv(ConnectContext.get(), db.getFullName(), + tblName, PrivPredicate.LOAD)) { + ErrorReport.reportDdlException(ErrorCode.ERR_TABLEACCESS_DENIED_ERROR, + command, + ConnectContext.get().getQualifiedUser(), + ConnectContext.get().getRemoteIP(), db.getFullName() + ": " + tblName); + } + } + } + } + + public void unprotectReadEndOperation(InsertJob replayLog) { + setJobStatus(replayLog.getJobStatus()); + progress = replayLog.getProgress(); + setStartTimeMs(replayLog.getStartTimeMs()); + setFinishTimeMs(replayLog.getFinishTimeMs()); + failMsg = replayLog.getFailMsg(); + } + + public String getResourceName() { + // TODO: get tvf param from tvf relation + return "N/A"; + } + + public boolean isRunning() { + return getJobStatus() != JobStatus.FINISHED; + } + + public boolean isPending() { + return getJobStatus() != JobStatus.FINISHED; + } + + public boolean isCancelled() { + return getJobStatus() == JobStatus.STOPPED; + } + + @Override + public void onRegister() throws JobException { + try { + if (StringUtils.isNotEmpty(labelName)) { + Env.getCurrentEnv().getLabelProcessor().addJob(this); + } + } catch (LabelAlreadyUsedException e) { + throw new JobException(e); + } + } + + @Override + public void onUnRegister() throws JobException { + // TODO: need record cancelled jobs in order to show cancelled job + // Env.getCurrentEnv().getLabelProcessor().removeJob(getDbId(), getLabelName()); + } + + @Override + public void onReplayCreate() throws JobException { + JobExecutionConfiguration jobConfig = new JobExecutionConfiguration(); + jobConfig.setExecuteType(JobExecuteType.INSTANT); + setJobConfig(jobConfig); + onRegister(); + checkJobParams(); + log.info(new LogBuilder(LogKey.LOAD_JOB, getJobId()).add("msg", "replay create load job").build()); + } + + @Override + public void onReplayEnd(AbstractJob> replayJob) throws JobException { + if (!(replayJob instanceof InsertJob)) { + return; + } + InsertJob insertJob = (InsertJob) replayJob; + unprotectReadEndOperation(insertJob); + log.info(new LogBuilder(LogKey.LOAD_JOB, + insertJob.getJobId()).add("operation", insertJob).add("msg", "replay end load job").build()); + } + + public int getProgress() { + return progress; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/InsertTask.java b/fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/InsertTask.java index 7e9889502678e0..e85e7a1b02708d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/InsertTask.java +++ b/fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/InsertTask.java @@ -25,7 +25,8 @@ import org.apache.doris.common.util.TimeUtils; import org.apache.doris.job.exception.JobException; import org.apache.doris.job.task.AbstractTask; -import org.apache.doris.load.loadv2.LoadJob; +import org.apache.doris.load.FailMsg; +import org.apache.doris.load.loadv2.LoadStatistic; import org.apache.doris.nereids.parser.NereidsParser; import org.apache.doris.nereids.trees.plans.commands.InsertIntoTableCommand; import org.apache.doris.qe.ConnectContext; @@ -34,12 +35,12 @@ import org.apache.doris.thrift.TRow; import org.apache.doris.thrift.TUniqueId; -import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import lombok.Getter; import lombok.Setter; import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.StringUtils; import java.util.Optional; import java.util.UUID; @@ -53,8 +54,6 @@ public class InsertTask extends AbstractTask { new Column("JobId", ScalarType.createStringType()), new Column("Label", ScalarType.createStringType()), new Column("Status", ScalarType.createStringType()), - new Column("EtlInfo", ScalarType.createStringType()), - new Column("TaskInfo", ScalarType.createStringType()), new Column("ErrorMsg", ScalarType.createStringType()), new Column("CreateTimeMs", ScalarType.createStringType()), new Column("FinishTimeMs", ScalarType.createStringType()), @@ -73,30 +72,69 @@ public class InsertTask extends AbstractTask { } private String labelName; - private InsertIntoTableCommand command; - private StmtExecutor stmtExecutor; - private ConnectContext ctx; - private String sql; - private String currentDb; - private UserIdentity userIdentity; - + private LoadStatistic loadStatistic; private AtomicBoolean isCanceled = new AtomicBoolean(false); - private AtomicBoolean isFinished = new AtomicBoolean(false); - private static final String LABEL_SPLITTER = "_"; + private FailMsg failMsg; + @Getter + private String trackingUrl; @Getter @Setter - private LoadJob loadJob; + private InsertJob jobInfo; + private TaskType taskType = TaskType.PENDING; + private MergeType mergeType = MergeType.APPEND; + + /** + * task merge type + */ + enum MergeType { + MERGE, + APPEND, + DELETE + } + + /** + * task type + */ + enum TaskType { + UNKNOWN, // this is only for ISSUE #2354 + PENDING, + LOADING, + FINISHED, + FAILED, + CANCELLED + } + + public InsertTask(InsertIntoTableCommand insertInto, + ConnectContext ctx, StmtExecutor executor, LoadStatistic statistic) { + this(insertInto.getLabelName().get(), insertInto, ctx, executor, statistic); + } + + public InsertTask(String labelName, String currentDb, String sql, UserIdentity userIdentity) { + this.labelName = labelName; + this.sql = sql; + this.currentDb = currentDb; + this.userIdentity = userIdentity; + } + public InsertTask(String labelName, InsertIntoTableCommand insertInto, + ConnectContext ctx, StmtExecutor executor, LoadStatistic statistic) { + this.labelName = labelName; + this.command = insertInto; + this.userIdentity = ctx.getCurrentUserIdentity(); + this.ctx = ctx; + this.stmtExecutor = executor; + this.loadStatistic = statistic; + } @Override public void before() throws JobException { @@ -109,15 +147,19 @@ public void before() throws JobException { ctx.setCurrentUserIdentity(userIdentity); ctx.getState().reset(); ctx.setThreadLocalInfo(); - ctx.setDatabase(currentDb); + if (StringUtils.isNotEmpty(currentDb)) { + ctx.setDatabase(currentDb); + } TUniqueId queryId = generateQueryId(UUID.randomUUID().toString()); ctx.getSessionVariable().enableFallbackToOriginalPlanner = false; stmtExecutor = new StmtExecutor(ctx, (String) null); ctx.setQueryId(queryId); - NereidsParser parser = new NereidsParser(); - this.command = (InsertIntoTableCommand) parser.parseSingle(sql); - this.command.setLabelName(Optional.of(getJobId() + LABEL_SPLITTER + getTaskId())); - this.command.setJobId(getTaskId()); + if (StringUtils.isNotEmpty(sql)) { + NereidsParser parser = new NereidsParser(); + this.command = (InsertIntoTableCommand) parser.parseSingle(sql); + this.command.setLabelName(Optional.of(getJobId() + LABEL_SPLITTER + getTaskId())); + this.command.setJobId(getTaskId()); + } super.before(); @@ -128,14 +170,6 @@ protected TUniqueId generateQueryId(String taskIdString) { return new TUniqueId(taskId.getMostSignificantBits(), taskId.getLeastSignificantBits()); } - public InsertTask(String labelName, String currentDb, String sql, UserIdentity userIdentity) { - this.labelName = labelName; - this.sql = sql; - this.currentDb = currentDb; - this.userIdentity = userIdentity; - - } - @Override public void run() throws JobException { try { @@ -143,7 +177,7 @@ public void run() throws JobException { log.info("task has been canceled, task id is {}", getTaskId()); return; } - command.run(ctx, stmtExecutor); + command.runWithUpdateInfo(ctx, stmtExecutor, loadStatistic); } catch (Exception e) { log.warn("execute insert task error, job id is {}, task id is {},sql is {}", getJobId(), getTaskId(), sql, e); @@ -178,42 +212,28 @@ public void cancel() throws JobException { @Override public TRow getTvfInfo() { TRow trow = new TRow(); - if (loadJob == null) { + if (jobInfo == null) { // if task not start, load job is null,return pending task show info return getPendingTaskTVFInfo(); } - trow.addToColumnValue(new TCell().setStringVal(String.valueOf(loadJob.getId()))); + trow.addToColumnValue(new TCell().setStringVal(String.valueOf(jobInfo.getJobId()))); trow.addToColumnValue(new TCell().setStringVal(String.valueOf(getJobId()))); - trow.addToColumnValue(new TCell().setStringVal(loadJob.getLabel())); - trow.addToColumnValue(new TCell().setStringVal(loadJob.getState().name())); - // etl info - String etlInfo = FeConstants.null_string; - if (!loadJob.getLoadingStatus().getCounters().isEmpty()) { - etlInfo = Joiner.on("; ").withKeyValueSeparator("=").join(loadJob.getLoadingStatus().getCounters()); - } - trow.addToColumnValue(new TCell().setStringVal(etlInfo)); - - // task info - String taskInfo = "cluster:" + loadJob.getResourceName() + "; timeout(s):" + loadJob.getTimeout() - + "; max_filter_ratio:" + loadJob.getMaxFilterRatio() + "; priority:" + loadJob.getPriority(); - trow.addToColumnValue(new TCell().setStringVal(taskInfo)); - + trow.addToColumnValue(new TCell().setStringVal(labelName)); + trow.addToColumnValue(new TCell().setStringVal(jobInfo.getJobStatus().name())); // err msg String errMsg = FeConstants.null_string; - if (loadJob.getFailMsg() != null) { - errMsg = "type:" + loadJob.getFailMsg().getCancelType() + "; msg:" + loadJob.getFailMsg().getMsg(); + if (failMsg != null) { + errMsg = "type:" + failMsg.getCancelType() + "; msg:" + failMsg.getMsg(); } trow.addToColumnValue(new TCell().setStringVal(errMsg)); - // create time - trow.addToColumnValue(new TCell().setStringVal(TimeUtils.longToTimeString(loadJob.getCreateTimestamp()))); - + trow.addToColumnValue(new TCell().setStringVal(TimeUtils.longToTimeString(jobInfo.getCreateTimeMs()))); // load end time - trow.addToColumnValue(new TCell().setStringVal(TimeUtils.longToTimeString(loadJob.getFinishTimestamp()))); + trow.addToColumnValue(new TCell().setStringVal(TimeUtils.longToTimeString(jobInfo.getFinishTimeMs()))); // tracking url - trow.addToColumnValue(new TCell().setStringVal(loadJob.getLoadingStatus().getTrackingUrl())); - trow.addToColumnValue(new TCell().setStringVal(loadJob.getLoadStatistic().toJson())); - trow.addToColumnValue(new TCell().setStringVal(loadJob.getUserInfo().getQualifiedUser())); + trow.addToColumnValue(new TCell().setStringVal(trackingUrl)); + trow.addToColumnValue(new TCell().setStringVal(loadStatistic.toJson())); + trow.addToColumnValue(new TCell().setStringVal(userIdentity.getQualifiedUser())); return trow; } @@ -225,8 +245,6 @@ private TRow getPendingTaskTVFInfo() { trow.addToColumnValue(new TCell().setStringVal(getJobId() + LABEL_SPLITTER + getTaskId())); trow.addToColumnValue(new TCell().setStringVal(getStatus().name())); trow.addToColumnValue(new TCell().setStringVal(FeConstants.null_string)); - trow.addToColumnValue(new TCell().setStringVal(FeConstants.null_string)); - trow.addToColumnValue(new TCell().setStringVal(FeConstants.null_string)); trow.addToColumnValue(new TCell().setStringVal(TimeUtils.longToTimeString(getCreateTimeMs()))); trow.addToColumnValue(new TCell().setStringVal(FeConstants.null_string)); trow.addToColumnValue(new TCell().setStringVal(FeConstants.null_string)); diff --git a/fe/fe-core/src/main/java/org/apache/doris/job/extensions/mtmv/MTMVJob.java b/fe/fe-core/src/main/java/org/apache/doris/job/extensions/mtmv/MTMVJob.java index c500e6932959d9..669a2806a543d9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/job/extensions/mtmv/MTMVJob.java +++ b/fe/fe-core/src/main/java/org/apache/doris/job/extensions/mtmv/MTMVJob.java @@ -125,7 +125,7 @@ public List createTasks(TaskType taskType, MTMVTaskContext taskContext task.setTaskType(taskType); ArrayList tasks = new ArrayList<>(); tasks.add(task); - super.initTasks(tasks); + super.initTasks(tasks, taskType); return tasks; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/job/manager/JobManager.java b/fe/fe-core/src/main/java/org/apache/doris/job/manager/JobManager.java index 776af152c69d88..814d6b773ad1ef 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/job/manager/JobManager.java +++ b/fe/fe-core/src/main/java/org/apache/doris/job/manager/JobManager.java @@ -17,7 +17,15 @@ package org.apache.doris.job.manager; +import org.apache.doris.analysis.CancelLoadStmt; +import org.apache.doris.analysis.CompoundPredicate; +import org.apache.doris.catalog.Database; import org.apache.doris.catalog.Env; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.CaseSensibility; +import org.apache.doris.common.DdlException; +import org.apache.doris.common.PatternMatcher; +import org.apache.doris.common.PatternMatcherWrapper; import org.apache.doris.common.io.Writable; import org.apache.doris.common.util.LogBuilder; import org.apache.doris.common.util.LogKey; @@ -26,45 +34,86 @@ import org.apache.doris.job.common.JobType; import org.apache.doris.job.common.TaskType; import org.apache.doris.job.exception.JobException; +import org.apache.doris.job.extensions.insert.InsertJob; import org.apache.doris.job.scheduler.JobScheduler; import org.apache.doris.job.task.AbstractTask; +import org.apache.doris.load.loadv2.JobState; +import com.google.common.collect.Lists; import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.StringUtils; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; @Log4j2 public class JobManager, C> implements Writable { - private final ConcurrentHashMap jobMap = new ConcurrentHashMap<>(32); - private JobScheduler jobScheduler; + private JobScheduler jobScheduler; + + // lock for job + // lock is private and must use after db lock + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + + private void readLock() { + lock.readLock().lock(); + } + + private void readUnlock() { + lock.readLock().unlock(); + } + + private void writeLock() { + lock.writeLock().lock(); + } + + private void writeUnlock() { + lock.writeLock().unlock(); + } public void start() { - jobScheduler = new JobScheduler(jobMap); + jobScheduler = new JobScheduler(jobMap); jobScheduler.start(); } + + /** + * get running job + * + * @param jobId id + * @return running job + */ + public T getJob(long jobId) { + return jobMap.get(jobId); + } + public void registerJob(T job) throws JobException { - job.checkJobParams(); - checkJobNameExist(job.getJobName()); - if (jobMap.get(job.getJobId()) != null) { - throw new JobException("job id exist, jobId:" + job.getJobId()); + writeLock(); + try { + job.onRegister(); + job.checkJobParams(); + checkJobNameExist(job.getJobName()); + if (jobMap.get(job.getJobId()) != null) { + throw new JobException("job id exist, jobId:" + job.getJobId()); + } + jobMap.put(job.getJobId(), job); + //check its need to scheduler + jobScheduler.scheduleOneJob(job); + job.logCreateOperation(); + } finally { + writeUnlock(); } - Env.getCurrentEnv().getEditLog().logCreateJob(job); - jobMap.put(job.getJobId(), job); - //check its need to scheduler - jobScheduler.scheduleOneJob(job); } - private void checkJobNameExist(String jobName) throws JobException { if (jobMap.values().stream().anyMatch(a -> a.getJobName().equals(jobName))) { throw new JobException("job name exist, jobName:" + jobName); @@ -72,11 +121,17 @@ private void checkJobNameExist(String jobName) throws JobException { } public void unregisterJob(Long jobId) throws JobException { - checkJobExist(jobId); - jobMap.get(jobId).setJobStatus(JobStatus.STOPPED); - jobMap.get(jobId).cancelAllTasks(); - Env.getCurrentEnv().getEditLog().logDeleteJob(jobMap.get(jobId)); - jobMap.remove(jobId); + writeLock(); + try { + checkJobExist(jobId); + jobMap.get(jobId).setJobStatus(JobStatus.STOPPED); + jobMap.get(jobId).cancelAllTasks(); + jobMap.get(jobId).logFinalOperation(); + jobMap.get(jobId).onUnRegister(); + jobMap.remove(jobId); + } finally { + writeUnlock(); + } } public void unregisterJob(String jobName) throws JobException { @@ -95,7 +150,7 @@ public void unregisterJob(String jobName) throws JobException { public void alterJobStatus(Long jobId, JobStatus status) throws JobException { checkJobExist(jobId); jobMap.get(jobId).updateJobStatus(status); - Env.getCurrentEnv().getEditLog().logUpdateJob(jobMap.get(jobId)); + jobMap.get(jobId).logUpdateOperation(); } public void alterJobStatus(String jobName, JobStatus jobStatus) throws JobException { @@ -169,13 +224,12 @@ public void triggerJob(long jobId, C context) throws JobException { jobScheduler.schedulerInstantJob(jobMap.get(jobId), TaskType.MANUAL, context); } - public void replayCreateJob(T job) { + public void replayCreateJob(T job) throws JobException { if (jobMap.containsKey(job.getJobId())) { return; } jobMap.putIfAbsent(job.getJobId(), job); - log.info(new LogBuilder(LogKey.SCHEDULER_JOB, job.getJobId()) - .add("msg", "replay create scheduler job").build()); + job.onReplayCreate(); } /** @@ -187,13 +241,12 @@ public void replayUpdateJob(T job) { .add("msg", "replay update scheduler job").build()); } - public void replayDeleteJob(T job) { - if (null == jobMap.get(job.getJobId())) { + public void replayEndJob(T replayJob) throws JobException { + T job = jobMap.get(replayJob.getJobId()); + if (null == job) { return; } - jobMap.remove(job.getJobId()); - log.info(new LogBuilder(LogKey.SCHEDULER_JOB, job.getJobId()) - .add("msg", "replay delete scheduler job").build()); + job.onReplayEnd(replayJob); } public void cancelTaskById(String jobName, Long taskId) throws JobException { @@ -236,4 +289,135 @@ public T getJob(Long jobId) { return jobMap.get(jobId); } + + /** + * get load info by db + * + * @param dbId db id + * @param dbName db name + * @param labelValue label name + * @param accurateMatch accurate match + * @param jobState state + * @return load infos + * @throws AnalysisException ex + */ + public List> getLoadJobInfosByDb(long dbId, String dbName, + String labelValue, + boolean accurateMatch, + JobState jobState) throws AnalysisException { + LinkedList> loadJobInfos = new LinkedList<>(); + if (!Env.getCurrentEnv().getLabelProcessor().existJobs(dbId)) { + return loadJobInfos; + } + readLock(); + try { + List loadJobList = Env.getCurrentEnv().getLabelProcessor() + .filterJobs(dbId, labelValue, accurateMatch); + // check state + for (InsertJob loadJob : loadJobList) { + try { + if (jobState != null && !validState(jobState, loadJob)) { + continue; + } + // add load job info, convert String list to Comparable list + loadJobInfos.add(new ArrayList<>(loadJob.getShowInfo())); + } catch (RuntimeException e) { + // ignore this load job + log.warn("get load job info failed. job id: {}", loadJob.getJobId(), e); + } + } + return loadJobInfos; + } finally { + readUnlock(); + } + } + + private static boolean validState(JobState jobState, InsertJob loadJob) { + JobStatus status = loadJob.getJobStatus(); + switch (status) { + case RUNNING: + return jobState == JobState.PENDING || jobState == JobState.ETL + || jobState == JobState.LOADING || jobState == JobState.COMMITTED; + case STOPPED: + return jobState == JobState.CANCELLED; + case FINISHED: + return jobState == JobState.FINISHED; + default: + return false; + } + } + + public void cancelLoadJob(CancelLoadStmt cs) + throws JobException, AnalysisException, DdlException { + String dbName = cs.getDbName(); + String label = cs.getLabel(); + String state = cs.getState(); + CompoundPredicate.Operator operator = cs.getOperator(); + Database db = Env.getCurrentInternalCatalog().getDbOrDdlException(dbName); + // List of load jobs waiting to be cancelled + List unfinishedLoadJob; + readLock(); + try { + List loadJobs = Env.getCurrentEnv().getLabelProcessor().getJobs(db); + List matchLoadJobs = Lists.newArrayList(); + addNeedCancelLoadJob(label, state, operator, loadJobs, matchLoadJobs); + if (matchLoadJobs.isEmpty()) { + throw new JobException("Load job does not exist"); + } + // check state here + unfinishedLoadJob = + matchLoadJobs.stream().filter(InsertJob::isRunning) + .collect(Collectors.toList()); + if (unfinishedLoadJob.isEmpty()) { + throw new JobException("There is no uncompleted job"); + } + } finally { + readUnlock(); + } + for (InsertJob loadJob : unfinishedLoadJob) { + try { + unregisterJob(loadJob.getJobId()); + } catch (JobException e) { + log.warn("Fail to cancel job, its label: {}", loadJob.getLabelName()); + } + } + } + + private static void addNeedCancelLoadJob(String label, String state, + CompoundPredicate.Operator operator, List loadJobs, + List matchLoadJobs) + throws AnalysisException { + PatternMatcher matcher = PatternMatcherWrapper.createMysqlPattern(label, + CaseSensibility.LABEL.getCaseSensibility()); + matchLoadJobs.addAll( + loadJobs.stream() + .filter(job -> !job.isCancelled()) + .filter(job -> { + if (operator != null) { + // compound + boolean labelFilter = + label.contains("%") ? matcher.match(job.getLabelName()) + : job.getLabelName().equalsIgnoreCase(label); + boolean stateFilter = job.getJobStatus().name().equalsIgnoreCase(state); + return CompoundPredicate.Operator.AND.equals(operator) ? labelFilter && stateFilter : + labelFilter || stateFilter; + } + if (StringUtils.isNotEmpty(label)) { + return label.contains("%") ? matcher.match(job.getLabelName()) + : job.getLabelName().equalsIgnoreCase(label); + } + if (StringUtils.isNotEmpty(state)) { + return job.getJobStatus().name().equalsIgnoreCase(state); + } + return false; + }).collect(Collectors.toList()) + ); + } + // public void updateJobProgress(Long jobId, Long beId, TUniqueId loadId, TUniqueId fragmentId, long scannedRows, + // long scannedBytes, boolean isDone) { + // AbstractJob job = jobMap.get(jobId); + // if (job != null) { + // job.updateLoadingStatus(beId, loadId, fragmentId, scannedRows, scannedBytes, isDone); + // } + // } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/job/task/AbstractTask.java b/fe/fe-core/src/main/java/org/apache/doris/job/task/AbstractTask.java index 7327183e95eda2..71f6ff1c4f7eeb 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/job/task/AbstractTask.java +++ b/fe/fe-core/src/main/java/org/apache/doris/job/task/AbstractTask.java @@ -27,6 +27,7 @@ import com.google.gson.annotations.SerializedName; import lombok.Data; import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.RandomUtils; @Data @Log4j2 @@ -52,6 +53,15 @@ public abstract class AbstractTask implements Task { @SerializedName(value = "emg") private String errMsg; + public AbstractTask() { + taskId = getNextTaskId(); + } + + private static long getNextTaskId() { + // do not use Env.getNextId(), just generate id without logging + return System.nanoTime() + RandomUtils.nextInt(); + } + @Override public void onFail(String msg) throws JobException { status = TaskStatus.FAILED; diff --git a/fe/fe-core/src/main/java/org/apache/doris/load/ExportJob.java b/fe/fe-core/src/main/java/org/apache/doris/load/ExportJob.java index 996c3ccbb32f6f..fc2f6fca9c5cf6 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/load/ExportJob.java +++ b/fe/fe-core/src/main/java/org/apache/doris/load/ExportJob.java @@ -73,6 +73,7 @@ import org.apache.doris.qe.OriginStatement; import org.apache.doris.qe.SessionVariable; import org.apache.doris.scheduler.exception.JobException; +import org.apache.doris.scheduler.executor.TransientTaskExecutor; import org.apache.doris.thrift.TNetworkAddress; import com.google.common.base.Preconditions; @@ -198,7 +199,7 @@ public class ExportJob implements Writable { private List jobExecutorList; - private ConcurrentHashMap taskIdToExecutor = new ConcurrentHashMap<>(); + private ConcurrentHashMap taskIdToExecutor = new ConcurrentHashMap<>(); private Integer finishedTaskCount = 0; private List> allOutfileInfo = Lists.newArrayList(); @@ -380,6 +381,10 @@ private StatementBase generateLogicalPlanAdapter(LogicalPlan outfileLogicalPlan) return statementBase; } + public List getTaskExecutors() { + return jobExecutorList; + } + private void generateExportJobExecutor() { jobExecutorList = Lists.newArrayList(); for (List selectStmts : selectStmtListPerParallel) { @@ -607,7 +612,7 @@ private void cancelExportTask(ExportFailMsg.CancelType type, String msg) throws // we need cancel all task taskIdToExecutor.keySet().forEach(id -> { try { - Env.getCurrentEnv().getExportTaskRegister().cancelTask(id); + Env.getCurrentEnv().getTransientTaskManager().cancelMemoryTask(id); } catch (JobException e) { LOG.warn("cancel export task {} exception: {}", id, e); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/load/ExportMgr.java b/fe/fe-core/src/main/java/org/apache/doris/load/ExportMgr.java index ae7a175b896a63..90be7f9f10e871 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/load/ExportMgr.java +++ b/fe/fe-core/src/main/java/org/apache/doris/load/ExportMgr.java @@ -59,23 +59,26 @@ public class ExportMgr { private static final Logger LOG = LogManager.getLogger(ExportJob.class); - - // lock for export job - // lock is private and must use after db lock - private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); - private Map exportIdToJob = Maps.newHashMap(); // exportJobId to exportJob // dbid ->