Skip to content

Commit e94482f

Browse files
authored
Merge pull request #1 from wyhaya/first
Add Rust Driver
2 parents 3c64d15 + efadc6b commit e94482f

File tree

12 files changed

+409
-17
lines changed

12 files changed

+409
-17
lines changed

.github/workflows/test.yml

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Test
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
build:
7+
name: ${{ matrix.job.target }}
8+
runs-on: ${{ matrix.job.os }}
9+
strategy:
10+
matrix:
11+
job:
12+
- target: x86_64-unknown-linux-gnu
13+
os: ubuntu-latest
14+
15+
- target: x86_64-apple-darwin
16+
os: macos-latest
17+
18+
- target: aarch64-apple-darwin
19+
os: macos-latest
20+
21+
# TODO
22+
# - target: x86_64-pc-windows-msvc
23+
# os: windows-latest
24+
25+
steps:
26+
- uses: actions/checkout@v4
27+
with:
28+
submodules: recursive
29+
30+
- uses: actions/cache@v4
31+
with:
32+
path: |
33+
~/.cargo/bin/
34+
~/.cargo/registry/index/
35+
~/.cargo/registry/cache/
36+
~/.cargo/git/db/
37+
target/
38+
key: ${{ matrix.job.target }}
39+
40+
- name: Setup Rust
41+
run: |
42+
rustup update
43+
rustup target add ${{ matrix.job.target }}
44+
45+
- name: Cargo fmt
46+
run: |
47+
cargo fmt --all -- --check
48+
49+
- name: Cargo test
50+
run: |
51+
cargo test --target ${{ matrix.job.target }}
52+
53+
- name: Cargo run --example
54+
run: |
55+
cargo run --example basic --target ${{ matrix.job.target }}

.gitignore

