Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement create space, drop space, add hosts, show hosts , remove hosts via MetaClient #223

Merged
merged 13 commits into from
Apr 16, 2019
Merged

Implement create space, drop space, add hosts, show hosts , remove hosts via MetaClient #223

merged 13 commits into from
Apr 16, 2019

Conversation

ayyt
Copy link
Contributor

@ayyt ayyt commented Mar 21, 2019

#200 Support some admin sentences in Query Engine.
Implement the following SQL via MetaClient.:
create space
drop space
add hosts
show hosts
remove hosts

@nebula-community-bot
Copy link
Member

Can one of the admins verify this patch?

@ayyt ayyt changed the title Add host and create space Implement create space, add hosts, show hosts via MetaClient Mar 21, 2019

switch (show_kind) {
case ShowKind::kShowHosts:
InitMetaClient();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About using the MetaClient, here are my thoughts:

  • You just should not create a meta client per action, not even per request. As for the current infra, the ExecutionEngine ought to create and own it, and each ExecutionContext shall have a reference to it via a raw pointer.
  • We disallow any synchronous action in the service, e.g. RPC. Here if the meta client doesn't support the async calls, we have to adjust it to do so.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent! I will do it.

;

create_space_sentence
: KW_CREATE KW_SPACE L_PAREN STRING COMMA INTEGER COMMA INTEGER R_PAREN {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opt to define the syntax like this:

KW_CREATE KW_SPACE LABEL L_PAREN space_opt_list R_PAREN

space_opt_list: space_opt_item
      | space_opt_list COMMA space_opt_item
      | space_opt_list COMMA

space_opt_item : KW_ENGINE_TYPE ASSIGN LABEL
      | KW_PARTITION_NUM ASSIGN INTEGER
      | ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right on!

@dutor
Copy link
Contributor

dutor commented Mar 26, 2019

@steppenwolfyuetong For your information, @dangleptr is working on the async interfaces of MetaClient

@ayyt
Copy link
Contributor Author

ayyt commented Mar 26, 2019

Fair enough!

@ayyt
Copy link
Contributor Author

ayyt commented Mar 26, 2019

reconstruct create space and ExecutionEngine could create and own metaClient.
meta client async interfaces need to wait for #238

@ayyt ayyt changed the title Implement create space, add hosts, show hosts via MetaClient Implement create space, add hosts, show hosts via MetaClient Mar 26, 2019
@ayyt ayyt changed the title Implement create space, add hosts, show hosts via MetaClient Implement create space, drop space, add hosts, show hosts , remove hosts via MetaClient Mar 29, 2019
void CreateSpaceExecutor::execute() {
CHECK_GT(partNum_, 0) << "partition_num value illegal";
CHECK_GT(replicaFactor_, 0) << "replica_factor value illegal";
auto ret = ectx()->getMetaClient()->createSpace(*spaceName_, partNum_, replicaFactor_).get();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use the callback here, don't block the worker.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I will do it.

while (iter->valid()) {
auto key = iter->key();
resp_.set_code(cpp2::ErrorCode::SUCCEEDED);
kvstore_->asyncRemove(kDefaultSpaceId_, kDefaultPartId_, key.toString(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use removeRange here maybe more simple.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent! I will do it.


for (auto& hostKey : hostsKey) {
resp_.set_code(cpp2::ErrorCode::SUCCEEDED);
kvstore_->asyncRemove(kDefaultSpaceId_, kDefaultPartId_, hostKey,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I think not use removeRange.
Because the hosts to be remove are not a range, are a list.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

}

resp_.set_code(cpp2::ErrorCode::SUCCEEDED);
onFinished();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you call onFinish, the instance of current processor will be destroyed. So if your process procedure is async, onFinish should be called in callback.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent! I will do it.

@@ -15,6 +15,10 @@ namespace graph {
ExecutionEngine::ExecutionEngine() {
schemaManager_ = std::make_unique<SchemaManager>();
storage_ = std::make_unique<StorageService>(schemaManager_.get());

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a TODO comment here , schemaManager/StorageClient should shared one meta client instance in the future

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -84,6 +84,29 @@ StatusOr<GraphSpaceID> BaseProcessor<RESP>::spaceExist(const std::string& name)
return Status::SpaceNotFound();
}

// TODO(YT) Maybe we could use index to improve the efficient
template<typename RESP>
Status BaseProcessor<RESP>::hostsExist(std::vector<std::string> &hostsKey) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't modify "hostKey", pass const reference is a better choice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!👍

// TODO(YT) Maybe we could use index to improve the efficient
template<typename RESP>
Status BaseProcessor<RESP>::hostsExist(std::vector<std::string> &hostsKey) {
for (auto& hostKey : hostsKey) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use const auto&

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

@@ -84,6 +84,29 @@ StatusOr<GraphSpaceID> BaseProcessor<RESP>::spaceExist(const std::string& name)
return Status::SpaceNotFound();
}

// TODO(YT) Maybe we could use index to improve the efficient
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hostKey is rowKey now, i think we don't need another index here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it Is my negligence.

namespace meta {

void DropSpaceProcessor::process(const cpp2::DropSpaceReq& req) {
guard_ = std::make_unique<std::lock_guard<std::mutex>>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the lock, i have optimized it in #225
I will merge it recently, you'd better rebase on it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

[this] (kvstore::ResultCode code, HostAddr leader) {
UNUSED(leader);
this->resp_.set_code(to(code));
this->onFinished();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's dangerous here. Because we can't ensure the callback order for the two asyncRemove

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent! I will fix it.

[this] (kvstore::ResultCode code, HostAddr leader) {
UNUSED(leader);
this->resp_.set_code(to(code));
this->onFinished();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's dangerous here. Because we can't ensure the callback order for the two asyncRemove

@dangleptr dangleptr mentioned this pull request Apr 8, 2019
@sherman-the-tank
Copy link
Member

jenkins go

@ayyt
Copy link
Contributor Author

ayyt commented Apr 8, 2019

need to deal with some problems caused by the merger, and request review later

@nebula-community-bot
Copy link
Member

Unit testing failed.

@ayyt
Copy link
Contributor Author

ayyt commented Apr 8, 2019

The update has been completed, please review。

@dangleptr
Copy link
Contributor

Ready to review now?

@ayyt
Copy link
Contributor Author

ayyt commented Apr 9, 2019

@dangleptr, yeah, please focus on the merge in yesterday.

Status AddHostsExecutor::prepare() {
host_ = sentence_->hosts();
if (host_.size() == 0) {
LOG(FATAL) << "Add hosts Sentence host address illegal";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FATAL means core dump. We'd better return ErrorCode here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.

Status RemoveHostsExecutor::prepare() {
host_ = sentence_->hosts();
if (host_.size() == 0) {
LOG(FATAL) << "Remove hosts Sentence host address illegal";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch.



void CreateSpaceExecutor::execute() {
CHECK_GT(partNum_, 0) << "partition_num value illegal";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd better check the numbers in prepare and return errorCode if failed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍,good catch!


auto cb = [this] (auto &&resp) {
auto spaceId = resp.value();
if (spaceId == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we need the if scope?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I will use if (spaceId <= 0) to ensure that return a valid spaceId.

} catch (std::exception& e) {
LOG(ERROR) << "Convert failed for " << val << ", msg " << e.what();
}
return *reinterpret_cast<const TagID*>(val.c_str());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, we prefer folly::to than cast by ourself. folly::to has error checks inside, it is important.

Copy link
Contributor Author

@ayyt ayyt Apr 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah,I see what you mean.
But storing to kvstore when creating space and getting from kvstore when droping space should be consistent,Otherwise an error occurs when getting. Also to Tag.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If here using folly::to, storing to kvstore also uses folly::to, not cast by ourself.

std::string end = MetaUtils::partKey(spaceId, 0x7FFFFFFF);
resp_.set_code(cpp2::ErrorCode::SUCCEEDED);

kvstore_->asyncRemoveRange(kDefaultSpaceId_, kDefaultPartId_, start, end,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reconsider the multi delete here. I think we should implement a batchDelete interface in kvstore. You could refer rocksdb::WriteBatch to implement it (Open another issue for it). wdyt?

Copy link
Contributor Author

@ayyt ayyt Apr 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sense.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't use asyncXXX in this way. Currently, to keep safe, you should wait for all asyncXXX finished before invoke onFinished method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, here use multi asyncXXX and collectAll.
batchDelete need to implement for remove hosts;


for (auto& hostKey : hostsKey) {
resp_.set_code(cpp2::ErrorCode::SUCCEEDED);
kvstore_->asyncRemove(kDefaultSpaceId_, kDefaultPartId_, hostKey,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto


std::string ShowSentence::toString() const {
std::string buf;
switch (showKind_) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buf.reserve

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

char buf[256];
switch (optType_) {
case PARTITION_NUM:
snprintf(buf, sizeof(buf), "partition_num = %ld", boost::get<int64_t>(optValue_));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use folly::stringPrintf directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point!

@ayyt
Copy link
Contributor Author

ayyt commented Apr 9, 2019

fixed, please review again

@sherman-the-tank
Copy link
Member

jenkins go

@nebula-community-bot
Copy link
Member

Unit testing passed.

Copy link
Member

@sherman-the-tank sherman-the-tank left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well done! Thanks for taking care of this

I have some inline comments


using nebula::network::NetworkUtils;

ShowExecutor::ShowExecutor(Sentence *sentence,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would highly recommend to break this file into multiple files, with each file contains one Executor implementation

Generally speaking, we want to keep every class in its own header file and implementation file, unless for those accessory classes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good, this makes sense.



void ShowExecutor::execute() {
auto show_kind = sentence_->showKind();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use C style variable name, please rename it to showKind

In addition, I discussed with @dutor this afternoon, it seems we use kind pretty much across the entire QE. But in American English, "type" is more preferable over "kind"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right on!👍,I will fix it.

cpp2::ExecutionResponse resp;
std::string query = "add hosts(\"127.0.0.1:1000\", \"127.0.0.1:1100\")";
auto code = client->execute(query, resp);
ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need ASSERT_EQ() here. EXPECT_EQ() could be more suitable

ASSERT_EQ() will stop running all following code, while EXPECT_EQ() will only print an error message and continue the testcase execusion

Copy link
Contributor Author

@ayyt ayyt Apr 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right on!
But I check the reference manual about *_EQ, the rule has changed. As follow:
Historical note: Before February 2016 *_EQ had a convention of calling it as ASSERT_EQ(expected, actual), so lots of existing code uses this order. Now *_EQ treats both parameters in the same way.

refer to https://github.com/google/googletest/blob/master/googletest/docs/primer.md

IMO,we maintain the original specification as *_EQ(expected, actual)

Copy link
Contributor

@dutor dutor Apr 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sherman-the-tank Here are my opinions:
From my experiences, in most cases, if an assertion(ASSERTs or EXPECTs) fails, the following testings usually make no sense. Even worse, the following testings are error-prone so that we need much more mental overheads to take care of these. So I recommend to use ASSERTs by default, unless we intentionally want the testing to continue. If you concern about that to fail earlier will print out less infos, I think we ought to take more advantages of the stream operator provided by the testing macros, e.g. ASSERT_TRUE(status.ok()) << status.

Take following codes as example:

{
        cpp2::ListSpacesReq req;
        auto* processor = ListSpacesProcessor::instance(kv.get());
        auto f = processor->getFuture();
        processor->process(req);
        auto resp = std::move(f).get();
        EXPECT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.code);
        EXPECT_EQ(1, resp.spaces.size());
        EXPECT_EQ(1, resp.spaces[0].id.get_space_id());
        EXPECT_EQ("default_space", resp.spaces[0].name);
    }

These code will definitely crash on failure, and nobody noticed it. Besides, reviewers often pay less attention to the testing codes, at least it is true so far.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dutor, @sherman-the-tank
I think we should use a same rule to unify all testcasts in new pr.
so future operations need to follow the same rules.
wdyt?

@ayyt
Copy link
Contributor Author

ayyt commented Apr 10, 2019

fixed, please review again.

@ayyt
Copy link
Contributor Author

ayyt commented Apr 11, 2019

rebase master

@ayyt
Copy link
Contributor Author

ayyt commented Apr 11, 2019

rebase master

Copy link
Contributor

@dangleptr dangleptr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The patch looks more clear now. There are some inline comments, please recheck it.

auto spaceRet = getSpaceId(req.get_space_name());

if (!spaceRet.ok()) {
if (spaceRet.status() == Status::SpaceNotFound()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement a function that convert Status to cpp2::ErrorCode

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point


auto hostsRet = hostsExist(hostsKey);
if (!hostsRet.ok()) {
if (hostsRet == Status::HostNotFound()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

buf.reserve(256);
switch (optType_) {
case PARTITION_NUM:
buf = folly::stringPrintf("partition_num = %ld", boost::get<int64_t>(optValue_));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"buf" seems useless. Return directly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

if (isInt()) {
return asInt();
} else {
LOG(FATAL) << "partition_num value illegal.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be carefully about FATAL. Do we really want to crash here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! 👍

if (isInt()) {
return asInt();
} else {
LOG(FATAL) << "replica_factor value illegal.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}

deleteKeys.emplace_back(MetaUtils::indexKey(EntryType::SPACE, req.get_space_name()));
deleteKeys.emplace_back(MetaUtils::spaceKey(spaceId));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add TODO comments, should we delete Tag/Edge under the space here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sense!

@@ -28,7 +28,7 @@ void AddTagProcessor::process(const cpp2::AddTagReq& req) {
auto version = time::TimeUtils::nowInMSeconds();
TagID tagId = autoIncrementId();
data.emplace_back(MetaUtils::indexKey(EntryType::TAG, req.get_tag_name()),
std::string(reinterpret_cast<const char*>(&tagId), sizeof(tagId)));
folly::to<std::string>(tagId));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, if cast from int to char*, you could cast directly. Because no errors should happen.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AddTagProcessor.cpp  is same to CreatesSpaceProcessor.cpp.
`
dangleptr 3 days ago
Generally, we prefer folly::to than cast by ourself. folly::to has error checks inside, it is important.

@steppenwolfyuetong
steppenwolfyuetong 3 days ago •
Author
Yeah,I see what you mean.
But storing to kvstore when creating space and getting from kvstore when droping space should be consistent,Otherwise an error occurs when getting. Also to Tag.

@steppenwolfyuetong
steppenwolfyuetong 3 days ago Author
If here using folly::to, storing to kvstore also uses folly::to, not cast by ourself.
`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reference to reply in CreatesSpaceProcessor.cpp

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed according to the offline discussion

@ayyt
Copy link
Contributor Author

ayyt commented Apr 12, 2019

fixed and rebase master, please review

@dangleptr
Copy link
Contributor

Jenkins go

@sherman-the-tank
Copy link
Member

jenkins go

@nebula-community-bot
Copy link
Member

Unit testing passed.

@ayyt
Copy link
Contributor Author

ayyt commented Apr 14, 2019

@dutor, @dangleptr, @sherman-the-tank, Thank you for your meticulous review. 👍
@dutor,I has fixed your suggestions and comments except that use default space in the prepare of executor. Let's talk about it offline on Monday, ok?

@ayyt
Copy link
Contributor Author

ayyt commented Apr 15, 2019

@dutor, fix and rebase master, please review again, thanks.

create_space_sentence
: KW_CREATE KW_SPACE LABEL L_PAREN space_opt_list R_PAREN {
auto sentence = new CreateSpaceSentence($3);
sentence->setOpts($5);
Copy link
Contributor

@dutor dutor Apr 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

src/parser/parser.yy Outdated Show resolved Hide resolved
$$->addOpt($1);
}
| space_opt_list COMMA space_opt_item {
$$ = $1;
Copy link
Contributor

@dutor dutor Apr 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indent four spaces.

$$->addOpt($3);
}
| space_opt_list COMMA {
$$ = $1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto.

sentence->setOpts($5);
$$ = sentence;
}
;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to add a blank line below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, good catch!

@ayyt
Copy link
Contributor Author

ayyt commented Apr 15, 2019

@dutor, fix and rebase. thanks.

@dutor
Copy link
Contributor

dutor commented Apr 15, 2019

Jenkins, go!

@nebula-community-bot
Copy link
Member

Unit testing passed.

Copy link
Contributor

@dutor dutor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zouni

@dutor dutor merged commit b52a911 into vesoft-inc:master Apr 16, 2019
@dutor dutor deleted the addHost_and_createSpace branch April 16, 2019 01:17
yixinglu pushed a commit to yixinglu/nebula that referenced this pull request Feb 16, 2020
tong-hao pushed a commit to tong-hao/nebula that referenced this pull request Jun 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants