asg017 / sqlite-vss

A SQLite extension for efficient vector search, based on Faiss!
MIT License
1.59k stars 59 forks source link

Tracking: Rust Bindings #50

Open asg017 opened 1 year ago

asg017 commented 1 year ago

Do you have any issues using the new Rust bindings for sqlite-vss? Comment on this issue with any bugs or crashes you come across, or with suggestions on how to make it better.

TODOs for Rust bindings:

andrew-pyle commented 11 months ago

I've hit compilation issues on M1 iMac.

@asg017 Could we get some more precise docs for using the Rust bindings?

Maybe you can distill my experience into better/more documentation for compiling sqlite-vss on M1 Macs? I'm not very knowledgeable about compiled dependencies.

Context

[dependencies]
rusqlite = { version = "0.29.0", features = ["bundled"] }
sqlite-vss = { version = "0.1.1", features = ["download-libs"] }
// build.rs
fn main() {
    if cfg!(target_os = "macos") {
        println!("cargo:rustc-link-arg=-Wl,-undefined,dynamic_lookup,-lomp");
    } else if cfg!(target_os = "linux") {
        println!("cargo:rustc-link-arg=-Wl,-undefined,dynamic_lookup,-lstdc++");
    }
}
// main.rs
use rusqlite::{ffi::sqlite3_auto_extension, Connection, Result};
use sqlite_vss::{sqlite3_vector_init, sqlite3_vss_init};

fn main() -> Result<()> {
    unsafe {
        sqlite3_auto_extension(Some(sqlite3_vector_init));
        sqlite3_auto_extension(Some(sqlite3_vss_init));
    }

    let db = Connection::open_in_memory()?;

    let (version, vector): (String, String) = db.query_row(
        "SELECT vss_version(), vector_to_json(?)",
        [[0x00, 0x00, 0x28, 0x42]],
        |row| Ok((row.get(0)?, row.get(1)?)),
    )?;
    println!("version={version} vector={vector}");

    Ok(())
}

What I did

$ cargo run
   Compiling sqlite-vss-demo v0.1.0
error: linking with `cc` failed: exit status: 1
  |
  = note: LC_ALL="C" PATH="..."

  # ... clipped ...

  = note: ld: library not found for -lomp
          clang: error: linker command failed with exit code 1 (use -v to see invocation)

error: could not compile `sqlite-vss-demo` (bin "sqlite-vss-demo") due to previous error

I saw #61, which mentioned an argument for RUSTFLAGS containing LLVM and libomp so I installed libomp with Homebrew. I am not sure why llvm is not also required to be installed by Homebrew.

$ brew install libomp

==> Fetching libomp
==> Downloading https://ghcr.io/v2/homebrew/core/libomp/manifests/16.0.6
==> Downloading https://ghcr.io/v2/homebrew/core/libomp/blobs/sha256:3dee22dd4f55d9bb85cc5b89ae5d99e6e2f52b151ca8768c9f71e41cf88f9986
==> Pouring libomp--16.0.6.arm64_monterey.bottle.tar.gz
==> Caveats
libomp is keg-only, which means it was not symlinked into /opt/homebrew,
because it can override GCC headers and result in broken builds.

For compilers to find libomp you may need to set:
  export LDFLAGS="-L/opt/homebrew/opt/libomp/lib"
  export CPPFLAGS="-I/opt/homebrew/opt/libomp/include"

I ran the Rust compiler again, but there was no change.

$ cargo run
   Compiling sqlite-vss-demo v0.1.0
error: linking with `cc` failed: exit status: 1
  |
  = note: LC_ALL="C" PATH="..."

  # ... clipped ...

  = note: ld: library not found for -lomp
          clang: error: linker command failed with exit code 1 (use -v to see invocation)

error: could not compile `sqlite-vss-demo` (bin "sqlite-vss-demo") due to previous error

I noticed the "Caveat " that Homebrew installed libomp as a keg-only,

==> Caveats
libomp is keg-only, which means it was not symlinked into /opt/homebrew,
because it can override GCC headers and result in broken builds.

