blob: c5726e5baf8b810dfdc15f89980c7a3aedb9bed4 [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 "paimon/format/orc/predicate_converter.h"
#include <utility>
#include "gtest/gtest.h"
#include "orc/Type.hh"
#include "orc/sargs/SearchArgument.hh"
#include "paimon/data/decimal.h"
#include "paimon/data/timestamp.h"
#include "paimon/defs.h"
#include "paimon/predicate/literal.h"
#include "paimon/predicate/predicate_builder.h"
#include "paimon/testing/utils/testharness.h"
namespace paimon::orc::test {
TEST(PredicateConverterTest, TestSimple) {
std::string orc_schema =
"struct<f0:bigint,f1:double,f2:string,f3:int,f4:tinyint,f5:decimal(6,2)>";
std::unique_ptr<::orc::Type> orc_type = ::orc::Type::buildTypeFromString(orc_schema);
{
auto predicate =
PredicateBuilder::IsNull(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT);
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("leaf-0 = (column(id=1) is null), expr = leaf-0", search_arg->toString());
}
{
auto predicate =
PredicateBuilder::IsNotNull(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT);
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("leaf-0 = (column(id=1) is null), expr = (not leaf-0)", search_arg->toString());
}
{
auto predicate = PredicateBuilder::Equal(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l));
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("leaf-0 = (column(id=1) = 5), expr = leaf-0", search_arg->toString());
}
{
auto predicate = PredicateBuilder::Equal(/*field_index=*/3, /*field_name=*/"f3",
FieldType::INT, Literal(10));
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("leaf-0 = (column(id=4) = 10), expr = leaf-0", search_arg->toString());
}
{
// Noted: equal null convert to is null in orc, which is different from java paimon,
// where any field value equal/not equal null return false
// therefore, we disable literal in predicate to be null (see PredicateValidator)
auto predicate = PredicateBuilder::Equal(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(FieldType::BIGINT));
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("leaf-0 = (column(id=1) is null), expr = leaf-0", search_arg->toString());
}
{
auto predicate = PredicateBuilder::NotEqual(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l));
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("leaf-0 = (column(id=1) = 5), expr = (not leaf-0)", search_arg->toString());
}
{
auto predicate = PredicateBuilder::GreaterThan(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l));
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("leaf-0 = (column(id=1) <= 5), expr = (not leaf-0)", search_arg->toString());
}
{
auto predicate = PredicateBuilder::GreaterOrEqual(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l));
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("leaf-0 = (column(id=1) < 5), expr = (not leaf-0)", search_arg->toString());
}
{
auto predicate = PredicateBuilder::GreaterOrEqual(
/*field_index=*/4, /*field_name=*/"f4", FieldType::TINYINT,
Literal(static_cast<int8_t>(16)));
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("leaf-0 = (column(id=5) < 16), expr = (not leaf-0)", search_arg->toString());
}
{
auto predicate = PredicateBuilder::LessThan(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l));
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("leaf-0 = (column(id=1) < 5), expr = leaf-0", search_arg->toString());
}
{
auto predicate = PredicateBuilder::LessOrEqual(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l));
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("leaf-0 = (column(id=1) <= 5), expr = leaf-0", search_arg->toString());
}
{
auto predicate =
PredicateBuilder::In(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT,
{Literal(1l), Literal(3l), Literal(5l)});
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("leaf-0 = (column(id=1) in [1, 3, 5]), expr = leaf-0", search_arg->toString());
}
{
ASSERT_OK_AND_ASSIGN(
const auto predicate,
PredicateBuilder::StartsWith(/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "aab", 3)));
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("expr = YES", search_arg->toString());
}
{
ASSERT_OK_AND_ASSIGN(
const auto predicate,
PredicateBuilder::EndsWith(/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "bcc", 3)));
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("expr = YES", search_arg->toString());
}
{
ASSERT_OK_AND_ASSIGN(
const auto predicate,
PredicateBuilder::Contains(/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "abc", 3)));
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("expr = YES", search_arg->toString());
}
{
ASSERT_OK_AND_ASSIGN(
const auto predicate,
PredicateBuilder::Like(/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "abc", 3)));
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("expr = YES", search_arg->toString());
}
{
auto predicate =
PredicateBuilder::NotIn(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT,
{Literal(1l), Literal(3l), Literal(5l)});
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("leaf-0 = (column(id=1) in [1, 3, 5]), expr = (not leaf-0)",
search_arg->toString());
}
{
// orc format do not support only one decimal for In Predicate
auto predicate = PredicateBuilder::In(/*field_index=*/5, /*field_name=*/"f5",
FieldType::DECIMAL, {Literal(Decimal(6, 2, 12345))});
ASSERT_NOK_WITH_MSG(PredicateConverter::Convert(*orc_type, predicate),
"At least two literals are required!");
}
{
// test idx mismatches in orc type
auto predicate =
PredicateBuilder::IsNull(/*field_index=*/3, /*field_name=*/"f0", FieldType::BIGINT);
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("leaf-0 = (column(id=1) is null), expr = leaf-0", search_arg->toString());
}
{
// support decimal precision and scale mismatches between literal and data
auto predicate =
PredicateBuilder::In(/*field_index=*/5, /*field_name=*/"f5", FieldType::DECIMAL,
{Literal(Decimal(5, 1, 12345)), Literal(Decimal(5, 1, 54321))});
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ("leaf-0 = (column(id=6) in [1234.5, 5432.1]), expr = leaf-0",
search_arg->toString());
}
}
TEST(PredicateConverterTest, TestCompound) {
std::string orc_schema =
"struct<f0:bigint,f1:float,f2:string,f3:boolean,f4:date,f5:timestamp,f6:decimal(6,2),f7:"
"binary>";
std::unique_ptr<::orc::Type> orc_type = ::orc::Type::buildTypeFromString(orc_schema);
{
ASSERT_OK_AND_ASSIGN(
auto predicate,
PredicateBuilder::And({
PredicateBuilder::Equal(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT,
Literal(3l)),
PredicateBuilder::Equal(/*field_index=*/1, /*field_name=*/"f1", FieldType::FLOAT,
Literal(static_cast<float>(5.0))),
PredicateBuilder::Equal(/*field_index=*/2, /*field_name=*/"f2", FieldType::STRING,
Literal(FieldType::STRING, "apple", 5)),
PredicateBuilder::Equal(/*field_index=*/3, /*field_name=*/"f3", FieldType::BOOLEAN,
Literal(true)),
PredicateBuilder::Equal(/*field_index=*/4, /*field_name=*/"f4", FieldType::DATE,
Literal(FieldType::DATE, 3)),
PredicateBuilder::Equal(/*field_index=*/5, /*field_name=*/"f5",
FieldType::TIMESTAMP,
Literal(Timestamp(1725875365442l, 12000))),
PredicateBuilder::Equal(/*field_index=*/6, /*field_name=*/"f6", FieldType::DECIMAL,
Literal(Decimal(6, 2, 123456))),
PredicateBuilder::Equal(/*field_index=*/7, /*field_name=*/"f7", FieldType::BINARY,
Literal(FieldType::BINARY, "add", 3)),
}));
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ(
"leaf-0 = (column(id=1) = 3), "
"leaf-1 = (column(id=2) = 5), "
"leaf-2 = (column(id=3) = apple), "
"leaf-3 = (column(id=4) = true), "
"leaf-4 = (column(id=5) = 3), "
"leaf-5 = (column(id=6) = 1725875365.442012000), "
"leaf-6 = (column(id=7) = 1234.56), "
"expr = (and leaf-0 leaf-1 leaf-2 leaf-3 leaf-4 leaf-5 leaf-6)",
search_arg->toString());
}
{
ASSERT_OK_AND_ASSIGN(
auto predicate,
PredicateBuilder::Or({
PredicateBuilder::Equal(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT,
Literal(3l)),
PredicateBuilder::Equal(/*field_index=*/1, /*field_name=*/"f1", FieldType::FLOAT,
Literal(static_cast<float>(5.0))),
PredicateBuilder::Equal(/*field_index=*/2, /*field_name=*/"f2", FieldType::STRING,
Literal(FieldType::STRING, "apple", 5)),
PredicateBuilder::Equal(/*field_index=*/3, /*field_name=*/"f3", FieldType::BOOLEAN,
Literal(true)),
}));
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ(
"leaf-0 = (column(id=1) = 3), "
"leaf-1 = (column(id=2) = 5), "
"leaf-2 = (column(id=3) = apple), "
"leaf-3 = (column(id=4) = true), "
"expr = (or leaf-0 leaf-1 leaf-2 leaf-3)",
search_arg->toString());
}
{
auto predicate =
PredicateBuilder::Or(
{PredicateBuilder::And(
{PredicateBuilder::Equal(/*field_index=*/3, /*field_name=*/"f3",
FieldType::BOOLEAN, Literal(true)),
PredicateBuilder::LessThan(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(3l))})
.value(),
PredicateBuilder::And(
{PredicateBuilder::Equal(/*field_index=*/3, /*field_name=*/"f3",
FieldType::BOOLEAN, Literal(false)),
PredicateBuilder::LessThan(/*field_index=*/1, /*field_name=*/"f1",
FieldType::FLOAT,
Literal(static_cast<float>(3.1)))})
.value()})
.value();
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ(
"leaf-0 = (column(id=4) = true), "
"leaf-1 = (column(id=4) = false), "
"leaf-2 = (column(id=1) < 3), "
"leaf-3 = (column(id=2) < 3.1), "
"expr = (and (or leaf-0 leaf-1) (or leaf-2 leaf-1) "
"(or leaf-0 leaf-3) (or leaf-2 leaf-3))",
search_arg->toString());
}
{
// predicate nodes containing binary type will not be pushed down
auto predicate = PredicateBuilder::And(
{PredicateBuilder::Or(
{PredicateBuilder::Equal(/*field_index=*/3, /*field_name=*/"f3",
FieldType::BOOLEAN, Literal(true)),
PredicateBuilder::LessThan(
/*field_index=*/7, /*field_name=*/"f7", FieldType::BINARY,
Literal(FieldType::BINARY, "add", 3))})
.value(),
PredicateBuilder::Or(
{PredicateBuilder::Equal(/*field_index=*/3, /*field_name=*/"f3",
FieldType::BOOLEAN, Literal(false)),
PredicateBuilder::LessThan(/*field_index=*/1,
/*field_name=*/"f1", FieldType::FLOAT,
Literal(static_cast<float>(3.1)))})
.value()})
.value();
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_EQ(
"leaf-0 = (column(id=4) = false), "
"leaf-1 = (column(id=2) < 3.1), "
"expr = (or leaf-0 leaf-1)",
search_arg->toString());
}
{
// predicate nodes containing binary type will not be pushed down with OR Predicate
auto predicate =
PredicateBuilder::Or(
{PredicateBuilder::Or(
{PredicateBuilder::Equal(/*field_index=*/3, /*field_name=*/"f3",
FieldType::BOOLEAN, Literal(true)),
PredicateBuilder::LessThan(
/*field_index=*/7, /*field_name=*/"f7", FieldType::BINARY,
Literal(FieldType::BINARY, "add", 3))})
.value(),
PredicateBuilder::Or(
{PredicateBuilder::Equal(/*field_index=*/4, /*field_name=*/"f4",
FieldType::DATE, Literal(FieldType::DATE, 3)),
PredicateBuilder::LessThan(/*field_index=*/1, /*field_name=*/"f1",
FieldType::FLOAT,
Literal(static_cast<float>(3.1)))})
.value()})
.value();
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, predicate));
ASSERT_FALSE(search_arg);
}
}
TEST(PredicateConverterTest, TestWithoutPredicate) {
std::string orc_schema = "struct<f0:int,f1:double,f2:string>";
std::unique_ptr<::orc::Type> orc_type = ::orc::Type::buildTypeFromString(orc_schema);
ASSERT_OK_AND_ASSIGN(auto search_arg, PredicateConverter::Convert(*orc_type, nullptr));
ASSERT_FALSE(search_arg);
}
TEST(PredicateConverterTest, TestInvalidCase) {
std::string orc_schema = "struct<f0:int,f1:double,f2:string>";
std::unique_ptr<::orc::Type> orc_type = ::orc::Type::buildTypeFromString(orc_schema);
{
auto predicate =
PredicateBuilder::In(/*field_index=*/0, /*field_name=*/"f0", FieldType::INT, {});
ASSERT_NOK_WITH_MSG(PredicateConverter::Convert(*orc_type, predicate),
"predicate [In] need literal on field f0");
}
{
auto predicate = PredicateBuilder::In(/*field_index=*/4, /*field_name=*/"f3",
FieldType::INT, {Literal(3)});
ASSERT_NOK_WITH_MSG(PredicateConverter::Convert(*orc_type, predicate),
"field f3 is not exist in data file");
}
}
} // namespace paimon::orc::test