blob: a5a6cdf6f5cb5eb1a76ed05587fd427ba6aa0d0a [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.
#pragma once
#include <string>
#include <string_view>
#include <vector>
#include "exec/exec-node.h"
#include "gen-cpp/DataSinks_types.h"
#include "gen-cpp/PlanNodes_types.h"
#include "runtime/types.h"
namespace impala {
class Tuple;
class TupleRow;
class FragmentState;
class DescriptorTbl;
class IcebergMergeCase;
class IcebergMergeCasePlan;
class ScalarExprEvaluator;
class ScalarExpr;
class RuntimeState;
class RowBatch;
/// Plan node for Iceberg merge node, contains plans for merge cases, scalar expression
/// for checking row presence, Iceberg-related virtual columns, and tuple identifiers
/// for merge action and for the target table.
class IcebergMergePlanNode : public PlanNode {
public:
Status Init(const TPlanNode& tnode, FragmentState* state) override;
Status CreateExecNode(RuntimeState* state, ExecNode** node) const override;
virtual void Close() override;
IcebergMergePlanNode() = default;
~IcebergMergePlanNode() override = default;
IcebergMergePlanNode(const IcebergMergePlanNode& other) = delete;
IcebergMergePlanNode(IcebergMergePlanNode&& other) = delete;
auto operator=(const IcebergMergePlanNode& other) = delete;
auto operator=(IcebergMergePlanNode&& other) = delete;
/// Plans for each merge case
std::vector<IcebergMergeCasePlan*> merge_case_plans_;
/// Expr that signals whether the merge row contains the
/// target tuple, the source tuple, or both.
ScalarExpr* row_present_ = nullptr;
/// Exprs used to identify the position/delete information of each target record
std::vector<ScalarExpr*> row_meta_exprs_;
/// Exprs used to identify the partitioning properties of a record
std::vector<ScalarExpr*> partition_meta_exprs_;
/// The identifier of the merge action tuple that contains the information whether
/// the output row should be updated, deleted or inserted.
TTupleId merge_action_tuple_id_ = -1;
/// Identifier of the target table's tuple
TTupleId target_tuple_id_ = -1;
};
// Node that evaluates merge rows targeting Iceberg tables. The node receives the rows
// from the preceding join operator, the row consists of a 'row_present' column, the
// columns of the target table, the virtual columns of the target table and the columns
// of the source table. The output of the node uses a similar row descriptor, the target
// table's tuple is set, the source expression's tuples are emptied, and a new merge
// action tuple is filled based on the evaluation of the row.
class IcebergMergeNode : public ExecNode {
public:
IcebergMergeNode(
ObjectPool* pool, const IcebergMergePlanNode& pnode, const DescriptorTbl& descs);
Status Prepare(RuntimeState* state) override;
Status Open(RuntimeState* state) override;
Status GetNext(RuntimeState* state, RowBatch* row_batch, bool* eos) override;
Status Reset(RuntimeState* state, RowBatch* row_batch) override;
void Close(RuntimeState* state) override;
const std::vector<ScalarExprEvaluator*>& RowMetaEvals();
const std::vector<ScalarExprEvaluator*>& PartitionMetaEvals();
private:
/// Processes the incoming row batch row-by-row, first check whether the incoming row
/// is a duplicate, the second check tests if the row contains both the target and
/// the source, or just the source. In the 'BOTH' case, the 'WHEN MATCHED' cases are
/// tested by their filter evaluators. The evaluation respects the order of cases as
/// they are defined in the query. In the 'SOURCE' case, the
/// 'WHEN NOT MATCHED (BY TARGET)' cases are checked, in the 'TARGET' case the
/// 'WHEN NOT MATCHED BY SOURCE' cases are checked similarly to the 'BOTH' case.
/// The 'selected_case' pointer stores the first matched case. If the 'selected_case'
/// is set, then a new row is added to the output row batch, and the output
/// expressions are evaluated into the new row. The merge action is also set
/// derived from the type of the selected case.
Status EvaluateCases(RowBatch* output_batch);
void AddRow(RowBatch* output_batch, IcebergMergeCase* merge_case, TupleRow* row);
bool CheckCase(const IcebergMergeCase * merge_case, TupleRow* row);
bool IsDuplicateTargetTuplePtr(TupleRow* actual_row);
bool IsDuplicateTargetRowIdent(TupleRow* actual_row);
void SavePreviousRowPtrAndIdent(TupleRow* actual_row);
std::vector<IcebergMergeCase*> matched_cases_;
std::vector<IcebergMergeCase*> not_matched_by_target_cases_;
std::vector<IcebergMergeCase*> not_matched_by_source_cases_;
std::vector<IcebergMergeCase*> all_cases_;
std::unique_ptr<RowBatch> child_row_batch_;
int child_row_idx_;
bool child_eos_;
ScalarExpr* row_present_;
std::vector<ScalarExpr*> row_meta_exprs_;
std::vector<ScalarExpr*> partition_meta_exprs_;
std::vector<ScalarExprEvaluator*> row_meta_evaluators_;
std::vector<ScalarExprEvaluator*> partition_meta_evaluators_;
ScalarExprEvaluator* row_present_evaluator_;
/// Pointer to the last processed tuple row from target table, used for duplicate
/// filtering
TupleRow* previous_row_ = nullptr;
/// Previous target row's file position
int64_t previous_row_file_pos_ = -1;
/// Previous target row's file path
std::string_view previous_row_file_path_;
/// Previous target row's file path materialized as a string
std::string previous_row_file_path_materialized_;
/// Index of the merge action tuple in the row descriptor
int merge_action_tuple_idx_ = -1;
/// Index of the target tuple in the row descriptor
int target_tuple_idx_ = -1;
/// Type of the merge action tuple
inline static const ColumnType merge_action_tuple_type_ = ColumnType(TYPE_TINYINT);
};
class IcebergMergeCasePlan {
public:
IcebergMergeCasePlan() = default;
~IcebergMergeCasePlan() = default;
Status Init(const TIcebergMergeCase& tmerge_case, FragmentState* state,
const RowDescriptor* row_desc);
void Close();
IcebergMergeCasePlan(const IcebergMergeCasePlan& other) = delete;
IcebergMergeCasePlan(IcebergMergeCasePlan&& other) = delete;
auto operator=(const IcebergMergeCasePlan& other) = delete;
auto operator=(IcebergMergeCasePlan&& other) = delete;
/// Filter conjuncts applied after matching the case
std::vector<ScalarExpr*> filter_conjuncts_;
std::vector<ScalarExpr*> output_exprs_;
TMergeCaseType::type case_type_{};
TMergeMatchType::type match_type_{};
};
class IcebergMergeCase {
public:
IcebergMergeCase(const IcebergMergeCasePlan* pcase);
IcebergMergeCase() = delete;
~IcebergMergeCase() = default;
IcebergMergeCase(const IcebergMergeCase& other) = delete;
IcebergMergeCase(IcebergMergeCase&& other) = delete;
auto operator=(const IcebergMergeCase& other) = delete;
auto operator=(IcebergMergeCase&& other) = delete;
Status Prepare(RuntimeState* state, IcebergMergeNode& parent);
Status Open(RuntimeState* state);
void Close(RuntimeState* state);
[[nodiscard]] TIcebergMergeSinkAction::type SinkAction() const {
if (case_type_ == TMergeCaseType::DELETE) {
return TIcebergMergeSinkAction::DELETE;
}
if (case_type_ == TMergeCaseType::INSERT) {
return TIcebergMergeSinkAction::DATA;
}
DCHECK(case_type_ == TMergeCaseType::UPDATE);
return TIcebergMergeSinkAction::BOTH;
}
std::vector<ScalarExpr*> filter_conjuncts_;
std::vector<ScalarExpr*> output_exprs_;
TMergeCaseType::type case_type_{};
TMergeMatchType::type match_type_{};
std::vector<ScalarExprEvaluator*> filter_evaluators_;
std::vector<ScalarExprEvaluator*> output_evaluators_;
std::vector<ScalarExprEvaluator*> combined_evaluators_;
};
} // namespace impala