From cebc15f684b0b5173f1a6db78a1089c72a8a2c52 Mon Sep 17 00:00:00 2001 From: laura-ding <48548375+laura-ding@users.noreply.github.com> Date: Wed, 8 Jul 2020 11:56:57 +0800 Subject: [PATCH] Add alter executor (#52) * Add alter executor * rebase upstream * rebase upstream * rebase upstream * rebase upstream Co-authored-by: dutor <440396+dutor@users.noreply.github.com> --- src/exec/CMakeLists.txt | 1 + src/exec/Executor.cpp | 17 ++- src/exec/maintain/AlterSchemaExecutor.cpp | 51 ++++++++ src/exec/maintain/AlterSchemaExecutor.h | 33 +++++ src/mock/MetaCache.cpp | 153 ++++++++++++++++++++++ src/mock/MetaCache.h | 12 ++ src/mock/MockMetaServiceHandler.cpp | 31 ++++- src/mock/test/SchemaTest.cpp | 70 ++++++++++ src/mock/test/TestBase.h | 82 ++++++++++++ src/planner/Maintain.h | 103 ++++++++++++++- src/planner/PlanNode.cpp | 4 + src/planner/PlanNode.h | 2 + src/validator/MaintainValidator.cpp | 104 +++++++++++++++ src/validator/MaintainValidator.h | 38 ++++++ src/validator/Validator.cpp | 8 +- 15 files changed, 701 insertions(+), 8 deletions(-) create mode 100644 src/exec/maintain/AlterSchemaExecutor.cpp create mode 100644 src/exec/maintain/AlterSchemaExecutor.h diff --git a/src/exec/CMakeLists.txt b/src/exec/CMakeLists.txt index c9af0007..24bea57e 100644 --- a/src/exec/CMakeLists.txt +++ b/src/exec/CMakeLists.txt @@ -32,6 +32,7 @@ nebula_add_library( maintain/CreateEdgeExecutor.cpp maintain/DescTagExecutor.cpp maintain/DescEdgeExecutor.cpp + maintain/AlterSchemaExecutor.cpp mutate/InsertVerticesExecutor.cpp mutate/InsertEdgesExecutor.cpp ) diff --git a/src/exec/Executor.cpp b/src/exec/Executor.cpp index 8deb6271..8a4e89f7 100644 --- a/src/exec/Executor.cpp +++ b/src/exec/Executor.cpp @@ -23,6 +23,7 @@ #include "exec/maintain/CreateTagExecutor.h" #include "exec/maintain/DescEdgeExecutor.h" #include "exec/maintain/DescTagExecutor.h" +#include "exec/maintain/AlterSchemaExecutor.h" #include "exec/mutate/InsertEdgesExecutor.h" #include "exec/mutate/InsertVerticesExecutor.h" #include "exec/query/AggregateExecutor.h" @@ -226,6 +227,13 @@ Executor *Executor::makeExecutor(const PlanNode *node, exec->addDependent(input); break; } + case PlanNode::Kind::kAlterTag: { + auto alterTag = asNode<AlterTag>(node); + auto input = makeExecutor(alterTag->input(), qctx, visited); + exec = new AlterTagExecutor(alterTag, qctx); + exec->addDependent(input); + break; + } case PlanNode::Kind::kCreateEdge: { auto createEdge = asNode<CreateEdge>(node); auto input = makeExecutor(createEdge->input(), qctx, visited); @@ -240,6 +248,13 @@ Executor *Executor::makeExecutor(const PlanNode *node, exec->addDependent(input); break; } + case PlanNode::Kind::kAlterEdge: { + auto alterEdge = asNode<AlterEdge>(node); + auto input = makeExecutor(alterEdge->input(), qctx, visited); + exec = new AlterEdgeExecutor(alterEdge, qctx); + exec->addDependent(input); + break; + } case PlanNode::Kind::kInsertVertices: { auto insertV = asNode<InsertVertices>(node); auto input = makeExecutor(insertV->input(), qctx, visited); @@ -263,7 +278,7 @@ Executor *Executor::makeExecutor(const PlanNode *node, } case PlanNode::Kind::kUnknown: default: - LOG(FATAL) << "Unknown plan node kind."; + LOG(FATAL) << "Unknown plan node kind " << static_cast<int32_t>(node->kind()); break; } diff --git a/src/exec/maintain/AlterSchemaExecutor.cpp b/src/exec/maintain/AlterSchemaExecutor.cpp new file mode 100644 index 00000000..000cd867 --- /dev/null +++ b/src/exec/maintain/AlterSchemaExecutor.cpp @@ -0,0 +1,51 @@ +/* Copyright (c) 2020 vesoft inc. All rights reserved. +* +* This source code is licensed under Apache 2.0 License, +* attached with Common Clause Condition 1.0, found in the LICENSES directory. +*/ + +#include "exec/maintain/AlterSchemaExecutor.h" +#include "planner/Maintain.h" +#include "context/QueryContext.h" + +namespace nebula { +namespace graph { + +folly::Future<Status> AlterTagExecutor::execute() { + dumpLog(); + + auto *aeNode = asNode<AlterTag>(node()); + return qctx()->getMetaClient()->alterTagSchema(aeNode->space(), + aeNode->getName(), + aeNode->getSchemaItems(), + aeNode->getSchemaProp()) + .via(runner()) + .then([](StatusOr<TagID> resp) { + if (!resp.ok()) { + LOG(ERROR) << resp.status(); + return resp.status(); + } + return Status::OK(); + }); +} + +folly::Future<Status> AlterEdgeExecutor::execute() { + dumpLog(); + + auto *aeNode = asNode<AlterEdge>(node()); + return qctx()->getMetaClient()->alterEdgeSchema(aeNode->space(), + aeNode->getName(), + aeNode->getSchemaItems(), + aeNode->getSchemaProp()) + .via(runner()) + .then([](StatusOr<EdgeType> resp) { + if (!resp.ok()) { + LOG(ERROR) << resp.status(); + return resp.status(); + } + return Status::OK(); + }); +} + +} // namespace graph +} // namespace nebula diff --git a/src/exec/maintain/AlterSchemaExecutor.h b/src/exec/maintain/AlterSchemaExecutor.h new file mode 100644 index 00000000..f67093a5 --- /dev/null +++ b/src/exec/maintain/AlterSchemaExecutor.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2020 vesoft inc. All rights reserved. +* +* This source code is licensed under Apache 2.0 License, +* attached with Common Clause Condition 1.0, found in the LICENSES directory. +*/ + +#ifndef EXEC_MAINTAIN_ALTERSCHEMAEXECUTOR_H_ +#define EXEC_MAINTAIN_ALTERSCHEMAEXECUTOR_H_ + +#include "exec/Executor.h" + +namespace nebula { +namespace graph { +class AlterTagExecutor final : public Executor { +public: + AlterTagExecutor(const PlanNode *node, QueryContext *qctx) + : Executor("AlterTagExecutor", node, qctx) {} + + folly::Future<Status> execute() override; +}; + +class AlterEdgeExecutor final : public Executor { +public: + AlterEdgeExecutor(const PlanNode *node, QueryContext *qctx) + : Executor("AlterEdgeExecutor", node, qctx) {} + + folly::Future<Status> execute() override; +}; + +} // namespace graph +} // namespace nebula + +#endif // EXEC_MAINTAIN_ALTERSCHEMAEXECUTOR_H_ diff --git a/src/mock/MetaCache.cpp b/src/mock/MetaCache.cpp index 646428aa..f8e61ebf 100644 --- a/src/mock/MetaCache.cpp +++ b/src/mock/MetaCache.cpp @@ -213,6 +213,46 @@ Status MetaCache::dropEdge(const meta::cpp2::DropEdgeReq& req) { return Status::OK(); } +Status MetaCache::AlterTag(const meta::cpp2::AlterTagReq &req) { + folly::RWSpinLock::WriteHolder holder(lock_); + CHECK_SPACE_ID(req.get_space_id()); + auto tagName = req.get_tag_name(); + auto &tagSchemas = spaceIter->second.tagSchemas_; + auto findIter = tagSchemas.find(tagName); + if (findIter == tagSchemas.end()) { + return Status::Error("Tag `%s' not existed", req.get_tag_name().c_str()); + } + + auto &schema = findIter->second.schema; + auto items = req.get_tag_items(); + auto prop = req.get_schema_prop(); + auto status = alterColumnDefs(schema, items); + if (!status.ok()) { + return status; + } + return alterSchemaProp(schema, prop); +} + +Status MetaCache::AlterEdge(const meta::cpp2::AlterEdgeReq &req) { + folly::RWSpinLock::WriteHolder holder(lock_); + CHECK_SPACE_ID(req.get_space_id()); + auto edgeName = req.get_edge_name(); + auto &edgeSchemas = spaceIter->second.edgeSchemas_; + auto findIter = edgeSchemas.find(edgeName); + if (findIter == edgeSchemas.end()) { + return Status::Error("Edge `%s' not existed", req.get_edge_name().c_str()); + } + + auto &schema = findIter->second.schema; + auto items = req.get_edge_items(); + auto prop = req.get_schema_prop(); + auto status = alterColumnDefs(schema, items); + if (!status.ok()) { + return status; + } + return alterSchemaProp(schema, prop); +} + Status MetaCache::createTagIndex(const meta::cpp2::CreateTagIndexReq&) { return Status::OK(); } @@ -279,5 +319,118 @@ std::unordered_map<PartitionID, std::vector<HostAddr>> MetaCache::getParts() { } return parts; } + +Status MetaCache::alterColumnDefs(meta::cpp2::Schema &schema, + const std::vector<meta::cpp2::AlterSchemaItem> &items) { + std::vector<meta::cpp2::ColumnDef> columns = schema.columns; + for (auto& item : items) { + auto& cols = item.get_schema().get_columns(); + auto op = item.op; + for (auto& col : cols) { + switch (op) { + case meta::cpp2::AlterSchemaOp::ADD: + for (auto it = schema.columns.begin(); it != schema.columns.end(); ++it) { + if (it->get_name() == col.get_name()) { + return Status::Error("Column existing: `%s'", col.get_name().c_str()); + } + } + columns.emplace_back(col); + break; + case meta::cpp2::AlterSchemaOp::CHANGE: { + bool isOk = false; + for (auto it = columns.begin(); it != columns.end(); ++it) { + auto colName = col.get_name(); + if (colName == it->get_name()) { + // If this col is ttl_col, change not allowed + if (schema.schema_prop.__isset.ttl_col && + (*schema.schema_prop.get_ttl_col() == colName)) { + return Status::Error("Column: `%s' as ttl_col, change not allowed", + colName.c_str()); + } + *it = col; + isOk = true; + break; + } + } + if (!isOk) { + return Status::Error("Column not found: `%s'", col.get_name().c_str()); + } + break; + } + case meta::cpp2::AlterSchemaOp::DROP: { + bool isOk = false; + for (auto it = columns.begin(); it != columns.end(); ++it) { + auto colName = col.get_name(); + if (colName == it->get_name()) { + if (schema.schema_prop.__isset.ttl_col && + (*schema.schema_prop.get_ttl_col() == colName)) { + schema.schema_prop.set_ttl_duration(0); + schema.schema_prop.set_ttl_col(""); + } + columns.erase(it); + isOk = true; + break; + } + } + if (!isOk) { + return Status::Error("Column not found: `%s'", col.get_name().c_str()); + } + break; + } + default: + return Status::Error("Alter schema operator not supported"); + } + } + } + schema.columns = std::move(columns); + return Status::OK(); +} + +Status MetaCache::alterSchemaProp(meta::cpp2::Schema &schema, + const meta::cpp2::SchemaProp &alterSchemaProp) { + meta::cpp2::SchemaProp schemaProp = schema.get_schema_prop(); + if (alterSchemaProp.__isset.ttl_duration) { + // Graph check <=0 to = 0 + schemaProp.set_ttl_duration(*alterSchemaProp.get_ttl_duration()); + } + if (alterSchemaProp.__isset.ttl_col) { + auto ttlCol = *alterSchemaProp.get_ttl_col(); + // Disable ttl, ttl_col is empty, ttl_duration is 0 + if (ttlCol.empty()) { + schemaProp.set_ttl_duration(0); + schemaProp.set_ttl_col(ttlCol); + return Status::OK(); + } + + auto existed = false; + for (auto& col : schema.columns) { + if (col.get_name() == ttlCol) { + // Only integer and timestamp columns can be used as ttl_col + if (col.type != meta::cpp2::PropertyType::INT32 && + col.type != meta::cpp2::PropertyType::INT64 && + col.type != meta::cpp2::PropertyType::TIMESTAMP) { + return Status::Error("TTL column type illegal"); + } + existed = true; + schemaProp.set_ttl_col(ttlCol); + break; + } + } + + if (!existed) { + return Status::Error("TTL column not found: `%s'", ttlCol.c_str()); + } + } + + // Disable implicit TTL mode + if ((schemaProp.get_ttl_duration() && (*schemaProp.get_ttl_duration() != 0)) && + (!schemaProp.get_ttl_col() || (schemaProp.get_ttl_col() && + schemaProp.get_ttl_col()->empty()))) { + return Status::Error("Implicit ttl_col not support"); + } + + schema.set_schema_prop(std::move(schemaProp)); + return Status::OK(); +} } // namespace graph } // namespace nebula diff --git a/src/mock/MetaCache.h b/src/mock/MetaCache.h index d183a480..e8704a68 100644 --- a/src/mock/MetaCache.h +++ b/src/mock/MetaCache.h @@ -45,6 +45,10 @@ public: Status dropEdge(const meta::cpp2::DropEdgeReq &req); + Status AlterTag(const meta::cpp2::AlterTagReq &req); + + Status AlterEdge(const meta::cpp2::AlterEdgeReq &req); + Status createTagIndex(const meta::cpp2::CreateTagIndexReq &req); Status createEdgeIndex(const meta::cpp2::CreateEdgeIndexReq &req); @@ -68,6 +72,14 @@ public: private: MetaCache() = default; + Status alterColumnDefs(meta::cpp2::Schema &schema, + const std::vector<meta::cpp2::AlterSchemaItem> &items); + + Status alterSchemaProp(meta::cpp2::Schema &schema, + const meta::cpp2::SchemaProp &alterSchemaProp); + + +private: enum class EntryType : int8_t { SPACE = 0x01, TAG = 0x02, diff --git a/src/mock/MockMetaServiceHandler.cpp b/src/mock/MockMetaServiceHandler.cpp index 4ace2fa3..a51e2549 100644 --- a/src/mock/MockMetaServiceHandler.cpp +++ b/src/mock/MockMetaServiceHandler.cpp @@ -188,8 +188,20 @@ MockMetaServiceHandler::future_createTag(const meta::cpp2::CreateTagReq& req) { } folly::Future<meta::cpp2::ExecResp> -MockMetaServiceHandler::future_alterTag(const meta::cpp2::AlterTagReq&) { - RETURN_SUCCESSED(); +MockMetaServiceHandler::future_alterTag(const meta::cpp2::AlterTagReq &req) { + folly::Promise<meta::cpp2::ExecResp> promise; + auto future = promise.getFuture(); + meta::cpp2::ExecResp resp; + auto status = MetaCache::instance().AlterTag(req); + if (!status.ok()) { + LOG(ERROR) << status; + resp.set_code(meta::cpp2::ErrorCode::E_UNKNOWN); + promise.setValue(std::move(resp)); + return future; + } + resp.set_code(meta::cpp2::ErrorCode::SUCCEEDED); + promise.setValue(std::move(resp)); + return future; } folly::Future<meta::cpp2::ExecResp> @@ -264,8 +276,19 @@ MockMetaServiceHandler::future_createEdge(const meta::cpp2::CreateEdgeReq& req) } folly::Future<meta::cpp2::ExecResp> -MockMetaServiceHandler::future_alterEdge(const meta::cpp2::AlterEdgeReq&) { - RETURN_SUCCESSED(); +MockMetaServiceHandler::future_alterEdge(const meta::cpp2::AlterEdgeReq &req) { + folly::Promise<meta::cpp2::ExecResp> promise; + auto future = promise.getFuture(); + meta::cpp2::ExecResp resp; + auto status = MetaCache::instance().AlterEdge(req); + if (!status.ok()) { + resp.set_code(meta::cpp2::ErrorCode::E_UNKNOWN); + promise.setValue(std::move(resp)); + return future; + } + resp.set_code(meta::cpp2::ErrorCode::SUCCEEDED); + promise.setValue(std::move(resp)); + return future; } folly::Future<meta::cpp2::ExecResp> diff --git a/src/mock/test/SchemaTest.cpp b/src/mock/test/SchemaTest.cpp index 9afda2df..ae65abb2 100644 --- a/src/mock/test/SchemaTest.cpp +++ b/src/mock/test/SchemaTest.cpp @@ -156,6 +156,76 @@ TEST_F(SchemaTest, TestEdge) { } } +TEST_F(SchemaTest, TestAlterTag) { + { + cpp2::ExecutionResponse resp; + std::string query = "CREATE TAG alterTag(col1 STRING, col2 INT8, " + "col3 DOUBLE, col4 FIXED_STRING(10));"; + auto code = client_->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "ALTER TAG alterTag " + "ADD (col5 TIMESTAMP, col6 DATE NOT NULL), " + "CHANGE (col2 INT8 DEFAULT 10), " + "DROP (col4)"; + auto code = client_->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "DESC TAG alterTag;"; + auto code = client_->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + ASSERT_TRUE(resp.__isset.data); + std::vector<std::string> colNames = {"Field", "Type", "Null", "Default"}; + ASSERT_TRUE(verifyColNames(resp, colNames)); + std::vector<std::vector<Value>> values = { + {Value("col1"), Value("string"), Value("YES"), Value()}, + {Value("col2"), Value("int8"), Value("YES"), Value(10)}, + {Value("col3"), Value("double"), Value("YES"), Value()}, + {Value("col5"), Value("timestamp"), Value("YES"), Value()}, + {Value("col6"), Value("date"), Value("NO"), Value()}}; + ASSERT_TRUE(verifyValues(resp, values)); + } +} + +TEST_F(SchemaTest, TestAlterEdge) { + { + cpp2::ExecutionResponse resp; + std::string query = "CREATE EDGE alterEdge(col1 STRING, col2 INT8, " + "col3 DOUBLE, col4 FIXED_STRING(10));"; + auto code = client_->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "ALTER EDGE alterEdge " + "ADD (col5 TIMESTAMP, col6 DATE NOT NULL), " + "CHANGE (col2 INT8 DEFAULT 10), " + "DROP (col4)"; + auto code = client_->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "DESC EDGE alterEdge;"; + auto code = client_->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + ASSERT_TRUE(resp.__isset.data); + std::vector<std::string> colNames = {"Field", "Type", "Null", "Default"}; + ASSERT_TRUE(verifyColNames(resp, colNames)); + std::vector<std::vector<Value>> values = { + {Value("col1"), Value("string"), Value("YES"), Value()}, + {Value("col2"), Value("int8"), Value("YES"), Value(10)}, + {Value("col3"), Value("double"), Value("YES"), Value()}, + {Value("col5"), Value("timestamp"), Value("YES"), Value()}, + {Value("col6"), Value("date"), Value("NO"), Value()}}; + ASSERT_TRUE(verifyValues(resp, values)); + } +} + TEST_F(SchemaTest, TestInsert) { sleep(FLAGS_heartbeat_interval_secs + 1); { diff --git a/src/mock/test/TestBase.h b/src/mock/test/TestBase.h index 21203192..c90825af 100644 --- a/src/mock/test/TestBase.h +++ b/src/mock/test/TestBase.h @@ -19,6 +19,88 @@ protected: void SetUp() override; void TearDown() override; + + static ::testing::AssertionResult TestOK() { + return ::testing::AssertionSuccess(); + } + + static ::testing::AssertionResult TestError() { + return ::testing::AssertionFailure(); + } + + ::testing::AssertionResult verifyColNames(const cpp2::ExecutionResponse &resp, + const std::vector<std::string> &expected) { + if (resp.get_error_code() != cpp2::ErrorCode::SUCCEEDED) { + return TestError() << "query failed: " + << cpp2::_ErrorCode_VALUES_TO_NAMES.at(resp.get_error_code()); + } + bool hasData = resp.__isset.data; + if (!hasData && expected.empty()) { + return TestOK(); + } + + if (!hasData) { + return TestError() << "data is empty"; + } + + auto colNames = resp.get_data()->colNames; + + if (colNames.size() != expected.size()) { + return TestError() << "ColNames' count not match: " + << colNames.size() << " vs. " << expected.size(); + } + for (auto i = 0u; i < colNames.size(); i++) { + if (colNames[i] != expected[i]) { + return TestError() << "wrong size, result size: " << colNames.size() + << ", expect size: " << expected.size(); + } + } + return TestOK(); + } + + ::testing::AssertionResult verifyValues(const cpp2::ExecutionResponse &resp, + const std::vector<Value> &expected) { + std::vector<std::vector<Value>> temp; + temp.emplace_back(expected); + return verifyValues(resp, temp); + } + + ::testing::AssertionResult verifyValues(const cpp2::ExecutionResponse &resp, + const std::vector<std::vector<Value>> &expected) { + if (resp.get_error_code() != cpp2::ErrorCode::SUCCEEDED) { + return TestError() << "query failed: " + << cpp2::_ErrorCode_VALUES_TO_NAMES.at(resp.get_error_code()); + } + + bool hasData = resp.__isset.data; + if (!hasData && expected.empty()) { + return TestOK(); + } + + if (!hasData) { + return TestError() << "data is empty"; + } + + auto rows = resp.get_data()->rows; + + if (rows.size() != expected.size()) { + return TestError() << "rows' count not match: " + << rows.size() << " vs. " << expected.size(); + } + + for (auto i = 0u; i < rows.size(); i++) { + if (rows[i].values.size() != expected[i].size()) { + return TestError() << "The row[" << i << "]' size not match " + << rows[i].values.size() << " vs. " << expected[i].size(); + } + for (auto j = 0u; j < rows[i].values.size(); j++) { + if (rows[i].values[j] != expected[i][j]) { + return TestError() << rows[i].values[j] << " vs. " << expected[i][j]; + } + } + } + return TestOK(); + } }; } // namespace graph diff --git a/src/planner/Maintain.h b/src/planner/Maintain.h index 7f59655a..1ba43ed5 100644 --- a/src/planner/Maintain.h +++ b/src/planner/Maintain.h @@ -112,18 +112,117 @@ private: } }; -class AlterTag final : public SingleInputNode { +class AlterSchemaNode : public SingleInputNode { +protected: + AlterSchemaNode(ExecutionPlan* plan, + Kind kind, + PlanNode* input, + GraphSpaceID space, + std::string name, + std::vector<meta::cpp2::AlterSchemaItem> items, + meta::cpp2::SchemaProp schemaProp) + : SingleInputNode(plan, kind, input) + , space_(space) + , name_(std::move(name)) + , schemaItems_(std::move(items)) + , schemaProp_(std::move(schemaProp)) {} + +public: + const std::string& getName() const { + return name_; + } + + const std::vector<meta::cpp2::AlterSchemaItem>& getSchemaItems() const { + return schemaItems_; + } + + const meta::cpp2::SchemaProp& getSchemaProp() const { + return schemaProp_; + } + + GraphSpaceID space() const { + return space_; + } + +protected: + GraphSpaceID space_; + std::string name_; + std::vector<meta::cpp2::AlterSchemaItem> schemaItems_; + meta::cpp2::SchemaProp schemaProp_; +}; + +class AlterTag final : public AlterSchemaNode { public: + static AlterTag* make(ExecutionPlan* plan, + PlanNode* input, + GraphSpaceID space, + std::string name, + std::vector<meta::cpp2::AlterSchemaItem> items, + meta::cpp2::SchemaProp schemaProp) { + return new AlterTag(plan, + input, + space, + std::move(name), + std::move(items), + std::move(schemaProp)); + } + std::string explain() const override { return "AlterTag"; } + +private: + AlterTag(ExecutionPlan* plan, + PlanNode* input, + GraphSpaceID space, + std::string name, + std::vector<meta::cpp2::AlterSchemaItem> items, + meta::cpp2::SchemaProp schemaProp) + : AlterSchemaNode(plan, + Kind::kAlterTag, + input, + space, + std::move(name), + std::move(items), + std::move(schemaProp)) { + } }; -class AlterEdge final : public SingleInputNode { +class AlterEdge final : public AlterSchemaNode { public: + static AlterEdge* make(ExecutionPlan* plan, + PlanNode* input, + GraphSpaceID space, + std::string name, + std::vector<meta::cpp2::AlterSchemaItem> items, + meta::cpp2::SchemaProp schemaProp) { + return new AlterEdge(plan, + input, + space, + std::move(name), + std::move(items), + std::move(schemaProp)); + } + std::string explain() const override { return "AlterEdge"; } + +private: + AlterEdge(ExecutionPlan* plan, + PlanNode* input, + GraphSpaceID space, + std::string name, + std::vector<meta::cpp2::AlterSchemaItem> items, + meta::cpp2::SchemaProp schemaProp) + : AlterSchemaNode(plan, + Kind::kAlterEdge, + input, + space, + std::move(name), + std::move(items), + std::move(schemaProp)) { + } }; class DescSchema : public SingleInputNode { diff --git a/src/planner/PlanNode.cpp b/src/planner/PlanNode.cpp index 12af9956..a81ec7c6 100644 --- a/src/planner/PlanNode.cpp +++ b/src/planner/PlanNode.cpp @@ -68,6 +68,10 @@ const char* PlanNode::toString(Kind kind) { return "DescTag"; case PlanNode::Kind::kDescEdge: return "DescEdge"; + case PlanNode::Kind::kAlterTag: + return "AlterTag"; + case PlanNode::Kind::kAlterEdge: + return "AlterEdge"; case PlanNode::Kind::kInsertVertices: return "InsertVertices"; case PlanNode::Kind::kInsertEdges: diff --git a/src/planner/PlanNode.h b/src/planner/PlanNode.h index 1cf3d117..459db15a 100644 --- a/src/planner/PlanNode.h +++ b/src/planner/PlanNode.h @@ -48,6 +48,8 @@ public: kDescSpace, kDescTag, kDescEdge, + kAlterTag, + kAlterEdge, kInsertVertices, kInsertEdges, kDataCollect, diff --git a/src/validator/MaintainValidator.cpp b/src/validator/MaintainValidator.cpp index 78bd8066..b57901f7 100644 --- a/src/validator/MaintainValidator.cpp +++ b/src/validator/MaintainValidator.cpp @@ -126,5 +126,109 @@ Status DescEdgeValidator::toPlan() { tail_ = root_; return Status::OK(); } + +Status AlterValidator::alterSchema(const std::vector<AlterSchemaOptItem*>& schemaOpts, + const std::vector<SchemaPropItem*>& schemaProps) { + for (auto& schemaOpt : schemaOpts) { + meta::cpp2::AlterSchemaItem schemaItem; + auto opType = schemaOpt->toType(); + schemaItem.set_op(opType); + meta::cpp2::Schema schema; + if (opType == meta::cpp2::AlterSchemaOp::DROP) { + const auto& colNames = schemaOpt->columnNames(); + for (auto& colName : colNames) { + meta::cpp2::ColumnDef column; + column.name = *colName; + schema.columns.emplace_back(std::move(column)); + } + } else { + const auto& specs = schemaOpt->columnSpecs(); + for (auto& spec : specs) { + meta::cpp2::ColumnDef column; + column.name = *spec->name(); + column.type = spec->type(); + if (spec->hasDefaultValue()) { + column.set_default_value(spec->getDefaultValue()); + } + if (spec->type() == meta::cpp2::PropertyType::FIXED_STRING) { + column.set_type_length(spec->typeLen()); + } + if (spec->isNull()) { + column.set_nullable(true); + } + schema.columns.emplace_back(std::move(column)); + } + } + + schemaItem.set_schema(std::move(schema)); + schemaItems_.emplace_back(std::move(schemaItem)); + } + + for (auto& schemaProp : schemaProps) { + auto propType = schemaProp->getPropType(); + StatusOr<int64_t> retInt; + StatusOr<std::string> retStr; + int ttlDuration; + switch (propType) { + case SchemaPropItem::TTL_DURATION: + retInt = schemaProp->getTtlDuration(); + if (!retInt.ok()) { + return retInt.status(); + } + ttlDuration = retInt.value(); + schemaProp_.set_ttl_duration(ttlDuration); + break; + case SchemaPropItem::TTL_COL: + // Check the legality of the column in meta + retStr = schemaProp->getTtlCol(); + if (!retStr.ok()) { + return retStr.status(); + } + schemaProp_.set_ttl_col(retStr.value()); + break; + default: + return Status::Error("Property type not support"); + } + } + return Status::OK(); +} + +Status AlterTagValidator::validateImpl() { + auto sentence = static_cast<AlterTagSentence*>(sentence_); + name_ = *sentence->name(); + return alterSchema(sentence->getSchemaOpts(), sentence->getSchemaProps()); +} + +Status AlterTagValidator::toPlan() { + auto* plan = qctx_->plan(); + auto *doNode = AlterTag::make(plan, + nullptr, + vctx_->whichSpace().id, + std::move(name_), + std::move(schemaItems_), + std::move(schemaProp_)); + root_ = doNode; + tail_ = root_; + return Status::OK(); +} + +Status AlterEdgeValidator::validateImpl() { + auto sentence = static_cast<AlterEdgeSentence*>(sentence_); + name_ = *sentence->name(); + return alterSchema(sentence->getSchemaOpts(), sentence->getSchemaProps()); +} + +Status AlterEdgeValidator::toPlan() { + auto* plan = qctx_->plan(); + auto *doNode = AlterEdge::make(plan, + nullptr, + vctx_->whichSpace().id, + std::move(name_), + std::move(schemaItems_), + std::move(schemaProp_)); + root_ = doNode; + tail_ = root_; + return Status::OK(); +} } // namespace graph } // namespace nebula diff --git a/src/validator/MaintainValidator.h b/src/validator/MaintainValidator.h index f03a3503..d4382423 100644 --- a/src/validator/MaintainValidator.h +++ b/src/validator/MaintainValidator.h @@ -77,6 +77,44 @@ private: Status toPlan() override; }; +class AlterValidator : public Validator { +public: + AlterValidator(Sentence* sentence, QueryContext* context) + : Validator(sentence, context) {} +protected: + Status alterSchema(const std::vector<AlterSchemaOptItem*>& schemaOpts, + const std::vector<SchemaPropItem*>& schemaProps); + +protected: + std::vector<meta::cpp2::AlterSchemaItem> schemaItems_; + meta::cpp2::SchemaProp schemaProp_; + std::string name_; +}; + +class AlterTagValidator final : public AlterValidator { +public: + AlterTagValidator(Sentence* sentence, QueryContext* context) + : AlterValidator(sentence, context) { + } + +private: + Status validateImpl() override; + + Status toPlan() override; +}; + +class AlterEdgeValidator final : public AlterValidator { +public: + AlterEdgeValidator(Sentence* sentence, QueryContext* context) + : AlterValidator(sentence, context) { + } + +private: + Status validateImpl() override; + + Status toPlan() override; +}; + } // namespace graph } // namespace nebula #endif // VALIDATOR_MAINTAINVALIDATOR_H_ diff --git a/src/validator/Validator.cpp b/src/validator/Validator.cpp index 31edaa79..7b449369 100644 --- a/src/validator/Validator.cpp +++ b/src/validator/Validator.cpp @@ -54,6 +54,10 @@ std::unique_ptr<Validator> Validator::makeValidator(Sentence* sentence, QueryCon return std::make_unique<DescTagValidator>(sentence, context); case Sentence::Kind::kDescribeEdge: return std::make_unique<DescEdgeValidator>(sentence, context); + case Sentence::Kind::kAlterTag: + return std::make_unique<AlterTagValidator>(sentence, context); + case Sentence::Kind::kAlterEdge: + return std::make_unique<AlterEdgeValidator>(sentence, context); case Sentence::Kind::kInsertVertices: return std::make_unique<InsertVerticesValidator>(sentence, context); case Sentence::Kind::kInsertEdges: @@ -81,7 +85,9 @@ Status Validator::appendPlan(PlanNode* node, PlanNode* appended) { case PlanNode::Kind::kDescEdge: case PlanNode::Kind::kInsertVertices: case PlanNode::Kind::kInsertEdges: - case PlanNode::Kind::kGetNeighbors: { + case PlanNode::Kind::kGetNeighbors: + case PlanNode::Kind::kAlterTag: + case PlanNode::Kind::kAlterEdge: { static_cast<SingleInputNode*>(node)->setInput(appended); break; } -- GitLab