diff --git a/.github/actions/tagname-action/action.yml b/.github/actions/tagname-action/action.yml
index 81c8b1011eb..8132c96f470 100644
--- a/.github/actions/tagname-action/action.yml
+++ b/.github/actions/tagname-action/action.yml
@@ -13,7 +13,9 @@ runs:
- id: tag
run: |
tag=$(echo ${{ github.ref }} | rev | cut -d/ -f1 | rev)
- tagnum=$(echo $tag |sed 's/^v//')
+ tagnum=$(echo $tag | sed 's/^v//')
+ majorver=$(echo $tag | cut -d '.' -f 1)
echo "::set-output name=tag::$tag"
echo "::set-output name=tagnum::$tagnum"
+ echo "::set-output name=majorver::$majorver"
shell: bash
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index cdcd55fd93d..0bb60d43d0f 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -39,7 +39,7 @@ jobs:
echo "::set-output name=subdir::$subdir"
- uses: actions/upload-artifact@v1
with:
- name: ${{ matrix.os }}-v2-nightly
+ name: ${{ matrix.os }}-nightly
path: pkg-build/cpack_output
- uses: ./.github/actions/upload-to-oss-action
with:
diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml
index 54ce3508195..9f9f2d4c40b 100644
--- a/.github/workflows/pull_request.yml
+++ b/.github/workflows/pull_request.yml
@@ -94,6 +94,7 @@ jobs:
-DCMAKE_C_COMPILER=$TOOLSET_CLANG_DIR/bin/gcc \
-DCMAKE_BUILD_TYPE=Debug \
-DENABLE_TESTING=on \
+ -DENABLE_COVERAGE=on \
-B build
echo "::set-output name=j::10"
echo "::set-output name=t::10"
@@ -126,7 +127,24 @@ jobs:
timeout-minutes: 20
- name: Setup cluster
run: |
- make up
+ case ${{ matrix.compiler }} in
+ gcc-*)
+ case ${{ matrix.os }} in
+ centos7)
+ # normal cluster
+ make up
+ ;;
+ ubuntu2004)
+ # ssl cluster
+ make ENABLE_SSL=true CA_SIGNED=true up
+ ;;
+ esac
+ ;;
+ clang-*)
+ # graph ssl only cluster
+ make ENABLE_SSL=false ENABLE_GRAPH_SSL=true up
+ ;;
+ esac
working-directory: tests/
timeout-minutes: 2
- name: Pytest
@@ -144,6 +162,11 @@ jobs:
make RM_DIR=false down
working-directory: tests/
timeout-minutes: 2
+ - name: coverage
+ if: ${{ matrix.compiler == 'gcc-9.2' && matrix.os == 'ubuntu2004' }}
+ run: |
+ ~/.local/bin/fastcov -d build -l -o fastcov.info -p --exclude /usr/include --exclude=/opt/vesoft --exclude scanner.lex
+ bash <(curl -s https://codecov.io/bash) -Z -f fastcov.info
- name: Sanitizer
if: ${{ always() }}
run: |
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 0456901f7dd..962e76d8330 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -62,6 +62,14 @@ jobs:
- uses: actions/checkout@v2
- uses: ./.github/actions/tagname-action
id: tagname
+ - id: docker
+ run: |
+ majorver=$(git tag -l --sort=v:refname | tail -n1 | cut -f1 -d'.')
+ tag=""
+ if [[ $majorver == ${{ steps.tagname.outputs.majorver }} ]]; then
+ tag="vesoft/nebula-${{ matrix.service }}:latest"
+ fi
+ echo "::set-output name=tag::$tag"
- uses: docker/setup-buildx-action@v1
- uses: docker/login-action@v1
with:
@@ -71,7 +79,10 @@ jobs:
with:
context: .
file: ./docker/Dockerfile.${{ matrix.service }}
- tags: vesoft/nebula-${{ matrix.service }}:${{ steps.tagname.outputs.tag }},vesoft/nebula-${{ matrix.service }}:latest
+ tags: |
+ vesoft/nebula-${{ matrix.service }}:${{ steps.tagname.outputs.tag }}
+ vesoft/nebula-${{ matrix.service }}:${{ steps.tagname.outputs.majorver }}
+ ${{ steps.docker.outputs.tag }}
push: true
build-args: |
BRANCH=${{ steps.tagname.outputs.tag }}
diff --git a/.gitignore b/.gitignore
index c9920bfafd5..1a2cf4c78f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,6 +52,7 @@ cmake-build*/
core.*
workspace.*
.metals/
+.cproject
#py
*.egg-info
diff --git a/README-CN.md b/README-CN.md
index a86cf921a5c..691ae03e97e 100644
--- a/README-CN.md
+++ b/README-CN.md
@@ -1,5 +1,5 @@
-
+
中文 | English
世界上唯一能够容纳千亿个顶点和万亿条边,并提供毫秒级查询延时的图数据库解决方案
@@ -34,7 +34,7 @@
## 发布通告
-v1.x和v2.5.0之后的版本,Nebula Graph在这个repo管理。如需获取v2.0.0到v2.5.0之间的版本,请访问[Nebula Graph repo](https://github.com/vesoft-inc/nebula-graph)。
+v1.x和v2.5.1之后的版本,Nebula Graph在这个repo管理。如需获取v2.0.0到v2.5.1之间的版本,请访问[Nebula Graph repo](https://github.com/vesoft-inc/nebula-graph)。
Nebula Graph 1.x 后续不再进行功能的更新,请升级到2.0版本中。Nebula Graph内核 1.x 与 2.x数据格式、通信协议、客户端等均双向不兼容,可参照[升级指导](https://docs.nebula-graph.com.cn/2.5.0/4.deployment-and-installation/3.upgrade-nebula-graph/upgrade-nebula-graph-to-250/)进行升级。
diff --git a/README.md b/README.md
index 1f9f13e5824..ea5c8e4f23e 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+
English | 中文
A distributed, scalable, lightning-fast graph database
@@ -31,7 +31,7 @@ Compared with other graph database solutions, **Nebula Graph** has the following
## Notice of Release
-This repository hosts the source code of Nebula Graph versions before 2.0.0-alpha and after v2.5.0. If you are looking to use the versions between v2.0.0 and v2.5.0, please head to [Nebula Graph repo](https://github.com/vesoft-inc/nebula-graph).
+This repository hosts the source code of Nebula Graph versions before 2.0.0-alpha and after v2.5.1. If you are looking to use the versions between v2.0.0 and v2.5.1, please head to [Nebula Graph repo](https://github.com/vesoft-inc/nebula-graph).
Nebula Graph 1.x is not actively maintained. Please move to Nebula Graph 2.x. The data format, rpc protocols, clients, etc. are not compatible between Nebula Graph v1.x and v2.x, but we do offer [upgrade guide from 1.x to v2.5.0](https://docs.nebula-graph.io/2.5.0/4.deployment-and-installation/3.upgrade-nebula-graph/upgrade-nebula-graph-to-250/).
diff --git a/cmake/nebula/GeneralCompilerConfig.cmake b/cmake/nebula/GeneralCompilerConfig.cmake
index 3512c1c0d3b..ca9f70c4e6c 100644
--- a/cmake/nebula/GeneralCompilerConfig.cmake
+++ b/cmake/nebula/GeneralCompilerConfig.cmake
@@ -51,6 +51,8 @@ if(ENABLE_TESTING AND ENABLE_COVERAGE)
add_compile_options(--coverage)
add_compile_options(-g)
add_compile_options(-O0)
+ nebula_add_exe_linker_flag(-coverage)
+ nebula_add_exe_linker_flag(-lgcov)
endif()
# TODO(doodle) Add option suggest-override for gnu
diff --git a/conf/nebula-storaged.conf.default b/conf/nebula-storaged.conf.default
index 79c1e8cd48b..4aead99ba67 100644
--- a/conf/nebula-storaged.conf.default
+++ b/conf/nebula-storaged.conf.default
@@ -90,8 +90,10 @@
# * kAll, Collect all stats
--rocksdb_stats_level=kExceptHistogramOrTimers
-# Whether or not to enable rocksdb's prefix bloom filter, disabled by default.
---enable_rocksdb_prefix_filtering=false
+# Whether or not to enable rocksdb's prefix bloom filter, enabled by default.
+--enable_rocksdb_prefix_filtering=true
+# Whether or not to enable rocksdb's whole key bloom filter, disabled by default.
+--enable_rocksdb_whole_key_filtering=false
############## rocksdb Options ##############
# rocksdb DBOptions in json, each name and value of option is a string, given as "option_name":"option_value" separated by comma
diff --git a/conf/nebula-storaged.conf.production b/conf/nebula-storaged.conf.production
index 0ba755fc189..8789ebdd08a 100644
--- a/conf/nebula-storaged.conf.production
+++ b/conf/nebula-storaged.conf.production
@@ -96,8 +96,10 @@
# * kAll, Collect all stats
--rocksdb_stats_level=kExceptHistogramOrTimers
-# Whether or not to enable rocksdb's prefix bloom filter, disabled by default.
---enable_rocksdb_prefix_filtering=false
+# Whether or not to enable rocksdb's prefix bloom filter, enabled by default.
+--enable_rocksdb_prefix_filtering=true
+# Whether or not to enable rocksdb's whole key bloom filter, disabled by default.
+--enable_rocksdb_whole_key_filtering=false
############### misc ####################
--snapshot_part_rate_limit=10485760
diff --git a/src/clients/meta/MetaClient.cpp b/src/clients/meta/MetaClient.cpp
index 87e276e5409..0f73cbad02a 100644
--- a/src/clients/meta/MetaClient.cpp
+++ b/src/clients/meta/MetaClient.cpp
@@ -19,6 +19,7 @@
#include "common/http/HttpClient.h"
#include "common/meta/NebulaSchemaProvider.h"
#include "common/network/NetworkUtils.h"
+#include "common/ssl/SSLConfig.h"
#include "common/stats/StatsManager.h"
#include "common/time/TimeUtils.h"
#include "version/Version.h"
@@ -49,7 +50,8 @@ MetaClient::MetaClient(std::shared_ptr ioThreadPool
CHECK(ioThreadPool_ != nullptr) << "IOThreadPool is required";
CHECK(!addrs_.empty())
<< "No meta server address is specified or can be solved. Meta server is required";
- clientsMan_ = std::make_shared>();
+ clientsMan_ = std::make_shared>(
+ FLAGS_enable_ssl || FLAGS_enable_meta_ssl);
updateActive();
updateLeader();
bgThread_ = std::make_unique();
@@ -798,6 +800,8 @@ Status MetaClient::handleResponse(const RESP& resp) {
return Status::Error("Failed to get meta dir!");
case nebula::cpp2::ErrorCode::E_INVALID_JOB:
return Status::Error("No valid job!");
+ case nebula::cpp2::ErrorCode::E_JOB_NOT_IN_SPACE:
+ return Status::Error("Job not in chosen space!");
case nebula::cpp2::ErrorCode::E_BACKUP_EMPTY_TABLE:
return Status::Error("Backup empty table!");
case nebula::cpp2::ErrorCode::E_BACKUP_TABLE_FAILED:
@@ -1060,6 +1064,21 @@ folly::Future> MetaClient::createSpace(meta::cpp2::SpaceD
return future;
}
+folly::Future> MetaClient::createSpaceAs(const std::string& oldSpaceName,
+ const std::string& newSpaceName) {
+ cpp2::CreateSpaceAsReq req;
+ req.set_old_space_name(oldSpaceName);
+ req.set_new_space_name(newSpaceName);
+ folly::Promise> promise;
+ auto future = promise.getFuture();
+ getResponse(
+ std::move(req),
+ [](auto client, auto request) { return client->future_createSpaceAs(request); },
+ [](cpp2::ExecResp&& resp) -> GraphSpaceID { return resp.get_id().get_space_id(); },
+ std::move(promise));
+ return future;
+}
+
folly::Future>> MetaClient::listSpaces() {
cpp2::ListSpacesReq req;
folly::Promise>> promise;
diff --git a/src/clients/meta/MetaClient.h b/src/clients/meta/MetaClient.h
index 546640b497d..6418e4ceced 100644
--- a/src/clients/meta/MetaClient.h
+++ b/src/clients/meta/MetaClient.h
@@ -215,6 +215,9 @@ class MetaClient {
folly::Future> createSpace(meta::cpp2::SpaceDesc spaceDesc,
bool ifNotExists = false);
+ folly::Future> createSpaceAs(const std::string& oldSpaceName,
+ const std::string& newSpaceName);
+
folly::Future>> listSpaces();
folly::Future> getSpace(std::string name);
diff --git a/src/clients/meta/test/CMakeLists.txt b/src/clients/meta/test/CMakeLists.txt
index b732a4748b6..80fc0bf0434 100644
--- a/src/clients/meta/test/CMakeLists.txt
+++ b/src/clients/meta/test/CMakeLists.txt
@@ -10,5 +10,6 @@ nebula_add_test(
$
$
$
+ $
LIBRARIES gtest
)
diff --git a/src/clients/storage/StorageClientBase-inl.h b/src/clients/storage/StorageClientBase-inl.h
index 749563a8879..108ff59ea2e 100644
--- a/src/clients/storage/StorageClientBase-inl.h
+++ b/src/clients/storage/StorageClientBase-inl.h
@@ -8,6 +8,7 @@
#include
+#include "common/ssl/SSLConfig.h"
#include "common/time/WallClock.h"
namespace nebula {
@@ -72,7 +73,7 @@ template
StorageClientBase::StorageClientBase(
std::shared_ptr threadPool, meta::MetaClient* metaClient)
: metaClient_(metaClient), ioThreadPool_(threadPool) {
- clientsMan_ = std::make_unique>();
+ clientsMan_ = std::make_unique>(FLAGS_enable_ssl);
}
template
diff --git a/src/codec/test/CMakeLists.txt b/src/codec/test/CMakeLists.txt
index 2ce9788956e..05581db1c94 100644
--- a/src/codec/test/CMakeLists.txt
+++ b/src/codec/test/CMakeLists.txt
@@ -31,6 +31,7 @@ set(CODEC_TEST_LIBS
$
$
$
+ $
)
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 9122bd1f76f..fe404277c93 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -25,3 +25,4 @@ nebula_add_subdirectory(function)
nebula_add_subdirectory(graph)
nebula_add_subdirectory(plugin)
nebula_add_subdirectory(utils)
+nebula_add_subdirectory(ssl)
diff --git a/src/common/algorithm/ReservoirSampling.h b/src/common/algorithm/ReservoirSampling.h
index 6d846e1ad10..04cc937ad66 100644
--- a/src/common/algorithm/ReservoirSampling.h
+++ b/src/common/algorithm/ReservoirSampling.h
@@ -37,7 +37,13 @@ class ReservoirSampling final {
return false;
}
- std::vector&& samples() && { return std::move(samples_); }
+ std::vector samples() {
+ auto result = std::move(samples_);
+ samples_.clear();
+ samples_.reserve(num_);
+ cnt_ = 0;
+ return result;
+ }
private:
std::vector samples_;
diff --git a/src/common/algorithm/test/ReservoirSamplingTest.cpp b/src/common/algorithm/test/ReservoirSamplingTest.cpp
index 67e2c22e8ac..1a5d6c32bbb 100644
--- a/src/common/algorithm/test/ReservoirSamplingTest.cpp
+++ b/src/common/algorithm/test/ReservoirSamplingTest.cpp
@@ -18,7 +18,7 @@ TEST(ReservoirSamplingTest, Sample) {
sampler.sampling(std::move(i));
}
- auto result = std::move(sampler).samples();
+ auto result = sampler.samples();
EXPECT_EQ(5, result.size());
for (auto i : result) {
EXPECT_LE(0, i);
@@ -27,16 +27,18 @@ TEST(ReservoirSamplingTest, Sample) {
}
{
ReservoirSampling sampler(5);
- std::vector sampleSpace = {0, 1, 2};
- for (auto i : sampleSpace) {
- sampler.sampling(std::move(i));
- }
+ for (size_t count = 0; count < 10; count++) {
+ std::vector sampleSpace = {0, 1, 2};
+ for (auto i : sampleSpace) {
+ sampler.sampling(std::move(i));
+ }
- auto result = std::move(sampler).samples();
- EXPECT_EQ(3, result.size());
- EXPECT_EQ(0, result[0]);
- EXPECT_EQ(1, result[1]);
- EXPECT_EQ(2, result[2]);
+ auto result = sampler.samples();
+ EXPECT_EQ(3, result.size());
+ EXPECT_EQ(0, result[0]);
+ EXPECT_EQ(1, result[1]);
+ EXPECT_EQ(2, result[2]);
+ }
}
}
} // namespace algorithm
diff --git a/src/common/datatypes/DataSet.h b/src/common/datatypes/DataSet.h
index b80cec79793..5c42bbc696d 100644
--- a/src/common/datatypes/DataSet.h
+++ b/src/common/datatypes/DataSet.h
@@ -7,6 +7,8 @@
#ifndef COMMON_DATATYPES_DATASET_H_
#define COMMON_DATATYPES_DATASET_H_
+#include
+
#include
#include
#include
@@ -153,6 +155,44 @@ struct DataSet {
return os.str();
}
+ // format:
+ // [
+ // {
+ // "row": [ row-data ],
+ // "meta": [ metadata ]
+ // },
+ // ]
+ folly::dynamic toJson() const {
+ // parse rows to json
+ auto dataBody = folly::dynamic::array();
+ for (auto& row : rows) {
+ dataBody.push_back(rowToJson(row));
+ }
+
+ return dataBody;
+ }
+
+ // parse Nebula::Row to json
+ // format:
+ // {
+ // "row": [ row-data ],
+ // "meta": [ metadata ]
+ // }
+ folly::dynamic rowToJson(const Row& row) const {
+ folly::dynamic rowJsonObj = folly::dynamic::object();
+ auto rowDataList = folly::dynamic::array();
+ auto metaDataList = folly::dynamic::array();
+
+ for (const auto& ele : row.values) {
+ rowDataList.push_back(ele.toJson());
+ metaDataList.push_back(ele.getMetaData());
+ }
+
+ rowJsonObj.insert("row", rowDataList);
+ rowJsonObj.insert("meta", metaDataList);
+ return rowJsonObj;
+ }
+
bool operator==(const DataSet& rhs) const { return colNames == rhs.colNames && rows == rhs.rows; }
};
diff --git a/src/common/datatypes/Date.h b/src/common/datatypes/Date.h
index 732fc786eda..7afd65f4549 100644
--- a/src/common/datatypes/Date.h
+++ b/src/common/datatypes/Date.h
@@ -7,6 +7,8 @@
#ifndef COMMON_DATATYPES_DATE_H_
#define COMMON_DATATYPES_DATE_H_
+#include
+
#include
namespace nebula {
@@ -62,6 +64,7 @@ struct Date {
Date operator-(int64_t days) const;
std::string toString() const;
+ folly::dynamic toJson() const { return toString(); }
// Return the number of days since -32768/1/1
int64_t toInt() const;
@@ -113,6 +116,8 @@ struct Time {
}
std::string toString() const;
+ // 'Z' representing UTC timezone
+ folly::dynamic toJson() const { return toString() + "Z"; }
};
inline std::ostream& operator<<(std::ostream& os, const Time& d) {
@@ -203,6 +208,8 @@ struct DateTime {
}
std::string toString() const;
+ // 'Z' representing UTC timezone
+ folly::dynamic toJson() const { return toString() + "Z"; }
};
inline std::ostream& operator<<(std::ostream& os, const DateTime& d) {
diff --git a/src/common/datatypes/Edge.cpp b/src/common/datatypes/Edge.cpp
index 95d04aa41a6..1893fb79fca 100644
--- a/src/common/datatypes/Edge.cpp
+++ b/src/common/datatypes/Edge.cpp
@@ -32,6 +32,45 @@ std::string Edge::toString() const {
return os.str();
}
+// format:
+// {
+// "prop1": val1,
+// "prop2": val2,
+// }
+folly::dynamic Edge::toJson() const {
+ folly::dynamic propObj = folly::dynamic::object();
+
+ for (const auto& iter : props) {
+ propObj.insert(iter.first, iter.second.toJson());
+ }
+
+ return propObj;
+}
+
+// Used in Json form query result
+// format:
+// {
+// "id": {
+// "name": _name,
+// "src": srcVID,
+// "dst": dstVID,
+// "type": _type,
+// "ranking": _rankding
+// }
+// "type": "edge"
+// }
+folly::dynamic Edge::getMetaData() const {
+ folly::dynamic edgeMetadataObj = folly::dynamic::object();
+
+ folly::dynamic edgeIdObj = folly::dynamic::object("name", name)("src", src.toJson())(
+ "dst", dst.toJson())("type", type)("ranking", ranking);
+
+ edgeMetadataObj.insert("id", edgeIdObj);
+ edgeMetadataObj.insert("type", "edge");
+
+ return edgeMetadataObj;
+}
+
bool Edge::contains(const Value& key) const {
if (!key.isStr()) {
return false;
diff --git a/src/common/datatypes/Edge.h b/src/common/datatypes/Edge.h
index 32440217eda..b1c3247af84 100644
--- a/src/common/datatypes/Edge.h
+++ b/src/common/datatypes/Edge.h
@@ -50,6 +50,9 @@ struct Edge {
void __clear() { clear(); }
std::string toString() const;
+ folly::dynamic toJson() const;
+ // Used in Json form query result
+ folly::dynamic getMetaData() const;
bool operator==(const Edge& rhs) const;
diff --git a/src/common/datatypes/List.cpp b/src/common/datatypes/List.cpp
index ca747dee6b9..ac2af1679ff 100644
--- a/src/common/datatypes/List.cpp
+++ b/src/common/datatypes/List.cpp
@@ -22,4 +22,24 @@ std::string List::toString() const {
return os.str();
}
+folly::dynamic List::toJson() const {
+ auto listJsonObj = folly::dynamic::array();
+
+ for (const auto& val : values) {
+ listJsonObj.push_back(val.toJson());
+ }
+
+ return listJsonObj;
+}
+
+folly::dynamic List::getMetaData() const {
+ auto listMetadataObj = folly::dynamic::array();
+
+ for (const auto& val : values) {
+ listMetadataObj.push_back(val.getMetaData());
+ }
+
+ return listMetadataObj;
+}
+
} // namespace nebula
diff --git a/src/common/datatypes/List.h b/src/common/datatypes/List.h
index c5622820905..be768a0de08 100644
--- a/src/common/datatypes/List.h
+++ b/src/common/datatypes/List.h
@@ -65,6 +65,9 @@ struct List {
size_t size() const { return values.size(); }
std::string toString() const;
+ folly::dynamic toJson() const;
+ // Extract the metadata of each element
+ folly::dynamic getMetaData() const;
};
inline std::ostream& operator<<(std::ostream& os, const List& l) { return os << l.toString(); }
diff --git a/src/common/datatypes/Map.cpp b/src/common/datatypes/Map.cpp
index b9839c120ae..7ab73d5c9b7 100644
--- a/src/common/datatypes/Map.cpp
+++ b/src/common/datatypes/Map.cpp
@@ -14,7 +14,7 @@ namespace nebula {
std::string Map::toString() const {
std::vector value(kvs.size());
- std::transform(kvs.begin(), kvs.end(), value.begin(), [](const auto &iter) -> std::string {
+ std::transform(kvs.begin(), kvs.end(), value.begin(), [](const auto& iter) -> std::string {
std::stringstream out;
out << iter.first << ":" << iter.second;
return out.str();
@@ -25,4 +25,24 @@ std::string Map::toString() const {
return os.str();
}
+folly::dynamic Map::toJson() const {
+ folly::dynamic mapJsonObj = folly::dynamic::object();
+
+ for (const auto& iter : kvs) {
+ mapJsonObj.insert(iter.first, iter.second.toJson());
+ }
+
+ return mapJsonObj;
+}
+
+folly::dynamic Map::getMetaData() const {
+ auto mapMetadataObj = folly::dynamic::array();
+
+ for (const auto& kv : kvs) {
+ mapMetadataObj.push_back(kv.second.getMetaData());
+ }
+
+ return mapMetadataObj;
+}
+
} // namespace nebula
diff --git a/src/common/datatypes/Map.h b/src/common/datatypes/Map.h
index 7f7ec9b6a81..333e5d0cb0d 100644
--- a/src/common/datatypes/Map.h
+++ b/src/common/datatypes/Map.h
@@ -43,6 +43,9 @@ struct Map {
// the configs of rocksdb will use the interface, so the value need modify to
// string
std::string toString() const;
+ folly::dynamic toJson() const;
+ // Extract the metadata of the value of each kv pair
+ folly::dynamic getMetaData() const;
bool operator==(const Map& rhs) const { return kvs == rhs.kvs; }
diff --git a/src/common/datatypes/Path.h b/src/common/datatypes/Path.h
index 1d4e2184ded..17eb77a3cee 100644
--- a/src/common/datatypes/Path.h
+++ b/src/common/datatypes/Path.h
@@ -130,6 +130,55 @@ struct Path {
return os.str();
}
+ folly::dynamic toJson() const {
+ folly::dynamic pathJsonObj = folly::dynamic::array();
+ auto srcVertex = src;
+ pathJsonObj.push_back(srcVertex.toJson());
+
+ for (const auto& s : steps) {
+ folly::dynamic edgeJsonObj = folly::dynamic::object();
+ // parse edge props map as json
+ for (const auto& iter : s.props) {
+ edgeJsonObj.insert(iter.first, iter.second.toJson());
+ }
+ // add edge json obj to path
+ pathJsonObj.push_back(edgeJsonObj);
+
+ // reset src vertex and add vertex json obj to path
+ srcVertex = s.dst;
+ pathJsonObj.push_back(srcVertex.toJson());
+ }
+
+ return pathJsonObj;
+ }
+
+ // Used in Json form query result
+ // format:
+ // [vertex1_metadata, edge1_metadata, vertex2_metadata, edge2_metadata,....]
+ folly::dynamic getMetaData() const {
+ auto dynamicObj = folly::dynamic::array();
+ auto srcVertex = src;
+ dynamicObj.push_back(srcVertex.getMetaData());
+
+ // Construct edge metadata
+ for (const auto& s : steps) {
+ folly::dynamic edgeIdObj = folly::dynamic::object();
+ edgeIdObj.insert("src", srcVertex.vid.toJson());
+ edgeIdObj.insert("dst", s.dst.vid.toJson());
+ edgeIdObj.insert("type", s.type);
+ edgeIdObj.insert("name", s.name);
+ edgeIdObj.insert("ranking", s.ranking);
+
+ folly::dynamic edgeMetadataObj = folly::dynamic::object("id", edgeIdObj)("type", "edge");
+ dynamicObj.push_back(edgeMetadataObj);
+ dynamicObj.push_back(s.dst.getMetaData());
+ // reset src vertex
+ srcVertex = s.dst;
+ }
+
+ return dynamicObj;
+ }
+
Path& operator=(Path&& rhs) noexcept {
if (&rhs != this) {
src = std::move(rhs.src);
diff --git a/src/common/datatypes/Set.cpp b/src/common/datatypes/Set.cpp
index c31ffc1de21..2e0e90a2f5a 100644
--- a/src/common/datatypes/Set.cpp
+++ b/src/common/datatypes/Set.cpp
@@ -22,4 +22,24 @@ std::string Set::toString() const {
return os.str();
}
+folly::dynamic Set::toJson() const {
+ auto setJsonObj = folly::dynamic::array();
+
+ for (const auto& val : values) {
+ setJsonObj.push_back(val.toJson());
+ }
+
+ return setJsonObj;
+}
+
+folly::dynamic Set::getMetaData() const {
+ auto setMetadataObj = folly::dynamic::array();
+
+ for (const auto& val : values) {
+ setMetadataObj.push_back(val.getMetaData());
+ }
+
+ return setMetadataObj;
+}
+
} // namespace nebula
diff --git a/src/common/datatypes/Set.h b/src/common/datatypes/Set.h
index e844aa879eb..dabc33e0d7e 100644
--- a/src/common/datatypes/Set.h
+++ b/src/common/datatypes/Set.h
@@ -26,6 +26,9 @@ struct Set {
void __clear() { clear(); }
std::string toString() const;
+ folly::dynamic toJson() const;
+ // Extract the metadata of each element
+ folly::dynamic getMetaData() const;
Set& operator=(const Set& rhs) {
if (this == &rhs) {
diff --git a/src/common/datatypes/Value.cpp b/src/common/datatypes/Value.cpp
index ad6e82e4043..19545de8135 100644
--- a/src/common/datatypes/Value.cpp
+++ b/src/common/datatypes/Value.cpp
@@ -1344,6 +1344,116 @@ void Value::setG(DataSet&& v) {
new (std::addressof(value_.gVal)) std::unique_ptr(new DataSet(std::move(v)));
}
+// Convert Nebula::Value to a value compatible with Json standard
+// DATE, TIME, DATETIME will be converted to strings in UTC
+// VERTEX, EDGES, PATH will be converted to objects
+folly::dynamic Value::toJson() const {
+ switch (type_) {
+ case Value::Type::__EMPTY__: {
+ return "__EMPTY__";
+ }
+ // Json null
+ case Value::Type::NULLVALUE: {
+ return folly::dynamic(nullptr);
+ }
+ // Json bool
+ case Value::Type::BOOL: {
+ return folly::dynamic(getBool());
+ }
+ // Json int
+ case Value::Type::INT: {
+ return folly::dynamic(getInt());
+ }
+ // json double
+ case Value::Type::FLOAT: {
+ return folly::dynamic(getFloat());
+ }
+ // json string
+ case Value::Type::STRING: {
+ return folly::dynamic(getStr());
+ }
+ // Json array
+ case Value::Type::LIST: {
+ return getList().toJson();
+ }
+ case Value::Type::SET: {
+ return getSet().toJson();
+ }
+ // Json object
+ case Value::Type::MAP: {
+ return getMap().toJson();
+ }
+ case Value::Type::DATE: {
+ return getDate().toJson();
+ }
+ case Value::Type::TIME: {
+ return getTime().toJson();
+ }
+ case Value::Type::DATETIME: {
+ return getDateTime().toJson();
+ }
+ case Value::Type::EDGE: {
+ return getEdge().toJson();
+ }
+ case Value::Type::VERTEX: {
+ return getVertex().toJson();
+ }
+ case Value::Type::PATH: {
+ return getPath().toJson();
+ }
+ case Value::Type::DATASET: {
+ return getDataSet().toJson();
+ }
+ // no default so the compiler will warning when lack
+ }
+
+ LOG(FATAL) << "Unknown value type " << static_cast(type_);
+}
+
+folly::dynamic Value::getMetaData() const {
+ auto dynamicObj = folly::dynamic();
+ switch (type_) {
+ // Privative datatypes has no meta data
+ case Value::Type::__EMPTY__:
+ case Value::Type::BOOL:
+ case Value::Type::INT:
+ case Value::Type::FLOAT:
+ case Value::Type::STRING:
+ case Value::Type::DATASET:
+ case Value::Type::NULLVALUE: {
+ return folly::dynamic(nullptr);
+ }
+ // Extract the meta info of each element as the metadata of the container
+ case Value::Type::LIST: {
+ return getList().getMetaData();
+ }
+ case Value::Type::SET: {
+ return getSet().getMetaData();
+ }
+ case Value::Type::MAP: {
+ return getMap().getMetaData();
+ }
+ case Value::Type::DATE:
+ case Value::Type::TIME:
+ case Value::Type::DATETIME: {
+ return folly::dynamic::object("type", typeName());
+ }
+ case Value::Type::VERTEX: {
+ return getVertex().getMetaData();
+ }
+ case Value::Type::EDGE: {
+ return getEdge().getMetaData();
+ }
+ case Value::Type::PATH: {
+ return getPath().getMetaData();
+ }
+ default:
+ break;
+ }
+
+ LOG(FATAL) << "Unknown value type " << static_cast(type_);
+}
+
std::string Value::toString() const {
switch (type_) {
case Value::Type::__EMPTY__: {
diff --git a/src/common/datatypes/Value.h b/src/common/datatypes/Value.h
index 00c413cb4a9..57dc60df4e4 100644
--- a/src/common/datatypes/Value.h
+++ b/src/common/datatypes/Value.h
@@ -7,6 +7,8 @@
#ifndef COMMON_DATATYPES_VALUE_H_
#define COMMON_DATATYPES_VALUE_H_
+#include
+
#include
#include "common/datatypes/Date.h"
@@ -268,6 +270,9 @@ struct Value {
static const Value& null() noexcept { return kNullValue; }
std::string toString() const;
+ folly::dynamic toJson() const;
+ // Used in Json form query result
+ folly::dynamic getMetaData() const;
Value toBool() const;
Value toFloat() const;
diff --git a/src/common/datatypes/Vertex.cpp b/src/common/datatypes/Vertex.cpp
index 1d0f227025e..9f839a26831 100644
--- a/src/common/datatypes/Vertex.cpp
+++ b/src/common/datatypes/Vertex.cpp
@@ -6,6 +6,7 @@
#include "common/datatypes/Vertex.h"
+#include
#include
#include
@@ -24,6 +25,20 @@ std::string Tag::toString() const {
return os.str();
}
+// {
+// "player.name" : "Tim Duncan",
+// "player.age" : 42,
+// }
+folly::dynamic Tag::toJson() const {
+ folly::dynamic tagJsonObj = folly::dynamic::object();
+
+ for (const auto& iter : props) {
+ tagJsonObj.insert(name + "." + iter.first, iter.second.toJson());
+ }
+
+ return tagJsonObj;
+}
+
bool Vertex::contains(const Value& key) const {
if (!key.isStr()) {
return false;
@@ -59,6 +74,31 @@ std::string Vertex::toString() const {
return os.str();
}
+// {
+// "player.name" : "Tim Duncan",
+// "player.age" : 42,
+// "bachelor.name" : "Tim Duncan",
+// "bachelor.speciality" : "psychology"
+// }
+folly::dynamic Vertex::toJson() const {
+ folly::dynamic propJsonObj = folly::dynamic::object();
+
+ for (const auto& tag : tags) {
+ propJsonObj.update(tag.toJson());
+ }
+ return propJsonObj;
+}
+
+// format:
+// {
+// "id": _vid
+// "type": "vertex"
+// }
+folly::dynamic Vertex::getMetaData() const {
+ folly::dynamic vertexMetadataObj = folly::dynamic::object("id", vid.toJson())("type", "vertex");
+ return vertexMetadataObj;
+}
+
Vertex& Vertex::operator=(Vertex&& rhs) noexcept {
if (&rhs != this) {
vid = std::move(rhs.vid);
diff --git a/src/common/datatypes/Vertex.h b/src/common/datatypes/Vertex.h
index a0c60318692..08d50b40f0e 100644
--- a/src/common/datatypes/Vertex.h
+++ b/src/common/datatypes/Vertex.h
@@ -34,6 +34,7 @@ struct Tag {
void __clear() { clear(); }
std::string toString() const;
+ folly::dynamic toJson() const;
Tag& operator=(Tag&& rhs) noexcept {
if (&rhs != this) {
@@ -71,6 +72,9 @@ struct Vertex {
void __clear() { clear(); }
std::string toString() const;
+ folly::dynamic toJson() const;
+ // Used in Json form query result
+ folly::dynamic getMetaData() const;
Vertex& operator=(Vertex&& rhs) noexcept;
diff --git a/src/common/datatypes/test/CMakeLists.txt b/src/common/datatypes/test/CMakeLists.txt
index a45c5d2ffd6..54517fda196 100644
--- a/src/common/datatypes/test/CMakeLists.txt
+++ b/src/common/datatypes/test/CMakeLists.txt
@@ -79,6 +79,25 @@ nebula_add_test(
gtest
)
+nebula_add_test(
+ NAME
+ value_to_json_test
+ SOURCES
+ ValueToJsonTest.cpp
+ OBJECTS
+ $
+ $
+ $
+ $
+ $
+ $
+ $
+ $
+ LIBRARIES
+ gtest
+ ${THRIFT_LIBRARIES}
+)
+
nebula_add_executable(
NAME
edge_bm
diff --git a/src/common/datatypes/test/ValueToJsonTest.cpp b/src/common/datatypes/test/ValueToJsonTest.cpp
new file mode 100644
index 00000000000..3f729d37a6b
--- /dev/null
+++ b/src/common/datatypes/test/ValueToJsonTest.cpp
@@ -0,0 +1,314 @@
+/* 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
+#include
+
+#include "common/base/Base.h"
+#include "common/datatypes/CommonCpp2Ops.h"
+#include "common/datatypes/DataSet.h"
+#include "common/datatypes/Date.h"
+#include "common/datatypes/Edge.h"
+#include "common/datatypes/List.h"
+#include "common/datatypes/Map.h"
+#include "common/datatypes/Path.h"
+#include "common/datatypes/Set.h"
+#include "common/datatypes/Value.h"
+#include "common/datatypes/Vertex.h"
+
+namespace nebula {
+
+using folly::dynamic;
+using serializer = apache::thrift::CompactSerializer;
+
+TEST(ValueToJson, vertex) {
+ // Test tag to json
+ auto tag1 = Tag("tagName", {{"prop", Value(2)}});
+ auto tag2 =
+ Tag("tagName1",
+ {{"prop1", Value(2)}, {"prop2", Value(NullType::__NULL__)}, {"prop3", Value("123")}});
+ {
+ dynamic expectedTagJson = dynamic::object("tagName.prop", 2);
+ ASSERT_EQ(expectedTagJson, tag1.toJson());
+ }
+ {
+ dynamic expectedTagJson =
+ dynamic::object("tagName1.prop1", 2)("tagName1.prop2", nullptr)("tagName1.prop3", "123");
+ ASSERT_EQ(expectedTagJson, tag2.toJson());
+ }
+
+ // vertex wtih string vid
+ auto vertexStrVid = Value(Vertex({"Vid",
+ {
+ tag1,
+ tag2,
+ }}));
+
+ // integerID vertex
+ auto vertexIntVid = Value(Vertex({001,
+ {
+ tag1,
+ tag2,
+ }}));
+ {
+ dynamic expectedVeretxJson = dynamic::object("tagName.prop", 2)("tagName1.prop1", 2)(
+ "tagName1.prop2", nullptr)("tagName1.prop3", "123");
+ ASSERT_EQ(expectedVeretxJson, vertexStrVid.toJson());
+
+ dynamic expectedVeretxMetaJson = dynamic::object("id", "Vid")("type", "vertex");
+ ASSERT_EQ(expectedVeretxMetaJson, vertexStrVid.getMetaData());
+ }
+ {
+ dynamic expectedVeretxJson = dynamic::object("tagName.prop", 2)("tagName1.prop1", 2)(
+ "tagName1.prop2", nullptr)("tagName1.prop3", "123");
+ ASSERT_EQ(expectedVeretxJson, vertexIntVid.toJson());
+
+ dynamic expectedVeretxMetaJson = dynamic::object("id", 001)("type", "vertex");
+ ASSERT_EQ(expectedVeretxMetaJson, vertexIntVid.getMetaData());
+ }
+}
+
+TEST(ValueToJson, edge) {
+ // edge
+ auto edge1 =
+ Value(Edge("Src", "Dst", 1, "Edge", 233, {{"prop1", Value(233)}, {"prop2", Value(2.3)}}));
+ // integerID edge
+ auto edge2 =
+ Value(Edge(101, 102, 1, "Edge", 233, {{"prop1", Value(233)}, {"prop2", Value(2.3)}}));
+ {
+ dynamic expectedEdgeJson = dynamic::object("prop1", 233)("prop2", 2.3);
+ ASSERT_EQ(expectedEdgeJson, edge1.toJson());
+
+ dynamic expectedEdgeMetaJson =
+ dynamic::object("id",
+ dynamic::object("name", "Edge")("src", "Src")("dst", "Dst")("type", 1)(
+ "name", "Edge")("ranking", 233))("type", "edge");
+ ASSERT_EQ(expectedEdgeMetaJson, edge1.getMetaData());
+ }
+
+ {
+ dynamic expectedEdgeJson = dynamic::object("prop1", 233)("prop2", 2.3);
+ ASSERT_EQ(expectedEdgeJson, edge2.toJson());
+
+ dynamic expectedEdgeMetaJson =
+ dynamic::object("id",
+ dynamic::object("name", "Edge")("src", 101)("dst", 102)("type", 1)(
+ "name", "Edge")("ranking", 233))("type", "edge");
+ ASSERT_EQ(expectedEdgeMetaJson, edge2.getMetaData());
+ }
+}
+
+TEST(ValueToJson, path) {
+ auto path = Value(Path(Vertex({"v1", {Tag("tagName", {{"prop1", Value(1)}})}}),
+ {Step(Vertex({"v2",
+ {Tag("tagName2",
+ {{"prop1", Value(2)},
+ {"prop2", Value(NullType::__NULL__)},
+ {"prop3", Value("123")}})}}),
+ 1,
+ "edgeName",
+ 100,
+ {{"edgeProp", "edgePropVal"}})}));
+ auto emptyPath = Value(Path());
+
+ dynamic expectedPathJsonObj = dynamic::array(
+ dynamic::object("tagName.prop1", 1),
+ dynamic::object("edgeProp", "edgePropVal"),
+ dynamic::object("tagName2.prop1", 2)("tagName2.prop2", nullptr)("tagName2.prop3", "123"));
+ ASSERT_EQ(expectedPathJsonObj, path.toJson());
+
+ dynamic expectedPathMetaJson = dynamic::array(
+ dynamic::object("id", "v1")("type", "vertex"),
+ dynamic::object("id",
+ dynamic::object("name", "Edge")("src", "v1")("dst", "v2")("type", 1)(
+ "name", "edgeName")("ranking", 100))("type", "edge"),
+ dynamic::object("id", "v2")("type", "vertex"));
+ ASSERT_EQ(expectedPathMetaJson, path.getMetaData());
+}
+
+TEST(ValueToJson, list) {
+ auto list1 = Value(List({Value(2), // int
+ Value(2.33), // float
+ Value(true), // bool
+ Value("str"), // string
+ Date(2021, 12, 21), // date
+ Time(13, 30, 15, 0), // time
+ DateTime(2021, 12, 21, 13, 30, 15, 0)})); // datetime
+ dynamic expectedListJsonObj = dynamic::array(
+ 2, 2.33, true, "str", "2021-12-21", "13:30:15.000000Z", "2021-12-21T13:30:15.0Z");
+ ASSERT_EQ(expectedListJsonObj, list1.toJson());
+
+ dynamic expectedListMetaObj = dynamic::array(nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ dynamic::object("type", "date"),
+ dynamic::object("type", "time"),
+ dynamic::object("type", "datetime"));
+ ASSERT_EQ(expectedListMetaObj, list1.getMetaData());
+}
+
+TEST(ValueToJson, Set) {
+ auto set = Value(Set({Value(2), // int
+ Value(2.33), // float
+ Value(true), // bool
+ Value("str"), // string
+ Date(2021, 12, 21), // date
+ Time(13, 30, 15, 0), // time
+ DateTime(2021, 12, 21, 13, 30, 15, 0)})); // datetime
+ dynamic expectedSetJsonObj = dynamic::array(
+ 2, 2.33, true, "str", "2021-12-21", "13:30:15.000000Z", "2021-12-21T13:30:15.0Z");
+ // The underlying data strcuture is unordered_set, so sort before the comparison
+ auto actualJson = set.toJson();
+ std::sort(actualJson.begin(), actualJson.end());
+ std::sort(expectedSetJsonObj.begin(), expectedSetJsonObj.end());
+ ASSERT_EQ(expectedSetJsonObj, actualJson);
+
+ // Skip meta json comparison since nested dynamic objects cannot be sorted. i.g. dynamic::object
+ // inside dynamic::array
+}
+
+TEST(ValueToJson, map) {
+ auto map = Value(Map({{"key1", Value(2)}, // int
+ {"key2", Value(2.33)}, // float
+ {"key3", Value(true)}, // bool
+ {"key4", Value("str")}, // string
+ {"key5", Date(2021, 12, 21)}, // date
+ {"key6", Time(13, 30, 15, 0)}, // time
+ {"key7", DateTime(2021, 12, 21, 13, 30, 15, 0)}})); // datetime
+ dynamic expectedMapJsonObj =
+ dynamic::object("key1", 2)("key2", 2.33)("key3", true)("key4", "str")("key5", "2021-12-21")(
+ "key6", "13:30:15.000000Z")("key7", "2021-12-21T13:30:15.0Z");
+ ASSERT_EQ(expectedMapJsonObj, map.toJson());
+ // Skip meta json comparison since nested dynamic objects cannot be sorted. i.g. dynamic::object
+ // inside dynamic::array
+}
+
+TEST(ValueToJson, dataset) {
+ DataSet dataset = DataSet({"col1", "col2", "col3", "col4", "col5", "col6", "col7"});
+ dataset.emplace_back(List({Value(2), // int
+ Value(2.33), // float
+ Value(true), // bool
+ Value("str"), // string
+ Date(2021, 12, 21), // date
+ Time(13, 30, 15, 0), // time
+ DateTime(2021, 12, 21, 13, 30, 15, 0)}));
+ dynamic expectedDatasetJsonObj = dynamic::array(dynamic::object(
+ "row",
+ dynamic::array(
+ 2, 2.33, true, "str", "2021-12-21", "13:30:15.000000Z", "2021-12-21T13:30:15.0Z"))(
+ "meta",
+ dynamic::array(nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ dynamic::object("type", "date"),
+ dynamic::object("type", "time"),
+ dynamic::object("type", "datetime"))));
+ ASSERT_EQ(expectedDatasetJsonObj, dataset.toJson());
+}
+
+TEST(ValueToJson, DecodeEncode) {
+ std::vector values{
+ // empty
+ Value(),
+
+ // null
+ Value(NullType::__NULL__),
+ Value(NullType::DIV_BY_ZERO),
+ Value(NullType::BAD_DATA),
+ Value(NullType::ERR_OVERFLOW),
+ Value(NullType::OUT_OF_RANGE),
+ Value(NullType::UNKNOWN_PROP),
+
+ // int
+ Value(0),
+ Value(1),
+ Value(2),
+
+ // float
+ Value(3.14),
+ Value(2.67),
+
+ // string
+ Value("Hello "),
+ Value("World"),
+
+ // bool
+ Value(false),
+ Value(true),
+
+ // date
+ Value(Date(2020, 1, 1)),
+ Value(Date(2019, 12, 1)),
+
+ // time
+ Value(Time{1, 2, 3, 4}),
+
+ // datatime
+ Value(DateTime{1, 2, 3, 4, 5, 6, 7}),
+
+ // vertex
+ Value(
+ Vertex({"Vid",
+ {
+ Tag("tagName", {{"prop", Value(2)}}),
+ Tag("tagName1", {{"prop1", Value(2)}, {"prop2", Value(NullType::__NULL__)}}),
+ }})),
+
+ // integerID vertex
+ Value(
+ Vertex({001,
+ {
+ Tag("tagName", {{"prop", Value(2)}}),
+ Tag("tagName1", {{"prop1", Value(2)}, {"prop2", Value(NullType::__NULL__)}}),
+ }})),
+
+ // edge
+ Value(Edge("Src", "Dst", 1, "Edge", 233, {{"prop1", Value(233)}, {"prop2", Value(2.3)}})),
+
+ // integerID edge
+ Value(Edge(001, 002, 1, "Edge", 233, {{"prop1", Value(233)}, {"prop2", Value(2.3)}})),
+
+ // Path
+ Value(Path(
+ Vertex({"1", {Tag("tagName", {{"prop", Value(2)}})}}),
+ {Step(Vertex({"1", {Tag("tagName", {{"prop", Value(2)}})}}), 1, "1", 1, {{"1", 1}})})),
+ Value(Path()),
+
+ // List
+ Value(List({Value(2), Value(true), Value(2.33)})),
+
+ // Set
+ Value(Set({Value(2), Value(true), Value(2.33)})),
+
+ // Map
+ Value(Map({{"Key1", Value(2)}, {"Key2", Value(true)}, {"Key3", Value(2.33)}})),
+
+ // DataSet
+ Value(DataSet({"col1", "col2"})),
+ };
+ for (const auto& val : values) {
+ std::string buf;
+ buf.reserve(128);
+ folly::dynamic jsonObj = val.toJson();
+ auto jsonString = folly::toJson(jsonObj);
+ serializer::serialize(jsonString, &buf);
+ std::string valCopy;
+ std::size_t s = serializer::deserialize(buf, valCopy);
+ ASSERT_EQ(s, buf.size());
+ EXPECT_EQ(jsonString, valCopy);
+ }
+}
+
+} // namespace nebula
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ folly::init(&argc, &argv, true);
+ google::SetStderrLogging(google::INFO);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/src/common/function/test/FunctionManagerTest.cpp b/src/common/function/test/FunctionManagerTest.cpp
index 817eb6ee15e..a02bc86a609 100644
--- a/src/common/function/test/FunctionManagerTest.cpp
+++ b/src/common/function/test/FunctionManagerTest.cpp
@@ -37,10 +37,12 @@ class FunctionManagerTest : public ::testing::Test {
}
auto res = result.value()(argsRef);
if (res.type() != expect.type()) {
- return ::testing::AssertionFailure() << "function return type check failed: " << expr;
+ return ::testing::AssertionFailure() << "function return type check failed, expect "
+ << expect.type() << ", got " << res.type();
}
if (res != expect) {
- return ::testing::AssertionFailure() << "function return value check failed: " << expr;
+ return ::testing::AssertionFailure()
+ << "function return value check failed, expect " << expect << ", got " << res;
}
return ::testing::AssertionSuccess();
}
@@ -519,6 +521,42 @@ TEST_F(FunctionManagerTest, functionCall) {
{Map({{"hour", 20}, {"minute", 9}, {"second", 15}})},
Value(time::TimeUtils::timeToUTC(Time(20, 9, 15, 0))));
}
+ {
+ TEST_FUNCTION(time,
+ {Map({{"hour", 20},
+ {"minute", 9},
+ {"second", 15},
+ {"millisecond", 10},
+ {"microsecond", 15}})},
+ Value(time::TimeUtils::timeToUTC(Time(20, 9, 15, 10015))));
+ }
+ {
+ TEST_FUNCTION(time,
+ {Map({{"hour", 20},
+ {"minute", 9},
+ {"second", 15},
+ {"millisecond", 999},
+ {"microsecond", 999}})},
+ Value(time::TimeUtils::timeToUTC(Time(20, 9, 15, 999999))));
+ }
+ {
+ TEST_FUNCTION(time,
+ {Map({{"hour", 20},
+ {"minute", 9},
+ {"second", 15},
+ {"millisecond", 1000},
+ {"microsecond", 999}})},
+ Value::kNullBadData);
+ }
+ {
+ TEST_FUNCTION(time,
+ {Map({{"hour", 20},
+ {"minute", 9},
+ {"second", 15},
+ {"millisecond", 999},
+ {"microsecond", 1000}})},
+ Value::kNullBadData);
+ }
// range [(0, 0, 0, 0), (23, 59, 59, 999999)]
{
TEST_FUNCTION(time,
@@ -566,8 +604,46 @@ TEST_F(FunctionManagerTest, functionCall) {
{"day", 15},
{"hour", 20},
{"minute", 9},
- {"second", 15}})},
- Value(time::TimeUtils::dateTimeToUTC(DateTime(2020, 9, 15, 20, 9, 15, 0))));
+ {"second", 15},
+ {"millisecond", 10},
+ {"microsecond", 15}})},
+ Value(time::TimeUtils::dateTimeToUTC(DateTime(2020, 9, 15, 20, 9, 15, 10015))));
+ }
+ {
+ TEST_FUNCTION(datetime,
+ {Map({{"year", 2020},
+ {"month", 9},
+ {"day", 15},
+ {"hour", 20},
+ {"minute", 9},
+ {"second", 15},
+ {"millisecond", 999},
+ {"microsecond", 999}})},
+ Value(time::TimeUtils::dateTimeToUTC(DateTime(2020, 9, 15, 20, 9, 15, 999999))));
+ }
+ {
+ TEST_FUNCTION(datetime,
+ {Map({{"year", 2020},
+ {"month", 9},
+ {"day", 15},
+ {"hour", 20},
+ {"minute", 9},
+ {"second", 15},
+ {"millisecond", 1000},
+ {"microsecond", 999}})},
+ Value::kNullBadData);
+ }
+ {
+ TEST_FUNCTION(datetime,
+ {Map({{"year", 2020},
+ {"month", 9},
+ {"day", 15},
+ {"hour", 20},
+ {"minute", 9},
+ {"second", 15},
+ {"millisecond", 999},
+ {"microsecond", 1000}})},
+ Value::kNullBadData);
}
{
TEST_FUNCTION(datetime,
diff --git a/src/common/graph/Response.cpp b/src/common/graph/Response.cpp
index a03885bd1fa..82f3def31c7 100644
--- a/src/common/graph/Response.cpp
+++ b/src/common/graph/Response.cpp
@@ -31,7 +31,7 @@ bool PlanNodeDescription::operator==(const PlanNodeDescription& rhs) const {
case ErrorCode::EnumName: \
return #EnumName;
-const char* errorCode(ErrorCode code) {
+const char* getErrorCode(ErrorCode code) {
switch (code) { ErrorCodeEnums }
return "Unknown error";
}
diff --git a/src/common/graph/Response.h b/src/common/graph/Response.h
index 0adfd2b52cf..3d7ee8b88c1 100644
--- a/src/common/graph/Response.h
+++ b/src/common/graph/Response.h
@@ -7,6 +7,9 @@
#ifndef COMMON_GRAPH_RESPONSE_H
#define COMMON_GRAPH_RESPONSE_H
+#include
+#include
+
#include
#include
#include
@@ -183,10 +186,10 @@ enum class ErrorCode { ErrorCodeEnums };
#undef X
-const char *errorCode(ErrorCode code);
+const char *getErrorCode(ErrorCode code);
static inline std::ostream &operator<<(std::ostream &os, ErrorCode code) {
- os << errorCode(code);
+ os << getErrorCode(code);
return os;
}
@@ -266,6 +269,16 @@ struct ProfilingStats {
int64_t totalDurationInUs{0};
// Other profiling stats data map
std::unique_ptr> otherStats;
+
+ folly::dynamic toJson() const {
+ folly::dynamic ProfilingStatsObj = folly::dynamic::object();
+ ProfilingStatsObj.insert("rows", rows);
+ ProfilingStatsObj.insert("execDurationInUs", execDurationInUs);
+ ProfilingStatsObj.insert("totalDurationInUs", totalDurationInUs);
+ ProfilingStatsObj.insert("otherStats", folly::toDynamic(*otherStats));
+
+ return ProfilingStatsObj;
+ }
};
// The info used for select/loop.
@@ -285,6 +298,14 @@ struct PlanNodeBranchInfo {
bool isDoBranch{0};
// select/loop node id
int64_t conditionNodeId{-1};
+
+ folly::dynamic toJson() const {
+ folly::dynamic PlanNodeBranchInfoObj = folly::dynamic::object();
+ PlanNodeBranchInfoObj.insert("isDoBranch", isDoBranch);
+ PlanNodeBranchInfoObj.insert("conditionNodeId", conditionNodeId);
+
+ return PlanNodeBranchInfoObj;
+ }
};
struct Pair {
@@ -299,6 +320,11 @@ struct Pair {
std::string key;
std::string value;
+
+ folly::dynamic toJson() const {
+ folly::dynamic pairObj = folly::dynamic::object(key, value);
+ return pairObj;
+ }
};
struct PlanNodeDescription {
@@ -326,6 +352,32 @@ struct PlanNodeDescription {
std::unique_ptr> profiles{nullptr};
std::unique_ptr branchInfo{nullptr};
std::unique_ptr> dependencies{nullptr};
+
+ folly::dynamic toJson() const {
+ folly::dynamic planNodeDescObj = folly::dynamic::object();
+ planNodeDescObj.insert("name", name);
+ planNodeDescObj.insert("id", id);
+ planNodeDescObj.insert("outputVar", outputVar);
+
+ auto descriptionObj = folly::dynamic::array();
+ descriptionObj.resize(description->size());
+ std::transform(
+ description->begin(), description->end(), descriptionObj.begin(), [](const auto &ele) {
+ return ele.toJson();
+ });
+ planNodeDescObj.insert("description", descriptionObj);
+
+ auto profilesObj = folly::dynamic::array();
+ profilesObj.resize(profiles->size());
+ std::transform(profiles->begin(), profiles->end(), profilesObj.begin(), [](const auto &ele) {
+ return ele.toJson();
+ });
+ planNodeDescObj.insert("profiles", profilesObj);
+ planNodeDescObj.insert("branchInfo", branchInfo->toJson());
+ planNodeDescObj.insert("dependencies", folly::toDynamic(*dependencies));
+
+ return planNodeDescObj;
+ }
};
struct PlanDescription {
@@ -350,6 +402,29 @@ struct PlanDescription {
std::string format;
// the optimization spent time
int32_t optimize_time_in_us{0};
+
+ folly::dynamic toJson() const {
+ folly::dynamic PlanDescObj = folly::dynamic::object();
+
+ auto planNodeDescsObj = folly::dynamic::array();
+ planNodeDescsObj.resize(planNodeDescs.size());
+ std::transform(planNodeDescs.begin(),
+ planNodeDescs.end(),
+ planNodeDescsObj.begin(),
+ [](const PlanNodeDescription &ele) { return ele.toJson(); });
+ PlanDescObj.insert("planNodeDescs", planNodeDescsObj);
+ // nodeIndexMap uses int as the key of the map, but strict json format only accepts string as
+ // the key, so convert the int to string here.
+ folly::dynamic nodeIndexMapObj = folly::dynamic::object();
+ for (const auto &kv : nodeIndexMap) {
+ nodeIndexMapObj.insert(folly::to(kv.first), kv.second);
+ }
+ PlanDescObj.insert("nodeIndexMap", nodeIndexMapObj);
+ PlanDescObj.insert("format", format);
+ PlanDescObj.insert("optimize_time_in_us", optimize_time_in_us);
+
+ return PlanDescObj;
+ }
};
struct ExecutionResponse {
@@ -397,6 +472,83 @@ struct ExecutionResponse {
std::unique_ptr errorMsg{nullptr};
std::unique_ptr planDesc{nullptr};
std::unique_ptr comment{nullptr};
+
+ // Return the response as a json string
+ // format
+ // "results": [
+ // {
+ // "columns": [],
+ // "data": [
+ // {
+ // "row": [ row-data ],
+ // "meta": [ metadata ]
+ // },
+ // ],
+ // "latencyInUs" : 0,
+ // "spaceName": "",
+ // "planDesc ": {
+ // "planNodeDescs": [ {
+ // "name" : "",
+ // "id" : 0,
+ // "outputVar" : "",
+ // "description" : {"key" : ""},
+ // "profiles" : [{
+ // "rows" : 1,
+ // "execDurationInUs" : 0,
+ // "totalDurationInUs" : 0,
+ // "otherStats" : {}, // map
+ // }],
+ // "branchInfo" : {
+ // "isDoBranch" : false,
+ // "conditionNodeId" : -1,
+ // },
+ // "dependencies" : [] // vector of ints
+ // }
+ // ],
+ // "nodeIndexMap" : {},
+ // "format" : "",
+ // "optimize_time_in_us" : 0,
+ // },
+ // "comment ": "",
+ // "errors" : "" // errorMsg
+ // }
+ // ]
+ // }
+ folly::dynamic toJson() const {
+ folly::dynamic respJsonObj = folly::dynamic::object();
+ folly::dynamic resultBody = folly::dynamic::object();
+
+ // required fields
+ folly::dynamic errorsBody = folly::dynamic::object();
+ errorsBody.insert("errorCode", getErrorCode(errorCode));
+ resultBody.insert("latencyInUs", latencyInUs);
+
+ // optional fields
+ if (errorMsg) {
+ errorsBody.insert("errorMsg", *errorMsg);
+ }
+ resultBody.insert("errors", errorsBody);
+
+ if (data) {
+ resultBody.insert("columns", folly::toDynamic(data->keys()));
+ resultBody.insert("data", data->toJson());
+ }
+ if (spaceName) {
+ resultBody.insert("spaceName", *spaceName);
+ }
+ if (planDesc) {
+ resultBody.insert("planDesc", planDesc->toJson());
+ }
+ if (comment) {
+ resultBody.insert("comment", *comment);
+ }
+
+ auto resultArray = folly::dynamic::array();
+ resultArray.push_back(resultBody);
+ respJsonObj.insert("results", resultArray);
+
+ return respJsonObj;
+ }
};
} // namespace nebula
diff --git a/src/common/graph/tests/ResponseEncodeDecodeTest.cpp b/src/common/graph/tests/ResponseEncodeDecodeTest.cpp
index 378525e0617..ec0a024df8c 100644
--- a/src/common/graph/tests/ResponseEncodeDecodeTest.cpp
+++ b/src/common/graph/tests/ResponseEncodeDecodeTest.cpp
@@ -89,4 +89,57 @@ TEST(ResponseEncodDecodeTest, Basic) {
}
}
+TEST(ResponseEncodDecodeTest, ToJson) {
+ // plan description
+ {
+ std::vector pds;
+ pds.emplace_back(PlanDescription{});
+ pds.emplace_back(PlanDescription{std::vector{},
+ std::unordered_map{{1, 2}, {4, 7}},
+ "format"});
+ for (const auto &pd : pds) {
+ std::string buf;
+ buf.reserve(128);
+ folly::dynamic jsonObj = pd.toJson();
+ auto jsonString = folly::toJson(jsonObj);
+ serializer::serialize(jsonString, &buf);
+ std::string copy;
+ std::size_t s = serializer::deserialize(buf, copy);
+ ASSERT_EQ(s, buf.size());
+ EXPECT_EQ(jsonString, copy);
+ }
+ }
+ // response
+ {
+ std::vector resps;
+ resps.emplace_back(ExecutionResponse{});
+ resps.emplace_back(ExecutionResponse{ErrorCode::SUCCEEDED, 233});
+ resps.emplace_back(ExecutionResponse{ErrorCode::SUCCEEDED,
+ 233,
+ std::make_unique(),
+ std::make_unique("test_space")});
+ resps.emplace_back(ExecutionResponse{ErrorCode::SUCCEEDED,
+ 233,
+ nullptr,
+ std::make_unique("test_space"),
+ nullptr,
+ std::make_unique()});
+ resps.emplace_back(ExecutionResponse{ErrorCode::E_SYNTAX_ERROR,
+ 233,
+ nullptr,
+ std::make_unique("test_space"),
+ std::make_unique("Error Msg.")});
+ for (const auto &resp : resps) {
+ std::string buf;
+ buf.reserve(128);
+ folly::dynamic jsonObj = resp.toJson();
+ auto jsonString = folly::toJson(jsonObj);
+ serializer::serialize(jsonString, &buf);
+ std::string copy;
+ std::size_t s = serializer::deserialize(buf, copy);
+ ASSERT_EQ(s, buf.size());
+ EXPECT_EQ(jsonString, copy);
+ }
+ }
+}
} // namespace nebula
diff --git a/src/common/ssl/CMakeLists.txt b/src/common/ssl/CMakeLists.txt
new file mode 100644
index 00000000000..eb19ddc3442
--- /dev/null
+++ b/src/common/ssl/CMakeLists.txt
@@ -0,0 +1,10 @@
+# Copyright (c) 2021 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.
+
+nebula_add_library(
+ ssl_obj
+ OBJECT
+ SSLConfig.cpp
+)
diff --git a/src/common/ssl/SSLConfig.cpp b/src/common/ssl/SSLConfig.cpp
new file mode 100644
index 00000000000..5f39f26c5ac
--- /dev/null
+++ b/src/common/ssl/SSLConfig.cpp
@@ -0,0 +1,38 @@
+/* Copyright (c) 2021 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 "common/ssl/SSLConfig.h"
+
+DEFINE_string(cert_path, "", "Path to cert pem.");
+DEFINE_string(key_path, "", "Path to cert key.");
+DEFINE_string(ca_path, "", "Path to trusted CA file.");
+DEFINE_bool(enable_ssl, false, "Whether to enable ssl.");
+DEFINE_bool(enable_graph_ssl, false, "Whether to enable ssl of graph server.");
+DEFINE_bool(enable_meta_ssl, false, "Whether to enable ssl of meta server.");
+
+namespace nebula {
+
+std::shared_ptr sslContextConfig() {
+ auto sslCfg = std::make_shared();
+ sslCfg->addCertificate(FLAGS_cert_path, FLAGS_key_path, "");
+ sslCfg->isDefault = true;
+ return sslCfg;
+}
+
+std::shared_ptr createSSLContext() {
+ auto context = std::make_shared();
+ if (!FLAGS_ca_path.empty()) {
+ context->loadTrustedCertificates(FLAGS_ca_path.c_str());
+ // don't do peer name validation
+ context->authenticate(true, false);
+ // verify the server cert
+ context->setVerificationOption(folly::SSLContext::SSLVerifyPeerEnum::VERIFY);
+ }
+ folly::ssl::setSignatureAlgorithms(*context);
+ return context;
+}
+
+} // namespace nebula
diff --git a/src/common/ssl/SSLConfig.h b/src/common/ssl/SSLConfig.h
new file mode 100644
index 00000000000..45889b86b27
--- /dev/null
+++ b/src/common/ssl/SSLConfig.h
@@ -0,0 +1,26 @@
+
+/* Copyright (c) 2021 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.
+ */
+
+#pragma once
+
+#include
+#include
+#include
+
+#include
+
+DECLARE_bool(enable_ssl);
+DECLARE_bool(enable_graph_ssl);
+DECLARE_bool(enable_meta_ssl);
+
+namespace nebula {
+
+extern std::shared_ptr sslContextConfig();
+
+extern std::shared_ptr createSSLContext();
+
+} // namespace nebula
diff --git a/src/common/thrift/ThriftClientManager-inl.h b/src/common/thrift/ThriftClientManager-inl.h
index fe907bcc7bb..caf0a27f252 100644
--- a/src/common/thrift/ThriftClientManager-inl.h
+++ b/src/common/thrift/ThriftClientManager-inl.h
@@ -6,11 +6,13 @@
#pragma once
+#include
#include
#include
#include
#include "common/network/NetworkUtils.h"
+#include "common/ssl/SSLConfig.h"
DECLARE_int32(conn_timeout_ms);
@@ -71,9 +73,14 @@ std::shared_ptr ThriftClientManager::client(const HostAd
VLOG(2) << "Connecting to " << host << " for " << ++connectionCount << " times";
std::shared_ptr socket;
- evb->runImmediatelyOrRunInEventBaseThreadAndWait([&socket, evb, resolved]() {
- socket =
- folly::AsyncSocket::newSocket(evb, resolved.host, resolved.port, FLAGS_conn_timeout_ms);
+ evb->runImmediatelyOrRunInEventBaseThreadAndWait([this, &socket, evb, resolved]() {
+ if (enableSSL_) {
+ socket = folly::AsyncSSLSocket::newSocket(nebula::createSSLContext(), evb);
+ socket->connect(nullptr, resolved.host, resolved.port, FLAGS_conn_timeout_ms);
+ } else {
+ socket =
+ folly::AsyncSocket::newSocket(evb, resolved.host, resolved.port, FLAGS_conn_timeout_ms);
+ }
});
auto headerClientChannel = apache::thrift::HeaderClientChannel::newChannel(socket);
if (timeout > 0) {
diff --git a/src/common/thrift/ThriftClientManager.h b/src/common/thrift/ThriftClientManager.h
index da5c16dcaf4..fa23b3678f3 100644
--- a/src/common/thrift/ThriftClientManager.h
+++ b/src/common/thrift/ThriftClientManager.h
@@ -7,6 +7,7 @@
#ifndef COMMON_THRIFT_THRIFTCLIENTMANAGER_H_
#define COMMON_THRIFT_THRIFTCLIENTMANAGER_H_
+#include
#include
#include "common/base/Base.h"
@@ -25,7 +26,9 @@ class ThriftClientManager final {
~ThriftClientManager() { VLOG(3) << "~ThriftClientManager"; }
- ThriftClientManager() { VLOG(3) << "ThriftClientManager"; }
+ explicit ThriftClientManager(bool enableSSL = false) : enableSSL_(enableSSL) {
+ VLOG(3) << "ThriftClientManager";
+ }
private:
using ClientMap = std::unordered_map, //
@@ -34,6 +37,8 @@ class ThriftClientManager final {
>;
folly::ThreadLocal clientMap_;
+ // whether enable ssl
+ bool enableSSL_{false};
};
} // namespace thrift
diff --git a/src/common/time/TimeUtils.cpp b/src/common/time/TimeUtils.cpp
index 694ab6600a5..01045531ee1 100644
--- a/src/common/time/TimeUtils.cpp
+++ b/src/common/time/TimeUtils.cpp
@@ -55,11 +55,16 @@ constexpr int64_t kMaxTimestamp = std::numeric_limits::max() / 10000000
return Status::Error("Invalid second number `%ld'.", kv.second.getInt());
}
dt.sec = kv.second.getInt();
+ } else if (kv.first == "millisecond") {
+ if (kv.second.getInt() < 0 || kv.second.getInt() > 999) {
+ return Status::Error("Invalid millisecond number `%ld'.", kv.second.getInt());
+ }
+ dt.microsec += kv.second.getInt() * 1000;
} else if (kv.first == "microsecond") {
- if (kv.second.getInt() < 0 || kv.second.getInt() > 999999) {
+ if (kv.second.getInt() < 0 || kv.second.getInt() > 999) {
return Status::Error("Invalid microsecond number `%ld'.", kv.second.getInt());
}
- dt.microsec = kv.second.getInt();
+ dt.microsec += kv.second.getInt();
} else {
return Status::Error("Invlaid parameter `%s'.", kv.first.c_str());
}
@@ -125,11 +130,16 @@ constexpr int64_t kMaxTimestamp = std::numeric_limits::max() / 10000000
return Status::Error("Invalid second number `%ld'.", kv.second.getInt());
}
t.sec = kv.second.getInt();
+ } else if (kv.first == "millisecond") {
+ if (kv.second.getInt() < 0 || kv.second.getInt() > 999) {
+ return Status::Error("Invalid millisecond number `%ld'.", kv.second.getInt());
+ }
+ t.microsec += kv.second.getInt() * 1000;
} else if (kv.first == "microsecond") {
- if (kv.second.getInt() < 0 || kv.second.getInt() > 999999) {
+ if (kv.second.getInt() < 0 || kv.second.getInt() > 999) {
return Status::Error("Invalid microsecond number `%ld'.", kv.second.getInt());
}
- t.microsec = kv.second.getInt();
+ t.microsec += kv.second.getInt();
} else {
return Status::Error("Invlaid parameter `%s'.", kv.first.c_str());
}
diff --git a/src/common/utils/NebulaKeyUtils.cpp b/src/common/utils/NebulaKeyUtils.cpp
index 5b3ad287f3e..2bac2e2b7ee 100644
--- a/src/common/utils/NebulaKeyUtils.cpp
+++ b/src/common/utils/NebulaKeyUtils.cpp
@@ -240,4 +240,13 @@ std::string NebulaKeyUtils::toEdgeKey(const folly::StringPiece& lockKey) {
return ret;
}
+std::string NebulaKeyUtils::adminTaskKey(int32_t seqId, JobID jobId, TaskID taskId) {
+ std::string key;
+ key.reserve(sizeof(int32_t) + sizeof(JobID) + sizeof(TaskID));
+ key.append(reinterpret_cast(&seqId), sizeof(int32_t));
+ key.append(reinterpret_cast(&jobId), sizeof(JobID));
+ key.append(reinterpret_cast(&taskId), sizeof(TaskID));
+ return key;
+}
+
} // namespace nebula
diff --git a/src/common/utils/NebulaKeyUtils.h b/src/common/utils/NebulaKeyUtils.h
index 6ff51baa9da..adbdea7c32e 100644
--- a/src/common/utils/NebulaKeyUtils.h
+++ b/src/common/utils/NebulaKeyUtils.h
@@ -269,6 +269,8 @@ class NebulaKeyUtils final {
LOG(FATAL) << msg.str();
}
+ static std::string adminTaskKey(int32_t seqId, JobID jobId, TaskID taskId);
+
static_assert(sizeof(NebulaKeyType) == sizeof(PartitionID));
private:
diff --git a/src/daemons/CMakeLists.txt b/src/daemons/CMakeLists.txt
index ce7ec27cb95..f69da66c10f 100644
--- a/src/daemons/CMakeLists.txt
+++ b/src/daemons/CMakeLists.txt
@@ -29,6 +29,7 @@ set(common_deps
$
$
$
+ $
)
set(storage_meta_deps
diff --git a/src/daemons/GraphDaemon.cpp b/src/daemons/GraphDaemon.cpp
index dccf4a0cae9..4bfee7617e2 100644
--- a/src/daemons/GraphDaemon.cpp
+++ b/src/daemons/GraphDaemon.cpp
@@ -5,6 +5,7 @@
*/
#include
+#include
#include
#include
#include
@@ -15,6 +16,7 @@
#include "common/fs/FileUtils.h"
#include "common/network/NetworkUtils.h"
#include "common/process/ProcessUtils.h"
+#include "common/ssl/SSLConfig.h"
#include "common/time/TimezoneInfo.h"
#include "graph/service/GraphFlags.h"
#include "graph/service/GraphService.h"
@@ -52,6 +54,9 @@ int main(int argc, char *argv[]) {
}
folly::init(&argc, &argv, true);
+ if (FLAGS_enable_ssl || FLAGS_enable_graph_ssl || FLAGS_enable_meta_ssl) {
+ folly::ssl::init();
+ }
nebula::initCounters();
if (FLAGS_flagfile.empty()) {
@@ -149,6 +154,9 @@ int main(int argc, char *argv[]) {
gServer->setIdleTimeout(std::chrono::seconds(FLAGS_client_idle_timeout_secs));
gServer->setNumAcceptThreads(FLAGS_num_accept_threads);
gServer->setListenBacklog(FLAGS_listen_backlog);
+ if (FLAGS_enable_ssl || FLAGS_enable_graph_ssl) {
+ gServer->setSSLConfig(nebula::sslContextConfig());
+ }
setupThreadManager();
// Setup the signal handlers
diff --git a/src/daemons/MetaDaemon.cpp b/src/daemons/MetaDaemon.cpp
index e2c176a8083..9c534951f43 100644
--- a/src/daemons/MetaDaemon.cpp
+++ b/src/daemons/MetaDaemon.cpp
@@ -4,6 +4,7 @@
* attached with Common Clause Condition 1.0, found in the LICENSES directory.
*/
+#include
#include
#include "common/base/Base.h"
@@ -12,6 +13,7 @@
#include "common/hdfs/HdfsHelper.h"
#include "common/network/NetworkUtils.h"
#include "common/process/ProcessUtils.h"
+#include "common/ssl/SSLConfig.h"
#include "common/thread/GenericThreadPool.h"
#include "common/time/TimezoneInfo.h"
#include "kvstore/NebulaStore.h"
@@ -204,6 +206,9 @@ int main(int argc, char* argv[]) {
}
folly::init(&argc, &argv, true);
+ if (FLAGS_enable_ssl || FLAGS_enable_meta_ssl) {
+ folly::ssl::init();
+ }
if (FLAGS_data_path.empty()) {
LOG(ERROR) << "Meta Data Path should not empty";
return EXIT_FAILURE;
@@ -307,6 +312,9 @@ int main(int argc, char* argv[]) {
gServer->setPort(FLAGS_port);
gServer->setIdleTimeout(std::chrono::seconds(0)); // No idle timeout on client connection
gServer->setInterface(std::move(handler));
+ if (FLAGS_enable_ssl || FLAGS_enable_meta_ssl) {
+ gServer->setSSLConfig(nebula::sslContextConfig());
+ }
gServer->serve(); // Will wait until the server shuts down
} catch (const std::exception& e) {
LOG(ERROR) << "Exception thrown: " << e.what();
diff --git a/src/daemons/StorageDaemon.cpp b/src/daemons/StorageDaemon.cpp
index 70b2930cb84..b8f7f933be3 100644
--- a/src/daemons/StorageDaemon.cpp
+++ b/src/daemons/StorageDaemon.cpp
@@ -4,6 +4,7 @@
* attached with Common Clause Condition 1.0, found in the LICENSES directory.
*/
+#include
#include
#include "common/base/Base.h"
@@ -69,6 +70,9 @@ int main(int argc, char *argv[]) {
}
folly::init(&argc, &argv, true);
+ if (FLAGS_enable_ssl || FLAGS_enable_meta_ssl) {
+ folly::ssl::init();
+ }
if (FLAGS_daemonize) {
google::SetStderrLogging(google::FATAL);
} else {
diff --git a/src/graph/context/ast/QueryAstContext.h b/src/graph/context/ast/QueryAstContext.h
index 6cc3e4e975e..9e994d745a7 100644
--- a/src/graph/context/ast/QueryAstContext.h
+++ b/src/graph/context/ast/QueryAstContext.h
@@ -128,6 +128,16 @@ struct SubgraphContext final : public AstContext {
bool getEdgeProp{false};
};
+struct FetchVerticesContext final : public AstContext {
+ Starts from;
+ bool distinct{false};
+ YieldColumns* yieldExpr{nullptr};
+ ExpressionProps exprProps;
+
+ // store the result of the previous sentence
+ std::string inputVarName;
+};
+
} // namespace graph
} // namespace nebula
#endif // GRAPH_CONTEXT_AST_QUERYASTCONTEXT_H_
diff --git a/src/graph/context/test/CMakeLists.txt b/src/graph/context/test/CMakeLists.txt
index fc579f8a74a..02d05be1d42 100644
--- a/src/graph/context/test/CMakeLists.txt
+++ b/src/graph/context/test/CMakeLists.txt
@@ -40,6 +40,7 @@ SET(CONTEXT_TEST_LIBS
$
$
$
+ $
)
nebula_add_test(
diff --git a/src/graph/executor/Executor.cpp b/src/graph/executor/Executor.cpp
index 647a29b96a2..de79bda805b 100644
--- a/src/graph/executor/Executor.cpp
+++ b/src/graph/executor/Executor.cpp
@@ -226,6 +226,9 @@ Executor *Executor::makeExecutor(QueryContext *qctx, const PlanNode *node) {
case PlanNode::Kind::kCreateSpace: {
return pool->add(new CreateSpaceExecutor(node, qctx));
}
+ case PlanNode::Kind::kCreateSpaceAs: {
+ return pool->add(new CreateSpaceAsExecutor(node, qctx));
+ }
case PlanNode::Kind::kDescSpace: {
return pool->add(new DescSpaceExecutor(node, qctx));
}
diff --git a/src/graph/executor/admin/ListRolesExecutor.cpp b/src/graph/executor/admin/ListRolesExecutor.cpp
index f72fddadb95..bd5b4ee808f 100644
--- a/src/graph/executor/admin/ListRolesExecutor.cpp
+++ b/src/graph/executor/admin/ListRolesExecutor.cpp
@@ -38,10 +38,17 @@ folly::Future ListRolesExecutor::listRoles() {
auto foundItem = std::find_if(items.begin(), items.end(), [&account](const auto &item) {
return item.get_user_id() == account;
});
- if (foundItem != items.end() && foundItem->get_role_type() != meta::cpp2::RoleType::ADMIN) {
- v.emplace_back(Row({foundItem->get_user_id(),
- apache::thrift::util::enumNameSafe(foundItem->get_role_type())}));
- } else {
+ if (foundItem != items.end()) {
+ if (foundItem->get_role_type() != meta::cpp2::RoleType::ADMIN) {
+ v.emplace_back(Row({foundItem->get_user_id(),
+ apache::thrift::util::enumNameSafe(foundItem->get_role_type())}));
+ } else {
+ for (const auto &item : items) {
+ v.emplace_back(nebula::Row(
+ {item.get_user_id(), apache::thrift::util::enumNameSafe(item.get_role_type())}));
+ }
+ }
+ } else if (qctx_->rctx()->session()->isGod()) {
for (const auto &item : items) {
v.emplace_back(nebula::Row(
{item.get_user_id(), apache::thrift::util::enumNameSafe(item.get_role_type())}));
diff --git a/src/graph/executor/admin/SpaceExecutor.cpp b/src/graph/executor/admin/SpaceExecutor.cpp
index 52f89905a43..59d03ed1699 100644
--- a/src/graph/executor/admin/SpaceExecutor.cpp
+++ b/src/graph/executor/admin/SpaceExecutor.cpp
@@ -32,6 +32,25 @@ folly::Future CreateSpaceExecutor::execute() {
});
}
+folly::Future CreateSpaceAsExecutor::execute() {
+ SCOPED_TIMER(&execTime_);
+
+ auto *csaNode = asNode(node());
+ auto oldSpace = csaNode->getOldSpaceName();
+ auto newSpace = csaNode->getNewSpaceName();
+ return qctx()
+ ->getMetaClient()
+ ->createSpaceAs(oldSpace, newSpace)
+ .via(runner())
+ .thenValue([](StatusOr resp) {
+ if (!resp.ok()) {
+ LOG(ERROR) << resp.status();
+ return resp.status();
+ }
+ return Status::OK();
+ });
+}
+
folly::Future DescSpaceExecutor::execute() {
SCOPED_TIMER(&execTime_);
diff --git a/src/graph/executor/admin/SpaceExecutor.h b/src/graph/executor/admin/SpaceExecutor.h
index accd84e8b5e..d3a339bb18a 100644
--- a/src/graph/executor/admin/SpaceExecutor.h
+++ b/src/graph/executor/admin/SpaceExecutor.h
@@ -20,6 +20,14 @@ class CreateSpaceExecutor final : public Executor {
folly::Future execute() override;
};
+class CreateSpaceAsExecutor final : public Executor {
+ public:
+ CreateSpaceAsExecutor(const PlanNode *node, QueryContext *qctx)
+ : Executor("CreateSpaceAsExecutor", node, qctx) {}
+
+ folly::Future execute() override;
+};
+
class DescSpaceExecutor final : public Executor {
public:
DescSpaceExecutor(const PlanNode *node, QueryContext *qctx)
diff --git a/src/graph/executor/test/CMakeLists.txt b/src/graph/executor/test/CMakeLists.txt
index c0a8925a452..f78ff4ed0c0 100644
--- a/src/graph/executor/test/CMakeLists.txt
+++ b/src/graph/executor/test/CMakeLists.txt
@@ -47,6 +47,7 @@ SET(EXEC_QUERY_TEST_OBJS
$
$
$
+ $
)
SET(EXEC_QUERY_TEST_LIBS
diff --git a/src/graph/optimizer/test/CMakeLists.txt b/src/graph/optimizer/test/CMakeLists.txt
index 7a5ed87dcb6..a1fa426ac72 100644
--- a/src/graph/optimizer/test/CMakeLists.txt
+++ b/src/graph/optimizer/test/CMakeLists.txt
@@ -43,6 +43,7 @@ set(OPTIMIZER_TEST_LIB
$
$
$
+ $
)
nebula_add_test(
diff --git a/src/graph/planner/CMakeLists.txt b/src/graph/planner/CMakeLists.txt
index 2262a2e2049..9f5791f2f3f 100644
--- a/src/graph/planner/CMakeLists.txt
+++ b/src/graph/planner/CMakeLists.txt
@@ -40,4 +40,5 @@ nebula_add_library(
ngql/GoPlanner.cpp
ngql/SubgraphPlanner.cpp
ngql/LookupPlanner.cpp
+ ngql/FetchVerticesPlanner.cpp
)
diff --git a/src/graph/planner/PlannersRegister.cpp b/src/graph/planner/PlannersRegister.cpp
index c67f5ea9d8c..0166d711c8f 100644
--- a/src/graph/planner/PlannersRegister.cpp
+++ b/src/graph/planner/PlannersRegister.cpp
@@ -13,6 +13,7 @@
#include "graph/planner/match/PropIndexSeek.h"
#include "graph/planner/match/StartVidFinder.h"
#include "graph/planner/match/VertexIdSeek.h"
+#include "graph/planner/ngql/FetchVerticesPlanner.h"
#include "graph/planner/ngql/GoPlanner.h"
#include "graph/planner/ngql/LookupPlanner.h"
#include "graph/planner/ngql/PathPlanner.h"
@@ -47,6 +48,10 @@ void PlannersRegister::registSequential() {
auto& planners = Planner::plannersMap()[Sentence::Kind::kGetSubgraph];
planners.emplace_back(&SubgraphPlanner::match, &SubgraphPlanner::make);
}
+ {
+ auto& planners = Planner::plannersMap()[Sentence::Kind::kFetchVertices];
+ planners.emplace_back(&FetchVerticesPlanner::match, &FetchVerticesPlanner::make);
+ }
}
void PlannersRegister::registMatch() {
diff --git a/src/graph/planner/ngql/FetchVerticesPlanner.cpp b/src/graph/planner/ngql/FetchVerticesPlanner.cpp
new file mode 100644
index 00000000000..2782ab622e6
--- /dev/null
+++ b/src/graph/planner/ngql/FetchVerticesPlanner.cpp
@@ -0,0 +1,69 @@
+/* Copyright (c) 2021 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 "graph/planner/ngql/FetchVerticesPlanner.h"
+
+#include "graph/planner/plan/Query.h"
+#include "graph/util/PlannerUtil.h"
+
+namespace nebula {
+namespace graph {
+
+std::unique_ptr FetchVerticesPlanner::buildVertexProps(
+ const ExpressionProps::TagIDPropsMap& propsMap) {
+ if (propsMap.empty()) {
+ return nullptr;
+ }
+ auto vertexProps = std::make_unique(propsMap.size());
+ auto fun = [](auto& tag) {
+ VertexProp vp;
+ vp.set_tag(tag.first);
+ std::vector props(tag.second.begin(), tag.second.end());
+ vp.set_props(std::move(props));
+ return vp;
+ };
+ std::transform(propsMap.begin(), propsMap.end(), vertexProps->begin(), fun);
+ return vertexProps;
+}
+
+StatusOr FetchVerticesPlanner::transform(AstContext* astCtx) {
+ fetchCtx_ = static_cast(astCtx);
+ auto qctx = fetchCtx_->qctx;
+ auto space = fetchCtx_->space;
+ auto& starts = fetchCtx_->from;
+
+ std::string vidsVar;
+ if (!starts.vids.empty() && starts.originalSrc == nullptr) {
+ PlannerUtil::buildConstantInput(qctx, starts, vidsVar);
+ } else {
+ starts.src = starts.originalSrc;
+ if (starts.fromType == kVariable) {
+ vidsVar = starts.userDefinedVarName;
+ } else {
+ vidsVar = fetchCtx_->inputVarName;
+ }
+ }
+
+ SubPlan subPlan;
+ auto* getVertices = GetVertices::make(qctx,
+ nullptr,
+ space.id,
+ starts.src,
+ buildVertexProps(fetchCtx_->exprProps.tagProps()),
+ {},
+ fetchCtx_->distinct);
+ getVertices->setInputVar(vidsVar);
+
+ subPlan.root = Project::make(qctx, getVertices, fetchCtx_->yieldExpr);
+ if (fetchCtx_->distinct) {
+ subPlan.root = Dedup::make(qctx, subPlan.root);
+ }
+ subPlan.tail = getVertices;
+ return subPlan;
+}
+
+} // namespace graph
+} // namespace nebula
diff --git a/src/graph/planner/ngql/FetchVerticesPlanner.h b/src/graph/planner/ngql/FetchVerticesPlanner.h
new file mode 100644
index 00000000000..7bbd9e34b91
--- /dev/null
+++ b/src/graph/planner/ngql/FetchVerticesPlanner.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2021 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 GRAPH_PLANNER_NGQL_FETCH_VERTICES_PLANNER_H_
+#define GRAPH_PLANNER_NGQL_FETCH_VERTICES_PLANNER_H_
+
+#include "common/base/Base.h"
+#include "graph/context/ast/QueryAstContext.h"
+#include "graph/planner/Planner.h"
+#include "graph/planner/plan/PlanNode.h"
+
+namespace nebula {
+namespace graph {
+class FetchVerticesPlanner final : public Planner {
+ public:
+ using VertexProp = nebula::storage::cpp2::VertexProp;
+ using VertexProps = std::vector;
+
+ static std::unique_ptr make() {
+ return std::unique_ptr(new FetchVerticesPlanner());
+ }
+
+ static bool match(AstContext* astCtx) {
+ return astCtx->sentence->kind() == Sentence::Kind::kFetchVertices;
+ }
+
+ StatusOr transform(AstContext* astCtx) override;
+
+ private:
+ std::unique_ptr buildVertexProps(const ExpressionProps::TagIDPropsMap& propsMap);
+
+ private:
+ FetchVerticesPlanner() = default;
+
+ FetchVerticesContext* fetchCtx_{nullptr};
+};
+} // namespace graph
+} // namespace nebula
+
+#endif // GRAPH_PLANNER_NGQL_FETCH_VERTICES_PLANNER_H
diff --git a/src/graph/planner/ngql/GoPlanner.cpp b/src/graph/planner/ngql/GoPlanner.cpp
index 926f8f10f8d..c12278417a1 100644
--- a/src/graph/planner/ngql/GoPlanner.cpp
+++ b/src/graph/planner/ngql/GoPlanner.cpp
@@ -6,12 +6,9 @@
#include "graph/planner/ngql/GoPlanner.h"
-#include "graph/planner/plan/Algo.h"
#include "graph/planner/plan/Logic.h"
#include "graph/util/ExpressionUtils.h"
-#include "graph/util/QueryUtil.h"
-#include "graph/util/SchemaUtil.h"
-#include "graph/validator/Validator.h"
+#include "graph/util/PlannerUtil.h"
namespace nebula {
namespace graph {
@@ -395,7 +392,7 @@ SubPlan GoPlanner::nStepsPlan(SubPlan& startVidPlan) {
gn->setEdgeProps(buildEdgeProps(true));
gn->setInputVar(goCtx_->vidsVar);
- auto* getDst = QueryUtil::extractDstFromGN(qctx, gn, goCtx_->vidsVar);
+ auto* getDst = PlannerUtil::extractDstFromGN(qctx, gn, goCtx_->vidsVar);
PlanNode* loopBody = getDst;
PlanNode* loopDep = nullptr;
@@ -429,7 +426,7 @@ SubPlan GoPlanner::mToNStepsPlan(SubPlan& startVidPlan) {
gn->setEdgeProps(buildEdgeProps(false));
gn->setInputVar(goCtx_->vidsVar);
- auto* getDst = QueryUtil::extractDstFromGN(qctx, gn, goCtx_->vidsVar);
+ auto* getDst = PlannerUtil::extractDstFromGN(qctx, gn, goCtx_->vidsVar);
auto* loopBody = getDst;
auto* loopDep = startVidPlan.root;
@@ -487,7 +484,7 @@ StatusOr GoPlanner::transform(AstContext* astCtx) {
goCtx_->joinInput = goCtx_->from.fromType != FromType::kInstantExpr;
goCtx_->joinDst = !goCtx_->exprProps.dstTagProps().empty();
- SubPlan startPlan = QueryUtil::buildStart(qctx, goCtx_->from, goCtx_->vidsVar);
+ SubPlan startPlan = PlannerUtil::buildStart(qctx, goCtx_->from, goCtx_->vidsVar);
auto& steps = goCtx_->steps;
if (steps.isMToN()) {
diff --git a/src/graph/planner/ngql/GoPlanner.h b/src/graph/planner/ngql/GoPlanner.h
index 4a7343bd6fb..19f7e5dbf93 100644
--- a/src/graph/planner/ngql/GoPlanner.h
+++ b/src/graph/planner/ngql/GoPlanner.h
@@ -12,7 +12,6 @@
#include "graph/planner/Planner.h"
#include "graph/planner/plan/PlanNode.h"
#include "graph/planner/plan/Query.h"
-#include "graph/util/ExpressionUtils.h"
namespace nebula {
namespace graph {
diff --git a/src/graph/planner/ngql/PathPlanner.cpp b/src/graph/planner/ngql/PathPlanner.cpp
index 4509e20e4b8..3e2dab8bca2 100644
--- a/src/graph/planner/ngql/PathPlanner.cpp
+++ b/src/graph/planner/ngql/PathPlanner.cpp
@@ -8,7 +8,7 @@
#include "graph/planner/plan/Algo.h"
#include "graph/planner/plan/Logic.h"
#include "graph/util/ExpressionUtils.h"
-#include "graph/util/QueryUtil.h"
+#include "graph/util/PlannerUtil.h"
#include "graph/util/SchemaUtil.h"
#include "graph/validator/Validator.h"
@@ -62,15 +62,15 @@ void PathPlanner::doBuildEdgeProps(std::unique_ptr>& edgeP
void PathPlanner::buildStart(Starts& starts, std::string& vidsVar, bool reverse) {
auto qctx = pathCtx_->qctx;
if (!starts.vids.empty() && starts.originalSrc == nullptr) {
- QueryUtil::buildConstantInput(qctx, starts, vidsVar);
+ PlannerUtil::buildConstantInput(qctx, starts, vidsVar);
} else {
if (reverse) {
- auto subPlan = QueryUtil::buildRuntimeInput(qctx, starts);
+ auto subPlan = PlannerUtil::buildRuntimeInput(qctx, starts);
pathCtx_->runtimeToProject = subPlan.tail;
pathCtx_->runtimeToDedup = subPlan.root;
vidsVar = pathCtx_->runtimeToDedup->outputVar();
} else {
- auto subPlan = QueryUtil::buildRuntimeInput(qctx, starts);
+ auto subPlan = PlannerUtil::buildRuntimeInput(qctx, starts);
pathCtx_->runtimeFromProject = subPlan.tail;
pathCtx_->runtimeFromDedup = subPlan.root;
vidsVar = pathCtx_->runtimeFromDedup->outputVar();
diff --git a/src/graph/planner/ngql/SubgraphPlanner.cpp b/src/graph/planner/ngql/SubgraphPlanner.cpp
index 1621ff1a573..71220a2a2a3 100644
--- a/src/graph/planner/ngql/SubgraphPlanner.cpp
+++ b/src/graph/planner/ngql/SubgraphPlanner.cpp
@@ -8,7 +8,7 @@
#include "graph/planner/plan/Algo.h"
#include "graph/planner/plan/Logic.h"
#include "graph/util/ExpressionUtils.h"
-#include "graph/util/QueryUtil.h"
+#include "graph/util/PlannerUtil.h"
#include "graph/util/SchemaUtil.h"
#include "graph/validator/Validator.h"
@@ -122,7 +122,7 @@ StatusOr SubgraphPlanner::transform(AstContext* astCtx) {
auto qctx = subgraphCtx_->qctx;
std::string vidsVar;
- SubPlan startPlan = QueryUtil::buildStart(qctx, subgraphCtx_->from, vidsVar);
+ SubPlan startPlan = PlannerUtil::buildStart(qctx, subgraphCtx_->from, vidsVar);
if (subgraphCtx_->steps.steps() == 0) {
return zeroStep(startPlan, vidsVar);
}
diff --git a/src/graph/planner/plan/Admin.cpp b/src/graph/planner/plan/Admin.cpp
index fba72ace130..2ae4d0cac35 100644
--- a/src/graph/planner/plan/Admin.cpp
+++ b/src/graph/planner/plan/Admin.cpp
@@ -21,6 +21,13 @@ std::unique_ptr CreateSpace::explain() const {
return desc;
}
+std::unique_ptr CreateSpaceAsNode::explain() const {
+ auto desc = SingleDependencyNode::explain();
+ addDescription("oldSpaceName", oldSpaceName_, desc.get());
+ addDescription("newSpaceName", newSpaceName_, desc.get());
+ return desc;
+}
+
std::unique_ptr DropSpace::explain() const {
auto desc = SingleDependencyNode::explain();
addDescription("spaceName", spaceName_, desc.get());
diff --git a/src/graph/planner/plan/Admin.h b/src/graph/planner/plan/Admin.h
index d2925c93ba6..8d06377c010 100644
--- a/src/graph/planner/plan/Admin.h
+++ b/src/graph/planner/plan/Admin.h
@@ -107,6 +107,33 @@ class CreateSpace final : public SingleDependencyNode {
bool ifNotExists_{false};
};
+class CreateSpaceAsNode final : public SingleDependencyNode {
+ public:
+ static CreateSpaceAsNode* make(QueryContext* qctx,
+ PlanNode* input,
+ const std::string& oldSpaceName,
+ const std::string& newSpaceName) {
+ return qctx->objPool()->add(new CreateSpaceAsNode(qctx, input, oldSpaceName, newSpaceName));
+ }
+
+ std::unique_ptr explain() const override;
+
+ public:
+ std::string getOldSpaceName() const { return oldSpaceName_; }
+
+ std::string getNewSpaceName() const { return newSpaceName_; }
+
+ private:
+ CreateSpaceAsNode(QueryContext* qctx, PlanNode* input, std::string oldName, std::string newName)
+ : SingleDependencyNode(qctx, Kind::kCreateSpaceAs, input),
+ oldSpaceName_(std::move(oldName)),
+ newSpaceName_(std::move(newName)) {}
+
+ private:
+ std::string oldSpaceName_;
+ std::string newSpaceName_;
+};
+
class DropSpace final : public SingleDependencyNode {
public:
static DropSpace* make(QueryContext* qctx,
diff --git a/src/graph/planner/plan/PlanNode.cpp b/src/graph/planner/plan/PlanNode.cpp
index 336a9faf083..6da34fa3093 100644
--- a/src/graph/planner/plan/PlanNode.cpp
+++ b/src/graph/planner/plan/PlanNode.cpp
@@ -92,6 +92,8 @@ const char* PlanNode::toString(PlanNode::Kind kind) {
return "RegisterSpaceToSession";
case Kind::kCreateSpace:
return "CreateSpace";
+ case Kind::kCreateSpaceAs:
+ return "CreateSpaceAs";
case Kind::kCreateTag:
return "CreateTag";
case Kind::kCreateEdge:
diff --git a/src/graph/planner/plan/PlanNode.h b/src/graph/planner/plan/PlanNode.h
index fe458cb2497..c969947e406 100644
--- a/src/graph/planner/plan/PlanNode.h
+++ b/src/graph/planner/plan/PlanNode.h
@@ -69,6 +69,7 @@ class PlanNode {
// schema related
kCreateSpace,
+ kCreateSpaceAs,
kCreateTag,
kCreateEdge,
kDescSpace,
diff --git a/src/graph/service/GraphService.cpp b/src/graph/service/GraphService.cpp
index 6b217fb0c1f..5bed12c6187 100644
--- a/src/graph/service/GraphService.cpp
+++ b/src/graph/service/GraphService.cpp
@@ -153,6 +153,13 @@ folly::Future GraphService::future_execute(int64_t sessionId,
return future;
}
+folly::Future GraphService::future_executeJson(int64_t sessionId,
+ const std::string& query) {
+ auto rawResp = future_execute(sessionId, query).get();
+ auto respJsonObj = rawResp.toJson();
+ return folly::toJson(respJsonObj);
+}
+
bool GraphService::auth(const std::string& username, const std::string& password) {
if (!FLAGS_enable_authorize) {
return true;
diff --git a/src/graph/service/GraphService.h b/src/graph/service/GraphService.h
index 0e15ef0e873..f72c36bb39e 100644
--- a/src/graph/service/GraphService.h
+++ b/src/graph/service/GraphService.h
@@ -36,6 +36,9 @@ class GraphService final : public cpp2::GraphServiceSvIf {
folly::Future future_execute(int64_t sessionId,
const std::string& stmt) override;
+ folly::Future future_executeJson(int64_t sessionId,
+ const std::string& stmt) override;
+
private:
bool auth(const std::string& username, const std::string& password);
diff --git a/src/graph/service/PermissionCheck.cpp b/src/graph/service/PermissionCheck.cpp
index 0cadb22c436..8cdc16008cf 100644
--- a/src/graph/service/PermissionCheck.cpp
+++ b/src/graph/service/PermissionCheck.cpp
@@ -52,6 +52,7 @@ Status PermissionCheck::permissionCheck(ClientSession *session,
return Status::OK();
}
case Sentence::Kind::kCreateSpace:
+ case Sentence::Kind::kCreateSpaceAs:
case Sentence::Kind::kDropSpace:
case Sentence::Kind::kCreateSnapshot:
case Sentence::Kind::kDropSnapshot:
diff --git a/src/graph/session/ClientSession.cpp b/src/graph/session/ClientSession.cpp
index 39875b8887a..3c3fa758abe 100644
--- a/src/graph/session/ClientSession.cpp
+++ b/src/graph/session/ClientSession.cpp
@@ -55,7 +55,7 @@ void ClientSession::deleteQuery(QueryContext* qctx) {
session_.queries_ref()->erase(epId);
}
-bool ClientSession::findQuery(nebula::ExecutionPlanID epId) {
+bool ClientSession::findQuery(nebula::ExecutionPlanID epId) const {
folly::RWSpinLock::ReadHolder rHolder(rwSpinLock_);
auto context = contexts_.find(epId);
if (context != contexts_.end()) {
diff --git a/src/graph/session/ClientSession.h b/src/graph/session/ClientSession.h
index 31154bc7cf8..6432d511147 100644
--- a/src/graph/session/ClientSession.h
+++ b/src/graph/session/ClientSession.h
@@ -29,12 +29,12 @@ class ClientSession final {
static std::shared_ptr create(meta::cpp2::Session&& session,
meta::MetaClient* metaClient);
- int64_t id() {
+ int64_t id() const {
folly::RWSpinLock::ReadHolder rHolder(rwSpinLock_);
return session_.get_session_id();
}
- const SpaceInfo space() {
+ const SpaceInfo& space() const {
folly::RWSpinLock::ReadHolder rHolder(rwSpinLock_);
return space_;
}
@@ -47,22 +47,22 @@ class ClientSession final {
}
}
- const std::string spaceName() {
+ const std::string& spaceName() const {
folly::RWSpinLock::ReadHolder rHolder(rwSpinLock_);
return session_.get_space_name();
}
- const std::string user() {
+ const std::string& user() const {
folly::RWSpinLock::ReadHolder rHolder(rwSpinLock_);
return session_.get_user_name();
}
- std::unordered_map roles() {
+ const std::unordered_map& roles() const {
folly::RWSpinLock::ReadHolder rHolder(rwSpinLock_);
return roles_;
}
- StatusOr roleWithSpace(GraphSpaceID space) {
+ StatusOr roleWithSpace(GraphSpaceID space) const {
folly::RWSpinLock::ReadHolder rHolder(rwSpinLock_);
auto ret = roles_.find(space);
if (ret == roles_.end()) {
@@ -71,7 +71,7 @@ class ClientSession final {
return ret->second;
}
- bool isGod() {
+ bool isGod() const {
folly::RWSpinLock::ReadHolder rHolder(rwSpinLock_);
// Cloud may have multiple God accounts
for (auto& role : roles_) {
@@ -91,12 +91,12 @@ class ClientSession final {
void charge();
- int32_t getTimezone() {
+ int32_t getTimezone() const {
folly::RWSpinLock::ReadHolder rHolder(rwSpinLock_);
return session_.get_timezone();
}
- HostAddr getGraphAddr() {
+ const HostAddr& getGraphAddr() const {
folly::RWSpinLock::ReadHolder rHolder(rwSpinLock_);
return session_.get_graph_addr();
}
@@ -120,7 +120,7 @@ class ClientSession final {
}
}
- const meta::cpp2::Session getSession() {
+ const meta::cpp2::Session& getSession() const {
folly::RWSpinLock::ReadHolder rHolder(rwSpinLock_);
return session_;
}
@@ -134,7 +134,7 @@ class ClientSession final {
void deleteQuery(QueryContext* qctx);
- bool findQuery(nebula::ExecutionPlanID epId);
+ bool findQuery(nebula::ExecutionPlanID epId) const;
void markQueryKilled(nebula::ExecutionPlanID epId);
@@ -150,7 +150,7 @@ class ClientSession final {
time::Duration idleDuration_;
meta::cpp2::Session session_;
meta::MetaClient* metaClient_{nullptr};
- folly::RWSpinLock rwSpinLock_;
+ mutable folly::RWSpinLock rwSpinLock_;
/*
* map
* One user can have roles in multiple spaces
diff --git a/src/graph/util/CMakeLists.txt b/src/graph/util/CMakeLists.txt
index a9feb25f5fa..da7bdf95a2e 100644
--- a/src/graph/util/CMakeLists.txt
+++ b/src/graph/util/CMakeLists.txt
@@ -13,7 +13,8 @@ nebula_add_library(
ZoneUtil.cpp
ToJson.cpp
ParserUtil.cpp
- QueryUtil.cpp
+ PlannerUtil.cpp
+ ValidateUtil.cpp
)
nebula_add_library(
diff --git a/src/graph/util/QueryUtil.cpp b/src/graph/util/PlannerUtil.cpp
similarity index 81%
rename from src/graph/util/QueryUtil.cpp
rename to src/graph/util/PlannerUtil.cpp
index 7c542aaa19a..f6613f55a06 100644
--- a/src/graph/util/QueryUtil.cpp
+++ b/src/graph/util/PlannerUtil.cpp
@@ -4,7 +4,7 @@
* attached with Common Clause Condition 1.0, found in the LICENSES directory.
*/
-#include "graph/util/QueryUtil.h"
+#include "graph/util/PlannerUtil.h"
#include "common/base/Base.h"
#include "common/expression/ColumnExpression.h"
@@ -17,7 +17,7 @@ namespace nebula {
namespace graph {
// static
-void QueryUtil::buildConstantInput(QueryContext* qctx, Starts& starts, std::string& vidsVar) {
+void PlannerUtil::buildConstantInput(QueryContext* qctx, Starts& starts, std::string& vidsVar) {
vidsVar = qctx->vctx()->anonVarGen()->getVar();
DataSet ds;
ds.colNames.emplace_back(kVid);
@@ -33,7 +33,7 @@ void QueryUtil::buildConstantInput(QueryContext* qctx, Starts& starts, std::stri
}
// static
-SubPlan QueryUtil::buildRuntimeInput(QueryContext* qctx, Starts& starts) {
+SubPlan PlannerUtil::buildRuntimeInput(QueryContext* qctx, Starts& starts) {
auto pool = qctx->objPool();
auto* columns = pool->add(new YieldColumns());
auto* column = new YieldColumn(starts.originalSrc->clone(), kVid);
@@ -54,7 +54,7 @@ SubPlan QueryUtil::buildRuntimeInput(QueryContext* qctx, Starts& starts) {
}
// static
-SubPlan QueryUtil::buildStart(QueryContext* qctx, Starts& starts, std::string& vidsVar) {
+SubPlan PlannerUtil::buildStart(QueryContext* qctx, Starts& starts, std::string& vidsVar) {
SubPlan subPlan;
if (!starts.vids.empty() && starts.originalSrc == nullptr) {
buildConstantInput(qctx, starts, vidsVar);
@@ -65,7 +65,9 @@ SubPlan QueryUtil::buildStart(QueryContext* qctx, Starts& starts, std::string& v
return subPlan;
}
-PlanNode* QueryUtil::extractDstFromGN(QueryContext* qctx, PlanNode* gn, const std::string& output) {
+PlanNode* PlannerUtil::extractDstFromGN(QueryContext* qctx,
+ PlanNode* gn,
+ const std::string& output) {
auto pool = qctx->objPool();
auto* columns = pool->add(new YieldColumns());
auto* column = new YieldColumn(EdgePropertyExpression::make(pool, "*", kDst), kVid);
@@ -77,5 +79,6 @@ PlanNode* QueryUtil::extractDstFromGN(QueryContext* qctx, PlanNode* gn, const st
dedup->setOutputVar(output);
return dedup;
}
+
} // namespace graph
} // namespace nebula
diff --git a/src/graph/util/QueryUtil.h b/src/graph/util/PlannerUtil.h
similarity index 82%
rename from src/graph/util/QueryUtil.h
rename to src/graph/util/PlannerUtil.h
index 3ff1ea0e951..7eb0accbec6 100644
--- a/src/graph/util/QueryUtil.h
+++ b/src/graph/util/PlannerUtil.h
@@ -4,8 +4,8 @@
* attached with Common Clause Condition 1.0, found in the LICENSES directory.
*/
-#ifndef GRAPH_UTIL_QUERYUTIL_H_
-#define GRAPH_UTIL_QUERYUTIL_H_
+#ifndef GRAPH_UTIL_PLANNER_UTIL_H_
+#define GRAPH_UTIL_PLANNER_UTIL_H_
#include "common/base/Base.h"
namespace nebula {
@@ -14,9 +14,9 @@ class QueryContext;
struct Starts;
struct SubPlan;
class PlanNode;
-class QueryUtil final {
+class PlannerUtil final {
public:
- QueryUtil() = delete;
+ PlannerUtil() = delete;
static void buildConstantInput(QueryContext* qctx, Starts& starts, std::string& vidsVar);
@@ -29,4 +29,4 @@ class QueryUtil final {
} // namespace graph
} // namespace nebula
-#endif // GRAPH_UTIL_ZONEUTIL_H_
+#endif // GRAPH_UTIL_PLANNER_UTIL_H_
diff --git a/src/graph/util/ValidateUtil.cpp b/src/graph/util/ValidateUtil.cpp
new file mode 100644
index 00000000000..953c7b75ab2
--- /dev/null
+++ b/src/graph/util/ValidateUtil.cpp
@@ -0,0 +1,93 @@
+/* Copyright (c) 2021 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 "graph/util/ValidateUtil.h"
+
+#include "common/base/Base.h"
+#include "common/expression/ColumnExpression.h"
+#include "graph/context/QueryContext.h"
+#include "graph/context/ast/QueryAstContext.h"
+#include "graph/planner/Planner.h"
+#include "graph/planner/plan/Query.h"
+#include "graph/util/ExpressionUtils.h"
+
+namespace nebula {
+namespace graph {
+
+Status ValidateUtil::validateStep(const StepClause* clause, StepClause& step) {
+ if (clause == nullptr) {
+ return Status::SemanticError("Step clause nullptr.");
+ }
+ step = *clause;
+ if (clause->isMToN()) {
+ if (step.mSteps() == 0) {
+ step.setMSteps(1);
+ }
+ if (step.nSteps() < step.mSteps()) {
+ return Status::SemanticError("`%s', upper bound steps should be greater than lower bound.",
+ step.toString().c_str());
+ }
+ }
+ return Status::OK();
+}
+
+Status ValidateUtil::validateOver(QueryContext* qctx, const OverClause* clause, Over& over) {
+ if (clause == nullptr) {
+ return Status::SemanticError("Over clause nullptr.");
+ }
+ auto space = qctx->vctx()->whichSpace();
+
+ over.direction = clause->direction();
+ auto* schemaMng = qctx->schemaMng();
+ if (clause->isOverAll()) {
+ auto edgeStatus = schemaMng->getAllEdge(space.id);
+ NG_RETURN_IF_ERROR(edgeStatus);
+ auto edges = std::move(edgeStatus).value();
+ if (edges.empty()) {
+ return Status::SemanticError("No edge type found in space `%s'", space.name.c_str());
+ }
+ for (auto edge : edges) {
+ auto edgeType = schemaMng->toEdgeType(space.id, edge);
+ if (!edgeType.ok()) {
+ return Status::SemanticError(
+ "`%s' not found in space [`%s'].", edge.c_str(), space.name.c_str());
+ }
+ over.edgeTypes.emplace_back(edgeType.value());
+ }
+ over.allEdges = std::move(edges);
+ over.isOverAll = true;
+ } else {
+ auto edges = clause->edges();
+ for (auto* edge : edges) {
+ auto edgeName = *edge->edge();
+ auto edgeType = schemaMng->toEdgeType(space.id, edgeName);
+ if (!edgeType.ok()) {
+ return Status::SemanticError(
+ "%s not found in space [%s].", edgeName.c_str(), space.name.c_str());
+ }
+ over.edgeTypes.emplace_back(edgeType.value());
+ }
+ }
+ return Status::OK();
+}
+
+Status ValidateUtil::invalidLabelIdentifiers(const Expression* expr) {
+ auto labelExprs = ExpressionUtils::collectAll(expr, {Expression::Kind::kLabel});
+ if (!labelExprs.empty()) {
+ std::stringstream ss;
+ ss << "Invalid label identifiers: ";
+ for (auto* label : labelExprs) {
+ ss << label->toString() << ",";
+ }
+ auto errMsg = ss.str();
+ errMsg.pop_back();
+ return Status::SemanticError(std::move(errMsg));
+ }
+ return Status::OK();
+}
+
+} // namespace graph
+} // namespace nebula
diff --git a/src/graph/util/ValidateUtil.h b/src/graph/util/ValidateUtil.h
new file mode 100644
index 00000000000..ea532e166ad
--- /dev/null
+++ b/src/graph/util/ValidateUtil.h
@@ -0,0 +1,33 @@
+/* Copyright (c) 2021 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 GRAPH_UTIL_VALIDATE_UTIL_H_
+#define GRAPH_UTIL_VALIDATE_UTIL_H_
+#include "common/base/Base.h"
+#include "common/base/StatusOr.h"
+#include "common/expression/Expression.h"
+#include "parser/Clauses.h"
+
+namespace nebula {
+namespace graph {
+class QueryContext;
+class PlanNode;
+struct Over;
+
+class ValidateUtil final {
+ public:
+ ValidateUtil() = delete;
+
+ static Status validateStep(const StepClause* clause, StepClause& step);
+
+ static Status validateOver(QueryContext* qctx, const OverClause* clause, Over& over);
+
+ static Status invalidLabelIdentifiers(const Expression* expr);
+};
+
+} // namespace graph
+} // namespace nebula
+#endif // GRAPH_UTIL_VALIDATE_UTIL_H_
diff --git a/src/graph/util/test/CMakeLists.txt b/src/graph/util/test/CMakeLists.txt
index f87291477e9..745ab224efe 100644
--- a/src/graph/util/test/CMakeLists.txt
+++ b/src/graph/util/test/CMakeLists.txt
@@ -32,6 +32,7 @@ nebula_add_test(
$
$
$
+ $
$
$
$
diff --git a/src/graph/validator/AdminJobValidator.cpp b/src/graph/validator/AdminJobValidator.cpp
index e174699b6d6..c2d9cfd7793 100644
--- a/src/graph/validator/AdminJobValidator.cpp
+++ b/src/graph/validator/AdminJobValidator.cpp
@@ -49,8 +49,9 @@ Status AdminJobValidator::validateImpl() {
}
}
}
+ } else {
+ sentence_->addPara(qctx()->rctx()->session()->space().name);
}
-
return Status::OK();
}
diff --git a/src/graph/validator/AdminJobValidator.h b/src/graph/validator/AdminJobValidator.h
index 37a650f6c1d..ea96710e10a 100644
--- a/src/graph/validator/AdminJobValidator.h
+++ b/src/graph/validator/AdminJobValidator.h
@@ -38,6 +38,7 @@ class AdminJobValidator final : public Validator {
case meta::cpp2::AdminCmd::COMPACT:
case meta::cpp2::AdminCmd::FLUSH:
return true;
+ // TODO: Also space related, but not available in CreateJobExcutor now.
case meta::cpp2::AdminCmd::DATA_BALANCE:
case meta::cpp2::AdminCmd::DOWNLOAD:
case meta::cpp2::AdminCmd::INGEST:
@@ -49,7 +50,7 @@ class AdminJobValidator final : public Validator {
case meta::cpp2::AdminJobOp::SHOW:
case meta::cpp2::AdminJobOp::STOP:
case meta::cpp2::AdminJobOp::RECOVER:
- return false;
+ return true;
}
return false;
}
diff --git a/src/graph/validator/AdminValidator.cpp b/src/graph/validator/AdminValidator.cpp
index f797e4e08ca..1f64b1f5171 100644
--- a/src/graph/validator/AdminValidator.cpp
+++ b/src/graph/validator/AdminValidator.cpp
@@ -160,6 +160,20 @@ Status CreateSpaceValidator::toPlan() {
return Status::OK();
}
+Status CreateSpaceAsValidator::validateImpl() {
+ auto sentence = static_cast(sentence_);
+ oldSpaceName_ = sentence->getOldSpaceName();
+ newSpaceName_ = sentence->getNewSpaceName();
+ return Status::OK();
+}
+
+Status CreateSpaceAsValidator::toPlan() {
+ auto *doNode = CreateSpaceAsNode::make(qctx_, nullptr, oldSpaceName_, newSpaceName_);
+ root_ = doNode;
+ tail_ = root_;
+ return Status::OK();
+}
+
Status DescSpaceValidator::validateImpl() { return Status::OK(); }
Status DescSpaceValidator::toPlan() {
diff --git a/src/graph/validator/AdminValidator.h b/src/graph/validator/AdminValidator.h
index bbd647436db..9eadf9c09fc 100644
--- a/src/graph/validator/AdminValidator.h
+++ b/src/graph/validator/AdminValidator.h
@@ -34,6 +34,22 @@ class CreateSpaceValidator final : public Validator {
bool ifNotExist_;
};
+class CreateSpaceAsValidator final : public Validator {
+ public:
+ CreateSpaceAsValidator(Sentence* sentence, QueryContext* context) : Validator(sentence, context) {
+ setNoSpaceRequired();
+ }
+
+ private:
+ Status validateImpl() override;
+
+ Status toPlan() override;
+
+ private:
+ std::string oldSpaceName_;
+ std::string newSpaceName_;
+};
+
class DescSpaceValidator final : public Validator {
public:
DescSpaceValidator(Sentence* sentence, QueryContext* context) : Validator(sentence, context) {
diff --git a/src/graph/validator/CMakeLists.txt b/src/graph/validator/CMakeLists.txt
index 9138284b442..0c84e7c290a 100644
--- a/src/graph/validator/CMakeLists.txt
+++ b/src/graph/validator/CMakeLists.txt
@@ -24,7 +24,6 @@ nebula_add_library(
LimitValidator.cpp
OrderByValidator.cpp
YieldValidator.cpp
- TraversalValidator.cpp
ExplainValidator.cpp
GroupByValidator.cpp
FindPathValidator.cpp
diff --git a/src/graph/validator/FetchEdgesValidator.cpp b/src/graph/validator/FetchEdgesValidator.cpp
index 60c5636389f..8158906b940 100644
--- a/src/graph/validator/FetchEdgesValidator.cpp
+++ b/src/graph/validator/FetchEdgesValidator.cpp
@@ -9,6 +9,7 @@
#include "graph/planner/plan/Query.h"
#include "graph/util/ExpressionUtils.h"
#include "graph/util/SchemaUtil.h"
+#include "graph/util/ValidateUtil.h"
namespace nebula {
namespace graph {
@@ -184,7 +185,7 @@ Status FetchEdgesValidator::preparePropertiesWithYield(const YieldClause *yield)
dedup_ = newYield_->isDistinct();
for (auto col : newYield_->columns()) {
col->setExpr(ExpressionUtils::rewriteLabelAttr2EdgeProp(col->expr()));
- NG_RETURN_IF_ERROR(invalidLabelIdentifiers(col->expr()));
+ NG_RETURN_IF_ERROR(ValidateUtil::invalidLabelIdentifiers(col->expr()));
const auto *invalidExpr = findInvalidYieldExpression(col->expr());
if (invalidExpr != nullptr) {
return Status::SemanticError("Invalid newYield_ expression `%s'.",
diff --git a/src/graph/validator/FetchVerticesValidator.cpp b/src/graph/validator/FetchVerticesValidator.cpp
index 10f870ea231..114c983a281 100644
--- a/src/graph/validator/FetchVerticesValidator.cpp
+++ b/src/graph/validator/FetchVerticesValidator.cpp
@@ -1,4 +1,4 @@
-/* Copyright (c) 2020 vesoft inc. All rights reserved.
+/* Copyright (c) 2021 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.
@@ -7,7 +7,7 @@
#include "graph/planner/plan/Query.h"
#include "graph/util/ExpressionUtils.h"
-#include "graph/util/SchemaUtil.h"
+#include "graph/util/ValidateUtil.h"
#include "graph/visitor/DeducePropsVisitor.h"
namespace nebula {
@@ -16,238 +16,136 @@ namespace graph {
static constexpr char VertexID[] = "VertexID";
Status FetchVerticesValidator::validateImpl() {
- props_ = std::make_unique>();
- exprs_ = std::make_unique>();
- NG_RETURN_IF_ERROR(check());
- NG_RETURN_IF_ERROR(prepareVertices());
- NG_RETURN_IF_ERROR(prepareProperties());
- return Status::OK();
-}
+ auto *fSentence = static_cast(sentence_);
+ fetchCtx_ = getContext();
+ fetchCtx_->inputVarName = inputVarName_;
-Status FetchVerticesValidator::toPlan() {
- // Start [-> some input] -> GetVertices [-> Project] [-> Dedup] [-> next
- // stage] -> End
- std::string vidsVar = (srcRef_ == nullptr ? buildConstantInput() : buildRuntimeInput());
- auto *getVerticesNode = GetVertices::make(qctx_,
- nullptr,
- space_.id,
- src_,
- std::move(props_),
- std::move(exprs_),
- dedup_,
- std::move(orderBy_),
- limit_,
- filter_);
- getVerticesNode->setInputVar(vidsVar);
- getVerticesNode->setColNames(gvColNames_);
- // pipe will set the input variable
- PlanNode *current = getVerticesNode;
-
- if (withYield_) {
- current = Project::make(qctx_, current, newYieldColumns_);
-
- // Project select properties then dedup
- if (dedup_) {
- current = Dedup::make(qctx_, current);
-
- // the framework will add data collect to collect the result
- // if the result is required
- }
- } else {
- auto *pool = qctx_->objPool();
- auto *columns = pool->add(new YieldColumns());
- columns->addColumn(new YieldColumn(VertexExpression::make(pool), "vertices_"));
- current = Project::make(qctx_, current, columns);
- }
- root_ = current;
- tail_ = getVerticesNode;
+ NG_RETURN_IF_ERROR(validateTag(fSentence->tags()));
+ NG_RETURN_IF_ERROR(validateStarts(fSentence->vertices(), fetchCtx_->from));
+ NG_RETURN_IF_ERROR(validateYield(fSentence->yieldClause()));
return Status::OK();
}
-Status FetchVerticesValidator::check() {
- auto *sentence = static_cast(sentence_);
-
- if (!sentence->isAllTagProps()) {
- onStar_ = false;
- auto tags = sentence->tags()->labels();
- for (const auto &tag : tags) {
- auto tagStatus = qctx_->schemaMng()->toTagID(space_.id, *tag);
- NG_RETURN_IF_ERROR(tagStatus);
- auto tagId = tagStatus.value();
+Expression *FetchVerticesValidator::rewriteIDVertex2Vid(const Expression *expr) {
+ auto *pool = qctx_->objPool();
+ auto matcher = [](const Expression *e) -> bool {
+ std::string lowerStr = e->toString();
+ folly::toLowerAscii(lowerStr);
+ return e->kind() == Expression::Kind::kFunctionCall && lowerStr == "id(vertex)";
+ };
+ auto rewriter = [pool](const Expression *e) -> Expression * {
+ UNUSED(e);
+ return InputPropertyExpression::make(pool, nebula::kVid);
+ };
+
+ return RewriteVisitor::transform(expr, std::move(matcher), std::move(rewriter));
+}
- tags_.emplace(*tag, tagId);
- auto schema = qctx_->schemaMng()->getTagSchema(space_.id, tagId);
- if (schema == nullptr) {
- LOG(ERROR) << "No schema found for " << *tag;
- return Status::SemanticError("No schema found for `%s'", tag->c_str());
- }
- tagsSchema_.emplace(tagId, schema);
- }
- } else {
- onStar_ = true;
- const auto allTagsResult = qctx_->schemaMng()->getAllLatestVerTagSchema(space_.id);
- NG_RETURN_IF_ERROR(allTagsResult);
- const auto allTags = std::move(allTagsResult).value();
- for (const auto &tag : allTags) {
+Status FetchVerticesValidator::validateTag(const NameLabelList *nameLabels) {
+ if (nameLabels == nullptr) {
+ // all tag
+ const auto &tagStatus = qctx_->schemaMng()->getAllLatestVerTagSchema(space_.id);
+ NG_RETURN_IF_ERROR(tagStatus);
+ for (const auto &tag : tagStatus.value()) {
tagsSchema_.emplace(tag.first, tag.second);
}
- for (const auto &tagSchema : tagsSchema_) {
- auto tagNameResult = qctx_->schemaMng()->toTagName(space_.id, tagSchema.first);
- NG_RETURN_IF_ERROR(tagNameResult);
- tags_.emplace(std::move(tagNameResult).value(), tagSchema.first);
+ } else {
+ auto labels = nameLabels->labels();
+ auto *schemaMng = qctx_->schemaMng();
+ for (const auto &label : labels) {
+ auto tagStatus = schemaMng->toTagID(space_.id, *label);
+ NG_RETURN_IF_ERROR(tagStatus);
+ auto tagID = tagStatus.value();
+ auto tagSchema = schemaMng->getTagSchema(space_.id, tagID);
+ if (tagSchema == nullptr) {
+ return Status::SemanticError("no schema found for `%s'", label->c_str());
+ }
+ tagsSchema_.emplace(tagID, tagSchema);
}
}
return Status::OK();
}
-Status FetchVerticesValidator::prepareVertices() {
- auto *sentence = static_cast(sentence_);
- // from ref, eval when execute
- if (sentence->vertices()->isRef()) {
- srcRef_ = sentence->vertices()->ref();
- auto result = checkRef(srcRef_, vidType_);
- NG_RETURN_IF_ERROR(result);
- inputVar_ = std::move(result).value();
- return Status::OK();
+Status FetchVerticesValidator::validateYield(YieldClause *yield) {
+ auto pool = qctx_->objPool();
+ bool noYield = false;
+ if (yield == nullptr) {
+ // TODO: compatible with previous version, this will be deprecated in version 3.0.
+ auto *yieldColumns = new YieldColumns();
+ auto *vertex = new YieldColumn(VertexExpression::make(pool), "vertices_");
+ yieldColumns->addColumn(vertex);
+ yield = pool->add(new YieldClause(yieldColumns));
+ noYield = true;
+ }
+ fetchCtx_->distinct = yield->isDistinct();
+ auto size = yield->columns().size();
+ outputs_.reserve(size + 1);
+
+ auto *newCols = pool->add(new YieldColumns());
+ if (!noYield) {
+ outputs_.emplace_back(VertexID, vidType_);
+ auto *vidCol = new YieldColumn(InputPropertyExpression::make(pool, nebula::kVid), VertexID);
+ newCols->addColumn(vidCol);
}
- // from constant, eval now
- // TODO(shylock) add eval() method for expression
- QueryExpressionContext dummy(nullptr);
- auto vids = sentence->vertices()->vidList();
- srcVids_.rows.reserve(vids.size());
- for (const auto vid : vids) {
- DCHECK(ExpressionUtils::isConstExpr(vid));
- auto v = vid->eval(dummy);
- if (v.type() != vidType_) {
- std::stringstream ss;
- ss << "`" << vid->toString() << "', the vid should be type of " << vidType_ << ", but was`"
- << v.type() << "'";
- return Status::SemanticError(ss.str());
+ auto &exprProps = fetchCtx_->exprProps;
+ for (const auto &col : yield->columns()) {
+ if (col->expr()->kind() == Expression::Kind::kVertex) {
+ extractVertexProp(exprProps);
+ break;
}
- srcVids_.emplace_back(nebula::Row({std::move(v)}));
}
- return Status::OK();
-}
-// TODO(shylock) select _vid property instead of return always.
-Status FetchVerticesValidator::prepareProperties() {
- auto *sentence = static_cast(sentence_);
- auto *yield = sentence->yieldClause();
- if (yield == nullptr) {
- return preparePropertiesWithoutYield();
- } else {
- return preparePropertiesWithYield(yield);
- }
-}
-
-Status FetchVerticesValidator::preparePropertiesWithYield(const YieldClause *yield) {
- withYield_ = true;
- // outputs
- auto yieldSize = yield->columns().size();
- outputs_.reserve(yieldSize + 1);
- gvColNames_.emplace_back(nebula::kVid);
- outputs_.emplace_back(VertexID, vidType_); // kVid
-
- dedup_ = yield->isDistinct();
- ExpressionProps exprProps;
- DeducePropsVisitor deducePropsVisitor(qctx_, space_.id, &exprProps, &userDefinedVarNameList_);
-
- auto *pool = qctx_->objPool();
for (auto col : yield->columns()) {
- col->setExpr(ExpressionUtils::rewriteLabelAttr2TagProp(col->expr()));
- NG_RETURN_IF_ERROR(invalidLabelIdentifiers(col->expr()));
- col->expr()->accept(&deducePropsVisitor);
- if (!deducePropsVisitor.ok()) {
- return std::move(deducePropsVisitor).status();
- }
- if (exprProps.hasInputVarProperty()) {
- return Status::SemanticError("Unsupported input/variable property expression in yield.");
+ if (ExpressionUtils::hasAny(col->expr(),
+ {Expression::Kind::kEdge, Expression::Kind::kPathBuild})) {
+ return Status::SemanticError("illegal yield clauses `%s'", col->toString().c_str());
}
- if (!exprProps.edgeProps().empty()) {
- return Status::SemanticError("Unsupported edge property expression in yield.");
- }
- if (exprProps.hasSrcDstTagProperty()) {
- return Status::SemanticError("Unsupported src/dst property expression in yield.");
+ col->setExpr(ExpressionUtils::rewriteLabelAttr2TagProp(col->expr()));
+ NG_RETURN_IF_ERROR(ValidateUtil::invalidLabelIdentifiers(col->expr()));
+ auto colExpr = col->expr();
+ auto typeStatus = deduceExprType(colExpr);
+ NG_RETURN_IF_ERROR(typeStatus);
+ outputs_.emplace_back(col->name(), typeStatus.value());
+ if (colExpr->kind() == Expression::Kind::kFunctionCall) {
+ col->setAlias(col->name());
+ col->setExpr(rewriteIDVertex2Vid(colExpr));
}
+ newCols->addColumn(col->clone().release());
- auto typeResult = deduceExprType(col->expr());
- NG_RETURN_IF_ERROR(typeResult);
- outputs_.emplace_back(col->name(), typeResult.value());
- // TODO(shylock) think about the push-down expr
+ NG_RETURN_IF_ERROR(deduceProps(colExpr, exprProps));
}
if (exprProps.tagProps().empty()) {
- return Status::SemanticError("Unsupported empty tag property expression in yield.");
+ for (const auto &tagSchema : tagsSchema_) {
+ exprProps.insertTagProp(tagSchema.first, nebula::kTag);
+ }
}
+ fetchCtx_->yieldExpr = newCols;
- for (const auto &tag : exprProps.tagNameIds()) {
- if (tags_.find(tag.first) == tags_.end()) {
- return Status::SemanticError("Mismatched tag: %s", tag.first.c_str());
- }
+ if (exprProps.hasInputVarProperty()) {
+ return Status::SemanticError("unsupported input/variable property expression in yield.");
}
- for (const auto &tagNameId : exprProps.tagNameIds()) {
- storage::cpp2::VertexProp vProp;
- std::vector propNames;
- propNames.reserve(exprProps.tagProps().at(tagNameId.second).size());
- vProp.set_tag(tagNameId.second);
- for (const auto &prop : exprProps.tagProps().at(tagNameId.second)) {
- propNames.emplace_back(prop.toString());
- gvColNames_.emplace_back(tagNameId.first + "." + prop.toString());
- }
- vProp.set_props(std::move(propNames));
- props_->emplace_back(std::move(vProp));
+ if (exprProps.hasSrcDstTagProperty()) {
+ return Status::SemanticError("unsupported src/dst property expression in yield.");
}
- // insert the reserved properties expression be compatible with 1.0
- // TODO(shylock) select kVid from storage
- newYieldColumns_ = qctx_->objPool()->add(new YieldColumns());
- // note eval vid by input expression
- newYieldColumns_->addColumn(
- new YieldColumn(InputPropertyExpression::make(pool, nebula::kVid), VertexID));
- for (auto col : yield->columns()) {
- newYieldColumns_->addColumn(col->clone().release());
+ for (const auto &tag : exprProps.tagNameIds()) {
+ if (tagsSchema_.find(tag.second) == tagsSchema_.end()) {
+ return Status::SemanticError("mismatched tag `%s'", tag.first.c_str());
+ }
}
return Status::OK();
}
-Status FetchVerticesValidator::preparePropertiesWithoutYield() {
- props_->clear();
- outputs_.emplace_back("vertices_", Value::Type::VERTEX);
- gvColNames_.emplace_back(nebula::kVid);
+void FetchVerticesValidator::extractVertexProp(ExpressionProps &exprProps) {
for (const auto &tagSchema : tagsSchema_) {
- storage::cpp2::VertexProp vProp;
- vProp.set_tag(tagSchema.first);
- std::vector propNames;
- propNames.reserve(tagSchema.second->getNumFields() + 1);
- auto tagNameResult = qctx_->schemaMng()->toTagName(space_.id, tagSchema.first);
- NG_RETURN_IF_ERROR(tagNameResult);
- auto tagName = std::move(tagNameResult).value();
+ auto tagID = tagSchema.first;
+ exprProps.insertTagProp(tagID, nebula::kTag);
for (std::size_t i = 0; i < tagSchema.second->getNumFields(); ++i) {
const auto propName = tagSchema.second->getFieldName(i);
- propNames.emplace_back(propName);
- gvColNames_.emplace_back(tagName + "." + propName);
+ exprProps.insertTagProp(tagID, propName);
}
- gvColNames_.emplace_back(tagName + "._tag");
- propNames.emplace_back(nebula::kTag); // "_tag"
- vProp.set_props(std::move(propNames));
- props_->emplace_back(std::move(vProp));
}
- return Status::OK();
-}
-
-// TODO(shylock) optimize dedup input when distinct given
-std::string FetchVerticesValidator::buildConstantInput() {
- auto input = vctx_->anonVarGen()->getVar();
- qctx_->ectx()->setResult(input, ResultBuilder().value(Value(std::move(srcVids_))).build());
-
- auto *pool = qctx_->objPool();
- src_ = VariablePropertyExpression::make(pool, input, kVid);
- return input;
-}
-
-std::string FetchVerticesValidator::buildRuntimeInput() {
- src_ = DCHECK_NOTNULL(srcRef_);
- return inputVar_;
}
} // namespace graph
diff --git a/src/graph/validator/FetchVerticesValidator.h b/src/graph/validator/FetchVerticesValidator.h
index b915a3cc0de..608aa39ffd7 100644
--- a/src/graph/validator/FetchVerticesValidator.h
+++ b/src/graph/validator/FetchVerticesValidator.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2020 vesoft inc. All rights reserved.
+/* Copyright (c) 2021 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.
@@ -7,8 +7,8 @@
#ifndef _VALIDATOR_FETCH_VERTICES_VALIDATOR_H_
#define _VALIDATOR_FETCH_VERTICES_VALIDATOR_H_
+#include "graph/context/ast/QueryAstContext.h"
#include "graph/validator/Validator.h"
-#include "interface/gen-cpp2/storage_types.h"
#include "parser/TraverseSentences.h"
namespace nebula {
@@ -16,52 +16,26 @@ namespace graph {
class FetchVerticesValidator final : public Validator {
public:
- using VertexProp = nebula::storage::cpp2::VertexProp;
- using Expr = nebula::storage::cpp2::Expr;
FetchVerticesValidator(Sentence* sentence, QueryContext* context)
: Validator(sentence, context) {}
private:
Status validateImpl() override;
- Status toPlan() override;
+ Status validateTag(const NameLabelList* nameLables);
- Status check();
+ Status validateYield(YieldClause* yield);
- Status prepareVertices();
+ AstContext* getAstContext() override { return fetchCtx_.get(); }
- Status preparePropertiesWithYield(const YieldClause* yield);
- Status preparePropertiesWithoutYield();
- Status prepareProperties();
+ void extractVertexProp(ExpressionProps& exprProps);
- // TODO(shylock) merge the code
- std::string buildConstantInput();
- std::string buildRuntimeInput();
+ Expression* rewriteIDVertex2Vid(const Expression* expr);
private:
- // src from constant
- DataSet srcVids_{{kVid}};
- // src from runtime
- Expression* srcRef_{nullptr};
- Expression* src_{nullptr};
- bool onStar_{false};
- std::unordered_map tags_;
std::map> tagsSchema_;
- std::unique_ptr> props_;
- std::unique_ptr> exprs_;
- bool dedup_{false};
- std::vector orderBy_{};
- int64_t limit_{std::numeric_limits::max()};
- Expression* filter_{nullptr};
- // valid when yield expression not require storage
- // So expression like these will be evaluate in Project Executor
- bool withYield_{false};
- // outputs
- std::vector gvColNames_;
- // new yield to inject reserved properties for compatible with 1.0
- YieldColumns* newYieldColumns_{nullptr};
- // input
- std::string inputVar_; // empty when pipe or no input in fact
+
+ std::unique_ptr fetchCtx_;
};
} // namespace graph
diff --git a/src/graph/validator/FindPathValidator.cpp b/src/graph/validator/FindPathValidator.cpp
index c7d4c5384d1..2f4975149f9 100644
--- a/src/graph/validator/FindPathValidator.cpp
+++ b/src/graph/validator/FindPathValidator.cpp
@@ -9,6 +9,7 @@
#include "common/expression/VariableExpression.h"
#include "graph/planner/plan/Algo.h"
#include "graph/planner/plan/Logic.h"
+#include "graph/util/ValidateUtil.h"
namespace nebula {
namespace graph {
@@ -22,9 +23,9 @@ Status FindPathValidator::validateImpl() {
NG_RETURN_IF_ERROR(validateStarts(fpSentence->from(), pathCtx_->from));
NG_RETURN_IF_ERROR(validateStarts(fpSentence->to(), pathCtx_->to));
- NG_RETURN_IF_ERROR(validateOver(fpSentence->over(), pathCtx_->over));
+ NG_RETURN_IF_ERROR(ValidateUtil::validateOver(qctx_, fpSentence->over(), pathCtx_->over));
NG_RETURN_IF_ERROR(validateWhere(fpSentence->where()));
- NG_RETURN_IF_ERROR(validateStep(fpSentence->step(), pathCtx_->steps));
+ NG_RETURN_IF_ERROR(ValidateUtil::validateStep(fpSentence->step(), pathCtx_->steps));
outputs_.emplace_back("path", Value::Type::PATH);
return Status::OK();
diff --git a/src/graph/validator/FindPathValidator.h b/src/graph/validator/FindPathValidator.h
index 2cc367dc197..0f2d03a23ec 100644
--- a/src/graph/validator/FindPathValidator.h
+++ b/src/graph/validator/FindPathValidator.h
@@ -9,15 +9,14 @@
#include "common/base/Base.h"
#include "graph/context/ast/QueryAstContext.h"
-#include "graph/validator/TraversalValidator.h"
+#include "graph/validator/Validator.h"
namespace nebula {
namespace graph {
-class FindPathValidator final : public TraversalValidator {
+class FindPathValidator final : public Validator {
public:
- FindPathValidator(Sentence* sentence, QueryContext* context)
- : TraversalValidator(sentence, context) {}
+ FindPathValidator(Sentence* sentence, QueryContext* context) : Validator(sentence, context) {}
private:
Status validateImpl() override;
diff --git a/src/graph/validator/GetSubgraphValidator.cpp b/src/graph/validator/GetSubgraphValidator.cpp
index 2f4fc5f68d3..fd7cf6fb184 100644
--- a/src/graph/validator/GetSubgraphValidator.cpp
+++ b/src/graph/validator/GetSubgraphValidator.cpp
@@ -14,6 +14,7 @@
#include "graph/context/QueryExpressionContext.h"
#include "graph/planner/plan/Logic.h"
#include "graph/planner/plan/Query.h"
+#include "graph/util/ValidateUtil.h"
#include "parser/TraverseSentences.h"
namespace nebula {
@@ -24,7 +25,7 @@ Status GetSubgraphValidator::validateImpl() {
subgraphCtx_ = getContext();
subgraphCtx_->withProp = gsSentence->withProp();
- NG_RETURN_IF_ERROR(validateStep(gsSentence->step(), subgraphCtx_->steps));
+ NG_RETURN_IF_ERROR(ValidateUtil::validateStep(gsSentence->step(), subgraphCtx_->steps));
NG_RETURN_IF_ERROR(validateStarts(gsSentence->from(), subgraphCtx_->from));
NG_RETURN_IF_ERROR(validateInBound(gsSentence->in()));
NG_RETURN_IF_ERROR(validateOutBound(gsSentence->out()));
diff --git a/src/graph/validator/GetSubgraphValidator.h b/src/graph/validator/GetSubgraphValidator.h
index 1bcfce43e03..e51bfefc4f1 100644
--- a/src/graph/validator/GetSubgraphValidator.h
+++ b/src/graph/validator/GetSubgraphValidator.h
@@ -8,15 +8,14 @@
#define GRAPH_VALIDATOR_GETSUBGRAPHVALIDATOR_H_
#include "graph/context/ast/QueryAstContext.h"
-#include "graph/validator/TraversalValidator.h"
+#include "graph/validator/Validator.h"
#include "parser/Clauses.h"
namespace nebula {
namespace graph {
-class GetSubgraphValidator final : public TraversalValidator {
+class GetSubgraphValidator final : public Validator {
public:
- GetSubgraphValidator(Sentence* sentence, QueryContext* context)
- : TraversalValidator(sentence, context) {}
+ GetSubgraphValidator(Sentence* sentence, QueryContext* context) : Validator(sentence, context) {}
private:
Status validateImpl() override;
diff --git a/src/graph/validator/GoValidator.cpp b/src/graph/validator/GoValidator.cpp
index a6f6c5f715a..ed43b87d03c 100644
--- a/src/graph/validator/GoValidator.cpp
+++ b/src/graph/validator/GoValidator.cpp
@@ -10,6 +10,7 @@
#include "common/expression/VariableExpression.h"
#include "graph/planner/plan/Logic.h"
#include "graph/util/ExpressionUtils.h"
+#include "graph/util/ValidateUtil.h"
#include "graph/visitor/ExtractPropExprVisitor.h"
#include "parser/TraverseSentences.h"
@@ -20,9 +21,9 @@ Status GoValidator::validateImpl() {
goCtx_ = getContext();
goCtx_->inputVarName = inputVarName_;
- NG_RETURN_IF_ERROR(validateStep(goSentence->stepClause(), goCtx_->steps));
+ NG_RETURN_IF_ERROR(ValidateUtil::validateStep(goSentence->stepClause(), goCtx_->steps));
NG_RETURN_IF_ERROR(validateStarts(goSentence->fromClause(), goCtx_->from));
- NG_RETURN_IF_ERROR(validateOver(goSentence->overClause(), goCtx_->over));
+ NG_RETURN_IF_ERROR(ValidateUtil::validateOver(qctx_, goSentence->overClause(), goCtx_->over));
NG_RETURN_IF_ERROR(validateWhere(goSentence->whereClause()));
NG_RETURN_IF_ERROR(validateYield(goSentence->yieldClause()));
NG_RETURN_IF_ERROR(validateTruncate(goSentence->truncateClause()));
@@ -116,9 +117,6 @@ Status GoValidator::validateTruncate(TruncateClause* truncate) {
}
Status GoValidator::validateYield(YieldClause* yield) {
- if (yield == nullptr) {
- return Status::SemanticError("Yield clause nullptr.");
- }
goCtx_->distinct = yield->isDistinct();
const auto& over = goCtx_->over;
auto* pool = qctx_->objPool();
@@ -140,7 +138,7 @@ Status GoValidator::validateYield(YieldClause* yield) {
for (auto col : cols) {
col->setExpr(ExpressionUtils::rewriteLabelAttr2EdgeProp(col->expr()));
- NG_RETURN_IF_ERROR(invalidLabelIdentifiers(col->expr()));
+ NG_RETURN_IF_ERROR(ValidateUtil::invalidLabelIdentifiers(col->expr()));
auto* colExpr = col->expr();
if (graph::ExpressionUtils::findAny(colExpr, {Expression::Kind::kAggregate})) {
diff --git a/src/graph/validator/GoValidator.h b/src/graph/validator/GoValidator.h
index 140bf92530c..e2f97866b17 100644
--- a/src/graph/validator/GoValidator.h
+++ b/src/graph/validator/GoValidator.h
@@ -9,16 +9,16 @@
#include "graph/context/ast/QueryAstContext.h"
#include "graph/planner/plan/Query.h"
-#include "graph/validator/TraversalValidator.h"
+#include "graph/validator/Validator.h"
namespace nebula {
namespace graph {
-class GoValidator final : public TraversalValidator {
+class GoValidator final : public Validator {
public:
using VertexProp = nebula::storage::cpp2::VertexProp;
using EdgeProp = nebula::storage::cpp2::EdgeProp;
- GoValidator(Sentence* sentence, QueryContext* context) : TraversalValidator(sentence, context) {}
+ GoValidator(Sentence* sentence, QueryContext* context) : Validator(sentence, context) {}
private:
Status validateImpl() override;
diff --git a/src/graph/validator/MatchValidator.cpp b/src/graph/validator/MatchValidator.cpp
index 4d8823e6a33..d5e8a3a92d6 100644
--- a/src/graph/validator/MatchValidator.cpp
+++ b/src/graph/validator/MatchValidator.cpp
@@ -13,7 +13,7 @@
namespace nebula {
namespace graph {
MatchValidator::MatchValidator(Sentence *sentence, QueryContext *context)
- : TraversalValidator(sentence, context) {
+ : Validator(sentence, context) {
matchCtx_ = getContext();
}
@@ -353,6 +353,12 @@ Status MatchValidator::validateReturn(MatchReturn *ret,
}
if (ret->returnItems()->columns()) {
for (auto *column : ret->returnItems()->columns()->columns()) {
+ if (ExpressionUtils::hasAny(column->expr(),
+ {Expression::Kind::kVertex, Expression::Kind::kEdge})) {
+ return Status::SemanticError(
+ "keywords: vertex and edge are not supported in return clause `%s'",
+ column->toString().c_str());
+ }
columns->addColumn(column->clone().release());
}
}
diff --git a/src/graph/validator/MatchValidator.h b/src/graph/validator/MatchValidator.h
index 997744b2107..762589a2dc8 100644
--- a/src/graph/validator/MatchValidator.h
+++ b/src/graph/validator/MatchValidator.h
@@ -11,14 +11,14 @@
#include "graph/context/ast/CypherAstContext.h"
#include "graph/planner/plan/Query.h"
#include "graph/util/AnonVarGenerator.h"
-#include "graph/validator/TraversalValidator.h"
+#include "graph/validator/Validator.h"
namespace nebula {
class MatchStepRange;
class ObjectPool;
namespace graph {
-class MatchValidator final : public TraversalValidator {
+class MatchValidator final : public Validator {
public:
MatchValidator(Sentence *sentence, QueryContext *context);
diff --git a/src/graph/validator/MutateValidator.cpp b/src/graph/validator/MutateValidator.cpp
index a99edceadb5..d913e3cbad6 100644
--- a/src/graph/validator/MutateValidator.cpp
+++ b/src/graph/validator/MutateValidator.cpp
@@ -148,7 +148,7 @@ Status InsertEdgesValidator::toPlan() {
nullptr,
spaceId_,
std::move(edges_),
- std::move(propNames_),
+ std::move(entirePropNames_),
ifNotExists_,
useChainInsert);
root_ = doNode;
@@ -196,6 +196,11 @@ Status InsertEdgesValidator::prepareEdges() {
auto useToss = isoLevel == IsoLevel::TOSS;
auto size = useToss ? rows_.size() : rows_.size() * 2;
edges_.reserve(size);
+
+ size_t fieldNum = schema_->getNumFields();
+ for (size_t j = 0; j < fieldNum; ++j) {
+ entirePropNames_.emplace_back(schema_->field(j)->name());
+ }
for (auto i = 0u; i < rows_.size(); i++) {
auto *row = rows_[i];
if (propNames_.size() != row->values().size()) {
@@ -233,6 +238,34 @@ Status InsertEdgesValidator::prepareEdges() {
auto valsRet = SchemaUtil::toValueVec(row->values());
NG_RETURN_IF_ERROR(valsRet);
auto props = std::move(valsRet).value();
+
+ std::vector entirePropValues;
+ for (size_t j = 0; j < fieldNum; ++j) {
+ auto *field = schema_->field(j);
+ auto propName = entirePropNames_[j];
+ auto iter = std::find(propNames_.begin(), propNames_.end(), propName);
+ if (iter == propNames_.end()) {
+ if (field->hasDefault()) {
+ auto *defaultValue = field->defaultValue();
+ DCHECK(!!defaultValue);
+ auto v = defaultValue->eval(QueryExpressionContext()(nullptr));
+ entirePropValues.emplace_back(v);
+ } else {
+ if (!field->nullable()) {
+ return Status::SemanticError(
+ "The property `%s' is not nullable and has no default value.", field->name());
+ }
+ entirePropValues.emplace_back(Value(NullType::__NULL__));
+ }
+ } else {
+ auto v = props[std::distance(propNames_.begin(), iter)];
+ if (!field->nullable() && v.isNull()) {
+ return Status::SemanticError("The non-nullable property `%s' could not be NULL.",
+ field->name());
+ }
+ entirePropValues.emplace_back(v);
+ }
+ }
storage::cpp2::NewEdge edge;
storage::cpp2::EdgeKey key;
@@ -241,7 +274,7 @@ Status InsertEdgesValidator::prepareEdges() {
key.set_edge_type(edgeType_);
key.set_ranking(rank);
edge.set_key(key);
- edge.set_props(std::move(props));
+ edge.set_props(std::move(entirePropValues));
edges_.emplace_back(edge);
if (!useToss) {
// inbound
diff --git a/src/graph/validator/MutateValidator.h b/src/graph/validator/MutateValidator.h
index 8ebd2a876df..58464c32b74 100644
--- a/src/graph/validator/MutateValidator.h
+++ b/src/graph/validator/MutateValidator.h
@@ -58,6 +58,7 @@ class InsertEdgesValidator final : public Validator {
EdgeType edgeType_{-1};
std::shared_ptr schema_;
std::vector propNames_;
+ std::vector entirePropNames_;
std::vector rows_;
std::vector edges_;
};
diff --git a/src/graph/validator/TraversalValidator.cpp b/src/graph/validator/TraversalValidator.cpp
deleted file mode 100644
index 01cb47f0bb3..00000000000
--- a/src/graph/validator/TraversalValidator.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
-/* 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 "graph/validator/TraversalValidator.h"
-
-#include
-
-#include "common/expression/VariableExpression.h"
-#include "graph/util/SchemaUtil.h"
-
-namespace nebula {
-namespace graph {
-
-Status TraversalValidator::validateStarts(const VerticesClause* clause, Starts& starts) {
- if (clause == nullptr) {
- return Status::SemanticError("From clause nullptr.");
- }
- if (clause->isRef()) {
- auto* src = clause->ref();
- if (src->kind() != Expression::Kind::kInputProperty &&
- src->kind() != Expression::Kind::kVarProperty) {
- return Status::SemanticError(
- "`%s', Only input and variable expression is acceptable"
- " when starts are evaluated at runtime.",
- src->toString().c_str());
- }
- starts.fromType = src->kind() == Expression::Kind::kInputProperty ? kPipe : kVariable;
- auto type = deduceExprType(src);
- if (!type.ok()) {
- return type.status();
- }
- auto vidType = space_.spaceDesc.vid_type_ref().value().get_type();
- if (type.value() != SchemaUtil::propTypeToValueType(vidType)) {
- std::stringstream ss;
- ss << "`" << src->toString() << "', the srcs should be type of "
- << apache::thrift::util::enumNameSafe(vidType) << ", but was`" << type.value() << "'";
- return Status::SemanticError(ss.str());
- }
- starts.originalSrc = src;
- auto* propExpr = static_cast(src);
- if (starts.fromType == kVariable) {
- starts.userDefinedVarName = propExpr->sym();
- userDefinedVarNameList_.emplace(starts.userDefinedVarName);
- }
- starts.runtimeVidName = propExpr->prop();
- } else {
- auto vidList = clause->vidList();
- QueryExpressionContext ctx;
- for (auto* expr : vidList) {
- if (!evaluableExpr(expr)) {
- return Status::SemanticError("`%s' is not an evaluable expression.",
- expr->toString().c_str());
- }
- auto vid = expr->eval(ctx(nullptr));
- auto vidType = space_.spaceDesc.vid_type_ref().value().get_type();
- if (!SchemaUtil::isValidVid(vid, vidType)) {
- std::stringstream ss;
- ss << "Vid should be a " << apache::thrift::util::enumNameSafe(vidType);
- return Status::SemanticError(ss.str());
- }
- starts.vids.emplace_back(std::move(vid));
- }
- }
- return Status::OK();
-}
-
-Status TraversalValidator::validateOver(const OverClause* clause, Over& over) {
- if (clause == nullptr) {
- return Status::SemanticError("Over clause nullptr.");
- }
-
- over.direction = clause->direction();
- auto* schemaMng = qctx_->schemaMng();
- if (clause->isOverAll()) {
- auto allEdgeStatus = schemaMng->getAllEdge(space_.id);
- NG_RETURN_IF_ERROR(allEdgeStatus);
- auto edges = std::move(allEdgeStatus).value();
- if (edges.empty()) {
- return Status::SemanticError("No edge type found in space `%s'", space_.name.c_str());
- }
- for (auto edge : edges) {
- auto edgeType = schemaMng->toEdgeType(space_.id, edge);
- if (!edgeType.ok()) {
- return Status::SemanticError(
- "`%s' not found in space [`%s'].", edge.c_str(), space_.name.c_str());
- }
- over.edgeTypes.emplace_back(edgeType.value());
- }
- over.allEdges = std::move(edges);
- over.isOverAll = true;
- } else {
- auto edges = clause->edges();
- for (auto* edge : edges) {
- auto edgeName = *edge->edge();
- auto edgeType = schemaMng->toEdgeType(space_.id, edgeName);
- if (!edgeType.ok()) {
- return Status::SemanticError(
- "%s not found in space [%s].", edgeName.c_str(), space_.name.c_str());
- }
- over.edgeTypes.emplace_back(edgeType.value());
- }
- }
- return Status::OK();
-}
-
-Status TraversalValidator::validateStep(const StepClause* clause, StepClause& step) {
- if (clause == nullptr) {
- return Status::SemanticError("Step clause nullptr.");
- }
- step = *clause;
- if (clause->isMToN()) {
- if (step.mSteps() == 0) {
- step.setMSteps(1);
- }
- if (step.nSteps() < step.mSteps()) {
- return Status::SemanticError("`%s', upper bound steps should be greater than lower bound.",
- step.toString().c_str());
- }
- }
- return Status::OK();
-}
-
-} // namespace graph
-} // namespace nebula
diff --git a/src/graph/validator/TraversalValidator.h b/src/graph/validator/TraversalValidator.h
deleted file mode 100644
index d6a622c1493..00000000000
--- a/src/graph/validator/TraversalValidator.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* 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 GRAPH_VALIDATOR_TRAVERSALVALIDATOR_H_
-#define GRAPH_VALIDATOR_TRAVERSALVALIDATOR_H_
-
-#include "common/base/Base.h"
-#include "graph/context/ast/QueryAstContext.h"
-#include "graph/planner/plan/Query.h"
-#include "graph/util/ExpressionUtils.h"
-#include "graph/validator/Validator.h"
-
-namespace nebula {
-namespace graph {
-
-// some utils for the validator to traverse the graph
-class TraversalValidator : public Validator {
- protected:
- TraversalValidator(Sentence* sentence, QueryContext* qctx) : Validator(sentence, qctx) {}
-
- Status validateStarts(const VerticesClause* clause, Starts& starts);
-
- Status validateOver(const OverClause* clause, Over& over);
-
- Status validateStep(const StepClause* clause, StepClause& step);
-};
-
-} // namespace graph
-} // namespace nebula
-
-#endif
diff --git a/src/graph/validator/Validator.cpp b/src/graph/validator/Validator.cpp
index dd5f330a2e3..dea25aba4d5 100644
--- a/src/graph/validator/Validator.cpp
+++ b/src/graph/validator/Validator.cpp
@@ -6,6 +6,8 @@
#include "graph/validator/Validator.h"
+#include
+
#include "common/function/FunctionManager.h"
#include "graph/planner/plan/Query.h"
#include "graph/util/ExpressionUtils.h"
@@ -78,6 +80,8 @@ std::unique_ptr Validator::makeValidator(Sentence* sentence, QueryCon
return std::make_unique(sentence, context);
case Sentence::Kind::kCreateSpace:
return std::make_unique(sentence, context);
+ case Sentence::Kind::kCreateSpaceAs:
+ return std::make_unique(sentence, context);
case Sentence::Kind::kCreateTag:
return std::make_unique(sentence, context);
case Sentence::Kind::kCreateEdge:
@@ -449,20 +453,58 @@ Status Validator::checkDuplicateColName() {
return Status::OK();
}
-Status Validator::invalidLabelIdentifiers(const Expression* expr) const {
- auto labelExprs = ExpressionUtils::collectAll(expr, {Expression::Kind::kLabel});
- if (!labelExprs.empty()) {
- std::stringstream ss;
- ss << "Invalid label identifiers: ";
- for (auto* label : labelExprs) {
- ss << label->toString() << ",";
+Status Validator::validateStarts(const VerticesClause* clause, Starts& starts) {
+ if (clause == nullptr) {
+ return Status::SemanticError("From clause nullptr.");
+ }
+ if (clause->isRef()) {
+ auto* src = clause->ref();
+ if (src->kind() != Expression::Kind::kInputProperty &&
+ src->kind() != Expression::Kind::kVarProperty) {
+ return Status::SemanticError(
+ "`%s', Only input and variable expression is acceptable"
+ " when starts are evaluated at runtime.",
+ src->toString().c_str());
+ }
+ starts.fromType = src->kind() == Expression::Kind::kInputProperty ? kPipe : kVariable;
+ auto type = deduceExprType(src);
+ if (!type.ok()) {
+ return type.status();
+ }
+ auto vidType = space_.spaceDesc.vid_type_ref().value().get_type();
+ if (type.value() != SchemaUtil::propTypeToValueType(vidType)) {
+ std::stringstream ss;
+ ss << "`" << src->toString() << "', the srcs should be type of "
+ << apache::thrift::util::enumNameSafe(vidType) << ", but was`" << type.value() << "'";
+ return Status::SemanticError(ss.str());
+ }
+ starts.originalSrc = src;
+ auto* propExpr = static_cast(src);
+ if (starts.fromType == kVariable) {
+ starts.userDefinedVarName = propExpr->sym();
+ userDefinedVarNameList_.emplace(starts.userDefinedVarName);
+ }
+ starts.runtimeVidName = propExpr->prop();
+ } else {
+ auto vidList = clause->vidList();
+ QueryExpressionContext ctx;
+ for (auto* expr : vidList) {
+ if (!evaluableExpr(expr)) {
+ return Status::SemanticError("`%s' is not an evaluable expression.",
+ expr->toString().c_str());
+ }
+ auto vid = expr->eval(ctx(nullptr));
+ auto vidType = space_.spaceDesc.vid_type_ref().value().get_type();
+ if (!SchemaUtil::isValidVid(vid, vidType)) {
+ std::stringstream ss;
+ ss << "Vid should be a " << apache::thrift::util::enumNameSafe(vidType);
+ return Status::SemanticError(ss.str());
+ }
+ starts.vids.emplace_back(std::move(vid));
}
- auto errMsg = ss.str();
- errMsg.pop_back();
- return Status::SemanticError(std::move(errMsg));
}
-
return Status::OK();
}
+
} // namespace graph
} // namespace nebula
diff --git a/src/graph/validator/Validator.h b/src/graph/validator/Validator.h
index 69e8bcbf8d1..3b204af51ab 100644
--- a/src/graph/validator/Validator.h
+++ b/src/graph/validator/Validator.h
@@ -19,7 +19,7 @@
namespace nebula {
namespace graph {
-
+struct Starts;
class Validator {
public:
virtual ~Validator() = default;
@@ -127,14 +127,14 @@ class Validator {
return Status::OK();
}
+ // Check the output for duplicate column names
+ Status checkDuplicateColName();
+
// Check the variable or input property reference
// return the input variable
StatusOr checkRef(const Expression* ref, const Value::Type type);
- // Check the output for duplicate column names
- Status checkDuplicateColName();
-
- Status invalidLabelIdentifiers(const Expression* expr) const;
+ Status validateStarts(const VerticesClause* clause, Starts& starts);
template
std::unique_ptr getContext() const {
diff --git a/src/graph/validator/YieldValidator.cpp b/src/graph/validator/YieldValidator.cpp
index fa61b759658..e36c57f9271 100644
--- a/src/graph/validator/YieldValidator.cpp
+++ b/src/graph/validator/YieldValidator.cpp
@@ -10,6 +10,7 @@
#include "graph/context/QueryContext.h"
#include "graph/planner/plan/Query.h"
#include "graph/util/ExpressionUtils.h"
+#include "graph/util/ValidateUtil.h"
#include "parser/Clauses.h"
#include "parser/TraverseSentences.h"
@@ -111,7 +112,7 @@ Status YieldValidator::validateYieldAndBuildOutputs(const YieldClause *clause) {
columns_ = pool->add(new YieldColumns);
for (auto column : columns) {
auto expr = DCHECK_NOTNULL(column->expr());
- NG_RETURN_IF_ERROR(invalidLabelIdentifiers(expr));
+ NG_RETURN_IF_ERROR(ValidateUtil::invalidLabelIdentifiers(expr));
if (expr->kind() == Expression::Kind::kInputProperty) {
auto ipe = static_cast(expr);
diff --git a/src/graph/validator/test/CMakeLists.txt b/src/graph/validator/test/CMakeLists.txt
index d0b9077c44d..487e7791f4a 100644
--- a/src/graph/validator/test/CMakeLists.txt
+++ b/src/graph/validator/test/CMakeLists.txt
@@ -51,6 +51,7 @@ set(VALIDATOR_TEST_LIBS
$
$
$
+ $
)
nebula_add_test(
diff --git a/src/graph/validator/test/FetchVerticesTest.cpp b/src/graph/validator/test/FetchVerticesTest.cpp
index d574882b93c..2d83805a4cb 100644
--- a/src/graph/validator/test/FetchVerticesTest.cpp
+++ b/src/graph/validator/test/FetchVerticesTest.cpp
@@ -23,7 +23,7 @@ class FetchVerticesValidatorTest : public ValidatorTestBase {
};
TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) {
- auto src = VariablePropertyExpression::make(pool_.get(), "_VARNAME_", "VertexID");
+ auto src = ColumnExpression::make(pool_.get(), 0);
{
auto qctx = getQCtx("FETCH PROP ON person \"1\"");
auto *pool = qctx->objPool();
@@ -42,7 +42,6 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) {
auto yieldColumns = std::make_unique();
yieldColumns->addColumn(new YieldColumn(VertexExpression::make(pool), "vertices_"));
auto *project = Project::make(qctx, gv, yieldColumns.get());
- project->setColNames({"vertices_"});
auto result = Eq(qctx->plan()->root(), project);
ASSERT_TRUE(result.ok()) << result;
}
@@ -73,7 +72,6 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) {
auto yieldColumns = std::make_unique();
yieldColumns->addColumn(new YieldColumn(VertexExpression::make(pool), "vertices_"));
auto *project = Project::make(qctx, gv, yieldColumns.get());
- project->setColNames({"vertices_"});
auto result = Eq(qctx->plan()->root(), project);
ASSERT_TRUE(result.ok()) << result;
}
@@ -407,7 +405,6 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) {
auto yieldColumns = std::make_unique();
yieldColumns->addColumn(new YieldColumn(VertexExpression::make(pool), "vertices_"));
auto *project = Project::make(qctx, gv, yieldColumns.get());
- project->setColNames({"vertices_"});
auto result = Eq(qctx->plan()->root(), project);
ASSERT_TRUE(result.ok()) << result;
}
@@ -422,7 +419,6 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) {
auto yieldColumns = std::make_unique();
yieldColumns->addColumn(new YieldColumn(VertexExpression::make(pool), "vertices_"));
auto *project = Project::make(qctx, gv, yieldColumns.get());
- project->setColNames({"vertices_"});
auto result = Eq(qctx->plan()->root(), project);
ASSERT_TRUE(result.ok()) << result;
}
@@ -443,7 +439,6 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) {
new YieldColumn(InputPropertyExpression::make(pool, nebula::kVid), "VertexID"));
yieldColumns->addColumn(new YieldColumn(TagPropertyExpression::make(pool, "person", "name")));
auto *project = Project::make(qctx, gv, yieldColumns.get());
- project->setColNames(colNames);
auto result = Eq(qctx->plan()->root(), project);
ASSERT_TRUE(result.ok()) << result;
@@ -465,7 +460,6 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) {
yieldColumns->addColumn(new YieldColumn(TagPropertyExpression::make(pool, "person", "name")));
yieldColumns->addColumn(new YieldColumn(TagPropertyExpression::make(pool, "person", "age")));
auto *project = Project::make(qctx, gv, yieldColumns.get());
- project->setColNames(colNames);
auto result = Eq(qctx->plan()->root(), project);
ASSERT_TRUE(result.ok()) << result;
@@ -488,7 +482,6 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) {
yieldColumns->addColumn(new YieldColumn(TagPropertyExpression::make(pool, "person", "name")));
yieldColumns->addColumn(new YieldColumn(TagPropertyExpression::make(pool, "person", "age")));
auto *project = Project::make(qctx, gv, yieldColumns.get());
- project->setColNames({"VertexID", "(1+1)", "person.name", "person.age"});
auto result = Eq(qctx->plan()->root(), project);
ASSERT_TRUE(result.ok()) << result;
diff --git a/src/graph/validator/test/MatchValidatorTest.cpp b/src/graph/validator/test/MatchValidatorTest.cpp
index 03947c59366..43bbb9faa6c 100644
--- a/src/graph/validator/test/MatchValidatorTest.cpp
+++ b/src/graph/validator/test/MatchValidatorTest.cpp
@@ -611,6 +611,34 @@ TEST_F(MatchValidatorTest, validateAlias) {
EXPECT_EQ(std::string(result.message()),
"SemanticError: Path `p' does not have the type attribute");
}
+ {
+ std::string query = "MATCH (v:person) return id(vertex)";
+ auto result = checkResult(query);
+ EXPECT_EQ(
+ std::string(result.message()),
+ "SemanticError: keywords: vertex and edge are not supported in return clause `id(VERTEX)'");
+ }
+ {
+ std::string query = "MATCH (v:person) return vertex as a";
+ auto result = checkResult(query);
+ EXPECT_EQ(std::string(result.message()),
+ "SemanticError: keywords: vertex and edge are not supported in return clause `VERTEX "
+ "AS a'");
+ }
+ {
+ std::string query = "MATCH (v:person)-[e]-(v2) return src(edge)";
+ auto result = checkResult(query);
+ EXPECT_EQ(
+ std::string(result.message()),
+ "SemanticError: keywords: vertex and edge are not supported in return clause `src(EDGE)'");
+ }
+ {
+ std::string query = "MATCH (v:person)-[e]-(v2) return edge as b";
+ auto result = checkResult(query);
+ EXPECT_EQ(
+ std::string(result.message()),
+ "SemanticError: keywords: vertex and edge are not supported in return clause `EDGE AS b'");
+ }
}
} // namespace graph
diff --git a/src/graph/validator/test/MutateValidatorTest.cpp b/src/graph/validator/test/MutateValidatorTest.cpp
index b60a4562b05..a0677381f4b 100644
--- a/src/graph/validator/test/MutateValidatorTest.cpp
+++ b/src/graph/validator/test/MutateValidatorTest.cpp
@@ -44,11 +44,18 @@ TEST_F(MutateValidatorTest, InsertEdgeTest) {
ASSERT_FALSE(checkResult(cmd, {}));
}
// vid use function call
+ {
+ auto cmd =
+ "INSERT EDGE like(start, end, likeness) VALUES lower(\"Lily\")->\"Tom\":(2010, "
+ "2020, 90);";
+ ASSERT_TRUE(checkResult(cmd, {PK::kInsertEdges, PK::kStart}));
+ }
+ // vid use function call
{
auto cmd =
"INSERT EDGE like(start, end) VALUES lower(\"Lily\")->\"Tom\":(2010, "
"2020);";
- ASSERT_TRUE(checkResult(cmd, {PK::kInsertEdges, PK::kStart}));
+ ASSERT_FALSE(checkResult(cmd, {PK::kInsertEdges, PK::kStart}));
}
}
diff --git a/src/graph/validator/test/QueryValidatorTest.cpp b/src/graph/validator/test/QueryValidatorTest.cpp
index 025e3475f5a..ac931a86420 100644
--- a/src/graph/validator/test/QueryValidatorTest.cpp
+++ b/src/graph/validator/test/QueryValidatorTest.cpp
@@ -950,6 +950,12 @@ TEST_F(QueryValidatorTest, GoInvalid) {
auto result = checkResult(query);
EXPECT_EQ(std::string(result.message()), "SemanticError: Duplicate Column Name : `id'");
}
+ {
+ std::string query = "GO FROM id(vertex) OVER * ";
+ auto result = checkResult(query);
+ EXPECT_EQ(std::string(result.message()),
+ "SemanticError: `id(VERTEX)' is not an evaluable expression.");
+ }
}
TEST_F(QueryValidatorTest, Limit) {
diff --git a/src/graph/visitor/test/CMakeLists.txt b/src/graph/visitor/test/CMakeLists.txt
index b5b61c02e28..159e50b613a 100644
--- a/src/graph/visitor/test/CMakeLists.txt
+++ b/src/graph/visitor/test/CMakeLists.txt
@@ -56,6 +56,7 @@ nebula_add_test(
$
$
$
+ $
LIBRARIES
gtest
${THRIFT_LIBRARIES}
diff --git a/src/interface/common.thrift b/src/interface/common.thrift
index 6f05fb32fbf..001ae49950d 100644
--- a/src/interface/common.thrift
+++ b/src/interface/common.thrift
@@ -324,6 +324,7 @@ enum ErrorCode {
E_BALANCER_FAILURE = -2047,
E_JOB_NOT_FINISHED = -2048,
E_TASK_REPORT_OUT_DATE = -2049,
+ E_JOB_NOT_IN_SPACE = -2050,
E_INVALID_JOB = -2065,
// Backup Failure
diff --git a/src/interface/meta.thrift b/src/interface/meta.thrift
index 922736f0f30..d4fe48298a6 100644
--- a/src/interface/meta.thrift
+++ b/src/interface/meta.thrift
@@ -328,6 +328,11 @@ struct CreateSpaceReq {
2: bool if_not_exists,
}
+struct CreateSpaceAsReq {
+ 1: binary old_space_name,
+ 2: binary new_space_name,
+}
+
struct DropSpaceReq {
1: binary space_name
2: bool if_exists,
@@ -1171,6 +1176,8 @@ service MetaService {
GetSpaceResp getSpace(1: GetSpaceReq req);
ListSpacesResp listSpaces(1: ListSpacesReq req);
+ ExecResp createSpaceAs(1: CreateSpaceAsReq req);
+
ExecResp createTag(1: CreateTagReq req);
ExecResp alterTag(1: AlterTagReq req);
ExecResp dropTag(1: DropTagReq req);
diff --git a/src/kvstore/EventListener.h b/src/kvstore/EventListener.h
index 2f63b8137ca..f10363552a3 100644
--- a/src/kvstore/EventListener.h
+++ b/src/kvstore/EventListener.h
@@ -210,8 +210,9 @@ class EventListener : public rocksdb::EventListener {
return "PeriodicCompaction";
case rocksdb::CompactionReason::kNumOfReasons:
return "NumOfReasons";
+ default:
+ return "Unknown";
}
- return "Unknown";
}
std::string flushReasonString(const rocksdb::FlushReason& reason) {
diff --git a/src/kvstore/KVEngine.h b/src/kvstore/KVEngine.h
index dd23f8ccd00..bd485a4333b 100644
--- a/src/kvstore/KVEngine.h
+++ b/src/kvstore/KVEngine.h
@@ -72,6 +72,8 @@ class KVEngine {
const std::string& prefix,
std::unique_ptr* iter) = 0;
+ virtual nebula::cpp2::ErrorCode scan(std::unique_ptr* storageIter) = 0;
+
// Write a single record
virtual nebula::cpp2::ErrorCode put(std::string key, std::string value) = 0;
@@ -111,6 +113,10 @@ class KVEngine {
virtual nebula::cpp2::ErrorCode setDBOption(const std::string& configKey,
const std::string& configValue) = 0;
+ // Get DB Property
+ virtual ErrorOr getProperty(
+ const std::string& property) = 0;
+
virtual nebula::cpp2::ErrorCode compact() = 0;
virtual nebula::cpp2::ErrorCode flush() = 0;
diff --git a/src/kvstore/KVStore.h b/src/kvstore/KVStore.h
index c4ac838a45a..dcef7e1a9f9 100644
--- a/src/kvstore/KVStore.h
+++ b/src/kvstore/KVStore.h
@@ -216,6 +216,9 @@ class KVStore {
virtual std::vector getDataRoot() const = 0;
+ virtual ErrorOr getProperty(
+ GraphSpaceID spaceId, const std::string& property) = 0;
+
protected:
KVStore() = default;
};
diff --git a/src/kvstore/NebulaSnapshotManager.cpp b/src/kvstore/NebulaSnapshotManager.cpp
index 5d31055be5e..26d41f44d6f 100644
--- a/src/kvstore/NebulaSnapshotManager.cpp
+++ b/src/kvstore/NebulaSnapshotManager.cpp
@@ -24,14 +24,13 @@ NebulaSnapshotManager::NebulaSnapshotManager(NebulaStore* kv) : store_(kv) {
// Snapshot rate is limited to FLAGS_snapshot_worker_threads * FLAGS_snapshot_part_rate_limit.
// So by default, the total send rate is limited to 4 * 10Mb = 40Mb.
LOG(INFO) << "Send snapshot is rate limited to " << FLAGS_snapshot_part_rate_limit
- << " for each part";
+ << " for each part by default";
}
void NebulaSnapshotManager::accessAllRowsInSnapshot(GraphSpaceID spaceId,
PartitionID partId,
raftex::SnapshotCallback cb) {
- auto rateLimiter = std::make_unique(FLAGS_snapshot_part_rate_limit,
- FLAGS_snapshot_part_rate_limit);
+ auto rateLimiter = std::make_unique();
CHECK_NOTNULL(store_);
auto tables = NebulaKeyUtils::snapshotPrefix(partId);
std::vector data;
@@ -74,7 +73,9 @@ bool NebulaSnapshotManager::accessTable(GraphSpaceID spaceId,
size_t batchSize = 0;
while (iter && iter->valid()) {
if (batchSize >= FLAGS_snapshot_batch_size) {
- rateLimiter->consume(batchSize);
+ rateLimiter->consume(static_cast(batchSize), // toConsume
+ static_cast(FLAGS_snapshot_part_rate_limit), // rate
+ static_cast(FLAGS_snapshot_part_rate_limit)); // burstSize
if (cb(data, totalCount, totalSize, raftex::SnapshotStatus::IN_PROGRESS)) {
data.clear();
batchSize = 0;
diff --git a/src/kvstore/NebulaStore.cpp b/src/kvstore/NebulaStore.cpp
index aa7bfc767fc..0727677f876 100644
--- a/src/kvstore/NebulaStore.cpp
+++ b/src/kvstore/NebulaStore.cpp
@@ -1168,5 +1168,26 @@ nebula::cpp2::ErrorCode NebulaStore::multiPutWithoutReplicator(GraphSpaceID spac
return nebula::cpp2::ErrorCode::SUCCEEDED;
}
+ErrorOr NebulaStore::getProperty(
+ GraphSpaceID spaceId, const std::string& property) {
+ auto spaceRet = space(spaceId);
+ if (!ok(spaceRet)) {
+ LOG(ERROR) << "Get Space " << spaceId << " Failed";
+ return error(spaceRet);
+ }
+ auto space = nebula::value(spaceRet);
+
+ folly::dynamic obj = folly::dynamic::object;
+ for (size_t i = 0; i < space->engines_.size(); i++) {
+ auto val = space->engines_[i]->getProperty(property);
+ if (!ok(val)) {
+ return error(val);
+ }
+ auto eng = folly::stringPrintf("Engine %zu", i);
+ obj[eng] = std::move(value(val));
+ }
+ return folly::toJson(obj);
+}
+
} // namespace kvstore
} // namespace nebula
diff --git a/src/kvstore/NebulaStore.h b/src/kvstore/NebulaStore.h
index 306f716827f..e8529f18010 100644
--- a/src/kvstore/NebulaStore.h
+++ b/src/kvstore/NebulaStore.h
@@ -11,6 +11,7 @@
#include
#include "common/base/Base.h"
+#include "common/ssl/SSLConfig.h"
#include "common/utils/Utils.h"
#include "interface/gen-cpp2/RaftexServiceAsyncClient.h"
#include "kvstore/DiskManager.h"
@@ -65,7 +66,8 @@ class NebulaStore : public KVStore, public Handler {
options_(std::move(options)) {
CHECK_NOTNULL(options_.partMan_);
clientMan_ =
- std::make_shared>();
+ std::make_shared>(
+ FLAGS_enable_ssl);
}
~NebulaStore();
@@ -273,6 +275,9 @@ class NebulaStore : public KVStore, public Handler {
nebula::cpp2::ErrorCode multiPutWithoutReplicator(GraphSpaceID spaceId,
std::vector keyValues) override;
+ ErrorOr getProperty(GraphSpaceID spaceId,
+ const std::string& property) override;
+
private:
void loadPartFromDataPath();
diff --git a/src/kvstore/RateLimiter.h b/src/kvstore/RateLimiter.h
index 7b91ad4b189..97b4ef196fa 100644
--- a/src/kvstore/RateLimiter.h
+++ b/src/kvstore/RateLimiter.h
@@ -21,32 +21,29 @@ namespace kvstore {
// For now, there are two major cases: snapshot (both for balance or catch up) and rebuild index.
class RateLimiter {
public:
- RateLimiter(int32_t rate, int32_t burstSize)
- : rate_(static_cast(rate)), burstSize_(static_cast(burstSize)) {
+ RateLimiter() {
// token will be available after 1 second, to prevent speed spike at the beginning
auto now = time::WallClock::fastNowInSec();
int64_t waitInSec = FLAGS_skip_wait_in_rate_limiter ? 0 : 1;
- bucket_.reset(new folly::TokenBucket(rate_, burstSize_, static_cast(now + waitInSec)));
+ bucket_.reset(new folly::DynamicTokenBucket(static_cast(now + waitInSec)));
}
// Caller must make sure the **the parition has been add, and won't be removed during consume.**
// Snaphot and rebuild index follow this principle by design.
- void consume(size_t toConsume) {
- if (toConsume > burstSize_) {
+ void consume(double toConsume, double rate, double burstSize) {
+ if (toConsume > burstSize) {
// consumeWithBorrowAndWait do nothing when toConsume > burstSize_, we sleep 1s instead
std::this_thread::sleep_for(std::chrono::seconds(1));
} else {
// If there are enouth tokens, consume and return immediately.
// If not, cosume anyway, but sleep enough time before return.
auto now = time::WallClock::fastNowInSec();
- bucket_->consumeWithBorrowAndWait(static_cast(toConsume), static_cast(now));
+ bucket_->consumeWithBorrowAndWait(toConsume, rate, burstSize, static_cast