├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── cargo_fmt.yml │ ├── cargo_publish.yml │ └── cargo_test.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── code_gen.sh ├── examples └── iotdb.rs ├── iotdb-rs.png ├── src ├── client.rs ├── common.rs ├── ds.rs ├── errors.rs └── lib.rs └── thrift ├── client.thrift └── common.thrift /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Platform (please complete the following information):** 27 | - OS: [e.g. linux-ubuntu] 28 | - Version [e.g. x.x.x] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## I'm submitting a... 2 | 3 | - [ ] Bug fix 4 | - [ ] Feature 5 | - [ ] Refactoring 6 | - [ ] Added tests 7 | - [ ] Documentation 8 | - [ ] Other (`Please describe in detail here`) 9 | 10 | ## Checklist 11 | 12 | - [ ] Commit Messages follow the [Conventional Commits](https://www.conventionalcommits.org/zh-hans/v1.0.0-beta.4/) pattern 13 | - A feature commit message is prefixed "feat:" 14 | - A bug fix commit message is prefixed "fix:" 15 | - [ ] Tests for the changes have been added 16 | 17 | ## Description 18 | 19 | _please describe the changes that you are making_ 20 | 21 | _for features, please describe how to use the new feature_ 22 | 23 | _please include a reference to an existing issue, if applicable_ 24 | 25 | ## Does this PR introduce a breaking change? 26 | 27 | - [ ] Yes 28 | - [ ] No -------------------------------------------------------------------------------- /.github/workflows/cargo_fmt.yml: -------------------------------------------------------------------------------- 1 | name: cargo-fmt 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: [ main ] 7 | 8 | jobs: 9 | rust-fmt: 10 | name: Rust Fmt 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@master 14 | - name: Install Rust 15 | run: rustup update stable && rustup default stable && rustup component add rustfmt 16 | - run: | 17 | cargo fmt -- --check 18 | cargo clippy -------------------------------------------------------------------------------- /.github/workflows/cargo_publish.yml: -------------------------------------------------------------------------------- 1 | name: cargo-publish 2 | 3 | on: 4 | release: 5 | types: [ created ] 6 | 7 | jobs: 8 | cargo-publish: 9 | name: Cargo Publish 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Publish to crates-io 14 | run: cargo publish --token ${{secrets.PUBLISH_KEY}} --verbose -------------------------------------------------------------------------------- /.github/workflows/cargo_test.yml: -------------------------------------------------------------------------------- 1 | name: cargo-test 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: [ main ] 7 | 8 | jobs: 9 | cargo-test: 10 | name: Run test on ${{ matrix.os }} 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | build: [ stable, nightly, macos, windows ] 15 | include: 16 | - build: stable 17 | os: ubuntu-latest 18 | rust: stable 19 | - build: nightly 20 | os: ubuntu-latest 21 | rust: nightly 22 | - build: macos 23 | os: macos-latest 24 | rust: stable 25 | - build: windows 26 | os: windows-latest 27 | rust: stable 28 | steps: 29 | - uses: actions/checkout@master 30 | - name: Install Rust 31 | run: | 32 | rustup set profile minimal 33 | rustup update --no-self-update ${{ matrix.rust }} 34 | rustup default ${{ matrix.rust }} 35 | rustup -V 36 | rustc -Vv 37 | cargo -V 38 | - name: Build and run tests 39 | run: cargo test --lib iotdb -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dir 2 | /target/ 3 | /.idea/ 4 | /dev/thrift* 5 | 6 | # File 7 | *.iml 8 | Cargo.lock 9 | **/*.rs.bk 10 | *.DS_Store -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iotdb" 3 | version = "0.0.7" 4 | edition = "2021" 5 | license = "Apache-2.0" 6 | readme = "README.md" 7 | authors = ["francis-du "] 8 | repository = "https://github.com/iotdb-lab/iotdb-rs" 9 | documentation = "https://docs.rs/iotdb" 10 | description = "Apache IotDB Client written in Rust" 11 | keywords = ["client", "iotdb", "lib", "db"] 12 | 13 | [dependencies] 14 | byteorder = "1" 15 | chrono = "0.4" 16 | thrift = "0.15" 17 | log = "0.4.13" 18 | prettytable-rs = "^0.8" 19 | polars = "0.19.1" 20 | anyhow = "1.0.53" 21 | thiserror = "1.0" 22 | mimalloc = { version = "0.1", default-features = false } 23 | 24 | [dev-dependencies] 25 | simplelog = "0.11.0" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [francis-du](https://github.com/francis-du) 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ![logo](iotdb-rs.png) 4 | 5 |

iotdb-rs

6 |

Apache IotDB Client written in Rust

