blob: b069a4a1e18cad79d8492f9fd89a4b2c731b6f9d [file]
// 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/cte-producer-node.h"
#include "exec/exec-node-util.h"
#include "runtime/exec-env.h"
#include "runtime/fragment-state.h"
#include "runtime/local-exchanger.h"
#include "runtime/query-state.h"
#include "runtime/row-batch.h"
#include "runtime/runtime-state.h"
#include "util/runtime-profile-counters.h"
#include "util/runtime-profile.h"
#include "util/scope-exit-trigger.h"
#include "common/names.h"
namespace impala {
Status CTEProducerPlanNode::Init(const TPlanNode& tnode, FragmentState* state) {
RETURN_IF_ERROR(PlanNode::Init(tnode, state));
DCHECK(tnode.__isset.cte_producer);
// Register the exchanger here while plan setup is single-threaded.
for (const TPlanFragmentInstanceCtx* instance_ctx : state->instance_ctxs()) {
DCHECK_GT(instance_ctx->num_cte_consumers, 0);
unique_ptr<LocalExchanger> exchanger(
new LocalExchanger(instance_ctx->num_cte_consumers));
string exchange_instance = GetCTEName(instance_ctx->per_fragment_instance_idx);
VLOG_QUERY << "Registering CTE exchange " << exchange_instance << " with "
<< instance_ctx->num_cte_consumers << " consumers.";
state->query_state()->RegisterExchanger(exchange_instance, exchanger.get());
exchangers_.emplace(instance_ctx->per_fragment_instance_idx, std::move(exchanger));
}
needs_batch_deep_copy_ = WillNeedDeepCopy(state->query_options());
return Status::OK();
}
Status CTEProducerPlanNode::CreateExecNode(RuntimeState* state, ExecNode** node) const {
ObjectPool* pool = state->obj_pool();
*node = pool->Add(new CTEProducerNode(pool, *this, state));
return Status::OK();
}
CTEProducerNode::CTEProducerNode(
ObjectPool* pool, const CTEProducerPlanNode& pnode, RuntimeState* state)
: ExecNode(pool, pnode, state->desc_tbl()),
needs_batch_deep_copy_(pnode.needs_batch_deep_copy_) {
int32_t idx = state->instance_ctx().per_fragment_instance_idx;
exchanger_ = pnode.exchangers_.at(idx).get();
name_ = pnode.GetCTEName(idx);
}
Status CTEProducerNode::Prepare(RuntimeState* state) {
RETURN_IF_ERROR(ExecNode::Prepare(state));
RETURN_IF_ERROR(exchanger_->Init(runtime_profile(), mem_tracker(), name_));
return Status::OK();
}
Status CTEProducerNode::Open(RuntimeState* state) {
SCOPED_TIMER(runtime_profile()->total_time_counter());
auto close_on_exit = MakeScopeExitTrigger([&]() {
VLOG_QUERY << "Closing producer of CTE exchange " << name_;
exchanger_->CloseProducer();
});
ScopedOpenEventAdder ea(this);
RETURN_IF_ERROR(ExecNode::Open(state));
RETURN_IF_ERROR(child(0)->Open(state));
// Claim reservation after the child has been opened to reduce the peak reservation
// requirement.
if (!buffer_pool_client()->is_registered()) {
RETURN_IF_ERROR(ClaimBufferReservation(state));
}
DCHECK(row_desc()->Equals(*child(0)->row_desc()));
bool eos = false;
do {
RETURN_IF_CANCELLED(state);
unique_ptr<RowBatch> child_batch(new RowBatch(
child(0)->row_desc(), state->batch_size(), mem_tracker()));
RETURN_IF_ERROR(children_[0]->GetNext(state, child_batch.get(), &eos));
VLOG_PROGRESS << "Adding " << child_batch->num_rows()
<< " rows to CTE exchange " << name_ << " in " << label();
// Add all row batches, even if empty, to avoid freeing the tuple data pool.
int num_rows = child_batch->num_rows();
if (needs_batch_deep_copy_ && num_rows > 0) {
unique_ptr<RowBatch> copy(
new RowBatch(child(0)->row_desc(), num_rows, mem_tracker()));
child_batch->DeepCopyTo(copy.get());
RETURN_IF_ERROR(exchanger_->Push(std::move(copy)));
} else {
DCHECK(!child_batch->needs_deep_copy() || num_rows == 0);
RETURN_IF_ERROR(exchanger_->Push(std::move(child_batch)));
}
IncrementNumRowsReturned(num_rows);
} while (!eos);
return Status::OK();
}
Status CTEProducerNode::GetNext(
RuntimeState* state, RowBatch* output_row_batch, bool* eos) {
RETURN_IF_ERROR(ExecDebugAction(TExecNodePhase::GETNEXT, state));
while (!exchanger_->ReadFinished(/*timeout_ms=*/ 10)) {
// Wait for exchanger to finish, handling cancellation and query maintenance.
RETURN_IF_CANCELLED(state);
RETURN_IF_ERROR(QueryMaintenance(state));
}
*eos = true;
return Status::OK();
}
Status CTEProducerNode::Reset(RuntimeState* state, RowBatch* row_batch) {
// Reset() is not supported.
const char* msg = "Internal error: CTE producer nodes should not appear in subplans.";
DCHECK(false) << msg;
return Status(msg);
}
void CTEProducerNode::Close(RuntimeState* state) {
if (is_closed()) return;
VLOG_QUERY << "Releasing CTE exchange " << name_;
exchanger_->Release();
ExecNode::Close(state);
}
void CTEProducerNode::DebugString(int indentation_level, stringstream* out) const {
*out << string(indentation_level * 2, ' ') << "CTEProducerNode(" << name_;
ExecNode::DebugString(indentation_level, out);
*out << ")";
}
}