For compilers to find libomp you may need to set:
  export LDFLAGS="-L/opt/homebrew/opt/libomp/lib"
  export CPPFLAGS="-I/opt/homebrew/opt/libomp/include"

So I ran cargo again, with the RUSTFLAGS from #61, and the program compiled!

RUSTFLAGS="-L/opt/homebrew/opt/libomp/lib -L/opt/homebrew/opt/llvm/lib -lblas -llapack" cargo run
   Compiling ...
   # ... clipped ... 
    Finished dev [unoptimized + debuginfo] target(s) in 12.46s
     Running `target/debug/sqlite-vss-demo`
version=v0.1.1 vector=[42.0]

Questions for the Docs:

aavshr commented 4 months ago

The Rust bindings are no longer compatible with later versions of rusqlite , they changed the sqlite3_auto_extension function signature since versions >=0.30.0

https://github.com/rusqlite/rusqlite/pull/1310/files

prabirshrestha commented 3 months ago

I was able to load sqlite-vss dynamically using sqlx. I personally want to statically compile it and filed a feature request for sqlx since it doesn't support it. https://github.com/launchbadge/sqlx/issues/3147#issuecomment-2016608238. They do mention that the current signature for sqlite-vss is not correct. Would be great if it was changed such that it would be compatible with newer version of rusqlite. Then could also add feature in sqlx to support it so the same signature works for both libraries.

ospfranco commented 2 months ago

I have managed to load the extension into sqlx by directly calling libsqlite3_sys. libsqlite3_sys is the underlaying sqlite3 library both sqlx and rusqlite use. You will need to pin your versions to avoid accidental breakage, but just adding it to your Cargo.toml and then calling the sqlite3_auto_extension works. Even though the signature does not match one can just unsafely cast it to whatever Rust neeeds:

use sqlite_vss::{sqlite3_vector_init, sqlite3_vss_init};

unsafe {
    let vss_vector_init = sqlite3_vector_init as *const ();
    let vss_vector_init_correct: extern "C" fn(
        db: *mut sqlite3,
        pzErrMsg: *mut *const c_char,
        pThunk: *const sqlite3_api_routines,
    ) -> i32 = std::mem::transmute(vss_vector_init);
    libsqlite3_sys::sqlite3_auto_extension(Some(vss_vector_init_correct));

    let vss_init = sqlite3_vss_init as *const ();
    let vss_init_correct: extern "C" fn(
        db: *mut sqlite3,
        pzErrMsg: *mut *const c_char,
        pThunk: *const sqlite3_api_routines,
    ) -> i32 = std::mem::transmute(vss_init);
    libsqlite3_sys::sqlite3_auto_extension(Some(vss_init_correct));
}

I also had to add the following to the build.rs after installing libomp from homebrew:

fn main() {
    if cfg!(target_os = "macos") {
        println!("cargo:rustc-link-arg=-Wl,-undefined,dynamic_lookup,-lomp,-L/opt/homebrew/opt/libomp/lib");
    } else if cfg!(target_os = "linux") {
        println!("cargo:rustc-link-arg=-Wl,-undefined,dynamic_lookup,-lstdc++");
    }
}
ospfranco commented 2 months ago

When I try to compile the library on Linux I'm getting the error:

cannot find -latlas: No such file or directory

For windows I'm getting the following error:

error: failed to run custom build command for `sqlite-vss v0.1.2`

Caused by:
  process didn't exit successfully: `D:\a\pulsar\pulsar\src-tauri\target\release\build\sqlite-vss-66204e7efd1cde22\build-script-build` (exit code: 101)
  --- stderr
  thread 'main' panicked at C:\Users\runneradmin/.cargo\registry\src\index.crates.io-6f17d22bba15001f\sqlite-vss-0.1.2\build.rs:35:14:
  Unsupported platform: windows x86_64
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

@asg017 would it be possible to fix the scripts to automatically take care of these changes?

Edit: I'm dumb and skipped the documentation building on platform for linux the following is required:

sudo apt-get install libgomp1 libatlas-base-dev liblapack-dev libsqlite3-dev

It seems however there is no distributable for Windows