7 | 8 | [![Crates.io](https://img.shields.io/crates/v/iotdb?style=flat-square&color=%23E5531A)](https://crates.io/crates/iotdb) 9 | [![Api Docs](https://img.shields.io/badge/Api-Doc-a94064?style=flat-square&color=%23E5531A)](https://docs.rs/iotdb) 10 | [![Crates.io](https://img.shields.io/crates/d/iotdb?style=flat-square&color=%23E5531A)](https://crates.io/crates/iotdb) 11 | [![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square&color=%23E5531A)](https://github.com/iotdb-lab/iotdb-rs/blob/main/LICENSE) 12 | [![Rust Build](https://img.shields.io/github/workflow/status/iotdb-lab/iotdb-rs/cargo-test?label=build&style=flat-square)](https://github.com/iotdb-lab/iotdb-rs/actions?query=workflow%3Acargo-test) 13 | [![Crates Publish](https://img.shields.io/github/workflow/status/iotdb-lab/iotdb-rs/cargo-publish?label=publish&style=flat-square)](https://github.com/iotdb-lab/iotdb-rs/actions?query=workflow%3Acargo-publish) 14 | 15 |
16 | 17 | --- 18 | 19 | [![Alt](https://repobeats.axiom.co/api/embed/15bcb8c6b0f3a63838c7ca62234867b58ec60b28.svg "Repobeats analytics image")](https://github.com/iotdb-lab/iotdb-rs/pulse) 20 | 21 | ## Overview 22 | 23 | IoTDB (Internet of Things Database) is a data management system for time series data, which can provide users specific 24 | services, such as, data collection, storage and analysis. Due to its light weight structure, high performance and usable 25 | features together with its seamless integration with the Hadoop and Spark ecology, IoTDB meets the requirements of 26 | massive dataset storage, high throughput data input, and complex data analysis in the industrial IoT field. 27 | 28 | ## How to use 29 | 30 | Add `iotdb` to your `Cargo.toml` 31 | 32 | ```toml 33 | [dependencies] 34 | iotdb = "0.0.7" 35 | simplelog = "0.11.0" 36 | ``` 37 | 38 | ## Example 39 | 40 | ```rust 41 | use chrono::Local; 42 | 43 | use iotdb::*; 44 | 45 | fn main() -> Result<(), anyhow::Error> { 46 | debug(false); 47 | 48 | let config = iotdb::ConfigBuilder::new() 49 | .endpoint("localhost:6667") 50 | .user("root") 51 | .password("root") 52 | .time_zone("UTC+8") 53 | .build(); 54 | 55 | // open session 56 | let mut session = Session::connect(config)?; 57 | println!("time_zone: {}", session.time_zone()?); 58 | session.delete_storage_group("root.ln")?; 59 | session.set_storage_group("root.ln")?; 60 | session.create_time_series( 61 | "root.ln.wf01.wt01.temperature", 62 | DataType::FLOAT, 63 | Encoding::default(), 64 | Compressor::default(), 65 | )?; 66 | 67 | session.create_time_series( 68 | "root.ln.wf01.wt01.status", 69 | DataType::BOOLEAN, 70 | Encoding::default(), 71 | Compressor::default(), 72 | )?; 73 | 74 | let now = Local::now().timestamp_millis(); 75 | session.sql( 76 | format!( 77 | "INSERT INTO root.ln.wf01.wt01(timestamp,status) values({},true)", 78 | now 79 | ) 80 | .as_str(), 81 | )?; 82 | session.sql( 83 | format!( 84 | "INSERT INTO root.ln.wf01.wt01(timestamp,status) values({},false)", 85 | now + 1000 86 | ) 87 | .as_str(), 88 | )?; 89 | session.sql( 90 | format!( 91 | "INSERT INTO root.ln.wf01.wt01(timestamp,status,temperature) values({},false,18.36)", 92 | now + 2000 93 | ) 94 | .as_str(), 95 | )?; 96 | session.sql( 97 | format!( 98 | "INSERT INTO root.ln.wf01.wt01(timestamp,status,temperature) values({},true,32.23)", 99 | now + 3000 100 | ) 101 | .as_str(), 102 | )?; 103 | session.sql("select * from root.ln")?.show(); 104 | 105 | // DF (TODO) 106 | let df = session.sql("select * from root.ln")?.to_df()?; 107 | println!("IoTDB DF is empty: {}", df.is_empty()); 108 | 109 | session.close()?; 110 | 111 | Ok(()) 112 | } 113 | 114 | fn debug(enable: bool) { 115 | use simplelog::*; 116 | let mut log_level = LevelFilter::Info; 117 | if enable { 118 | log_level = LevelFilter::Debug; 119 | } 120 | let _ = CombinedLogger::init(vec![TermLogger::new( 121 | log_level, 122 | Default::default(), 123 | TerminalMode::Mixed, 124 | ColorChoice::Auto, 125 | )]); 126 | } 127 | ``` 128 | 129 | ## Run example 130 | 131 | 1. Download the package from [here](https://archive.apache.org/dist/iotdb) 132 | 133 | ```shell 134 | curl -O https://archive.apache.org/dist/iotdb/0.11.2/apache-iotdb-0.11.2-bin.zip 135 | ``` 136 | 137 | 2. Install and start iotdb server 138 | 139 | ```shell 140 | cd $IOTDB_HOME && sbin/start-server -c conf -rpc_port 6667 141 | ``` 142 | 143 | 3. Install rust toolchain from [here](https://www.rust-lang.org/tools/install) 144 | 145 | ```shell 146 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 147 | ``` 148 | 149 | 4.Run example 150 | 151 | ```shell 152 | git clone https://github.com/francis-du/iotdb-rs.git 153 | 154 | cargo run --example iotdb 155 | ``` 156 | 157 | ## LICENSE 158 | 159 | [Apache License 2.0](LICENSE) -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | fn main() { 4 | fetch_rpc_file("thrift"); 5 | Command::new("sh") 6 | .args(&[format!( 7 | "{}/code_gen.sh", 8 | std::env::var("CARGO_MANIFEST_DIR").unwrap() 9 | )]) 10 | .output() 11 | .unwrap(); 12 | } 13 | 14 | fn fetch_rpc_file(thrift_dir: &str) { 15 | let urls = [ 16 | ("https://raw.githubusercontent.com/apache/iotdb/master/thrift/src/main/thrift/client.thrift", "client.thrift") 17 | , ("https://raw.githubusercontent.com/apache/iotdb/master/thrift-commons/src/main/thrift/common.thrift", "common.thrift") 18 | ]; 19 | 20 | for url in urls { 21 | let out = format!("{}/{}", thrift_dir, url.1); 22 | match Command::new("curl") 23 | .args(&["-o", out.as_str(), url.0]) 24 | .output() 25 | { 26 | Ok(_) => { 27 | println!("Get thrift file to {:?}", thrift_dir); 28 | } 29 | Err(error) => { 30 | println!("Curl is not installed \n{:?}", error); 31 | } 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /code_gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" 4 | BUILD_DIR="${CURRENT_DIR}/dev" 5 | PKG_VERSION="0.15.0" 6 | PKG_NAME="thrift-${PKG_VERSION}.tar.gz" 7 | OS_RUNNER="thrift" 8 | DEV_RUNNER="${CURRENT_DIR}/dev/thrift" 9 | RUNNER=${OS_RUNNER} 10 | 11 | if [ ! -d "${BUILD_DIR}" ]; then 12 | mkdir "${BUILD_DIR}" 13 | fi 14 | 15 | IS_EXIST_COMMAND=0 16 | function check_command() { 17 | if ! [ -x "$(command -v $1)" ]; then 18 | echo 'ERROR: '$1' is not installed.' 19 | IS_EXIST_COMMAND=0 20 | else 21 | echo 'INFO: '$1' exists.' 22 | IS_EXIST_COMMAND=1 23 | fi 24 | } 25 | 26 | function gen_code() { 27 | file_name=$1 28 | gen_command="${RUNNER} -out ${CURRENT_DIR}/src --gen rs ${CURRENT_DIR}/thrift/${file_name}.thrift" 29 | echo "INFO: Gen command '${gen_command}'" 30 | command ${gen_command} 31 | sleep 3 32 | 33 | if [ -f "${CURRENT_DIR}/src/${file_name}.rs" ]; then 34 | echo "INFO: Gen code to '${CURRENT_DIR}/src'" 35 | else 36 | echo "ERROR: Code gen failed" 37 | fi 38 | } 39 | 40 | function gen() { 41 | gen_code "client" 42 | gen_code "common" 43 | } 44 | 45 | function download_source() { 46 | download_file="${BUILD_DIR}/${PKG_NAME}" 47 | if [ ! -f ${download_file} ]; then 48 | echo "INFO: Download thrift source code to ${download_file}" 49 | curl -o ${download_file} https://downloads.apache.org/thrift/${PKG_VERSION}/${PKG_NAME} 50 | tar xzf ${download_file} -C ${BUILD_DIR} 51 | rm -rf ${download_file} 52 | else 53 | echo "WARN: File ${download_file} exits " 54 | tar xzf ${download_file} -C ${BUILD_DIR} 55 | rm -rf ${download_file} 56 | fi 57 | } 58 | 59 | PKG_DIR=${BUILD_DIR}"/thrift-"${PKG_VERSION} 60 | function build() { 61 | RUNNER=${DEV_RUNNER} 62 | build_file="${BUILD_DIR}/thrift" 63 | 64 | cd ${PKG_DIR} 65 | if [ ! -f ${build_file} ]; then 66 | echo "INFO: Build thrift from $(pwd)" 67 | ./configure --bindir=${BUILD_DIR} && make install 68 | 69 | if [ ! -f ${build_file} ]; then 70 | echo "ERROR: Build error, please retry" 71 | else 72 | chmod +x ${build_file} 73 | echo "INFO: Build successful. $(command ${build_file} -version)" 74 | gen 75 | fi 76 | else 77 | echo "INFO: Thrift exits. $(command ${build_file} -version)" 78 | gen 79 | fi 80 | } 81 | 82 | function build_from_source() { 83 | if [ -d ${PKG_DIR} ]; then 84 | build 85 | else 86 | download_source 87 | build 88 | fi 89 | } 90 | 91 | function fetch() { 92 | if [[ "$OSTYPE" == "linux-gnu" ]]; then 93 | if [ -f /etc/redhat-release ]; then 94 | echo "Redhat Linux" 95 | build_from_source 96 | elif [ -f /etc/SuSE-release ]; then 97 | echo "Suse Linux" 98 | build_from_source 99 | elif [ -f /etc/arch-release ]; then 100 | echo "Arch Linux" 101 | build_from_source 102 | elif [ -f /etc/mandrake-release ]; then 103 | echo "Mandrake Linux" 104 | build_from_source 105 | elif [ -f /etc/debian_version ]; then 106 | echo "Ubuntu/Debian Linux" && check_command "apt-get" 107 | if [ ${IS_EXIST_COMMAND} == 1 ]; then 108 | sudo apt-get install thrift-compiler 109 | gen 110 | else 111 | build_from_source 112 | fi 113 | else 114 | echo "Unknown Linux distribution." 115 | build_from_source 116 | fi 117 | elif [[ "$OSTYPE" == "darwin"* ]]; then 118 | echo "Mac OS (Darwin)" && check_command "brew" 119 | if [ ${IS_EXIST_COMMAND} == 1 ]; then 120 | brew reinstall thrift && thrift -version && gen 121 | else 122 | build_from_source 123 | fi 124 | elif [[ "$OSTYPE" == "freebsd"* ]]; then 125 | echo "FreeBSD" 126 | build_from_source 127 | else 128 | echo "Unknown operating system." 129 | build_from_source 130 | fi 131 | } 132 | 133 | function run() { 134 | check_command ${OS_RUNNER} 135 | if [ ${IS_EXIST_COMMAND} == 1 ]; then 136 | ${OS_RUNNER} -version && gen 137 | else 138 | fetch 139 | fi 140 | } 141 | 142 | run 143 | -------------------------------------------------------------------------------- /examples/iotdb.rs: -------------------------------------------------------------------------------- 1 | use chrono::Local; 2 | 3 | use iotdb::*; 4 | 5 | fn main() -> Result<(), anyhow::Error> { 6 | debug(false); 7 | 8 | let config = ConfigBuilder::new() 9 | .endpoint("119.84.128.59:6667") 10 | .user("root") 11 | .password("root") 12 | .time_zone("UTC+8") 13 | .build(); 14 | 15 | // open session 16 | let mut session = Session::connect(config)?; 17 | println!( 18 | "Server version {:#?}", 19 | session.get_properties()?.version.as_str() 20 | ); 21 | println!("Time Zone: {}", session.time_zone()?); 22 | session.delete_storage_group("root.ln")?; 23 | session.set_storage_group("root.ln")?; 24 | session.create_time_series( 25 | "root.ln.wf01.wt01.temperature", 26 | DataType::FLOAT, 27 | Encoding::default(), 28 | Compressor::default(), 29 | )?; 30 | 31 | session.create_time_series( 32 | "root.ln.wf01.wt01.status", 33 | DataType::BOOLEAN, 34 | Encoding::default(), 35 | Compressor::default(), 36 | )?; 37 | 38 | let now = Local::now().timestamp_millis(); 39 | session.sql( 40 | format!( 41 | "INSERT INTO root.ln.wf01.wt01(timestamp,status) values({},true)", 42 | now 43 | ) 44 | .as_str(), 45 | )?; 46 | session.sql( 47 | format!( 48 | "INSERT INTO root.ln.wf01.wt01(timestamp,status) values({},false)", 49 | now + 1000 50 | ) 51 | .as_str(), 52 | )?; 53 | session.sql( 54 | format!( 55 | "INSERT INTO root.ln.wf01.wt01(timestamp,status,temperature) values({},false,18.36)", 56 | now + 2000 57 | ) 58 | .as_str(), 59 | )?; 60 | session.sql( 61 | format!( 62 | "INSERT INTO root.ln.wf01.wt01(timestamp,status,temperature) values({},true,32.23)", 63 | now + 3000 64 | ) 65 | .as_str(), 66 | )?; 67 | session.sql("select * from root.ln")?.show(); 68 | 69 | // DF (TODO) 70 | let df = session.sql("select * from root.ln")?.to_df()?; 71 | println!("IoTDB DF is empty: {}", df.is_empty()); 72 | 73 | session.close()?; 74 | 75 | Ok(()) 76 | } 77 | 78 | fn debug(enable: bool) { 79 | use simplelog::*; 80 | let mut log_level = LevelFilter::Info; 81 | if enable { 82 | log_level = LevelFilter::Debug; 83 | } 84 | let _ = CombinedLogger::init(vec![TermLogger::new( 85 | log_level, 86 | Default::default(), 87 | TerminalMode::Mixed, 88 | ColorChoice::Auto, 89 | )]); 90 | } 91 | -------------------------------------------------------------------------------- /iotdb-rs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iotdb-lab/iotdb-rs/a3e35ab83f190a32d25d06e889f3366992d13092/iotdb-rs.png -------------------------------------------------------------------------------- /src/common.rs: -------------------------------------------------------------------------------- 1 | // Autogenerated by Thrift Compiler (0.16.0) 2 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 3 | 4 | #![allow(unused_imports)] 5 | #![allow(unused_extern_crates)] 6 | #![allow(clippy::too_many_arguments, clippy::type_complexity, clippy::vec_box)] 7 | #![cfg_attr(rustfmt, rustfmt_skip)] 8 | 9 | use std::cell::RefCell; 10 | use std::collections::{BTreeMap, BTreeSet}; 11 | use std::convert::{From, TryFrom}; 12 | use std::default::Default; 13 | use std::error::Error; 14 | use std::fmt; 15 | use std::fmt::{Display, Formatter}; 16 | use std::rc::Rc; 17 | 18 | use thrift::OrderedFloat; 19 | use thrift::{ApplicationError, ApplicationErrorKind, ProtocolError, ProtocolErrorKind, TThriftClient}; 20 | use thrift::protocol::{TFieldIdentifier, TListIdentifier, TMapIdentifier, TMessageIdentifier, TMessageType, TInputProtocol, TOutputProtocol, TSetIdentifier, TStructIdentifier, TType}; 21 | use thrift::protocol::field_id; 22 | use thrift::protocol::verify_expected_message_type; 23 | use thrift::protocol::verify_expected_sequence_number; 24 | use thrift::protocol::verify_expected_service_call; 25 | use thrift::protocol::verify_required_field_exists; 26 | use thrift::server::TProcessor; 27 | 28 | #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 29 | pub struct TConsensusGroupType(pub i32); 30 | 31 | impl TConsensusGroupType { 32 | pub const PARTITION_REGION: TConsensusGroupType = TConsensusGroupType(0); 33 | pub const DATA_REGION: TConsensusGroupType = TConsensusGroupType(1); 34 | pub const SCHEMA_REGION: TConsensusGroupType = TConsensusGroupType(2); 35 | pub const ENUM_VALUES: &'static [Self] = &[ 36 | Self::PARTITION_REGION, 37 | Self::DATA_REGION, 38 | Self::SCHEMA_REGION, 39 | ]; 40 | #[allow(clippy::trivially_copy_pass_by_ref)] 41 | pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> { 42 | o_prot.write_i32(self.0) 43 | } 44 | pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { 45 | let enum_value = i_prot.read_i32()?; 46 | Ok(TConsensusGroupType::from(enum_value)) 47 | } 48 | } 49 | 50 | impl From for TConsensusGroupType { 51 | fn from(i: i32) -> Self { 52 | match i { 53 | 0 => TConsensusGroupType::PARTITION_REGION, 54 | 1 => TConsensusGroupType::DATA_REGION, 55 | 2 => TConsensusGroupType::SCHEMA_REGION, 56 | _ => TConsensusGroupType(i) 57 | } 58 | } 59 | } 60 | 61 | impl From<&i32> for TConsensusGroupType { 62 | fn from(i: &i32) -> Self { 63 | TConsensusGroupType::from(*i) 64 | } 65 | } 66 | 67 | impl From for i32 { 68 | fn from(e: TConsensusGroupType) -> i32 { 69 | e.0 70 | } 71 | } 72 | 73 | impl From<&TConsensusGroupType> for i32 { 74 | fn from(e: &TConsensusGroupType) -> i32 { 75 | e.0 76 | } 77 | } 78 | 79 | #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 80 | pub struct TRegionMigrateFailedType(pub i32); 81 | 82 | impl TRegionMigrateFailedType { 83 | pub const ADD_PEER_FAILED: TRegionMigrateFailedType = TRegionMigrateFailedType(0); 84 | pub const REMOVE_PEER_FAILED: TRegionMigrateFailedType = TRegionMigrateFailedType(1); 85 | pub const REMOVE_CONSENSUS_GROUP_FAILED: TRegionMigrateFailedType = TRegionMigrateFailedType(2); 86 | pub const DELETE_REGION_FAILED: TRegionMigrateFailedType = TRegionMigrateFailedType(3); 87 | pub const CREATE_REGION_FAILED: TRegionMigrateFailedType = TRegionMigrateFailedType(4); 88 | pub const ENUM_VALUES: &'static [Self] = &[ 89 | Self::ADD_PEER_FAILED, 90 | Self::REMOVE_PEER_FAILED, 91 | Self::REMOVE_CONSENSUS_GROUP_FAILED, 92 | Self::DELETE_REGION_FAILED, 93 | Self::CREATE_REGION_FAILED, 94 | ]; 95 | #[allow(clippy::trivially_copy_pass_by_ref)] 96 | pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> { 97 | o_prot.write_i32(self.0) 98 | } 99 | pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { 100 | let enum_value = i_prot.read_i32()?; 101 | Ok(TRegionMigrateFailedType::from(enum_value)) 102 | } 103 | } 104 | 105 | impl From for TRegionMigrateFailedType { 106 | fn from(i: i32) -> Self { 107 | match i { 108 | 0 => TRegionMigrateFailedType::ADD_PEER_FAILED, 109 | 1 => TRegionMigrateFailedType::REMOVE_PEER_FAILED, 110 | 2 => TRegionMigrateFailedType::REMOVE_CONSENSUS_GROUP_FAILED, 111 | 3 => TRegionMigrateFailedType::DELETE_REGION_FAILED, 112 | 4 => TRegionMigrateFailedType::CREATE_REGION_FAILED, 113 | _ => TRegionMigrateFailedType(i) 114 | } 115 | } 116 | } 117 | 118 | impl From<&i32> for TRegionMigrateFailedType { 119 | fn from(i: &i32) -> Self { 120 | TRegionMigrateFailedType::from(*i) 121 | } 122 | } 123 | 124 | impl From for i32 { 125 | fn from(e: TRegionMigrateFailedType) -> i32 { 126 | e.0 127 | } 128 | } 129 | 130 | impl From<&TRegionMigrateFailedType> for i32 { 131 | fn from(e: &TRegionMigrateFailedType) -> i32 { 132 | e.0 133 | } 134 | } 135 | 136 | // 137 | // TEndPoint 138 | // 139 | 140 | #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 141 | pub struct TEndPoint { 142 | pub ip: String, 143 | pub port: i32, 144 | } 145 | 146 | impl TEndPoint { 147 | pub fn new(ip: String, port: i32) -> TEndPoint { 148 | TEndPoint { 149 | ip, 150 | port, 151 | } 152 | } 153 | pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { 154 | i_prot.read_struct_begin()?; 155 | let mut f_1: Option = None; 156 | let mut f_2: Option = None; 157 | loop { 158 | let field_ident = i_prot.read_field_begin()?; 159 | if field_ident.field_type == TType::Stop { 160 | break; 161 | } 162 | let field_id = field_id(&field_ident)?; 163 | match field_id { 164 | 1 => { 165 | let val = i_prot.read_string()?; 166 | f_1 = Some(val); 167 | }, 168 | 2 => { 169 | let val = i_prot.read_i32()?; 170 | f_2 = Some(val); 171 | }, 172 | _ => { 173 | i_prot.skip(field_ident.field_type)?; 174 | }, 175 | }; 176 | i_prot.read_field_end()?; 177 | } 178 | i_prot.read_struct_end()?; 179 | verify_required_field_exists("TEndPoint.ip", &f_1)?; 180 | verify_required_field_exists("TEndPoint.port", &f_2)?; 181 | let ret = TEndPoint { 182 | ip: f_1.expect("auto-generated code should have checked for presence of required fields"), 183 | port: f_2.expect("auto-generated code should have checked for presence of required fields"), 184 | }; 185 | Ok(ret) 186 | } 187 | pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> { 188 | let struct_ident = TStructIdentifier::new("TEndPoint"); 189 | o_prot.write_struct_begin(&struct_ident)?; 190 | o_prot.write_field_begin(&TFieldIdentifier::new("ip", TType::String, 1))?; 191 | o_prot.write_string(&self.ip)?; 192 | o_prot.write_field_end()?; 193 | o_prot.write_field_begin(&TFieldIdentifier::new("port", TType::I32, 2))?; 194 | o_prot.write_i32(self.port)?; 195 | o_prot.write_field_end()?; 196 | o_prot.write_field_stop()?; 197 | o_prot.write_struct_end() 198 | } 199 | } 200 | 201 | // 202 | // TSStatus 203 | // 204 | 205 | #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 206 | pub struct TSStatus { 207 | pub code: i32, 208 | pub message: Option, 209 | pub sub_status: Option>>, 210 | pub redirect_node: Option, 211 | } 212 | 213 | impl TSStatus { 214 | pub fn new(code: i32, message: F2, sub_status: F3, redirect_node: F4) -> TSStatus where F2: Into>, F3: Into>>>, F4: Into> { 215 | TSStatus { 216 | code, 217 | message: message.into(), 218 | sub_status: sub_status.into(), 219 | redirect_node: redirect_node.into(), 220 | } 221 | } 222 | pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { 223 | i_prot.read_struct_begin()?; 224 | let mut f_1: Option = None; 225 | let mut f_2: Option = None; 226 | let mut f_3: Option>> = None; 227 | let mut f_4: Option = None; 228 | loop { 229 | let field_ident = i_prot.read_field_begin()?; 230 | if field_ident.field_type == TType::Stop { 231 | break; 232 | } 233 | let field_id = field_id(&field_ident)?; 234 | match field_id { 235 | 1 => { 236 | let val = i_prot.read_i32()?; 237 | f_1 = Some(val); 238 | }, 239 | 2 => { 240 | let val = i_prot.read_string()?; 241 | f_2 = Some(val); 242 | }, 243 | 3 => { 244 | let list_ident = i_prot.read_list_begin()?; 245 | let mut val: Vec> = Vec::with_capacity(list_ident.size as usize); 246 | for _ in 0..list_ident.size { 247 | let list_elem_0 = Box::new(TSStatus::read_from_in_protocol(i_prot)?); 248 | val.push(list_elem_0); 249 | } 250 | i_prot.read_list_end()?; 251 | f_3 = Some(val); 252 | }, 253 | 4 => { 254 | let val = TEndPoint::read_from_in_protocol(i_prot)?; 255 | f_4 = Some(val); 256 | }, 257 | _ => { 258 | i_prot.skip(field_ident.field_type)?; 259 | }, 260 | }; 261 | i_prot.read_field_end()?; 262 | } 263 | i_prot.read_struct_end()?; 264 | verify_required_field_exists("TSStatus.code", &f_1)?; 265 | let ret = TSStatus { 266 | code: f_1.expect("auto-generated code should have checked for presence of required fields"), 267 | message: f_2, 268 | sub_status: f_3, 269 | redirect_node: f_4, 270 | }; 271 | Ok(ret) 272 | } 273 | pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> { 274 | let struct_ident = TStructIdentifier::new("TSStatus"); 275 | o_prot.write_struct_begin(&struct_ident)?; 276 | o_prot.write_field_begin(&TFieldIdentifier::new("code", TType::I32, 1))?; 277 | o_prot.write_i32(self.code)?; 278 | o_prot.write_field_end()?; 279 | if let Some(ref fld_var) = self.message { 280 | o_prot.write_field_begin(&TFieldIdentifier::new("message", TType::String, 2))?; 281 | o_prot.write_string(fld_var)?; 282 | o_prot.write_field_end()? 283 | } 284 | if let Some(ref fld_var) = self.sub_status { 285 | o_prot.write_field_begin(&TFieldIdentifier::new("subStatus", TType::List, 3))?; 286 | o_prot.write_list_begin(&TListIdentifier::new(TType::Struct, fld_var.len() as i32))?; 287 | for e in fld_var { 288 | e.write_to_out_protocol(o_prot)?; 289 | } 290 | o_prot.write_list_end()?; 291 | o_prot.write_field_end()? 292 | } 293 | if let Some(ref fld_var) = self.redirect_node { 294 | o_prot.write_field_begin(&TFieldIdentifier::new("redirectNode", TType::Struct, 4))?; 295 | fld_var.write_to_out_protocol(o_prot)?; 296 | o_prot.write_field_end()? 297 | } 298 | o_prot.write_field_stop()?; 299 | o_prot.write_struct_end() 300 | } 301 | } 302 | 303 | // 304 | // TConsensusGroupId 305 | // 306 | 307 | #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 308 | pub struct TConsensusGroupId { 309 | pub type_: TConsensusGroupType, 310 | pub id: i32, 311 | } 312 | 313 | impl TConsensusGroupId { 314 | pub fn new(type_: TConsensusGroupType, id: i32) -> TConsensusGroupId { 315 | TConsensusGroupId { 316 | type_, 317 | id, 318 | } 319 | } 320 | pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { 321 | i_prot.read_struct_begin()?; 322 | let mut f_1: Option = None; 323 | let mut f_2: Option = None; 324 | loop { 325 | let field_ident = i_prot.read_field_begin()?; 326 | if field_ident.field_type == TType::Stop { 327 | break; 328 | } 329 | let field_id = field_id(&field_ident)?; 330 | match field_id { 331 | 1 => { 332 | let val = TConsensusGroupType::read_from_in_protocol(i_prot)?; 333 | f_1 = Some(val); 334 | }, 335 | 2 => { 336 | let val = i_prot.read_i32()?; 337 | f_2 = Some(val); 338 | }, 339 | _ => { 340 | i_prot.skip(field_ident.field_type)?; 341 | }, 342 | }; 343 | i_prot.read_field_end()?; 344 | } 345 | i_prot.read_struct_end()?; 346 | verify_required_field_exists("TConsensusGroupId.type_", &f_1)?; 347 | verify_required_field_exists("TConsensusGroupId.id", &f_2)?; 348 | let ret = TConsensusGroupId { 349 | type_: f_1.expect("auto-generated code should have checked for presence of required fields"), 350 | id: f_2.expect("auto-generated code should have checked for presence of required fields"), 351 | }; 352 | Ok(ret) 353 | } 354 | pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> { 355 | let struct_ident = TStructIdentifier::new("TConsensusGroupId"); 356 | o_prot.write_struct_begin(&struct_ident)?; 357 | o_prot.write_field_begin(&TFieldIdentifier::new("type", TType::I32, 1))?; 358 | self.type_.write_to_out_protocol(o_prot)?; 359 | o_prot.write_field_end()?; 360 | o_prot.write_field_begin(&TFieldIdentifier::new("id", TType::I32, 2))?; 361 | o_prot.write_i32(self.id)?; 362 | o_prot.write_field_end()?; 363 | o_prot.write_field_stop()?; 364 | o_prot.write_struct_end() 365 | } 366 | } 367 | 368 | // 369 | // TSeriesPartitionSlot 370 | // 371 | 372 | #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 373 | pub struct TSeriesPartitionSlot { 374 | pub slot_id: i32, 375 | } 376 | 377 | impl TSeriesPartitionSlot { 378 | pub fn new(slot_id: i32) -> TSeriesPartitionSlot { 379 | TSeriesPartitionSlot { 380 | slot_id, 381 | } 382 | } 383 | pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { 384 | i_prot.read_struct_begin()?; 385 | let mut f_1: Option = None; 386 | loop { 387 | let field_ident = i_prot.read_field_begin()?; 388 | if field_ident.field_type == TType::Stop { 389 | break; 390 | } 391 | let field_id = field_id(&field_ident)?; 392 | match field_id { 393 | 1 => { 394 | let val = i_prot.read_i32()?; 395 | f_1 = Some(val); 396 | }, 397 | _ => { 398 | i_prot.skip(field_ident.field_type)?; 399 | }, 400 | }; 401 | i_prot.read_field_end()?; 402 | } 403 | i_prot.read_struct_end()?; 404 | verify_required_field_exists("TSeriesPartitionSlot.slot_id", &f_1)?; 405 | let ret = TSeriesPartitionSlot { 406 | slot_id: f_1.expect("auto-generated code should have checked for presence of required fields"), 407 | }; 408 | Ok(ret) 409 | } 410 | pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> { 411 | let struct_ident = TStructIdentifier::new("TSeriesPartitionSlot"); 412 | o_prot.write_struct_begin(&struct_ident)?; 413 | o_prot.write_field_begin(&TFieldIdentifier::new("slotId", TType::I32, 1))?; 414 | o_prot.write_i32(self.slot_id)?; 415 | o_prot.write_field_end()?; 416 | o_prot.write_field_stop()?; 417 | o_prot.write_struct_end() 418 | } 419 | } 420 | 421 | // 422 | // TTimePartitionSlot 423 | // 424 | 425 | #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 426 | pub struct TTimePartitionSlot { 427 | pub start_time: i64, 428 | } 429 | 430 | impl TTimePartitionSlot { 431 | pub fn new(start_time: i64) -> TTimePartitionSlot { 432 | TTimePartitionSlot { 433 | start_time, 434 | } 435 | } 436 | pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { 437 | i_prot.read_struct_begin()?; 438 | let mut f_1: Option = None; 439 | loop { 440 | let field_ident = i_prot.read_field_begin()?; 441 | if field_ident.field_type == TType::Stop { 442 | break; 443 | } 444 | let field_id = field_id(&field_ident)?; 445 | match field_id { 446 | 1 => { 447 | let val = i_prot.read_i64()?; 448 | f_1 = Some(val); 449 | }, 450 | _ => { 451 | i_prot.skip(field_ident.field_type)?; 452 | }, 453 | }; 454 | i_prot.read_field_end()?; 455 | } 456 | i_prot.read_struct_end()?; 457 | verify_required_field_exists("TTimePartitionSlot.start_time", &f_1)?; 458 | let ret = TTimePartitionSlot { 459 | start_time: f_1.expect("auto-generated code should have checked for presence of required fields"), 460 | }; 461 | Ok(ret) 462 | } 463 | pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> { 464 | let struct_ident = TStructIdentifier::new("TTimePartitionSlot"); 465 | o_prot.write_struct_begin(&struct_ident)?; 466 | o_prot.write_field_begin(&TFieldIdentifier::new("startTime", TType::I64, 1))?; 467 | o_prot.write_i64(self.start_time)?; 468 | o_prot.write_field_end()?; 469 | o_prot.write_field_stop()?; 470 | o_prot.write_struct_end() 471 | } 472 | } 473 | 474 | // 475 | // TRegionReplicaSet 476 | // 477 | 478 | #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 479 | pub struct TRegionReplicaSet { 480 | pub region_id: TConsensusGroupId, 481 | pub data_node_locations: Vec>, 482 | } 483 | 484 | impl TRegionReplicaSet { 485 | pub fn new(region_id: TConsensusGroupId, data_node_locations: Vec>) -> TRegionReplicaSet { 486 | TRegionReplicaSet { 487 | region_id, 488 | data_node_locations, 489 | } 490 | } 491 | pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { 492 | i_prot.read_struct_begin()?; 493 | let mut f_1: Option = None; 494 | let mut f_2: Option>> = None; 495 | loop { 496 | let field_ident = i_prot.read_field_begin()?; 497 | if field_ident.field_type == TType::Stop { 498 | break; 499 | } 500 | let field_id = field_id(&field_ident)?; 501 | match field_id { 502 | 1 => { 503 | let val = TConsensusGroupId::read_from_in_protocol(i_prot)?; 504 | f_1 = Some(val); 505 | }, 506 | 2 => { 507 | let list_ident = i_prot.read_list_begin()?; 508 | let mut val: Vec> = Vec::with_capacity(list_ident.size as usize); 509 | for _ in 0..list_ident.size { 510 | let list_elem_1 = Box::new(TDataNodeLocation::read_from_in_protocol(i_prot)?); 511 | val.push(list_elem_1); 512 | } 513 | i_prot.read_list_end()?; 514 | f_2 = Some(val); 515 | }, 516 | _ => { 517 | i_prot.skip(field_ident.field_type)?; 518 | }, 519 | }; 520 | i_prot.read_field_end()?; 521 | } 522 | i_prot.read_struct_end()?; 523 | verify_required_field_exists("TRegionReplicaSet.region_id", &f_1)?; 524 | verify_required_field_exists("TRegionReplicaSet.data_node_locations", &f_2)?; 525 | let ret = TRegionReplicaSet { 526 | region_id: f_1.expect("auto-generated code should have checked for presence of required fields"), 527 | data_node_locations: f_2.expect("auto-generated code should have checked for presence of required fields"), 528 | }; 529 | Ok(ret) 530 | } 531 | pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> { 532 | let struct_ident = TStructIdentifier::new("TRegionReplicaSet"); 533 | o_prot.write_struct_begin(&struct_ident)?; 534 | o_prot.write_field_begin(&TFieldIdentifier::new("regionId", TType::Struct, 1))?; 535 | self.region_id.write_to_out_protocol(o_prot)?; 536 | o_prot.write_field_end()?; 537 | o_prot.write_field_begin(&TFieldIdentifier::new("dataNodeLocations", TType::List, 2))?; 538 | o_prot.write_list_begin(&TListIdentifier::new(TType::Struct, self.data_node_locations.len() as i32))?; 539 | for e in &self.data_node_locations { 540 | e.write_to_out_protocol(o_prot)?; 541 | } 542 | o_prot.write_list_end()?; 543 | o_prot.write_field_end()?; 544 | o_prot.write_field_stop()?; 545 | o_prot.write_struct_end() 546 | } 547 | } 548 | 549 | // 550 | // TNodeResource 551 | // 552 | 553 | #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 554 | pub struct TNodeResource { 555 | pub cpu_core_num: i32, 556 | pub max_memory: i64, 557 | } 558 | 559 | impl TNodeResource { 560 | pub fn new(cpu_core_num: i32, max_memory: i64) -> TNodeResource { 561 | TNodeResource { 562 | cpu_core_num, 563 | max_memory, 564 | } 565 | } 566 | pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { 567 | i_prot.read_struct_begin()?; 568 | let mut f_1: Option = None; 569 | let mut f_2: Option = None; 570 | loop { 571 | let field_ident = i_prot.read_field_begin()?; 572 | if field_ident.field_type == TType::Stop { 573 | break; 574 | } 575 | let field_id = field_id(&field_ident)?; 576 | match field_id { 577 | 1 => { 578 | let val = i_prot.read_i32()?; 579 | f_1 = Some(val); 580 | }, 581 | 2 => { 582 | let val = i_prot.read_i64()?; 583 | f_2 = Some(val); 584 | }, 585 | _ => { 586 | i_prot.skip(field_ident.field_type)?; 587 | }, 588 | }; 589 | i_prot.read_field_end()?; 590 | } 591 | i_prot.read_struct_end()?; 592 | verify_required_field_exists("TNodeResource.cpu_core_num", &f_1)?; 593 | verify_required_field_exists("TNodeResource.max_memory", &f_2)?; 594 | let ret = TNodeResource { 595 | cpu_core_num: f_1.expect("auto-generated code should have checked for presence of required fields"), 596 | max_memory: f_2.expect("auto-generated code should have checked for presence of required fields"), 597 | }; 598 | Ok(ret) 599 | } 600 | pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> { 601 | let struct_ident = TStructIdentifier::new("TNodeResource"); 602 | o_prot.write_struct_begin(&struct_ident)?; 603 | o_prot.write_field_begin(&TFieldIdentifier::new("cpuCoreNum", TType::I32, 1))?; 604 | o_prot.write_i32(self.cpu_core_num)?; 605 | o_prot.write_field_end()?; 606 | o_prot.write_field_begin(&TFieldIdentifier::new("maxMemory", TType::I64, 2))?; 607 | o_prot.write_i64(self.max_memory)?; 608 | o_prot.write_field_end()?; 609 | o_prot.write_field_stop()?; 610 | o_prot.write_struct_end() 611 | } 612 | } 613 | 614 | // 615 | // TConfigNodeLocation 616 | // 617 | 618 | #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 619 | pub struct TConfigNodeLocation { 620 | pub config_node_id: i32, 621 | pub internal_end_point: TEndPoint, 622 | pub consensus_end_point: TEndPoint, 623 | } 624 | 625 | impl TConfigNodeLocation { 626 | pub fn new(config_node_id: i32, internal_end_point: TEndPoint, consensus_end_point: TEndPoint) -> TConfigNodeLocation { 627 | TConfigNodeLocation { 628 | config_node_id, 629 | internal_end_point, 630 | consensus_end_point, 631 | } 632 | } 633 | pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { 634 | i_prot.read_struct_begin()?; 635 | let mut f_1: Option = None; 636 | let mut f_2: Option = None; 637 | let mut f_3: Option = None; 638 | loop { 639 | let field_ident = i_prot.read_field_begin()?; 640 | if field_ident.field_type == TType::Stop { 641 | break; 642 | } 643 | let field_id = field_id(&field_ident)?; 644 | match field_id { 645 | 1 => { 646 | let val = i_prot.read_i32()?; 647 | f_1 = Some(val); 648 | }, 649 | 2 => { 650 | let val = TEndPoint::read_from_in_protocol(i_prot)?; 651 | f_2 = Some(val); 652 | }, 653 | 3 => { 654 | let val = TEndPoint::read_from_in_protocol(i_prot)?; 655 | f_3 = Some(val); 656 | }, 657 | _ => { 658 | i_prot.skip(field_ident.field_type)?; 659 | }, 660 | }; 661 | i_prot.read_field_end()?; 662 | } 663 | i_prot.read_struct_end()?; 664 | verify_required_field_exists("TConfigNodeLocation.config_node_id", &f_1)?; 665 | verify_required_field_exists("TConfigNodeLocation.internal_end_point", &f_2)?; 666 | verify_required_field_exists("TConfigNodeLocation.consensus_end_point", &f_3)?; 667 | let ret = TConfigNodeLocation { 668 | config_node_id: f_1.expect("auto-generated code should have checked for presence of required fields"), 669 | internal_end_point: f_2.expect("auto-generated code should have checked for presence of required fields"), 670 | consensus_end_point: f_3.expect("auto-generated code should have checked for presence of required fields"), 671 | }; 672 | Ok(ret) 673 | } 674 | pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> { 675 | let struct_ident = TStructIdentifier::new("TConfigNodeLocation"); 676 | o_prot.write_struct_begin(&struct_ident)?; 677 | o_prot.write_field_begin(&TFieldIdentifier::new("configNodeId", TType::I32, 1))?; 678 | o_prot.write_i32(self.config_node_id)?; 679 | o_prot.write_field_end()?; 680 | o_prot.write_field_begin(&TFieldIdentifier::new("internalEndPoint", TType::Struct, 2))?; 681 | self.internal_end_point.write_to_out_protocol(o_prot)?; 682 | o_prot.write_field_end()?; 683 | o_prot.write_field_begin(&TFieldIdentifier::new("consensusEndPoint", TType::Struct, 3))?; 684 | self.consensus_end_point.write_to_out_protocol(o_prot)?; 685 | o_prot.write_field_end()?; 686 | o_prot.write_field_stop()?; 687 | o_prot.write_struct_end() 688 | } 689 | } 690 | 691 | // 692 | // TDataNodeLocation 693 | // 694 | 695 | #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 696 | pub struct TDataNodeLocation { 697 | pub data_node_id: i32, 698 | pub client_rpc_end_point: TEndPoint, 699 | pub internal_end_point: TEndPoint, 700 | pub m_p_p_data_exchange_end_point: TEndPoint, 701 | pub data_region_consensus_end_point: TEndPoint, 702 | pub schema_region_consensus_end_point: TEndPoint, 703 | } 704 | 705 | impl TDataNodeLocation { 706 | pub fn new(data_node_id: i32, client_rpc_end_point: TEndPoint, internal_end_point: TEndPoint, m_p_p_data_exchange_end_point: TEndPoint, data_region_consensus_end_point: TEndPoint, schema_region_consensus_end_point: TEndPoint) -> TDataNodeLocation { 707 | TDataNodeLocation { 708 | data_node_id, 709 | client_rpc_end_point, 710 | internal_end_point, 711 | m_p_p_data_exchange_end_point, 712 | data_region_consensus_end_point, 713 | schema_region_consensus_end_point, 714 | } 715 | } 716 | pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { 717 | i_prot.read_struct_begin()?; 718 | let mut f_1: Option = None; 719 | let mut f_2: Option = None; 720 | let mut f_3: Option = None; 721 | let mut f_4: Option = None; 722 | let mut f_5: Option = None; 723 | let mut f_6: Option = None; 724 | loop { 725 | let field_ident = i_prot.read_field_begin()?; 726 | if field_ident.field_type == TType::Stop { 727 | break; 728 | } 729 | let field_id = field_id(&field_ident)?; 730 | match field_id { 731 | 1 => { 732 | let val = i_prot.read_i32()?; 733 | f_1 = Some(val); 734 | }, 735 | 2 => { 736 | let val = TEndPoint::read_from_in_protocol(i_prot)?; 737 | f_2 = Some(val); 738 | }, 739 | 3 => { 740 | let val = TEndPoint::read_from_in_protocol(i_prot)?; 741 | f_3 = Some(val); 742 | }, 743 | 4 => { 744 | let val = TEndPoint::read_from_in_protocol(i_prot)?; 745 | f_4 = Some(val); 746 | }, 747 | 5 => { 748 | let val = TEndPoint::read_from_in_protocol(i_prot)?; 749 | f_5 = Some(val); 750 | }, 751 | 6 => { 752 | let val = TEndPoint::read_from_in_protocol(i_prot)?; 753 | f_6 = Some(val); 754 | }, 755 | _ => { 756 | i_prot.skip(field_ident.field_type)?; 757 | }, 758 | }; 759 | i_prot.read_field_end()?; 760 | } 761 | i_prot.read_struct_end()?; 762 | verify_required_field_exists("TDataNodeLocation.data_node_id", &f_1)?; 763 | verify_required_field_exists("TDataNodeLocation.client_rpc_end_point", &f_2)?; 764 | verify_required_field_exists("TDataNodeLocation.internal_end_point", &f_3)?; 765 | verify_required_field_exists("TDataNodeLocation.m_p_p_data_exchange_end_point", &f_4)?; 766 | verify_required_field_exists("TDataNodeLocation.data_region_consensus_end_point", &f_5)?; 767 | verify_required_field_exists("TDataNodeLocation.schema_region_consensus_end_point", &f_6)?; 768 | let ret = TDataNodeLocation { 769 | data_node_id: f_1.expect("auto-generated code should have checked for presence of required fields"), 770 | client_rpc_end_point: f_2.expect("auto-generated code should have checked for presence of required fields"), 771 | internal_end_point: f_3.expect("auto-generated code should have checked for presence of required fields"), 772 | m_p_p_data_exchange_end_point: f_4.expect("auto-generated code should have checked for presence of required fields"), 773 | data_region_consensus_end_point: f_5.expect("auto-generated code should have checked for presence of required fields"), 774 | schema_region_consensus_end_point: f_6.expect("auto-generated code should have checked for presence of required fields"), 775 | }; 776 | Ok(ret) 777 | } 778 | pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> { 779 | let struct_ident = TStructIdentifier::new("TDataNodeLocation"); 780 | o_prot.write_struct_begin(&struct_ident)?; 781 | o_prot.write_field_begin(&TFieldIdentifier::new("dataNodeId", TType::I32, 1))?; 782 | o_prot.write_i32(self.data_node_id)?; 783 | o_prot.write_field_end()?; 784 | o_prot.write_field_begin(&TFieldIdentifier::new("clientRpcEndPoint", TType::Struct, 2))?; 785 | self.client_rpc_end_point.write_to_out_protocol(o_prot)?; 786 | o_prot.write_field_end()?; 787 | o_prot.write_field_begin(&TFieldIdentifier::new("internalEndPoint", TType::Struct, 3))?; 788 | self.internal_end_point.write_to_out_protocol(o_prot)?; 789 | o_prot.write_field_end()?; 790 | o_prot.write_field_begin(&TFieldIdentifier::new("mPPDataExchangeEndPoint", TType::Struct, 4))?; 791 | self.m_p_p_data_exchange_end_point.write_to_out_protocol(o_prot)?; 792 | o_prot.write_field_end()?; 793 | o_prot.write_field_begin(&TFieldIdentifier::new("dataRegionConsensusEndPoint", TType::Struct, 5))?; 794 | self.data_region_consensus_end_point.write_to_out_protocol(o_prot)?; 795 | o_prot.write_field_end()?; 796 | o_prot.write_field_begin(&TFieldIdentifier::new("schemaRegionConsensusEndPoint", TType::Struct, 6))?; 797 | self.schema_region_consensus_end_point.write_to_out_protocol(o_prot)?; 798 | o_prot.write_field_end()?; 799 | o_prot.write_field_stop()?; 800 | o_prot.write_struct_end() 801 | } 802 | } 803 | 804 | // 805 | // TDataNodeConfiguration 806 | // 807 | 808 | #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 809 | pub struct TDataNodeConfiguration { 810 | pub location: TDataNodeLocation, 811 | pub resource: TNodeResource, 812 | } 813 | 814 | impl TDataNodeConfiguration { 815 | pub fn new(location: TDataNodeLocation, resource: TNodeResource) -> TDataNodeConfiguration { 816 | TDataNodeConfiguration { 817 | location, 818 | resource, 819 | } 820 | } 821 | pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { 822 | i_prot.read_struct_begin()?; 823 | let mut f_1: Option = None; 824 | let mut f_2: Option = None; 825 | loop { 826 | let field_ident = i_prot.read_field_begin()?; 827 | if field_ident.field_type == TType::Stop { 828 | break; 829 | } 830 | let field_id = field_id(&field_ident)?; 831 | match field_id { 832 | 1 => { 833 | let val = TDataNodeLocation::read_from_in_protocol(i_prot)?; 834 | f_1 = Some(val); 835 | }, 836 | 2 => { 837 | let val = TNodeResource::read_from_in_protocol(i_prot)?; 838 | f_2 = Some(val); 839 | }, 840 | _ => { 841 | i_prot.skip(field_ident.field_type)?; 842 | }, 843 | }; 844 | i_prot.read_field_end()?; 845 | } 846 | i_prot.read_struct_end()?; 847 | verify_required_field_exists("TDataNodeConfiguration.location", &f_1)?; 848 | verify_required_field_exists("TDataNodeConfiguration.resource", &f_2)?; 849 | let ret = TDataNodeConfiguration { 850 | location: f_1.expect("auto-generated code should have checked for presence of required fields"), 851 | resource: f_2.expect("auto-generated code should have checked for presence of required fields"), 852 | }; 853 | Ok(ret) 854 | } 855 | pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> { 856 | let struct_ident = TStructIdentifier::new("TDataNodeConfiguration"); 857 | o_prot.write_struct_begin(&struct_ident)?; 858 | o_prot.write_field_begin(&TFieldIdentifier::new("location", TType::Struct, 1))?; 859 | self.location.write_to_out_protocol(o_prot)?; 860 | o_prot.write_field_end()?; 861 | o_prot.write_field_begin(&TFieldIdentifier::new("resource", TType::Struct, 2))?; 862 | self.resource.write_to_out_protocol(o_prot)?; 863 | o_prot.write_field_end()?; 864 | o_prot.write_field_stop()?; 865 | o_prot.write_struct_end() 866 | } 867 | } 868 | 869 | // 870 | // TFlushReq 871 | // 872 | 873 | #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 874 | pub struct TFlushReq { 875 | pub is_seq: Option, 876 | pub storage_groups: Option>, 877 | pub data_node_id: Option, 878 | } 879 | 880 | impl TFlushReq { 881 | pub fn new(is_seq: F1, storage_groups: F2, data_node_id: F3) -> TFlushReq where F1: Into>, F2: Into>>, F3: Into> { 882 | TFlushReq { 883 | is_seq: is_seq.into(), 884 | storage_groups: storage_groups.into(), 885 | data_node_id: data_node_id.into(), 886 | } 887 | } 888 | pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { 889 | i_prot.read_struct_begin()?; 890 | let mut f_1: Option = None; 891 | let mut f_2: Option> = None; 892 | let mut f_3: Option = None; 893 | loop { 894 | let field_ident = i_prot.read_field_begin()?; 895 | if field_ident.field_type == TType::Stop { 896 | break; 897 | } 898 | let field_id = field_id(&field_ident)?; 899 | match field_id { 900 | 1 => { 901 | let val = i_prot.read_string()?; 902 | f_1 = Some(val); 903 | }, 904 | 2 => { 905 | let list_ident = i_prot.read_list_begin()?; 906 | let mut val: Vec = Vec::with_capacity(list_ident.size as usize); 907 | for _ in 0..list_ident.size { 908 | let list_elem_2 = i_prot.read_string()?; 909 | val.push(list_elem_2); 910 | } 911 | i_prot.read_list_end()?; 912 | f_2 = Some(val); 913 | }, 914 | 3 => { 915 | let val = i_prot.read_i32()?; 916 | f_3 = Some(val); 917 | }, 918 | _ => { 919 | i_prot.skip(field_ident.field_type)?; 920 | }, 921 | }; 922 | i_prot.read_field_end()?; 923 | } 924 | i_prot.read_struct_end()?; 925 | let ret = TFlushReq { 926 | is_seq: f_1, 927 | storage_groups: f_2, 928 | data_node_id: f_3, 929 | }; 930 | Ok(ret) 931 | } 932 | pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> { 933 | let struct_ident = TStructIdentifier::new("TFlushReq"); 934 | o_prot.write_struct_begin(&struct_ident)?; 935 | if let Some(ref fld_var) = self.is_seq { 936 | o_prot.write_field_begin(&TFieldIdentifier::new("isSeq", TType::String, 1))?; 937 | o_prot.write_string(fld_var)?; 938 | o_prot.write_field_end()? 939 | } 940 | if let Some(ref fld_var) = self.storage_groups { 941 | o_prot.write_field_begin(&TFieldIdentifier::new("storageGroups", TType::List, 2))?; 942 | o_prot.write_list_begin(&TListIdentifier::new(TType::String, fld_var.len() as i32))?; 943 | for e in fld_var { 944 | o_prot.write_string(e)?; 945 | } 946 | o_prot.write_list_end()?; 947 | o_prot.write_field_end()? 948 | } 949 | if let Some(fld_var) = self.data_node_id { 950 | o_prot.write_field_begin(&TFieldIdentifier::new("dataNodeId", TType::I32, 3))?; 951 | o_prot.write_i32(fld_var)?; 952 | o_prot.write_field_end()? 953 | } 954 | o_prot.write_field_stop()?; 955 | o_prot.write_struct_end() 956 | } 957 | } 958 | 959 | impl Default for TFlushReq { 960 | fn default() -> Self { 961 | TFlushReq{ 962 | is_seq: Some("".to_owned()), 963 | storage_groups: Some(Vec::new()), 964 | data_node_id: Some(0), 965 | } 966 | } 967 | } 968 | 969 | // 970 | // TSchemaNode 971 | // 972 | 973 | #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 974 | pub struct TSchemaNode { 975 | pub node_name: String, 976 | pub node_type: i8, 977 | } 978 | 979 | impl TSchemaNode { 980 | pub fn new(node_name: String, node_type: i8) -> TSchemaNode { 981 | TSchemaNode { 982 | node_name, 983 | node_type, 984 | } 985 | } 986 | pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { 987 | i_prot.read_struct_begin()?; 988 | let mut f_1: Option = None; 989 | let mut f_2: Option = None; 990 | loop { 991 | let field_ident = i_prot.read_field_begin()?; 992 | if field_ident.field_type == TType::Stop { 993 | break; 994 | } 995 | let field_id = field_id(&field_ident)?; 996 | match field_id { 997 | 1 => { 998 | let val = i_prot.read_string()?; 999 | f_1 = Some(val); 1000 | }, 1001 | 2 => { 1002 | let val = i_prot.read_i8()?; 1003 | f_2 = Some(val); 1004 | }, 1005 | _ => { 1006 | i_prot.skip(field_ident.field_type)?; 1007 | }, 1008 | }; 1009 | i_prot.read_field_end()?; 1010 | } 1011 | i_prot.read_struct_end()?; 1012 | verify_required_field_exists("TSchemaNode.node_name", &f_1)?; 1013 | verify_required_field_exists("TSchemaNode.node_type", &f_2)?; 1014 | let ret = TSchemaNode { 1015 | node_name: f_1.expect("auto-generated code should have checked for presence of required fields"), 1016 | node_type: f_2.expect("auto-generated code should have checked for presence of required fields"), 1017 | }; 1018 | Ok(ret) 1019 | } 1020 | pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> { 1021 | let struct_ident = TStructIdentifier::new("TSchemaNode"); 1022 | o_prot.write_struct_begin(&struct_ident)?; 1023 | o_prot.write_field_begin(&TFieldIdentifier::new("nodeName", TType::String, 1))?; 1024 | o_prot.write_string(&self.node_name)?; 1025 | o_prot.write_field_end()?; 1026 | o_prot.write_field_begin(&TFieldIdentifier::new("nodeType", TType::I08, 2))?; 1027 | o_prot.write_i8(self.node_type)?; 1028 | o_prot.write_field_end()?; 1029 | o_prot.write_field_stop()?; 1030 | o_prot.write_struct_end() 1031 | } 1032 | } 1033 | 1034 | // 1035 | // TSetTTLReq 1036 | // 1037 | 1038 | #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 1039 | pub struct TSetTTLReq { 1040 | pub storage_group_path_pattern: Vec, 1041 | pub t_t_l: i64, 1042 | } 1043 | 1044 | impl TSetTTLReq { 1045 | pub fn new(storage_group_path_pattern: Vec, t_t_l: i64) -> TSetTTLReq { 1046 | TSetTTLReq { 1047 | storage_group_path_pattern, 1048 | t_t_l, 1049 | } 1050 | } 1051 | pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { 1052 | i_prot.read_struct_begin()?; 1053 | let mut f_1: Option> = None; 1054 | let mut f_2: Option = None; 1055 | loop { 1056 | let field_ident = i_prot.read_field_begin()?; 1057 | if field_ident.field_type == TType::Stop { 1058 | break; 1059 | } 1060 | let field_id = field_id(&field_ident)?; 1061 | match field_id { 1062 | 1 => { 1063 | let list_ident = i_prot.read_list_begin()?; 1064 | let mut val: Vec = Vec::with_capacity(list_ident.size as usize); 1065 | for _ in 0..list_ident.size { 1066 | let list_elem_3 = i_prot.read_string()?; 1067 | val.push(list_elem_3); 1068 | } 1069 | i_prot.read_list_end()?; 1070 | f_1 = Some(val); 1071 | }, 1072 | 2 => { 1073 | let val = i_prot.read_i64()?; 1074 | f_2 = Some(val); 1075 | }, 1076 | _ => { 1077 | i_prot.skip(field_ident.field_type)?; 1078 | }, 1079 | }; 1080 | i_prot.read_field_end()?; 1081 | } 1082 | i_prot.read_struct_end()?; 1083 | verify_required_field_exists("TSetTTLReq.storage_group_path_pattern", &f_1)?; 1084 | verify_required_field_exists("TSetTTLReq.t_t_l", &f_2)?; 1085 | let ret = TSetTTLReq { 1086 | storage_group_path_pattern: f_1.expect("auto-generated code should have checked for presence of required fields"), 1087 | t_t_l: f_2.expect("auto-generated code should have checked for presence of required fields"), 1088 | }; 1089 | Ok(ret) 1090 | } 1091 | pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> { 1092 | let struct_ident = TStructIdentifier::new("TSetTTLReq"); 1093 | o_prot.write_struct_begin(&struct_ident)?; 1094 | o_prot.write_field_begin(&TFieldIdentifier::new("storageGroupPathPattern", TType::List, 1))?; 1095 | o_prot.write_list_begin(&TListIdentifier::new(TType::String, self.storage_group_path_pattern.len() as i32))?; 1096 | for e in &self.storage_group_path_pattern { 1097 | o_prot.write_string(e)?; 1098 | } 1099 | o_prot.write_list_end()?; 1100 | o_prot.write_field_end()?; 1101 | o_prot.write_field_begin(&TFieldIdentifier::new("TTL", TType::I64, 2))?; 1102 | o_prot.write_i64(self.t_t_l)?; 1103 | o_prot.write_field_end()?; 1104 | o_prot.write_field_stop()?; 1105 | o_prot.write_struct_end() 1106 | } 1107 | } 1108 | 1109 | -------------------------------------------------------------------------------- /src/ds.rs: -------------------------------------------------------------------------------- 1 | use std::io::Cursor; 2 | 3 | use byteorder::{BigEndian, ReadBytesExt}; 4 | use chrono::{DateTime, Local, TimeZone}; 5 | use log::debug; 6 | use polars::prelude::*; 7 | use polars::prelude::{DataFrame, Series}; 8 | use prettytable::Row as PrettyRow; 9 | use prettytable::{Cell, Table}; 10 | 11 | use crate::client::TSExecuteStatementResp; 12 | use crate::DataType; 13 | 14 | #[derive(Clone, Debug)] 15 | pub struct Field { 16 | data_type: DataType, 17 | pub bool_value: Option, 18 | pub int_value: Option, 19 | pub long_value: Option, 20 | pub float_value: Option, 21 | pub double_value: Option, 22 | pub binary_value: Option>, 23 | } 24 | 25 | impl Field { 26 | pub fn new(data_type: DataType) -> Field { 27 | Self { 28 | data_type, 29 | bool_value: None, 30 | int_value: None, 31 | long_value: None, 32 | float_value: None, 33 | double_value: None, 34 | binary_value: None, 35 | } 36 | } 37 | } 38 | 39 | #[derive(Clone, Debug, Default)] 40 | pub struct ValueRow { 41 | timestamp: i64, 42 | fields: Vec, 43 | } 44 | 45 | impl ValueRow { 46 | pub fn new() -> Self { 47 | Self::default() 48 | } 49 | 50 | pub fn timestamp(&mut self, timestamp: i64) -> &mut ValueRow { 51 | self.timestamp = timestamp; 52 | self 53 | } 54 | 55 | pub fn add_field(&mut self, field: Field) -> &mut ValueRow { 56 | self.fields.push(field); 57 | self 58 | } 59 | } 60 | 61 | #[derive(Clone, Debug)] 62 | pub struct RecordBatch { 63 | columns: Vec, 64 | values: Vec, 65 | is_empty: bool, 66 | } 67 | 68 | impl Default for RecordBatch { 69 | fn default() -> Self { 70 | Self { 71 | columns: vec![], 72 | values: vec![], 73 | is_empty: true, 74 | } 75 | } 76 | } 77 | 78 | impl RecordBatch { 79 | fn new(columns: Vec, values: Vec) -> Self { 80 | Self { 81 | columns, 82 | values, 83 | is_empty: false, 84 | } 85 | } 86 | } 87 | 88 | #[derive(Clone, Debug, Default)] 89 | pub struct DataSet { 90 | record_batch: RecordBatch, 91 | ignore_time_stamp: Option, 92 | } 93 | 94 | impl DataSet { 95 | pub(crate) fn new(resp: TSExecuteStatementResp) -> DataSet { 96 | debug!("{:#?}", resp); 97 | // set data_types 98 | let data_types: Vec = match resp.data_type_list.clone() { 99 | None => vec![], 100 | Some(data_type_list) => { 101 | let mut tmp_data_types: Vec = Vec::new(); 102 | data_type_list.iter().for_each(|data_type| { 103 | let ds_data_type = DataType::from(data_type.as_str()); 104 | tmp_data_types.push(ds_data_type); 105 | }); 106 | tmp_data_types 107 | } 108 | }; 109 | 110 | let record_batch = if resp.query_data_set.is_some() { 111 | Self::resp_to_rows(resp.clone(), data_types) 112 | } else { 113 | RecordBatch::default() 114 | }; 115 | 116 | Self { 117 | record_batch, 118 | ignore_time_stamp: resp.ignore_time_stamp, 119 | } 120 | } 121 | 122 | fn resp_to_rows(resp: TSExecuteStatementResp, data_types: Vec) -> RecordBatch { 123 | const FLAG: i32 = 0x80; 124 | let query_data_set = resp.query_data_set.unwrap(); 125 | let columns = resp.columns.unwrap(); 126 | let bitmap_buffer = query_data_set.bitmap_list; 127 | let mut value_list = query_data_set.value_list; 128 | 129 | let mut values: Vec = Vec::new(); 130 | let mut row_num = 0; 131 | loop { 132 | let sum_len: usize = value_list.iter().map(|value| value.len()).sum(); 133 | if sum_len == 0 { 134 | break; 135 | } 136 | 137 | // construct time field 138 | let mut value_row: ValueRow = ValueRow::new(); 139 | let mut time = query_data_set.time.clone(); 140 | if !time.is_empty() { 141 | let mut time_buffer = Cursor::new(time.drain(..8).collect::>()); 142 | value_row.timestamp(time_buffer.read_i64::().unwrap()); 143 | } 144 | 145 | // construct value field 146 | for col_index in 0..columns.len() { 147 | // add a new field 148 | let column_name = columns[col_index].clone(); 149 | let data_type = data_types[col_index]; 150 | let mut field = Field::new(data_type); 151 | 152 | // reset column name index 153 | let col_index = match resp.column_name_index_map.clone() { 154 | None => col_index, 155 | Some(column_name_index_map) => *column_name_index_map 156 | .get(column_name.as_str()) 157 | .unwrap_or(&(col_index as i32)) 158 | as usize, 159 | }; 160 | 161 | // is NaN value 162 | let bitmap = bitmap_buffer[col_index][0] as i32; 163 | let is_null = ((FLAG >> (row_num % 8)) & (bitmap & 0xFF)) == 0; 164 | 165 | if !is_null { 166 | match data_type { 167 | DataType::BOOLEAN => { 168 | field.bool_value = Some(value_list[col_index][0].eq(&1)); 169 | value_list[col_index].remove(0); 170 | } 171 | DataType::INT32 => { 172 | let mut buffer = 173 | Cursor::new(value_list[col_index].drain(..4).collect::>()); 174 | field.int_value = Some(buffer.read_i32::().unwrap()); 175 | } 176 | DataType::INT64 => { 177 | let mut buffer = 178 | Cursor::new(value_list[col_index].drain(..8).collect::>()); 179 | field.long_value = Some(buffer.read_i64::().unwrap()); 180 | } 181 | DataType::FLOAT => { 182 | let mut buffer = 183 | Cursor::new(value_list[col_index].drain(..4).collect::>()); 184 | field.float_value = Some(buffer.read_f32::().unwrap()); 185 | } 186 | DataType::DOUBLE => { 187 | let mut buffer = 188 | Cursor::new(value_list[col_index].drain(..8).collect::>()); 189 | field.double_value = Some(buffer.read_f64::().unwrap()); 190 | } 191 | DataType::TEXT => { 192 | let mut length_buffer = 193 | Cursor::new(value_list[col_index].drain(..4).collect::>()); 194 | let length = length_buffer.read_i32::().unwrap() as usize; 195 | let binary = value_list[col_index].drain(..length).collect::>(); 196 | field.binary_value = Some(binary); 197 | } 198 | } 199 | } 200 | value_row.add_field(field); 201 | } 202 | row_num += 1; 203 | values.push(value_row); 204 | } 205 | 206 | RecordBatch::new(columns, values) 207 | } 208 | 209 | pub fn to_df(&self) -> Result { 210 | let columns: Vec = Vec::new(); 211 | 212 | DataFrame::new(columns) 213 | } 214 | 215 | pub fn show(&mut self) { 216 | let mut batch = self.record_batch.clone(); 217 | debug!("{:#?}", &batch); 218 | 219 | let mut table: Table = Table::new(); 220 | table.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE); 221 | if !batch.is_empty { 222 | let ignore_time_stamp = self.ignore_time_stamp.unwrap_or(false); 223 | 224 | // add col name row 225 | if !ignore_time_stamp { 226 | batch.columns.insert(0, "Time".to_string()); 227 | } 228 | let mut col_name_cells: Vec = Vec::new(); 229 | batch 230 | .columns 231 | .iter() 232 | .for_each(|col_name| col_name_cells.push(cell!(col_name))); 233 | table.set_titles(PrettyRow::new(col_name_cells)); 234 | 235 | // add value rows 236 | batch.values.iter().for_each(|row| { 237 | let mut value_cells: Vec = Vec::new(); 238 | if !ignore_time_stamp { 239 | let dt: DateTime = Local.timestamp_millis(row.timestamp); 240 | value_cells.push(cell!(dt.to_string())); 241 | } 242 | 243 | row.fields.iter().for_each(|field| match field.data_type { 244 | DataType::BOOLEAN => match field.bool_value { 245 | None => value_cells.push(cell!("NaN")), 246 | Some(bool_value) => value_cells.push(cell!(bool_value)), 247 | }, 248 | DataType::INT32 => match field.int_value { 249 | None => value_cells.push(cell!("NaN")), 250 | Some(int_value) => value_cells.push(cell!(int_value)), 251 | }, 252 | DataType::INT64 => match field.long_value { 253 | None => value_cells.push(cell!("NaN")), 254 | Some(long_value) => value_cells.push(cell!(long_value)), 255 | }, 256 | DataType::FLOAT => match field.float_value { 257 | None => value_cells.push(cell!("NaN")), 258 | Some(float_value) => value_cells.push(cell!(float_value)), 259 | }, 260 | DataType::DOUBLE => match field.double_value { 261 | None => value_cells.push(cell!("NaN")), 262 | Some(double_value) => value_cells.push(cell!(double_value)), 263 | }, 264 | DataType::TEXT => { 265 | match field.clone().binary_value { 266 | None => value_cells.push(cell!("NaN")), 267 | Some(binary) => { 268 | value_cells.push(cell!(String::from_utf8(binary).unwrap())) 269 | } 270 | }; 271 | } 272 | }); 273 | 274 | table.add_row(PrettyRow::new(value_cells)); 275 | }) 276 | } 277 | if !table.is_empty() { 278 | table.printstd(); 279 | } 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | #[derive(Error, Debug)] 4 | pub enum IotDBError { 5 | #[error("IO error: {0}")] 6 | IO(#[from] std::io::Error), 7 | 8 | #[error("IoTDB thrift rpc connection error")] 9 | Thrift(#[from] thrift::Error), 10 | 11 | #[error("IoTDB Polars DataFrame error")] 12 | Polars(#[from] polars::error::PolarsError), 13 | } 14 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! ![Logo](https://raw.githubusercontent.com/francis-du/iotdb-rs/main/iotdb-rs.png) 2 | //! 3 | //! [![Crates.io](https://img.shields.io/crates/v/iotdb?style=flat-square&color=%23E5531A)](https://crates.io/crates/iotdb) 4 | //! [![Api Docs](https://img.shields.io/badge/Api-Doc-a94064?style=flat-square&color=%23E5531A)](https://docs.rs/iotdb) 5 | //! [![Crates.io](https://img.shields.io/crates/d/iotdb?style=flat-square&color=%23E5531A)](https://crates.io/crates/iotdb) 6 | //! [![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square&color=%23E5531A)](https://github.com/iotdb-lab/iotdb-rs/blob/main/LICENSE) 7 | //! [![Rust Build](https://img.shields.io/github/workflow/status/iotdb-lab/iotdb-rs/cargo-test?label=build&style=flat-square)](https://github.com/iotdb-lab/iotdb-rs/actions?query=workflow%3Acargo-test) 8 | //! [![Crates Publish](https://img.shields.io/github/workflow/status/iotdb-lab/iotdb-rs/cargo-publish?label=publish&style=flat-square)](https://github.com/iotdb-lab/iotdb-rs/actions?query=workflow%3Acargo-publish) 9 | //! 10 | //! Apache IotDB Client written in Rust 11 | //! 12 | //! # Overview 13 | //! 14 | //! IoTDB (Internet of Things Database) is a data management system for time series data, which can provide users specific services, 15 | //! such as, data collection, storage and analysis. Due to its light weight structure, high performance and usable features together 16 | //! with its seamless integration with the Hadoop and Spark ecology, IoTDB meets the requirements of massive dataset storage, 17 | //! high throughput data input, and complex data analysis in the industrial IoT field. 18 | //! 19 | //! # How to use 20 | //! 21 | //! Add `iotdb` to your `Cargo.toml` 22 | //! 23 | //! ```toml 24 | //! [dependencies] 25 | //! iotdb = "0.0.7" 26 | //! simplelog = "0.11.0" 27 | //! ``` 28 | //! 29 | //! # Example 30 | //! 31 | //! ```rust 32 | //! use chrono::Local; 33 | //! 34 | //! use iotdb::*; 35 | //! 36 | //! fn main() -> Result<(), anyhow::Error> { 37 | //! debug(false); 38 | //! 39 | //! let config = iotdb::ConfigBuilder::new() 40 | //! .endpoint("localhost:6667") 41 | //! .user("root") 42 | //! .password("root") 43 | //! .time_zone("UTC+8") 44 | //! .build(); 45 | //! 46 | //! // open session 47 | //! let mut session = Session::connect(config)?; 48 | //! println!("time_zone: {}", session.time_zone()?); 49 | //! session.delete_storage_group("root.ln")?; 50 | //! session.set_storage_group("root.ln")?; 51 | //! session.create_time_series( 52 | //! "root.ln.wf01.wt01.temperature", 53 | //! DataType::FLOAT, 54 | //! Encoding::default(), 55 | //! Compressor::default(), 56 | //! )?; 57 | //! 58 | //! session.create_time_series( 59 | //! "root.ln.wf01.wt01.status", 60 | //! DataType::BOOLEAN, 61 | //! Encoding::default(), 62 | //! Compressor::default(), 63 | //! )?; 64 | //! 65 | //! let now = Local::now().timestamp_millis(); 66 | //! session.sql( 67 | //! format!( 68 | //! "INSERT INTO root.ln.wf01.wt01(timestamp,status) values({},true)", 69 | //! now 70 | //! ) 71 | //! .as_str(), 72 | //! )?; 73 | //! session.sql( 74 | //! format!( 75 | //! "INSERT INTO root.ln.wf01.wt01(timestamp,status) values({},false)", 76 | //! now + 1000 77 | //! ) 78 | //! .as_str(), 79 | //! )?; 80 | //! session.sql( 81 | //! format!( 82 | //! "INSERT INTO root.ln.wf01.wt01(timestamp,status,temperature) values({},false,18.36)", 83 | //! now + 2000 84 | //! ) 85 | //! .as_str(), 86 | //! )?; 87 | //! session.sql( 88 | //! format!( 89 | //! "INSERT INTO root.ln.wf01.wt01(timestamp,status,temperature) values({},true,32.23)", 90 | //! now + 3000 91 | //! ) 92 | //! .as_str(), 93 | //! )?; 94 | //! session.sql("select * from root.ln")?.show(); 95 | //! 96 | //! // DF (TODO) 97 | //! let df = session.sql("select * from root.ln")?.to_df()?; 98 | //! println!("IoTDB DF is empty: {}", df.is_empty()); 99 | //! 100 | //! session.close()?; 101 | //! 102 | //! Ok(()) 103 | //! } 104 | //! 105 | //! fn debug(enable: bool) { 106 | //! use simplelog::*; 107 | //! let mut log_level = LevelFilter::Info; 108 | //! if enable { 109 | //! log_level = LevelFilter::Debug; 110 | //! } 111 | //! let _ = CombinedLogger::init(vec![TermLogger::new( 112 | //! log_level, 113 | //! Default::default(), 114 | //! TerminalMode::Mixed, 115 | //! ColorChoice::Auto, 116 | //! )]); 117 | //! } 118 | //! ``` 119 | #[macro_use] 120 | extern crate prettytable; 121 | 122 | use std::collections::BTreeMap; 123 | use std::net::TcpStream; 124 | use std::ptr::addr_of_mut; 125 | use std::str::FromStr; 126 | 127 | use anyhow::bail; 128 | pub use chrono; 129 | use chrono::{Local, Utc}; 130 | use log::{debug, error, info}; 131 | use mimalloc::MiMalloc; 132 | pub use polars; 133 | pub use thrift; 134 | use thrift::protocol::*; 135 | use thrift::transport::*; 136 | 137 | use crate::client::*; 138 | use crate::common::*; 139 | use crate::ds::DataSet; 140 | 141 | mod client; 142 | mod common; 143 | mod ds; 144 | mod errors; 145 | 146 | #[global_allocator] 147 | static GLOBAL: MiMalloc = MiMalloc; 148 | const SUCCESS_CODE: i32 = 200; 149 | 150 | type ClientType = IClientRPCServiceSyncClient, Box>; 151 | 152 | /// IotDB datatype enum 153 | #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 154 | pub enum DataType { 155 | BOOLEAN, 156 | INT32, 157 | INT64, 158 | FLOAT, 159 | DOUBLE, 160 | TEXT, 161 | } 162 | 163 | impl From<&String> for DataType { 164 | fn from(value: &String) -> Self { 165 | match value.as_str() { 166 | "BOOLEAN" => DataType::BOOLEAN, 167 | "INT32" => DataType::INT32, 168 | "INT64" => DataType::INT64, 169 | "FLOAT" => DataType::FLOAT, 170 | "DOUBLE" => DataType::DOUBLE, 171 | "TEXT" => DataType::TEXT, 172 | _ => panic!("This '{}' data type doesn't exist", value), 173 | } 174 | } 175 | } 176 | 177 | impl From<&str> for DataType { 178 | fn from(value: &str) -> Self { 179 | match value { 180 | "BOOLEAN" => DataType::BOOLEAN, 181 | "INT32" => DataType::INT32, 182 | "INT64" => DataType::INT64, 183 | "FLOAT" => DataType::FLOAT, 184 | "DOUBLE" => DataType::DOUBLE, 185 | "TEXT" => DataType::TEXT, 186 | _ => panic!("This '{}' data type doesn't exist", value), 187 | } 188 | } 189 | } 190 | 191 | #[allow(clippy::from_over_into)] 192 | impl Into for DataType { 193 | fn into(self) -> i32 { 194 | match self { 195 | DataType::BOOLEAN => 0, 196 | DataType::INT32 => 1, 197 | DataType::INT64 => 2, 198 | DataType::FLOAT => 3, 199 | DataType::DOUBLE => 4, 200 | DataType::TEXT => 5, 201 | } 202 | } 203 | } 204 | 205 | /// IotDB encoding enum 206 | #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 207 | pub enum Encoding { 208 | PLAIN, 209 | PlainDictionary, 210 | RLE, 211 | DIFF, 212 | Ts2diff, 213 | BITMAP, 214 | GorillaV1, 215 | REGULAR, 216 | GORILLA, 217 | } 218 | 219 | impl Default for Encoding { 220 | fn default() -> Self { 221 | Encoding::PLAIN 222 | } 223 | } 224 | 225 | impl From for Encoding { 226 | fn from(value: i32) -> Self { 227 | match value { 228 | 0 => Encoding::PLAIN, 229 | 1 => Encoding::PlainDictionary, 230 | 2 => Encoding::RLE, 231 | 3 => Encoding::DIFF, 232 | 4 => Encoding::Ts2diff, 233 | 5 => Encoding::BITMAP, 234 | 6 => Encoding::GorillaV1, 235 | 7 => Encoding::REGULAR, 236 | 8 => Encoding::GORILLA, 237 | _ => panic!("This '{}' encoding doesn't exist", value), 238 | } 239 | } 240 | } 241 | 242 | impl From for Encoding { 243 | fn from(value: String) -> Self { 244 | match value.as_str() { 245 | "PLAIN" => Encoding::PLAIN, 246 | "PlainDictionary" => Encoding::PlainDictionary, 247 | "RLE" => Encoding::RLE, 248 | "DIFF" => Encoding::DIFF, 249 | "Ts2diff" => Encoding::Ts2diff, 250 | "BITMAP" => Encoding::BITMAP, 251 | "GorillaV1" => Encoding::GorillaV1, 252 | "REGULAR" => Encoding::REGULAR, 253 | "GORILLA" => Encoding::GORILLA, 254 | _ => panic!("This '{}' encoding doesn't exist", value), 255 | } 256 | } 257 | } 258 | 259 | #[allow(clippy::from_over_into)] 260 | impl Into for Encoding { 261 | fn into(self) -> i32 { 262 | match self { 263 | Encoding::PLAIN => 0, 264 | Encoding::PlainDictionary => 1, 265 | Encoding::RLE => 2, 266 | Encoding::DIFF => 3, 267 | Encoding::Ts2diff => 4, 268 | Encoding::BITMAP => 5, 269 | Encoding::GorillaV1 => 6, 270 | Encoding::REGULAR => 7, 271 | Encoding::GORILLA => 8, 272 | } 273 | } 274 | } 275 | 276 | /// IotDB compressor enum 277 | #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 278 | pub enum Compressor { 279 | UNCOMPRESSED, 280 | SNAPPY, 281 | GZIP, 282 | LZO, 283 | SDT, 284 | PAA, 285 | PLA, 286 | LZ4, 287 | } 288 | 289 | impl Default for Compressor { 290 | fn default() -> Self { 291 | Compressor::SNAPPY 292 | } 293 | } 294 | 295 | impl From for Compressor { 296 | fn from(value: i32) -> Self { 297 | match value { 298 | 0 => Compressor::UNCOMPRESSED, 299 | 1 => Compressor::SNAPPY, 300 | 2 => Compressor::GZIP, 301 | 3 => Compressor::LZO, 302 | 4 => Compressor::SDT, 303 | 5 => Compressor::PAA, 304 | 6 => Compressor::PLA, 305 | 7 => Compressor::LZ4, 306 | _ => panic!("This '{}' compressor doesn't exist", value), 307 | } 308 | } 309 | } 310 | 311 | impl From<&str> for Compressor { 312 | fn from(value: &str) -> Self { 313 | match value { 314 | "UNCOMPRESSED" => Compressor::UNCOMPRESSED, 315 | "SNAPPY" => Compressor::SNAPPY, 316 | "GZIP" => Compressor::GZIP, 317 | "LZO" => Compressor::LZO, 318 | "SDT" => Compressor::SDT, 319 | "PAA" => Compressor::PAA, 320 | "PLA" => Compressor::PLA, 321 | "LZ4" => Compressor::LZ4, 322 | _ => panic!("This '{}' compressor doesn't exist", value), 323 | } 324 | } 325 | } 326 | 327 | impl From for Compressor { 328 | fn from(value: String) -> Self { 329 | match value.as_str() { 330 | "UNCOMPRESSED" => Compressor::UNCOMPRESSED, 331 | "SNAPPY" => Compressor::SNAPPY, 332 | "GZIP" => Compressor::GZIP, 333 | "LZO" => Compressor::LZO, 334 | "SDT" => Compressor::SDT, 335 | "PAA" => Compressor::PAA, 336 | "PLA" => Compressor::PLA, 337 | "LZ4" => Compressor::LZ4, 338 | _ => panic!("This '{}' compressor doesn't exist", value), 339 | } 340 | } 341 | } 342 | 343 | #[allow(clippy::from_over_into)] 344 | impl Into for Compressor { 345 | fn into(self) -> i32 { 346 | match self { 347 | Compressor::UNCOMPRESSED => 0, 348 | Compressor::SNAPPY => 1, 349 | Compressor::GZIP => 2, 350 | Compressor::LZO => 3, 351 | Compressor::SDT => 4, 352 | Compressor::PAA => 5, 353 | Compressor::PLA => 6, 354 | Compressor::LZ4 => 7, 355 | } 356 | } 357 | } 358 | 359 | /// Session Endpoint 360 | #[derive(Clone, Debug)] 361 | pub struct Endpoint { 362 | pub host: String, 363 | pub port: String, 364 | } 365 | 366 | impl FromStr for Endpoint { 367 | type Err = anyhow::Error; 368 | 369 | fn from_str(str: &str) -> anyhow::Result { 370 | let host_port: Vec<&str> = str.split(':').collect(); 371 | if host_port.is_empty() || host_port.len() != 2 { 372 | bail!("Endpoint format error, endpoint: '{}'", str) 373 | // panic!("Endpoint format error, endpoint: '{}'", str) 374 | } else { 375 | Ok(Self { 376 | host: String::from(host_port[0]), 377 | port: String::from(host_port[1]), 378 | }) 379 | } 380 | } 381 | } 382 | 383 | impl Default for Endpoint { 384 | fn default() -> Self { 385 | "127.0.0.1:6667".parse::().unwrap() 386 | } 387 | } 388 | 389 | impl Endpoint { 390 | pub fn new(host: &str, port: &str) -> Self { 391 | Self { 392 | host: String::from(host), 393 | port: String::from(port), 394 | } 395 | } 396 | } 397 | 398 | impl ToString for Endpoint { 399 | fn to_string(&self) -> String { 400 | format!("{}:{}", self.host, self.port) 401 | } 402 | } 403 | 404 | /// IotDB Config 405 | #[derive(Clone, Debug)] 406 | pub struct Config { 407 | pub user: String, 408 | pub password: String, 409 | pub time_zone: String, 410 | pub timeout: i64, 411 | pub fetch_size: i32, 412 | pub endpoint: Endpoint, 413 | pub rpc_compaction: bool, 414 | pub protocol_version: TSProtocolVersion, 415 | pub enable_redirect_query: bool, 416 | pub config_map: BTreeMap, 417 | } 418 | 419 | impl Default for Config { 420 | fn default() -> Self { 421 | Self { 422 | endpoint: Endpoint::default(), 423 | user: "root".to_string(), 424 | password: "root".to_string(), 425 | timeout: 3000, 426 | time_zone: format!("{}{}", Utc::now().offset(), Local::now().offset()), 427 | fetch_size: 1024, 428 | rpc_compaction: false, 429 | protocol_version: TSProtocolVersion::IOTDB_SERVICE_PROTOCOL_V3, 430 | enable_redirect_query: false, 431 | config_map: BTreeMap::new(), 432 | } 433 | } 434 | } 435 | 436 | /// IotDB Config Builder 437 | pub struct ConfigBuilder(Config); 438 | 439 | impl Default for ConfigBuilder { 440 | fn default() -> Self { 441 | Self::new() 442 | } 443 | } 444 | 445 | impl ConfigBuilder { 446 | pub fn new() -> Self { 447 | ConfigBuilder(Config::default()) 448 | } 449 | 450 | pub fn host_port(&mut self, host: &str, port: &str) -> &mut Self { 451 | self.0.endpoint = Endpoint::new(host, port); 452 | self 453 | } 454 | 455 | pub fn endpoint(&mut self, endpoint: &str) -> &mut Self { 456 | self.0.endpoint = Endpoint::from_str(endpoint).unwrap(); 457 | self 458 | } 459 | 460 | pub fn user(&mut self, user: &str) -> &mut Self { 461 | self.0.user = user.to_string(); 462 | self 463 | } 464 | 465 | pub fn password(&mut self, password: &str) -> &mut Self { 466 | self.0.password = password.to_string(); 467 | self 468 | } 469 | 470 | pub fn timeout(&mut self, timeout: i64) -> &mut Self { 471 | self.0.timeout = timeout; 472 | self 473 | } 474 | 475 | pub fn time_zone(&mut self, time_zone: &str) -> &mut Self { 476 | self.0.time_zone = time_zone.to_uppercase(); 477 | self 478 | } 479 | 480 | pub fn fetch_size(&mut self, fetch_size: i32) -> &mut Self { 481 | self.0.fetch_size = fetch_size; 482 | self 483 | } 484 | 485 | pub fn enable_rpc_compaction(&mut self) -> &mut Self { 486 | self.0.rpc_compaction = true; 487 | self 488 | } 489 | 490 | pub fn set_protocol_version(&mut self, protocol_version: TSProtocolVersion) -> &mut Self { 491 | self.0.protocol_version = protocol_version; 492 | self 493 | } 494 | 495 | pub fn enable_redirect_query(&mut self, enable_redirect_query: bool) -> &mut Self { 496 | self.0.enable_redirect_query = enable_redirect_query; 497 | self 498 | } 499 | 500 | pub fn set_config(&mut self, key: &str, value: &str) -> &mut Self { 501 | self.0.config_map.insert(key.to_string(), value.to_string()); 502 | self 503 | } 504 | 505 | pub fn set_config_map(&mut self, map: &mut BTreeMap) -> &mut Self { 506 | self.0.config_map.append(map); 507 | self 508 | } 509 | 510 | pub fn build(&self) -> Config { 511 | self.0.clone() 512 | } 513 | } 514 | 515 | /// IotDB Session 516 | 517 | pub struct Session { 518 | client: ClientType, 519 | config: Config, 520 | session_id: i64, 521 | statement_id: i64, 522 | is_close: bool, 523 | } 524 | 525 | impl Session { 526 | // Open Session 527 | pub fn connect(config: Config) -> anyhow::Result { 528 | debug!("{:#?}", &config); 529 | let stream = TcpStream::connect(config.endpoint.to_string())?; 530 | debug!("TcpStream connect to {:?}", config.endpoint); 531 | 532 | let (channel_in, channel_out) = TTcpChannel::with_stream(stream).split()?; 533 | 534 | let (transport_in, transport_out) = ( 535 | TFramedReadTransport::new(channel_in), 536 | TFramedWriteTransport::new(channel_out), 537 | ); 538 | 539 | let (protocol_in, protocol_out): (Box, Box); 540 | if config.rpc_compaction { 541 | protocol_in = Box::new(TCompactInputProtocol::new(transport_in)); 542 | protocol_out = Box::new(TCompactOutputProtocol::new(transport_out)); 543 | debug!("Create TCompactProtocol client"); 544 | } else { 545 | protocol_in = Box::new(TBinaryInputProtocol::new(transport_in, true)); 546 | protocol_out = Box::new(TBinaryOutputProtocol::new(transport_out, true)); 547 | debug!("Create TBinaryProtocol client",); 548 | } 549 | 550 | let mut client = IClientRPCServiceSyncClient::new(protocol_in, protocol_out); 551 | 552 | let open_req = TSOpenSessionReq::new( 553 | config.protocol_version, 554 | config.time_zone.clone(), 555 | config.user.clone(), 556 | config.password.clone(), 557 | config.config_map.clone(), 558 | ); 559 | 560 | let TSOpenSessionResp { 561 | status, 562 | server_protocol_version, 563 | session_id, 564 | configuration: _, 565 | } = client.open_session(open_req)?; 566 | if status.code == SUCCESS_CODE { 567 | if config.protocol_version != server_protocol_version { 568 | let msg = format!( 569 | "Protocol version is different, client is {:?}, server is {:?}", 570 | config.protocol_version.clone(), 571 | server_protocol_version 572 | ); 573 | error!("{}", msg); 574 | bail!(msg) 575 | } else { 576 | let statement_id = client.request_statement_id(session_id.unwrap())?; 577 | debug!( 578 | "Open a session,session id: {}, statement id: {} ", 579 | session_id.unwrap().clone(), 580 | statement_id, 581 | ); 582 | 583 | Ok(Session { 584 | client, 585 | config, 586 | is_close: false, 587 | session_id: session_id.unwrap(), 588 | statement_id, 589 | }) 590 | } 591 | } else { 592 | let msg = status.message.unwrap_or_else(|| "None".to_string()); 593 | error!("{}", msg); 594 | bail!(msg) 595 | } 596 | } 597 | 598 | pub fn is_open(&self) -> bool { 599 | !self.is_close 600 | } 601 | 602 | pub fn is_close(&self) -> bool { 603 | self.is_close 604 | } 605 | 606 | // Close Session 607 | pub fn close(&mut self) -> anyhow::Result<()> { 608 | if !self.is_close { 609 | let req = TSCloseSessionReq::new(self.session_id); 610 | let status = self.client.close_session(req)?; 611 | if self.is_success(&status) { 612 | self.is_close = true; 613 | debug!("Session closed"); 614 | Ok(()) 615 | } else { 616 | error!( 617 | "Session closed failed, code: {}, reason: {}", 618 | status.code.clone(), 619 | status.message.clone().unwrap_or_else(|| "None".to_string()) 620 | ); 621 | bail!(status.message.unwrap_or_else(|| "None".to_string())) 622 | } 623 | } else { 624 | Ok(()) 625 | } 626 | } 627 | 628 | /// Set a storage group 629 | pub fn set_storage_group(&mut self, storage_group: &str) -> anyhow::Result<()> { 630 | let status = self 631 | .client 632 | .set_storage_group(self.session_id, storage_group.to_string())?; 633 | 634 | if self.is_success(&status) { 635 | debug!( 636 | "Set storage group {:?}, message: {:?}", 637 | storage_group, 638 | status.message.unwrap_or_else(|| "None".to_string()) 639 | ); 640 | Ok(()) 641 | } else { 642 | error!( 643 | "{}", 644 | status.message.clone().unwrap_or_else(|| "None".to_string()) 645 | ); 646 | bail!(status.message.unwrap_or_else(|| "None".to_string())) 647 | } 648 | } 649 | 650 | /// Delete a storage group. 651 | pub fn delete_storage_group(&mut self, storage_group: &str) -> anyhow::Result<()> { 652 | debug!("Delete storage group {:?}", storage_group); 653 | self.delete_storage_groups(vec![storage_group.to_string()]) 654 | } 655 | 656 | /// Delete storage groups. 657 | pub fn delete_storage_groups(&mut self, storage_groups: Vec) -> anyhow::Result<()> { 658 | let status = self 659 | .client 660 | .delete_storage_groups(self.session_id, storage_groups.clone())?; 661 | if self.is_success(&status) { 662 | debug!( 663 | "Delete storage group(s) {:?}, message: {:?}", 664 | storage_groups, 665 | status.message.unwrap_or_else(|| "None".to_string()) 666 | ); 667 | Ok(()) 668 | } else { 669 | error!( 670 | "{}", 671 | status.message.clone().unwrap_or_else(|| "None".to_string()) 672 | ); 673 | bail!(status.message.unwrap_or_else(|| "None".to_string())) 674 | } 675 | } 676 | 677 | /// Create single time-series 678 | pub fn create_time_series( 679 | &mut self, 680 | ts_path: &str, 681 | data_type: DataType, 682 | encoding: Encoding, 683 | compressor: Compressor, 684 | ) -> anyhow::Result<()> { 685 | let req = TSCreateTimeseriesReq::new( 686 | self.session_id, 687 | ts_path.to_string(), 688 | data_type.into(), 689 | encoding.into(), 690 | compressor.into(), 691 | None, 692 | None, 693 | None, 694 | None, 695 | ); 696 | 697 | match self.check_time_series_exists(ts_path)? { 698 | true => { 699 | info!("{} time series exists", ts_path); 700 | Ok(()) 701 | } 702 | false => { 703 | let status = self.client.create_timeseries(req)?; 704 | if self.is_success(&status) { 705 | debug!( 706 | "Creat time series {:?}, message: {:?}", 707 | ts_path, 708 | status.message.unwrap_or_else(|| "None".to_string()) 709 | ); 710 | Ok(()) 711 | } else { 712 | error!( 713 | "{}", 714 | status.message.clone().unwrap_or_else(|| "None".to_string()) 715 | ); 716 | bail!(status.message.unwrap_or_else(|| "None".to_string())) 717 | } 718 | } 719 | } 720 | } 721 | 722 | /// Create multiple time-series 723 | pub fn create_multi_time_series( 724 | &mut self, 725 | ts_path_vec: Vec, 726 | data_type_vec: Vec, 727 | encoding_vec: Vec, 728 | compressor_vec: Vec, 729 | ) -> anyhow::Result<()> { 730 | let req = TSCreateMultiTimeseriesReq::new( 731 | self.session_id, 732 | ts_path_vec.clone(), 733 | data_type_vec, 734 | encoding_vec, 735 | compressor_vec, 736 | None, 737 | None, 738 | None, 739 | None, 740 | ); 741 | let status = self.client.create_multi_timeseries(req)?; 742 | if self.is_success(&status) { 743 | debug!( 744 | "Creating multiple time series {:?}, message: {:?}", 745 | ts_path_vec, 746 | status.message.unwrap_or_else(|| "None".to_string()) 747 | ); 748 | Ok(()) 749 | } else { 750 | error!( 751 | "{}", 752 | status.message.clone().unwrap_or_else(|| "None".to_string()) 753 | ); 754 | bail!(status.message.unwrap_or_else(|| "None".to_string())) 755 | } 756 | } 757 | 758 | /// Delete multiple time series 759 | pub fn delete_time_series(&mut self, path_vec: Vec) -> anyhow::Result<()> { 760 | let status = self 761 | .client 762 | .delete_timeseries(self.session_id, path_vec.clone())?; 763 | if self.is_success(&status) { 764 | debug!( 765 | "Deleting multiple time series {:?}, message: {:?}", 766 | path_vec, 767 | status.message.unwrap_or_else(|| "None".to_string()) 768 | ); 769 | Ok(()) 770 | } else { 771 | error!( 772 | "{}", 773 | status.message.clone().unwrap_or_else(|| "None".to_string()) 774 | ); 775 | bail!(status.message.unwrap_or_else(|| "None".to_string())) 776 | } 777 | } 778 | 779 | /// Check whether a specific time-series exists 780 | pub fn check_time_series_exists(&mut self, path: &str) -> anyhow::Result { 781 | let config = self.config.clone(); 782 | let statement = format!("SHOW TIMESERIES {}", path); 783 | let req = TSExecuteStatementReq::new( 784 | self.session_id, 785 | statement, 786 | self.statement_id, 787 | config.fetch_size, 788 | config.timeout, 789 | config.enable_redirect_query, 790 | false, 791 | ); 792 | 793 | let TSExecuteStatementResp { query_data_set, .. } = 794 | self.client.execute_query_statement(req)?; 795 | if let Some(..) = query_data_set { 796 | Ok(false) 797 | } else { 798 | Ok(query_data_set.unwrap().value_list.is_empty()) 799 | } 800 | } 801 | 802 | /// Delete all data <= time in multiple time-series 803 | pub fn delete_data(&mut self, path_vec: Vec, timestamp: i64) -> anyhow::Result<()> { 804 | let req = TSDeleteDataReq::new(self.session_id, path_vec.clone(), 0, timestamp); 805 | let status = self.client.delete_data(req)?; 806 | if self.is_success(&status) { 807 | debug!( 808 | "Delete data from {:?}, message: {:?}", 809 | path_vec, 810 | status.message.unwrap_or_else(|| "None".to_string()) 811 | ); 812 | Ok(()) 813 | } else { 814 | error!( 815 | "{}", 816 | status.message.clone().unwrap_or_else(|| "None".to_string()) 817 | ); 818 | bail!(status.message.unwrap_or_else(|| "None".to_string())) 819 | } 820 | } 821 | 822 | /// special case for inserting one row of String (TEXT) value 823 | pub fn insert_string_records( 824 | &mut self, 825 | device_ids: Vec, 826 | timestamps: Vec, 827 | measurements_list: Vec>, 828 | values_list: Vec>, 829 | is_aligned: bool, 830 | ) -> anyhow::Result<()> { 831 | let req = TSInsertStringRecordsReq::new( 832 | self.session_id, 833 | device_ids.clone(), 834 | measurements_list, 835 | values_list, 836 | timestamps, 837 | is_aligned, 838 | ); 839 | 840 | let status = self.client.insert_string_records(req)?; 841 | if self.is_success(&status) { 842 | debug!( 843 | "Insert string records to device {:?}, message: {:?}", 844 | device_ids, 845 | status.message.unwrap_or_else(|| "None".to_string()) 846 | ); 847 | Ok(()) 848 | } else { 849 | error!( 850 | "{}", 851 | status.message.clone().unwrap_or_else(|| "None".to_string()) 852 | ); 853 | bail!(status.message.unwrap_or_else(|| "None".to_string())) 854 | } 855 | } 856 | 857 | /// Insert record 858 | pub fn insert_record( 859 | &mut self, 860 | device_id: &str, 861 | timestamp: i64, 862 | measurements: Vec, 863 | values: Vec, 864 | is_aligned: bool, 865 | ) -> anyhow::Result<()> { 866 | let req = TSInsertRecordReq::new( 867 | self.session_id, 868 | device_id.to_string(), 869 | measurements, 870 | values, 871 | timestamp, 872 | is_aligned, 873 | ); 874 | 875 | let status = self.client.insert_record(req)?; 876 | if self.is_success(&status) { 877 | debug!( 878 | "Insert one record to device {:?}, message: {:?}", 879 | device_id, 880 | status.message.unwrap_or_else(|| "None".to_string()) 881 | ); 882 | Ok(()) 883 | } else { 884 | error!( 885 | "{}", 886 | status.message.clone().unwrap_or_else(|| "None".to_string()) 887 | ); 888 | bail!(status.message.unwrap_or_else(|| "None".to_string())) 889 | } 890 | } 891 | 892 | /// this method NOT insert data into database and the server just return after accept the 893 | /// request, this method should be used to test other time cost in client 894 | pub fn test_insert_record( 895 | &mut self, 896 | prefix_path: &str, 897 | timestamp: i64, 898 | measurements: Vec, 899 | values: Vec, 900 | is_aligned: bool, 901 | ) -> anyhow::Result<()> { 902 | let req = TSInsertRecordReq::new( 903 | self.session_id, 904 | prefix_path.to_string(), 905 | measurements, 906 | values, 907 | timestamp, 908 | is_aligned, 909 | ); 910 | let status = self.client.test_insert_record(req)?; 911 | if self.is_success(&status) { 912 | debug!( 913 | "Testing! insert one record to prefix path {:?}, message: {:?}", 914 | prefix_path, 915 | status.message.unwrap_or_else(|| "None".to_string()) 916 | ); 917 | Ok(()) 918 | } else { 919 | error!( 920 | "{}", 921 | status.message.clone().unwrap_or_else(|| "None".to_string()) 922 | ); 923 | bail!(status.message.unwrap_or_else(|| "None".to_string())) 924 | } 925 | } 926 | 927 | /// Insert records 928 | pub fn insert_records( 929 | &mut self, 930 | prefix_paths: Vec, 931 | timestamps: Vec, 932 | measurements_list: Vec>, 933 | values_list: Vec>, 934 | is_aligned: bool, 935 | ) -> anyhow::Result<()> { 936 | let req = TSInsertRecordsReq::new( 937 | self.session_id, 938 | prefix_paths.clone(), 939 | measurements_list, 940 | values_list, 941 | timestamps, 942 | is_aligned, 943 | ); 944 | let status = self.client.insert_records(req)?; 945 | if self.is_success(&status) { 946 | debug!( 947 | "Insert multiple records to prefix path {:?}, message: {:?}", 948 | prefix_paths, 949 | status.message.unwrap_or_else(|| "None".to_string()) 950 | ); 951 | Ok(()) 952 | } else { 953 | error!( 954 | "{}", 955 | status.message.clone().unwrap_or_else(|| "None".to_string()) 956 | ); 957 | bail!(status.message.unwrap_or_else(|| "None".to_string())) 958 | } 959 | } 960 | 961 | /// this method NOT insert data into database and the server just return after accept the 962 | /// request, this method should be used to test other time cost in client 963 | pub fn test_insert_records( 964 | &mut self, 965 | prefix_paths: Vec, 966 | timestamps: Vec, 967 | measurements_list: Vec>, 968 | values_list: Vec>, 969 | is_aligned: bool, 970 | ) -> anyhow::Result<()> { 971 | let req = TSInsertRecordsReq::new( 972 | self.session_id, 973 | prefix_paths, 974 | measurements_list, 975 | values_list, 976 | timestamps, 977 | is_aligned, 978 | ); 979 | let status = self.client.test_insert_records(req)?; 980 | if self.is_success(&status) { 981 | debug!( 982 | "Testing! insert multiple records, message: {:?}", 983 | status.message.unwrap_or_else(|| "None".to_string()) 984 | ); 985 | Ok(()) 986 | } else { 987 | error!( 988 | "{}", 989 | status.message.clone().unwrap_or_else(|| "None".to_string()) 990 | ); 991 | bail!(status.message.unwrap_or_else(|| "None".to_string())) 992 | } 993 | } 994 | 995 | /// insert one tablet, in a tablet, for each timestamp, the number of measurements is same 996 | /// for example three records in the same device can form a tablet: 997 | /// timestamps, m1, m2, m3 998 | /// 1, 125.3, True, text1 999 | /// 2, 111.6, False, text2 1000 | /// 3, 688.6, True, text3 1001 | /// Notice: The tablet should not have empty cell 1002 | /// The tablet itself is sorted 1003 | /// TODO 1004 | #[allow(clippy::too_many_arguments)] 1005 | pub fn insert_tablet( 1006 | &mut self, 1007 | prefix_path: &str, 1008 | measurements: Vec, 1009 | values: Vec, 1010 | timestamps: Vec, 1011 | types: Vec, 1012 | size: i32, 1013 | is_aligned: bool, 1014 | ) -> anyhow::Result<()> { 1015 | let req = TSInsertTabletReq::new( 1016 | self.session_id, 1017 | prefix_path.to_string(), 1018 | measurements, 1019 | values, 1020 | timestamps, 1021 | types, 1022 | size, 1023 | is_aligned, 1024 | ); 1025 | let status = self.client.insert_tablet(req)?; 1026 | if self.is_success(&status) { 1027 | debug!( 1028 | "Testing! insert multiple records, message: {:?}", 1029 | status.message.unwrap_or_else(|| "None".to_string()) 1030 | ); 1031 | Ok(()) 1032 | } else { 1033 | error!( 1034 | "{}", 1035 | status.message.clone().unwrap_or_else(|| "None".to_string()) 1036 | ); 1037 | bail!(status.message.unwrap_or_else(|| "None".to_string())) 1038 | } 1039 | } 1040 | 1041 | /// insert multiple tablets, tablets are independent to each other 1042 | /// TODO 1043 | #[allow(clippy::too_many_arguments)] 1044 | pub fn insert_tablets( 1045 | &mut self, 1046 | prefix_paths: Vec, 1047 | measurements_list: Vec>, 1048 | values_list: Vec>, 1049 | timestamps_list: Vec>, 1050 | types_list: Vec>, 1051 | size_list: Vec, 1052 | is_aligned: bool, 1053 | ) -> anyhow::Result<()> { 1054 | let req = TSInsertTabletsReq::new( 1055 | self.session_id, 1056 | prefix_paths, 1057 | measurements_list, 1058 | values_list, 1059 | timestamps_list, 1060 | types_list, 1061 | size_list, 1062 | is_aligned, 1063 | ); 1064 | let status = self.client.insert_tablets(req)?; 1065 | if self.is_success(&status) { 1066 | debug!( 1067 | "Testing! insert multiple records, message: {:?}", 1068 | status.message.unwrap_or_else(|| "None".to_string()) 1069 | ); 1070 | Ok(()) 1071 | } else { 1072 | error!( 1073 | "{}", 1074 | status.message.clone().unwrap_or_else(|| "None".to_string()) 1075 | ); 1076 | bail!(status.message.unwrap_or_else(|| "None".to_string())) 1077 | } 1078 | } 1079 | 1080 | /// TODO 1081 | pub fn insert_records_of_one_device() {} 1082 | 1083 | /// TODO 1084 | pub fn insert_records_of_one_device_sorte() {} 1085 | 1086 | /// TODO 1087 | pub fn gen_insert_records_of_one_device_request() {} 1088 | 1089 | /// this method NOT insert data into database and the server just return after accept the 1090 | /// request, this method should be used to test other time cost in client 1091 | /// TODO 1092 | pub fn test_insert_table() {} 1093 | 1094 | /// this method NOT insert data into database and the server just return after accept the 1095 | /// request, this method should be used to test other time cost in client 1096 | /// TODO 1097 | pub fn test_insert_tablets() {} 1098 | 1099 | /// TODO 1100 | pub fn gen_insert_tablet_req() {} 1101 | 1102 | /// TODO 1103 | pub fn gen_insert_tablets_req() {} 1104 | 1105 | pub fn sql(&mut self, sql: &str) -> anyhow::Result { 1106 | self.exec(sql) 1107 | } 1108 | 1109 | /// execute query sql statement and return a DataSet 1110 | fn exec(&mut self, statement: &str) -> anyhow::Result { 1111 | debug!("Exec statement \"{}\"", statement); 1112 | let req = TSExecuteStatementReq::new( 1113 | self.session_id, 1114 | statement.to_string(), 1115 | self.statement_id, 1116 | self.config.fetch_size, 1117 | self.config.timeout, 1118 | self.config.enable_redirect_query, 1119 | false, 1120 | ); 1121 | let resp = self.client.execute_statement(req)?; 1122 | let status = resp.clone().status; 1123 | let msg = status.clone().message.unwrap_or_else(|| "None".to_string()); 1124 | if self.is_success(&status) { 1125 | debug!("Execute statement {:?}, message: {:?}", statement, msg); 1126 | Ok(DataSet::new(resp)) 1127 | } else { 1128 | error!("{}", msg); 1129 | bail!(msg) 1130 | } 1131 | } 1132 | 1133 | /// execute batch statement and return a DataSets 1134 | pub fn exec_batch(&mut self, statements: Vec) -> anyhow::Result<()> { 1135 | let req = TSExecuteBatchStatementReq::new(self.session_id, statements); 1136 | let status = self.client.execute_batch_statement(req)?; 1137 | let msg = status.clone().message.unwrap_or_else(|| "None".to_string()); 1138 | if self.is_success(&status) { 1139 | info!("{}", msg); 1140 | Ok(()) 1141 | } else { 1142 | error!("{}", msg); 1143 | bail!(msg) 1144 | } 1145 | } 1146 | 1147 | /// execute query sql statement and return a DataSet 1148 | pub fn exec_query(&mut self, query: &str) -> anyhow::Result { 1149 | debug!("Exec query \"{}\"", &query); 1150 | let req = TSExecuteStatementReq::new( 1151 | self.session_id, 1152 | query.to_string(), 1153 | self.statement_id, 1154 | self.config.fetch_size, 1155 | self.config.timeout, 1156 | self.config.enable_redirect_query, 1157 | false, 1158 | ); 1159 | 1160 | let resp = self.client.execute_query_statement(req)?; 1161 | if self.is_success(&resp.status) { 1162 | debug!( 1163 | "Execute query {:?}, message: {:?}", 1164 | query, 1165 | resp.status 1166 | .clone() 1167 | .message 1168 | .unwrap_or_else(|| "None".to_string()) 1169 | ); 1170 | Ok(DataSet::new(resp)) 1171 | } else { 1172 | error!( 1173 | "Exec query failed, code: {}, reason: {}", 1174 | resp.status.code.clone(), 1175 | resp.status 1176 | .clone() 1177 | .message 1178 | .unwrap_or_else(|| "None".to_string()) 1179 | ); 1180 | bail!(resp.status.message.unwrap_or_else(|| "None".to_string())) 1181 | } 1182 | } 1183 | 1184 | /// execute update statement and return a DataSet 1185 | pub fn exec_update(&mut self, statement: &str) -> anyhow::Result { 1186 | let req = TSExecuteStatementReq::new( 1187 | self.session_id, 1188 | statement.to_string(), 1189 | self.statement_id, 1190 | self.config.fetch_size, 1191 | self.config.timeout, 1192 | self.config.enable_redirect_query, 1193 | false, 1194 | ); 1195 | 1196 | let resp = self.client.execute_update_statement(req)?; 1197 | if self.is_success(&resp.status) { 1198 | debug!( 1199 | "Execute update statement {:?}, message: {:?}", 1200 | statement, 1201 | resp.status 1202 | .clone() 1203 | .message 1204 | .unwrap_or_else(|| "None".to_string()) 1205 | ); 1206 | Ok(DataSet::new(resp)) 1207 | } else { 1208 | error!( 1209 | "{}", 1210 | resp.status 1211 | .message 1212 | .clone() 1213 | .unwrap_or_else(|| "None".to_string()) 1214 | ); 1215 | bail!(resp.status.message.unwrap_or_else(|| "None".to_string())) 1216 | } 1217 | } 1218 | 1219 | /// execute row statement and return a DataSets 1220 | pub fn exec_raw_data_query( 1221 | &mut self, 1222 | paths: Vec, 1223 | start_time: i64, 1224 | end_time: i64, 1225 | ) -> anyhow::Result { 1226 | let req = TSRawDataQueryReq::new( 1227 | self.session_id, 1228 | paths, 1229 | self.config.fetch_size, 1230 | start_time, 1231 | end_time, 1232 | self.statement_id, 1233 | Some(self.config.enable_redirect_query), 1234 | Some(false), 1235 | Some(self.config.timeout), 1236 | ); 1237 | let resp = self.client.execute_raw_data_query(req)?; 1238 | if self.is_success(&resp.status) { 1239 | Ok(DataSet::new(resp)) 1240 | } else { 1241 | error!( 1242 | "{}", 1243 | resp.status 1244 | .message 1245 | .clone() 1246 | .unwrap_or_else(|| "None".to_string()) 1247 | ); 1248 | bail!(resp.status.message.unwrap_or_else(|| "None".to_string())) 1249 | } 1250 | } 1251 | 1252 | /// Set time zone 1253 | pub fn set_time_zone(&mut self, time_zone: &str) -> anyhow::Result<()> { 1254 | let req = TSSetTimeZoneReq::new(self.session_id, time_zone.to_string()); 1255 | let status = self.client.set_time_zone(req)?; 1256 | if !self.is_success(&status) { 1257 | error!( 1258 | "{}", 1259 | status.message.clone().unwrap_or_else(|| "None".to_string()) 1260 | ); 1261 | bail!(status.message.unwrap_or_else(|| "None".to_string())) 1262 | } else { 1263 | Ok(()) 1264 | } 1265 | } 1266 | 1267 | /// Get time zone 1268 | pub fn time_zone(&mut self) -> anyhow::Result { 1269 | let resp = self.client.get_time_zone(self.session_id)?; 1270 | if self.is_success(&resp.status) { 1271 | Ok(resp.time_zone) 1272 | } else { 1273 | error!( 1274 | "{}", 1275 | resp.status.message.unwrap_or_else(|| "None".to_string()) 1276 | ); 1277 | Ok(String::new()) 1278 | } 1279 | } 1280 | 1281 | /// Get Server properties 1282 | pub fn get_properties(&mut self) -> anyhow::Result { 1283 | match self.client.get_properties() { 1284 | Ok(properties) => Ok(properties), 1285 | Err(error) => { 1286 | bail!(error) 1287 | } 1288 | } 1289 | } 1290 | 1291 | /// Verify success status of operation 1292 | fn is_success(&self, status: &TSStatus) -> bool { 1293 | status.code == SUCCESS_CODE 1294 | } 1295 | 1296 | /// Cancel operation 1297 | #[allow(dead_code)] 1298 | fn cancel_operation(&mut self, query_id: i64) -> anyhow::Result<()> { 1299 | let req = TSCancelOperationReq::new(self.session_id, query_id); 1300 | let status = self.client.cancel_operation(req)?; 1301 | if !self.is_success(&status) { 1302 | let msg = format!("Cancel operation failed,'{:?}'", query_id); 1303 | error!("{}", msg); 1304 | bail!(status.message.unwrap_or_else(|| "None".to_string())) 1305 | } else { 1306 | Ok(()) 1307 | } 1308 | } 1309 | } 1310 | -------------------------------------------------------------------------------- /thrift/client.thrift: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | include "common.thrift" 20 | namespace java org.apache.iotdb.service.rpc.thrift 21 | namespace py iotdb.thrift.rpc 22 | 23 | struct TSQueryDataSet{ 24 | // ByteBuffer for time column 25 | 1: required binary time 26 | // ByteBuffer for each column values 27 | 2: required list valueList 28 | // Bitmap for each column to indicate whether it is a null value 29 | 3: required list bitmapList 30 | } 31 | 32 | struct TSQueryNonAlignDataSet{ 33 | // ByteBuffer for each time column 34 | 1: required list timeList 35 | // ByteBuffer for each column values 36 | 2: required list valueList 37 | } 38 | 39 | struct TSTracingInfo{ 40 | 1: required list activityList 41 | 2: required list elapsedTimeList 42 | 3: optional i32 seriesPathNum 43 | 4: optional i32 seqFileNum 44 | 5: optional i32 unSeqFileNum 45 | 6: optional i32 sequenceChunkNum 46 | 7: optional i64 sequenceChunkPointNum 47 | 8: optional i32 unsequenceChunkNum 48 | 9: optional i64 unsequenceChunkPointNum 49 | 10: optional i32 totalPageNum 50 | 11: optional i32 overlappedPageNum 51 | } 52 | 53 | struct TSExecuteStatementResp { 54 | 1: required common.TSStatus status 55 | 2: optional i64 queryId 56 | // Column names in select statement of SQL 57 | 3: optional list columns 58 | 4: optional string operationType 59 | 5: optional bool ignoreTimeStamp 60 | // Data type list of columns in select statement of SQL 61 | 6: optional list dataTypeList 62 | 7: optional TSQueryDataSet queryDataSet 63 | // for disable align statements, queryDataSet is null and nonAlignQueryDataSet is not null 64 | 8: optional TSQueryNonAlignDataSet nonAlignQueryDataSet 65 | 9: optional map columnNameIndexMap 66 | 10: optional list sgColumns 67 | 11: optional list aliasColumns 68 | 12: optional TSTracingInfo tracingInfo 69 | } 70 | 71 | enum TSProtocolVersion { 72 | IOTDB_SERVICE_PROTOCOL_V1, 73 | IOTDB_SERVICE_PROTOCOL_V2,//V2 is the first version that we can check version compatibility 74 | IOTDB_SERVICE_PROTOCOL_V3,//V3 is incompatible with V2 75 | } 76 | 77 | struct TSOpenSessionResp { 78 | 1: required common.TSStatus status 79 | 80 | // The protocol version that the server is using. 81 | 2: required TSProtocolVersion serverProtocolVersion = TSProtocolVersion.IOTDB_SERVICE_PROTOCOL_V1 82 | 83 | // Session id 84 | 3: optional i64 sessionId 85 | 86 | // The configuration settings for this session. 87 | 4: optional map configuration 88 | } 89 | 90 | // OpenSession() 91 | // Open a session (connection) on the server against which operations may be executed. 92 | struct TSOpenSessionReq { 93 | 1: required TSProtocolVersion client_protocol = TSProtocolVersion.IOTDB_SERVICE_PROTOCOL_V3 94 | 2: required string zoneId 95 | 3: optional string username 96 | 4: optional string password 97 | 5: optional map configuration 98 | } 99 | 100 | // CloseSession() 101 | // Closes the specified session and frees any resources currently allocated to that session. 102 | // Any open operations in that session will be canceled. 103 | struct TSCloseSessionReq { 104 | 1: required i64 sessionId 105 | } 106 | 107 | // ExecuteStatement() 108 | // 109 | // Execute a statement. 110 | // The returned OperationHandle can be used to check on the status of the statement, and to fetch results once the 111 | // statement has finished executing. 112 | struct TSExecuteStatementReq { 113 | // The session to execute the statement against 114 | 1: required i64 sessionId 115 | 116 | // The statement to be executed (DML, DDL, SET, etc) 117 | 2: required string statement 118 | 119 | // statementId 120 | 3: required i64 statementId 121 | 122 | 4: optional i32 fetchSize 123 | 124 | 5: optional i64 timeout 125 | 126 | 6: optional bool enableRedirectQuery; 127 | 128 | 7: optional bool jdbcQuery; 129 | } 130 | 131 | struct TSExecuteBatchStatementReq{ 132 | // The session to execute the statement against 133 | 1: required i64 sessionId 134 | 135 | // The statements to be executed (DML, DDL, SET, etc) 136 | 2: required list statements 137 | } 138 | 139 | struct TSGetOperationStatusReq { 140 | 1: required i64 sessionId 141 | // Session to run this request against 142 | 2: required i64 queryId 143 | } 144 | 145 | // CancelOperation() 146 | // 147 | // Cancels processing on the specified operation handle and frees any resources which were allocated. 148 | struct TSCancelOperationReq { 149 | 1: required i64 sessionId 150 | // Operation to cancel 151 | 2: required i64 queryId 152 | } 153 | 154 | // CloseOperation() 155 | struct TSCloseOperationReq { 156 | 1: required i64 sessionId 157 | 2: optional i64 queryId 158 | 3: optional i64 statementId 159 | } 160 | 161 | struct TSFetchResultsReq{ 162 | 1: required i64 sessionId 163 | 2: required string statement 164 | 3: required i32 fetchSize 165 | 4: required i64 queryId 166 | 5: required bool isAlign 167 | 6: optional i64 timeout 168 | } 169 | 170 | struct TSFetchResultsResp{ 171 | 1: required common.TSStatus status 172 | 2: required bool hasResultSet 173 | 3: required bool isAlign 174 | 4: optional TSQueryDataSet queryDataSet 175 | 5: optional TSQueryNonAlignDataSet nonAlignQueryDataSet 176 | } 177 | 178 | struct TSFetchMetadataResp{ 179 | 1: required common.TSStatus status 180 | 2: optional string metadataInJson 181 | 3: optional list columnsList 182 | 4: optional string dataType 183 | } 184 | 185 | struct TSFetchMetadataReq{ 186 | 1: required i64 sessionId 187 | 2: required string type 188 | 3: optional string columnPath 189 | } 190 | 191 | struct TSGetTimeZoneResp { 192 | 1: required common.TSStatus status 193 | 2: required string timeZone 194 | } 195 | 196 | struct TSSetTimeZoneReq { 197 | 1: required i64 sessionId 198 | 2: required string timeZone 199 | } 200 | 201 | // for session 202 | struct TSInsertRecordReq { 203 | 1: required i64 sessionId 204 | 2: required string prefixPath 205 | 3: required list measurements 206 | 4: required binary values 207 | 5: required i64 timestamp 208 | 6: optional bool isAligned 209 | } 210 | 211 | struct TSInsertStringRecordReq { 212 | 1: required i64 sessionId 213 | 2: required string prefixPath 214 | 3: required list measurements 215 | 4: required list values 216 | 5: required i64 timestamp 217 | 6: optional bool isAligned 218 | 7: optional i64 timeout 219 | } 220 | 221 | struct TSInsertTabletReq { 222 | 1: required i64 sessionId 223 | 2: required string prefixPath 224 | 3: required list measurements 225 | 4: required binary values 226 | 5: required binary timestamps 227 | 6: required list types 228 | 7: required i32 size 229 | 8: optional bool isAligned 230 | } 231 | 232 | struct TSInsertTabletsReq { 233 | 1: required i64 sessionId 234 | 2: required list prefixPaths 235 | 3: required list> measurementsList 236 | 4: required list valuesList 237 | 5: required list timestampsList 238 | 6: required list> typesList 239 | 7: required list sizeList 240 | 8: optional bool isAligned 241 | } 242 | 243 | struct TSInsertRecordsReq { 244 | 1: required i64 sessionId 245 | 2: required list prefixPaths 246 | 3: required list> measurementsList 247 | 4: required list valuesList 248 | 5: required list timestamps 249 | 6: optional bool isAligned 250 | } 251 | 252 | struct TSInsertRecordsOfOneDeviceReq { 253 | 1: required i64 sessionId 254 | 2: required string prefixPath 255 | 3: required list> measurementsList 256 | 4: required list valuesList 257 | 5: required list timestamps 258 | 6: optional bool isAligned 259 | } 260 | 261 | struct TSInsertStringRecordsOfOneDeviceReq { 262 | 1: required i64 sessionId 263 | 2: required string prefixPath 264 | 3: required list> measurementsList 265 | 4: required list> valuesList 266 | 5: required list timestamps 267 | 6: optional bool isAligned 268 | } 269 | 270 | struct TSInsertStringRecordsReq { 271 | 1: required i64 sessionId 272 | 2: required list prefixPaths 273 | 3: required list> measurementsList 274 | 4: required list> valuesList 275 | 5: required list timestamps 276 | 6: optional bool isAligned 277 | } 278 | 279 | struct TSDeleteDataReq { 280 | 1: required i64 sessionId 281 | 2: required list paths 282 | 3: required i64 startTime 283 | 4: required i64 endTime 284 | } 285 | 286 | struct TSCreateTimeseriesReq { 287 | 1: required i64 sessionId 288 | 2: required string path 289 | 3: required i32 dataType 290 | 4: required i32 encoding 291 | 5: required i32 compressor 292 | 6: optional map props 293 | 7: optional map tags 294 | 8: optional map attributes 295 | 9: optional string measurementAlias 296 | } 297 | 298 | struct TSCreateAlignedTimeseriesReq { 299 | 1: required i64 sessionId 300 | 2: required string prefixPath 301 | 3: required list measurements 302 | 4: required list dataTypes 303 | 5: required list encodings 304 | 6: required list compressors 305 | 7: optional list measurementAlias 306 | 8: optional list> tagsList 307 | 9: optional list> attributesList 308 | } 309 | 310 | struct TSRawDataQueryReq { 311 | 1: required i64 sessionId 312 | 2: required list paths 313 | 3: optional i32 fetchSize 314 | 4: required i64 startTime 315 | 5: required i64 endTime 316 | 6: required i64 statementId 317 | 7: optional bool enableRedirectQuery; 318 | 8: optional bool jdbcQuery; 319 | 9: optional i64 timeout 320 | } 321 | 322 | struct TSLastDataQueryReq { 323 | 1: required i64 sessionId 324 | 2: required list paths 325 | 3: optional i32 fetchSize 326 | 4: required i64 time 327 | 5: required i64 statementId 328 | 6: optional bool enableRedirectQuery; 329 | 7: optional bool jdbcQuery; 330 | 8: optional i64 timeout 331 | } 332 | 333 | struct TSCreateMultiTimeseriesReq { 334 | 1: required i64 sessionId 335 | 2: required list paths 336 | 3: required list dataTypes 337 | 4: required list encodings 338 | 5: required list compressors 339 | 6: optional list> propsList 340 | 7: optional list> tagsList 341 | 8: optional list> attributesList 342 | 9: optional list measurementAliasList 343 | } 344 | 345 | struct ServerProperties { 346 | 1: required string version; 347 | 2: required list supportedTimeAggregationOperations; 348 | 3: required string timestampPrecision; 349 | 4: i32 maxConcurrentClientNum; 350 | 5: optional string watermarkSecretKey; 351 | 6: optional string watermarkBitString 352 | 7: optional i32 watermarkParamMarkRate; 353 | 8: optional i32 watermarkParamMaxRightBit; 354 | 9: optional i32 thriftMaxFrameSize; 355 | 10:optional bool isReadOnly; 356 | } 357 | 358 | struct TSSetSchemaTemplateReq { 359 | 1: required i64 sessionId 360 | 2: required string templateName 361 | 3: required string prefixPath 362 | } 363 | 364 | struct TSCreateSchemaTemplateReq { 365 | 1: required i64 sessionId 366 | 2: required string name 367 | 3: required binary serializedTemplate 368 | } 369 | 370 | struct TSAppendSchemaTemplateReq { 371 | 1: required i64 sessionId 372 | 2: required string name 373 | 3: required bool isAligned 374 | 4: required list measurements 375 | 5: required list dataTypes 376 | 6: required list encodings 377 | 7: required list compressors 378 | } 379 | 380 | struct TSPruneSchemaTemplateReq { 381 | 1: required i64 sessionId 382 | 2: required string name 383 | 3: required string path 384 | } 385 | 386 | struct TSQueryTemplateReq { 387 | 1: required i64 sessionId 388 | 2: required string name 389 | 3: required i32 queryType 390 | 4: optional string measurement 391 | } 392 | 393 | struct TSQueryTemplateResp { 394 | 1: required common.TSStatus status 395 | 2: required i32 queryType 396 | 3: optional bool result 397 | 4: optional i32 count 398 | 5: optional list measurements 399 | } 400 | 401 | struct TSUnsetSchemaTemplateReq { 402 | 1: required i64 sessionId 403 | 2: required string prefixPath 404 | 3: required string templateName 405 | } 406 | 407 | struct TSDropSchemaTemplateReq { 408 | 1: required i64 sessionId 409 | 2: required string templateName 410 | } 411 | 412 | service IClientRPCService { 413 | TSOpenSessionResp openSession(1:TSOpenSessionReq req); 414 | 415 | common.TSStatus closeSession(1:TSCloseSessionReq req); 416 | 417 | TSExecuteStatementResp executeStatement(1:TSExecuteStatementReq req); 418 | 419 | common.TSStatus executeBatchStatement(1:TSExecuteBatchStatementReq req); 420 | 421 | TSExecuteStatementResp executeQueryStatement(1:TSExecuteStatementReq req); 422 | 423 | TSExecuteStatementResp executeUpdateStatement(1:TSExecuteStatementReq req); 424 | 425 | TSFetchResultsResp fetchResults(1:TSFetchResultsReq req) 426 | 427 | TSFetchMetadataResp fetchMetadata(1:TSFetchMetadataReq req) 428 | 429 | common.TSStatus cancelOperation(1:TSCancelOperationReq req); 430 | 431 | common.TSStatus closeOperation(1:TSCloseOperationReq req); 432 | 433 | TSGetTimeZoneResp getTimeZone(1:i64 sessionId); 434 | 435 | common.TSStatus setTimeZone(1:TSSetTimeZoneReq req); 436 | 437 | ServerProperties getProperties(); 438 | 439 | common.TSStatus setStorageGroup(1:i64 sessionId, 2:string storageGroup); 440 | 441 | common.TSStatus createTimeseries(1:TSCreateTimeseriesReq req); 442 | 443 | common.TSStatus createAlignedTimeseries(1:TSCreateAlignedTimeseriesReq req); 444 | 445 | common.TSStatus createMultiTimeseries(1:TSCreateMultiTimeseriesReq req); 446 | 447 | common.TSStatus deleteTimeseries(1:i64 sessionId, 2:list path) 448 | 449 | common.TSStatus deleteStorageGroups(1:i64 sessionId, 2:list storageGroup); 450 | 451 | common.TSStatus insertRecord(1:TSInsertRecordReq req); 452 | 453 | common.TSStatus insertStringRecord(1:TSInsertStringRecordReq req); 454 | 455 | common.TSStatus insertTablet(1:TSInsertTabletReq req); 456 | 457 | common.TSStatus insertTablets(1:TSInsertTabletsReq req); 458 | 459 | common.TSStatus insertRecords(1:TSInsertRecordsReq req); 460 | 461 | common.TSStatus insertRecordsOfOneDevice(1:TSInsertRecordsOfOneDeviceReq req); 462 | 463 | common.TSStatus insertStringRecordsOfOneDevice(1:TSInsertStringRecordsOfOneDeviceReq req); 464 | 465 | common.TSStatus insertStringRecords(1:TSInsertStringRecordsReq req); 466 | 467 | common.TSStatus testInsertTablet(1:TSInsertTabletReq req); 468 | 469 | common.TSStatus testInsertTablets(1:TSInsertTabletsReq req); 470 | 471 | common.TSStatus testInsertRecord(1:TSInsertRecordReq req); 472 | 473 | common.TSStatus testInsertStringRecord(1:TSInsertStringRecordReq req); 474 | 475 | common.TSStatus testInsertRecords(1:TSInsertRecordsReq req); 476 | 477 | common.TSStatus testInsertRecordsOfOneDevice(1:TSInsertRecordsOfOneDeviceReq req); 478 | 479 | common.TSStatus testInsertStringRecords(1:TSInsertStringRecordsReq req); 480 | 481 | common.TSStatus deleteData(1:TSDeleteDataReq req); 482 | 483 | TSExecuteStatementResp executeRawDataQuery(1:TSRawDataQueryReq req); 484 | 485 | TSExecuteStatementResp executeLastDataQuery(1:TSLastDataQueryReq req); 486 | 487 | i64 requestStatementId(1:i64 sessionId); 488 | 489 | common.TSStatus createSchemaTemplate(1:TSCreateSchemaTemplateReq req); 490 | 491 | common.TSStatus appendSchemaTemplate(1:TSAppendSchemaTemplateReq req); 492 | 493 | common.TSStatus pruneSchemaTemplate(1:TSPruneSchemaTemplateReq req); 494 | 495 | TSQueryTemplateResp querySchemaTemplate(1:TSQueryTemplateReq req); 496 | 497 | common.TSStatus setSchemaTemplate(1:TSSetSchemaTemplateReq req); 498 | 499 | common.TSStatus unsetSchemaTemplate(1:TSUnsetSchemaTemplateReq req); 500 | 501 | common.TSStatus dropSchemaTemplate(1:TSDropSchemaTemplateReq req); 502 | } -------------------------------------------------------------------------------- /thrift/common.thrift: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | namespace java org.apache.iotdb.common.rpc.thrift 21 | namespace py iotdb.thrift.common 22 | 23 | // Define a set of ip:port address 24 | struct TEndPoint { 25 | 1: required string ip 26 | 2: required i32 port 27 | } 28 | 29 | // The return status code and message in each response. 30 | struct TSStatus { 31 | 1: required i32 code 32 | 2: optional string message 33 | 3: optional list subStatus 34 | 4: optional TEndPoint redirectNode 35 | } 36 | 37 | enum TConsensusGroupType { 38 | PartitionRegion, 39 | DataRegion, 40 | SchemaRegion 41 | } 42 | 43 | struct TConsensusGroupId { 44 | 1: required TConsensusGroupType type 45 | 2: required i32 id 46 | } 47 | 48 | struct TSeriesPartitionSlot { 49 | 1: required i32 slotId 50 | } 51 | 52 | struct TTimePartitionSlot { 53 | 1: required i64 startTime 54 | } 55 | 56 | struct TRegionReplicaSet { 57 | 1: required TConsensusGroupId regionId 58 | 2: required list dataNodeLocations 59 | } 60 | 61 | struct TNodeResource { 62 | 1: required i32 cpuCoreNum 63 | 2: required i64 maxMemory 64 | } 65 | 66 | struct TConfigNodeLocation { 67 | 1: required i32 configNodeId 68 | 2: required TEndPoint internalEndPoint 69 | 3: required TEndPoint consensusEndPoint 70 | } 71 | 72 | struct TDataNodeLocation { 73 | 1: required i32 dataNodeId 74 | // TEndPoint for DataNode's client rpc 75 | 2: required TEndPoint clientRpcEndPoint 76 | // TEndPoint for DataNode's cluster internal rpc 77 | 3: required TEndPoint internalEndPoint 78 | // TEndPoint for exchange data between DataNodes 79 | 4: required TEndPoint mPPDataExchangeEndPoint 80 | // TEndPoint for DataNode's dataRegion consensus protocol 81 | 5: required TEndPoint dataRegionConsensusEndPoint 82 | // TEndPoint for DataNode's schemaRegion consensus protocol 83 | 6: required TEndPoint schemaRegionConsensusEndPoint 84 | } 85 | 86 | struct TDataNodeConfiguration { 87 | 1: required TDataNodeLocation location 88 | 2: required TNodeResource resource 89 | } 90 | 91 | enum TRegionMigrateFailedType { 92 | AddPeerFailed, 93 | RemovePeerFailed, 94 | RemoveConsensusGroupFailed, 95 | DeleteRegionFailed, 96 | CreateRegionFailed 97 | } 98 | 99 | struct TFlushReq { 100 | 1: optional string isSeq 101 | 2: optional list storageGroups 102 | 3: optional i32 dataNodeId 103 | } 104 | 105 | // for node management 106 | struct TSchemaNode { 107 | 1: required string nodeName 108 | 2: required byte nodeType 109 | } 110 | 111 | struct TSetTTLReq { 112 | 1: required list storageGroupPathPattern 113 | 2: required i64 TTL 114 | } 115 | --------------------------------------------------------------------------------