+2-17
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,6 @@
1-
# Generated by Cargo
2-
# will have compiled files and executables
3-
debug/
41
target/
5-
6-
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7-
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
82
Cargo.lock
9-
10-
# These are backup files generated by rustfmt
113
**/*.rs.bk
12-
13-
# MSVC Windows builds of rustc generate these, which store debugging information
144
*.pdb
15-
16-
# RustRover
17-
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18-
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19-
# and can be added to the global gitignore or merged into this file. For a more nuclear
20-
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
21-
#.idea/
5+
.DS_Store
6+
test.db/

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "crossdb"]
2+
path = crossdb
3+
url = https://github.com/crossdb-org/crossdb

Cargo.toml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "crossdb"
3+
version = "0.0.1"
4+
edition = "2021"
5+
license = "MIT"
6+
description = "CrossDB Rust Driver"
7+
readme = "README.md"
8+
homepage = "hhttps://github.com/crossdb-org/crossdb-rust"
9+
repository = "https://github.com/crossdb-org/crossdb-rust.git"
10+
11+
[dependencies]
12+
thiserror = "1.0"
13+
14+
[build-dependencies]
15+
bindgen = "0.70"
16+
cc = "1.1"

README.md

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# crossdb-rs
2+
3+
```toml
4+
[dependencies]
5+
crossdb = { git = "https://github.com/crossdb-org/crossdb-rust" }
6+
```
7+
8+
```rs
9+
use crossdb::Connection;
10+
11+
fn main() {
12+
let conn = Connection::open_with_memory().unwrap();
13+
let mut rst = conn.exec("select * from system.databases;").unwrap();
14+
15+
for i in 0..rst.column_count() {
16+
println!("Column {i}: {} {}", rst.column_name(i), rst.column_type(i));
17+
}
18+
19+
while let Some(row) = (&mut rst).next() {
20+
dbg!(row);
21+
}
22+
}
23+
```
24+
25+
## TODO
26+
* Add more apis
27+
* Windows support
28+
* Dynamic link crossdb
29+
* use serde to serialize/deserialize

build.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use std::env;
2+
use std::path::PathBuf;
3+
4+
fn main() {
5+
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
6+
bindgen::builder()
7+
.header("crossdb/include/crossdb.h")
8+
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
9+
.generate()
10+
.unwrap()
11+
.write_to_file(out_path.join("./bindings.rs"))
12+
.unwrap();
13+
14+
let mut builder = cc::Build::new();
15+
builder
16+
.file("crossdb/src/crossdb.c")
17+
.include("crossdb/include")
18+
.flag("-fPIC")
19+
.opt_level(2)
20+
.static_flag(true)
21+
.compile("crossdb");
22+
println!("cargo:rustc-link-lib=static=crossdb");
23+
println!("cargo:rustc-link-lib=pthread");
24+
25+
println!("cargo:rerun-if-changed=crossdb/");
26+
}

crossdb

Submodule crossdb added at 53faea9

examples/basic.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use crossdb::Connection;
2+
3+
fn main() {
4+
let conn = Connection::open("test.db").unwrap();
5+
let mut rst = conn.exec("select * FROM system.databases;").unwrap();
6+
7+
for i in 0..rst.column_count() {
8+
println!("Column {i}: {} {}", rst.column_name(i), rst.column_type(i));
9+
}
10+
11+
while let Some(row) = (&mut rst).next() {
12+
dbg!(row);
13+
}
14+
}

src/column.rs

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use crate::*;
2+
3+
// https://crossdb.org/client/api-c/#xdb_type_t
4+
#[derive(Debug, Clone, Copy)]
5+
pub enum ColumnType {
6+
Null,
7+
TinyInt,
8+
SmallInt,
9+
Int,
10+
BigInt,
11+
UTinyInt,
12+
USmallInt,
13+
UInt,
14+
UBigInt,
15+
Float,
16+
Double,
17+
Timestamp,
18+
Char,
19+
Binary,
20+
VChar,
21+
VBinary,
22+
Max,
23+
}
24+
25+
impl From<u32> for ColumnType {
26+
#[allow(non_upper_case_globals)]
27+
fn from(value: u32) -> Self {
28+
match value {
29+
xdb_type_t_XDB_TYPE_NULL => ColumnType::Null,
30+
xdb_type_t_XDB_TYPE_TINYINT => ColumnType::TinyInt,
31+
xdb_type_t_XDB_TYPE_SMALLINT => ColumnType::SmallInt,
32+
xdb_type_t_XDB_TYPE_INT => ColumnType::Int,
33+
xdb_type_t_XDB_TYPE_BIGINT => ColumnType::BigInt,
34+
xdb_type_t_XDB_TYPE_UTINYINT => ColumnType::UTinyInt,
35+
xdb_type_t_XDB_TYPE_USMALLINT => ColumnType::USmallInt,
36+
xdb_type_t_XDB_TYPE_UINT => ColumnType::UInt,
37+
xdb_type_t_XDB_TYPE_UBIGINT => ColumnType::UBigInt,
38+
xdb_type_t_XDB_TYPE_FLOAT => ColumnType::Float,
39+
xdb_type_t_XDB_TYPE_DOUBLE => ColumnType::Double,
40+
xdb_type_t_XDB_TYPE_TIMESTAMP => ColumnType::Timestamp,
41+
xdb_type_t_XDB_TYPE_CHAR => ColumnType::Char,
42+
xdb_type_t_XDB_TYPE_BINARY => ColumnType::Binary,
43+
xdb_type_t_XDB_TYPE_VCHAR => ColumnType::VChar,
44+
xdb_type_t_XDB_TYPE_VBINARY => ColumnType::VBinary,
45+
xdb_type_t_XDB_TYPE_MAX => ColumnType::Max,
46+
_ => unreachable!(),
47+
}
48+
}
49+
}
50+
51+
impl Display for ColumnType {
52+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53+
match self {
54+
ColumnType::Null => write!(f, "NULL"),
55+
ColumnType::TinyInt => write!(f, "TINYINT"),
56+
ColumnType::SmallInt => write!(f, "SMALLINT"),
57+
ColumnType::Int => write!(f, "INT"),
58+
ColumnType::BigInt => write!(f, "BIGINT"),
59+
ColumnType::UTinyInt => write!(f, "UTINYINT"),
60+
ColumnType::USmallInt => write!(f, "USMALLINT"),
61+
ColumnType::UInt => write!(f, "UINT"),
62+
ColumnType::UBigInt => write!(f, "UBIGINT"),
63+
ColumnType::Float => write!(f, "FLOAT"),
64+
ColumnType::Double => write!(f, "DOUBLE"),
65+
ColumnType::Timestamp => write!(f, "TIMESTAMP"),
66+
ColumnType::Char => write!(f, "CHAR"),
67+
ColumnType::Binary => write!(f, "BINARY"),
68+
ColumnType::VChar => write!(f, "VCHAR"),
69+
ColumnType::VBinary => write!(f, "VBINARY"),
70+
ColumnType::Max => write!(f, "MAX"),
71+
}
72+
}
73+
}
74+
75+
impl ColumnType {
76+
pub(crate) fn all(res: &xdb_res_t) -> Vec<Self> {
77+
let mut vec = Vec::with_capacity(res.col_count as usize);
78+
for i in 0..vec.capacity() {
79+
unsafe {
80+
let t = xdb_column_type(res.col_meta, i as u16);
81+
vec.push(Self::from(t));
82+
}
83+
}
84+
vec
85+
}
86+
}

src/error.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use std::ffi::NulError;
2+
3+
pub type Result<T, E = Error> = std::result::Result<T, E>;
4+
5+
#[derive(Debug, thiserror::Error)]
6+
pub enum Error {
7+
#[error("CString error: {0}")]
8+
CString(#[from] NulError),
9+
#[error("UTF8 error: {0}")]
10+
Utf8(#[from] std::str::Utf8Error),
11+
#[error("Query error: {0}, {1}")]
12+
Query(u16, String),
13+
}

0 commit comments

Comments
 (0)