Skip to content

Repository files navigation

cubrid-rs

Native Rust database driver for CUBRID — built by reverse-engineering the CAS wire protocol. Pure Rust, no FFI, sync + async.

crates.io Rust 1.75+ CI Coverage license GitHub stars

Built by Reverse Engineering

cubrid-rs was not built from an official protocol specification — none exists. Instead, the entire CAS (Common Application Server) binary wire protocol was decoded by cross-referencing three existing open-source implementations (cubrid-go, cubrid-client, pycubrid), running targeted experiments against live CUBRID servers, and reading server-side C source code when the clients disagreed.

The full reverse engineering story — methodology, discoveries, pitfalls, and protocol details — is documented in PROTOCOL_RESEARCH.md.

Key discoveries include:

  • The FC=41 (PrepareAndExecute) function code doesn't support server-side bind parameters — requiring client-side SQL interpolation
  • Stored procedure results embed actual type codes inside value data (column metadata reports NULL type)
  • Two distinct write families (write_* raw vs add_* length-prefixed) that cause silent data corruption when confused
  • A gap in the DataType enum (no type code 20) that's intentional, not a bug

Why cubrid-rs?

cubrid-rs CCI (C interface)
FFI Required No — pure Rust Yes
Cross-compilation Standard Cargo targets Requires C toolchain
Sync + Async Native crates for both Manual wrappers
Connection Pooling Native Rust pool crate Manual management
Deployment Rust binary + crates only Shared library dependency
Test Coverage 95.11% (366 tests) Varies

cubrid-rs speaks the CUBRID CAS protocol directly over TCP with native Rust crates designed for modern sync and async services.

Installation

# Sync client
cargo add cubrid-client

# Async client (tokio)
cargo add cubrid-tokio

# Connection pool
cargo add cubrid-pool

Requirements: Rust 1.75+

Quick Start — Sync Client

use cubrid_client::Client;

fn main() -> Result<(), cubrid_client::Error> {
    let mut client = Client::connect("cubrid://dba:@localhost:33000/demodb")?;
    let rows = client.query("SELECT * FROM athlete WHERE nation_code = ?", &["KOR"])?;
    for row in rows {
        println!("{:?}", row);
    }
    Ok(())
}

Quick Start — Async Client (tokio)

use cubrid_tokio::Client;

#[tokio::main]
async fn main() -> Result<(), cubrid_tokio::Error> {
    let mut client = Client::connect("cubrid://dba:@localhost:33000/demodb").await?;
    let rows = client.query("SELECT 1 + 1", &[]).await?;
    for row in rows {
        println!("{:?}", row);
    }
    Ok(())
}

Quick Start — Connection Pool

use cubrid_pool::Pool;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let pool = Pool::builder()
        .max_size(10)
        .build("cubrid://dba:@localhost:33000/demodb")
        .await?;

    let mut client = pool.get().await?;
    let rows = client.query("SELECT 1", &[]).await?;
    println!("{:?}", rows);
    Ok(())
}

DSN Format

cubrid://[user[:password]]@host[:port]/database
Parameter Default Description
host localhost CUBRID broker host
port 33000 CUBRID broker port
database (required) Target database name
user "" Database user
password "" Database password

Crate Overview

Crate Status Description
cubrid-protocol ✅ Stable CAS wire protocol codec — zero I/O dependencies
cubrid-client ✅ Stable Synchronous client with full query/transaction support
cubrid-tokio ✅ Stable Async client built on tokio
cubrid-pool ✅ Stable Async connection pool with configurable limits

Type Mapping

