From dbe9c84ba2ec6bd1aa5f4e7a1fa6fb6840780e23 Mon Sep 17 00:00:00 2001
From: Shylock Hg <33566796+Shylock-Hg@users.noreply.github.com>
Date: Wed, 11 Nov 2020 12:36:19 +0800
Subject: [PATCH] Tests/datetime curd (#371)

* Initialize the timezone configuration.

* Log the error when initialize failed.

* Add tests about datetime type crud.

* Add newline.

* Add reserved keyword time test cases.

* Add wrong value insert.

* Support time type.

* Format the query string.

* Add datetime keyword test cases.

Co-authored-by: cpw <13495049+CPWstatic@users.noreply.github.com>
Co-authored-by: dutor <440396+dutor@users.noreply.github.com>
---
 .linters/cpp/checkKeyword.py           |   1 +
 src/parser/parser.yy                   |  15 ++-
 src/parser/scanner.lex                 |   1 +
 src/parser/test/ParserTest.cpp         |  56 ++++++-----
 src/parser/test/ScannerTest.cpp        |   6 ++
 src/util/SchemaUtil.cpp                |  23 +++--
 tests/common/nebula_test_suite.py      |   6 ++
 tests/maintain/test_tag_edge.py        |  26 ++---
 tests/query/v2/test_basic_type_crud.py | 133 +++++++++++++++++++++++++
 9 files changed, 217 insertions(+), 50 deletions(-)
 create mode 100644 tests/query/v2/test_basic_type_crud.py

diff --git a/.linters/cpp/checkKeyword.py b/.linters/cpp/checkKeyword.py
index 3ee52371..deba075b 100755
--- a/.linters/cpp/checkKeyword.py
+++ b/.linters/cpp/checkKeyword.py
@@ -58,6 +58,7 @@ reserved_key_words = [
     'KW_FIXED_STRING',
     'KW_TIMESTAMP',
     'KW_DATE',
+    'KW_TIME',
     'KW_DATETIME',
     'KW_VID_SIZE',
     'KW_TAG',
diff --git a/src/parser/parser.yy b/src/parser/parser.yy
index 9433e614..b588943c 100644
--- a/src/parser/parser.yy
+++ b/src/parser/parser.yy
@@ -128,7 +128,7 @@ static constexpr size_t MAX_ABS_INTEGER = 9223372036854775808ULL;
 
 /* keywords */
 %token KW_BOOL KW_INT8 KW_INT16 KW_INT32 KW_INT64 KW_INT KW_FLOAT KW_DOUBLE
-%token KW_STRING KW_FIXED_STRING KW_TIMESTAMP KW_DATE KW_DATETIME
+%token KW_STRING KW_FIXED_STRING KW_TIMESTAMP KW_DATE KW_TIME KW_DATETIME
 %token KW_GO KW_AS KW_TO KW_USE KW_SET KW_FROM KW_WHERE KW_ALTER
 %token KW_MATCH KW_INSERT KW_VALUES KW_YIELD KW_RETURN KW_CREATE KW_VERTEX
 %token KW_EDGE KW_EDGES KW_STEPS KW_OVER KW_UPTO KW_REVERSELY KW_SPACE KW_DELETE KW_FIND
@@ -708,6 +708,15 @@ function_call_expression
     : LABEL L_PAREN opt_argument_list R_PAREN {
         $$ = new FunctionCallExpression($1, $3);
     }
+    | KW_DATE L_PAREN opt_argument_list R_PAREN {
+        $$ = new FunctionCallExpression(new std::string("date"), $3);
+    }
+    | KW_TIME L_PAREN opt_argument_list R_PAREN {
+        $$ = new FunctionCallExpression(new std::string("time"), $3);
+    }
+    | KW_DATETIME L_PAREN opt_argument_list R_PAREN {
+        $$ = new FunctionCallExpression(new std::string("datetime"), $3);
+    }
     ;
 
 uuid_expression
@@ -793,6 +802,10 @@ type_spec
         $$ = new meta::cpp2::ColumnTypeDef();
         $$->set_type(meta::cpp2::PropertyType::DATE);
     }
+    | KW_TIME {
+        $$ = new meta::cpp2::ColumnTypeDef();
+        $$->set_type(meta::cpp2::PropertyType::TIME);
+    }
     | KW_DATETIME {
         $$ = new meta::cpp2::ColumnTypeDef();
         $$->set_type(meta::cpp2::PropertyType::DATETIME);
diff --git a/src/parser/scanner.lex b/src/parser/scanner.lex
index 5fc13a99..7dfb3197 100644
--- a/src/parser/scanner.lex
+++ b/src/parser/scanner.lex
@@ -88,6 +88,7 @@ IP_OCTET                    ([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])
 "FIXED_STRING"              { return TokenType::KW_FIXED_STRING; }
 "TIMESTAMP"                 { return TokenType::KW_TIMESTAMP; }
 "DATE"                      { return TokenType::KW_DATE; }
+"TIME"                      { return TokenType::KW_TIME; }
 "DATETIME"                  { return TokenType::KW_DATETIME; }
 "TAG"                       { return TokenType::KW_TAG; }
 "TAGS"                      { return TokenType::KW_TAGS; }
diff --git a/src/parser/test/ParserTest.cpp b/src/parser/test/ParserTest.cpp
index e8e3e72b..ffb22ab1 100644
--- a/src/parser/test/ParserTest.cpp
+++ b/src/parser/test/ParserTest.cpp
@@ -812,7 +812,7 @@ TEST(Parser, UpdateVertex) {
                             "WHEN $^.job.salary > 10000 AND $^.job.name == \"CTO\" OR "
                             "$^.person.age < 30"
                             "YIELD $^.person.name AS Name, $^.job.salary AS Salary, "
-                            "$^.person.create_time AS Time";
+                            "$^.person.create_time AS Time_";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
@@ -821,7 +821,7 @@ TEST(Parser, UpdateVertex) {
         std::string query = "UPDATE VERTEX ON person \"12345\" "
                             "SET name=\"Tom\", age=30, married=true "
                             "WHEN salary > 10000 AND name == \"CTO\" OR age < 30"
-                            "YIELD name AS Name, salary AS Salary, create_time AS Time";
+                            "YIELD name AS Name, salary AS Salary, create_time AS Time_";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
@@ -831,7 +831,7 @@ TEST(Parser, UpdateVertex) {
                             "SET person.name=\"Tom\", person.age = 30, job.name =\"CTO\" "
                             "WHEN $^.job.salary > 10000 "
                             "YIELD $^.person.name AS Name, $^.job.salary AS Salary, "
-                            "$^.person.create_time AS Time";
+                            "$^.person.create_time AS Time_";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
@@ -840,7 +840,7 @@ TEST(Parser, UpdateVertex) {
         std::string query = "UPSERT VERTEX ON person \"12345\" "
                             "SET name=\"Tom\", age = 30, name =\"CTO\" "
                             "WHEN salary > 10000 "
-                            "YIELD name AS Name, salary AS Salary, create_time AS Time";
+                            "YIELD name AS Name, salary AS Salary, create_time AS Time_";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
@@ -849,21 +849,21 @@ TEST(Parser, UpdateVertex) {
 TEST(Parser, InsertEdge) {
     {
         GQLParser parser;
-        std::string query = "INSERT EDGE transfer(amount, time) "
+        std::string query = "INSERT EDGE transfer(amount, time_) "
                             "VALUES \"12345\"->\"54321\":(3.75, 1537408527)";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
-        std::string query = "INSERT EDGE transfer(amount, time) "
+        std::string query = "INSERT EDGE transfer(amount, time_) "
                             "VALUES \"from\"->\"to\":(3.75, 1537408527)";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
-        std::string query = "INSERT EDGE transfer(amount, time) "
+        std::string query = "INSERT EDGE transfer(amount, time_) "
                             "VALUES \"from\"->\"to\":(3.75, 1537408527)";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
@@ -871,7 +871,7 @@ TEST(Parser, InsertEdge) {
     // multi edge
     {
         GQLParser parser;
-        std::string query = "INSERT EDGE transfer(amount, time) "
+        std::string query = "INSERT EDGE transfer(amount, time_) "
                             "VALUES \"12345\"->\"54321@1537408527\":(3.75, 1537408527),"
                             "\"56789\"->\"98765@1537408527\":(3.5, 1537408527)";
         auto result = parser.parse(query);
@@ -887,21 +887,21 @@ TEST(Parser, InsertEdge) {
     }
     {
         GQLParser parser;
-        std::string query = "INSERT EDGE NO OVERWRITE transfer(amount, time) "
+        std::string query = "INSERT EDGE NO OVERWRITE transfer(amount, time_) "
                             "VALUES \"-12345\"->\"54321\":(3.75, 1537408527)";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
-        std::string query = "INSERT EDGE transfer(amount, time) "
+        std::string query = "INSERT EDGE transfer(amount, time_) "
                             "VALUES \"12345\"->\"54321\"@1537408527:(3.75, 1537408527)";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
-        std::string query = "INSERT EDGE NO OVERWRITE transfer(amount, time) "
+        std::string query = "INSERT EDGE NO OVERWRITE transfer(amount, time_) "
                             "VALUES \"12345\"->\"54321@1537408527\":(3.75, 1537408527)";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
@@ -920,21 +920,21 @@ TEST(Parser, UpdateEdge) {
     {
         GQLParser parser;
         std::string query = "UPDATE EDGE \"12345\" -> \"54321\" OF transfer "
-                            "SET amount=3.14, time=1537408527";
+                            "SET amount=3.14, time_=1537408527";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
         std::string query = "UPDATE EDGE ON transfer \"12345\" -> \"54321\" "
-                            "SET amount=3.14, time=1537408527";
+                            "SET amount=3.14, time_=1537408527";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
         std::string query = "UPDATE EDGE \"12345\" -> \"54321\"@789 OF transfer "
-                            "SET amount=3.14,time=1537408527 "
+                            "SET amount=3.14,time_=1537408527 "
                             "WHEN transfer.amount > 3.14 AND $^.person.name == \"Tom\"";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
@@ -942,7 +942,7 @@ TEST(Parser, UpdateEdge) {
     {
         GQLParser parser;
         std::string query = "UPDATE EDGE ON transfer \"12345\" -> \"54321\"@789 "
-                            "SET amount=3.14,time=1537408527 "
+                            "SET amount=3.14,time_=1537408527 "
                             "WHEN amount > 3.14";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
@@ -950,9 +950,9 @@ TEST(Parser, UpdateEdge) {
     {
         GQLParser parser;
         std::string query = "UPDATE EDGE \"12345\" -> \"54321\" OF transfer "
-                            "SET amount = 3.14 + $^.job.salary, time = 1537408527 "
+                            "SET amount = 3.14 + $^.job.salary, time_ = 1537408527 "
                             "WHEN transfer.amount > 3.14 OR $^.job.salary >= 10000 "
-                            "YIELD transfer.amount, transfer.time AS Time, "
+                            "YIELD transfer.amount, transfer.time_ AS Time_, "
                             "$^.person.name AS PayFrom";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
@@ -960,27 +960,27 @@ TEST(Parser, UpdateEdge) {
     {
         GQLParser parser;
         std::string query = "UPDATE EDGE ON transfer \"12345\" -> \"54321\" "
-                            "SET amount = 3.14 + amount, time = 1537408527 "
+                            "SET amount = 3.14 + amount, time_ = 1537408527 "
                             "WHEN amount > 3.14 "
-                            "YIELD amount, time AS Time";
+                            "YIELD amount, time_ AS Time_";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
         std::string query = "UPSERT EDGE \"12345\" -> \"54321\" @789 OF transfer "
-                            "SET amount=$^.job.salary + 3.14, time=1537408527 "
+                            "SET amount=$^.job.salary + 3.14, time_=1537408527 "
                             "WHEN transfer.amount > 3.14 AND $^.job.salary >= 10000 "
-                            "YIELD transfer.amount,transfer.time, $^.person.name AS Name";
+                            "YIELD transfer.amount,transfer.time_, $^.person.name AS Name";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
         std::string query = "UPSERT EDGE ON transfer \"12345\" -> \"54321\" @789 "
-                            "SET amount=$^.job.salary + 3.14, time=1537408527 "
+                            "SET amount=$^.job.salary + 3.14, time_=1537408527 "
                             "WHEN amount > 3.14 AND salary >= 10000 "
-                            "YIELD amount, time, name AS Name";
+                            "YIELD amount, time_, name AS Name";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
@@ -1168,14 +1168,14 @@ TEST(Parser, FetchEdge) {
     {
         GQLParser parser;
         std::string query = "FETCH PROP ON transfer \"12345\" -> \"54321\" "
-                            "YIELD transfer.time";
+                            "YIELD transfer.time_";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
         std::string query = "GO FROM \"12345\" OVER transfer "
-                            "YIELD transfer.time";
+                            "YIELD transfer.time_";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
@@ -2176,6 +2176,12 @@ TEST(Parser, UseReservedKeyword) {
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok());
     }
+    {
+        GQLParser parser;
+        std::string query = "CREATE TAG `time`(`time` string)";
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok());
+    }
 }
 
 TEST(Parser, TypeCast) {
diff --git a/src/parser/test/ScannerTest.cpp b/src/parser/test/ScannerTest.cpp
index 59e0cd4a..4d5463aa 100644
--- a/src/parser/test/ScannerTest.cpp
+++ b/src/parser/test/ScannerTest.cpp
@@ -447,6 +447,12 @@ TEST(Scanner, Basic) {
         CHECK_SEMANTIC_TYPE("skip", TokenType::KW_SKIP),
         CHECK_SEMANTIC_TYPE("OPTIONAL", TokenType::KW_OPTIONAL),
         CHECK_SEMANTIC_TYPE("optional", TokenType::KW_OPTIONAL),
+        CHECK_SEMANTIC_TYPE("DATE", TokenType::KW_DATE),
+        CHECK_SEMANTIC_TYPE("date", TokenType::KW_DATE),
+        CHECK_SEMANTIC_TYPE("TIME", TokenType::KW_TIME),
+        CHECK_SEMANTIC_TYPE("time", TokenType::KW_TIME),
+        CHECK_SEMANTIC_TYPE("DATETIME", TokenType::KW_DATETIME),
+        CHECK_SEMANTIC_TYPE("datetime", TokenType::KW_DATETIME),
 
         CHECK_SEMANTIC_TYPE("_type", TokenType::TYPE_PROP),
         CHECK_SEMANTIC_TYPE("_id", TokenType::ID_PROP),
diff --git a/src/util/SchemaUtil.cpp b/src/util/SchemaUtil.cpp
index 71fa145f..30bd6f16 100644
--- a/src/util/SchemaUtil.cpp
+++ b/src/util/SchemaUtil.cpp
@@ -84,30 +84,31 @@ StatusOr<nebula::Value> SchemaUtil::toSchemaValue(const meta::cpp2::PropertyType
             return Value(timestamp.value());
         }
         case meta::cpp2::PropertyType::DATE: {
-            if (v.type() != Value::Type::INT && v.type() != Value::Type::STRING) {
+            if (v.type() != Value::Type::DATE) {
                 LOG(ERROR) << "ValueType is wrong, input type "
                            << static_cast<int32_t>(type)
                            << ", v type " <<  v.type();
                 return Status::Error("Wrong type");
             }
-            auto date = TimeFunction::toDate(v);
-            if (!date.ok()) {
-                return date.status();
+            return v;
+        }
+        case meta::cpp2::PropertyType::TIME: {
+            if (v.type() != Value::Type::TIME) {
+                LOG(ERROR) << "ValueType is wrong, input type "
+                           << static_cast<int32_t>(type)
+                           << ", v type " <<  v.type();
+                return Status::Error("Wrong type");
             }
-            return Value(date.value());
+            return v;
         }
         case meta::cpp2::PropertyType::DATETIME: {
-            if (v.type() != Value::Type::INT && v.type() != Value::Type::STRING) {
+            if (v.type() != Value::Type::DATETIME) {
                 LOG(ERROR) << "ValueType is wrong, input type "
                            << static_cast<int32_t>(type)
                            << ", v type " <<  v.type();
                 return Status::Error("Wrong type");
             }
-            auto datetime = TimeFunction::toDateTime(v);
-            if (!datetime.ok()) {
-                return datetime.status();
-            }
-            return Value(datetime.value());
+            return v;
         }
         default: {
             return v;
diff --git a/tests/common/nebula_test_suite.py b/tests/common/nebula_test_suite.py
index 1e5b83a7..a4263d74 100644
--- a/tests/common/nebula_test_suite.py
+++ b/tests/common/nebula_test_suite.py
@@ -389,6 +389,12 @@ class NebulaTestSuite(object):
             value.set_fVal(col)
         elif isinstance(col, str):
             value.set_sVal(col.encode('utf-8'))
+        elif isinstance(col, CommonTtypes.Date):
+            value.set_dVal(col)
+        elif isinstance(col, CommonTtypes.Time):
+            value.set_tVal(col)
+        elif isinstance(col, CommonTtypes.DateTime):
+            value.set_dtVal(col)
         elif isinstance(col, dict):
             map_val = CommonTtypes.Map()
             map_val.kvs = dict()
diff --git a/tests/maintain/test_tag_edge.py b/tests/maintain/test_tag_edge.py
index a44f95d2..14391d37 100644
--- a/tests/maintain/test_tag_edge.py
+++ b/tests/maintain/test_tag_edge.py
@@ -241,35 +241,35 @@ class TestSchema(NebulaTestSuite):
         self.check_out_of_order_result(resp, expect_result)
 
         # create edge succeeded
-        resp = self.client.execute('CREATE EDGE buy(id int, time string)')
+        resp = self.client.execute('CREATE EDGE buy(id int, time_ string)')
         self.check_resp_succeeded(resp)
 
         # create Edge with duplicate field
-        resp = self.client.execute('CREATE EDGE duplicate_buy(time int, time string)')
+        resp = self.client.execute('CREATE EDGE duplicate_buy(time_ int, time_ string)')
         self.check_resp_failed(resp)
 
         # create Edge with duplicate field
-        resp = self.client.execute('CREATE EDGE duplicate_buy(time int, time int)')
+        resp = self.client.execute('CREATE EDGE duplicate_buy(time_ int, time_ int)')
         self.check_resp_failed(resp)
 
         # create Edge with DEFAULT
         resp = self.client.execute('CREATE EDGE buy_with_default(id int, name string DEFAULT "NULL",'
-                                   'time timestamp DEFAULT 2020)')
+                                   'time_ timestamp DEFAULT 2020)')
         self.check_resp_succeeded(resp)
 
         # DEFAULT value not match type
-        resp = self.client.execute('CREATE EDGE buy_type_mismatch(id int, time string DEFAULT 0)')
+        resp = self.client.execute('CREATE EDGE buy_type_mismatch(id int, time_ string DEFAULT 0)')
         self.check_resp_failed(resp)
 
         # existent edge
-        resp = self.client.execute('CREATE EDGE buy(id int, time string)')
+        resp = self.client.execute('CREATE EDGE buy(id int, time_ string)')
         self.check_resp_failed(resp)
 
         # DESCRIBE edge
         resp = self.client.execute_query('DESCRIBE EDGE buy')
         self.check_resp_succeeded(resp)
         expect_result = [['id', 'int64', 'YES', T_EMPTY],
-                         ['time', 'string', 'YES', T_EMPTY]]
+                         ['time_', 'string', 'YES', T_EMPTY]]
         self.check_out_of_order_result(resp, expect_result)
 
         # desc nonexistent edge
@@ -280,7 +280,7 @@ class TestSchema(NebulaTestSuite):
         resp = self.client.execute_query('DESC EDGE buy')
         self.check_resp_succeeded(resp)
         expect_result = [['id', 'int64', 'YES', T_EMPTY],
-                         ['time', 'string', 'YES', T_EMPTY]]
+                         ['time_', 'string', 'YES', T_EMPTY]]
         self.check_out_of_order_result(resp, expect_result)
 
         # show create edge
@@ -289,20 +289,20 @@ class TestSchema(NebulaTestSuite):
         create_edge_str = 'CREATE EDGE `buy_with_default` (\n' \
                           ' `id` int64 NULL,\n' \
                           ' `name` string NULL DEFAULT "NULL",\n' \
-                          ' `time` timestamp NULL DEFAULT 2020\n' \
+                          ' `time_` timestamp NULL DEFAULT 2020\n' \
                           ') ttl_duration = 0, ttl_col = "\"'
         expect_result = [['buy_with_default', create_edge_str]]
         self.check_result(resp, expect_result)
 
         # create edge succeed
-        resp = self.client.execute('CREATE EDGE education(id int, time timestamp, school string)')
+        resp = self.client.execute('CREATE EDGE education(id int, time_ timestamp, school string)')
         self.check_resp_succeeded(resp)
 
         # DESC edge
         resp = self.client.execute_query('DESC EDGE education')
         self.check_resp_succeeded(resp)
         expect_result = [['id', 'int64', 'YES', T_EMPTY],
-                         ['time', 'timestamp', 'YES', T_EMPTY],
+                         ['time_', 'timestamp', 'YES', T_EMPTY],
                          ['school', 'string', 'YES', T_EMPTY]]
         self.check_out_of_order_result(resp, expect_result)
 
@@ -316,11 +316,11 @@ class TestSchema(NebulaTestSuite):
         resp = self.client.execute('ALTER EDGE education '
                                    'ADD (col1 int, col2 string), '
                                    'CHANGE (school int), '
-                                   'DROP (id, time)')
+                                   'DROP (id, time_)')
         self.check_resp_succeeded(resp)
 
         # drop not exist prop, failed
-        resp = self.client.execute('ALTER EDGE education DROP (id, time)')
+        resp = self.client.execute('ALTER EDGE education DROP (id, time_)')
         self.check_resp_failed(resp)
 
         # check result
diff --git a/tests/query/v2/test_basic_type_crud.py b/tests/query/v2/test_basic_type_crud.py
new file mode 100644
index 00000000..aff5479f
--- /dev/null
+++ b/tests/query/v2/test_basic_type_crud.py
@@ -0,0 +1,133 @@
+# --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 pytest
+
+import time
+
+from tests.common.nebula_test_suite import NebulaTestSuite
+from nebula2.common import ttypes as CommonTtypes
+
+class TestBasicTypeCrud(NebulaTestSuite):
+    @classmethod
+    def prepare(self):
+        resp = self.execute('CREATE SPACE basic_type_crud')
+        self.check_resp_succeeded(resp)
+        time.sleep(self.delay)
+
+        resp = self.execute('USE basic_type_crud')
+        self.check_resp_succeeded(resp)
+
+    @classmethod
+    def cleanup(self):
+        resp = self.execute('DROP SPACE basic_type_crud')
+        self.check_resp_succeeded(resp)
+
+    # TODO(shylock) add time datetype
+    def test_date_crud(self):
+        # create schema
+        resp = self.execute('CREATE TAG tag_date(f_date DATE, f_time TIME, f_datetime DATETIME)')
+        self.check_resp_succeeded(resp)
+
+        resp = self.execute_query("SHOW CREATE TAG tag_date")
+        self.check_resp_succeeded(resp)
+        create_str = 'CREATE TAG `tag_date` (\n' \
+                     ' `f_date` date NULL,\n' \
+                     ' `f_time` time NULL,\n' \
+                     ' `f_datetime` datetime NULL\n' \
+                     ') ttl_duration = 0, ttl_col = ""'
+        result = [['tag_date', create_str]]
+        self.check_out_of_order_result(resp, result)
+
+        resp = self.execute('CREATE EDGE edge_date(f_date DATE, f_time TIME, f_datetime DATETIME)')
+        self.check_resp_succeeded(resp)
+
+        resp = self.execute_query("SHOW CREATE EDGE edge_date")
+        self.check_resp_succeeded(resp)
+        create_str = 'CREATE EDGE `edge_date` (\n' \
+                     ' `f_date` date NULL,\n' \
+                     ' `f_time` time NULL,\n' \
+                     ' `f_datetime` datetime NULL\n' \
+                     ') ttl_duration = 0, ttl_col = ""'
+        result = [['edge_date', create_str]]
+        self.check_out_of_order_result(resp, result)
+
+        time.sleep(self.delay)
+        # insert invalid type
+        resp = self.execute('''
+                            INSERT VERTEX tag_date(f_date, f_time, f_datetime)
+                            VALUES "test":("2017-03-04", "23:01:00", 1234)
+                            ''')
+        self.check_resp_failed(resp)
+
+        resp = self.execute('''
+                            INSERT EDGE edge_date(f_date, f_time, f_datetime)
+                            VALUES "test_src"->"test_dst":(true, "23:01:00", "2017-03-04T22:30:40")
+                            ''')
+        self.check_resp_failed(resp)
+
+        # insert date
+        # TODO(shylock) add us
+        resp = self.execute('''
+                            INSERT VERTEX tag_date(f_date, f_time, f_datetime)
+                            VALUES "test":(date("2017-03-04"), time("23:01:00"), datetime("2017-03-04T22:30:40"))
+                            ''')
+        self.check_resp_succeeded(resp)
+
+        resp = self.execute('''
+                            INSERT EDGE edge_date(f_date, f_time, f_datetime)
+                            VALUES "test_src"->"test_dst":(date("2017-03-04"), time("23:01:00"), datetime("2017-03-04T22:30:40"))
+                            ''')
+        self.check_resp_succeeded(resp)
+
+        # query
+        resp = self.execute_query('FETCH PROP ON tag_date "test"')
+        self.check_resp_succeeded(resp)
+        result = [["test", CommonTtypes.Date(2017, 3, 4), CommonTtypes.Time(23, 1, 0, 0), CommonTtypes.DateTime(2017, 3, 4, 22, 30, 40, 0)]]
+        self.check_out_of_order_result(resp, result)
+
+        resp = self.execute_query('FETCH PROP ON edge_date "test_src"->"test_dst"')
+        self.check_resp_succeeded(resp)
+        result = [["test_src", "test_dst", 0, CommonTtypes.Date(2017, 3, 4), CommonTtypes.Time(23, 1, 0, 0), CommonTtypes.DateTime(2017, 3, 4, 22, 30, 40, 0)]]
+        self.check_out_of_order_result(resp, result)
+
+        # update
+        resp = self.execute_query('''
+                                  UPDATE VERTEX "test"
+                                  SET tag_date.f_date = Date("2018-03-04"), tag_date.f_time = Time("22:01:00"), tag_date.f_datetime = DateTime("2018-03-04T22:30:40")
+                                  YIELD f_date, f_time, f_datetime
+                                  ''')
+        self.check_resp_succeeded(resp)
+        result = [[CommonTtypes.Date(2018, 3, 4), CommonTtypes.Time(22, 1, 0, 0), CommonTtypes.DateTime(2018, 3, 4, 22, 30, 40, 0)]]
+        self.check_out_of_order_result(resp, result)
+
+        resp = self.execute_query('''
+                                  UPDATE EDGE "test_src"->"test_dst" OF edge_date
+                                  SET edge_date.f_date = Date("2018-03-04"), edge_date.f_time = Time("22:01:00"), edge_date.f_datetime = DateTime("2018-03-04T22:30:40")
+                                  YIELD f_date, f_time, f_datetime
+                                  ''')
+        self.check_resp_succeeded(resp)
+        result = [[CommonTtypes.Date(2018, 3, 4), CommonTtypes.Time(22, 1, 0, 0), CommonTtypes.DateTime(2018, 3, 4, 22, 30, 40, 0)]]
+        self.check_out_of_order_result(resp, result)
+
+        # delete
+        resp = self.execute('DELETE VERTEX "test"')
+        self.check_resp_succeeded(resp)
+
+        resp = self.execute('DELETE EDGE edge_date "test_src"->"test_dst"')
+        self.check_resp_succeeded(resp)
+
+        # check deletion
+        resp = self.execute_query('FETCH PROP ON tag_date "test"')
+        self.check_resp_succeeded(resp)
+        result = []
+        self.check_out_of_order_result(resp, result)
+
+        resp = self.execute_query('FETCH PROP ON edge_date "test_src"->"test_dst"')
+        self.check_resp_succeeded(resp)
+        result = []
+        self.check_out_of_order_result(resp, result)
-- 
GitLab