├── .github
└── workflows
│ └── rust.yml
├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── build.rs
├── src
├── arg.rs
├── bindings.rs
├── error.rs
├── format.rs
├── lib.rs
├── log_level.rs
├── query_result.rs
└── session.rs
├── tests
├── examples.rs
└── logs.csv
├── update_libchdb.sh
└── wrapper.h
/.github/workflows/rust.yml:
--------------------------------------------------------------------------------
1 | name: Rust
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "main" ]
8 |
9 | env:
10 | CARGO_TERM_COLOR: always
11 |
12 | jobs:
13 | build:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v3
17 | - name: Fetch library
18 | run: |
19 | #wget https://github.com/chdb-io/chdb/releases/latest/download/linux-x86_64-libchdb.tar.gz
20 | #Fix libchdb version to v2.1.1 until libchdb v3 based chdb-rust refactor
21 | wget https://github.com/chdb-io/chdb/releases/download/v2.1.1/linux-x86_64-libchdb.tar.gz
22 | tar -xzf linux-x86_64-libchdb.tar.gz
23 | sudo mv libchdb.so /usr/lib/libchdb.so
24 | sudo ldconfig
25 | - name: Build
26 | run: cargo build --verbose
27 | env:
28 | RUST_BACKTRACE: full
29 | - name: Run tests
30 | run: cargo test
31 | env:
32 | RUST_BACKTRACE: full
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | Cargo.lock
3 | *.tar.gz
4 | *.so
5 | chdb.h
6 | var
7 | udf
8 | *.parquet
9 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "chdb-rust"
3 | version = "1.0.0"
4 | edition = "2021"
5 | keywords = ["clickhouse", "chdb", "database", "embedded", "analytics"]
6 |
7 | [dependencies]
8 | thiserror = "1"
9 |
10 | [build-dependencies]
11 | bindgen = "0.70.1"
12 |
13 | [dev-dependencies]
14 | tempdir = "0.3.7"
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [](https://github.com/chdb-io/chdb-rust/actions/workflows/rust.yml)
4 |
5 | # chdb-rust
6 |
7 | Experimental [chDB](https://github.com/chdb-io/chdb) FFI bindings for Rust
8 |
9 | ## Status
10 |
11 | - Experimental, unstable, subject to changes
12 | - Requires [`libchdb`](https://github.com/chdb-io/chdb) on the system. You can install the compatible version from
13 | `install_libchdb.sh`
14 |
15 | ## Usage
16 |
17 | ### Install libchdb
18 |
19 | You can install it system-wide
20 |
21 | ```bash
22 | ./update_libchdb.sh --global
23 | ```
24 |
25 | or use it in a local directory
26 |
27 | ```bash
28 | ./update_libchdb.sh --local
29 | ```
30 |
31 | ### Build
32 |
33 | ```bash
34 | RUST_BACKTRACE=full cargo build --verbose
35 |
36 | ```
37 |
38 | ### Run tests
39 |
40 | `cargo test`
41 |
42 | ### Examples
43 |
44 | See `tests` directory.
45 |
--------------------------------------------------------------------------------
/build.rs:
--------------------------------------------------------------------------------
1 | use std::path::PathBuf;
2 |
3 | fn main() {
4 | // Tell cargo to look for shared libraries in the specified directory
5 | println!("cargo:rustc-link-search=./");
6 |
7 | // Tell cargo to tell rustc to link the system chdb library.
8 | println!("cargo:rustc-link-lib=chdb");
9 |
10 | // Tell cargo to invalidate the built crate whenever the wrapper changes.
11 | println!("cargo:rerun-if-changed=chdb.h");
12 |
13 | // The bindgen::Builder is the main entry point
14 | // to bindgen, and lets you build up options for
15 | // the resulting bindings.
16 | let bindings = bindgen::Builder::default()
17 | // The input header we would like to generate
18 | // bindings for.
19 | .header("wrapper.h")
20 | // Tell cargo to invalidate the built crate whenever any of the
21 | // included header files changed.
22 | .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
23 | // Finish the builder and generate the bindings.
24 | .generate()
25 | // Unwrap the Result and panic on failure.
26 | .expect("Unable to generate bindings");
27 |
28 | // Write the bindings to the $OUT_DIR/bindings.rs file.
29 | let out_path = PathBuf::from("./src/");
30 | bindings
31 | .write_to_file(out_path.join("bindings.rs"))
32 | .expect("Couldn't write bindings!");
33 | }
34 |
--------------------------------------------------------------------------------
/src/arg.rs:
--------------------------------------------------------------------------------
1 | use std::borrow::Cow;
2 | use std::ffi::CString;
3 |
4 | use crate::error::Error;
5 | use crate::format::OutputFormat;
6 | use crate::log_level::LogLevel;
7 |
8 | #[derive(Debug)]
9 | pub enum Arg<'a> {
10 | /// --config-file=
11 | ConfigFilePath(Cow<'a, str>),
12 | /// --log-level=
13 | LogLevel(LogLevel),
14 | /// --output-format=
15 | OutputFormat(OutputFormat),
16 | /// --multiquery
17 | MultiQuery,
18 | /// Custom argument.
19 | ///
20 | /// "--path=/tmp/chdb" translates into one of the following:
21 | /// 1. Arg::Custom("path".to_string().into(), Some("/tmp/chdb".to_string().into())).
22 | /// 2. Arg::Custom("path".into(), Some("/tmp/chdb".into())).
23 | ///
24 | /// "--multiline" translates into one of the following:
25 | /// 1. Arg::Custom("multiline".to_string().into(), None).
26 | /// 2. Arg::Custom("multiline".into(), None).
27 | ///
28 | /// We should tell user where to look for officially supported arguments.
29 | /// Here is some hint for now: https://github.com/fixcik/chdb-rs/blob/master/OPTIONS.md .
30 | Custom(Cow<'a, str>, Option>),
31 | }
32 |
33 | impl<'a> Arg<'a> {
34 | pub(crate) fn to_cstring(&self) -> Result {
35 | Ok(match self {
36 | Self::ConfigFilePath(v) => CString::new(format!("--config-file={}", v)),
37 | Self::LogLevel(v) => CString::new(format!("--log-level={}", v.as_str())),
38 | Self::OutputFormat(v) => CString::new(format!("--output-format={}", v.as_str())),
39 | Self::MultiQuery => CString::new("-n"),
40 | Self::Custom(k, v) => match v {
41 | None => CString::new(k.as_ref()),
42 | Some(v) => CString::new(format!("--{}={}", k, v)),
43 | },
44 | }?)
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/bindings.rs:
--------------------------------------------------------------------------------
1 | /* automatically generated by rust-bindgen 0.70.1 */
2 |
3 | pub const _STDINT_H: u32 = 1;
4 | pub const _FEATURES_H: u32 = 1;
5 | pub const _DEFAULT_SOURCE: u32 = 1;
6 | pub const __GLIBC_USE_ISOC2X: u32 = 0;
7 | pub const __USE_ISOC11: u32 = 1;
8 | pub const __USE_ISOC99: u32 = 1;
9 | pub const __USE_ISOC95: u32 = 1;
10 | pub const __USE_POSIX_IMPLICITLY: u32 = 1;
11 | pub const _POSIX_SOURCE: u32 = 1;
12 | pub const _POSIX_C_SOURCE: u32 = 200809;
13 | pub const __USE_POSIX: u32 = 1;
14 | pub const __USE_POSIX2: u32 = 1;
15 | pub const __USE_POSIX199309: u32 = 1;
16 | pub const __USE_POSIX199506: u32 = 1;
17 | pub const __USE_XOPEN2K: u32 = 1;
18 | pub const __USE_XOPEN2K8: u32 = 1;
19 | pub const _ATFILE_SOURCE: u32 = 1;
20 | pub const __WORDSIZE: u32 = 64;
21 | pub const __WORDSIZE_TIME64_COMPAT32: u32 = 1;
22 | pub const __SYSCALL_WORDSIZE: u32 = 64;
23 | pub const __TIMESIZE: u32 = 64;
24 | pub const __USE_MISC: u32 = 1;
25 | pub const __USE_ATFILE: u32 = 1;
26 | pub const __USE_FORTIFY_LEVEL: u32 = 0;
27 | pub const __GLIBC_USE_DEPRECATED_GETS: u32 = 0;
28 | pub const __GLIBC_USE_DEPRECATED_SCANF: u32 = 0;
29 | pub const __GLIBC_USE_C2X_STRTOL: u32 = 0;
30 | pub const _STDC_PREDEF_H: u32 = 1;
31 | pub const __STDC_IEC_559__: u32 = 1;
32 | pub const __STDC_IEC_60559_BFP__: u32 = 201404;
33 | pub const __STDC_IEC_559_COMPLEX__: u32 = 1;
34 | pub const __STDC_IEC_60559_COMPLEX__: u32 = 201404;
35 | pub const __STDC_ISO_10646__: u32 = 201706;
36 | pub const __GNU_LIBRARY__: u32 = 6;
37 | pub const __GLIBC__: u32 = 2;
38 | pub const __GLIBC_MINOR__: u32 = 39;
39 | pub const _SYS_CDEFS_H: u32 = 1;
40 | pub const __glibc_c99_flexarr_available: u32 = 1;
41 | pub const __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI: u32 = 0;
42 | pub const __HAVE_GENERIC_SELECTION: u32 = 1;
43 | pub const __GLIBC_USE_LIB_EXT2: u32 = 0;
44 | pub const __GLIBC_USE_IEC_60559_BFP_EXT: u32 = 0;
45 | pub const __GLIBC_USE_IEC_60559_BFP_EXT_C2X: u32 = 0;
46 | pub const __GLIBC_USE_IEC_60559_EXT: u32 = 0;
47 | pub const __GLIBC_USE_IEC_60559_FUNCS_EXT: u32 = 0;
48 | pub const __GLIBC_USE_IEC_60559_FUNCS_EXT_C2X: u32 = 0;
49 | pub const __GLIBC_USE_IEC_60559_TYPES_EXT: u32 = 0;
50 | pub const _BITS_TYPES_H: u32 = 1;
51 | pub const _BITS_TYPESIZES_H: u32 = 1;
52 | pub const __OFF_T_MATCHES_OFF64_T: u32 = 1;
53 | pub const __INO_T_MATCHES_INO64_T: u32 = 1;
54 | pub const __RLIM_T_MATCHES_RLIM64_T: u32 = 1;
55 | pub const __STATFS_MATCHES_STATFS64: u32 = 1;
56 | pub const __KERNEL_OLD_TIMEVAL_MATCHES_TIMEVAL64: u32 = 1;
57 | pub const __FD_SETSIZE: u32 = 1024;
58 | pub const _BITS_TIME64_H: u32 = 1;
59 | pub const _BITS_WCHAR_H: u32 = 1;
60 | pub const _BITS_STDINT_INTN_H: u32 = 1;
61 | pub const _BITS_STDINT_UINTN_H: u32 = 1;
62 | pub const _BITS_STDINT_LEAST_H: u32 = 1;
63 | pub const INT8_MIN: i32 = -128;
64 | pub const INT16_MIN: i32 = -32768;
65 | pub const INT32_MIN: i32 = -2147483648;
66 | pub const INT8_MAX: u32 = 127;
67 | pub const INT16_MAX: u32 = 32767;
68 | pub const INT32_MAX: u32 = 2147483647;
69 | pub const UINT8_MAX: u32 = 255;
70 | pub const UINT16_MAX: u32 = 65535;
71 | pub const UINT32_MAX: u32 = 4294967295;
72 | pub const INT_LEAST8_MIN: i32 = -128;
73 | pub const INT_LEAST16_MIN: i32 = -32768;
74 | pub const INT_LEAST32_MIN: i32 = -2147483648;
75 | pub const INT_LEAST8_MAX: u32 = 127;
76 | pub const INT_LEAST16_MAX: u32 = 32767;
77 | pub const INT_LEAST32_MAX: u32 = 2147483647;
78 | pub const UINT_LEAST8_MAX: u32 = 255;
79 | pub const UINT_LEAST16_MAX: u32 = 65535;
80 | pub const UINT_LEAST32_MAX: u32 = 4294967295;
81 | pub const INT_FAST8_MIN: i32 = -128;
82 | pub const INT_FAST16_MIN: i64 = -9223372036854775808;
83 | pub const INT_FAST32_MIN: i64 = -9223372036854775808;
84 | pub const INT_FAST8_MAX: u32 = 127;
85 | pub const INT_FAST16_MAX: u64 = 9223372036854775807;
86 | pub const INT_FAST32_MAX: u64 = 9223372036854775807;
87 | pub const UINT_FAST8_MAX: u32 = 255;
88 | pub const UINT_FAST16_MAX: i32 = -1;
89 | pub const UINT_FAST32_MAX: i32 = -1;
90 | pub const INTPTR_MIN: i64 = -9223372036854775808;
91 | pub const INTPTR_MAX: u64 = 9223372036854775807;
92 | pub const UINTPTR_MAX: i32 = -1;
93 | pub const PTRDIFF_MIN: i64 = -9223372036854775808;
94 | pub const PTRDIFF_MAX: u64 = 9223372036854775807;
95 | pub const SIG_ATOMIC_MIN: i32 = -2147483648;
96 | pub const SIG_ATOMIC_MAX: u32 = 2147483647;
97 | pub const SIZE_MAX: i32 = -1;
98 | pub const WINT_MIN: u32 = 0;
99 | pub const WINT_MAX: u32 = 4294967295;
100 | pub type wchar_t = ::std::os::raw::c_int;
101 | #[repr(C)]
102 | #[repr(align(16))]
103 | #[derive(Debug, Copy, Clone)]
104 | pub struct max_align_t {
105 | pub __clang_max_align_nonce1: ::std::os::raw::c_longlong,
106 | pub __bindgen_padding_0: u64,
107 | pub __clang_max_align_nonce2: u128,
108 | }
109 | #[allow(clippy::unnecessary_operation, clippy::identity_op)]
110 | const _: () = {
111 | ["Size of max_align_t"][::std::mem::size_of::() - 32usize];
112 | ["Alignment of max_align_t"][::std::mem::align_of::() - 16usize];
113 | ["Offset of field: max_align_t::__clang_max_align_nonce1"]
114 | [::std::mem::offset_of!(max_align_t, __clang_max_align_nonce1) - 0usize];
115 | ["Offset of field: max_align_t::__clang_max_align_nonce2"]
116 | [::std::mem::offset_of!(max_align_t, __clang_max_align_nonce2) - 16usize];
117 | };
118 | pub type __u_char = ::std::os::raw::c_uchar;
119 | pub type __u_short = ::std::os::raw::c_ushort;
120 | pub type __u_int = ::std::os::raw::c_uint;
121 | pub type __u_long = ::std::os::raw::c_ulong;
122 | pub type __int8_t = ::std::os::raw::c_schar;
123 | pub type __uint8_t = ::std::os::raw::c_uchar;
124 | pub type __int16_t = ::std::os::raw::c_short;
125 | pub type __uint16_t = ::std::os::raw::c_ushort;
126 | pub type __int32_t = ::std::os::raw::c_int;
127 | pub type __uint32_t = ::std::os::raw::c_uint;
128 | pub type __int64_t = ::std::os::raw::c_long;
129 | pub type __uint64_t = ::std::os::raw::c_ulong;
130 | pub type __int_least8_t = __int8_t;
131 | pub type __uint_least8_t = __uint8_t;
132 | pub type __int_least16_t = __int16_t;
133 | pub type __uint_least16_t = __uint16_t;
134 | pub type __int_least32_t = __int32_t;
135 | pub type __uint_least32_t = __uint32_t;
136 | pub type __int_least64_t = __int64_t;
137 | pub type __uint_least64_t = __uint64_t;
138 | pub type __quad_t = ::std::os::raw::c_long;
139 | pub type __u_quad_t = ::std::os::raw::c_ulong;
140 | pub type __intmax_t = ::std::os::raw::c_long;
141 | pub type __uintmax_t = ::std::os::raw::c_ulong;
142 | pub type __dev_t = ::std::os::raw::c_ulong;
143 | pub type __uid_t = ::std::os::raw::c_uint;
144 | pub type __gid_t = ::std::os::raw::c_uint;
145 | pub type __ino_t = ::std::os::raw::c_ulong;
146 | pub type __ino64_t = ::std::os::raw::c_ulong;
147 | pub type __mode_t = ::std::os::raw::c_uint;
148 | pub type __nlink_t = ::std::os::raw::c_ulong;
149 | pub type __off_t = ::std::os::raw::c_long;
150 | pub type __off64_t = ::std::os::raw::c_long;
151 | pub type __pid_t = ::std::os::raw::c_int;
152 | #[repr(C)]
153 | #[derive(Debug, Copy, Clone)]
154 | pub struct __fsid_t {
155 | pub __val: [::std::os::raw::c_int; 2usize],
156 | }
157 | #[allow(clippy::unnecessary_operation, clippy::identity_op)]
158 | const _: () = {
159 | ["Size of __fsid_t"][::std::mem::size_of::<__fsid_t>() - 8usize];
160 | ["Alignment of __fsid_t"][::std::mem::align_of::<__fsid_t>() - 4usize];
161 | ["Offset of field: __fsid_t::__val"][::std::mem::offset_of!(__fsid_t, __val) - 0usize];
162 | };
163 | pub type __clock_t = ::std::os::raw::c_long;
164 | pub type __rlim_t = ::std::os::raw::c_ulong;
165 | pub type __rlim64_t = ::std::os::raw::c_ulong;
166 | pub type __id_t = ::std::os::raw::c_uint;
167 | pub type __time_t = ::std::os::raw::c_long;
168 | pub type __useconds_t = ::std::os::raw::c_uint;
169 | pub type __suseconds_t = ::std::os::raw::c_long;
170 | pub type __suseconds64_t = ::std::os::raw::c_long;
171 | pub type __daddr_t = ::std::os::raw::c_int;
172 | pub type __key_t = ::std::os::raw::c_int;
173 | pub type __clockid_t = ::std::os::raw::c_int;
174 | pub type __timer_t = *mut ::std::os::raw::c_void;
175 | pub type __blksize_t = ::std::os::raw::c_long;
176 | pub type __blkcnt_t = ::std::os::raw::c_long;
177 | pub type __blkcnt64_t = ::std::os::raw::c_long;
178 | pub type __fsblkcnt_t = ::std::os::raw::c_ulong;
179 | pub type __fsblkcnt64_t = ::std::os::raw::c_ulong;
180 | pub type __fsfilcnt_t = ::std::os::raw::c_ulong;
181 | pub type __fsfilcnt64_t = ::std::os::raw::c_ulong;
182 | pub type __fsword_t = ::std::os::raw::c_long;
183 | pub type __ssize_t = ::std::os::raw::c_long;
184 | pub type __syscall_slong_t = ::std::os::raw::c_long;
185 | pub type __syscall_ulong_t = ::std::os::raw::c_ulong;
186 | pub type __loff_t = __off64_t;
187 | pub type __caddr_t = *mut ::std::os::raw::c_char;
188 | pub type __intptr_t = ::std::os::raw::c_long;
189 | pub type __socklen_t = ::std::os::raw::c_uint;
190 | pub type __sig_atomic_t = ::std::os::raw::c_int;
191 | pub type int_least8_t = __int_least8_t;
192 | pub type int_least16_t = __int_least16_t;
193 | pub type int_least32_t = __int_least32_t;
194 | pub type int_least64_t = __int_least64_t;
195 | pub type uint_least8_t = __uint_least8_t;
196 | pub type uint_least16_t = __uint_least16_t;
197 | pub type uint_least32_t = __uint_least32_t;
198 | pub type uint_least64_t = __uint_least64_t;
199 | pub type int_fast8_t = ::std::os::raw::c_schar;
200 | pub type int_fast16_t = ::std::os::raw::c_long;
201 | pub type int_fast32_t = ::std::os::raw::c_long;
202 | pub type int_fast64_t = ::std::os::raw::c_long;
203 | pub type uint_fast8_t = ::std::os::raw::c_uchar;
204 | pub type uint_fast16_t = ::std::os::raw::c_ulong;
205 | pub type uint_fast32_t = ::std::os::raw::c_ulong;
206 | pub type uint_fast64_t = ::std::os::raw::c_ulong;
207 | pub type intmax_t = __intmax_t;
208 | pub type uintmax_t = __uintmax_t;
209 | #[repr(C)]
210 | #[derive(Debug, Copy, Clone)]
211 | pub struct local_result {
212 | pub buf: *mut ::std::os::raw::c_char,
213 | pub len: usize,
214 | pub _vec: *mut ::std::os::raw::c_void,
215 | pub elapsed: f64,
216 | pub rows_read: u64,
217 | pub bytes_read: u64,
218 | }
219 | #[allow(clippy::unnecessary_operation, clippy::identity_op)]
220 | const _: () = {
221 | ["Size of local_result"][::std::mem::size_of::() - 48usize];
222 | ["Alignment of local_result"][::std::mem::align_of::() - 8usize];
223 | ["Offset of field: local_result::buf"][::std::mem::offset_of!(local_result, buf) - 0usize];
224 | ["Offset of field: local_result::len"][::std::mem::offset_of!(local_result, len) - 8usize];
225 | ["Offset of field: local_result::_vec"][::std::mem::offset_of!(local_result, _vec) - 16usize];
226 | ["Offset of field: local_result::elapsed"]
227 | [::std::mem::offset_of!(local_result, elapsed) - 24usize];
228 | ["Offset of field: local_result::rows_read"]
229 | [::std::mem::offset_of!(local_result, rows_read) - 32usize];
230 | ["Offset of field: local_result::bytes_read"]
231 | [::std::mem::offset_of!(local_result, bytes_read) - 40usize];
232 | };
233 | #[repr(C)]
234 | #[derive(Debug, Copy, Clone)]
235 | pub struct local_result_v2 {
236 | pub buf: *mut ::std::os::raw::c_char,
237 | pub len: usize,
238 | pub _vec: *mut ::std::os::raw::c_void,
239 | pub elapsed: f64,
240 | pub rows_read: u64,
241 | pub bytes_read: u64,
242 | pub error_message: *mut ::std::os::raw::c_char,
243 | }
244 | #[allow(clippy::unnecessary_operation, clippy::identity_op)]
245 | const _: () = {
246 | ["Size of local_result_v2"][::std::mem::size_of::() - 56usize];
247 | ["Alignment of local_result_v2"][::std::mem::align_of::() - 8usize];
248 | ["Offset of field: local_result_v2::buf"]
249 | [::std::mem::offset_of!(local_result_v2, buf) - 0usize];
250 | ["Offset of field: local_result_v2::len"]
251 | [::std::mem::offset_of!(local_result_v2, len) - 8usize];
252 | ["Offset of field: local_result_v2::_vec"]
253 | [::std::mem::offset_of!(local_result_v2, _vec) - 16usize];
254 | ["Offset of field: local_result_v2::elapsed"]
255 | [::std::mem::offset_of!(local_result_v2, elapsed) - 24usize];
256 | ["Offset of field: local_result_v2::rows_read"]
257 | [::std::mem::offset_of!(local_result_v2, rows_read) - 32usize];
258 | ["Offset of field: local_result_v2::bytes_read"]
259 | [::std::mem::offset_of!(local_result_v2, bytes_read) - 40usize];
260 | ["Offset of field: local_result_v2::error_message"]
261 | [::std::mem::offset_of!(local_result_v2, error_message) - 48usize];
262 | };
263 | extern "C" {
264 | pub fn query_stable(
265 | argc: ::std::os::raw::c_int,
266 | argv: *mut *mut ::std::os::raw::c_char,
267 | ) -> *mut local_result;
268 | }
269 | extern "C" {
270 | pub fn free_result(result: *mut local_result);
271 | }
272 | extern "C" {
273 | pub fn query_stable_v2(
274 | argc: ::std::os::raw::c_int,
275 | argv: *mut *mut ::std::os::raw::c_char,
276 | ) -> *mut local_result_v2;
277 | }
278 | extern "C" {
279 | pub fn free_result_v2(result: *mut local_result_v2);
280 | }
281 |
--------------------------------------------------------------------------------
/src/error.rs:
--------------------------------------------------------------------------------
1 | use std::ffi::NulError;
2 | use std::string::FromUtf8Error;
3 |
4 | #[derive(Debug, thiserror::Error)]
5 | pub enum Error {
6 | #[error("An unknown error has occurred")]
7 | Unknown,
8 | #[error("No result")]
9 | NoResult,
10 | #[error("Invalid data: {0}")]
11 | InvalidData(String),
12 | #[error("Invalid path")]
13 | PathError,
14 | #[error(transparent)]
15 | Io(#[from] std::io::Error),
16 | #[error(transparent)]
17 | Nul(#[from] NulError),
18 | #[error("Insufficient dir permissions")]
19 | InsufficientPermissions,
20 | #[error("Non UTF-8 sequence: {0}")]
21 | NonUtf8Sequence(FromUtf8Error),
22 | #[error("{0}")]
23 | QueryError(String),
24 | }
25 |
26 | pub type Result = std::result::Result;
27 |
--------------------------------------------------------------------------------
/src/format.rs:
--------------------------------------------------------------------------------
1 | #[derive(Debug, Clone, Copy)]
2 | pub enum InputFormat {
3 | TabSeparated,
4 | TabSeparatedRaw,
5 | TabSeparatedWithNames,
6 | TabSeparatedWithNamesAndTypes,
7 | TabSeparatedRawWithNames,
8 | TabSeparatedRawWithNamesAndTypes,
9 | Template,
10 | TemplateIgnoreSpaces,
11 | CSV,
12 | CSVWithNames,
13 | CSVWithNamesAndTypes,
14 | CustomSeparated,
15 | CustomSeparatedWithNames,
16 | CustomSeparatedWithNamesAndTypes,
17 | Values,
18 | JSON,
19 | JSONAsString,
20 | JSONAsObject,
21 | JSONStrings,
22 | JSONColumns,
23 | JSONColumnsWithMetadata,
24 | JSONCompact,
25 | JSONCompactColumns,
26 | JSONEachRow,
27 | JSONStringsEachRow,
28 | JSONCompactEachRow,
29 | JSONCompactEachRowWithNames,
30 | JSONCompactEachRowWithNamesAndTypes,
31 | JSONCompactStringsEachRow,
32 | JSONCompactStringsEachRowWithNames,
33 | JSONCompactStringsEachRowWithNamesAndTypes,
34 | JSONObjectEachRow,
35 | BSONEachRow,
36 | TSKV,
37 | Protobuf,
38 | ProtobufSingle,
39 | ProtobufList,
40 | Avro,
41 | AvroConfluent,
42 | Parquet,
43 | ParquetMetadata,
44 | Arrow,
45 | ArrowStream,
46 | ORC,
47 | One,
48 | Npy,
49 | RowBinary,
50 | RowBinaryWithNames,
51 | RowBinaryWithNamesAndTypes,
52 | RowBinaryWithDefaults,
53 | Native,
54 | CapnProto,
55 | LineAsString,
56 | Regexp,
57 | RawBLOB,
58 | MsgPack,
59 | MySQLDump,
60 | DWARF,
61 | Form,
62 | }
63 |
64 | #[derive(Debug, Clone, Copy)]
65 | pub enum OutputFormat {
66 | TabSeparated,
67 | TabSeparatedRaw,
68 | TabSeparatedWithNames,
69 | TabSeparatedWithNamesAndTypes,
70 | TabSeparatedRawWithNames,
71 | TabSeparatedRawWithNamesAndTypes,
72 | Template,
73 | CSV,
74 | CSVWithNames,
75 | CSVWithNamesAndTypes,
76 | CustomSeparated,
77 | CustomSeparatedWithNames,
78 | CustomSeparatedWithNamesAndTypes,
79 | Values,
80 | JSON,
81 | JSONStrings,
82 | JSONColumns,
83 | JSONColumnsWithMetadata,
84 | JSONCompact,
85 | JSONCompactStrings,
86 | JSONCompactColumns,
87 | JSONEachRow,
88 | PrettyJSONEachRow,
89 | JSONEachRowWithProgress,
90 | JSONStringsEachRow,
91 | JSONStringsEachRowWithProgress,
92 | JSONCompactEachRow,
93 | JSONCompactEachRowWithNames,
94 | JSONCompactEachRowWithNamesAndTypes,
95 | JSONCompactStringsEachRow,
96 | JSONCompactStringsEachRowWithNames,
97 | JSONCompactStringsEachRowWithNamesAndTypes,
98 | JSONObjectEachRow,
99 | BSONEachRow,
100 | TSKV,
101 | Pretty,
102 | PrettyNoEscapes,
103 | PrettyMonoBlock,
104 | PrettyNoEscapesMonoBlock,
105 | PrettyCompact,
106 | PrettyCompactNoEscapes,
107 | PrettyCompactMonoBlock,
108 | PrettyCompactNoEscapesMonoBlock,
109 | PrettySpace,
110 | PrettySpaceNoEscapes,
111 | PrettySpaceMonoBlock,
112 | PrettySpaceNoEscapesMonoBlock,
113 | Prometheus,
114 | Protobuf,
115 | ProtobufSingle,
116 | ProtobufList,
117 | Avro,
118 | Parquet,
119 | Arrow,
120 | ArrowStream,
121 | ORC,
122 | Npy,
123 | RowBinary,
124 | RowBinaryWithNames,
125 | RowBinaryWithNamesAndTypes,
126 | Native,
127 | Null,
128 | XML,
129 | CapnProto,
130 | LineAsString,
131 | RawBLOB,
132 | MsgPack,
133 | Markdown,
134 | Vertical,
135 | }
136 |
137 | impl InputFormat {
138 | pub const fn as_str(self) -> &'static str {
139 | match self {
140 | Self::TabSeparated => "TabSeparated",
141 | Self::TabSeparatedRaw => "TabSeparatedRaw",
142 | Self::TabSeparatedWithNames => "TabSeparatedWithNames",
143 | Self::TabSeparatedWithNamesAndTypes => "TabSeparatedWithNamesAndTypes",
144 | Self::TabSeparatedRawWithNames => "TabSeparatedRawWithNames",
145 | Self::TabSeparatedRawWithNamesAndTypes => "TabSeparatedRawWithNamesAndTypes",
146 | Self::Template => "Template",
147 | Self::TemplateIgnoreSpaces => "TemplateIgnoreSpaces",
148 | Self::CSV => "CSV",
149 | Self::CSVWithNames => "CSVWithNames",
150 | Self::CSVWithNamesAndTypes => "CSVWithNamesAndTypes",
151 | Self::CustomSeparated => "CustomSeparated",
152 | Self::CustomSeparatedWithNames => "CustomSeparatedWithNames",
153 | Self::CustomSeparatedWithNamesAndTypes => "CustomSeparatedWithNamesAndTypes",
154 | Self::Values => "Values",
155 | Self::JSON => "JSON",
156 | Self::JSONAsString => "JSONAsString",
157 | Self::JSONAsObject => "JSONAsObject",
158 | Self::JSONStrings => "JSONStrings",
159 | Self::JSONColumns => "JSONColumns",
160 | Self::JSONColumnsWithMetadata => "JSONColumnsWithMetadata",
161 | Self::JSONCompact => "JSONCompact",
162 | Self::JSONCompactColumns => "JSONCompactColumns",
163 | Self::JSONEachRow => "JSONEachRow",
164 | Self::JSONStringsEachRow => "JSONStringsEachRow",
165 | Self::JSONCompactEachRow => "JSONCompactEachRow",
166 | Self::JSONCompactEachRowWithNames => "JSONCompactEachRowWithNames",
167 | Self::JSONCompactEachRowWithNamesAndTypes => "JSONCompactEachRowWithNamesAndTypes",
168 | Self::JSONCompactStringsEachRow => "JSONCompactStringsEachRow",
169 | Self::JSONCompactStringsEachRowWithNames => "JSONCompactStringsEachRowWithNames",
170 | Self::JSONCompactStringsEachRowWithNamesAndTypes => {
171 | "JSONCompactStringsEachRowWithNamesAndTypes"
172 | }
173 | Self::JSONObjectEachRow => "JSONObjectEachRow",
174 | Self::BSONEachRow => "BSONEachRow",
175 | Self::TSKV => "TSKV",
176 | Self::Protobuf => "Protobuf",
177 | Self::ProtobufSingle => "ProtobufSingle",
178 | Self::ProtobufList => "ProtobufList",
179 | Self::Avro => "Avro",
180 | Self::AvroConfluent => "AvroConfluent",
181 | Self::Parquet => "Parquet",
182 | Self::ParquetMetadata => "ParquetMetadata",
183 | Self::Arrow => "Arrow",
184 | Self::ArrowStream => "ArrowStream",
185 | Self::ORC => "ORC",
186 | Self::One => "One",
187 | Self::Npy => "Npy",
188 | Self::RowBinary => "RowBinary",
189 | Self::RowBinaryWithNames => "RowBinaryWithNames",
190 | Self::RowBinaryWithNamesAndTypes => "RowBinaryWithNamesAndTypes",
191 | Self::RowBinaryWithDefaults => "RowBinaryWithDefaults",
192 | Self::Native => "Native",
193 | Self::CapnProto => "CapnProto",
194 | Self::LineAsString => "LineAsString",
195 | Self::Regexp => "Regexp",
196 | Self::RawBLOB => "RawBLOB",
197 | Self::MsgPack => "MsgPack",
198 | Self::MySQLDump => "MySQLDump",
199 | Self::DWARF => "DWARF",
200 | Self::Form => "Form",
201 | }
202 | }
203 | }
204 |
205 | impl OutputFormat {
206 | pub const fn as_str(self) -> &'static str {
207 | match self {
208 | Self::TabSeparated => "TabSeparated",
209 | Self::TabSeparatedRaw => "TabSeparatedRaw",
210 | Self::TabSeparatedWithNames => "TabSeparatedWithNames",
211 | Self::TabSeparatedWithNamesAndTypes => "TabSeparatedWithNamesAndTypes",
212 | Self::TabSeparatedRawWithNames => "TabSeparatedRawWithNames",
213 | Self::TabSeparatedRawWithNamesAndTypes => "TabSeparatedRawWithNamesAndTypes",
214 | Self::Template => "Template",
215 | Self::CSV => "CSV",
216 | Self::CSVWithNames => "CSVWithNames",
217 | Self::CSVWithNamesAndTypes => "CSVWithNamesAndTypes",
218 | Self::CustomSeparated => "CustomSeparated",
219 | Self::CustomSeparatedWithNames => "CustomSeparatedWithNames",
220 | Self::CustomSeparatedWithNamesAndTypes => "CustomSeparatedWithNamesAndTypes",
221 | Self::Values => "Values",
222 | Self::JSON => "JSON",
223 | Self::JSONStrings => "JSONStrings",
224 | Self::JSONColumns => "JSONColumns",
225 | Self::JSONColumnsWithMetadata => "JSONColumnsWithMetadata",
226 | Self::JSONCompact => "JSONCompact",
227 | Self::JSONCompactStrings => "JSONCompactStrings",
228 | Self::JSONCompactColumns => "JSONCompactColumns",
229 | Self::JSONEachRow => "JSONEachRow",
230 | Self::PrettyJSONEachRow => "PrettyJSONEachRow",
231 | Self::JSONEachRowWithProgress => "JSONEachRowWithProgress",
232 | Self::JSONStringsEachRow => "JSONStringsEachRow",
233 | Self::JSONStringsEachRowWithProgress => "JSONStringsEachRowWithProgress",
234 | Self::JSONCompactEachRow => "JSONCompactEachRow",
235 | Self::JSONCompactEachRowWithNames => "JSONCompactEachRowWithNames",
236 | Self::JSONCompactEachRowWithNamesAndTypes => "JSONCompactEachRowWithNamesAndTypes",
237 | Self::JSONCompactStringsEachRow => "JSONCompactStringsEachRow",
238 | Self::JSONCompactStringsEachRowWithNames => "JSONCompactStringsEachRowWithNames",
239 | Self::JSONCompactStringsEachRowWithNamesAndTypes => {
240 | "JSONCompactStringsEachRowWithNamesAndTypes"
241 | }
242 | Self::JSONObjectEachRow => "JSONObjectEachRow",
243 | Self::BSONEachRow => "BSONEachRow",
244 | Self::TSKV => "TSKV",
245 | Self::Pretty => "Pretty",
246 | Self::PrettyNoEscapes => "PrettyNoEscapes",
247 | Self::PrettyMonoBlock => "PrettyMonoBlock",
248 | Self::PrettyNoEscapesMonoBlock => "PrettyNoEscapesMonoBlock",
249 | Self::PrettyCompact => "PrettyCompact",
250 | Self::PrettyCompactNoEscapes => "PrettyCompactNoEscapes",
251 | Self::PrettyCompactMonoBlock => "PrettyCompactMonoBlock",
252 | Self::PrettyCompactNoEscapesMonoBlock => "PrettyCompactNoEscapesMonoBlock",
253 | Self::PrettySpace => "PrettySpace",
254 | Self::PrettySpaceNoEscapes => "PrettySpaceNoEscapes",
255 | Self::PrettySpaceMonoBlock => "PrettySpaceMonoBlock",
256 | Self::PrettySpaceNoEscapesMonoBlock => "PrettySpaceNoEscapesMonoBlock",
257 | Self::Prometheus => "Prometheus",
258 | Self::Protobuf => "Protobuf",
259 | Self::ProtobufSingle => "ProtobufSingle",
260 | Self::ProtobufList => "ProtobufList",
261 | Self::Avro => "Avro",
262 | Self::Parquet => "Parquet",
263 | Self::Arrow => "Arrow",
264 | Self::ArrowStream => "ArrowStream",
265 | Self::ORC => "ORC",
266 | Self::Npy => "Npy",
267 | Self::RowBinary => "RowBinary",
268 | Self::RowBinaryWithNames => "RowBinaryWithNames",
269 | Self::RowBinaryWithNamesAndTypes => "RowBinaryWithNamesAndTypes",
270 | Self::Native => "Native",
271 | Self::Null => "Null",
272 | Self::XML => "XML",
273 | Self::CapnProto => "CapnProto",
274 | Self::LineAsString => "LineAsString",
275 | Self::RawBLOB => "RawBLOB",
276 | Self::MsgPack => "MsgPack",
277 | Self::Markdown => "Markdown",
278 | Self::Vertical => "Vertical",
279 | }
280 | }
281 | }
282 |
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod arg;
2 | #[allow(
3 | dead_code,
4 | unused,
5 | non_snake_case,
6 | non_camel_case_types,
7 | non_upper_case_globals
8 | )]
9 | mod bindings;
10 | pub mod error;
11 | pub mod format;
12 | pub mod log_level;
13 | pub mod query_result;
14 | pub mod session;
15 |
16 | use std::ffi::{c_char, CString};
17 |
18 | use crate::arg::Arg;
19 | use crate::error::Error;
20 | use crate::error::Result;
21 | use crate::query_result::QueryResult;
22 |
23 | pub fn execute(query: &str, query_args: Option<&[Arg]>) -> Result {
24 | let mut argv = Vec::with_capacity(query_args.as_ref().map_or(0, |v| v.len()) + 2);
25 | argv.push(arg_clickhouse()?.into_raw());
26 |
27 | if let Some(args) = query_args {
28 | for arg in args {
29 | argv.push(arg.to_cstring()?.into_raw());
30 | }
31 | }
32 |
33 | argv.push(arg_query(query)?.into_raw());
34 | call_chdb(argv)
35 | }
36 |
37 | fn call_chdb(mut argv: Vec<*mut c_char>) -> Result {
38 | let argc = argv.len() as i32;
39 | let argv = argv.as_mut_ptr();
40 | let result_ptr = unsafe { bindings::query_stable_v2(argc, argv) };
41 |
42 | if result_ptr.is_null() {
43 | return Err(Error::NoResult);
44 | }
45 | let result = QueryResult::new(result_ptr);
46 | let result = result.check_error()?;
47 |
48 | Ok(result)
49 | }
50 |
51 | fn arg_clickhouse() -> Result {
52 | Ok(CString::new("clickhouse")?)
53 | }
54 |
55 | fn arg_data_path(value: &str) -> Result {
56 | Ok(CString::new(format!("--path={}", value))?)
57 | }
58 |
59 | fn arg_query(value: &str) -> Result {
60 | Ok(CString::new(format!("--query={}", value))?)
61 | }
62 |
--------------------------------------------------------------------------------
/src/log_level.rs:
--------------------------------------------------------------------------------
1 | #[derive(Debug, Clone, Copy)]
2 | pub enum LogLevel {
3 | Trace,
4 | Debug,
5 | Info,
6 | Warn,
7 | Error,
8 | }
9 |
10 | impl LogLevel {
11 | pub const fn as_str(self) -> &'static str {
12 | match self {
13 | Self::Trace => "trace",
14 | Self::Debug => "debug",
15 | Self::Info => "information",
16 | Self::Warn => "warning",
17 | Self::Error => "error",
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/query_result.rs:
--------------------------------------------------------------------------------
1 | use core::slice;
2 | use std::borrow::Cow;
3 | use std::ffi::CStr;
4 | use std::time::Duration;
5 |
6 | use crate::bindings;
7 | use crate::error::Error;
8 | use crate::error::Result;
9 | #[derive(Clone)]
10 | pub struct QueryResult {
11 | inner: *mut bindings::local_result_v2,
12 | }
13 |
14 | impl QueryResult {
15 | pub(crate) fn new(inner: *mut bindings::local_result_v2) -> Self {
16 | Self { inner }
17 | }
18 | pub fn data_utf8(&self) -> Result {
19 | let buf = self.data_ref();
20 |
21 | String::from_utf8(buf.to_vec()).map_err(Error::NonUtf8Sequence)
22 | }
23 |
24 | pub fn data_utf8_lossy(&self) -> Cow {
25 | String::from_utf8_lossy(self.data_ref())
26 | }
27 |
28 | pub fn data_utf8_unchecked(&self) -> String {
29 | unsafe { String::from_utf8_unchecked(self.data_ref().to_vec()) }
30 | }
31 |
32 | pub fn data_ref(&self) -> &[u8] {
33 | let inner = self.inner;
34 | let buf = unsafe { (*inner).buf };
35 | let len = unsafe { (*inner).len };
36 | let bytes: &[u8] = unsafe { slice::from_raw_parts(buf as *const u8, len) };
37 | bytes
38 | }
39 |
40 | pub fn rows_read(&self) -> u64 {
41 | let inner = self.inner;
42 | unsafe { *inner }.rows_read
43 | }
44 |
45 | pub fn bytes_read(&self) -> u64 {
46 | let inner = self.inner;
47 | unsafe { *inner }.bytes_read
48 | }
49 |
50 | pub fn elapsed(&self) -> Duration {
51 | let elapsed = unsafe { (*self.inner).elapsed };
52 | Duration::from_secs_f64(elapsed)
53 | }
54 |
55 | pub(crate) fn check_error(self) -> Result {
56 | self.check_error_ref()?;
57 | Ok(self)
58 | }
59 | pub(crate) fn check_error_ref(&self) -> Result<()> {
60 | let err_ptr = unsafe { (*self.inner).error_message };
61 |
62 | if err_ptr.is_null() {
63 | return Ok(());
64 | }
65 |
66 | Err(Error::QueryError(unsafe {
67 | CStr::from_ptr(err_ptr).to_string_lossy().to_string()
68 | }))
69 | }
70 | }
71 |
72 | impl Drop for QueryResult {
73 | fn drop(&mut self) {
74 | unsafe { bindings::free_result_v2(self.inner) };
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/session.rs:
--------------------------------------------------------------------------------
1 | use std::ffi::CString;
2 | use std::fs;
3 | use std::path::PathBuf;
4 |
5 | use crate::arg::Arg;
6 | use crate::arg_clickhouse;
7 | use crate::arg_data_path;
8 | use crate::arg_query;
9 | use crate::call_chdb;
10 | use crate::error::Error;
11 | use crate::query_result::QueryResult;
12 |
13 | pub struct SessionBuilder<'a> {
14 | data_path: PathBuf,
15 | default_args: Vec>,
16 | auto_cleanup: bool,
17 | }
18 |
19 | #[derive(Clone)]
20 | pub struct Session {
21 | default_args: Vec,
22 | data_path: String,
23 | auto_cleanup: bool,
24 | }
25 |
26 | impl<'a> SessionBuilder<'a> {
27 | pub fn new() -> Self {
28 | let mut data_path = std::env::current_dir().unwrap();
29 | data_path.push("chdb");
30 |
31 | Self {
32 | data_path,
33 | default_args: Vec::new(),
34 | auto_cleanup: false,
35 | }
36 | }
37 |
38 | pub fn with_data_path(mut self, path: impl Into) -> Self {
39 | self.data_path = path.into();
40 | self
41 | }
42 |
43 | pub fn with_arg(mut self, arg: Arg<'a>) -> Self {
44 | self.default_args.push(arg);
45 | self
46 | }
47 |
48 | /// If set Session will delete data directory before it is dropped.
49 | pub fn with_auto_cleanup(mut self, value: bool) -> Self {
50 | self.auto_cleanup = value;
51 | self
52 | }
53 |
54 | pub fn build(self) -> Result {
55 | let data_path = self.data_path.to_str().ok_or(Error::PathError)?.to_string();
56 |
57 | fs::create_dir_all(&self.data_path)?;
58 | if fs::metadata(&self.data_path)?.permissions().readonly() {
59 | return Err(Error::InsufficientPermissions);
60 | }
61 |
62 | let mut default_args = Vec::with_capacity(self.default_args.len() + 2);
63 | default_args.push(arg_clickhouse()?);
64 | default_args.push(arg_data_path(&data_path)?);
65 |
66 | for default_arg in self.default_args {
67 | default_args.push(default_arg.to_cstring()?);
68 | }
69 |
70 | Ok(Session {
71 | data_path,
72 | default_args,
73 | auto_cleanup: self.auto_cleanup,
74 | })
75 | }
76 | }
77 |
78 | impl<'a> Default for SessionBuilder<'a> {
79 | fn default() -> Self {
80 | Self::new()
81 | }
82 | }
83 |
84 | impl Session {
85 | pub fn execute(&self, query: &str, query_args: Option<&[Arg]>) -> Result {
86 | let mut argv = Vec::with_capacity(
87 | self.default_args.len() + query_args.as_ref().map_or(0, |v| v.len()) + 1,
88 | );
89 |
90 | for arg in &self.default_args {
91 | argv.push(arg.clone().into_raw())
92 | }
93 |
94 | if let Some(args) = query_args {
95 | for arg in args {
96 | argv.push(arg.to_cstring()?.into_raw());
97 | }
98 | }
99 |
100 | argv.push(arg_query(query)?.into_raw());
101 | call_chdb(argv)
102 | }
103 | }
104 |
105 | impl Drop for Session {
106 | fn drop(&mut self) {
107 | if self.auto_cleanup {
108 | fs::remove_dir_all(&self.data_path).ok();
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/tests/examples.rs:
--------------------------------------------------------------------------------
1 | use chdb_rust::arg::Arg;
2 | use chdb_rust::error::Result;
3 | use chdb_rust::execute;
4 | use chdb_rust::format::InputFormat;
5 | use chdb_rust::format::OutputFormat;
6 | use chdb_rust::log_level::LogLevel;
7 | use chdb_rust::session::SessionBuilder;
8 |
9 | #[test]
10 | fn test_stateful() -> Result<()> {
11 | //
12 | // Create session.
13 | //
14 | let tmp = tempdir::TempDir::new("chdb-rust")?;
15 | let session = SessionBuilder::new()
16 | .with_data_path(tmp.path())
17 | .with_arg(Arg::LogLevel(LogLevel::Debug))
18 | .with_arg(Arg::Custom("priority".into(), Some("1".into())))
19 | .with_auto_cleanup(true)
20 | .build()?;
21 |
22 | //
23 | // Create database.
24 | //
25 |
26 | session.execute("CREATE DATABASE demo; USE demo", Some(&[Arg::MultiQuery]))?;
27 |
28 | //
29 | // Create table.
30 | //
31 |
32 | session.execute(
33 | "CREATE TABLE logs (id UInt64, msg String) ENGINE = MergeTree() ORDER BY id",
34 | None,
35 | )?;
36 |
37 | //
38 | // Insert into table.
39 | //
40 |
41 | session.execute("INSERT INTO logs (id, msg) VALUES (1, 'test')", None)?;
42 |
43 | //
44 | // Select from table.
45 | //
46 | let len = session.execute(
47 | "SELECT COUNT(*) FROM logs",
48 | Some(&[Arg::OutputFormat(OutputFormat::JSONEachRow)]),
49 | )?;
50 |
51 | assert_eq!(len.data_utf8_lossy(), "{\"COUNT()\":1}\n");
52 |
53 | let result = session.execute(
54 | "SELECT * FROM logs",
55 | Some(&[Arg::OutputFormat(OutputFormat::JSONEachRow)]),
56 | )?;
57 | assert_eq!(result.data_utf8_lossy(), "{\"id\":1,\"msg\":\"test\"}\n");
58 | Ok(())
59 | }
60 |
61 | #[test]
62 | fn test_stateless() -> Result<()> {
63 | let query = format!(
64 | "SELECT * FROM file('tests/logs.csv', {})",
65 | InputFormat::CSV.as_str()
66 | );
67 |
68 | let result = execute(
69 | &query,
70 | Some(&[Arg::OutputFormat(OutputFormat::JSONEachRow)]),
71 | )?;
72 |
73 | assert_eq!(result.data_utf8_lossy(), "{\"id\":1,\"msg\":\"test\"}\n");
74 | Ok(())
75 | }
76 |
--------------------------------------------------------------------------------
/tests/logs.csv:
--------------------------------------------------------------------------------
1 | id,msg
2 | 1,test
--------------------------------------------------------------------------------
/update_libchdb.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | # Check for necessary tools
6 | command -v curl >/dev/null 2>&1 || { echo >&2 "curl is required but it's not installed. Aborting."; exit 1; }
7 | command -v tar >/dev/null 2>&1 || { echo >&2 "tar is required but it's not installed. Aborting."; exit 1; }
8 |
9 | # Function to download and extract the file
10 | download_and_extract() {
11 | local url=$1
12 | local file="libchdb.tar.gz"
13 |
14 | echo "Attempting to download $PLATFORM from $url"
15 |
16 | # Download the file with a retry logic
17 | if curl -L -o "$file" "$url"; then
18 | echo "Download successful."
19 |
20 | # Optional: Verify download integrity here, if checksums are provided
21 |
22 | # Untar the file
23 | if tar -xzf "$file"; then
24 | echo "Extraction successful."
25 | return 0
26 | fi
27 | fi
28 | return 1
29 | }
30 |
31 | # Get the newest release version
32 | LATEST_RELEASE=v2.1.1
33 |
34 | # Select the correct package based on OS and architecture
35 | case "$(uname -s)" in
36 | Linux)
37 | if [[ $(uname -m) == "aarch64" ]]; then
38 | PLATFORM="linux-aarch64-libchdb.tar.gz"
39 | else
40 | PLATFORM="linux-x86_64-libchdb.tar.gz"
41 | fi
42 | ;;
43 | Darwin)
44 | if [[ $(uname -m) == "arm64" ]]; then
45 | PLATFORM="macos-arm64-libchdb.tar.gz"
46 | else
47 | PLATFORM="macos-x86_64-libchdb.tar.gz"
48 | fi
49 | ;;
50 | *)
51 | echo "Unsupported platform"
52 | exit 1
53 | ;;
54 | esac
55 |
56 | # Main download URL
57 | DOWNLOAD_URL="https://github.com/chdb-io/chdb/releases/download/$LATEST_RELEASE/$PLATFORM"
58 | FALLBACK_URL="https://github.com/chdb-io/chdb/releases/latest/download/$PLATFORM"
59 |
60 | # Try the main download URL first
61 | if ! download_and_extract "$DOWNLOAD_URL"; then
62 | echo "Retrying with fallback URL..."
63 | if ! download_and_extract "$FALLBACK_URL"; then
64 | echo "Both primary and fallback downloads failed. Aborting."
65 | exit 1
66 | fi
67 | fi
68 |
69 | chmod +x libchdb.so
70 |
71 | if [[ "$1" == "--global" ]]; then
72 | # If current uid is not 0, check if sudo is available and request the user to input the password
73 | if [[ $EUID -ne 0 ]]; then
74 | command -v sudo >/dev/null 2>&1 || { echo >&2 "This script requires sudo privileges but sudo is not installed. Aborting."; exit 1; }
75 | echo "Installation requires administrative access. You will be prompted for your password."
76 | fi
77 |
78 | # Define color messages if terminal supports them
79 | if [[ -t 1 ]]; then
80 | RED='\033[0;31m'
81 | GREEN='\033[0;32m'
82 | NC='\033[0m' # No Color
83 | REDECHO() { echo -e "${RED}$@${NC}"; }
84 | GREENECHO() { echo -e "${GREEN}$@${NC}"; }
85 | ENDECHO() { echo -ne "${NC}"; }
86 | else
87 | REDECHO() { echo "$@"; }
88 | GREENECHO() { echo "$@"; }
89 | ENDECHO() { :; }
90 | fi
91 |
92 | # Use sudo if not running as root
93 | SUDO=''
94 | if [[ $EUID -ne 0 ]]; then
95 | SUDO='sudo'
96 | GREENECHO "\nYou will be asked for your sudo password to install:"
97 | echo " libchdb.so to /usr/local/lib/"
98 | echo " chdb.h to /usr/local/include/"
99 | fi
100 |
101 | # Make sure the library and header directory exists
102 | ${SUDO} mkdir -p /usr/local/lib /usr/local/include || true
103 |
104 | # Install the library and header file
105 | ${SUDO} /bin/cp libchdb.so /usr/local/lib/
106 | ${SUDO} /bin/cp chdb.h /usr/local/include/
107 |
108 | # Set execute permission for libchdb.so
109 | ${SUDO} chmod +x /usr/local/lib/libchdb.so
110 |
111 | # Update library cache (Linux specific)
112 | if [[ "$(uname -s)" == "Linux" ]]; then
113 | ${SUDO} ldconfig
114 | fi
115 |
116 | GREENECHO "Installation completed successfully." ; ENDECHO
117 | GREENECHO "If any error occurred, please report it to:" ; ENDECHO
118 | GREENECHO " https://github.com/chdb-io/chdb/issues/new/choose" ; ENDECHO
119 | fi
120 |
--------------------------------------------------------------------------------
/wrapper.h:
--------------------------------------------------------------------------------
1 | #include "chdb.h"
2 |
--------------------------------------------------------------------------------