diff --git a/src/optimizer/rule_impls.cpp b/src/optimizer/rule_impls.cpp index e540555c9e3..5b094252cf7 100644 --- a/src/optimizer/rule_impls.cpp +++ b/src/optimizer/rule_impls.cpp @@ -6,7 +6,7 @@ // // Identification: src/optimizer/rule_impls.cpp // -// Copyright (c) 2015-16, Carnegie Mellon University Database Group +// Copyright (c) 2015-2018, Carnegie Mellon University Database Group // //===----------------------------------------------------------------------===// @@ -386,17 +386,22 @@ void GetToIndexScan::Transform( std::vector index_key_column_id_list; std::vector index_expr_type_list; std::vector index_value_list; - std::unordered_set index_col_set( - index_object->GetKeyAttrs().begin(), - index_object->GetKeyAttrs().end()); - for (size_t offset = 0; offset < key_column_id_list.size(); offset++) { - auto col_id = key_column_id_list[offset]; - if (index_col_set.find(col_id) != index_col_set.end()) { - index_key_column_id_list.push_back(col_id); + + // Only pick the index if the query columns match the index's columns in + // the same order. + auto index_id_list = index_object->GetKeyAttrs(); + for (size_t offset = 0; (offset < key_column_id_list.size()) && + (offset < index_id_list.size()); + offset++) { + if (index_id_list[offset] == key_column_id_list[offset]) { + index_key_column_id_list.push_back(key_column_id_list[offset]); index_expr_type_list.push_back(expr_type_list[offset]); index_value_list.push_back(value_list[offset]); + } else { + break; } } + // Add transformed plan if (!index_key_column_id_list.empty()) { auto index_scan_op = PhysicalIndexScan::make( diff --git a/test/optimizer/optimizer_test.cpp b/test/optimizer/optimizer_test.cpp index 99348b12b44..d17cb419324 100644 --- a/test/optimizer/optimizer_test.cpp +++ b/test/optimizer/optimizer_test.cpp @@ -28,6 +28,7 @@ #include "planner/update_plan.h" #include "sql/testing_sql_util.h" #include "planner/seq_scan_plan.h" +#include "planner/index_scan_plan.h" #include "planner/abstract_join_plan.h" #include "planner/hash_join_plan.h" #include "binder/bind_node_visitor.h" @@ -494,5 +495,90 @@ TEST_F(OptimizerTests, ExecuteTaskStackTest) { ASSERT_GT(timer.GetDuration(), start_time); } +TEST_F(OptimizerTests, MultiColumnIndexScanPlanTest) { + auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); + auto txn = txn_manager.BeginTransaction(); + catalog::Catalog::GetInstance()->CreateDatabase(DEFAULT_DB_NAME, txn); + txn_manager.CommitTransaction(txn); + + auto tuple_count = 100; + + // Create table and insert tuples. + TestingSQLUtil::ExecuteSQLQuery( + "CREATE TABLE test(a INT, b INT, c INT, d INT, e INT);"); + + for (auto i = 0; i < tuple_count; i++) { + std::stringstream oss; + oss << "INSERT into test VALUES(" << i << "," << i + 1 << "," << i + 2 + << "," << i + 3 << "," << i + 4 << ");"; + TestingSQLUtil::ExecuteSQLQuery(oss.str()); + } + + // Create a multi-column index + TestingSQLUtil::ExecuteSQLQuery("CREATE INDEX Index1 on test(a, c, d, e);"); + + txn = txn_manager.BeginTransaction(); + optimizer::Optimizer optimizer; + auto &peloton_parser = parser::PostgresParser::GetInstance(); + + auto create_stmt = + peloton_parser.BuildParseTree("SELECT * FROM test where e = 8"); + auto plan = optimizer.BuildPelotonPlanTree(create_stmt, DEFAULT_DB_NAME, txn); + EXPECT_EQ(plan->GetPlanNodeType(), PlanNodeType::SEQSCAN); + + create_stmt = + peloton_parser.BuildParseTree("SELECT * FROM test where c = 4 and e = 6"); + plan = optimizer.BuildPelotonPlanTree(create_stmt, DEFAULT_DB_NAME, txn); + EXPECT_EQ(plan->GetPlanNodeType(), PlanNodeType::SEQSCAN); + + create_stmt = + peloton_parser.BuildParseTree("SELECT * FROM test where a = 4 and e = 8"); + plan = optimizer.BuildPelotonPlanTree(create_stmt, DEFAULT_DB_NAME, txn); + EXPECT_EQ(plan->GetPlanNodeType(), PlanNodeType::INDEXSCAN); + auto index_scan_plan = static_cast(plan.get()); + EXPECT_EQ(index_scan_plan->GetKeyColumnIds().size(), 1); + EXPECT_EQ(index_scan_plan->GetKeyColumnIds()[0], 0); + + create_stmt = + peloton_parser.BuildParseTree("SELECT * FROM test where a = 4 and c = 6"); + plan = optimizer.BuildPelotonPlanTree(create_stmt, DEFAULT_DB_NAME, txn); + EXPECT_EQ(plan->GetPlanNodeType(), PlanNodeType::INDEXSCAN); + index_scan_plan = static_cast(plan.get()); + EXPECT_EQ(index_scan_plan->GetKeyColumnIds().size(), 2); + EXPECT_EQ(index_scan_plan->GetKeyColumnIds()[0], 0); + EXPECT_EQ(index_scan_plan->GetKeyColumnIds()[1], 2); + + create_stmt = peloton_parser.BuildParseTree( + "SELECT * FROM test where a = 4 and c = 6 and d = 7"); + plan = optimizer.BuildPelotonPlanTree(create_stmt, DEFAULT_DB_NAME, txn); + EXPECT_EQ(plan->GetPlanNodeType(), PlanNodeType::INDEXSCAN); + index_scan_plan = static_cast(plan.get()); + EXPECT_EQ(index_scan_plan->GetKeyColumnIds().size(), 3); + EXPECT_EQ(index_scan_plan->GetKeyColumnIds()[0], 0); + EXPECT_EQ(index_scan_plan->GetKeyColumnIds()[1], 2); + EXPECT_EQ(index_scan_plan->GetKeyColumnIds()[2], 3); + + create_stmt = peloton_parser.BuildParseTree( + "SELECT * FROM test where a = 4 and b = 5 and c = 6 and d = 7"); + plan = optimizer.BuildPelotonPlanTree(create_stmt, DEFAULT_DB_NAME, txn); + EXPECT_EQ(plan->GetPlanNodeType(), PlanNodeType::INDEXSCAN); + index_scan_plan = static_cast(plan.get()); + EXPECT_EQ(index_scan_plan->GetKeyColumnIds().size(), 1); + EXPECT_EQ(index_scan_plan->GetKeyColumnIds()[0], 0); + + create_stmt = peloton_parser.BuildParseTree( + "SELECT * FROM test where a = 4 and c = 6 and d = 7 and e = 8"); + plan = optimizer.BuildPelotonPlanTree(create_stmt, DEFAULT_DB_NAME, txn); + EXPECT_EQ(plan->GetPlanNodeType(), PlanNodeType::INDEXSCAN); + index_scan_plan = static_cast(plan.get()); + EXPECT_EQ(index_scan_plan->GetKeyColumnIds().size(), 4); + EXPECT_EQ(index_scan_plan->GetKeyColumnIds()[0], 0); + EXPECT_EQ(index_scan_plan->GetKeyColumnIds()[1], 2); + EXPECT_EQ(index_scan_plan->GetKeyColumnIds()[2], 3); + EXPECT_EQ(index_scan_plan->GetKeyColumnIds()[3], 4); + + txn_manager.CommitTransaction(txn); +} + } // namespace test } // namespace peloton