diff --git a/src/exec/CMakeLists.txt b/src/exec/CMakeLists.txt index 46a2d78bfedd7e72d7fbb34784f32681d9400693..1f91ab9d2528f56ce9f71932f1558dbb1d69438c 100644 --- a/src/exec/CMakeLists.txt +++ b/src/exec/CMakeLists.txt @@ -30,6 +30,7 @@ nebula_add_library( admin/ShowHostsExecutor.cpp admin/SpaceExecutor.cpp admin/SnapshotExecutor.cpp + admin/PartExecutor.cpp maintain/TagExecutor.cpp maintain/EdgeExecutor.cpp mutate/InsertExecutor.cpp diff --git a/src/exec/Executor.cpp b/src/exec/Executor.cpp index d1f1a2af5dfd44098cb7262fa23aeb97e293a703..a69641fcd0d287bdbea70cc8ec4a952c15207fe6 100644 --- a/src/exec/Executor.cpp +++ b/src/exec/Executor.cpp @@ -17,6 +17,7 @@ #include "exec/admin/SnapshotExecutor.h" #include "exec/admin/SpaceExecutor.h" #include "exec/admin/SwitchSpaceExecutor.h" +#include "exec/admin/PartExecutor.h" #include "exec/logic/LoopExecutor.h" #include "exec/logic/MultiOutputsExecutor.h" #include "exec/logic/SelectExecutor.h" @@ -407,6 +408,13 @@ Executor *Executor::makeExecutor(const PlanNode *node, exec->dependsOn(input); break; } + case PlanNode::Kind::kShowParts: { + auto showParts = asNode<ShowParts>(node); + auto input = makeExecutor(showParts->dep(), qctx, visited); + exec = new ShowPartsExecutor(showParts, qctx); + exec->dependsOn(input); + break; + } case PlanNode::Kind::kUnknown: default: LOG(FATAL) << "Unknown plan node kind " << static_cast<int32_t>(node->kind()); diff --git a/src/exec/admin/PartExecutor.cpp b/src/exec/admin/PartExecutor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9963e8344a874fea5f6f0f17372b61c47df4bc0e --- /dev/null +++ b/src/exec/admin/PartExecutor.cpp @@ -0,0 +1,56 @@ +/* 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/admin/PartExecutor.h" +#include "planner/Admin.h" +#include "context/QueryContext.h" +#include "util/ScopedTimer.h" + +namespace nebula { +namespace graph { +folly::Future<Status> ShowPartsExecutor::execute() { + SCOPED_TIMER(&execTime_); + + auto *spNode = asNode<ShowParts>(node()); + return qctx()->getMetaClient()->listParts(spNode->getSpaceId(), spNode->getPartIds()) + .via(runner()) + .then([this](StatusOr<std::vector<meta::cpp2::PartItem>> resp) { + if (!resp.ok()) { + LOG(ERROR) << resp.status(); + return resp.status(); + } + auto partItems = std::move(resp).value(); + + std::sort(partItems.begin(), partItems.end(), + [] (const auto& a, const auto& b) { + return a.get_part_id() < b.get_part_id(); + }); + + DataSet dataSet({"Partition ID", "Leader", "Peers", "Losts"}); + for (auto& item : partItems) { + Row row; + row.values.resize(4); + row.values[0].setInt(item.get_part_id()); + + if (item.__isset.leader) { + std::string leaderStr = NetworkUtils::toHostsStr({*item.get_leader()}); + row.values[1].setStr(std::move(leaderStr)); + } else { + row.values[1].setStr(""); + } + + row.values[2].setStr(NetworkUtils::toHostsStr(item.get_peers())); + row.values[3].setStr(NetworkUtils::toHostsStr(item.get_losts())); + dataSet.emplace_back(std::move(row)); + } + return finish(ResultBuilder() + .value(Value(std::move(dataSet))) + .iter(Iterator::Kind::kDefault) + .finish()); + }); +} +} // namespace graph +} // namespace nebula diff --git a/src/exec/admin/PartExecutor.h b/src/exec/admin/PartExecutor.h new file mode 100644 index 0000000000000000000000000000000000000000..e1806161fa194001fe3364aae22868ea28e50da2 --- /dev/null +++ b/src/exec/admin/PartExecutor.h @@ -0,0 +1,26 @@ +/* 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_ADMIN_PARTEXECUTOR_H_ +#define EXEC_ADMIN_PARTEXECUTOR_H_ + +#include "exec/Executor.h" + +namespace nebula { +namespace graph { + +class ShowPartsExecutor final : public Executor { +public: + ShowPartsExecutor(const PlanNode *node, QueryContext *ectx) + : Executor("ShowPartsExecutor", node, ectx) {} + + folly::Future<Status> execute() override; +}; + +} // namespace graph +} // namespace nebula + +#endif // EXEC_ADMIN_PARTEXECUTOR_H_ diff --git a/src/parser/AdminSentences.h b/src/parser/AdminSentences.h index 40fc531de92a1c38d1dde3378973bf0d0a2de677..0a2f7b1ad008bd6ff8cf30ba4a275bb98644d877 100644 --- a/src/parser/AdminSentences.h +++ b/src/parser/AdminSentences.h @@ -58,6 +58,11 @@ public: list_.reset(list); kind_ = Kind::kShowParts; } + + std::vector<int32_t>* getList() const { + return list_.get(); + } + std::string toString() const override; private: diff --git a/src/planner/Admin.h b/src/planner/Admin.h index 25e1a59b4760eccfc23aa8d647956d1debf77094..188b3b1b32194658e790a7c5c86493bc2e7891d2 100644 --- a/src/planner/Admin.h +++ b/src/planner/Admin.h @@ -271,6 +271,42 @@ public: return "Ingest"; } }; + +class ShowParts final : public SingleInputNode { +public: + static ShowParts* make(ExecutionPlan* plan, + PlanNode* input, + GraphSpaceID spaceId, + std::vector<PartitionID> partIds) { + return new ShowParts(plan, input, spaceId, std::move(partIds)); + } + + std::string explain() const override { + return "ShowParts"; + } + + GraphSpaceID getSpaceId() const { + return spaceId_; + } + + const std::vector<PartitionID>& getPartIds() const { + return partIds_; + } + +private: + explicit ShowParts(ExecutionPlan* plan, + PlanNode* input, + GraphSpaceID spaceId, + std::vector<PartitionID> partIds) + : SingleInputNode(plan, Kind::kShowParts, input) { + spaceId_ = spaceId; + partIds_ = std::move(partIds); + } + +private: + GraphSpaceID spaceId_{-1}; + std::vector<PartitionID> partIds_; +}; } // namespace graph } // namespace nebula #endif // PLANNER_ADMIN_H_ diff --git a/src/planner/PlanNode.cpp b/src/planner/PlanNode.cpp index ad8870f399ec5bbdb0a6c704649a70af8e669aa4..ac94a51a01da17d5ecc3d55473cabd1a7f8c04c6 100644 --- a/src/planner/PlanNode.cpp +++ b/src/planner/PlanNode.cpp @@ -114,6 +114,8 @@ const char* PlanNode::toString(PlanNode::Kind kind) { return "UpdateEdge"; case Kind::kShowHosts: return "ShowHosts"; + case Kind::kShowParts: + return "ShowParts"; } LOG(FATAL) << "Impossible kind plan node " << static_cast<int>(kind); } diff --git a/src/planner/PlanNode.h b/src/planner/PlanNode.h index 4ddd9808f946a43f007937d2a463e30dbbac225f..dd97153bea7db509b816b4739db9e1dd2dff98c8 100644 --- a/src/planner/PlanNode.h +++ b/src/planner/PlanNode.h @@ -71,6 +71,7 @@ public: kDeleteEdges, kUpdateVertex, kUpdateEdge, + kShowParts, }; PlanNode(ExecutionPlan* plan, Kind kind); diff --git a/src/validator/AdminValidator.cpp b/src/validator/AdminValidator.cpp index 683262daa29d422922ba0a46c9da418d717d3d9d..047b9c6074cb8232481548030083ecc06a64c9d1 100644 --- a/src/validator/AdminValidator.cpp +++ b/src/validator/AdminValidator.cpp @@ -201,5 +201,25 @@ Status ShowSnapshotsValidator::toPlan() { tail_ = root_; return Status::OK(); } + +Status ShowPartsValidator::validateImpl() { + return Status::OK(); +} + +Status ShowPartsValidator::toPlan() { + auto* plan = qctx_->plan(); + auto sentence = static_cast<ShowPartsSentence*>(sentence_); + std::vector<PartitionID> partIds; + if (sentence->getList() != nullptr) { + partIds = *sentence->getList(); + } + auto *node = ShowParts::make(plan, + nullptr, + vctx_->whichSpace().id, + std::move(partIds)); + root_ = node; + tail_ = root_; + return Status::OK(); +} } // namespace graph } // namespace nebula diff --git a/src/validator/AdminValidator.h b/src/validator/AdminValidator.h index 867ba023ff164f736cf8d2efdf95d74f487e3aa4..7caabb9b683061ff92d0e1ba027274e34189e64e 100644 --- a/src/validator/AdminValidator.h +++ b/src/validator/AdminValidator.h @@ -48,7 +48,7 @@ private: class ShowSpacesValidator final : public Validator { public: ShowSpacesValidator(Sentence* sentence, QueryContext* context) - : Validator(sentence, context) { + : Validator(sentence, context) { setNoSpaceRequired(); } @@ -61,7 +61,7 @@ private: class DropSpaceValidator final : public Validator { public: DropSpaceValidator(Sentence* sentence, QueryContext* context) - : Validator(sentence, context) { + : Validator(sentence, context) { setNoSpaceRequired(); } @@ -74,7 +74,7 @@ private: class ShowCreateSpaceValidator final : public Validator { public: ShowCreateSpaceValidator(Sentence* sentence, QueryContext* context) - : Validator(sentence, context) { + : Validator(sentence, context) { setNoSpaceRequired(); } @@ -87,7 +87,7 @@ private: class CreateSnapshotValidator final : public Validator { public: CreateSnapshotValidator(Sentence* sentence, QueryContext* context) - : Validator(sentence, context) { + : Validator(sentence, context) { setNoSpaceRequired(); } @@ -100,7 +100,7 @@ private: class DropSnapshotValidator final : public Validator { public: DropSnapshotValidator(Sentence* sentence, QueryContext* context) - : Validator(sentence, context) { + : Validator(sentence, context) { setNoSpaceRequired(); } @@ -113,10 +113,22 @@ private: class ShowSnapshotsValidator final : public Validator { public: ShowSnapshotsValidator(Sentence* sentence, QueryContext* context) - : Validator(sentence, context) { + : Validator(sentence, context) { setNoSpaceRequired(); } +private: + Status validateImpl() override; + + Status toPlan() override; +}; + +class ShowPartsValidator final : public Validator { +public: + ShowPartsValidator(Sentence* sentence, QueryContext* context) + : Validator(sentence, context) { + } + private: Status validateImpl() override; diff --git a/src/validator/Validator.cpp b/src/validator/Validator.cpp index 222ee95b8a777a474862afaee45ea0e7789120f2..f8e2b3f26c66679ae746b04abe8be59567270956 100644 --- a/src/validator/Validator.cpp +++ b/src/validator/Validator.cpp @@ -118,6 +118,8 @@ std::unique_ptr<Validator> Validator::makeValidator(Sentence* sentence, QueryCon return std::make_unique<UpdateVertexValidator>(sentence, context); case Sentence::Kind::kUpdateEdge: return std::make_unique<UpdateEdgeValidator>(sentence, context); + case Sentence::Kind::kShowParts: + return std::make_unique<ShowPartsValidator>(sentence, context); default: return std::make_unique<ReportError>(sentence, context); } @@ -163,7 +165,8 @@ Status Validator::appendPlan(PlanNode* node, PlanNode* appended) { case PlanNode::Kind::kDeleteVertices: case PlanNode::Kind::kDeleteEdges: case PlanNode::Kind::kUpdateVertex: - case PlanNode::Kind::kUpdateEdge: { + case PlanNode::Kind::kUpdateEdge: + case PlanNode::Kind::kShowParts: { static_cast<SingleDependencyNode*>(node)->dependsOn(appended); break; } diff --git a/src/validator/test/ValidatorTestBase.cpp b/src/validator/test/ValidatorTestBase.cpp index 55fb6b236eeb6f5dd814e6ad47265dc651a6a5b4..ac12c6bab8f4f76093ec4db047ee43abc96298d7 100644 --- a/src/validator/test/ValidatorTestBase.cpp +++ b/src/validator/test/ValidatorTestBase.cpp @@ -43,48 +43,6 @@ namespace graph { case PlanNode::Kind::kStart: case PlanNode::Kind::kDedup: return Status::OK(); - case PlanNode::Kind::kShowHosts: - case PlanNode::Kind::kDeleteEdges: - case PlanNode::Kind::kDeleteVertices: - case PlanNode::Kind::kIndexScan: - case PlanNode::Kind::kGetNeighbors: - case PlanNode::Kind::kFilter: - case PlanNode::Kind::kSort: - case PlanNode::Kind::kLimit: - case PlanNode::Kind::kAggregate: - case PlanNode::Kind::kSwitchSpace: - case PlanNode::Kind::kMultiOutputs: - case PlanNode::Kind::kCreateSpace: - case PlanNode::Kind::kCreateTag: - case PlanNode::Kind::kCreateEdge: - case PlanNode::Kind::kDescSpace: - case PlanNode::Kind::kDescTag: - case PlanNode::Kind::kDescEdge: - case PlanNode::Kind::kInsertVertices: - case PlanNode::Kind::kInsertEdges: - case PlanNode::Kind::kUnion: - case PlanNode::Kind::kIntersect: - case PlanNode::Kind::kMinus: - case PlanNode::Kind::kSelect: - case PlanNode::Kind::kLoop: - case PlanNode::Kind::kAlterEdge: - case PlanNode::Kind::kAlterTag: - case PlanNode::Kind::kShowCreateSpace: - case PlanNode::Kind::kShowCreateTag: - case PlanNode::Kind::kShowCreateEdge: - case PlanNode::Kind::kDropSpace: - case PlanNode::Kind::kDropTag: - case PlanNode::Kind::kDropEdge: - case PlanNode::Kind::kShowSpaces: - case PlanNode::Kind::kShowTags: - case PlanNode::Kind::kShowEdges: - case PlanNode::Kind::kCreateSnapshot: - case PlanNode::Kind::kDropSnapshot: - case PlanNode::Kind::kShowSnapshots: - case PlanNode::Kind::kDataJoin: - case PlanNode::Kind::kUpdateVertex: - case PlanNode::Kind::kUpdateEdge: - LOG(FATAL) << "Unimplemented"; case PlanNode::Kind::kDataCollect: { const auto *lDC = static_cast<const DataCollect*>(l); const auto *rDC = static_cast<const DataCollect*>(r); @@ -192,8 +150,9 @@ namespace graph { } return Status::OK(); } + default: + LOG(FATAL) << "Unknow plan node kind " << static_cast<int>(l->kind()); } - LOG(FATAL) << "Unknow plan node kind " << static_cast<int>(l->kind()); } // only traversal the plan by inputs, only `select` or `loop` make ring diff --git a/tests/README.md b/tests/README.md index f071aed3ac893809ed2d5cdeab35c027efdf9b1f..11eab16cc075288dd93d458894bb7ee145fe6a31 100644 --- a/tests/README.md +++ b/tests/README.md @@ -26,9 +26,9 @@ - check_resp_failed(resp) - 鍜宑heck_resp_succeeded鐩稿弽 - search_result(col, rows, expect) - - 鐢ㄦ潵妫€娴嬭繑鍥炵殑琛屾槸鍚﹀拰鏈熸湜涓€鑷达紝杩欓噷琛屽彲浠ユ槸涔卞簭锛屽苟涓斿垪鍊兼敮鎸佹鍒�, 浣跨敤姝e垯鐨勬椂鍊欙紝闇€瑕佽缃笅 is_regex=True + - 鐢ㄦ潵妫€娴嬭繑鍥炵殑琛屾槸鍚﹀拰鏈熸湜涓€鑷达紝杩欓噷琛屽彲浠ユ槸涔卞簭锛屽苟涓斿垪鍊兼敮鎸佹鍒�, 浣跨敤姝e垯鐨勬椂鍊欙紝闇€瑕佽缃笅 is_regex=True, 骞朵笖鎵€鏈夊垪閮借鐢ㄦ鍒� - check_result(rows, expect) - - 鐢ㄦ潵妫€娴嬭繑鍥炵殑琛屽拰鏈熸湜涓€鑷达紝杩欓噷鏄鏄寜鐓т弗鏍奸『搴忥紝骞朵笖鍒楀€兼敮鎸佹鍒�, 闇€瑕佽缃笅 is_regex=True + - 鐢ㄦ潵妫€娴嬭繑鍥炵殑琛屽拰鏈熸湜涓€鑷达紝杩欓噷鏄鏄寜鐓т弗鏍奸『搴忥紝骞朵笖鍒楀€兼敮鎸佹鍒�, 闇€瑕佽缃笅 is_regex=True, 骞朵笖鎵€鏈夊垪閮借鐢ㄦ鍒� - check_out_of_order_result - 鐢ㄦ潵妫€娴嬭繑鍥炵殑琛屾槸鍚﹀拰鏈熸湜涓€鑷达紝琛屽彲浠ユ槸涔卞簭锛屼笉鏀寔姝e垯 - check_empty_result diff --git a/tests/admin/test_parts.py b/tests/admin/test_parts.py new file mode 100644 index 0000000000000000000000000000000000000000..5444f61dcf9077a362c2ab58149398dbdafbf6f6 --- /dev/null +++ b/tests/admin/test_parts.py @@ -0,0 +1,57 @@ +# --coding:utf-8-- +# +# 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. + +import time +import re + +from tests.common.nebula_test_suite import NebulaTestSuite + + +class TestParts(NebulaTestSuite): + + @classmethod + def prepare(self): + resp = self.client.execute('CREATE SPACE space_show_parts(partition_num=5); ' + 'USE space_show_parts;') + self.check_resp_succeeded(resp) + + # Wait for leader info + time.sleep(self.delay) + + @classmethod + def cleanup(self): + resp = self.client.execute('DROP SPACE space_show_parts;') + self.check_resp_succeeded(resp) + + def test_part(self): + # All + resp = self.client.execute_query('SHOW PARTS') + self.check_resp_succeeded(resp) + expected_col_names = ["Partition ID", "Leader", "Peers", "Losts"] + self.check_column_names(resp, expected_col_names) + expected_result = [[re.compile(r'1'), re.compile(r'127.0.0.1:.*'), re.compile(r'127.0.0.1:.*'), re.compile(r'')], + [re.compile(r'2'), re.compile(r'127.0.0.1:.*'), re.compile(r'127.0.0.1:.*'), re.compile(r'')], + [re.compile(r'3'), re.compile(r'127.0.0.1:.*'), re.compile(r'127.0.0.1:.*'), re.compile(r'')], + [re.compile(r'4'), re.compile(r'127.0.0.1:.*'), re.compile(r'127.0.0.1:.*'), re.compile(r'')], + [re.compile(r'5'), re.compile(r'127.0.0.1:.*'), re.compile(r'127.0.0.1:.*'), re.compile(r'')]] + self.check_result(resp, expected_result, is_regex = True) + + # Specify the part id + resp = self.client.execute_query('SHOW PART 3') + self.check_resp_succeeded(resp) + expected_col_names = ["Partition ID", "Leader", "Peers", "Losts"] + self.check_column_names(resp, expected_col_names) + expected_result = [[re.compile(r'3'), re.compile(r'127.0.0.1:.*'), re.compile(r'127.0.0.1:.*'), re.compile(r'')]] + self.check_result(resp, expected_result, is_regex = True) + + # Not exist part id + resp = self.client.execute_query('SHOW PART 10') + self.check_resp_succeeded(resp) + expected_col_names = ["Partition ID", "Leader", "Peers", "Losts"] + self.check_column_names(resp, expected_col_names) + expected_result = [] + self.check_result(resp, expected_result)