├── .asf.yaml ├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── DEPENDENCIES.tsv ├── DISCLAIMER ├── LICENSE ├── Makefile ├── NOTICE ├── README.md ├── examples └── read_write.rs ├── licenserc.toml ├── rust-toolchain.toml ├── rustfmt.toml └── src ├── config.rs ├── db_client ├── builder.rs ├── inner.rs ├── mod.rs ├── raw.rs └── route_based.rs ├── errors.rs ├── lib.rs ├── model ├── mod.rs ├── route.rs ├── sql_query │ ├── display.rs │ ├── mod.rs │ ├── request.rs │ ├── response.rs │ └── row.rs ├── value.rs └── write │ ├── mod.rs │ ├── point.rs │ ├── request.rs │ └── response.rs ├── router.rs ├── rpc_client ├── mock_rpc_client.rs ├── mod.rs └── rpc_client_impl.rs └── util.rs /.asf.yaml: -------------------------------------------------------------------------------- 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 | # For more information, see https://cwiki.apache.org/confluence/display/INFRA/Git+-+.asf.yaml+features. 19 | 20 | github: 21 | description: >- 22 | Apache HoraeDB (Incubating) Rust Client. 23 | homepage: https://horaedb.apache.org/ 24 | labels: 25 | - rust 26 | - sql 27 | - database 28 | - distributed-database 29 | - cloud-native 30 | - tsdb 31 | - timeseries-database 32 | - timeseries-analysis 33 | - iot-database 34 | - horaedb 35 | enabled_merge_buttons: 36 | squash: true 37 | merge: false 38 | rebase: true 39 | protected_branches: 40 | main: 41 | required_pull_request_reviews: 42 | dismiss_stale_reviews: true 43 | required_approving_review_count: 1 44 | 45 | notifications: 46 | commits: commits@horaedb.apache.org 47 | issues: commits@horaedb.apache.org 48 | pullrequests: commits@horaedb.apache.org 49 | jobs: commits@horaedb.apache.org 50 | discussions: commits@horaedb.apache.org 51 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 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 | name: CI 19 | 20 | on: 21 | push: 22 | branches: 23 | - main 24 | paths-ignore: 25 | - 'etc/**' 26 | - '**.md' 27 | pull_request: 28 | branches: 29 | - main 30 | paths-ignore: 31 | - 'etc/**' 32 | - '**.md' 33 | 34 | env: 35 | CARGO_TERM_COLOR: always 36 | RUSTFLAGS: "-C debuginfo=1" 37 | 38 | jobs: 39 | style-check: 40 | name: style-check 41 | runs-on: ubuntu-latest 42 | timeout-minutes: 60 43 | steps: 44 | - uses: actions/checkout@v3 45 | with: 46 | submodules: true 47 | - uses: Swatinem/rust-cache@v2 48 | - name: Check License Header 49 | uses: korandoru/hawkeye@v5 50 | - name: Setup Build Environment 51 | run: sudo apt update && sudo apt install -y protobuf-compiler 52 | - name: Install cargo binaries 53 | run: cargo install cargo-sort --locked 54 | - name: Run Style Check 55 | run: make fmt clippy check-toml 56 | 57 | test: 58 | name: test 59 | runs-on: ubuntu-latest 60 | timeout-minutes: 60 61 | steps: 62 | - uses: actions/checkout@v3 63 | with: 64 | submodules: true 65 | - uses: Swatinem/rust-cache@v2 66 | - name: Setup Build Environment 67 | run: | 68 | sudo apt update 69 | sudo apt install --yes protobuf-compiler 70 | - name: Run Test 71 | run: make test 72 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 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 | name: Release 19 | 20 | on: 21 | workflow_dispatch: 22 | 23 | jobs: 24 | publish: 25 | name: Publish 26 | runs-on: ubuntu-latest 27 | steps: 28 | - name: Checkout sources 29 | uses: actions/checkout@v2 30 | 31 | - name: Install stable toolchain 32 | uses: actions-rs/toolchain@v1 33 | with: 34 | profile: minimal 35 | toolchain: stable 36 | override: true 37 | 38 | - run: cargo publish 39 | env: 40 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .DS_Store 3 | .idea/ 4 | .vscode 5 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.22.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "ahash" 22 | version = "0.8.11" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 25 | dependencies = [ 26 | "cfg-if", 27 | "const-random", 28 | "getrandom", 29 | "once_cell", 30 | "version_check", 31 | "zerocopy", 32 | ] 33 | 34 | [[package]] 35 | name = "aho-corasick" 36 | version = "1.1.3" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 39 | dependencies = [ 40 | "memchr", 41 | ] 42 | 43 | [[package]] 44 | name = "android-tzdata" 45 | version = "0.1.1" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 48 | 49 | [[package]] 50 | name = "android_system_properties" 51 | version = "0.1.5" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 54 | dependencies = [ 55 | "libc", 56 | ] 57 | 58 | [[package]] 59 | name = "anyhow" 60 | version = "1.0.86" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" 63 | 64 | [[package]] 65 | name = "arrow" 66 | version = "38.0.0" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "c107a57b5913d852da9d5a40e280e4695f2258b5b87733c13b770c63a7117287" 69 | dependencies = [ 70 | "ahash", 71 | "arrow-arith", 72 | "arrow-array", 73 | "arrow-buffer", 74 | "arrow-cast", 75 | "arrow-csv", 76 | "arrow-data", 77 | "arrow-ipc", 78 | "arrow-json", 79 | "arrow-ord", 80 | "arrow-row", 81 | "arrow-schema", 82 | "arrow-select", 83 | "arrow-string", 84 | ] 85 | 86 | [[package]] 87 | name = "arrow-arith" 88 | version = "38.0.0" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "ace6aa3d5617c5d03041a05e01c6819428a8ddf49dd0b055df9b40fef9d96094" 91 | dependencies = [ 92 | "arrow-array", 93 | "arrow-buffer", 94 | "arrow-data", 95 | "arrow-schema", 96 | "chrono", 97 | "half", 98 | "num", 99 | ] 100 | 101 | [[package]] 102 | name = "arrow-array" 103 | version = "38.0.0" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "104a04520692cc674e6afd7682f213ca41f9b13ff1873f63a5a2857a590b87b3" 106 | dependencies = [ 107 | "ahash", 108 | "arrow-buffer", 109 | "arrow-data", 110 | "arrow-schema", 111 | "chrono", 112 | "half", 113 | "hashbrown 0.13.2", 114 | "num", 115 | ] 116 | 117 | [[package]] 118 | name = "arrow-buffer" 119 | version = "38.0.0" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "72c875bcb9530ec403998fb0b2dc6d180a7c64563ca4bc22b90eafb84b113143" 122 | dependencies = [ 123 | "half", 124 | "num", 125 | ] 126 | 127 | [[package]] 128 | name = "arrow-cast" 129 | version = "38.0.0" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "d6d6e18281636c8fc0b93be59834da6bf9a72bb70fd0c98ddfdaf124da466c28" 132 | dependencies = [ 133 | "arrow-array", 134 | "arrow-buffer", 135 | "arrow-data", 136 | "arrow-schema", 137 | "arrow-select", 138 | "chrono", 139 | "lexical-core", 140 | "num", 141 | ] 142 | 143 | [[package]] 144 | name = "arrow-csv" 145 | version = "38.0.0" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "3197dab0963a236ff8e7c82e2272535745955ac1321eb740c29f2f88b353f54e" 148 | dependencies = [ 149 | "arrow-array", 150 | "arrow-buffer", 151 | "arrow-cast", 152 | "arrow-data", 153 | "arrow-schema", 154 | "chrono", 155 | "csv", 156 | "csv-core", 157 | "lazy_static", 158 | "lexical-core", 159 | "regex", 160 | ] 161 | 162 | [[package]] 163 | name = "arrow-data" 164 | version = "38.0.0" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "eb68113d6ecdbe8bba48b2c4042c151bf9e1c61244e45072a50250a6fc59bafe" 167 | dependencies = [ 168 | "arrow-buffer", 169 | "arrow-schema", 170 | "half", 171 | "num", 172 | ] 173 | 174 | [[package]] 175 | name = "arrow-ipc" 176 | version = "38.0.0" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "eab4bbf2dd3078facb5ce0a9641316a64f42bfd8cf357e6775c8a5e6708e3a8d" 179 | dependencies = [ 180 | "arrow-array", 181 | "arrow-buffer", 182 | "arrow-cast", 183 | "arrow-data", 184 | "arrow-schema", 185 | "flatbuffers", 186 | ] 187 | 188 | [[package]] 189 | name = "arrow-json" 190 | version = "38.0.0" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "48c5b650d23746a494665d914a7fa3d21d939153cff9d53bdebe39bffa88f263" 193 | dependencies = [ 194 | "arrow-array", 195 | "arrow-buffer", 196 | "arrow-cast", 197 | "arrow-data", 198 | "arrow-schema", 199 | "chrono", 200 | "half", 201 | "indexmap 1.9.3", 202 | "lexical-core", 203 | "num", 204 | "serde", 205 | "serde_json", 206 | ] 207 | 208 | [[package]] 209 | name = "arrow-ord" 210 | version = "38.0.0" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "68c6fce28e5011e30acc7466b5efcb8ed0197c396240bd2b10e167f275a3c208" 213 | dependencies = [ 214 | "arrow-array", 215 | "arrow-buffer", 216 | "arrow-data", 217 | "arrow-schema", 218 | "arrow-select", 219 | "half", 220 | "num", 221 | ] 222 | 223 | [[package]] 224 | name = "arrow-row" 225 | version = "38.0.0" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "f20a421f19799d8b93eb8edde5217e910fa1e2d6ceb3c529f000e57b6db144c0" 228 | dependencies = [ 229 | "ahash", 230 | "arrow-array", 231 | "arrow-buffer", 232 | "arrow-data", 233 | "arrow-schema", 234 | "half", 235 | "hashbrown 0.13.2", 236 | ] 237 | 238 | [[package]] 239 | name = "arrow-schema" 240 | version = "38.0.0" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "bc85923d8d6662cc66ac6602c7d1876872e671002d60993dfdf492a6badeae92" 243 | 244 | [[package]] 245 | name = "arrow-select" 246 | version = "38.0.0" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "f6ab6613ce65b61d85a3410241744e84e48fbab0fe06e1251b4429d21b3470fd" 249 | dependencies = [ 250 | "arrow-array", 251 | "arrow-buffer", 252 | "arrow-data", 253 | "arrow-schema", 254 | "num", 255 | ] 256 | 257 | [[package]] 258 | name = "arrow-string" 259 | version = "38.0.0" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "f3008641239e884aefba66d8b8532da6af40d14296349fcc85935de4ba67b89e" 262 | dependencies = [ 263 | "arrow-array", 264 | "arrow-buffer", 265 | "arrow-data", 266 | "arrow-schema", 267 | "arrow-select", 268 | "regex", 269 | "regex-syntax 0.6.29", 270 | ] 271 | 272 | [[package]] 273 | name = "async-stream" 274 | version = "0.3.5" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" 277 | dependencies = [ 278 | "async-stream-impl", 279 | "futures-core", 280 | "pin-project-lite", 281 | ] 282 | 283 | [[package]] 284 | name = "async-stream-impl" 285 | version = "0.3.5" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" 288 | dependencies = [ 289 | "proc-macro2", 290 | "quote", 291 | "syn 2.0.70", 292 | ] 293 | 294 | [[package]] 295 | name = "async-trait" 296 | version = "0.1.81" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" 299 | dependencies = [ 300 | "proc-macro2", 301 | "quote", 302 | "syn 2.0.70", 303 | ] 304 | 305 | [[package]] 306 | name = "autocfg" 307 | version = "1.3.0" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 310 | 311 | [[package]] 312 | name = "axum" 313 | version = "0.6.20" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" 316 | dependencies = [ 317 | "async-trait", 318 | "axum-core", 319 | "bitflags 1.3.2", 320 | "bytes", 321 | "futures-util", 322 | "http", 323 | "http-body", 324 | "hyper", 325 | "itoa", 326 | "matchit", 327 | "memchr", 328 | "mime", 329 | "percent-encoding", 330 | "pin-project-lite", 331 | "rustversion", 332 | "serde", 333 | "sync_wrapper", 334 | "tower", 335 | "tower-layer", 336 | "tower-service", 337 | ] 338 | 339 | [[package]] 340 | name = "axum-core" 341 | version = "0.3.4" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" 344 | dependencies = [ 345 | "async-trait", 346 | "bytes", 347 | "futures-util", 348 | "http", 349 | "http-body", 350 | "mime", 351 | "rustversion", 352 | "tower-layer", 353 | "tower-service", 354 | ] 355 | 356 | [[package]] 357 | name = "backtrace" 358 | version = "0.3.73" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" 361 | dependencies = [ 362 | "addr2line", 363 | "cc", 364 | "cfg-if", 365 | "libc", 366 | "miniz_oxide", 367 | "object", 368 | "rustc-demangle", 369 | ] 370 | 371 | [[package]] 372 | name = "base64" 373 | version = "0.13.1" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" 376 | 377 | [[package]] 378 | name = "base64" 379 | version = "0.22.1" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 382 | 383 | [[package]] 384 | name = "bitflags" 385 | version = "1.3.2" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 388 | 389 | [[package]] 390 | name = "bitflags" 391 | version = "2.6.0" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 394 | 395 | [[package]] 396 | name = "bumpalo" 397 | version = "3.16.0" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 400 | 401 | [[package]] 402 | name = "bytes" 403 | version = "1.6.0" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" 406 | 407 | [[package]] 408 | name = "cc" 409 | version = "1.1.0" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "eaff6f8ce506b9773fa786672d63fc7a191ffea1be33f72bbd4aeacefca9ffc8" 412 | dependencies = [ 413 | "jobserver", 414 | "libc", 415 | "once_cell", 416 | ] 417 | 418 | [[package]] 419 | name = "cfg-if" 420 | version = "1.0.0" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 423 | 424 | [[package]] 425 | name = "chrono" 426 | version = "0.4.38" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 429 | dependencies = [ 430 | "android-tzdata", 431 | "iana-time-zone", 432 | "js-sys", 433 | "num-traits", 434 | "wasm-bindgen", 435 | "windows-targets 0.52.6", 436 | ] 437 | 438 | [[package]] 439 | name = "const-random" 440 | version = "0.1.18" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" 443 | dependencies = [ 444 | "const-random-macro", 445 | ] 446 | 447 | [[package]] 448 | name = "const-random-macro" 449 | version = "0.1.16" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" 452 | dependencies = [ 453 | "getrandom", 454 | "once_cell", 455 | "tiny-keccak", 456 | ] 457 | 458 | [[package]] 459 | name = "core-foundation-sys" 460 | version = "0.8.6" 461 | source = "registry+https://github.com/rust-lang/crates.io-index" 462 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 463 | 464 | [[package]] 465 | name = "crunchy" 466 | version = "0.2.2" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 469 | 470 | [[package]] 471 | name = "csv" 472 | version = "1.3.0" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" 475 | dependencies = [ 476 | "csv-core", 477 | "itoa", 478 | "ryu", 479 | "serde", 480 | ] 481 | 482 | [[package]] 483 | name = "csv-core" 484 | version = "0.1.11" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" 487 | dependencies = [ 488 | "memchr", 489 | ] 490 | 491 | [[package]] 492 | name = "dashmap" 493 | version = "5.5.3" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" 496 | dependencies = [ 497 | "cfg-if", 498 | "hashbrown 0.14.5", 499 | "lock_api", 500 | "once_cell", 501 | "parking_lot_core", 502 | ] 503 | 504 | [[package]] 505 | name = "either" 506 | version = "1.13.0" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 509 | 510 | [[package]] 511 | name = "equivalent" 512 | version = "1.0.1" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 515 | 516 | [[package]] 517 | name = "errno" 518 | version = "0.3.9" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" 521 | dependencies = [ 522 | "libc", 523 | "windows-sys 0.52.0", 524 | ] 525 | 526 | [[package]] 527 | name = "fastrand" 528 | version = "2.1.0" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" 531 | 532 | [[package]] 533 | name = "fixedbitset" 534 | version = "0.4.2" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" 537 | 538 | [[package]] 539 | name = "flatbuffers" 540 | version = "23.5.26" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "4dac53e22462d78c16d64a1cd22371b54cc3fe94aa15e7886a2fa6e5d1ab8640" 543 | dependencies = [ 544 | "bitflags 1.3.2", 545 | "rustc_version", 546 | ] 547 | 548 | [[package]] 549 | name = "fnv" 550 | version = "1.0.7" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 553 | 554 | [[package]] 555 | name = "futures" 556 | version = "0.3.30" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 559 | dependencies = [ 560 | "futures-channel", 561 | "futures-core", 562 | "futures-executor", 563 | "futures-io", 564 | "futures-sink", 565 | "futures-task", 566 | "futures-util", 567 | ] 568 | 569 | [[package]] 570 | name = "futures-channel" 571 | version = "0.3.30" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 574 | dependencies = [ 575 | "futures-core", 576 | "futures-sink", 577 | ] 578 | 579 | [[package]] 580 | name = "futures-core" 581 | version = "0.3.30" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 584 | 585 | [[package]] 586 | name = "futures-executor" 587 | version = "0.3.30" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" 590 | dependencies = [ 591 | "futures-core", 592 | "futures-task", 593 | "futures-util", 594 | ] 595 | 596 | [[package]] 597 | name = "futures-io" 598 | version = "0.3.30" 599 | source = "registry+https://github.com/rust-lang/crates.io-index" 600 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 601 | 602 | [[package]] 603 | name = "futures-macro" 604 | version = "0.3.30" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" 607 | dependencies = [ 608 | "proc-macro2", 609 | "quote", 610 | "syn 2.0.70", 611 | ] 612 | 613 | [[package]] 614 | name = "futures-sink" 615 | version = "0.3.30" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 618 | 619 | [[package]] 620 | name = "futures-task" 621 | version = "0.3.30" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 624 | 625 | [[package]] 626 | name = "futures-util" 627 | version = "0.3.30" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 630 | dependencies = [ 631 | "futures-channel", 632 | "futures-core", 633 | "futures-io", 634 | "futures-macro", 635 | "futures-sink", 636 | "futures-task", 637 | "memchr", 638 | "pin-project-lite", 639 | "pin-utils", 640 | "slab", 641 | ] 642 | 643 | [[package]] 644 | name = "getrandom" 645 | version = "0.2.15" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 648 | dependencies = [ 649 | "cfg-if", 650 | "libc", 651 | "wasi", 652 | ] 653 | 654 | [[package]] 655 | name = "gimli" 656 | version = "0.29.0" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" 659 | 660 | [[package]] 661 | name = "h2" 662 | version = "0.3.26" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" 665 | dependencies = [ 666 | "bytes", 667 | "fnv", 668 | "futures-core", 669 | "futures-sink", 670 | "futures-util", 671 | "http", 672 | "indexmap 2.2.6", 673 | "slab", 674 | "tokio", 675 | "tokio-util", 676 | "tracing", 677 | ] 678 | 679 | [[package]] 680 | name = "half" 681 | version = "2.4.1" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" 684 | dependencies = [ 685 | "cfg-if", 686 | "crunchy", 687 | "num-traits", 688 | ] 689 | 690 | [[package]] 691 | name = "hashbrown" 692 | version = "0.12.3" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 695 | 696 | [[package]] 697 | name = "hashbrown" 698 | version = "0.13.2" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" 701 | 702 | [[package]] 703 | name = "hashbrown" 704 | version = "0.14.5" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 707 | 708 | [[package]] 709 | name = "heck" 710 | version = "0.4.1" 711 | source = "registry+https://github.com/rust-lang/crates.io-index" 712 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 713 | 714 | [[package]] 715 | name = "hermit-abi" 716 | version = "0.3.9" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 719 | 720 | [[package]] 721 | name = "home" 722 | version = "0.5.9" 723 | source = "registry+https://github.com/rust-lang/crates.io-index" 724 | checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" 725 | dependencies = [ 726 | "windows-sys 0.52.0", 727 | ] 728 | 729 | [[package]] 730 | name = "horaedb-client" 731 | version = "2.0.0" 732 | dependencies = [ 733 | "anyhow", 734 | "arrow", 735 | "async-trait", 736 | "base64 0.22.1", 737 | "chrono", 738 | "dashmap", 739 | "futures", 740 | "horaedbproto", 741 | "paste", 742 | "thiserror", 743 | "tokio", 744 | "tonic", 745 | "zstd", 746 | ] 747 | 748 | [[package]] 749 | name = "horaedbproto" 750 | version = "1.0.24" 751 | source = "registry+https://github.com/rust-lang/crates.io-index" 752 | checksum = "5907c770ee20818978cf2050341ca2c4c7fb7888423ccb090cbb2fda250dfad7" 753 | dependencies = [ 754 | "prost", 755 | "protoc-bin-vendored", 756 | "tonic", 757 | "tonic-build", 758 | "walkdir", 759 | ] 760 | 761 | [[package]] 762 | name = "http" 763 | version = "0.2.12" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" 766 | dependencies = [ 767 | "bytes", 768 | "fnv", 769 | "itoa", 770 | ] 771 | 772 | [[package]] 773 | name = "http-body" 774 | version = "0.4.6" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" 777 | dependencies = [ 778 | "bytes", 779 | "http", 780 | "pin-project-lite", 781 | ] 782 | 783 | [[package]] 784 | name = "httparse" 785 | version = "1.9.4" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" 788 | 789 | [[package]] 790 | name = "httpdate" 791 | version = "1.0.3" 792 | source = "registry+https://github.com/rust-lang/crates.io-index" 793 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 794 | 795 | [[package]] 796 | name = "hyper" 797 | version = "0.14.30" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" 800 | dependencies = [ 801 | "bytes", 802 | "futures-channel", 803 | "futures-core", 804 | "futures-util", 805 | "h2", 806 | "http", 807 | "http-body", 808 | "httparse", 809 | "httpdate", 810 | "itoa", 811 | "pin-project-lite", 812 | "socket2", 813 | "tokio", 814 | "tower-service", 815 | "tracing", 816 | "want", 817 | ] 818 | 819 | [[package]] 820 | name = "hyper-timeout" 821 | version = "0.4.1" 822 | source = "registry+https://github.com/rust-lang/crates.io-index" 823 | checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" 824 | dependencies = [ 825 | "hyper", 826 | "pin-project-lite", 827 | "tokio", 828 | "tokio-io-timeout", 829 | ] 830 | 831 | [[package]] 832 | name = "iana-time-zone" 833 | version = "0.1.60" 834 | source = "registry+https://github.com/rust-lang/crates.io-index" 835 | checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" 836 | dependencies = [ 837 | "android_system_properties", 838 | "core-foundation-sys", 839 | "iana-time-zone-haiku", 840 | "js-sys", 841 | "wasm-bindgen", 842 | "windows-core", 843 | ] 844 | 845 | [[package]] 846 | name = "iana-time-zone-haiku" 847 | version = "0.1.2" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 850 | dependencies = [ 851 | "cc", 852 | ] 853 | 854 | [[package]] 855 | name = "indexmap" 856 | version = "1.9.3" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 859 | dependencies = [ 860 | "autocfg", 861 | "hashbrown 0.12.3", 862 | ] 863 | 864 | [[package]] 865 | name = "indexmap" 866 | version = "2.2.6" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" 869 | dependencies = [ 870 | "equivalent", 871 | "hashbrown 0.14.5", 872 | ] 873 | 874 | [[package]] 875 | name = "itertools" 876 | version = "0.10.5" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 879 | dependencies = [ 880 | "either", 881 | ] 882 | 883 | [[package]] 884 | name = "itoa" 885 | version = "1.0.11" 886 | source = "registry+https://github.com/rust-lang/crates.io-index" 887 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 888 | 889 | [[package]] 890 | name = "jobserver" 891 | version = "0.1.31" 892 | source = "registry+https://github.com/rust-lang/crates.io-index" 893 | checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" 894 | dependencies = [ 895 | "libc", 896 | ] 897 | 898 | [[package]] 899 | name = "js-sys" 900 | version = "0.3.69" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" 903 | dependencies = [ 904 | "wasm-bindgen", 905 | ] 906 | 907 | [[package]] 908 | name = "lazy_static" 909 | version = "1.5.0" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 912 | 913 | [[package]] 914 | name = "lexical-core" 915 | version = "0.8.5" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" 918 | dependencies = [ 919 | "lexical-parse-float", 920 | "lexical-parse-integer", 921 | "lexical-util", 922 | "lexical-write-float", 923 | "lexical-write-integer", 924 | ] 925 | 926 | [[package]] 927 | name = "lexical-parse-float" 928 | version = "0.8.5" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" 931 | dependencies = [ 932 | "lexical-parse-integer", 933 | "lexical-util", 934 | "static_assertions", 935 | ] 936 | 937 | [[package]] 938 | name = "lexical-parse-integer" 939 | version = "0.8.6" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" 942 | dependencies = [ 943 | "lexical-util", 944 | "static_assertions", 945 | ] 946 | 947 | [[package]] 948 | name = "lexical-util" 949 | version = "0.8.5" 950 | source = "registry+https://github.com/rust-lang/crates.io-index" 951 | checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" 952 | dependencies = [ 953 | "static_assertions", 954 | ] 955 | 956 | [[package]] 957 | name = "lexical-write-float" 958 | version = "0.8.5" 959 | source = "registry+https://github.com/rust-lang/crates.io-index" 960 | checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" 961 | dependencies = [ 962 | "lexical-util", 963 | "lexical-write-integer", 964 | "static_assertions", 965 | ] 966 | 967 | [[package]] 968 | name = "lexical-write-integer" 969 | version = "0.8.5" 970 | source = "registry+https://github.com/rust-lang/crates.io-index" 971 | checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" 972 | dependencies = [ 973 | "lexical-util", 974 | "static_assertions", 975 | ] 976 | 977 | [[package]] 978 | name = "libc" 979 | version = "0.2.155" 980 | source = "registry+https://github.com/rust-lang/crates.io-index" 981 | checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" 982 | 983 | [[package]] 984 | name = "libm" 985 | version = "0.2.8" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" 988 | 989 | [[package]] 990 | name = "linux-raw-sys" 991 | version = "0.4.14" 992 | source = "registry+https://github.com/rust-lang/crates.io-index" 993 | checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 994 | 995 | [[package]] 996 | name = "lock_api" 997 | version = "0.4.12" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 1000 | dependencies = [ 1001 | "autocfg", 1002 | "scopeguard", 1003 | ] 1004 | 1005 | [[package]] 1006 | name = "log" 1007 | version = "0.4.22" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 1010 | 1011 | [[package]] 1012 | name = "matchit" 1013 | version = "0.7.3" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" 1016 | 1017 | [[package]] 1018 | name = "memchr" 1019 | version = "2.7.4" 1020 | source = "registry+https://github.com/rust-lang/crates.io-index" 1021 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 1022 | 1023 | [[package]] 1024 | name = "mime" 1025 | version = "0.3.17" 1026 | source = "registry+https://github.com/rust-lang/crates.io-index" 1027 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 1028 | 1029 | [[package]] 1030 | name = "miniz_oxide" 1031 | version = "0.7.4" 1032 | source = "registry+https://github.com/rust-lang/crates.io-index" 1033 | checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" 1034 | dependencies = [ 1035 | "adler", 1036 | ] 1037 | 1038 | [[package]] 1039 | name = "mio" 1040 | version = "0.8.11" 1041 | source = "registry+https://github.com/rust-lang/crates.io-index" 1042 | checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" 1043 | dependencies = [ 1044 | "libc", 1045 | "wasi", 1046 | "windows-sys 0.48.0", 1047 | ] 1048 | 1049 | [[package]] 1050 | name = "multimap" 1051 | version = "0.8.3" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" 1054 | 1055 | [[package]] 1056 | name = "num" 1057 | version = "0.4.3" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" 1060 | dependencies = [ 1061 | "num-bigint", 1062 | "num-complex", 1063 | "num-integer", 1064 | "num-iter", 1065 | "num-rational", 1066 | "num-traits", 1067 | ] 1068 | 1069 | [[package]] 1070 | name = "num-bigint" 1071 | version = "0.4.6" 1072 | source = "registry+https://github.com/rust-lang/crates.io-index" 1073 | checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" 1074 | dependencies = [ 1075 | "num-integer", 1076 | "num-traits", 1077 | ] 1078 | 1079 | [[package]] 1080 | name = "num-complex" 1081 | version = "0.4.6" 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" 1083 | checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" 1084 | dependencies = [ 1085 | "num-traits", 1086 | ] 1087 | 1088 | [[package]] 1089 | name = "num-integer" 1090 | version = "0.1.46" 1091 | source = "registry+https://github.com/rust-lang/crates.io-index" 1092 | checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 1093 | dependencies = [ 1094 | "num-traits", 1095 | ] 1096 | 1097 | [[package]] 1098 | name = "num-iter" 1099 | version = "0.1.45" 1100 | source = "registry+https://github.com/rust-lang/crates.io-index" 1101 | checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" 1102 | dependencies = [ 1103 | "autocfg", 1104 | "num-integer", 1105 | "num-traits", 1106 | ] 1107 | 1108 | [[package]] 1109 | name = "num-rational" 1110 | version = "0.4.2" 1111 | source = "registry+https://github.com/rust-lang/crates.io-index" 1112 | checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" 1113 | dependencies = [ 1114 | "num-bigint", 1115 | "num-integer", 1116 | "num-traits", 1117 | ] 1118 | 1119 | [[package]] 1120 | name = "num-traits" 1121 | version = "0.2.19" 1122 | source = "registry+https://github.com/rust-lang/crates.io-index" 1123 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1124 | dependencies = [ 1125 | "autocfg", 1126 | "libm", 1127 | ] 1128 | 1129 | [[package]] 1130 | name = "num_cpus" 1131 | version = "1.16.0" 1132 | source = "registry+https://github.com/rust-lang/crates.io-index" 1133 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 1134 | dependencies = [ 1135 | "hermit-abi", 1136 | "libc", 1137 | ] 1138 | 1139 | [[package]] 1140 | name = "object" 1141 | version = "0.36.1" 1142 | source = "registry+https://github.com/rust-lang/crates.io-index" 1143 | checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" 1144 | dependencies = [ 1145 | "memchr", 1146 | ] 1147 | 1148 | [[package]] 1149 | name = "once_cell" 1150 | version = "1.19.0" 1151 | source = "registry+https://github.com/rust-lang/crates.io-index" 1152 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 1153 | 1154 | [[package]] 1155 | name = "parking_lot" 1156 | version = "0.12.3" 1157 | source = "registry+https://github.com/rust-lang/crates.io-index" 1158 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 1159 | dependencies = [ 1160 | "lock_api", 1161 | "parking_lot_core", 1162 | ] 1163 | 1164 | [[package]] 1165 | name = "parking_lot_core" 1166 | version = "0.9.10" 1167 | source = "registry+https://github.com/rust-lang/crates.io-index" 1168 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1169 | dependencies = [ 1170 | "cfg-if", 1171 | "libc", 1172 | "redox_syscall", 1173 | "smallvec", 1174 | "windows-targets 0.52.6", 1175 | ] 1176 | 1177 | [[package]] 1178 | name = "paste" 1179 | version = "1.0.15" 1180 | source = "registry+https://github.com/rust-lang/crates.io-index" 1181 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 1182 | 1183 | [[package]] 1184 | name = "percent-encoding" 1185 | version = "2.3.1" 1186 | source = "registry+https://github.com/rust-lang/crates.io-index" 1187 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1188 | 1189 | [[package]] 1190 | name = "petgraph" 1191 | version = "0.6.5" 1192 | source = "registry+https://github.com/rust-lang/crates.io-index" 1193 | checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" 1194 | dependencies = [ 1195 | "fixedbitset", 1196 | "indexmap 2.2.6", 1197 | ] 1198 | 1199 | [[package]] 1200 | name = "pin-project" 1201 | version = "1.1.5" 1202 | source = "registry+https://github.com/rust-lang/crates.io-index" 1203 | checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" 1204 | dependencies = [ 1205 | "pin-project-internal", 1206 | ] 1207 | 1208 | [[package]] 1209 | name = "pin-project-internal" 1210 | version = "1.1.5" 1211 | source = "registry+https://github.com/rust-lang/crates.io-index" 1212 | checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" 1213 | dependencies = [ 1214 | "proc-macro2", 1215 | "quote", 1216 | "syn 2.0.70", 1217 | ] 1218 | 1219 | [[package]] 1220 | name = "pin-project-lite" 1221 | version = "0.2.14" 1222 | source = "registry+https://github.com/rust-lang/crates.io-index" 1223 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 1224 | 1225 | [[package]] 1226 | name = "pin-utils" 1227 | version = "0.1.0" 1228 | source = "registry+https://github.com/rust-lang/crates.io-index" 1229 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1230 | 1231 | [[package]] 1232 | name = "pkg-config" 1233 | version = "0.3.30" 1234 | source = "registry+https://github.com/rust-lang/crates.io-index" 1235 | checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" 1236 | 1237 | [[package]] 1238 | name = "ppv-lite86" 1239 | version = "0.2.17" 1240 | source = "registry+https://github.com/rust-lang/crates.io-index" 1241 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 1242 | 1243 | [[package]] 1244 | name = "prettyplease" 1245 | version = "0.1.25" 1246 | source = "registry+https://github.com/rust-lang/crates.io-index" 1247 | checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" 1248 | dependencies = [ 1249 | "proc-macro2", 1250 | "syn 1.0.109", 1251 | ] 1252 | 1253 | [[package]] 1254 | name = "proc-macro2" 1255 | version = "1.0.86" 1256 | source = "registry+https://github.com/rust-lang/crates.io-index" 1257 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 1258 | dependencies = [ 1259 | "unicode-ident", 1260 | ] 1261 | 1262 | [[package]] 1263 | name = "prost" 1264 | version = "0.11.9" 1265 | source = "registry+https://github.com/rust-lang/crates.io-index" 1266 | checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" 1267 | dependencies = [ 1268 | "bytes", 1269 | "prost-derive", 1270 | ] 1271 | 1272 | [[package]] 1273 | name = "prost-build" 1274 | version = "0.11.9" 1275 | source = "registry+https://github.com/rust-lang/crates.io-index" 1276 | checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" 1277 | dependencies = [ 1278 | "bytes", 1279 | "heck", 1280 | "itertools", 1281 | "lazy_static", 1282 | "log", 1283 | "multimap", 1284 | "petgraph", 1285 | "prettyplease", 1286 | "prost", 1287 | "prost-types", 1288 | "regex", 1289 | "syn 1.0.109", 1290 | "tempfile", 1291 | "which", 1292 | ] 1293 | 1294 | [[package]] 1295 | name = "prost-derive" 1296 | version = "0.11.9" 1297 | source = "registry+https://github.com/rust-lang/crates.io-index" 1298 | checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" 1299 | dependencies = [ 1300 | "anyhow", 1301 | "itertools", 1302 | "proc-macro2", 1303 | "quote", 1304 | "syn 1.0.109", 1305 | ] 1306 | 1307 | [[package]] 1308 | name = "prost-types" 1309 | version = "0.11.9" 1310 | source = "registry+https://github.com/rust-lang/crates.io-index" 1311 | checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" 1312 | dependencies = [ 1313 | "prost", 1314 | ] 1315 | 1316 | [[package]] 1317 | name = "protoc-bin-vendored" 1318 | version = "3.0.0" 1319 | source = "registry+https://github.com/rust-lang/crates.io-index" 1320 | checksum = "005ca8623e5633e298ad1f917d8be0a44bcf406bf3cde3b80e63003e49a3f27d" 1321 | dependencies = [ 1322 | "protoc-bin-vendored-linux-aarch_64", 1323 | "protoc-bin-vendored-linux-ppcle_64", 1324 | "protoc-bin-vendored-linux-x86_32", 1325 | "protoc-bin-vendored-linux-x86_64", 1326 | "protoc-bin-vendored-macos-x86_64", 1327 | "protoc-bin-vendored-win32", 1328 | ] 1329 | 1330 | [[package]] 1331 | name = "protoc-bin-vendored-linux-aarch_64" 1332 | version = "3.0.0" 1333 | source = "registry+https://github.com/rust-lang/crates.io-index" 1334 | checksum = "8fb9fc9cce84c8694b6ea01cc6296617b288b703719b725b8c9c65f7c5874435" 1335 | 1336 | [[package]] 1337 | name = "protoc-bin-vendored-linux-ppcle_64" 1338 | version = "3.0.0" 1339 | source = "registry+https://github.com/rust-lang/crates.io-index" 1340 | checksum = "02d2a07dcf7173a04d49974930ccbfb7fd4d74df30ecfc8762cf2f895a094516" 1341 | 1342 | [[package]] 1343 | name = "protoc-bin-vendored-linux-x86_32" 1344 | version = "3.0.0" 1345 | source = "registry+https://github.com/rust-lang/crates.io-index" 1346 | checksum = "d54fef0b04fcacba64d1d80eed74a20356d96847da8497a59b0a0a436c9165b0" 1347 | 1348 | [[package]] 1349 | name = "protoc-bin-vendored-linux-x86_64" 1350 | version = "3.0.0" 1351 | source = "registry+https://github.com/rust-lang/crates.io-index" 1352 | checksum = "b8782f2ce7d43a9a5c74ea4936f001e9e8442205c244f7a3d4286bd4c37bc924" 1353 | 1354 | [[package]] 1355 | name = "protoc-bin-vendored-macos-x86_64" 1356 | version = "3.0.0" 1357 | source = "registry+https://github.com/rust-lang/crates.io-index" 1358 | checksum = "b5de656c7ee83f08e0ae5b81792ccfdc1d04e7876b1d9a38e6876a9e09e02537" 1359 | 1360 | [[package]] 1361 | name = "protoc-bin-vendored-win32" 1362 | version = "3.0.0" 1363 | source = "registry+https://github.com/rust-lang/crates.io-index" 1364 | checksum = "9653c3ed92974e34c5a6e0a510864dab979760481714c172e0a34e437cb98804" 1365 | 1366 | [[package]] 1367 | name = "quote" 1368 | version = "1.0.36" 1369 | source = "registry+https://github.com/rust-lang/crates.io-index" 1370 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 1371 | dependencies = [ 1372 | "proc-macro2", 1373 | ] 1374 | 1375 | [[package]] 1376 | name = "rand" 1377 | version = "0.8.5" 1378 | source = "registry+https://github.com/rust-lang/crates.io-index" 1379 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1380 | dependencies = [ 1381 | "libc", 1382 | "rand_chacha", 1383 | "rand_core", 1384 | ] 1385 | 1386 | [[package]] 1387 | name = "rand_chacha" 1388 | version = "0.3.1" 1389 | source = "registry+https://github.com/rust-lang/crates.io-index" 1390 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1391 | dependencies = [ 1392 | "ppv-lite86", 1393 | "rand_core", 1394 | ] 1395 | 1396 | [[package]] 1397 | name = "rand_core" 1398 | version = "0.6.4" 1399 | source = "registry+https://github.com/rust-lang/crates.io-index" 1400 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1401 | dependencies = [ 1402 | "getrandom", 1403 | ] 1404 | 1405 | [[package]] 1406 | name = "redox_syscall" 1407 | version = "0.5.2" 1408 | source = "registry+https://github.com/rust-lang/crates.io-index" 1409 | checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" 1410 | dependencies = [ 1411 | "bitflags 2.6.0", 1412 | ] 1413 | 1414 | [[package]] 1415 | name = "regex" 1416 | version = "1.10.5" 1417 | source = "registry+https://github.com/rust-lang/crates.io-index" 1418 | checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" 1419 | dependencies = [ 1420 | "aho-corasick", 1421 | "memchr", 1422 | "regex-automata", 1423 | "regex-syntax 0.8.4", 1424 | ] 1425 | 1426 | [[package]] 1427 | name = "regex-automata" 1428 | version = "0.4.7" 1429 | source = "registry+https://github.com/rust-lang/crates.io-index" 1430 | checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" 1431 | dependencies = [ 1432 | "aho-corasick", 1433 | "memchr", 1434 | "regex-syntax 0.8.4", 1435 | ] 1436 | 1437 | [[package]] 1438 | name = "regex-syntax" 1439 | version = "0.6.29" 1440 | source = "registry+https://github.com/rust-lang/crates.io-index" 1441 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 1442 | 1443 | [[package]] 1444 | name = "regex-syntax" 1445 | version = "0.8.4" 1446 | source = "registry+https://github.com/rust-lang/crates.io-index" 1447 | checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" 1448 | 1449 | [[package]] 1450 | name = "rustc-demangle" 1451 | version = "0.1.24" 1452 | source = "registry+https://github.com/rust-lang/crates.io-index" 1453 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1454 | 1455 | [[package]] 1456 | name = "rustc_version" 1457 | version = "0.4.0" 1458 | source = "registry+https://github.com/rust-lang/crates.io-index" 1459 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 1460 | dependencies = [ 1461 | "semver", 1462 | ] 1463 | 1464 | [[package]] 1465 | name = "rustix" 1466 | version = "0.38.34" 1467 | source = "registry+https://github.com/rust-lang/crates.io-index" 1468 | checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" 1469 | dependencies = [ 1470 | "bitflags 2.6.0", 1471 | "errno", 1472 | "libc", 1473 | "linux-raw-sys", 1474 | "windows-sys 0.52.0", 1475 | ] 1476 | 1477 | [[package]] 1478 | name = "rustversion" 1479 | version = "1.0.17" 1480 | source = "registry+https://github.com/rust-lang/crates.io-index" 1481 | checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" 1482 | 1483 | [[package]] 1484 | name = "ryu" 1485 | version = "1.0.18" 1486 | source = "registry+https://github.com/rust-lang/crates.io-index" 1487 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 1488 | 1489 | [[package]] 1490 | name = "same-file" 1491 | version = "1.0.6" 1492 | source = "registry+https://github.com/rust-lang/crates.io-index" 1493 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 1494 | dependencies = [ 1495 | "winapi-util", 1496 | ] 1497 | 1498 | [[package]] 1499 | name = "scopeguard" 1500 | version = "1.2.0" 1501 | source = "registry+https://github.com/rust-lang/crates.io-index" 1502 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1503 | 1504 | [[package]] 1505 | name = "semver" 1506 | version = "1.0.23" 1507 | source = "registry+https://github.com/rust-lang/crates.io-index" 1508 | checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" 1509 | 1510 | [[package]] 1511 | name = "serde" 1512 | version = "1.0.204" 1513 | source = "registry+https://github.com/rust-lang/crates.io-index" 1514 | checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" 1515 | dependencies = [ 1516 | "serde_derive", 1517 | ] 1518 | 1519 | [[package]] 1520 | name = "serde_derive" 1521 | version = "1.0.204" 1522 | source = "registry+https://github.com/rust-lang/crates.io-index" 1523 | checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" 1524 | dependencies = [ 1525 | "proc-macro2", 1526 | "quote", 1527 | "syn 2.0.70", 1528 | ] 1529 | 1530 | [[package]] 1531 | name = "serde_json" 1532 | version = "1.0.120" 1533 | source = "registry+https://github.com/rust-lang/crates.io-index" 1534 | checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" 1535 | dependencies = [ 1536 | "itoa", 1537 | "ryu", 1538 | "serde", 1539 | ] 1540 | 1541 | [[package]] 1542 | name = "signal-hook-registry" 1543 | version = "1.4.2" 1544 | source = "registry+https://github.com/rust-lang/crates.io-index" 1545 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 1546 | dependencies = [ 1547 | "libc", 1548 | ] 1549 | 1550 | [[package]] 1551 | name = "slab" 1552 | version = "0.4.9" 1553 | source = "registry+https://github.com/rust-lang/crates.io-index" 1554 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1555 | dependencies = [ 1556 | "autocfg", 1557 | ] 1558 | 1559 | [[package]] 1560 | name = "smallvec" 1561 | version = "1.13.2" 1562 | source = "registry+https://github.com/rust-lang/crates.io-index" 1563 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1564 | 1565 | [[package]] 1566 | name = "socket2" 1567 | version = "0.5.7" 1568 | source = "registry+https://github.com/rust-lang/crates.io-index" 1569 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 1570 | dependencies = [ 1571 | "libc", 1572 | "windows-sys 0.52.0", 1573 | ] 1574 | 1575 | [[package]] 1576 | name = "static_assertions" 1577 | version = "1.1.0" 1578 | source = "registry+https://github.com/rust-lang/crates.io-index" 1579 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1580 | 1581 | [[package]] 1582 | name = "syn" 1583 | version = "1.0.109" 1584 | source = "registry+https://github.com/rust-lang/crates.io-index" 1585 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1586 | dependencies = [ 1587 | "proc-macro2", 1588 | "quote", 1589 | "unicode-ident", 1590 | ] 1591 | 1592 | [[package]] 1593 | name = "syn" 1594 | version = "2.0.70" 1595 | source = "registry+https://github.com/rust-lang/crates.io-index" 1596 | checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" 1597 | dependencies = [ 1598 | "proc-macro2", 1599 | "quote", 1600 | "unicode-ident", 1601 | ] 1602 | 1603 | [[package]] 1604 | name = "sync_wrapper" 1605 | version = "0.1.2" 1606 | source = "registry+https://github.com/rust-lang/crates.io-index" 1607 | checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" 1608 | 1609 | [[package]] 1610 | name = "tempfile" 1611 | version = "3.10.1" 1612 | source = "registry+https://github.com/rust-lang/crates.io-index" 1613 | checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" 1614 | dependencies = [ 1615 | "cfg-if", 1616 | "fastrand", 1617 | "rustix", 1618 | "windows-sys 0.52.0", 1619 | ] 1620 | 1621 | [[package]] 1622 | name = "thiserror" 1623 | version = "1.0.61" 1624 | source = "registry+https://github.com/rust-lang/crates.io-index" 1625 | checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" 1626 | dependencies = [ 1627 | "thiserror-impl", 1628 | ] 1629 | 1630 | [[package]] 1631 | name = "thiserror-impl" 1632 | version = "1.0.61" 1633 | source = "registry+https://github.com/rust-lang/crates.io-index" 1634 | checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" 1635 | dependencies = [ 1636 | "proc-macro2", 1637 | "quote", 1638 | "syn 2.0.70", 1639 | ] 1640 | 1641 | [[package]] 1642 | name = "tiny-keccak" 1643 | version = "2.0.2" 1644 | source = "registry+https://github.com/rust-lang/crates.io-index" 1645 | checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" 1646 | dependencies = [ 1647 | "crunchy", 1648 | ] 1649 | 1650 | [[package]] 1651 | name = "tokio" 1652 | version = "1.38.0" 1653 | source = "registry+https://github.com/rust-lang/crates.io-index" 1654 | checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" 1655 | dependencies = [ 1656 | "backtrace", 1657 | "bytes", 1658 | "libc", 1659 | "mio", 1660 | "num_cpus", 1661 | "parking_lot", 1662 | "pin-project-lite", 1663 | "signal-hook-registry", 1664 | "socket2", 1665 | "tokio-macros", 1666 | "windows-sys 0.48.0", 1667 | ] 1668 | 1669 | [[package]] 1670 | name = "tokio-io-timeout" 1671 | version = "1.2.0" 1672 | source = "registry+https://github.com/rust-lang/crates.io-index" 1673 | checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" 1674 | dependencies = [ 1675 | "pin-project-lite", 1676 | "tokio", 1677 | ] 1678 | 1679 | [[package]] 1680 | name = "tokio-macros" 1681 | version = "2.3.0" 1682 | source = "registry+https://github.com/rust-lang/crates.io-index" 1683 | checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" 1684 | dependencies = [ 1685 | "proc-macro2", 1686 | "quote", 1687 | "syn 2.0.70", 1688 | ] 1689 | 1690 | [[package]] 1691 | name = "tokio-stream" 1692 | version = "0.1.15" 1693 | source = "registry+https://github.com/rust-lang/crates.io-index" 1694 | checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" 1695 | dependencies = [ 1696 | "futures-core", 1697 | "pin-project-lite", 1698 | "tokio", 1699 | ] 1700 | 1701 | [[package]] 1702 | name = "tokio-util" 1703 | version = "0.7.11" 1704 | source = "registry+https://github.com/rust-lang/crates.io-index" 1705 | checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" 1706 | dependencies = [ 1707 | "bytes", 1708 | "futures-core", 1709 | "futures-sink", 1710 | "pin-project-lite", 1711 | "tokio", 1712 | ] 1713 | 1714 | [[package]] 1715 | name = "tonic" 1716 | version = "0.8.3" 1717 | source = "registry+https://github.com/rust-lang/crates.io-index" 1718 | checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" 1719 | dependencies = [ 1720 | "async-stream", 1721 | "async-trait", 1722 | "axum", 1723 | "base64 0.13.1", 1724 | "bytes", 1725 | "futures-core", 1726 | "futures-util", 1727 | "h2", 1728 | "http", 1729 | "http-body", 1730 | "hyper", 1731 | "hyper-timeout", 1732 | "percent-encoding", 1733 | "pin-project", 1734 | "prost", 1735 | "prost-derive", 1736 | "tokio", 1737 | "tokio-stream", 1738 | "tokio-util", 1739 | "tower", 1740 | "tower-layer", 1741 | "tower-service", 1742 | "tracing", 1743 | "tracing-futures", 1744 | ] 1745 | 1746 | [[package]] 1747 | name = "tonic-build" 1748 | version = "0.8.4" 1749 | source = "registry+https://github.com/rust-lang/crates.io-index" 1750 | checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" 1751 | dependencies = [ 1752 | "prettyplease", 1753 | "proc-macro2", 1754 | "prost-build", 1755 | "quote", 1756 | "syn 1.0.109", 1757 | ] 1758 | 1759 | [[package]] 1760 | name = "tower" 1761 | version = "0.4.13" 1762 | source = "registry+https://github.com/rust-lang/crates.io-index" 1763 | checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" 1764 | dependencies = [ 1765 | "futures-core", 1766 | "futures-util", 1767 | "indexmap 1.9.3", 1768 | "pin-project", 1769 | "pin-project-lite", 1770 | "rand", 1771 | "slab", 1772 | "tokio", 1773 | "tokio-util", 1774 | "tower-layer", 1775 | "tower-service", 1776 | "tracing", 1777 | ] 1778 | 1779 | [[package]] 1780 | name = "tower-layer" 1781 | version = "0.3.2" 1782 | source = "registry+https://github.com/rust-lang/crates.io-index" 1783 | checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" 1784 | 1785 | [[package]] 1786 | name = "tower-service" 1787 | version = "0.3.2" 1788 | source = "registry+https://github.com/rust-lang/crates.io-index" 1789 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 1790 | 1791 | [[package]] 1792 | name = "tracing" 1793 | version = "0.1.40" 1794 | source = "registry+https://github.com/rust-lang/crates.io-index" 1795 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 1796 | dependencies = [ 1797 | "pin-project-lite", 1798 | "tracing-attributes", 1799 | "tracing-core", 1800 | ] 1801 | 1802 | [[package]] 1803 | name = "tracing-attributes" 1804 | version = "0.1.27" 1805 | source = "registry+https://github.com/rust-lang/crates.io-index" 1806 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 1807 | dependencies = [ 1808 | "proc-macro2", 1809 | "quote", 1810 | "syn 2.0.70", 1811 | ] 1812 | 1813 | [[package]] 1814 | name = "tracing-core" 1815 | version = "0.1.32" 1816 | source = "registry+https://github.com/rust-lang/crates.io-index" 1817 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 1818 | dependencies = [ 1819 | "once_cell", 1820 | ] 1821 | 1822 | [[package]] 1823 | name = "tracing-futures" 1824 | version = "0.2.5" 1825 | source = "registry+https://github.com/rust-lang/crates.io-index" 1826 | checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" 1827 | dependencies = [ 1828 | "pin-project", 1829 | "tracing", 1830 | ] 1831 | 1832 | [[package]] 1833 | name = "try-lock" 1834 | version = "0.2.5" 1835 | source = "registry+https://github.com/rust-lang/crates.io-index" 1836 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1837 | 1838 | [[package]] 1839 | name = "unicode-ident" 1840 | version = "1.0.12" 1841 | source = "registry+https://github.com/rust-lang/crates.io-index" 1842 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 1843 | 1844 | [[package]] 1845 | name = "version_check" 1846 | version = "0.9.4" 1847 | source = "registry+https://github.com/rust-lang/crates.io-index" 1848 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1849 | 1850 | [[package]] 1851 | name = "walkdir" 1852 | version = "2.5.0" 1853 | source = "registry+https://github.com/rust-lang/crates.io-index" 1854 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 1855 | dependencies = [ 1856 | "same-file", 1857 | "winapi-util", 1858 | ] 1859 | 1860 | [[package]] 1861 | name = "want" 1862 | version = "0.3.1" 1863 | source = "registry+https://github.com/rust-lang/crates.io-index" 1864 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1865 | dependencies = [ 1866 | "try-lock", 1867 | ] 1868 | 1869 | [[package]] 1870 | name = "wasi" 1871 | version = "0.11.0+wasi-snapshot-preview1" 1872 | source = "registry+https://github.com/rust-lang/crates.io-index" 1873 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1874 | 1875 | [[package]] 1876 | name = "wasm-bindgen" 1877 | version = "0.2.92" 1878 | source = "registry+https://github.com/rust-lang/crates.io-index" 1879 | checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" 1880 | dependencies = [ 1881 | "cfg-if", 1882 | "wasm-bindgen-macro", 1883 | ] 1884 | 1885 | [[package]] 1886 | name = "wasm-bindgen-backend" 1887 | version = "0.2.92" 1888 | source = "registry+https://github.com/rust-lang/crates.io-index" 1889 | checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" 1890 | dependencies = [ 1891 | "bumpalo", 1892 | "log", 1893 | "once_cell", 1894 | "proc-macro2", 1895 | "quote", 1896 | "syn 2.0.70", 1897 | "wasm-bindgen-shared", 1898 | ] 1899 | 1900 | [[package]] 1901 | name = "wasm-bindgen-macro" 1902 | version = "0.2.92" 1903 | source = "registry+https://github.com/rust-lang/crates.io-index" 1904 | checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" 1905 | dependencies = [ 1906 | "quote", 1907 | "wasm-bindgen-macro-support", 1908 | ] 1909 | 1910 | [[package]] 1911 | name = "wasm-bindgen-macro-support" 1912 | version = "0.2.92" 1913 | source = "registry+https://github.com/rust-lang/crates.io-index" 1914 | checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" 1915 | dependencies = [ 1916 | "proc-macro2", 1917 | "quote", 1918 | "syn 2.0.70", 1919 | "wasm-bindgen-backend", 1920 | "wasm-bindgen-shared", 1921 | ] 1922 | 1923 | [[package]] 1924 | name = "wasm-bindgen-shared" 1925 | version = "0.2.92" 1926 | source = "registry+https://github.com/rust-lang/crates.io-index" 1927 | checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" 1928 | 1929 | [[package]] 1930 | name = "which" 1931 | version = "4.4.2" 1932 | source = "registry+https://github.com/rust-lang/crates.io-index" 1933 | checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" 1934 | dependencies = [ 1935 | "either", 1936 | "home", 1937 | "once_cell", 1938 | "rustix", 1939 | ] 1940 | 1941 | [[package]] 1942 | name = "winapi-util" 1943 | version = "0.1.8" 1944 | source = "registry+https://github.com/rust-lang/crates.io-index" 1945 | checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" 1946 | dependencies = [ 1947 | "windows-sys 0.52.0", 1948 | ] 1949 | 1950 | [[package]] 1951 | name = "windows-core" 1952 | version = "0.52.0" 1953 | source = "registry+https://github.com/rust-lang/crates.io-index" 1954 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 1955 | dependencies = [ 1956 | "windows-targets 0.52.6", 1957 | ] 1958 | 1959 | [[package]] 1960 | name = "windows-sys" 1961 | version = "0.48.0" 1962 | source = "registry+https://github.com/rust-lang/crates.io-index" 1963 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1964 | dependencies = [ 1965 | "windows-targets 0.48.5", 1966 | ] 1967 | 1968 | [[package]] 1969 | name = "windows-sys" 1970 | version = "0.52.0" 1971 | source = "registry+https://github.com/rust-lang/crates.io-index" 1972 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1973 | dependencies = [ 1974 | "windows-targets 0.52.6", 1975 | ] 1976 | 1977 | [[package]] 1978 | name = "windows-targets" 1979 | version = "0.48.5" 1980 | source = "registry+https://github.com/rust-lang/crates.io-index" 1981 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 1982 | dependencies = [ 1983 | "windows_aarch64_gnullvm 0.48.5", 1984 | "windows_aarch64_msvc 0.48.5", 1985 | "windows_i686_gnu 0.48.5", 1986 | "windows_i686_msvc 0.48.5", 1987 | "windows_x86_64_gnu 0.48.5", 1988 | "windows_x86_64_gnullvm 0.48.5", 1989 | "windows_x86_64_msvc 0.48.5", 1990 | ] 1991 | 1992 | [[package]] 1993 | name = "windows-targets" 1994 | version = "0.52.6" 1995 | source = "registry+https://github.com/rust-lang/crates.io-index" 1996 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1997 | dependencies = [ 1998 | "windows_aarch64_gnullvm 0.52.6", 1999 | "windows_aarch64_msvc 0.52.6", 2000 | "windows_i686_gnu 0.52.6", 2001 | "windows_i686_gnullvm", 2002 | "windows_i686_msvc 0.52.6", 2003 | "windows_x86_64_gnu 0.52.6", 2004 | "windows_x86_64_gnullvm 0.52.6", 2005 | "windows_x86_64_msvc 0.52.6", 2006 | ] 2007 | 2008 | [[package]] 2009 | name = "windows_aarch64_gnullvm" 2010 | version = "0.48.5" 2011 | source = "registry+https://github.com/rust-lang/crates.io-index" 2012 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2013 | 2014 | [[package]] 2015 | name = "windows_aarch64_gnullvm" 2016 | version = "0.52.6" 2017 | source = "registry+https://github.com/rust-lang/crates.io-index" 2018 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2019 | 2020 | [[package]] 2021 | name = "windows_aarch64_msvc" 2022 | version = "0.48.5" 2023 | source = "registry+https://github.com/rust-lang/crates.io-index" 2024 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2025 | 2026 | [[package]] 2027 | name = "windows_aarch64_msvc" 2028 | version = "0.52.6" 2029 | source = "registry+https://github.com/rust-lang/crates.io-index" 2030 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2031 | 2032 | [[package]] 2033 | name = "windows_i686_gnu" 2034 | version = "0.48.5" 2035 | source = "registry+https://github.com/rust-lang/crates.io-index" 2036 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 2037 | 2038 | [[package]] 2039 | name = "windows_i686_gnu" 2040 | version = "0.52.6" 2041 | source = "registry+https://github.com/rust-lang/crates.io-index" 2042 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2043 | 2044 | [[package]] 2045 | name = "windows_i686_gnullvm" 2046 | version = "0.52.6" 2047 | source = "registry+https://github.com/rust-lang/crates.io-index" 2048 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2049 | 2050 | [[package]] 2051 | name = "windows_i686_msvc" 2052 | version = "0.48.5" 2053 | source = "registry+https://github.com/rust-lang/crates.io-index" 2054 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2055 | 2056 | [[package]] 2057 | name = "windows_i686_msvc" 2058 | version = "0.52.6" 2059 | source = "registry+https://github.com/rust-lang/crates.io-index" 2060 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2061 | 2062 | [[package]] 2063 | name = "windows_x86_64_gnu" 2064 | version = "0.48.5" 2065 | source = "registry+https://github.com/rust-lang/crates.io-index" 2066 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 2067 | 2068 | [[package]] 2069 | name = "windows_x86_64_gnu" 2070 | version = "0.52.6" 2071 | source = "registry+https://github.com/rust-lang/crates.io-index" 2072 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2073 | 2074 | [[package]] 2075 | name = "windows_x86_64_gnullvm" 2076 | version = "0.48.5" 2077 | source = "registry+https://github.com/rust-lang/crates.io-index" 2078 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 2079 | 2080 | [[package]] 2081 | name = "windows_x86_64_gnullvm" 2082 | version = "0.52.6" 2083 | source = "registry+https://github.com/rust-lang/crates.io-index" 2084 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2085 | 2086 | [[package]] 2087 | name = "windows_x86_64_msvc" 2088 | version = "0.48.5" 2089 | source = "registry+https://github.com/rust-lang/crates.io-index" 2090 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2091 | 2092 | [[package]] 2093 | name = "windows_x86_64_msvc" 2094 | version = "0.52.6" 2095 | source = "registry+https://github.com/rust-lang/crates.io-index" 2096 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2097 | 2098 | [[package]] 2099 | name = "zerocopy" 2100 | version = "0.7.35" 2101 | source = "registry+https://github.com/rust-lang/crates.io-index" 2102 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 2103 | dependencies = [ 2104 | "zerocopy-derive", 2105 | ] 2106 | 2107 | [[package]] 2108 | name = "zerocopy-derive" 2109 | version = "0.7.35" 2110 | source = "registry+https://github.com/rust-lang/crates.io-index" 2111 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 2112 | dependencies = [ 2113 | "proc-macro2", 2114 | "quote", 2115 | "syn 2.0.70", 2116 | ] 2117 | 2118 | [[package]] 2119 | name = "zstd" 2120 | version = "0.12.4" 2121 | source = "registry+https://github.com/rust-lang/crates.io-index" 2122 | checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" 2123 | dependencies = [ 2124 | "zstd-safe", 2125 | ] 2126 | 2127 | [[package]] 2128 | name = "zstd-safe" 2129 | version = "6.0.6" 2130 | source = "registry+https://github.com/rust-lang/crates.io-index" 2131 | checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" 2132 | dependencies = [ 2133 | "libc", 2134 | "zstd-sys", 2135 | ] 2136 | 2137 | [[package]] 2138 | name = "zstd-sys" 2139 | version = "2.0.12+zstd.1.5.6" 2140 | source = "registry+https://github.com/rust-lang/crates.io-index" 2141 | checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" 2142 | dependencies = [ 2143 | "cc", 2144 | "pkg-config", 2145 | ] 2146 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 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 | [package] 19 | name = "horaedb-client" 20 | version = "2.0.0" 21 | authors = ["HoraeDB Authors"] 22 | edition = "2021" 23 | repository = "https://github.com/apache/horaedb-client-rs" 24 | license = "Apache-2.0" 25 | description = "Apache HoraeDB (Incubating) Rust Client." 26 | readme = "README.md" 27 | 28 | [dependencies] 29 | anyhow = "1.0.83" 30 | arrow = "38.0.0" 31 | async-trait = "0.1.72" 32 | base64 = "0.22.1" 33 | dashmap = "5.3.4" 34 | futures = "0.3" 35 | horaedbproto = "1.0.23" 36 | paste = "1.0" 37 | thiserror = "1.0.38" 38 | tokio = "1.29" 39 | tonic = "0.8.1" 40 | zstd = { version = "0.12", default-features = false } 41 | 42 | [dev-dependencies] 43 | chrono = "0.4" 44 | tokio = { version = "1.15", features = ["full"] } 45 | 46 | [lib] 47 | name = "horaedb_client" 48 | -------------------------------------------------------------------------------- /DEPENDENCIES.tsv: -------------------------------------------------------------------------------- 1 | crate 0BSD Apache-2.0 Apache-2.0 WITH LLVM-exception BSD-3-Clause BSL-1.0 CC0-1.0 MIT Unicode-DFS-2016 Unlicense Zlib 2 | addr2line@0.21.0 X X 3 | adler@1.0.2 X X X 4 | ahash@0.8.3 X X 5 | aho-corasick@1.0.1 X X 6 | android_system_properties@0.1.5 X X 7 | anyhow@1.0.83 X X 8 | arrow@38.0.0 X 9 | arrow-arith@38.0.0 X 10 | arrow-array@38.0.0 X 11 | arrow-buffer@38.0.0 X 12 | arrow-cast@38.0.0 X 13 | arrow-csv@38.0.0 X 14 | arrow-data@38.0.0 X 15 | arrow-ipc@38.0.0 X 16 | arrow-json@38.0.0 X 17 | arrow-ord@38.0.0 X 18 | arrow-row@38.0.0 X 19 | arrow-schema@38.0.0 X 20 | arrow-select@38.0.0 X 21 | arrow-string@38.0.0 X 22 | async-stream@0.3.5 X 23 | async-stream-impl@0.3.5 X 24 | async-trait@0.1.74 X X 25 | autocfg@1.1.0 X X 26 | axum@0.6.18 X 27 | axum-core@0.3.4 X 28 | backtrace@0.3.69 X X 29 | base64@0.13.1 X X 30 | base64@0.22.1 X X 31 | bitflags@1.3.2 X X 32 | bumpalo@3.13.0 X X 33 | bytes@1.6.0 X 34 | cc@1.0.79 X X 35 | cfg-if@1.0.0 X X 36 | chrono@0.4.24 X X 37 | const-random@0.1.15 X X 38 | const-random-macro@0.1.15 X X 39 | core-foundation-sys@0.8.4 X X 40 | crunchy@0.2.2 X 41 | csv@1.2.1 X X 42 | csv-core@0.1.10 X X 43 | dashmap@5.4.0 X 44 | either@1.8.1 X X 45 | errno@0.3.1 X X 46 | errno-dragonfly@0.1.2 X 47 | fastrand@1.9.0 X X 48 | fixedbitset@0.4.2 X X 49 | flatbuffers@23.1.21 X 50 | fnv@1.0.7 X X 51 | futures@0.3.28 X X 52 | futures-channel@0.3.28 X X 53 | futures-core@0.3.28 X X 54 | futures-executor@0.3.28 X X 55 | futures-io@0.3.28 X X 56 | futures-macro@0.3.28 X X 57 | futures-sink@0.3.28 X X 58 | futures-task@0.3.28 X X 59 | futures-util@0.3.28 X X 60 | getrandom@0.2.9 X X 61 | gimli@0.28.0 X X 62 | h2@0.3.19 X 63 | half@2.2.1 X X 64 | hashbrown@0.12.3 X X 65 | hashbrown@0.13.2 X X 66 | heck@0.4.1 X X 67 | hermit-abi@0.2.6 X X 68 | hermit-abi@0.3.1 X X 69 | horaedb-client@2.0.0 X 70 | horaedbproto@1.0.23 X 71 | http@0.2.9 X X 72 | http-body@0.4.5 X 73 | httparse@1.8.0 X X 74 | httpdate@1.0.2 X X 75 | hyper@0.14.26 X 76 | hyper-timeout@0.4.1 X X 77 | iana-time-zone@0.1.56 X X 78 | iana-time-zone-haiku@0.1.2 X X 79 | indexmap@1.9.3 X X 80 | instant@0.1.12 X 81 | io-lifetimes@1.0.10 X X X 82 | itertools@0.10.5 X X 83 | itoa@1.0.6 X X 84 | jobserver@0.1.26 X X 85 | js-sys@0.3.63 X X 86 | lazy_static@1.4.0 X X 87 | lexical-core@0.8.5 X X 88 | lexical-parse-float@0.8.5 X X 89 | lexical-parse-integer@0.8.6 X X 90 | lexical-util@0.8.5 X X 91 | lexical-write-float@0.8.5 X X 92 | lexical-write-integer@0.8.5 X X 93 | libc@0.2.150 X X 94 | libm@0.2.7 X X 95 | linux-raw-sys@0.3.8 X X X 96 | lock_api@0.4.9 X X 97 | log@0.4.17 X X 98 | matchit@0.7.0 X 99 | memchr@2.5.0 X X 100 | mime@0.3.17 X X 101 | miniz_oxide@0.7.1 X X X 102 | mio@0.8.9 X 103 | multimap@0.8.3 X X 104 | num@0.4.0 X X 105 | num-bigint@0.4.3 X X 106 | num-complex@0.4.3 X X 107 | num-integer@0.1.45 X X 108 | num-iter@0.1.43 X X 109 | num-rational@0.4.1 X X 110 | num-traits@0.2.15 X X 111 | num_cpus@1.15.0 X X 112 | object@0.32.1 X X 113 | once_cell@1.17.1 X X 114 | parking_lot@0.12.1 X X 115 | parking_lot_core@0.9.7 X X 116 | paste@1.0.12 X X 117 | percent-encoding@2.2.0 X X 118 | petgraph@0.6.3 X X 119 | pin-project@1.1.0 X X 120 | pin-project-internal@1.1.0 X X 121 | pin-project-lite@0.2.13 X X 122 | pin-utils@0.1.0 X X 123 | pkg-config@0.3.27 X X 124 | ppv-lite86@0.2.17 X X 125 | prettyplease@0.1.25 X X 126 | proc-macro-hack@0.5.20+deprecated X X 127 | proc-macro2@1.0.69 X X 128 | prost@0.11.9 X 129 | prost-build@0.11.9 X 130 | prost-derive@0.11.9 X 131 | prost-types@0.11.9 X 132 | protoc-bin-vendored@3.0.0 X 133 | protoc-bin-vendored-linux-aarch_64@3.0.0 X 134 | protoc-bin-vendored-linux-ppcle_64@3.0.0 X 135 | protoc-bin-vendored-linux-x86_32@3.0.0 X 136 | protoc-bin-vendored-linux-x86_64@3.0.0 X 137 | protoc-bin-vendored-macos-x86_64@3.0.0 X 138 | protoc-bin-vendored-win32@3.0.0 X 139 | quote@1.0.33 X X 140 | rand@0.8.5 X X 141 | rand_chacha@0.3.1 X X 142 | rand_core@0.6.4 X X 143 | redox_syscall@0.2.16 X 144 | redox_syscall@0.3.5 X 145 | regex@1.8.2 X X 146 | regex-syntax@0.6.29 X X 147 | regex-syntax@0.7.2 X X 148 | rustc-demangle@0.1.23 X X 149 | rustc_version@0.4.0 X X 150 | rustix@0.37.19 X X X 151 | rustversion@1.0.12 X X 152 | ryu@1.0.13 X X 153 | same-file@1.0.6 X X 154 | scopeguard@1.1.0 X X 155 | semver@1.0.17 X X 156 | serde@1.0.163 X X 157 | serde_json@1.0.96 X X 158 | signal-hook-registry@1.4.1 X X 159 | slab@0.4.8 X 160 | smallvec@1.10.0 X X 161 | socket2@0.4.9 X X 162 | socket2@0.5.5 X X 163 | static_assertions@1.1.0 X X 164 | syn@1.0.109 X X 165 | syn@2.0.39 X X 166 | sync_wrapper@0.1.2 X 167 | tempfile@3.5.0 X X 168 | thiserror@1.0.40 X X 169 | thiserror-impl@1.0.40 X X 170 | time@0.1.45 X X 171 | tiny-keccak@2.0.2 X 172 | tokio@1.34.0 X 173 | tokio-io-timeout@1.2.0 X X 174 | tokio-macros@2.2.0 X 175 | tokio-stream@0.1.14 X 176 | tokio-util@0.7.8 X 177 | tonic@0.8.3 X 178 | tonic-build@0.8.4 X 179 | tower@0.4.13 X 180 | tower-layer@0.3.2 X 181 | tower-service@0.3.2 X 182 | tracing@0.1.37 X 183 | tracing-attributes@0.1.24 X 184 | tracing-core@0.1.31 X 185 | tracing-futures@0.2.5 X 186 | try-lock@0.2.4 X 187 | unicode-ident@1.0.8 X X X 188 | version_check@0.9.4 X X 189 | walkdir@2.3.3 X X 190 | want@0.3.0 X 191 | wasi@0.10.0+wasi-snapshot-preview1 X X X 192 | wasi@0.11.0+wasi-snapshot-preview1 X X X 193 | wasm-bindgen@0.2.86 X X 194 | wasm-bindgen-backend@0.2.86 X X 195 | wasm-bindgen-macro@0.2.86 X X 196 | wasm-bindgen-macro-support@0.2.86 X X 197 | wasm-bindgen-shared@0.2.86 X X 198 | which@4.4.0 X 199 | winapi@0.3.9 X X 200 | winapi-i686-pc-windows-gnu@0.4.0 X X 201 | winapi-util@0.1.5 X X 202 | winapi-x86_64-pc-windows-gnu@0.4.0 X X 203 | windows@0.48.0 X X 204 | windows-sys@0.45.0 X X 205 | windows-sys@0.48.0 X X 206 | windows-targets@0.42.2 X X 207 | windows-targets@0.48.0 X X 208 | windows_aarch64_gnullvm@0.42.2 X X 209 | windows_aarch64_gnullvm@0.48.0 X X 210 | windows_aarch64_msvc@0.42.2 X X 211 | windows_aarch64_msvc@0.48.0 X X 212 | windows_i686_gnu@0.42.2 X X 213 | windows_i686_gnu@0.48.0 X X 214 | windows_i686_msvc@0.42.2 X X 215 | windows_i686_msvc@0.48.0 X X 216 | windows_x86_64_gnu@0.42.2 X X 217 | windows_x86_64_gnu@0.48.0 X X 218 | windows_x86_64_gnullvm@0.42.2 X X 219 | windows_x86_64_gnullvm@0.48.0 X X 220 | windows_x86_64_msvc@0.42.2 X X 221 | windows_x86_64_msvc@0.48.0 X X 222 | zstd@0.12.3+zstd.1.5.2 X 223 | zstd-safe@6.0.5+zstd.1.5.4 X X 224 | zstd-sys@2.0.8+zstd.1.5.5 X X 225 | -------------------------------------------------------------------------------- /DISCLAIMER: -------------------------------------------------------------------------------- 1 | Apache HoraeDB (incubating) is an effort undergoing incubation at the Apache 2 | Software Foundation (ASF), sponsored by the Apache Incubator PMC. 3 | 4 | Incubation is required of all newly accepted projects until a further review 5 | indicates that the infrastructure, communications, and decision making process 6 | have stabilized in a manner consistent with other successful ASF projects. 7 | 8 | While incubation status is not necessarily a reflection of the completeness 9 | or stability of the code, it does indicate that the project has yet to be 10 | fully endorsed by the ASF. 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL = /bin/bash 2 | 3 | DIR=$(shell pwd) 4 | 5 | fmt: 6 | cd $(DIR); cargo fmt --all --check 7 | 8 | clippy: 9 | cd $(DIR); cargo clippy --tests --all-features --all-targets --workspace -- -D warnings 10 | 11 | test: 12 | cd $(DIR); cargo test --workspace 13 | 14 | check-toml: 15 | cd $(DIR); cargo sort --workspace --check 16 | 17 | dry-run: 18 | cd $(DIR); cargo publish --dry-run --registry crates-io 19 | 20 | list-deps: 21 | cd $(DIR); cargo deny list -f tsv -l crate > DEPENDENCIES.tsv 22 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Apache HoraeDB (Incubating) 2 | Copyright 2024 The Apache Software Foundation 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org/). -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apache HoraeDB (Incubating) Rust Client 2 | 3 | ![License](https://img.shields.io/badge/license-Apache--2.0-green.svg) 4 | [![Crates.io](https://img.shields.io/crates/v/horaedb-client.svg)](https://crates.io/crates/horaedb-client) 5 | 6 | ## Introduction 7 | 8 | Rust client for [Apache HoraeDB (Incubating)](https://github.com/apache/horaedb). 9 | 10 | > [!IMPORTANT] 11 | > Apache HoraeDB (incubating) is an effort undergoing incubation at the Apache 12 | > Software Foundation (ASF), sponsored by the Apache Incubator PMC. 13 | > 14 | > Please read the [DISCLAIMER](DISCLAIMER) and a full explanation of ["incubating"](https://incubator.apache.org/policy/incubation.html). 15 | 16 | ## Support features 17 | 18 | - [x] Query 19 | - [x] Write 20 | 21 | ## Contributing 22 | 23 | Any contribution is welcome! 24 | 25 | Read our [Contributing Guide](https://github.com/apache/horaedb/blob/main/CONTRIBUTING.md) and make your first contribution! 26 | 27 | ## License 28 | 29 | Under [Apache License 2.0](LICENSE). 30 | -------------------------------------------------------------------------------- /examples/read_write.rs: -------------------------------------------------------------------------------- 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 | use std::sync::Arc; 19 | 20 | use chrono::Local; 21 | use horaedb_client::{ 22 | db_client::{Builder, DbClient, Mode}, 23 | model::{ 24 | sql_query::{display::CsvFormatter, Request as SqlQueryRequest}, 25 | value::Value, 26 | write::{point::PointBuilder, Request as WriteRequest}, 27 | }, 28 | Authorization, RpcContext, 29 | }; 30 | 31 | async fn create_table(client: &Arc, rpc_ctx: &RpcContext) { 32 | let create_table_sql = r#"CREATE TABLE IF NOT EXISTS horaedb ( 33 | str_tag string TAG, 34 | int_tag int32 TAG, 35 | var_tag varbinary TAG, 36 | str_field string, 37 | int_field int32, 38 | bin_field varbinary, 39 | t timestamp NOT NULL, 40 | TIMESTAMP KEY(t)) ENGINE=Analytic with 41 | (enable_ttl='false')"#; 42 | let req = SqlQueryRequest { 43 | tables: vec!["horaedb".to_string()], 44 | sql: create_table_sql.to_string(), 45 | }; 46 | let resp = client 47 | .sql_query(rpc_ctx, &req) 48 | .await 49 | .expect("Should succeed to create table"); 50 | println!("Create table result:{resp:?}"); 51 | } 52 | 53 | async fn drop_table(client: &Arc, rpc_ctx: &RpcContext) { 54 | let drop_table_sql = "DROP TABLE horaedb"; 55 | let req = SqlQueryRequest { 56 | tables: vec!["horaedb".to_string()], 57 | sql: drop_table_sql.to_string(), 58 | }; 59 | let _resp = client 60 | .sql_query(rpc_ctx, &req) 61 | .await 62 | .expect("Should succeed to drop table"); 63 | println!("Drop table success!"); 64 | } 65 | 66 | async fn write(client: &Arc, rpc_ctx: &RpcContext) { 67 | let ts1 = Local::now().timestamp_millis(); 68 | let mut write_req = WriteRequest::default(); 69 | let test_table = "horaedb"; 70 | 71 | let points = vec![ 72 | PointBuilder::new(test_table) 73 | .timestamp(ts1) 74 | .tag("str_tag", Value::String("tag_val1".to_string())) 75 | .tag("int_tag", Value::Int32(42)) 76 | .tag("var_tag", Value::Varbinary(b"tag_bin_val1".to_vec())) 77 | .field("str_field", Value::String("field_val1".to_string())) 78 | .field("int_field".to_string(), Value::Int32(42)) 79 | .field("bin_field", Value::Varbinary(b"field_bin_val1".to_vec())) 80 | .build() 81 | .unwrap(), 82 | PointBuilder::new(test_table) 83 | .timestamp(ts1 + 40) 84 | .tag("str_tag", Value::String("tag_val2".to_string())) 85 | .tag("int_tag", Value::Int32(43)) 86 | .tag("var_tag", Value::Varbinary(b"tag_bin_val2".to_vec())) 87 | .field("str_field", Value::String("field_val2".to_string())) 88 | .field("bin_field", Value::Varbinary(b"field_bin_val2".to_vec())) 89 | .build() 90 | .unwrap(), 91 | ]; 92 | 93 | write_req.add_points(points); 94 | 95 | let res = client 96 | .write(rpc_ctx, &write_req) 97 | .await 98 | .expect("Should success to write"); 99 | println!("{res:?}"); 100 | } 101 | 102 | async fn sql_query(client: &Arc, rpc_ctx: &RpcContext) { 103 | let req = SqlQueryRequest { 104 | tables: vec!["horaedb".to_string()], 105 | sql: "select * from horaedb;".to_string(), 106 | }; 107 | let resp = client 108 | .sql_query(rpc_ctx, &req) 109 | .await 110 | .expect("Should succeed to query"); 111 | let csv_formatter = CsvFormatter { resp }; 112 | println!("Rows in the resp:\n{csv_formatter}"); 113 | } 114 | 115 | #[tokio::main] 116 | async fn main() { 117 | // you should ensure horaedb is running, and grpc port is set to 8831 118 | let client = Builder::new("127.0.0.1:8831".to_string(), Mode::Direct) 119 | // Set authorization if needed 120 | .authorization(Authorization { 121 | username: "user".to_string(), 122 | password: "pass".to_string(), 123 | }) 124 | .build(); 125 | let rpc_ctx = RpcContext::default().database("public".to_string()); 126 | 127 | println!("------------------------------------------------------------------"); 128 | println!("### create table:"); 129 | create_table(&client, &rpc_ctx).await; 130 | println!("------------------------------------------------------------------"); 131 | 132 | println!("### write:"); 133 | write(&client, &rpc_ctx).await; 134 | println!("------------------------------------------------------------------"); 135 | 136 | println!("### read:"); 137 | sql_query(&client, &rpc_ctx).await; 138 | println!("------------------------------------------------------------------"); 139 | 140 | println!("### drop table:"); 141 | drop_table(&client, &rpc_ctx).await; 142 | println!("------------------------------------------------------------------"); 143 | } 144 | -------------------------------------------------------------------------------- /licenserc.toml: -------------------------------------------------------------------------------- 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 | headerPath = "Apache-2.0-ASF.txt" 19 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 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 | [toolchain] 19 | channel = "stable" 20 | components = ["cargo", "rustfmt", "clippy", "rust-analyzer"] 21 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 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 | # https://github.com/rust-lang/rustfmt/blob/master/Configurations.md 19 | 20 | # Break comments to fit on the line 21 | wrap_comments = true 22 | # Merge multiple imports into a single nested import. 23 | imports_granularity = "Crate" 24 | # Format code snippet included in doc comments. 25 | format_code_in_doc_comments = true 26 | # Reorder impl items. type and const are put first, then macros and methods. 27 | reorder_impl_items = true 28 | # Discard existing import groups, and create three groups for std, external crates, crates 29 | group_imports = "StdExternalCrate" 30 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 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 | use std::time::Duration; 19 | 20 | /// Config for the underlying grpc client 21 | #[derive(Debug, Clone)] 22 | pub struct RpcConfig { 23 | /// Thread num used by the grpc client. 24 | /// 25 | /// The number of cpu cores will be used if not set. 26 | pub thread_num: Option, 27 | /// The max length of the message sent to server. 28 | /// 29 | /// -1 means unlimited, and the default value is 20MB. 30 | pub max_send_msg_len: i32, 31 | /// The max length of the message received from server. 32 | /// 33 | /// -1 means unlimited, and the default value is 1GB. 34 | pub max_recv_msg_len: i32, 35 | /// The interval for htt2 ping frames. 36 | /// 37 | /// Default value is 600s. 38 | pub keep_alive_interval: Duration, 39 | /// Timeout for http2 ping frame acknowledgement. 40 | /// 41 | /// If the ping is not acknowledged within the timeout, the connection will 42 | /// be closed, and default value is 3s. 43 | pub keep_alive_timeout: Duration, 44 | /// Enables http2_keep_alive or not. 45 | /// 46 | /// It is enabled by default. 47 | pub keep_alive_while_idle: bool, 48 | /// Timeout for write operation. 49 | /// 50 | /// Default value is 5s. 51 | pub default_write_timeout: Duration, 52 | /// Timeout for sql_query operation. 53 | /// 54 | /// Default value is 60s. 55 | pub default_sql_query_timeout: Duration, 56 | /// Timeout for connection. 57 | /// 58 | /// Default value is 3s. 59 | pub connect_timeout: Duration, 60 | } 61 | 62 | #[derive(Debug, Clone)] 63 | pub struct Authorization { 64 | pub username: String, 65 | pub password: String, 66 | } 67 | 68 | impl Default for RpcConfig { 69 | fn default() -> Self { 70 | Self { 71 | thread_num: None, 72 | // 20MB 73 | max_send_msg_len: 20 * (1 << 20), 74 | // 1GB 75 | max_recv_msg_len: 1 << 30, 76 | keep_alive_interval: Duration::from_secs(60 * 10), 77 | keep_alive_timeout: Duration::from_secs(3), 78 | keep_alive_while_idle: true, 79 | default_write_timeout: Duration::from_secs(5), 80 | default_sql_query_timeout: Duration::from_secs(60), 81 | connect_timeout: Duration::from_secs(3), 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/db_client/builder.rs: -------------------------------------------------------------------------------- 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 | use std::sync::Arc; 19 | 20 | use crate::{ 21 | db_client::{raw::RawImpl, route_based::RouteBasedImpl, DbClient}, 22 | rpc_client::RpcClientImplFactory, 23 | Authorization, RpcConfig, 24 | }; 25 | 26 | /// Access mode to HoraeDB server(s). 27 | #[derive(Debug, Clone)] 28 | pub enum Mode { 29 | /// When accessing HoraeDB cluster by `Direct` mode, the requests will be 30 | /// sent directly to the right HoraeDB instance determined by routing 31 | /// information. 32 | Direct, 33 | /// When accessing HoraeDB by `Proxy` mode, the requests are just sent to 34 | /// any one HoraeDB instance, which takes the responsibilities for 35 | /// forwarding the requests. 36 | Proxy, 37 | } 38 | 39 | /// The builder for building [`DbClient`](DbClient). 40 | #[derive(Debug, Clone)] 41 | pub struct Builder { 42 | mode: Mode, 43 | endpoint: String, 44 | default_database: Option, 45 | rpc_config: RpcConfig, 46 | authorization: Option, 47 | } 48 | 49 | impl Builder { 50 | // We hide this detail new method for the convenience of users. 51 | pub fn new(endpoint: String, mode: Mode) -> Self { 52 | Self { 53 | mode, 54 | endpoint, 55 | rpc_config: RpcConfig::default(), 56 | default_database: None, 57 | authorization: None, 58 | } 59 | } 60 | 61 | #[inline] 62 | pub fn default_database(mut self, default_database: impl Into) -> Self { 63 | self.default_database = Some(default_database.into()); 64 | self 65 | } 66 | 67 | #[inline] 68 | pub fn rpc_config(mut self, rpc_config: RpcConfig) -> Self { 69 | self.rpc_config = rpc_config; 70 | self 71 | } 72 | 73 | #[inline] 74 | pub fn authorization(mut self, authorization: Authorization) -> Self { 75 | self.authorization = Some(authorization); 76 | self 77 | } 78 | 79 | pub fn build(self) -> Arc { 80 | let rpc_client_factory = Arc::new(RpcClientImplFactory::new( 81 | self.rpc_config, 82 | self.authorization, 83 | )); 84 | 85 | match self.mode { 86 | Mode::Direct => Arc::new(RouteBasedImpl::new( 87 | rpc_client_factory, 88 | self.endpoint, 89 | self.default_database, 90 | )), 91 | Mode::Proxy => Arc::new(RawImpl::new( 92 | rpc_client_factory, 93 | self.endpoint, 94 | self.default_database, 95 | )), 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/db_client/inner.rs: -------------------------------------------------------------------------------- 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 | use std::sync::Arc; 19 | 20 | use horaedbproto::storage; 21 | use tokio::sync::OnceCell; 22 | 23 | use crate::{ 24 | model::{ 25 | sql_query::{Request as SqlQueryRequest, Response as SqlQueryResponse}, 26 | write::{Request as WriteRequest, Response as WriteResponse, WriteTableRequestPbsBuilder}, 27 | }, 28 | rpc_client::{RpcClient, RpcClientFactory, RpcContext}, 29 | Result, 30 | }; 31 | 32 | /// Inner client for both standalone and route based modes. 33 | /// 34 | /// Now, [`InnerClient`] just wraps [`RpcClient`] simply. 35 | pub(crate) struct InnerClient { 36 | factory: Arc, 37 | endpoint: String, 38 | inner_client: OnceCell>, 39 | } 40 | 41 | impl InnerClient { 42 | pub fn new(factory: Arc, endpoint: String) -> Self { 43 | InnerClient { 44 | factory, 45 | endpoint, 46 | inner_client: OnceCell::new(), 47 | } 48 | } 49 | 50 | #[inline] 51 | async fn init(&self) -> Result> { 52 | self.factory.build(self.endpoint.clone()).await 53 | } 54 | 55 | pub async fn sql_query_internal( 56 | &self, 57 | ctx: &RpcContext, 58 | req: &SqlQueryRequest, 59 | ) -> Result { 60 | assert!(ctx.database.is_some()); 61 | 62 | let client_handle = self.inner_client.get_or_try_init(|| self.init()).await?; 63 | let req_ctx = storage::RequestContext { 64 | database: ctx.database.clone().unwrap(), 65 | }; 66 | let req_pb = storage::SqlQueryRequest { 67 | context: Some(req_ctx), 68 | tables: req.tables.clone(), 69 | sql: req.sql.clone(), 70 | }; 71 | 72 | client_handle 73 | .as_ref() 74 | .sql_query(ctx, req_pb) 75 | .await 76 | .and_then(SqlQueryResponse::try_from) 77 | } 78 | 79 | pub async fn write_internal( 80 | &self, 81 | ctx: &RpcContext, 82 | req: &WriteRequest, 83 | ) -> Result { 84 | assert!(ctx.database.is_some()); 85 | 86 | let client_handle = self.inner_client.get_or_try_init(|| self.init()).await?; 87 | let req_ctx = storage::RequestContext { 88 | database: ctx.database.clone().unwrap(), 89 | }; 90 | let write_table_request_pbs = WriteTableRequestPbsBuilder(req.clone()).build(); 91 | let req_pb = storage::WriteRequest { 92 | context: Some(req_ctx), 93 | table_requests: write_table_request_pbs, 94 | }; 95 | 96 | client_handle 97 | .write(ctx, req_pb) 98 | .await 99 | .map(|resp_pb| resp_pb.into()) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/db_client/mod.rs: -------------------------------------------------------------------------------- 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 | //! This module provides the definition and implementations of the `DbClient`. 19 | 20 | mod builder; 21 | mod inner; 22 | mod raw; 23 | mod route_based; 24 | 25 | use async_trait::async_trait; 26 | pub use builder::{Builder, Mode}; 27 | 28 | use crate::{ 29 | model::{ 30 | sql_query::{Request as SqlQueryRequest, Response as SqlQueryResponse}, 31 | write::{Request as WriteRequest, Response as WriteResponse}, 32 | }, 33 | rpc_client::RpcContext, 34 | Result, 35 | }; 36 | 37 | #[async_trait] 38 | pub trait DbClient: Send + Sync { 39 | async fn sql_query(&self, ctx: &RpcContext, req: &SqlQueryRequest) -> Result; 40 | async fn write(&self, ctx: &RpcContext, req: &WriteRequest) -> Result; 41 | } 42 | 43 | pub(crate) fn resolve_database( 44 | ctx: &RpcContext, 45 | default_database: &Option, 46 | ) -> Result { 47 | match (&ctx.database, default_database) { 48 | (Some(_), _) => Ok(ctx.clone()), 49 | (None, Some(default_database)) => Ok(RpcContext { 50 | database: Some(default_database.clone()), 51 | ..ctx.clone() 52 | }), 53 | (None, None) => Err(crate::Error::NoDatabase), 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/db_client/raw.rs: -------------------------------------------------------------------------------- 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 | use std::sync::Arc; 19 | 20 | use async_trait::async_trait; 21 | 22 | use crate::{ 23 | db_client::{inner::InnerClient, DbClient}, 24 | model::{ 25 | sql_query::{Request as SqlQueryRequest, Response as SqlQueryResponse}, 26 | write::{Request as WriteRequest, Response as WriteResponse}, 27 | }, 28 | rpc_client::{RpcClientFactory, RpcContext}, 29 | Result, 30 | }; 31 | 32 | /// Client for horaedb of standalone mode. 33 | /// 34 | /// Now, [`RawImpl`] just wraps [`InnerClient`] simply. 35 | pub struct RawImpl { 36 | inner_client: InnerClient, 37 | default_database: Option, 38 | } 39 | 40 | impl RawImpl { 41 | pub fn new(factory: Arc, endpoint: String, default_database: Option) -> Self { 42 | Self { 43 | inner_client: InnerClient::new(factory, endpoint), 44 | default_database, 45 | } 46 | } 47 | } 48 | 49 | #[async_trait] 50 | impl DbClient for RawImpl { 51 | async fn sql_query(&self, ctx: &RpcContext, req: &SqlQueryRequest) -> Result { 52 | let ctx = crate::db_client::resolve_database(ctx, &self.default_database)?; 53 | self.inner_client.sql_query_internal(&ctx, req).await 54 | } 55 | 56 | async fn write(&self, ctx: &RpcContext, req: &WriteRequest) -> Result { 57 | let ctx = crate::db_client::resolve_database(ctx, &self.default_database)?; 58 | self.inner_client.write_internal(&ctx, req).await 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/db_client/route_based.rs: -------------------------------------------------------------------------------- 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 | use std::{collections::HashMap, sync::Arc}; 19 | 20 | use async_trait::async_trait; 21 | use dashmap::DashMap; 22 | use futures::future::join_all; 23 | use tokio::sync::OnceCell; 24 | 25 | use crate::{ 26 | db_client::{inner::InnerClient, DbClient}, 27 | errors::RouteBasedWriteError, 28 | model::{ 29 | route::Endpoint, 30 | sql_query::{Request as SqlQueryRequest, Response as SqlQueryResponse}, 31 | write::{Request as WriteRequest, Response as WriteResponse}, 32 | }, 33 | router::{Router, RouterImpl}, 34 | rpc_client::{RpcClientFactory, RpcContext}, 35 | util::should_refresh, 36 | Error, Result, 37 | }; 38 | 39 | /// Client implementation for horaedb while using route based mode. 40 | pub struct RouteBasedImpl { 41 | factory: Arc, 42 | router_endpoint: String, 43 | router: OnceCell>, 44 | standalone_pool: DirectClientPool, 45 | default_database: Option, 46 | } 47 | 48 | impl RouteBasedImpl { 49 | pub fn new(factory: Arc, router_endpoint: String, default_database: Option) -> Self { 50 | Self { 51 | factory: factory.clone(), 52 | router_endpoint, 53 | router: OnceCell::new(), 54 | standalone_pool: DirectClientPool::new(factory), 55 | default_database, 56 | } 57 | } 58 | 59 | async fn init_router(&self) -> Result> { 60 | let router_client = self.factory.build(self.router_endpoint.clone()).await?; 61 | let default_endpoint: Endpoint = self.router_endpoint.parse().map_err(|e| { 62 | Error::Client(format!( 63 | "Failed to parse default endpoint:{}, err:{}", 64 | self.router_endpoint, e 65 | )) 66 | })?; 67 | Ok(Box::new(RouterImpl::new(default_endpoint, router_client))) 68 | } 69 | } 70 | 71 | #[async_trait] 72 | impl DbClient for RouteBasedImpl { 73 | async fn sql_query(&self, ctx: &RpcContext, req: &SqlQueryRequest) -> Result { 74 | if req.tables.is_empty() { 75 | return Err(Error::Unknown( 76 | "tables in query request can't be empty in route based mode".to_string(), 77 | )); 78 | } 79 | let ctx = crate::db_client::resolve_database(ctx, &self.default_database)?; 80 | 81 | let router_handle = self.router.get_or_try_init(|| self.init_router()).await?; 82 | 83 | let endpoint = match router_handle.route(&req.tables, &ctx).await { 84 | Ok(mut eps) => { 85 | if let Some(ep) = eps[0].take() { 86 | ep 87 | } else { 88 | return Err(Error::Unknown( 89 | "table doesn't have corresponding endpoint".to_string(), 90 | )); 91 | } 92 | } 93 | Err(e) => { 94 | return Err(e); 95 | } 96 | }; 97 | 98 | let client = self.standalone_pool.get_or_create(&endpoint).clone(); 99 | 100 | client.sql_query_internal(&ctx, req).await.map_err(|e| { 101 | router_handle.evict(&req.tables); 102 | e 103 | }) 104 | } 105 | 106 | async fn write(&self, ctx: &RpcContext, req: &WriteRequest) -> Result { 107 | let ctx = crate::db_client::resolve_database(ctx, &self.default_database)?; 108 | 109 | // Get tables' related endpoints(some may not exist). 110 | let should_routes: Vec<_> = req.point_groups.keys().cloned().collect(); 111 | let router_handle = self.router.get_or_try_init(|| self.init_router()).await?; 112 | let endpoints = router_handle.route(&should_routes, &ctx).await?; 113 | 114 | // Partition write entries in request according to related endpoints. 115 | let mut no_corresponding_endpoints = Vec::new(); 116 | let mut partition_by_endpoint = HashMap::new(); 117 | endpoints 118 | .into_iter() 119 | .zip(should_routes) 120 | .for_each(|(ep, m)| match ep { 121 | Some(ep) => { 122 | let write_req = partition_by_endpoint 123 | .entry(ep) 124 | .or_insert_with(WriteRequest::default); 125 | write_req.point_groups.insert( 126 | m.clone(), 127 | req.point_groups.get(m.as_str()).cloned().unwrap(), 128 | ); 129 | } 130 | None => { 131 | no_corresponding_endpoints.push(m); 132 | } 133 | }); 134 | 135 | // Get client and send. 136 | let mut write_tables = vec![Vec::new(); partition_by_endpoint.len()]; 137 | let client_req_paris: Vec<_> = partition_by_endpoint 138 | .into_iter() 139 | .enumerate() 140 | .map(|(idx, (ep, req))| { 141 | assert!(idx < write_tables.len()); 142 | write_tables[idx].extend(req.point_groups.keys().cloned()); 143 | (self.standalone_pool.get_or_create(&ep), req) 144 | }) 145 | .collect(); 146 | let mut futures = Vec::with_capacity(client_req_paris.len()); 147 | for (client, req) in client_req_paris { 148 | let ctx_clone = ctx.clone(); 149 | futures.push(async move { client.write_internal(&ctx_clone, &req).await }) 150 | } 151 | 152 | // Await rpc results and collect results. 153 | let mut tables_result_pairs: Vec<_> = join_all(futures) 154 | .await 155 | .into_iter() 156 | .zip(write_tables) 157 | .map(|(results, tables)| (tables, results)) 158 | .collect(); 159 | 160 | if !no_corresponding_endpoints.is_empty() { 161 | tables_result_pairs.push(( 162 | no_corresponding_endpoints, 163 | Err(Error::Unknown( 164 | "tables don't have corresponding endpoints".to_string(), 165 | )), 166 | )); 167 | } 168 | 169 | // Process results: 170 | // + Evict outdated endpoints. 171 | // + Merge results and return. 172 | let evicts: Vec<_> = tables_result_pairs 173 | .iter() 174 | .filter_map(|(tables, result)| { 175 | if let Err(Error::Server(server_error)) = &result { 176 | if should_refresh(server_error.code, &server_error.msg) { 177 | Some(tables.clone()) 178 | } else { 179 | None 180 | } 181 | } else { 182 | None 183 | } 184 | }) 185 | .flatten() 186 | .collect(); 187 | router_handle.evict(&evicts); 188 | 189 | let route_based_error: RouteBasedWriteError = tables_result_pairs.into(); 190 | if route_based_error.all_ok() { 191 | Ok(route_based_error.ok.1) 192 | } else { 193 | Err(Error::RouteBasedWriteError(route_based_error)) 194 | } 195 | } 196 | } 197 | 198 | /// DirectClientPool is the pool actually holding connections to data nodes. 199 | struct DirectClientPool { 200 | pool: DashMap>>, 201 | factory: Arc, 202 | } 203 | 204 | impl DirectClientPool { 205 | fn new(factory: Arc) -> Self { 206 | Self { 207 | pool: DashMap::new(), 208 | factory, 209 | } 210 | } 211 | 212 | fn get_or_create(&self, endpoint: &Endpoint) -> Arc> { 213 | if let Some(c) = self.pool.get(endpoint) { 214 | // If exist in cache, return. 215 | c.value().clone() 216 | } else { 217 | // If not exist, build --> insert --> return. 218 | self.pool 219 | .entry(endpoint.clone()) 220 | .or_insert(Arc::new(InnerClient::new( 221 | self.factory.clone(), 222 | endpoint.to_string(), 223 | ))) 224 | .clone() 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 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 | use std::fmt::Display; 19 | 20 | use thiserror::Error as ThisError; 21 | 22 | use crate::model::write::Response; 23 | 24 | /// An error generated by the client. 25 | #[derive(Debug, ThisError)] 26 | pub enum Error { 27 | /// Error from the running server 28 | #[error("failed in server, err:{0}")] 29 | Server(ServerError), 30 | 31 | /// Error from the rpc 32 | /// Note that any error caused by a running server wont be wrapped in the 33 | /// grpc errors. 34 | #[error("failed in grpc, err:{0}")] 35 | Rpc(tonic::Status), 36 | 37 | /// Error about rpc. 38 | /// It will be throw while connection between client and server is broken 39 | /// and try for reconnecting is failed(timeout). 40 | #[error("failed to connect, addr:{addr:?}, err:{source:?}")] 41 | Connect { 42 | addr: String, 43 | source: Box, 44 | }, 45 | 46 | /// Error from the client and basically the rpc request has not been called 47 | /// yet or the rpc request has already been finished successfully. 48 | #[error("failed in client, msg:{0}")] 49 | Client(String), 50 | 51 | /// Error about authentication 52 | #[error("failed to check auth, err:{0}")] 53 | AuthFail(AuthFailStatus), 54 | 55 | /// Error from write in route based mode, some of rows may be written 56 | /// successfully, and others may fail. 57 | #[error("failed to write with route based client, err:{0}")] 58 | RouteBasedWriteError(RouteBasedWriteError), 59 | 60 | /// Error unknown 61 | #[error("unknown error, msg:{0}")] 62 | Unknown(String), 63 | 64 | #[error("failed to decode, msg:{0}")] 65 | BuildRows(String), 66 | 67 | #[error("failed to decode arrow payload, msg:{0}")] 68 | DecodeArrowPayload(Box), 69 | 70 | #[error("failed to find a database")] 71 | NoDatabase, 72 | 73 | #[error(transparent)] 74 | Other { 75 | #[from] 76 | source: anyhow::Error, 77 | }, 78 | } 79 | 80 | #[derive(Debug)] 81 | pub struct RouteBasedWriteError { 82 | pub ok: (Vec, Response), // (tables, write_response) 83 | pub errors: Vec<(Vec, Error)>, // [(tables, errors)] 84 | } 85 | 86 | impl From, Result)>> for RouteBasedWriteError { 87 | fn from(write_results: Vec<(Vec, Result)>) -> Self { 88 | let mut success_total = 0; 89 | let mut failed_total = 0; 90 | let mut ok_tables = Vec::new(); 91 | let mut errors = Vec::new(); 92 | for (tables, write_result) in write_results { 93 | match write_result { 94 | Ok(write_resp) => { 95 | success_total += write_resp.success; 96 | failed_total += write_resp.failed; 97 | ok_tables.extend(tables); 98 | } 99 | Err(e) => { 100 | errors.push((tables, e)); 101 | } 102 | } 103 | } 104 | 105 | Self { 106 | ok: (ok_tables, Response::new(success_total, failed_total)), 107 | errors, 108 | } 109 | } 110 | } 111 | 112 | impl RouteBasedWriteError { 113 | pub fn all_ok(&self) -> bool { 114 | self.errors.is_empty() 115 | } 116 | } 117 | 118 | impl Display for RouteBasedWriteError { 119 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 120 | f.debug_struct("RouteBasedWriteError") 121 | .field("ok", &self.ok) 122 | .field("errors", &self.errors) 123 | .finish() 124 | } 125 | } 126 | 127 | #[derive(Debug, Clone)] 128 | pub struct ServerError { 129 | pub code: u32, 130 | pub msg: String, 131 | } 132 | 133 | impl Display for ServerError { 134 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 135 | f.debug_struct("ServerError") 136 | .field("code", &self.code) 137 | .field("msg", &self.msg) 138 | .finish() 139 | } 140 | } 141 | 142 | #[derive(Debug, Clone)] 143 | pub struct AuthFailStatus { 144 | pub code: AuthCode, 145 | pub msg: String, 146 | } 147 | 148 | #[derive(Debug, Clone)] 149 | pub enum AuthCode { 150 | Ok = 0, 151 | 152 | InvalidTenantMeta = 1, 153 | 154 | InvalidTokenMeta = 2, 155 | } 156 | 157 | impl Display for AuthFailStatus { 158 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 159 | f.debug_struct("AuthFailStatus") 160 | .field("code", &self.code) 161 | .field("msg", &self.msg) 162 | .finish() 163 | } 164 | } 165 | 166 | /// A result holding the an [`Error`](Error). 167 | pub type Result = std::result::Result; 168 | 169 | #[cfg(test)] 170 | mod test { 171 | use super::*; 172 | 173 | #[test] 174 | fn test_error_standardizing() { 175 | let source_error = Box::new(Error::Unknown("unknown error".to_string())); 176 | let connect_error = Error::Connect { 177 | addr: "1.1.1.1:1111".to_string(), 178 | source: source_error as _, 179 | }; 180 | assert_eq!( 181 | &format!("{connect_error}"), 182 | r#"failed to connect, addr:"1.1.1.1:1111", err:Unknown("unknown error")"# 183 | ); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 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 | //! This crate provides an user-friendly client for [HoraeDB](https://github.com/HoraeDB /horaedb). 19 | //! 20 | //! With this crate, you can access a standalone HoraeDB or a HoraeDB cluster 21 | //! and manipulates the data in it. And the underlying communication between the 22 | //! client the HoraeDB servers is based on the gRPC, and the protocol is defined 23 | //! in the [horaedbproto](https://github.com/CeresDB/horaedbproto). 24 | //! 25 | //! ## Choose Mode 26 | //! 27 | //! Two access [`Mode`](Mode)s are provided by the client, `Proxy` and `Direct`: 28 | //! - When accessing HoraeDB cluster by `Direct` mode, the requests will be sent 29 | //! directly to the right HoraeDB instance determined by routing information. 30 | //! - When accessing HoraeDB by `Proxy` mode, the requests are just sent to any 31 | //! one HoraeDB instance, which takes the responsibilities for forwarding the 32 | //! requests. 33 | //! 34 | //! If the client can't access the HoraeDB server directly because of the 35 | //! network partition, `Proxy` mode is the only choice. Otherwise, `Direct` mode 36 | //! is suggested for better performance. 37 | //! 38 | //! ## Usage 39 | //! 40 | //! Build the client, and then manipulate the HoraeDB by writing and querying. 41 | //! 42 | //! ### Example 43 | //! Here is an example to create a table in HoraeDB by the client. 44 | //! 45 | //! ```rust,no_run 46 | //! # use futures::prelude::*; 47 | //! 48 | //! # use horaedb_client::{Builder, Mode, RpcContext, SqlQueryRequest}; 49 | //! # fn main() { 50 | //! # futures::executor::block_on(async { 51 | //! let client = Builder::new("127.0.0.1:8831".to_string(), Mode::Direct).build(); 52 | //! let rpc_ctx = RpcContext::default().database("public".to_string()); 53 | //! 54 | //! let create_table_sql = r#"CREATE TABLE IF NOT EXISTS horaedb ( 55 | //! str_tag string TAG, 56 | //! int_tag int32 TAG, 57 | //! var_tag varbinary TAG, 58 | //! str_field string, 59 | //! int_field int32, 60 | //! bin_field varbinary, 61 | //! t timestamp NOT NULL, 62 | //! TIMESTAMP KEY(t)) ENGINE=Analytic with 63 | //! (enable_ttl='false')"#; 64 | //! 65 | //! let req = SqlQueryRequest { 66 | //! tables: vec!["horaedb".to_string()], 67 | //! sql: create_table_sql.to_string(), 68 | //! }; 69 | //! let resp = client 70 | //! .sql_query(&rpc_ctx, &req) 71 | //! .await 72 | //! .expect("Should succeed to create table"); 73 | //! 74 | //! println!("Create table result:{:?}", resp); 75 | //! # }); 76 | //! # } 77 | //! ``` 78 | 79 | mod config; 80 | #[doc(hidden)] 81 | pub mod db_client; 82 | mod errors; 83 | #[doc(hidden)] 84 | pub mod model; 85 | mod router; 86 | mod rpc_client; 87 | mod util; 88 | 89 | #[doc(inline)] 90 | pub use crate::{ 91 | config::{Authorization, RpcConfig}, 92 | db_client::{Builder, DbClient, Mode}, 93 | errors::{Error, Result}, 94 | model::{ 95 | sql_query::{Request as SqlQueryRequest, Response as SqlQueryResponse}, 96 | write::{Request as WriteRequest, Response as WriteResponse}, 97 | }, 98 | rpc_client::RpcContext, 99 | }; 100 | -------------------------------------------------------------------------------- /src/model/mod.rs: -------------------------------------------------------------------------------- 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 | pub mod route; 19 | pub mod sql_query; 20 | pub mod value; 21 | pub mod write; 22 | -------------------------------------------------------------------------------- /src/model/route.rs: -------------------------------------------------------------------------------- 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 | use std::{fmt::Display, str::FromStr}; 19 | 20 | use horaedbproto::storage::Endpoint as EndPointPb; 21 | 22 | #[derive(Debug, Clone, Hash, Eq, PartialEq)] 23 | pub struct Endpoint { 24 | pub addr: String, 25 | pub port: u32, 26 | } 27 | 28 | impl Display for Endpoint { 29 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 30 | f.write_fmt(format_args!("{}:{}", self.addr, self.port)) 31 | } 32 | } 33 | 34 | impl Endpoint { 35 | pub fn new(ip: String, port: u32) -> Self { 36 | Self { addr: ip, port } 37 | } 38 | } 39 | 40 | impl FromStr for Endpoint { 41 | type Err = Box; 42 | 43 | fn from_str(s: &str) -> std::result::Result { 44 | let (addr, raw_port) = match s.rsplit_once(':') { 45 | Some(v) => v, 46 | None => { 47 | let err_msg = "Can't find ':' in the source string".to_string(); 48 | return Err(Self::Err::from(err_msg)); 49 | } 50 | }; 51 | 52 | if addr.is_empty() { 53 | let err_msg = "Empty addr in the source string".to_string(); 54 | return Err(Self::Err::from(err_msg)); 55 | } 56 | 57 | let port = raw_port.parse().map_err(|e| { 58 | let err_msg = format!("Fail to parse port:{raw_port}, err:{e}"); 59 | Self::Err::from(err_msg) 60 | })?; 61 | if port > u16::MAX as u32 { 62 | let err_msg = "Too large port (<=65536)".to_string(); 63 | return Err(Self::Err::from(err_msg)); 64 | } 65 | 66 | Ok(Endpoint { 67 | addr: addr.to_string(), 68 | port, 69 | }) 70 | } 71 | } 72 | 73 | impl From for Endpoint { 74 | fn from(endpoint_pb: EndPointPb) -> Self { 75 | Self { 76 | addr: endpoint_pb.ip, 77 | port: endpoint_pb.port, 78 | } 79 | } 80 | } 81 | 82 | #[cfg(test)] 83 | mod tests { 84 | use super::*; 85 | 86 | #[test] 87 | fn test_parse_endpoint() { 88 | let normal_cases = vec![ 89 | ("127.0.0.1:80", "127.0.0.1", 80), 90 | ("hello.world.com:1080", "hello.world.com", 1080), 91 | ("horaedb.io:8831", "horaedb.io", 8831), 92 | ]; 93 | 94 | for (raw_endpoint, addr, port) in normal_cases { 95 | let endpoint: Endpoint = raw_endpoint.parse().unwrap(); 96 | assert_eq!(addr, endpoint.addr); 97 | assert_eq!(port, endpoint.port); 98 | } 99 | 100 | let abnormal_cases = vec!["127.0.0.1", ":1080", "", "0:99999999"]; 101 | for raw_endpoint in abnormal_cases { 102 | let parse_res = raw_endpoint.parse::(); 103 | assert!(parse_res.is_err()); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/model/sql_query/display.rs: -------------------------------------------------------------------------------- 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 | use std::fmt::Display; 19 | 20 | use crate::model::sql_query::response::Response; 21 | 22 | /// Display [`SqlQueryResponse`](Response) in csv format. 23 | pub struct CsvFormatter { 24 | pub resp: Response, 25 | } 26 | 27 | impl Display for CsvFormatter { 28 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 29 | // Just print while returned `rows` in not empty. 30 | if !self.resp.rows.is_empty() { 31 | // Get and output column names, unwrap is safe here. 32 | let first_row = self.resp.rows.first().unwrap(); 33 | let col_names = first_row 34 | .columns() 35 | .iter() 36 | .map(|col| col.name().to_string()) 37 | .collect::>(); 38 | for col_name in &col_names { 39 | f.write_fmt(format_args!("{col_name},"))?; 40 | } 41 | f.write_str("\n")?; 42 | 43 | // Get and output rows. 44 | for row in &self.resp.rows { 45 | for column in row.columns() { 46 | f.write_fmt(format_args!("{:?},", column.value()))?; 47 | } 48 | f.write_str("\n")?; 49 | } 50 | } 51 | 52 | Ok(()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/model/sql_query/mod.rs: -------------------------------------------------------------------------------- 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 | pub mod display; 19 | pub(crate) mod request; 20 | pub(crate) mod response; 21 | pub mod row; 22 | 23 | pub use request::Request; 24 | pub use response::Response; 25 | -------------------------------------------------------------------------------- /src/model/sql_query/request.rs: -------------------------------------------------------------------------------- 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 | #[derive(Debug, Clone)] 19 | pub struct Request { 20 | /// The tables involved in the sql. 21 | /// 22 | /// This is a hint, by which the client can find the right server to handle 23 | /// the query, can accelerate query. 24 | pub tables: Vec, 25 | /// The sql for query. 26 | pub sql: String, 27 | } 28 | -------------------------------------------------------------------------------- /src/model/sql_query/response.rs: -------------------------------------------------------------------------------- 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 | use std::io::Cursor; 19 | 20 | use arrow::{ipc::reader::StreamReader, record_batch::RecordBatch}; 21 | use horaedbproto::storage::{ 22 | arrow_payload::Compression, sql_query_response::Output as OutputPb, ArrowPayload, 23 | SqlQueryResponse, 24 | }; 25 | 26 | use crate::{ 27 | errors::{Error, Result}, 28 | model::sql_query::row::{Row, RowBuilder}, 29 | }; 30 | 31 | /// The response for [`SqlQueryRequest`](crate::model::sql_query::Request). 32 | #[derive(Debug, Default)] 33 | pub struct Response { 34 | /// The affected rows by the query sql. 35 | pub affected_rows: u32, 36 | /// The rows of the sql result. 37 | pub rows: Vec, 38 | } 39 | 40 | #[derive(Debug)] 41 | enum Output { 42 | AffectedRows(u32), 43 | Rows(Vec), 44 | } 45 | 46 | impl TryFrom for Response { 47 | type Error = Error; 48 | 49 | fn try_from(sql_resp_pb: SqlQueryResponse) -> std::result::Result { 50 | let output_pb = sql_resp_pb 51 | .output 52 | .ok_or_else(|| Error::Unknown("output is empty in sql query response".to_string()))?; 53 | let output = Output::try_from(output_pb)?; 54 | 55 | let resp = match output { 56 | Output::AffectedRows(affected) => Response { 57 | affected_rows: affected, 58 | ..Default::default() 59 | }, 60 | Output::Rows(rows) => Response { 61 | rows, 62 | ..Default::default() 63 | }, 64 | }; 65 | 66 | Ok(resp) 67 | } 68 | } 69 | 70 | impl TryFrom for Output { 71 | type Error = Error; 72 | 73 | fn try_from(output_pb: OutputPb) -> std::result::Result { 74 | let output = match output_pb { 75 | OutputPb::AffectedRows(affected) => Output::AffectedRows(affected), 76 | OutputPb::Arrow(arrow_payload) => { 77 | let arrow_record_batches = decode_arrow_payload(arrow_payload)?; 78 | let rows_group = arrow_record_batches 79 | .into_iter() 80 | .map(|record_batch| { 81 | let row_builder = match RowBuilder::with_arrow_record_batch(record_batch) { 82 | Ok(builder) => builder, 83 | Err(e) => return Err(e), 84 | }; 85 | Ok(row_builder.build()) 86 | }) 87 | .collect::>>()?; 88 | let rows = rows_group.into_iter().flatten().collect::>(); 89 | 90 | Output::Rows(rows) 91 | } 92 | }; 93 | 94 | Ok(output) 95 | } 96 | } 97 | 98 | pub fn decode_arrow_payload(arrow_payload: ArrowPayload) -> Result> { 99 | let compression = arrow_payload.compression(); 100 | let byte_batches = arrow_payload.record_batches; 101 | 102 | // Maybe unzip payload bytes firstly. 103 | let unzip_byte_batches = byte_batches 104 | .into_iter() 105 | .map(|bytes_batch| match compression { 106 | Compression::None => Ok(bytes_batch), 107 | Compression::Zstd => zstd::stream::decode_all(Cursor::new(bytes_batch)) 108 | .map_err(|e| Error::DecodeArrowPayload(Box::new(e))), 109 | }) 110 | .collect::>>>()?; 111 | 112 | // Decode the byte batches to record batches, multiple record batches may be 113 | // included in one byte batch. 114 | let record_batches_group = unzip_byte_batches 115 | .into_iter() 116 | .map(|byte_batch| { 117 | // Decode bytes to `RecordBatch`. 118 | let stream_reader = match StreamReader::try_new(Cursor::new(byte_batch), None) 119 | .map_err(|e| Error::DecodeArrowPayload(Box::new(e))) 120 | { 121 | Ok(reader) => reader, 122 | Err(e) => return Err(e), 123 | }; 124 | 125 | stream_reader 126 | .into_iter() 127 | .map(|decode_result| { 128 | decode_result.map_err(|e| Error::DecodeArrowPayload(Box::new(e))) 129 | }) 130 | .collect::>>() 131 | }) 132 | .collect::>>>()?; 133 | 134 | let record_batches = record_batches_group 135 | .into_iter() 136 | .flatten() 137 | .collect::>(); 138 | 139 | Ok(record_batches) 140 | } 141 | -------------------------------------------------------------------------------- /src/model/sql_query/row.rs: -------------------------------------------------------------------------------- 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 | use arrow::{ 19 | array::{ 20 | ArrayAccessor, ArrayRef, AsArray, BinaryArray, BooleanArray, Float32Array, Float64Array, 21 | Int16Array, Int32Array, Int64Array, Int8Array, StringArray, Time32MillisecondArray, 22 | TimestampMillisecondArray, UInt16Array, UInt32Array, UInt64Array, UInt8Array, 23 | }, 24 | datatypes::{DataType, Int32Type, TimeUnit}, 25 | record_batch::RecordBatch, 26 | }; 27 | use paste::paste; 28 | 29 | use crate::{model::value::Value, Error, Result}; 30 | 31 | /// A row in the 32 | /// [`SqlQueryResponse`](crate::model::sql_query::Response). 33 | #[derive(Clone, Debug, PartialEq)] 34 | pub struct Row { 35 | // It is better to iterate in a fixed order, also can save memory. 36 | columns: Vec, 37 | } 38 | 39 | impl Row { 40 | /// Find the [`Column`] by the column name. 41 | pub fn column(&self, name: &str) -> Option<&Column> { 42 | self.columns.iter().find(|column| column.name == name) 43 | } 44 | 45 | /// Get the slice of all the columns. 46 | pub fn columns(&self) -> &[Column] { 47 | &self.columns 48 | } 49 | } 50 | 51 | /// A column in the [`Row`]. 52 | #[derive(Clone, Debug, PartialEq)] 53 | pub struct Column { 54 | name: String, 55 | value: Value, 56 | } 57 | 58 | impl Column { 59 | pub(crate) fn new(name: String, value: Value) -> Self { 60 | Self { name, value } 61 | } 62 | 63 | /// Return the name of the column. 64 | pub fn name(&self) -> &str { 65 | &self.name 66 | } 67 | 68 | /// Return the [`Value`] of the column. 69 | pub fn value(&self) -> &Value { 70 | &self.value 71 | } 72 | } 73 | 74 | macro_rules! fill_column { 75 | ($arrow_column:expr, $arrow_array_type:ty, $value_type:ty, $rows:expr, $col_idx:expr) => { 76 | paste! { 77 | let row_count = $rows.len(); 78 | let cast_arrow_column = $arrow_column 79 | .as_any() 80 | .downcast_ref::<$arrow_array_type>().unwrap(); 81 | for row_idx in 0..row_count { 82 | let value = cast_arrow_column.value(row_idx).to_owned(); 83 | let row = $rows.get_mut(row_idx).unwrap(); 84 | let col = row.get_mut($col_idx).unwrap(); 85 | *col = $value_type(value) 86 | } 87 | } 88 | }; 89 | } 90 | 91 | #[derive(Clone, Debug, Default)] 92 | pub struct RowBuilder { 93 | pub col_idx_to_name: Vec, 94 | pub row_values: Vec>, 95 | } 96 | 97 | impl RowBuilder { 98 | pub fn build(self) -> Vec { 99 | self.row_values 100 | .into_iter() 101 | .map(|row| { 102 | let columns = row 103 | .into_iter() 104 | .enumerate() 105 | .map(|(col_idx, value)| { 106 | // Find its name. 107 | let col_name = self.col_idx_to_name[col_idx].clone(); 108 | 109 | Column::new(col_name, value) 110 | }) 111 | .collect::>(); 112 | 113 | Row { columns } 114 | }) 115 | .collect::>() 116 | } 117 | 118 | pub fn with_arrow_record_batch(record_batch: RecordBatch) -> Result { 119 | // Build `col_idx_to_name`. 120 | let col_idx_to_name = record_batch 121 | .schema() 122 | .fields() 123 | .iter() 124 | .map(|field| field.name().clone()) 125 | .collect::>(); 126 | 127 | // Build `rows`. 128 | let col_count = record_batch.num_columns(); 129 | let row_count = record_batch.num_rows(); 130 | 131 | let mut rows = vec![vec![Value::Null; col_count]; row_count]; 132 | 133 | // Fill row row batch column by column. 134 | for col_idx in 0..col_count { 135 | let arrow_column = record_batch.column(col_idx); 136 | Self::fill_column_in_row_batch(&mut rows, col_idx, arrow_column)?; 137 | } 138 | 139 | Ok(RowBuilder { 140 | col_idx_to_name, 141 | row_values: rows, 142 | }) 143 | } 144 | 145 | fn fill_column_in_row_batch( 146 | rows: &mut [Vec], 147 | col_idx: usize, 148 | arrow_column: &ArrayRef, 149 | ) -> Result<()> { 150 | let row_count = rows.len(); 151 | let arrow_type = arrow_column.data_type(); 152 | // TODO: may we can make it simpler with macro. 153 | match arrow_type { 154 | // Because `rows` will be initialized with `Value::Null`, just do nothing while 155 | // encounter `DataType::Null`. 156 | DataType::Null => {} 157 | DataType::Boolean => { 158 | fill_column!(arrow_column, BooleanArray, Value::Boolean, rows, col_idx); 159 | } 160 | DataType::Int8 => { 161 | fill_column!(arrow_column, Int8Array, Value::Int8, rows, col_idx); 162 | } 163 | DataType::Int16 => { 164 | fill_column!(arrow_column, Int16Array, Value::Int16, rows, col_idx); 165 | } 166 | DataType::Int32 => { 167 | fill_column!(arrow_column, Int32Array, Value::Int32, rows, col_idx); 168 | } 169 | DataType::Int64 => { 170 | fill_column!(arrow_column, Int64Array, Value::Int64, rows, col_idx); 171 | } 172 | DataType::UInt8 => { 173 | fill_column!(arrow_column, UInt8Array, Value::UInt8, rows, col_idx); 174 | } 175 | DataType::UInt16 => { 176 | fill_column!(arrow_column, UInt16Array, Value::UInt16, rows, col_idx); 177 | } 178 | DataType::UInt32 => { 179 | fill_column!(arrow_column, UInt32Array, Value::UInt32, rows, col_idx); 180 | } 181 | DataType::UInt64 => { 182 | fill_column!(arrow_column, UInt64Array, Value::UInt64, rows, col_idx); 183 | } 184 | DataType::Float32 => { 185 | fill_column!(arrow_column, Float32Array, Value::Float, rows, col_idx); 186 | } 187 | DataType::Float64 => { 188 | fill_column!(arrow_column, Float64Array, Value::Double, rows, col_idx); 189 | } 190 | DataType::Utf8 | DataType::LargeUtf8 => { 191 | fill_column!(arrow_column, StringArray, Value::String, rows, col_idx); 192 | } 193 | DataType::Binary | DataType::LargeBinary => { 194 | fill_column!(arrow_column, BinaryArray, Value::Varbinary, rows, col_idx); 195 | } 196 | DataType::Timestamp(TimeUnit::Millisecond, _) => { 197 | fill_column!( 198 | arrow_column, 199 | TimestampMillisecondArray, 200 | Value::Timestamp, 201 | rows, 202 | col_idx 203 | ); 204 | } 205 | DataType::Time32(TimeUnit::Millisecond) => { 206 | let cast_arrow_column = arrow_column 207 | .as_any() 208 | .downcast_ref::() 209 | .unwrap(); 210 | for row_idx in 0..row_count { 211 | let value = cast_arrow_column.value(row_idx); 212 | let row = rows.get_mut(row_idx).unwrap(); 213 | let col = row.get_mut(col_idx).unwrap(); 214 | *col = Value::Timestamp(value as i64) 215 | } 216 | } 217 | DataType::Dictionary(index_type, encode_type) 218 | if index_type.as_ref() == &DataType::Int32 219 | && encode_type.as_ref() == &DataType::Utf8 => 220 | { 221 | let row_count = rows.len(); 222 | let cast_arrow_column = arrow_column 223 | .as_dictionary::() 224 | .downcast_dict::() 225 | .unwrap(); 226 | for row_idx in 0..row_count { 227 | let value = cast_arrow_column.value(row_idx).to_owned(); 228 | let row = rows.get_mut(row_idx).unwrap(); 229 | let col = row.get_mut(col_idx).unwrap(); 230 | *col = Value::String(value) 231 | } 232 | } 233 | // Encounter unsupported type. 234 | _ => { 235 | return Err(Error::BuildRows(format!( 236 | "Unsupported arrow type:{arrow_type}", 237 | ))); 238 | } 239 | } 240 | Ok(()) 241 | } 242 | } 243 | 244 | #[cfg(test)] 245 | mod test { 246 | use std::sync::Arc; 247 | 248 | use arrow::{ 249 | array::{ 250 | BinaryArray, DictionaryArray, Int32Array, StringArray, Time32MillisecondArray, 251 | TimestampMillisecondArray, 252 | }, 253 | datatypes::{DataType, Field, Int32Type, Schema}, 254 | record_batch::RecordBatch, 255 | }; 256 | 257 | use super::{Row, RowBuilder}; 258 | use crate::model::{sql_query::row::Column, value::Value}; 259 | 260 | #[test] 261 | fn test_build_row() { 262 | let int_values = vec![1, 2, 3]; 263 | let string_values = vec![ 264 | "test1".to_string(), 265 | "test2".to_string(), 266 | "test3".to_string(), 267 | ]; 268 | let binary_values = vec![b"0.1".as_slice(), b"0.2".as_slice(), b"0.3".as_slice()]; 269 | let timestamp_values = vec![1001, 1002, 1003]; 270 | let timestamp32_values = vec![1004, 1005, 1006]; 271 | // Built rows. 272 | let int_array = Int32Array::from(int_values.clone()); 273 | let string_array = StringArray::from(string_values.clone()); 274 | let binary_array = BinaryArray::from(binary_values.clone()); 275 | let timestamp_array = TimestampMillisecondArray::from(timestamp_values.clone()); 276 | let timestamp32_array = Time32MillisecondArray::from(timestamp32_values.clone()); 277 | let string_dictionary_array: DictionaryArray = 278 | string_values.iter().map(|s| s.as_str()).collect(); 279 | let schema = Schema::new(vec![ 280 | Field::new("int", DataType::Int32, false), 281 | Field::new("string", DataType::Utf8, false), 282 | Field::new("varbinary", DataType::Binary, false), 283 | Field::new( 284 | "timestamp", 285 | DataType::Timestamp(arrow::datatypes::TimeUnit::Millisecond, None), 286 | false, 287 | ), 288 | Field::new( 289 | "timestamp32", 290 | DataType::Time32(arrow::datatypes::TimeUnit::Millisecond), 291 | false, 292 | ), 293 | Field::new( 294 | "string_dictionary", 295 | DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)), 296 | true, 297 | ), 298 | ]); 299 | let arrow_batch = RecordBatch::try_new( 300 | Arc::new(schema), 301 | vec![ 302 | Arc::new(int_array), 303 | Arc::new(string_array), 304 | Arc::new(binary_array), 305 | Arc::new(timestamp_array), 306 | Arc::new(timestamp32_array), 307 | Arc::new(string_dictionary_array), 308 | ], 309 | ) 310 | .unwrap(); 311 | 312 | let built_rows = RowBuilder::with_arrow_record_batch(arrow_batch) 313 | .unwrap() 314 | .build(); 315 | 316 | // Expected rows 317 | let int_col_values = int_values.into_iter().map(Value::Int32).collect::>(); 318 | let string_col_values = string_values 319 | .into_iter() 320 | .map(Value::String) 321 | .collect::>(); 322 | let binary_col_values = binary_values 323 | .into_iter() 324 | .map(|v| Value::Varbinary(v.to_vec())) 325 | .collect::>(); 326 | let timestamp_col_values = timestamp_values 327 | .into_iter() 328 | .map(Value::Timestamp) 329 | .collect::>(); 330 | let timestamp32_col_values = timestamp32_values 331 | .into_iter() 332 | .map(|v| Value::Timestamp(v as i64)) 333 | .collect::>(); 334 | let row1 = Row { 335 | columns: vec![ 336 | Column::new("int".to_string(), int_col_values[0].clone()), 337 | Column::new("string".to_string(), string_col_values[0].clone()), 338 | Column::new("varbinary".to_string(), binary_col_values[0].clone()), 339 | Column::new("timestamp".to_string(), timestamp_col_values[0].clone()), 340 | Column::new("timestamp32".to_string(), timestamp32_col_values[0].clone()), 341 | Column::new( 342 | "string_dictionary".to_string(), 343 | string_col_values[0].clone(), 344 | ), 345 | ], 346 | }; 347 | let row2 = Row { 348 | columns: vec![ 349 | Column::new("int".to_string(), int_col_values[1].clone()), 350 | Column::new("string".to_string(), string_col_values[1].clone()), 351 | Column::new("varbinary".to_string(), binary_col_values[1].clone()), 352 | Column::new("timestamp".to_string(), timestamp_col_values[1].clone()), 353 | Column::new("timestamp32".to_string(), timestamp32_col_values[1].clone()), 354 | Column::new( 355 | "string_dictionary".to_string(), 356 | string_col_values[1].clone(), 357 | ), 358 | ], 359 | }; 360 | let row3 = Row { 361 | columns: vec![ 362 | Column::new("int".to_string(), int_col_values[2].clone()), 363 | Column::new("string".to_string(), string_col_values[2].clone()), 364 | Column::new("varbinary".to_string(), binary_col_values[2].clone()), 365 | Column::new("timestamp".to_string(), timestamp_col_values[2].clone()), 366 | Column::new("timestamp32".to_string(), timestamp32_col_values[2].clone()), 367 | Column::new( 368 | "string_dictionary".to_string(), 369 | string_col_values[2].clone(), 370 | ), 371 | ], 372 | }; 373 | let expected_rows = vec![row1, row2, row3]; 374 | 375 | assert_eq!(built_rows, expected_rows); 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /src/model/value.rs: -------------------------------------------------------------------------------- 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 | use std::any::Any; 19 | 20 | use horaedbproto::storage::{value, Value as ValuePb}; 21 | 22 | pub type TimestampMs = i64; 23 | 24 | /// The value enum to express the data in HoraeDB. 25 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 26 | pub enum Value { 27 | Null, 28 | Timestamp(TimestampMs), 29 | Double(f64), 30 | Float(f32), 31 | Varbinary(Vec), 32 | String(String), 33 | UInt64(u64), 34 | UInt32(u32), 35 | UInt16(u16), 36 | UInt8(u8), 37 | Int64(i64), 38 | Int32(i32), 39 | Int16(i16), 40 | Int8(i8), 41 | Boolean(bool), 42 | } 43 | 44 | impl Value { 45 | pub fn data_type(&self) -> DataType { 46 | match self { 47 | Value::Null => DataType::Null, 48 | Value::Timestamp(_) => DataType::Timestamp, 49 | Value::Double(_) => DataType::Double, 50 | Value::Float(_) => DataType::Float, 51 | Value::Varbinary(_) => DataType::Varbinary, 52 | Value::String(_) => DataType::String, 53 | Value::UInt64(_) => DataType::UInt64, 54 | Value::UInt32(_) => DataType::UInt32, 55 | Value::UInt16(_) => DataType::UInt16, 56 | Value::UInt8(_) => DataType::UInt8, 57 | Value::Int64(_) => DataType::Int64, 58 | Value::Int32(_) => DataType::Int32, 59 | Value::Int16(_) => DataType::Int16, 60 | Value::Int8(_) => DataType::Int8, 61 | Value::Boolean(_) => DataType::Boolean, 62 | } 63 | } 64 | 65 | pub fn is_null(&self) -> bool { 66 | matches!(self, Value::Null) 67 | } 68 | 69 | pub fn as_any(&self) -> &dyn Any { 70 | self 71 | } 72 | 73 | pub fn as_i8(&self) -> Option { 74 | match self { 75 | Value::Boolean(v) => Some(*v as i8), 76 | Value::Int8(v) => Some(*v), 77 | _ => None, 78 | } 79 | } 80 | 81 | pub fn as_u8(&self) -> Option { 82 | match self { 83 | Value::Boolean(v) => Some(*v as u8), 84 | Value::Int8(v) => Some(*v as u8), 85 | Value::UInt8(v) => Some(*v), 86 | _ => None, 87 | } 88 | } 89 | 90 | pub fn as_i16(&self) -> Option { 91 | match self { 92 | Value::Boolean(v) => Some(*v as i16), 93 | Value::Int8(v) => Some(*v as i16), 94 | Value::UInt8(v) => Some(*v as i16), 95 | Value::Int16(v) => Some(*v), 96 | _ => None, 97 | } 98 | } 99 | 100 | pub fn as_u16(&self) -> Option { 101 | match self { 102 | Value::Boolean(v) => Some(*v as u16), 103 | Value::Int8(v) => Some(*v as u16), 104 | Value::UInt8(v) => Some(*v as u16), 105 | Value::Int16(v) => Some(*v as u16), 106 | Value::UInt16(v) => Some(*v), 107 | _ => None, 108 | } 109 | } 110 | 111 | pub fn as_i32(&self) -> Option { 112 | match self { 113 | Value::Boolean(v) => Some(*v as i32), 114 | Value::Int8(v) => Some(*v as i32), 115 | Value::UInt8(v) => Some(*v as i32), 116 | Value::Int16(v) => Some(*v as i32), 117 | Value::UInt16(v) => Some(*v as i32), 118 | Value::Int32(v) => Some(*v), 119 | _ => None, 120 | } 121 | } 122 | 123 | pub fn as_u32(&self) -> Option { 124 | match self { 125 | Value::Boolean(v) => Some(*v as u32), 126 | Value::Int8(v) => Some(*v as u32), 127 | Value::UInt8(v) => Some(*v as u32), 128 | Value::Int16(v) => Some(*v as u32), 129 | Value::UInt16(v) => Some(*v as u32), 130 | Value::Int32(v) => Some(*v as u32), 131 | Value::UInt32(v) => Some(*v), 132 | _ => None, 133 | } 134 | } 135 | 136 | pub fn as_i64(&self) -> Option { 137 | match self { 138 | Value::Boolean(v) => Some(*v as i64), 139 | Value::Int8(v) => Some(*v as i64), 140 | Value::UInt8(v) => Some(*v as i64), 141 | Value::Int16(v) => Some(*v as i64), 142 | Value::UInt16(v) => Some(*v as i64), 143 | Value::Int32(v) => Some(*v as i64), 144 | Value::UInt32(v) => Some(*v as i64), 145 | Value::Int64(v) => Some(*v), 146 | _ => None, 147 | } 148 | } 149 | 150 | pub fn as_u64(&self) -> Option { 151 | match self { 152 | Value::Boolean(v) => Some(*v as u64), 153 | Value::Int8(v) => Some(*v as u64), 154 | Value::UInt8(v) => Some(*v as u64), 155 | Value::Int16(v) => Some(*v as u64), 156 | Value::UInt16(v) => Some(*v as u64), 157 | Value::Int32(v) => Some(*v as u64), 158 | Value::UInt32(v) => Some(*v as u64), 159 | Value::Int64(v) => Some(*v as u64), 160 | Value::UInt64(v) => Some(*v), 161 | _ => None, 162 | } 163 | } 164 | 165 | pub fn as_f32(&self) -> Option { 166 | match self { 167 | Value::Float(v) => Some(*v), 168 | Value::UInt64(v) => Some(*v as f32), 169 | Value::UInt32(v) => Some(*v as f32), 170 | Value::UInt16(v) => Some(*v as f32), 171 | Value::UInt8(v) => Some(*v as f32), 172 | Value::Int64(v) => Some(*v as f32), 173 | Value::Int32(v) => Some(*v as f32), 174 | Value::Int16(v) => Some(*v as f32), 175 | Value::Int8(v) => Some(*v as f32), 176 | Value::Boolean(_) 177 | | Value::Double(_) 178 | | Value::Null 179 | | Value::Timestamp(_) 180 | | Value::Varbinary(_) 181 | | Value::String(_) => None, 182 | } 183 | } 184 | 185 | pub fn as_f64(&self) -> Option { 186 | match self { 187 | Value::Double(v) => Some(*v), 188 | Value::Float(v) => Some(*v as f64), 189 | Value::UInt64(v) => Some(*v as f64), 190 | Value::UInt32(v) => Some(*v as f64), 191 | Value::UInt16(v) => Some(*v as f64), 192 | Value::UInt8(v) => Some(*v as f64), 193 | Value::Int64(v) => Some(*v as f64), 194 | Value::Int32(v) => Some(*v as f64), 195 | Value::Int16(v) => Some(*v as f64), 196 | Value::Int8(v) => Some(*v as f64), 197 | Value::Boolean(_) 198 | | Value::Null 199 | | Value::Timestamp(_) 200 | | Value::Varbinary(_) 201 | | Value::String(_) => None, 202 | } 203 | } 204 | 205 | pub fn as_varbinary(&self) -> Option> { 206 | match &self { 207 | Value::Varbinary(v) => Some(v.clone()), 208 | _ => None, 209 | } 210 | } 211 | 212 | /// Cast datum to &str. 213 | pub fn as_str(&self) -> Option { 214 | match self { 215 | Value::String(v) => Some(v.clone()), 216 | _ => None, 217 | } 218 | } 219 | 220 | pub fn to_bytes(&self) -> Vec { 221 | match self { 222 | Value::Null => b"".to_vec(), 223 | Value::Timestamp(v) => v.to_le_bytes().to_vec(), 224 | Value::Double(v) => v.to_le_bytes().to_vec(), 225 | Value::Float(v) => v.to_le_bytes().to_vec(), 226 | Value::Varbinary(v) => v.clone(), 227 | Value::String(v) => v.as_bytes().to_vec(), 228 | Value::UInt64(v) => v.to_le_bytes().to_vec(), 229 | Value::UInt32(v) => v.to_le_bytes().to_vec(), 230 | Value::UInt16(v) => v.to_le_bytes().to_vec(), 231 | Value::UInt8(v) => v.to_le_bytes().to_vec(), 232 | Value::Int64(v) => v.to_le_bytes().to_vec(), 233 | Value::Int32(v) => v.to_le_bytes().to_vec(), 234 | Value::Int16(v) => v.to_le_bytes().to_vec(), 235 | Value::Int8(v) => v.to_le_bytes().to_vec(), 236 | Value::Boolean(v) => (*v as u8).to_le_bytes().to_vec(), 237 | } 238 | } 239 | } 240 | 241 | impl Default for Value { 242 | fn default() -> Self { 243 | Self::Null 244 | } 245 | } 246 | 247 | impl From for ValuePb { 248 | fn from(val: Value) -> Self { 249 | let value = match val { 250 | Value::Null => None, 251 | Value::Timestamp(v) => Some(value::Value::TimestampValue(v)), 252 | Value::Double(v) => Some(value::Value::Float64Value(v)), 253 | Value::Float(v) => Some(value::Value::Float32Value(v)), 254 | Value::Varbinary(v) => Some(value::Value::VarbinaryValue(v)), 255 | Value::String(v) => Some(value::Value::StringValue(v)), 256 | Value::UInt64(v) => Some(value::Value::Uint64Value(v)), 257 | Value::UInt32(v) => Some(value::Value::Uint32Value(v)), 258 | Value::UInt16(v) => Some(value::Value::Uint16Value(v.into())), 259 | Value::UInt8(v) => Some(value::Value::Uint8Value(v.into())), 260 | Value::Int64(v) => Some(value::Value::Int64Value(v)), 261 | Value::Int32(v) => Some(value::Value::Int32Value(v)), 262 | Value::Int16(v) => Some(value::Value::Int16Value(v.into())), 263 | Value::Int8(v) => Some(value::Value::Int8Value(v.into())), 264 | Value::Boolean(v) => Some(value::Value::BoolValue(v)), 265 | }; 266 | 267 | ValuePb { value } 268 | } 269 | } 270 | 271 | impl From for Value { 272 | fn from(value_pb: ValuePb) -> Self { 273 | if value_pb.value.is_none() { 274 | return Value::Null; 275 | } 276 | 277 | let value = value_pb.value.unwrap(); 278 | match value { 279 | value::Value::Float64Value(v) => Value::Double(v), 280 | value::Value::StringValue(v) => Value::String(v), 281 | value::Value::Int64Value(v) => Value::Int64(v), 282 | value::Value::Float32Value(v) => Value::Float(v), 283 | value::Value::Int32Value(v) => Value::Int32(v), 284 | value::Value::Int16Value(v) => Value::Int16(v as i16), 285 | value::Value::Int8Value(v) => Value::Int8(v as i8), 286 | value::Value::BoolValue(v) => Value::Boolean(v), 287 | value::Value::Uint64Value(v) => Value::UInt64(v), 288 | value::Value::Uint32Value(v) => Value::UInt32(v), 289 | value::Value::Uint16Value(v) => Value::UInt16(v as u16), 290 | value::Value::Uint8Value(v) => Value::UInt8(v as u8), 291 | value::Value::TimestampValue(v) => Value::Timestamp(v), 292 | value::Value::VarbinaryValue(v) => Value::Varbinary(v), 293 | } 294 | } 295 | } 296 | 297 | /// The data type supported by HoraeDB. 298 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 299 | pub enum DataType { 300 | Null = 0, 301 | Timestamp, 302 | Double, 303 | Float, 304 | Varbinary, 305 | String, 306 | UInt64, 307 | UInt32, 308 | UInt16, 309 | UInt8, 310 | Int64, 311 | Int32, 312 | Int16, 313 | Int8, 314 | Boolean, 315 | } 316 | -------------------------------------------------------------------------------- /src/model/write/mod.rs: -------------------------------------------------------------------------------- 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 | pub mod point; 19 | mod request; 20 | mod response; 21 | 22 | pub use request::{pb_builder::WriteTableRequestPbsBuilder, Request}; 23 | pub use response::Response; 24 | -------------------------------------------------------------------------------- /src/model/write/point.rs: -------------------------------------------------------------------------------- 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 | use std::collections::BTreeMap; 19 | 20 | use crate::model::value::Value; 21 | 22 | const TSID: &str = "tsid"; 23 | const TIMESTAMP: &str = "timestamp"; 24 | 25 | #[inline] 26 | pub fn is_reserved_column_name(name: &str) -> bool { 27 | name.eq_ignore_ascii_case(TSID) || name.eq_ignore_ascii_case(TIMESTAMP) 28 | } 29 | 30 | /// One point in the [`WriteRequest`](crate::WriteRequest). 31 | #[derive(Clone, Debug, Default, PartialEq)] 32 | pub struct Point { 33 | pub table: String, 34 | pub timestamp: i64, 35 | pub tags: BTreeMap, 36 | pub fields: BTreeMap, 37 | } 38 | 39 | /// Builder for building a point. 40 | #[derive(Debug)] 41 | pub struct PointBuilder { 42 | table: String, 43 | timestamp: Option, 44 | // tags' traversing should have definite order 45 | tags: BTreeMap, 46 | fields: BTreeMap, 47 | contains_reserved_column_name: bool, 48 | } 49 | 50 | impl PointBuilder { 51 | pub fn new(table: impl Into) -> Self { 52 | Self { 53 | table: table.into(), 54 | timestamp: None, 55 | tags: BTreeMap::new(), 56 | fields: BTreeMap::new(), 57 | contains_reserved_column_name: false, 58 | } 59 | } 60 | 61 | /// Set the table name for the point. 62 | pub fn table(mut self, table: impl Into) -> Self { 63 | self.table = table.into(); 64 | self 65 | } 66 | 67 | /// Set the timestamp for the point. 68 | pub fn timestamp(mut self, timestamp: i64) -> Self { 69 | self.timestamp = Some(timestamp); 70 | self 71 | } 72 | 73 | /// Set tag name and value of the write entry. 74 | /// 75 | /// You cannot set tag with name like 'timestamp' or 'tsid', 76 | /// because they are keywords in horaedb. 77 | pub fn tag(mut self, name: impl Into, value: Value) -> Self { 78 | let name = name.into(); 79 | if is_reserved_column_name(&name) { 80 | self.contains_reserved_column_name = true; 81 | } 82 | 83 | let _ = self.tags.insert(name, value); 84 | self 85 | } 86 | 87 | /// Set the name and value of a field specified by its `name`. 88 | pub fn field(mut self, name: impl Into, value: Value) -> Self { 89 | let name = name.into(); 90 | if is_reserved_column_name(&name) { 91 | self.contains_reserved_column_name = true; 92 | } 93 | 94 | let _ = self.fields.insert(name, value); 95 | self 96 | } 97 | 98 | /// Build the final point. 99 | pub fn build(self) -> Result { 100 | if self.contains_reserved_column_name { 101 | return Err("Tag or field name reserved column name in horaedb".to_string()); 102 | } 103 | 104 | if self.fields.is_empty() { 105 | return Err("Fields should not be empty".to_string()); 106 | } 107 | 108 | let timestamp = self 109 | .timestamp 110 | .ok_or_else(|| "Timestamp must be set".to_string())?; 111 | 112 | Ok(Point { 113 | table: self.table, 114 | timestamp, 115 | tags: self.tags, 116 | fields: self.fields, 117 | }) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/model/write/request.rs: -------------------------------------------------------------------------------- 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 | use std::collections::HashMap; 19 | 20 | use crate::model::write::point::Point; 21 | 22 | /// Write request. 23 | #[derive(Clone, Debug, Default)] 24 | pub struct Request { 25 | /// The points of different tables. 26 | pub point_groups: HashMap>, 27 | } 28 | 29 | impl Request { 30 | /// Add one point to the request. 31 | pub fn add_point(&mut self, point: Point) -> &mut Self { 32 | let points = self.point_groups.entry(point.table.clone()).or_default(); 33 | points.push(point); 34 | 35 | self 36 | } 37 | 38 | /// Add a batch points to the request. 39 | pub fn add_points(&mut self, points: Vec) -> &mut Self { 40 | for point in points { 41 | self.add_point(point); 42 | } 43 | 44 | self 45 | } 46 | } 47 | 48 | pub mod pb_builder { 49 | use std::collections::{BTreeMap, HashMap}; 50 | 51 | use horaedbproto::storage::{ 52 | Field, FieldGroup as FieldGroupPb, Tag as TagPb, WriteSeriesEntry as WriteSeriesEntryPb, 53 | WriteTableRequest as WriteTableRequestPb, 54 | }; 55 | 56 | use crate::model::{ 57 | value::{TimestampMs, Value}, 58 | write::{point::Point, Request}, 59 | }; 60 | 61 | type TagsKey = Vec; 62 | 63 | /// Used to build [`WriteRequestPb`](WriteTableRequestPb) from [Request]. 64 | pub struct WriteTableRequestPbsBuilder(pub Request); 65 | 66 | impl WriteTableRequestPbsBuilder { 67 | pub fn build(self) -> Vec { 68 | // Partition points by table. 69 | let point_group = self.0.point_groups; 70 | 71 | // Build pb. 72 | let mut table_request_pbs = Vec::with_capacity(point_group.len()); 73 | for (table, points) in point_group { 74 | let write_table_request_pb_builder = TableRequestPbBuilder::new(table, points); 75 | let write_table_request_pb = write_table_request_pb_builder.build(); 76 | table_request_pbs.push(write_table_request_pb); 77 | } 78 | 79 | table_request_pbs 80 | } 81 | } 82 | 83 | struct TableRequestPbBuilder { 84 | table: String, 85 | series_entires: Vec, 86 | } 87 | 88 | impl TableRequestPbBuilder { 89 | pub fn new(table: String, points: Vec) -> Self { 90 | // Partition points according to tags and build [WriteSeriesEntry]. 91 | let mut series_entries_by_tags = HashMap::new(); 92 | for point in points { 93 | assert_eq!(point.table, table); 94 | let tags_key = make_tags_key(&point.tags); 95 | let series_entry = 96 | series_entries_by_tags 97 | .entry(tags_key) 98 | .or_insert_with(|| SeriesEntry { 99 | tags: point.tags, 100 | ts_fields: BTreeMap::new(), 101 | }); 102 | series_entry.ts_fields.insert(point.timestamp, point.fields); 103 | } 104 | 105 | // Flatten the write series entires. 106 | let series_entires = series_entries_by_tags.into_values().collect(); 107 | 108 | Self { 109 | table, 110 | series_entires, 111 | } 112 | } 113 | 114 | pub fn build(self) -> WriteTableRequestPb { 115 | let mut tags_dict = NameDict::new(); 116 | let mut fields_dict = NameDict::new(); 117 | let mut wirte_entries_pb = Vec::with_capacity(self.series_entires.len()); 118 | for entry in self.series_entires { 119 | wirte_entries_pb.push(Self::build_series_entry( 120 | &mut tags_dict, 121 | &mut fields_dict, 122 | entry, 123 | )); 124 | } 125 | 126 | WriteTableRequestPb { 127 | table: self.table, 128 | tag_names: tags_dict.convert_ordered(), 129 | field_names: fields_dict.convert_ordered(), 130 | entries: wirte_entries_pb, 131 | } 132 | } 133 | 134 | fn build_series_entry( 135 | tags_dict: &mut NameDict, 136 | fields_dict: &mut NameDict, 137 | entry: SeriesEntry, 138 | ) -> WriteSeriesEntryPb { 139 | let tags = Self::build_tags(tags_dict, entry.tags); 140 | let field_groups = Self::build_ts_fields(fields_dict, entry.ts_fields); 141 | 142 | WriteSeriesEntryPb { tags, field_groups } 143 | } 144 | 145 | fn build_tags(tags_dict: &mut NameDict, tags: BTreeMap) -> Vec { 146 | if tags.is_empty() { 147 | return Vec::new(); 148 | } 149 | 150 | let mut tag_pbs = Vec::with_capacity(tags.len()); 151 | for (name, val) in tags { 152 | let tag_pb = TagPb { 153 | name_index: tags_dict.insert(name), 154 | value: Some(val.into()), 155 | }; 156 | tag_pbs.push(tag_pb); 157 | } 158 | 159 | tag_pbs 160 | } 161 | 162 | fn build_ts_fields( 163 | fields_dict: &mut NameDict, 164 | ts_fields: BTreeMap, 165 | ) -> Vec { 166 | if ts_fields.is_empty() { 167 | return Vec::new(); 168 | } 169 | 170 | let mut field_group_pbs = Vec::with_capacity(ts_fields.len()); 171 | for (ts, fields) in ts_fields { 172 | // Ts + fields will be converted to field group in pb. 173 | let mut field_pbs = Vec::with_capacity(fields.len()); 174 | for (name, val) in fields { 175 | let field_pb = Field { 176 | name_index: fields_dict.insert(name), 177 | value: Some(val.into()), 178 | }; 179 | field_pbs.push(field_pb); 180 | } 181 | let field_group_pb = FieldGroupPb { 182 | timestamp: ts, 183 | fields: field_pbs, 184 | }; 185 | 186 | // Collect field group. 187 | field_group_pbs.push(field_group_pb); 188 | } 189 | 190 | field_group_pbs 191 | } 192 | } 193 | 194 | #[derive(Clone, Default, Debug)] 195 | pub struct SeriesEntry { 196 | tags: BTreeMap, 197 | ts_fields: BTreeMap, 198 | } 199 | 200 | type Fields = BTreeMap; 201 | 202 | /// Struct helps to convert [`WriteRequest`] to [`WriteRequestPb`]. 203 | struct NameDict { 204 | dict: HashMap, 205 | name_idx: u32, 206 | } 207 | 208 | impl NameDict { 209 | fn new() -> Self { 210 | NameDict { 211 | dict: HashMap::new(), 212 | name_idx: 0, 213 | } 214 | } 215 | 216 | fn insert(&mut self, name: String) -> u32 { 217 | *self.dict.entry(name).or_insert_with(|| { 218 | let old_name_idx = self.name_idx; 219 | self.name_idx += 1; 220 | old_name_idx 221 | }) 222 | } 223 | 224 | fn convert_ordered(self) -> Vec { 225 | let mut ordered = vec![String::new(); self.dict.len()]; 226 | self.dict 227 | .into_iter() 228 | .for_each(|(name, idx)| ordered[idx as usize] = name); 229 | ordered 230 | } 231 | } 232 | 233 | pub fn make_tags_key(tags: &BTreeMap) -> TagsKey { 234 | let mut series_key = Vec::default(); 235 | for (name, val) in tags { 236 | series_key.extend(name.as_bytes()); 237 | series_key.extend_from_slice(&val.to_bytes()); 238 | } 239 | 240 | series_key 241 | } 242 | } 243 | 244 | #[cfg(test)] 245 | mod test { 246 | use std::collections::BTreeMap; 247 | 248 | use chrono::Local; 249 | 250 | use super::pb_builder::make_tags_key; 251 | use crate::model::{ 252 | value::Value, 253 | write::{ 254 | point::{Point, PointBuilder}, 255 | request::pb_builder::WriteTableRequestPbsBuilder, 256 | Request, 257 | }, 258 | }; 259 | 260 | #[test] 261 | fn test_build_write_table() { 262 | let ts1 = Local::now().timestamp_millis(); 263 | let ts2 = ts1 + 50; 264 | // With same table and tags. 265 | let test_table = "test_table"; 266 | let test_tag1 = ("test_tag1", 42); 267 | let test_tag2 = ("test_tag2", "test_tag_val"); 268 | let test_field1 = ("test_field1", 42); 269 | let test_field2 = ("test_field2", "test_field_val"); 270 | let test_field3 = ("test_field3", 0.42); 271 | // With same table but different tags. 272 | let test_tag3 = ("test_tag1", b"binarybinary"); 273 | // With different table. 274 | let test_table2 = "test_table2"; 275 | 276 | // Build write request. 277 | let mut write_req = Request::default(); 278 | 279 | let points = vec![ 280 | PointBuilder::new(test_table.to_string()) 281 | .timestamp(ts1) 282 | .tag(test_tag1.0.to_owned(), Value::Int32(test_tag1.1)) 283 | .tag( 284 | test_tag2.0.to_owned(), 285 | Value::String(test_tag2.1.to_owned()), 286 | ) 287 | .field(test_field1.0.to_owned(), Value::Int32(test_field1.1)) 288 | .build() 289 | .unwrap(), 290 | PointBuilder::new(test_table.to_string()) 291 | .timestamp(ts1) 292 | .tag(test_tag1.0.to_owned(), Value::Int32(test_tag1.1)) 293 | .tag( 294 | test_tag2.0.to_owned(), 295 | Value::String(test_tag2.1.to_owned()), 296 | ) 297 | .field( 298 | test_field2.0.to_owned(), 299 | Value::String(test_field2.1.to_owned()), 300 | ) 301 | .build() 302 | .unwrap(), 303 | PointBuilder::new(test_table.to_string()) 304 | .timestamp(ts2) 305 | .tag(test_tag1.0.to_owned(), Value::Int32(test_tag1.1)) 306 | .tag( 307 | test_tag2.0.to_owned(), 308 | Value::String(test_tag2.1.to_owned()), 309 | ) 310 | .field(test_field3.0.to_owned(), Value::Double(test_field3.1)) 311 | .build() 312 | .unwrap(), 313 | PointBuilder::new(test_table.to_string()) 314 | .timestamp(ts1) 315 | .tag(test_tag1.0.to_owned(), Value::Int32(test_tag1.1)) 316 | .tag( 317 | test_tag2.0.to_owned(), 318 | Value::String(test_tag2.1.to_owned()), 319 | ) 320 | .tag( 321 | test_tag3.0.to_owned(), 322 | Value::Varbinary(test_tag3.1.to_vec()), 323 | ) 324 | .field(test_field1.0.to_owned(), Value::Int32(test_field1.1)) 325 | .build() 326 | .unwrap(), 327 | ]; 328 | 329 | let points2 = vec![PointBuilder::new(test_table2.to_string()) 330 | .timestamp(ts1) 331 | .tag(test_tag1.0.to_owned(), Value::Int32(test_tag1.1)) 332 | .tag( 333 | test_tag2.0.to_owned(), 334 | Value::String(test_tag2.1.to_owned()), 335 | ) 336 | .field(test_field1.0.to_owned(), Value::Int32(test_field1.1)) 337 | .build() 338 | .unwrap()]; 339 | 340 | write_req.add_points(points).add_points(points2); 341 | 342 | // Build pb. 343 | let table_requests = WriteTableRequestPbsBuilder(write_req.clone()).build(); 344 | // Recover points from pb and compare. 345 | let mut points = Vec::new(); 346 | for table_request in table_requests { 347 | let tag_names = table_request.tag_names; 348 | let field_names = table_request.field_names; 349 | for entry in table_request.entries { 350 | let tags = entry 351 | .tags 352 | .into_iter() 353 | .map(|tag| { 354 | let tag_name = tag_names[tag.name_index as usize].clone(); 355 | let tag_value = Value::from(tag.value.unwrap()); 356 | (tag_name, tag_value) 357 | }) 358 | .collect::>(); 359 | 360 | for ts_field in entry.field_groups { 361 | let timestamp = ts_field.timestamp; 362 | let fields = ts_field 363 | .fields 364 | .into_iter() 365 | .map(|field| { 366 | let field_name = field_names[field.name_index as usize].clone(); 367 | let field_value = Value::from(field.value.unwrap()); 368 | (field_name, field_value) 369 | }) 370 | .collect::>(); 371 | 372 | let point = Point { 373 | table: table_request.table.clone(), 374 | timestamp, 375 | tags: tags.clone(), 376 | fields, 377 | }; 378 | 379 | points.push(point); 380 | } 381 | } 382 | } 383 | 384 | // Compare original and recovered. 385 | let mut expected_points = BTreeMap::new(); 386 | for (_, points) in write_req.point_groups { 387 | let points = points.into_iter().map(|point| { 388 | let cmp_key = make_cmp_key(&point); 389 | (cmp_key, point) 390 | }); 391 | expected_points.extend(points); 392 | } 393 | let expected_points = expected_points.into_values().collect::>(); 394 | 395 | make_ordered(&mut points); 396 | 397 | assert_eq!(points, expected_points); 398 | } 399 | 400 | fn make_cmp_key(point: &Point) -> (Vec, i64) { 401 | let mut series_key = point.table.as_bytes().to_vec(); 402 | let tagks_key = make_tags_key(&point.tags); 403 | series_key.extend(tagks_key); 404 | 405 | (series_key, point.timestamp) 406 | } 407 | 408 | fn make_ordered(points: &mut [Point]) { 409 | points.sort_by(|point1, point2| { 410 | let mut series_key1 = point1.table.as_bytes().to_vec(); 411 | let mut series_key2 = point2.table.as_bytes().to_vec(); 412 | let tagks_key1 = make_tags_key(&point1.tags); 413 | let tagks_key2 = make_tags_key(&point2.tags); 414 | series_key1.extend(tagks_key1); 415 | series_key2.extend(tagks_key2); 416 | let cmp_key1 = (series_key1, point1.timestamp); 417 | let cmp_key2 = (series_key2, point2.timestamp); 418 | 419 | cmp_key1.cmp(&cmp_key2) 420 | }); 421 | } 422 | } 423 | -------------------------------------------------------------------------------- /src/model/write/response.rs: -------------------------------------------------------------------------------- 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 | use horaedbproto::storage::WriteResponse as WriteResponsePb; 19 | 20 | /// The response for the [`WriteRequest`](crate::model::write::Request). 21 | #[derive(Clone, Debug)] 22 | pub struct Response { 23 | /// The number of the rows written successfully 24 | pub success: u32, 25 | /// The number of the rows which fail to write 26 | pub failed: u32, 27 | } 28 | 29 | impl Response { 30 | pub fn new(success: u32, failed: u32) -> Self { 31 | Self { success, failed } 32 | } 33 | } 34 | 35 | impl From for Response { 36 | fn from(resp_pb: WriteResponsePb) -> Self { 37 | Response { 38 | success: resp_pb.success, 39 | failed: resp_pb.failed, 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/router.rs: -------------------------------------------------------------------------------- 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 | use std::{collections::HashMap, sync::Arc}; 19 | 20 | use async_trait::async_trait; 21 | use dashmap::DashMap; 22 | use horaedbproto::storage::{self, RouteRequest}; 23 | 24 | use crate::{ 25 | errors::Result, 26 | model::route::Endpoint, 27 | rpc_client::{RpcClient, RpcContext}, 28 | Error, 29 | }; 30 | 31 | /// Used to route tables to endpoints. 32 | #[async_trait] 33 | pub trait Router: Send + Sync { 34 | async fn route(&self, tables: &[String], ctx: &RpcContext) -> Result>>; 35 | 36 | fn evict(&self, tables: &[String]); 37 | } 38 | 39 | /// Implementation for [`Router`]. 40 | /// 41 | /// There is cache in [`RouterImpl`], it will return endpoints in cache first. 42 | /// If returned endpoints is outdated, you should call [`evict`] to remove them. 43 | /// And [`RouterImpl`] will fetch new endpoints when you call ['route'] again. 44 | /// 45 | /// [`route`]: RouterImpl::route 46 | /// [`evict`]: RouterImpl::evict 47 | pub struct RouterImpl { 48 | default_endpoint: Endpoint, 49 | cache: DashMap, 50 | rpc_client: Arc, 51 | } 52 | 53 | impl RouterImpl { 54 | pub fn new(default_endpoint: Endpoint, rpc_client: Arc) -> Self { 55 | Self { 56 | default_endpoint, 57 | cache: DashMap::new(), 58 | rpc_client, 59 | } 60 | } 61 | } 62 | 63 | #[async_trait] 64 | impl Router for RouterImpl { 65 | async fn route(&self, tables: &[String], ctx: &RpcContext) -> Result>> { 66 | assert!(ctx.database.is_some()); 67 | 68 | let mut target_endpoints = vec![Some(self.default_endpoint.clone()); tables.len()]; 69 | 70 | // Find from cache firstly and collect misses. 71 | let misses = { 72 | let mut misses = HashMap::new(); 73 | for (idx, table) in tables.iter().enumerate() { 74 | match self.cache.get(table) { 75 | Some(pair) => { 76 | target_endpoints[idx] = Some(pair.value().clone()); 77 | } 78 | 79 | None => { 80 | misses.insert(table.clone(), idx); 81 | } 82 | } 83 | } 84 | misses 85 | }; 86 | 87 | // Get endpoints of misses from remote. 88 | let req_ctx = storage::RequestContext { 89 | database: ctx.database.clone().unwrap(), 90 | }; 91 | let miss_tables = misses.keys().cloned().collect(); 92 | let req = RouteRequest { 93 | context: Some(req_ctx), 94 | tables: miss_tables, 95 | }; 96 | let resp = self.rpc_client.route(ctx, req).await?; 97 | 98 | // Fill miss endpoint and update cache. 99 | for route in resp.routes { 100 | // Endpoint may be none, and not cache it when it is none. 101 | if route.endpoint.is_none() { 102 | continue; 103 | } 104 | 105 | // Impossible to get none. 106 | let idx = misses.get(&route.table).ok_or_else(|| { 107 | Error::Unknown(format!("Unknown table:{} in response", route.table)) 108 | })?; 109 | let endpoint: Endpoint = route.endpoint.unwrap().into(); 110 | self.cache.insert(route.table, endpoint.clone()); 111 | target_endpoints[*idx] = Some(endpoint); 112 | } 113 | 114 | Ok(target_endpoints) 115 | } 116 | 117 | fn evict(&self, tables: &[String]) { 118 | tables.iter().for_each(|e| { 119 | self.cache.remove(e.as_str()); 120 | }) 121 | } 122 | } 123 | 124 | #[cfg(test)] 125 | mod test { 126 | use std::sync::Arc; 127 | 128 | use dashmap::DashMap; 129 | 130 | use super::{Router, RouterImpl}; 131 | use crate::{ 132 | model::route::Endpoint, 133 | rpc_client::{MockRpcClient, RpcContext}, 134 | }; 135 | 136 | #[tokio::test] 137 | async fn test_basic_flow() { 138 | // Init mock route table 139 | let table1 = "table1".to_string(); 140 | let table2 = "table2".to_string(); 141 | let table3 = "table3".to_string(); 142 | let table4 = "table4".to_string(); 143 | let endpoint1 = Endpoint::new("192.168.0.1".to_string(), 11); 144 | let endpoint2 = Endpoint::new("192.168.0.2".to_string(), 12); 145 | let endpoint3 = Endpoint::new("192.168.0.3".to_string(), 13); 146 | let endpoint4 = Endpoint::new("192.168.0.4".to_string(), 14); 147 | let default_endpoint = Endpoint::new("192.168.0.5".to_string(), 15); 148 | 149 | // Init mock client with route1 and route2 150 | let route_table = Arc::new(DashMap::default()); 151 | let mock_rpc_client = MockRpcClient { 152 | route_table: route_table.clone(), 153 | }; 154 | mock_rpc_client 155 | .route_table 156 | .insert(table1.clone(), endpoint1.clone()); 157 | mock_rpc_client 158 | .route_table 159 | .insert(table2.clone(), endpoint2.clone()); 160 | 161 | // Follow these steps to check wether cache is used or not: 162 | // route --> change route_table --> route again. 163 | let ctx = RpcContext { 164 | database: Some("db".to_string()), 165 | timeout: None, 166 | }; 167 | let tables = vec![table1.clone(), table2.clone()]; 168 | let route_client = RouterImpl::new(default_endpoint.clone(), Arc::new(mock_rpc_client)); 169 | let route_res1 = route_client.route(&tables, &ctx).await.unwrap(); 170 | assert_eq!(&endpoint1, route_res1.first().unwrap().as_ref().unwrap()); 171 | assert_eq!(&endpoint2, route_res1.get(1).unwrap().as_ref().unwrap()); 172 | 173 | route_table.insert(table1.clone(), endpoint3.clone()); 174 | route_table.insert(table2.clone(), endpoint4.clone()); 175 | 176 | let route_res2 = route_client.route(&tables, &ctx).await.unwrap(); 177 | assert_eq!(&endpoint1, route_res2.first().unwrap().as_ref().unwrap()); 178 | assert_eq!(&endpoint2, route_res2.get(1).unwrap().as_ref().unwrap()); 179 | 180 | route_client.evict(&[table1.clone(), table2.clone()]); 181 | 182 | let route_res3 = route_client.route(&tables, &ctx).await.unwrap(); 183 | assert_eq!(&endpoint3, route_res3.first().unwrap().as_ref().unwrap()); 184 | assert_eq!(&endpoint4, route_res3.get(1).unwrap().as_ref().unwrap()); 185 | 186 | let route_res4 = route_client.route(&[table3, table4], &ctx).await.unwrap(); 187 | assert_eq!( 188 | &default_endpoint, 189 | route_res4.first().unwrap().as_ref().unwrap() 190 | ); 191 | assert_eq!( 192 | &default_endpoint, 193 | route_res4.get(1).unwrap().as_ref().unwrap() 194 | ); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/rpc_client/mock_rpc_client.rs: -------------------------------------------------------------------------------- 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 | use std::sync::Arc; 19 | 20 | use async_trait::async_trait; 21 | use dashmap::DashMap; 22 | use horaedbproto::storage::{ 23 | Endpoint as EndpointPb, Route as RoutePb, RouteRequest as RouteRequestPb, 24 | RouteResponse as RouteResponsePb, SqlQueryRequest as QueryRequestPb, 25 | SqlQueryResponse as QueryResponsePb, WriteRequest as WriteRequestPb, 26 | WriteResponse as WriteResponsePb, 27 | }; 28 | 29 | use crate::{ 30 | model::route::Endpoint, 31 | rpc_client::{RpcClient, RpcContext}, 32 | Result, 33 | }; 34 | 35 | /// Rpc client used for testing. 36 | pub struct MockRpcClient { 37 | pub route_table: Arc>, 38 | } 39 | 40 | #[async_trait] 41 | impl RpcClient for MockRpcClient { 42 | async fn sql_query(&self, _ctx: &RpcContext, _req: QueryRequestPb) -> Result { 43 | todo!() 44 | } 45 | 46 | async fn write(&self, _ctx: &RpcContext, _req: WriteRequestPb) -> Result { 47 | todo!() 48 | } 49 | 50 | async fn route(&self, _ctx: &RpcContext, req: RouteRequestPb) -> Result { 51 | let route_tables = self.route_table.clone(); 52 | let routes: Vec<_> = req 53 | .tables 54 | .iter() 55 | .filter_map(|m| { 56 | let endpoint = match route_tables.get(m.as_str()) { 57 | Some(v) => v.value().clone(), 58 | None => return None, 59 | }; 60 | let mut route_pb = RoutePb::default(); 61 | let endpoint_pb = EndpointPb { 62 | ip: endpoint.addr, 63 | port: endpoint.port, 64 | }; 65 | route_pb.table.clone_from(m); 66 | route_pb.endpoint = Some(endpoint_pb); 67 | Some(route_pb) 68 | }) 69 | .collect(); 70 | let route_resp = RouteResponsePb { 71 | header: None, 72 | routes, 73 | }; 74 | Ok(route_resp) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/rpc_client/mod.rs: -------------------------------------------------------------------------------- 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 | mod mock_rpc_client; 19 | mod rpc_client_impl; 20 | 21 | use std::{sync::Arc, time::Duration}; 22 | 23 | use async_trait::async_trait; 24 | use horaedbproto::storage::{ 25 | RouteRequest as RouteRequestPb, RouteResponse as RouteResponsePb, 26 | SqlQueryRequest as QueryRequestPb, SqlQueryResponse as QueryResponsePb, 27 | WriteRequest as WriteRequestPb, WriteResponse as WriteResponsePb, 28 | }; 29 | #[cfg(test)] 30 | pub use mock_rpc_client::MockRpcClient; 31 | pub use rpc_client_impl::RpcClientImplFactory; 32 | 33 | use crate::errors::Result; 34 | 35 | /// Context for rpc request. 36 | #[derive(Clone, Debug, Default)] 37 | pub struct RpcContext { 38 | pub database: Option, 39 | pub timeout: Option, 40 | } 41 | 42 | impl RpcContext { 43 | pub fn database(mut self, database: String) -> Self { 44 | self.database = Some(database); 45 | self 46 | } 47 | 48 | pub fn timeout(mut self, timeout: Duration) -> Self { 49 | self.timeout = Some(timeout); 50 | self 51 | } 52 | } 53 | #[async_trait] 54 | pub trait RpcClient: Send + Sync { 55 | async fn sql_query(&self, ctx: &RpcContext, req: QueryRequestPb) -> Result; 56 | async fn write(&self, ctx: &RpcContext, req: WriteRequestPb) -> Result; 57 | async fn route(&self, ctx: &RpcContext, req: RouteRequestPb) -> Result; 58 | } 59 | 60 | #[async_trait] 61 | pub trait RpcClientFactory: Send + Sync { 62 | /// Build `RpcClient`. 63 | /// 64 | /// It may fail because of invalid endpoint. Any caller calls this method 65 | /// should handle the potential error. 66 | async fn build(&self, endpoint: String) -> Result>; 67 | } 68 | -------------------------------------------------------------------------------- /src/rpc_client/rpc_client_impl.rs: -------------------------------------------------------------------------------- 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 | use std::{sync::Arc, time::Duration}; 19 | 20 | use anyhow::Context; 21 | use async_trait::async_trait; 22 | use base64::{prelude::BASE64_STANDARD, Engine}; 23 | use horaedbproto::{ 24 | common::ResponseHeader, 25 | storage::{ 26 | storage_service_client::StorageServiceClient, RouteRequest as RouteRequestPb, 27 | RouteResponse as RouteResponsePb, SqlQueryRequest, SqlQueryResponse, 28 | WriteRequest as WriteRequestPb, WriteResponse as WriteResponsePb, 29 | }, 30 | }; 31 | use tonic::{ 32 | metadata::{Ascii, MetadataValue}, 33 | transport::{Channel, Endpoint}, 34 | Request, 35 | }; 36 | 37 | use crate::{ 38 | config::RpcConfig, 39 | errors::{Error, Result, ServerError}, 40 | rpc_client::{RpcClient, RpcClientFactory, RpcContext}, 41 | util::is_ok, 42 | Authorization, 43 | }; 44 | 45 | struct RpcClientImpl { 46 | channel: Channel, 47 | default_read_timeout: Duration, 48 | default_write_timeout: Duration, 49 | metadata: Option>, 50 | } 51 | 52 | impl RpcClientImpl { 53 | fn new( 54 | channel: Channel, 55 | default_read_timeout: Duration, 56 | default_write_timeout: Duration, 57 | metadata: Option>, 58 | ) -> Self { 59 | Self { 60 | channel, 61 | default_read_timeout, 62 | default_write_timeout, 63 | metadata, 64 | } 65 | } 66 | 67 | fn check_status(header: ResponseHeader) -> Result<()> { 68 | if !is_ok(header.code) { 69 | return Err(Error::Server(ServerError { 70 | code: header.code, 71 | msg: header.error, 72 | })); 73 | } 74 | 75 | Ok(()) 76 | } 77 | 78 | fn make_request(&self, ctx: &RpcContext, req: T, default_timeout: Duration) -> Request { 79 | let timeout = ctx.timeout.unwrap_or(default_timeout); 80 | let mut req = Request::new(req); 81 | req.set_timeout(timeout); 82 | if let Some(md) = &self.metadata { 83 | req.metadata_mut().insert("authorization", md.clone()); 84 | } 85 | req 86 | } 87 | 88 | fn make_query_request(&self, ctx: &RpcContext, req: T) -> Request { 89 | self.make_request(ctx, req, self.default_read_timeout) 90 | } 91 | 92 | fn make_write_request(&self, ctx: &RpcContext, req: T) -> Request { 93 | self.make_request(ctx, req, self.default_write_timeout) 94 | } 95 | } 96 | 97 | #[async_trait] 98 | impl RpcClient for RpcClientImpl { 99 | async fn sql_query(&self, ctx: &RpcContext, req: SqlQueryRequest) -> Result { 100 | let mut client = StorageServiceClient::::new(self.channel.clone()); 101 | 102 | let resp = client 103 | .sql_query(self.make_query_request(ctx, req)) 104 | .await 105 | .map_err(Error::Rpc)?; 106 | let mut resp = resp.into_inner(); 107 | 108 | if let Some(header) = resp.header.take() { 109 | Self::check_status(header)?; 110 | } 111 | 112 | Ok(resp) 113 | } 114 | 115 | async fn write(&self, ctx: &RpcContext, req: WriteRequestPb) -> Result { 116 | let mut client = StorageServiceClient::::new(self.channel.clone()); 117 | 118 | let resp = client 119 | .write(self.make_write_request(ctx, req)) 120 | .await 121 | .map_err(Error::Rpc)?; 122 | let mut resp = resp.into_inner(); 123 | 124 | if let Some(header) = resp.header.take() { 125 | Self::check_status(header)?; 126 | } 127 | 128 | Ok(resp) 129 | } 130 | 131 | async fn route(&self, ctx: &RpcContext, req: RouteRequestPb) -> Result { 132 | let mut client = StorageServiceClient::::new(self.channel.clone()); 133 | 134 | // use the write timeout for the route request. 135 | let route_req = self.make_request(ctx, req, self.default_write_timeout); 136 | let resp = client.route(route_req).await.map_err(Error::Rpc)?; 137 | let mut resp = resp.into_inner(); 138 | 139 | if let Some(header) = resp.header.take() { 140 | Self::check_status(header)?; 141 | } 142 | 143 | Ok(resp) 144 | } 145 | } 146 | 147 | pub struct RpcClientImplFactory { 148 | rpc_config: RpcConfig, 149 | authorization: Option, 150 | } 151 | 152 | impl RpcClientImplFactory { 153 | pub fn new(rpc_config: RpcConfig, authorization: Option) -> Self { 154 | Self { 155 | rpc_config, 156 | authorization, 157 | } 158 | } 159 | 160 | #[inline] 161 | fn make_endpoint_with_scheme(endpoint: &str) -> String { 162 | format!("http://{endpoint}") 163 | } 164 | } 165 | 166 | #[async_trait] 167 | impl RpcClientFactory for RpcClientImplFactory { 168 | /// The endpoint should be in the form: `{ip_addr}:{port}`. 169 | async fn build(&self, endpoint: String) -> Result> { 170 | let endpoint_with_scheme = Self::make_endpoint_with_scheme(&endpoint); 171 | let configured_endpoint = 172 | Endpoint::from_shared(endpoint_with_scheme).map_err(|e| Error::Connect { 173 | addr: endpoint.clone(), 174 | source: Box::new(e), 175 | })?; 176 | 177 | let configured_endpoint = match self.rpc_config.keep_alive_while_idle { 178 | true => configured_endpoint 179 | .connect_timeout(self.rpc_config.connect_timeout) 180 | .keep_alive_timeout(self.rpc_config.keep_alive_timeout) 181 | .keep_alive_while_idle(true) 182 | .http2_keep_alive_interval(self.rpc_config.keep_alive_interval), 183 | false => configured_endpoint 184 | .connect_timeout(self.rpc_config.connect_timeout) 185 | .keep_alive_while_idle(false), 186 | }; 187 | let channel = configured_endpoint 188 | .connect() 189 | .await 190 | .map_err(|e| Error::Connect { 191 | addr: endpoint, 192 | source: Box::new(e), 193 | })?; 194 | 195 | let metadata = if let Some(auth) = &self.authorization { 196 | let mut buf = Vec::with_capacity(auth.username.len() + auth.password.len() + 1); 197 | buf.extend_from_slice(auth.username.as_bytes()); 198 | buf.push(b':'); 199 | buf.extend_from_slice(auth.password.as_bytes()); 200 | let auth = BASE64_STANDARD.encode(&buf); 201 | let metadata: MetadataValue = format!("Basic {}", auth) 202 | .parse() 203 | .context("invalid grpc metadata")?; 204 | 205 | Some(metadata) 206 | } else { 207 | None 208 | }; 209 | Ok(Arc::new(RpcClientImpl::new( 210 | channel, 211 | self.rpc_config.default_sql_query_timeout, 212 | self.rpc_config.default_write_timeout, 213 | metadata, 214 | ))) 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 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 | /// Server status code 19 | #[allow(dead_code)] 20 | #[derive(Debug, Clone, Copy)] 21 | pub enum StatusCode { 22 | Ok = 200, 23 | InvalidArgument = 400, 24 | NotFound = 404, 25 | TooManyRequests = 429, 26 | InternalError = 500, 27 | } 28 | 29 | impl StatusCode { 30 | pub fn as_u32(&self) -> u32 { 31 | *self as u32 32 | } 33 | } 34 | 35 | #[inline] 36 | pub fn is_ok(code: u32) -> bool { 37 | code == StatusCode::Ok.as_u32() 38 | } 39 | 40 | // TODO may change in future. 41 | #[inline] 42 | pub fn should_refresh(code: u32, msg: &str) -> bool { 43 | code == StatusCode::InvalidArgument.as_u32() 44 | && msg.contains("Table") 45 | && msg.contains("not found") 46 | } 47 | --------------------------------------------------------------------------------