Skip to content

Add Rust Driver #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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