Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions tests/expression_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -569,4 +569,69 @@ TEST(ExpressionTests, ParameterExprType) {
EXPECT_EQ(expr.type(), ExprType::Parameter);
}

// ============= InExpr Edge Case Tests =============

TEST(ExpressionTests, InExprWithDuplicates) {
// Column value appears multiple times in IN list
auto column = std::make_unique<ConstantExpr>(Value::make_int64(5));
std::vector<std::unique_ptr<Expression>> values;
values.push_back(std::make_unique<ConstantExpr>(Value::make_int64(5)));
values.push_back(std::make_unique<ConstantExpr>(Value::make_int64(5))); // duplicate
values.push_back(std::make_unique<ConstantExpr>(Value::make_int64(5))); // duplicate

InExpr expr(std::move(column), std::move(values), false);
auto result = expr.evaluate();
EXPECT_TRUE(result.as_bool()); // 5 IN (5, 5, 5) = true
}

TEST(ExpressionTests, InExprWithManyValues) {
// IN with many values (10)
auto column = std::make_unique<ConstantExpr>(Value::make_int64(7));
std::vector<std::unique_ptr<Expression>> values;
for (int i = 1; i <= 10; i++) {
values.push_back(std::make_unique<ConstantExpr>(Value::make_int64(i)));
}

InExpr expr(std::move(column), std::move(values), false);
auto result = expr.evaluate();
EXPECT_TRUE(result.as_bool()); // 7 IN (1..10) = true
}

TEST(ExpressionTests, InExprNotWithManyValues) {
// NOT IN with many values where value is not present
auto column = std::make_unique<ConstantExpr>(Value::make_int64(15));
std::vector<std::unique_ptr<Expression>> values;
for (int i = 1; i <= 10; i++) {
values.push_back(std::make_unique<ConstantExpr>(Value::make_int64(i)));
}

InExpr expr(std::move(column), std::move(values), true); // NOT IN
auto result = expr.evaluate();
EXPECT_TRUE(result.as_bool()); // 15 NOT IN (1..10) = true
}

TEST(ExpressionTests, InExprWithNullInList) {
// IN list containing NULL - SQL semantics: 5 IN (1, NULL, 10) = false
// because 5 == NULL is NULL (not true), so NULL doesn't match
auto column = std::make_unique<ConstantExpr>(Value::make_int64(5));
std::vector<std::unique_ptr<Expression>> values;
values.push_back(std::make_unique<ConstantExpr>(Value::make_int64(1)));
values.push_back(std::make_unique<ConstantExpr>(Value::make_null())); // NULL in list
values.push_back(std::make_unique<ConstantExpr>(Value::make_int64(10)));

InExpr expr(std::move(column), std::move(values), false);
auto result = expr.evaluate();
EXPECT_FALSE(result.as_bool()); // 5 IN (1, NULL, 10) = false (NULL doesn't match)
}

TEST(ExpressionTests, InExprEmptyList) {
// IN with empty list
auto column = std::make_unique<ConstantExpr>(Value::make_int64(5));
std::vector<std::unique_ptr<Expression>> values; // empty

InExpr expr(std::move(column), std::move(values), false);
auto result = expr.evaluate();
EXPECT_FALSE(result.as_bool()); // 5 IN () = false
}

} // namespace
7 changes: 4 additions & 3 deletions tests/operator_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,8 @@ TEST_F(OperatorTests, HashJoinRightOuter) {
// RIGHT join output: matched rows + unmatched right rows with NULLs
// Matched: (2, 2)
// Unmatched right: (NULL, 3), (NULL, 4)
std::vector<std::pair<int64_t, int64_t>> results; // (left_value, right_value); use INT64_MIN as sentinel for NULL
std::vector<std::pair<int64_t, int64_t>>
results; // (left_value, right_value); use INT64_MIN as sentinel for NULL
Tuple tuple;
while (join->next(tuple)) {
int64_t left_val = tuple.get(0).is_null() ? INT64_MIN : tuple.get(0).to_int64();
Expand Down Expand Up @@ -880,11 +881,11 @@ TEST_F(OperatorTests, HashJoinNullKeys) {
Schema left_schema = make_schema({{"id", common::ValueType::TYPE_INT64}});
std::vector<Tuple> left_data;
left_data.push_back(make_tuple({common::Value::make_int64(1)})); // matches 1
left_data.push_back(make_tuple({common::Value()})); // NULL - currently matches NULL
left_data.push_back(make_tuple({common::Value()})); // NULL - currently matches NULL

Schema right_schema = make_schema({{"id", common::ValueType::TYPE_INT64}});
std::vector<Tuple> right_data;
right_data.push_back(make_tuple({common::Value()})); // NULL - currently matches
right_data.push_back(make_tuple({common::Value()})); // NULL - currently matches
right_data.push_back(make_tuple({common::Value::make_int64(1)})); // matches 1

auto left_scan = make_buffer_scan("left_table", left_data, left_schema);
Expand Down