Skip to content

Commit

Permalink
Merge pull request #1 from wyhaya/first
Browse files Browse the repository at this point in the history
Add Rust Driver
  • Loading branch information
wyhaya authored Oct 12, 2024
2 parents 3c64d15 + efadc6b commit e94482f
Show file tree
Hide file tree
Showing 12 changed files with 409 additions and 17 deletions.
55 changes: 55 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Test

on: [push, pull_request]

jobs:
build:
name: ${{ matrix.job.target }}
runs-on: ${{ matrix.job.os }}
strategy:
matrix:
job:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest

- target: x86_64-apple-darwin
os: macos-latest

- target: aarch64-apple-darwin
os: macos-latest

# TODO
# - target: x86_64-pc-windows-msvc
# os: windows-latest

steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ matrix.job.target }}

- name: Setup Rust
run: |
rustup update
rustup target add ${{ matrix.job.target }}
- name: Cargo fmt
run: |
cargo fmt --all -- --check
- name: Cargo test
run: |
cargo test --target ${{ matrix.job.target }}
- name: Cargo run --example
run: |
cargo run --example basic --target ${{ matrix.job.target }}
19 changes: 2 additions & 17 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,21 +1,6 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

# RustRover
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
.DS_Store
test.db/
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "crossdb"]
path = crossdb
url = https://github.com/crossdb-org/crossdb
16 changes: 16 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "crossdb"
version = "0.0.1"
edition = "2021"
license = "MIT"
description = "CrossDB Rust Driver"
readme = "README.md"
homepage = "hhttps://github.com/crossdb-org/crossdb-rust"
repository = "https://github.com/crossdb-org/crossdb-rust.git"

[dependencies]
thiserror = "1.0"

[build-dependencies]
bindgen = "0.70"
cc = "1.1"
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# crossdb-rs

```toml
[dependencies]
crossdb = { git = "https://github.com/crossdb-org/crossdb-rust" }
```

```rs
use crossdb::Connection;

fn main() {
let conn = Connection::open_with_memory().unwrap();
let mut rst = conn.exec("select * from system.databases;").unwrap();

for i in 0..rst.column_count() {
println!("Column {i}: {} {}", rst.column_name(i), rst.column_type(i));
}

while let Some(row) = (&mut rst).next() {
dbg!(row);
}
}
```

## TODO
* Add more apis
* Windows support
* Dynamic link crossdb
* use serde to serialize/deserialize
26 changes: 26 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::env;
use std::path::PathBuf;

fn main() {
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindgen::builder()
.header("crossdb/include/crossdb.h")
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()
.unwrap()
.write_to_file(out_path.join("./bindings.rs"))
.unwrap();

let mut builder = cc::Build::new();
builder
.file("crossdb/src/crossdb.c")
.include("crossdb/include")
.flag("-fPIC")
.opt_level(2)
.static_flag(true)
.compile("crossdb");
println!("cargo:rustc-link-lib=static=crossdb");
println!("cargo:rustc-link-lib=pthread");

println!("cargo:rerun-if-changed=crossdb/");
}
1 change: 1 addition & 0 deletions crossdb
Submodule crossdb added at 53faea
14 changes: 14 additions & 0 deletions examples/basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use crossdb::Connection;

fn main() {
let conn = Connection::open("test.db").unwrap();
let mut rst = conn.exec("select * FROM system.databases;").unwrap();

for i in 0..rst.column_count() {
println!("Column {i}: {} {}", rst.column_name(i), rst.column_type(i));
}

while let Some(row) = (&mut rst).next() {
dbg!(row);
}
}
86 changes: 86 additions & 0 deletions src/column.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use crate::*;

// https://crossdb.org/client/api-c/#xdb_type_t
#[derive(Debug, Clone, Copy)]
pub enum ColumnType {
Null,
TinyInt,
SmallInt,
Int,
BigInt,
UTinyInt,
USmallInt,
UInt,
UBigInt,
Float,
Double,
Timestamp,
Char,
Binary,
VChar,
VBinary,
Max,
}

impl From<u32> for ColumnType {
#[allow(non_upper_case_globals)]
fn from(value: u32) -> Self {
match value {
xdb_type_t_XDB_TYPE_NULL => ColumnType::Null,
xdb_type_t_XDB_TYPE_TINYINT => ColumnType::TinyInt,
xdb_type_t_XDB_TYPE_SMALLINT => ColumnType::SmallInt,
xdb_type_t_XDB_TYPE_INT => ColumnType::Int,
xdb_type_t_XDB_TYPE_BIGINT => ColumnType::BigInt,
xdb_type_t_XDB_TYPE_UTINYINT => ColumnType::UTinyInt,
xdb_type_t_XDB_TYPE_USMALLINT => ColumnType::USmallInt,
xdb_type_t_XDB_TYPE_UINT => ColumnType::UInt,
xdb_type_t_XDB_TYPE_UBIGINT => ColumnType::UBigInt,
xdb_type_t_XDB_TYPE_FLOAT => ColumnType::Float,
xdb_type_t_XDB_TYPE_DOUBLE => ColumnType::Double,
xdb_type_t_XDB_TYPE_TIMESTAMP => ColumnType::Timestamp,
xdb_type_t_XDB_TYPE_CHAR => ColumnType::Char,
xdb_type_t_XDB_TYPE_BINARY => ColumnType::Binary,
xdb_type_t_XDB_TYPE_VCHAR => ColumnType::VChar,
xdb_type_t_XDB_TYPE_VBINARY => ColumnType::VBinary,
xdb_type_t_XDB_TYPE_MAX => ColumnType::Max,
_ => unreachable!(),
}
}
}

impl Display for ColumnType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ColumnType::Null => write!(f, "NULL"),
ColumnType::TinyInt => write!(f, "TINYINT"),
ColumnType::SmallInt => write!(f, "SMALLINT"),
ColumnType::Int => write!(f, "INT"),
ColumnType::BigInt => write!(f, "BIGINT"),
ColumnType::UTinyInt => write!(f, "UTINYINT"),
ColumnType::USmallInt => write!(f, "USMALLINT"),
ColumnType::UInt => write!(f, "UINT"),
ColumnType::UBigInt => write!(f, "UBIGINT"),
ColumnType::Float => write!(f, "FLOAT"),
ColumnType::Double => write!(f, "DOUBLE"),
ColumnType::Timestamp => write!(f, "TIMESTAMP"),
ColumnType::Char => write!(f, "CHAR"),
ColumnType::Binary => write!(f, "BINARY"),
ColumnType::VChar => write!(f, "VCHAR"),
ColumnType::VBinary => write!(f, "VBINARY"),
ColumnType::Max => write!(f, "MAX"),
}
}
}

impl ColumnType {
pub(crate) fn all(res: &xdb_res_t) -> Vec<Self> {
let mut vec = Vec::with_capacity(res.col_count as usize);
for i in 0..vec.capacity() {
unsafe {
let t = xdb_column_type(res.col_meta, i as u16);
vec.push(Self::from(t));
}
}
vec
}
}
13 changes: 13 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use std::ffi::NulError;

pub type Result<T, E = Error> = std::result::Result<T, E>;

#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("CString error: {0}")]
CString(#[from] NulError),
#[error("UTF8 error: {0}")]
Utf8(#[from] std::str::Utf8Error),
#[error("Query error: {0}, {1}")]
Query(u16, String),
}
Loading

0 comments on commit e94482f

Please sign in to comment.