262 lines
9.4 KiB
C++
262 lines
9.4 KiB
C++
// 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.
|
|
|
|
#include "exec/merge_node.h"
|
|
|
|
#include "exprs/expr.h"
|
|
#include "gen_cpp/PlanNodes_types.h"
|
|
#include "runtime/row_batch.h"
|
|
#include "runtime/runtime_state.h"
|
|
#include "runtime/raw_value.h"
|
|
|
|
using std::vector;
|
|
|
|
namespace doris {
|
|
|
|
MergeNode::MergeNode(ObjectPool* pool, const TPlanNode& tnode,
|
|
const DescriptorTbl& descs) :
|
|
ExecNode(pool, tnode, descs),
|
|
_tuple_id(tnode.merge_node.tuple_id),
|
|
_const_result_expr_idx(0),
|
|
_child_idx(INVALID_CHILD_IDX),
|
|
_child_row_batch(NULL),
|
|
_child_eos(false),
|
|
_child_row_idx(0) {
|
|
}
|
|
|
|
Status MergeNode::init(const TPlanNode& tnode, RuntimeState* state) {
|
|
RETURN_IF_ERROR(ExecNode::init(tnode, state));
|
|
DCHECK(tnode.__isset.merge_node);
|
|
// Create _const_expr_lists from thrift exprs.
|
|
const vector<vector<TExpr> >& const_texpr_lists = tnode.merge_node.const_expr_lists;
|
|
for (int i = 0; i < const_texpr_lists.size(); ++i) {
|
|
vector<ExprContext*> ctxs;
|
|
RETURN_IF_ERROR(Expr::create_expr_trees(_pool, const_texpr_lists[i], &ctxs));
|
|
_const_result_expr_ctx_lists.push_back(ctxs);
|
|
}
|
|
// Create _result_expr__ctx_lists from thrift exprs.
|
|
const vector<vector<TExpr> >& result_texpr_lists = tnode.merge_node.result_expr_lists;
|
|
for (int i = 0; i < result_texpr_lists.size(); ++i) {
|
|
vector<ExprContext*> ctxs;
|
|
RETURN_IF_ERROR(Expr::create_expr_trees(_pool, result_texpr_lists[i], &ctxs));
|
|
_result_expr_ctx_lists.push_back(ctxs);
|
|
}
|
|
|
|
return Status::OK;
|
|
}
|
|
|
|
Status MergeNode::prepare(RuntimeState* state) {
|
|
RETURN_IF_ERROR(ExecNode::prepare(state));
|
|
_tuple_desc = state->desc_tbl().get_tuple_descriptor(_tuple_id);
|
|
DCHECK(_tuple_desc != NULL);
|
|
|
|
// Prepare const expr lists.
|
|
for (int i = 0; i < _const_result_expr_ctx_lists.size(); ++i) {
|
|
RETURN_IF_ERROR(Expr::prepare(
|
|
_const_result_expr_ctx_lists[i], state, row_desc(), expr_mem_tracker()));
|
|
DCHECK_EQ(_const_result_expr_ctx_lists[i].size(), _tuple_desc->slots().size());
|
|
}
|
|
|
|
// prepare materialized_slots_
|
|
for (int i = 0; i < _tuple_desc->slots().size(); ++i) {
|
|
SlotDescriptor* desc = _tuple_desc->slots()[i];
|
|
if (desc->is_materialized()) {
|
|
_materialized_slots.push_back(desc);
|
|
}
|
|
}
|
|
|
|
// Prepare result expr lists.
|
|
for (int i = 0; i < _result_expr_ctx_lists.size(); ++i) {
|
|
RETURN_IF_ERROR(Expr::prepare(
|
|
_result_expr_ctx_lists[i], state, child(i)->row_desc(), expr_mem_tracker()));
|
|
// DCHECK_EQ(_result_expr_ctx_lists[i].size(), _tuple_desc->slots().size());
|
|
DCHECK_EQ(_result_expr_ctx_lists[i].size(), _materialized_slots.size());
|
|
}
|
|
|
|
return Status::OK;
|
|
}
|
|
|
|
Status MergeNode::open(RuntimeState* state) {
|
|
RETURN_IF_ERROR(ExecNode::open(state));
|
|
// Prepare const expr lists.
|
|
for (int i = 0; i < _const_result_expr_ctx_lists.size(); ++i) {
|
|
RETURN_IF_ERROR(Expr::open(_const_result_expr_ctx_lists[i], state));
|
|
}
|
|
|
|
// Prepare result expr lists.
|
|
for (int i = 0; i < _result_expr_ctx_lists.size(); ++i) {
|
|
RETURN_IF_ERROR(Expr::open(_result_expr_ctx_lists[i], state));
|
|
}
|
|
|
|
return Status::OK;
|
|
}
|
|
|
|
Status MergeNode::get_next(RuntimeState* state, RowBatch* row_batch, bool* eos) {
|
|
RETURN_IF_ERROR(exec_debug_action(TExecNodePhase::GETNEXT));
|
|
RETURN_IF_CANCELLED(state);
|
|
SCOPED_TIMER(_runtime_profile->total_time_counter());
|
|
// Create new tuple buffer for row_batch.
|
|
int tuple_buffer_size = row_batch->capacity() * _tuple_desc->byte_size();
|
|
void* tuple_buffer = row_batch->tuple_data_pool()->allocate(tuple_buffer_size);
|
|
bzero(tuple_buffer, tuple_buffer_size);
|
|
Tuple* tuple = reinterpret_cast<Tuple*>(tuple_buffer);
|
|
|
|
// Evaluate and materialize the const expr lists exactly once.
|
|
while (_const_result_expr_idx < _const_result_expr_ctx_lists.size()) {
|
|
// Materialize expr results into row_batch.
|
|
eval_and_materialize_exprs(
|
|
_const_result_expr_ctx_lists[_const_result_expr_idx], true, &tuple, row_batch);
|
|
++_const_result_expr_idx;
|
|
*eos = reached_limit();
|
|
|
|
if (*eos || row_batch->is_full()) {
|
|
return Status::OK;
|
|
}
|
|
}
|
|
|
|
if (_child_idx == INVALID_CHILD_IDX) {
|
|
_child_idx = 0;
|
|
}
|
|
|
|
// Fetch from children, evaluate corresponding exprs and materialize.
|
|
while (_child_idx < _children.size()) {
|
|
// Row batch was either never set or we're moving on to a different child.
|
|
if (_child_row_batch.get() == NULL) {
|
|
RETURN_IF_CANCELLED(state);
|
|
_child_row_batch.reset(
|
|
new RowBatch(child(_child_idx)->row_desc(), state->batch_size(), mem_tracker()));
|
|
// Open child and fetch the first row batch.
|
|
RETURN_IF_ERROR(child(_child_idx)->open(state));
|
|
RETURN_IF_ERROR(child(_child_idx)->get_next(state, _child_row_batch.get(),
|
|
&_child_eos));
|
|
_child_row_idx = 0;
|
|
}
|
|
|
|
// Start (or continue) consuming row batches from current child.
|
|
while (true) {
|
|
// Continue materializing exprs on _child_row_batch into row batch.
|
|
if (eval_and_materialize_exprs(_result_expr_ctx_lists[_child_idx], false, &tuple,
|
|
row_batch)) {
|
|
*eos = reached_limit();
|
|
|
|
if (*eos) {
|
|
_child_idx = INVALID_CHILD_IDX;
|
|
}
|
|
|
|
return Status::OK;
|
|
}
|
|
|
|
// Fetch new batch if one is available, otherwise move on to next child.
|
|
if (_child_eos) {
|
|
break;
|
|
}
|
|
|
|
RETURN_IF_CANCELLED(state);
|
|
_child_row_batch->reset();
|
|
RETURN_IF_ERROR(child(_child_idx)->get_next(state, _child_row_batch.get(),
|
|
&_child_eos));
|
|
_child_row_idx = 0;
|
|
}
|
|
|
|
// Close current child and move on to next one.
|
|
++_child_idx;
|
|
_child_row_batch.reset(NULL);
|
|
}
|
|
|
|
_child_idx = INVALID_CHILD_IDX;
|
|
*eos = true;
|
|
return Status::OK;
|
|
}
|
|
|
|
Status MergeNode::close(RuntimeState* state) {
|
|
if (is_closed()) {
|
|
return Status::OK;
|
|
}
|
|
// don't call ExecNode::close(), it always closes all children
|
|
_child_row_batch.reset(NULL);
|
|
for (int i = 0; i < _const_result_expr_ctx_lists.size(); ++i) {
|
|
Expr::close(_const_result_expr_ctx_lists[i], state);
|
|
}
|
|
for (int i = 0; i < _result_expr_ctx_lists.size(); ++i) {
|
|
Expr::close(_result_expr_ctx_lists[i], state);
|
|
}
|
|
|
|
return ExecNode::close(state);
|
|
}
|
|
|
|
bool MergeNode::eval_and_materialize_exprs(
|
|
const std::vector<ExprContext*>& ctxs,
|
|
bool const_exprs,
|
|
Tuple** tuple,
|
|
RowBatch* row_batch) {
|
|
// Make sure there are rows left in the batch.
|
|
if (!const_exprs && _child_row_idx >= _child_row_batch->num_rows()) {
|
|
return false;
|
|
}
|
|
|
|
// Execute the body at least once.
|
|
bool done = true;
|
|
ExprContext* const* conjunct_ctxs = &_conjunct_ctxs[0];
|
|
int num_conjunct_ctxs = _conjunct_ctxs.size();
|
|
|
|
do {
|
|
TupleRow* child_row = NULL;
|
|
|
|
if (!const_exprs) {
|
|
DCHECK(_child_row_batch != NULL);
|
|
// Non-const expr list. Fetch next row from batch.
|
|
child_row = _child_row_batch->get_row(_child_row_idx);
|
|
++_child_row_idx;
|
|
done = _child_row_idx >= _child_row_batch->num_rows();
|
|
}
|
|
|
|
// Add a new row to the batch.
|
|
int row_idx = row_batch->add_row();
|
|
DCHECK(row_idx != RowBatch::INVALID_ROW_INDEX);
|
|
TupleRow* row = row_batch->get_row(row_idx);
|
|
row->set_tuple(0, *tuple);
|
|
|
|
// Materialize expr results into tuple.
|
|
for (int i = 0; i < ctxs.size(); ++i) {
|
|
SlotDescriptor* slot_desc = _tuple_desc->slots()[i];
|
|
RawValue::write(ctxs[i]->get_value(child_row), *tuple, slot_desc,
|
|
row_batch->tuple_data_pool());
|
|
}
|
|
|
|
if (ExecNode::eval_conjuncts(conjunct_ctxs, num_conjunct_ctxs, row)) {
|
|
row_batch->commit_last_row();
|
|
++_num_rows_returned;
|
|
COUNTER_SET(_rows_returned_counter, _num_rows_returned);
|
|
char* new_tuple = reinterpret_cast<char*>(*tuple);
|
|
new_tuple += _tuple_desc->byte_size();
|
|
*tuple = reinterpret_cast<Tuple*>(new_tuple);
|
|
} else {
|
|
// Make sure to reset null indicators since we're overwriting
|
|
// the tuple assembled for the previous row.
|
|
(*tuple)->init(_tuple_desc->byte_size());
|
|
}
|
|
|
|
if (row_batch->is_full() || row_batch->at_resource_limit() || reached_limit()) {
|
|
return true;
|
|
}
|
|
} while (!done);
|
|
|
|
return false;
|
|
}
|
|
|
|
}
|