Skip to content

feat: add columnar-dbc Rust crates linking dbc statically - #548

Open
zeroshade wants to merge 5 commits into
feat/capifrom
feat/rust-crates
Open

zeroshade wants to merge 5 commits into
feat/capifrom
feat/rust-crates

Conversation

@zeroshade

@zeroshade zeroshade commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

rust/: columnar-dbc (safe API), columnar-dbc-sys (bindgen 0.73.2 bindings generated from libdbc.h, committed) and five columnar-dbc-sys-<platform> crates, each bundling a prebuilt libdbc.a. Supported targets: x86_64/aarch64 Linux (glibc), x86_64/aarch64 macOS, x86_64-pc-windows-msvc and x86_64-pc-windows-gnu. Other targets fail at build time.

MSVC: a Go c-archive registers its runtime entry in .ctors, which only the MinGW CRT runs. Under MSVC the first dbc call blocked forever. The Windows platform crate registers _rt0_amd64_windows_lib in .CRT$XCU for target_env = "msvc". A local cargo xwin link map shows the pointer in the CRT's XCA..XCZ initializer table. CI in the top PR of the stack runs the tests.

Stacked on #547.

Linking libdbc

Modeled on zstd-sys, with pkg-config as the default. The source is chosen in this order:

  1. DBC_SYS_LIB_DIR=<dir>: a custom directory.
  2. DBC_SYS_USE_PKG_CONFIG set: pkg-config module dbc, even with bundled on.
  3. The opt-in bundled feature: the per-platform prebuilt crates.
  4. Otherwise (default): pkg-config module dbc.

By default the platform crates aren't downloaded at all; features = ["bundled"] opts into them. Env overrides win even when bundled is on: the platform crate is never extern crated, so its archive isn't linked. docs.rs builds with bundled since it has no installed libdbc.

Linking is static by default; DBC_DYNAMIC (pkg-config-rs's own convention) links libdbc.so. pkg-config-rs refuses static links for archives under /usr, so build.rs emits the link directives itself rather than using .statik(true).

The MSVC Go runtime initializer moved from the Windows platform crate into -sys. It's applied to any static x86_64 MSVC link, and static MSVC links on other architectures fail with a clear error.

@zeroshade
zeroshade added this pull request to stack #550 October 28, 2026 22:46
…http

tiny_http 0.12 unwraps peer_addr() on accept; on Windows it fails with WSAENOTCONN when the client has already closed the connection, killing the worker and failing the next request with EOF.
…LIB_DIR

Keep bundled platform crates as the default, with custom-directory and pkg-config overrides selecting static or opt-in dynamic linking. Move the MSVC Go initializer into the sys crate and reject non-x86_64 static MSVC links.

pkg-config 0.3.34 parse_libs defaults the non-macOS/non-Windows system root to /usr, and is_static_available requires an archive path outside every system root. Thus statik(true) alone emits an unqualified link for /usr/lib/<triplet>/libdbc.a, allowing the shared library to win. Probe without Cargo link metadata and explicitly emit static=dbc, leaving private system libraries dynamic.
The default build now locates an installed libdbc through the dbc
pkg-config module. The prebuilt platform crates move behind the
`bundled` feature, and the `pkg-config` feature is removed since it is
now the default. DBC_SYS_LIB_DIR and DBC_SYS_USE_PKG_CONFIG keep their
precedence over `bundled`.
@zeroshade
zeroshade marked this pull request as ready for review November 3, 2026 19:27

@amoeba amoeba left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally I think this looks fine. I looked closer at some files than others and I expect I may have more review comments once this is all merged together.

```rust
use columnar_dbc::{list_installed, Client, InstallLocation};

let client = Client::new()?;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let client = Client::new()?;
// First create a Client
let client = Client::new()?;

use columnar_dbc::{list_installed, Client, InstallLocation};

let client = Client::new()?;
for d in client.search("postgres")?.drivers {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for d in client.search("postgres")?.drivers {
// Search for a driver using a pattern
for d in client.search("postgres")?.drivers {

println!("{} - {}", d.path, d.description);
}

// Into dbc's user-level location, like `dbc install -l user` ...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Into dbc's user-level location, like `dbc install -l user` ...
// Install a driver to the user level install location. Same behavior as `dbc install -l user`.

}

// Into dbc's user-level location, like `dbc install -l user` ...
let manifest = client.install("postgresql", &InstallLocation::User)?;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm.... So I think the C API and therefore Rust require an explicit install location. I'm now wondering whether we can have bindings for install behave more like the CLI. I think the ideal code here would be,

let driver = client.install("postgresql")?;

let dir = InstallLocation::Directory("/opt/adbc/drivers".into());
client.install("postgresql", &dir)?;
assert_eq!(list_installed(&dir)?.len(), 1);
# Ok::<(), columnar_dbc::Error>(())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this line doing? If this was supposed to be a doctest, I don't think the README is hooked up yet. May as well. I guess you can add this to lib.rs,

#![doc = include_str!("../README.md")]

and fence the codeblock with ````rust,no_run`.

Comment on lines +29 to +30
`install` verifies each driver's signature with Columnar's key and removes a
driver that fails. For drivers from a trusted private registry,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`install` verifies each driver's signature with Columnar's key and removes a
driver that fails. For drivers from a trusted private registry,
`install` verifies each driver's signature with Columnar's public signing key and cleans up a
driver that fails verification. For drivers from a trusted private registry,

Comment on lines +40 to +43
User,
/// dbc's system-level location, as `dbc install -l system`; usually needs
/// elevated privileges.
System,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as in earlier PRs. dbc's config.ConfigLevel is System|User|Env. I'm not sure if it matters to align.


#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct Driver {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this struct be missing the other fields in the registry index like urls, docs_url, pkginfo, and an association to a registry so we can disambiguate drivers with the same path on multiple registries?

let manifest = client.install("postgresql", &InstallLocation::User)?;
println!("installed {} {} at {}", manifest.id, manifest.version, manifest.driver_path);

// ... or into an explicit directory.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// ... or into an explicit directory.
// You can also install the driver to a specific directory.

I don't really like how these PR stacks are exposing this new way of installing drivers. The old way was to set ADBC_DRIVER_PATH before doing things and now we have two ways.

This branch was previously deployed

1 inactive deployment
snapshot — e74846d3 Deployed Nov 3, 2026 by zeroshade via snapshot #1119
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants