This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch dev-1.1.2
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/dev-1.1.2 by this push:
new 87c2bdd972 [fix](grouping sets) Fix the query result error caused by
the grouping sets statement grouping as an expression (#11433)
87c2bdd972 is described below
commit 87c2bdd97265bcef5b6456c27f6109f3491f1b19
Author: luozenglin <[email protected]>
AuthorDate: Wed Aug 3 08:47:59 2022 +0800
[fix](grouping sets) Fix the query result error caused by the grouping sets
statement grouping as an expression (#11433)
---
be/src/exec/repeat_node.cpp | 125 +++++++++---------
be/src/exec/repeat_node.h | 10 +-
be/src/vec/exec/vrepeat_node.cpp | 69 +++++-----
be/src/vec/exec/vrepeat_node.h | 12 +-
.../org/apache/doris/analysis/GroupingInfo.java | 118 +++++++++++++++--
.../java/org/apache/doris/analysis/SelectStmt.java | 9 +-
.../java/org/apache/doris/analysis/SlotRef.java | 2 +-
.../org/apache/doris/analysis/VirtualSlotRef.java | 10 ++
.../java/org/apache/doris/planner/RepeatNode.java | 94 ++++----------
.../org/apache/doris/planner/QueryPlanTest.java | 2 +-
.../org/apache/doris/planner/RepeatNodeTest.java | 140 +++++++++++----------
gensrc/thrift/PlanNodes.thrift | 1 +
12 files changed, 330 insertions(+), 262 deletions(-)
diff --git a/be/src/exec/repeat_node.cpp b/be/src/exec/repeat_node.cpp
index 78d937edd2..80520e8a3b 100644
--- a/be/src/exec/repeat_node.cpp
+++ b/be/src/exec/repeat_node.cpp
@@ -33,7 +33,7 @@ RepeatNode::RepeatNode(ObjectPool* pool, const TPlanNode&
tnode, const Descripto
_repeat_id_list(tnode.repeat_node.repeat_id_list),
_grouping_list(tnode.repeat_node.grouping_list),
_output_tuple_id(tnode.repeat_node.output_tuple_id),
- _tuple_desc(nullptr),
+ _output_tuple_desc(nullptr),
_child_row_batch(nullptr),
_child_eos(false),
_repeat_id_idx(0),
@@ -41,21 +41,39 @@ RepeatNode::RepeatNode(ObjectPool* pool, const TPlanNode&
tnode, const Descripto
RepeatNode::~RepeatNode() {}
+Status RepeatNode::init(const TPlanNode& tnode, RuntimeState* state) {
+ RETURN_IF_ERROR(ExecNode::init(tnode, state));
+ const RowDescriptor& row_desc = child(0)->row_desc();
+ RETURN_IF_ERROR(Expr::create(tnode.repeat_node.exprs, row_desc, state,
&_exprs, mem_tracker()));
+ DCHECK(!_exprs.empty());
+ return Status::OK();
+}
+
Status RepeatNode::prepare(RuntimeState* state) {
SCOPED_TIMER(_runtime_profile->total_time_counter());
RETURN_IF_ERROR(ExecNode::prepare(state));
_runtime_state = state;
- _tuple_desc = state->desc_tbl().get_tuple_descriptor(_output_tuple_id);
- if (_tuple_desc == nullptr) {
+ _output_tuple_desc =
state->desc_tbl().get_tuple_descriptor(_output_tuple_id);
+ if (_output_tuple_desc == nullptr) {
return Status::InternalError("Failed to get tuple descriptor.");
}
+ for (int i = 0; i < _exprs.size(); i++) {
+ ExprContext* context = _pool->add(new ExprContext(_exprs[i]));
+ RETURN_IF_ERROR(context->prepare(state, child(0)->row_desc(),
mem_tracker()));
+ _expr_evals.push_back(context);
+ }
+ DCHECK_EQ(_exprs.size(), _expr_evals.size());
return Status::OK();
}
Status RepeatNode::open(RuntimeState* state) {
SCOPED_TIMER(_runtime_profile->total_time_counter());
RETURN_IF_ERROR(ExecNode::open(state));
+
+ for (int i = 0; i < _expr_evals.size(); i++) {
+ RETURN_IF_ERROR(_expr_evals[i]->open(state));
+ }
RETURN_IF_CANCELLED(state);
RETURN_IF_ERROR(child(0)->open(state));
return Status::OK();
@@ -74,66 +92,14 @@ Status RepeatNode::get_repeated_batch(RowBatch*
child_row_batch, int repeat_id_i
// Fill all slots according to child
MemPool* tuple_pool = row_batch->tuple_data_pool();
- const std::vector<TupleDescriptor*>& src_tuple_descs =
- child_row_batch->row_desc().tuple_descriptors();
- const std::vector<TupleDescriptor*>& dst_tuple_descs =
- row_batch->row_desc().tuple_descriptors();
- std::vector<Tuple*> dst_tuples(src_tuple_descs.size(), nullptr);
- for (int i = 0; i < child_row_batch->num_rows(); ++i) {
+ Tuple* tuple = nullptr;
+ for (int row_index = 0; row_index < child_row_batch->num_rows();
++row_index) {
int row_idx = row_batch->add_row();
TupleRow* dst_row = row_batch->get_row(row_idx);
- TupleRow* src_row = child_row_batch->get_row(i);
-
- auto src_it = src_tuple_descs.begin();
- auto dst_it = dst_tuple_descs.begin();
- for (int j = 0; src_it != src_tuple_descs.end() && dst_it !=
dst_tuple_descs.end();
- ++src_it, ++dst_it, ++j) {
- Tuple* src_tuple = src_row->get_tuple(j);
- if (src_tuple == nullptr) {
- dst_row->set_tuple(j, nullptr);
- continue;
- }
-
- if (dst_tuples[j] == nullptr) {
- int size = row_batch->capacity() * (*dst_it)->byte_size();
- void* tuple_buffer = tuple_pool->allocate(size);
- if (tuple_buffer == nullptr) {
- return Status::InternalError("Allocate memory for row
batch failed.");
- }
- dst_tuples[j] = reinterpret_cast<Tuple*>(tuple_buffer);
- } else {
- char* new_tuple = reinterpret_cast<char*>(dst_tuples[j]);
- new_tuple += (*dst_it)->byte_size();
- dst_tuples[j] = reinterpret_cast<Tuple*>(new_tuple);
- }
- dst_row->set_tuple(j, dst_tuples[j]);
- memset(dst_tuples[j], 0, (*dst_it)->num_null_bytes());
- src_tuple->deep_copy(dst_tuples[j], **dst_it, tuple_pool);
- for (int k = 0; k < (*src_it)->slots().size(); k++) {
- SlotDescriptor* src_slot_desc = (*src_it)->slots()[k];
- SlotDescriptor* dst_slot_desc = (*dst_it)->slots()[k];
- DCHECK_EQ(src_slot_desc->type().type,
dst_slot_desc->type().type);
- DCHECK_EQ(src_slot_desc->col_name(),
dst_slot_desc->col_name());
- // set null base on repeated list
- if (_all_slot_ids.find(src_slot_desc->id()) !=
_all_slot_ids.end()) {
- std::set<SlotId>& repeat_ids =
_slot_id_set_list[repeat_id_idx];
- if (repeat_ids.find(src_slot_desc->id()) ==
repeat_ids.end()) {
-
dst_tuples[j]->set_null(dst_slot_desc->null_indicator_offset());
- continue;
- }
- }
- }
- }
- row_batch->commit_last_row();
- }
- Tuple* tuple = nullptr;
- // Fill grouping ID to tuple
- for (int i = 0; i < child_row_batch->num_rows(); ++i) {
- int row_idx = i;
- TupleRow* row = row_batch->get_row(row_idx);
+ TupleRow* src_row = child_row_batch->get_row(row_index);
- if (tuple == nullptr) {
- int size = row_batch->capacity() * _tuple_desc->byte_size();
+ if (UNLIKELY(tuple == nullptr)) {
+ int size = row_batch->capacity() * _output_tuple_desc->byte_size();
void* tuple_buffer = tuple_pool->allocate(size);
if (tuple_buffer == nullptr) {
return Status::InternalError("Allocate memory for row batch
failed.");
@@ -141,21 +107,38 @@ Status RepeatNode::get_repeated_batch(RowBatch*
child_row_batch, int repeat_id_i
tuple = reinterpret_cast<Tuple*>(tuple_buffer);
} else {
char* new_tuple = reinterpret_cast<char*>(tuple);
- new_tuple += _tuple_desc->byte_size();
+ new_tuple += _output_tuple_desc->byte_size();
tuple = reinterpret_cast<Tuple*>(new_tuple);
}
+ dst_row->set_tuple(0, tuple);
+ memset(tuple, 0, _output_tuple_desc->num_null_bytes());
+
+ int slot_index = 0;
+ for (; slot_index < _expr_evals.size(); ++slot_index) {
+ const SlotDescriptor* slot_desc =
_output_tuple_desc->slots()[slot_index];
+ // set null base on repeated list
+ if (_all_slot_ids.find(slot_desc->id()) != _all_slot_ids.end()) {
+ std::set<SlotId>& repeat_ids =
_slot_id_set_list[repeat_id_idx];
+ if (repeat_ids.find(slot_desc->id()) == repeat_ids.end()) {
+ tuple->set_null(slot_desc->null_indicator_offset());
+ continue;
+ }
+ }
- row->set_tuple(src_tuple_descs.size(), tuple);
- memset(tuple, 0, _tuple_desc->num_null_bytes());
+ void* val = _expr_evals[slot_index]->get_value(src_row);
+ tuple->set_not_null(slot_desc->null_indicator_offset());
+ RawValue::write(val, tuple, slot_desc, tuple_pool);
+ }
- for (size_t slot_idx = 0; slot_idx < _grouping_list.size();
++slot_idx) {
- int64_t val = _grouping_list[slot_idx][repeat_id_idx];
- DCHECK_LT(slot_idx, _tuple_desc->slots().size())
- << "TupleDescriptor: " << _tuple_desc->debug_string();
- const SlotDescriptor* slot_desc = _tuple_desc->slots()[slot_idx];
+ DCHECK_EQ(slot_index + _grouping_list.size(),
_output_tuple_desc->slots().size());
+ for (int i = 0; slot_index < _output_tuple_desc->slots().size(); ++i,
++slot_index) {
+ const SlotDescriptor* slot_desc =
_output_tuple_desc->slots()[slot_index];
tuple->set_not_null(slot_desc->null_indicator_offset());
+
+ int64_t val = _grouping_list[i][repeat_id_idx];
RawValue::write(&val, tuple, slot_desc, tuple_pool);
}
+ row_batch->commit_last_row();
}
return Status::OK();
@@ -204,6 +187,11 @@ Status RepeatNode::close(RuntimeState* state) {
return Status::OK();
}
_child_row_batch.reset(nullptr);
+ for (int i = 0; i < _expr_evals.size(); i++) {
+ _expr_evals[i]->close(state);
+ }
+ _expr_evals.clear();
+ Expr::close(_exprs);
RETURN_IF_ERROR(child(0)->close(state));
return ExecNode::close(state);
}
@@ -213,6 +201,7 @@ void RepeatNode::debug_string(int indentation_level,
std::stringstream* out) con
*out << "RepeatNode(";
*out << "repeat pattern: [" << JoinElements(_repeat_id_list, ",") << "]\n";
*out << "add " << _grouping_list.size() << " columns. \n";
+ *out << "_exprs: " << Expr::debug_string(_exprs);
*out << "added column values: ";
for (const std::vector<int64_t>& v : _grouping_list) {
*out << "[" << JoinElements(v, ",") << "] ";
diff --git a/be/src/exec/repeat_node.h b/be/src/exec/repeat_node.h
index d9dce75278..9c43a33c86 100644
--- a/be/src/exec/repeat_node.h
+++ b/be/src/exec/repeat_node.h
@@ -18,6 +18,8 @@
#pragma once
#include "exec/exec_node.h"
+#include "exprs/expr.h"
+#include "exprs/expr_context.h"
namespace doris {
@@ -32,6 +34,7 @@ public:
RepeatNode(ObjectPool* pool, const TPlanNode& tnode, const DescriptorTbl&
descs);
~RepeatNode();
+ virtual Status init(const TPlanNode& tnode, RuntimeState* state = nullptr)
override;
virtual Status prepare(RuntimeState* state) override;
virtual Status open(RuntimeState* state) override;
virtual Status get_next(RuntimeState* state, RowBatch* row_batch, bool*
eos) override;
@@ -52,12 +55,17 @@ protected:
std::vector<std::vector<int64_t>> _grouping_list;
// Tuple id used for output, it has new slots.
TupleId _output_tuple_id;
- const TupleDescriptor* _tuple_desc;
+ const TupleDescriptor* _output_tuple_desc;
std::unique_ptr<RowBatch> _child_row_batch;
bool _child_eos;
int _repeat_id_idx;
RuntimeState* _runtime_state;
+
+ // Exprs used to evaluate input rows
+ std::vector<Expr*> _exprs;
+
+ std::vector<ExprContext*> _expr_evals;
};
} // namespace doris
diff --git a/be/src/vec/exec/vrepeat_node.cpp b/be/src/vec/exec/vrepeat_node.cpp
index a6e36a25cb..be0394d2bb 100644
--- a/be/src/vec/exec/vrepeat_node.cpp
+++ b/be/src/vec/exec/vrepeat_node.cpp
@@ -17,47 +17,29 @@
#include "vec/exec/vrepeat_node.h"
-#include "exprs/expr.h"
#include "gutil/strings/join.h"
#include "runtime/runtime_state.h"
#include "util/runtime_profile.h"
+#include "vec/exprs/vexpr.h"
namespace doris::vectorized {
VRepeatNode::VRepeatNode(ObjectPool* pool, const TPlanNode& tnode, const
DescriptorTbl& descs)
- : RepeatNode(pool, tnode, descs),
- _child_block(nullptr),
- _virtual_tuple_id(tnode.repeat_node.output_tuple_id) {}
+ : RepeatNode(pool, tnode, descs) {}
+
+Status VRepeatNode::init(const TPlanNode& tnode, RuntimeState* state) {
+ RETURN_IF_ERROR(ExecNode::init(tnode, state));
+ RETURN_IF_ERROR(VExpr::create_expr_trees(_pool, tnode.repeat_node.exprs,
&_expr_ctxs));
+ return Status::OK();
+}
Status VRepeatNode::prepare(RuntimeState* state) {
VLOG_CRITICAL << "VRepeatNode::prepare";
SCOPED_TIMER(_runtime_profile->total_time_counter());
RETURN_IF_ERROR(RepeatNode::prepare(state));
+ RETURN_IF_ERROR(VExpr::prepare(_expr_ctxs, state, child(0)->row_desc(),
expr_mem_tracker()));
- // get current all output slots
- for (const auto& tuple_desc : this->row_desc().tuple_descriptors()) {
- for (const auto& slot_desc : tuple_desc->slots()) {
- _output_slots.push_back(slot_desc);
- }
- }
-
- // get all input slots
- for (const auto& child_tuple_desc :
child(0)->row_desc().tuple_descriptors()) {
- for (const auto& child_slot_desc : child_tuple_desc->slots()) {
- _child_slots.push_back(child_slot_desc);
- }
- }
-
- _virtual_tuple_desc =
state->desc_tbl().get_tuple_descriptor(_virtual_tuple_id);
- if (_virtual_tuple_desc == NULL) {
- return Status::InternalError("Failed to get virtual tuple
descriptor.");
- }
-
- std::stringstream ss;
- ss << "The output slots size " << _output_slots.size()
- << " is not equal to the sum of child_slots_size " <<
_child_slots.size()
- << ",virtual_slots_size " << _virtual_tuple_desc->slots().size();
- if (_output_slots.size() != (_child_slots.size() +
_virtual_tuple_desc->slots().size())) {
- return Status::InternalError(ss.str());
+ for (const auto& slot_desc : _output_tuple_desc->slots()) {
+ _output_slots.push_back(slot_desc);
}
_child_block.reset(new Block());
@@ -69,6 +51,7 @@ Status VRepeatNode::open(RuntimeState* state) {
VLOG_CRITICAL << "VRepeatNode::open";
SCOPED_TIMER(_runtime_profile->total_time_counter());
RETURN_IF_ERROR(RepeatNode::open(state));
+ RETURN_IF_ERROR(VExpr::open(_expr_ctxs, state));
return Status::OK();
}
@@ -80,7 +63,6 @@ Status VRepeatNode::get_repeated_block(Block* child_block,
int repeat_id_idx, Bl
size_t child_column_size = child_block->columns();
size_t column_size = _output_slots.size();
bool mem_reuse = output_block->mem_reuse();
- DCHECK_EQ(child_column_size, _child_slots.size());
DCHECK_LT(child_column_size, column_size);
std::vector<vectorized::MutableColumnPtr> columns(column_size);
for (size_t i = 0; i < column_size; i++) {
@@ -101,9 +83,6 @@ Status VRepeatNode::get_repeated_block(Block* child_block,
int repeat_id_idx, Bl
for (size_t i = 0; i < child_column_size; i++) {
const ColumnWithTypeAndName& src_column =
child_block->get_by_position(i);
- DCHECK_EQ(_child_slots[i]->type().type,
_output_slots[cur_col]->type().type);
- DCHECK_EQ(_child_slots[i]->col_name(),
_output_slots[cur_col]->col_name());
-
std::set<SlotId>& repeat_ids = _slot_id_set_list[repeat_id_idx];
bool is_repeat_slot =
_all_slot_ids.find(_output_slots[cur_col]->id()) !=
_all_slot_ids.end();
@@ -137,8 +116,8 @@ Status VRepeatNode::get_repeated_block(Block* child_block,
int repeat_id_idx, Bl
// Fill grouping ID to block
for (auto slot_idx = 0; slot_idx < _grouping_list.size(); slot_idx++) {
- DCHECK_LT(slot_idx, _virtual_tuple_desc->slots().size());
- const SlotDescriptor* _virtual_slot_desc =
_virtual_tuple_desc->slots()[slot_idx];
+ DCHECK_LT(slot_idx, _output_tuple_desc->slots().size());
+ const SlotDescriptor* _virtual_slot_desc =
_output_tuple_desc->slots()[cur_col];
DCHECK_EQ(_virtual_slot_desc->type().type,
_output_slots[cur_col]->type().type);
DCHECK_EQ(_virtual_slot_desc->col_name(),
_output_slots[cur_col]->col_name());
int64_t val = _grouping_list[slot_idx][repeat_id_idx];
@@ -195,15 +174,29 @@ Status VRepeatNode::get_next(RuntimeState* state, Block*
block, bool* eos) {
*eos = true;
return Status::OK();
}
+
+ DCHECK(!_expr_ctxs.empty());
+ _intermediate_block.reset(new Block());
+ for (auto vexpr_ctx : _expr_ctxs) {
+ int result_column_id = -1;
+ RETURN_IF_ERROR(vexpr_ctx->execute(_child_block.get(),
&result_column_id));
+ DCHECK(result_column_id != -1);
+ _child_block->get_by_position(result_column_id).column =
+ _child_block->get_by_position(result_column_id)
+ .column->convert_to_full_column_if_const();
+
_intermediate_block->insert(_child_block->get_by_position(result_column_id));
+ }
+ DCHECK_EQ(_expr_ctxs.size(), _intermediate_block->columns());
}
- RETURN_IF_ERROR(get_repeated_block(_child_block.get(), _repeat_id_idx,
block));
+ RETURN_IF_ERROR(get_repeated_block(_intermediate_block.get(),
_repeat_id_idx, block));
_repeat_id_idx++;
int size = _repeat_id_list.size();
if (_repeat_id_idx >= size) {
- release_block_memory(*_child_block.get());
+ _intermediate_block->clear();
+ release_block_memory(*_child_block);
_repeat_id_idx = 0;
}
@@ -218,7 +211,7 @@ Status VRepeatNode::close(RuntimeState* state) {
if (is_closed()) {
return Status::OK();
}
- release_block_memory(*_child_block.get());
+ VExpr::close(_expr_ctxs, state);
RETURN_IF_ERROR(child(0)->close(state));
return ExecNode::close(state);
}
diff --git a/be/src/vec/exec/vrepeat_node.h b/be/src/vec/exec/vrepeat_node.h
index 26efa9a1fe..cc857dc33a 100644
--- a/be/src/vec/exec/vrepeat_node.h
+++ b/be/src/vec/exec/vrepeat_node.h
@@ -28,11 +28,14 @@ class RuntimeState;
class Status;
namespace vectorized {
+class VExprContext;
+
class VRepeatNode : public RepeatNode {
public:
VRepeatNode(ObjectPool* pool, const TPlanNode& tnode, const DescriptorTbl&
descs);
~VRepeatNode() override = default;
+ virtual Status init(const TPlanNode& tnode, RuntimeState* state = nullptr)
override;
virtual Status prepare(RuntimeState* state) override;
virtual Status open(RuntimeState* state) override;
virtual Status get_next(RuntimeState* state, Block* block, bool* eos)
override;
@@ -45,13 +48,12 @@ private:
using RepeatNode::get_next;
Status get_repeated_block(Block* child_block, int repeat_id_idx, Block*
output_block);
- std::unique_ptr<Block> _child_block;
- std::vector<SlotDescriptor*> _child_slots;
+ std::unique_ptr<Block> _child_block {};
+ std::unique_ptr<Block> _intermediate_block {};
+
std::vector<SlotDescriptor*> _output_slots;
- // _virtual_tuple_id id used for GROUPING_ID().
- TupleId _virtual_tuple_id;
- const TupleDescriptor* _virtual_tuple_desc;
+ std::vector<VExprContext*> _expr_ctxs;
};
} // namespace vectorized
} // namespace doris
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/analysis/GroupingInfo.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/GroupingInfo.java
index 7968fb305d..e153c5f929 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/GroupingInfo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/GroupingInfo.java
@@ -22,6 +22,7 @@ import org.apache.doris.catalog.Type;
import org.apache.doris.common.AnalysisException;
import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.BitSet;
@@ -36,47 +37,76 @@ public class GroupingInfo {
public static final String GROUPING_PREFIX = "GROUPING_PREFIX_";
private VirtualSlotRef groupingIDSlot;
private TupleDescriptor virtualTuple;
- private Set<VirtualSlotRef> groupingSlots;
+ private TupleDescriptor outputTupleDesc;
+ private ExprSubstitutionMap outputTupleSmap;
+ private List<SlotDescriptor> groupingSlotDescList;
+ private Set<VirtualSlotRef> virtualSlotRefs;
private List<BitSet> groupingIdList;
private GroupByClause.GroupingType groupingType;
private BitSet bitSetAll;
+ private List<Expr> preRepeatExprs;
+
public GroupingInfo(Analyzer analyzer, GroupByClause groupByClause) throws
AnalysisException {
this.groupingType = groupByClause.getGroupingType();
- groupingSlots = new LinkedHashSet<>();
+ virtualSlotRefs = new LinkedHashSet<>();
virtualTuple =
analyzer.getDescTbl().createTupleDescriptor("VIRTUAL_TUPLE");
groupingIDSlot = new VirtualSlotRef(COL_GROUPING_ID, Type.BIGINT,
virtualTuple, new ArrayList<>());
groupingIDSlot.analyze(analyzer);
- groupingSlots.add(groupingIDSlot);
+ virtualSlotRefs.add(groupingIDSlot);
+
+ outputTupleDesc =
analyzer.getDescTbl().createTupleDescriptor("repeat-tuple");
+ outputTupleSmap = new ExprSubstitutionMap();
+ groupingSlotDescList = Lists.newArrayList();
+ preRepeatExprs = Lists.newArrayList();
}
- public Set<VirtualSlotRef> getGroupingSlots() {
- return groupingSlots;
+ public Set<VirtualSlotRef> getVirtualSlotRefs() {
+ return virtualSlotRefs;
}
public TupleDescriptor getVirtualTuple() {
return virtualTuple;
}
+ public TupleDescriptor getOutputTupleDesc() {
+ return outputTupleDesc;
+ }
+
+ public ExprSubstitutionMap getOutputTupleSmap() {
+ return outputTupleSmap;
+ }
+
+ public List<SlotDescriptor> getGroupingSlotDescList() {
+ return groupingSlotDescList;
+ }
+
public List<BitSet> getGroupingIdList() {
return groupingIdList;
}
+ public List<Expr> getPreRepeatExprs() {
+ return preRepeatExprs;
+ }
+
+ public void substitutePreRepeatExprs(ExprSubstitutionMap smap, Analyzer
analyzer) {
+ preRepeatExprs = Expr.substituteList(preRepeatExprs, smap, analyzer,
true);
+ }
+
// generate virtual slots for grouping or grouping_id functions
public VirtualSlotRef addGroupingSlots(List<Expr> realSlots, Analyzer
analyzer) throws AnalysisException {
- String colName = realSlots.stream().map(expr ->
expr.toSql()).collect(Collectors.joining(
- "_"));
+ String colName = realSlots.stream().map(expr ->
expr.toSql()).collect(Collectors.joining("_"));
colName = GROUPING_PREFIX + colName;
VirtualSlotRef virtualSlot = new VirtualSlotRef(colName, Type.BIGINT,
virtualTuple, realSlots);
virtualSlot.analyze(analyzer);
- if (groupingSlots.contains(virtualSlot)) {
- for (VirtualSlotRef vs : groupingSlots) {
+ if (virtualSlotRefs.contains(virtualSlot)) {
+ for (VirtualSlotRef vs : virtualSlotRefs) {
if (vs.equals(virtualSlot)) {
return vs;
}
}
}
- groupingSlots.add(virtualSlot);
+ virtualSlotRefs.add(virtualSlot);
return virtualSlot;
}
@@ -124,13 +154,13 @@ public class GroupingInfo {
default:
Preconditions.checkState(false);
}
- groupingExprs.addAll(groupingSlots);
+ groupingExprs.addAll(virtualSlotRefs);
}
// generate grouping function's value
public List<List<Long>> genGroupingList(ArrayList<Expr> groupingExprs)
throws AnalysisException {
List<List<Long>> groupingList = new ArrayList<>();
- for (SlotRef slot : groupingSlots) {
+ for (SlotRef slot : virtualSlotRefs) {
List<Long> glist = new ArrayList<>();
for (BitSet bitSet : groupingIdList) {
long l = 0L;
@@ -150,7 +180,7 @@ public class GroupingInfo {
int slotSize = ((VirtualSlotRef)
slot).getRealSlots().size();
for (int i = 0; i < slotSize; ++i) {
int j = groupingExprs.indexOf(((VirtualSlotRef)
slot).getRealSlots().get(i));
- if (j < 0 || j >= bitSet.size()) {
+ if (j < 0 || j >= bitSet.size()) {
throw new AnalysisException("Column " +
((VirtualSlotRef) slot).getRealColumnName()
+ " in GROUP_ID() does not exist in GROUP
BY clause.");
}
@@ -164,6 +194,68 @@ public class GroupingInfo {
return groupingList;
}
+ public void genOutputTupleDescAndSMap(Analyzer analyzer, ArrayList<Expr>
groupingAndVirtualSlotExprs,
+ List<FunctionCallExpr> aggExprs) {
+ List<Expr> groupingExprs = Lists.newArrayList();
+ List<Expr> virtualSlotExprs = Lists.newArrayList();
+ for (Expr expr : groupingAndVirtualSlotExprs) {
+ if (expr instanceof VirtualSlotRef) {
+ virtualSlotExprs.add(expr);
+ } else {
+ groupingExprs.add(expr);
+ }
+ }
+ for (Expr expr : groupingExprs) {
+ SlotDescriptor slotDesc = addSlot(analyzer, expr);
+ slotDesc.setIsNullable(true);
+ groupingSlotDescList.add(slotDesc);
+ preRepeatExprs.add(expr);
+ // register equivalence between grouping slot and grouping expr;
+ // do this only when the grouping expr isn't a constant, otherwise
+ // it'll simply show up as a gratuitous HAVING predicate
+ // (which would actually be incorrect if the constant happens to
be NULL)
+ if (!expr.isConstant()) {
+ analyzer.createAuxEquivPredicate(new SlotRef(slotDesc),
expr.clone());
+ }
+ }
+ List<SlotRef> aggSlot = Lists.newArrayList();
+ aggExprs.forEach(expr -> aggSlot.addAll(getSlotRefChildren(expr)));
+ for (SlotRef slotRef : aggSlot) {
+ addSlot(analyzer, slotRef);
+ preRepeatExprs.add(slotRef);
+ }
+ for (Expr expr : virtualSlotExprs) {
+ addSlot(analyzer, expr);
+ }
+ }
+
+ private SlotDescriptor addSlot(Analyzer analyzer, Expr expr) {
+ SlotDescriptor slotDesc = analyzer.addSlotDescriptor(outputTupleDesc);
+ slotDesc.initFromExpr(expr);
+ slotDesc.setIsMaterialized(true);
+ if (expr instanceof SlotRef) {
+ slotDesc.setColumn(((SlotRef) expr).getColumn());
+ }
+ if (expr instanceof VirtualSlotRef) {
+ outputTupleSmap.put(expr.clone(), new VirtualSlotRef(slotDesc));
+ } else {
+ outputTupleSmap.put(expr.clone(), new SlotRef(slotDesc));
+ }
+ return slotDesc;
+ }
+
+ private List<SlotRef> getSlotRefChildren(Expr root) {
+ List<SlotRef> result = new ArrayList<>();
+ for (Expr child : root.getChildren()) {
+ if (child instanceof SlotRef) {
+ result.add((SlotRef) child);
+ } else {
+ result.addAll(getSlotRefChildren(child));
+ }
+ }
+ return result;
+ }
+
public void substituteGroupingFn(List<Expr> exprs, Analyzer analyzer)
throws AnalysisException {
if (groupingType == GroupByClause.GroupingType.GROUP_BY) {
throw new AnalysisException("cannot use GROUPING functions without
[grouping sets|rollup|cube] a"
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 955f9650b4..10f1b0264e 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
@@ -1050,10 +1050,6 @@ public class SelectStmt extends QueryStmt {
List<TupleId> groupingByTupleIds = new ArrayList<>();
if (groupByClause != null) {
- // must do it before copying for createAggInfo()
- if (groupingInfo != null) {
- groupingByTupleIds.add(groupingInfo.getVirtualTuple().getId());
- }
groupByClause.genGroupingExprs();
if (groupingInfo != null) {
GroupByClause.GroupingType groupingType =
groupByClause.getGroupingType();
@@ -1066,6 +1062,11 @@ public class SelectStmt extends QueryStmt {
groupingInfo.buildRepeat(groupByClause.getGroupingExprs(),
groupByClause.getGroupingSetList());
}
substituteOrdinalsAliases(groupByClause.getGroupingExprs(), "GROUP
BY", analyzer);
+ if (groupingInfo != null) {
+ groupingInfo.genOutputTupleDescAndSMap(analyzer,
groupByClause.getGroupingExprs(), aggExprs);
+ // must do it before copying for createAggInfo()
+
groupingByTupleIds.add(groupingInfo.getOutputTupleDesc().getId());
+ }
groupByClause.analyze(analyzer);
createAggInfo(groupByClause.getGroupingExprs(), aggExprs,
analyzer);
} else {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/SlotRef.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/SlotRef.java
index 09c6138a6d..eafdd5fcbb 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/SlotRef.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/SlotRef.java
@@ -69,7 +69,7 @@ public class SlotRef extends Expr {
public SlotRef(SlotDescriptor desc) {
super();
this.tblName = null;
- this.col = null;
+ this.col = desc.getColumn() != null ? desc.getColumn().getName() :
null;
this.desc = desc;
this.type = desc.getType();
// TODO(zc): label is meaningful
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/analysis/VirtualSlotRef.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/VirtualSlotRef.java
index 9696babe05..4cfd7a62cc 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/VirtualSlotRef.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/VirtualSlotRef.java
@@ -29,6 +29,8 @@ import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* It like a SlotRef except that it is not a real column exist in table.
@@ -55,6 +57,10 @@ public class VirtualSlotRef extends SlotRef {
tupleDescriptor = other.tupleDescriptor;
}
+ public VirtualSlotRef(SlotDescriptor desc) {
+ super(desc);
+ }
+
public static VirtualSlotRef read(DataInput in) throws IOException {
VirtualSlotRef virtualSlotRef = new VirtualSlotRef(null, Type.BIGINT,
null, new ArrayList<>());
virtualSlotRef.readFields(in);
@@ -68,6 +74,10 @@ public class VirtualSlotRef extends SlotRef {
return getColumnName();
}
+ @Override
+ public void getTableIdToColumnNames(Map<Long, Set<String>>
tableIdToColumnNames) {
+ }
+
@Override
public void write(DataOutput out) throws IOException {
super.write(out);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/planner/RepeatNode.java
b/fe/fe-core/src/main/java/org/apache/doris/planner/RepeatNode.java
index b70b374c00..61c212b9e4 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/planner/RepeatNode.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/planner/RepeatNode.java
@@ -19,9 +19,8 @@ package org.apache.doris.planner;
import org.apache.doris.analysis.Analyzer;
import org.apache.doris.analysis.Expr;
-import org.apache.doris.analysis.FunctionCallExpr;
+import org.apache.doris.analysis.ExprSubstitutionMap;
import org.apache.doris.analysis.GroupByClause;
-import org.apache.doris.analysis.GroupingFunctionCallExpr;
import org.apache.doris.analysis.GroupingInfo;
import org.apache.doris.analysis.SlotDescriptor;
import org.apache.doris.analysis.SlotId;
@@ -67,17 +66,16 @@ public class RepeatNode extends PlanNode {
private GroupByClause groupByClause;
protected RepeatNode(PlanNodeId id, PlanNode input, GroupingInfo
groupingInfo, GroupByClause groupByClause) {
- super(id, input.getTupleIds(), "REPEAT_NODE");
+ super(id, groupingInfo.getOutputTupleDesc().getId().asList(),
"REPEAT_NODE");
this.children.add(input);
this.groupingInfo = groupingInfo;
this.input = input;
this.groupByClause = groupByClause;
-
}
// only for unittest
protected RepeatNode(PlanNodeId id, PlanNode input, List<Set<SlotId>>
repeatSlotIdList,
- TupleDescriptor outputTupleDesc, List<List<Long>>
groupingList) {
+ TupleDescriptor outputTupleDesc, List<List<Long>> groupingList) {
super(id, input.getTupleIds(), "REPEAT_NODE");
this.children.add(input);
this.repeatSlotIdList = buildIdSetList(repeatSlotIdList);
@@ -112,25 +110,18 @@ public class RepeatNode extends PlanNode {
@Override
public void init(Analyzer analyzer) throws UserException {
Preconditions.checkState(conjuncts.isEmpty());
- groupByClause.substituteGroupingExprs(groupingInfo.getGroupingSlots(),
input.getOutputSmap(),
- analyzer);
-
- for (Expr expr : groupByClause.getGroupingExprs()) {
- if (expr instanceof SlotRef || (expr instanceof
GroupingFunctionCallExpr)) {
- continue;
- }
- // throw new AnalysisException("function or expr is not allowed in
grouping sets clause.");
+ ExprSubstitutionMap childSmap = getCombinedChildSmap();
+
groupByClause.substituteGroupingExprs(groupingInfo.getVirtualSlotRefs(),
childSmap, analyzer);
+ groupingInfo.substitutePreRepeatExprs(childSmap, analyzer);
+ outputSmap = groupingInfo.getOutputTupleSmap();
+ conjuncts = Expr.substituteList(conjuncts, outputSmap, analyzer,
false);
+ outputTupleDesc = groupingInfo.getOutputTupleDesc();
+ List<TupleId> inputTupleIds = input.getOutputTupleIds();
+ if (inputTupleIds.size() == 1) {
+ // used for MaterializedViewSelector getTableIdToColumnNames
+
outputTupleDesc.setTable(analyzer.getTupleDesc(inputTupleIds.get(0)).getTable());
}
- // build new BitSet List for tupleDesc
- Set<SlotDescriptor> slotDescSet = new HashSet<>();
- for (TupleId tupleId : input.getTupleIds()) {
- TupleDescriptor tupleDescriptor =
analyzer.getDescTbl().getTupleDesc(tupleId);
- slotDescSet.addAll(tupleDescriptor.getSlots());
- }
-
- // build tupleDesc according to child's tupleDesc info
- outputTupleDesc = groupingInfo.getVirtualTuple();
//set aggregate nullable
for (Expr slot : groupByClause.getGroupingExprs()) {
if (slot instanceof SlotRef && !(slot instanceof VirtualSlotRef)) {
@@ -140,74 +131,42 @@ public class RepeatNode extends PlanNode {
outputTupleDesc.computeStatAndMemLayout();
List<Set<SlotId>> groupingIdList = new ArrayList<>();
- List<Expr> exprList = groupByClause.getGroupingExprs();
- Preconditions.checkState(exprList.size() >= 2);
- allSlotId = new HashSet<>();
+ List<SlotDescriptor> groupingSlotDescList =
groupingInfo.getGroupingSlotDescList();
for (BitSet bitSet :
Collections.unmodifiableList(groupingInfo.getGroupingIdList())) {
Set<SlotId> slotIdSet = new HashSet<>();
- for (SlotDescriptor slotDesc : slotDescSet) {
- SlotId slotId = slotDesc.getId();
- if (slotId == null) {
- continue;
- }
- for (int i = 0; i < exprList.size(); i++) {
- if (exprList.get(i) instanceof SlotRef) {
- SlotRef slotRef = (SlotRef) (exprList.get(i));
- if (bitSet.get(i) && slotRef.getSlotId() == slotId) {
- slotIdSet.add(slotId);
- break;
- }
- } else if (exprList.get(i) instanceof FunctionCallExpr) {
- List<SlotRef> slotRefs =
getSlotRefChildren(exprList.get(i));
- for (SlotRef slotRef : slotRefs) {
- if (bitSet.get(i) && slotRef.getSlotId() ==
slotId) {
- slotIdSet.add(slotId);
- break;
- }
- }
- }
+ for (int i = 0; i < groupingSlotDescList.size(); i++) {
+ if (bitSet.get(i)) {
+ slotIdSet.add(groupingSlotDescList.get(i).getId());
}
}
groupingIdList.add(slotIdSet);
}
this.repeatSlotIdList = buildIdSetList(groupingIdList);
+ allSlotId = new HashSet<>();
for (Set<Integer> s : this.repeatSlotIdList) {
allSlotId.addAll(s);
}
this.groupingList =
groupingInfo.genGroupingList(groupByClause.getGroupingExprs());
- tupleIds.add(outputTupleDesc.getId());
for (TupleId id : tupleIds) {
analyzer.getTupleDesc(id).setIsMaterialized(true);
}
computeTupleStatAndMemLayout(analyzer);
computeStats(analyzer);
- createDefaultSmap(analyzer);
- }
-
- private List<SlotRef> getSlotRefChildren(Expr root) {
- List<SlotRef> result = new ArrayList<>();
- for (Expr child : root.getChildren()) {
- if (child instanceof SlotRef) {
- result.add((SlotRef) child);
- } else {
- result.addAll(getSlotRefChildren(child));
- }
- }
- return result;
}
@Override
protected void toThrift(TPlanNode msg) {
msg.node_type = TPlanNodeType.REPEAT_NODE;
- msg.repeat_node = new TRepeatNode(outputTupleDesc.getId().asInt(),
repeatSlotIdList, groupingList.get(0),
- groupingList, allSlotId);
+ msg.repeat_node =
+ new TRepeatNode(outputTupleDesc.getId().asInt(),
repeatSlotIdList, groupingList.get(0), groupingList,
+ allSlotId,
Expr.treesToThrift(groupingInfo.getPreRepeatExprs()));
}
@Override
protected String debugString() {
- return MoreObjects.toStringHelper(this).add("Repeat",
repeatSlotIdList.size()).addValue(
- super.debugString()).toString();
+ return MoreObjects.toStringHelper(this).add("Repeat",
repeatSlotIdList.size()).addValue(super.debugString())
+ .toString();
}
@Override
@@ -219,11 +178,12 @@ public class RepeatNode extends PlanNode {
output.append(detailPrefix + "repeat: repeat ");
output.append(repeatSlotIdList.size() - 1);
output.append(" lines ");
- output.append(repeatSlotIdList);
+ output.append(repeatSlotIdList).append("\n");
+ output.append(detailPrefix).append("exprs:
").append(getExplainString(groupingInfo.getPreRepeatExprs()));
output.append("\n");
if (CollectionUtils.isNotEmpty(outputTupleDesc.getSlots())) {
- output.append(detailPrefix + "generate: ");
- output.append(outputTupleDesc.getSlots().stream().map(slot -> "`"
+ slot.getColumn().getName() + "`")
+ output.append(detailPrefix + "output slots: ");
+ output.append(outputTupleDesc.getSlots().stream().map(slot -> "`"
+ slot.getLabel() + "`")
.collect(Collectors.joining(", ")) + "\n");
}
return output.toString();
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/planner/QueryPlanTest.java
b/fe/fe-core/src/test/java/org/apache/doris/planner/QueryPlanTest.java
index 2c421cc100..cfbb0c2055 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/planner/QueryPlanTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/planner/QueryPlanTest.java
@@ -454,7 +454,7 @@ public class QueryPlanTest {
public void testFunctionViewGroupingSet() throws Exception {
String queryStr = "select query_id, client_ip, concat from
test.function_view group by rollup(query_id, client_ip, concat);";
String explainStr = UtFrameUtils.getSQLPlanOrErrorMsg(connectContext,
queryStr);
- Assert.assertTrue(explainStr.contains("repeat: repeat 3 lines [[],
[0], [0, 1], [0, 1, 2, 3]]"));
+ Assert.assertTrue(explainStr.contains("repeat: repeat 3 lines [[],
[8], [8, 9], [8, 9, 10]]"));
}
@Test
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/planner/RepeatNodeTest.java
b/fe/fe-core/src/test/java/org/apache/doris/planner/RepeatNodeTest.java
index 95b58482dc..408f63eced 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/planner/RepeatNodeTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/planner/RepeatNodeTest.java
@@ -17,79 +17,91 @@
package org.apache.doris.planner;
-import org.apache.doris.analysis.AccessTestUtil;
-import org.apache.doris.analysis.Analyzer;
-import org.apache.doris.analysis.DescriptorTable;
-import org.apache.doris.analysis.SlotId;
-import org.apache.doris.analysis.SlotRef;
-import org.apache.doris.analysis.TableName;
-import org.apache.doris.analysis.TupleDescriptor;
-import org.apache.doris.analysis.TupleId;
-import org.apache.doris.thrift.TExplainLevel;
-import org.apache.doris.thrift.TPlanNode;
-import org.apache.doris.thrift.TPlanNodeType;
-
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Multimap;
+import org.apache.doris.analysis.CreateDbStmt;
+import org.apache.doris.analysis.CreateTableStmt;
+import org.apache.doris.catalog.Catalog;
+import org.apache.doris.common.jmockit.Deencapsulation;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.utframe.UtFrameUtils;
import org.junit.Assert;
-import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
+import java.util.UUID;
public class RepeatNodeTest {
- private Analyzer analyzer;
- private RepeatNode node;
- private TupleDescriptor virtualTuple;
- private List<Set<SlotId>> groupingIdList = new ArrayList<>();
- private List<List<Long>> groupingList = new ArrayList<>();
-
- @Before
- public void setUp() throws Exception {
- Analyzer analyzerBase = AccessTestUtil.fetchTableAnalyzer();
- analyzer = new Analyzer(analyzerBase.getCatalog(),
analyzerBase.getContext());
- String[] cols = {"k1", "k2", "k3"};
- List<SlotRef> slots = new ArrayList<>();
- for (String col : cols) {
- SlotRef expr = new SlotRef(new TableName("testdb", "t"), col);
- slots.add(expr);
- }
- try {
- Field f = analyzer.getClass().getDeclaredField("tupleByAlias");
- f.setAccessible(true);
- Multimap<String, TupleDescriptor> tupleByAlias =
ArrayListMultimap.create();
- TupleDescriptor td = new TupleDescriptor(new TupleId(0));
- td.setTable(analyzerBase.getTableOrAnalysisException(new
TableName("testdb", "t")));
- tupleByAlias.put("testdb.t", td);
- f.set(analyzer, tupleByAlias);
- } catch (NoSuchFieldException | IllegalAccessException e) {
- e.printStackTrace();
- }
- virtualTuple =
analyzer.getDescTbl().createTupleDescriptor("VIRTUAL_TUPLE");
- groupingList.add(Arrays.asList(0L, 7L, 3L, 5L, 1L, 6L, 2L, 4L));
- groupingList.add(Arrays.asList(0L, 7L, 3L, 5L, 1L, 6L, 2L, 4L));
- DescriptorTable descTable = new DescriptorTable();
- TupleDescriptor tuple = descTable.createTupleDescriptor("DstTable");
- node = new RepeatNode(new PlanNodeId(1),
- new OlapScanNode(new PlanNodeId(0), tuple, "null"),
groupingIdList, virtualTuple, groupingList);
+ private static String runningDir = "fe/mocked/RepeatNodeTest/" +
UUID.randomUUID() + "/";
+
+ private static ConnectContext connectContext;
+
+ @BeforeClass
+ public static void beforeClass() throws Exception {
+ UtFrameUtils.createDorisCluster(runningDir);
+
+ // create connect context
+ connectContext = UtFrameUtils.createDefaultCtx();
+
+ // disable bucket shuffle join
+ Deencapsulation.setField(connectContext.getSessionVariable(),
"enableBucketShuffleJoin", false);
+
+ // create database
+ String createDbStmtStr = "create database testdb;";
+ CreateDbStmt createDbStmt = (CreateDbStmt)
UtFrameUtils.parseAndAnalyzeStmt(createDbStmtStr, connectContext);
+ Catalog.getCurrentCatalog().createDb(createDbStmt);
+
+
+ String createMycostSql =
+ " CREATE TABLE `testdb`.`mycost` (\n" + " `id` tinyint(4)
NULL,\n" + " `name` varchar(20) NULL,\n"
+ + " `date` date NULL,\n" + " `cost` bigint(20) SUM
NULL\n" + ") ENGINE=OLAP\n"
+ + "AGGREGATE KEY(`id`, `name`, `date`)\n" + "COMMENT
'OLAP'\n" + "PARTITION BY RANGE(`date`)\n"
+ + "(PARTITION p2020 VALUES [('0000-01-01'),
('2021-01-01')),\n"
+ + "PARTITION p2021 VALUES [('2021-01-01'),
('2022-01-01')),\n"
+ + "PARTITION p2022 VALUES [('2022-01-01'),
('2023-01-01')))\n"
+ + "DISTRIBUTED BY HASH(`id`) BUCKETS 8\n" +
"PROPERTIES (\n"
+ + "\"replication_allocation\" =
\"tag.location.default: 1\",\n" + "\"in_memory\" = \"false\",\n"
+ + "\"storage_format\" = \"V2\"\n" + ");";
+
+ Catalog.getCurrentCatalog()
+ .createTable((CreateTableStmt)
UtFrameUtils.parseAndAnalyzeStmt(createMycostSql, connectContext));
+
+ String createMypeopleSql =
+ " CREATE TABLE `testdb`.`mypeople` (\n" + " `id` bigint(20)
NULL,\n" + " `name` varchar(20) NULL,\n"
+ + " `sex` varchar(10) NULL,\n" + " `age` int(11)
NULL,\n" + " `phone` char(15) NULL,\n"
+ + " `address` varchar(50) NULL\n" + ") ENGINE=OLAP\n"
+ "DUPLICATE KEY(`id`, `name`)\n"
+ + "COMMENT 'OLAP'\n" + "DISTRIBUTED BY HASH(`id`)
BUCKETS 8\n" + "PROPERTIES (\n"
+ + "\"replication_allocation\" =
\"tag.location.default: 1\",\n" + "\"in_memory\" = \"false\",\n"
+ + "\"storage_format\" = \"V2\"\n" + ");";
+ Catalog.getCurrentCatalog()
+ .createTable((CreateTableStmt)
UtFrameUtils.parseAndAnalyzeStmt(createMypeopleSql, connectContext));
}
@Test
- public void testNornal() {
- try {
- TPlanNode msg = new TPlanNode();
- node.toThrift(msg);
- node.getNodeExplainString("", TExplainLevel.NORMAL);
- node.debugString();
- Assert.assertEquals(TPlanNodeType.REPEAT_NODE, msg.node_type);
- } catch (Exception e) {
- Assert.fail("throw exceptions");
- }
+ public void testNormal() throws Exception {
+ String sql = "select id, name, sum(cost), grouping_id(id, name) from
testdb.mycost group by cube(id, name);";
+ String explainString =
UtFrameUtils.getSQLPlanOrErrorMsg(connectContext, sql);
+ Assert.assertTrue(explainString.contains("exprs: `id`, `name`,
`cost`"));
+ Assert.assertTrue(explainString.contains(
+ "output slots: ``id``, ``name``, ``cost``, ``GROUPING_ID``,
``GROUPING_PREFIX_`id`_`name```"));
+ }
+
+ @Test
+ public void testExpr() throws Exception {
+ String sql1 = "select if(c.id > 0, 1, 0) as id_, p.name, sum(c.cost)
from testdb.mycost c "
+ + "join testdb.mypeople p on c.id = p.id group by grouping
sets((id_, name),());";
+ String explainString1 =
UtFrameUtils.getSQLPlanOrErrorMsg(connectContext, sql1);
+ Assert.assertTrue(explainString1.contains(
+ "output slots: `if(`c`.`id` > 0, 1, 0)`, ``p`.`name``,
``c`.`cost``, ``GROUPING_ID``"));
+
+ String sql2 = "select (id + 1) id_, name, sum(cost) from testdb.mycost
group by grouping sets((id_, name),());";
+ String explainString2 =
UtFrameUtils.getSQLPlanOrErrorMsg(connectContext, sql2);
+ Assert.assertTrue(explainString2.contains("exprs: (`id` + 1), `name`,
`cost`"));
+ Assert.assertTrue(explainString2.contains(" output slots: `(`id` +
1)`, ``name``, ``cost``, ``GROUPING_ID``"));
+
+ String sql3 = "select 1 as id_, name, sum(cost) from testdb.mycost
group by grouping sets((id_, name),());";
+ String explainString3 =
UtFrameUtils.getSQLPlanOrErrorMsg(connectContext, sql3);
+ Assert.assertTrue(explainString3.contains("exprs: 1, `name`, `cost`"));
+ Assert.assertTrue(explainString3.contains("output slots: `1`,
``name``, ``cost``, ``GROUPING_ID``"));
}
}
diff --git a/gensrc/thrift/PlanNodes.thrift b/gensrc/thrift/PlanNodes.thrift
index 8ca90ec29c..9f23f96cef 100644
--- a/gensrc/thrift/PlanNodes.thrift
+++ b/gensrc/thrift/PlanNodes.thrift
@@ -482,6 +482,7 @@ struct TRepeatNode {
4: required list<list<i64>> grouping_list
// A list of all slot
5: required set<Types.TSlotId> all_slot_ids
+ 6: required list<Exprs.TExpr> exprs
}
struct TPreAggregationNode {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]