diff --git a/ci/test.sh b/ci/test.sh
index 5dcb5285afd4ba661211e41a0b6c432537282fef..8ba5d84aaa1e81affe1789bf02ef838aa085a33d 100755
--- a/ci/test.sh
+++ b/ci/test.sh
@@ -87,6 +87,7 @@ function run_test() {
         $PROJ_DIR/tests/query/v1/* \
         $PROJ_DIR/tests/query/v2/* \
         $PROJ_DIR/tests/query/stateless/test_schema.py \
+        $PROJ_DIR/tests/query/stateless/test_admin.py \
         $PROJ_DIR/tests/query/stateless/test_if_exists.py \
         $PROJ_DIR/tests/query/stateless/test_range.py \
         $PROJ_DIR/tests/query/stateless/test_go.py \
diff --git a/resources/gflags.json b/resources/gflags.json
index bdc747278792a1460dd9f24ef1c8e3ed1918df57..c265b9cd19e3b1671755d7e69caeb62cb2539520 100644
--- a/resources/gflags.json
+++ b/resources/gflags.json
@@ -1,69 +1,15 @@
 {
-    "IMMUTABLE": [
-    ],
-    "REBOOT": [
-    ],
     "MUTABLE": [
-        "load_data_interval_secs",
         "max_edge_returned_per_vertex",
         "minloglevel",
         "v",
         "heartbeat_interval_secs",
         "meta_client_retry_times",
         "slow_op_threshhold_ms",
-        "wal_ttl"
-    ],
-    "IGNORED": [
-        "logging",
-        "flagfile",
-        "fromenv",
-        "tryfromenv",
-        "undefok",
-        "tab_completion_columns",
-        "tab_completion_word",
-        "alsologtostderr",
-        "drop_log_memory",
-        "log_backtrace_at",
-        "log_dir",
-        "log_link",
-        "log_prefix",
-        "logbuflevel",
-        "logbufsecs",
-        "logemaillevel",
-        "logfile_mode",
-        "logmailer",
-        "logtostderr",
-        "max_log_size",
-        "stderrthreshold",
-        "stop_logging_if_full_disk",
-        "symbolize_stacktrace",
-        "vmodule",
-        "help",
-        "helpon",
-        "helpxml",
-        "helpfull",
-        "helpmatch",
-        "helpshort",
-        "alsologtoemail",
-        "version",
-        "helppackage",
-        "sasl_policy",
-        "codel_enabled",
-        "codel_interval",
-        "colorlogtostderr",
-        "dcache_unit_test",
-        "service_identity",
-        "thrift_ssl_policy",
-        "codel_target_delay",
-        "pending_interval",
-        "threadtimeout_ms",
-        "pin_service_identity",
-        "thrift_pcap_logging_prohibit",
-        "thrift_security_kill_switch_file",
-        "thrift_cpp2_protocol_reader_string_limit",
-        "thrift_cpp2_protocol_reader_container_limit",
-        "zlib_compressor_buffer_growth",
-        "gflags_mode_json"
+        "wal_ttl",
+        "enable_reservoir_sampling",
+        "custom_filter_interval_secs",
+        "enable_multi_versions"
     ],
     "NESTED": [
         "rocksdb_db_options",
@@ -71,3 +17,4 @@
         "rocksdb_block_based_table_options"
     ]
 }
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f62da9c0606ff8a6d24761da57f707c9b385f894..8811ab4d918a12053889ef3a43ec53affae89edd 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -3,7 +3,6 @@
 # This source code is licensed under Apache 2.0 License,
 # attached with Common Clause Condition 1.0, found in the LICENSES directory.
 
-#nebula_add_subdirectory(graph)
 nebula_add_subdirectory(daemons)
 nebula_add_subdirectory(service)
 nebula_add_subdirectory(parser)
@@ -11,6 +10,5 @@ nebula_add_subdirectory(planner)
 nebula_add_subdirectory(validator)
 nebula_add_subdirectory(exec)
 nebula_add_subdirectory(util)
-nebula_add_subdirectory(mock)
 nebula_add_subdirectory(context)
 nebula_add_subdirectory(scheduler)
diff --git a/src/daemons/CMakeLists.txt b/src/daemons/CMakeLists.txt
index a05778776e7c9d5275ff4cc8e27899d7e204a2de..400f105d2d1fd3cb1fdfb04250478e2fa63dfa9a 100644
--- a/src/daemons/CMakeLists.txt
+++ b/src/daemons/CMakeLists.txt
@@ -42,7 +42,6 @@ nebula_add_executable(
         $<TARGET_OBJECTS:common_meta_obj>
         $<TARGET_OBJECTS:common_ws_obj>
         $<TARGET_OBJECTS:common_ws_common_obj>
-        # $<TARGET_OBJECTS:meta_gflags_man_obj>
         $<TARGET_OBJECTS:common_thread_obj>
         $<TARGET_OBJECTS:common_fs_obj>
         $<TARGET_OBJECTS:common_base_obj>
@@ -54,6 +53,7 @@ nebula_add_executable(
         $<TARGET_OBJECTS:common_encryption_obj>
         $<TARGET_OBJECTS:common_function_manager_obj>
         $<TARGET_OBJECTS:common_agg_function_obj>
+        $<TARGET_OBJECTS:common_conf_obj>
     LIBRARIES
         proxygenhttpserver
         proxygenlib
diff --git a/src/exec/CMakeLists.txt b/src/exec/CMakeLists.txt
index 711d850419440bfba9a78ac7e09bbca4661ba056..ed3347441412ca64d2a64a3ee4def3ffda6545b8 100644
--- a/src/exec/CMakeLists.txt
+++ b/src/exec/CMakeLists.txt
@@ -38,6 +38,7 @@ nebula_add_library(
     mutate/InsertExecutor.cpp
     mutate/DeleteExecutor.cpp
     mutate/UpdateExecutor.cpp
+    admin/ConfigExecutor.cpp
 )
 
 nebula_add_subdirectory(query/test)
diff --git a/src/exec/Executor.cpp b/src/exec/Executor.cpp
index e49012a3e81ab04404e037486ce8a3bcc058bd7c..407ecf5d4dbddeee33b6a70c71cdeb971fa83832 100644
--- a/src/exec/Executor.cpp
+++ b/src/exec/Executor.cpp
@@ -20,6 +20,7 @@
 #include "exec/admin/SwitchSpaceExecutor.h"
 #include "exec/admin/PartExecutor.h"
 #include "exec/admin/CharsetExecutor.h"
+#include "exec/admin/ConfigExecutor.h"
 #include "exec/logic/LoopExecutor.h"
 #include "exec/logic/MultiOutputsExecutor.h"
 #include "exec/logic/SelectExecutor.h"
@@ -403,6 +404,27 @@ Executor *Executor::makeExecutor(const PlanNode *node,
             exec->dependsOn(input);
             break;
         }
+        case PlanNode::Kind::kShowConfigs: {
+            auto showConfigs = asNode<ShowConfigs>(node);
+            auto input = makeExecutor(showConfigs->dep(), qctx, visited);
+            exec = new ShowConfigsExecutor(showConfigs, qctx);
+            exec->dependsOn(input);
+            break;
+        }
+        case PlanNode::Kind::kSetConfig: {
+            auto setConfig = asNode<SetConfig>(node);
+            auto input = makeExecutor(setConfig->dep(), qctx, visited);
+            exec = new SetConfigExecutor(setConfig, qctx);
+            exec->dependsOn(input);
+            break;
+        }
+        case PlanNode::Kind::kGetConfig: {
+            auto getConfig = asNode<GetConfig>(node);
+            auto input = makeExecutor(getConfig->dep(), qctx, visited);
+            exec = new GetConfigExecutor(getConfig, qctx);
+            exec->dependsOn(input);
+            break;
+        }
         case PlanNode::Kind::kSubmitJob: {
             auto submitJob = asNode<SubmitJob>(node);
             auto input = makeExecutor(submitJob->dep(), qctx, visited);
diff --git a/src/exec/admin/ConfigExecutor.cpp b/src/exec/admin/ConfigExecutor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3bfb4a56435dd3df9ce52ac1bca19e51de115bbb
--- /dev/null
+++ b/src/exec/admin/ConfigExecutor.cpp
@@ -0,0 +1,101 @@
+/* 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 "common/conf/Configuration.h"
+#include "exec/admin/ConfigExecutor.h"
+#include "planner/Admin.h"
+#include "util/SchemaUtil.h"
+#include "context/QueryContext.h"
+#include "util/ScopedTimer.h"
+
+namespace nebula {
+namespace graph {
+
+std::vector<Value> ConfigBaseExecutor::generateColumns(const meta::cpp2::ConfigItem& item) {
+    std::vector<Value> columns;
+    columns.resize(5);
+    auto value = item.get_value();
+    columns[0].setStr(meta::cpp2::_ConfigModule_VALUES_TO_NAMES.at(item.get_module()));
+    columns[1].setStr(item.get_name());
+    columns[2].setStr(value.typeName());
+    columns[3].setStr(meta::cpp2::_ConfigMode_VALUES_TO_NAMES.at(item.get_mode()));
+    columns[4] = std::move(value);
+    return columns;
+}
+
+DataSet ConfigBaseExecutor::generateResult(const std::vector<meta::cpp2::ConfigItem> &items) {
+    DataSet result;
+    result.colNames = {"module", "name", "type", "mode", "value"};
+    for (const auto &item : items) {
+        auto columns = generateColumns(item);
+        result.rows.emplace_back(std::move(columns));
+    }
+    return result;
+}
+
+folly::Future<Status> ShowConfigsExecutor::execute() {
+    SCOPED_TIMER(&execTime_);
+
+    auto *scNode = asNode<ShowConfigs>(node());
+    return qctx()->getMetaClient()->listConfigs(scNode->getModule())
+            .via(runner())
+            .then([this, scNode](StatusOr<std::vector<meta::cpp2::ConfigItem>> resp) {
+                if (!resp.ok()) {
+                    auto module = meta::cpp2::_ConfigModule_VALUES_TO_NAMES.at(scNode->getModule());
+                    LOG(ERROR) << "Show configs `" << module
+                               << "' failed: " << resp.status();
+                    return Status::Error("Show config `%s' failed: %s",
+                                          module,
+                                          resp.status().toString().c_str());
+                }
+
+                auto result = generateResult(resp.value());
+                return finish(ResultBuilder().value(Value(std::move(result))).finish());
+            });
+}
+
+folly::Future<Status> SetConfigExecutor::execute() {
+    SCOPED_TIMER(&execTime_);
+
+    auto *scNode = asNode<SetConfig>(node());
+    return qctx()->getMetaClient()->setConfig(scNode->getModule(),
+                                              scNode->getName(),
+                                              scNode->getValue())
+            .via(runner())
+            .then([scNode](StatusOr<bool> resp) {
+                if (!resp.ok()) {
+                    LOG(ERROR) << "Set config `" << scNode->getName()
+                               << "' failed: " << resp.status();
+                    return Status::Error("Set config `%s' failed: %s",
+                                          scNode->getName().c_str(),
+                                          resp.status().toString().c_str());
+                }
+                return Status::OK();
+            });
+}
+
+folly::Future<Status> GetConfigExecutor::execute() {
+    SCOPED_TIMER(&execTime_);
+
+    auto *gcNode = asNode<GetConfig>(node());
+    return qctx()->getMetaClient()->getConfig(gcNode->getModule(),
+                                              gcNode->getName())
+            .via(runner())
+            .then([this, gcNode](StatusOr<std::vector<meta::cpp2::ConfigItem>> resp) {
+                if (!resp.ok()) {
+                    LOG(ERROR) << "Get config `" << gcNode->getName()
+                               << "' failed: " << resp.status();
+                    return Status::Error("Get config `%s' failed: %s",
+                                          gcNode->getName().c_str(),
+                                          resp.status().toString().c_str());
+                }
+                auto result = generateResult(resp.value());
+                return finish(ResultBuilder().value(Value(std::move(result))).finish());
+            });
+}
+
+}   // namespace graph
+}   // namespace nebula
diff --git a/src/exec/admin/ConfigExecutor.h b/src/exec/admin/ConfigExecutor.h
new file mode 100644
index 0000000000000000000000000000000000000000..3084fdab50478c3593e1e71f0adbf6d8015885a6
--- /dev/null
+++ b/src/exec/admin/ConfigExecutor.h
@@ -0,0 +1,60 @@
+/* Copyright (c) 2020 vesoft inc. All rights reserved.
+ *
+ * This source code is licensed under Apache 2.0 License,
+ * attached with Common Clause Condition 1.0, found in the LICENSES directory.
+ */
+
+#ifndef EXEC_ADMIN_CONFIGEXECUTOR_H_
+#define EXEC_ADMIN_CONFIGEXECUTOR_H_
+
+#include "exec/Executor.h"
+#include "common/interface/gen-cpp2/meta_types.h"
+
+namespace nebula {
+namespace graph {
+
+class ConfigBaseExecutor : public Executor {
+public:
+    ConfigBaseExecutor(const std::string &name, const PlanNode *node, QueryContext *ectx)
+        : Executor(name, node, ectx) {}
+
+protected:
+    std::vector<Value> generateColumns(const meta::cpp2::ConfigItem &item);
+    DataSet generateResult(const std::vector<meta::cpp2::ConfigItem> &items);
+};
+
+class ShowConfigsExecutor final : public ConfigBaseExecutor {
+public:
+    ShowConfigsExecutor(const PlanNode *node, QueryContext *ectx)
+        : ConfigBaseExecutor("ShowConfigsExecutor", node, ectx) {}
+
+    folly::Future<Status> execute() override;
+};
+
+class SetConfigExecutor final : public ConfigBaseExecutor {
+public:
+    SetConfigExecutor(const PlanNode *node, QueryContext *ectx)
+        : ConfigBaseExecutor("SetConfigExecutor", node, ectx) {}
+
+    folly::Future<Status> execute() override;
+
+private:
+    std::string                              name_;
+    Value                                    value_;
+};
+
+class GetConfigExecutor final : public ConfigBaseExecutor {
+public:
+    GetConfigExecutor(const PlanNode *node, QueryContext *ectx)
+        : ConfigBaseExecutor("GetConfigExecutor", node, ectx) {}
+
+    folly::Future<Status> execute() override;
+
+private:
+    std::string                              name_;
+};
+
+}   // namespace graph
+}   // namespace nebula
+
+#endif   // EXEC_ADMIN_CONFIGEXECUTOR_H_
diff --git a/src/parser/AdminSentences.cpp b/src/parser/AdminSentences.cpp
index e772a0d8ee1c404ce45dc257b0cd88f4b3cc34a9..5592f6fe6f966e74822d9042946595c14a0f46f8 100644
--- a/src/parser/AdminSentences.cpp
+++ b/src/parser/AdminSentences.cpp
@@ -104,18 +104,16 @@ std::string ConfigRowItem::toString() const {
     return "";
 }
 
-std::string ConfigSentence::toString() const {
-    switch (subType_) {
-        case SubType::kShow:
-            return std::string("SHOW CONFIGS ") + configItem_->toString();
-        case SubType::kSet:
-            return std::string("SET CONFIGS ") + configItem_->toString();
-        case SubType::kGet:
-            return std::string("GET CONFIGS ") + configItem_->toString();
-        default:
-            FLOG_FATAL("Type illegal");
-    }
-    return "Unknown";
+std::string ShowConfigsSentence::toString() const {
+    return std::string("SHOW CONFIGS ") + configItem_->toString();
+}
+
+std::string SetConfigSentence::toString() const {
+    return std::string("SET CONFIGS ") + configItem_->toString();
+}
+
+std::string GetConfigSentence::toString() const {
+    return std::string("GET CONFIGS ") + configItem_->toString();
 }
 
 std::string BalanceSentence::toString() const {
diff --git a/src/parser/AdminSentences.h b/src/parser/AdminSentences.h
index 0cda54e91594bc0eddd6b5768a62dc83f864168d..268df1c67a971c48eac9d9aca450eed6a62af9d8 100644
--- a/src/parser/AdminSentences.h
+++ b/src/parser/AdminSentences.h
@@ -296,97 +296,91 @@ private:
     std::unique_ptr<std::string>     spaceName_;
 };
 
-enum ConfigModule {
-    ALL, GRAPH, META, STORAGE
-};
-
 class ConfigRowItem {
 public:
-    explicit ConfigRowItem(ConfigModule module) {
-        module_ = std::make_unique<ConfigModule>(module);
+    explicit ConfigRowItem(meta::cpp2::ConfigModule module) {
+        module_ = module;
     }
 
-    ConfigRowItem(ConfigModule module, std::string* name, Expression* value) {
-        module_ = std::make_unique<ConfigModule>(module);
+    ConfigRowItem(meta::cpp2::ConfigModule module, std::string* name, Expression* value) {
+        module_ = module;
         name_.reset(name);
         value_.reset(value);
     }
 
-    ConfigRowItem(ConfigModule module, std::string* name) {
-        module_ = std::make_unique<ConfigModule>(module);
+    ConfigRowItem(meta::cpp2::ConfigModule module, std::string* name) {
+        module_ = module;
         name_.reset(name);
     }
 
-    ConfigRowItem(ConfigModule module, std::string* name, UpdateList *items) {
-        module_ = std::make_unique<ConfigModule>(module);
+    ConfigRowItem(meta::cpp2::ConfigModule module, std::string* name, UpdateList *items) {
+        module_ = module;
         name_.reset(name);
         updateItems_.reset(items);
     }
 
-    const ConfigModule* getModule() {
-        return module_.get();
+    meta::cpp2::ConfigModule getModule() const {
+        return module_;
     }
 
-    const std::string* getName() {
+    const std::string* getName() const {
         return name_.get();
     }
 
-    const Expression* getValue() {
+    Expression* getValue() const {
         return value_.get();
     }
 
-    const UpdateList* getUpdateItems() {
+    const UpdateList* getUpdateItems() const {
         return updateItems_.get();
     }
 
     std::string toString() const;
 
 private:
-    std::unique_ptr<ConfigModule>   module_;
+    meta::cpp2::ConfigModule        module_;
     std::unique_ptr<std::string>    name_;
     std::unique_ptr<Expression>     value_;
     std::unique_ptr<UpdateList>     updateItems_;
 };
 
-class ConfigSentence final : public Sentence {
+class ConfigBaseSentence : public Sentence {
 public:
-    enum class SubType : uint32_t {
-        kUnknown,
-        kShow,
-        kSet,
-        kGet,
-    };
-
-    explicit ConfigSentence(SubType subType) {
-        kind_ = Kind::kConfig;
-        subType_ = std::move(subType);
+    explicit ConfigBaseSentence(Kind kind, ConfigRowItem* item) {
+        kind_ = kind;
+        configItem_.reset(item);
     }
 
-    ConfigSentence(SubType subType, ConfigRowItem* item, bool force = false) {
-        kind_ = Kind::kConfig;
-        subType_ = std::move(subType);
-        configItem_.reset(item);
-        isForce_ = force;
+    ConfigRowItem* configItem() {
+        return configItem_.get();
     }
 
+protected:
+    std::unique_ptr<ConfigRowItem>  configItem_;
+};
+
+class ShowConfigsSentence final : public ConfigBaseSentence {
+public:
+    explicit ShowConfigsSentence(ConfigRowItem* item)
+        : ConfigBaseSentence(Kind::kShowConfigs, item) {}
+
     std::string toString() const override;
+};
 
-    SubType subType() const {
-        return subType_;
-    }
+class SetConfigSentence final : public ConfigBaseSentence {
+public:
+    explicit SetConfigSentence(ConfigRowItem* item)
+        : ConfigBaseSentence(Kind::kSetConfig, item) {}
 
-    ConfigRowItem* configItem() {
-        return configItem_.get();
-    }
+    std::string toString() const override;
+};
 
-    bool isForce() {
-        return isForce_;
-    }
+class GetConfigSentence final : public ConfigBaseSentence {
+public:
+    explicit GetConfigSentence(ConfigRowItem* item)
+        : ConfigBaseSentence(Kind::kGetConfig, item) {}
 
-private:
-    SubType                         subType_{SubType::kUnknown};
-    bool                            isForce_{false};
-    std::unique_ptr<ConfigRowItem>  configItem_;
+    std::string toString() const override;
 };
 
 class HostList final {
diff --git a/src/parser/Sentence.h b/src/parser/Sentence.h
index e91a088954f453e6dca998c95cccc84e0f90f470..a640af1322f38590e24fdf46979d2511d18d7ef5 100644
--- a/src/parser/Sentence.h
+++ b/src/parser/Sentence.h
@@ -91,7 +91,9 @@ public:
         kDownload,
         kIngest,
         kOrderBy,
-        kConfig,
+        kShowConfigs,
+        kSetConfig,
+        kGetConfig,
         kFetchVertices,
         kFetchEdges,
         kBalance,
diff --git a/src/parser/parser.yy b/src/parser/parser.yy
index b90d63814a8d168be6c70a901ac0c0bc0495536b..1b80a7d541693b690288ba158cfcecf974ebfd00 100644
--- a/src/parser/parser.yy
+++ b/src/parser/parser.yy
@@ -89,7 +89,7 @@ static constexpr size_t MAX_ABS_INTEGER = 9223372036854775808ULL;
     nebula::SchemaPropItem                 *alter_schema_prop_item;
     nebula::OrderFactor                    *order_factor;
     nebula::OrderFactors                   *order_factors;
-    nebula::ConfigModule                    config_module;
+    nebula::meta::cpp2::ConfigModule        config_module;
     nebula::ConfigRowItem                  *config_row_item;
     nebula::EdgeKey                        *edge_key;
     nebula::EdgeKeys                       *edge_keys;
@@ -1862,7 +1862,7 @@ show_sentence
         $$ = new ShowRolesSentence($4);
     }
     | KW_SHOW KW_CONFIGS show_config_item {
-        $$ = new ConfigSentence(ConfigSentence::SubType::kShow, $3);
+        $$ = new ShowConfigsSentence($3);
     }
     | KW_SHOW KW_CREATE KW_SPACE name_label {
         $$ = new ShowCreateSpaceSentence($4);
@@ -1897,9 +1897,9 @@ show_sentence
     ;
 
 config_module_enum
-    : KW_GRAPH      { $$ = ConfigModule::GRAPH; }
-    | KW_META       { $$ = ConfigModule::META; }
-    | KW_STORAGE    { $$ = ConfigModule::STORAGE; }
+    : KW_GRAPH      { $$ = meta::cpp2::ConfigModule::GRAPH; }
+    | KW_META       { $$ = meta::cpp2::ConfigModule::META; }
+    | KW_STORAGE    { $$ = meta::cpp2::ConfigModule::STORAGE; }
     ;
 
 get_config_item
@@ -1907,7 +1907,7 @@ get_config_item
         $$ = new ConfigRowItem($1, $3);
     }
     | name_label {
-        $$ = new ConfigRowItem(ConfigModule::ALL, $1);
+        $$ = new ConfigRowItem(meta::cpp2::ConfigModule::ALL, $1);
     }
     ;
 
@@ -1916,13 +1916,13 @@ set_config_item
         $$ = new ConfigRowItem($1, $3, $5);
     }
     | name_label ASSIGN expression {
-        $$ = new ConfigRowItem(ConfigModule::ALL, $1, $3);
+        $$ = new ConfigRowItem(meta::cpp2::ConfigModule::ALL, $1, $3);
     }
     | config_module_enum COLON name_label ASSIGN L_BRACE update_list R_BRACE {
         $$ = new ConfigRowItem($1, $3, $6);
     }
     | name_label ASSIGN L_BRACE update_list R_BRACE {
-        $$ = new ConfigRowItem(ConfigModule::ALL, $1, $4);
+        $$ = new ConfigRowItem(meta::cpp2::ConfigModule::ALL, $1, $4);
     }
     ;
 
@@ -2070,16 +2070,13 @@ revoke_sentence
 
 get_config_sentence
     : KW_GET KW_CONFIGS get_config_item {
-        $$ = new ConfigSentence(ConfigSentence::SubType::kGet, $3);
+        $$ = new GetConfigSentence($3);
     }
     ;
 
 set_config_sentence
     : KW_UPDATE KW_CONFIGS set_config_item {
-        $$ = new ConfigSentence(ConfigSentence::SubType::kSet, $3);
-    }
-    | KW_UPDATE KW_CONFIGS set_config_item KW_FORCE {
-        $$ = new ConfigSentence(ConfigSentence::SubType::kSet, $3, true);
+        $$ = new SetConfigSentence($3);
     }
     ;
 
diff --git a/src/planner/Admin.cpp b/src/planner/Admin.cpp
index 81e2e97170b131ce9dda4a4b90ecb34778af8cdb..98e3e015ab2f433512bbd621f11e5e38221224b3 100644
--- a/src/planner/Admin.cpp
+++ b/src/planner/Admin.cpp
@@ -51,5 +51,26 @@ std::unique_ptr<cpp2::PlanNodeDescription> ShowParts::explain() const {
     addDescription("partIds", folly::toJson(util::toJson(partIds_)), desc.get());
     return desc;
 }
+
+std::unique_ptr<cpp2::PlanNodeDescription> ShowConfigs::explain() const {
+    auto desc = SingleInputNode::explain();
+    addDescription("module", meta::cpp2::_ConfigModule_VALUES_TO_NAMES.at(module_), desc.get());
+    return desc;
+}
+
+std::unique_ptr<cpp2::PlanNodeDescription> SetConfig::explain() const {
+    auto desc = SingleInputNode::explain();
+    addDescription("module", meta::cpp2::_ConfigModule_VALUES_TO_NAMES.at(module_), desc.get());
+    addDescription("name", name_, desc.get());
+    addDescription("value", value_.toString(), desc.get());
+    return desc;
+}
+
+std::unique_ptr<cpp2::PlanNodeDescription> GetConfig::explain() const {
+    auto desc = SingleInputNode::explain();
+    addDescription("module", meta::cpp2::_ConfigModule_VALUES_TO_NAMES.at(module_), desc.get());
+    addDescription("name", name_, desc.get());
+    return desc;
+}
 }   // namespace graph
 }   // namespace nebula
diff --git a/src/planner/Admin.h b/src/planner/Admin.h
index a745e60d0115e8cb5db1161440777c065064f77b..4e45230af6af1e2aa76b4cfa4559a7acec86afae 100644
--- a/src/planner/Admin.h
+++ b/src/planner/Admin.h
@@ -141,11 +141,106 @@ public:
 
 private:
     explicit ShowSpaces(ExecutionPlan* plan, PlanNode* input)
-            : SingleInputNode(plan, Kind::kShowSpaces, input) {}
+        : SingleInputNode(plan, Kind::kShowSpaces, input) {}
 };
 
-class Config final : public SingleInputNode {
+class ShowConfigs final : public SingleInputNode {
 public:
+    static ShowConfigs* make(ExecutionPlan* plan,
+                             PlanNode* input,
+                             meta::cpp2::ConfigModule module) {
+        return new ShowConfigs(plan, input, module);
+    }
+
+    std::unique_ptr<cpp2::PlanNodeDescription> explain() const override;
+
+    meta::cpp2::ConfigModule getModule() const {
+        return module_;
+    }
+
+private:
+    ShowConfigs(ExecutionPlan* plan,
+                PlanNode* input,
+                meta::cpp2::ConfigModule module)
+        : SingleInputNode(plan, Kind::kShowConfigs, input)
+        , module_(module) {}
+
+private:
+    meta::cpp2::ConfigModule    module_;
+};
+
+class SetConfig final : public SingleInputNode {
+public:
+    static SetConfig* make(ExecutionPlan* plan,
+                           PlanNode* input,
+                           meta::cpp2::ConfigModule module,
+                           std::string name,
+                           Value value) {
+        return new SetConfig(plan, input, module, std::move(name), std::move(value));
+    }
+
+    std::unique_ptr<cpp2::PlanNodeDescription> explain() const override;
+
+    meta::cpp2::ConfigModule getModule() const {
+        return module_;
+    }
+
+    const std::string& getName() const {
+        return name_;
+    }
+
+    const Value& getValue() const {
+        return value_;
+    }
+
+private:
+    SetConfig(ExecutionPlan* plan,
+              PlanNode* input,
+              meta::cpp2::ConfigModule module,
+              std::string name,
+              Value value)
+        : SingleInputNode(plan, Kind::kSetConfig, input)
+        , module_(module)
+        , name_(std::move(name))
+        , value_(std::move(value)) {}
+
+private:
+    meta::cpp2::ConfigModule       module_;
+    std::string                    name_;
+    Value                          value_;
+};
+
+class GetConfig final : public SingleInputNode {
+public:
+    static GetConfig* make(ExecutionPlan* plan,
+                           PlanNode* input,
+                           meta::cpp2::ConfigModule module,
+                           std::string name) {
+        return new GetConfig(plan, input, module, std::move(name));
+    }
+
+    std::unique_ptr<cpp2::PlanNodeDescription> explain() const override;
+
+    meta::cpp2::ConfigModule getModule() const {
+        return module_;
+    }
+
+    const std::string& getName() const {
+        return name_;
+    }
+
+private:
+    explicit GetConfig(ExecutionPlan* plan,
+                       PlanNode* input,
+                       meta::cpp2::ConfigModule module,
+                       std::string name)
+        : SingleInputNode(plan, Kind::kGetConfig, input)
+        , module_(module)
+        , name_(std::move(name)) {}
+
+private:
+    meta::cpp2::ConfigModule       module_;
+    std::string                    name_;
 };
 
 class ShowCreateSpace final : public SingleInputNode {
@@ -336,3 +431,4 @@ private:
 }  // namespace graph
 }  // namespace nebula
 #endif  // PLANNER_ADMIN_H_
+
diff --git a/src/planner/PlanNode.cpp b/src/planner/PlanNode.cpp
index b16e2000aba4f1ff39a1af42796f7367226928c3..e354fa5d0c7e891b12c54b5c960c8efa97a11389 100644
--- a/src/planner/PlanNode.cpp
+++ b/src/planner/PlanNode.cpp
@@ -125,6 +125,12 @@ const char* PlanNode::toString(PlanNode::Kind kind) {
             return "ShowCharset";
         case Kind::kShowCollation:
             return "ShowCollation";
+        case Kind::kShowConfigs:
+            return "ShowConfigs";
+        case Kind::kSetConfig:
+            return "SetConfig";
+        case Kind::kGetConfig:
+            return "GetConfig";
         // no default so the compiler will warning when lack
     }
     LOG(FATAL) << "Impossible kind plan node " << static_cast<int>(kind);
diff --git a/src/planner/PlanNode.h b/src/planner/PlanNode.h
index bd9cc35c9475af761497a21bbae8fc8f4bf80e42..1baa6935ca1e616a2442917e6bfd8b54b004ea83 100644
--- a/src/planner/PlanNode.h
+++ b/src/planner/PlanNode.h
@@ -79,6 +79,9 @@ public:
         kShowParts,
         kShowCharset,
         kShowCollation,
+        kShowConfigs,
+        kSetConfig,
+        kGetConfig,
     };
 
     PlanNode(ExecutionPlan* plan, Kind kind);
diff --git a/src/planner/test/CMakeLists.txt b/src/planner/test/CMakeLists.txt
index 3c639639a3a6060d679f779c9786bed3422c1a87..455c036caeafdaae3fff7b1f4d45689321be0878 100644
--- a/src/planner/test/CMakeLists.txt
+++ b/src/planner/test/CMakeLists.txt
@@ -9,6 +9,7 @@ nebula_add_test(
         ExecutionPlanTest.cpp
     OBJECTS
         $<TARGET_OBJECTS:common_time_function_obj>
+        $<TARGET_OBJECTS:common_conf_obj>
         $<TARGET_OBJECTS:common_expression_obj>
         $<TARGET_OBJECTS:common_http_client_obj>
         $<TARGET_OBJECTS:common_network_obj>
diff --git a/src/service/PermissionCheck.cpp b/src/service/PermissionCheck.cpp
index 11500ef3c5b09ce2f918e10a85b77ce34dcd5610..4f0476628a4051d28bd3953c4ea7f3e75fc87dfb 100644
--- a/src/service/PermissionCheck.cpp
+++ b/src/service/PermissionCheck.cpp
@@ -57,8 +57,10 @@ bool PermissionCheck::permissionCheck(Session *session,
         case Sentence::Kind::kDropSnapshot :
         case Sentence::Kind::kBalance :
         case Sentence::Kind::kAdminJob :
+        case Sentence::Kind::kShowConfigs :
+        case Sentence::Kind::kSetConfig :
+        case Sentence::Kind::kGetConfig :
         case Sentence::Kind::kIngest :
-        case Sentence::Kind::kConfig :
         case Sentence::Kind::kDownload : {
             return PermissionManager::canWriteSpace(session);
         }
diff --git a/src/validator/AdminValidator.cpp b/src/validator/AdminValidator.cpp
index 6ccb1f9475a475bad602f59266f592c4cdf4a86d..7e4e4a0d8a1174ed55037145318e214c502da1f8 100644
--- a/src/validator/AdminValidator.cpp
+++ b/src/validator/AdminValidator.cpp
@@ -256,5 +256,108 @@ Status ShowCollationValidator::toPlan() {
     tail_ = root_;
     return Status::OK();
 }
+
+Status ShowConfigsValidator::validateImpl() {
+    return Status::OK();
+}
+
+Status ShowConfigsValidator::toPlan() {
+    auto sentence = static_cast<ShowConfigsSentence*>(sentence_);
+    meta::cpp2::ConfigModule module;
+    auto item = sentence->configItem();
+    if (item != nullptr) {
+        module = item->getModule();
+    } else {
+        module = meta::cpp2::ConfigModule::ALL;
+    }
+    auto* plan = qctx_->plan();
+    auto *doNode = ShowConfigs::make(plan, nullptr, module);
+    root_ = doNode;
+    tail_ = root_;
+    return Status::OK();
+}
+
+Status SetConfigValidator::validateImpl() {
+    auto sentence = static_cast<SetConfigSentence*>(sentence_);
+    auto item = sentence->configItem();
+    if (item == nullptr) {
+        return Status::Error("Empty config item");
+    }
+    if (item->getName() != nullptr) {
+        name_ = *item->getName();
+    }
+    name_ = *item->getName();
+    module_ = item->getModule();
+    auto updateItems = item->getUpdateItems();
+    QueryExpressionContext ctx(nullptr, nullptr);
+    if (updateItems == nullptr) {
+        module_ = item->getModule();
+        if (item->getName() != nullptr) {
+            name_ = *item->getName();
+        }
+
+        if (item->getValue() != nullptr) {
+            value_ = Expression::eval(item->getValue(), ctx);
+        }
+    } else {
+        Map configs;
+        for (auto &updateItem : updateItems->items()) {
+            std::string name;
+            Value value;
+            if (updateItem->getFieldName() == nullptr || updateItem->value() == nullptr) {
+                return Status::Error("Empty item");
+            }
+            name = *updateItem->getFieldName();
+
+            value = Expression::eval(const_cast<Expression*>(updateItem->value()), ctx);
+
+            if (value.isNull() || (!value.isNumeric() && !value.isStr() && !value.isBool())) {
+                return Status::Error("Wrong value: %s", name.c_str());
+            }
+            configs.kvs.emplace(std::move(name), std::move(value));
+        }
+        value_.setMap(std::move(configs));
+    }
+
+    return Status::OK();
+}
+
+Status SetConfigValidator::toPlan() {
+    auto* plan = qctx_->plan();
+    auto *doNode = SetConfig::make(plan,
+                                   nullptr,
+                                   module_,
+                                   std::move(name_),
+                                   std::move(value_));
+    root_ = doNode;
+    tail_ = root_;
+    return Status::OK();
+}
+
+Status GetConfigValidator::validateImpl() {
+    auto sentence = static_cast<GetConfigSentence*>(sentence_);
+    auto item = sentence->configItem();
+    if (item == nullptr) {
+        return Status::Error("Empty config item");
+    }
+
+    module_ = item->getModule();
+    if (item->getName() != nullptr) {
+        name_ = *item->getName();
+    }
+    name_ = *item->getName();
+    return Status::OK();
+}
+
+Status GetConfigValidator::toPlan() {
+    auto* plan = qctx_->plan();
+    auto *doNode = GetConfig::make(plan,
+                                   nullptr,
+                                   module_,
+                                   std::move(name_));
+    root_ = doNode;
+    tail_ = root_;
+    return Status::OK();
+}
 }  // namespace graph
 }  // namespace nebula
diff --git a/src/validator/AdminValidator.h b/src/validator/AdminValidator.h
index 1a29a95982d69a9847bc071212995019f3ad197f..f1b20f647141eaded0aa3fa7973b294c16444158 100644
--- a/src/validator/AdminValidator.h
+++ b/src/validator/AdminValidator.h
@@ -173,6 +173,54 @@ private:
 
     Status toPlan() override;
 };
+
+class ShowConfigsValidator final : public Validator {
+public:
+    ShowConfigsValidator(Sentence* sentence, QueryContext* context)
+        : Validator(sentence, context) {
+        setNoSpaceRequired();
+    }
+
+private:
+    Status validateImpl() override;
+
+    Status toPlan() override;
+};
+
+class SetConfigValidator final : public Validator {
+public:
+    SetConfigValidator(Sentence* sentence, QueryContext* context)
+        : Validator(sentence, context) {
+        setNoSpaceRequired();
+    }
+
+private:
+    Status validateImpl() override;
+
+    Status toPlan() override;
+
+private:
+    meta::cpp2::ConfigModule                 module_;
+    std::string                              name_;
+    Value                                    value_;
+};
+
+class GetConfigValidator final : public Validator {
+public:
+    GetConfigValidator(Sentence* sentence, QueryContext* context)
+        : Validator(sentence, context) {
+        setNoSpaceRequired();
+    }
+
+private:
+    Status validateImpl() override;
+
+    Status toPlan() override;
+
+private:
+    meta::cpp2::ConfigModule                 module_;
+    std::string                              name_;
+};
 }  // namespace graph
 }  // namespace nebula
 #endif  // VALIDATOR_ADMINVALIDATOR_H_
diff --git a/src/validator/ReportError.h b/src/validator/ReportError.h
index fba7a439561a919dab457e79b7b506b07be2a5ea..6d8d91e3206a909c8d8634e45191b985a346ae82 100644
--- a/src/validator/ReportError.h
+++ b/src/validator/ReportError.h
@@ -14,7 +14,9 @@ namespace graph {
 class ReportError final : public Validator {
 public:
     ReportError(Sentence* sentence, QueryContext* context)
-        : Validator(sentence, context) {}
+        : Validator(sentence, context) {
+        setNoSpaceRequired();
+    }
 
 private:
     Status validateImpl() override {
diff --git a/src/validator/Validator.cpp b/src/validator/Validator.cpp
index 428507b0386073113bedbad625735c96a1989301..195285a812d09a16a72abf49caa770fea49c4282 100644
--- a/src/validator/Validator.cpp
+++ b/src/validator/Validator.cpp
@@ -132,6 +132,12 @@ std::unique_ptr<Validator> Validator::makeValidator(Sentence* sentence, QueryCon
             return std::make_unique<ShowCharsetValidator>(sentence, context);
         case Sentence::Kind::kShowCollation:
             return std::make_unique<ShowCollationValidator>(sentence, context);
+        case Sentence::Kind::kGetConfig:
+            return std::make_unique<GetConfigValidator>(sentence, context);
+        case Sentence::Kind::kSetConfig:
+            return std::make_unique<SetConfigValidator>(sentence, context);
+        case Sentence::Kind::kShowConfigs:
+            return std::make_unique<ShowConfigsValidator>(sentence, context);
         case Sentence::Kind::kUnknown:
         case Sentence::Kind::kMatch:
         case Sentence::Kind::kCreateTagIndex:
@@ -160,7 +166,6 @@ std::unique_ptr<Validator> Validator::makeValidator(Sentence* sentence, QueryCon
         case Sentence::Kind::kDownload:
         case Sentence::Kind::kIngest:
         case Sentence::Kind::kBalance:
-        case Sentence::Kind::kConfig:
         case Sentence::Kind::kFindPath:
         case Sentence::Kind::kReturn: {
             // nothing
@@ -215,7 +220,10 @@ Status Validator::appendPlan(PlanNode* node, PlanNode* appended) {
         case PlanNode::Kind::kUpdateEdge:
         case PlanNode::Kind::kShowParts:
         case PlanNode::Kind::kShowCharset:
-        case PlanNode::Kind::kShowCollation: {
+        case PlanNode::Kind::kShowCollation:
+        case PlanNode::Kind::kShowConfigs:
+        case PlanNode::Kind::kSetConfig:
+        case PlanNode::Kind::kGetConfig: {
             static_cast<SingleDependencyNode*>(node)->dependsOn(appended);
             break;
         }
diff --git a/tests/admin/test_configs.py b/tests/admin/test_configs.py
new file mode 100644
index 0000000000000000000000000000000000000000..b0db1132c5b683990856e2278e62db630634dbb7
--- /dev/null
+++ b/tests/admin/test_configs.py
@@ -0,0 +1,122 @@
+# --coding:utf-8--
+#
+# Copyright (c) 2020 vesoft inc. All rights reserved.
+#
+# This source code is licensed under Apache 2.0 License,
+# attached with Common Clause Condition 1.0, found in the LICENSES directory.
+
+import time
+import re
+
+from tests.common.nebula_test_suite import NebulaTestSuite
+from nebula2.common import ttypes
+
+class TestConfigs(NebulaTestSuite):
+
+    @classmethod
+    def prepare(self):
+        pass
+
+    @classmethod
+    def cleanup(self):
+        pass
+
+    def test_configs(self):
+        # set/get without declaration
+        resp = self.client.execute('UPDATE CONFIGS storage:notRegistered=123;')
+        self.check_resp_failed(resp)
+
+        # update immutable config will fail, read-only
+        resp = self.client.execute_query('UPDATE CONFIGS storage:num_io_threads=10')
+        self.check_resp_failed(resp)
+
+        # set and get config after declaration
+        v = 3
+        resp = self.client.execute('UPDATE CONFIGS meta:v={}'.format(3))
+        self.check_resp_failed(resp)
+        resp = self.client.execute('UPDATE CONFIGS graph:v={}'.format(3))
+        self.check_resp_succeeded(resp)
+        resp = self.client.execute('UPDATE CONFIGS storage:v={}'.format(3))
+        self.check_resp_succeeded(resp)
+
+        # get
+        resp = self.client.execute('GET CONFIGS meta:v')
+        self.check_resp_failed(resp)
+
+        resp = self.client.execute_query('GET CONFIGS graph:v')
+        self.check_resp_succeeded(resp)
+        expected_result = [['GRAPH', 'v', 'int', 'MUTABLE', 3]]
+        self.check_result(resp, expected_result)
+
+        resp = self.client.execute_query('GET CONFIGS storage:v')
+        self.check_resp_succeeded(resp)
+        expected_result = [['STORAGE', 'v', 'int', 'MUTABLE', 3]]
+        self.check_result(resp, expected_result)
+
+        # show configs
+        resp = self.client.execute_query('SHOW CONFIGS meta')
+        self.check_resp_succeeded(resp)
+
+        resp = self.client.execute_query('SHOW CONFIGS graph')
+        self.check_resp_succeeded(resp)
+        expected_result = [['GRAPH', 'v', 'int', 'MUTABLE', v],
+                           ['GRAPH', 'minloglevel', 'int', 'MUTABLE', 0],
+                           ['GRAPH', 'slow_op_threshhold_ms', 'int', 'MUTABLE', 50],
+                           ['GRAPH', 'heartbeat_interval_secs', 'int', 'MUTABLE', 1],
+                           ['GRAPH', 'meta_client_retry_times', 'int', 'MUTABLE', 3]]
+        self.check_out_of_order_result(resp, expected_result)
+
+        resp = self.client.execute_query('SHOW CONFIGS storage')
+        self.check_resp_succeeded(resp)
+        expected_result = [['STORAGE', 'v', 'int', 'MUTABLE', 3],
+                           ['STORAGE', 'wal_ttl', 'int', 'MUTABLE', 14400],
+                           ['STORAGE', 'minloglevel', 'int', 'MUTABLE', 0],
+                           ['STORAGE', 'enable_reservoir_sampling', 'bool', 'MUTABLE', False],
+                           ['STORAGE', 'custom_filter_interval_secs', 'int', 'MUTABLE', 86400],
+                           ['STORAGE', 'slow_op_threshhold_ms', 'int', 'MUTABLE', 50],
+                           ['STORAGE', 'heartbeat_interval_secs', 'int', 'MUTABLE', 1],
+                           ['STORAGE', 'meta_client_retry_times', 'int', 'MUTABLE', 3],
+                           ['STORAGE', 'rocksdb_db_options', 'map', 'MUTABLE', {}],
+                           ['STORAGE', 'max_edge_returned_per_vertex', 'int', 'MUTABLE', 2147483647],
+                           ['STORAGE', 'rocksdb_column_family_options', 'map', 'MUTABLE',
+                            {"write_buffer_size":"67108864","max_bytes_for_level_base":"268435456","max_write_buffer_number":"4"}],
+                           ['STORAGE', 'rocksdb_block_based_table_options', 'map', 'MUTABLE', {"block_size":"8192"}]]
+        self.check_out_of_order_result(resp, expected_result)
+
+        # set and get a config of all module
+        resp = self.client.execute('UPDATE CONFIGS minloglevel={}'.format(2))
+        self.check_resp_succeeded(resp)
+
+        # check result
+        resp = self.client.execute_query('GET CONFIGS minloglevel')
+        self.check_resp_succeeded(resp)
+        expected_result = [['GRAPH', 'minloglevel', 'int', 'MUTABLE', 2],
+                           ['STORAGE', 'minloglevel', 'int', 'MUTABLE', 2]]
+        self.check_out_of_order_result(resp, expected_result)
+
+        # update storage
+        resp = self.client.execute('UPDATE CONFIGS storage:minloglevel={}'.format(3))
+        self.check_resp_succeeded(resp)
+
+        # get result
+        resp = self.client.execute_query('GET CONFIGS minloglevel')
+        self.check_resp_succeeded(resp)
+        expected_result = [['GRAPH', 'minloglevel', 'int', 'MUTABLE', 2],
+                           ['STORAGE', 'minloglevel', 'int', 'MUTABLE', 3]]
+        self.check_result(resp, expected_result)
+
+        # update rocksdb
+        resp = self.client.execute('''
+                                   UPDATE CONFIGS storage:rocksdb_column_family_options={
+                                   max_bytes_for_level_base=1024,
+                                   write_buffer_size=1024,
+                                   max_write_buffer_number=4}
+                                   ''')
+        self.check_resp_succeeded(resp)
+
+        # get result
+        resp = self.client.execute_query('GET CONFIGS storage:rocksdb_column_family_options')
+        self.check_resp_succeeded(resp)
+        value = {"max_bytes_for_level_base": 1024, "write_buffer_size": 1024, "max_write_buffer_number": 4}
+        expected_result = [['STORAGE', 'rocksdb_column_family_options', 'map', 'MUTABLE', value]]
+        self.check_result(resp, expected_result)
diff --git a/tests/common/nebula_service.py b/tests/common/nebula_service.py
index 0fd1511663871ae4981348f33e5b16826bd9087c..32e99eea0df0474d8ec0331d9f8146e78c40149a 100644
--- a/tests/common/nebula_service.py
+++ b/tests/common/nebula_service.py
@@ -48,6 +48,11 @@ class NebulaService(object):
         shutil.copy(storage_conf_path + '/nebula-metad.conf.default',
                     self.work_dir + '/conf/nebula-metad.conf')
 
+        # gflags.json
+        resources_dir = self.work_dir + '/share/resources/'
+        os.makedirs(resources_dir)
+        shutil.copy(self.build_dir + '/../resources/gflags.json', resources_dir)
+
     def _format_nebula_command(self, name, meta_port, ports, debug_log = True):
         param_format = "--meta_server_addrs={} --port={} --ws_http_port={} --ws_h2_port={} --heartbeat_interval_secs=1"
         param = param_format.format("127.0.0.1:" + str(meta_port), ports[0],
diff --git a/tests/common/nebula_test_suite.py b/tests/common/nebula_test_suite.py
index c8a6b3421a5b264fdf9ec28ffd4b4e6f55777724..4463240d52db3062f5708f6a2ebf1de0eeb70564 100644
--- a/tests/common/nebula_test_suite.py
+++ b/tests/common/nebula_test_suite.py
@@ -219,26 +219,61 @@ class NebulaTestSuite(object):
         else:
             return False, msg
 
+    @classmethod
+    def date_to_string(self, date):
+        return '{}/{}/{}'.format(date.year, date.month, date.day)
+
+    @classmethod
+    def date_time_to_string(self, date_time):
+        zone = '+'
+        if date_time.timezone < 0:
+            zone = '-'
+        return '{}/{}/{} {}:{}:{}.{} {}{}.{}'.format(date_time.year,
+                                                     date_time.month,
+                                                     date_time.day,
+                                                     date_time.hour,
+                                                     date_time.minute,
+                                                     date_time.sec,
+                                                     date_time.microsec,
+                                                     zone,
+                                                     date_time.timezone / 3600,
+                                                     date_time.timezone % 3600)
+    @classmethod
+    def map_to_string(self, map):
+        kvStrs = []
+        if map.kvs is not None:
+            for key in map.kvs:
+                kvStrs.append('"{}":"{}"'.format(key.decode('utf-8'), self.value_to_string(map.kvs[key])))
+            return '{' + ','.join(kvStrs) + '}'
+        return ''
+
+    @classmethod
+    def value_to_string(self, value):
+        if value.getType() == CommonTtypes.Value.__EMPTY__:
+            return '__EMPTY__'
+        elif value.getType() == CommonTtypes.Value.NVAL:
+            return '__NULL__'
+        elif value.getType() == CommonTtypes.Value.BVAL:
+            return str(value.get_bVal())
+        elif value.getType() == CommonTtypes.Value.IVAL:
+            return str(value.get_iVal())
+        elif value.getType() == CommonTtypes.Value.FVAL:
+            return str(value.get_fVal())
+        elif value.getType() == CommonTtypes.Value.SVAL:
+            return value.get_sVal().decode('utf-8')
+        elif value.getType() == CommonTtypes.Value.DVAL:
+            return self.date_time_to_string(value.get_dVal())
+        elif value.getType() == CommonTtypes.Value.TVAL:
+            return self.date_time_to_string(value.get_tVal())
+        elif value.getType() == CommonTtypes.Value.MVAL:
+            return self.map_to_string(value.get_mVal())
+        return 'Unsupported type'
+
     @classmethod
     def row_to_string(self, row):
         value_list = []
         for col in row.values:
-            if col.getType() == CommonTtypes.Value.__EMPTY__:
-                value_list.append('__EMPTY__')
-            elif col.getType() == CommonTtypes.Value.NVAL:
-                value_list.append('__NULL__')
-            elif col.getType() == CommonTtypes.Value.BVAL:
-                value_list.append(col.get_bVal())
-            elif col.getType() == CommonTtypes.Value.IVAL:
-                value_list.append(col.get_iVal())
-            elif col.getType() == CommonTtypes.Value.FVAL:
-                value_list.append(col.get_fVal())
-            elif col.getType() == CommonTtypes.Value.SVAL:
-                value_list.append(col.get_sVal().decode('utf-8'))
-            elif col.getType() == CommonTtypes.Type.DVAL:
-                value_list.append(col.get_dVal().decode('utf-8'))
-            elif col.getType() == CommonTtypes.Type.DATETIME:
-                value_list.append(col.get_datetime())
+            value_list.append(self.value_to_string(col))
         return str(value_list)
 
     @classmethod
@@ -304,6 +339,30 @@ class NebulaTestSuite(object):
             ok = (expect[i] == result)
             assert ok, "different column name, expect: {} vs. result: {}".format(expect[i], result)
 
+    @classmethod
+    def to_value(self, col):
+        value = CommonTtypes.Value()
+        if isinstance(col, bool):
+            value.set_bVal(col)
+        elif isinstance(col, int):
+            value.set_iVal(col)
+        elif isinstance(col, float):
+            value.set_fVal(col)
+        elif isinstance(col, str):
+            value.set_sVal(col.encode('utf-8'))
+        elif isinstance(col, dict):
+            map_val = CommonTtypes.Map()
+            map_val.kvs = dict()
+            for key in col:
+                ok, temp = self.to_value(col[key])
+                if not ok:
+                    return ok, temp
+                map_val.kvs[key.encode('utf-8')] = temp
+            value.set_mVal(map_val)
+        else:
+            return False, 'Wrong val type'
+        return True, value
+
     @classmethod
     def convert_expect(self, expect):
         result = []
@@ -316,15 +375,9 @@ class NebulaTestSuite(object):
                 if isinstance(col, CommonTtypes.Value):
                     new_row.values.append(col)
                 else:
-                    value = CommonTtypes.Value()
-                    if isinstance(col, bool):
-                        value.set_bVal(col)
-                    elif isinstance(col, int):
-                        value.set_iVal(col)
-                    elif isinstance(col, float):
-                        value.set_fVal(col)
-                    elif isinstance(col, str):
-                        value.set_sVal(col.encode('utf-8'))
+                    ok, value = self.to_value(col)
+                    if not ok:
+                        return ok, value
                     new_row.values.append(value)
             result.append(new_row)
         return True, result, ''
diff --git a/tests/nebula-test-run.py b/tests/nebula-test-run.py
index a8e83a05f3efa94740e9739a5e65b7ad4f680475..3fa5770a3f6331f6d5d865a3676d4c72d8517b35 100755
--- a/tests/nebula-test-run.py
+++ b/tests/nebula-test-run.py
@@ -61,7 +61,6 @@ class TestExecutor(object):
         #self.total_executed += len(plugin.tests_executed)
         return error_code
 
-
 if __name__ == "__main__":
     # If the user is just asking for --help, just print the help test and then exit.
     executor = TestExecutor()
diff --git a/tests/query/stateless/test_admin.py b/tests/query/stateless/test_admin.py
index 28834b56bed5ee6eab55a2f47514a3b2d3ae494b..f6d9c2d3fe6c085a65e94656067497132727598b 100644
--- a/tests/query/stateless/test_admin.py
+++ b/tests/query/stateless/test_admin.py
@@ -40,10 +40,10 @@ class TestAdmin(NebulaTestSuite):
         # Backup
         resp = self.client.execute_query('GET CONFIGS graph:minloglevel')
         self.check_resp_succeeded(resp)
-        graph_minloglevel = resp.rows[0].columns[4].get_str()
+        graph_minloglevel = resp.data.rows[0].values[4].get_iVal()
         resp = self.client.execute_query('GET CONFIGS storage:minloglevel')
         self.check_resp_succeeded(resp)
-        storage_minloglevel = resp.rows[0].columns[4].get_str()
+        storage_minloglevel = resp.data.rows[0].values[4].get_iVal()
 
         # Set
         minloglevel = 3
@@ -57,14 +57,14 @@ class TestAdmin(NebulaTestSuite):
         # get
         resp = self.client.execute('GET CONFIGS meta:minloglevel')
         self.check_resp_failed(resp)
-        result = [['GRAPH', 'minloglevel', 'INT64', 'MUTABLE', str(minloglevel)]]
+        result = [['GRAPH', 'minloglevel', 'int', 'MUTABLE', minloglevel]]
         resp = self.client.execute_query('GET CONFIGS graph:minloglevel')
         self.check_resp_succeeded(resp)
-        self.check_result(resp.rows, result)
-        result = [['STORAGE', 'minloglevel', 'INT64', 'MUTABLE', str(minloglevel)]]
+        self.check_result(resp, result)
+        result = [['STORAGE', 'minloglevel', 'int', 'MUTABLE', minloglevel]]
         resp = self.client.execute_query('GET CONFIGS storage:minloglevel')
         self.check_resp_succeeded(resp)
-        self.check_result(resp.rows, result)
+        self.check_result(resp, result)
 
         # rollback
         resp = self.client.execute('UPDATE CONFIGS graph:minloglevel={}'.format(int(graph_minloglevel)))