├── .asf.yaml ├── .github ├── dependabot.yml └── workflows │ ├── e2e.yml │ └── go.yml ├── .gitignore ├── LICENSE ├── LICENSE-binary ├── Makefile ├── NOTICE ├── NOTICE-binary ├── README.md ├── README_ZH.md ├── client ├── bitmap.go ├── column.go ├── column_decoder.go ├── errors.go ├── field.go ├── field_test.go ├── protocol.go ├── rowrecord.go ├── rowrecord_test.go ├── rpcdataset.go ├── session.go ├── sessiondataset.go ├── sessionpool.go ├── tablesession.go ├── tablesessionpool.go ├── tablet.go ├── tablet_test.go ├── tsblock.go ├── utils.go └── utils_test.go ├── common ├── GoUnusedProtection__.go ├── common-consts.go └── common.go ├── example ├── session_example.go ├── session_pool │ ├── session_pool_example.go │ └── table │ │ └── table_session_pool_example.go └── table │ └── table_session_example.go ├── go.mod ├── go.sum ├── rpc ├── GoUnusedProtection__.go ├── client-consts.go └── client.go └── test └── e2e ├── Dockerfile.iotdb-client-go ├── Dockerfile.iotdb-server ├── docker-compose.yml ├── e2e_table_test.go ├── e2e_test.go └── start-1c1d.sh /.asf.yaml: -------------------------------------------------------------------------------- 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 | github: 21 | description: "Apache IoTDB Client for Go" 22 | homepage: https://iotdb.apache.org/ 23 | labels: 24 | - timeseries 25 | - database 26 | - go 27 | - client 28 | features: 29 | wiki: true 30 | issues: true 31 | enabled_merge_buttons: 32 | squash: true 33 | merge: false 34 | rebase: false 35 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to You under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | version: 2 19 | 20 | updates: 21 | - package-ecosystem: gomod 22 | directory: "/" 23 | schedule: 24 | interval: daily 25 | 26 | - package-ecosystem: "github-actions" 27 | directory: "/" 28 | schedule: 29 | interval: daily 30 | -------------------------------------------------------------------------------- /.github/workflows/e2e.yml: -------------------------------------------------------------------------------- 1 | name: E2E Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - 'rel/*' 8 | pull_request: 9 | branches: 10 | - main 11 | - 'rel/*' 12 | # allow manually run the action: 13 | workflow_dispatch: 14 | 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ github.ref }} 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | 21 | build: 22 | name: e2e test 23 | runs-on: ubuntu-latest 24 | 25 | steps: 26 | 27 | - name: Check out code into the Go module directory 28 | uses: actions/checkout@v4 29 | 30 | - name: Cache Go dependency 31 | uses: actions/cache@v4 32 | with: 33 | path: | 34 | ~/.cache/go-build 35 | ~/go/pkg/mod 36 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 37 | restore-keys: | 38 | ${{ runner.os }}-go- 39 | 40 | - name: Cache Maven dependency 41 | uses: actions/cache@v4 42 | with: 43 | path: ~/.m2/repository 44 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 45 | restore-keys: | 46 | ${{ runner.os }}-maven- 47 | 48 | - name: Integration test 49 | run: | 50 | make e2e_test e2e_test_clean 51 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - 'rel/*' 8 | pull_request: 9 | branches: 10 | - main 11 | - 'rel/*' 12 | # allow manually run the action: 13 | workflow_dispatch: 14 | 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ github.ref }} 17 | cancel-in-progress: true 18 | 19 | 20 | jobs: 21 | 22 | build: 23 | name: Build 24 | runs-on: ${{ matrix.os }} 25 | strategy: 26 | matrix: 27 | os: [macos-latest, ubuntu-latest, windows-latest] 28 | go: ['1.13', 'stable'] 29 | exclude: 30 | - os: macos-latest 31 | go: '1.13' 32 | steps: 33 | 34 | - name: Check out code into the Go module directory 35 | uses: actions/checkout@v4 36 | - name: Set up Go ${{ matrix.go }} 37 | uses: actions/setup-go@v5 38 | with: 39 | go-version: ${{ matrix.go }} 40 | 41 | - name: Get dependencies 42 | run: | 43 | go get -v -t -d ./... 44 | 45 | - name: Install curl on Ubuntu 46 | if: matrix.os == 'ubuntu-latest' 47 | run: sudo apt-get update && sudo apt-get install -y curl 48 | 49 | - name: Install curl on Windows 50 | if: matrix.os == 'windows-latest' 51 | run: choco install curl -y 52 | 53 | - name: Build 54 | run: | 55 | make generate 56 | go build -v ./... 57 | 58 | - name: Test 59 | run: make test 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .DS_Store 3 | output/ 4 | 5 | # Binaries for programs and plugins 6 | *.exe 7 | *.exe~ 8 | *.dll 9 | *.so 10 | *.dylib 11 | thrift/ 12 | 13 | # Test binary, built with `go test -c` 14 | *.test 15 | 16 | # Output of the go coverage tool, specifically when used with LiteIDE 17 | *.out 18 | 19 | # Dependency directories (remove the comment below to include it) 20 | # vendor/ 21 | 22 | 23 | **/*.pid 24 | **/gc.log* 25 | **/logs/* 26 | **/lib/** 27 | **/data/** 28 | **/raft/** 29 | 30 | 31 | # Eclipse IDE files 32 | **/.classpath 33 | **/.project 34 | **/.settings/ 35 | # src/main/resources/ 36 | # intellij IDE files 37 | **/*.iml 38 | **/.idea/ 39 | **/*.log 40 | **/*.ipr 41 | **/*.iws 42 | # Apple OS X related 43 | **/.DS_Store 44 | derby-tsfile-db 45 | 46 | # build generated 47 | **/target/ 48 | 49 | # intermediately generated locally 50 | 51 | *.txt 52 | 53 | *.jar 54 | *.gz 55 | *.tar.gz 56 | *.tar 57 | #src/test/resources/logback.xml 58 | 59 | ### CSV ### 60 | *.csv 61 | ### Maven ### 62 | grafana/target/ 63 | !grafana/.mvn/wrapper/maven-wrapper.jar 64 | grafana/.mvn/ 65 | 66 | grafana/logs/ 67 | *.log 68 | 69 | ### STS ### 70 | .apt_generated 71 | .classpath 72 | .factorypath 73 | .project 74 | .settings 75 | .springBeans 76 | 77 | 78 | ### NetBeans ### 79 | **/nbproject/private/ 80 | **/nbbuild/ 81 | **/dist/ 82 | **/nbdist/ 83 | **/.nb-gradle/ 84 | grafana/data/ 85 | 86 | ### vscode project 87 | **/.vscode/ 88 | 89 | 90 | grafana/data/test.csv 91 | **/lib/ 92 | /target/ 93 | *.tsfile 94 | tsfile/src/test/resources/*.ts 95 | 96 | ### Apache release ### 97 | local-snapshots-dir/ 98 | venv/ 99 | 100 | partitions.tmp 101 | partitions 102 | node_identifier 103 | 104 | ### temporary file of the distributed version ### 105 | remote/ 106 | 107 | # gitpod 108 | .theia/ 109 | 110 | classes/ 111 | 112 | ### Cmake files ### 113 | *.cmake 114 | Makefile 115 | **/CMakeFiles/ 116 | -------------------------------------------------------------------------------- /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 [yyyy] [name of copyright owner] 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 | 203 | -------------------------------------------------------------------------------- 204 | APACHE IOTDB SUBCOMPONENTS 205 | -------------------------------------------------------------------------------- 206 | 207 | 208 | -------------------------------------------------------------------------------- /LICENSE-binary: -------------------------------------------------------------------------------- 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 [yyyy] [name of copyright owner] 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 | 203 | 204 | ================================================================ 205 | APACHE IOTDB SUBCOMPONENTS: 206 | 207 | Apache IoTDB project includes a number of submodules with separate copyright notices 208 | and license terms. Your use of these submodules is subject to the terms and 209 | conditions of the following licenses. 210 | 211 | ================================================================ 212 | 213 | The binary distribution of this product bundles these dependencies under the 214 | following license. See licenses/ for text of these licenses. 215 | 216 | Apache Software Foundation License 2.0 217 | -------------------------------------- 218 | github.com/apache/thrift@v0.15.0 219 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | 19 | UNAME_S := $(shell uname -s) 20 | UNAME_P := $(shell uname -p) 21 | 22 | ifeq ($(UNAME_S),Linux) 23 | ifeq ($(UNAME_P),x86_64) 24 | OS_CLASSIFIER := linux-x86_64 25 | THRIFT_EXEC := thrift/bin/thrift 26 | endif 27 | ifeq ($(UNAME_P),aarch64) 28 | OS_CLASSIFIER := linux-aarch64 29 | THRIFT_EXEC := thrift/bin/thrift 30 | endif 31 | endif 32 | ifeq ($(UNAME_S),Darwin) 33 | ifeq ($(UNAME_P),x86_64) 34 | OS_CLASSIFIER := mac-x86_64 35 | THRIFT_EXEC := thrift/bin/thrift 36 | endif 37 | ifeq ($(UNAME_P),arm) 38 | OS_CLASSIFIER := mac-aarch64 39 | THRIFT_EXEC := thrift/bin/thrift 40 | endif 41 | endif 42 | ifneq ($(UNAME_S),Linux) 43 | ifneq ($(UNAME_S),Darwin) 44 | OS_CLASSIFIER := windows-x86_64 45 | THRIFT_EXEC := thrift/bin/Release/thrift.exe 46 | endif 47 | endif 48 | 49 | all: generate 50 | 51 | generate: 52 | 53 | @if [ -f "../../iotdb-protocol/thrift-commons/src/main/thrift/common.thrift" ]; then \ 54 | cd ../..; \ 55 | mvn clean package -pl iotdb-protocol/thrift-datanode -am; \ 56 | cd iotdb-client/client-go; \ 57 | cp -r ../../iotdb-protocol/thrift-commons/target/generated-sources-go/common common; \ 58 | cp -r ../../iotdb-protocol/thrift-datanode/target/generated-sources-go/rpc rpc; \ 59 | else \ 60 | echo "Downloading and unpacking iotdb-tools-thrift-0.14.1.0-$(OS_CLASSIFIER).zip"; \ 61 | rm -rf thrift; \ 62 | mkdir -p thrift; \ 63 | curl -L -o thrift/iotdb-tools-thrift.zip https://repo1.maven.org/maven2/org/apache/iotdb/tools/iotdb-tools-thrift/0.14.1.0/iotdb-tools-thrift-0.14.1.0-$(OS_CLASSIFIER).zip; \ 64 | unzip -o thrift/iotdb-tools-thrift.zip -d thrift; \ 65 | curl -o common.thrift https://raw.githubusercontent.com/apache/iotdb/master/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift; \ 66 | $(THRIFT_EXEC) -out . -gen go:package_prefix=github.com/apache/iotdb-client-go/ common.thrift; \ 67 | curl -o client.thrift https://raw.githubusercontent.com/apache/iotdb/master/iotdb-protocol/thrift-datanode/src/main/thrift/client.thrift; \ 68 | $(THRIFT_EXEC) -out . -gen go:package_prefix=github.com/apache/iotdb-client-go/ client.thrift; \ 69 | rm -f common.thrift; \ 70 | rm -f client.thrift; \ 71 | fi 72 | @rm -rf rpc/i_client_r_p_c_service-remote 73 | 74 | .PHONY: generate all test e2e_test e2e_test_clean 75 | 76 | test: 77 | go test -v ./client/... 78 | 79 | e2e_test: 80 | sh -c "cd /tmp/ && rm -rf iotdb && git clone https://github.com/apache/iotdb.git && cd iotdb && mvn clean package -pl distribution -am -DskipTests" 81 | mkdir -p target/iotdb 82 | unzip -o -q /tmp/iotdb/distribution/target/apache-iotdb-*-all-bin.zip -d target 83 | mv target/*/* target/iotdb 84 | docker compose -f test/e2e/docker-compose.yml up --build --abort-on-container-exit --remove-orphans 85 | 86 | e2e_test_clean: 87 | rm -rf /tmp/iotdb target 88 | docker compose -f test/e2e/docker-compose.yml down 89 | 90 | #only used for project structure that the iotdb main project is in the parent folder of this project. 91 | e2e_test_for_parent_git_repo: 92 | mkdir -p target/iotdb 93 | unzip -o -q ../../distribution/target/apache-iotdb-*-all-bin.zip -d target 94 | mv target/*/* target/iotdb 95 | docker compose -f test/e2e/docker-compose.yml up --build --abort-on-container-exit --remove-orphans 96 | 97 | e2e_test_clean_for_parent_git_repo: 98 | rm -rf target 99 | docker compose -f test/e2e/docker-compose.yml down 100 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Apache IoTDB 2 | Copyright 2018-2020 The Apache Software Foundation. 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org/). 6 | 7 | ============================================================================ 8 | 9 | IoTDB project uses 4 Chinese Patents: 10 | * 201711384490X 11 | * 201810111712.9 12 | * 201711322631.5 13 | * 201711319331.1 14 | 15 | According to the Apache 2.0 License. The owner of the patents, Tsinghua University, 16 | grant the users the right to the use of patent under the requirement of Apache 2.0 License. 17 | 18 | ============================================================================ 19 | 20 | -------------------------------------------------------------------------------- /NOTICE-binary: -------------------------------------------------------------------------------- 1 | Apache IoTDB 2 | Copyright 2018-2020 The Apache Software Foundation. 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org/). 6 | 7 | ============================================================================ 8 | 9 | IoTDB project uses 4 Chinese Patents: 10 | * 201711384490X 11 | * 201810111712.9 12 | * 201711322631.5 13 | * 201711319331.1 14 | 15 | According to the Apache 2.0 License. The owner of the patents, Tsinghua University, 16 | grant the users the right to the use of patent under the requirement of Apache 2.0 License. 17 | 18 | ============================================================================ 19 | 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 21 | [English](./README.md) | [中文](./README_ZH.md) 22 | 23 | # Apache IoTDB 24 | 25 | Apache IoTDB (Database for Internet of Things) is an IoT native database with high performance for 26 | data management and analysis, deployable on the edge and the cloud. Due to its light-weight 27 | architecture, high performance and rich feature set together with its deep integration with 28 | Apache Hadoop, Spark and Flink, Apache IoTDB can meet the requirements of massive data storage, 29 | high-speed data ingestion and complex data analysis in the IoT industrial fields. 30 | 31 | # Apache IoTDB Client for Go 32 | 33 | [![E2E Tests](https://github.com/apache/iotdb-client-go/actions/workflows/e2e.yml/badge.svg)](https://github.com/apache/iotdb-client-go/actions/workflows/e2e.yml) 34 | [![GitHub release](https://img.shields.io/github/release/apache/iotdb-client-go.svg)](https://github.com/apache/iotdb-client-go/releases) 35 | [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) 36 | ![](https://github-size-badge.herokuapp.com/apache/iotdb-client-go.svg) 37 | ![](https://img.shields.io/badge/platform-win%20%7C%20macos%20%7C%20linux-yellow.svg) 38 | [![IoTDB Website](https://img.shields.io/website-up-down-green-red/https/shields.io.svg?label=iotdb-website)](https://iotdb.apache.org/) 39 | 40 | ## Overview 41 | 42 | This is the GoLang client of Apache IoTDB. 43 | 44 | Apache IoTDB website: 45 | Apache IoTDB GitHub: 46 | 47 | ## Prerequisites 48 | 49 | golang >= 1.13 50 | 51 | ## How to Use the Client (Quick Start) 52 | 53 | With go mod 54 | 55 | ```sh 56 | export GO111MODULE=on 57 | export GOPROXY=https://goproxy.io 58 | 59 | mkdir session_example && cd session_example 60 | 61 | curl -o session_example.go -L https://github.com/apache/iotdb-client-go/raw/main/example/session_example.go 62 | 63 | go mod init session_example 64 | go run session_example.go 65 | ``` 66 | 67 | Without go mod 68 | 69 | ```sh 70 | # get thrift 0.15.0 71 | go get github.com/apache/thrift 72 | cd $GOPATH/src/github.com/apache/thrift 73 | git checkout 0.15.0 74 | 75 | mkdir -p $GOPATH/src/iotdb-client-go-example/session_example 76 | cd $GOPATH/src/iotdb-client-go-example/session_example 77 | 78 | curl -o session_example.go -L https://github.com/apache/iotdb-client-go/raw/main/example/session_example.go 79 | go run session_example.go 80 | ``` 81 | 82 | ## How to Use the SessionPool 83 | 84 | SessionPool is a wrapper of a Session Set. Using SessionPool, the user do not need to consider how to reuse a session connection. 85 | If there is no available connections and the pool reaches its max size, the all methods will hang until there is a available connection. 86 | The PutBack method must be called after use 87 | 88 | ### New sessionPool 89 | 90 | standalone 91 | 92 | ```golang 93 | 94 | config := &client.PoolConfig{ 95 | Host: host, 96 | Port: port, 97 | UserName: user, 98 | Password: password, 99 | } 100 | sessionPool = client.NewSessionPool(config, 3, 60000, 60000, false) 101 | 102 | ``` 103 | 104 | cluster or doubleLive 105 | 106 | ```golang 107 | 108 | config := &client.PoolConfig{ 109 | UserName: user, 110 | Password: password, 111 | NodeUrls: strings.Split("127.0.0.1:6667,127.0.0.1:6668", ","), 112 | } 113 | sessionPool = client.NewSessionPool(config, 3, 60000, 60000, false) 114 | 115 | ``` 116 | 117 | ### Get session through sessionPool, putback after use 118 | 119 | set storage group 120 | 121 | ```golang 122 | 123 | session, err := sessionPool.GetSession() 124 | defer sessionPool.PutBack(session) 125 | if err == nil { 126 | session.SetStorageGroup(sg) 127 | } 128 | 129 | ``` 130 | 131 | query statement 132 | 133 | ```golang 134 | 135 | var timeout int64 = 1000 136 | session, err := sessionPool.GetSession() 137 | defer sessionPool.PutBack(session) 138 | if err != nil { 139 | log.Print(err) 140 | return 141 | } 142 | sessionDataSet, err := session.ExecuteQueryStatement(sql, &timeout) 143 | if err == nil { 144 | defer sessionDataSet.Close() 145 | printDataSet1(sessionDataSet) 146 | } else { 147 | log.Println(err) 148 | } 149 | 150 | ``` 151 | 152 | ## Developer environment requirements for iotdb-client-go 153 | 154 | ### OS 155 | 156 | * Linux, Macos or other unix-like OS 157 | * Windows+bash(WSL, cygwin, Git Bash) 158 | 159 | ### Command Line Tools 160 | 161 | * golang >= 1.13 162 | * make >= 3.0 163 | * curl >= 7.1.1 164 | * thrift 0.15.0 165 | 166 | ## Troubleshooting 167 | 168 | ### Thrift version compatibility issues 169 | 170 | In the branch `rel/0.13` and earlier versions, the version of apache/thrift is `v0.14.1`. 171 | In the latest version, apache/thrift has been upgraded to `v0.15.0`. 172 | 173 | The two versions are not compatible on some interfaces. Using mismatched version will cause compilation errors. 174 | 175 | The interfaces changed in the two versions are as follows: 176 | 177 | 1. `NewTSocketConf`. This function returns two values in the version `v0.14.1` and only one value in the version `v0.15.0`. 178 | 2. `NewTFramedTransport` has been deprecated, use `NewTFramedTransportConf` instead. 179 | 180 | For more details, please take a look at this PR: [update thrift to 0.15.0 to fit IoTDB 0.13.0](https://github.com/apache/iotdb-client-go/pull/41) 181 | 182 | ### Parameter name mismatch with actual usage in function 'Open' 183 | 184 | The implementation of the function ```client/session.go/Open()``` is mismatched with the description. 185 | The parameter `connectionTimeoutInMs` represents connection timeout in milliseconds. 186 | However, in the older version, this function did not implement correctly, regarding it as nanosecond instead. 187 | The bug is now fixed. 188 | Positive value of this parameter means connection timeout in milliseconds. 189 | Set 0 for no timeout. 190 | -------------------------------------------------------------------------------- /README_ZH.md: -------------------------------------------------------------------------------- 1 | 21 | [English](./README.md) | [中文](./README_ZH.md) 22 | 23 | # Apache IoTDB 24 | 25 | Apache IoTDB(物联网数据库)是一个物联网原生数据库,在数据管理和分析方面表现良好,可部署在边缘设备和云上。 26 | 由于其轻量级架构、高性能和丰富的功能集,以及与Apache Hadoop、Spark和Flink的深度集成, 27 | Apache IoTDB可以满足物联网工业领域的海量数据存储、高速数据摄取和复杂数据分析的要求。 28 | 29 | # Apache IoTDB Go语言客户端 30 | 31 | [![E2E Tests](https://github.com/apache/iotdb-client-go/actions/workflows/e2e.yml/badge.svg)](https://github.com/apache/iotdb-client-go/actions/workflows/e2e.yml) 32 | [![GitHub release](https://img.shields.io/github/release/apache/iotdb-client-go.svg)](https://github.com/apache/iotdb-client-go/releases) 33 | [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) 34 | ![](https://github-size-badge.herokuapp.com/apache/iotdb-client-go.svg) 35 | ![](https://img.shields.io/badge/platform-win%20%7C%20macos%20%7C%20linux-yellow.svg) 36 | [![IoTDB Website](https://img.shields.io/website-up-down-green-red/https/shields.io.svg?label=iotdb-website)](https://iotdb.apache.org/) 37 | 38 | ## 概览 39 | 40 | 本仓库是Apache IoTDB的Go语言客户端. 41 | 42 | Apache IoTDB website: https://iotdb.apache.org 43 | Apache IoTDB GitHub: https://github.com/apache/iotdb 44 | 45 | ## 环境准备 46 | 47 | golang >= 1.13 48 | 49 | ## 如何使用 (快速上手) 50 | 51 | 使用go mod 52 | 53 | ```sh 54 | export GO111MODULE=on 55 | export GOPROXY=https://goproxy.io 56 | 57 | mkdir session_example && cd session_example 58 | 59 | curl -o session_example.go -L https://github.com/apache/iotdb-client-go/raw/main/example/session_example.go 60 | 61 | go mod init session_example 62 | go run session_example.go 63 | ``` 64 | 65 | 不使用go mod,采用GOPATH 66 | 67 | ```sh 68 | # get thrift 0.15.0 69 | go get github.com/apache/thrift 70 | cd $GOPATH/src/github.com/apache/thrift 71 | git checkout 0.15.0 72 | 73 | mkdir -p $GOPATH/src/iotdb-client-go-example/session_example 74 | cd $GOPATH/src/iotdb-client-go-example/session_example 75 | curl -o session_example.go -L https://github.com/apache/iotdb-client-go/raw/main/example/session_example.go 76 | go run session_example.go 77 | ``` 78 | 79 | ## SessionPool 80 | 通过SessionPool管理session,用户不需要考虑如何重用session,当到达pool的最大值时,获取session的请求会阻塞 81 | 注意:session使用完成后需要调用PutBack方法 82 | 83 | ### 创建sessionPool 84 | 85 | 单实例 86 | ```golang 87 | 88 | config := &client.PoolConfig{ 89 | Host: host, 90 | Port: port, 91 | UserName: user, 92 | Password: password, 93 | } 94 | sessionPool = client.NewSessionPool(config, 3, 60000, 60000, false) 95 | 96 | ``` 97 | 98 | 分布式或双活 99 | 100 | ```golang 101 | 102 | config := &client.PoolConfig{ 103 | UserName: user, 104 | Password: password, 105 | NodeUrls: strings.Split("127.0.0.1:6667,127.0.0.1:6668", ","), 106 | } 107 | sessionPool = client.NewSessionPool(config, 3, 60000, 60000, false) 108 | 109 | ``` 110 | 111 | 112 | ### 使用sessionPool获取session,使用完手动调用PutBack 113 | 114 | 例1:设置存储组 115 | 116 | ```golang 117 | 118 | session, err := sessionPool.GetSession() 119 | defer sessionPool.PutBack(session) 120 | if err == nil { 121 | session.SetStorageGroup(sg) 122 | } 123 | 124 | ``` 125 | 126 | 例2:查询 127 | 128 | ```golang 129 | 130 | var timeout int64 = 1000 131 | session, err := sessionPool.GetSession() 132 | defer sessionPool.PutBack(session) 133 | if err != nil { 134 | log.Print(err) 135 | return 136 | } 137 | sessionDataSet, err := session.ExecuteQueryStatement(sql, &timeout) 138 | if err == nil { 139 | defer sessionDataSet.Close() 140 | printDataSet1(sessionDataSet) 141 | } else { 142 | log.Println(err) 143 | } 144 | 145 | ``` 146 | 147 | 148 | ## iotdb-client-go的开发者环境要求 149 | 150 | ### 操作系统 151 | 152 | * Linux、Macos或其他类unix系统 153 | * Windows+bash(WSL、cygwin、Git Bash) 154 | 155 | ### 命令行工具 156 | 157 | * golang >= 1.13 158 | * make >= 3.0 159 | * curl >= 7.1.1 160 | * thrift 0.15.0 161 | 162 | ## 疑难解答 163 | 164 | ### thrift 版本兼容性问题 165 | 166 | 分支`rel/0.13`以及更早前的版本中,apache/thrift的版本为`v0.14.1`。 167 | 在更新的版本中,apache/thrift已经升级为`v0.15.0`。 168 | 这两个版本在一些接口上并不兼容,使用不对应的版本,会导致编译报错。 169 | 170 | 两个版本中有改动的接口如下: 171 | 172 | 1. `NewTSocketConf`。该接口在`v0.14.1`版本中返回2个值,在`v0.15.0`版本中返回1个值。 173 | 2. `NewTFramedTransport`已弃用,改用为`NewTFramedTransportConf`。 174 | 175 | 更多相关的内容可以参考这个PR:[update thrift to 0.15.0 to fit IoTDB 0.13.0](https://github.com/apache/iotdb-client-go/pull/41) 176 | 177 | ### Open函数参数名称与实际功能不匹配的问题 178 | 179 | 函数```client/session.go/Open()```有一个参数`connectionTimeoutInMs`,表示了以毫秒为单位的连接超时时间。 180 | 但旧版本中,该函数在实现时并未正确进行单位转换,而是将其看作了纳秒。现在该问题已修复。 181 | 当该参数为0时,表示不设置超时时间;当设置为正数时表示以毫秒为单位的超时时间。 182 | -------------------------------------------------------------------------------- /client/bitmap.go: -------------------------------------------------------------------------------- 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 | package client 21 | 22 | type BitMap struct { 23 | size int 24 | bits []byte 25 | } 26 | 27 | var BitUtil = []byte{1, 2, 4, 8, 16, 32, 64, 128} 28 | var UnmarkBitUtil = []byte{ 29 | 0xFE, // 11111110 30 | 0xFD, // 11111101 31 | 0xFB, // 11111011 32 | 0xF7, // 11110111 33 | 0xEF, // 11101111 34 | 0xDF, // 11011111 35 | 0xBF, // 10111111 36 | 0x7F, // 01111111 37 | } 38 | 39 | func NewBitMap(size int) *BitMap { 40 | // Need to maintain consistency with the calculation method on the IoTDB side. 41 | bitMap := &BitMap{ 42 | size: size, 43 | bits: make([]byte, size/8+1), 44 | } 45 | return bitMap 46 | } 47 | 48 | func (b *BitMap) Mark(position int) { 49 | b.bits[position/8] |= BitUtil[position%8] 50 | } 51 | 52 | func (b *BitMap) UnMark(position int) { 53 | b.bits[position/8] &= UnmarkBitUtil[position%8] 54 | } 55 | 56 | func (b *BitMap) IsMarked(position int) bool { 57 | return (b.bits[position/8] & BitUtil[position%8]) != 0 58 | } 59 | 60 | func (b *BitMap) IsAllUnmarked() bool { 61 | for i := 0; i < b.size/8; i++ { 62 | if b.bits[i] != 0 { 63 | return false 64 | } 65 | } 66 | for i := 0; i < b.size%8; i++ { 67 | if (b.bits[b.size/8] & BitUtil[i]) != 0 { 68 | return false 69 | } 70 | } 71 | return true 72 | } 73 | 74 | func (b *BitMap) GetBits() []byte { 75 | return b.bits 76 | } 77 | -------------------------------------------------------------------------------- /client/column_decoder.go: -------------------------------------------------------------------------------- 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 | package client 21 | 22 | import ( 23 | "bytes" 24 | "encoding/binary" 25 | "fmt" 26 | ) 27 | 28 | type ColumnDecoder interface { 29 | ReadColumn(reader *bytes.Reader, dataType TSDataType, positionCount int32) (Column, error) 30 | } 31 | 32 | func deserializeNullIndicators(reader *bytes.Reader, positionCount int32) ([]bool, error) { 33 | b, err := reader.ReadByte() 34 | if err != nil { 35 | return nil, err 36 | } 37 | mayHaveNull := b != 0 38 | if !mayHaveNull { 39 | return nil, nil 40 | } 41 | return deserializeBooleanArray(reader, positionCount) 42 | } 43 | 44 | func deserializeBooleanArray(reader *bytes.Reader, size int32) ([]bool, error) { 45 | packedSize := (size + 7) / 8 46 | packedBytes := make([]byte, packedSize) 47 | 48 | _, err := reader.Read(packedBytes) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | // read null bits 8 at a time 54 | output := make([]bool, size) 55 | currentByte := 0 56 | fullGroups := int(size) & ^0b111 57 | for pos := 0; pos < fullGroups; pos += 8 { 58 | b := packedBytes[currentByte] 59 | currentByte++ 60 | 61 | output[pos+0] = (b & 0b10000000) != 0 62 | output[pos+1] = (b & 0b01000000) != 0 63 | output[pos+2] = (b & 0b00100000) != 0 64 | output[pos+3] = (b & 0b00010000) != 0 65 | output[pos+4] = (b & 0b00001000) != 0 66 | output[pos+5] = (b & 0b00000100) != 0 67 | output[pos+6] = (b & 0b00000010) != 0 68 | output[pos+7] = (b & 0b00000001) != 0 69 | } 70 | 71 | // read last null bits 72 | if remaining := int(size) % 8; remaining > 0 { 73 | b := packedBytes[len(packedBytes)-1] 74 | mask := uint8(0b10000000) 75 | 76 | for pos := fullGroups; pos < int(size); pos++ { 77 | output[pos] = (b & mask) != 0 78 | mask >>= 1 79 | } 80 | } 81 | 82 | return output, nil 83 | } 84 | 85 | type baseColumnDecoder struct{} 86 | 87 | type Int32ArrayColumnDecoder struct { 88 | baseColumnDecoder 89 | } 90 | 91 | func (decoder *Int32ArrayColumnDecoder) ReadColumn(reader *bytes.Reader, dataType TSDataType, positionCount int32) (Column, error) { 92 | // Serialized data layout: 93 | // +---------------+-----------------+-------------+ 94 | // | may have null | null indicators | values | 95 | // +---------------+-----------------+-------------+ 96 | // | byte | list[byte] | list[int32] | 97 | // +---------------+-----------------+-------------+ 98 | nullIndicators, err := deserializeNullIndicators(reader, positionCount) 99 | if err != nil { 100 | return nil, err 101 | } 102 | switch dataType { 103 | case INT32, DATE: 104 | intValues := make([]int32, positionCount) 105 | for i := int32(0); i < positionCount; i++ { 106 | if nullIndicators != nil && nullIndicators[i] { 107 | continue 108 | } 109 | err := binary.Read(reader, binary.BigEndian, &intValues[i]) 110 | if err != nil { 111 | return nil, err 112 | } 113 | } 114 | return NewIntColumn(0, positionCount, nullIndicators, intValues) 115 | case FLOAT: 116 | floatValues := make([]float32, positionCount) 117 | for i := int32(0); i < positionCount; i++ { 118 | if nullIndicators != nil && nullIndicators[i] { 119 | continue 120 | } 121 | err := binary.Read(reader, binary.BigEndian, &floatValues[i]) 122 | if err != nil { 123 | return nil, err 124 | } 125 | } 126 | return NewFloatColumn(0, positionCount, nullIndicators, floatValues) 127 | } 128 | return nil, fmt.Errorf("invalid data type: %v", dataType) 129 | } 130 | 131 | type Int64ArrayColumnDecoder struct { 132 | baseColumnDecoder 133 | } 134 | 135 | func (decoder *Int64ArrayColumnDecoder) ReadColumn(reader *bytes.Reader, dataType TSDataType, positionCount int32) (Column, error) { 136 | // Serialized data layout: 137 | // +---------------+-----------------+-------------+ 138 | // | may have null | null indicators | values | 139 | // +---------------+-----------------+-------------+ 140 | // | byte | list[byte] | list[int64] | 141 | // +---------------+-----------------+-------------+ 142 | nullIndicators, err := deserializeNullIndicators(reader, positionCount) 143 | if err != nil { 144 | return nil, err 145 | } 146 | switch dataType { 147 | case INT64, TIMESTAMP: 148 | values := make([]int64, positionCount) 149 | for i := int32(0); i < positionCount; i++ { 150 | if nullIndicators != nil && nullIndicators[i] { 151 | continue 152 | } 153 | if err = binary.Read(reader, binary.BigEndian, &values[i]); err != nil { 154 | return nil, err 155 | } 156 | } 157 | return NewLongColumn(0, positionCount, nullIndicators, values) 158 | case DOUBLE: 159 | values := make([]float64, positionCount) 160 | for i := int32(0); i < positionCount; i++ { 161 | if nullIndicators != nil && nullIndicators[i] { 162 | continue 163 | } 164 | if err = binary.Read(reader, binary.BigEndian, &values[i]); err != nil { 165 | return nil, err 166 | } 167 | } 168 | return NewDoubleColumn(0, positionCount, nullIndicators, values) 169 | } 170 | return nil, fmt.Errorf("invalid data type: %v", dataType) 171 | } 172 | 173 | type ByteArrayColumnDecoder struct { 174 | baseColumnDecoder 175 | } 176 | 177 | func (decoder *ByteArrayColumnDecoder) ReadColumn(reader *bytes.Reader, dataType TSDataType, positionCount int32) (Column, error) { 178 | // Serialized data layout: 179 | // +---------------+-----------------+-------------+ 180 | // | may have null | null indicators | values | 181 | // +---------------+-----------------+-------------+ 182 | // | byte | list[byte] | list[byte] | 183 | // +---------------+-----------------+-------------+ 184 | 185 | if dataType != BOOLEAN { 186 | return nil, fmt.Errorf("invalid data type: %v", dataType) 187 | } 188 | nullIndicators, err := deserializeNullIndicators(reader, positionCount) 189 | if err != nil { 190 | return nil, err 191 | } 192 | values, err := deserializeBooleanArray(reader, positionCount) 193 | if err != nil { 194 | return nil, err 195 | } 196 | return NewBooleanColumn(0, positionCount, nullIndicators, values) 197 | } 198 | 199 | type BinaryArrayColumnDecoder struct { 200 | baseColumnDecoder 201 | } 202 | 203 | func (decoder *BinaryArrayColumnDecoder) ReadColumn(reader *bytes.Reader, dataType TSDataType, positionCount int32) (Column, error) { 204 | // Serialized data layout: 205 | // +---------------+-----------------+-------------+ 206 | // | may have null | null indicators | values | 207 | // +---------------+-----------------+-------------+ 208 | // | byte | list[byte] | list[entry] | 209 | // +---------------+-----------------+-------------+ 210 | // 211 | // Each entry is represented as: 212 | // +---------------+-------+ 213 | // | value length | value | 214 | // +---------------+-------+ 215 | // | int32 | bytes | 216 | // +---------------+-------+ 217 | 218 | if TEXT != dataType { 219 | return nil, fmt.Errorf("invalid data type: %v", dataType) 220 | } 221 | nullIndicators, err := deserializeNullIndicators(reader, positionCount) 222 | if err != nil { 223 | return nil, err 224 | } 225 | values := make([]*Binary, positionCount) 226 | for i := int32(0); i < positionCount; i++ { 227 | if nullIndicators != nil && nullIndicators[i] { 228 | continue 229 | } 230 | var length int32 231 | err := binary.Read(reader, binary.BigEndian, &length) 232 | if err != nil { 233 | return nil, err 234 | } 235 | value := make([]byte, length) 236 | _, err = reader.Read(value) 237 | if err != nil { 238 | return nil, err 239 | } 240 | values[i] = NewBinary(value) 241 | } 242 | return NewBinaryColumn(0, positionCount, nullIndicators, values) 243 | } 244 | 245 | type RunLengthColumnDecoder struct { 246 | baseColumnDecoder 247 | } 248 | 249 | func (decoder *RunLengthColumnDecoder) ReadColumn(reader *bytes.Reader, dataType TSDataType, positionCount int32) (Column, error) { 250 | // Serialized data layout: 251 | // +-----------+-------------------------+ 252 | // | encoding | serialized inner column | 253 | // +-----------+-------------------------+ 254 | // | byte | list[byte] | 255 | // +-----------+-------------------------+ 256 | columnEncoding, err := deserializeColumnEncoding(reader) 257 | if err != nil { 258 | return nil, err 259 | } 260 | columnDecoder, err := getColumnDecoder(columnEncoding) 261 | if err != nil { 262 | return nil, err 263 | } 264 | column, err := columnDecoder.ReadColumn(reader, dataType, 1) 265 | if err != nil { 266 | return nil, err 267 | } 268 | return NewRunLengthEncodedColumn(column, positionCount) 269 | } 270 | -------------------------------------------------------------------------------- /client/errors.go: -------------------------------------------------------------------------------- 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 | package client 21 | 22 | import ( 23 | "bytes" 24 | "github.com/apache/iotdb-client-go/common" 25 | ) 26 | 27 | type BatchError struct { 28 | statuses []*common.TSStatus 29 | } 30 | 31 | func (e *BatchError) Error() string { 32 | buff := bytes.Buffer{} 33 | for _, status := range e.statuses { 34 | buff.WriteString(*status.Message + ";") 35 | } 36 | return buff.String() 37 | } 38 | 39 | func (e *BatchError) GetStatuses() []*common.TSStatus { 40 | return e.statuses 41 | } 42 | 43 | func NewBatchError(statuses []*common.TSStatus) *BatchError { 44 | return &BatchError{ 45 | statuses: statuses, 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /client/field.go: -------------------------------------------------------------------------------- 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 | package client 21 | 22 | import "time" 23 | 24 | type Field struct { 25 | dataType TSDataType 26 | name string 27 | value interface{} 28 | } 29 | 30 | func (f *Field) IsNull() bool { 31 | return f.value == nil 32 | } 33 | 34 | func (f *Field) GetDataType() TSDataType { 35 | return f.dataType 36 | } 37 | 38 | func (f *Field) GetValue() interface{} { 39 | return f.value 40 | } 41 | 42 | func (f *Field) GetInt32() int32 { 43 | if f.value == nil { 44 | return 0 45 | } 46 | return f.value.(int32) 47 | } 48 | 49 | func (f *Field) GetInt64() int64 { 50 | if f.value == nil { 51 | return 0 52 | } 53 | return f.value.(int64) 54 | } 55 | 56 | func (f *Field) GetFloat32() float32 { 57 | if f.value == nil { 58 | return 0 59 | } 60 | return f.value.(float32) 61 | } 62 | 63 | func (f *Field) GetFloat64() float64 { 64 | if f.value == nil { 65 | return 0 66 | } 67 | return f.value.(float64) 68 | } 69 | 70 | func (f *Field) GetName() string { 71 | return f.name 72 | } 73 | 74 | func (f *Field) GetText() string { 75 | if f.value == nil { 76 | return "" 77 | } 78 | switch f.value.(type) { 79 | case bool: 80 | if f.value.(bool) { 81 | return "true" 82 | } 83 | return "false" 84 | case int32: 85 | return int32ToString(f.value.(int32)) 86 | case int64: 87 | return int64ToString(f.value.(int64)) 88 | case float32: 89 | return float32ToString(f.value.(float32)) 90 | case float64: 91 | return float64ToString(f.value.(float64)) 92 | case string: 93 | return f.value.(string) 94 | case []byte: 95 | return bytesToHexString(f.value.([]byte)) 96 | case time.Time: 97 | return f.value.(time.Time).Format("2006-01-02") 98 | } 99 | return "" 100 | } 101 | -------------------------------------------------------------------------------- /client/field_test.go: -------------------------------------------------------------------------------- 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 | package client 21 | 22 | import ( 23 | "reflect" 24 | "testing" 25 | "time" 26 | ) 27 | 28 | func TestField_IsNull(t *testing.T) { 29 | type fields struct { 30 | dataType TSDataType 31 | name string 32 | value interface{} 33 | } 34 | tests := []struct { 35 | name string 36 | fields fields 37 | want bool 38 | }{ 39 | { 40 | name: "IsNull-1", 41 | fields: fields{ 42 | dataType: 0, 43 | name: "", 44 | value: nil, 45 | }, 46 | want: true, 47 | }, { 48 | name: "IsNull-2", 49 | fields: fields{ 50 | dataType: 0, 51 | name: "", 52 | value: 1, 53 | }, 54 | want: false, 55 | }, 56 | } 57 | for _, tt := range tests { 58 | t.Run(tt.name, func(t *testing.T) { 59 | f := &Field{ 60 | dataType: tt.fields.dataType, 61 | name: tt.fields.name, 62 | value: tt.fields.value, 63 | } 64 | if got := f.IsNull(); got != tt.want { 65 | t.Errorf("Field.IsNull() = %v, want %v", got, tt.want) 66 | } 67 | }) 68 | } 69 | } 70 | 71 | func TestField_GetDataType(t *testing.T) { 72 | type fields struct { 73 | dataType TSDataType 74 | name string 75 | value interface{} 76 | } 77 | tests := []struct { 78 | name string 79 | fields fields 80 | want TSDataType 81 | }{ 82 | { 83 | name: "GetDataType-BOOLEAN", 84 | fields: fields{ 85 | dataType: BOOLEAN, 86 | name: "", 87 | value: nil, 88 | }, 89 | want: BOOLEAN, 90 | }, { 91 | name: "GetDataType-INT32", 92 | fields: fields{ 93 | dataType: INT32, 94 | name: "", 95 | value: nil, 96 | }, 97 | want: INT32, 98 | }, { 99 | name: "GetDataType-INT64", 100 | fields: fields{ 101 | dataType: INT64, 102 | name: "", 103 | value: nil, 104 | }, 105 | want: INT64, 106 | }, { 107 | name: "GetDataType-FLOAT", 108 | fields: fields{ 109 | dataType: FLOAT, 110 | name: "", 111 | value: nil, 112 | }, 113 | want: FLOAT, 114 | }, { 115 | name: "GetDataType-DOUBLE", 116 | fields: fields{ 117 | dataType: DOUBLE, 118 | name: "", 119 | value: nil, 120 | }, 121 | want: DOUBLE, 122 | }, { 123 | name: "GetDataType-TEXT", 124 | fields: fields{ 125 | dataType: TEXT, 126 | name: "", 127 | value: nil, 128 | }, 129 | want: TEXT, 130 | }, { 131 | name: "GetDataType-STRING", 132 | fields: fields{ 133 | dataType: STRING, 134 | name: "", 135 | value: nil, 136 | }, 137 | want: STRING, 138 | }, { 139 | name: "GetDataType-BLOB", 140 | fields: fields{ 141 | dataType: BLOB, 142 | name: "", 143 | value: nil, 144 | }, 145 | want: BLOB, 146 | }, { 147 | name: "GetDataType-TIMESTAMP", 148 | fields: fields{ 149 | dataType: TIMESTAMP, 150 | name: "", 151 | value: nil, 152 | }, 153 | want: TIMESTAMP, 154 | }, { 155 | name: "GetDataType-DATE", 156 | fields: fields{ 157 | dataType: DATE, 158 | name: "", 159 | value: nil, 160 | }, 161 | want: DATE, 162 | }, 163 | } 164 | for _, tt := range tests { 165 | t.Run(tt.name, func(t *testing.T) { 166 | f := &Field{ 167 | dataType: tt.fields.dataType, 168 | name: tt.fields.name, 169 | value: tt.fields.value, 170 | } 171 | if got := f.GetDataType(); got != tt.want { 172 | t.Errorf("Field.GetDataType() = %v, want %v", got, tt.want) 173 | } 174 | }) 175 | } 176 | } 177 | 178 | func TestField_GetValue(t *testing.T) { 179 | type fields struct { 180 | dataType TSDataType 181 | name string 182 | value interface{} 183 | } 184 | tests := []struct { 185 | name string 186 | fields fields 187 | want interface{} 188 | }{ 189 | { 190 | name: "GetValue-BOOLEAN", 191 | fields: fields{ 192 | dataType: BOOLEAN, 193 | name: "", 194 | value: true, 195 | }, 196 | want: true, 197 | }, { 198 | name: "GetValue-INT32", 199 | fields: fields{ 200 | dataType: INT32, 201 | name: "", 202 | value: int32(65535), 203 | }, 204 | want: int32(65535), 205 | }, { 206 | name: "GetValue-INT64", 207 | fields: fields{ 208 | dataType: INT64, 209 | name: "", 210 | value: int64(65535), 211 | }, 212 | want: int64(65535), 213 | }, { 214 | name: "GetValue-FLOAT", 215 | fields: fields{ 216 | dataType: FLOAT, 217 | name: "", 218 | value: float32(32.768), 219 | }, 220 | want: float32(32.768), 221 | }, { 222 | name: "GetValue-DOUBLE", 223 | fields: fields{ 224 | dataType: DOUBLE, 225 | name: "", 226 | value: float64(32.768), 227 | }, 228 | want: float64(32.768), 229 | }, { 230 | name: "GetValue-TEXT", 231 | fields: fields{ 232 | dataType: TEXT, 233 | name: "", 234 | value: "TEXT", 235 | }, 236 | want: "TEXT", 237 | }, { 238 | name: "GetValue-STRING", 239 | fields: fields{ 240 | dataType: STRING, 241 | name: "", 242 | value: "STRING", 243 | }, 244 | want: "STRING", 245 | }, { 246 | name: "GetValue-BLOB", 247 | fields: fields{ 248 | dataType: BLOB, 249 | name: "", 250 | value: []byte("BLOB"), 251 | }, 252 | want: []byte("BLOB"), 253 | }, { 254 | name: "GetValue-TIMESTAMP", 255 | fields: fields{ 256 | dataType: TIMESTAMP, 257 | name: "", 258 | value: int64(65535), 259 | }, 260 | want: int64(65535), 261 | }, { 262 | name: "GetValue-DATE", 263 | fields: fields{ 264 | dataType: DATE, 265 | name: "", 266 | value: time.Date(2024, time.Month(4), 1, 0, 0, 0, 0, time.UTC), 267 | }, 268 | want: time.Date(2024, time.Month(4), 1, 0, 0, 0, 0, time.UTC), 269 | }, 270 | } 271 | for _, tt := range tests { 272 | t.Run(tt.name, func(t *testing.T) { 273 | f := &Field{ 274 | dataType: tt.fields.dataType, 275 | name: tt.fields.name, 276 | value: tt.fields.value, 277 | } 278 | if got := f.GetValue(); !reflect.DeepEqual(got, tt.want) { 279 | t.Errorf("Field.GetValue() = %v, want %v", got, tt.want) 280 | } 281 | }) 282 | } 283 | } 284 | 285 | func TestField_GetInt32(t *testing.T) { 286 | type fields struct { 287 | dataType TSDataType 288 | name string 289 | value interface{} 290 | } 291 | tests := []struct { 292 | name string 293 | fields fields 294 | want int32 295 | }{ 296 | { 297 | name: "GetInt32-01", 298 | fields: fields{ 299 | dataType: INT32, 300 | name: "", 301 | value: int32(65535), 302 | }, 303 | want: 65535, 304 | }, { 305 | name: "GetInt32-02", 306 | fields: fields{ 307 | dataType: INT32, 308 | name: "restart_count", 309 | value: nil, 310 | }, 311 | want: 0, 312 | }, 313 | } 314 | for _, tt := range tests { 315 | t.Run(tt.name, func(t *testing.T) { 316 | f := &Field{ 317 | dataType: tt.fields.dataType, 318 | name: tt.fields.name, 319 | value: tt.fields.value, 320 | } 321 | if got := f.GetInt32(); got != tt.want { 322 | t.Errorf("Field.GetInt32() = %v, want %v", got, tt.want) 323 | } 324 | }) 325 | } 326 | } 327 | 328 | func TestField_GetInt64(t *testing.T) { 329 | type fields struct { 330 | dataType TSDataType 331 | name string 332 | value interface{} 333 | } 334 | tests := []struct { 335 | name string 336 | fields fields 337 | want int64 338 | }{ 339 | { 340 | name: "GetInt64-01", 341 | fields: fields{ 342 | dataType: INT64, 343 | name: "", 344 | value: int64(65535), 345 | }, 346 | want: 65535, 347 | }, { 348 | name: "GetInt64-02", 349 | fields: fields{ 350 | dataType: INT64, 351 | name: "tickCount", 352 | value: nil, 353 | }, 354 | want: 0, 355 | }, 356 | } 357 | for _, tt := range tests { 358 | t.Run(tt.name, func(t *testing.T) { 359 | f := &Field{ 360 | dataType: tt.fields.dataType, 361 | name: tt.fields.name, 362 | value: tt.fields.value, 363 | } 364 | if got := f.GetInt64(); got != tt.want { 365 | t.Errorf("Field.GetInt64() = %v, want %v", got, tt.want) 366 | } 367 | }) 368 | } 369 | } 370 | 371 | func TestField_GetFloat32(t *testing.T) { 372 | type fields struct { 373 | dataType TSDataType 374 | name string 375 | value interface{} 376 | } 377 | tests := []struct { 378 | name string 379 | fields fields 380 | want float32 381 | }{ 382 | { 383 | name: "GetFloat32", 384 | fields: fields{ 385 | dataType: FLOAT, 386 | name: "", 387 | value: float32(32.768), 388 | }, 389 | want: 32.768, 390 | }, 391 | } 392 | for _, tt := range tests { 393 | t.Run(tt.name, func(t *testing.T) { 394 | f := &Field{ 395 | dataType: tt.fields.dataType, 396 | name: tt.fields.name, 397 | value: tt.fields.value, 398 | } 399 | if got := f.GetFloat32(); got != tt.want { 400 | t.Errorf("Field.GetFloat32() = %v, want %v", got, tt.want) 401 | } 402 | }) 403 | } 404 | } 405 | 406 | func TestField_GetFloat64(t *testing.T) { 407 | type fields struct { 408 | dataType TSDataType 409 | name string 410 | value interface{} 411 | } 412 | tests := []struct { 413 | name string 414 | fields fields 415 | want float64 416 | }{ 417 | { 418 | name: "GetFloat64", 419 | fields: fields{ 420 | dataType: DOUBLE, 421 | name: "", 422 | value: float64(32.768), 423 | }, 424 | want: 32.768, 425 | }, 426 | } 427 | for _, tt := range tests { 428 | t.Run(tt.name, func(t *testing.T) { 429 | f := &Field{ 430 | dataType: tt.fields.dataType, 431 | name: tt.fields.name, 432 | value: tt.fields.value, 433 | } 434 | if got := f.GetFloat64(); got != tt.want { 435 | t.Errorf("Field.GetFloat64() = %v, want %v", got, tt.want) 436 | } 437 | }) 438 | } 439 | } 440 | 441 | func TestField_GetText(t *testing.T) { 442 | type fields struct { 443 | dataType TSDataType 444 | name string 445 | value interface{} 446 | } 447 | tests := []struct { 448 | name string 449 | fields fields 450 | want string 451 | }{ 452 | { 453 | name: "GetText-01", 454 | fields: fields{ 455 | dataType: TEXT, 456 | name: "", 457 | value: "32.768", 458 | }, 459 | want: "32.768", 460 | }, { 461 | name: "GetText-02", 462 | fields: fields{ 463 | dataType: TEXT, 464 | name: "", 465 | value: nil, 466 | }, 467 | want: "", 468 | }, { 469 | name: "GetText-03", 470 | fields: fields{ 471 | dataType: INT32, 472 | name: "", 473 | value: int32(1), 474 | }, 475 | want: "1", 476 | }, { 477 | name: "GetText-04", 478 | fields: fields{ 479 | dataType: STRING, 480 | name: "", 481 | value: "STRING", 482 | }, 483 | want: "STRING", 484 | }, { 485 | name: "GetText-05", 486 | fields: fields{ 487 | dataType: BLOB, 488 | name: "", 489 | value: []byte("BLOB"), 490 | }, 491 | want: "0x424c4f42", 492 | }, { 493 | name: "GetText-06", 494 | fields: fields{ 495 | dataType: DATE, 496 | name: "", 497 | value: time.Date(2024, time.Month(4), 1, 0, 0, 0, 0, time.UTC), 498 | }, 499 | want: "2024-04-01", 500 | }, 501 | } 502 | for _, tt := range tests { 503 | t.Run(tt.name, func(t *testing.T) { 504 | f := &Field{ 505 | dataType: tt.fields.dataType, 506 | name: tt.fields.name, 507 | value: tt.fields.value, 508 | } 509 | if got := f.GetText(); got != tt.want { 510 | t.Errorf("Field.GetText() = %v, want %v", got, tt.want) 511 | } 512 | }) 513 | } 514 | } 515 | 516 | func TestField_getName(t *testing.T) { 517 | type fields struct { 518 | dataType TSDataType 519 | name string 520 | value interface{} 521 | } 522 | tests := []struct { 523 | name string 524 | fields fields 525 | want string 526 | }{ 527 | { 528 | name: "GetName", 529 | fields: fields{ 530 | dataType: TEXT, 531 | name: "temperature", 532 | value: float32(32), 533 | }, 534 | want: "temperature", 535 | }, 536 | } 537 | for _, tt := range tests { 538 | t.Run(tt.name, func(t *testing.T) { 539 | f := &Field{ 540 | dataType: tt.fields.dataType, 541 | name: tt.fields.name, 542 | value: tt.fields.value, 543 | } 544 | if got := f.GetName(); got != tt.want { 545 | t.Errorf("Field.GetName() = %v, want %v", got, tt.want) 546 | } 547 | }) 548 | } 549 | } 550 | -------------------------------------------------------------------------------- /client/protocol.go: -------------------------------------------------------------------------------- 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 | package client 21 | 22 | import "fmt" 23 | 24 | type TSDataType int8 25 | 26 | type TSEncoding uint8 27 | 28 | type TSCompressionType uint8 29 | 30 | const ( 31 | UNKNOWN TSDataType = -1 32 | BOOLEAN TSDataType = 0 33 | INT32 TSDataType = 1 34 | INT64 TSDataType = 2 35 | FLOAT TSDataType = 3 36 | DOUBLE TSDataType = 4 37 | TEXT TSDataType = 5 38 | TIMESTAMP TSDataType = 8 39 | DATE TSDataType = 9 40 | BLOB TSDataType = 10 41 | STRING TSDataType = 11 42 | ) 43 | 44 | var tsTypeMap = map[string]TSDataType{ 45 | "BOOLEAN": BOOLEAN, 46 | "INT32": INT32, 47 | "INT64": INT64, 48 | "FLOAT": FLOAT, 49 | "DOUBLE": DOUBLE, 50 | "TEXT": TEXT, 51 | "TIMESTAMP": TIMESTAMP, 52 | "DATE": DATE, 53 | "BLOB": BLOB, 54 | "STRING": STRING, 55 | } 56 | 57 | var byteToTsDataType = map[byte]TSDataType{ 58 | 0: BOOLEAN, 59 | 1: INT32, 60 | 2: INT64, 61 | 3: FLOAT, 62 | 4: DOUBLE, 63 | 5: TEXT, 64 | 8: TIMESTAMP, 65 | 9: DATE, 66 | 10: BLOB, 67 | 11: STRING, 68 | } 69 | 70 | func GetDataTypeByStr(name string) (TSDataType, error) { 71 | dataType, exists := tsTypeMap[name] 72 | if !exists { 73 | return UNKNOWN, fmt.Errorf("invalid input: %v", name) 74 | } 75 | return dataType, nil 76 | } 77 | 78 | func getDataTypeByByte(b byte) (TSDataType, error) { 79 | dataType, exists := byteToTsDataType[b] 80 | if !exists { 81 | return UNKNOWN, fmt.Errorf("invalid input: %v", b) 82 | } 83 | return dataType, nil 84 | } 85 | 86 | const ( 87 | PLAIN TSEncoding = 0 88 | DICTIONARY TSEncoding = 1 89 | RLE TSEncoding = 2 90 | DIFF TSEncoding = 3 91 | TS_2DIFF TSEncoding = 4 92 | BITMAP TSEncoding = 5 93 | GORILLA_V1 TSEncoding = 6 94 | REGULAR TSEncoding = 7 95 | GORILLA TSEncoding = 8 96 | ZIGZAG TSEncoding = 9 97 | FREQ TSEncoding = 10 98 | CHIMP TSEncoding = 11 99 | SPRINTZ TSEncoding = 12 100 | RLBE TSEncoding = 13 101 | ) 102 | 103 | const ( 104 | UNCOMPRESSED TSCompressionType = 0 105 | SNAPPY TSCompressionType = 1 106 | GZIP TSCompressionType = 2 107 | LZ4 TSCompressionType = 7 108 | ZSTD TSCompressionType = 8 109 | LZMA2 TSCompressionType = 9 110 | ) 111 | 112 | // TSStatusCode 113 | const ( 114 | SuccessStatus int32 = 200 115 | IncompatibleVersion int32 = 201 116 | ConfigurationError int32 = 202 117 | StartUpError int32 = 203 118 | ShutDownError int32 = 204 119 | 120 | UnsupportedOperation int32 = 300 121 | ExecuteStatementError int32 = 301 122 | MultipleError int32 = 302 123 | IllegalParameter int32 = 303 124 | OverlapWithExistingTask int32 = 304 125 | InternalServerError int32 = 305 126 | 127 | RedirectionRecommend int32 = 400 128 | 129 | DatabaseNotExist int32 = 500 130 | DatabaseAlreadyExists int32 = 501 131 | SeriesOverflow int32 = 502 132 | TimeseriesAlreadyExist int32 = 503 133 | TimeseriesInBlackList int32 = 504 134 | AliasAlreadyExist int32 = 505 135 | PathAlreadyExist int32 = 506 136 | MetadataError int32 = 507 137 | PathNotExist int32 = 508 138 | IllegalPath int32 = 509 139 | CreateTemplateError int32 = 510 140 | DuplicatedTemplate int32 = 511 141 | UndefinedTemplate int32 = 512 142 | TemplateNotSet int32 = 513 143 | DifferentTemplate int32 = 514 144 | TemplateIsInUse int32 = 515 145 | TemplateIncompatible int32 = 516 146 | SegmentNotFound int32 = 517 147 | PageOutOfSpace int32 = 518 148 | RecordDuplicated int32 = 519 149 | SegmentOutOfSpace int32 = 520 150 | SchemaFileNotExists int32 = 521 151 | OversizeRecord int32 = 522 152 | SchemaFileRedoLogBroken int32 = 523 153 | TemplateNotActivated int32 = 524 154 | 155 | SystemReadOnly int32 = 600 156 | StorageEngineError int32 = 601 157 | StorageEngineNotReady int32 = 602 158 | DataregionProcessError int32 = 603 159 | TsfileProcessorError int32 = 604 160 | WriteProcessError int32 = 605 161 | WriteProcessReject int32 = 606 162 | OutOfTtl int32 = 607 163 | CompactionError int32 = 608 164 | AlignedTimeseriesError int32 = 609 165 | WalError int32 = 610 166 | DiskSpaceInsufficient int32 = 611 167 | 168 | SqlParseError int32 = 700 169 | SemanticError int32 = 701 170 | GenerateTimeZoneError int32 = 702 171 | SetTimeZoneError int32 = 703 172 | QueryNotAllowed int32 = 704 173 | LogicalOperatorError int32 = 705 174 | LogicalOptimizeError int32 = 706 175 | UnsupportedFillType int32 = 707 176 | QueryProcessError int32 = 708 177 | MppMemoryNotEnough int32 = 709 178 | CloseOperationError int32 = 710 179 | TsblockSerializeError int32 = 711 180 | InternalRequestTimeOut int32 = 712 181 | InternalRequestRetryError int32 = 713 182 | 183 | InitAuthError int32 = 800 184 | WrongLoginPassword int32 = 801 185 | NotLogin int32 = 802 186 | NoPermission int32 = 803 187 | UserNotExist int32 = 804 188 | UserAlreadyExist int32 = 805 189 | UserAlreadyHasRole int32 = 806 190 | UserNotHasRole int32 = 807 191 | RoleNotExist int32 = 808 192 | RoleAlreadyExist int32 = 809 193 | AlreadyHasPrivilege int32 = 810 194 | NotHasPrivilege int32 = 811 195 | ClearPermissionCacheError int32 = 812 196 | UnknownAuthPrivilege int32 = 813 197 | UnsupportedAuthOperation int32 = 814 198 | AuthIoException int32 = 815 199 | 200 | MigrateRegionError int32 = 900 201 | CreateRegionError int32 = 901 202 | DeleteRegionError int32 = 902 203 | PartitionCacheUpdateError int32 = 903 204 | ConsensusNotInitialized int32 = 904 205 | RegionLeaderChangeError int32 = 905 206 | NoAvailableRegionGroup int32 = 906 207 | 208 | DatanodeAlreadyRegistered int32 = 1000 209 | NoEnoughDatanode int32 = 1001 210 | AddConfignodeError int32 = 1002 211 | RemoveConfignodeError int32 = 1003 212 | DatanodeNotExist int32 = 1004 213 | DatanodeStopError int32 = 1005 214 | RemoveDatanodeError int32 = 1006 215 | RegisterRemovedDatanode int32 = 1007 216 | CanNotConnectDatanode int32 = 1008 217 | 218 | LoadFileError int32 = 1100 219 | LoadPieceOfTsfileError int32 = 1101 220 | DeserializePieceOfTsfileError int32 = 1102 221 | SyncConnectionError int32 = 1103 222 | SyncFileRedirectionError int32 = 1104 223 | SyncFileError int32 = 1105 224 | CreatePipeSinkError int32 = 1106 225 | PipeError int32 = 1107 226 | PipeserverError int32 = 1108 227 | VerifyMetadataError int32 = 1109 228 | 229 | UdfLoadClassError int32 = 1200 230 | UdfDownloadError int32 = 1201 231 | CreateUdfOnDatanodeError int32 = 1202 232 | DropUdfOnDatanodeError int32 = 1203 233 | 234 | CreateTriggerError int32 = 1300 235 | DropTriggerError int32 = 1301 236 | TriggerFireError int32 = 1302 237 | TriggerLoadClassError int32 = 1303 238 | TriggerDownloadError int32 = 1304 239 | CreateTriggerInstanceError int32 = 1305 240 | ActiveTriggerInstanceError int32 = 1306 241 | DropTriggerInstanceError int32 = 1307 242 | UpdateTriggerLocationError int32 = 1308 243 | 244 | NoSuchCq int32 = 1400 245 | CqAlreadyActive int32 = 1401 246 | CqAlreadyExist int32 = 1402 247 | CqUpdateLastExecTimeError int32 = 1403 248 | ) 249 | 250 | const ( 251 | TimestampColumnName = "Time" 252 | ) 253 | -------------------------------------------------------------------------------- /client/rowrecord.go: -------------------------------------------------------------------------------- 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 | package client 21 | 22 | type RowRecord struct { 23 | timestamp int64 24 | fields []*Field 25 | } 26 | 27 | func (r *RowRecord) GetFields() []*Field { 28 | return r.fields 29 | } 30 | 31 | func (r *RowRecord) GetTimestamp() int64 { 32 | return r.timestamp 33 | } 34 | -------------------------------------------------------------------------------- /client/rowrecord_test.go: -------------------------------------------------------------------------------- 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 | package client 21 | 22 | import ( 23 | "reflect" 24 | "testing" 25 | ) 26 | 27 | func TestRowRecord_GetFields(t *testing.T) { 28 | type fields struct { 29 | timestamp int64 30 | fields []*Field 31 | } 32 | tests := []struct { 33 | name string 34 | fields fields 35 | want []*Field 36 | }{ 37 | { 38 | name: "GetFields", 39 | fields: fields{ 40 | timestamp: 0, 41 | fields: []*Field{{ 42 | dataType: FLOAT, 43 | name: "temperature", 44 | value: 0.1, 45 | }}, 46 | }, 47 | want: []*Field{{ 48 | dataType: FLOAT, 49 | name: "temperature", 50 | value: 0.1, 51 | }}, 52 | }, 53 | } 54 | for _, tt := range tests { 55 | t.Run(tt.name, func(t *testing.T) { 56 | r := &RowRecord{ 57 | timestamp: tt.fields.timestamp, 58 | fields: tt.fields.fields, 59 | } 60 | if got := r.GetFields(); !reflect.DeepEqual(got, tt.want) { 61 | t.Errorf("RowRecord.GetFields() = %v, want %v", got, tt.want) 62 | } 63 | }) 64 | } 65 | } 66 | 67 | func TestRowRecord_GetTimestamp(t *testing.T) { 68 | type fields struct { 69 | timestamp int64 70 | fields []*Field 71 | } 72 | tests := []struct { 73 | name string 74 | fields fields 75 | want int64 76 | }{ 77 | { 78 | name: "GetTimestamp", 79 | fields: fields{ 80 | timestamp: 1024, 81 | }, 82 | want: 1024, 83 | }, 84 | } 85 | for _, tt := range tests { 86 | t.Run(tt.name, func(t *testing.T) { 87 | r := &RowRecord{ 88 | timestamp: tt.fields.timestamp, 89 | fields: tt.fields.fields, 90 | } 91 | if got := r.GetTimestamp(); got != tt.want { 92 | t.Errorf("RowRecord.GetTimestamp() = %v, want %v", got, tt.want) 93 | } 94 | }) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /client/sessiondataset.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/apache/iotdb-client-go/rpc" 5 | "time" 6 | ) 7 | 8 | type SessionDataSet struct { 9 | ioTDBRpcDataSet *IoTDBRpcDataSet 10 | } 11 | 12 | func NewSessionDataSet(sql string, columnNameList []string, columnTypeList []string, columnNameIndex map[string]int32, queryId int64, statementId int64, client *rpc.IClientRPCServiceClient, sessionId int64, queryResult [][]byte, ignoreTimestamp bool, timeout *int64, moreData bool, fetchSize int32, zoneId string, timeFactor int32, columnIndex2TsBlockColumnIndexList []int32) (*SessionDataSet, error) { 13 | rpcDataSet, err := NewIoTDBRpcDataSet(sql, columnNameList, columnTypeList, columnNameIndex, ignoreTimestamp, moreData, queryId, statementId, client, sessionId, queryResult, fetchSize, timeout, zoneId, DEFAULT_TIME_FORMAT, timeFactor, columnIndex2TsBlockColumnIndexList) 14 | if err != nil { 15 | return nil, err 16 | } 17 | return &SessionDataSet{ioTDBRpcDataSet: rpcDataSet}, nil 18 | } 19 | 20 | func (s *SessionDataSet) Next() (bool, error) { 21 | return s.ioTDBRpcDataSet.Next() 22 | } 23 | 24 | func (s *SessionDataSet) Close() error { 25 | return s.ioTDBRpcDataSet.Close() 26 | } 27 | 28 | func (s *SessionDataSet) IsNull(columnName string) (bool, error) { 29 | return s.ioTDBRpcDataSet.isNullByColumnName(columnName) 30 | } 31 | 32 | func (s *SessionDataSet) IsNullByIndex(columnIndex int32) (bool, error) { 33 | return s.ioTDBRpcDataSet.isNullByIndex(columnIndex) 34 | } 35 | 36 | func (s *SessionDataSet) GetBooleanByIndex(columnIndex int32) (bool, error) { 37 | return s.ioTDBRpcDataSet.getBooleanByIndex(columnIndex) 38 | } 39 | 40 | func (s *SessionDataSet) GetBoolean(columnName string) (bool, error) { 41 | return s.ioTDBRpcDataSet.getBoolean(columnName) 42 | } 43 | 44 | func (s *SessionDataSet) GetDoubleByIndex(columnIndex int32) (float64, error) { 45 | return s.ioTDBRpcDataSet.getDoubleByIndex(columnIndex) 46 | } 47 | 48 | func (s *SessionDataSet) GetDouble(columnName string) (float64, error) { 49 | return s.ioTDBRpcDataSet.getDouble(columnName) 50 | } 51 | 52 | func (s *SessionDataSet) GetFloatByIndex(columnIndex int32) (float32, error) { 53 | return s.ioTDBRpcDataSet.getFloatByIndex(columnIndex) 54 | } 55 | 56 | func (s *SessionDataSet) GetFloat(columnName string) (float32, error) { 57 | return s.ioTDBRpcDataSet.getFloat(columnName) 58 | } 59 | 60 | func (s *SessionDataSet) GetIntByIndex(columnIndex int32) (int32, error) { 61 | return s.ioTDBRpcDataSet.getIntByIndex(columnIndex) 62 | } 63 | 64 | func (s *SessionDataSet) GetInt(columnName string) (int32, error) { 65 | return s.ioTDBRpcDataSet.getInt(columnName) 66 | } 67 | 68 | func (s *SessionDataSet) GetLongByIndex(columnIndex int32) (int64, error) { 69 | return s.ioTDBRpcDataSet.getLongByIndex(columnIndex) 70 | } 71 | 72 | func (s *SessionDataSet) GetLong(columnName string) (int64, error) { 73 | return s.ioTDBRpcDataSet.getLong(columnName) 74 | } 75 | 76 | func (s *SessionDataSet) GetObjectByIndex(columnIndex int32) (interface{}, error) { 77 | return s.ioTDBRpcDataSet.getObjectByIndex(columnIndex) 78 | } 79 | 80 | func (s *SessionDataSet) GetObject(columnName string) (interface{}, error) { 81 | return s.ioTDBRpcDataSet.getObject(columnName) 82 | } 83 | 84 | func (s *SessionDataSet) GetStringByIndex(columnIndex int32) (string, error) { 85 | return s.ioTDBRpcDataSet.getStringByIndex(columnIndex) 86 | } 87 | 88 | func (s *SessionDataSet) GetString(columnName string) (string, error) { 89 | return s.ioTDBRpcDataSet.getString(columnName) 90 | } 91 | 92 | func (s *SessionDataSet) GetTimestampByIndex(columnIndex int32) (time.Time, error) { 93 | return s.ioTDBRpcDataSet.getTimestampByIndex(columnIndex) 94 | } 95 | 96 | func (s *SessionDataSet) GetTimestamp(columnName string) (time.Time, error) { 97 | return s.ioTDBRpcDataSet.getTimestamp(columnName) 98 | } 99 | 100 | func (s *SessionDataSet) GetDateByIndex(columnIndex int32) (time.Time, error) { 101 | return s.ioTDBRpcDataSet.GetDateByIndex(columnIndex) 102 | } 103 | 104 | func (s *SessionDataSet) GetDate(columnName string) (time.Time, error) { 105 | return s.ioTDBRpcDataSet.GetDate(columnName) 106 | } 107 | 108 | func (s *SessionDataSet) GetBlobByIndex(columnIndex int32) (*Binary, error) { 109 | return s.ioTDBRpcDataSet.getBinaryByIndex(columnIndex) 110 | } 111 | 112 | func (s *SessionDataSet) GetBlob(columnName string) (*Binary, error) { 113 | return s.ioTDBRpcDataSet.getBinary(columnName) 114 | } 115 | 116 | func (s *SessionDataSet) FindColumn(columnName string) int32 { 117 | return s.ioTDBRpcDataSet.findColumn(columnName) 118 | } 119 | 120 | func (s *SessionDataSet) GetColumnNames() []string { 121 | return s.ioTDBRpcDataSet.columnNameList 122 | } 123 | 124 | func (s *SessionDataSet) GetColumnTypes() []string { 125 | return s.ioTDBRpcDataSet.columnTypeList 126 | } 127 | -------------------------------------------------------------------------------- /client/sessionpool.go: -------------------------------------------------------------------------------- 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 | package client 21 | 22 | import ( 23 | "errors" 24 | "log" 25 | "runtime" 26 | "time" 27 | ) 28 | 29 | var errTimeout = errors.New("get session timeout") 30 | var errPoolClosed = errors.New("sessionPool has closed") 31 | var defaultMultiple = 5 32 | 33 | type SessionPool struct { 34 | config *PoolConfig 35 | maxSize int 36 | waitToGetSessionTimeoutInMs int 37 | enableCompression bool 38 | connectionTimeoutInMs int 39 | ch chan Session 40 | sem chan int8 41 | } 42 | 43 | type PoolConfig struct { 44 | Host string 45 | Port string 46 | NodeUrls []string 47 | UserName string 48 | Password string 49 | FetchSize int32 50 | TimeZone string 51 | ConnectRetryMax int 52 | Database string 53 | sqlDialect string 54 | } 55 | 56 | func NewSessionPool(conf *PoolConfig, maxSize, connectionTimeoutInMs, waitToGetSessionTimeoutInMs int, 57 | enableCompression bool) SessionPool { 58 | return newSessionPoolWithSqlDialect(conf, maxSize, connectionTimeoutInMs, waitToGetSessionTimeoutInMs, enableCompression, TreeSqlDialect) 59 | } 60 | 61 | func newSessionPoolWithSqlDialect(conf *PoolConfig, maxSize, connectionTimeoutInMs, waitToGetSessionTimeoutInMs int, 62 | enableCompression bool, sqlDialect string) SessionPool { 63 | conf.sqlDialect = sqlDialect 64 | if maxSize <= 0 { 65 | maxSize = runtime.NumCPU() * defaultMultiple 66 | } 67 | var ch = make(chan Session, maxSize) 68 | var sem = make(chan int8, maxSize) 69 | return SessionPool{ 70 | config: conf, 71 | maxSize: maxSize, 72 | waitToGetSessionTimeoutInMs: waitToGetSessionTimeoutInMs, 73 | connectionTimeoutInMs: connectionTimeoutInMs, 74 | enableCompression: enableCompression, 75 | ch: ch, 76 | sem: sem, 77 | } 78 | } 79 | 80 | func (spool *SessionPool) GetSession() (session Session, err error) { 81 | for { 82 | select { 83 | case spool.sem <- 1: 84 | select { 85 | case session, ok := <-spool.ch: 86 | if ok { 87 | return session, nil 88 | } else { 89 | log.Println("sessionPool has closed") 90 | return session, errPoolClosed 91 | } 92 | default: 93 | config := spool.config 94 | session, err := spool.ConstructSession(config) 95 | return session, err 96 | } 97 | case <-time.After(time.Millisecond * time.Duration(spool.waitToGetSessionTimeoutInMs)): 98 | log.Println("get session timeout") 99 | return session, errTimeout 100 | } 101 | } 102 | } 103 | 104 | func (spool *SessionPool) getTableSession() (ITableSession, error) { 105 | tableSession := PooledTableSession{} 106 | session, err := spool.GetSession() 107 | if err != nil { 108 | return nil, err 109 | } 110 | tableSession.session = session 111 | tableSession.sessionPool = spool 112 | return &tableSession, nil 113 | } 114 | 115 | func (spool *SessionPool) ConstructSession(config *PoolConfig) (session Session, err error) { 116 | if len(config.NodeUrls) > 0 { 117 | session, err = newClusterSessionWithSqlDialect(getClusterSessionConfig(config)) 118 | if err != nil { 119 | return session, err 120 | } 121 | if err = session.OpenCluster(spool.enableCompression); err != nil { 122 | log.Print(err) 123 | return session, err 124 | } 125 | } else { 126 | session = newSessionWithSpecifiedSqlDialect(getSessionConfig(config)) 127 | if err := session.Open(spool.enableCompression, spool.connectionTimeoutInMs); err != nil { 128 | log.Print(err) 129 | return session, err 130 | } 131 | } 132 | return session, nil 133 | } 134 | 135 | func getSessionConfig(config *PoolConfig) *Config { 136 | return &Config{ 137 | Host: config.Host, 138 | Port: config.Port, 139 | UserName: config.UserName, 140 | Password: config.Password, 141 | FetchSize: config.FetchSize, 142 | TimeZone: config.TimeZone, 143 | ConnectRetryMax: config.ConnectRetryMax, 144 | sqlDialect: config.sqlDialect, 145 | Database: config.Database, 146 | } 147 | } 148 | 149 | func getClusterSessionConfig(config *PoolConfig) *ClusterConfig { 150 | return &ClusterConfig{ 151 | NodeUrls: config.NodeUrls, 152 | UserName: config.UserName, 153 | Password: config.Password, 154 | FetchSize: config.FetchSize, 155 | TimeZone: config.TimeZone, 156 | ConnectRetryMax: config.ConnectRetryMax, 157 | sqlDialect: config.sqlDialect, 158 | Database: config.Database, 159 | } 160 | } 161 | 162 | func (spool *SessionPool) PutBack(session Session) { 163 | defer func() { 164 | if r := recover(); r != nil { 165 | session.Close() 166 | } 167 | }() 168 | if session.trans.IsOpen() { 169 | spool.ch <- session 170 | } 171 | <-spool.sem 172 | } 173 | 174 | func (spool *SessionPool) dropSession(session Session) { 175 | defer func() { 176 | if e := recover(); e != nil { 177 | session.Close() 178 | } 179 | }() 180 | err := session.Close() 181 | if err != nil { 182 | log.Println("Failed to close session ", session) 183 | } 184 | <-spool.sem 185 | } 186 | 187 | func (spool *SessionPool) Close() { 188 | close(spool.ch) 189 | for s := range spool.ch { 190 | s.Close() 191 | } 192 | close(spool.sem) 193 | } 194 | -------------------------------------------------------------------------------- /client/tablesession.go: -------------------------------------------------------------------------------- 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 | package client 21 | 22 | import "github.com/apache/iotdb-client-go/common" 23 | 24 | // ITableSession defines an interface for interacting with IoTDB tables. 25 | // It supports operations such as data insertion, executing queries, and closing the session. 26 | // Implementations of this interface are expected to manage connections and ensure 27 | // proper resource cleanup. 28 | // 29 | // Each method may return an error to indicate issues such as connection errors 30 | // or execution failures. 31 | // 32 | // Since this interface includes a Close method, it is recommended to use 33 | // defer to ensure the session is properly closed. 34 | type ITableSession interface { 35 | 36 | // Insert inserts a Tablet into the database. 37 | // 38 | // Parameters: 39 | // - tablet: A pointer to a Tablet containing time-series data to be inserted. 40 | // 41 | // Returns: 42 | // - r: A pointer to TSStatus indicating the execution result. 43 | // - err: An error if an issue occurs during the operation, such as a connection error or execution failure. 44 | Insert(tablet *Tablet) (r *common.TSStatus, err error) 45 | 46 | // ExecuteNonQueryStatement executes a non-query SQL statement, such as a DDL or DML command. 47 | // 48 | // Parameters: 49 | // - sql: The SQL statement to execute. 50 | // 51 | // Returns: 52 | // - r: A pointer to TSStatus indicating the execution result. 53 | // - err: An error if an issue occurs during the operation, such as a connection error or execution failure. 54 | ExecuteNonQueryStatement(sql string) (r *common.TSStatus, err error) 55 | 56 | // ExecuteQueryStatement executes a query SQL statement and returns the result set. 57 | // 58 | // Parameters: 59 | // - sql: The SQL query statement to execute. 60 | // - timeoutInMs: A pointer to the timeout duration in milliseconds for the query execution. 61 | // 62 | // Returns: 63 | // - result: A pointer to SessionDataSet containing the query results. 64 | // - err: An error if an issue occurs during the operation, such as a connection error or execution failure. 65 | ExecuteQueryStatement(sql string, timeoutInMs *int64) (*SessionDataSet, error) 66 | 67 | // Close closes the session, releasing any held resources. 68 | // 69 | // Returns: 70 | // - err: An error if there is an issue with closing the IoTDB connection. 71 | Close() (err error) 72 | } 73 | 74 | // TableSession represents a session for interacting with IoTDB in relational mode. 75 | // It wraps a Session instance, providing methods for executing SQL statements 76 | // and managing the lifecycle of the session. 77 | type TableSession struct { 78 | session Session 79 | } 80 | 81 | // NewTableSession creates a new TableSession instance using the provided configuration. 82 | // 83 | // Parameters: 84 | // - config: The configuration for the session. 85 | // - enableRPCCompression: A boolean indicating whether RPC compression is enabled. 86 | // - connectionTimeoutInMs: The timeout in milliseconds for establishing a connection. 87 | // 88 | // Returns: 89 | // - An ITableSession instance if the session is successfully created. 90 | // - An error if there is an issue during session initialization. 91 | func NewTableSession(config *Config, enableRPCCompression bool, connectionTimeoutInMs int) (ITableSession, error) { 92 | config.sqlDialect = TableSqlDialect 93 | session := newSessionWithSpecifiedSqlDialect(config) 94 | 95 | if err := session.Open(enableRPCCompression, connectionTimeoutInMs); err != nil { 96 | return nil, err 97 | } 98 | return &TableSession{session: session}, nil 99 | } 100 | 101 | // NewClusterTableSession creates a new TableSession instance for a cluster setup. 102 | // 103 | // Parameters: 104 | // - clusterConfig: The configuration for the cluster session. 105 | // - enableRPCCompression: A boolean indicating whether RPC compression is enabled. 106 | // 107 | // Returns: 108 | // - An ITableSession instance if the session is successfully created. 109 | // - An error if there is an issue during session initialization. 110 | func NewClusterTableSession(clusterConfig *ClusterConfig, enableRPCCompression bool) (ITableSession, error) { 111 | clusterConfig.sqlDialect = TableSqlDialect 112 | session, err := newClusterSessionWithSqlDialect(clusterConfig) 113 | if err != nil { 114 | return nil, err 115 | } 116 | if err = session.OpenCluster(enableRPCCompression); err != nil { 117 | return nil, err 118 | } 119 | return &TableSession{session: session}, nil 120 | } 121 | 122 | // Insert inserts a Tablet into the IoTDB. 123 | // 124 | // Parameters: 125 | // - tablet: A pointer to a Tablet containing the data to be inserted. 126 | // 127 | // Returns: 128 | // - r: A pointer to TSStatus indicating the execution result. 129 | // - err: An error if the operation fails. 130 | func (s *TableSession) Insert(tablet *Tablet) (r *common.TSStatus, err error) { 131 | return s.session.insertRelationalTablet(tablet) 132 | } 133 | 134 | // ExecuteNonQueryStatement executes a non-query SQL statement, such as a DDL or DML command. 135 | // 136 | // Parameters: 137 | // - sql: The SQL statement to be executed. 138 | // 139 | // Returns: 140 | // - r: A pointer to TSStatus indicating the execution result. 141 | // - err: An error if the operation fails. 142 | func (s *TableSession) ExecuteNonQueryStatement(sql string) (r *common.TSStatus, err error) { 143 | return s.session.ExecuteNonQueryStatement(sql) 144 | } 145 | 146 | // ExecuteQueryStatement executes a query SQL statement and retrieves the result set. 147 | // 148 | // Parameters: 149 | // - sql: The SQL query to be executed. 150 | // - timeoutInMs: (Optional) A pointer to the timeout duration in milliseconds for the query. 151 | // 152 | // Returns: 153 | // - result: A pointer to SessionDataSet containing the query results. 154 | // - err: An error if the operation fails. 155 | func (s *TableSession) ExecuteQueryStatement(sql string, timeoutInMs *int64) (*SessionDataSet, error) { 156 | return s.session.ExecuteQueryStatement(sql, timeoutInMs) 157 | } 158 | 159 | // Close closes the TableSession, releasing any associated resources. 160 | // 161 | // Returns: 162 | // - err: An error if the session fails to close properly. 163 | func (s *TableSession) Close() error { 164 | return s.session.Close() 165 | } 166 | -------------------------------------------------------------------------------- /client/tablesessionpool.go: -------------------------------------------------------------------------------- 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 | package client 21 | 22 | import ( 23 | "github.com/apache/iotdb-client-go/common" 24 | "log" 25 | "sync/atomic" 26 | ) 27 | 28 | // TableSessionPool manages a pool of ITableSession instances, enabling efficient 29 | // reuse and management of resources. It provides methods to acquire a session 30 | // from the pool and to close the pool, releasing all held resources. 31 | // 32 | // This implementation ensures proper lifecycle management of sessions, 33 | // including efficient reuse and cleanup of resources. 34 | type TableSessionPool struct { 35 | sessionPool SessionPool 36 | } 37 | 38 | // NewTableSessionPool creates a new TableSessionPool with the specified configuration. 39 | // 40 | // Parameters: 41 | // - conf: PoolConfig defining the configuration for the pool. 42 | // - maxSize: The maximum number of sessions the pool can hold. 43 | // - connectionTimeoutInMs: Timeout for establishing a connection in milliseconds. 44 | // - waitToGetSessionTimeoutInMs: Timeout for waiting to acquire a session in milliseconds. 45 | // - enableCompression: A boolean indicating whether to enable compression. 46 | // 47 | // Returns: 48 | // - A TableSessionPool instance. 49 | func NewTableSessionPool(conf *PoolConfig, maxSize, connectionTimeoutInMs, waitToGetSessionTimeoutInMs int, 50 | enableCompression bool) TableSessionPool { 51 | return TableSessionPool{sessionPool: newSessionPoolWithSqlDialect(conf, maxSize, connectionTimeoutInMs, waitToGetSessionTimeoutInMs, enableCompression, TableSqlDialect)} 52 | } 53 | 54 | // GetSession acquires an ITableSession instance from the pool. 55 | // 56 | // Returns: 57 | // - A usable ITableSession instance for interacting with IoTDB. 58 | // - An error if a session cannot be acquired. 59 | func (spool *TableSessionPool) GetSession() (ITableSession, error) { 60 | return spool.sessionPool.getTableSession() 61 | } 62 | 63 | // Close closes the TableSessionPool, releasing all held resources. 64 | // Once closed, no further sessions can be acquired from the pool. 65 | func (spool *TableSessionPool) Close() { 66 | spool.sessionPool.Close() 67 | } 68 | 69 | // PooledTableSession represents a session managed within a TableSessionPool. 70 | // It ensures proper cleanup and reusability of the session. 71 | type PooledTableSession struct { 72 | session Session 73 | sessionPool *SessionPool 74 | closed int32 75 | } 76 | 77 | // Insert inserts a Tablet into the database. 78 | // 79 | // Parameters: 80 | // - tablet: A pointer to a Tablet containing time-series data to be inserted. 81 | // 82 | // Returns: 83 | // - r: A pointer to TSStatus indicating the execution result. 84 | // - err: An error if an issue occurs during the operation. 85 | func (s *PooledTableSession) Insert(tablet *Tablet) (r *common.TSStatus, err error) { 86 | r, err = s.session.insertRelationalTablet(tablet) 87 | if err == nil { 88 | return 89 | } 90 | s.sessionPool.dropSession(s.session) 91 | atomic.StoreInt32(&s.closed, 1) 92 | s.session = Session{} 93 | return 94 | } 95 | 96 | // ExecuteNonQueryStatement executes a non-query SQL statement, such as a DDL or DML command. 97 | // 98 | // Parameters: 99 | // - sql: The SQL statement to execute. 100 | // 101 | // Returns: 102 | // - r: A pointer to TSStatus indicating the execution result. 103 | // - err: An error if an issue occurs during the operation. 104 | func (s *PooledTableSession) ExecuteNonQueryStatement(sql string) (r *common.TSStatus, err error) { 105 | r, err = s.session.ExecuteNonQueryStatement(sql) 106 | if err == nil { 107 | return 108 | } 109 | s.sessionPool.dropSession(s.session) 110 | atomic.StoreInt32(&s.closed, 1) 111 | s.session = Session{} 112 | return 113 | } 114 | 115 | // ExecuteQueryStatement executes a query SQL statement and returns the result set. 116 | // 117 | // Parameters: 118 | // - sql: The SQL query statement to execute. 119 | // - timeoutInMs: A pointer to the timeout duration in milliseconds for query execution. 120 | // 121 | // Returns: 122 | // - result: A pointer to SessionDataSet containing the query results. 123 | // - err: An error if an issue occurs during the operation. 124 | func (s *PooledTableSession) ExecuteQueryStatement(sql string, timeoutInMs *int64) (*SessionDataSet, error) { 125 | sessionDataSet, err := s.session.ExecuteQueryStatement(sql, timeoutInMs) 126 | if err == nil { 127 | return sessionDataSet, nil 128 | } 129 | s.sessionPool.dropSession(s.session) 130 | atomic.StoreInt32(&s.closed, 1) 131 | s.session = Session{} 132 | return nil, err 133 | } 134 | 135 | // Close closes the PooledTableSession, releasing it back to the pool. 136 | // 137 | // Returns: 138 | // - err: An error if there is an issue with session closure or cleanup. 139 | func (s *PooledTableSession) Close() error { 140 | if atomic.CompareAndSwapInt32(&s.closed, 0, 1) { 141 | if s.session.config.Database != s.sessionPool.config.Database && s.sessionPool.config.Database != "" { 142 | r, err := s.session.ExecuteNonQueryStatement("use " + s.sessionPool.config.Database) 143 | if r.Code == ExecuteStatementError || err != nil { 144 | log.Println("Failed to change back database by executing: use ", s.sessionPool.config.Database) 145 | s.session.Close() 146 | return nil 147 | } 148 | } 149 | } 150 | s.sessionPool.PutBack(s.session) 151 | s.session = Session{} 152 | return nil 153 | } 154 | -------------------------------------------------------------------------------- /client/tablet.go: -------------------------------------------------------------------------------- 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 | package client 21 | 22 | import ( 23 | "bytes" 24 | "encoding/binary" 25 | "fmt" 26 | "reflect" 27 | "sort" 28 | "time" 29 | ) 30 | 31 | type MeasurementSchema struct { 32 | Measurement string 33 | DataType TSDataType 34 | } 35 | 36 | type ColumnCategory int8 37 | 38 | const ( 39 | TAG = iota 40 | FIELD 41 | ATTRIBUTE 42 | ) 43 | 44 | type Tablet struct { 45 | insertTargetName string 46 | measurementSchemas []*MeasurementSchema 47 | columnCategories []ColumnCategory 48 | timestamps []int64 49 | values []interface{} 50 | bitMaps []*BitMap 51 | maxRowNumber int 52 | RowSize int 53 | } 54 | 55 | func (t *Tablet) Len() int { 56 | return t.RowSize 57 | } 58 | 59 | func (t *Tablet) Swap(i, j int) { 60 | for index, schema := range t.measurementSchemas { 61 | switch schema.DataType { 62 | case BOOLEAN: 63 | sortedSlice := t.values[index].([]bool) 64 | sortedSlice[i], sortedSlice[j] = sortedSlice[j], sortedSlice[i] 65 | case INT32, DATE: 66 | sortedSlice := t.values[index].([]int32) 67 | sortedSlice[i], sortedSlice[j] = sortedSlice[j], sortedSlice[i] 68 | case INT64, TIMESTAMP: 69 | sortedSlice := t.values[index].([]int64) 70 | sortedSlice[i], sortedSlice[j] = sortedSlice[j], sortedSlice[i] 71 | case FLOAT: 72 | sortedSlice := t.values[index].([]float32) 73 | sortedSlice[i], sortedSlice[j] = sortedSlice[j], sortedSlice[i] 74 | case DOUBLE: 75 | sortedSlice := t.values[index].([]float64) 76 | sortedSlice[i], sortedSlice[j] = sortedSlice[j], sortedSlice[i] 77 | case TEXT, BLOB, STRING: 78 | sortedSlice := t.values[index].([][]byte) 79 | sortedSlice[i], sortedSlice[j] = sortedSlice[j], sortedSlice[i] 80 | } 81 | } 82 | if t.bitMaps != nil { 83 | for _, bitMap := range t.bitMaps { 84 | if bitMap != nil { 85 | isNilI := bitMap.IsMarked(i) 86 | isNilJ := bitMap.IsMarked(j) 87 | if isNilI { 88 | bitMap.Mark(j) 89 | } else { 90 | bitMap.UnMark(j) 91 | } 92 | if isNilJ { 93 | bitMap.Mark(i) 94 | } else { 95 | bitMap.UnMark(i) 96 | } 97 | } 98 | } 99 | } 100 | t.timestamps[i], t.timestamps[j] = t.timestamps[j], t.timestamps[i] 101 | } 102 | 103 | func (t *Tablet) Less(i, j int) bool { 104 | return t.timestamps[i] < t.timestamps[j] 105 | } 106 | 107 | func (t *Tablet) SetTimestamp(timestamp int64, rowIndex int) { 108 | t.timestamps[rowIndex] = timestamp 109 | } 110 | 111 | func (t *Tablet) SetValueAt(value interface{}, columnIndex, rowIndex int) error { 112 | 113 | if columnIndex < 0 || columnIndex > len(t.measurementSchemas) { 114 | return fmt.Errorf("illegal argument columnIndex %d", columnIndex) 115 | } 116 | 117 | if rowIndex < 0 || rowIndex > t.maxRowNumber { 118 | return fmt.Errorf("illegal argument rowIndex %d", rowIndex) 119 | } 120 | 121 | if value == nil { 122 | // Init the bitMap to mark nil value 123 | if t.bitMaps == nil { 124 | t.bitMaps = make([]*BitMap, len(t.values)) 125 | } 126 | if t.bitMaps[columnIndex] == nil { 127 | t.bitMaps[columnIndex] = NewBitMap(t.maxRowNumber) 128 | } 129 | // Mark the nil value position 130 | t.bitMaps[columnIndex].Mark(rowIndex) 131 | return nil 132 | } 133 | 134 | switch t.measurementSchemas[columnIndex].DataType { 135 | case BOOLEAN: 136 | values := t.values[columnIndex].([]bool) 137 | switch v := value.(type) { 138 | case bool: 139 | values[rowIndex] = v 140 | case *bool: 141 | values[rowIndex] = *v 142 | default: 143 | return fmt.Errorf("illegal argument value %v %v", value, reflect.TypeOf(value)) 144 | } 145 | case INT32: 146 | values := t.values[columnIndex].([]int32) 147 | switch v := value.(type) { 148 | case int32: 149 | values[rowIndex] = v 150 | case *int32: 151 | values[rowIndex] = *v 152 | default: 153 | return fmt.Errorf("illegal argument value %v %v", value, reflect.TypeOf(value)) 154 | } 155 | case INT64, TIMESTAMP: 156 | values := t.values[columnIndex].([]int64) 157 | switch v := value.(type) { 158 | case int64: 159 | values[rowIndex] = v 160 | case *int64: 161 | values[rowIndex] = *v 162 | default: 163 | return fmt.Errorf("illegal argument value %v %v", value, reflect.TypeOf(value)) 164 | } 165 | case FLOAT: 166 | values := t.values[columnIndex].([]float32) 167 | switch v := value.(type) { 168 | case float32: 169 | values[rowIndex] = v 170 | case *float32: 171 | values[rowIndex] = *v 172 | default: 173 | return fmt.Errorf("illegal argument value %v %v", value, reflect.TypeOf(value)) 174 | } 175 | case DOUBLE: 176 | values := t.values[columnIndex].([]float64) 177 | switch v := value.(type) { 178 | case float64: 179 | values[rowIndex] = v 180 | case *float64: 181 | values[rowIndex] = *v 182 | default: 183 | return fmt.Errorf("illegal argument value %v %v", value, reflect.TypeOf(value)) 184 | } 185 | case TEXT, STRING: 186 | values := t.values[columnIndex].([][]byte) 187 | switch v := value.(type) { 188 | case string: 189 | values[rowIndex] = []byte(v) 190 | case []byte: 191 | values[rowIndex] = v 192 | default: 193 | return fmt.Errorf("illegal argument value %v %v", value, reflect.TypeOf(value)) 194 | } 195 | case BLOB: 196 | values := t.values[columnIndex].([][]byte) 197 | switch v := value.(type) { 198 | case []byte: 199 | values[rowIndex] = v 200 | default: 201 | return fmt.Errorf("illegal argument value %v %v", value, reflect.TypeOf(value)) 202 | } 203 | case DATE: 204 | values := t.values[columnIndex].([]int32) 205 | switch v := value.(type) { 206 | case time.Time: 207 | val, err := DateToInt32(v) 208 | if err != nil { 209 | return err 210 | } 211 | values[rowIndex] = val 212 | default: 213 | return fmt.Errorf("illegal argument value %v %v", value, reflect.TypeOf(value)) 214 | } 215 | } 216 | return nil 217 | } 218 | 219 | func (t *Tablet) GetMaxRowNumber() int { 220 | return t.maxRowNumber 221 | } 222 | 223 | func (t *Tablet) GetValueAt(columnIndex, rowIndex int) (interface{}, error) { 224 | if columnIndex < 0 || columnIndex > len(t.measurementSchemas) { 225 | return nil, fmt.Errorf("illegal argument columnIndex %d", columnIndex) 226 | } 227 | 228 | if rowIndex < 0 || rowIndex > t.maxRowNumber { 229 | return nil, fmt.Errorf("illegal argument rowIndex %d", rowIndex) 230 | } 231 | 232 | schema := t.measurementSchemas[columnIndex] 233 | 234 | if t.bitMaps != nil && t.bitMaps[columnIndex] != nil && t.bitMaps[columnIndex].IsMarked(rowIndex) { 235 | return nil, nil 236 | } 237 | switch schema.DataType { 238 | case BOOLEAN: 239 | return t.values[columnIndex].([]bool)[rowIndex], nil 240 | case INT32: 241 | return t.values[columnIndex].([]int32)[rowIndex], nil 242 | case INT64, TIMESTAMP: 243 | return t.values[columnIndex].([]int64)[rowIndex], nil 244 | case FLOAT: 245 | return t.values[columnIndex].([]float32)[rowIndex], nil 246 | case DOUBLE: 247 | return t.values[columnIndex].([]float64)[rowIndex], nil 248 | case TEXT, STRING: 249 | return string(t.values[columnIndex].([][]byte)[rowIndex]), nil 250 | case BLOB: 251 | return t.values[columnIndex].([][]byte)[rowIndex], nil 252 | case DATE: 253 | return Int32ToDate(t.values[columnIndex].([]int32)[rowIndex]) 254 | default: 255 | return nil, fmt.Errorf("illegal datatype %v", schema.DataType) 256 | } 257 | } 258 | 259 | func (t *Tablet) GetTimestampBytes() []byte { 260 | buff := &bytes.Buffer{} 261 | binary.Write(buff, binary.BigEndian, t.timestamps[0:t.RowSize]) 262 | return buff.Bytes() 263 | } 264 | 265 | func (t *Tablet) GetMeasurements() []string { 266 | measurements := make([]string, len(t.measurementSchemas)) 267 | for i, s := range t.measurementSchemas { 268 | measurements[i] = s.Measurement 269 | } 270 | return measurements 271 | } 272 | 273 | func (t *Tablet) getDataTypes() []int32 { 274 | types := make([]int32, len(t.measurementSchemas)) 275 | for i, s := range t.measurementSchemas { 276 | types[i] = int32(s.DataType) 277 | } 278 | return types 279 | } 280 | 281 | func (t *Tablet) getColumnCategories() []int8 { 282 | columnCategories := make([]int8, len(t.columnCategories)) 283 | for i := range t.columnCategories { 284 | columnCategories[i] = int8(t.columnCategories[i]) 285 | } 286 | return columnCategories 287 | } 288 | 289 | func (t *Tablet) getValuesBytes() ([]byte, error) { 290 | buff := &bytes.Buffer{} 291 | for i, schema := range t.measurementSchemas { 292 | switch schema.DataType { 293 | case BOOLEAN: 294 | binary.Write(buff, binary.BigEndian, t.values[i].([]bool)[0:t.RowSize]) 295 | case INT32, DATE: 296 | binary.Write(buff, binary.BigEndian, t.values[i].([]int32)[0:t.RowSize]) 297 | case INT64, TIMESTAMP: 298 | binary.Write(buff, binary.BigEndian, t.values[i].([]int64)[0:t.RowSize]) 299 | case FLOAT: 300 | binary.Write(buff, binary.BigEndian, t.values[i].([]float32)[0:t.RowSize]) 301 | case DOUBLE: 302 | binary.Write(buff, binary.BigEndian, t.values[i].([]float64)[0:t.RowSize]) 303 | case TEXT, STRING, BLOB: 304 | for _, s := range t.values[i].([][]byte)[0:t.RowSize] { 305 | binary.Write(buff, binary.BigEndian, int32(len(s))) 306 | binary.Write(buff, binary.BigEndian, s) 307 | } 308 | default: 309 | return nil, fmt.Errorf("illegal datatype %v", schema.DataType) 310 | } 311 | } 312 | if t.bitMaps != nil { 313 | for _, bitMap := range t.bitMaps { 314 | columnHasNil := bitMap != nil && !bitMap.IsAllUnmarked() 315 | binary.Write(buff, binary.BigEndian, columnHasNil) 316 | if columnHasNil { 317 | // Need to maintain consistency with the calculation method on the IoTDB side. 318 | binary.Write(buff, binary.BigEndian, bitMap.GetBits()[0:t.RowSize/8+1]) 319 | } 320 | } 321 | } 322 | return buff.Bytes(), nil 323 | } 324 | 325 | func (t *Tablet) Sort() error { 326 | sort.Sort(t) 327 | return nil 328 | } 329 | 330 | func (t *Tablet) Reset() { 331 | t.RowSize = 0 332 | t.bitMaps = nil 333 | } 334 | 335 | func NewTablet(insertTargetName string, measurementSchemas []*MeasurementSchema, maxRowNumber int) (*Tablet, error) { 336 | tablet := &Tablet{ 337 | insertTargetName: insertTargetName, 338 | measurementSchemas: measurementSchemas, 339 | maxRowNumber: maxRowNumber, 340 | } 341 | tablet.timestamps = make([]int64, maxRowNumber) 342 | tablet.values = make([]interface{}, len(measurementSchemas)) 343 | for i, schema := range tablet.measurementSchemas { 344 | switch schema.DataType { 345 | case BOOLEAN: 346 | tablet.values[i] = make([]bool, maxRowNumber) 347 | case INT32, DATE: 348 | tablet.values[i] = make([]int32, maxRowNumber) 349 | case INT64, TIMESTAMP: 350 | tablet.values[i] = make([]int64, maxRowNumber) 351 | case FLOAT: 352 | tablet.values[i] = make([]float32, maxRowNumber) 353 | case DOUBLE: 354 | tablet.values[i] = make([]float64, maxRowNumber) 355 | case TEXT, STRING, BLOB: 356 | tablet.values[i] = make([][]byte, maxRowNumber) 357 | default: 358 | return nil, fmt.Errorf("illegal datatype %v", schema.DataType) 359 | } 360 | } 361 | return tablet, nil 362 | } 363 | 364 | func NewRelationalTablet(tableName string, measurementSchemas []*MeasurementSchema, columnCategories []ColumnCategory, maxRowNumber int) (*Tablet, error) { 365 | tablet, err := NewTablet(tableName, measurementSchemas, maxRowNumber) 366 | if err != nil { 367 | return nil, err 368 | } 369 | tablet.columnCategories = columnCategories 370 | return tablet, nil 371 | } 372 | -------------------------------------------------------------------------------- /client/tablet_test.go: -------------------------------------------------------------------------------- 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 | package client 21 | 22 | import ( 23 | "reflect" 24 | "testing" 25 | "time" 26 | ) 27 | 28 | func createTablet(size int) (*Tablet, error) { 29 | tablet, err := NewTablet("root.ln.TestDevice", []*MeasurementSchema{ 30 | { 31 | Measurement: "restart_count", 32 | DataType: INT32, 33 | }, { 34 | Measurement: "price", 35 | DataType: DOUBLE, 36 | }, { 37 | Measurement: "tick_count", 38 | DataType: INT64, 39 | }, { 40 | Measurement: "temperature", 41 | DataType: FLOAT, 42 | }, { 43 | Measurement: "description", 44 | DataType: TEXT, 45 | }, 46 | { 47 | Measurement: "status", 48 | DataType: BOOLEAN, 49 | }, 50 | { 51 | Measurement: "description_string", 52 | DataType: STRING, 53 | }, 54 | { 55 | Measurement: "description_blob", 56 | DataType: BLOB, 57 | }, 58 | { 59 | Measurement: "date", 60 | DataType: DATE, 61 | }, 62 | { 63 | Measurement: "ts", 64 | DataType: TIMESTAMP, 65 | }, 66 | }, size) 67 | return tablet, err 68 | } 69 | 70 | func TestTablet_getDataTypes(t *testing.T) { 71 | type fields struct { 72 | deviceId string 73 | measurementSchemas []*MeasurementSchema 74 | timestamps []int64 75 | values []interface{} 76 | rowCount int 77 | } 78 | tests := []struct { 79 | name string 80 | fields fields 81 | want []int32 82 | }{ 83 | { 84 | name: "", 85 | fields: fields{ 86 | deviceId: "root.ln.device5", 87 | measurementSchemas: []*MeasurementSchema{ 88 | { 89 | Measurement: "restart_count", 90 | DataType: INT32, 91 | }, { 92 | Measurement: "price", 93 | DataType: DOUBLE, 94 | }, { 95 | Measurement: "tick_count", 96 | DataType: INT64, 97 | }, { 98 | Measurement: "temperature", 99 | DataType: FLOAT, 100 | }, { 101 | Measurement: "description", 102 | DataType: TEXT, 103 | }, { 104 | Measurement: "status", 105 | DataType: BOOLEAN, 106 | }, { 107 | Measurement: "description_string", 108 | DataType: STRING, 109 | }, { 110 | Measurement: "description_blob", 111 | DataType: BLOB, 112 | }, { 113 | Measurement: "date", 114 | DataType: DATE, 115 | }, { 116 | Measurement: "ts", 117 | DataType: TIMESTAMP, 118 | }, 119 | }, 120 | timestamps: []int64{}, 121 | values: []interface{}{}, 122 | rowCount: 0, 123 | }, 124 | want: []int32{int32(INT32), int32(DOUBLE), int32(INT64), int32(FLOAT), int32(TEXT), int32(BOOLEAN), int32(STRING), int32(BLOB), int32(DATE), int32(TIMESTAMP)}, 125 | }, 126 | } 127 | for _, tt := range tests { 128 | t.Run(tt.name, func(t *testing.T) { 129 | tablet := &Tablet{ 130 | insertTargetName: tt.fields.deviceId, 131 | measurementSchemas: tt.fields.measurementSchemas, 132 | timestamps: tt.fields.timestamps, 133 | values: tt.fields.values, 134 | maxRowNumber: tt.fields.rowCount, 135 | } 136 | if got := tablet.getDataTypes(); !reflect.DeepEqual(got, tt.want) { 137 | t.Errorf("Tablet.getDataTypes() = %v, want %v", got, tt.want) 138 | } 139 | }) 140 | } 141 | } 142 | 143 | func TestTablet_SetTimestamp(t *testing.T) { 144 | type args struct { 145 | timestamp int64 146 | rowIndex int 147 | } 148 | tests := []struct { 149 | name string 150 | args args 151 | want int64 152 | }{ 153 | { 154 | name: "", 155 | args: args{ 156 | timestamp: 1608268702769, 157 | rowIndex: 0, 158 | }, 159 | want: 1608268702769, 160 | }, 161 | } 162 | for _, tt := range tests { 163 | t.Run(tt.name, func(t *testing.T) { 164 | tablet, err := createTablet(1) 165 | if err != nil { 166 | t.Error(err) 167 | } 168 | tablet.SetTimestamp(tt.args.timestamp, tt.args.rowIndex) 169 | if got := tablet.timestamps[0]; got != tt.want { 170 | t.Errorf("Tablet.SetTimestamp(int64, int32) = %v, want %v", got, tt.want) 171 | } 172 | }) 173 | } 174 | } 175 | 176 | func TestTablet_SetValueAt(t *testing.T) { 177 | type args struct { 178 | value interface{} 179 | columnIndex int 180 | rowIndex int 181 | } 182 | tests := []struct { 183 | name string 184 | args args 185 | wantErr bool 186 | }{ 187 | { 188 | name: "nil", 189 | args: args{ 190 | value: nil, 191 | columnIndex: 0, 192 | rowIndex: 0, 193 | }, 194 | wantErr: false, 195 | }, { 196 | name: "columnIndex-1", 197 | args: args{ 198 | value: 0, 199 | columnIndex: -1, 200 | rowIndex: 0, 201 | }, 202 | wantErr: true, 203 | }, { 204 | name: "rowIndex-1", 205 | args: args{ 206 | value: 0, 207 | columnIndex: 0, 208 | rowIndex: -1, 209 | }, 210 | wantErr: true, 211 | }, { 212 | name: "rowIndex-65535", 213 | args: args{ 214 | value: 0, 215 | columnIndex: 0, 216 | rowIndex: 65535, 217 | }, 218 | wantErr: true, 219 | }, { 220 | name: "columnIndex-65535", 221 | args: args{ 222 | value: 0, 223 | columnIndex: 65535, 224 | rowIndex: 0, 225 | }, 226 | wantErr: true, 227 | }, { 228 | name: "restart_count", 229 | args: args{ 230 | value: int32(0), 231 | columnIndex: 0, 232 | rowIndex: 0, 233 | }, 234 | wantErr: false, 235 | }, { 236 | name: "price", 237 | args: args{ 238 | value: float64(32.768), 239 | columnIndex: 1, 240 | rowIndex: 0, 241 | }, 242 | wantErr: false, 243 | }, { 244 | name: "tick_count", 245 | args: args{ 246 | value: int64(1608268702780), 247 | columnIndex: 2, 248 | rowIndex: 0, 249 | }, 250 | wantErr: false, 251 | }, { 252 | name: "temperature", 253 | args: args{ 254 | value: float32(36.5), 255 | columnIndex: 3, 256 | rowIndex: 0, 257 | }, 258 | wantErr: false, 259 | }, { 260 | name: "description", 261 | args: args{ 262 | value: "Hello world!", 263 | columnIndex: 4, 264 | rowIndex: 0, 265 | }, 266 | wantErr: false, 267 | }, { 268 | name: "status", 269 | args: args{ 270 | value: true, 271 | columnIndex: 5, 272 | rowIndex: 0, 273 | }, 274 | wantErr: false, 275 | }, { 276 | name: "description_string", 277 | args: args{ 278 | value: "Hello world!", 279 | columnIndex: 6, 280 | rowIndex: 0, 281 | }, 282 | wantErr: false, 283 | }, { 284 | name: "description_blob", 285 | args: args{ 286 | value: []byte("Hello world!"), 287 | columnIndex: 7, 288 | rowIndex: 0, 289 | }, 290 | wantErr: false, 291 | }, { 292 | name: "date", 293 | args: args{ 294 | value: time.Date(2024, time.April, 1, 0, 0, 0, 0, time.UTC), 295 | columnIndex: 8, 296 | rowIndex: 0, 297 | }, 298 | wantErr: false, 299 | }, { 300 | name: "ts", 301 | args: args{ 302 | value: int64(1608268702780), 303 | columnIndex: 9, 304 | rowIndex: 0, 305 | }, 306 | wantErr: false, 307 | }, 308 | } 309 | for _, tt := range tests { 310 | t.Run(tt.name, func(t *testing.T) { 311 | tablet, err := createTablet(1) 312 | if err != nil { 313 | t.Error(err) 314 | } 315 | if err := tablet.SetValueAt(tt.args.value, tt.args.columnIndex, tt.args.rowIndex); (err != nil) != tt.wantErr { 316 | t.Errorf("Tablet.SetValueAt() error = %v, wantErr %v", err, tt.wantErr) 317 | } 318 | }) 319 | } 320 | } 321 | 322 | func TestTablet_GetValueAt(t *testing.T) { 323 | type args struct { 324 | columnIndex int 325 | rowIndex int 326 | } 327 | tests := []struct { 328 | name string 329 | args args 330 | want interface{} 331 | wantErr bool 332 | }{ 333 | { 334 | name: "INT32", 335 | args: args{ 336 | columnIndex: 0, 337 | rowIndex: 0, 338 | }, 339 | want: int32(256), 340 | wantErr: false, 341 | }, { 342 | name: "FLOAT64", 343 | args: args{ 344 | columnIndex: 1, 345 | rowIndex: 0, 346 | }, 347 | want: float64(32.768), 348 | wantErr: false, 349 | }, { 350 | name: "INT64", 351 | args: args{ 352 | columnIndex: 2, 353 | rowIndex: 0, 354 | }, 355 | want: int64(65535), 356 | wantErr: false, 357 | }, { 358 | name: "FLOAT32", 359 | args: args{ 360 | columnIndex: 3, 361 | rowIndex: 0, 362 | }, 363 | want: float32(36.5), 364 | wantErr: false, 365 | }, { 366 | name: "TEXT", 367 | args: args{ 368 | columnIndex: 4, 369 | rowIndex: 0, 370 | }, 371 | want: "Hello World!", 372 | wantErr: false, 373 | }, { 374 | name: "BOOLEAN", 375 | args: args{ 376 | columnIndex: 5, 377 | rowIndex: 0, 378 | }, 379 | want: true, 380 | wantErr: false, 381 | }, { 382 | name: "TEXT", 383 | args: args{ 384 | columnIndex: 6, 385 | rowIndex: 0, 386 | }, 387 | want: "Hello World!", 388 | wantErr: false, 389 | }, { 390 | name: "BLOB", 391 | args: args{ 392 | columnIndex: 7, 393 | rowIndex: 0, 394 | }, 395 | want: []byte("Hello World!"), 396 | wantErr: false, 397 | }, { 398 | name: "DATE", 399 | args: args{ 400 | columnIndex: 8, 401 | rowIndex: 0, 402 | }, 403 | want: time.Date(2024, time.April, 1, 0, 0, 0, 0, time.UTC), 404 | wantErr: false, 405 | }, { 406 | name: "TIMESTAMP", 407 | args: args{ 408 | columnIndex: 9, 409 | rowIndex: 0, 410 | }, 411 | want: int64(1608268702780), 412 | wantErr: false, 413 | }, 414 | } 415 | if tablet, err := createTablet(1); err == nil { 416 | tablet.SetValueAt(int32(256), 0, 0) 417 | tablet.SetValueAt(32.768, 1, 0) 418 | tablet.SetValueAt(int64(65535), 2, 0) 419 | tablet.SetValueAt(float32(36.5), 3, 0) 420 | tablet.SetValueAt("Hello World!", 4, 0) 421 | tablet.SetValueAt(true, 5, 0) 422 | tablet.SetValueAt("Hello World!", 6, 0) 423 | tablet.SetValueAt([]byte("Hello World!"), 7, 0) 424 | tablet.SetValueAt(time.Date(2024, time.April, 1, 0, 0, 0, 0, time.UTC), 8, 0) 425 | tablet.SetValueAt(int64(1608268702780), 9, 0) 426 | for _, tt := range tests { 427 | t.Run(tt.name, func(t *testing.T) { 428 | got, err := tablet.GetValueAt(tt.args.columnIndex, tt.args.rowIndex) 429 | if (err != nil) != tt.wantErr { 430 | t.Errorf("Tablet.GetValueAt() error = %v, wantErr %v", err, tt.wantErr) 431 | return 432 | } 433 | if !reflect.DeepEqual(got, tt.want) { 434 | t.Errorf("Tablet.GetValueAt() = %v, want %v", got, tt.want) 435 | } 436 | }) 437 | } 438 | } 439 | } 440 | 441 | func TestTablet_GetNilValueAt(t *testing.T) { 442 | type args struct { 443 | columnIndex int 444 | rowIndex int 445 | } 446 | tests := []struct { 447 | name string 448 | args args 449 | want interface{} 450 | wantErr bool 451 | }{ 452 | { 453 | name: "INT32", 454 | args: args{ 455 | columnIndex: 0, 456 | rowIndex: 0, 457 | }, 458 | want: int32(256), 459 | wantErr: false, 460 | }, { 461 | name: "FLOAT64", 462 | args: args{ 463 | columnIndex: 1, 464 | rowIndex: 0, 465 | }, 466 | want: nil, 467 | wantErr: false, 468 | }, { 469 | name: "INT64", 470 | args: args{ 471 | columnIndex: 2, 472 | rowIndex: 0, 473 | }, 474 | want: int64(65535), 475 | wantErr: false, 476 | }, { 477 | name: "FLOAT32", 478 | args: args{ 479 | columnIndex: 3, 480 | rowIndex: 0, 481 | }, 482 | want: float32(36.5), 483 | wantErr: false, 484 | }, { 485 | name: "STRING", 486 | args: args{ 487 | columnIndex: 4, 488 | rowIndex: 0, 489 | }, 490 | want: "Hello World!", 491 | wantErr: false, 492 | }, { 493 | name: "BOOLEAN", 494 | args: args{ 495 | columnIndex: 5, 496 | rowIndex: 0, 497 | }, 498 | want: true, 499 | wantErr: false, 500 | }, { 501 | name: "STRING", 502 | args: args{ 503 | columnIndex: 6, 504 | rowIndex: 0, 505 | }, 506 | want: nil, 507 | wantErr: false, 508 | }, { 509 | name: "BLOB", 510 | args: args{ 511 | columnIndex: 7, 512 | rowIndex: 0, 513 | }, 514 | want: nil, 515 | wantErr: false, 516 | }, { 517 | name: "DATE", 518 | args: args{ 519 | columnIndex: 8, 520 | rowIndex: 0, 521 | }, 522 | want: nil, 523 | wantErr: false, 524 | }, { 525 | name: "TIMESTAMP", 526 | args: args{ 527 | columnIndex: 9, 528 | rowIndex: 0, 529 | }, 530 | want: nil, 531 | wantErr: false, 532 | }, 533 | } 534 | if tablet, err := createTablet(1); err == nil { 535 | tablet.SetValueAt(int32(256), 0, 0) 536 | tablet.SetValueAt(nil, 1, 0) 537 | tablet.SetValueAt(int64(65535), 2, 0) 538 | tablet.SetValueAt(float32(36.5), 3, 0) 539 | tablet.SetValueAt("Hello World!", 4, 0) 540 | tablet.SetValueAt(true, 5, 0) 541 | tablet.SetValueAt(nil, 6, 0) 542 | tablet.SetValueAt(nil, 7, 0) 543 | tablet.SetValueAt(nil, 8, 0) 544 | tablet.SetValueAt(nil, 9, 0) 545 | for _, tt := range tests { 546 | t.Run(tt.name, func(t *testing.T) { 547 | got, err := tablet.GetValueAt(tt.args.columnIndex, tt.args.rowIndex) 548 | if (err != nil) != tt.wantErr { 549 | t.Errorf("Tablet.GetValueAt() error = %v, wantErr %v", err, tt.wantErr) 550 | return 551 | } 552 | if !reflect.DeepEqual(got, tt.want) { 553 | t.Errorf("Tablet.GetValueAt() = %v, want %v", got, tt.want) 554 | } 555 | }) 556 | } 557 | } 558 | } 559 | 560 | func TestTablet_Sort(t *testing.T) { 561 | 562 | tests := []struct { 563 | name string 564 | want [][]interface{} 565 | wantErr bool 566 | }{ 567 | { 568 | name: "item-1", 569 | want: [][]interface{}{ 570 | {int32(3), float64(3.0), int64(3), float32(3.0), "3", true}, 571 | {int32(4), float64(4.0), int64(4), float32(4.0), "4", true}, 572 | {int32(1), float64(1.0), int64(1), float32(1.0), "1", true}, 573 | {int32(2), float64(2.0), int64(2), float32(2.0), "2", true}, 574 | }, 575 | wantErr: false, 576 | }, 577 | } 578 | for _, tt := range tests { 579 | t.Run(tt.name, func(t *testing.T) { 580 | tablet, _ := createTablet(4) 581 | tablet.SetValueAt(int32(1), 0, 0) 582 | tablet.SetValueAt(float64(1.0), 1, 0) 583 | tablet.SetValueAt(int64(1), 2, 0) 584 | tablet.SetValueAt(float32(1.0), 3, 0) 585 | tablet.SetValueAt("1", 4, 0) 586 | tablet.SetValueAt(true, 5, 0) 587 | tablet.SetTimestamp(3, 0) 588 | tablet.RowSize++ 589 | 590 | tablet.SetValueAt(int32(2), 0, 1) 591 | tablet.SetValueAt(float64(2.0), 1, 1) 592 | tablet.SetValueAt(int64(2), 2, 1) 593 | tablet.SetValueAt(float32(2.0), 3, 1) 594 | tablet.SetValueAt("2", 4, 1) 595 | tablet.SetValueAt(true, 5, 1) 596 | tablet.SetTimestamp(4, 1) 597 | tablet.RowSize++ 598 | 599 | tablet.SetValueAt(int32(3), 0, 2) 600 | tablet.SetValueAt(float64(3.0), 1, 2) 601 | tablet.SetValueAt(int64(3), 2, 2) 602 | tablet.SetValueAt(float32(3.0), 3, 2) 603 | tablet.SetValueAt("3", 4, 2) 604 | tablet.SetValueAt(true, 5, 2) 605 | tablet.SetTimestamp(1, 2) 606 | tablet.RowSize++ 607 | 608 | tablet.SetValueAt(int32(4), 0, 3) 609 | tablet.SetValueAt(float64(4.0), 1, 3) 610 | tablet.SetValueAt(int64(4), 2, 3) 611 | tablet.SetValueAt(float32(4.0), 3, 3) 612 | tablet.SetValueAt("4", 4, 3) 613 | tablet.SetValueAt(true, 5, 3) 614 | tablet.SetTimestamp(2, 3) 615 | tablet.RowSize++ 616 | 617 | if err := tablet.Sort(); (err != nil) != tt.wantErr { 618 | t.Errorf("Tablet.Sort() error = %v, wantErr %v", err, tt.wantErr) 619 | } 620 | for rowIndex, row := range tt.want { 621 | for columnIndex, wantValue := range row { 622 | value, _ := tablet.GetValueAt(columnIndex, rowIndex) 623 | if !reflect.DeepEqual(value, wantValue) { 624 | t.Errorf("Tablet.Sort() colum: %d, row: %d, value: %v != %v", columnIndex, rowIndex, value, wantValue) 625 | } 626 | } 627 | } 628 | }) 629 | } 630 | } 631 | -------------------------------------------------------------------------------- /client/tsblock.go: -------------------------------------------------------------------------------- 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 | package client 21 | 22 | import ( 23 | "bytes" 24 | "encoding/binary" 25 | "fmt" 26 | ) 27 | 28 | type TsBlock struct { 29 | timeColumn Column 30 | valueColumns []Column 31 | positionCount int32 32 | } 33 | 34 | func NewTsBlock(positionCount int32, timeColumn Column, valueColumns ...Column) (*TsBlock, error) { 35 | if valueColumns == nil { 36 | return nil, fmt.Errorf("blocks is null") 37 | } 38 | return &TsBlock{ 39 | timeColumn: timeColumn, 40 | valueColumns: valueColumns, 41 | positionCount: positionCount, 42 | }, nil 43 | } 44 | 45 | func DeserializeTsBlock(data []byte) (*TsBlock, error) { 46 | // Serialized tsblock: 47 | // +-------------+---------------+---------+------------+-----------+----------+ 48 | // | val col cnt | val col types | pos cnt | encodings | time col | val col | 49 | // +-------------+---------------+---------+------------+-----------+----------+ 50 | // | int32 | list[byte] | int32 | list[byte] | bytes | bytes | 51 | // +-------------+---------------+---------+------------+-----------+----------+ 52 | 53 | reader := bytes.NewReader(data) 54 | // value column count 55 | var valueColumnCount int32 56 | if err := binary.Read(reader, binary.BigEndian, &valueColumnCount); err != nil { 57 | return nil, err 58 | } 59 | 60 | // value column data types 61 | valueColumnDataTypes := make([]TSDataType, valueColumnCount) 62 | for i := int32(0); i < valueColumnCount; i++ { 63 | dataType, err := deserializeDataType(reader) 64 | if err != nil { 65 | return nil, err 66 | } 67 | valueColumnDataTypes[i] = dataType 68 | } 69 | 70 | // position count 71 | var positionCount int32 72 | if err := binary.Read(reader, binary.BigEndian, &positionCount); err != nil { 73 | return nil, err 74 | } 75 | 76 | // column encodings 77 | columnEncodings := make([]ColumnEncoding, valueColumnCount+1) 78 | for i := int32(0); i < valueColumnCount+1; i++ { 79 | columnEncoding, err := deserializeColumnEncoding(reader) 80 | if err != nil { 81 | return nil, err 82 | } 83 | columnEncodings[i] = columnEncoding 84 | } 85 | 86 | // time column 87 | timeColumnDecoder, err := getColumnDecoder(columnEncodings[0]) 88 | if err != nil { 89 | return nil, err 90 | } 91 | timeColumn, err := timeColumnDecoder.ReadColumn(reader, INT64, positionCount) 92 | if err != nil { 93 | return nil, err 94 | } 95 | 96 | // value columns 97 | valueColumns := make([]Column, valueColumnCount) 98 | for i := int32(0); i < valueColumnCount; i++ { 99 | valueColumnDecoder, err := getColumnDecoder(columnEncodings[i+1]) 100 | if err != nil { 101 | return nil, err 102 | } 103 | valueColumn, err := valueColumnDecoder.ReadColumn(reader, valueColumnDataTypes[i], positionCount) 104 | if err != nil { 105 | return nil, err 106 | } 107 | valueColumns[i] = valueColumn 108 | } 109 | return NewTsBlock(positionCount, timeColumn, valueColumns...) 110 | } 111 | 112 | func deserializeDataType(reader *bytes.Reader) (TSDataType, error) { 113 | b, err := reader.ReadByte() 114 | if err != nil { 115 | return UNKNOWN, err 116 | } 117 | return getDataTypeByByte(b) 118 | } 119 | 120 | func deserializeColumnEncoding(reader *bytes.Reader) (ColumnEncoding, error) { 121 | b, err := reader.ReadByte() 122 | if err != nil { 123 | return RLE_COLUMN_ENCODING, err 124 | } 125 | return getColumnEncodingByByte(b) 126 | } 127 | 128 | func (t *TsBlock) GetPositionCount() int32 { 129 | return t.positionCount 130 | } 131 | 132 | func (t *TsBlock) GetStartTime() (int64, error) { 133 | return t.timeColumn.GetLong(0) 134 | } 135 | 136 | func (t *TsBlock) GetEndTime() (int64, error) { 137 | return t.timeColumn.GetLong(t.positionCount - 1) 138 | } 139 | 140 | func (t *TsBlock) IsEmpty() bool { 141 | return t.positionCount == 0 142 | } 143 | 144 | func (t *TsBlock) GetTimeByIndex(index int32) (int64, error) { 145 | return t.timeColumn.GetLong(index) 146 | } 147 | 148 | func (t *TsBlock) GetValueColumnCount() int32 { 149 | return int32(len(t.valueColumns)) 150 | } 151 | 152 | func (t *TsBlock) GetTimeColumn() Column { 153 | return t.timeColumn 154 | } 155 | 156 | func (t *TsBlock) GetValueColumns() *[]Column { 157 | return &t.valueColumns 158 | } 159 | 160 | func (t *TsBlock) GetColumn(columnIndex int32) Column { 161 | return t.valueColumns[columnIndex] 162 | } 163 | -------------------------------------------------------------------------------- /client/utils.go: -------------------------------------------------------------------------------- 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 | package client 21 | 22 | import ( 23 | "bytes" 24 | "encoding/binary" 25 | "errors" 26 | "fmt" 27 | "github.com/apache/iotdb-client-go/common" 28 | "github.com/apache/iotdb-client-go/rpc" 29 | "strconv" 30 | "time" 31 | ) 32 | 33 | const ( 34 | DEFAULT_TIME_FORMAT = "default" 35 | TIME_PRECISION = "timestamp_precision" 36 | MILLISECOND = "ms" 37 | MICROSECOND = "us" 38 | NANOSECOND = "ns" 39 | ) 40 | 41 | func getTimeFactor(openResp *rpc.TSOpenSessionResp) (int32, error) { 42 | if !openResp.IsSetConfiguration() { 43 | return 1_000, nil 44 | } 45 | precision, exists := openResp.GetConfiguration()[TIME_PRECISION] 46 | if !exists { 47 | return 1_000, nil 48 | } 49 | switch precision { 50 | case MILLISECOND: 51 | return 1_000, nil 52 | case MICROSECOND: 53 | return 1_000_000, nil 54 | case NANOSECOND: 55 | return 1_000_000_000, nil 56 | default: 57 | return 0, fmt.Errorf("unknown time precision: %v", precision) 58 | } 59 | } 60 | 61 | func getTimePrecision(timeFactor int32) (string, error) { 62 | switch timeFactor { 63 | case 1_000: 64 | return MILLISECOND, nil 65 | case 1_000_000: 66 | return MICROSECOND, nil 67 | case 1_000_000_000: 68 | return NANOSECOND, nil 69 | default: 70 | return "", fmt.Errorf("unknown time factor: %v", timeFactor) 71 | } 72 | } 73 | 74 | func formatDatetime(timeFormat, timePrecision string, timestamp int64, zone *time.Location) string { 75 | switch timeFormat { 76 | case "long", "number": 77 | return strconv.FormatInt(timestamp, 10) 78 | case "default", "iso8601": 79 | return parseLongToDateWithPrecision(timestamp, zone, timePrecision) 80 | default: 81 | sec := timestamp / 1000 82 | nsec := (timestamp % 1000) * int64(time.Millisecond) 83 | t := time.Unix(sec, nsec).In(zone) 84 | return t.Format(timeFormat) 85 | } 86 | } 87 | 88 | func getMilliSecond(timeValue int64, timeFactor int32) int64 { 89 | return (timeValue / int64(timeFactor)) * 1_000 90 | } 91 | 92 | func getNanoSecond(timeValue int64, timeFactor int32) int { 93 | return int((timeValue % int64(timeFactor)) * (1_000_000_000 / int64(timeFactor))) 94 | } 95 | 96 | func convertToTimestamp(timeValue int64, timeFactor int32) time.Time { 97 | millis := getMilliSecond(timeValue, timeFactor) 98 | nanos := getNanoSecond(timeValue, timeFactor) 99 | return time.Unix(millis/1e3, (millis%1e3)*1e6).Add(time.Duration(nanos) * time.Nanosecond) 100 | } 101 | 102 | func parseLongToDateWithPrecision(timestamp int64, zone *time.Location, precision string) string { 103 | var divisor int64 104 | var digits int 105 | 106 | switch precision { 107 | case MILLISECOND: 108 | divisor = 1000 109 | digits = 3 110 | case MICROSECOND: 111 | divisor = 1_000_000 112 | digits = 6 113 | case NANOSECOND: 114 | divisor = 1_000_000_000 115 | digits = 9 116 | default: 117 | return "" 118 | } 119 | 120 | quotient := timestamp / divisor 121 | remainder := timestamp % divisor 122 | 123 | if timestamp < 0 && remainder != 0 { 124 | quotient-- 125 | remainder += divisor 126 | } 127 | 128 | t := time.Unix(quotient, 0).In(zone) 129 | year, month, day := t.Date() 130 | hour, min, sec := t.Clock() 131 | 132 | _, offset := t.Zone() 133 | offsetSeconds := offset 134 | sign := "+" 135 | if offsetSeconds < 0 { 136 | sign = "-" 137 | offsetSeconds = -offsetSeconds 138 | } 139 | hours := offsetSeconds / 3600 140 | minutes := (offsetSeconds % 3600) / 60 141 | 142 | zoneOffset := fmt.Sprintf("%s%02d:%02d", sign, hours, minutes) 143 | if offset == 0 { 144 | zoneOffset = "Z" 145 | } 146 | 147 | formatStr := fmt.Sprintf("%%0%dd", digits) 148 | fraction := fmt.Sprintf(formatStr, remainder) 149 | 150 | isoStr := fmt.Sprintf("%04d-%02d-%02dT%02d:%02d:%02d.%s%s", 151 | year, month, day, hour, min, sec, fraction, zoneOffset) 152 | 153 | return isoStr 154 | } 155 | 156 | func int32ToString(n int32) string { 157 | return strconv.Itoa(int(n)) 158 | } 159 | 160 | func int64ToString(n int64) string { 161 | return strconv.FormatInt(n, 10) 162 | } 163 | 164 | func float32ToString(val float32) string { 165 | return strconv.FormatFloat(float64(val), 'f', -1, 32) 166 | } 167 | 168 | func float64ToString(val float64) string { 169 | return strconv.FormatFloat(val, 'f', -1, 64) 170 | } 171 | 172 | func int32ToBytes(n int32) []byte { 173 | bytesBuffer := bytes.NewBuffer([]byte{}) 174 | binary.Write(bytesBuffer, binary.BigEndian, n) 175 | return bytesBuffer.Bytes() 176 | } 177 | 178 | func int64ToBytes(n int64) []byte { 179 | bytesBuffer := bytes.NewBuffer([]byte{}) 180 | binary.Write(bytesBuffer, binary.BigEndian, n) 181 | return bytesBuffer.Bytes() 182 | } 183 | 184 | func bytesToInt32(bys []byte) int32 { 185 | bytesBuffer := bytes.NewBuffer(bys) 186 | var data int32 187 | binary.Read(bytesBuffer, binary.BigEndian, &data) 188 | return data 189 | } 190 | 191 | func bytesToInt64(bys []byte) int64 { 192 | bytesBuffer := bytes.NewBuffer(bys) 193 | var data int64 194 | binary.Read(bytesBuffer, binary.BigEndian, &data) 195 | return data 196 | } 197 | 198 | func bytesToHexString(input []byte) string { 199 | hexString := "0x" 200 | if input != nil { 201 | for _, b := range input { 202 | hexString += fmt.Sprintf("%02x", b) 203 | } 204 | } 205 | return hexString 206 | } 207 | 208 | func DateToInt32(localDate time.Time) (int32, error) { 209 | if localDate.IsZero() { 210 | return 0, errors.New("date expression is null or empty") 211 | } 212 | 213 | year := localDate.Year() 214 | if year < 1000 || year > 9999 { 215 | return 0, errors.New("year must be between 1000 and 9999") 216 | } 217 | 218 | // Convert to YYYY/MM/DD format 219 | result := year*10000 + int(localDate.Month())*100 + localDate.Day() 220 | return int32(result), nil 221 | } 222 | 223 | func Int32ToDate(val int32) (time.Time, error) { 224 | date := int(val) 225 | year := date / 10000 226 | month := (date / 100) % 100 227 | day := date % 100 228 | 229 | localDate := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC) 230 | 231 | if localDate.Year() != year || int(localDate.Month()) != month || localDate.Day() != day { 232 | return time.Time{}, errors.New("invalid date format") 233 | } 234 | 235 | return localDate, nil 236 | } 237 | 238 | func bytesToDate(bys []byte) (time.Time, error) { 239 | return Int32ToDate(bytesToInt32(bys)) 240 | } 241 | 242 | func verifySuccesses(statuses []*common.TSStatus) error { 243 | buff := bytes.Buffer{} 244 | for _, status := range statuses { 245 | if status.Code != SuccessStatus && status.Code != RedirectionRecommend { 246 | buff.WriteString(*status.Message + ";") 247 | } 248 | } 249 | errMsg := buff.String() 250 | if len(errMsg) > 0 { 251 | return NewBatchError(statuses) 252 | } 253 | return nil 254 | } 255 | 256 | func VerifySuccess(status *common.TSStatus) error { 257 | if status.Code == RedirectionRecommend { 258 | return nil 259 | } 260 | 261 | if status.Code == MultipleError { 262 | if err := verifySuccesses(status.GetSubStatus()); err != nil { 263 | return err 264 | } 265 | return nil 266 | } 267 | if status.Code != SuccessStatus { 268 | if status.Message != nil { 269 | return fmt.Errorf("error code: %d, message: %v", status.Code, *status.Message) 270 | } else { 271 | return fmt.Errorf("error code: %d", status.Code) 272 | } 273 | } 274 | return nil 275 | } 276 | 277 | type Binary struct { 278 | values []byte 279 | } 280 | 281 | func NewBinary(v []byte) *Binary { 282 | return &Binary{v} 283 | } 284 | 285 | func (b *Binary) GetStringValue() string { 286 | return string(b.values) 287 | } 288 | 289 | func (b *Binary) GetValues() []byte { 290 | return b.values 291 | } 292 | -------------------------------------------------------------------------------- /client/utils_test.go: -------------------------------------------------------------------------------- 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 | package client 21 | 22 | import ( 23 | "github.com/apache/iotdb-client-go/common" 24 | "testing" 25 | ) 26 | 27 | func Test_bytesToInt32(t *testing.T) { 28 | type args struct { 29 | bys []byte 30 | } 31 | tests := []struct { 32 | name string 33 | args args 34 | want int32 35 | }{ 36 | { 37 | name: "", 38 | args: args{ 39 | bys: int32ToBytes(65535), 40 | }, 41 | want: 65535, 42 | }, 43 | } 44 | for _, tt := range tests { 45 | t.Run(tt.name, func(t *testing.T) { 46 | if got := bytesToInt32(tt.args.bys); got != tt.want { 47 | t.Errorf("bytesToInt32() = %v, want %v", got, tt.want) 48 | } 49 | }) 50 | } 51 | } 52 | 53 | func Test_bytesToInt64(t *testing.T) { 54 | type args struct { 55 | bys []byte 56 | } 57 | tests := []struct { 58 | name string 59 | args args 60 | want int64 61 | }{ 62 | { 63 | name: "", 64 | args: args{ 65 | bys: int64ToBytes(1607237683018), 66 | }, 67 | want: 1607237683018, 68 | }, 69 | } 70 | for _, tt := range tests { 71 | t.Run(tt.name, func(t *testing.T) { 72 | if got := bytesToInt64(tt.args.bys); got != tt.want { 73 | t.Errorf("bytesToInt64() = %v, want %v", got, tt.want) 74 | } 75 | }) 76 | } 77 | } 78 | 79 | func Test_int32ToString(t *testing.T) { 80 | type args struct { 81 | n int32 82 | } 83 | tests := []struct { 84 | name string 85 | args args 86 | want string 87 | }{ 88 | { 89 | name: "Test Int32", 90 | args: args{ 91 | n: 65535, 92 | }, 93 | want: "65535", 94 | }, 95 | } 96 | for _, tt := range tests { 97 | t.Run(tt.name, func(t *testing.T) { 98 | if got := int32ToString(tt.args.n); got != tt.want { 99 | t.Errorf("int32ToString() = %v, want %v", got, tt.want) 100 | } 101 | }) 102 | } 103 | } 104 | 105 | func Test_int64ToString(t *testing.T) { 106 | type args struct { 107 | n int64 108 | } 109 | tests := []struct { 110 | name string 111 | args args 112 | want string 113 | }{ 114 | { 115 | name: "Test Int64", 116 | args: args{ 117 | n: 7684873721715404507, 118 | }, 119 | want: "7684873721715404507", 120 | }, 121 | } 122 | for _, tt := range tests { 123 | t.Run(tt.name, func(t *testing.T) { 124 | if got := int64ToString(tt.args.n); got != tt.want { 125 | t.Errorf("int64ToString() = %v, want %v", got, tt.want) 126 | } 127 | }) 128 | } 129 | } 130 | 131 | func Test_float32ToString(t *testing.T) { 132 | type args struct { 133 | val float32 134 | } 135 | tests := []struct { 136 | name string 137 | args args 138 | want string 139 | }{ 140 | { 141 | name: "Test Float32", 142 | args: args{ 143 | val: 0.97800666, 144 | }, 145 | want: "0.97800666", 146 | }, 147 | } 148 | for _, tt := range tests { 149 | t.Run(tt.name, func(t *testing.T) { 150 | if got := float32ToString(tt.args.val); got != tt.want { 151 | t.Errorf("float32ToString() = %v, want %v", got, tt.want) 152 | } 153 | }) 154 | } 155 | } 156 | 157 | func Test_float64ToString(t *testing.T) { 158 | type args struct { 159 | val float64 160 | } 161 | tests := []struct { 162 | name string 163 | args args 164 | want string 165 | }{ 166 | { 167 | name: "Test Float64", 168 | args: args{ 169 | val: 0.39751212862981283, 170 | }, 171 | want: "0.39751212862981283", 172 | }, 173 | } 174 | for _, tt := range tests { 175 | t.Run(tt.name, func(t *testing.T) { 176 | if got := float64ToString(tt.args.val); got != tt.want { 177 | t.Errorf("float64ToString() = %v, want %v", got, tt.want) 178 | } 179 | }) 180 | } 181 | } 182 | 183 | func Test_bytesToHexString(t *testing.T) { 184 | type args struct { 185 | val []byte 186 | } 187 | tests := []struct { 188 | name string 189 | args args 190 | want string 191 | }{ 192 | { 193 | name: "Test bytes", 194 | args: args{ 195 | val: []byte("bytes"), 196 | }, 197 | want: "0x6279746573", 198 | }, 199 | } 200 | for _, tt := range tests { 201 | t.Run(tt.name, func(t *testing.T) { 202 | if got := bytesToHexString(tt.args.val); got != tt.want { 203 | t.Errorf("bytesToHexString() = %v, want %v", got, tt.want) 204 | } 205 | }) 206 | } 207 | } 208 | 209 | func Test_verifySuccess(t *testing.T) { 210 | type args struct { 211 | status *common.TSStatus 212 | } 213 | var errMsg = "error occurred" 214 | tests := []struct { 215 | name string 216 | args args 217 | wantErr bool 218 | }{ 219 | { 220 | name: "RedirectionRecommend", 221 | args: args{ 222 | status: &common.TSStatus{ 223 | Code: RedirectionRecommend, 224 | Message: &errMsg, 225 | SubStatus: []*common.TSStatus{}, 226 | }, 227 | }, 228 | wantErr: false, 229 | }, { 230 | name: "SuccessStatus", 231 | args: args{ 232 | status: &common.TSStatus{ 233 | Code: SuccessStatus, 234 | Message: &errMsg, 235 | SubStatus: []*common.TSStatus{}, 236 | }, 237 | }, 238 | wantErr: false, 239 | }, { 240 | name: "MultipleError", 241 | args: args{ 242 | status: &common.TSStatus{ 243 | Code: MultipleError, 244 | Message: &errMsg, 245 | SubStatus: []*common.TSStatus{ 246 | { 247 | Code: ShutDownError, 248 | Message: &errMsg, 249 | }, 250 | }, 251 | }, 252 | }, 253 | wantErr: true, 254 | }, { 255 | name: "CloseOperationError", 256 | args: args{ 257 | status: &common.TSStatus{ 258 | Code: CloseOperationError, 259 | Message: &errMsg, 260 | SubStatus: []*common.TSStatus{}, 261 | }, 262 | }, 263 | wantErr: true, 264 | }, 265 | } 266 | for _, tt := range tests { 267 | t.Run(tt.name, func(t *testing.T) { 268 | if err := VerifySuccess(tt.args.status); (err != nil) != tt.wantErr { 269 | t.Errorf("VerifySuccess() error = %v, wantErr %v", err, tt.wantErr) 270 | } 271 | }) 272 | } 273 | } 274 | 275 | func Test_verifySuccesses(t *testing.T) { 276 | type args struct { 277 | statuses []*common.TSStatus 278 | } 279 | var internalServerError = "InternalServerError" 280 | var success = "Success" 281 | var redirectionRecommend = "RedirectionRecommend" 282 | tests := []struct { 283 | name string 284 | args args 285 | wantErr bool 286 | }{ 287 | { 288 | name: "InternalServerError", 289 | args: args{ 290 | statuses: []*common.TSStatus{ 291 | { 292 | Code: InternalServerError, 293 | Message: &internalServerError, 294 | SubStatus: []*common.TSStatus{}, 295 | }, 296 | }, 297 | }, 298 | wantErr: true, 299 | }, { 300 | name: "SuccessStatus", 301 | args: args{ 302 | statuses: []*common.TSStatus{ 303 | { 304 | Code: SuccessStatus, 305 | Message: &success, 306 | SubStatus: []*common.TSStatus{}, 307 | }, 308 | }, 309 | }, 310 | wantErr: false, 311 | }, 312 | { 313 | name: "RedirectionRecommend", 314 | args: args{ 315 | statuses: []*common.TSStatus{ 316 | { 317 | Code: RedirectionRecommend, 318 | Message: &redirectionRecommend, 319 | SubStatus: []*common.TSStatus{}, 320 | }, 321 | }, 322 | }, 323 | wantErr: false, 324 | }, 325 | } 326 | for _, tt := range tests { 327 | t.Run(tt.name, func(t *testing.T) { 328 | if err := verifySuccesses(tt.args.statuses); (err != nil) != tt.wantErr { 329 | t.Errorf("verifySuccesses() error = %v, wantErr %v", err, tt.wantErr) 330 | } 331 | }) 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /common/GoUnusedProtection__.go: -------------------------------------------------------------------------------- 1 | // Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. 2 | 3 | package common 4 | 5 | var GoUnusedProtection__ int; 6 | 7 | -------------------------------------------------------------------------------- /common/common-consts.go: -------------------------------------------------------------------------------- 1 | // Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. 2 | 3 | package common 4 | 5 | import( 6 | "bytes" 7 | "context" 8 | "fmt" 9 | "time" 10 | "github.com/apache/thrift/lib/go/thrift" 11 | ) 12 | 13 | // (needed to ensure safety because of naive import list construction.) 14 | var _ = thrift.ZERO 15 | var _ = fmt.Printf 16 | var _ = context.Background 17 | var _ = time.Now 18 | var _ = bytes.Equal 19 | 20 | 21 | func init() { 22 | } 23 | 24 | -------------------------------------------------------------------------------- /example/session_example.go: -------------------------------------------------------------------------------- 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 | package main 21 | 22 | import ( 23 | "flag" 24 | "fmt" 25 | "github.com/apache/iotdb-client-go/common" 26 | "log" 27 | "math/rand" 28 | "strings" 29 | "time" 30 | 31 | "github.com/apache/iotdb-client-go/client" 32 | ) 33 | 34 | var ( 35 | host string 36 | port string 37 | user string 38 | password string 39 | ) 40 | var session client.Session 41 | 42 | func main() { 43 | flag.StringVar(&host, "host", "127.0.0.1", "--host=192.168.1.100") 44 | flag.StringVar(&port, "port", "6667", "--port=6667") 45 | flag.StringVar(&user, "user", "root", "--user=root") 46 | flag.StringVar(&password, "password", "root", "--password=root") 47 | flag.Parse() 48 | config := &client.Config{ 49 | Host: host, 50 | Port: port, 51 | UserName: user, 52 | Password: password, 53 | } 54 | session = client.NewSession(config) 55 | if err := session.Open(false, 0); err != nil { 56 | log.Fatal(err) 57 | } 58 | defer session.Close() 59 | 60 | //connectCluster() 61 | 62 | setStorageGroup("root.ln1") 63 | deleteStorageGroup("root.ln1") 64 | 65 | setStorageGroup("root.ln1") 66 | setStorageGroup("root.ln2") 67 | deleteStorageGroups("root.ln1", "root.ln2") 68 | 69 | createTimeseries("root.sg1.dev1.status") 70 | deleteTimeseries("root.sg1.dev1.status") 71 | 72 | createTimeseriesByNonQueryStatement("create timeseries root.sg1.dev1.status with datatype = int32") 73 | deleteTimeseries("root.sg1.dev1.status") 74 | 75 | createMultiTimeseries() 76 | deleteTimeseries("root.sg1.dev1.temperature") 77 | 78 | createAlignedTimeseries("root.sg1.dev1", []string{"status", "temperature"}, []string{"sts", "temp"}) 79 | deleteTimeseries("root.sg1.dev1.status") 80 | deleteTimeseries("root.sg1.dev1.temperature") 81 | 82 | insertStringRecord() 83 | deleteTimeseries("root.ln.wf02.wt02.hardware") 84 | 85 | insertRecord() 86 | deleteTimeseries("root.sg1.dev1.status") 87 | 88 | insertRecords() 89 | deleteTimeseries("root.sg1.dev1.status") 90 | 91 | insertTablet() 92 | var timeout int64 = 1000 93 | if ds, err := session.ExecuteQueryStatement("select * from root.ln.device1", &timeout); err == nil { 94 | printDevice1(ds) 95 | ds.Close() 96 | } else { 97 | log.Fatal(err) 98 | } 99 | deleteTimeseries("root.ln.device1.restart_count", "root.ln.device1.price", "root.ln.device1.tick_count", "root.ln.device1.temperature", "root.ln.device1.description", "root.ln.device1.status") 100 | insertTablets() 101 | deleteTimeseries("root.ln.device1.restart_count", "root.ln.device1.price", "root.ln.device1.tick_count", "root.ln.device1.temperature", "root.ln.device1.description", "root.ln.device1.status") 102 | 103 | insertRecord() 104 | deleteData() 105 | 106 | setTimeZone() 107 | if tz, err := getTimeZone(); err == nil { 108 | fmt.Printf("TimeZone: %s\n", tz) 109 | } else { 110 | fmt.Printf("getTimeZone ERROR: %v\n", err) 111 | } 112 | 113 | executeStatement() 114 | executeQueryStatement("select count(s3) from root.sg1.dev1") 115 | executeRawDataQuery() 116 | executeBatchStatement() 117 | 118 | var startTime int64 = 1 119 | var endTime int64 = 10 120 | var interval int64 = 2 121 | executeAggregationQueryStatementWithLegalNodes([]string{"root.ln.wf02.wt02.s5"}, []common.TAggregationType{common.TAggregationType_COUNT}, &startTime, &endTime, &interval) 122 | 123 | deleteTimeseries("root.sg1.dev1.status") 124 | deleteTimeseries("root.ln.wf02.wt02.s5") 125 | 126 | //0.12.x and newer 127 | insertRecordsOfOneDevice() 128 | deleteTimeseries("root.sg1.dev0.*") 129 | 130 | insertAlignedRecord() 131 | deleteTimeseries("root.al1.dev1.*") 132 | 133 | insertAlignedRecords() 134 | deleteTimeseries("root.al1.**") 135 | 136 | insertAlignedRecordsOfOneDevice() 137 | deleteTimeseries("root.al1.dev4.*") 138 | 139 | deleteStorageGroup("root.ln") 140 | insertAlignedTablet() 141 | deleteTimeseries("root.ln.device1.*") 142 | 143 | deleteStorageGroup("root.ln") 144 | insertAlignedTablets() 145 | deleteTimeseries("root.ln.device1.*") 146 | } 147 | 148 | // If your IotDB is a cluster version, you can use the following code for multi node connection 149 | func connectCluster() { 150 | config := &client.ClusterConfig{ 151 | NodeUrls: strings.Split("127.0.0.1:6667,127.0.0.1:6668,127.0.0.1:6669", ","), 152 | UserName: "root", 153 | Password: "root", 154 | } 155 | session, err := client.NewClusterSession(config) 156 | if err != nil { 157 | log.Fatal(err) 158 | } 159 | if err = session.OpenCluster(false); err != nil { 160 | log.Fatal(err) 161 | } 162 | } 163 | 164 | func printDevice1(sds *client.SessionDataSet) { 165 | for _, columnName := range sds.GetColumnNames() { 166 | fmt.Printf("%s\t", columnName) 167 | } 168 | fmt.Println() 169 | 170 | for next, err := sds.Next(); err == nil && next; next, err = sds.Next() { 171 | timestamp, _ := sds.GetStringByIndex(1) 172 | restartCount, _ := sds.GetIntByIndex(2) 173 | tickCount, _ := sds.GetLongByIndex(3) 174 | price, _ := sds.GetDoubleByIndex(4) 175 | temperature, _ := sds.GetFloatByIndex(5) 176 | description, _ := sds.GetStringByIndex(6) 177 | status, _ := sds.GetBooleanByIndex(7) 178 | 179 | whitespace := "\t\t" 180 | fmt.Printf("%s\t", timestamp) 181 | fmt.Printf("%v%s", restartCount, whitespace) 182 | fmt.Printf("%v%s", price, whitespace) 183 | fmt.Printf("%v%s", tickCount, whitespace) 184 | fmt.Printf("%v%s", temperature, whitespace) 185 | fmt.Printf("%v%s", description, whitespace) 186 | fmt.Printf("%v%s", status, whitespace) 187 | 188 | fmt.Println() 189 | } 190 | } 191 | 192 | func printDataSet0(sessionDataSet *client.SessionDataSet) { 193 | columns := sessionDataSet.GetColumnNames() 194 | for _, columnName := range columns { 195 | fmt.Printf("%s\t", columnName) 196 | } 197 | fmt.Println() 198 | 199 | for next, err := sessionDataSet.Next(); err == nil && next; next, err = sessionDataSet.Next() { 200 | for i, columnName := range columns { 201 | dataType, _ := client.GetDataTypeByStr(sessionDataSet.GetColumnTypes()[i]) 202 | switch dataType { 203 | case client.BOOLEAN: 204 | value, _ := sessionDataSet.GetBoolean(columnName) 205 | fmt.Print(value) 206 | case client.INT32: 207 | value, _ := sessionDataSet.GetInt(columnName) 208 | fmt.Print(value) 209 | case client.INT64, client.TIMESTAMP: 210 | value, _ := sessionDataSet.GetLong(columnName) 211 | fmt.Print(value) 212 | case client.FLOAT: 213 | value, _ := sessionDataSet.GetFloat(columnName) 214 | fmt.Print(value) 215 | case client.DOUBLE: 216 | value, _ := sessionDataSet.GetDouble(columnName) 217 | fmt.Print(value) 218 | case client.TEXT, client.STRING, client.BLOB, client.DATE: 219 | value, _ := sessionDataSet.GetString(columnName) 220 | fmt.Print(value) 221 | default: 222 | } 223 | fmt.Print("\t\t") 224 | } 225 | fmt.Println() 226 | } 227 | } 228 | 229 | func printDataSet1(sds *client.SessionDataSet) { 230 | columnNames := sds.GetColumnNames() 231 | for _, value := range columnNames { 232 | fmt.Printf("%s\t", value) 233 | } 234 | fmt.Println() 235 | 236 | for next, err := sds.Next(); err == nil && next; next, err = sds.Next() { 237 | for _, columnName := range columnNames { 238 | isNull, _ := sds.IsNull(columnName) 239 | 240 | if isNull { 241 | fmt.Printf("%v\t\t", "null") 242 | } else { 243 | v, _ := sds.GetString(columnName) 244 | fmt.Printf("%v\t\t", v) 245 | } 246 | } 247 | fmt.Println() 248 | } 249 | } 250 | 251 | func printDataSet2(sds *client.SessionDataSet) { 252 | columnNames := sds.GetColumnNames() 253 | for _, value := range columnNames { 254 | fmt.Printf("%s\t", value) 255 | } 256 | fmt.Println() 257 | 258 | for next, err := sds.Next(); err == nil && next; next, err = sds.Next() { 259 | for i := int32(0); i < int32(len(columnNames)); i++ { 260 | isNull, _ := sds.IsNullByIndex(i) 261 | 262 | if isNull { 263 | fmt.Printf("%v\t\t", "null") 264 | } else { 265 | v, _ := sds.GetStringByIndex(i) 266 | fmt.Printf("%v\t\t", v) 267 | } 268 | } 269 | fmt.Println() 270 | } 271 | } 272 | 273 | func setStorageGroup(sg string) { 274 | checkError(session.SetStorageGroup(sg)) 275 | } 276 | 277 | func deleteStorageGroup(sg string) { 278 | checkError(session.DeleteStorageGroup(sg)) 279 | } 280 | 281 | func deleteStorageGroups(sgs ...string) { 282 | checkError(session.DeleteStorageGroups(sgs...)) 283 | } 284 | 285 | func createTimeseries(path string) { 286 | var ( 287 | dataType = client.FLOAT 288 | encoding = client.PLAIN 289 | compressor = client.SNAPPY 290 | ) 291 | checkError(session.CreateTimeseries(path, dataType, encoding, compressor, nil, nil)) 292 | } 293 | 294 | func createTimeseriesByNonQueryStatement(sql string) { 295 | checkError(session.ExecuteNonQueryStatement(sql)) 296 | } 297 | 298 | func createAlignedTimeseries(prefixPath string, measurements, measurementAlias []string) { 299 | var ( 300 | dataTypes = []client.TSDataType{ 301 | client.FLOAT, 302 | client.FLOAT, 303 | } 304 | encodings = []client.TSEncoding{ 305 | client.PLAIN, 306 | client.PLAIN, 307 | } 308 | compressors = []client.TSCompressionType{ 309 | client.LZ4, 310 | client.LZ4, 311 | } 312 | ) 313 | checkError(session.CreateAlignedTimeseries(prefixPath, measurements, dataTypes, encodings, compressors, measurementAlias)) 314 | } 315 | 316 | func createMultiTimeseries() { 317 | var ( 318 | paths = []string{"root.sg1.dev1.temperature"} 319 | dataTypes = []client.TSDataType{client.TEXT} 320 | encodings = []client.TSEncoding{client.PLAIN} 321 | compressors = []client.TSCompressionType{client.SNAPPY} 322 | ) 323 | checkError(session.CreateMultiTimeseries(paths, dataTypes, encodings, compressors)) 324 | } 325 | 326 | func deleteTimeseries(paths ...string) { 327 | checkError(session.DeleteTimeseries(paths)) 328 | } 329 | 330 | func insertStringRecord() { 331 | var ( 332 | deviceId = "root.ln.wf02.wt02" 333 | measurements = []string{"hardware"} 334 | values = []string{"123"} 335 | timestamp int64 = 12 336 | ) 337 | checkError(session.InsertStringRecord(deviceId, measurements, values, timestamp)) 338 | } 339 | 340 | func insertRecord() { 341 | var ( 342 | deviceId = "root.sg1.dev1" 343 | measurements = []string{"status"} 344 | values = []interface{}{"123"} 345 | dataTypes = []client.TSDataType{client.TEXT} 346 | timestamp int64 = 12 347 | ) 348 | checkError(session.InsertRecord(deviceId, measurements, dataTypes, values, timestamp)) 349 | } 350 | 351 | func insertAlignedRecord() { 352 | var ( 353 | deviceId = "root.al1.dev1" 354 | measurements = []string{"status"} 355 | values = []interface{}{"123"} 356 | dataTypes = []client.TSDataType{client.TEXT} 357 | timestamp int64 = 12 358 | ) 359 | checkError(session.InsertAlignedRecord(deviceId, measurements, dataTypes, values, timestamp)) 360 | sessionDataSet, err := session.ExecuteStatement("show devices") 361 | if err == nil { 362 | printDataSet0(sessionDataSet) 363 | sessionDataSet.Close() 364 | } else { 365 | log.Println(err) 366 | } 367 | fmt.Println() 368 | } 369 | 370 | func insertRecords() { 371 | var ( 372 | deviceId = []string{"root.sg1.dev1"} 373 | measurements = [][]string{{"status"}} 374 | dataTypes = [][]client.TSDataType{{client.TEXT}} 375 | values = [][]interface{}{{"123"}} 376 | timestamp = []int64{12} 377 | ) 378 | checkError(session.InsertRecords(deviceId, measurements, dataTypes, values, timestamp)) 379 | } 380 | 381 | func insertAlignedRecords() { 382 | var ( 383 | deviceIds = []string{"root.al1.dev2", "root.al1.dev3"} 384 | measurements = [][]string{{"status"}, {"temperature"}} 385 | dataTypes = [][]client.TSDataType{{client.TEXT}, {client.TEXT}} 386 | values = [][]interface{}{{"33"}, {"44"}} 387 | timestamps = []int64{12, 13} 388 | ) 389 | checkError(session.InsertAlignedRecords(deviceIds, measurements, dataTypes, values, timestamps)) 390 | sessionDataSet, err := session.ExecuteStatement("show devices") 391 | if err == nil { 392 | printDataSet0(sessionDataSet) 393 | sessionDataSet.Close() 394 | } else { 395 | log.Println(err) 396 | } 397 | fmt.Println() 398 | } 399 | 400 | func insertRecordsOfOneDevice() { 401 | ts := time.Now().UTC().UnixNano() / 1000000 402 | var ( 403 | deviceId = "root.sg1.dev0" 404 | measurementsSlice = [][]string{ 405 | {"restart_count", "tick_count", "price"}, 406 | {"temperature", "description", "status"}, 407 | } 408 | dataTypes = [][]client.TSDataType{ 409 | {client.INT32, client.INT64, client.DOUBLE}, 410 | {client.FLOAT, client.TEXT, client.BOOLEAN}, 411 | } 412 | values = [][]interface{}{ 413 | {int32(1), int64(2018), float64(1988.1)}, 414 | {float32(12.1), "Test Device 1", false}, 415 | } 416 | timestamps = []int64{ts, ts - 1} 417 | ) 418 | checkError(session.InsertRecordsOfOneDevice(deviceId, timestamps, measurementsSlice, dataTypes, values, false)) 419 | } 420 | 421 | func insertAlignedRecordsOfOneDevice() { 422 | ts := time.Now().UTC().UnixNano() / 1000000 423 | var ( 424 | deviceId = "root.al1.dev4" 425 | measurementsSlice = [][]string{ 426 | {"restart_count", "tick_count", "price"}, 427 | {"temperature", "description", "status"}, 428 | } 429 | dataTypes = [][]client.TSDataType{ 430 | {client.INT32, client.INT64, client.DOUBLE}, 431 | {client.FLOAT, client.TEXT, client.BOOLEAN}, 432 | } 433 | values = [][]interface{}{ 434 | {int32(1), int64(2018), float64(1988.1)}, 435 | {float32(12.1), "Test Device 1", false}, 436 | } 437 | timestamps = []int64{ts, ts - 1} 438 | ) 439 | checkError(session.InsertAlignedRecordsOfOneDevice(deviceId, timestamps, measurementsSlice, dataTypes, values, false)) 440 | sessionDataSet, err := session.ExecuteStatement("show devices") 441 | if err == nil { 442 | printDataSet0(sessionDataSet) 443 | sessionDataSet.Close() 444 | } else { 445 | log.Println(err) 446 | } 447 | sessionDataSetNew, err := session.ExecuteStatement("select restart_count,tick_count,price,temperature,description,status from root.al1.dev4") 448 | if err == nil { 449 | printDataSet0(sessionDataSetNew) 450 | sessionDataSetNew.Close() 451 | } else { 452 | log.Println(err) 453 | } 454 | fmt.Println() 455 | } 456 | 457 | func deleteData() { 458 | var ( 459 | paths = []string{"root.sg1.dev1.status"} 460 | startTime int64 = 0 461 | endTime int64 = 12 462 | ) 463 | checkError(session.DeleteData(paths, startTime, endTime)) 464 | } 465 | 466 | func insertTablet() { 467 | if tablet, err := createTablet(12); err == nil { 468 | status, err := session.InsertTablet(tablet, false) 469 | tablet.Reset() 470 | checkError(status, err) 471 | } else { 472 | log.Fatal(err) 473 | } 474 | } 475 | 476 | func insertAlignedTablet() { 477 | if tablet, err := createTablet(12); err == nil { 478 | status, err := session.InsertAlignedTablet(tablet, false) 479 | tablet.Reset() 480 | checkError(status, err) 481 | } else { 482 | log.Fatal(err) 483 | } 484 | var timeout int64 = 1000 485 | if ds, err := session.ExecuteQueryStatement("select * from root.ln.device1", &timeout); err == nil { 486 | printDevice1(ds) 487 | ds.Close() 488 | } else { 489 | log.Fatal(err) 490 | } 491 | } 492 | 493 | func createTablet(rowCount int) (*client.Tablet, error) { 494 | tablet, err := client.NewTablet("root.ln.device1", []*client.MeasurementSchema{ 495 | { 496 | Measurement: "restart_count", 497 | DataType: client.INT32, 498 | }, { 499 | Measurement: "price", 500 | DataType: client.DOUBLE, 501 | }, { 502 | Measurement: "tick_count", 503 | DataType: client.INT64, 504 | }, { 505 | Measurement: "temperature", 506 | DataType: client.FLOAT, 507 | }, { 508 | Measurement: "description", 509 | DataType: client.TEXT, 510 | }, 511 | { 512 | Measurement: "status", 513 | DataType: client.BOOLEAN, 514 | }, 515 | }, rowCount) 516 | 517 | if err != nil { 518 | return nil, err 519 | } 520 | ts := time.Now().UTC().UnixNano() / 1000000 521 | for row := 0; row < int(rowCount); row++ { 522 | ts++ 523 | tablet.SetTimestamp(ts, row) 524 | tablet.SetValueAt(rand.Int31(), 0, row) 525 | if row%2 == 1 { 526 | tablet.SetValueAt(rand.Float64(), 1, row) 527 | } else { 528 | tablet.SetValueAt(nil, 1, row) 529 | } 530 | tablet.SetValueAt(rand.Int63(), 2, row) 531 | tablet.SetValueAt(rand.Float32(), 3, row) 532 | tablet.SetValueAt(fmt.Sprintf("Test Device %d", row+1), 4, row) 533 | tablet.SetValueAt(bool(ts%2 == 0), 5, row) 534 | tablet.RowSize++ 535 | } 536 | return tablet, nil 537 | } 538 | 539 | func insertTablets() { 540 | tablet1, err := createTablet(8) 541 | if err != nil { 542 | log.Fatal(err) 543 | } 544 | tablet2, err := createTablet(4) 545 | if err != nil { 546 | log.Fatal(err) 547 | } 548 | 549 | tablets := []*client.Tablet{tablet1, tablet2} 550 | checkError(session.InsertTablets(tablets, false)) 551 | } 552 | 553 | func insertAlignedTablets() { 554 | tablet1, err := createTablet(8) 555 | if err != nil { 556 | log.Fatal(err) 557 | } 558 | tablet2, err := createTablet(4) 559 | if err != nil { 560 | log.Fatal(err) 561 | } 562 | 563 | tablets := []*client.Tablet{tablet1, tablet2} 564 | checkError(session.InsertAlignedTablets(tablets, false)) 565 | } 566 | 567 | func setTimeZone() { 568 | var timeZone = "GMT" 569 | session.SetTimeZone(timeZone) 570 | } 571 | 572 | func getTimeZone() (string, error) { 573 | return session.GetTimeZone() 574 | } 575 | 576 | func executeStatement() { 577 | var sql = "show storage group" 578 | sessionDataSet, err := session.ExecuteStatement(sql) 579 | if err == nil { 580 | printDataSet0(sessionDataSet) 581 | sessionDataSet.Close() 582 | } else { 583 | log.Println(err) 584 | } 585 | } 586 | 587 | func executeQueryStatement(sql string) { 588 | var timeout int64 = 1000 589 | sessionDataSet, err := session.ExecuteQueryStatement(sql, &timeout) 590 | if err == nil { 591 | printDataSet1(sessionDataSet) 592 | sessionDataSet.Close() 593 | } else { 594 | log.Println(err) 595 | } 596 | } 597 | 598 | func executeAggregationQueryStatementWithLegalNodes(paths []string, aggregations []common.TAggregationType, 599 | startTime *int64, endTime *int64, interval *int64) { 600 | var timeout int64 = 1000 601 | var legal bool = true 602 | sessionDataSet, err := session.ExecuteAggregationQueryWithLegalNodes(paths, aggregations, startTime, endTime, interval, &timeout, &legal) 603 | if err == nil { 604 | printDataSet1(sessionDataSet) 605 | sessionDataSet.Close() 606 | } else { 607 | log.Println(err) 608 | } 609 | } 610 | 611 | func executeRawDataQuery() { 612 | session.ExecuteNonQueryStatement("insert into root.ln.wf02.wt02(time,s5) values(1,true)") 613 | var ( 614 | paths = []string{"root.ln.wf02.wt02.s5"} 615 | startTime int64 = 1 616 | endTime int64 = 200 617 | ) 618 | sessionDataSet, err := session.ExecuteRawDataQuery(paths, startTime, endTime) 619 | if err == nil { 620 | printDataSet2(sessionDataSet) 621 | sessionDataSet.Close() 622 | } else { 623 | log.Println(err) 624 | } 625 | } 626 | 627 | func executeBatchStatement() { 628 | var sqls = []string{"insert into root.ln.wf02.wt02(time,s5) values(1,true)", 629 | "insert into root.ln.wf02.wt02(time,s5) values(2,true)"} 630 | checkError(session.ExecuteBatchStatement(sqls)) 631 | var ( 632 | paths []string = []string{"root.ln.wf02.wt02.s5"} 633 | startTime int64 = 1 634 | endTime int64 = 200 635 | ) 636 | sessionDataSet, err := session.ExecuteRawDataQuery(paths, startTime, endTime) 637 | if err == nil { 638 | printDataSet2(sessionDataSet) 639 | sessionDataSet.Close() 640 | } else { 641 | log.Println(err) 642 | } 643 | } 644 | 645 | func checkError(status *common.TSStatus, err error) { 646 | if err != nil { 647 | log.Fatal(err) 648 | } 649 | 650 | if status != nil { 651 | if err = client.VerifySuccess(status); err != nil { 652 | log.Println(err) 653 | } 654 | } 655 | } 656 | -------------------------------------------------------------------------------- /example/session_pool/table/table_session_pool_example.go: -------------------------------------------------------------------------------- 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 | package main 21 | 22 | import ( 23 | "github.com/apache/iotdb-client-go/client" 24 | "github.com/apache/iotdb-client-go/common" 25 | "log" 26 | "strconv" 27 | "sync" 28 | "sync/atomic" 29 | "time" 30 | ) 31 | 32 | func main() { 33 | sessionPoolWithSpecificDatabaseExample() 34 | sessionPoolWithoutSpecificDatabaseExample() 35 | putBackToSessionPoolExample() 36 | } 37 | 38 | func putBackToSessionPoolExample() { 39 | // should create database test_db before executing 40 | config := &client.PoolConfig{ 41 | Host: "127.0.0.1", 42 | Port: "6667", 43 | UserName: "root", 44 | Password: "root", 45 | Database: "test_db", 46 | } 47 | sessionPool := client.NewTableSessionPool(config, 3, 60000, 4000, false) 48 | defer sessionPool.Close() 49 | 50 | num := 4 51 | successGetSessionNum := int32(0) 52 | var wg sync.WaitGroup 53 | wg.Add(num) 54 | for i := 0; i < num; i++ { 55 | dbName := "db" + strconv.Itoa(i) 56 | go func() { 57 | defer wg.Done() 58 | session, err := sessionPool.GetSession() 59 | if err != nil { 60 | log.Println("Failed to create database "+dbName+"because ", err) 61 | return 62 | } 63 | atomic.AddInt32(&successGetSessionNum, 1) 64 | defer func() { 65 | time.Sleep(6 * time.Second) 66 | // put back to session pool 67 | session.Close() 68 | }() 69 | checkError(session.ExecuteNonQueryStatement("create database " + dbName)) 70 | checkError(session.ExecuteNonQueryStatement("use " + dbName)) 71 | checkError(session.ExecuteNonQueryStatement("create table table_of_" + dbName + " (tag1 string tag, tag2 string tag, s1 text field, s2 text field)")) 72 | }() 73 | } 74 | wg.Wait() 75 | log.Println("success num is", successGetSessionNum) 76 | 77 | log.Println("All session's database have been reset.") 78 | // the using database will automatically reset to session pool's database after the session closed 79 | wg.Add(5) 80 | for i := 0; i < 5; i++ { 81 | go func() { 82 | defer wg.Done() 83 | session, err := sessionPool.GetSession() 84 | if err != nil { 85 | log.Println("Failed to get session because ", err) 86 | } 87 | defer session.Close() 88 | timeout := int64(3000) 89 | dataSet, err := session.ExecuteQueryStatement("show tables", &timeout) 90 | for { 91 | hasNext, err := dataSet.Next() 92 | if err != nil { 93 | log.Fatal(err) 94 | } 95 | if !hasNext { 96 | break 97 | } 98 | value, err := dataSet.GetString("TableName") 99 | if err != nil { 100 | log.Fatal(err) 101 | } 102 | log.Println("table is", value) 103 | } 104 | dataSet.Close() 105 | }() 106 | } 107 | wg.Wait() 108 | } 109 | 110 | func sessionPoolWithSpecificDatabaseExample() { 111 | // should create database test_db before executing 112 | config := &client.PoolConfig{ 113 | Host: "127.0.0.1", 114 | Port: "6667", 115 | UserName: "root", 116 | Password: "root", 117 | Database: "test_db", 118 | } 119 | sessionPool := client.NewTableSessionPool(config, 3, 60000, 8000, false) 120 | defer sessionPool.Close() 121 | num := 10 122 | var wg sync.WaitGroup 123 | wg.Add(num) 124 | for i := 0; i < num; i++ { 125 | tableName := "t" + strconv.Itoa(i) 126 | go func() { 127 | defer wg.Done() 128 | session, err := sessionPool.GetSession() 129 | if err != nil { 130 | log.Println("Failed to create table "+tableName+"because ", err) 131 | return 132 | } 133 | defer session.Close() 134 | checkError(session.ExecuteNonQueryStatement("create table " + tableName + " (tag1 string tag, tag2 string tag, s1 text field, s2 text field)")) 135 | }() 136 | } 137 | wg.Wait() 138 | } 139 | 140 | func sessionPoolWithoutSpecificDatabaseExample() { 141 | config := &client.PoolConfig{ 142 | Host: "127.0.0.1", 143 | Port: "6667", 144 | UserName: "root", 145 | Password: "root", 146 | } 147 | sessionPool := client.NewTableSessionPool(config, 3, 60000, 8000, false) 148 | defer sessionPool.Close() 149 | num := 10 150 | var wg sync.WaitGroup 151 | wg.Add(num) 152 | for i := 0; i < num; i++ { 153 | dbName := "db" + strconv.Itoa(i) 154 | go func() { 155 | defer wg.Done() 156 | session, err := sessionPool.GetSession() 157 | if err != nil { 158 | log.Println("Failed to create database ", dbName, err) 159 | return 160 | } 161 | defer session.Close() 162 | checkError(session.ExecuteNonQueryStatement("create database " + dbName)) 163 | checkError(session.ExecuteNonQueryStatement("use " + dbName)) 164 | checkError(session.ExecuteNonQueryStatement("create table t1 (tag1 string tag, tag2 string tag, s1 text field, s2 text field)")) 165 | }() 166 | } 167 | wg.Wait() 168 | } 169 | 170 | func checkError(status *common.TSStatus, err error) { 171 | if err != nil { 172 | log.Fatal(err) 173 | } 174 | 175 | if status != nil { 176 | if err = client.VerifySuccess(status); err != nil { 177 | log.Println(err) 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /example/table/table_session_example.go: -------------------------------------------------------------------------------- 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 | package main 21 | 22 | import ( 23 | "flag" 24 | "github.com/apache/iotdb-client-go/client" 25 | "github.com/apache/iotdb-client-go/common" 26 | "log" 27 | "math/rand" 28 | "strconv" 29 | "time" 30 | ) 31 | 32 | func main() { 33 | flag.Parse() 34 | config := &client.Config{ 35 | Host: "127.0.0.1", 36 | Port: "6667", 37 | UserName: "root", 38 | Password: "root", 39 | Database: "test_session", 40 | } 41 | session, err := client.NewTableSession(config, false, 0) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | defer session.Close() 46 | 47 | checkError(session.ExecuteNonQueryStatement("create database test_db")) 48 | checkError(session.ExecuteNonQueryStatement("use test_db")) 49 | checkError(session.ExecuteNonQueryStatement("create table t1 (tag1 string tag, tag2 string tag, s1 text field, s2 text field)")) 50 | insertRelationalTablet(session) 51 | showTables(session) 52 | query(session) 53 | } 54 | 55 | func getTextValueFromDataSet(dataSet *client.SessionDataSet, columnName string) string { 56 | if isNull, err := dataSet.IsNull(columnName); err != nil { 57 | log.Fatal(err) 58 | } else if isNull { 59 | return "null" 60 | } 61 | v, err := dataSet.GetString(columnName) 62 | if err != nil { 63 | log.Fatal(err) 64 | } 65 | return v 66 | } 67 | 68 | func insertRelationalTablet(session client.ITableSession) { 69 | tablet, err := client.NewRelationalTablet("t1", []*client.MeasurementSchema{ 70 | { 71 | Measurement: "tag1", 72 | DataType: client.STRING, 73 | }, 74 | { 75 | Measurement: "tag2", 76 | DataType: client.STRING, 77 | }, 78 | { 79 | Measurement: "s1", 80 | DataType: client.TEXT, 81 | }, 82 | { 83 | Measurement: "s2", 84 | DataType: client.TEXT, 85 | }, 86 | }, []client.ColumnCategory{client.TAG, client.TAG, client.FIELD, client.FIELD}, 1024) 87 | if err != nil { 88 | log.Fatal("Failed to create relational tablet {}", err) 89 | } 90 | ts := time.Now().UTC().UnixNano() / 1000000 91 | for row := 0; row < 16; row++ { 92 | ts++ 93 | tablet.SetTimestamp(ts, row) 94 | tablet.SetValueAt("tag1_value_"+strconv.Itoa(row), 0, row) 95 | tablet.SetValueAt("tag2_value_"+strconv.Itoa(row), 1, row) 96 | tablet.SetValueAt("s1_value_"+strconv.Itoa(row), 2, row) 97 | tablet.SetValueAt("s2_value_"+strconv.Itoa(row), 3, row) 98 | tablet.RowSize++ 99 | } 100 | checkError(session.Insert(tablet)) 101 | 102 | tablet.Reset() 103 | 104 | for row := 0; row < 16; row++ { 105 | ts++ 106 | tablet.SetTimestamp(ts, row) 107 | tablet.SetValueAt("tag1_value_1", 0, row) 108 | tablet.SetValueAt("tag2_value_1", 1, row) 109 | tablet.SetValueAt("s1_value_"+strconv.Itoa(row), 2, row) 110 | tablet.SetValueAt("s2_value_"+strconv.Itoa(row), 3, row) 111 | 112 | nullValueColumn := rand.Intn(4) 113 | tablet.SetValueAt(nil, nullValueColumn, row) 114 | tablet.RowSize++ 115 | } 116 | checkError(session.Insert(tablet)) 117 | } 118 | 119 | func showTables(session client.ITableSession) { 120 | timeout := int64(2000) 121 | dataSet, err := session.ExecuteQueryStatement("show tables", &timeout) 122 | defer dataSet.Close() 123 | if err != nil { 124 | log.Fatal(err) 125 | } 126 | for { 127 | hasNext, err := dataSet.Next() 128 | if err != nil { 129 | log.Fatal(err) 130 | } 131 | if !hasNext { 132 | break 133 | } 134 | value, err := dataSet.GetString("TableName") 135 | if err != nil { 136 | log.Fatal(err) 137 | } 138 | log.Printf("tableName is %v", value) 139 | } 140 | } 141 | 142 | func query(session client.ITableSession) { 143 | timeout := int64(2000) 144 | dataSet, err := session.ExecuteQueryStatement("select * from t1", &timeout) 145 | defer dataSet.Close() 146 | if err != nil { 147 | log.Fatal(err) 148 | } 149 | for { 150 | hasNext, err := dataSet.Next() 151 | if err != nil { 152 | log.Fatal(err) 153 | } 154 | if !hasNext { 155 | break 156 | } 157 | log.Printf("%v %v %v %v", getTextValueFromDataSet(dataSet, "tag1"), getTextValueFromDataSet(dataSet, "tag2"), getTextValueFromDataSet(dataSet, "s1"), getTextValueFromDataSet(dataSet, "s2")) 158 | } 159 | } 160 | 161 | func checkError(status *common.TSStatus, err error) { 162 | if err != nil { 163 | log.Fatal(err) 164 | } 165 | 166 | if status != nil { 167 | if err = client.VerifySuccess(status); err != nil { 168 | log.Println(err) 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/apache/iotdb-client-go 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/apache/thrift v0.15.0 7 | github.com/stretchr/testify v1.8.2 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/apache/thrift v0.15.0 h1:aGvdaR0v1t9XLgjtBYwxcBvBOTMqClzwE26CHOgjW1Y= 2 | github.com/apache/thrift v0.15.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 10 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 11 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 12 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 13 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 14 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 15 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 16 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 17 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 18 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 19 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 20 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 21 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 22 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 23 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 24 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 25 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 26 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 27 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 28 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 29 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 30 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 31 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 32 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 33 | -------------------------------------------------------------------------------- /rpc/GoUnusedProtection__.go: -------------------------------------------------------------------------------- 1 | // Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. 2 | 3 | package rpc 4 | 5 | var GoUnusedProtection__ int; 6 | 7 | -------------------------------------------------------------------------------- /rpc/client-consts.go: -------------------------------------------------------------------------------- 1 | // Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. 2 | 3 | package rpc 4 | 5 | import( 6 | "bytes" 7 | "context" 8 | "fmt" 9 | "time" 10 | "github.com/apache/thrift/lib/go/thrift" 11 | "github.com/apache/iotdb-client-go/common" 12 | 13 | ) 14 | 15 | // (needed to ensure safety because of naive import list construction.) 16 | var _ = thrift.ZERO 17 | var _ = fmt.Printf 18 | var _ = context.Background 19 | var _ = time.Now 20 | var _ = bytes.Equal 21 | 22 | var _ = common.GoUnusedProtection__ 23 | 24 | func init() { 25 | } 26 | 27 | -------------------------------------------------------------------------------- /test/e2e/Dockerfile.iotdb-client-go: -------------------------------------------------------------------------------- 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 | FROM golang:1.13-alpine 21 | 22 | ENV CGO_ENABLED=0 23 | COPY . /go/src/github.com/apache/iotdb-client-go/ 24 | WORKDIR /go/src/github.com/apache/iotdb-client-go 25 | CMD [ "go", "test", "-v", "./test/e2e/..." ] 26 | -------------------------------------------------------------------------------- /test/e2e/Dockerfile.iotdb-server: -------------------------------------------------------------------------------- 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 | FROM eclipse-temurin:17-jre-focal 21 | 22 | RUN apt update \ 23 | && apt install -y procps && apt clean 24 | ADD target /usr/local 25 | ADD test/e2e/start-1c1d.sh / 26 | WORKDIR /usr/local/iotdb 27 | EXPOSE 6667 28 | CMD ["/start-1c1d.sh"] 29 | -------------------------------------------------------------------------------- /test/e2e/docker-compose.yml: -------------------------------------------------------------------------------- 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 | version: "3.1" 21 | 22 | services: 23 | iotdb: 24 | build: 25 | context: ../.. 26 | dockerfile: test/e2e/Dockerfile.iotdb-server 27 | ports: 28 | - 6667:6667 29 | healthcheck: 30 | test: ["CMD", "ls", "/usr/local/iotdb/data"] 31 | interval: 3s 32 | timeout: 15s 33 | retries: 30 34 | start_period: 30s 35 | iotdb_client_go: 36 | build: 37 | context: ../.. 38 | dockerfile: test/e2e/Dockerfile.iotdb-client-go 39 | depends_on: 40 | iotdb: 41 | condition: service_healthy -------------------------------------------------------------------------------- /test/e2e/e2e_table_test.go: -------------------------------------------------------------------------------- 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 | package e2e 21 | 22 | import ( 23 | "github.com/apache/iotdb-client-go/client" 24 | "github.com/apache/iotdb-client-go/common" 25 | "github.com/stretchr/testify/suite" 26 | "log" 27 | "strconv" 28 | "strings" 29 | "sync" 30 | "sync/atomic" 31 | "testing" 32 | "time" 33 | ) 34 | 35 | var ( 36 | nodeUrls = "iotdb:6668,iotdb:6667,iotdb:6669" 37 | host = "iotdb" 38 | port = "6667" 39 | username = "root" 40 | password = "root" 41 | database = "test_db" 42 | ) 43 | 44 | type e2eTableTestSuite struct { 45 | suite.Suite 46 | session client.ITableSession 47 | } 48 | 49 | func TestE2ETableTestSuite(t *testing.T) { 50 | suite.Run(t, &e2eTableTestSuite{}) 51 | } 52 | 53 | func (s *e2eTableTestSuite) SetupSuite() { 54 | clusterConfig := client.ClusterConfig{ 55 | NodeUrls: strings.Split(nodeUrls, ","), 56 | UserName: username, 57 | Password: password, 58 | } 59 | session, err := client.NewClusterTableSession(&clusterConfig, false) 60 | s.Require().NoError(err) 61 | s.session = session 62 | } 63 | 64 | func (s *e2eTableTestSuite) TearDownSuite() { 65 | s.session.Close() 66 | } 67 | 68 | func (s *e2eTableTestSuite) SetupTest() { 69 | s.checkError(s.session.ExecuteNonQueryStatement("create database " + database)) 70 | s.checkError(s.session.ExecuteNonQueryStatement("use " + database)) 71 | for i := 0; i < 10; i++ { 72 | s.checkError(s.session.ExecuteNonQueryStatement("create database db" + strconv.Itoa(i))) 73 | } 74 | } 75 | 76 | func (s *e2eTableTestSuite) TearDownTest() { 77 | s.checkError(s.session.ExecuteNonQueryStatement("drop database " + database)) 78 | for i := 0; i < 10; i++ { 79 | s.checkError(s.session.ExecuteNonQueryStatement("drop database db" + strconv.Itoa(i))) 80 | } 81 | } 82 | 83 | func (s *e2eTableTestSuite) Test_CreateTableSession() { 84 | assert := s.Require() 85 | config := &client.Config{ 86 | Host: host, 87 | Port: port, 88 | UserName: username, 89 | Password: password, 90 | } 91 | session, err := client.NewTableSession(config, false, 3000) 92 | assert.NoError(err) 93 | defer session.Close() 94 | s.checkError(session.ExecuteNonQueryStatement("use " + database)) 95 | } 96 | 97 | func (s *e2eTableTestSuite) Test_CreateTableSessionWithDatabase() { 98 | assert := s.Require() 99 | config := &client.Config{ 100 | Host: host, 101 | Port: port, 102 | UserName: username, 103 | Password: password, 104 | Database: database, 105 | } 106 | session, err := client.NewTableSession(config, false, 3000) 107 | defer session.Close() 108 | assert.NoError(err) 109 | timeoutInMs := int64(1000) 110 | _, err = session.ExecuteQueryStatement("show tables", &timeoutInMs) 111 | assert.NoError(err) 112 | } 113 | 114 | func (s *e2eTableTestSuite) Test_GetSessionFromTableSessionPool() { 115 | assert := s.Require() 116 | poolConfig := &client.PoolConfig{ 117 | Host: host, 118 | Port: port, 119 | UserName: username, 120 | Password: password, 121 | } 122 | sessionPool := client.NewTableSessionPool(poolConfig, 3, 10000, 3000, false) 123 | defer sessionPool.Close() 124 | 125 | session1, err := sessionPool.GetSession() 126 | assert.NoError(err) 127 | s.checkError(session1.ExecuteNonQueryStatement("use " + database)) 128 | session1.Close() 129 | 130 | // test get session timeout 131 | var wg sync.WaitGroup 132 | getNum := 4 133 | wg.Add(getNum) 134 | successNum := int32(0) 135 | for i := 0; i < getNum; i++ { 136 | go func() { 137 | defer wg.Done() 138 | session, getSessionErr := sessionPool.GetSession() 139 | // timeout after 3s 140 | if getSessionErr != nil { 141 | return 142 | } 143 | atomic.AddInt32(&successNum, 1) 144 | defer func() { 145 | time.Sleep(time.Second * 4) 146 | session.Close() 147 | }() 148 | }() 149 | } 150 | wg.Wait() 151 | assert.Equal(int32(3), successNum) 152 | 153 | // test get session 154 | getNum = 10 155 | wg.Add(getNum) 156 | successNum = int32(0) 157 | for i := 0; i < getNum; i++ { 158 | go func() { 159 | defer wg.Done() 160 | session, getSessionErr := sessionPool.GetSession() 161 | if getSessionErr != nil { 162 | return 163 | } 164 | atomic.AddInt32(&successNum, 1) 165 | defer session.Close() 166 | s.checkError(session.ExecuteNonQueryStatement("use " + database)) 167 | }() 168 | } 169 | wg.Wait() 170 | assert.Equal(int32(10), successNum) 171 | } 172 | 173 | func (s *e2eTableTestSuite) Test_GetSessionFromSessionPoolWithSpecificDatabase() { 174 | assert := s.Require() 175 | poolConfig := &client.PoolConfig{ 176 | Host: host, 177 | Port: port, 178 | UserName: username, 179 | Password: password, 180 | Database: database, 181 | } 182 | sessionPool := client.NewTableSessionPool(poolConfig, 3, 10000, 3000, false) 183 | defer sessionPool.Close() 184 | 185 | session1, err := sessionPool.GetSession() 186 | assert.NoError(err) 187 | s.checkError(session1.ExecuteNonQueryStatement("create table table_in_" + database + " (tag1 string tag, tag2 string tag, s1 text field, s2 text field)")) 188 | session1.Close() 189 | 190 | var wg sync.WaitGroup 191 | getNum := 10 192 | wg.Add(getNum) 193 | successNum := int32(0) 194 | for i := 0; i < getNum; i++ { 195 | currentDbName := "db" + strconv.Itoa(i) 196 | go func() { 197 | defer wg.Done() 198 | session, getSessionErr := sessionPool.GetSession() 199 | if getSessionErr != nil { 200 | return 201 | } 202 | defer session.Close() 203 | 204 | timeoutInMs := int64(3000) 205 | dataSet, queryErr := session.ExecuteQueryStatement("show tables", &timeoutInMs) 206 | defer dataSet.Close() 207 | assert.NoError(queryErr) 208 | assert.True(dataSet.Next()) 209 | value, err := dataSet.GetString("TableName") 210 | assert.NoError(err) 211 | assert.Equal("table_in_"+database, value) 212 | 213 | // modify using database 214 | s.checkError(session.ExecuteNonQueryStatement("use " + currentDbName)) 215 | atomic.AddInt32(&successNum, 1) 216 | }() 217 | } 218 | wg.Wait() 219 | assert.Equal(int32(10), successNum) 220 | 221 | // database in session should be reset to test_db 222 | wg.Add(getNum) 223 | for i := 0; i < getNum; i++ { 224 | go func() { 225 | defer wg.Done() 226 | session, getSessionErr := sessionPool.GetSession() 227 | // timeout after 3s 228 | if getSessionErr != nil { 229 | return 230 | } 231 | defer session.Close() 232 | atomic.AddInt32(&successNum, 1) 233 | 234 | timeoutInMs := int64(3000) 235 | dataSet, queryErr := session.ExecuteQueryStatement("show tables", &timeoutInMs) 236 | defer dataSet.Close() 237 | assert.NoError(queryErr) 238 | assert.True(dataSet.Next()) 239 | value, err := dataSet.GetString("TableName") 240 | assert.NoError(err) 241 | assert.Equal("table_in_"+database, value) 242 | }() 243 | } 244 | wg.Wait() 245 | } 246 | 247 | func (s *e2eTableTestSuite) Test_InsertTabletAndQuery() { 248 | assert := s.Require() 249 | s.checkError(s.session.ExecuteNonQueryStatement("create table t1 (tag1 string tag, tag2 string tag, s1 text field, s2 text field)")) 250 | 251 | timeoutInMs := int64(10000) 252 | 253 | // show tables 254 | dataSet, err := s.session.ExecuteQueryStatement("show tables", &timeoutInMs) 255 | assert.NoError(err) 256 | 257 | hasNext, err := dataSet.Next() 258 | assert.NoError(err) 259 | assert.True(hasNext) 260 | value, err := dataSet.GetString("TableName") 261 | assert.NoError(err) 262 | assert.Equal("t1", value) 263 | dataSet.Close() 264 | 265 | // insert relational tablet 266 | tablet, err := client.NewRelationalTablet("t1", []*client.MeasurementSchema{ 267 | { 268 | Measurement: "tag1", 269 | DataType: client.STRING, 270 | }, 271 | { 272 | Measurement: "tag2", 273 | DataType: client.STRING, 274 | }, 275 | { 276 | Measurement: "s1", 277 | DataType: client.TEXT, 278 | }, 279 | { 280 | Measurement: "s2", 281 | DataType: client.TEXT, 282 | }, 283 | }, []client.ColumnCategory{client.TAG, client.TAG, client.FIELD, client.FIELD}, 1024) 284 | assert.NoError(err) 285 | 286 | values := [][]interface{}{ 287 | {"tag1_value_1", "tag2_value_1", "s1_value_1", "s2_value_1"}, 288 | {"tag1_value_1", "tag2_value_1", "s1_value_2", "s2_value_2"}, 289 | {"tag1_value_1", "tag2_value_1", nil, "s2_value_2"}, 290 | {"tag1_value_2", "tag2_value_2", "s1_value_1", "s2_value_1"}, 291 | {"tag1_value_2", "tag2_value_2", "s1_value_1", "s2_value_1"}, 292 | {"tag1_value_3", "tag2_value_3", "s1_value_1", "s2_value_1"}, 293 | {"tag1_value_3", "tag2_value_3", "s1_value_2", nil}, 294 | {"tag1_value_3", "tag2_value_3", "s1_value_3", "s2_value_3"}, 295 | } 296 | 297 | ts := int64(0) 298 | for row := 0; row < 8; row++ { 299 | tablet.SetTimestamp(ts, row) 300 | assert.NoError(tablet.SetValueAt(values[row][0], 0, row)) 301 | assert.NoError(tablet.SetValueAt(values[row][1], 1, row)) 302 | assert.NoError(tablet.SetValueAt(values[row][2], 2, row)) 303 | assert.NoError(tablet.SetValueAt(values[row][3], 3, row)) 304 | ts++ 305 | tablet.RowSize++ 306 | } 307 | s.checkError(s.session.Insert(tablet)) 308 | 309 | // query 310 | dataSet, err = s.session.ExecuteQueryStatement("select * from t1 order by time asc", &timeoutInMs) 311 | assert.NoError(err) 312 | 313 | count := int64(0) 314 | for { 315 | hasNext, err := dataSet.Next() 316 | assert.NoError(err) 317 | if !hasNext { 318 | break 319 | } 320 | value, err := dataSet.GetLong("time") 321 | assert.NoError(err) 322 | assert.Equal(count, value) 323 | assert.Equal(values[count][0], getValueFromDataSet(dataSet, "tag1")) 324 | assert.Equal(values[count][1], getValueFromDataSet(dataSet, "tag2")) 325 | assert.Equal(values[count][2], getValueFromDataSet(dataSet, "s1")) 326 | assert.Equal(values[count][3], getValueFromDataSet(dataSet, "s2")) 327 | count++ 328 | } 329 | assert.Equal(int64(8), count) 330 | dataSet.Close() 331 | 332 | // query 333 | dataSet, err = s.session.ExecuteQueryStatement("select s1, s1 from t1 order by time asc", &timeoutInMs) 334 | assert.NoError(err) 335 | 336 | count = int64(0) 337 | for { 338 | hasNext, err := dataSet.Next() 339 | assert.NoError(err) 340 | if !hasNext { 341 | break 342 | } 343 | assert.Equal(values[count][2], getValueFromDataSetByIndex(dataSet, 1)) 344 | assert.Equal(values[count][2], getValueFromDataSetByIndex(dataSet, 2)) 345 | count++ 346 | } 347 | assert.Equal(int64(8), count) 348 | dataSet.Close() 349 | 350 | // query 351 | dataSet, err = s.session.ExecuteQueryStatement("select s1, s2 as s1 from t1 order by time asc", &timeoutInMs) 352 | defer dataSet.Close() 353 | assert.NoError(err) 354 | 355 | count = int64(0) 356 | for { 357 | hasNext, err := dataSet.Next() 358 | assert.NoError(err) 359 | if !hasNext { 360 | break 361 | } 362 | assert.Equal(values[count][2], getValueFromDataSetByIndex(dataSet, 1)) 363 | assert.Equal(values[count][3], getValueFromDataSetByIndex(dataSet, 2)) 364 | count++ 365 | } 366 | assert.Equal(int64(8), count) 367 | } 368 | 369 | func getValueFromDataSet(dataSet *client.SessionDataSet, columnName string) interface{} { 370 | if isNull, err := dataSet.IsNull(columnName); err != nil { 371 | log.Fatal(err) 372 | } else if isNull { 373 | return nil 374 | } 375 | v, err := dataSet.GetString(columnName) 376 | if err != nil { 377 | log.Fatal(err) 378 | } 379 | return v 380 | } 381 | 382 | func getValueFromDataSetByIndex(dataSet *client.SessionDataSet, columnIndex int32) interface{} { 383 | if isNull, err := dataSet.IsNullByIndex(columnIndex); err != nil { 384 | log.Fatal(err) 385 | } else if isNull { 386 | return nil 387 | } 388 | v, err := dataSet.GetStringByIndex(columnIndex) 389 | if err != nil { 390 | log.Fatal(err) 391 | } 392 | return v 393 | } 394 | 395 | func (s *e2eTableTestSuite) checkError(status *common.TSStatus, err error) { 396 | s.Require().NoError(err) 397 | if status != nil { 398 | s.Require().NoError(client.VerifySuccess(status)) 399 | } 400 | } 401 | -------------------------------------------------------------------------------- /test/e2e/start-1c1d.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # 20 | nohup /usr/local/iotdb/sbin/start-confignode.sh > /dev/null 2>&1 & 21 | sleep 5 22 | ls /usr/local/iotdb/sbin 23 | /usr/local/iotdb/sbin/start-datanode.sh 24 | --------------------------------------------------------------------------------