-
Notifications
You must be signed in to change notification settings - Fork 412
support nulleq in tiflash #10726
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
support nulleq in tiflash #10726
Changes from all commits
6a3e95b
89ddff5
219e0c6
288d67e
00b60b5
285492d
3be6797
e325c0e
af5acc1
0276b7e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| // Copyright 2023 PingCAP, Inc. | ||
| // | ||
| // Licensed 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 <Flash/Coprocessor/DAGUtils.h> | ||
| #include <gtest/gtest.h> | ||
|
|
||
| namespace DB::tests | ||
| { | ||
| TEST(TiDBNullEQFuncTest, DagUtilsMappedToTidbNullEQ) | ||
| { | ||
| { | ||
| tipb::Expr expr; | ||
| expr.set_tp(tipb::ExprType::ScalarFunc); | ||
| expr.set_sig(tipb::ScalarFuncSig::NullEQInt); | ||
|
|
||
| ASSERT_TRUE(isScalarFunctionExpr(expr)); | ||
| ASSERT_EQ(getFunctionName(expr), "tidbNullEQ"); | ||
| } | ||
| { | ||
| tipb::Expr expr; | ||
| expr.set_tp(tipb::ExprType::ScalarFunc); | ||
| expr.set_sig(tipb::ScalarFuncSig::NullEQString); | ||
|
|
||
| ASSERT_TRUE(isScalarFunctionExpr(expr)); | ||
| ASSERT_EQ(getFunctionName(expr), "tidbNullEQ"); | ||
| } | ||
| { | ||
| tipb::Expr expr; | ||
| expr.set_tp(tipb::ExprType::ScalarFunc); | ||
| expr.set_sig(tipb::ScalarFuncSig::NullEQDecimal); | ||
|
|
||
| ASSERT_TRUE(isScalarFunctionExpr(expr)); | ||
| ASSERT_EQ(getFunctionName(expr), "tidbNullEQ"); | ||
| } | ||
| { | ||
| tipb::Expr expr; | ||
| expr.set_tp(tipb::ExprType::ScalarFunc); | ||
| expr.set_sig(tipb::ScalarFuncSig::NullEQVectorFloat32); | ||
|
|
||
| ASSERT_TRUE(isScalarFunctionExpr(expr)); | ||
| ASSERT_EQ(getFunctionName(expr), "tidbNullEQ"); | ||
| } | ||
| } | ||
|
|
||
| } // namespace DB::tests | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -12,12 +12,214 @@ | |||||
| // See the License for the specific language governing permissions and | ||||||
| // limitations under the License. | ||||||
|
|
||||||
| #include <Columns/ColumnConst.h> | ||||||
| #include <Columns/ColumnNullable.h> | ||||||
| #include <Common/typeid_cast.h> | ||||||
| #include <Functions/FunctionFactory.h> | ||||||
| #include <Functions/FunctionsComparison.h> | ||||||
|
|
||||||
| namespace DB | ||||||
| { | ||||||
| namespace ErrorCodes | ||||||
| { | ||||||
| extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; | ||||||
| extern const int ILLEGAL_COLUMN; | ||||||
| extern const int LOGICAL_ERROR; | ||||||
| } // namespace ErrorCodes | ||||||
|
|
||||||
| class FunctionTiDBNullEQ : public IFunction | ||||||
| { | ||||||
| public: | ||||||
| static constexpr auto name = "tidbNullEQ"; | ||||||
|
|
||||||
| static FunctionPtr create(const Context &) { return std::make_shared<FunctionTiDBNullEQ>(); } | ||||||
|
|
||||||
| String getName() const override { return name; } | ||||||
|
|
||||||
| size_t getNumberOfArguments() const override { return 2; } | ||||||
|
|
||||||
| bool useDefaultImplementationForNulls() const override { return false; } | ||||||
| bool useDefaultImplementationForConstants() const override { return true; } | ||||||
|
|
||||||
| void setCollator(const TiDB::TiDBCollatorPtr & collator_) override | ||||||
| { | ||||||
| collator = collator_; | ||||||
| equals_function->setCollator(collator_); | ||||||
| } | ||||||
|
|
||||||
| DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override | ||||||
| { | ||||||
| if (arguments.size() != 2) | ||||||
| throw Exception( | ||||||
| ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, | ||||||
| "Number of arguments for function {} doesn't match: passed {}, should be 2.", | ||||||
| getName(), | ||||||
| arguments.size()); | ||||||
|
|
||||||
| /// `NULL <=> x` is always true/false (never NULL), even if `NULL` is represented as `Nothing`. | ||||||
| if (arguments[0]->onlyNull() || arguments[1]->onlyNull()) | ||||||
| return std::make_shared<DataTypeUInt8>(); | ||||||
|
|
||||||
| /// Use equals to validate that the input types are comparable. | ||||||
| /// Always return non-nullable UInt8 because `NULL <=> x` is always true/false (not NULL). | ||||||
| FunctionEquals().getReturnTypeImpl({removeNullable(arguments[0]), removeNullable(arguments[1])}); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can directly return at L65 as
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It also does some input types validation in |
||||||
| return std::make_shared<DataTypeUInt8>(); | ||||||
| } | ||||||
|
|
||||||
| void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result) const override | ||||||
| { | ||||||
| const auto & left = block.getByPosition(arguments[0]); | ||||||
| const auto & right = block.getByPosition(arguments[1]); | ||||||
|
|
||||||
| ColumnPtr left_col = left.column; | ||||||
| ColumnPtr right_col = right.column; | ||||||
|
|
||||||
| const size_t rows = left_col->size(); | ||||||
| if (unlikely(right_col->size() != rows)) | ||||||
| throw Exception( | ||||||
| ErrorCodes::ILLEGAL_COLUMN, | ||||||
| "Columns sizes are different in function {}: left {}, right {}.", | ||||||
| getName(), | ||||||
| rows, | ||||||
| right_col->size()); | ||||||
|
|
||||||
| /// Fast path for always-NULL columns (Nullable(Nothing)). | ||||||
| /// `NULL <=> x` equals to `isNull(x)`; `NULL <=> NULL` is always 1. | ||||||
| if (left_col->onlyNull() || right_col->onlyNull()) | ||||||
| { | ||||||
| if (left_col->onlyNull() && right_col->onlyNull()) | ||||||
| { | ||||||
| block.getByPosition(result).column = ColumnUInt8::create(rows, 1); | ||||||
| return; | ||||||
| } | ||||||
|
|
||||||
| const ColumnPtr & other_col = left_col->onlyNull() ? right_col : left_col; | ||||||
| if (other_col->isColumnNullable()) | ||||||
| { | ||||||
| const auto & other_nullmap = assert_cast<const ColumnNullable &>(*other_col).getNullMapData(); | ||||||
| auto res_col = ColumnUInt8::create(); | ||||||
| auto & res_data = res_col->getData(); | ||||||
| res_data.assign(other_nullmap.begin(), other_nullmap.end()); | ||||||
| block.getByPosition(result).column = std::move(res_col); | ||||||
| } | ||||||
| else | ||||||
| { | ||||||
| block.getByPosition(result).column = ColumnUInt8::create(rows, 0); | ||||||
| } | ||||||
| return; | ||||||
| } | ||||||
|
|
||||||
| auto unwrap_nullable_column = [rows](const ColumnPtr & col, ColumnPtr & nested_col, const NullMap *& nullmap) { | ||||||
| nested_col = col; | ||||||
| nullmap = nullptr; | ||||||
|
|
||||||
| if (const auto * const_col = typeid_cast<const ColumnConst *>(col.get())) | ||||||
| { | ||||||
| const auto & data_col = const_col->getDataColumn(); | ||||||
| if (data_col.isColumnNullable()) | ||||||
| { | ||||||
| /// `ColumnConst(ColumnNullable(NULL))` is handled by the `onlyNull()` fast path above. | ||||||
| /// If we reach here, the nullable constant must be non-NULL, so there is no nullmap to apply. | ||||||
| const auto & nullable_col = assert_cast<const ColumnNullable &>(data_col); | ||||||
| nested_col = ColumnConst::create(nullable_col.getNestedColumnPtr(), rows); | ||||||
| } | ||||||
| return; | ||||||
| } | ||||||
|
|
||||||
| if (col->isColumnNullable()) | ||||||
| { | ||||||
| const auto & nullable_col = assert_cast<const ColumnNullable &>(*col); | ||||||
| nested_col = nullable_col.getNestedColumnPtr(); | ||||||
| nullmap = &nullable_col.getNullMapData(); | ||||||
| } | ||||||
| }; | ||||||
|
|
||||||
| ColumnPtr left_nested_col = left_col; | ||||||
| const NullMap * left_nullmap = nullptr; | ||||||
| unwrap_nullable_column(left_col, left_nested_col, left_nullmap); | ||||||
|
|
||||||
| ColumnPtr right_nested_col = right_col; | ||||||
| const NullMap * right_nullmap = nullptr; | ||||||
| unwrap_nullable_column(right_col, right_nested_col, right_nullmap); | ||||||
|
|
||||||
| /// Execute `equals` on nested columns. | ||||||
| Block temp_block; | ||||||
| temp_block.insert({left_nested_col, removeNullable(left.type), "a"}); | ||||||
| temp_block.insert({right_nested_col, removeNullable(right.type), "b"}); | ||||||
| temp_block.insert({nullptr, std::make_shared<DataTypeUInt8>(), "res"}); | ||||||
| DefaultExecutable(equals_function).execute(temp_block, {0, 1}, 2); | ||||||
|
|
||||||
| ColumnPtr eq_col = temp_block.getByPosition(2).column; | ||||||
| if (left_nullmap == nullptr && right_nullmap == nullptr) | ||||||
| { | ||||||
| block.getByPosition(result).column = std::move(eq_col); | ||||||
| return; | ||||||
| } | ||||||
|
|
||||||
| if (ColumnPtr converted = eq_col->convertToFullColumnIfConst()) | ||||||
| eq_col = converted; | ||||||
|
|
||||||
| /// Adjust for NULL values: | ||||||
| /// - both NULL => 1 | ||||||
| /// - one NULL => 0 | ||||||
| /// - no NULL => equals result | ||||||
| auto eq_mutable = (*std::move(eq_col)).mutate(); | ||||||
|
||||||
| auto eq_mutable = (*std::move(eq_col)).mutate(); | |
| auto eq_mutable = eq_col->mutate(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, the mutate() can only be called on rvalue
Uh oh!
There was an error while loading. Please reload this page.