This is an automated email from the ASF dual-hosted git repository.

haonan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 93dae13b47 [IOTDB-3351] Add python Client schema template functions: 
create template function and drop template function (#6092)
93dae13b47 is described below

commit 93dae13b47f06aac10b1e3ed4a8ce565c84af9d3
Author: Tom <[email protected]>
AuthorDate: Thu Jun 2 09:22:04 2022 +0800

    [IOTDB-3351] Add python Client schema template functions: create template 
function and drop template function (#6092)
---
 client-py/README.md                                | 31 ++++++++
 client-py/SessionExample.py                        | 49 ++++++++++++
 client-py/iotdb/Session.py                         | 55 ++++++++++++++
 client-py/iotdb/template/InternalNode.py           | 41 +++++++++++
 client-py/iotdb/template/MeasurementNode.py        | 56 ++++++++++++++
 client-py/iotdb/template/Template.py               | 86 ++++++++++++++++++++++
 client-py/iotdb/template/TemplateNode.py           | 46 ++++++++++++
 client-py/iotdb/template/__init__.py               | 17 +++++
 .../iotdb/tsfile/common/constant/TsFileConstant.py | 36 +++++++++
 client-py/iotdb/tsfile/common/constant/__init__.py | 17 +++++
 client-py/iotdb/tsfile/utils/Pair.py               | 26 +++++++
 client-py/iotdb/tsfile/utils/ReadWriteIOUtils.py   | 77 +++++++++++++++++++
 client-py/iotdb/tsfile/utils/__init__.py           | 17 +++++
 client-py/tests/test_template.py                   | 74 +++++++++++++++++++
 .../UserGuide/API/Programming-Python-Native-API.md | 31 ++++++++
 .../UserGuide/API/Programming-Python-Native-API.md | 31 ++++++++
 16 files changed, 690 insertions(+)

diff --git a/client-py/README.md b/client-py/README.md
index 41c0a113b8..e632e400b3 100644
--- a/client-py/README.md
+++ b/client-py/README.md
@@ -274,6 +274,37 @@ session.execute_non_query_statement(sql)
 ```
 
 
+### Schema Template
+#### Create Schema Template
+The step for creating a metadata template is as follows
+1. Create the template class
+2. Adding child Node,InternalNode and MeasurementNode can be chose
+3. Execute create schema template function
+
+```python
+template = Template(name="treeTemplate_python", share_time=True)
+
+i_node_gps = InternalNode(name="GPS", share_time=False)
+i_node_v = InternalNode(name="vehicle", share_time=True)
+m_node_x = MeasurementNode("x", TSDataType.FLOAT, TSEncoding.RLE, 
Compressor.SNAPPY)
+
+i_node_gps.add_child(m_node_x)
+i_node_gps.add_child(m_node_x)
+i_node_v.add_child(m_node_x)
+
+template.add_template(i_node_gps)
+template.add_template(i_node_v)
+template.add_template(m_node_x)
+
+session.create_schema_template(template)
+```
+#### Drop Schema Template
+Delete an existing metadata template,dropping an already set template is not 
supported
+```python
+session.drop_schema_template("template_python")
+```
+
+
 ### Pandas Support
 
 To easily transform a query result to a [Pandas 
Dataframe](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html)
diff --git a/client-py/SessionExample.py b/client-py/SessionExample.py
index 93aa839c3b..c34bf553d6 100644
--- a/client-py/SessionExample.py
+++ b/client-py/SessionExample.py
@@ -20,6 +20,9 @@
 import numpy as np
 
 from iotdb.Session import Session
+from iotdb.template.InternalNode import InternalNode
+from iotdb.template.MeasurementNode import MeasurementNode
+from iotdb.template.Template import Template
 from iotdb.utils.IoTDBConstants import TSDataType, TSEncoding, Compressor
 from iotdb.utils.Tablet import Tablet
 from iotdb.utils.NumpyTablet import NumpyTablet
@@ -313,6 +316,52 @@ with session.execute_last_data_query(
 # delete storage group
 session.delete_storage_group("root.sg_test_01")
 
+# create measurement node template
+template = Template(name="template_python", share_time=False)
+m_node_1 = MeasurementNode(
+    name="s1",
+    data_type=TSDataType.INT64,
+    encoding=TSEncoding.RLE,
+    compression_type=Compressor.SNAPPY,
+)
+m_node_2 = MeasurementNode(
+    name="s2",
+    data_type=TSDataType.INT64,
+    encoding=TSEncoding.RLE,
+    compression_type=Compressor.SNAPPY,
+)
+m_node_3 = MeasurementNode(
+    name="s3",
+    data_type=TSDataType.INT64,
+    encoding=TSEncoding.RLE,
+    compression_type=Compressor.SNAPPY,
+)
+template.add_template(m_node_1)
+template.add_template(m_node_2)
+template.add_template(m_node_3)
+session.create_schema_template(template)
+print("create template success template_python")
+
+# create internal node template
+template = Template(name="treeTemplate_python", share_time=True)
+i_node_gps = InternalNode(name="GPS", share_time=False)
+i_node_v = InternalNode(name="vehicle", share_time=True)
+m_node_x = MeasurementNode("x", TSDataType.FLOAT, TSEncoding.RLE, 
Compressor.SNAPPY)
+
+i_node_gps.add_child(m_node_x)
+i_node_gps.add_child(m_node_x)
+i_node_v.add_child(m_node_x)
+template.add_template(i_node_gps)
+template.add_template(i_node_v)
+template.add_template(m_node_x)
+
+session.create_schema_template(template)
+print("create template success treeTemplate_python}")
+
+# drop template
+session.drop_schema_template("template_python")
+session.drop_schema_template("treeTemplate_python")
+print("drop template success, template_python and treeTemplate_python")
 # close session connection.
 session.close()
 
diff --git a/client-py/iotdb/Session.py b/client-py/iotdb/Session.py
index 780ecc7d7c..1603c37b2e 100644
--- a/client-py/iotdb/Session.py
+++ b/client-py/iotdb/Session.py
@@ -24,6 +24,7 @@ from iotdb.utils.SessionDataSet import SessionDataSet
 from thrift.protocol import TBinaryProtocol, TCompactProtocol
 from thrift.transport import TSocket, TTransport
 
+from .template.Template import Template
 from .thrift.rpc.TSIService import (
     Client,
     TSCreateTimeseriesReq,
@@ -38,6 +39,8 @@ from .thrift.rpc.TSIService import (
     TSInsertTabletsReq,
     TSInsertRecordsReq,
     TSInsertRecordsOfOneDeviceReq,
+    TSCreateSchemaTemplateReq,
+    TSDropSchemaTemplateReq,
 )
 from .thrift.rpc.ttypes import (
     TSDeleteDataReq,
@@ -1033,6 +1036,13 @@ class Session(object):
     def execute_raw_data_query(
         self, paths: list, start_time: int, end_time: int
     ) -> SessionDataSet:
+        """
+        execute query statement and returns SessionDataSet
+        :param paths: String path list
+        :param start_time: Query start time
+        :param end_time: Query end time
+        :return: SessionDataSet, contains query results and relevant info (see 
SessionDataSet.py)
+        """
         request = TSRawDataQueryReq(
             self.__session_id,
             paths,
@@ -1057,6 +1067,12 @@ class Session(object):
         )
 
     def execute_last_data_query(self, paths: list, last_time: int) -> 
SessionDataSet:
+        """
+        execute query statement and returns SessionDataSet
+        :param paths: String path list
+        :param last_time: Query last time
+        :return: SessionDataSet, contains query results and relevant info (see 
SessionDataSet.py)
+        """
         request = TSLastDataQueryReq(
             self.__session_id,
             paths,
@@ -1088,6 +1104,16 @@ class Session(object):
         values_list: list,
         have_sorted: bool = False,
     ):
+        """
+        insert multiple row of string record into database:
+                 timestamp,     m1,    m2,     m3
+                         0,  text1,  text2, text3
+        :param device_id: String, device id
+        :param times: Timestamp list
+        :param measurements_list: Measurements list
+        :param values_list: Value list
+        :param have_sorted: have these list been sorted by timestamp
+        """
         if (len(times) != len(measurements_list)) or (len(times) != 
len(values_list)):
             raise RuntimeError(
                 "insert records of one device error: times, measurementsList 
and valuesList's size should be equal!"
@@ -1151,3 +1177,32 @@ class Session(object):
             is_aligned,
         )
         return request
+
+    def create_schema_template(self, template: Template):
+        """
+        create schema template, users using this method should use the 
template class as an argument
+        :param template: The template contain multiple child node(see 
Template.py)
+        """
+        bytes_array = template.serialize()
+        request = TSCreateSchemaTemplateReq(
+            self.__session_id, template.get_name(), bytes_array
+        )
+        status = self.__client.createSchemaTemplate(request)
+        logger.debug(
+            "create one template {} template name: 
{}".format(self.__session_id, template.get_name())
+        )
+        return Session.verify_success(status)
+
+    def drop_schema_template(self, template_name: str):
+        """
+        drop schema template, this method should be used to the template unset 
anything
+        :param template_name: template name
+        """
+        request = TSDropSchemaTemplateReq(self.__session_id, template_name)
+        status = self.__client.dropSchemaTemplate(request)
+        logger.debug(
+            "drop one template {} template name: {}".format(
+                self.__session_id, template_name
+            )
+        )
+        return Session.verify_success(status)
diff --git a/client-py/iotdb/template/InternalNode.py 
b/client-py/iotdb/template/InternalNode.py
new file mode 100644
index 0000000000..bac17ca90a
--- /dev/null
+++ b/client-py/iotdb/template/InternalNode.py
@@ -0,0 +1,41 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from .TemplateNode import TemplateNode
+
+
+class InternalNode(TemplateNode):
+    def __init__(self, name, share_time):
+        super().__init__(name)
+        self.children = {}
+        self.share_time = share_time
+
+    def add_child(self, node: TemplateNode):
+        if node.get_name() in self.children.keys():
+            assert "Duplicated child of node in template."
+
+        self.children.update({node.get_name(): node})
+
+    def delete_child(self, node):
+        self.children.pop(node.get_name(), None)
+
+    def get_children(self):
+        return self.children
+
+    def is_share_time(self):
+        return self.share_time
diff --git a/client-py/iotdb/template/MeasurementNode.py 
b/client-py/iotdb/template/MeasurementNode.py
new file mode 100644
index 0000000000..7d96d4bc0d
--- /dev/null
+++ b/client-py/iotdb/template/MeasurementNode.py
@@ -0,0 +1,56 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from iotdb.utils.IoTDBConstants import TSDataType, TSEncoding, Compressor
+from .TemplateNode import TemplateNode
+from ..tsfile.utils.ReadWriteIOUtils import ReadWriteUtils
+
+
+class MeasurementNode(TemplateNode):
+    def __init__(
+        self,
+        name: str,
+        data_type: TSDataType,
+        encoding: TSEncoding,
+        compression_type: Compressor,
+    ):
+        self.name = name
+        self.data_type = data_type
+        self.encoding = encoding
+        self.compression_type = compression_type
+
+    def is_measurement(self):
+        return True
+
+    def get_data_type(self):
+        return self.data_type
+
+    def get_encoding(self):
+        return self.encoding
+
+    def get_compression_type(self):
+        return self.compression_type
+
+    def serialize(self, *args, **kwargs):
+        format_str_list, values_tobe_packed = args
+        ReadWriteUtils.write(self.get_name(), format_str_list, 
values_tobe_packed)
+        ReadWriteUtils.write(self.get_data_type(), format_str_list, 
values_tobe_packed)
+        ReadWriteUtils.write(self.get_encoding(), format_str_list, 
values_tobe_packed)
+        ReadWriteUtils.write(
+            self.get_compression_type(), format_str_list, values_tobe_packed
+        )
diff --git a/client-py/iotdb/template/Template.py 
b/client-py/iotdb/template/Template.py
new file mode 100644
index 0000000000..d5137d3343
--- /dev/null
+++ b/client-py/iotdb/template/Template.py
@@ -0,0 +1,86 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import struct
+
+from .TemplateNode import TemplateNode
+from ..tsfile.common.constant.TsFileConstant import TsFileConstant
+from ..tsfile.utils.Pair import Pair
+from ..tsfile.utils.ReadWriteIOUtils import ReadWriteUtils
+
+
+class Template:
+    def __init__(self, name, share_time: bool = False):
+        self.name = name
+        self.children = dict()
+        self.share_time = share_time
+
+    def get_name(self) -> object:
+        return self.name
+
+    def is_share_time(self) -> object:
+        return self.share_time
+
+    def set_share_time(self, share_time: bool):
+        self.share_time = share_time
+
+    def add_template(self, child: TemplateNode):
+        if self.children.get(child.get_name()):
+            raise Exception("Duplicated child of node in template.")
+        self.children.update({child.get_name(): child})
+
+    def delete_from_template(self, name: str):
+        if not self.children.pop(name, []):
+            raise Exception("It is not a direct child of the template: " + 
name)
+
+    def serialize(self):
+        format_str_list = [">"]
+        values_tobe_packed = []
+        stack = []
+        aligned_prefix = set()
+        ReadWriteUtils.write(self.get_name(), format_str_list, 
values_tobe_packed)
+        ReadWriteUtils.write(self.is_share_time(), format_str_list, 
values_tobe_packed)
+        if self.is_share_time():
+            aligned_prefix.add("")
+
+        for child in self.children:
+            stack.append(Pair("", self.children[child]))
+
+        while stack:
+            pair = stack.pop()
+            prefix = pair.left
+            cur_node = pair.right
+            full_path = list()
+            if not cur_node.is_measurement():
+                if prefix != "":
+                    full_path.append(TsFileConstant.PATH_SEPARATOR)
+                full_path.append(cur_node.get_name())
+                if cur_node.is_share_time():
+                    aligned_prefix.add("".join(full_path))
+                for child in cur_node.children:
+                    stack.append(Pair("".join(full_path), 
self.children[child]))
+            else:
+                ReadWriteUtils.write(prefix, format_str_list, 
values_tobe_packed)
+                if prefix in aligned_prefix:
+                    ReadWriteUtils.write(True, format_str_list, 
values_tobe_packed)
+                else:
+                    ReadWriteUtils.write(False, format_str_list, 
values_tobe_packed)
+                cur_node.serialize(format_str_list, values_tobe_packed)
+
+        format_str = "".join(format_str_list)
+        return struct.pack(format_str, *values_tobe_packed)
diff --git a/client-py/iotdb/template/TemplateNode.py 
b/client-py/iotdb/template/TemplateNode.py
new file mode 100644
index 0000000000..62a6c70eac
--- /dev/null
+++ b/client-py/iotdb/template/TemplateNode.py
@@ -0,0 +1,46 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+class TemplateNode(object):
+    """
+    Template class, this class should be used to schema template node
+    """
+    def __init__(self, name):
+        self.name = name
+
+    def get_name(self):
+        return self.name
+
+    def get_children(self):
+        return None
+
+    def add_child(self, node):
+        ...
+
+    def delete_child(self, node):
+        ...
+
+    def is_measurement(self):
+        return False
+
+    def is_share_time(self):
+        return False
+
+    def serialize(self, buffer):
+        ...
diff --git a/client-py/iotdb/template/__init__.py 
b/client-py/iotdb/template/__init__.py
new file mode 100644
index 0000000000..2a1e720805
--- /dev/null
+++ b/client-py/iotdb/template/__init__.py
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
diff --git a/client-py/iotdb/tsfile/common/constant/TsFileConstant.py 
b/client-py/iotdb/tsfile/common/constant/TsFileConstant.py
new file mode 100644
index 0000000000..0baad6a5fb
--- /dev/null
+++ b/client-py/iotdb/tsfile/common/constant/TsFileConstant.py
@@ -0,0 +1,36 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+class TsFileConstant:
+    TSFILE_SUFFIX = ".tsfile"
+    TSFILE_HOME = "TSFILE_HOME"
+    TSFILE_CONF = "TSFILE_CONF"
+    PATH_ROOT = "root"
+    TMP_SUFFIX = "tmp"
+    PATH_SEPARATOR = "."
+    PATH_SEPARATOR_CHAR = "."
+    PATH_SEPARATER_NO_REGEX = "\\."
+    DOUBLE_QUOTE = '"'
+
+    TIME_COLUMN_MASK = 0x80
+
+    VALUE_COLUMN_MASK = 0x40
+
+    def __ts_file_constant(self):
+        ...
diff --git a/client-py/iotdb/tsfile/common/constant/__init__.py 
b/client-py/iotdb/tsfile/common/constant/__init__.py
new file mode 100644
index 0000000000..2a1e720805
--- /dev/null
+++ b/client-py/iotdb/tsfile/common/constant/__init__.py
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
diff --git a/client-py/iotdb/tsfile/utils/Pair.py 
b/client-py/iotdb/tsfile/utils/Pair.py
new file mode 100644
index 0000000000..70bb3f7258
--- /dev/null
+++ b/client-py/iotdb/tsfile/utils/Pair.py
@@ -0,0 +1,26 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from iotdb.template.TemplateNode import TemplateNode
+
+
+class Pair:
+    def __init__(self, left: str, right: TemplateNode):
+        self.__serialVersionUID = -1398609631703707002
+        self.left = left
+        self.right = right
diff --git a/client-py/iotdb/tsfile/utils/ReadWriteIOUtils.py 
b/client-py/iotdb/tsfile/utils/ReadWriteIOUtils.py
new file mode 100644
index 0000000000..6101906ced
--- /dev/null
+++ b/client-py/iotdb/tsfile/utils/ReadWriteIOUtils.py
@@ -0,0 +1,77 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from iotdb.utils.IoTDBConstants import TSDataType, TSEncoding, Compressor
+
+
+class ReadWriteUtils:
+    BOOLEAN_LEN = 1
+    SHORT_LEN = 2
+    INT_LEN = 4
+    LONG_LEN = 8
+    DOUBLE_LEN = 8
+    FLOAT_LEN = 4
+    BIT_LEN = 0.125
+    NO_BYTE_TO_READ = -1
+    magicStringBytes = []
+    RETURN_ERROR = "Intend to read %d bytes but %d are actually returned"
+    URN_ERROR = "Intend to read %d bytes but %d are actually returned"
+
+    @classmethod
+    def write(cls, *args, **kwargs):
+        value, format_str_list, values_tobe_packed = args
+        if isinstance(value, bool):
+            cls.write_bool(value, format_str_list, values_tobe_packed)
+        elif isinstance(value, str):
+            cls.write_str(value, format_str_list, values_tobe_packed)
+        elif isinstance(value, int):
+            cls.write_int(value, format_str_list, values_tobe_packed)
+        elif isinstance(value, TSDataType):
+            cls.write_byte(value.value, format_str_list, values_tobe_packed)
+        elif isinstance(value, TSEncoding):
+            cls.write_byte(value.value, format_str_list, values_tobe_packed)
+        elif isinstance(value, Compressor):
+            cls.write_byte(value.value, format_str_list, values_tobe_packed)
+
+    @classmethod
+    def write_str(cls, s: str, format_str_list, values_tobe_packed):
+        if s is None:
+            cls.write_int(cls.NO_BYTE_TO_READ, format_str_list, 
values_tobe_packed)
+
+        value_bytes = bytes(s, "utf-8")
+        format_str_list.append("i")
+        format_str_list.append(str(len(value_bytes)))
+        format_str_list.append("s")
+
+        values_tobe_packed.append(len(value_bytes))
+        values_tobe_packed.append(value_bytes)
+
+    @classmethod
+    def write_int(cls, i: int, format_str_list, values_tobe_packed):
+        format_str_list.append("i")
+        values_tobe_packed.append(i)
+
+    @classmethod
+    def write_bool(cls, flag: bool, format_str_list, values_tobe_packed):
+        format_str_list.append("?")
+        values_tobe_packed.append(flag)
+
+    @classmethod
+    def write_byte(cls, b, format_str_list, values_tobe_packed):
+        format_str_list.append("b")
+        values_tobe_packed.append(b)
diff --git a/client-py/iotdb/tsfile/utils/__init__.py 
b/client-py/iotdb/tsfile/utils/__init__.py
new file mode 100644
index 0000000000..2a1e720805
--- /dev/null
+++ b/client-py/iotdb/tsfile/utils/__init__.py
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
diff --git a/client-py/tests/test_template.py b/client-py/tests/test_template.py
new file mode 100644
index 0000000000..0b8a84aefd
--- /dev/null
+++ b/client-py/tests/test_template.py
@@ -0,0 +1,74 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+from iotdb.IoTDBContainer import IoTDBContainer
+from iotdb.Session import Session
+from iotdb.template.InternalNode import InternalNode
+from iotdb.template.MeasurementNode import MeasurementNode
+from iotdb.template.Template import Template
+from iotdb.utils.IoTDBConstants import TSDataType, Compressor, TSEncoding
+
+
+def test_template_create():
+    with IoTDBContainer("iotdb:dev") as db:
+        db: IoTDBContainer
+        session = Session(db.get_container_host_ip(), 
db.get_exposed_port(6667))
+        session.open(False)
+
+        template = Template(name="template_python", share_time=False)
+        m_node_1 = MeasurementNode(
+            name="s1",
+            data_type=TSDataType.INT64,
+            encoding=TSEncoding.RLE,
+            compression_type=Compressor.SNAPPY,
+        )
+        m_node_2 = MeasurementNode(
+            name="s2",
+            data_type=TSDataType.INT64,
+            encoding=TSEncoding.RLE,
+            compression_type=Compressor.SNAPPY,
+        )
+        m_node_3 = MeasurementNode(
+            name="s3",
+            data_type=TSDataType.INT64,
+            encoding=TSEncoding.RLE,
+            compression_type=Compressor.SNAPPY,
+        )
+        template.add_template(m_node_1)
+        template.add_template(m_node_2)
+        template.add_template(m_node_3)
+        session.create_schema_template(template)
+
+        template = Template(name="treeTemplate_python", share_time=True)
+        i_node_gps = InternalNode(name="GPS", share_time=False)
+        i_node_v = InternalNode(name="vehicle", share_time=True)
+        m_node_x = MeasurementNode(
+            "x", TSDataType.FLOAT, TSEncoding.RLE, Compressor.SNAPPY
+        )
+
+        i_node_gps.add_child(m_node_x)
+        i_node_gps.add_child(m_node_x)
+        i_node_v.add_child(m_node_x)
+        template.add_template(i_node_gps)
+        template.add_template(i_node_v)
+        template.add_template(m_node_x)
+        session.create_schema_template(template)
+
+        session.drop_schema_template("template_python")
+        session.drop_schema_template("treeTemplate_python")
+
+        session.close()
diff --git a/docs/UserGuide/API/Programming-Python-Native-API.md 
b/docs/UserGuide/API/Programming-Python-Native-API.md
index c200586bbf..30799133e2 100644
--- a/docs/UserGuide/API/Programming-Python-Native-API.md
+++ b/docs/UserGuide/API/Programming-Python-Native-API.md
@@ -252,6 +252,37 @@ session.execute_non_query_statement(sql)
 ```
 
 
+### Schema Template
+#### Create Schema Template
+The step for creating a metadata template is as follows
+1. Create the template class
+2. Adding child Node,InternalNode and MeasurementNode can be chose
+3. Execute create schema template function
+
+```python
+template = Template(name="treeTemplate_python", share_time=True)
+
+i_node_gps = InternalNode(name="GPS", share_time=False)
+i_node_v = InternalNode(name="vehicle", share_time=True)
+m_node_x = MeasurementNode("x", TSDataType.FLOAT, TSEncoding.RLE, 
Compressor.SNAPPY)
+
+i_node_gps.add_child(m_node_x)
+i_node_gps.add_child(m_node_x)
+i_node_v.add_child(m_node_x)
+
+template.add_template(i_node_gps)
+template.add_template(i_node_v)
+template.add_template(m_node_x)
+
+session.create_schema_template(template)
+```
+#### Drop Schema Template
+Delete an existing metadata template,dropping an already set template is not 
supported
+```python
+session.drop_schema_template("template_python")
+```
+
+
 ### Pandas Support
 
 To easily transform a query result to a [Pandas 
Dataframe](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html)
diff --git a/docs/zh/UserGuide/API/Programming-Python-Native-API.md 
b/docs/zh/UserGuide/API/Programming-Python-Native-API.md
index 55fc59d031..aec3c832a1 100644
--- a/docs/zh/UserGuide/API/Programming-Python-Native-API.md
+++ b/docs/zh/UserGuide/API/Programming-Python-Native-API.md
@@ -246,6 +246,37 @@ session.execute_query_statement(sql)
 session.execute_non_query_statement(sql)
 ```
 
+
+### 元数据模版接口
+#### 构建元数据模版
+1. 首先构建Template类
+2. 添加子节点,可以选择InternalNode或MeasurementNode
+3. 调用创建元数据模版接口
+
+```python
+template = Template(name="treeTemplate_python", share_time=True)
+
+i_node_gps = InternalNode(name="GPS", share_time=False)
+i_node_v = InternalNode(name="vehicle", share_time=True)
+m_node_x = MeasurementNode("x", TSDataType.FLOAT, TSEncoding.RLE, 
Compressor.SNAPPY)
+
+i_node_gps.add_child(m_node_x)
+i_node_gps.add_child(m_node_x)
+i_node_v.add_child(m_node_x)
+
+template.add_template(i_node_gps)
+template.add_template(i_node_v)
+template.add_template(m_node_x)
+
+session.create_schema_template(template)
+```
+#### 删除元数据模版
+删除已经存在的元数据模版,不支持删除已经挂载的模版
+```python
+session.drop_schema_template("template_python")
+```
+
+
 ### 对 Pandas 的支持
 
 我们支持将查询结果轻松地转换为 [Pandas 
Dataframe](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html)。

Reply via email to