CUBRID Rust Notes
SMALLINT i16
INTEGER i32
BIGINT i64
FLOAT f32
DOUBLE, MONETARY f64
CHAR, VARCHAR, STRING String
NCHAR, VARNCHAR String
BIT, VARBIT Vec<u8>
NUMERIC String Preserves arbitrary precision
DATE String "YYYY-MM-DD"
TIME String "HH:MM:SS"
DATETIME String "YYYY-MM-DD HH:MM:SS.fff"
TIMESTAMP String "YYYY-MM-DD HH:MM:SS"
BLOB Vec<u8>
CLOB String
SET, MULTISET, SEQUENCE Vec<Value> Nested type-tagged arrays
ENUM String
OBJECT String OID representation

Architecture

flowchart TD
    A[cubrid-rs/\n4 crates, 1 workspace]
    A --> B[crates/]
    B --> C[cubrid-protocol/\nPure protocol codec (no I/O)]
    C --> C1[constants.rs\nCAS function codes, type codes, flags]
    C --> C2[handshake.rs\nBroker handshake + OpenDatabase]
    C --> C3[codec.rs\nPacketWriter / PacketReader]
    C --> C4[request.rs\nRequest frame builders]
    C --> C5[response.rs\nResponse parsers]
    C --> C6[value.rs\nValue enum + type conversions]
    B --> D[cubrid-client/\nSync TCP client]
    B --> E[cubrid-tokio/\nAsync tokio client]
    B --> F[cubrid-pool/\nAsync connection pool]
    A --> G[docs/]
    G --> G1[PROTOCOL_RESEARCH.md\nReverse engineering narrative]
    G --> G2[PRD.md\nProduct requirements]
    G --> G3[TDD.md\nTechnical design decisions]
    G --> G4[ARCHITECTURE.md\nWorkspace + dependency graph]
    G --> G5[ROADMAP.md\nRelease plan]
    A --> H[examples/]
Loading
flowchart LR
    cubrid_pool[cubrid-pool] --> cubrid_client[cubrid-client]
    cubrid_client --> cubrid_protocol[cubrid-protocol]
    cubrid_tokio[cubrid-tokio] --> cubrid_protocol
Loading

Protocol Notes

The CAS connection flow, decoded by reverse engineering:

  1. Broker handshake — Send CUBRK + client metadata (10 bytes), receive CAS port redirect (4 bytes)
  2. Open database — Send 628-byte fixed credential payload, receive session + protocol version
  3. Framed RPC — All operations use [DATA_LENGTH][CAS_INFO][FC + args] frames
  4. Function codes — 11 core FCs implemented: PREPARE, EXECUTE, FETCH, END_TRAN, etc.

See PROTOCOL_RESEARCH.md for the complete story.

flowchart LR
    A[Handshake] --> B[OpenDatabase]
    B --> C[Execute]
    C --> D[CloseDatabase]
Loading

Documentation

Document Description
Protocol Research Full reverse engineering narrative — methodology, discoveries, pitfalls
PRD Product requirements and phased plan
TDD Technical design decisions
Architecture Workspace and dependency graph
Roadmap Planned releases

FAQ

How do I connect?

Use the DSN format: cubrid://[user[:password]]@host[:port]/database.

What Rust version is required?

Rust 1.75 or later.

Does cubrid-rs require unsafe code?

No. All crates enforce #![deny(unsafe_code)].

Is async supported?

Yes. cubrid-tokio provides a fully async client built on tokio. cubrid-pool provides async connection pooling on top of it.

Does this use C libraries or FFI?

No. The project is pure Rust. The CAS protocol was reverse-engineered and implemented from scratch.

How was the protocol decoded?

By cross-referencing three existing open-source client implementations (Go, TypeScript, Python) and testing against live CUBRID servers. See PROTOCOL_RESEARCH.md.

Ecosystem

Package Description
cubrid-rs Native Rust CUBRID workspace
sea-orm-cubrid SeaORM backend for CUBRID

License

Roadmap

See docs/ROADMAP.md for detailed release plans and protocol implementation progress.

For the ecosystem-wide view, see the CUBRID Labs Ecosystem Roadmap and Project Board.

MIT

About

Native Rust database driver for CUBRID — sync + async, pure Rust, no FFI

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages