diff --git a/src/database/graph_db_accessor.hpp b/src/database/graph_db_accessor.hpp index 9347b1db0..333a5ab70 100644 --- a/src/database/graph_db_accessor.hpp +++ b/src/database/graph_db_accessor.hpp @@ -14,6 +14,11 @@ #include "storage/edge_accessor.hpp" #include "storage/vertex_accessor.hpp" +/** Thrown when creating an index which already exists. */ +class IndexExistsException : public utils::BasicException { + using utils::BasicException::BasicException; +}; + /** * An accessor for the database object: exposes functions * for operating on the database. All the functions in @@ -253,7 +258,7 @@ class GraphDbAccessor { const GraphDbTypes::Property &property) { const LabelPropertyIndex::Key key(label, property); if (db_.label_property_index_.CreateIndex(key) == false) { - throw utils::BasicException( + throw IndexExistsException( "Index is either being created by another transaction or already " "exists."); } diff --git a/src/query/interpreter.hpp b/src/query/interpreter.hpp index 9aef7e9ce..c7a80ba98 100644 --- a/src/query/interpreter.hpp +++ b/src/query/interpreter.hpp @@ -136,7 +136,8 @@ class Interpreter { dynamic_cast(logical_plan.get()) || dynamic_cast(logical_plan.get()) || dynamic_cast(logical_plan.get()) || - dynamic_cast(logical_plan.get())) { + dynamic_cast(logical_plan.get()) || + dynamic_cast(logical_plan.get())) { stream.Header(header); auto cursor = logical_plan->MakeCursor(db_accessor); while (cursor->Pull(frame, symbol_table)) continue; diff --git a/src/query/plan/cost_estimator.cpp b/src/query/plan/cost_estimator.cpp index 8c73934e6..c642bcd5c 100644 --- a/src/query/plan/cost_estimator.cpp +++ b/src/query/plan/cost_estimator.cpp @@ -58,5 +58,6 @@ bool CostEstimator::PostVisit(Unwind &unwind) { } bool CostEstimator::Visit(Once &) { return true; } +bool CostEstimator::Visit(CreateIndex &) { return true; } } // namespace query::plan diff --git a/src/query/plan/cost_estimator.hpp b/src/query/plan/cost_estimator.hpp index 49cfe833a..9f92ef750 100644 --- a/src/query/plan/cost_estimator.hpp +++ b/src/query/plan/cost_estimator.hpp @@ -64,6 +64,7 @@ class CostEstimator : public HierarchicalLogicalOperatorVisitor { bool PostVisit(ExpandUniquenessFilter &) override; bool PostVisit(Unwind &unwind) override; bool Visit(Once &) override; + bool Visit(CreateIndex &) override; auto cost() const { return cost_; } auto cardinality() const { return cardinality_; } diff --git a/src/query/plan/operator.cpp b/src/query/plan/operator.cpp index d0118a517..d5fa4831a 100644 --- a/src/query/plan/operator.cpp +++ b/src/query/plan/operator.cpp @@ -1637,4 +1637,40 @@ void Distinct::DistinctCursor::Reset() { seen_rows_.clear(); } +CreateIndex::CreateIndex(GraphDbTypes::Label label, + GraphDbTypes::Property property) + : label_(label), property_(property) {} + +bool CreateIndex::Accept(HierarchicalLogicalOperatorVisitor &visitor) { + return visitor.Visit(*this); +} + +class CreateIndexCursor : public Cursor { + public: + CreateIndexCursor(CreateIndex &self, GraphDbAccessor &db) + : self_(self), db_(db) {} + + bool Pull(Frame &, const SymbolTable &) override { + if (did_create_) return false; + try { + db_.BuildIndex(self_.label(), self_.property()); + } catch (const IndexExistsException &) { + // Ignore creating an existing index. + } + did_create_ = true; + return true; + } + + void Reset() override { did_create_ = false; } + + private: + const CreateIndex &self_; + GraphDbAccessor &db_; + bool did_create_ = false; +}; + +std::unique_ptr CreateIndex::MakeCursor(GraphDbAccessor &db) { + return std::make_unique(*this, db); +} + } // namespace query::plan diff --git a/src/query/plan/operator.hpp b/src/query/plan/operator.hpp index feb7b958e..a6dc8c8d0 100644 --- a/src/query/plan/operator.hpp +++ b/src/query/plan/operator.hpp @@ -75,6 +75,7 @@ class Merge; class Optional; class Unwind; class Distinct; +class CreateIndex; using LogicalOperatorCompositeVisitor = ::utils::CompositeVisitor< Once, CreateNode, CreateExpand, ScanAll, ScanAllByLabel, Expand, Filter, @@ -83,7 +84,7 @@ using LogicalOperatorCompositeVisitor = ::utils::CompositeVisitor< ExpandUniquenessFilter, Accumulate, AdvanceCommand, Aggregate, Skip, Limit, OrderBy, Merge, Optional, Unwind, Distinct>; -using LogicalOperatorLeafVisitor = ::utils::LeafVisitor; +using LogicalOperatorLeafVisitor = ::utils::LeafVisitor; /** * @brief Base class for hierarhical visitors of @c LogicalOperator class @@ -1287,5 +1288,27 @@ class Distinct : public LogicalOperator { }; }; +/** + * Creates an index for a combination of label and a property. + * + * This operator takes no input and it shouldn't serve as an input to any + * operator. Pulling from the cursor of this operator will create an index in + * the database for the vertices which have the given label and property. In + * case the index already exists, nothing happens. + */ +class CreateIndex : public LogicalOperator { + public: + CreateIndex(GraphDbTypes::Label label, GraphDbTypes::Property property); + bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override; + std::unique_ptr MakeCursor(GraphDbAccessor &db) override; + + auto label() const { return label_; } + auto property() const { return property_; } + + private: + GraphDbTypes::Label label_; + GraphDbTypes::Property property_; +}; + } // namespace plan } // namespace query diff --git a/tests/unit/query_plan_create_set_remove_delete.cpp b/tests/unit/query_plan_create_set_remove_delete.cpp index d217a9f56..c4d461854 100644 --- a/tests/unit/query_plan_create_set_remove_delete.cpp +++ b/tests/unit/query_plan_create_set_remove_delete.cpp @@ -934,3 +934,16 @@ TEST(QueryPlan, RemoveLabelsOnNull) { EXPECT_EQ(0, CountIterable(dba->vertices(false))); EXPECT_EQ(1, PullAll(remove_op, *dba, symbol_table)); } + +TEST(QueryPlan, CreateIndex) { + // CREATE INDEX ON :label(property) + Dbms dbms; + auto dba = dbms.active(); + auto label = dba->label("label"); + auto property = dba->property("property"); + EXPECT_FALSE(dba->LabelPropertyIndexExists(label, property)); + auto create_index = std::make_shared(label, property); + SymbolTable symbol_table; + EXPECT_EQ(PullAll(create_index, *dba, symbol_table), 1); + EXPECT_TRUE(dba->LabelPropertyIndexExists(label, property)); +} diff --git a/tests/unit/query_planner.cpp b/tests/unit/query_planner.cpp index 133f1226d..ade86b657 100644 --- a/tests/unit/query_planner.cpp +++ b/tests/unit/query_planner.cpp @@ -82,6 +82,11 @@ class PlanChecker : public HierarchicalLogicalOperatorVisitor { // Ignore checking Once, it is implicitly at the end. return true; } + + bool Visit(CreateIndex &op) override { + CheckOp(op); + return true; + } #undef PRE_VISIT std::list checkers_;